Title: [289698] trunk/Source/WebCore
Revision
289698
Author
[email protected]
Date
2022-02-12 11:25:51 -0800 (Sat, 12 Feb 2022)

Log Message

Web Inspector: [Flexbox] Show item bounds, gaps, and free space in flex overlays
https://bugs.webkit.org/show_bug.cgi?id=236410

Reviewed by Devin Rousso.

* inspector/InspectorInstrumentation.cpp:
(WebCore::InspectorInstrumentation::flexibleBoxRendererBeganLayoutImpl):
(WebCore::InspectorInstrumentation::flexibleBoxRendererWrappedToNextLineImpl):
(WebCore::InspectorInstrumentation::instrumentingAgents):
* inspector/InspectorInstrumentation.h:
(WebCore::InspectorInstrumentation::flexibleBoxRendererBeganLayout):
(WebCore::InspectorInstrumentation::flexibleBoxRendererWrappedToNextLine):
* inspector/agents/InspectorDOMAgent.cpp:
(WebCore::InspectorDOMAgent::willDestroyFrontendAndBackend):
(WebCore::InspectorDOMAgent::reset):
(WebCore::InspectorDOMAgent::flexibleBoxRendererBeganLayout):
(WebCore::InspectorDOMAgent::flexibleBoxRendererWrappedToNextLine):
(WebCore::InspectorDOMAgent::flexibleBoxRendererCachedItemsAtStartOfLine):
* inspector/agents/InspectorDOMAgent.h:
- Add instrumentation points specifically for flexbox renderers to keep track of which items start a new line
inside flex containers. The start of the first line is not recorded because it will always be zero.

(WebCore::InspectorDOMAgent::didCreateFrontendAndBackend):
- Force a layout of the document to ensure that our collection of flexbox line starts is correctly populated
when attaching an inspector, since without an inspector this information is not kept beyond layout.

* inspector/InspectorOverlay.cpp:
(WebCore::drawLayoutPattern):
- Generalize `drawLayoutHatching` to support different line styles in order to support the new stippling fill.
- In order to support "flipping" the pattern we now use a rectangle encompassing the provided quad as the edges
we follow for filling the pattern (the existing clipping ensures that the final product is still within the
quad). This also resolves an issue that could occur in transformed containers (non-rectangular) where the
spacing was inconsistent at different rotations/perspectives.

(WebCore::drawLayoutStippling):
- A new dot-pattern effect similar to hatching, but using small dots to fill the space instead.

(WebCore::drawLayoutHatching):
- Updated to use the new generic `drawLayoutPattern` helper.

(WebCore::InspectorOverlay::drawFlexOverlay):
(WebCore::InspectorOverlay::buildFlexOverlay):
- Handle iterating through the flex children to show their bounds as well as the spacing/gaps between them.
Almost all this work is done in relative terms, like leading/trailing/cross-axis/main-axis to make it easier to
reason about what should happen for different writing modes, text direction, flex direction, and flex wrapping.
To accomplish this coordinates of children are read through special `corrected*` helper functions that take in
to account the determined direction, main-axis reversal, and cross axis-reversal from all of the relevant
properties. This means there are only 8 (2^3) actual permutations of flex layout (since the layout inside each
individual child is irrelevant here). Throughout we are working with flex children frames that are relative to
their parent, which saves us from having to deal with transforms until after we have constructed most of our
overlay representation, only needing to be passed through `childQuadToRootQuad` before being added to the
appropriate part of the highlight object.

* inspector/InspectorOverlay.h:
(WebCore::InspectorOverlay::Highlight::FlexHighlightOverlay::encode const):
(WebCore::InspectorOverlay::Highlight::FlexHighlightOverlay::decode):

* rendering/RenderBox.h:
(WebCore::RenderBox::marginBox const):
- Add a way to get the entire margin box instead of its individual components.

* rendering/RenderFlexibleBox.cpp:
(WebCore::RenderFlexibleBox::layoutFlexItems):
* rendering/RenderFlexibleBox.h:
- Make getting the computed inter-item and inter-line gap public so we can use them in the overlay.
- Add instrumentation calls to keep track of the indexes of items that start a new line during layout when an
Inspector is attached.

* platform/LayoutUnit.h:
(WebCore::operator!=):

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (289697 => 289698)


