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 };