Combine event and touch action regions into a single class
[WebKit-https.git] / Source / WebCore / rendering / SimpleLineLayoutCoverage.cpp
1 /*
2  * Copyright (C) 2017 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 "SimpleLineLayoutCoverage.h"
28
29 #include "Logging.h"
30 #include "RenderBlockFlow.h"
31 #include "RenderChildIterator.h"
32 #include "RenderStyle.h"
33 #include "RenderView.h"
34 #include "Settings.h"
35 #include "SimpleLineLayout.h"
36 #include <wtf/text/TextStream.h>
37
38 namespace WebCore {
39 namespace SimpleLineLayout {
40
41 #ifndef NDEBUG
42 static void printReason(AvoidanceReason reason, TextStream& stream)
43 {
44     switch (reason) {
45     case FlowIsInsideANonMultiColumnThread:
46         stream << "flow is inside a non-multicolumn container";
47         break;
48     case FlowHasHorizonalWritingMode:
49         stream << "horizontal writing mode";
50         break;
51     case FlowHasOutline:
52         stream << "outline";
53         break;
54     case FlowIsRuby:
55         stream << "ruby";
56         break;
57     case FlowHasHangingPunctuation:
58         stream << "hanging punctuation";
59         break;
60     case FlowIsPaginated:
61         stream << "paginated";
62         break;
63     case FlowHasTextOverflow:
64         stream << "text-overflow";
65         break;
66     case FlowIsDepricatedFlexBox:
67         stream << "depricatedFlexBox";
68         break;
69     case FlowParentIsPlaceholderElement:
70         stream << "placeholder element";
71         break;
72     case FlowParentIsTextAreaWithWrapping:
73         stream << "wrapping textarea";
74         break;
75     case FlowHasNonSupportedChild:
76         stream << "nested renderers";
77         break;
78     case FlowHasUnsupportedFloat:
79         stream << "complicated float";
80         break;
81     case FlowHasUnsupportedUnderlineDecoration:
82         stream << "text-underline-position: under";
83         break;
84     case FlowHasJustifiedNonLatinText:
85         stream << "text-align: justify with non-latin text";
86         break;
87     case FlowHasOverflowNotVisible:
88         stream << "overflow: hidden | scroll | auto";
89         break;
90     case FlowHasWebKitNBSPMode:
91         stream << "-webkit-nbsp-mode: space";
92         break;
93     case FlowIsNotLTR:
94         stream << "dir is not LTR";
95         break;
96     case FlowHasLineBoxContainProperty:
97         stream << "line-box-contain value indicates variable line height";
98         break;
99     case FlowIsNotTopToBottom:
100         stream << "non top-to-bottom flow";
101         break;
102     case FlowHasLineBreak:
103         stream << "line-break property";
104         break;
105     case FlowHasNonNormalUnicodeBiDi:
106         stream << "non-normal Unicode bidi";
107         break;
108     case FlowHasRTLOrdering:
109         stream << "-webkit-rtl-ordering";
110         break;
111     case FlowHasLineAlignEdges:
112         stream << "-webkit-line-align edges";
113         break;
114     case FlowHasLineSnap:
115         stream << "-webkit-line-snap property";
116         break;
117     case FlowHasTextEmphasisFillOrMark:
118         stream << "text-emphasis (fill/mark)";
119         break;
120     case FlowHasPseudoFirstLine:
121         stream << "first-line";
122         break;
123     case FlowHasPseudoFirstLetter:
124         stream << "first-letter";
125         break;
126     case FlowHasTextCombine:
127         stream << "text combine";
128         break;
129     case FlowHasTextFillBox:
130         stream << "background-color (text-fill)";
131         break;
132     case FlowHasBorderFitLines:
133         stream << "-webkit-border-fit";
134         break;
135     case FlowHasNonAutoLineBreak:
136         stream << "line-break is not auto";
137         break;
138     case FlowHasSVGFont:
139         stream << "SVG font";
140         break;
141     case FlowTextHasSoftHyphen:
142         stream << "soft hyphen character";
143         break;
144     case FlowTextHasDirectionCharacter:
145         stream << "direction character";
146         break;
147     case FlowIsMissingPrimaryFont:
148         stream << "missing primary font";
149         break;
150     case FlowPrimaryFontIsInsufficient:
151         stream << "missing glyph or glyph needs another font";
152         break;
153     case FlowTextIsCombineText:
154         stream << "text is combine";
155         break;
156     case FlowTextIsRenderCounter:
157         stream << "unsupported RenderCounter";
158         break;
159     case FlowTextIsRenderQuote:
160         stream << "unsupported RenderQuote";
161         break;
162     case FlowTextIsTextFragment:
163         stream << "unsupported TextFragment";
164         break;
165     case FlowTextIsSVGInlineText:
166         stream << "unsupported SVGInlineText";
167         break;
168     case FlowHasComplexFontCodePath:
169         stream << "text with complex font codepath";
170         break;
171     case FlowHasTextShadow:
172         stream << "text-shadow";
173         break;
174     case FlowChildIsSelected:
175         stream << "selected content";
176         break;
177     case FlowFontHasOverflowGlyph:
178         stream << "-webkit-line-box-contain: glyphs with overflowing text.";
179         break;
180     case FlowTextHasSurrogatePair:
181         stream << "surrogate pair";
182         break;
183     case MultiColumnFlowIsNotTopLevel:
184         stream << "non top level column";
185         break;
186     case MultiColumnFlowHasColumnSpanner:
187         stream << "column has spanner";
188         break;
189     case MultiColumnFlowVerticalAlign:
190         stream << "column with vertical-align != baseline";
191         break;
192     case MultiColumnFlowIsFloating:
193         stream << "column with floating objects";
194         break;
195     case FlowIncludesDocumentMarkers:
196         stream << "text includes document markers";
197         break;
198     case FlowTextIsEmpty:
199     case FlowHasNoChild:
200     case FlowHasNoParent:
201     case FeatureIsDisabled:
202     default:
203         break;
204     }
205 }
206
207 static void printReasons(AvoidanceReasonFlags reasons, TextStream& stream)
208 {
209     bool first = true;
210     for (auto reasonItem = EndOfReasons >> 1; reasonItem != NoReason; reasonItem >>= 1) {
211         if (!(reasons & reasonItem))
212             continue;
213         stream << (first ? " " : ", ");
214         first = false;
215         printReason(reasonItem, stream);
216     }
217 }
218
219 static void printTextForSubtree(const RenderObject& renderer, unsigned& charactersLeft, TextStream& stream)
220 {
221     if (!charactersLeft)
222         return;
223     if (is<RenderText>(renderer)) {
224         String text = downcast<RenderText>(renderer).text();
225         text = text.stripWhiteSpace();
226         unsigned len = std::min(charactersLeft, text.length());
227         stream << text.left(len);
228         charactersLeft -= len;
229         return;
230     }
231     if (!is<RenderElement>(renderer))
232         return;
233     for (const auto* child = downcast<RenderElement>(renderer).firstChild(); child; child = child->nextSibling())
234         printTextForSubtree(*child, charactersLeft, stream);
235 }
236
237 static unsigned textLengthForSubtree(const RenderObject& renderer)
238 {
239     if (is<RenderText>(renderer))
240         return downcast<RenderText>(renderer).text().length();
241     if (!is<RenderElement>(renderer))
242         return 0;
243     unsigned textLength = 0;
244     for (const auto* child = downcast<RenderElement>(renderer).firstChild(); child; child = child->nextSibling())
245         textLength += textLengthForSubtree(*child);
246     return textLength;
247 }
248
249 static void collectNonEmptyLeafRenderBlockFlows(const RenderObject& renderer, HashSet<const RenderBlockFlow*>& leafRenderers)
250 {
251     if (is<RenderText>(renderer)) {
252         if (!downcast<RenderText>(renderer).text().length())
253             return;
254         // Find RenderBlockFlow ancestor.
255         for (const auto* current = renderer.parent(); current; current = current->parent()) {
256             if (!is<RenderBlockFlow>(current))
257                 continue;
258             leafRenderers.add(downcast<RenderBlockFlow>(current));
259             break;
260         }
261         return;
262     }
263     if (!is<RenderElement>(renderer))
264         return;
265     for (const auto* child = downcast<RenderElement>(renderer).firstChild(); child; child = child->nextSibling())
266         collectNonEmptyLeafRenderBlockFlows(*child, leafRenderers);
267 }
268
269 static void collectNonEmptyLeafRenderBlockFlowsForCurrentPage(HashSet<const RenderBlockFlow*>& leafRenderers)
270 {
271     for (const auto* document : Document::allDocuments()) {
272         if (!document->renderView() || document->pageCacheState() != Document::NotInPageCache)
273             continue;
274         if (!document->isHTMLDocument() && !document->isXHTMLDocument())
275             continue;
276         collectNonEmptyLeafRenderBlockFlows(*document->renderView(), leafRenderers);
277     }
278 }
279
280 void toggleSimpleLineLayout()
281 {
282     for (auto* document : Document::allDocuments()) {
283         auto& settings = document->mutableSettings();
284         settings.setSimpleLineLayoutEnabled(!settings.simpleLineLayoutEnabled());
285     }
286 }
287
288 void printSimpleLineLayoutBlockList()
289 {
290     HashSet<const RenderBlockFlow*> leafRenderers;
291     collectNonEmptyLeafRenderBlockFlowsForCurrentPage(leafRenderers);
292     if (!leafRenderers.size()) {
293         WTFLogAlways("No text found in this document\n");
294         return;
295     }
296     TextStream stream;
297     stream << "---------------------------------------------------\n";
298     for (const auto* flow : leafRenderers) {
299         auto reason = canUseForWithReason(*flow, IncludeReasons::All);
300         if (reason == NoReason)
301             continue;
302         unsigned printedLength = 30;
303         stream << "\"";
304         printTextForSubtree(*flow, printedLength, stream);
305         for (;printedLength > 0; --printedLength)
306             stream << " ";
307         stream << "\"(" << textLengthForSubtree(*flow) << "):";
308         printReasons(reason, stream);
309         stream << "\n";
310     }
311     stream << "---------------------------------------------------\n";
312     WTFLogAlways("%s", stream.release().utf8().data());
313 }
314
315 void printSimpleLineLayoutCoverage()
316 {
317     HashSet<const RenderBlockFlow*> leafRenderers;
318     collectNonEmptyLeafRenderBlockFlowsForCurrentPage(leafRenderers);
319     if (!leafRenderers.size()) {
320         WTFLogAlways("No text found in this document\n");
321         return;
322     }
323     TextStream stream;
324     HashMap<AvoidanceReason, unsigned> flowStatistics;
325     unsigned textLength = 0;
326     unsigned unsupportedTextLength = 0;
327     unsigned numberOfUnsupportedLeafBlocks = 0;
328     unsigned supportedButForcedToLineLayoutTextLength = 0;
329     unsigned numberOfSupportedButForcedToLineLayoutLeafBlocks = 0;
330     for (const auto* flow : leafRenderers) {
331         auto flowLength = textLengthForSubtree(*flow);
332         textLength += flowLength;
333         auto reasons = canUseForWithReason(*flow, IncludeReasons::All);
334         if (reasons == NoReason) {
335             if (flow->lineLayoutPath() == RenderBlockFlow::ForceLineBoxesPath) {
336                 supportedButForcedToLineLayoutTextLength += flowLength;
337                 ++numberOfSupportedButForcedToLineLayoutLeafBlocks;
338             }
339             continue;
340         }
341         ++numberOfUnsupportedLeafBlocks;
342         unsupportedTextLength += flowLength;
343         for (auto reasonItem = EndOfReasons >> 1; reasonItem != NoReason; reasonItem >>= 1) {
344             if (!(reasons & reasonItem))
345                 continue;
346             auto result = flowStatistics.add(reasonItem, flowLength);
347             if (!result.isNewEntry)
348                 result.iterator->value += flowLength;
349         }
350     }
351     stream << "---------------------------------------------------\n";
352     stream << "Number of blocks: total(" <<  leafRenderers.size() << ") non-simple(" << numberOfUnsupportedLeafBlocks << ")\nContent length: total(" <<
353         textLength << ") non-simple(" << unsupportedTextLength << ")\n";
354     for (const auto& reasonEntry : flowStatistics) {
355         printReason(reasonEntry.key, stream);
356         stream << ": " << (float)reasonEntry.value / (float)textLength * 100 << "%\n";
357     }
358     if (supportedButForcedToLineLayoutTextLength) {
359         stream << "Simple line layout potential coverage: " << (float)(textLength - unsupportedTextLength) / (float)textLength * 100 << "%\n\n";
360         stream << "Simple line layout actual coverage: " << (float)(textLength - unsupportedTextLength - supportedButForcedToLineLayoutTextLength) / (float)textLength * 100 << "%\nForced line layout blocks: " << numberOfSupportedButForcedToLineLayoutLeafBlocks << " content length: " << supportedButForcedToLineLayoutTextLength << "(" << (float)supportedButForcedToLineLayoutTextLength / (float)textLength * 100 << "%)";
361     } else
362         stream << "Simple line layout coverage: " << (float)(textLength - unsupportedTextLength) / (float)textLength * 100 << "%";
363     stream << "\n---------------------------------------------------\n";
364     WTFLogAlways("%s", stream.release().utf8().data());
365 }
366 #endif
367
368 }
369 }