--- trunk/Source/WebCore/ChangeLog	2022-02-12 18:16:51 UTC (rev 289697)
+++ trunk/Source/WebCore/ChangeLog	2022-02-12 19:25:51 UTC (rev 289698)
@@ -1,3 +1,76 @@
+2022-02-12  Patrick Angle  <[email protected]>
+
+        Web Inspector: [Flexbox] Show item bounds, gaps, and free space in flex overlays
+        https://bugs.webkit.org/show_bug.cgi?id=236410
+
+        Reviewed by Devin Rousso.
+
+        * inspector/InspectorInstrumentation.cpp:
+        (WebCore::InspectorInstrumentation::flexibleBoxRendererBeganLayoutImpl):
+        (WebCore::InspectorInstrumentation::flexibleBoxRendererWrappedToNextLineImpl):
+        (WebCore::InspectorInstrumentation::instrumentingAgents):
+        * inspector/InspectorInstrumentation.h:
+        (WebCore::InspectorInstrumentation::flexibleBoxRendererBeganLayout):
+        (WebCore::InspectorInstrumentation::flexibleBoxRendererWrappedToNextLine):
+        * inspector/agents/InspectorDOMAgent.cpp:
+        (WebCore::InspectorDOMAgent::willDestroyFrontendAndBackend):
+        (WebCore::InspectorDOMAgent::reset):
+        (WebCore::InspectorDOMAgent::flexibleBoxRendererBeganLayout):
+        (WebCore::InspectorDOMAgent::flexibleBoxRendererWrappedToNextLine):
+        (WebCore::InspectorDOMAgent::flexibleBoxRendererCachedItemsAtStartOfLine):
+        * inspector/agents/InspectorDOMAgent.h:
+        - Add instrumentation points specifically for flexbox renderers to keep track of which items start a new line
+        inside flex containers. The start of the first line is not recorded because it will always be zero.
+        
+        (WebCore::InspectorDOMAgent::didCreateFrontendAndBackend):
+        - Force a layout of the document to ensure that our collection of flexbox line starts is correctly populated
+        when attaching an inspector, since without an inspector this information is not kept beyond layout.
+
+        * inspector/InspectorOverlay.cpp:
+        (WebCore::drawLayoutPattern):
+        - Generalize `drawLayoutHatching` to support different line styles in order to support the new stippling fill.
+        - In order to support "flipping" the pattern we now use a rectangle encompassing the provided quad as the edges
+        we follow for filling the pattern (the existing clipping ensures that the final product is still within the
+        quad). This also resolves an issue that could occur in transformed containers (non-rectangular) where the
+        spacing was inconsistent at different rotations/perspectives.
+
+        (WebCore::drawLayoutStippling):
+        - A new dot-pattern effect similar to hatching, but using small dots to fill the space instead.
+
+        (WebCore::drawLayoutHatching):
+        - Updated to use the new generic `drawLayoutPattern` helper.
+        
+        (WebCore::InspectorOverlay::drawFlexOverlay):
+        (WebCore::InspectorOverlay::buildFlexOverlay):
+        - Handle iterating through the flex children to show their bounds as well as the spacing/gaps between them.
+        Almost all this work is done in relative terms, like leading/trailing/cross-axis/main-axis to make it easier to
+        reason about what should happen for different writing modes, text direction, flex direction, and flex wrapping.
+        To accomplish this coordinates of children are read through special `corrected*` helper functions that take in
+        to account the determined direction, main-axis reversal, and cross axis-reversal from all of the relevant
+        properties. This means there are only 8 (2^3) actual permutations of flex layout (since the layout inside each
+        individual child is irrelevant here). Throughout we are working with flex children frames that are relative to
+        their parent, which saves us from having to deal with transforms until after we have constructed most of our
+        overlay representation, only needing to be passed through `childQuadToRootQuad` before being added to the
+        appropriate part of the highlight object.
+
+        * inspector/InspectorOverlay.h:
+        (WebCore::InspectorOverlay::Highlight::FlexHighlightOverlay::encode const):
+        (WebCore::InspectorOverlay::Highlight::FlexHighlightOverlay::decode):
+
+        * rendering/RenderBox.h:
+        (WebCore::RenderBox::marginBox const):
+        - Add a way to get the entire margin box instead of its individual components.
+
+        * rendering/RenderFlexibleBox.cpp:
+        (WebCore::RenderFlexibleBox::layoutFlexItems):
+        * rendering/RenderFlexibleBox.h:
+        - Make getting the computed inter-item and inter-line gap public so we can use them in the overlay.
+        - Add instrumentation calls to keep track of the indexes of items that start a new line during layout when an
+        Inspector is attached.
+
+        * platform/LayoutUnit.h:
+        (WebCore::operator!=):
+
 2022-02-12  Jer Noble  <[email protected]>
 
         Add settings to restrict media containers and codecs when in Captive Portal mode

Modified: trunk/Source/WebCore/inspector/InspectorInstrumentation.cpp (289697 => 289698)


--- trunk/Source/WebCore/inspector/InspectorInstrumentation.cpp	2022-02-12 18:16:51 UTC (rev 289697)
+++ trunk/Source/WebCore/inspector/InspectorInstrumentation.cpp	2022-02-12 19:25:51 UTC (rev 289698)
@@ -578,6 +578,18 @@
         pageAgent->applyEmulatedMedia(media);
 }
 
+void InspectorInstrumentation::flexibleBoxRendererBeganLayoutImpl(InstrumentingAgents& instrumentingAgents, const RenderObject& renderer)
+{
+    if (auto* domAgent = instrumentingAgents.persistentDOMAgent())
+        domAgent->flexibleBoxRendererBeganLayout(renderer);
+}
+
+void InspectorInstrumentation::flexibleBoxRendererWrappedToNextLineImpl(InstrumentingAgents& instrumentingAgents, const RenderObject& renderer, size_t lineStartItemIndex)
+{
+    if (auto* domAgent = instrumentingAgents.persistentDOMAgent())
+        domAgent->flexibleBoxRendererWrappedToNextLine(renderer, lineStartItemIndex);
+}
+
 void InspectorInstrumentation::willSendRequestImpl(InstrumentingAgents& instrumentingAgents, ResourceLoaderIdentifier identifier, DocumentLoader* loader, ResourceRequest& request, const ResourceResponse& redirectResponse, const CachedResource* cachedResource)
 {
     if (auto* networkAgent = instrumentingAgents.enabledNetworkAgent())
@@ -1272,7 +1284,7 @@
     }
 }
 
-InstrumentingAgents* InspectorInstrumentation::instrumentingAgents(RenderObject& renderer)
+InstrumentingAgents* InspectorInstrumentation::instrumentingAgents(const RenderObject& renderer)
 {
     return instrumentingAgents(renderer.frame());
 }

Modified: trunk/Source/WebCore/inspector/InspectorInstrumentation.h (289697 => 289698)


--- trunk/Source/WebCore/inspector/InspectorInstrumentation.h	2022-02-12 18:16:51 UTC (rev 289697)
+++ trunk/Source/WebCore/inspector/InspectorInstrumentation.h	2022-02-12 19:25:51 UTC (rev 289698)
@@ -191,6 +191,9 @@
     static void applyUserAgentOverride(Frame&, String&);
     static void applyEmulatedMedia(Frame&, String&);
 
