Combine event and touch action regions into a single class
[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 "Settings.h"
41 #include "StyleResolver.h"
42
43 namespace WebCore {
44
45 static RenderStyle cloneRenderStyleWithState(const RenderStyle& currentStyle)
46 {
47     auto newStyle = RenderStyle::clone(currentStyle);
48     if (currentStyle.lastChildState())
49         newStyle.setLastChildState();
50     if (currentStyle.firstChildState())
51         newStyle.setFirstChildState();
52     return newStyle;
53 }
54
55 TextAutoSizingKey::TextAutoSizingKey(DeletedTag)
56 {
57     HashTraits<std::unique_ptr<RenderStyle>>::constructDeletedValue(m_style);
58 }
59
60 TextAutoSizingKey::TextAutoSizingKey(const RenderStyle& style, unsigned hash)
61     : m_style(RenderStyle::clonePtr(style)) // FIXME: This seems very inefficient.
62     , m_hash(hash)
63 {
64 }
65
66 void TextAutoSizingValue::addTextNode(Text& node, float size)
67 {
68     node.renderer()->setCandidateComputedTextSize(size);
69     m_autoSizedNodes.add(&node);
70 }
71
72 auto TextAutoSizingValue::adjustTextNodeSizes() -> StillHasNodes
73 {
74     // Remove stale nodes. Nodes may have had their renderers detached. We'll also need to remove the style from the documents m_textAutoSizedNodes
75     // collection. Return true indicates we need to do that removal.
76     Vector<Text*> nodesForRemoval;
77     for (auto& textNode : m_autoSizedNodes) {
78         auto* renderer = textNode->renderer();
79         if (!renderer || !renderer->style().textSizeAdjust().isAuto() || !renderer->candidateComputedTextSize())
80             nodesForRemoval.append(textNode.get());
81     }
82
83     for (auto& node : nodesForRemoval)
84         m_autoSizedNodes.remove(node);
85
86     StillHasNodes stillHasNodes = m_autoSizedNodes.isEmpty() ? StillHasNodes::No : StillHasNodes::Yes;
87
88     // If we only have one piece of text with the style on the page don't adjust it's size.
89     if (m_autoSizedNodes.size() <= 1)
90         return stillHasNodes;
91
92     // Compute average size.
93     float cumulativeSize = 0;
94     for (auto& node : m_autoSizedNodes)
95         cumulativeSize += node->renderer()->candidateComputedTextSize();
96
97     float averageSize = std::round(cumulativeSize / m_autoSizedNodes.size());
98
99     // FIXME: Figure out how to make this code use RenderTreeUpdater/Builder properly.
100     RenderTreeBuilder builder((*m_autoSizedNodes.begin())->renderer()->view());
101
102     // Adjust sizes.
103     bool firstPass = true;
104     for (auto& node : m_autoSizedNodes) {
105         auto& renderer = *node->renderer();
106         if (renderer.style().fontDescription().computedSize() == averageSize)
107             continue;
108
109         float specifiedSize = renderer.style().fontDescription().specifiedSize();
110         float maxScaleIncrease = renderer.settings().maxTextAutosizingScaleIncrease();
111         float scaleChange = averageSize / specifiedSize;
112         if (scaleChange > maxScaleIncrease && firstPass) {
113             firstPass = false;
114             averageSize = std::round(specifiedSize * maxScaleIncrease);
115             scaleChange = averageSize / specifiedSize;
116         }
117
118         LOG(TextAutosizing, "  adjust node size %p firstPass=%d averageSize=%f scaleChange=%f", node.get(), firstPass, averageSize, scaleChange);
119
120         auto* parentRenderer = renderer.parent();
121
122         auto style = cloneRenderStyleWithState(renderer.style());
123         auto fontDescription = style.fontDescription();
124         fontDescription.setComputedSize(averageSize);
125         style.setFontDescription(FontCascadeDescription { fontDescription });
126         style.fontCascade().update(&node->document().fontSelector());
127         parentRenderer->setStyle(WTFMove(style));
128
129         if (parentRenderer->isAnonymousBlock())
130             parentRenderer = parentRenderer->parent();
131
132         // If we have a list we should resize ListMarkers separately.
133         if (is<RenderListMarker>(*parentRenderer->firstChild())) {
134             auto& listMarkerRenderer = downcast<RenderListMarker>(*parentRenderer->firstChild());
135             auto style = cloneRenderStyleWithState(listMarkerRenderer.style());
136             style.setFontDescription(FontCascadeDescription { fontDescription });
137             style.fontCascade().update(&node->document().fontSelector());
138             listMarkerRenderer.setStyle(WTFMove(style));
139         }
140
141         // Resize the line height of the parent.
142         auto& parentStyle = parentRenderer->style();
143         auto& lineHeightLength = parentStyle.specifiedLineHeight();
144
145         int specifiedLineHeight;
146         if (lineHeightLength.isPercent())
147             specifiedLineHeight = minimumValueForLength(lineHeightLength, fontDescription.specifiedSize());
148         else
149             specifiedLineHeight = lineHeightLength.value();
150
151         // This calculation matches the line-height computed size calculation in StyleBuilderCustom::applyValueLineHeight().
152         int lineHeight = specifiedLineHeight * scaleChange;
153         if (lineHeightLength.isFixed() && lineHeightLength.value() == lineHeight)
154             continue;
155
156         auto newParentStyle = cloneRenderStyleWithState(parentStyle);
157         newParentStyle.setLineHeight(Length(lineHeight, Fixed));
158         newParentStyle.setSpecifiedLineHeight(Length { lineHeightLength });
159         newParentStyle.setFontDescription(WTFMove(fontDescription));
160         newParentStyle.fontCascade().update(&node->document().fontSelector());
161         parentRenderer->setStyle(WTFMove(newParentStyle));
162
163         builder.updateAfterDescendants(*parentRenderer);
164     }
165
166     for (auto& node : m_autoSizedNodes) {
167         auto& textRenderer = *node->renderer();
168         if (!is<RenderTextFragment>(textRenderer))
169             continue;
170         auto* block = downcast<RenderTextFragment>(textRenderer).blockForAccompanyingFirstLetter();
171         if (!block)
172             continue;
173         builder.updateAfterDescendants(*block);
174     }
175
176     return stillHasNodes;
177 }
178
179 TextAutoSizingValue::~TextAutoSizingValue()
180 {
181     reset();
182 }
183
184 void TextAutoSizingValue::reset()
185 {
186     for (auto& node : m_autoSizedNodes) {
187         auto* renderer = node->renderer();
188         if (!renderer)
189             continue;
190
191         auto* parentRenderer = renderer->parent();
192         if (!parentRenderer)
193             continue;
194
195         // Reset the font size back to the original specified size
196         auto fontDescription = renderer->style().fontDescription();
197         float originalSize = fontDescription.specifiedSize();
198         if (fontDescription.computedSize() != originalSize) {
199             fontDescription.setComputedSize(originalSize);
200             auto style = cloneRenderStyleWithState(renderer->style());
201             style.setFontDescription(FontCascadeDescription { fontDescription });
202             style.fontCascade().update(&node->document().fontSelector());
203             parentRenderer->setStyle(WTFMove(style));
204         }
205
206         // Reset the line height of the parent.
207         if (parentRenderer->isAnonymousBlock())
208             parentRenderer = parentRenderer->parent();
209
210         auto& parentStyle = parentRenderer->style();
211         auto& originalLineHeight = parentStyle.specifiedLineHeight();
212         if (originalLineHeight == parentStyle.lineHeight())
213             continue;
214
215         auto newParentStyle = cloneRenderStyleWithState(parentStyle);
216         newParentStyle.setLineHeight(Length { originalLineHeight });
217         newParentStyle.setFontDescription(WTFMove(fontDescription));
218         newParentStyle.fontCascade().update(&node->document().fontSelector());
219         parentRenderer->setStyle(WTFMove(newParentStyle));
220     }
221 }
222
223 void TextAutoSizing::addTextNode(Text& node, float candidateSize)
224 {
225     LOG(TextAutosizing, " addAutoSizedNode %p candidateSize=%f", &node, candidateSize);
226     auto addResult = m_textNodes.add<TextAutoSizingHashTranslator>(node.renderer()->style(), nullptr);
227     if (addResult.isNewEntry)
228         addResult.iterator->value = std::make_unique<TextAutoSizingValue>();
229     addResult.iterator->value->addTextNode(node, candidateSize);
230 }
231
232 void TextAutoSizing::updateRenderTree()
233 {
234     m_textNodes.removeIf([](auto& keyAndValue) {
235         return keyAndValue.value->adjustTextNodeSizes() == TextAutoSizingValue::StillHasNodes::No;
236     });
237 }
238
239 void TextAutoSizing::reset()
240 {
241     m_textNodes.clear();
242 }
243
244 } // namespace WebCore
245
246 #endif // ENABLE(TEXT_AUTOSIZING)