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); }
AffineTransform SVGLayoutSupport::deprecatedCalculateTransformToLayer( const LayoutObject* layoutObject) { AffineTransform transform; while (layoutObject) { transform = layoutObject->localToSVGParentTransform() * transform; if (layoutObject->isSVGRoot()) break; layoutObject = layoutObject->parent(); } // Continue walking up the layer tree, accumulating CSS transforms. // FIXME: this queries layer compositing state - which is not // supported during layout. Hence, the result may not include all CSS // transforms. PaintLayer* layer = layoutObject ? layoutObject->enclosingLayer() : 0; while (layer && layer->isAllowedToQueryCompositingState()) { // We can stop at compositing layers, to match the backing resolution. // FIXME: should we be computing the transform to the nearest composited // layer, or the nearest composited layer that does not paint into its // ancestor? I think this is the nearest composited ancestor since we will // inherit its transforms in the composited layer tree. if (layer->compositingState() != NotComposited) break; if (TransformationMatrix* layerTransform = layer->transform()) transform = layerTransform->toAffineTransform() * transform; layer = layer->parent(); } return transform; }
PaintLayerStackingNode* PaintLayerStackingNode::ancestorStackingContextNode() const { for (PaintLayer* ancestor = layer()->parent(); ancestor; ancestor = ancestor->parent()) { PaintLayerStackingNode* stackingNode = ancestor->stackingNode(); if (stackingNode->isStackingContext()) return stackingNode; } return 0; }
void MouseRelatedEvent::computeRelativePosition() { Node* targetNode = target() ? target()->toNode() : nullptr; if (!targetNode) return; // Compute coordinates that are based on the target. m_layerLocation = m_pageLocation; m_offsetLocation = m_pageLocation; // Must have an updated layout tree for this math to work correctly. targetNode->document().updateLayoutIgnorePendingStylesheets(); // Adjust offsetLocation to be relative to the target's padding box. if (LayoutObject* r = targetNode->layoutObject()) { FloatPoint localPos = r->absoluteToLocal(FloatPoint(absoluteLocation()), UseTransforms); // Adding this here to address crbug.com/570666. Basically we'd like to // find the local coordinates relative to the padding box not the border box. if (r->isBoxModelObject()) { LayoutBoxModelObject* layoutBox = toLayoutBoxModelObject(r); localPos.move(-layoutBox->borderLeft(), -layoutBox->borderTop()); } m_offsetLocation = roundedLayoutPoint(localPos); float scaleFactor = 1 / pageZoomFactor(this); if (scaleFactor != 1.0f) m_offsetLocation.scale(scaleFactor, scaleFactor); } // Adjust layerLocation to be relative to the layer. // FIXME: event.layerX and event.layerY are poorly defined, // and probably don't always correspond to PaintLayer offsets. // https://bugs.webkit.org/show_bug.cgi?id=21868 Node* n = targetNode; while (n && !n->layoutObject()) n = n->parentNode(); if (n) { // FIXME: This logic is a wrong implementation of convertToLayerCoords. for (PaintLayer* layer = n->layoutObject()->enclosingLayer(); layer; layer = layer->parent()) m_layerLocation -= toLayoutSize(layer->location()); } m_hasCachedRelativePosition = true; }
void ScrollAnchor::clear() { LayoutObject* layoutObject = m_anchorObject ? m_anchorObject : scrollerLayoutBox(m_scroller); PaintLayer* layer = nullptr; if (LayoutObject* parent = layoutObject->parent()) layer = parent->enclosingLayer(); // Walk up the layer tree to clear any scroll anchors. while (layer) { if (PaintLayerScrollableArea* scrollableArea = layer->getScrollableArea()) { ScrollAnchor* anchor = scrollableArea->scrollAnchor(); DCHECK(anchor); anchor->clearSelf(); } layer = layer->parent(); } if (FrameView* view = layoutObject->frameView()) { ScrollAnchor* anchor = view->scrollAnchor(); DCHECK(anchor); anchor->clearSelf(); } }
void LayerClipRecorder::collectRoundedRectClips(PaintLayer& paintLayer, const PaintLayerPaintingInfo& localPaintingInfo, GraphicsContext& context, const LayoutPoint& fragmentOffset, PaintLayerFlags paintFlags, BorderRadiusClippingRule rule, Vector<FloatRoundedRect>& roundedRectClips) { // If the clip rect has been tainted by a border radius, then we have to walk up our layer chain applying the clips from // any layers with overflow. The condition for being able to apply these clips is that the overflow object be in our // containing block chain so we check that also. for (PaintLayer* layer = rule == IncludeSelfForBorderRadius ? &paintLayer : paintLayer.parent(); layer; layer = layer->parent()) { // Composited scrolling layers handle border-radius clip in the compositor via a mask layer. We do not // want to apply a border-radius clip to the layer contents itself, because that would require re-rastering // every frame to update the clip. We only want to make sure that the mask layer is properly clipped so // that it can in turn clip the scrolled contents in the compositor. if (layer->needsCompositedScrolling() && !(paintFlags & PaintLayerPaintingChildClippingMaskPhase)) break; if (layer->layoutObject()->hasOverflowClip() && layer->layoutObject()->style()->hasBorderRadius() && inContainingBlockChain(&paintLayer, layer)) { LayoutPoint delta(fragmentOffset); layer->convertToLayerCoords(localPaintingInfo.rootLayer, delta); roundedRectClips.append(layer->layoutObject()->style()->getRoundedInnerBorderFor(LayoutRect(delta, LayoutSize(layer->size())))); } if (layer == localPaintingInfo.rootLayer) break; } }
PaintLayerPainter::PaintResult PaintLayerPainter::paintChildren(unsigned childrenToVisit, GraphicsContext* context, const PaintLayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags) { PaintResult result = FullyPainted; if (!m_paintLayer.hasSelfPaintingLayerDescendant()) return result; #if ENABLE(ASSERT) LayerListMutationDetector mutationChecker(m_paintLayer.stackingNode()); #endif PaintLayerStackingNodeIterator iterator(*m_paintLayer.stackingNode(), childrenToVisit); PaintLayerStackingNode* child = iterator.next(); if (!child) return result; DisplayItem::Type subsequenceType; if (childrenToVisit == NegativeZOrderChildren) { subsequenceType = DisplayItem::SubsequenceNegativeZOrder; } else { ASSERT(childrenToVisit == (NormalFlowChildren | PositiveZOrderChildren)); subsequenceType = DisplayItem::SubsequenceNormalFlowAndPositiveZOrder; } Optional<SubsequenceRecorder> subsequenceRecorder; if (!paintingInfo.disableSubsequenceCache && !(paintingInfo.globalPaintFlags() & GlobalPaintFlattenCompositingLayers) && !(paintFlags & PaintLayerPaintingReflection) && !(paintFlags & PaintLayerPaintingRootBackgroundOnly)) { if (!m_paintLayer.needsRepaint() && paintingInfo.scrollOffsetAccumulation == m_paintLayer.previousScrollOffsetAccumulationForPainting() && SubsequenceRecorder::useCachedSubsequenceIfPossible(*context, m_paintLayer, subsequenceType)) return result; subsequenceRecorder.emplace(*context, m_paintLayer, subsequenceType); } IntSize scrollOffsetAccumulationForChildren = paintingInfo.scrollOffsetAccumulation; if (m_paintLayer.layoutObject()->hasOverflowClip()) scrollOffsetAccumulationForChildren += m_paintLayer.layoutBox()->scrolledContentOffset(); bool disableChildSubsequenceCache = !RuntimeEnabledFeatures::slimmingPaintV2Enabled() && (m_paintLayer.layoutObject()->hasOverflowClip() || m_paintLayer.layoutObject()->hasClip()); for (; child; child = iterator.next()) { PaintLayerPainter childPainter(*child->layer()); // If this Layer should paint into its own backing or a grouped backing, that will be done via CompositedLayerMapping::paintContents() // and CompositedLayerMapping::doPaintTask(). if (!childPainter.shouldPaintLayerInSoftwareMode(paintingInfo.globalPaintFlags(), paintFlags)) continue; PaintLayerPaintingInfo childPaintingInfo = paintingInfo; childPaintingInfo.disableSubsequenceCache = disableChildSubsequenceCache; childPaintingInfo.scrollOffsetAccumulation = scrollOffsetAccumulationForChildren; // Rare case: accumulate scroll offset of non-stacking-context ancestors up to m_paintLayer. for (PaintLayer* parentLayer = child->layer()->parent(); parentLayer != &m_paintLayer; parentLayer = parentLayer->parent()) { if (parentLayer->layoutObject()->hasOverflowClip()) childPaintingInfo.scrollOffsetAccumulation += parentLayer->layoutBox()->scrolledContentOffset(); } if (childPainter.paintLayer(context, childPaintingInfo, paintFlags) == MaybeNotFullyPainted) result = MaybeNotFullyPainted; } // Set subsequence not cacheable if the bounding box of this layer and descendants is not fully contained // by paintRect, because later paintRect changes may expose new contents which will need repainting. if (result == MaybeNotFullyPainted && subsequenceRecorder) subsequenceRecorder->setUncacheable(); return result; }
PaintLayerPainter::PaintResult PaintLayerPainter::paintChildren(unsigned childrenToVisit, GraphicsContext& context, const PaintLayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags) { PaintResult result = FullyPainted; if (!m_paintLayer.hasSelfPaintingLayerDescendant()) return result; #if ENABLE(ASSERT) LayerListMutationDetector mutationChecker(m_paintLayer.stackingNode()); #endif PaintLayerStackingNodeIterator iterator(*m_paintLayer.stackingNode(), childrenToVisit); PaintLayerStackingNode* child = iterator.next(); if (!child) return result; IntSize scrollOffsetAccumulationForChildren = paintingInfo.scrollOffsetAccumulation; if (m_paintLayer.layoutObject()->hasOverflowClip()) scrollOffsetAccumulationForChildren += m_paintLayer.layoutBox()->scrolledContentOffset(); for (; child; child = iterator.next()) { PaintLayerPainter childPainter(*child->layer()); // If this Layer should paint into its own backing or a grouped backing, that will be done via CompositedLayerMapping::paintContents() // and CompositedLayerMapping::doPaintTask(). if (!childPainter.shouldPaintLayerInSoftwareMode(paintingInfo.globalPaintFlags(), paintFlags)) continue; PaintLayerPaintingInfo childPaintingInfo = paintingInfo; childPaintingInfo.scrollOffsetAccumulation = scrollOffsetAccumulationForChildren; // Rare case: accumulate scroll offset of non-stacking-context ancestors up to m_paintLayer. for (PaintLayer* parentLayer = child->layer()->parent(); parentLayer != &m_paintLayer; parentLayer = parentLayer->parent()) { if (parentLayer->layoutObject()->hasOverflowClip()) childPaintingInfo.scrollOffsetAccumulation += parentLayer->layoutBox()->scrolledContentOffset(); } if (childPainter.paintLayer(context, childPaintingInfo, paintFlags) == MayBeClippedByPaintDirtyRect) result = MayBeClippedByPaintDirtyRect; } return result; }