+    static void flexibleBoxRendererBeganLayout(const RenderObject&);
+    static void flexibleBoxRendererWrappedToNextLine(const RenderObject&, size_t lineStartItemIndex);
+
     static void willSendRequest(Frame*, ResourceLoaderIdentifier, DocumentLoader*, ResourceRequest&, const ResourceResponse& redirectResponse, const CachedResource*);
     static void didLoadResourceFromMemoryCache(Page&, DocumentLoader*, CachedResource*);
     static void didReceiveResourceResponse(Frame&, ResourceLoaderIdentifier, DocumentLoader*, const ResourceResponse&, ResourceLoader*);
@@ -410,6 +413,9 @@
     static void applyUserAgentOverrideImpl(InstrumentingAgents&, String&);
     static void applyEmulatedMediaImpl(InstrumentingAgents&, String&);
 
+    static void flexibleBoxRendererBeganLayoutImpl(InstrumentingAgents&, const RenderObject&);
+    static void flexibleBoxRendererWrappedToNextLineImpl(InstrumentingAgents&, const RenderObject&, size_t lineStartItemIndex);
+
     static void willSendRequestImpl(InstrumentingAgents&, ResourceLoaderIdentifier, DocumentLoader*, ResourceRequest&, const ResourceResponse& redirectResponse, const CachedResource*);
     static void willSendRequestOfTypeImpl(InstrumentingAgents&, ResourceLoaderIdentifier, DocumentLoader*, ResourceRequest&, LoadType);
     static void markResourceAsCachedImpl(InstrumentingAgents&, ResourceLoaderIdentifier);
@@ -523,7 +529,7 @@
     static InstrumentingAgents* instrumentingAgents(ScriptExecutionContext&);
     static InstrumentingAgents* instrumentingAgents(Document&);
     static InstrumentingAgents* instrumentingAgents(Document*);
-    static InstrumentingAgents* instrumentingAgents(RenderObject&);
+    static InstrumentingAgents* instrumentingAgents(const RenderObject&);
     static InstrumentingAgents* instrumentingAgents(WorkerOrWorkletGlobalScope*);
 };
 
@@ -1040,6 +1046,20 @@
         applyEmulatedMediaImpl(*agents, media);
 }
 
