Text Autosizing: Increase line height in proportion to font size.
[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     IntSize windowSize = m_document->settings()->textAutosizingWindowSizeOverride();
54     if (windowSize.isEmpty()) {
55         Frame* mainFrame = m_document->page()->mainFrame();
56         bool includeScrollbars = !InspectorInstrumentation::shouldApplyScreenWidthOverride(mainFrame);
57         windowSize = mainFrame->view()->visibleContentRect(includeScrollbars).size(); // FIXME: Check that this is always in logical (density-independent) pixels (see wkbug.com/87440).
58     }
59
60     for (RenderObject* descendant = layoutRoot->nextInPreOrder(layoutRoot); descendant; descendant = descendant->nextInPreOrder(layoutRoot)) {
61         if (isNotAnAutosizingContainer(descendant))
62             continue;
63         processBox(toRenderBox(descendant), windowSize);
64     }
65
66     return true;
67 }
68
69 void TextAutosizer::processBox(RenderBox* box, const IntSize& windowSize)
70 {
71     int windowLogicalWidth = box->isHorizontalWritingMode() ? windowSize.width() : windowSize.height();
72     float multiplier = static_cast<float>(box->logicalWidth()) / windowLogicalWidth; // FIXME: This is overly simplistic.
73     multiplier *= m_document->settings()->textAutosizingFontScaleFactor();
74
75     if (multiplier < 1)
76         return;
77     RenderObject* descendant = nextInPreOrderMatchingFilter(box, box, isNotAnAutosizingContainer);
78     while (descendant) {
79         if (descendant->isText()) {
80             setMultiplier(descendant, multiplier);
81             setMultiplier(descendant->parent(), multiplier); // Parent does line spacing.
82             // FIXME: Increase list marker size proportionately.
83         }
84         descendant = nextInPreOrderMatchingFilter(descendant, box, isNotAnAutosizingContainer);
85     }
86 }
87
88 void TextAutosizer::setMultiplier(RenderObject* renderer, float multiplier)
89 {
90     RefPtr<RenderStyle> newStyle = RenderStyle::clone(renderer->style());
91     newStyle->setTextAutosizingMultiplier(multiplier);
92     renderer->setStyle(newStyle.release());
93 }
94
95 bool TextAutosizer::isNotAnAutosizingContainer(const RenderObject* renderer)
96 {
97     // "Autosizing containers" are the smallest unit for which we can enable/disable
98     // Text Autosizing. A uniform text size multiplier is enforced within them.
99     // - Must be RenderBoxes since they need a well-defined logicalWidth().
100     // - Must not be inline, as different multipliers on one line looks terrible.
101     // - Must not be list items, as items in the same list should look consistent.
102     return !renderer->isBox() || renderer->isInline() || renderer->isListItem();
103 }
104
105 RenderObject* TextAutosizer::nextInPreOrderMatchingFilter(RenderObject* current, const RenderObject* stayWithin, RenderObjectFilterFunctor filter)
106 {
107     for (RenderObject* child = current->firstChild(); child; child = child->nextSibling())
108         if (filter(child))
109             return child;
110
111     for (const RenderObject* ancestor = current; ancestor; ancestor = ancestor->parent()) {
112         if (ancestor == stayWithin)
113             return 0;
114         for (RenderObject* sibling = ancestor->nextSibling(); sibling; sibling = sibling->nextSibling())
115             if (filter(sibling))
116                 return sibling;
117     }
118
119     return 0;
120 }
121
122 } // namespace WebCore
123
124 #endif // ENABLE(TEXT_AUTOSIZING)