Title: [269681] trunk/Source/WebCore
Revision
269681
Author
za...@apple.com
Date
2020-11-11 06:19:46 -0800 (Wed, 11 Nov 2020)

Log Message

[LFC][Integration] Add modern line layout statistics
https://bugs.webkit.org/show_bug.cgi?id=218782

Reviewed by Antti Koivisto.

notifyutil -p com.apple.WebKit.showModernLineLayoutCoverage and com.apple.WebKit.showModernLineLayoutReasons.
e.g.

nytimes.com:
---------------------------------------------------
Modern line layout coverage: 64.20%

Number of blocks: total(196) legacy(71)
Content length: total(8313) legacy(2976)
nested renderers: 20.77%
flow does not establishes inline formatting context: 3.04%
non top level column: 6.56%
missing glyph or glyph needs another font: 0.02%
unsupported TextFragment: 0.31%
text-shadow: 12.80%
---------------------------------------------------

* layout/integration/LayoutIntegrationCoverage.cpp:
(WebCore::LayoutIntegration::printReason):
(WebCore::LayoutIntegration::printReasons):
(WebCore::LayoutIntegration::printTextForSubtree):
(WebCore::LayoutIntegration::textLengthForSubtree):
(WebCore::LayoutIntegration::collectNonEmptyLeafRenderBlockFlows):
(WebCore::LayoutIntegration::collectNonEmptyLeafRenderBlockFlowsForCurrentPage):
(WebCore::LayoutIntegration::printModernLineLayoutBlockList):
(WebCore::LayoutIntegration::printModernLineLayoutCoverage):
(WebCore::LayoutIntegration::canUseForLineLayoutWithReason):

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (269680 => 269681)


--- trunk/Source/WebCore/ChangeLog	2020-11-11 12:27:43 UTC (rev 269680)
+++ trunk/Source/WebCore/ChangeLog	2020-11-11 14:19:46 UTC (rev 269681)
@@ -1,3 +1,38 @@
+2020-11-11  Zalan Bujtas  <za...@apple.com>
+
+        [LFC][Integration] Add modern line layout statistics
+        https://bugs.webkit.org/show_bug.cgi?id=218782
+
+        Reviewed by Antti Koivisto.
+
+        notifyutil -p com.apple.WebKit.showModernLineLayoutCoverage and com.apple.WebKit.showModernLineLayoutReasons.
+        e.g.
+
+        nytimes.com:
+        ---------------------------------------------------
+        Modern line layout coverage: 64.20%
+
+        Number of blocks: total(196) legacy(71)
+        Content length: total(8313) legacy(2976)
+        nested renderers: 20.77%
+        flow does not establishes inline formatting context: 3.04%
+        non top level column: 6.56%
+        missing glyph or glyph needs another font: 0.02%
+        unsupported TextFragment: 0.31%
+        text-shadow: 12.80%
+        ---------------------------------------------------
+
+        * layout/integration/LayoutIntegrationCoverage.cpp:
+        (WebCore::LayoutIntegration::printReason):
+        (WebCore::LayoutIntegration::printReasons):
+        (WebCore::LayoutIntegration::printTextForSubtree):
+        (WebCore::LayoutIntegration::textLengthForSubtree):
+        (WebCore::LayoutIntegration::collectNonEmptyLeafRenderBlockFlows):
+        (WebCore::LayoutIntegration::collectNonEmptyLeafRenderBlockFlowsForCurrentPage):
+        (WebCore::LayoutIntegration::printModernLineLayoutBlockList):
+        (WebCore::LayoutIntegration::printModernLineLayoutCoverage):
+        (WebCore::LayoutIntegration::canUseForLineLayoutWithReason):
+
 2020-11-11  Kimmo Kinnunen  <kkinnu...@apple.com>
 
         Remove uses of MakeCurrent from WebGL implementation

Modified: trunk/Source/WebCore/layout/integration/LayoutIntegrationCoverage.cpp (269680 => 269681)


