Text Autosizing: Only take into account block width <= document layout width.
[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
35 namespace WebCore {
36
37 TextAutosizer::TextAutosizer(Document* document)
38     : m_document(document)
39 {
40 }
41
42 TextAutosizer::~TextAutosizer()
43 {
44 }
45
46 bool TextAutosizer::processSubtree(RenderObject* layoutRoot)
47 {
48     // FIXME: Text Autosizing should only be enabled when m_document->page()->mainFrame()->view()->useFixedLayout()
49     // is true, but for now it's useful to ignore this so that it can be tested on desktop.
50     if (!m_document->settings() || !m_document->settings()->textAutosizingEnabled() || layoutRoot->view()->printing() || !m_document->page())
51         return false;
52
53     Frame* mainFrame = m_document->page()->mainFrame();
54
55     // Window area, in logical (density-independent) pixels.
56     IntSize windowSize = m_document->settings()->textAutosizingWindowSizeOverride();
57     if (windowSize.isEmpty()) {
58         bool includeScrollbars = !InspectorInstrumentation::shouldApplyScreenWidthOverride(mainFrame);
59         windowSize = mainFrame->view()->visibleContentRect(includeScrollbars).size(); // FIXME: Check that this is always in logical (density-independent) pixels (see wkbug.com/87440).
60     }
61
62     // Largest area of block that can be visible at once (assuming the main
63     // frame doesn't get scaled to less than overview scale), in CSS pixels.
64     IntSize minLayoutSize = mainFrame->view()->layoutSize();
65     for (Frame* frame = m_document->frame(); frame; frame = frame->tree()->parent()) {
66         if (!frame->view()->isInChildFrameWithFrameFlattening())
67             minLayoutSize = minLayoutSize.shrunkTo(frame->view()->layoutSize());
68     }
69
70     for (RenderObject* descendant = layoutRoot->nextInPreOrder(layoutRoot); descendant; descendant = descendant->nextInPreOrder(layoutRoot)) {
71         if (isNotAnAutosizingContainer(descendant))
72             continue;
73         processBox(toRenderBox(descendant), windowSize, minLayoutSize);
74     }
75
76     return true;
77 }
78
79 void TextAutosizer::processBox(RenderBox* box, const IntSize& windowSize, const IntSize& layoutSize)
80 {
81     int logicalWindowWidth = box->isHorizontalWritingMode() ? windowSize.width() : windowSize.height();
82     int logicalLayoutWidth = box->isHorizontalWritingMode() ? layoutSize.width() : layoutSize.height();
83     // Ignore box width in excess of the layout width, to avoid extreme multipliers.
84     float logicalBoxWidth = std::min<float>(box->logicalWidth(), logicalLayoutWidth);
85
86     float multiplier = logicalBoxWidth / logicalWindowWidth;
87     multiplier *= m_document->settings()->textAutosizingFontScaleFactor();
88
89     if (multiplier < 1)
90         return;
91     RenderObject* descendant = nextInPreOrderMatchingFilter(box, box, isNotAnAutosizingContainer);
92     while (descendant) {
93         if (descendant->isText()) {
94             setMultiplier(descendant, multiplier);
95             setMultiplier(descendant->parent(), multiplier); // Parent does line spacing.
96             // FIXME: Increase list marker size proportionately.
97         }
98         descendant = nextInPreOrderMatchingFilter(descendant, box, isNotAnAutosizingContainer);
99     }
100 }
101
102 void TextAutosizer::setMultiplier(RenderObject* renderer, float multiplier)
103 {
104     RefPtr<RenderStyle> newStyle = RenderStyle::clone(renderer->style());
105     newStyle->setTextAutosizingMultiplier(multiplier);
106     renderer->setStyle(newStyle.release());
107 }
108
109 bool TextAutosizer::isNotAnAutosizingContainer(const RenderObject* renderer)
110 {
111     // "Autosizing containers" are the smallest unit for which we can enable/disable
112     // Text Autosizing. A uniform text size multiplier is enforced within them.
113     // - Must be RenderBoxes since they need a well-defined logicalWidth().
114     // - Must not be inline, as different multipliers on one line looks terrible.
115     // - Must not be list items, as items in the same list should look consistent.
116     return !renderer->isBox() || renderer->isInline() || renderer->isListItem();
117 }
118
119 RenderObject* TextAutosizer::nextInPreOrderMatchingFilter(RenderObject* current, const RenderObject* stayWithin, RenderObjectFilterFunctor filter)
120 {
121     for (RenderObject* child = current->firstChild(); child; child = child->nextSibling())
122         if (filter(child))
123             return child;
124
125     for (const RenderObject* ancestor = current; ancestor; ancestor = ancestor->parent()) {
126         if (ancestor == stayWithin)
127             return 0;
128         for (RenderObject* sibling = ancestor->nextSibling(); sibling; sibling = sibling->nextSibling())
129             if (filter(sibling))
130                 return sibling;
131     }
132
133     return 0;
134 }
135
136 } // namespace WebCore
137
138 #endif // ENABLE(TEXT_AUTOSIZING)