+inline void InspectorInstrumentation::flexibleBoxRendererBeganLayout(const RenderObject& renderer)
+{
+    FAST_RETURN_IF_NO_FRONTENDS(void());
+    if (auto* agents = instrumentingAgents(renderer))
+        flexibleBoxRendererBeganLayoutImpl(*agents, renderer);
+}
+
+inline void InspectorInstrumentation::flexibleBoxRendererWrappedToNextLine(const RenderObject& renderer, size_t lineStartItemIndex)
+{
+    FAST_RETURN_IF_NO_FRONTENDS(void());
+    if (auto* agents = instrumentingAgents(renderer))
+        flexibleBoxRendererWrappedToNextLineImpl(*agents, renderer, lineStartItemIndex);
+}
+
 inline void InspectorInstrumentation::willSendRequest(Frame* frame, ResourceLoaderIdentifier identifier, DocumentLoader* loader, ResourceRequest& request, const ResourceResponse& redirectResponse, const CachedResource* cachedResource)
 {
     FAST_RETURN_IF_NO_FRONTENDS(void());

Modified: trunk/Source/WebCore/inspector/InspectorOverlay.cpp (289697 => 289698)


--- trunk/Source/WebCore/inspector/InspectorOverlay.cpp	2022-02-12 18:16:51 UTC (rev 289697)
+++ trunk/Source/WebCore/inspector/InspectorOverlay.cpp	2022-02-12 19:25:51 UTC (rev 289698)
@@ -51,6 +51,8 @@
 #include "GridArea.h"
 #include "GridPositionsResolver.h"
 #include "InspectorClient.h"
+#include "InspectorController.h"
+#include "InspectorDOMAgent.h"
 #include "IntPoint.h"
 #include "IntRect.h"
 #include "IntSize.h"
@@ -57,6 +59,7 @@
 #include "Node.h"
 #include "NodeList.h"
 #include "NodeRenderStyle.h"
+#include "OrderIterator.h"
 #include "Page.h"
 #include "PseudoElement.h"
 #include "RenderBox.h"
@@ -68,6 +71,7 @@
 #include "Settings.h"
 #include "StyleGridData.h"
 #include "StyleResolver.h"
+#include "TextDirection.h"
 #include <wtf/MathExtras.h>
 #include <wtf/text/StringBuilder.h>
 
@@ -95,6 +99,8 @@
 static constexpr UChar thinSpace = 0x2009;
 static constexpr UChar emSpace = 0x2003;
 
+enum class Flip : bool { No, Yes };
+
 static void truncateWithEllipsis(String& string, size_t length)
 {
     if (string.length() > length) {
@@ -1220,40 +1226,63 @@
     return path;
 }
 
-static void drawLayoutHatching(GraphicsContext& context, FloatQuad quad)
+static void drawLayoutPattern(GraphicsContext& context, const FloatQuad& quad, int hatchSpacing, Flip flip)
 {
     GraphicsContextStateSaver saver(context);
     context.clipPath(quadToPath(quad));
-    context.setStrokeThickness(0.5);
-    context.setStrokeStyle(StrokeStyle::DashedStroke);
-    context.setLineDash({ 2, 2 }, 2);
-        
-    constexpr auto hatchSpacing = 12;
+
     Path hatchPath;
-    
-    FloatLine topSide = { quad.p1(), quad.p2() };
-    FloatLine leftSide = { quad.p1(), quad.p4() };
-    
+
+    auto boundingBox = quad.enclosingBoundingBox();
+
+    auto correctedLineForPoints = [&](const FloatPoint& start, const FloatPoint& end) {
+        return (flip == Flip::Yes) ? FloatLine(end, start) : FloatLine(start, end);
+    };
+
+    auto topSide = correctedLineForPoints(boundingBox.minXMinYCorner(), boundingBox.maxXMinYCorner());
+    auto leftSide = correctedLineForPoints(boundingBox.minXMinYCorner(), boundingBox.minXMaxYCorner());
+
     // The opposite axis' length is used to determine how far to draw a hatch line in both dimensions, which keeps the lines at a 45deg angle.
     if (topSide.length() > leftSide.length()) {
-        FloatLine bottomSide = { quad.p4(), quad.p3() };
-        // Move across the relative top of the quad, starting left of `0, 0` to ensure that the tail of the previous hatch line is drawn while scrolling.
+        auto bottomSide = correctedLineForPoints(boundingBox.minXMaxYCorner(), boundingBox.maxXMaxYCorner());
+        // Move across the relative top of the area, starting left of `0, 0` to ensure that the tail of the previous hatch line is drawn while scrolling.
         for (float x = -leftSide.length(); x < topSide.length(); x += hatchSpacing) {
             hatchPath.moveTo(topSide.pointAtAbsoluteDistance(x));
             hatchPath.addLineTo(bottomSide.pointAtAbsoluteDistance(x + leftSide.length()));
         }
     } else {
-        FloatLine rightSide = { quad.p2(), quad.p3() };
-        // Move down the relative left side of the quad, starting above `0, 0` to ensure that the tail of the previous hatch line is drawn while scrolling.
+        auto rightSide = correctedLineForPoints(boundingBox.maxXMinYCorner(), boundingBox.maxXMaxYCorner());
+        // Move down the relative left side of the area, starting above `0, 0` to ensure that the tail of the previous hatch line is drawn while scrolling.
         for (float y = -topSide.length(); y < leftSide.length(); y += hatchSpacing) {
             hatchPath.moveTo(leftSide.pointAtAbsoluteDistance(y));
             hatchPath.addLineTo(rightSide.pointAtAbsoluteDistance(y + topSide.length()));
         }
     }
-    
+
     context.strokePath(hatchPath);
 }
 
+static void drawLayoutStippling(GraphicsContext& context, const FloatQuad& quad, float density)
+{
+    GraphicsContextStateSaver saver(context);
+    context.setStrokeThickness(1);
+    context.setStrokeStyle(StrokeStyle::DashedStroke);
+    context.setLineDash({ 1, density }, 1);
+
+    drawLayoutPattern(context, quad, density, Flip::No);
+}
+
+static void drawLayoutHatching(GraphicsContext& context, const FloatQuad& quad, Flip flip = Flip::No)
+{
+    GraphicsContextStateSaver saver(context);
+    context.setStrokeThickness(0.5);
+    context.setStrokeStyle(StrokeStyle::DashedStroke);
+    context.setLineDash({ 2, 2 }, 2);
+
+    constexpr auto defaultLayoutHatchSpacing = 12;
+    drawLayoutPattern(context, quad, defaultLayoutHatchSpacing, flip);
+}
+
 static FontCascade fontForLayoutLabel()
 {
     FontCascadeDescription fontDescription;
@@ -1993,6 +2022,33 @@
     context.setStrokeThickness(1);
     context.setStrokeColor(flexHighlightOverlay.color);
     context.strokePath(quadToPath(flexHighlightOverlay.containerBounds));
+
+    for (const auto& bounds : flexHighlightOverlay.itemBounds)
+        context.strokePath(quadToPath(bounds));
+
+    for (const auto& mainAxisGap : flexHighlightOverlay.mainAxisGaps) {
+        context.strokePath(quadToPath(mainAxisGap));
+        drawLayoutHatching(context, mainAxisGap);
+    }
+
+    {
+        GraphicsContextStateSaver mainAxisSpaceContextSaver(context);
+        context.setAlpha(0.5);
+
+        constexpr auto mainAxisSpaceDensity = 3;
+        for (auto mainAxisSpaceBetweenItemAndGap : flexHighlightOverlay.mainAxisSpaceBetweenItemsAndGaps)
+            drawLayoutStippling(context, mainAxisSpaceBetweenItemAndGap, mainAxisSpaceDensity);
+    }
+
+    for (const auto& crossAxisGap : flexHighlightOverlay.crossAxisGaps) {
+        context.strokePath(quadToPath(crossAxisGap));
+        drawLayoutHatching(context, crossAxisGap, Flip::Yes);
+    }
+
+    context.setAlpha(0.7);
+    constexpr auto spaceBetweenItemsAndCrossAxisSpaceStipplingDensity = 6;
+    for (const auto& crossAxisSpaceBetweenItemAndGap : flexHighlightOverlay.spaceBetweenItemsAndCrossAxisSpace)
+        drawLayoutStippling(context, crossAxisSpaceBetweenItemAndGap, spaceBetweenItemsAndCrossAxisSpaceStipplingDensity);
 }
 
 std::optional<InspectorOverlay::Highlight::FlexHighlightOverlay> InspectorOverlay::buildFlexOverlay(const InspectorOverlay::Flex& flexOverlay)
@@ -2016,24 +2072,181 @@
 
     auto& renderFlex = *downcast<RenderFlexibleBox>(renderer);
 
+    auto itemsAtStartOfLine = m_page.inspectorController().ensureDOMAgent().flexibleBoxRendererCachedItemsAtStartOfLine(renderFlex);
+
     Frame* containingFrame = node->document().frame();
     if (!containingFrame)
         return { };
     FrameView* containingView = containingFrame->view();
 
-    auto localQuadToRootQuad = [&](const FloatQuad& quad) -> FloatQuad {
-        return {
+    auto computedStyle = node->computedStyle();
+    if (!computedStyle)
+        return { };
+
+    auto wasRowDirection = !computedStyle->isColumnFlexDirection();
+    auto isFlippedBlocksWritingMode = computedStyle->isFlippedBlocksWritingMode();
+    auto isRightToLeftDirection = computedStyle->direction() == TextDirection::RTL;
+
+    auto isRowDirection = wasRowDirection ^ !computedStyle->isHorizontalWritingMode();
+    auto isMainAxisDirectionReversed = computedStyle->isReverseFlexDirection() ^ (wasRowDirection ? isRightToLeftDirection : isFlippedBlocksWritingMode);
+    auto isCrossAxisDirectionReversed = (computedStyle->flexWrap() == FlexWrap::Reverse) ^ (wasRowDirection ? isFlippedBlocksWritingMode : isRightToLeftDirection);
+
+    auto localQuadToRootQuad = [&](const FloatQuad& quad) {
+        return FloatQuad(
             localPointToRootPoint(containingView, quad.p1()),
             localPointToRootPoint(containingView, quad.p2()),
             localPointToRootPoint(containingView, quad.p3()),
             localPointToRootPoint(containingView, quad.p4())
-        };
+        );
     };
 
+    auto childQuadToRootQuad = [&](const FloatQuad& quad) {
+        return FloatQuad(
+            localPointToRootPoint(containingView, renderFlex.localToContainerPoint(quad.p1(), nullptr)),
+            localPointToRootPoint(containingView, renderFlex.localToContainerPoint(quad.p2(), nullptr)),
+            localPointToRootPoint(containingView, renderFlex.localToContainerPoint(quad.p3(), nullptr)),
+            localPointToRootPoint(containingView, renderFlex.localToContainerPoint(quad.p4(), nullptr))
+        );
+    };
+
+    auto correctedMainAxisLeadingEdge = [&](const LayoutRect& rect) {
+        if (isRowDirection)
+            return isMainAxisDirectionReversed ? rect.maxX() : rect.x();
+        return isMainAxisDirectionReversed ? rect.maxY() : rect.y();
+    };
+
+    auto correctedMainAxisTrailingEdge = [&](const LayoutRect& rect) {
+        if (isRowDirection)
+            return isMainAxisDirectionReversed ? rect.x() : rect.maxX();
+        return isMainAxisDirectionReversed ? rect.y() : rect.maxY();
+    };
+
+    auto correctedCrossAxisLeadingEdge = [&](const LayoutRect& rect) {
+        if (isRowDirection)
+            return isCrossAxisDirectionReversed ? rect.maxY() : rect.y();
+        return isCrossAxisDirectionReversed ? rect.maxX() : rect.x();
+    };
+
+    auto correctedCrossAxisTrailingEdge = [&](const LayoutRect& rect) {
+        if (isRowDirection)
+            return isCrossAxisDirectionReversed ? rect.y() : rect.maxY();
+        return isCrossAxisDirectionReversed ? rect.x() : rect.maxX();
+    };
+
+    auto correctedCrossAxisMin = [&](float a, float b) {
+        return isCrossAxisDirectionReversed ? std::fmax(a, b) : std::fmin(a, b);
+    };
+
+    auto correctedCrossAxisMax = [&](float a, float b) {
+        return isCrossAxisDirectionReversed ? std::fmin(a, b) : std::fmax(a, b);
+    };
+
+    auto correctedPoint = [&](float mainAxisLocation, float crossAxisLocation) {
+        return isRowDirection ? FloatPoint(mainAxisLocation, crossAxisLocation) : FloatPoint(crossAxisLocation, mainAxisLocation);
+    };
+
+    auto populateHighlightForGapOrSpace = [&](float fromMainAxisEdge, float toMainAxisEdge, float fromCrossAxisEdge, float toCrossAxisEdge, Vector<FloatQuad>& gapsSet) {
+        gapsSet.append(childQuadToRootQuad({
+            correctedPoint(fromMainAxisEdge, fromCrossAxisEdge),
+            correctedPoint(toMainAxisEdge, fromCrossAxisEdge),
+            correctedPoint(toMainAxisEdge, toCrossAxisEdge),
+            correctedPoint(fromMainAxisEdge, toCrossAxisEdge),
+        }));
+    };
+
     InspectorOverlay::Highlight::FlexHighlightOverlay flexHighlightOverlay;
     flexHighlightOverlay.color = flexOverlay.config.flexColor;
-    flexHighlightOverlay.containerBounds = localQuadToRootQuad({ renderFlex.absoluteContentQuad() });
+    flexHighlightOverlay.containerBounds = localQuadToRootQuad(renderFlex.absoluteContentQuad());
 
+    float computedMainAxisGap = renderFlex.computeGap(RenderFlexibleBox::GapType::BetweenItems).toFloat();
+    float computedCrossAxisGap = renderFlex.computeGap(RenderFlexibleBox::GapType::BetweenLines).toFloat();
+
+    // For reasoning about the edges of the flex container, use the untransformed content rect moved to the origin of the
+    // inner top-left corner of padding, which is the same relative coordinate space that each item's `frameRect()` will be in.
+    auto containerRect = renderFlex.absoluteContentBox();
+    containerRect.setLocation({ renderFlex.paddingLeft() + renderFlex.borderLeft(), renderFlex.paddingTop() + renderFlex.borderTop() });
+
+    float containerMainAxisLeadingEdge = correctedMainAxisLeadingEdge(containerRect);
+    float containerMainAxisTrailingEdge = correctedMainAxisTrailingEdge(containerRect);
+
+    Vector<LayoutRect> currentLineChildrenRects;
+    float currentLineCrossAxisLeadingEdge = isCrossAxisDirectionReversed ? 0.0f : std::numeric_limits<float>::max();
+    float currentLineCrossAxisTrailingEdge = isCrossAxisDirectionReversed ? std::numeric_limits<float>::max() : 0.0f;
+    float previousLineCrossAxisTrailingEdge = correctedCrossAxisLeadingEdge(containerRect);
+
+    size_t currentChildIndex = 0;
+    auto childOrderIterator = renderFlex.orderIterator();
+    for (RenderBox* renderChild = childOrderIterator.first(); renderChild; renderChild = childOrderIterator.next()) {
+        if (childOrderIterator.shouldSkipChild(*renderChild))
+            continue;
+
+        // Build bounds for each child and collect children on the same logical line.
+        {
+            auto childRect = renderChild->frameRect();
+            renderFlex.flipForWritingMode(childRect);
+            childRect.expand(renderChild->marginBox());
+            flexHighlightOverlay.itemBounds.append(childQuadToRootQuad({ childRect }));
+
+            currentLineCrossAxisLeadingEdge = correctedCrossAxisMin(currentLineCrossAxisLeadingEdge, correctedCrossAxisLeadingEdge(childRect));
+            currentLineCrossAxisTrailingEdge = correctedCrossAxisMax(currentLineCrossAxisTrailingEdge, correctedCrossAxisTrailingEdge(childRect));
+
+            currentLineChildrenRects.append(WTFMove(childRect));
+            ++currentChildIndex;
+        }
+
+        // The remaining work can only be done once we have collected all of the children on the current line.
+        if (!itemsAtStartOfLine.contains(currentChildIndex))
+            continue;
+
+        float previousChildMainAxisTrailingEdge = correctedMainAxisLeadingEdge(containerRect);
+        for (const auto& childRect : currentLineChildrenRects) {
+            auto childMainAxisLeadingEdge = correctedMainAxisLeadingEdge(childRect);
+            auto childMainAxisTrailingEdge = correctedMainAxisTrailingEdge(childRect);
+            auto childCrossAxisLeadingEdge = correctedCrossAxisLeadingEdge(childRect);
+            auto childCrossAxisTrailingEdge = correctedCrossAxisTrailingEdge(childRect);
+
+            // Build bounds for space between the current item and the cross-axis space.
+            if (std::fabs(childCrossAxisLeadingEdge - currentLineCrossAxisLeadingEdge) > 1)
+                populateHighlightForGapOrSpace(childMainAxisLeadingEdge, childMainAxisTrailingEdge, currentLineCrossAxisLeadingEdge, childCrossAxisLeadingEdge, flexHighlightOverlay.spaceBetweenItemsAndCrossAxisSpace);
+            if (std::fabs(childCrossAxisTrailingEdge - currentLineCrossAxisTrailingEdge) > 1)
+                populateHighlightForGapOrSpace(childMainAxisLeadingEdge, childMainAxisTrailingEdge, currentLineCrossAxisTrailingEdge, childCrossAxisTrailingEdge, flexHighlightOverlay.spaceBetweenItemsAndCrossAxisSpace);
+
+            // Build bounds for gaps and space between the current item and previous item (or container edge).
+            if (computedMainAxisGap && previousChildMainAxisTrailingEdge != correctedMainAxisLeadingEdge(containerRect)) {
+                // Regardless of flipped axises, we need to do the below calculations from left to right or top to bottom.
+                float startEdge = std::fmin(previousChildMainAxisTrailingEdge, childMainAxisLeadingEdge);
+                float endEdge = std::fmax(previousChildMainAxisTrailingEdge, childMainAxisLeadingEdge);
+
+                float spaceBetweenEdgeAndGap = (endEdge - startEdge - computedMainAxisGap) / 2;
+
+                populateHighlightForGapOrSpace(startEdge, startEdge + spaceBetweenEdgeAndGap, currentLineCrossAxisLeadingEdge, currentLineCrossAxisTrailingEdge, flexHighlightOverlay.mainAxisSpaceBetweenItemsAndGaps);
+                populateHighlightForGapOrSpace(startEdge + spaceBetweenEdgeAndGap, endEdge - spaceBetweenEdgeAndGap, currentLineCrossAxisLeadingEdge, currentLineCrossAxisTrailingEdge, flexHighlightOverlay.mainAxisGaps);
+                populateHighlightForGapOrSpace(endEdge - spaceBetweenEdgeAndGap, endEdge, currentLineCrossAxisLeadingEdge, currentLineCrossAxisTrailingEdge, flexHighlightOverlay.mainAxisSpaceBetweenItemsAndGaps);
+            } else
+                populateHighlightForGapOrSpace(previousChildMainAxisTrailingEdge, childMainAxisLeadingEdge, currentLineCrossAxisLeadingEdge, currentLineCrossAxisTrailingEdge, flexHighlightOverlay.mainAxisSpaceBetweenItemsAndGaps);
+
+            previousChildMainAxisTrailingEdge = childMainAxisTrailingEdge;
+        }
+        populateHighlightForGapOrSpace(previousChildMainAxisTrailingEdge, containerMainAxisTrailingEdge, currentLineCrossAxisLeadingEdge, currentLineCrossAxisTrailingEdge, flexHighlightOverlay.mainAxisSpaceBetweenItemsAndGaps);
+
+        // Build gaps between the current line and the previous line.
+        if (computedCrossAxisGap && previousLineCrossAxisTrailingEdge != correctedCrossAxisLeadingEdge(containerRect)) {
+            // Regardless of flipped axises, we need to do the below calculations from left to right or top to bottom.
+            float startEdge = std::fmin(previousLineCrossAxisTrailingEdge, currentLineCrossAxisLeadingEdge);
+            float endEdge = std::fmax(previousLineCrossAxisTrailingEdge, currentLineCrossAxisLeadingEdge);
+
+            float spaceBetweenEdgeAndGap = (endEdge - startEdge - computedCrossAxisGap) / 2;
+
+            populateHighlightForGapOrSpace(containerMainAxisLeadingEdge, containerMainAxisTrailingEdge, startEdge + spaceBetweenEdgeAndGap, endEdge - spaceBetweenEdgeAndGap, flexHighlightOverlay.crossAxisGaps);
+        }
+
+        previousLineCrossAxisTrailingEdge = currentLineCrossAxisTrailingEdge;
+
+        currentLineChildrenRects.clear();
+        currentLineCrossAxisLeadingEdge = isCrossAxisDirectionReversed ? 0.0f : std::numeric_limits<float>::max();
+        currentLineCrossAxisTrailingEdge = isCrossAxisDirectionReversed ? std::numeric_limits<float>::max() : 0.0f;
+    }
+
     return { flexHighlightOverlay };
 }
 

Modified: trunk/Source/WebCore/inspector/InspectorOverlay.h (289697 => 289698)


--- trunk/Source/WebCore/inspector/InspectorOverlay.h	2022-02-12 18:16:51 UTC (rev 289697)
+++ trunk/Source/WebCore/inspector/InspectorOverlay.h	2022-02-12 19:25:51 UTC (rev 289698)
@@ -39,6 +39,7 @@
 #include <wtf/MonotonicTime.h>
 #include <wtf/RefPtr.h>
 #include <wtf/Vector.h>
+#include <wtf/WeakHashMap.h>
 #include <wtf/WeakPtr.h>
 #include <wtf/text/WTFString.h>
 
@@ -146,6 +147,11 @@
 
             Color color;
             FloatQuad containerBounds;
+            Vector<FloatQuad> itemBounds;
+            Vector<FloatQuad> mainAxisGaps;
+            Vector<FloatQuad> mainAxisSpaceBetweenItemsAndGaps;
+            Vector<FloatQuad> spaceBetweenItemsAndCrossAxisSpace;
+            Vector<FloatQuad> crossAxisGaps;
 
 #if PLATFORM(IOS_FAMILY)
             template<class Encoder> void encode(Encoder&) const;
@@ -303,6 +309,11 @@
 {
     encoder << color;
     encoder << containerBounds;
+    encoder << itemBounds;
+    encoder << mainAxisGaps;
+    encoder << mainAxisSpaceBetweenItemsAndGaps;
+    encoder << spaceBetweenItemsAndCrossAxisSpace;
+    encoder << crossAxisGaps;
 }
 
 template<class Decoder> std::optional<InspectorOverlay::Highlight::FlexHighlightOverlay> InspectorOverlay::Highlight::FlexHighlightOverlay::decode(Decoder& decoder)
@@ -312,6 +323,16 @@
         return { };
     if (!decoder.decode(flexHighlightOverlay.containerBounds))
         return { };
+    if (!decoder.decode(flexHighlightOverlay.itemBounds))
+        return { };
+    if (!decoder.decode(flexHighlightOverlay.mainAxisGaps))
+        return { };
+    if (!decoder.decode(flexHighlightOverlay.mainAxisSpaceBetweenItemsAndGaps))
+        return { };
+    if (!decoder.decode(flexHighlightOverlay.spaceBetweenItemsAndCrossAxisSpace))
+        return { };
+    if (!decoder.decode(flexHighlightOverlay.crossAxisGaps))
+        return { };
     return { flexHighlightOverlay };
 }
 

Modified: trunk/Source/WebCore/inspector/agents/InspectorDOMAgent.cpp (289697 => 289698)


--- trunk/Source/WebCore/inspector/agents/InspectorDOMAgent.cpp	2022-02-12 18:16:51 UTC (rev 289697)
+++ trunk/Source/WebCore/inspector/agents/InspectorDOMAgent.cpp	2022-02-12 19:25:51 UTC (rev 289698)
@@ -95,6 +95,7 @@
 #include "Pasteboard.h"
 #include "PseudoElement.h"
 #include "RenderGrid.h"
+#include "RenderObject.h"
 #include "RenderStyle.h"
 #include "RenderStyleConstants.h"
 #include "ScriptController.h"
@@ -307,6 +308,10 @@
     m_instrumentingAgents.setPersistentDOMAgent(this);
     m_document = m_inspectedPage.mainFrame().document();
 
+    // Force a layout so that we can collect additional information from the layout process.
+    if (m_document)
+        m_document->updateLayout();
+
 #if ENABLE(VIDEO)
     if (m_document)
         addEventListenersToNode(*m_document);
@@ -329,6 +334,7 @@
     hideHighlight();
 
     m_overlay->clearAllGridOverlays();
+    m_overlay->clearAllFlexOverlays();
 
     m_instrumentingAgents.setPersistentDOMAgent(nullptr);
     m_documentRequested = false;
@@ -356,6 +362,7 @@
     if (m_revalidateStyleAttrTask)
         m_revalidateStyleAttrTask->reset();
     m_document = nullptr;
+    m_flexibleBoxRendererCachedItemsAtStartOfLine.clear();
 
     m_destroyedDetachedNodeIdentifiers.clear();
     m_destroyedAttachedNodeIdentifiers.clear();
@@ -2790,6 +2797,23 @@
     m_dispatchedEvents.remove(&event);
 }
 
