6122962b2e7d3c29b0389f2ad4e130fc44377b0a
[WebKit-https.git] / Source / WebCore / rendering / TextAutosizer.cpp
1 /*
2  * Copyright (C) 2012 Google Inc. All rights reserved.
3  * Copyright (C) 2012 Apple Inc. All rights reserved.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public License
16  * along with this library; see the file COPYING.LIB.  If not, write to
17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 #include "config.h"
22
23 #if ENABLE(TEXT_AUTOSIZING)
24
25 #include "TextAutosizer.h"
26
27 #include "Document.h"
28 #include "InspectorInstrumentation.h"
29 #include "RenderObject.h"
30 #include "RenderStyle.h"
31 #include "RenderText.h"
32 #include "RenderView.h"
33 #include "Settings.h"
34 #include "StyleInheritedData.h"
35
36 namespace WebCore {
37
38 TextAutosizer::TextAutosizer(Document* document)
39     : m_document(document)
40 {
41 }
42
43 TextAutosizer::~TextAutosizer()
44 {
45 }
46
47 bool TextAutosizer::processSubtree(RenderObject* layoutRoot)
48 {
49     // FIXME: Text Autosizing should only be enabled when m_document->page()->mainFrame()->view()->useFixedLayout()
50     // is true, but for now it's useful to ignore this so that it can be tested on desktop.
51     if (!m_document->settings() || !m_document->settings()->textAutosizingEnabled() || layoutRoot->view()->printing() || !m_document->page())
52         return false;
53
54     Frame* mainFrame = m_document->page()->mainFrame();
55
56     // Window area, in logical (density-independent) pixels.
57     IntSize windowSize = m_document->settings()->textAutosizingWindowSizeOverride();
58     if (windowSize.isEmpty()) {
59         bool includeScrollbars = !InspectorInstrumentation::shouldApplyScreenWidthOverride(mainFrame);
60         windowSize = mainFrame->view()->visibleContentRect(includeScrollbars).size(); // FIXME: Check that this is always in logical (density-independent) pixels (see wkbug.com/87440).
61     }
62
63     // Largest area of block that can be visible at once (assuming the main
64     // frame doesn't get scaled to less than overview scale), in CSS pixels.
65     IntSize minLayoutSize = mainFrame->view()->layoutSize();
66     for (Frame* frame = m_document->frame(); frame; frame = frame->tree()->parent()) {
67         if (!frame->view()->isInChildFrameWithFrameFlattening())
68             minLayoutSize = minLayoutSize.shrunkTo(frame->view()->layoutSize());
69     }
70
71     for (RenderObject* descendant = layoutRoot->nextInPreOrder(layoutRoot); descendant; descendant = descendant->nextInPreOrder(layoutRoot)) {
72         if (isNotAnAutosizingContainer(descendant))
73             continue;
74         processBox(toRenderBox(descendant), windowSize, minLayoutSize);
75     }
76
77     return true;
78 }
79
80 void TextAutosizer::processBox(RenderBox* box, const IntSize& windowSize, const IntSize& layoutSize)
81 {
82     int logicalWindowWidth = box->isHorizontalWritingMode() ? windowSize.width() : windowSize.height();
83     int logicalLayoutWidth = box->isHorizontalWritingMode() ? layoutSize.width() : layoutSize.height();
84     // Ignore box width in excess of the layout width, to avoid extreme multipliers.
85     float logicalBoxWidth = std::min<float>(box->logicalWidth(), logicalLayoutWidth);
86
87     float multiplier = logicalBoxWidth / logicalWindowWidth;
88     multiplier *= m_document->settings()->textAutosizingFontScaleFactor();
89
90     if (multiplier < 1)
91         return;
92     RenderObject* descendant = nextInPreOrderMatchingFilter(box, box, isNotAnAutosizingContainer);
93     while (descendant) {
94         if (descendant->isText()) {
95             setMultiplier(descendant, multiplier);
96             setMultiplier(descendant->parent(), multiplier); // Parent does line spacing.
97             // FIXME: Increase list marker size proportionately.
98         }
99         descendant = nextInPreOrderMatchingFilter(descendant, box, isNotAnAutosizingContainer);
100     }
101 }
102
103 void TextAutosizer::setMultiplier(RenderObject* renderer, float multiplier)
104 {
105     RefPtr<RenderStyle> newStyle = RenderStyle::clone(renderer->style());
106     newStyle->setTextAutosizingMultiplier(multiplier);
107     renderer->setStyle(newStyle.release());
108 }
109
110 bool TextAutosizer::isNotAnAutosizingContainer(const RenderObject* renderer)
111 {
112     // "Autosizing containers" are the smallest unit for which we can enable/disable
113     // Text Autosizing. A uniform text size multiplier is enforced within them.
114     // - Must be RenderBoxes since they need a well-defined logicalWidth().
115     // - Must not be inline, as different multipliers on one line looks terrible.
116     // - Must not be list items, as items in the same list should look consistent.
117     return !renderer->isBox() || renderer->isInline() || renderer->isListItem();
118 }
119
120 RenderObject* TextAutosizer::nextInPreOrderMatchingFilter(RenderObject* current, const RenderObject* stayWithin, RenderObjectFilterFunctor filter)
121 {
122     for (RenderObject* child = current->firstChild(); child; child = child->nextSibling())
123         if (filter(child))
124             return child;
125
126     for (const RenderObject* ancestor = current; ancestor; ancestor = ancestor->parent()) {
127         if (ancestor == stayWithin)
128             return 0;
129         for (RenderObject* sibling = ancestor->nextSibling(); sibling; sibling = sibling->nextSibling())
130             if (filter(sibling))
131                 return sibling;
132     }
133
134     return 0;
135 }
136
137 } // namespace WebCore
138
139 #endif // ENABLE(TEXT_AUTOSIZING)