--- trunk/Source/WebCore/layout/integration/LayoutIntegrationCoverage.cpp	2020-11-11 12:27:43 UTC (rev 269680)
+++ trunk/Source/WebCore/layout/integration/LayoutIntegrationCoverage.cpp	2020-11-11 14:19:46 UTC (rev 269681)
@@ -38,6 +38,7 @@
 #include "RenderView.h"
 #include "RuntimeEnabledFeatures.h"
 #include "Settings.h"
+#include <pal/Logging.h>
 #include <wtf/OptionSet.h>
 
 #if ENABLE(LAYOUT_FORMATTING_CONTEXT)
@@ -77,6 +78,317 @@
 namespace WebCore {
 namespace LayoutIntegration {
 
+#ifndef NDEBUG
+static void printReason(AvoidanceReason reason, TextStream& stream)
+{
+    switch (reason) {
+    case AvoidanceReason::FlowIsInsideANonMultiColumnThread:
+        stream << "flow is inside a non-multicolumn container";
+        break;
+    case AvoidanceReason::FlowHasHorizonalWritingMode:
+        stream << "horizontal writing mode";
+        break;
+    case AvoidanceReason::FlowHasOutline:
+        stream << "outline";
+        break;
+    case AvoidanceReason::FlowIsRuby:
+        stream << "ruby";
+        break;
+    case AvoidanceReason::FlowHasHangingPunctuation:
+        stream << "hanging punctuation";
+        break;
+    case AvoidanceReason::FlowIsPaginated:
+        stream << "paginated";
+        break;
+    case AvoidanceReason::FlowHasTextOverflow:
+        stream << "text-overflow";
+        break;
+    case AvoidanceReason::FlowIsDepricatedFlexBox:
+        stream << "depricatedFlexBox";
+        break;
+    case AvoidanceReason::FlowParentIsPlaceholderElement:
+        stream << "placeholder element";
+        break;
+    case AvoidanceReason::FlowParentIsTextAreaWithWrapping:
+        stream << "wrapping textarea";
+        break;
+    case AvoidanceReason::FlowHasNonSupportedChild:
+        stream << "nested renderers";
+        break;
+    case AvoidanceReason::FlowHasUnsupportedFloat:
+        stream << "complicated float";
+        break;
+    case AvoidanceReason::FlowHasUnsupportedUnderlineDecoration:
+        stream << "text-underline-position: under";
+        break;
+    case AvoidanceReason::FlowHasJustifiedNonLatinText:
+        stream << "text-align: justify with non-latin text";
+        break;
+    case AvoidanceReason::FlowHasOverflowNotVisible:
+        stream << "overflow: hidden | scroll | auto";
+        break;
+    case AvoidanceReason::FlowHasWebKitNBSPMode:
+        stream << "-webkit-nbsp-mode: space";
+        break;
+    case AvoidanceReason::FlowIsNotLTR:
+        stream << "dir is not LTR";
+        break;
+    case AvoidanceReason::FlowHasLineBoxContainProperty:
+        stream << "line-box-contain value indicates variable line height";
+        break;
+    case AvoidanceReason::FlowIsNotTopToBottom:
+        stream << "non top-to-bottom flow";
+        break;
+    case AvoidanceReason::FlowHasNonNormalUnicodeBiDi:
+        stream << "non-normal Unicode bidi";
+        break;
+    case AvoidanceReason::FlowHasRTLOrdering:
+        stream << "-webkit-rtl-ordering";
+        break;
+    case AvoidanceReason::FlowHasLineAlignEdges:
+        stream << "-webkit-line-align edges";
+        break;
+    case AvoidanceReason::FlowHasLineSnap:
+        stream << "-webkit-line-snap property";
+        break;
+    case AvoidanceReason::FlowHasTextEmphasisFillOrMark:
+        stream << "text-emphasis (fill/mark)";
+        break;
+    case AvoidanceReason::FlowHasPseudoFirstLine:
+        stream << "first-line";
+        break;
+    case AvoidanceReason::FlowHasPseudoFirstLetter:
+        stream << "first-letter";
+        break;
+    case AvoidanceReason::FlowHasTextCombine:
+        stream << "text combine";
+        break;
+    case AvoidanceReason::FlowHasTextFillBox:
+        stream << "background-color (text-fill)";
+        break;
+    case AvoidanceReason::FlowHasBorderFitLines:
+        stream << "-webkit-border-fit";
+        break;
+    case AvoidanceReason::FlowHasNonAutoLineBreak:
+        stream << "line-break is not auto";
+        break;
+    case AvoidanceReason::FlowHasTextSecurity:
+        stream << "text-security is not none";
+        break;
+    case AvoidanceReason::FlowHasSVGFont:
+        stream << "SVG font";
+        break;
+    case AvoidanceReason::FlowTextHasDirectionCharacter:
+        stream << "direction character";
+        break;
+    case AvoidanceReason::FlowIsMissingPrimaryFont:
+        stream << "missing primary font";
+        break;
+    case AvoidanceReason::FlowPrimaryFontIsInsufficient:
+        stream << "missing glyph or glyph needs another font";
+        break;
+    case AvoidanceReason::FlowTextIsCombineText:
+        stream << "text is combine";
+        break;
+    case AvoidanceReason::FlowTextIsRenderCounter:
+        stream << "unsupported RenderCounter";
+        break;
+    case AvoidanceReason::FlowTextIsRenderQuote:
+        stream << "unsupported RenderQuote";
+        break;
+    case AvoidanceReason::FlowTextIsTextFragment:
+        stream << "unsupported TextFragment";
+        break;
+    case AvoidanceReason::FlowTextIsSVGInlineText:
+        stream << "unsupported SVGInlineText";
+        break;
+    case AvoidanceReason::FlowHasComplexFontCodePath:
+        stream << "text with complex font codepath";
+        break;
+    case AvoidanceReason::FlowHasTextShadow:
+        stream << "text-shadow";
+        break;
+    case AvoidanceReason::FlowChildIsSelected:
+        stream << "selected content";
+        break;
+    case AvoidanceReason::FlowFontHasOverflowGlyph:
+        stream << "-webkit-line-box-contain: glyphs with overflowing text.";
+        break;
+    case AvoidanceReason::FlowTextHasSurrogatePair:
+        stream << "surrogate pair";
+        break;
+    case AvoidanceReason::MultiColumnFlowIsNotTopLevel:
+        stream << "non top level column";
+        break;
+    case AvoidanceReason::MultiColumnFlowHasColumnSpanner:
+        stream << "column has spanner";
+        break;
+    case AvoidanceReason::MultiColumnFlowVerticalAlign:
+        stream << "column with vertical-align != baseline";
+        break;
+    case AvoidanceReason::MultiColumnFlowIsFloating:
+        stream << "column with floating objects";
+        break;
+    case AvoidanceReason::FlowIncludesDocumentMarkers:
+        stream << "text includes document markers";
+        break;
+    case AvoidanceReason::FlowDoesNotEstablishInlineFormattingContext:
+        stream << "flow does not establishes inline formatting context";
+        break;
+    default:
+        break;
+    }
+}
+
+static void printReasons(OptionSet<AvoidanceReason> reasons, TextStream& stream)
+{
+    stream << " ";
+    for (auto reason : reasons) {
+        printReason(reason, stream);
+        stream << ", ";
+    }
+}
+
+static void printTextForSubtree(const RenderObject& renderer, unsigned& charactersLeft, TextStream& stream)
+{
+    if (!charactersLeft)
+        return;
+    if (is<RenderText>(renderer)) {
+        String text = downcast<RenderText>(renderer).text();
+        text = text.stripWhiteSpace();
+        unsigned len = std::min(charactersLeft, text.length());
+        stream << text.left(len);
+        charactersLeft -= len;
+        return;
+    }
+    if (!is<RenderElement>(renderer))
+        return;
+    for (const auto* child = downcast<RenderElement>(renderer).firstChild(); child; child = child->nextSibling())
+        printTextForSubtree(*child, charactersLeft, stream);
+}
+
+static unsigned textLengthForSubtree(const RenderObject& renderer)
+{
+    if (is<RenderText>(renderer))
+        return downcast<RenderText>(renderer).text().length();
+    if (!is<RenderElement>(renderer))
+        return 0;
+    unsigned textLength = 0;
+    for (const auto* child = downcast<RenderElement>(renderer).firstChild(); child; child = child->nextSibling())
+        textLength += textLengthForSubtree(*child);
+    return textLength;
+}
+
+static void collectNonEmptyLeafRenderBlockFlows(const RenderObject& renderer, HashSet<const RenderBlockFlow*>& leafRenderers)
+{
+    if (is<RenderText>(renderer)) {
+        if (!downcast<RenderText>(renderer).text().length())
+            return;
+        // Find RenderBlockFlow ancestor.
+        for (const auto* current = renderer.parent(); current; current = current->parent()) {
+            if (!is<RenderBlockFlow>(current))
+                continue;
+            leafRenderers.add(downcast<RenderBlockFlow>(current));
+            break;
+        }
+        return;
+    }
+    if (!is<RenderElement>(renderer))
+        return;
+    for (const auto* child = downcast<RenderElement>(renderer).firstChild(); child; child = child->nextSibling())
+        collectNonEmptyLeafRenderBlockFlows(*child, leafRenderers);
+}
+
+static void collectNonEmptyLeafRenderBlockFlowsForCurrentPage(HashSet<const RenderBlockFlow*>& leafRenderers)
+{
+    for (const auto* document : Document::allDocuments()) {
+        if (!document->renderView() || document->backForwardCacheState() != Document::NotInBackForwardCache)
+            continue;
+        if (!document->isHTMLDocument() && !document->isXHTMLDocument())
+            continue;
+        collectNonEmptyLeafRenderBlockFlows(*document->renderView(), leafRenderers);
+    }
+}
+
+static void printModernLineLayoutBlockList(void)
+{
+    HashSet<const RenderBlockFlow*> leafRenderers;
+    collectNonEmptyLeafRenderBlockFlowsForCurrentPage(leafRenderers);
+    if (!leafRenderers.size()) {
+        WTFLogAlways("No text found in this document\n");
+        return;
+    }
+    TextStream stream;
+    stream << "---------------------------------------------------\n";
+    for (const auto* flow : leafRenderers) {
+        auto reasons = canUseForLineLayoutWithReason(*flow, IncludeReasons::All);
+        if (reasons.isEmpty())
+            continue;
+        unsigned printedLength = 30;
+        stream << "\"";
+        printTextForSubtree(*flow, printedLength, stream);
+        for (;printedLength > 0; --printedLength)
+            stream << " ";
+        stream << "\"(" << textLengthForSubtree(*flow) << "):";
+        printReasons(reasons, stream);
+        stream << "\n";
+    }
+    stream << "---------------------------------------------------\n";
+    WTFLogAlways("%s", stream.release().utf8().data());
+}
+
+static void printModernLineLayoutCoverage(void)
+{
+    HashSet<const RenderBlockFlow*> leafRenderers;
+    collectNonEmptyLeafRenderBlockFlowsForCurrentPage(leafRenderers);
+    if (!leafRenderers.size()) {
+        WTFLogAlways("No text found in this document\n");
+        return;
+    }
+    TextStream stream;
+    HashMap<AvoidanceReason, unsigned, DefaultHash<uint64_t>, WTF::UnsignedWithZeroKeyHashTraits<uint64_t>> flowStatistics;
+    unsigned textLength = 0;
+    unsigned unsupportedTextLength = 0;
+    unsigned numberOfUnsupportedLeafBlocks = 0;
+    unsigned supportedButForcedToLineLayoutTextLength = 0;
+    unsigned numberOfSupportedButForcedToLineLayoutLeafBlocks = 0;
+    for (const auto* flow : leafRenderers) {
+        auto flowLength = textLengthForSubtree(*flow);
+        textLength += flowLength;
+        auto reasons = canUseForLineLayoutWithReason(*flow, IncludeReasons::All);
+        if (reasons.isEmpty()) {
+            if (flow->lineLayoutPath() == RenderBlockFlow::ForceLineBoxesPath) {
+                supportedButForcedToLineLayoutTextLength += flowLength;
+                ++numberOfSupportedButForcedToLineLayoutLeafBlocks;
+            }
+            continue;
+        }
+        ++numberOfUnsupportedLeafBlocks;
+        unsupportedTextLength += flowLength;
+        for (auto reason : reasons) {
+            auto result = flowStatistics.add(static_cast<uint64_t>(reason), flowLength);
+            if (!result.isNewEntry)
+                result.iterator->value += flowLength;
+        }
+    }
+    stream << "---------------------------------------------------\n";
+    if (supportedButForcedToLineLayoutTextLength) {
+        stream << "Modern line layout potential coverage: " << (float)(textLength - unsupportedTextLength) / (float)textLength * 100 << "%\n\n";
+        stream << "Modern line layout actual coverage: " << (float)(textLength - unsupportedTextLength - supportedButForcedToLineLayoutTextLength) / (float)textLength * 100 << "%\nForced line layout blocks: " << numberOfSupportedButForcedToLineLayoutLeafBlocks << " content length: " << supportedButForcedToLineLayoutTextLength << "(" << (float)supportedButForcedToLineLayoutTextLength / (float)textLength * 100 << "%)";
+    } else
+        stream << "Modern line layout coverage: " << (float)(textLength - unsupportedTextLength) / (float)textLength * 100 << "%";
+    stream << "\n\n";
+    stream << "Number of blocks: total(" <<  leafRenderers.size() << ") legacy(" << numberOfUnsupportedLeafBlocks << ")\nContent length: total(" <<
+        textLength << ") legacy(" << unsupportedTextLength << ")\n";
+    for (const auto& reasonEntry : flowStatistics) {
+        printReason(static_cast<AvoidanceReason>(reasonEntry.key), stream);
+        stream << ": " << (float)reasonEntry.value / (float)textLength * 100 << "%\n";
+    }
+    stream << "---------------------------------------------------\n";
+    WTFLogAlways("%s", stream.release().utf8().data());
+}
+#endif
+
 template <typename CharacterType> OptionSet<AvoidanceReason> canUseForCharacter(CharacterType, bool textIsJustified, IncludeReasons);
 
 template<> OptionSet<AvoidanceReason> canUseForCharacter(UChar character, bool textIsJustified, IncludeReasons includeReasons)
@@ -312,9 +624,15 @@
     return reasons;
 }
 
-
 OptionSet<AvoidanceReason> canUseForLineLayoutWithReason(const RenderBlockFlow& flow, IncludeReasons includeReasons)
 {
+#ifndef NDEBUG
+    static std::once_flag onceFlag;
+    std::call_once(onceFlag, [] {
+        PAL::registerNotifyCallback("com.apple.WebKit.showModernLineLayoutCoverage", WTF::Function<void()> { printModernLineLayoutCoverage });
+        PAL::registerNotifyCallback("com.apple.WebKit.showModernLineLayoutReasons", WTF::Function<void()> { printModernLineLayoutBlockList });
+    });
+#endif
     OptionSet<AvoidanceReason> reasons;
     // FIXME: For tests that disable SLL and expect to get CLL.
     if (!flow.settings().simpleLineLayoutEnabled())
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to