+void InspectorDOMAgent::flexibleBoxRendererBeganLayout(const RenderObject& renderer)
+{
+    m_flexibleBoxRendererCachedItemsAtStartOfLine.remove(renderer);
+}
+
+void InspectorDOMAgent::flexibleBoxRendererWrappedToNextLine(const RenderObject& renderer, size_t lineStartItemIndex)
+{
+    m_flexibleBoxRendererCachedItemsAtStartOfLine.ensure(renderer, [] {
+        return Vector<size_t>();
+    }).iterator->value.append(lineStartItemIndex);
+}
+
+Vector<size_t> InspectorDOMAgent::flexibleBoxRendererCachedItemsAtStartOfLine(const RenderObject& renderer)
+{
+    return m_flexibleBoxRendererCachedItemsAtStartOfLine.get(renderer);
+}
+
 RefPtr<JSC::Breakpoint> InspectorDOMAgent::breakpointForEventListener(EventTarget& target, const AtomString& eventType, EventListener& listener, bool capture)
 {
     for (auto& inspectorEventListener : m_eventListenerEntries.values()) {

Modified: trunk/Source/WebCore/inspector/agents/InspectorDOMAgent.h (289697 => 289698)


--- trunk/Source/WebCore/inspector/agents/InspectorDOMAgent.h	2022-02-12 18:16:51 UTC (rev 289697)
+++ trunk/Source/WebCore/inspector/agents/InspectorDOMAgent.h	2022-02-12 19:25:51 UTC (rev 289698)
@@ -72,6 +72,7 @@
 class Node;
 class Page;
 class PseudoElement;
+class RenderObject;
 class RevalidateStyleAttributeTask;
 class ShadowRoot;
 
@@ -182,6 +183,8 @@
     void willRemoveEventListener(EventTarget&, const AtomString& eventType, EventListener&, bool capture);
     bool isEventListenerDisabled(EventTarget&, const AtomString& eventType, EventListener&, bool capture);
     void eventDidResetAfterDispatch(const Event&);
+    void flexibleBoxRendererBeganLayout(const RenderObject&);
+    void flexibleBoxRendererWrappedToNextLine(const RenderObject&, size_t lineStartItemIndex);
 
     // Callbacks that don't directly correspond to an instrumentation entry point.
     void setDocument(Document*);
@@ -205,6 +208,7 @@
 
     InspectorHistory* history() { return m_history.get(); }
     Vector<Document*> documents();
+    Vector<size_t> flexibleBoxRendererCachedItemsAtStartOfLine(const RenderObject&);
     void reset();
 
     Node* assertNode(Inspector::Protocol::ErrorString&, Inspector::Protocol::DOM::NodeId);
@@ -269,6 +273,7 @@
     std::unique_ptr<InspectorOverlay::Highlight::Config> m_inspectModeHighlightConfig;
     std::unique_ptr<InspectorHistory> m_history;
     std::unique_ptr<DOMEditor> m_domEditor;
+    WeakHashMap<RenderObject, Vector<size_t>> m_flexibleBoxRendererCachedItemsAtStartOfLine;
 
     Vector<Inspector::Protocol::DOM::NodeId> m_destroyedDetachedNodeIdentifiers;
     Vector<std::pair<Inspector::Protocol::DOM::NodeId, Inspector::Protocol::DOM::NodeId>> m_destroyedAttachedNodeIdentifiers;

Modified: trunk/Source/WebCore/platform/LayoutUnit.h (289697 => 289698)


--- trunk/Source/WebCore/platform/LayoutUnit.h	2022-02-12 18:16:51 UTC (rev 289697)
+++ trunk/Source/WebCore/platform/LayoutUnit.h	2022-02-12 19:25:51 UTC (rev 289698)
@@ -378,6 +378,11 @@
     return a.rawValue() != b.rawValue();
 }
 
+inline bool operator!=(const float a, const LayoutUnit& b)
+{
+    return LayoutUnit(a) != b;
+}
+
 inline bool operator!=(const LayoutUnit& a, float b)
 {
     return a != LayoutUnit(b);

Modified: trunk/Source/WebCore/rendering/RenderBox.h (289697 => 289698)


--- trunk/Source/WebCore/rendering/RenderBox.h	2022-02-12 18:16:51 UTC (rev 289697)
+++ trunk/Source/WebCore/rendering/RenderBox.h	2022-02-12 19:25:51 UTC (rev 289698)
@@ -262,6 +262,7 @@
     virtual void setScrollTop(int, const ScrollPositionChangeOptions&);
     void setScrollPosition(const ScrollPosition&, const ScrollPositionChangeOptions&);
 
+    const LayoutBoxExtent& marginBox() const { return m_marginBox; }
     LayoutUnit marginTop() const override { return m_marginBox.top(); }
     LayoutUnit marginBottom() const override { return m_marginBox.bottom(); }
     LayoutUnit marginLeft() const override { return m_marginBox.left(); }

Modified: trunk/Source/WebCore/rendering/RenderFlexibleBox.cpp (289697 => 289698)


--- trunk/Source/WebCore/rendering/RenderFlexibleBox.cpp	2022-02-12 18:16:51 UTC (rev 289697)
+++ trunk/Source/WebCore/rendering/RenderFlexibleBox.cpp	2022-02-12 19:25:51 UTC (rev 289698)
@@ -1153,8 +1153,11 @@
     Vector<FlexItem> lineItems;
     size_t nextIndex = 0;
     size_t numLines = 0;
+    InspectorInstrumentation::flexibleBoxRendererBeganLayout(*this);
     while (flexAlgorithm.computeNextFlexLine(nextIndex, lineItems, sumFlexBaseSize, totalFlexGrow, totalFlexShrink, totalWeightedFlexShrink, sumHypotheticalMainSize)) {
         ++numLines;
+        InspectorInstrumentation::flexibleBoxRendererWrappedToNextLine(*this, nextIndex);
+
         LayoutUnit containerMainInnerSize = mainAxisContentExtent(sumHypotheticalMainSize);
         // availableFreeSpace is the initial amount of free space in this flexbox.
         // remainingFreeSpace starts out at the same value but as we place and lay

Modified: trunk/Source/WebCore/rendering/RenderFlexibleBox.h (289697 => 289698)


--- trunk/Source/WebCore/rendering/RenderFlexibleBox.h	2022-02-12 18:16:51 UTC (rev 289697)
+++ trunk/Source/WebCore/rendering/RenderFlexibleBox.h	2022-02-12 19:25:51 UTC (rev 289698)
@@ -87,6 +87,9 @@
     // be laid out again.
     bool setStaticPositionForPositionedLayout(const RenderBox&);
 
+    enum class GapType { BetweenLines, BetweenItems };
+    LayoutUnit computeGap(GapType) const;
+
 protected:
     void computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const override;
 
@@ -205,9 +208,6 @@
 
     void resetHasDefiniteHeight() { m_hasDefiniteHeight = SizeDefiniteness::Unknown; }
 
-    enum class GapType { BetweenLines, BetweenItems };
-    LayoutUnit computeGap(GapType) const;
-
     // This is used to cache the preferred size for orthogonal flow children so we
     // don't have to relayout to get it
     HashMap<const RenderBox*, LayoutUnit> m_intrinsicSizeAlongMainAxis;
@@ -222,7 +222,7 @@
     // the first layout likely did not use the correct value for percentage
     // sizing of children.
     HashSet<const RenderBox*> m_relaidOutChildren;
-    
+
     mutable OrderIterator m_orderIterator { *this };
     int m_numberOfInFlowChildrenOnFirstLine { -1 };
     
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to