Introduce RenderTreeBuilder
[WebKit-https.git] / Source / WebCore / rendering / TextAutoSizing.cpp
1 /*
2  * Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "TextAutoSizing.h"
28
29 #if ENABLE(TEXT_AUTOSIZING)
30
31 #include "CSSFontSelector.h"
32 #include "Document.h"
33 #include "FontCascade.h"
34 #include "Logging.h"
35 #include "RenderBlock.h"
36 #include "RenderListMarker.h"
37 #include "RenderText.h"
38 #include "RenderTextFragment.h"
39 #include "RenderTreeBuilder.h"
40 #include "RenderTreeUpdaterFirstLetter.h"
41 #include "RenderTreeUpdaterListItem.h"
42 #include "StyleResolver.h"
43
44 namespace WebCore {
45
46 static RenderStyle cloneRenderStyleWithState(const RenderStyle& currentStyle)
47 {
48     auto newStyle = RenderStyle::clone(currentStyle);
49     if (currentStyle.lastChildState())
50         newStyle.setLastChildState();
51     if (currentStyle.firstChildState())
52         newStyle.setFirstChildState();
53     return newStyle;
54 }
55
56 TextAutoSizingKey::TextAutoSizingKey(DeletedTag)
57 {
58     HashTraits<std::unique_ptr<RenderStyle>>::constructDeletedValue(m_style);
59 }
60
61 TextAutoSizingKey::TextAutoSizingKey(const RenderStyle& style, unsigned hash)
62     : m_style(RenderStyle::clonePtr(style)) // FIXME: This seems very inefficient.
63     , m_hash(hash)
64 {
65 }
66
67 void TextAutoSizingValue::addTextNode(Text& node, float size)
68 {
69     node.renderer()->setCandidateComputedTextSize(size);
70     m_autoSizedNodes.add(&node);
71 }
72
73 static const float maxScaleIncrease = 1.7f;
74
75 auto TextAutoSizingValue::adjustTextNodeSizes() -> StillHasNodes
76 {
77     // Remove stale nodes. Nodes may have had their renderers detached. We'll also need to remove the style from the documents m_textAutoSizedNodes
78     // collection. Return true indicates we need to do that removal.
79     Vector<Text*> nodesForRemoval;
80     for (auto& textNode : m_autoSizedNodes) {
81         auto* renderer = textNode->renderer();
82         if (!renderer || !renderer->style().textSizeAdjust().isAuto() || !renderer->candidateComputedTextSize())
83             nodesForRemoval.append(textNode.get());
84     }
85
86     for (auto& node : nodesForRemoval)
87         m_autoSizedNodes.remove(node);
88
89     StillHasNodes stillHasNodes = m_autoSizedNodes.isEmpty() ? StillHasNodes::No : StillHasNodes::Yes;
90
91     // If we only have one piece of text with the style on the page don't adjust it's size.
92     if (m_autoSizedNodes.size() <= 1)
93         return stillHasNodes;
94
95     // Compute average size.
96     float cumulativeSize = 0;
97     for (auto& node : m_autoSizedNodes)
98         cumulativeSize += node->renderer()->candidateComputedTextSize();
99
100     float averageSize = std::round(cumulativeSize / m_autoSizedNodes.size());
101
102     // FIXME: Figure out how to make this code use RenderTreeUpdater/Builder properly.
103     RenderTreeBuilder builder;
104
105     // Adjust sizes.
106     bool firstPass = true;
107     for (auto& node : m_autoSizedNodes) {
108         auto& renderer = *node->renderer();
109         if (renderer.style().fontDescription().computedSize() == averageSize)
110             continue;
111
112         float specifiedSize = renderer.style().fontDescription().specifiedSize();
113         float scaleChange = averageSize / specifiedSize;
114         if (scaleChange > maxScaleIncrease && firstPass) {
115             firstPass = false;
116             averageSize = std::round(specifiedSize * maxScaleIncrease);
117             scaleChange = averageSize / specifiedSize;
118         }
119
120         LOG(TextAutosizing, "  adjust node size %p firstPass=%d averageSize=%f scaleChange=%f", node.get(), firstPass, averageSize, scaleChange);
121
122         auto* parentRenderer = renderer.parent();
123
124         auto style = cloneRenderStyleWithState(renderer.style());
125         auto fontDescription = style.fontDescription();
126         fontDescription.setComputedSize(averageSize);
127         style.setFontDescription(fontDescription);
128         style.fontCascade().update(&node->document().fontSelector());
129         parentRenderer->setStyle(WTFMove(style));
130
131         if (parentRenderer->isAnonymousBlock())
132             parentRenderer = parentRenderer->parent();
133
134         // If we have a list we should resize ListMarkers separately.
135         if (is<RenderListMarker>(*parentRenderer->firstChild())) {
136             auto& listMarkerRenderer = downcast<RenderListMarker>(*parentRenderer->firstChild());
137             auto style = cloneRenderStyleWithState(listMarkerRenderer.style());
138             style.setFontDescription(fontDescription);
139             style.fontCascade().update(&node->document().fontSelector());
140             listMarkerRenderer.setStyle(WTFMove(style));
141         }
142
143         // Resize the line height of the parent.
144         auto& parentStyle = parentRenderer->style();
145         auto& lineHeightLength = parentStyle.specifiedLineHeight();
146
147         int specifiedLineHeight;
148         if (lineHeightLength.isPercent())
149             specifiedLineHeight = minimumValueForLength(lineHeightLength, fontDescription.specifiedSize());
150         else
151             specifiedLineHeight = lineHeightLength.value();
152
153         // This calculation matches the line-height computed size calculation in StyleBuilderCustom::applyValueLineHeight().
154         int lineHeight = specifiedLineHeight * scaleChange;
155         if (lineHeightLength.isFixed() && lineHeightLength.value() == lineHeight)
156             continue;
157
158         auto newParentStyle = cloneRenderStyleWithState(parentStyle);
159         newParentStyle.setLineHeight(Length(lineHeight, Fixed));
160         newParentStyle.setSpecifiedLineHeight(Length { lineHeightLength });
161         newParentStyle.setFontDescription(fontDescription);
162         newParentStyle.fontCascade().update(&node->document().fontSelector());
163         parentRenderer->setStyle(WTFMove(newParentStyle));
164
165         if (is<RenderListItem>(*parentRenderer))
166             RenderTreeUpdater::ListItem::updateMarker(builder, downcast<RenderListItem>(*parentRenderer));
167     }
168
169     for (auto& node : m_autoSizedNodes) {
170         auto& textRenderer = *node->renderer();
171         if (!is<RenderTextFragment>(textRenderer))
172             continue;
173         auto* block = downcast<RenderTextFragment>(textRenderer).blockForAccompanyingFirstLetter();
174         if (!block)
175             continue;
176         // FIXME: All render tree mutations should be done by RenderTreeUpdater commit.
177         RenderTreeUpdater::FirstLetter::update(*block);
178     }
179
180     return stillHasNodes;
181 }
182
183 TextAutoSizingValue::~TextAutoSizingValue()
184 {
185     reset();
186 }
187
188 void TextAutoSizingValue::reset()
189 {
190     for (auto& node : m_autoSizedNodes) {
191         auto* renderer = node->renderer();
192         if (!renderer)
193             continue;
194
195         auto* parentRenderer = renderer->parent();
196         if (!parentRenderer)
197             continue;
198
199         // Reset the font size back to the original specified size
200         auto fontDescription = renderer->style().fontDescription();
201         float originalSize = fontDescription.specifiedSize();
202         if (fontDescription.computedSize() != originalSize) {
203             fontDescription.setComputedSize(originalSize);
204             auto style = cloneRenderStyleWithState(renderer->style());
205             style.setFontDescription(fontDescription);
206             style.fontCascade().update(&node->document().fontSelector());
207             parentRenderer->setStyle(WTFMove(style));
208         }
209
210         // Reset the line height of the parent.
211         if (parentRenderer->isAnonymousBlock())
212             parentRenderer = parentRenderer->parent();
213
214         auto& parentStyle = parentRenderer->style();
215         auto& originalLineHeight = parentStyle.specifiedLineHeight();
216         if (originalLineHeight == parentStyle.lineHeight())
217             continue;
218
219         auto newParentStyle = cloneRenderStyleWithState(parentStyle);
220         newParentStyle.setLineHeight(Length { originalLineHeight });
221         newParentStyle.setFontDescription(fontDescription);
222         newParentStyle.fontCascade().update(&node->document().fontSelector());
223         parentRenderer->setStyle(WTFMove(newParentStyle));
224     }
225 }
226
227 void TextAutoSizing::addTextNode(Text& node, float candidateSize)
228 {
229     LOG(TextAutosizing, " addAutoSizedNode %p candidateSize=%f", &node, candidateSize);
230     auto addResult = m_textNodes.add<TextAutoSizingHashTranslator>(node.renderer()->style(), nullptr);
231     if (addResult.isNewEntry)
232         addResult.iterator->value = std::make_unique<TextAutoSizingValue>();
233     addResult.iterator->value->addTextNode(node, candidateSize);
234 }
235
236 void TextAutoSizing::updateRenderTree()
237 {
238     m_textNodes.removeIf([](auto& keyAndValue) {
239         return keyAndValue.value->adjustTextNodeSizes() == TextAutoSizingValue::StillHasNodes::No;
240     });
241 }
242
243 void TextAutoSizing::reset()
244 {
245     m_textNodes.clear();
246 }
247
248 } // namespace WebCore
249
250 #endif // ENABLE(TEXT_AUTOSIZING)