diff options
Diffstat (limited to 'chromium/third_party/WebKit/Source/core/rendering/RenderLayerClipper.cpp')
-rw-r--r-- | chromium/third_party/WebKit/Source/core/rendering/RenderLayerClipper.cpp | 244 |
1 files changed, 220 insertions, 24 deletions
diff --git a/chromium/third_party/WebKit/Source/core/rendering/RenderLayerClipper.cpp b/chromium/third_party/WebKit/Source/core/rendering/RenderLayerClipper.cpp index f14aa06ee27..786a17f7440 100644 --- a/chromium/third_party/WebKit/Source/core/rendering/RenderLayerClipper.cpp +++ b/chromium/third_party/WebKit/Source/core/rendering/RenderLayerClipper.cpp @@ -53,9 +53,12 @@ void RenderLayerClipper::updateClipRects(const ClipRectsContext& clipRectsContex { ClipRectsType clipRectsType = clipRectsContext.clipRectsType; ASSERT(clipRectsType < NumCachedClipRectsTypes); - if (m_clipRectsCache && m_clipRectsCache->getClipRects(clipRectsType, clipRectsContext.respectOverflowClip)) { - // FIXME: these asserts trigger for squashing. Need to update this code to support squashing as appropriate. - ASSERT(clipRectsContext.rootLayer == m_clipRectsCache->m_clipRectsRoot[clipRectsType]); + if (m_clipRectsCache + && clipRectsContext.rootLayer == m_clipRectsCache->clipRectsRoot(clipRectsType) + && m_clipRectsCache->getClipRects(clipRectsType, clipRectsContext.respectOverflowClip)) { + // FIXME: We used to ASSERT that we always got a consistent root layer. + // We should add a test that has an inconsistent root. See + // http://crbug.com/366118 for an example. ASSERT(m_clipRectsCache->m_scrollbarRelevancy[clipRectsType] == clipRectsContext.overlayScrollbarSizeRelevancy); #ifdef CHECK_CACHED_CLIP_RECTS @@ -71,7 +74,7 @@ void RenderLayerClipper::updateClipRects(const ClipRectsContext& clipRectsContex // 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. - RenderLayer* parentLayer = clipRectsContext.rootLayer != m_renderer->layer() ? m_renderer->layer()->parent() : 0; + RenderLayer* parentLayer = !isClippingRootForContext(clipRectsContext) ? m_renderer.layer()->parent() : 0; if (parentLayer) parentLayer->clipper().updateClipRects(clipRectsContext); @@ -82,12 +85,11 @@ void RenderLayerClipper::updateClipRects(const ClipRectsContext& clipRectsContex m_clipRectsCache = adoptPtr(new ClipRectsCache); if (parentLayer && parentLayer->clipper().clipRects(clipRectsContext) && clipRects == *parentLayer->clipper().clipRects(clipRectsContext)) - m_clipRectsCache->setClipRects(clipRectsType, clipRectsContext.respectOverflowClip, parentLayer->clipper().clipRects(clipRectsContext)); + m_clipRectsCache->setClipRects(clipRectsType, clipRectsContext.respectOverflowClip, parentLayer->clipper().clipRects(clipRectsContext), clipRectsContext.rootLayer); else - m_clipRectsCache->setClipRects(clipRectsType, clipRectsContext.respectOverflowClip, ClipRects::create(clipRects)); + m_clipRectsCache->setClipRects(clipRectsType, clipRectsContext.respectOverflowClip, ClipRects::create(clipRects), clipRectsContext.rootLayer); #ifndef NDEBUG - m_clipRectsCache->m_clipRectsRoot[clipRectsType] = clipRectsContext.rootLayer; m_clipRectsCache->m_scrollbarRelevancy[clipRectsType] = clipRectsContext.overlayScrollbarSizeRelevancy; #endif } @@ -100,7 +102,7 @@ void RenderLayerClipper::clearClipRectsIncludingDescendants(ClipRectsType typeTo clearClipRects(typeToClear); - for (RenderLayer* layer = m_renderer->layer()->firstChild(); layer; layer = layer->nextSibling()) + for (RenderLayer* layer = m_renderer.layer()->firstChild(); layer; layer = layer->nextSibling()) layer->clipper().clearClipRectsIncludingDescendants(typeToClear); } @@ -108,17 +110,118 @@ void RenderLayerClipper::clearClipRects(ClipRectsType typeToClear) { if (typeToClear == AllClipRectTypes) { m_clipRectsCache = nullptr; + m_compositingClipRectsDirty = false; } else { + if (typeToClear == CompositingClipRects) + m_compositingClipRectsDirty = false; + ASSERT(typeToClear < NumCachedClipRectsTypes); RefPtr<ClipRects> dummy; - m_clipRectsCache->setClipRects(typeToClear, RespectOverflowClip, dummy); - m_clipRectsCache->setClipRects(typeToClear, IgnoreOverflowClip, dummy); + m_clipRectsCache->setClipRects(typeToClear, RespectOverflowClip, dummy, 0); + m_clipRectsCache->setClipRects(typeToClear, IgnoreOverflowClip, dummy, 0); + } +} + +LayoutRect RenderLayerClipper::childrenClipRect() const +{ + // FIXME: border-radius not accounted for. + // FIXME: Regions not accounted for. + RenderView* renderView = m_renderer.view(); + RenderLayer* clippingRootLayer = clippingRootForPainting(); + LayoutRect layerBounds; + ClipRect backgroundRect, foregroundRect, outlineRect; + ClipRectsContext clipRectsContext(clippingRootLayer, TemporaryClipRects); + // Need to use temporary clip rects, because the value of 'dontClipToOverflow' may be different from the painting path (<rdar://problem/11844909>). + calculateRects(clipRectsContext, renderView->unscaledDocumentRect(), layerBounds, backgroundRect, foregroundRect, outlineRect); + return clippingRootLayer->renderer()->localToAbsoluteQuad(FloatQuad(foregroundRect.rect())).enclosingBoundingBox(); +} + +LayoutRect RenderLayerClipper::localClipRect() const +{ + // FIXME: border-radius not accounted for. + RenderLayer* clippingRootLayer = clippingRootForPainting(); + LayoutRect layerBounds; + ClipRect backgroundRect, foregroundRect, outlineRect; + ClipRectsContext clipRectsContext(clippingRootLayer, PaintingClipRects); + calculateRects(clipRectsContext, PaintInfo::infiniteRect(), layerBounds, backgroundRect, foregroundRect, outlineRect); + + LayoutRect clipRect = backgroundRect.rect(); + if (clipRect == PaintInfo::infiniteRect()) + return clipRect; + + LayoutPoint clippingRootOffset; + m_renderer.layer()->convertToLayerCoords(clippingRootLayer, clippingRootOffset); + clipRect.moveBy(-clippingRootOffset); + + return clipRect; +} + +void RenderLayerClipper::calculateRects(const ClipRectsContext& clipRectsContext, const LayoutRect& paintDirtyRect, LayoutRect& layerBounds, + ClipRect& backgroundRect, ClipRect& foregroundRect, ClipRect& outlineRect, const LayoutPoint* offsetFromRoot) const +{ + bool isClippingRoot = isClippingRootForContext(clipRectsContext); + + if (!isClippingRoot && m_renderer.layer()->parent()) { + backgroundRect = backgroundClipRect(clipRectsContext); + backgroundRect.move(roundedIntSize(clipRectsContext.subPixelAccumulation)); + backgroundRect.intersect(paintDirtyRect); + } else { + backgroundRect = paintDirtyRect; + } + + foregroundRect = backgroundRect; + outlineRect = backgroundRect; + + LayoutPoint offset; + if (offsetFromRoot) + offset = *offsetFromRoot; + else + m_renderer.layer()->convertToLayerCoords(clipRectsContext.rootLayer, offset); + layerBounds = LayoutRect(offset, m_renderer.layer()->size()); + + // Update the clip rects that will be passed to child layers. + if (m_renderer.hasOverflowClip()) { + // This layer establishes a clip of some kind. + if (!isClippingRoot || clipRectsContext.respectOverflowClip == RespectOverflowClip) { + foregroundRect.intersect(toRenderBox(m_renderer).overflowClipRect(offset, clipRectsContext.overlayScrollbarSizeRelevancy)); + if (m_renderer.style()->hasBorderRadius()) + foregroundRect.setHasRadius(true); + } + + // If we establish an overflow clip at all, then go ahead and make sure our background + // rect is intersected with our layer's bounds including our visual overflow, + // since any visual overflow like box-shadow or border-outset is not clipped by overflow:auto/hidden. + if (toRenderBox(m_renderer).hasVisualOverflow()) { + // FIXME: Perhaps we should be propagating the borderbox as the clip rect for children, even though + // we may need to inflate our clip specifically for shadows or outsets. + // FIXME: Does not do the right thing with CSS regions yet, since we don't yet factor in the + // individual region boxes as overflow. + LayoutRect layerBoundsWithVisualOverflow = toRenderBox(m_renderer).visualOverflowRect(); + toRenderBox(m_renderer).flipForWritingMode(layerBoundsWithVisualOverflow); // Layers are in physical coordinates, so the overflow has to be flipped. + layerBoundsWithVisualOverflow.moveBy(offset); + if (!isClippingRoot || clipRectsContext.respectOverflowClip == RespectOverflowClip) + backgroundRect.intersect(layerBoundsWithVisualOverflow); + } else { + LayoutRect bounds = toRenderBox(m_renderer).borderBoxRect(); + bounds.moveBy(offset); + if (!isClippingRoot || clipRectsContext.respectOverflowClip == RespectOverflowClip) + backgroundRect.intersect(bounds); + } + } + + // CSS clip (different than clipping due to overflow) can clip to any box, even if it falls outside of the border box. + if (m_renderer.hasClip()) { + // Clip applies to *us* as well, so go ahead and update the damageRect. + LayoutRect newPosClip = toRenderBox(m_renderer).clipRect(offset); + backgroundRect.intersect(newPosClip); + foregroundRect.intersect(newPosClip); + outlineRect.intersect(newPosClip); } } void RenderLayerClipper::calculateClipRects(const ClipRectsContext& clipRectsContext, ClipRects& clipRects) const { - if (!m_renderer->layer()->parent()) { + if (!m_renderer.layer()->parent()) { // The root layer's clip rect is always infinite. clipRects.reset(PaintInfo::infiniteRect()); return; @@ -127,9 +230,11 @@ void RenderLayerClipper::calculateClipRects(const ClipRectsContext& clipRectsCon ClipRectsType clipRectsType = clipRectsContext.clipRectsType; bool useCached = clipRectsType != TemporaryClipRects; + bool isClippingRoot = isClippingRootForContext(clipRectsContext); + // 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. - RenderLayer* parentLayer = clipRectsContext.rootLayer != m_renderer->layer() ? m_renderer->layer()->parent() : 0; + RenderLayer* parentLayer = !isClippingRoot ? m_renderer.layer()->parent() : 0; // Ensure that our parent's clip has been calculated so that we can examine the values. if (parentLayer) { @@ -146,41 +251,41 @@ void RenderLayerClipper::calculateClipRects(const ClipRectsContext& clipRectsCon // A fixed object is essentially the root of its containing block hierarchy, so when // we encounter such an object, we reset our clip rects to the fixedClipRect. - if (m_renderer->style()->position() == FixedPosition) { + if (m_renderer.style()->position() == FixedPosition) { clipRects.setPosClipRect(clipRects.fixedClipRect()); clipRects.setOverflowClipRect(clipRects.fixedClipRect()); clipRects.setFixed(true); - } else if (m_renderer->style()->hasInFlowPosition()) { + } else if (m_renderer.style()->hasInFlowPosition()) { clipRects.setPosClipRect(clipRects.overflowClipRect()); - } else if (m_renderer->style()->position() == AbsolutePosition) { + } else if (m_renderer.style()->position() == AbsolutePosition) { clipRects.setOverflowClipRect(clipRects.posClipRect()); } // Update the clip rects that will be passed to child layers. - if ((m_renderer->hasOverflowClip() && (clipRectsContext.respectOverflowClip == RespectOverflowClip || m_renderer->layer() != clipRectsContext.rootLayer)) || m_renderer->hasClip()) { + if ((m_renderer.hasOverflowClip() && (clipRectsContext.respectOverflowClip == RespectOverflowClip || !isClippingRoot)) || m_renderer.hasClip()) { // This layer establishes a clip of some kind. // This offset cannot use convertToLayerCoords, because sometimes our rootLayer may be across // some transformed layer boundary, for example, in the RenderLayerCompositor overlapMap, where // clipRects are needed in view space. LayoutPoint offset; - offset = roundedLayoutPoint(m_renderer->localToContainerPoint(FloatPoint(), clipRectsContext.rootLayer->renderer())); - RenderView* view = m_renderer->view(); + offset = roundedLayoutPoint(m_renderer.localToContainerPoint(FloatPoint(), clipRectsContext.rootLayer->renderer())); + RenderView* view = m_renderer.view(); ASSERT(view); if (view && clipRects.fixed() && clipRectsContext.rootLayer->renderer() == view) { offset -= view->frameView()->scrollOffsetForFixedPosition(); } - if (m_renderer->hasOverflowClip()) { - ClipRect newOverflowClip = toRenderBox(m_renderer)->overflowClipRect(offset, clipRectsContext.region, clipRectsContext.overlayScrollbarSizeRelevancy); - if (m_renderer->style()->hasBorderRadius()) + if (m_renderer.hasOverflowClip()) { + ClipRect newOverflowClip = toRenderBox(m_renderer).overflowClipRect(offset, clipRectsContext.overlayScrollbarSizeRelevancy); + if (m_renderer.style()->hasBorderRadius()) newOverflowClip.setHasRadius(true); clipRects.setOverflowClipRect(intersection(newOverflowClip, clipRects.overflowClipRect())); - if (m_renderer->isPositioned()) + if (m_renderer.isPositioned()) clipRects.setPosClipRect(intersection(newOverflowClip, clipRects.posClipRect())); } - if (m_renderer->hasClip()) { - LayoutRect newPosClip = toRenderBox(m_renderer)->clipRect(offset, clipRectsContext.region); + if (m_renderer.hasClip()) { + LayoutRect newPosClip = toRenderBox(m_renderer).clipRect(offset); clipRects.setPosClipRect(intersection(newPosClip, clipRects.posClipRect())); clipRects.setOverflowClipRect(intersection(newPosClip, clipRects.overflowClipRect())); clipRects.setFixedClipRect(intersection(newPosClip, clipRects.fixedClipRect())); @@ -188,4 +293,95 @@ void RenderLayerClipper::calculateClipRects(const ClipRectsContext& clipRectsCon } } +static inline ClipRect backgroundClipRectForPosition(const ClipRects& parentRects, EPosition position) +{ + if (position == FixedPosition) + return parentRects.fixedClipRect(); + + if (position == AbsolutePosition) + return parentRects.posClipRect(); + + return parentRects.overflowClipRect(); +} + +void RenderLayerClipper::setCompositingClipRectsDirty() +{ + m_compositingClipRectsDirty = true; +} + +ClipRect RenderLayerClipper::backgroundClipRect(const ClipRectsContext& clipRectsContext) const +{ + ASSERT(m_renderer.layer()->parent()); + + if (clipRectsContext.clipRectsType == CompositingClipRects) + const_cast<RenderLayerClipper*>(this)->clearClipRectsIncludingDescendants(CompositingClipRects); + + ClipRects parentRects; + + // If we cross into a different pagination context, then we can't rely on the cache. + // Just switch over to using TemporaryClipRects. + if (clipRectsContext.clipRectsType != TemporaryClipRects && m_renderer.layer()->parent()->enclosingPaginationLayer() != m_renderer.layer()->enclosingPaginationLayer()) { + ClipRectsContext tempContext(clipRectsContext); + tempContext.clipRectsType = TemporaryClipRects; + parentClipRects(tempContext, parentRects); + } else { + parentClipRects(clipRectsContext, parentRects); + } + + ClipRect backgroundClipRect = backgroundClipRectForPosition(parentRects, m_renderer.style()->position()); + RenderView* view = m_renderer.view(); + ASSERT(view); + + // Note: infinite clipRects should not be scrolled here, otherwise they will accidentally no longer be considered infinite. + if (parentRects.fixed() && clipRectsContext.rootLayer->renderer() == view && backgroundClipRect != PaintInfo::infiniteRect()) + backgroundClipRect.move(view->frameView()->scrollOffsetForFixedPosition()); + + return backgroundClipRect; +} + +bool RenderLayerClipper::isClippingRootForContext(const ClipRectsContext& clipRectsContext) const +{ + return clipRectsContext.rootLayer == m_renderer.layer(); +} + +void RenderLayerClipper::parentClipRects(const ClipRectsContext& clipRectsContext, ClipRects& clipRects) const +{ + // The root is not clipped. + if (isClippingRootForContext(clipRectsContext)) { + clipRects.reset(PaintInfo::infiniteRect()); + return; + } + + ASSERT(m_renderer.layer()->parent()); + + RenderLayerClipper& parentClipper = m_renderer.layer()->parent()->clipper(); + if (clipRectsContext.clipRectsType == TemporaryClipRects) { + parentClipper.calculateClipRects(clipRectsContext, clipRects); + return; + } + + parentClipper.updateClipRects(clipRectsContext); + clipRects = *parentClipper.clipRects(clipRectsContext); +} + +RenderLayer* RenderLayerClipper::clippingRootForPainting() const +{ + if (m_renderer.hasCompositedLayerMapping() || m_renderer.groupedMapping()) + return const_cast<RenderLayer*>(m_renderer.layer()); + + const RenderLayer* current = m_renderer.layer(); + while (current) { + if (current->isRootLayer()) + return const_cast<RenderLayer*>(current); + + current = current->compositingContainer(); + ASSERT(current); + if (current->transform() || (current->compositingState() == PaintsIntoOwnBacking) || current->groupedMapping()) + return const_cast<RenderLayer*>(current); + } + + ASSERT_NOT_REACHED(); + return 0; +} + } // namespace WebCore |