Index: Source/core/rendering/RenderLayer.cpp |
diff --git a/Source/core/rendering/RenderLayer.cpp b/Source/core/rendering/RenderLayer.cpp |
index 5d5b5c227e0b7e53a8fbd031a0299635c782e065..25469e280e3919d1a8bce2a8ae9ae5e3d449315e 100644 |
--- a/Source/core/rendering/RenderLayer.cpp |
+++ b/Source/core/rendering/RenderLayer.cpp |
@@ -110,6 +110,7 @@ RenderLayer::RenderLayer(RenderLayerModelObject* renderer) |
, m_hasOutOfFlowPositionedDescendantDirty(true) |
, m_hasUnclippedDescendant(false) |
, m_isUnclippedDescendant(false) |
+ , m_hasComplexClippedAncestor(false) |
, m_isRootLayer(renderer->isRenderView()) |
, m_usedTransparency(false) |
, m_childLayerHasBlendMode(false) |
@@ -1398,6 +1399,7 @@ void RenderLayer::addChild(RenderLayer* child, RenderLayer* beforeChild) |
child->stackingNode()->dirtyStackingContainerZOrderLists(); |
} |
+ child->updateHasComplexClippedAncestor(); |
child->updateDescendantDependentFlags(); |
if (child->m_hasVisibleContent || child->m_hasVisibleDescendant) |
setAncestorChainHasVisibleDescendant(); |
@@ -2014,43 +2016,11 @@ void RenderLayer::paintLayerContents(GraphicsContext* context, const LayerPainti |
bool rootRelativeBoundsComputed = false; |
// Apply clip-path to context. |
- bool hasClipPath = false; |
- RenderStyle* style = renderer()->style(); |
- RenderSVGResourceClipper* resourceClipper = 0; |
+ bool needsContextRestore = false; |
+ Vector<RenderSVGResourceClipper*> resourceClippers; |
ClipperContext clipperContext; |
- if (renderer()->hasClipPath() && !context->paintingDisabled() && style) { |
- ASSERT(style->clipPath()); |
- if (style->clipPath()->type() == ClipPathOperation::SHAPE) { |
- hasClipPath = true; |
- context->save(); |
- ShapeClipPathOperation* clipPath = toShapeClipPathOperation(style->clipPath()); |
- |
- if (!rootRelativeBoundsComputed) { |
- rootRelativeBounds = calculateLayerBounds(paintingInfo.rootLayer, &offsetFromRoot, 0); |
- rootRelativeBoundsComputed = true; |
- } |
- |
- context->clipPath(clipPath->path(rootRelativeBounds), clipPath->windRule()); |
- } else if (style->clipPath()->type() == ClipPathOperation::REFERENCE) { |
- ReferenceClipPathOperation* referenceClipPathOperation = toReferenceClipPathOperation(style->clipPath()); |
- Document& document = renderer()->document(); |
- // FIXME: It doesn't work with forward or external SVG references (https://bugs.webkit.org/show_bug.cgi?id=90405) |
- Element* element = document.getElementById(referenceClipPathOperation->fragment()); |
- if (element && element->hasTagName(SVGNames::clipPathTag) && element->renderer()) { |
- if (!rootRelativeBoundsComputed) { |
- rootRelativeBounds = calculateLayerBounds(paintingInfo.rootLayer, &offsetFromRoot, 0); |
- rootRelativeBoundsComputed = true; |
- } |
- |
- resourceClipper = toRenderSVGResourceClipper(toRenderSVGResourceContainer(element->renderer())); |
- if (!resourceClipper->applyClippingToContext(renderer(), rootRelativeBounds, |
- paintingInfo.paintDirtyRect, context, clipperContext)) { |
- // No need to post-apply the clipper if this failed. |
- resourceClipper = 0; |
- } |
- } |
- } |
- } |
+ if (!context->paintingDisabled()) |
+ applyComplexClip(context, this, &clipperContext, paintingInfo, offsetFromRoot, &needsContextRestore, &rootRelativeBoundsComputed, &rootRelativeBounds, &resourceClippers); |
// Blending operations must be performed only with the nearest ancestor stacking context. |
// Note that there is no need to create a transparency layer if we're painting the root. |
@@ -2185,13 +2155,78 @@ void RenderLayer::paintLayerContents(GraphicsContext* context, const LayerPainti |
m_usedTransparency = false; |
} |
- if (resourceClipper) |
- resourceClipper->postApplyStatefulResource(renderer(), context, clipperContext); |
+ while (resourceClippers.size()) { |
+ resourceClippers.last()->postApplyStatefulResource(renderer(), context, clipperContext); |
+ resourceClippers.removeLast(); |
+ } |
- if (hasClipPath) |
+ if (needsContextRestore) |
context->restore(); |
} |
+void RenderLayer::applyComplexClip(GraphicsContext* context, RenderLayer* currentLayer, ClipperContext* clipperContext, const LayerPaintingInfo& paintingInfo, const LayoutPoint& offsetFromRoot, bool* needsContextRestore, bool* rootRelativeBoundsComputed, IntRect* rootRelativeBounds, Vector<RenderSVGResourceClipper*>* resourceClippers) |
+{ |
+ bool isRecursing = currentLayer != this; |
+ if (parent() && parent()->hasComplexClippedAncestor() && (hasCompositedLayerMapping() || isRecursing)) { |
+ bool parentRelativeBoundsComputed = false; |
+ IntRect parentRootRelativeBounds; |
+ LayoutPoint delta; |
+ convertToLayerCoords(parent(), delta); |
+ LayoutPoint parentOffsetFromRoot = toPoint(offsetFromRoot - delta); |
+ AffineTransform savedCTM; |
+ if (hasTransform()) { |
+ // Apply inverse transform to bring parent layer's clip into currentLayer's frame of reference. |
+ // Can't use save/restore because clip must be preserved. |
+ savedCTM = context->getCTM(); |
+ // FIXME: Flattening to an affine transform at each recursion level does not handle cascading |
+ // 3D transforms correctly when preserves3D() == true. |
+ context->concatCTM(transform()->inverse().toAffineTransform()); |
+ } |
+ parent()->applyComplexClip(context, currentLayer, clipperContext, paintingInfo, parentOffsetFromRoot, needsContextRestore, &parentRelativeBoundsComputed, &parentRootRelativeBounds, resourceClippers); |
+ if (hasTransform()) |
+ context->setCTM(savedCTM); |
+ } |
+ |
+ RenderStyle* style = renderer()->style(); |
+ if (style && inContainingBlockChain(currentLayer, this)) { |
+ if (renderer()->hasClipPath()) { |
+ ASSERT(style->clipPath()); |
+ if (style->clipPath()->type() == ClipPathOperation::SHAPE) { |
+ if (!*needsContextRestore) { |
+ // we have not yet saved the context in the current applyClipPath recursion chain |
+ *needsContextRestore = true; |
+ context->save(); |
+ } |
+ ShapeClipPathOperation* clipPath = static_cast<ShapeClipPathOperation*>(style->clipPath()); |
+ if (!*rootRelativeBoundsComputed) { |
+ *rootRelativeBounds = calculateLayerBounds(paintingInfo.rootLayer, &offsetFromRoot, 0); |
+ *rootRelativeBoundsComputed = true; |
+ } |
+ context->clipPath(clipPath->path(*rootRelativeBounds), clipPath->windRule()); |
+ } else if (style->clipPath()->type() == ClipPathOperation::REFERENCE) { |
+ ReferenceClipPathOperation* referenceClipPathOperation = static_cast<ReferenceClipPathOperation*>(style->clipPath()); |
+ Document& document = renderer()->document(); |
+ // FIXME: It doesn't work with forward or external SVG references (https://bugs.webkit.org/show_bug.cgi?id=90405) |
+ Element* element = document.getElementById(referenceClipPathOperation->fragment()); |
+ if (element && element->hasTagName(SVGNames::clipPathTag) && element->renderer()) { |
+ if (!*rootRelativeBoundsComputed) { |
+ *rootRelativeBounds = calculateLayerBounds(paintingInfo.rootLayer, &offsetFromRoot, 0); |
+ *rootRelativeBoundsComputed = true; |
+ } |
+ RenderSVGResourceClipper* resourceClipper = toRenderSVGResourceClipper(toRenderSVGResourceContainer(element->renderer())); |
+ if (resourceClipper->applyClippingToContext(renderer(), *rootRelativeBounds, paintingInfo.paintDirtyRect, context, *clipperContext)) |
+ resourceClippers->append(resourceClipper); |
+ } |
+ } |
+ } |
+ if (isRecursing && renderer()->hasOverflowClip() && style->hasBorderRadius()) { |
+ // Propagate border radius clipping from ancestors. |
+ // The border radius of currentLayer is skipped because it is already taken care of by clipToRect |
+ context->clipRoundedRect(style->getRoundedInnerBorderFor(LayoutRect(offsetFromRoot, size()))); |
+ } |
+ } |
+} |
+ |
void RenderLayer::paintLayerByApplyingTransform(GraphicsContext* context, const LayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags, const LayoutPoint& translationOffset) |
{ |
// This involves subtracting out the position of the layer in our current coordinate space, but preserving |
@@ -3716,6 +3751,9 @@ bool RenderLayer::backgroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect) |
if (m_stackingNode->zOrderListsDirty() || m_stackingNode->normalFlowListDirty()) |
return false; |
+ if (hasComplexClippedAncestor()) |
+ return false; |
+ |
// FIXME: We currently only check the immediate renderer, |
// which will miss many cases. |
if (renderer()->backgroundIsKnownToBeOpaqueInRect(localRect)) |
@@ -3896,6 +3934,22 @@ void RenderLayer::updateOutOfFlowPositioned(const RenderStyle* oldStyle) |
} |
} |
+void RenderLayer::updateHasComplexClippedAncestor() |
+{ |
+ bool hasComplexClip = renderer()->hasClipPath() |
+ || (renderer()->hasOverflowClip() && renderer()->style()->hasBorderRadius()) |
+ || (parent() && parent()->hasComplexClippedAncestor()); |
+ |
+ if (hasComplexClip == hasComplexClippedAncestor()) |
+ return; |
+ |
+ setHasComplexClippedAncestor(hasComplexClip); |
+ |
+ for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) { |
+ child->updateHasComplexClippedAncestor(); |
+ } |
+} |
+ |
static bool hasOrHadFilters(const RenderStyle* oldStyle, const RenderStyle* newStyle) |
{ |
ASSERT(newStyle); |
@@ -3984,6 +4038,8 @@ void RenderLayer::styleChanged(StyleDifference, const RenderStyle* oldStyle) |
bool didPaintWithFilters = false; |
+ updateHasComplexClippedAncestor(); |
+ |
if (paintsWithFilters()) |
didPaintWithFilters = true; |
updateFilters(oldStyle, renderer()->style()); |