void PaintLayerClipper::calculateClipRects(const ClipRectsContext& context, ClipRects& clipRects) const { bool rootLayerScrolls = m_layoutObject.document().settings() && m_layoutObject.document().settings()->rootLayerScrolls(); if (!m_layoutObject.layer()->parent() && !rootLayerScrolls) { // The root layer's clip rect is always infinite. clipRects.reset(LayoutRect(LayoutRect::infiniteIntRect())); return; } bool isClippingRoot = m_layoutObject.layer() == context.rootLayer; // For transformed layers, the root layer was shifted to be us, so there is no need to // examine the parent. We want to cache clip rects with us as the root. PaintLayer* parentLayer = !isClippingRoot ? m_layoutObject.layer()->parent() : 0; // Ensure that our parent's clip has been calculated so that we can examine the values. if (parentLayer) { // FIXME: Why don't we just call getClipRects here? if (context.usesCache() && parentLayer->clipper().cachedClipRects(context)) { clipRects = *parentLayer->clipper().cachedClipRects(context); } else { parentLayer->clipper().calculateClipRects(context, clipRects); } } else { clipRects.reset(LayoutRect(LayoutRect::infiniteIntRect())); } adjustClipRectsForChildren(m_layoutObject, clipRects); if ((m_layoutObject.hasOverflowClip() && shouldRespectOverflowClip(context)) || m_layoutObject.hasClip()) { // This offset cannot use convertToLayerCoords, because sometimes our rootLayer may be across // some transformed layer boundary, for example, in the PaintLayerCompositor overlapMap, where // clipRects are needed in view space. applyClipRects(context, m_layoutObject, roundedLayoutPoint(m_layoutObject.localToContainerPoint(FloatPoint(), context.rootLayer->layoutObject())), clipRects); } }
TEST_F(PaintLayerClipperTest, NestedContainPaintClip) { setBodyInnerHTML( "<div style='contain: paint; width: 200px; height: 200px; overflow: " "auto'>" " <div id='target' style='contain: paint; height: 400px'>" " </div>" "</div>"); LayoutRect infiniteRect(LayoutRect::infiniteIntRect()); PaintLayer* layer = toLayoutBoxModelObject(getLayoutObjectByElementId("target"))->layer(); ClipRectsContext context(layer->parent(), PaintingClipRectsIgnoringOverflowClip); LayoutRect layerBounds; ClipRect backgroundRect, foregroundRect; layer->clipper().calculateRects(context, infiniteRect, layerBounds, backgroundRect, foregroundRect); EXPECT_EQ(LayoutRect(0, 0, 200, 400), backgroundRect.rect()); EXPECT_EQ(LayoutRect(0, 0, 200, 400), foregroundRect.rect()); EXPECT_EQ(LayoutRect(0, 0, 200, 400), layerBounds); ClipRectsContext contextClip(layer->parent(), PaintingClipRects); layer->clipper().calculateRects(contextClip, infiniteRect, layerBounds, backgroundRect, foregroundRect); EXPECT_EQ(LayoutRect(0, 0, 200, 200), backgroundRect.rect()); EXPECT_EQ(LayoutRect(0, 0, 200, 200), foregroundRect.rect()); EXPECT_EQ(LayoutRect(0, 0, 200, 400), layerBounds); }
static bool shouldRepaintSubsequence(PaintLayer& paintLayer, const PaintLayerPaintingInfo& paintingInfo, ShouldRespectOverflowClip respectOverflowClip, const LayoutSize& subpixelAccumulation) { bool needsRepaint = false; // Repaint subsequence if the layer is marked for needing repaint. if (paintLayer.needsRepaint()) needsRepaint = true; // Repaint if layer's clip changes. ClipRects& clipRects = paintLayer.clipper().paintingClipRects(paintingInfo.rootLayer, respectOverflowClip, subpixelAccumulation); ClipRects* previousClipRects = paintLayer.previousPaintingClipRects(); if (!needsRepaint && &clipRects != previousClipRects && (!previousClipRects || clipRects != *previousClipRects)) needsRepaint = true; paintLayer.setPreviousPaintingClipRects(clipRects); // Repaint if previously the layer might be clipped by paintDirtyRect and paintDirtyRect changes. if (!needsRepaint && paintLayer.previousPaintResult() == PaintLayerPainter::MayBeClippedByPaintDirtyRect && paintLayer.previousPaintDirtyRect() != paintingInfo.paintDirtyRect) needsRepaint = true; paintLayer.setPreviousPaintDirtyRect(paintingInfo.paintDirtyRect); // Repaint if scroll offset accumulation changes. if (!needsRepaint && paintingInfo.scrollOffsetAccumulation != paintLayer.previousScrollOffsetAccumulationForPainting()) needsRepaint = true; paintLayer.setPreviousScrollOffsetAccumulationForPainting(paintingInfo.scrollOffsetAccumulation); return needsRepaint; }
void PaintLayerClipper::clearClipRectsIncludingDescendants() { m_cache = nullptr; for (PaintLayer* layer = m_layoutObject.layer()->firstChild(); layer; layer = layer->nextSibling()) { layer->clipper().clearClipRectsIncludingDescendants(); } }
void PaintLayerClipper::clearClipRectsIncludingDescendants(ClipRectsCacheSlot cacheSlot) { if (m_cache) m_cache->clear(cacheSlot); for (PaintLayer* layer = m_layoutObject.layer()->firstChild(); layer; layer = layer->nextSibling()) { layer->clipper().clearClipRectsIncludingDescendants(cacheSlot); } }
void PaintLayerClipper::clearClipRectsIncludingDescendants() { if (m_geometryMapper) m_geometryMapper.reset(new GeometryMapper); m_layer.clearClipRectsCache(); for (PaintLayer* layer = m_layer.firstChild(); layer; layer = layer->nextSibling()) { layer->clipper().clearClipRectsIncludingDescendants(); } }
TEST_F(PaintLayerClipperTest, LocalClipRectFixedUnderTransform) { setBodyInnerHTML( "<div id='transformed'" " style='will-change: transform; width: 100px; height: 100px;" " overflow: hidden'>" " <div id='fixed' " " style='position: fixed; width: 100px; height: 100px;" " top: -50px'>" " </div>" "</div>"); LayoutRect infiniteRect(LayoutRect::infiniteIntRect()); PaintLayer* transformed = toLayoutBoxModelObject(getLayoutObjectByElementId("transformed")) ->layer(); PaintLayer* fixed = toLayoutBoxModelObject(getLayoutObjectByElementId("fixed"))->layer(); EXPECT_EQ(LayoutRect(0, 0, 100, 100), transformed->clipper().localClipRect(transformed)); EXPECT_EQ(LayoutRect(0, 50, 100, 100), fixed->clipper().localClipRect(transformed)); }
TEST_F(PaintLayerClipperTest, LayoutSVGRoot) { setBodyInnerHTML( "<!DOCTYPE html>" "<svg id=target width=200 height=300 style='position: relative'>" " <rect width=400 height=500 fill='blue'/>" "</svg>"); Element* target = document().getElementById("target"); PaintLayer* targetPaintLayer = toLayoutBoxModelObject(target->layoutObject())->layer(); ClipRectsContext context(document().layoutView()->layer(), UncachedClipRects); LayoutRect layerBounds; ClipRect backgroundRect, foregroundRect; targetPaintLayer->clipper().calculateRects( context, LayoutRect(LayoutRect::infiniteIntRect()), layerBounds, backgroundRect, foregroundRect); EXPECT_EQ(LayoutRect(LayoutRect::infiniteIntRect()), backgroundRect.rect()); EXPECT_EQ(LayoutRect(LayoutRect::infiniteIntRect()), foregroundRect.rect()); EXPECT_EQ(LayoutRect(8, 8, 200, 300), layerBounds); }
TEST_F(PaintLayerClipperTest, LayoutSVGRootChild) { setBodyInnerHTML( "<svg width=200 height=300 style='position: relative'>" " <foreignObject width=400 height=500>" " <div id=target xmlns='http://www.w3.org/1999/xhtml' " "style='position: relative'></div>" " </foreignObject>" "</svg>"); Element* target = document().getElementById("target"); PaintLayer* targetPaintLayer = toLayoutBoxModelObject(target->layoutObject())->layer(); ClipRectsContext context(document().layoutView()->layer(), UncachedClipRects); LayoutRect layerBounds; ClipRect backgroundRect, foregroundRect; targetPaintLayer->clipper().calculateRects( context, LayoutRect(LayoutRect::infiniteIntRect()), layerBounds, backgroundRect, foregroundRect); EXPECT_EQ(LayoutRect(8, 8, 200, 300), backgroundRect.rect()); EXPECT_EQ(LayoutRect(8, 8, 200, 300), foregroundRect.rect()); EXPECT_EQ(LayoutRect(8, 8, 400, 0), layerBounds); }
void PaintLayerClipper::calculateClipRects(const ClipRectsContext& context, ClipRects& clipRects) const { const LayoutBoxModelObject& layoutObject = *m_layer.layoutObject(); if (!m_layer.parent() && !RuntimeEnabledFeatures::rootLayerScrollingEnabled()) { // The root layer's clip rect is always infinite. clipRects.reset(LayoutRect(LayoutRect::infiniteIntRect())); return; } bool isClippingRoot = &m_layer == context.rootLayer; // For transformed layers, the root layer was shifted to be us, so there is no // need to examine the parent. We want to cache clip rects with us as the // root. PaintLayer* parentLayer = !isClippingRoot ? m_layer.parent() : nullptr; // Ensure that our parent's clip has been calculated so that we can examine // the values. if (parentLayer) { parentLayer->clipper().getOrCalculateClipRects(context, clipRects); } else { clipRects.reset(LayoutRect(LayoutRect::infiniteIntRect())); } adjustClipRectsForChildren(layoutObject, clipRects); if (shouldClipOverflow(context) || layoutObject.hasClip() || (layoutObject.isSVGRoot() && toLayoutSVGRoot(&layoutObject)->shouldApplyViewportClip())) { // This offset cannot use convertToLayerCoords, because sometimes our // rootLayer may be across some transformed layer boundary, for example, in // the PaintLayerCompositor overlapMap, where clipRects are needed in view // space. applyClipRects(context, layoutObject, roundedLayoutPoint(layoutObject.localToAncestorPoint( FloatPoint(), context.rootLayer->layoutObject())), clipRects); } }