| /* |
| * Copyright (C) 2009, 2010, 2011 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "core/layout/compositing/CompositedLayerMapping.h" |
| |
| #include <memory> |
| |
| #include "core/HTMLNames.h" |
| #include "core/dom/DOMNodeIds.h" |
| #include "core/frame/FrameHost.h" |
| #include "core/frame/FrameView.h" |
| #include "core/frame/RemoteFrame.h" |
| #include "core/frame/Settings.h" |
| #include "core/frame/VisualViewport.h" |
| #include "core/html/HTMLCanvasElement.h" |
| #include "core/html/HTMLIFrameElement.h" |
| #include "core/html/HTMLMediaElement.h" |
| #include "core/html/HTMLVideoElement.h" |
| #include "core/html/canvas/CanvasRenderingContext.h" |
| #include "core/inspector/InspectorInstrumentation.h" |
| #include "core/layout/LayoutEmbeddedObject.h" |
| #include "core/layout/LayoutHTMLCanvas.h" |
| #include "core/layout/LayoutImage.h" |
| #include "core/layout/LayoutPart.h" |
| #include "core/layout/LayoutVideo.h" |
| #include "core/layout/LayoutView.h" |
| #include "core/layout/api/LayoutAPIShim.h" |
| #include "core/layout/api/LayoutPartItem.h" |
| #include "core/layout/compositing/PaintLayerCompositor.h" |
| #include "core/loader/resource/ImageResourceContent.h" |
| #include "core/page/ChromeClient.h" |
| #include "core/page/Page.h" |
| #include "core/page/scrolling/ScrollingCoordinator.h" |
| #include "core/page/scrolling/StickyPositionScrollingConstraints.h" |
| #include "core/page/scrolling/TopDocumentRootScrollerController.h" |
| #include "core/paint/ObjectPaintInvalidator.h" |
| #include "core/paint/PaintInfo.h" |
| #include "core/paint/PaintLayerPainter.h" |
| #include "core/paint/PaintLayerStackingNodeIterator.h" |
| #include "core/paint/ScrollableAreaPainter.h" |
| #include "core/paint/TransformRecorder.h" |
| #include "core/plugins/PluginView.h" |
| #include "platform/LengthFunctions.h" |
| #include "platform/RuntimeEnabledFeatures.h" |
| #include "platform/fonts/FontCache.h" |
| #include "platform/geometry/TransformState.h" |
| #include "platform/graphics/BitmapImage.h" |
| #include "platform/graphics/CompositorFilterOperations.h" |
| #include "platform/graphics/CompositorMutableProperties.h" |
| #include "platform/graphics/GraphicsContext.h" |
| #include "platform/graphics/paint/ClipDisplayItem.h" |
| #include "platform/graphics/paint/CullRect.h" |
| #include "platform/graphics/paint/DrawingRecorder.h" |
| #include "platform/graphics/paint/PaintController.h" |
| #include "platform/graphics/paint/PaintRecordBuilder.h" |
| #include "platform/graphics/paint/TransformDisplayItem.h" |
| #include "public/platform/WebLayerStickyPositionConstraint.h" |
| #include "wtf/CurrentTime.h" |
| #include "wtf/text/StringBuilder.h" |
| |
| namespace blink { |
| |
| using namespace HTMLNames; |
| |
| static IntRect clipBox(LayoutBox& layoutObject); |
| |
| static IntRect contentsRect(const LayoutObject& layoutObject) { |
| if (!layoutObject.isBox()) |
| return IntRect(); |
| if (layoutObject.isCanvas()) { |
| return pixelSnappedIntRect( |
| toLayoutHTMLCanvas(layoutObject).replacedContentRect()); |
| } |
| if (layoutObject.isVideo()) { |
| return pixelSnappedIntRect( |
| toLayoutVideo(layoutObject).replacedContentRect()); |
| } |
| |
| return pixelSnappedIntRect(toLayoutBox(layoutObject).contentBoxRect()); |
| } |
| |
| static IntRect backgroundRect(const LayoutObject& layoutObject) { |
| if (!layoutObject.isBox()) |
| return IntRect(); |
| |
| LayoutRect rect; |
| const LayoutBox& box = toLayoutBox(layoutObject); |
| return pixelSnappedIntRect(box.backgroundRect(BackgroundClipRect)); |
| } |
| |
| static inline bool isAcceleratedCanvas(const LayoutObject& layoutObject) { |
| if (layoutObject.isCanvas()) { |
| HTMLCanvasElement* canvas = toHTMLCanvasElement(layoutObject.node()); |
| if (CanvasRenderingContext* context = canvas->renderingContext()) |
| return context->isAccelerated(); |
| } |
| return false; |
| } |
| |
| static inline bool isPlaceholderCanvas(const LayoutObject& layoutObject) { |
| if (layoutObject.isCanvas()) { |
| HTMLCanvasElement* canvas = toHTMLCanvasElement(layoutObject.node()); |
| return canvas->surfaceLayerBridge(); |
| } |
| return false; |
| } |
| |
| static bool hasBoxDecorationsOrBackgroundImage(const ComputedStyle& style) { |
| return style.hasBoxDecorations() || style.hasBackgroundImage(); |
| } |
| |
| static bool contentLayerSupportsDirectBackgroundComposition( |
| const LayoutObject& layoutObject) { |
| // No support for decorations - border, border-radius or outline. |
| // Only simple background - solid color or transparent. |
| if (hasBoxDecorationsOrBackgroundImage(layoutObject.styleRef())) |
| return false; |
| |
| // If there is no background, there is nothing to support. |
| if (!layoutObject.style()->hasBackground()) |
| return true; |
| |
| // Simple background that is contained within the contents rect. |
| return contentsRect(layoutObject).contains(backgroundRect(layoutObject)); |
| } |
| |
| static WebLayer* platformLayerForPlugin(LayoutObject& layoutObject) { |
| if (!layoutObject.isEmbeddedObject()) |
| return nullptr; |
| FrameViewBase* frameViewBase = |
| toLayoutEmbeddedObject(layoutObject).frameViewBase(); |
| if (!frameViewBase || !frameViewBase->isPluginView()) |
| return nullptr; |
| return toPluginView(frameViewBase)->platformLayer(); |
| } |
| |
| static inline bool isAcceleratedContents(LayoutObject& layoutObject) { |
| return isAcceleratedCanvas(layoutObject) || |
| (layoutObject.isEmbeddedObject() && |
| toLayoutEmbeddedObject(layoutObject) |
| .requiresAcceleratedCompositing()) || |
| layoutObject.isVideo(); |
| } |
| |
| // Get the scrolling coordinator in a way that works inside |
| // CompositedLayerMapping's destructor. |
| static ScrollingCoordinator* scrollingCoordinatorFromLayer(PaintLayer& layer) { |
| Page* page = layer.layoutObject().frame()->page(); |
| return (!page) ? nullptr : page->scrollingCoordinator(); |
| } |
| |
| CompositedLayerMapping::CompositedLayerMapping(PaintLayer& layer) |
| : m_owningLayer(layer), |
| m_contentOffsetInCompositingLayerDirty(false), |
| m_pendingUpdateScope(GraphicsLayerUpdateNone), |
| m_isMainFrameLayoutViewLayer(false), |
| m_backgroundLayerPaintsFixedRootBackground(false), |
| m_scrollingContentsAreEmpty(false), |
| m_backgroundPaintsOntoScrollingContentsLayer(false), |
| m_backgroundPaintsOntoGraphicsLayer(false), |
| m_drawsBackgroundOntoContentLayer(false) { |
| if (layer.isRootLayer() && layoutObject().frame()->isMainFrame()) |
| m_isMainFrameLayoutViewLayer = true; |
| |
| createPrimaryGraphicsLayer(); |
| } |
| |
| CompositedLayerMapping::~CompositedLayerMapping() { |
| // Hits in compositing/squashing/squash-onto-nephew.html. |
| DisableCompositingQueryAsserts disabler; |
| |
| // Do not leave the destroyed pointer dangling on any Layers that painted to |
| // this mapping's squashing layer. |
| for (size_t i = 0; i < m_squashedLayers.size(); ++i) { |
| PaintLayer* oldSquashedLayer = m_squashedLayers[i].paintLayer; |
| // Assert on incorrect mappings between layers and groups |
| ASSERT(oldSquashedLayer->groupedMapping() == this); |
| if (oldSquashedLayer->groupedMapping() == this) { |
| oldSquashedLayer->setGroupedMapping( |
| 0, PaintLayer::DoNotInvalidateLayerAndRemoveFromMapping); |
| oldSquashedLayer->setLostGroupedMapping(true); |
| } |
| } |
| |
| updateClippingLayers(false, false, false); |
| updateOverflowControlsLayers(false, false, false, false); |
| updateChildTransformLayer(false); |
| updateForegroundLayer(false); |
| updateBackgroundLayer(false); |
| updateMaskLayer(false); |
| updateChildClippingMaskLayer(false); |
| updateScrollingLayers(false); |
| updateSquashingLayers(false); |
| destroyGraphicsLayers(); |
| } |
| |
| std::unique_ptr<GraphicsLayer> CompositedLayerMapping::createGraphicsLayer( |
| CompositingReasons reasons, |
| SquashingDisallowedReasons squashingDisallowedReasons) { |
| std::unique_ptr<GraphicsLayer> graphicsLayer = GraphicsLayer::create(this); |
| |
| graphicsLayer->setCompositingReasons(reasons); |
| graphicsLayer->setSquashingDisallowedReasons(squashingDisallowedReasons); |
| if (Node* owningNode = m_owningLayer.layoutObject().node()) |
| graphicsLayer->setOwnerNodeId(DOMNodeIds::idForNode(owningNode)); |
| |
| return graphicsLayer; |
| } |
| |
| void CompositedLayerMapping::createPrimaryGraphicsLayer() { |
| m_graphicsLayer = |
| createGraphicsLayer(m_owningLayer.getCompositingReasons(), |
| m_owningLayer.getSquashingDisallowedReasons()); |
| |
| updateOpacity(layoutObject().styleRef()); |
| updateTransform(layoutObject().styleRef()); |
| updateFilters(layoutObject().styleRef()); |
| updateBackdropFilters(layoutObject().styleRef()); |
| updateLayerBlendMode(layoutObject().styleRef()); |
| updateIsRootForIsolatedGroup(); |
| } |
| |
| void CompositedLayerMapping::destroyGraphicsLayers() { |
| if (m_graphicsLayer) |
| m_graphicsLayer->removeFromParent(); |
| |
| m_ancestorClippingLayer = nullptr; |
| m_ancestorClippingMaskLayer = nullptr; |
| m_graphicsLayer = nullptr; |
| m_foregroundLayer = nullptr; |
| m_backgroundLayer = nullptr; |
| m_childContainmentLayer = nullptr; |
| m_childTransformLayer = nullptr; |
| m_maskLayer = nullptr; |
| m_childClippingMaskLayer = nullptr; |
| |
| m_scrollingLayer = nullptr; |
| m_scrollingContentsLayer = nullptr; |
| } |
| |
| void CompositedLayerMapping::updateOpacity(const ComputedStyle& style) { |
| m_graphicsLayer->setOpacity(compositingOpacity(style.opacity())); |
| } |
| |
| void CompositedLayerMapping::updateTransform(const ComputedStyle& style) { |
| // FIXME: This could use m_owningLayer.transform(), but that currently has |
| // transform-origin baked into it, and we don't want that. |
| TransformationMatrix t; |
| if (m_owningLayer.hasTransformRelatedProperty()) { |
| style.applyTransform( |
| t, LayoutSize(toLayoutBox(layoutObject()).pixelSnappedSize()), |
| ComputedStyle::ExcludeTransformOrigin, ComputedStyle::IncludeMotionPath, |
| ComputedStyle::IncludeIndependentTransformProperties); |
| makeMatrixRenderable(t, compositor()->hasAcceleratedCompositing()); |
| } |
| |
| m_graphicsLayer->setTransform(t); |
| } |
| |
| void CompositedLayerMapping::updateFilters(const ComputedStyle& style) { |
| m_graphicsLayer->setFilters( |
| owningLayer().createCompositorFilterOperationsForFilter(style)); |
| } |
| |
| void CompositedLayerMapping::updateBackdropFilters(const ComputedStyle& style) { |
| m_graphicsLayer->setBackdropFilters( |
| owningLayer().createCompositorFilterOperationsForBackdropFilter(style)); |
| } |
| |
| void CompositedLayerMapping::updateStickyConstraints( |
| const ComputedStyle& style, |
| const PaintLayer* compositingContainer) { |
| bool sticky = style.position() == EPosition::kSticky; |
| const PaintLayer* ancestorOverflowLayer = |
| m_owningLayer.ancestorOverflowLayer(); |
| // TODO(flackr): Do we still need this? |
| if (sticky) { |
| if (!ancestorOverflowLayer->isRootLayer()) { |
| sticky = ancestorOverflowLayer->needsCompositedScrolling(); |
| } else { |
| sticky = layoutObject().view()->frameView()->isScrollable(); |
| } |
| } |
| |
| WebLayerStickyPositionConstraint webConstraint; |
| if (sticky) { |
| const StickyConstraintsMap& constraintsMap = |
| ancestorOverflowLayer->getScrollableArea()->stickyConstraintsMap(); |
| const StickyPositionScrollingConstraints& constraints = |
| constraintsMap.at(&m_owningLayer); |
| |
| // Find the layout offset of the unshifted sticky box within its parent |
| // composited layer. This information is used by the compositor side to |
| // compute the additional offset required to keep the element stuck under |
| // compositor scrolling. |
| // |
| // Starting from the scroll container relative location, removing the |
| // enclosing layer's offset and the content offset in the composited layer |
| // results in the parent-layer relative offset. |
| FloatPoint parentRelativeStickyBoxOffset = |
| constraints.scrollContainerRelativeStickyBoxRect().location(); |
| |
| // The enclosing layers offset returned from |convertToLayerCoords| must be |
| // adjusted for both scroll and ancestor sticky elements. |
| LayoutPoint enclosingLayerOffset; |
| compositingContainer->convertToLayerCoords(ancestorOverflowLayer, |
| enclosingLayerOffset); |
| DCHECK(!scrollParent() || scrollParent() == ancestorOverflowLayer); |
| if (!scrollParent() && compositingContainer != ancestorOverflowLayer) { |
| enclosingLayerOffset += LayoutSize( |
| ancestorOverflowLayer->getScrollableArea()->getScrollOffset()); |
| } |
| // TODO(smcgruer): Until http://crbug.com/702229 is fixed, the nearest |
| // sticky ancestor may be non-composited which will make this offset wrong. |
| if (const LayoutBoxModelObject* ancestor = |
| constraints.nearestStickyAncestor()) { |
| enclosingLayerOffset -= |
| roundedIntSize(constraintsMap.at(ancestor->layer()) |
| .getTotalContainingBlockStickyOffset()); |
| } |
| |
| DCHECK(!m_contentOffsetInCompositingLayerDirty); |
| parentRelativeStickyBoxOffset.moveBy( |
| FloatPoint(-enclosingLayerOffset) - |
| FloatSize(contentOffsetInCompositingLayer())); |
| |
| webConstraint.isSticky = true; |
| webConstraint.isAnchoredLeft = |
| constraints.anchorEdges() & |
| StickyPositionScrollingConstraints::AnchorEdgeLeft; |
| webConstraint.isAnchoredRight = |
| constraints.anchorEdges() & |
| StickyPositionScrollingConstraints::AnchorEdgeRight; |
| webConstraint.isAnchoredTop = |
| constraints.anchorEdges() & |
| StickyPositionScrollingConstraints::AnchorEdgeTop; |
| webConstraint.isAnchoredBottom = |
| constraints.anchorEdges() & |
| StickyPositionScrollingConstraints::AnchorEdgeBottom; |
| webConstraint.leftOffset = constraints.leftOffset(); |
| webConstraint.rightOffset = constraints.rightOffset(); |
| webConstraint.topOffset = constraints.topOffset(); |
| webConstraint.bottomOffset = constraints.bottomOffset(); |
| webConstraint.parentRelativeStickyBoxOffset = |
| roundedIntPoint(parentRelativeStickyBoxOffset); |
| webConstraint.scrollContainerRelativeStickyBoxRect = |
| enclosingIntRect(constraints.scrollContainerRelativeStickyBoxRect()); |
| webConstraint.scrollContainerRelativeContainingBlockRect = enclosingIntRect( |
| constraints.scrollContainerRelativeContainingBlockRect()); |
| // TODO(smcgruer): Until http://crbug.com/702229 is fixed, the nearest |
| // sticky layers may not be composited and we may incorrectly end up with |
| // invalid layer IDs. |
| LayoutBoxModelObject* stickyBoxShiftingAncestor = |
| constraints.nearestStickyBoxShiftingStickyBox(); |
| if (stickyBoxShiftingAncestor && |
| stickyBoxShiftingAncestor->layer()->compositedLayerMapping()) { |
| webConstraint.nearestLayerShiftingStickyBox = |
| stickyBoxShiftingAncestor->layer() |
| ->compositedLayerMapping() |
| ->mainGraphicsLayer() |
| ->platformLayer() |
| ->id(); |
| } |
| LayoutBoxModelObject* containingBlockShiftingAncestor = |
| constraints.nearestStickyBoxShiftingContainingBlock(); |
| if (containingBlockShiftingAncestor && |
| containingBlockShiftingAncestor->layer()->compositedLayerMapping()) { |
| webConstraint.nearestLayerShiftingContainingBlock = |
| containingBlockShiftingAncestor->layer() |
| ->compositedLayerMapping() |
| ->mainGraphicsLayer() |
| ->platformLayer() |
| ->id(); |
| } |
| } |
| |
| m_graphicsLayer->setStickyPositionConstraint(webConstraint); |
| } |
| |
| void CompositedLayerMapping::updateLayerBlendMode(const ComputedStyle& style) { |
| setBlendMode(style.blendMode()); |
| } |
| |
| void CompositedLayerMapping::updateIsRootForIsolatedGroup() { |
| bool isolate = m_owningLayer.shouldIsolateCompositedDescendants(); |
| |
| // non stacking context layers should never isolate |
| ASSERT(m_owningLayer.stackingNode()->isStackingContext() || !isolate); |
| |
| m_graphicsLayer->setIsRootForIsolatedGroup(isolate); |
| } |
| |
| void CompositedLayerMapping:: |
| updateBackgroundPaintsOntoScrollingContentsLayer() { |
| // We can only paint the background onto the scrolling contents layer if |
| // it would be visually correct and we are using composited scrolling meaning |
| // we have a scrolling contents layer to paint it into. |
| BackgroundPaintLocation paintLocation = |
| m_owningLayer.backgroundPaintLocation(); |
| bool shouldPaintOntoScrollingContentsLayer = |
| paintLocation & BackgroundPaintInScrollingContents && |
| m_owningLayer.getScrollableArea()->usesCompositedScrolling(); |
| if (shouldPaintOntoScrollingContentsLayer != |
| backgroundPaintsOntoScrollingContentsLayer()) { |
| m_backgroundPaintsOntoScrollingContentsLayer = |
| shouldPaintOntoScrollingContentsLayer; |
| // The scrolling contents layer needs to be updated for changed |
| // m_backgroundPaintsOntoScrollingContentsLayer. |
| if (hasScrollingLayer()) |
| m_scrollingContentsLayer->setNeedsDisplay(); |
| } |
| bool shouldPaintOntoGraphicsLayer = |
| !m_backgroundPaintsOntoScrollingContentsLayer || |
| paintLocation & BackgroundPaintInGraphicsLayer; |
| if (shouldPaintOntoGraphicsLayer != !!m_backgroundPaintsOntoGraphicsLayer) { |
| m_backgroundPaintsOntoGraphicsLayer = shouldPaintOntoGraphicsLayer; |
| // The graphics layer needs to be updated for changed |
| // m_backgroundPaintsOntoGraphicsLayer. |
| m_graphicsLayer->setNeedsDisplay(); |
| } |
| } |
| |
| void CompositedLayerMapping::updateContentsOpaque() { |
| if (isAcceleratedCanvas(layoutObject())) { |
| CanvasRenderingContext* context = |
| toHTMLCanvasElement(layoutObject().node())->renderingContext(); |
| WebLayer* layer = context ? context->platformLayer() : nullptr; |
| // Determine whether the external texture layer covers the whole graphics |
| // layer. This may not be the case if there are box decorations or |
| // shadows. |
| if (layer && |
| layer->bounds() == m_graphicsLayer->platformLayer()->bounds()) { |
| // Determine whether the rendering context's external texture layer is |
| // opaque. |
| if (!context->creationAttributes().alpha()) { |
| m_graphicsLayer->setContentsOpaque(true); |
| } else { |
| m_graphicsLayer->setContentsOpaque( |
| !Color(layer->backgroundColor()).hasAlpha()); |
| } |
| } else { |
| m_graphicsLayer->setContentsOpaque(false); |
| } |
| } else if (m_backgroundLayer) { |
| m_graphicsLayer->setContentsOpaque(false); |
| m_backgroundLayer->setContentsOpaque( |
| m_owningLayer.backgroundIsKnownToBeOpaqueInRect(compositedBounds())); |
| } else if (isPlaceholderCanvas(layoutObject())) { |
| // TODO(crbug.com/705019): Contents could be opaque, but that cannot be |
| // determined from the main thread. Or can it? |
| m_graphicsLayer->setContentsOpaque(false); |
| } else { |
| // For non-root layers, background is painted by the scrolling contents |
| // layer if all backgrounds are background attachment local, otherwise |
| // background is painted by the primary graphics layer. |
| if (hasScrollingLayer() && m_backgroundPaintsOntoScrollingContentsLayer) { |
| // Backgrounds painted onto the foreground are clipped by the padding box |
| // rect. |
| // TODO(flackr): This should actually check the entire overflow rect |
| // within the scrolling contents layer but since we currently only trigger |
| // this for solid color backgrounds the answer will be the same. |
| m_scrollingContentsLayer->setContentsOpaque( |
| m_owningLayer.backgroundIsKnownToBeOpaqueInRect( |
| toLayoutBox(layoutObject()).paddingBoxRect())); |
| |
| if (m_owningLayer.backgroundPaintLocation() & |
| BackgroundPaintInGraphicsLayer) { |
| m_graphicsLayer->setContentsOpaque( |
| m_owningLayer.backgroundIsKnownToBeOpaqueInRect( |
| compositedBounds())); |
| } else { |
| // If we only paint the background onto the scrolling contents layer we |
| // are going to leave a hole in the m_graphicsLayer where the background |
| // is so it is not opaque. |
| m_graphicsLayer->setContentsOpaque(false); |
| } |
| } else { |
| if (hasScrollingLayer()) |
| m_scrollingContentsLayer->setContentsOpaque(false); |
| m_graphicsLayer->setContentsOpaque( |
| m_owningLayer.backgroundIsKnownToBeOpaqueInRect(compositedBounds())); |
| } |
| } |
| } |
| |
| void CompositedLayerMapping::updateCompositedBounds() { |
| ASSERT(m_owningLayer.compositor()->lifecycle().state() == |
| DocumentLifecycle::InCompositingUpdate); |
| // FIXME: if this is really needed for performance, it would be better to |
| // store it on Layer. |
| m_compositedBounds = m_owningLayer.boundingBoxForCompositing(); |
| m_contentOffsetInCompositingLayerDirty = true; |
| } |
| |
| void CompositedLayerMapping::updateAfterPartResize() { |
| if (layoutObject().isLayoutPart()) { |
| if (PaintLayerCompositor* innerCompositor = |
| PaintLayerCompositor::frameContentsCompositor( |
| toLayoutPart(layoutObject()))) { |
| innerCompositor->frameViewDidChangeSize(); |
| // We can floor this point because our frameviews are always aligned to |
| // pixel boundaries. |
| ASSERT(m_compositedBounds.location() == |
| flooredIntPoint(m_compositedBounds.location())); |
| innerCompositor->frameViewDidChangeLocation( |
| flooredIntPoint(contentsBox().location())); |
| } |
| } |
| } |
| |
| void CompositedLayerMapping::updateCompositingReasons() { |
| // All other layers owned by this mapping will have the same compositing |
| // reason for their lifetime, so they are initialized only when created. |
| m_graphicsLayer->setCompositingReasons(m_owningLayer.getCompositingReasons()); |
| m_graphicsLayer->setSquashingDisallowedReasons( |
| m_owningLayer.getSquashingDisallowedReasons()); |
| } |
| |
| bool CompositedLayerMapping::ancestorRoundedCornersWontClip( |
| const LayoutBoxModelObject& child, |
| const LayoutBoxModelObject& clippingAncestor) { |
| LayoutRect localVisualRect = m_compositedBounds; |
| child.mapToVisualRectInAncestorSpace(&clippingAncestor, localVisualRect); |
| FloatRoundedRect roundedClipRect = |
| clippingAncestor.style()->getRoundedInnerBorderFor( |
| clippingAncestor.localVisualRect()); |
| FloatRect innerClipRect = roundedClipRect.radiusCenterRect(); |
| // The first condition catches cases where the child is certainly inside |
| // the rounded corner portion of the border, and cannot be clipped by |
| // the rounded portion. The second catches cases where the child is |
| // entirely outside the rectangular border (ignoring rounded corners) so |
| // is also unaffected by the rounded corners. In both cases the existing |
| // rectangular clip is adequate and the mask is unnecessary. |
| return innerClipRect.contains(FloatRect(localVisualRect)) || |
| !localVisualRect.intersects( |
| enclosingLayoutRect(roundedClipRect.rect())); |
| } |
| |
| void CompositedLayerMapping:: |
| owningLayerClippedOrMaskedByLayerNotAboveCompositedAncestor( |
| const PaintLayer* scrollParent, |
| bool& owningLayerIsClipped, |
| bool& owningLayerIsMasked) { |
| owningLayerIsClipped = false; |
| owningLayerIsMasked = false; |
| |
| if (!m_owningLayer.parent()) |
| return; |
| |
| const PaintLayer* compositingAncestor = |
| m_owningLayer.enclosingLayerWithCompositedLayerMapping(ExcludeSelf); |
| if (!compositingAncestor) |
| return; |
| |
| const LayoutBoxModelObject* clippingContainer = |
| m_owningLayer.clippingContainer(); |
| if (!clippingContainer) |
| return; |
| |
| if (clippingContainer->enclosingLayer() == scrollParent) |
| return; |
| |
| if (compositingAncestor->layoutObject().isDescendantOf(clippingContainer)) |
| return; |
| |
| // We ignore overflow clip here; we want composited overflow content to |
| // behave as if it lives in an unclipped universe so it can prepaint, etc. |
| // This means that we need to check if we are actually clipped before |
| // setting up m_ancestorClippingLayer otherwise |
| // updateAncestorClippingLayerGeometry will fail as the clip rect will be |
| // infinite. |
| // FIXME: this should use cached clip rects, but this sometimes give |
| // inaccurate results (and trips the ASSERTS in PaintLayerClipper). |
| ClipRectsContext clipRectsContext(compositingAncestor, UncachedClipRects, |
| IgnorePlatformOverlayScrollbarSize); |
| clipRectsContext.setIgnoreOverflowClip(); |
| |
| ClipRect clipRect; |
| m_owningLayer.clipper(PaintLayer::DoNotUseGeometryMapper) |
| .calculateBackgroundClipRect(clipRectsContext, clipRect); |
| IntRect parentClipRect = pixelSnappedIntRect(clipRect.rect()); |
| owningLayerIsClipped = parentClipRect != LayoutRect::infiniteIntRect(); |
| |
| // TODO(schenney): CSS clips are not applied to composited children, and |
| // should be via mask or by compositing the parent too. |
| // https://bugs.chromium.org/p/chromium/issues/detail?id=615870 |
| DCHECK(clippingContainer->style()); |
| owningLayerIsMasked = |
| owningLayerIsClipped && clippingContainer->style()->hasBorderRadius() && |
| !ancestorRoundedCornersWontClip(layoutObject(), *clippingContainer); |
| } |
| |
| const PaintLayer* CompositedLayerMapping::scrollParent() { |
| const PaintLayer* scrollParent = m_owningLayer.scrollParent(); |
| if (scrollParent && !scrollParent->needsCompositedScrolling()) |
| return nullptr; |
| return scrollParent; |
| } |
| |
| bool CompositedLayerMapping::updateGraphicsLayerConfiguration() { |
| ASSERT(m_owningLayer.compositor()->lifecycle().state() == |
| DocumentLifecycle::InCompositingUpdate); |
| |
| // Note carefully: here we assume that the compositing state of all |
| // descendants have been updated already, so it is legitimate to compute and |
| // cache the composited bounds for this layer. |
| updateCompositedBounds(); |
| |
| PaintLayerCompositor* compositor = this->compositor(); |
| LayoutObject& layoutObject = this->layoutObject(); |
| const ComputedStyle& style = layoutObject.styleRef(); |
| |
| bool layerConfigChanged = false; |
| setBackgroundLayerPaintsFixedRootBackground( |
| compositor->needsFixedRootBackgroundLayer(&m_owningLayer)); |
| |
| // The background layer is currently only used for fixed root backgrounds. |
| if (updateBackgroundLayer(m_backgroundLayerPaintsFixedRootBackground)) |
| layerConfigChanged = true; |
| |
| if (updateForegroundLayer( |
| compositor->needsContentsCompositingLayer(&m_owningLayer))) |
| layerConfigChanged = true; |
| |
| bool needsDescendantsClippingLayer = |
| compositor->clipsCompositingDescendants(&m_owningLayer); |
| |
| // Our scrolling layer will clip. |
| if (m_owningLayer.needsCompositedScrolling()) |
| needsDescendantsClippingLayer = false; |
| |
| const PaintLayer* scrollParent = this->scrollParent(); |
| |
| // This is required because compositing layers are parented according to the |
| // z-order hierarchy, yet clipping goes down the layoutObject hierarchy. Thus, |
| // a PaintLayer can be clipped by a PaintLayer that is an ancestor in the |
| // layoutObject hierarchy, but a sibling in the z-order hierarchy. Further, |
| // that sibling need not be composited at all. In such scenarios, an ancestor |
| // clipping layer is necessary to apply the composited clip for this layer. |
| bool needsAncestorClip = false; |
| bool needsAncestorClippingMask = false; |
| owningLayerClippedOrMaskedByLayerNotAboveCompositedAncestor( |
| scrollParent, needsAncestorClip, needsAncestorClippingMask); |
| if (updateClippingLayers(needsAncestorClip, needsAncestorClippingMask, |
| needsDescendantsClippingLayer)) |
| layerConfigChanged = true; |
| |
| bool scrollingConfigChanged = false; |
| if (updateScrollingLayers(m_owningLayer.needsCompositedScrolling())) { |
| layerConfigChanged = true; |
| scrollingConfigChanged = true; |
| } |
| |
| // If the outline needs to draw over the composited scrolling contents layer |
| // or scrollbar layers it needs to be drawn into a separate layer. |
| int minBorderWidth = |
| std::min(layoutObject.style()->borderTopWidth(), |
| std::min(layoutObject.style()->borderLeftWidth(), |
| std::min(layoutObject.style()->borderRightWidth(), |
| layoutObject.style()->borderBottomWidth()))); |
| bool needsDecorationOutlineLayer = |
| m_owningLayer.getScrollableArea() && |
| m_owningLayer.getScrollableArea()->usesCompositedScrolling() && |
| layoutObject.style()->hasOutline() && |
| layoutObject.style()->outlineOffset() < -minBorderWidth; |
| |
| if (updateDecorationOutlineLayer(needsDecorationOutlineLayer)) |
| layerConfigChanged = true; |
| |
| if (updateOverflowControlsLayers( |
| requiresHorizontalScrollbarLayer(), requiresVerticalScrollbarLayer(), |
| requiresScrollCornerLayer(), needsAncestorClip)) |
| layerConfigChanged = true; |
| |
| bool hasPerspective = style.hasPerspective(); |
| bool needsChildTransformLayer = hasPerspective && layoutObject.isBox(); |
| if (updateChildTransformLayer(needsChildTransformLayer)) |
| layerConfigChanged = true; |
| |
| if (updateSquashingLayers(!m_squashedLayers.isEmpty())) |
| layerConfigChanged = true; |
| |
| updateScrollParent(scrollParent); |
| updateClipParent(scrollParent); |
| |
| if (layerConfigChanged) |
| updateInternalHierarchy(); |
| |
| if (scrollingConfigChanged) { |
| if (layoutObject.view()) |
| compositor->scrollingLayerDidChange(&m_owningLayer); |
| } |
| |
| // A mask layer is not part of the hierarchy proper, it's an auxiliary layer |
| // that's plugged into another GraphicsLayer that is part of the hierarchy. |
| // It has no parent or child GraphicsLayer. For that reason, we process it |
| // here, after the hierarchy has been updated. |
| bool maskLayerChanged = updateMaskLayer(layoutObject.hasMask()); |
| if (maskLayerChanged) |
| m_graphicsLayer->setMaskLayer(m_maskLayer.get()); |
| |
| bool hasChildClippingLayer = |
| compositor->clipsCompositingDescendants(&m_owningLayer) && |
| (hasClippingLayer() || hasScrollingLayer()); |
| // If we have a border radius or clip path on a scrolling layer, we need a |
| // clipping mask to properly clip the scrolled contents, even if there are no |
| // composited descendants. |
| bool hasClipPath = style.clipPath(); |
| bool needsChildClippingMask = |
| (hasClipPath || style.hasBorderRadius()) && |
| (hasChildClippingLayer || isAcceleratedContents(layoutObject) || |
| hasScrollingLayer()); |
| |
| GraphicsLayer* layerToApplyChildClippingMask = nullptr; |
| bool shouldApplyChildClippingMaskOnContents = false; |
| if (needsChildClippingMask) { |
| if (hasClipPath) { |
| // Clip path clips the entire subtree, including scrollbars. It must be |
| // attached directly onto the main m_graphicsLayer. |
| layerToApplyChildClippingMask = m_graphicsLayer.get(); |
| } else if (hasClippingLayer()) { |
| layerToApplyChildClippingMask = clippingLayer(); |
| } else if (hasScrollingLayer()) { |
| layerToApplyChildClippingMask = scrollingLayer(); |
| } else if (isAcceleratedContents(layoutObject)) { |
| shouldApplyChildClippingMaskOnContents = true; |
| } |
| } |
| |
| updateChildClippingMaskLayer(needsChildClippingMask); |
| |
| if (layerToApplyChildClippingMask == m_graphicsLayer.get()) { |
| if (m_graphicsLayer->maskLayer() != m_childClippingMaskLayer.get()) { |
| m_graphicsLayer->setMaskLayer(m_childClippingMaskLayer.get()); |
| maskLayerChanged = true; |
| } |
| } else if (m_graphicsLayer->maskLayer() && |
| m_graphicsLayer->maskLayer() != m_maskLayer.get()) { |
| m_graphicsLayer->setMaskLayer(nullptr); |
| maskLayerChanged = true; |
| } |
| if (hasClippingLayer()) |
| clippingLayer()->setMaskLayer(layerToApplyChildClippingMask == |
| clippingLayer() |
| ? m_childClippingMaskLayer.get() |
| : nullptr); |
| if (hasScrollingLayer()) |
| scrollingLayer()->setMaskLayer(layerToApplyChildClippingMask == |
| scrollingLayer() |
| ? m_childClippingMaskLayer.get() |
| : nullptr); |
| m_graphicsLayer->setContentsClippingMaskLayer( |
| shouldApplyChildClippingMaskOnContents ? m_childClippingMaskLayer.get() |
| : nullptr); |
| |
| updateBackgroundColor(); |
| |
| if (layoutObject.isImage()) { |
| if (isDirectlyCompositedImage()) { |
| updateImageContents(); |
| } else if (m_graphicsLayer->hasContentsLayer()) { |
| m_graphicsLayer->setContentsToImage(nullptr); |
| } |
| } |
| |
| if (WebLayer* layer = platformLayerForPlugin(layoutObject)) { |
| m_graphicsLayer->setContentsToPlatformLayer(layer); |
| } else if (layoutObject.node() && |
| layoutObject.node()->isFrameOwnerElement() && |
| toHTMLFrameOwnerElement(layoutObject.node())->contentFrame()) { |
| Frame* frame = toHTMLFrameOwnerElement(layoutObject.node())->contentFrame(); |
| if (frame->isRemoteFrame()) { |
| WebLayer* layer = toRemoteFrame(frame)->webLayer(); |
| m_graphicsLayer->setContentsToPlatformLayer(layer); |
| } |
| } else if (layoutObject.isVideo()) { |
| HTMLMediaElement* mediaElement = toHTMLMediaElement(layoutObject.node()); |
| m_graphicsLayer->setContentsToPlatformLayer(mediaElement->platformLayer()); |
| } else if (isPlaceholderCanvas(layoutObject)) { |
| HTMLCanvasElement* canvas = toHTMLCanvasElement(layoutObject.node()); |
| m_graphicsLayer->setContentsToPlatformLayer( |
| canvas->surfaceLayerBridge()->getWebLayer()); |
| layerConfigChanged = true; |
| } else if (isAcceleratedCanvas(layoutObject)) { |
| HTMLCanvasElement* canvas = toHTMLCanvasElement(layoutObject.node()); |
| if (CanvasRenderingContext* context = canvas->renderingContext()) |
| m_graphicsLayer->setContentsToPlatformLayer(context->platformLayer()); |
| layerConfigChanged = true; |
| } |
| if (layoutObject.isLayoutPart()) { |
| if (PaintLayerCompositor::attachFrameContentLayersToIframeLayer( |
| toLayoutPart(layoutObject))) |
| layerConfigChanged = true; |
| } |
| |
| // Changes to either the internal hierarchy or the mask layer have an impact |
| // on painting phases, so we need to update when either are updated. |
| if (layerConfigChanged || maskLayerChanged) |
| updatePaintingPhases(); |
| |
| updateElementIdAndCompositorMutableProperties(); |
| |
| m_graphicsLayer->setHasWillChangeTransformHint( |
| style.hasWillChangeTransformHint()); |
| |
| if (style.preserves3D() && style.hasOpacity() && |
| m_owningLayer.has3DTransformedDescendant()) |
| UseCounter::count(layoutObject.document(), |
| UseCounter::OpacityWithPreserve3DQuirk); |
| |
| return layerConfigChanged; |
| } |
| |
| static IntRect clipBox(LayoutBox& layoutObject) { |
| // TODO(chrishtr): pixel snapping is most likely incorrect here. |
| return pixelSnappedIntRect(layoutObject.clippingRect()); |
| } |
| |
| static LayoutPoint computeOffsetFromCompositedAncestor( |
| const PaintLayer* layer, |
| const PaintLayer* compositedAncestor, |
| const LayoutPoint& localRepresentativePointForFragmentation) { |
| // Add in the offset of the composited bounds from the coordinate space of |
| // the PaintLayer, since visualOffsetFromAncestor() requires the pre-offset |
| // input to be in the space of the PaintLayer. We also need to add in this |
| // offset before computation of visualOffsetFromAncestor(), because it affects |
| // fragmentation offset if compositedAncestor crosses a pagination boundary. |
| // |
| // Currently, visual fragmentation for composited layers is not implemented. |
| // For fragmented contents, we paint in the logical coordinates of the flow |
| // thread, then split the result by fragment boundary and paste each part |
| // into each fragment's physical position. |
| // Since composited layers don't support visual fragmentation, we have to |
| // choose a "representative" fragment to position the painted contents. This |
| // is where localRepresentativePointForFragmentation comes into play. |
| // The fragment that the representative point resides in will be chosen as |
| // the representative fragment for layer position purpose. |
| // For layers that are not fragmented, the point doesn't affect behavior as |
| // there is one and only one fragment. |
| LayoutPoint offset = layer->visualOffsetFromAncestor( |
| compositedAncestor, localRepresentativePointForFragmentation); |
| if (compositedAncestor) |
| offset.move(compositedAncestor->compositedLayerMapping() |
| ->owningLayer() |
| .subpixelAccumulation()); |
| offset.moveBy(-localRepresentativePointForFragmentation); |
| return offset; |
| } |
| |
| void CompositedLayerMapping::computeBoundsOfOwningLayer( |
| const PaintLayer* compositedAncestor, |
| IntRect& localBounds, |
| IntRect& compositingBoundsRelativeToCompositedAncestor, |
| LayoutPoint& offsetFromCompositedAncestor, |
| IntPoint& snappedOffsetFromCompositedAncestor) { |
| LayoutRect localRawCompositingBounds = compositedBounds(); |
| offsetFromCompositedAncestor = computeOffsetFromCompositedAncestor( |
| &m_owningLayer, compositedAncestor, localRawCompositingBounds.location()); |
| snappedOffsetFromCompositedAncestor = |
| IntPoint(offsetFromCompositedAncestor.x().round(), |
| offsetFromCompositedAncestor.y().round()); |
| |
| LayoutSize subpixelAccumulation = |
| offsetFromCompositedAncestor - snappedOffsetFromCompositedAncestor; |
| m_owningLayer.setSubpixelAccumulation(subpixelAccumulation); |
| |
| // Move the bounds by the subpixel accumulation so that it pixel-snaps |
| // relative to absolute pixels instead of local coordinates. |
| localRawCompositingBounds.move(subpixelAccumulation); |
| localBounds = pixelSnappedIntRect(localRawCompositingBounds); |
| |
| compositingBoundsRelativeToCompositedAncestor = localBounds; |
| compositingBoundsRelativeToCompositedAncestor.moveBy( |
| snappedOffsetFromCompositedAncestor); |
| } |
| |
| void CompositedLayerMapping::updateSquashingLayerGeometry( |
| const IntPoint& graphicsLayerParentLocation, |
| const PaintLayer* compositingContainer, |
| Vector<GraphicsLayerPaintInfo>& layers, |
| GraphicsLayer* squashingLayer, |
| LayoutPoint* offsetFromTransformedAncestor, |
| Vector<PaintLayer*>& layersNeedingPaintInvalidation) { |
| if (!squashingLayer) |
| return; |
| |
| LayoutPoint compositingContainerOffsetFromParentGraphicsLayer = |
| -graphicsLayerParentLocation; |
| if (compositingContainer) |
| compositingContainerOffsetFromParentGraphicsLayer += |
| compositingContainer->subpixelAccumulation(); |
| |
| #if 0 && DCHECK_IS_ON() |
| // TODO(trchen): We should enable this for below comment out |DCHECK()| once |
| // we have simple reproduce case and fix it. See http://crbug.com/646437 for |
| // details. |
| const PaintLayer* commonTransformAncestor = nullptr; |
| if (compositingContainer && compositingContainer->transform()) |
| commonTransformAncestor = compositingContainer; |
| else if (compositingContainer) |
| commonTransformAncestor = compositingContainer->transformAncestor(); |
| #endif |
| // FIXME: Cache these offsets. |
| LayoutPoint compositingContainerOffsetFromTransformedAncestor; |
| if (compositingContainer && !compositingContainer->transform()) |
| compositingContainerOffsetFromTransformedAncestor = |
| compositingContainer->computeOffsetFromTransformedAncestor(); |
| |
| LayoutRect totalSquashBounds; |
| for (size_t i = 0; i < layers.size(); ++i) { |
| LayoutRect squashedBounds = |
| layers[i].paintLayer->boundingBoxForCompositing(); |
| |
| // Store the local bounds of the Layer subtree before applying the offset. |
| layers[i].compositedBounds = squashedBounds; |
| |
| #if 0 && DCHECK_IS_ON() |
| // TODO(trchen): We should enable this |DCHECK()| once we have simple |
| // reproduce case and fix it. See http://crbug.com/646437 for details. |
| DCHECK(layers[i].paintLayer->transformAncestor() == |
| commonTransformAncestor); |
| #endif |
| LayoutPoint squashedLayerOffsetFromTransformedAncestor = |
| layers[i].paintLayer->computeOffsetFromTransformedAncestor(); |
| LayoutSize squashedLayerOffsetFromCompositingContainer = |
| squashedLayerOffsetFromTransformedAncestor - |
| compositingContainerOffsetFromTransformedAncestor; |
| |
| squashedBounds.move(squashedLayerOffsetFromCompositingContainer); |
| totalSquashBounds.unite(squashedBounds); |
| } |
| |
| // The totalSquashBounds is positioned with respect to compositingContainer. |
| // But the squashingLayer needs to be positioned with respect to the |
| // graphicsLayerParent. The conversion between compositingContainer and the |
| // graphicsLayerParent is already computed as |
| // compositingContainerOffsetFromParentGraphicsLayer. |
| totalSquashBounds.moveBy(compositingContainerOffsetFromParentGraphicsLayer); |
| const IntRect squashLayerBounds = enclosingIntRect(totalSquashBounds); |
| const IntPoint squashLayerOrigin = squashLayerBounds.location(); |
| const LayoutSize squashLayerOriginInCompositingContainerSpace = |
| squashLayerOrigin - compositingContainerOffsetFromParentGraphicsLayer; |
| |
| // Now that the squashing bounds are known, we can convert the PaintLayer |
| // painting offsets from compositingContainer space to the squashing layer |
| // space. |
| // |
| // The painting offset we want to compute for each squashed PaintLayer is |
| // essentially the position of the squashed PaintLayer described w.r.t. |
| // compositingContainer's origin. So we just need to convert that point from |
| // compositingContainer space to the squashing layer's space. This is done by |
| // subtracting squashLayerOriginInCompositingContainerSpace, but then the |
| // offset overall needs to be negated because that's the direction that the |
| // painting code expects the offset to be. |
| for (size_t i = 0; i < layers.size(); ++i) { |
| const LayoutPoint squashedLayerOffsetFromTransformedAncestor = |
| layers[i].paintLayer->computeOffsetFromTransformedAncestor(); |
| const LayoutSize offsetFromSquashLayerOrigin = |
| (squashedLayerOffsetFromTransformedAncestor - |
| compositingContainerOffsetFromTransformedAncestor) - |
| squashLayerOriginInCompositingContainerSpace; |
| |
| IntSize newOffsetFromLayoutObject = |
| -IntSize(offsetFromSquashLayerOrigin.width().round(), |
| offsetFromSquashLayerOrigin.height().round()); |
| LayoutSize subpixelAccumulation = |
| offsetFromSquashLayerOrigin + newOffsetFromLayoutObject; |
| if (layers[i].offsetFromLayoutObjectSet && |
| layers[i].offsetFromLayoutObject != newOffsetFromLayoutObject) { |
| // It is ok to issue paint invalidation here, because all of the geometry |
| // needed to correctly invalidate paint is computed by this point. |
| DisablePaintInvalidationStateAsserts disabler; |
| ObjectPaintInvalidator(layers[i].paintLayer->layoutObject()) |
| .invalidatePaintIncludingNonCompositingDescendants(); |
| |
| TRACE_LAYER_INVALIDATION(layers[i].paintLayer, |
| InspectorLayerInvalidationTrackingEvent:: |
| SquashingLayerGeometryWasUpdated); |
| layersNeedingPaintInvalidation.push_back(layers[i].paintLayer); |
| } |
| layers[i].offsetFromLayoutObject = newOffsetFromLayoutObject; |
| layers[i].offsetFromLayoutObjectSet = true; |
| |
| layers[i].paintLayer->setSubpixelAccumulation(subpixelAccumulation); |
| } |
| |
| squashingLayer->setPosition(squashLayerBounds.location()); |
| squashingLayer->setSize(FloatSize(squashLayerBounds.size())); |
| |
| *offsetFromTransformedAncestor = |
| compositingContainerOffsetFromTransformedAncestor; |
| offsetFromTransformedAncestor->move( |
| squashLayerOriginInCompositingContainerSpace); |
| |
| for (size_t i = 0; i < layers.size(); ++i) |
| layers[i].localClipRectForSquashedLayer = |
| localClipRectForSquashedLayer(m_owningLayer, layers[i], layers); |
| } |
| |
| void CompositedLayerMapping::updateGraphicsLayerGeometry( |
| const PaintLayer* compositingContainer, |
| const PaintLayer* compositingStackingContext, |
| Vector<PaintLayer*>& layersNeedingPaintInvalidation) { |
| ASSERT(m_owningLayer.compositor()->lifecycle().state() == |
| DocumentLifecycle::InCompositingUpdate); |
| |
| // Set transform property, if it is not animating. We have to do this here |
| // because the transform is affected by the layer dimensions. |
| if (!layoutObject().style()->isRunningTransformAnimationOnCompositor()) |
| updateTransform(layoutObject().styleRef()); |
| |
| // Set opacity, if it is not animating. |
| if (!layoutObject().style()->isRunningOpacityAnimationOnCompositor()) |
| updateOpacity(layoutObject().styleRef()); |
| |
| if (!layoutObject().style()->isRunningFilterAnimationOnCompositor()) |
| updateFilters(layoutObject().styleRef()); |
| |
| if (!layoutObject().style()->isRunningBackdropFilterAnimationOnCompositor()) |
| updateBackdropFilters(layoutObject().styleRef()); |
| |
| // We compute everything relative to the enclosing compositing layer. |
| IntRect ancestorCompositingBounds; |
| if (compositingContainer) { |
| ASSERT(compositingContainer->hasCompositedLayerMapping()); |
| ancestorCompositingBounds = compositingContainer->compositedLayerMapping() |
| ->pixelSnappedCompositedBounds(); |
| } |
| |
| IntRect localCompositingBounds; |
| IntRect relativeCompositingBounds; |
| LayoutPoint offsetFromCompositedAncestor; |
| IntPoint snappedOffsetFromCompositedAncestor; |
| computeBoundsOfOwningLayer( |
| compositingContainer, localCompositingBounds, relativeCompositingBounds, |
| offsetFromCompositedAncestor, snappedOffsetFromCompositedAncestor); |
| |
| IntPoint graphicsLayerParentLocation; |
| computeGraphicsLayerParentLocation(compositingContainer, |
| ancestorCompositingBounds, |
| graphicsLayerParentLocation); |
| |
| // Might update graphicsLayerParentLocation. |
| updateAncestorClippingLayerGeometry(compositingContainer, |
| snappedOffsetFromCompositedAncestor, |
| graphicsLayerParentLocation); |
| |
| FloatSize contentsSize(relativeCompositingBounds.size()); |
| |
| updateMainGraphicsLayerGeometry(relativeCompositingBounds, |
| localCompositingBounds, |
| graphicsLayerParentLocation); |
| updateOverflowControlsHostLayerGeometry(compositingStackingContext, |
| compositingContainer, |
| graphicsLayerParentLocation); |
| updateContentsOffsetInCompositingLayer(snappedOffsetFromCompositedAncestor, |
| graphicsLayerParentLocation); |
| updateStickyConstraints(layoutObject().styleRef(), compositingContainer); |
| updateSquashingLayerGeometry( |
| graphicsLayerParentLocation, compositingContainer, m_squashedLayers, |
| m_squashingLayer.get(), &m_squashingLayerOffsetFromTransformedAncestor, |
| layersNeedingPaintInvalidation); |
| |
| // If we have a layer that clips children, position it. |
| IntRect clippingBox; |
| if (m_childContainmentLayer && layoutObject().isBox()) |
| clippingBox = clipBox(toLayoutBox(layoutObject())); |
| |
| updateChildTransformLayerGeometry(); |
| updateChildContainmentLayerGeometry(clippingBox, localCompositingBounds); |
| |
| updateMaskLayerGeometry(); |
| updateTransformGeometry(snappedOffsetFromCompositedAncestor, |
| relativeCompositingBounds); |
| updateForegroundLayerGeometry(contentsSize, clippingBox); |
| updateBackgroundLayerGeometry(contentsSize); |
| // TODO(yigu): Currently the decoration layer uses the same contentSize |
| // as background layer and foreground layer. There are scenarios that |
| // the sizes could be different. The actual size of the decoration layer |
| // should be calculated separately. |
| // The size of the background layer should be different as well. We need to |
| // check whether we are painting the decoration layer into the background and |
| // then ignore or consider the outline when determining the contentSize. |
| updateDecorationOutlineLayerGeometry(contentsSize); |
| updateScrollingLayerGeometry(localCompositingBounds); |
| updateChildClippingMaskLayerGeometry(); |
| |
| if (m_owningLayer.getScrollableArea() && |
| m_owningLayer.getScrollableArea()->scrollsOverflow()) |
| m_owningLayer.getScrollableArea()->positionOverflowControls(); |
| |
| updateLayerBlendMode(layoutObject().styleRef()); |
| updateIsRootForIsolatedGroup(); |
| updateContentsRect(); |
| updateBackgroundColor(); |
| updateDrawsContent(); |
| updateElementIdAndCompositorMutableProperties(); |
| updateBackgroundPaintsOntoScrollingContentsLayer(); |
| updateContentsOpaque(); |
| updateAfterPartResize(); |
| updateRenderingContext(); |
| updateShouldFlattenTransform(); |
| updateChildrenTransform(); |
| updateScrollParent(scrollParent()); |
| registerScrollingLayers(); |
| |
| updateCompositingReasons(); |
| } |
| |
| void CompositedLayerMapping::updateMainGraphicsLayerGeometry( |
| const IntRect& relativeCompositingBounds, |
| const IntRect& localCompositingBounds, |
| const IntPoint& graphicsLayerParentLocation) { |
| m_graphicsLayer->setPosition(FloatPoint(relativeCompositingBounds.location() - |
| graphicsLayerParentLocation)); |
| m_graphicsLayer->setOffsetFromLayoutObject( |
| toIntSize(localCompositingBounds.location())); |
| |
| FloatSize oldSize = m_graphicsLayer->size(); |
| const FloatSize contentsSize(relativeCompositingBounds.size()); |
| if (oldSize != contentsSize) |
| m_graphicsLayer->setSize(contentsSize); |
| |
| // m_graphicsLayer is the corresponding GraphicsLayer for this PaintLayer and |
| // its non-compositing descendants. So, the visibility flag for |
| // m_graphicsLayer should be true if there are any non-compositing visible |
| // layers. |
| bool contentsVisible = m_owningLayer.hasVisibleContent() || |
| hasVisibleNonCompositingDescendant(&m_owningLayer); |
| m_graphicsLayer->setContentsVisible(contentsVisible); |
| |
| m_graphicsLayer->setBackfaceVisibility( |
| layoutObject().style()->backfaceVisibility() == |
| BackfaceVisibilityVisible); |
| } |
| |
| void CompositedLayerMapping::computeGraphicsLayerParentLocation( |
| const PaintLayer* compositingContainer, |
| const IntRect& ancestorCompositingBounds, |
| IntPoint& graphicsLayerParentLocation) { |
| if (compositingContainer && |
| compositingContainer->compositedLayerMapping()->hasClippingLayer() && |
| compositingContainer->layoutObject().isBox()) { |
| // If the compositing ancestor has a layer to clip children, we parent in |
| // that, and therefore position relative to it. |
| IntRect clippingBox = |
| clipBox(toLayoutBox(compositingContainer->layoutObject())); |
| graphicsLayerParentLocation = |
| clippingBox.location() + |
| roundedIntSize(compositingContainer->subpixelAccumulation()); |
| } else if (compositingContainer && |
| compositingContainer->compositedLayerMapping() |
| ->childTransformLayer()) { |
| // Similarly, if the compositing ancestor has a child transform layer, we |
| // parent in that, and therefore position relative to it. It's already taken |
| // into account the contents offset, so we do not need to here. |
| graphicsLayerParentLocation = |
| roundedIntPoint(compositingContainer->subpixelAccumulation()); |
| } else if (compositingContainer) { |
| graphicsLayerParentLocation = ancestorCompositingBounds.location(); |
| } else { |
| graphicsLayerParentLocation = |
| layoutObject().view()->documentRect().location(); |
| } |
| |
| if (compositingContainer && |
| compositingContainer->needsCompositedScrolling()) { |
| LayoutBox& layoutBox = toLayoutBox(compositingContainer->layoutObject()); |
| IntSize scrollOffset = layoutBox.scrolledContentOffset(); |
| IntPoint scrollOrigin = |
| compositingContainer->getScrollableArea()->scrollOrigin(); |
| scrollOrigin.move(-layoutBox.borderLeft().toInt(), |
| -layoutBox.borderTop().toInt()); |
| graphicsLayerParentLocation = -(scrollOrigin + scrollOffset); |
| } |
| } |
| |
| void CompositedLayerMapping::updateAncestorClippingLayerGeometry( |
| const PaintLayer* compositingContainer, |
| const IntPoint& snappedOffsetFromCompositedAncestor, |
| IntPoint& graphicsLayerParentLocation) { |
| if (!compositingContainer || !m_ancestorClippingLayer) |
| return; |
| |
| ClipRectsContext clipRectsContext(compositingContainer, |
| PaintingClipRectsIgnoringOverflowClip, |
| IgnorePlatformOverlayScrollbarSize); |
| |
| ClipRect parentClipRect; |
| m_owningLayer.clipper(PaintLayer::DoNotUseGeometryMapper) |
| .calculateBackgroundClipRect(clipRectsContext, parentClipRect); |
| |
| IntRect snappedParentClipRect(pixelSnappedIntRect(parentClipRect.rect())); |
| |
| DCHECK(snappedParentClipRect != LayoutRect::infiniteIntRect()); |
| m_ancestorClippingLayer->setPosition(FloatPoint( |
| snappedParentClipRect.location() - graphicsLayerParentLocation)); |
| m_ancestorClippingLayer->setSize(FloatSize(snappedParentClipRect.size())); |
| |
| // backgroundRect is relative to compositingContainer, so subtract |
| // snappedOffsetFromCompositedAncestor.X/snappedOffsetFromCompositedAncestor.Y |
| // to get back to local coords. |
| m_ancestorClippingLayer->setOffsetFromLayoutObject( |
| snappedParentClipRect.location() - snappedOffsetFromCompositedAncestor); |
| |
| if (m_ancestorClippingMaskLayer) { |
| m_ancestorClippingMaskLayer->setOffsetFromLayoutObject( |
| m_ancestorClippingLayer->offsetFromLayoutObject()); |
| m_ancestorClippingMaskLayer->setSize(m_ancestorClippingLayer->size()); |
| m_ancestorClippingMaskLayer->setNeedsDisplay(); |
| } |
| |
| // The primary layer is then parented in, and positioned relative to this |
| // clipping layer. |
| graphicsLayerParentLocation = snappedParentClipRect.location(); |
| } |
| |
| void CompositedLayerMapping::updateOverflowControlsHostLayerGeometry( |
| const PaintLayer* compositingStackingContext, |
| const PaintLayer* compositingContainer, |
| IntPoint graphicsLayerParentLocation) { |
| if (!m_overflowControlsHostLayer) |
| return; |
| |
| // To position and clip the scrollbars correctly, m_overflowControlsHostLayer |
| // should match our border box rect, which is at the origin of our |
| // LayoutObject. Its position is computed in various ways depending on who its |
| // parent GraphicsLayer is going to be. |
| LayoutPoint hostLayerPosition; |
| |
| if (needsToReparentOverflowControls()) { |
| CompositedLayerMapping* stackingCLM = |
| compositingStackingContext->compositedLayerMapping(); |
| DCHECK(stackingCLM); |
| |
| // Either m_overflowControlsHostLayer or |
| // m_overflowControlsAncestorClippingLayer (if it exists) will be a child of |
| // the main GraphicsLayer of the compositing stacking context. |
| IntSize stackingOffsetFromLayoutObject = |
| stackingCLM->mainGraphicsLayer()->offsetFromLayoutObject(); |
| |
| if (m_overflowControlsAncestorClippingLayer) { |
| m_overflowControlsAncestorClippingLayer->setSize( |
| m_ancestorClippingLayer->size()); |
| m_overflowControlsAncestorClippingLayer->setOffsetFromLayoutObject( |
| m_ancestorClippingLayer->offsetFromLayoutObject()); |
| m_overflowControlsAncestorClippingLayer->setMasksToBounds(true); |
| |
| FloatPoint position; |
| if (compositingStackingContext == compositingContainer) { |
| position = m_ancestorClippingLayer->position(); |
| } else { |
| // graphicsLayerParentLocation is the location of |
| // m_ancestorClippingLayer relative to compositingContainer (including |
| // any offset from compositingContainer's m_childContainmentLayer). |
| LayoutPoint offset = LayoutPoint(graphicsLayerParentLocation); |
| compositingContainer->convertToLayerCoords(compositingStackingContext, |
| offset); |
| position = |
| FloatPoint(offset) - FloatSize(stackingOffsetFromLayoutObject); |
| } |
| |
| m_overflowControlsAncestorClippingLayer->setPosition(position); |
| hostLayerPosition.move( |
| -m_ancestorClippingLayer->offsetFromLayoutObject()); |
| } else { |
| // The controls are in the same 2D space as the compositing container, so |
| // we can map them into the space of the container. |
| TransformState transformState(TransformState::ApplyTransformDirection, |
| FloatPoint()); |
| m_owningLayer.layoutObject().mapLocalToAncestor( |
| &compositingStackingContext->layoutObject(), transformState, |
| ApplyContainerFlip); |
| transformState.flatten(); |
| hostLayerPosition = LayoutPoint(transformState.lastPlanarPoint()); |
| if (PaintLayerScrollableArea* scrollableArea = |
| compositingStackingContext->getScrollableArea()) { |
| hostLayerPosition.move( |
| LayoutSize(toFloatSize(scrollableArea->scrollPosition()))); |
| } |
| hostLayerPosition.move(-stackingOffsetFromLayoutObject); |
| } |
| } else { |
| hostLayerPosition.move(-m_graphicsLayer->offsetFromLayoutObject()); |
| } |
| |
| m_overflowControlsHostLayer->setPosition(FloatPoint(hostLayerPosition)); |
| |
| const IntRect borderBox = |
| toLayoutBox(m_owningLayer.layoutObject()).pixelSnappedBorderBoxRect(); |
| m_overflowControlsHostLayer->setSize(FloatSize(borderBox.size())); |
| m_overflowControlsHostLayer->setMasksToBounds(true); |
| } |
| |
| void CompositedLayerMapping::updateChildContainmentLayerGeometry( |
| const IntRect& clippingBox, |
| const IntRect& localCompositingBounds) { |
| if (!m_childContainmentLayer) |
| return; |
| |
| FloatPoint clipPositionInLayoutObjectSpace( |
| clippingBox.location() - localCompositingBounds.location() + |
| roundedIntSize(m_owningLayer.subpixelAccumulation())); |
| |
| // If there are layers between the the child containment layer and |
| // m_graphicsLayer (eg, the child transform layer), we must adjust the clip |
| // position to get it in the correct space. |
| FloatPoint clipPositionInParentSpace = clipPositionInLayoutObjectSpace; |
| for (GraphicsLayer* ancestor = m_childContainmentLayer->parent(); |
| ancestor != mainGraphicsLayer(); ancestor = ancestor->parent()) |
| clipPositionInParentSpace -= toFloatSize(ancestor->position()); |
| |
| m_childContainmentLayer->setPosition(clipPositionInParentSpace); |
| m_childContainmentLayer->setSize(FloatSize(clippingBox.size())); |
| m_childContainmentLayer->setOffsetFromLayoutObject( |
| toIntSize(clippingBox.location())); |
| if (m_childClippingMaskLayer && !m_scrollingLayer && |
| !layoutObject().style()->clipPath()) { |
| m_childClippingMaskLayer->setSize(m_childContainmentLayer->size()); |
| m_childClippingMaskLayer->setOffsetFromLayoutObject( |
| m_childContainmentLayer->offsetFromLayoutObject()); |
| } |
| } |
| |
| void CompositedLayerMapping::updateChildTransformLayerGeometry() { |
| if (!m_childTransformLayer) |
| return; |
| const IntRect borderBox = |
| toLayoutBox(m_owningLayer.layoutObject()).pixelSnappedBorderBoxRect(); |
| m_childTransformLayer->setSize(FloatSize(borderBox.size())); |
| m_childTransformLayer->setPosition( |
| FloatPoint(contentOffsetInCompositingLayer())); |
| } |
| |
| void CompositedLayerMapping::updateMaskLayerGeometry() { |
| if (!m_maskLayer) |
| return; |
| |
| if (m_maskLayer->size() != m_graphicsLayer->size()) { |
| m_maskLayer->setSize(m_graphicsLayer->size()); |
| m_maskLayer->setNeedsDisplay(); |
| } |
| m_maskLayer->setPosition(FloatPoint()); |
| m_maskLayer->setOffsetFromLayoutObject( |
| m_graphicsLayer->offsetFromLayoutObject()); |
| } |
| |
| void CompositedLayerMapping::updateTransformGeometry( |
| const IntPoint& snappedOffsetFromCompositedAncestor, |
| const IntRect& relativeCompositingBounds) { |
| if (m_owningLayer.hasTransformRelatedProperty()) { |
| const LayoutRect borderBox = toLayoutBox(layoutObject()).borderBoxRect(); |
| |
| // Get layout bounds in the coords of compositingContainer to match |
| // relativeCompositingBounds. |
| IntRect layerBounds = pixelSnappedIntRect( |
| toLayoutPoint(m_owningLayer.subpixelAccumulation()), borderBox.size()); |
| layerBounds.moveBy(snappedOffsetFromCompositedAncestor); |
| |
| // Update properties that depend on layer dimensions |
| FloatPoint3D transformOrigin = |
| computeTransformOrigin(IntRect(IntPoint(), layerBounds.size())); |
| |
| // |transformOrigin| is in the local space of this layer. |
| // layerBounds - relativeCompositingBounds converts to the space of the |
| // compositing bounds relative to the composited ancestor. This does not |
| // apply to the z direction, since the page is 2D. |
| FloatPoint3D compositedTransformOrigin( |
| layerBounds.x() - relativeCompositingBounds.x() + transformOrigin.x(), |
| layerBounds.y() - relativeCompositingBounds.y() + transformOrigin.y(), |
| transformOrigin.z()); |
| m_graphicsLayer->setTransformOrigin(compositedTransformOrigin); |
| } else { |
| FloatPoint3D compositedTransformOrigin( |
| relativeCompositingBounds.width() * 0.5f, |
| relativeCompositingBounds.height() * 0.5f, 0.f); |
| m_graphicsLayer->setTransformOrigin(compositedTransformOrigin); |
| } |
| } |
| |
| void CompositedLayerMapping::updateScrollingLayerGeometry( |
| const IntRect& localCompositingBounds) { |
| if (!m_scrollingLayer) |
| return; |
| |
| ASSERT(m_scrollingContentsLayer); |
| LayoutBox& layoutBox = toLayoutBox(layoutObject()); |
| IntRect overflowClipRect = |
| pixelSnappedIntRect(layoutBox.overflowClipRect(LayoutPoint())); |
| |
| const TopDocumentRootScrollerController& globalRootScrollerController = |
| layoutBox.document().page()->globalRootScrollerController(); |
| |
| if (&m_owningLayer == globalRootScrollerController.rootScrollerPaintLayer()) { |
| LayoutRect clipRect = |
| layoutBox.document().layoutView()->overflowClipRect(LayoutPoint()); |
| DCHECK(clipRect.size() == LayoutSize(pixelSnappedIntRect(clipRect).size())); |
| overflowClipRect.setSize(pixelSnappedIntRect(clipRect).size()); |
| } |
| |
| // When a m_childTransformLayer exists, local content offsets for the |
| // m_scrollingLayer have already been applied. Otherwise, we apply them here. |
| IntSize localContentOffset(0, 0); |
| if (!m_childTransformLayer) { |
| localContentOffset = roundedIntPoint(m_owningLayer.subpixelAccumulation()) - |
| localCompositingBounds.location(); |
| } |
| m_scrollingLayer->setPosition( |
| FloatPoint(overflowClipRect.location() + localContentOffset)); |
| m_scrollingLayer->setSize(FloatSize(overflowClipRect.size())); |
| |
| IntSize oldScrollingLayerOffset = m_scrollingLayer->offsetFromLayoutObject(); |
| m_scrollingLayer->setOffsetFromLayoutObject( |
| -toIntSize(overflowClipRect.location())); |
| |
| if (m_childClippingMaskLayer && !layoutObject().style()->clipPath()) { |
| m_childClippingMaskLayer->setPosition(m_scrollingLayer->position()); |
| m_childClippingMaskLayer->setSize(m_scrollingLayer->size()); |
| m_childClippingMaskLayer->setOffsetFromLayoutObject( |
| toIntSize(overflowClipRect.location())); |
| } |
| |
| bool overflowClipRectOffsetChanged = |
| oldScrollingLayerOffset != m_scrollingLayer->offsetFromLayoutObject(); |
| |
| IntSize scrollSize(layoutBox.pixelSnappedScrollWidth(), |
| layoutBox.pixelSnappedScrollHeight()); |
| if (overflowClipRectOffsetChanged) |
| m_scrollingContentsLayer->setNeedsDisplay(); |
| |
| FloatPoint scrollPosition = |
| m_owningLayer.getScrollableArea()->scrollPosition(); |
| DoubleSize scrollingContentsOffset( |
| overflowClipRect.location().x() - scrollPosition.x(), |
| overflowClipRect.location().y() - scrollPosition.y()); |
| // The scroll offset change is compared using floating point so that |
| // fractional scroll offset change can be propagated to compositor. |
| if (scrollingContentsOffset != m_scrollingContentsOffset || |
| scrollSize != m_scrollingContentsLayer->size()) { |
| bool coordinatorHandlesOffset = |
| compositor()->scrollingLayerDidChange(&m_owningLayer); |
| m_scrollingContentsLayer->setPosition( |
| coordinatorHandlesOffset ? FloatPoint() |
| : FloatPoint(-toFloatSize(scrollPosition))); |
| } |
| m_scrollingContentsOffset = scrollingContentsOffset; |
| |
| m_scrollingContentsLayer->setSize(FloatSize(scrollSize)); |
| |
| IntPoint scrollingContentsLayerOffsetFromLayoutObject; |
| if (PaintLayerScrollableArea* scrollableArea = |
| m_owningLayer.getScrollableArea()) { |
| scrollingContentsLayerOffsetFromLayoutObject = |
| -scrollableArea->scrollOrigin(); |
| } |
| scrollingContentsLayerOffsetFromLayoutObject.moveBy( |
| overflowClipRect.location()); |
| |
| m_scrollingContentsLayer->setOffsetDoubleFromLayoutObject( |
| toIntSize(scrollingContentsLayerOffsetFromLayoutObject), |
| GraphicsLayer::DontSetNeedsDisplay); |
| |
| if (m_foregroundLayer) { |
| if (m_foregroundLayer->size() != m_scrollingContentsLayer->size()) |
| m_foregroundLayer->setSize(m_scrollingContentsLayer->size()); |
| m_foregroundLayer->setNeedsDisplay(); |
| m_foregroundLayer->setOffsetFromLayoutObject( |
| m_scrollingContentsLayer->offsetFromLayoutObject()); |
| } |
| } |
| |
| void CompositedLayerMapping::updateChildClippingMaskLayerGeometry() { |
| if (!m_childClippingMaskLayer || !layoutObject().style()->clipPath() || |
| !layoutObject().isBox()) |
| return; |
| LayoutBox& layoutBox = toLayoutBox(layoutObject()); |
| IntRect clientBox = enclosingIntRect(layoutBox.clientBoxRect()); |
| |
| m_childClippingMaskLayer->setPosition(m_graphicsLayer->position()); |
| m_childClippingMaskLayer->setSize(m_graphicsLayer->size()); |
| m_childClippingMaskLayer->setOffsetFromLayoutObject( |
| toIntSize(clientBox.location())); |
| |
| // NOTE: also some stuff happening in updateChildContainmentLayerGeometry(). |
| } |
| |
| void CompositedLayerMapping::updateForegroundLayerGeometry( |
| const FloatSize& relativeCompositingBoundsSize, |
| const IntRect& clippingBox) { |
| if (!m_foregroundLayer) |
| return; |
| |
| FloatSize foregroundSize = relativeCompositingBoundsSize; |
| IntSize foregroundOffset = m_graphicsLayer->offsetFromLayoutObject(); |
| m_foregroundLayer->setPosition(FloatPoint()); |
| |
| if (hasClippingLayer()) { |
| // If we have a clipping layer (which clips descendants), then the |
| // foreground layer is a child of it, so that it gets correctly sorted with |
| // children. In that case, position relative to the clipping layer. |
| foregroundSize = FloatSize(clippingBox.size()); |
| foregroundOffset = toIntSize(clippingBox.location()); |
| } else if (m_childTransformLayer) { |
| // Things are different if we have a child transform layer rather |
| // than a clipping layer. In this case, we want to actually change |
| // the position of the layer (to compensate for our ancestor |
| // compositing PaintLayer's position) rather than leave the position the |
| // same and use offset-from-layoutObject + size to describe a clipped |
| // "window" onto the clipped layer. |
| |
| m_foregroundLayer->setPosition(-m_childTransformLayer->position()); |
| } |
| |
| if (foregroundSize != m_foregroundLayer->size()) { |
| m_foregroundLayer->setSize(foregroundSize); |
| m_foregroundLayer->setNeedsDisplay(); |
| } |
| m_foregroundLayer->setOffsetFromLayoutObject(foregroundOffset); |
| |
| // NOTE: there is some more configuring going on in |
| // updateScrollingLayerGeometry(). |
| } |
| |
| void CompositedLayerMapping::updateBackgroundLayerGeometry( |
| const FloatSize& relativeCompositingBoundsSize) { |
| if (!m_backgroundLayer) |
| return; |
| |
| FloatSize backgroundSize = relativeCompositingBoundsSize; |
| if (backgroundLayerPaintsFixedRootBackground()) { |
| FrameView* frameView = toLayoutView(layoutObject()).frameView(); |
| backgroundSize = FloatSize(frameView->visibleContentRect().size()); |
| } |
| m_backgroundLayer->setPosition(FloatPoint()); |
| if (backgroundSize != m_backgroundLayer->size()) { |
| m_backgroundLayer->setSize(backgroundSize); |
| m_backgroundLayer->setNeedsDisplay(); |
| } |
| m_backgroundLayer->setOffsetFromLayoutObject( |
| m_graphicsLayer->offsetFromLayoutObject()); |
| } |
| |
| void CompositedLayerMapping::updateDecorationOutlineLayerGeometry( |
| const FloatSize& relativeCompositingBoundsSize) { |
| if (!m_decorationOutlineLayer) |
| return; |
| FloatSize decorationSize = relativeCompositingBoundsSize; |
| m_decorationOutlineLayer->setPosition(FloatPoint()); |
| if (decorationSize != m_decorationOutlineLayer->size()) { |
| m_decorationOutlineLayer->setSize(decorationSize); |
| m_decorationOutlineLayer->setNeedsDisplay(); |
| } |
| m_decorationOutlineLayer->setOffsetFromLayoutObject( |
| m_graphicsLayer->offsetFromLayoutObject()); |
| } |
| |
| void CompositedLayerMapping::registerScrollingLayers() { |
| // Register fixed position layers and their containers with the scrolling |
| // coordinator. |
| ScrollingCoordinator* scrollingCoordinator = |
| scrollingCoordinatorFromLayer(m_owningLayer); |
| if (!scrollingCoordinator) |
| return; |
| |
| scrollingCoordinator->updateLayerPositionConstraint(&m_owningLayer); |
| |
| // Page scale is applied as a transform on the root layout view layer. Because |
| // the scroll layer is further up in the hierarchy, we need to avoid marking |
| // the root layout view layer as a container. |
| bool isContainer = |
| m_owningLayer.layoutObject().canContainFixedPositionObjects() && |
| !m_owningLayer.isRootLayer(); |
| scrollingCoordinator->setLayerIsContainerForFixedPositionLayers( |
| m_graphicsLayer.get(), isContainer); |
| } |
| |
| void CompositedLayerMapping::updateInternalHierarchy() { |
| // m_foregroundLayer has to be inserted in the correct order with child |
| // layers, so it's not inserted here. |
| if (m_ancestorClippingLayer) |
| m_ancestorClippingLayer->removeAllChildren(); |
| |
| m_graphicsLayer->removeFromParent(); |
| |
| if (m_ancestorClippingLayer) |
| m_ancestorClippingLayer->addChild(m_graphicsLayer.get()); |
| |
| // Layer to which children should be attached as we build the hierarchy. |
| GraphicsLayer* bottomLayer = m_graphicsLayer.get(); |
| auto updateBottomLayer = [&bottomLayer](GraphicsLayer* layer) { |
| if (layer) { |
| bottomLayer->addChild(layer); |
| bottomLayer = layer; |
| } |
| }; |
| |
| updateBottomLayer(m_childTransformLayer.get()); |
| updateBottomLayer(m_childContainmentLayer.get()); |
| updateBottomLayer(m_scrollingLayer.get()); |
| |
| // Now constructing the subtree for the overflow controls. |
| bottomLayer = m_graphicsLayer.get(); |
| // TODO(pdr): Ensure painting uses the correct GraphicsLayer when root layer |
| // scrolls is enabled. crbug.com/638719 |
| if (m_isMainFrameLayoutViewLayer && |
| !RuntimeEnabledFeatures::slimmingPaintV2Enabled()) { |
| bottomLayer = |
| layoutObject().frame()->page()->visualViewport().containerLayer(); |
| } |
| updateBottomLayer(m_overflowControlsAncestorClippingLayer.get()); |
| updateBottomLayer(m_overflowControlsHostLayer.get()); |
| if (m_layerForHorizontalScrollbar) |
| m_overflowControlsHostLayer->addChild(m_layerForHorizontalScrollbar.get()); |
| if (m_layerForVerticalScrollbar) |
| m_overflowControlsHostLayer->addChild(m_layerForVerticalScrollbar.get()); |
| if (m_layerForScrollCorner) |
| m_overflowControlsHostLayer->addChild(m_layerForScrollCorner.get()); |
| |
| // Now add the DecorationOutlineLayer as a subtree to GraphicsLayer |
| if (m_decorationOutlineLayer.get()) |
| m_graphicsLayer->addChild(m_decorationOutlineLayer.get()); |
| |
| // The squashing containment layer, if it exists, becomes a no-op parent. |
| if (m_squashingLayer) { |
| ASSERT((m_ancestorClippingLayer && !m_squashingContainmentLayer) || |
| (!m_ancestorClippingLayer && m_squashingContainmentLayer)); |
| |
| if (m_squashingContainmentLayer) { |
| m_squashingContainmentLayer->removeAllChildren(); |
| m_squashingContainmentLayer->addChild(m_graphicsLayer.get()); |
| m_squashingContainmentLayer->addChild(m_squashingLayer.get()); |
| } else { |
| // The ancestor clipping layer is already set up and has m_graphicsLayer |
| // under it. |
| m_ancestorClippingLayer->addChild(m_squashingLayer.get()); |
| } |
| } |
| } |
| |
| void CompositedLayerMapping::updatePaintingPhases() { |
| m_graphicsLayer->setPaintingPhase(paintingPhaseForPrimaryLayer()); |
| if (m_scrollingContentsLayer) { |
| GraphicsLayerPaintingPhase paintPhase = |
| GraphicsLayerPaintOverflowContents | GraphicsLayerPaintCompositedScroll; |
| if (!m_foregroundLayer) |
| paintPhase |= GraphicsLayerPaintForeground; |
| m_scrollingContentsLayer->setPaintingPhase(paintPhase); |
| } |
| if (m_foregroundLayer) { |
| GraphicsLayerPaintingPhase paintPhase = GraphicsLayerPaintForeground; |
| if (m_scrollingContentsLayer) |
| paintPhase |= GraphicsLayerPaintOverflowContents; |
| m_foregroundLayer->setPaintingPhase(paintPhase); |
| } |
| } |
| |
| void CompositedLayerMapping::updateContentsRect() { |
| m_graphicsLayer->setContentsRect(pixelSnappedIntRect(contentsBox())); |
| } |
| |
| void CompositedLayerMapping::updateContentsOffsetInCompositingLayer( |
| const IntPoint& snappedOffsetFromCompositedAncestor, |
| const IntPoint& graphicsLayerParentLocation) { |
| // m_graphicsLayer is positioned relative to our compositing ancestor |
| // PaintLayer, but it's not positioned at the origin of m_owningLayer, it's |
| // offset by m_contentBounds.location(). This is what |
| // contentOffsetInCompositingLayer is meant to capture, roughly speaking |
| // (ignoring rounding and subpixel accumulation). |
| // |
| // Our ancestor graphics layers in this CLM (m_graphicsLayer and potentially |
| // m_ancestorClippingLayer) have pixel snapped, so if we don't adjust this |
| // offset, we'll see accumulated rounding errors due to that snapping. |
| // |
| // In order to ensure that we account for this rounding, we compute |
| // contentsOffsetInCompositingLayer in a somewhat roundabout way. |
| // |
| // our position = (desired position) - (inherited graphics layer offset). |
| // |
| // Precisely, |
| // Offset = snappedOffsetFromCompositedAncestor - |
| // offsetDueToAncestorGraphicsLayers (See code below) |
| // = snappedOffsetFromCompositedAncestor - |
| // (m_graphicsLayer->position() + graphicsLayerParentLocation) |
| // = snappedOffsetFromCompositedAncestor - |
| // (relativeCompositingBounds.location() - |
| // graphicsLayerParentLocation + |
| // graphicsLayerParentLocation) |
| // (See updateMainGraphicsLayerGeometry) |
| // = snappedOffsetFromCompositedAncestor - |
| // relativeCompositingBounds.location() |
| // = snappedOffsetFromCompositedAncestor - |
| // (pixelSnappedIntRect(contentBounds.location()) + |
| // snappedOffsetFromCompositedAncestor) |
| // (See computeBoundsOfOwningLayer) |
| // = -pixelSnappedIntRect(contentBounds.location()) |
| // |
| // As you can see, we've ended up at the same spot |
| // (-contentBounds.location()), but by subtracting off our ancestor graphics |
| // layers positions, we can be sure we've accounted correctly for any pixel |
| // snapping due to ancestor graphics layers. |
| // |
| // And drawing of composited children takes into account the subpixel |
| // accumulation of this CLM already (through its own |
| // graphicsLayerParentLocation it appears). |
| FloatPoint offsetDueToAncestorGraphicsLayers = |
| m_graphicsLayer->position() + graphicsLayerParentLocation; |
| m_contentOffsetInCompositingLayer = LayoutSize( |
| snappedOffsetFromCompositedAncestor - offsetDueToAncestorGraphicsLayers); |
| m_contentOffsetInCompositingLayerDirty = false; |
| } |
| |
| void CompositedLayerMapping::updateDrawsContent() { |
| bool inOverlayFullscreenVideo = false; |
| if (layoutObject().isVideo()) { |
| HTMLVideoElement* videoElement = toHTMLVideoElement(layoutObject().node()); |
| if (videoElement->isFullscreen() && |
| videoElement->usesOverlayFullscreenVideo()) |
| inOverlayFullscreenVideo = true; |
| } |
| bool hasPaintedContent = |
| inOverlayFullscreenVideo ? false : containsPaintedContent(); |
| m_graphicsLayer->setDrawsContent(hasPaintedContent); |
| |
| if (m_scrollingLayer) { |
| // m_scrollingLayer never has backing store. |
| // m_scrollingContentsLayer only needs backing store if the scrolled |
| // contents need to paint. |
| m_scrollingContentsAreEmpty = |
| !m_owningLayer.hasVisibleContent() || |
| !(layoutObject().styleRef().hasBackground() || |
| layoutObject().hasBackdropFilter() || paintsChildren()); |
| m_scrollingContentsLayer->setDrawsContent(!m_scrollingContentsAreEmpty); |
| } |
| |
| m_drawsBackgroundOntoContentLayer = false; |
| |
| if (hasPaintedContent && isAcceleratedCanvas(layoutObject())) { |
| CanvasRenderingContext* context = |
| toHTMLCanvasElement(layoutObject().node())->renderingContext(); |
| // Content layer may be null if context is lost. |
| if (WebLayer* contentLayer = context->platformLayer()) { |
| Color bgColor(Color::transparent); |
| if (contentLayerSupportsDirectBackgroundComposition(layoutObject())) { |
| bgColor = layoutObjectBackgroundColor(); |
| hasPaintedContent = false; |
| m_drawsBackgroundOntoContentLayer = true; |
| } |
| contentLayer->setBackgroundColor(bgColor.rgb()); |
| } |
| } |
| |
| // FIXME: we could refine this to only allocate backings for one of these |
| // layers if possible. |
| if (m_foregroundLayer) |
| m_foregroundLayer->setDrawsContent(hasPaintedContent); |
| |
| // TODO(yigu): The background should no longer setDrawsContent(true) if we |
| // only have an outline and we are drawing the outline into the decoration |
| // layer (i.e. if there is nothing actually drawn into the |
| // background anymore.) |
| // "hasPaintedContent" should be calculated in a way that does not take the |
| // outline into consideration. |
| if (m_backgroundLayer) |
| m_backgroundLayer->setDrawsContent(hasPaintedContent); |
| |
| if (m_decorationOutlineLayer) |
| m_decorationOutlineLayer->setDrawsContent(true); |
| |
| if (m_ancestorClippingMaskLayer) |
| m_ancestorClippingMaskLayer->setDrawsContent(true); |
| |
| if (m_maskLayer) |
| m_maskLayer->setDrawsContent(true); |
| |
| if (m_childClippingMaskLayer) |
| m_childClippingMaskLayer->setDrawsContent(true); |
| } |
| |
| void CompositedLayerMapping::updateChildrenTransform() { |
| if (GraphicsLayer* childTransformLayer = this->childTransformLayer()) { |
| childTransformLayer->setTransform(owningLayer().perspectiveTransform()); |
| childTransformLayer->setTransformOrigin(owningLayer().perspectiveOrigin()); |
| } |
| |
| updateShouldFlattenTransform(); |
| } |
| |
| // Return true if the layers changed. |
| bool CompositedLayerMapping::updateClippingLayers( |
| bool needsAncestorClip, |
| bool needsAncestorClippingMask, |
| bool needsDescendantClip) { |
| bool layersChanged = false; |
| |
| if (needsAncestorClip) { |
| if (!m_ancestorClippingLayer) { |
| m_ancestorClippingLayer = |
| createGraphicsLayer(CompositingReasonLayerForAncestorClip); |
| m_ancestorClippingLayer->setMasksToBounds(true); |
| m_ancestorClippingLayer->setShouldFlattenTransform(false); |
| layersChanged = true; |
| } |
| } else if (m_ancestorClippingLayer) { |
| if (m_ancestorClippingMaskLayer) { |
| m_ancestorClippingMaskLayer->removeFromParent(); |
| m_ancestorClippingMaskLayer = nullptr; |
| } |
| m_ancestorClippingLayer->removeFromParent(); |
| m_ancestorClippingLayer = nullptr; |
| layersChanged = true; |
| } |
| |
| if (needsAncestorClippingMask) { |
| DCHECK(m_ancestorClippingLayer); |
| if (!m_ancestorClippingMaskLayer) { |
| m_ancestorClippingMaskLayer = |
| createGraphicsLayer(CompositingReasonLayerForAncestorClippingMask); |
| m_ancestorClippingMaskLayer->setPaintingPhase( |
| GraphicsLayerPaintAncestorClippingMask); |
| m_ancestorClippingLayer->setMaskLayer(m_ancestorClippingMaskLayer.get()); |
| layersChanged = true; |
| } |
| } else if (m_ancestorClippingMaskLayer) { |
| m_ancestorClippingMaskLayer->removeFromParent(); |
| m_ancestorClippingMaskLayer = nullptr; |
| m_ancestorClippingLayer->setMaskLayer(nullptr); |
| layersChanged = true; |
| } |
| |
| if (needsDescendantClip) { |
| // We don't need a child containment layer if we're the main frame layout |
| // view layer. It's redundant as the frame clip above us will handle this |
| // clipping. |
| if (!m_childContainmentLayer && !m_isMainFrameLayoutViewLayer) { |
| m_childContainmentLayer = |
| createGraphicsLayer(CompositingReasonLayerForDescendantClip); |
| m_childContainmentLayer->setMasksToBounds(true); |
| layersChanged = true; |
| } |
| } else if (hasClippingLayer()) { |
| m_childContainmentLayer->removeFromParent(); |
| m_childContainmentLayer = nullptr; |
| layersChanged = true; |
| } |
| |
| return layersChanged; |
| } |
| |
| bool CompositedLayerMapping::updateChildTransformLayer( |
| bool needsChildTransformLayer) { |
| bool layersChanged = false; |
| |
| if (needsChildTransformLayer) { |
| if (!m_childTransformLayer) { |
| m_childTransformLayer = |
| createGraphicsLayer(CompositingReasonLayerForPerspective); |
| m_childTransformLayer->setDrawsContent(false); |
| layersChanged = true; |
| } |
| } else if (m_childTransformLayer) { |
| m_childTransformLayer->removeFromParent(); |
| m_childTransformLayer = nullptr; |
| layersChanged = true; |
| } |
| |
| return layersChanged; |
| } |
| |
| void CompositedLayerMapping::setBackgroundLayerPaintsFixedRootBackground( |
| bool backgroundLayerPaintsFixedRootBackground) { |
| m_backgroundLayerPaintsFixedRootBackground = |
| backgroundLayerPaintsFixedRootBackground; |
| } |
| |
| bool CompositedLayerMapping::toggleScrollbarLayerIfNeeded( |
| std::unique_ptr<GraphicsLayer>& layer, |
| bool needsLayer, |
| CompositingReasons reason) { |
| if (needsLayer == !!layer) |
| return false; |
| layer = needsLayer ? createGraphicsLayer(reason) : nullptr; |
| |
| if (PaintLayerScrollableArea* scrollableArea = |
| m_owningLayer.getScrollableArea()) { |
| if (ScrollingCoordinator* scrollingCoordinator = |
| scrollingCoordinatorFromLayer(m_owningLayer)) { |
| if (reason == CompositingReasonLayerForHorizontalScrollbar) |
| scrollingCoordinator->scrollableAreaScrollbarLayerDidChange( |
| scrollableArea, HorizontalScrollbar); |
| else if (reason == CompositingReasonLayerForVerticalScrollbar) |
| scrollingCoordinator->scrollableAreaScrollbarLayerDidChange( |
| scrollableArea, VerticalScrollbar); |
| } |
| } |
| return true; |
| } |
| |
| bool CompositedLayerMapping::updateOverflowControlsLayers( |
| bool needsHorizontalScrollbarLayer, |
| bool needsVerticalScrollbarLayer, |
| bool needsScrollCornerLayer, |
| bool needsAncestorClip) { |
| if (PaintLayerScrollableArea* scrollableArea = |
| m_owningLayer.getScrollableArea()) { |
| // If the scrollable area is marked as needing a new scrollbar layer, |
| // destroy the layer now so that it will be created again below. |
| if (m_layerForHorizontalScrollbar && needsHorizontalScrollbarLayer && |
| scrollableArea->shouldRebuildHorizontalScrollbarLayer()) |
| toggleScrollbarLayerIfNeeded( |
| m_layerForHorizontalScrollbar, false, |
| CompositingReasonLayerForHorizontalScrollbar); |
| if (m_layerForVerticalScrollbar && needsVerticalScrollbarLayer && |
| scrollableArea->shouldRebuildVerticalScrollbarLayer()) |
| toggleScrollbarLayerIfNeeded(m_layerForVerticalScrollbar, false, |
| CompositingReasonLayerForVerticalScrollbar); |
| scrollableArea->resetRebuildScrollbarLayerFlags(); |
| } |
| |
| // If the subtree is invisible, we don't actually need scrollbar layers. |
| // Only do this check if at least one of the bits is currently true. |
| // This is important because this method is called during the destructor |
| // of CompositedLayerMapping, which may happen during style recalc, |
| // and therefore visible content status may be invalid. |
| if (needsHorizontalScrollbarLayer || needsVerticalScrollbarLayer || |
| needsScrollCornerLayer) { |
| bool invisible = m_owningLayer.subtreeIsInvisible(); |
| needsHorizontalScrollbarLayer &= !invisible; |
| needsVerticalScrollbarLayer &= !invisible; |
| needsScrollCornerLayer &= !invisible; |
| } |
| |
| bool horizontalScrollbarLayerChanged = toggleScrollbarLayerIfNeeded( |
| m_layerForHorizontalScrollbar, needsHorizontalScrollbarLayer, |
| CompositingReasonLayerForHorizontalScrollbar); |
| bool verticalScrollbarLayerChanged = toggleScrollbarLayerIfNeeded( |
| m_layerForVerticalScrollbar, needsVerticalScrollbarLayer, |
| CompositingReasonLayerForVerticalScrollbar); |
| bool scrollCornerLayerChanged = toggleScrollbarLayerIfNeeded( |
| m_layerForScrollCorner, needsScrollCornerLayer, |
| CompositingReasonLayerForScrollCorner); |
| |
| bool needsOverflowControlsHostLayer = needsHorizontalScrollbarLayer || |
| needsVerticalScrollbarLayer || |
| needsScrollCornerLayer; |
| toggleScrollbarLayerIfNeeded(m_overflowControlsHostLayer, |
| needsOverflowControlsHostLayer, |
| CompositingReasonLayerForOverflowControlsHost); |
| bool needsOverflowAncestorClipLayer = |
| needsOverflowControlsHostLayer && needsAncestorClip; |
| toggleScrollbarLayerIfNeeded(m_overflowControlsAncestorClippingLayer, |
| needsOverflowAncestorClipLayer, |
| CompositingReasonLayerForOverflowControlsHost); |
| |
| return horizontalScrollbarLayerChanged || verticalScrollbarLayerChanged || |
| scrollCornerLayerChanged; |
| } |
| |
| void CompositedLayerMapping::positionOverflowControlsLayers() { |
| if (GraphicsLayer* layer = layerForHorizontalScrollbar()) { |
| Scrollbar* hBar = m_owningLayer.getScrollableArea()->horizontalScrollbar(); |
| if (hBar) { |
| layer->setPosition(hBar->frameRect().location()); |
| layer->setSize(FloatSize(hBar->frameRect().size())); |
| if (layer->hasContentsLayer()) |
| layer->setContentsRect(IntRect(IntPoint(), hBar->frameRect().size())); |
| } |
| layer->setDrawsContent(hBar && !layer->hasContentsLayer()); |
| } |
| |
| if (GraphicsLayer* layer = layerForVerticalScrollbar()) { |
| Scrollbar* vBar = m_owningLayer.getScrollableArea()->verticalScrollbar(); |
| if (vBar) { |
| layer->setPosition(vBar->frameRect().location()); |
| layer->setSize(FloatSize(vBar->frameRect().size())); |
| if (layer->hasContentsLayer()) |
| layer->setContentsRect(IntRect(IntPoint(), vBar->frameRect().size())); |
| } |
| layer->setDrawsContent(vBar && !layer->hasContentsLayer()); |
| } |
| |
| if (GraphicsLayer* layer = layerForScrollCorner()) { |
| const IntRect& scrollCornerAndResizer = |
| m_owningLayer.getScrollableArea()->scrollCornerAndResizerRect(); |
| layer->setPosition(FloatPoint(scrollCornerAndResizer.location())); |
| layer->setSize(FloatSize(scrollCornerAndResizer.size())); |
| layer->setDrawsContent(!scrollCornerAndResizer.isEmpty()); |
| } |
| } |
| |
| enum ApplyToGraphicsLayersModeFlags { |
| ApplyToLayersAffectedByPreserve3D = (1 << 0), |
| ApplyToSquashingLayer = (1 << 1), |
| ApplyToScrollbarLayers = (1 << 2), |
| ApplyToBackgroundLayer = (1 << 3), |
| ApplyToMaskLayers = (1 << 4), |
| ApplyToContentLayers = (1 << 5), |
| ApplyToChildContainingLayers = |
| (1 << 6), // layers between m_graphicsLayer and children |
| ApplyToNonScrollingContentLayers = (1 << 7), |
| ApplyToScrollingContentLayers = (1 << 8), |
| ApplyToDecorationOutlineLayer = (1 << 9), |
| ApplyToAllGraphicsLayers = |
| (ApplyToSquashingLayer | ApplyToScrollbarLayers | ApplyToBackgroundLayer | |
| ApplyToMaskLayers | |
| ApplyToLayersAffectedByPreserve3D | |
| ApplyToContentLayers | |
| ApplyToScrollingContentLayers | |
| ApplyToDecorationOutlineLayer) |
| }; |
| typedef unsigned ApplyToGraphicsLayersMode; |
| |
| template <typename Func> |
| static void ApplyToGraphicsLayers(const CompositedLayerMapping* mapping, |
| const Func& f, |
| ApplyToGraphicsLayersMode mode) { |
| ASSERT(mode); |
| |
| if ((mode & ApplyToLayersAffectedByPreserve3D) && |
| mapping->childTransformLayer()) |
| f(mapping->childTransformLayer()); |
| if (((mode & ApplyToLayersAffectedByPreserve3D) || |
| (mode & ApplyToContentLayers) || |
| (mode & ApplyToNonScrollingContentLayers)) && |
| mapping->mainGraphicsLayer()) |
| f(mapping->mainGraphicsLayer()); |
| if (((mode & ApplyToLayersAffectedByPreserve3D) || |
| (mode & ApplyToChildContainingLayers)) && |
| mapping->clippingLayer()) |
| f(mapping->clippingLayer()); |
| if (((mode & ApplyToLayersAffectedByPreserve3D) || |
| (mode & ApplyToChildContainingLayers)) && |
| mapping->scrollingLayer()) |
| f(mapping->scrollingLayer()); |
| if (((mode & ApplyToLayersAffectedByPreserve3D) || |
| (mode & ApplyToContentLayers) || (mode & ApplyToChildContainingLayers) || |
| (mode & ApplyToScrollingContentLayers)) && |
| mapping->scrollingContentsLayer()) |
| f(mapping->scrollingContentsLayer()); |
| if (((mode & ApplyToLayersAffectedByPreserve3D) || |
| (mode & ApplyToContentLayers) || |
| (mode & ApplyToScrollingContentLayers)) && |
| mapping->foregroundLayer()) |
| f(mapping->foregroundLayer()); |
| |
| if ((mode & ApplyToChildContainingLayers) && mapping->childTransformLayer()) |
| f(mapping->childTransformLayer()); |
| |
| if ((mode & ApplyToSquashingLayer) && mapping->squashingLayer()) |
| f(mapping->squashingLayer()); |
| |
| if (((mode & ApplyToMaskLayers) || (mode & ApplyToContentLayers) || |
| (mode & ApplyToNonScrollingContentLayers)) && |
| mapping->maskLayer()) |
| f(mapping->maskLayer()); |
| if (((mode & ApplyToMaskLayers) || (mode & ApplyToContentLayers) || |
| (mode & ApplyToNonScrollingContentLayers)) && |
| mapping->childClippingMaskLayer()) |
| f(mapping->childClippingMaskLayer()); |
| if (((mode & ApplyToMaskLayers) || (mode & ApplyToContentLayers) || |
| (mode & ApplyToNonScrollingContentLayers)) && |
| mapping->ancestorClippingMaskLayer()) |
| f(mapping->ancestorClippingMaskLayer()); |
| |
| if (((mode & ApplyToBackgroundLayer) || (mode & ApplyToContentLayers) || |
| (mode & ApplyToNonScrollingContentLayers)) && |
| mapping->backgroundLayer()) |
| f(mapping->backgroundLayer()); |
| |
| if ((mode & ApplyToScrollbarLayers) && mapping->layerForHorizontalScrollbar()) |
| f(mapping->layerForHorizontalScrollbar()); |
| if ((mode & ApplyToScrollbarLayers) && mapping->layerForVerticalScrollbar()) |
| f(mapping->layerForVerticalScrollbar()); |
| if ((mode & ApplyToScrollbarLayers) && mapping->layerForScrollCorner()) |
| f(mapping->layerForScrollCorner()); |
| |
| if (((mode & ApplyToDecorationOutlineLayer) || |
| (mode & ApplyToNonScrollingContentLayers)) && |
| mapping->decorationOutlineLayer()) |
| f(mapping->decorationOutlineLayer()); |
| } |
| |
| struct UpdateRenderingContextFunctor { |
| void operator()(GraphicsLayer* layer) const { |
| layer->setRenderingContext(renderingContext); |
| } |
| int renderingContext; |
| }; |
| |
| void CompositedLayerMapping::updateRenderingContext() { |
| // All layers but the squashing layer (which contains 'alien' content) should |
| // be included in this rendering context. |
| int id = 0; |
| |
| // NB, it is illegal at this point to query an ancestor's compositing state. |
| // Some compositing reasons depend on the compositing state of ancestors. So |
| // if we want a rendering context id for the context root, we cannot ask for |
| // the id of its associated WebLayer now; it may not have one yet. We could do |
| // a second pass after doing the compositing updates to get these ids, but |
| // this would actually be harmful. We do not want to attach any semantic |
| // meaning to the context id other than the fact that they group a number of |
| // layers together for the sake of 3d sorting. So instead we will ask the |
| // compositor to vend us an arbitrary, but consistent id. |
| if (PaintLayer* root = m_owningLayer.renderingContextRoot()) { |
| if (Node* node = root->layoutObject().node()) |
| id = static_cast<int>(PtrHash<Node>::hash(node)); |
| } |
| |
| UpdateRenderingContextFunctor functor = {id}; |
| ApplyToGraphicsLayers<UpdateRenderingContextFunctor>( |
| this, functor, ApplyToAllGraphicsLayers); |
| } |
| |
| struct UpdateShouldFlattenTransformFunctor { |
| void operator()(GraphicsLayer* layer) const { |
| layer->setShouldFlattenTransform(shouldFlatten); |
| } |
| bool shouldFlatten; |
| }; |
| |
| void CompositedLayerMapping::updateShouldFlattenTransform() { |
| // All CLM-managed layers that could affect a descendant layer should update |
| // their should-flatten-transform value (the other layers' transforms don't |
| // matter here). |
| UpdateShouldFlattenTransformFunctor functor = { |
| !m_owningLayer.shouldPreserve3D()}; |
| ApplyToGraphicsLayersMode mode = ApplyToLayersAffectedByPreserve3D; |
| ApplyToGraphicsLayers(this, functor, mode); |
| |
| // Note, if we apply perspective, we have to set should flatten differently |
| // so that the transform propagates to child layers correctly. |
| if (hasChildTransformLayer()) { |
| ApplyToGraphicsLayers( |
| this, |
| [](GraphicsLayer* layer) { layer->setShouldFlattenTransform(false); }, |
| ApplyToChildContainingLayers); |
| } |
| |
| // Regardless, mark the graphics layer, scrolling layer and scrolling block |
| // selection layer (if they exist) as not flattening. Having them flatten |
| // causes unclipped render surfaces which cause bugs. |
| // http://crbug.com/521768 |
| if (hasScrollingLayer()) { |
| m_graphicsLayer->setShouldFlattenTransform(false); |
| m_scrollingLayer->setShouldFlattenTransform(false); |
| } |
| } |
| |
| // Some background on when you receive an element id or mutable properties. |
| // |
| // element id: |
| // If you have a compositor proxy, an animation, or you're a scroller (and |
| // might impl animate). |
| // |
| // mutable properties: |
| // Only if you have a compositor proxy. |
| // |
| // The element id for the scroll layers is assigned when they're constructed, |
| // since this is unconditional. However, the element id for the primary layer as |
| // well as the mutable properties for all layers may change according to the |
| // rules above so we update those values here. |
| void CompositedLayerMapping::updateElementIdAndCompositorMutableProperties() { |
| int elementId = 0; |
| uint32_t primaryMutableProperties = CompositorMutableProperty::kNone; |
| uint32_t scrollMutableProperties = CompositorMutableProperty::kNone; |
| |
| Node* owningNode = m_owningLayer.layoutObject().node(); |
| Element* animatingElement = nullptr; |
| const ComputedStyle* animatingStyle = nullptr; |
| if (owningNode) { |
| Document& document = owningNode->document(); |
| Element* scrollingElement = document.scrollingElementNoLayout(); |
| if (owningNode->isElementNode() && |
| (!RuntimeEnabledFeatures::rootLayerScrollingEnabled() || |
| owningNode != scrollingElement)) { |
| animatingElement = toElement(owningNode); |
| animatingStyle = m_owningLayer.layoutObject().style(); |
| } else if (owningNode->isDocumentNode() && |
| RuntimeEnabledFeatures::rootLayerScrollingEnabled()) { |
| owningNode = animatingElement = scrollingElement; |
| if (scrollingElement && scrollingElement->layoutObject()) |
| animatingStyle = scrollingElement->layoutObject()->style(); |
| } |
| } |
| |
| if (RuntimeEnabledFeatures::compositorWorkerEnabled() && animatingStyle && |
| animatingStyle->hasCompositorProxy()) { |
| uint32_t compositorMutableProperties = |
| animatingElement->compositorMutableProperties(); |
| elementId = DOMNodeIds::idForNode(owningNode); |
| primaryMutableProperties = (CompositorMutableProperty::kOpacity | |
| CompositorMutableProperty::kTransform) & |
| compositorMutableProperties; |
| scrollMutableProperties = (CompositorMutableProperty::kScrollLeft | |
| CompositorMutableProperty::kScrollTop) & |
| compositorMutableProperties; |
| } |
| |
| if (animatingStyle && animatingStyle->shouldCompositeForCurrentAnimations()) |
| elementId = DOMNodeIds::idForNode(owningNode); |
| |
| CompositorElementId compositorElementId; |
| if (elementId) |
| compositorElementId = |
| createCompositorElementId(elementId, CompositorSubElementId::Primary); |
| |
| m_graphicsLayer->setElementId(compositorElementId); |
| m_graphicsLayer->setCompositorMutableProperties(primaryMutableProperties); |
| |
| // We always set the elementId for m_scrollingContentsLayer since it can be |
| // animated for smooth scrolling, so we don't need to set it conditionally |
| // here. |
| if (m_scrollingContentsLayer.get()) |
| m_scrollingContentsLayer->setCompositorMutableProperties( |
| scrollMutableProperties); |
| } |
| |
| bool CompositedLayerMapping::updateForegroundLayer(bool needsForegroundLayer) { |
| bool layerChanged = false; |
| if (needsForegroundLayer) { |
| if (!m_foregroundLayer) { |
| m_foregroundLayer = |
| createGraphicsLayer(CompositingReasonLayerForForeground); |
| layerChanged = true; |
| } |
| } else if (m_foregroundLayer) { |
| m_foregroundLayer->removeFromParent(); |
| m_foregroundLayer = nullptr; |
| layerChanged = true; |
| } |
| |
| return layerChanged; |
| } |
| |
| bool CompositedLayerMapping::updateBackgroundLayer(bool needsBackgroundLayer) { |
| bool layerChanged = false; |
| if (needsBackgroundLayer) { |
| if (!m_backgroundLayer) { |
| m_backgroundLayer = |
| createGraphicsLayer(CompositingReasonLayerForBackground); |
| m_backgroundLayer->setTransformOrigin(FloatPoint3D()); |
| m_backgroundLayer->setPaintingPhase(GraphicsLayerPaintBackground); |
| layerChanged = true; |
| } |
| } else { |
| if (m_backgroundLayer) { |
| m_backgroundLayer->removeFromParent(); |
| m_backgroundLayer = nullptr; |
| layerChanged = true; |
| } |
| } |
| |
| if (layerChanged && !m_owningLayer.layoutObject().documentBeingDestroyed()) |
| compositor()->rootFixedBackgroundsChanged(); |
| |
| return layerChanged; |
| } |
| |
| bool CompositedLayerMapping::updateDecorationOutlineLayer( |
| bool needsDecorationOutlineLayer) { |
| bool layerChanged = false; |
| if (needsDecorationOutlineLayer) { |
| if (!m_decorationOutlineLayer) { |
| m_decorationOutlineLayer = |
| createGraphicsLayer(CompositingReasonLayerForDecoration); |
| m_decorationOutlineLayer->setPaintingPhase(GraphicsLayerPaintDecoration); |
| layerChanged = true; |
| } |
| } else if (m_decorationOutlineLayer) { |
| m_decorationOutlineLayer = nullptr; |
| layerChanged = true; |
| } |
| |
| return layerChanged; |
| } |
| |
| bool CompositedLayerMapping::updateMaskLayer(bool needsMaskLayer) { |
| bool layerChanged = false; |
| if (needsMaskLayer) { |
| if (!m_maskLayer) { |
| m_maskLayer = createGraphicsLayer(CompositingReasonLayerForMask); |
| m_maskLayer->setPaintingPhase(GraphicsLayerPaintMask); |
| layerChanged = true; |
| } |
| } else if (m_maskLayer) { |
| m_maskLayer = nullptr; |
| layerChanged = true; |
| } |
| |
| return layerChanged; |
| } |
| |
| void CompositedLayerMapping::updateChildClippingMaskLayer( |
| bool needsChildClippingMaskLayer) { |
| if (needsChildClippingMaskLayer) { |
| if (!m_childClippingMaskLayer) { |
| m_childClippingMaskLayer = |
| createGraphicsLayer(CompositingReasonLayerForClippingMask); |
| m_childClippingMaskLayer->setPaintingPhase( |
| GraphicsLayerPaintChildClippingMask); |
| } |
| return; |
| } |
| m_childClippingMaskLayer = nullptr; |
| } |
| |
| bool CompositedLayerMapping::updateScrollingLayers(bool needsScrollingLayers) { |
| ScrollingCoordinator* scrollingCoordinator = |
| scrollingCoordinatorFromLayer(m_owningLayer); |
| |
| bool layerChanged = false; |
| if (needsScrollingLayers) { |
| if (!m_scrollingLayer) { |
| // Outer layer which corresponds with the scroll view. |
| m_scrollingLayer = |
| createGraphicsLayer(CompositingReasonLayerForScrollingContainer); |
| m_scrollingLayer->setDrawsContent(false); |
| m_scrollingLayer->setMasksToBounds(true); |
| |
| // Inner layer which renders the content that scrolls. |
| m_scrollingContentsLayer = |
| createGraphicsLayer(CompositingReasonLayerForScrollingContents); |
| |
| if (Node* owningNode = m_owningLayer.layoutObject().node()) |
| m_scrollingContentsLayer->setElementId(createCompositorElementId( |
| DOMNodeIds::idForNode(owningNode), CompositorSubElementId::Scroll)); |
| |
| m_scrollingLayer->addChild(m_scrollingContentsLayer.get()); |
| |
| layerChanged = true; |
| if (scrollingCoordinator) { |
| scrollingCoordinator->scrollableAreaScrollLayerDidChange( |
| m_owningLayer.getScrollableArea()); |
| scrollingCoordinator->scrollableAreasDidChange(); |
| } |
| } |
| } else if (m_scrollingLayer) { |
| m_scrollingLayer = nullptr; |
| m_scrollingContentsLayer = nullptr; |
| layerChanged = true; |
| if (scrollingCoordinator) { |
| scrollingCoordinator->scrollableAreaScrollLayerDidChange( |
| m_owningLayer.getScrollableArea()); |
| scrollingCoordinator->scrollableAreasDidChange(); |
| } |
| } |
| |
| return layerChanged; |
| } |
| |
| static void updateScrollParentForGraphicsLayer( |
| GraphicsLayer* layer, |
| GraphicsLayer* topmostLayer, |
| const PaintLayer* scrollParent, |
| ScrollingCoordinator* scrollingCoordinator) { |
| if (!layer) |
| return; |
| |
| // Only the topmost layer has a scroll parent. All other layers have a null |
| // scroll parent. |
| if (layer != topmostLayer) |
| scrollParent = 0; |
| |
| scrollingCoordinator->updateScrollParentForGraphicsLayer(layer, scrollParent); |
| } |
| |
| void CompositedLayerMapping::updateScrollParent( |
| const PaintLayer* scrollParent) { |
| if (ScrollingCoordinator* scrollingCoordinator = |
| scrollingCoordinatorFromLayer(m_owningLayer)) { |
| GraphicsLayer* topmostLayer = childForSuperlayers(); |
| updateScrollParentForGraphicsLayer(m_squashingContainmentLayer.get(), |
| topmostLayer, scrollParent, |
| scrollingCoordinator); |
| updateScrollParentForGraphicsLayer(m_ancestorClippingLayer.get(), |
| topmostLayer, scrollParent, |
| scrollingCoordinator); |
| updateScrollParentForGraphicsLayer(m_graphicsLayer.get(), topmostLayer, |
| scrollParent, scrollingCoordinator); |
| } |
| } |
| |
| static void updateClipParentForGraphicsLayer( |
| GraphicsLayer* layer, |
| GraphicsLayer* topmostLayer, |
| const PaintLayer* clipParent, |
| ScrollingCoordinator* scrollingCoordinator) { |
| if (!layer) |
| return; |
| |
| // Only the topmost layer has a scroll parent. All other layers have a null |
| // scroll parent. |
| if (layer != topmostLayer) |
| clipParent = 0; |
| |
| scrollingCoordinator->updateClipParentForGraphicsLayer(layer, clipParent); |
| } |
| |
| void CompositedLayerMapping::updateClipParent(const PaintLayer* scrollParent) { |
| const PaintLayer* clipParent = nullptr; |
| bool haveAncestorClipLayer = false; |
| bool haveAncestorMaskLayer = false; |
| owningLayerClippedOrMaskedByLayerNotAboveCompositedAncestor( |
| scrollParent, haveAncestorClipLayer, haveAncestorMaskLayer); |
| if (!haveAncestorClipLayer) { |
| clipParent = m_owningLayer.clipParent(); |
| if (clipParent) |
| clipParent = |
| clipParent->enclosingLayerWithCompositedLayerMapping(IncludeSelf); |
| } |
| |
| if (ScrollingCoordinator* scrollingCoordinator = |
| scrollingCoordinatorFromLayer(m_owningLayer)) { |
| GraphicsLayer* topmostLayer = childForSuperlayers(); |
| updateClipParentForGraphicsLayer(m_squashingContainmentLayer.get(), |
| topmostLayer, clipParent, |
| scrollingCoordinator); |
| updateClipParentForGraphicsLayer(m_ancestorClippingLayer.get(), |
| topmostLayer, clipParent, |
| scrollingCoordinator); |
| updateClipParentForGraphicsLayer(m_graphicsLayer.get(), topmostLayer, |
| clipParent, scrollingCoordinator); |
| } |
| } |
| |
| bool CompositedLayerMapping::updateSquashingLayers(bool needsSquashingLayers) { |
| bool layersChanged = false; |
| |
| if (needsSquashingLayers) { |
| if (!m_squashingLayer) { |
| m_squashingLayer = |
| createGraphicsLayer(CompositingReasonLayerForSquashingContents); |
| m_squashingLayer->setDrawsContent(true); |
| layersChanged = true; |
| } |
| |
| if (m_ancestorClippingLayer) { |
| if (m_squashingContainmentLayer) { |
| m_squashingContainmentLayer->removeFromParent(); |
| m_squashingContainmentLayer = nullptr; |
| layersChanged = true; |
| } |
| } else { |
| if (!m_squashingContainmentLayer) { |
| m_squashingContainmentLayer = |
| createGraphicsLayer(CompositingReasonLayerForSquashingContainer); |
| m_squashingContainmentLayer->setShouldFlattenTransform(false); |
| layersChanged = true; |
| } |
| } |
| |
| ASSERT((m_ancestorClippingLayer && !m_squashingContainmentLayer) || |
| (!m_ancestorClippingLayer && m_squashingContainmentLayer)); |
| ASSERT(m_squashingLayer); |
| } else { |
| if (m_squashingLayer) { |
| m_squashingLayer->removeFromParent(); |
| m_squashingLayer = nullptr; |
| layersChanged = true; |
| } |
| if (m_squashingContainmentLayer) { |
| m_squashingContainmentLayer->removeFromParent(); |
| m_squashingContainmentLayer = nullptr; |
| layersChanged = true; |
| } |
| ASSERT(!m_squashingLayer && !m_squashingContainmentLayer); |
| } |
| |
| return layersChanged; |
| } |
| |
| GraphicsLayerPaintingPhase |
| CompositedLayerMapping::paintingPhaseForPrimaryLayer() const { |
| unsigned phase = 0; |
| if (!m_backgroundLayer) |
| phase |= GraphicsLayerPaintBackground; |
| if (!m_foregroundLayer) |
| phase |= GraphicsLayerPaintForeground; |
| if (!m_maskLayer) |
| phase |= GraphicsLayerPaintMask; |
| if (!m_decorationOutlineLayer) |
| phase |= GraphicsLayerPaintDecoration; |
| |
| if (m_scrollingContentsLayer) { |
| phase &= ~GraphicsLayerPaintForeground; |
| phase |= GraphicsLayerPaintCompositedScroll; |
| } |
| |
| return static_cast<GraphicsLayerPaintingPhase>(phase); |
| } |
| |
| float CompositedLayerMapping::compositingOpacity( |
| float layoutObjectOpacity) const { |
| float finalOpacity = layoutObjectOpacity; |
| |
| for (PaintLayer* curr = m_owningLayer.parent(); curr; curr = curr->parent()) { |
| // We only care about parents that are stacking contexts. |
| // Recall that opacity creates stacking context. |
| if (!curr->stackingNode()->isStackingContext()) |
| continue; |
| |
| // If we found a composited layer, regardless of whether it actually |
| // paints into it, we want to compute opacity relative to it. So we can |
| // break here. |
| // |
| // FIXME: with grouped backings, a composited descendant will have to |
| // continue past the grouped (squashed) layers that its parents may |
| // contribute to. This whole confusion can be avoided by specifying |
| // explicitly the composited ancestor where we would stop accumulating |
| // opacity. |
| if (curr->compositingState() == PaintsIntoOwnBacking) |
| break; |
| |
| finalOpacity *= curr->layoutObject().opacity(); |
| } |
| |
| return finalOpacity; |
| } |
| |
| Color CompositedLayerMapping::layoutObjectBackgroundColor() const { |
| return layoutObject().resolveColor(CSSPropertyBackgroundColor); |
| } |
| |
| void CompositedLayerMapping::updateBackgroundColor() { |
| m_graphicsLayer->setBackgroundColor(layoutObjectBackgroundColor()); |
| } |
| |
| bool CompositedLayerMapping::paintsChildren() const { |
| if (m_owningLayer.hasVisibleContent() && |
| m_owningLayer.hasNonEmptyChildLayoutObjects()) |
| return true; |
| |
| if (hasVisibleNonCompositingDescendant(&m_owningLayer)) |
| return true; |
| |
| return false; |
| } |
| |
| static bool isCompositedPlugin(LayoutObject& layoutObject) { |
| return layoutObject.isEmbeddedObject() && |
| toLayoutEmbeddedObject(layoutObject).requiresAcceleratedCompositing(); |
| } |
| |
| bool CompositedLayerMapping::hasVisibleNonCompositingDescendant( |
| PaintLayer* parent) { |
| if (!parent->hasVisibleDescendant()) |
| return false; |
| |
| // FIXME: We shouldn't be called with a stale z-order lists. See bug 85512. |
| parent->stackingNode()->updateLayerListsIfNeeded(); |
| |
| #if DCHECK_IS_ON() |
| LayerListMutationDetector mutationChecker(parent->stackingNode()); |
| #endif |
| |
| PaintLayerStackingNodeIterator normalFlowIterator(*parent->stackingNode(), |
| AllChildren); |
| while (PaintLayerStackingNode* curNode = normalFlowIterator.next()) { |
| PaintLayer* curLayer = curNode->layer(); |
| if (curLayer->hasCompositedLayerMapping()) |
| continue; |
| if (curLayer->hasVisibleContent() || |
| hasVisibleNonCompositingDescendant(curLayer)) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool CompositedLayerMapping::containsPaintedContent() const { |
| if (layoutObject().isImage() && isDirectlyCompositedImage()) |
| return false; |
| |
| LayoutObject& layoutObject = this->layoutObject(); |
| // FIXME: we could optimize cases where the image, video or canvas is known to |
| // fill the border box entirely, and set background color on the layer in that |
| // case, instead of allocating backing store and painting. |
| if (layoutObject.isVideo() && |
| toLayoutVideo(layoutObject).shouldDisplayVideo()) |
| return m_owningLayer.hasBoxDecorationsOrBackground(); |
| |
| if (m_owningLayer.hasVisibleBoxDecorations()) |
| return true; |
| |
| if (layoutObject.hasMask()) // masks require special treatment |
| return true; |
| |
| if (layoutObject.isAtomicInlineLevel() && !isCompositedPlugin(layoutObject)) |
| return true; |
| |
| if (layoutObject.isLayoutMultiColumnSet()) |
| return true; |
| |
| if (layoutObject.node() && layoutObject.node()->isDocumentNode()) { |
| // Look to see if the root object has a non-simple background |
| LayoutObject* rootObject = |
| layoutObject.document().documentElement() |
| ? layoutObject.document().documentElement()->layoutObject() |
| : 0; |
| // Reject anything that has a border, a border-radius or outline, |
| // or is not a simple background (no background, or solid color). |
| if (rootObject && |
| hasBoxDecorationsOrBackgroundImage(rootObject->styleRef())) |
| return true; |
| |
| // Now look at the body's layoutObject. |
| HTMLElement* body = layoutObject.document().body(); |
| LayoutObject* bodyObject = |
| isHTMLBodyElement(body) ? body->layoutObject() : 0; |
| if (bodyObject && |
| hasBoxDecorationsOrBackgroundImage(bodyObject->styleRef())) |
| return true; |
| } |
| |
| // FIXME: it's O(n^2). A better solution is needed. |
| return paintsChildren(); |
| } |
| |
| // An image can be directly composited if it's the sole content of the layer, |
| // and has no box decorations or clipping that require painting. Direct |
| // compositing saves a backing store. |
| bool CompositedLayerMapping::isDirectlyCompositedImage() const { |
| DCHECK(layoutObject().isImage()); |
| LayoutImage& imageLayoutObject = toLayoutImage(layoutObject()); |
| |
| if (m_owningLayer.hasBoxDecorationsOrBackground() || |
| imageLayoutObject.hasClip() || imageLayoutObject.hasClipPath() || |
| imageLayoutObject.hasObjectFit()) |
| return false; |
| |
| if (ImageResourceContent* cachedImage = imageLayoutObject.cachedImage()) { |
| if (!cachedImage->hasImage()) |
| return false; |
| |
| Image* image = cachedImage->getImage(); |
| if (!image->isBitmapImage()) |
| return false; |
| |
| return true; |
| } |
| |
| return false; |
| } |
| |
| void CompositedLayerMapping::contentChanged(ContentChangeType changeType) { |
| if ((changeType == ImageChanged) && layoutObject().isImage() && |
| isDirectlyCompositedImage()) { |
| updateImageContents(); |
| return; |
| } |
| |
| if (changeType == CanvasChanged && isAcceleratedCanvas(layoutObject())) { |
| m_graphicsLayer->setContentsNeedsDisplay(); |
| return; |
| } |
| } |
| |
| void CompositedLayerMapping::updateImageContents() { |
| DCHECK(layoutObject().isImage()); |
| LayoutImage& imageLayoutObject = toLayoutImage(layoutObject()); |
| |
| ImageResourceContent* cachedImage = imageLayoutObject.cachedImage(); |
| if (!cachedImage) |
| return; |
| |
| Image* image = cachedImage->getImage(); |
| if (!image) |
| return; |
| |
| // This is a no-op if the layer doesn't have an inner layer for the image. |
| m_graphicsLayer->setContentsToImage( |
| image, LayoutObject::shouldRespectImageOrientation(&imageLayoutObject)); |
| |
| m_graphicsLayer->setFilterQuality(layoutObject().style()->imageRendering() == |
| ImageRenderingPixelated |
| ? kNone_SkFilterQuality |
| : kLow_SkFilterQuality); |
| |
| // Prevent double-drawing: https://bugs.webkit.org/show_bug.cgi?id=58632 |
| updateDrawsContent(); |
| |
| // Image animation is "lazy", in that it automatically stops unless someone is |
| // drawing the image. So we have to kick the animation each time; this has the |
| // downside that the image will keep animating, even if its layer is not |
| // visible. |
| image->startAnimation(); |
| } |
| |
| FloatPoint3D CompositedLayerMapping::computeTransformOrigin( |
| const IntRect& borderBox) const { |
| const ComputedStyle& style = layoutObject().styleRef(); |
| |
| FloatPoint3D origin; |
| origin.setX(floatValueForLength(style.transformOriginX(), borderBox.width())); |
| origin.setY( |
| floatValueForLength(style.transformOriginY(), borderBox.height())); |
| origin.setZ(style.transformOriginZ()); |
| |
| return origin; |
| } |
| |
| // Return the offset from the top-left of this compositing layer at which the |
| // LayoutObject's contents are painted. |
| LayoutSize CompositedLayerMapping::contentOffsetInCompositingLayer() const { |
| ASSERT(!m_contentOffsetInCompositingLayerDirty); |
| return m_contentOffsetInCompositingLayer; |
| } |
| |
| LayoutRect CompositedLayerMapping::contentsBox() const { |
| LayoutRect contentsBox = LayoutRect(contentsRect(layoutObject())); |
| contentsBox.move(contentOffsetInCompositingLayer()); |
| return contentsBox; |
| } |
| |
| bool CompositedLayerMapping::needsToReparentOverflowControls() const { |
| return m_owningLayer.getScrollableArea() && |
| m_owningLayer.getScrollableArea()->hasOverlayScrollbars() && |
| m_owningLayer.getScrollableArea()->topmostScrollChild(); |
| } |
| |
| GraphicsLayer* CompositedLayerMapping::detachLayerForOverflowControls() { |
| GraphicsLayer* host = m_overflowControlsAncestorClippingLayer.get(); |
| if (!host) |
| host = m_overflowControlsHostLayer.get(); |
| host->removeFromParent(); |
| return host; |
| } |
| |
| GraphicsLayer* CompositedLayerMapping::parentForSublayers() const { |
| if (m_scrollingContentsLayer) |
| return m_scrollingContentsLayer.get(); |
| |
| if (m_childContainmentLayer) |
| return m_childContainmentLayer.get(); |
| |
| if (m_childTransformLayer) |
| return m_childTransformLayer.get(); |
| |
| return m_graphicsLayer.get(); |
| } |
| |
| void CompositedLayerMapping::setSublayers( |
| const GraphicsLayerVector& sublayers) { |
| GraphicsLayer* overflowControlsContainer = |
| m_overflowControlsAncestorClippingLayer |
| ? m_overflowControlsAncestorClippingLayer.get() |
| : m_overflowControlsHostLayer.get(); |
| GraphicsLayer* parent = parentForSublayers(); |
| bool needsOverflowControlsReattached = |
| overflowControlsContainer && |
| overflowControlsContainer->parent() == parent; |
| |
| parent->setChildren(sublayers); |
| |
| // If we have scrollbars, but are not using composited scrolling, then |
| // parentForSublayers may return m_graphicsLayer. In that case, the above |
| // call to setChildren has clobbered the overflow controls host layer, so we |
| // need to reattach it. |
| if (needsOverflowControlsReattached) |
| parent->addChild(overflowControlsContainer); |
| } |
| |
| GraphicsLayer* CompositedLayerMapping::childForSuperlayers() const { |
| if (m_squashingContainmentLayer) |
| return m_squashingContainmentLayer.get(); |
| |
| if (m_ancestorClippingLayer) |
| return m_ancestorClippingLayer.get(); |
| |
| return m_graphicsLayer.get(); |
| } |
| |
| void CompositedLayerMapping::setBlendMode(WebBlendMode blendMode) { |
| if (m_ancestorClippingLayer) { |
| m_ancestorClippingLayer->setBlendMode(blendMode); |
| m_graphicsLayer->setBlendMode(WebBlendModeNormal); |
| } else { |
| m_graphicsLayer->setBlendMode(blendMode); |
| } |
| } |
| |
| GraphicsLayerUpdater::UpdateType CompositedLayerMapping::updateTypeForChildren( |
| GraphicsLayerUpdater::UpdateType updateType) const { |
| if (m_pendingUpdateScope >= GraphicsLayerUpdateSubtree) |
| return GraphicsLayerUpdater::ForceUpdate; |
| return updateType; |
| } |
| |
| struct SetContentsNeedsDisplayFunctor { |
| void operator()(GraphicsLayer* layer) const { |
| if (layer->drawsContent()) |
| layer->setNeedsDisplay(); |
| } |
| }; |
| |
| void CompositedLayerMapping::setSquashingContentsNeedDisplay() { |
| ApplyToGraphicsLayers(this, SetContentsNeedsDisplayFunctor(), |
| ApplyToSquashingLayer); |
| } |
| |
| void CompositedLayerMapping::setContentsNeedDisplay() { |
| // FIXME: need to split out paint invalidations for the background. |
| ApplyToGraphicsLayers(this, SetContentsNeedsDisplayFunctor(), |
| ApplyToContentLayers); |
| } |
| |
| struct SetContentsNeedsDisplayInRectFunctor { |
| void operator()(GraphicsLayer* layer) const { |
| if (layer->drawsContent()) { |
| IntRect layerDirtyRect = r; |
| layerDirtyRect.move(-layer->offsetFromLayoutObject()); |
| layer->setNeedsDisplayInRect(layerDirtyRect, invalidationReason, client); |
| } |
| } |
| |
| IntRect r; |
| PaintInvalidationReason invalidationReason; |
| const DisplayItemClient& client; |
| }; |
| |
| void CompositedLayerMapping::setContentsNeedDisplayInRect( |
| const LayoutRect& r, |
| PaintInvalidationReason invalidationReason, |
| const DisplayItemClient& client) { |
| DCHECK(!m_owningLayer.layoutObject().usesCompositedScrolling()); |
| // TODO(wangxianzhu): Enable the following assert after paint invalidation for |
| // spv2 is ready. |
| // ASSERT(!RuntimeEnabledFeatures::slimmingPaintV2Enabled()); |
| |
| SetContentsNeedsDisplayInRectFunctor functor = { |
| enclosingIntRect(LayoutRect( |
| r.location() + m_owningLayer.subpixelAccumulation(), r.size())), |
| invalidationReason, client}; |
| ApplyToGraphicsLayers(this, functor, ApplyToContentLayers); |
| } |
| |
| void CompositedLayerMapping::setNonScrollingContentsNeedDisplayInRect( |
| const LayoutRect& r, |
| PaintInvalidationReason invalidationReason, |
| const DisplayItemClient& client) { |
| DCHECK(m_owningLayer.layoutObject().usesCompositedScrolling()); |
| // TODO(wangxianzhu): Enable the following assert after paint invalidation for |
| // spv2 is ready. |
| // ASSERT(!RuntimeEnabledFeatures::slimmingPaintV2Enabled()); |
| |
| SetContentsNeedsDisplayInRectFunctor functor = { |
| enclosingIntRect(LayoutRect( |
| r.location() + m_owningLayer.subpixelAccumulation(), r.size())), |
| invalidationReason, client}; |
| ApplyToGraphicsLayers(this, functor, ApplyToNonScrollingContentLayers); |
| } |
| |
| void CompositedLayerMapping::setScrollingContentsNeedDisplayInRect( |
| const LayoutRect& r, |
| PaintInvalidationReason invalidationReason, |
| const DisplayItemClient& client) { |
| DCHECK(m_owningLayer.layoutObject().usesCompositedScrolling()); |
| // TODO(wangxianzhu): Enable the following assert after paint invalidation for |
| // spv2 is ready. |
| // ASSERT(!RuntimeEnabledFeatures::slimmingPaintV2Enabled()); |
| |
| SetContentsNeedsDisplayInRectFunctor functor = { |
| enclosingIntRect(LayoutRect( |
| r.location() + m_owningLayer.subpixelAccumulation(), r.size())), |
| invalidationReason, client}; |
| ApplyToGraphicsLayers(this, functor, ApplyToScrollingContentLayers); |
| } |
| |
| const GraphicsLayerPaintInfo* CompositedLayerMapping::containingSquashedLayer( |
| const LayoutObject* layoutObject, |
| const Vector<GraphicsLayerPaintInfo>& layers, |
| unsigned maxSquashedLayerIndex) { |
| if (!layoutObject) |
| return nullptr; |
| for (size_t i = 0; i < layers.size() && i < maxSquashedLayerIndex; ++i) { |
| if (layoutObject->isDescendantOf(&layers[i].paintLayer->layoutObject())) |
| return &layers[i]; |
| } |
| return nullptr; |
| } |
| |
| const GraphicsLayerPaintInfo* CompositedLayerMapping::containingSquashedLayer( |
| const LayoutObject* layoutObject, |
| unsigned maxSquashedLayerIndex) { |
| return CompositedLayerMapping::containingSquashedLayer( |
| layoutObject, m_squashedLayers, maxSquashedLayerIndex); |
| } |
| |
| IntRect CompositedLayerMapping::localClipRectForSquashedLayer( |
| const PaintLayer& referenceLayer, |
| const GraphicsLayerPaintInfo& paintInfo, |
| const Vector<GraphicsLayerPaintInfo>& layers) { |
| const LayoutObject* clippingContainer = |
| paintInfo.paintLayer->clippingContainer(); |
| if (clippingContainer == referenceLayer.clippingContainer()) |
| return LayoutRect::infiniteIntRect(); |
| |
| ASSERT(clippingContainer); |
| |
| const GraphicsLayerPaintInfo* ancestorPaintInfo = |
| containingSquashedLayer(clippingContainer, layers, layers.size()); |
| // Must be there, otherwise |
| // CompositingLayerAssigner::canSquashIntoCurrentSquashingOwner would have |
| // disallowed squashing. |
| ASSERT(ancestorPaintInfo); |
| |
| // FIXME: this is a potential performance issue. We should consider caching |
| // these clip rects or otherwise optimizing. |
| ClipRectsContext clipRectsContext(ancestorPaintInfo->paintLayer, |
| UncachedClipRects); |
| ClipRect parentClipRect; |
| paintInfo.paintLayer->clipper(PaintLayer::DoNotUseGeometryMapper) |
| .calculateBackgroundClipRect(clipRectsContext, parentClipRect); |
| |
| IntRect snappedParentClipRect(pixelSnappedIntRect(parentClipRect.rect())); |
| DCHECK(snappedParentClipRect != LayoutRect::infiniteIntRect()); |
| |
| // Convert from ancestor to local coordinates. |
| IntSize ancestorToLocalOffset = paintInfo.offsetFromLayoutObject - |
| ancestorPaintInfo->offsetFromLayoutObject; |
| snappedParentClipRect.move(ancestorToLocalOffset); |
| return snappedParentClipRect; |
| } |
| |
| void CompositedLayerMapping::doPaintTask( |
| const GraphicsLayerPaintInfo& paintInfo, |
| const GraphicsLayer& graphicsLayer, |
| const PaintLayerFlags& paintLayerFlags, |
| GraphicsContext& context, |
| const IntRect& clip /* In the coords of rootLayer */) const { |
| FontCachePurgePreventer fontCachePurgePreventer; |
| |
| IntSize offset = paintInfo.offsetFromLayoutObject; |
| AffineTransform translation; |
| translation.translate(-offset.width(), -offset.height()); |
| TransformRecorder transformRecorder(context, graphicsLayer, translation); |
| |
| // The dirtyRect is in the coords of the painting root. |
| IntRect dirtyRect(clip); |
| dirtyRect.move(offset); |
| |
| if (paintLayerFlags & (PaintLayerPaintingOverflowContents | |
| PaintLayerPaintingAncestorClippingMaskPhase)) { |
| dirtyRect.move( |
| roundedIntSize(paintInfo.paintLayer->subpixelAccumulation())); |
| } else { |
| LayoutRect bounds = paintInfo.compositedBounds; |
| bounds.move(paintInfo.paintLayer->subpixelAccumulation()); |
| dirtyRect.intersect(pixelSnappedIntRect(bounds)); |
| } |
| |
| #if DCHECK_IS_ON() |
| if (!layoutObject().view()->frame() || |
| !layoutObject().view()->frame()->shouldThrottleRendering()) |
| paintInfo.paintLayer->layoutObject().assertSubtreeIsLaidOut(); |
| #endif |
| |
| float deviceScaleFactor = blink::deviceScaleFactorDeprecated( |
| paintInfo.paintLayer->layoutObject().frame()); |
| context.setDeviceScaleFactor(deviceScaleFactor); |
| |
| if (paintInfo.paintLayer->compositingState() != PaintsIntoGroupedBacking) { |
| // FIXME: GraphicsLayers need a way to split for multicol. |
| PaintLayerPaintingInfo paintingInfo( |
| paintInfo.paintLayer, LayoutRect(dirtyRect), GlobalPaintNormalPhase, |
| paintInfo.paintLayer->subpixelAccumulation()); |
| PaintLayerPainter(*paintInfo.paintLayer) |
| .paintLayerContents(context, paintingInfo, paintLayerFlags); |
| |
| if (paintInfo.paintLayer->containsDirtyOverlayScrollbars()) |
| PaintLayerPainter(*paintInfo.paintLayer) |
| .paintLayerContents( |
| context, paintingInfo, |
| paintLayerFlags | PaintLayerPaintingOverlayScrollbars); |
| } else { |
| PaintLayerPaintingInfo paintingInfo( |
| paintInfo.paintLayer, LayoutRect(dirtyRect), GlobalPaintNormalPhase, |
| paintInfo.paintLayer->subpixelAccumulation()); |
| |
| // PaintLayer::paintLayer assumes that the caller clips to the passed rect. |
| // Squashed layers need to do this clipping in software, since there is no |
| // graphics layer to clip them precisely. Furthermore, in some cases we |
| // squash layers that need clipping in software from clipping ancestors (see |
| // CompositedLayerMapping::localClipRectForSquashedLayer()). |
| // FIXME: Is it correct to clip to dirtyRect in slimming paint mode? |
| // FIXME: Combine similar code here and LayerClipRecorder. |
| dirtyRect.intersect(paintInfo.localClipRectForSquashedLayer); |
| context.getPaintController().createAndAppend<ClipDisplayItem>( |
| graphicsLayer, DisplayItem::kClipLayerOverflowControls, dirtyRect); |
| |
| PaintLayerPainter(*paintInfo.paintLayer) |
| .paint(context, paintingInfo, paintLayerFlags); |
| context.getPaintController().endItem<EndClipDisplayItem>( |
| graphicsLayer, DisplayItem::clipTypeToEndClipType( |
| DisplayItem::kClipLayerOverflowControls)); |
| } |
| } |
| |
| static void paintScrollbar(const Scrollbar* scrollbar, |
| GraphicsContext& context, |
| const IntRect& clip) { |
| if (!scrollbar) |
| return; |
| |
| const IntRect& scrollbarRect = scrollbar->frameRect(); |
| TransformRecorder transformRecorder( |
| context, *scrollbar, |
| AffineTransform::translation(-scrollbarRect.x(), -scrollbarRect.y())); |
| IntRect transformedClip = clip; |
| transformedClip.moveBy(scrollbarRect.location()); |
| scrollbar->paint(context, CullRect(transformedClip)); |
| } |
| |
| // TODO(eseckler): Make recording distance configurable, e.g. for use in |
| // headless, where we would like to record an exact area. |
| // Note however that the minimum value for this constant is the size of a |
| // raster tile. This is because the raster system is not able to raster a |
| // tile that is not completely covered by a display list. If the constant |
| // were less than the size of a tile, then a tile which partially overlaps |
| // the screen may not be rastered. |
| static const int kPixelDistanceToRecord = 4000; |
| |
| IntRect CompositedLayerMapping::recomputeInterestRect( |
| const GraphicsLayer* graphicsLayer) const { |
| FloatRect graphicsLayerBounds(FloatPoint(), graphicsLayer->size()); |
| |
| IntSize offsetFromAnchorLayoutObject; |
| const LayoutBoxModelObject* anchorLayoutObject; |
| if (graphicsLayer == m_squashingLayer.get()) { |
| // TODO(chrishtr): this is a speculative fix for crbug.com/561306. However, |
| // it should never be the case that m_squashingLayer exists, |
| // yet m_squashedLayers.size() == 0. There must be a bug elsewhere. |
| if (m_squashedLayers.size() == 0) |
| return IntRect(); |
| // All squashed layers have the same clip and transform space, so we can use |
| // the first squashed layer's layoutObject to map the squashing layer's |
| // bounds into viewport space, with offsetFromAnchorLayoutObject to |
| // translate squashing layer's bounds into the first squashed layer's space. |
| anchorLayoutObject = &m_squashedLayers[0].paintLayer->layoutObject(); |
| offsetFromAnchorLayoutObject = m_squashedLayers[0].offsetFromLayoutObject; |
| } else { |
| ASSERT(graphicsLayer == m_graphicsLayer.get() || |
| graphicsLayer == m_scrollingContentsLayer.get()); |
| anchorLayoutObject = &m_owningLayer.layoutObject(); |
| offsetFromAnchorLayoutObject = graphicsLayer->offsetFromLayoutObject(); |
| adjustForCompositedScrolling(graphicsLayer, offsetFromAnchorLayoutObject); |
| } |
| |
| // Start with the bounds of the graphics layer in the space of the anchor |
| // LayoutObject. |
| FloatRect graphicsLayerBoundsInObjectSpace(graphicsLayerBounds); |
| graphicsLayerBoundsInObjectSpace.move(offsetFromAnchorLayoutObject); |
| |
| // Now map the bounds to its visible content rect in root view space, |
| // including applying clips along the way. |
| LayoutRect graphicsLayerBoundsInRootViewSpace( |
| graphicsLayerBoundsInObjectSpace); |
| LayoutView* rootView = anchorLayoutObject->view(); |
| while (!rootView->frame()->ownerLayoutItem().isNull()) |
| rootView = |
| LayoutAPIShim::layoutObjectFrom(rootView->frame()->ownerLayoutItem()) |
| ->view(); |
| anchorLayoutObject->mapToVisualRectInAncestorSpace( |
| rootView, graphicsLayerBoundsInRootViewSpace); |
| FloatRect visibleContentRect(graphicsLayerBoundsInRootViewSpace); |
| rootView->frameView()->clipPaintRect(&visibleContentRect); |
| |
| IntRect enclosingGraphicsLayerBounds(enclosingIntRect(graphicsLayerBounds)); |
| |
| // Map the visible content rect from root view space to local graphics layer |
| // space. |
| IntRect localInterestRect; |
| // If the visible content rect is empty, then it makes no sense to map it back |
| // since there is nothing to map. |
| if (!visibleContentRect.isEmpty()) { |
| localInterestRect = |
| anchorLayoutObject |
| ->absoluteToLocalQuad(visibleContentRect, |
| UseTransforms | TraverseDocumentBoundaries) |
| .enclosingBoundingBox(); |
| localInterestRect.move(-offsetFromAnchorLayoutObject); |
| // TODO(chrishtr): the code below is a heuristic, instead we should detect |
| // and return whether the mapping failed. In some cases, |
| // absoluteToLocalQuad can fail to map back to the local space, due to |
| // passing through non-invertible transforms or floating-point accuracy |
| // issues. Examples include rotation near 90 degrees or perspective. In such |
| // cases, fall back to painting the first kPixelDistanceToRecord pixels in |
| // each direction. |
| localInterestRect.intersect(enclosingGraphicsLayerBounds); |
| } |
| // Expand by interest rect padding amount. |
| localInterestRect.inflate(kPixelDistanceToRecord); |
| localInterestRect.intersect(enclosingGraphicsLayerBounds); |
| return localInterestRect; |
| } |
| |
| static const int kMinimumDistanceBeforeRepaint = 512; |
| |
| bool CompositedLayerMapping::interestRectChangedEnoughToRepaint( |
| const IntRect& previousInterestRect, |
| const IntRect& newInterestRect, |
| const IntSize& layerSize) { |
| if (previousInterestRect.isEmpty() && newInterestRect.isEmpty()) |
| return false; |
| |
| // Repaint when going from empty to not-empty, to cover cases where the layer |
| // is painted for the first time, or otherwise becomes visible. |
| if (previousInterestRect.isEmpty()) |
| return true; |
| |
| // Repaint if the new interest rect includes area outside of a skirt around |
| // the existing interest rect. |
| IntRect expandedPreviousInterestRect(previousInterestRect); |
| expandedPreviousInterestRect.inflate(kMinimumDistanceBeforeRepaint); |
| if (!expandedPreviousInterestRect.contains(newInterestRect)) |
| return true; |
| |
| // Even if the new interest rect doesn't include enough new area to satisfy |
| // the condition above, repaint anyway if it touches a layer edge not touched |
| // by the existing interest rect. Because it's impossible to expose more area |
| // in the direction, repainting cannot be deferred until the exposed new area |
| // satisfies the condition above. |
| if (newInterestRect.x() == 0 && previousInterestRect.x() != 0) |
| return true; |
| if (newInterestRect.y() == 0 && previousInterestRect.y() != 0) |
| return true; |
| if (newInterestRect.maxX() == layerSize.width() && |
| previousInterestRect.maxX() != layerSize.width()) |
| return true; |
| if (newInterestRect.maxY() == layerSize.height() && |
| previousInterestRect.maxY() != layerSize.height()) |
| return true; |
| |
| return false; |
| } |
| |
| IntRect CompositedLayerMapping::computeInterestRect( |
| const GraphicsLayer* graphicsLayer, |
| const IntRect& previousInterestRect) const { |
| // Use the previous interest rect if it covers the whole layer. |
| IntRect wholeLayerRect = |
| IntRect(IntPoint(), expandedIntSize(graphicsLayer->size())); |
| if (!needsRepaint(*graphicsLayer) && previousInterestRect == wholeLayerRect) |
| return previousInterestRect; |
| |
| if (graphicsLayer != m_graphicsLayer.get() && |
| graphicsLayer != m_squashingLayer.get() && |
| graphicsLayer != m_scrollingContentsLayer.get()) |
| return wholeLayerRect; |
| |
| IntRect newInterestRect = recomputeInterestRect(graphicsLayer); |
| if (needsRepaint(*graphicsLayer) || |
| interestRectChangedEnoughToRepaint( |
| previousInterestRect, newInterestRect, |
| expandedIntSize(graphicsLayer->size()))) |
| return newInterestRect; |
| return previousInterestRect; |
| } |
| |
| LayoutSize CompositedLayerMapping::subpixelAccumulation() const { |
| return m_owningLayer.subpixelAccumulation(); |
| } |
| |
| bool CompositedLayerMapping::needsRepaint( |
| const GraphicsLayer& graphicsLayer) const { |
| return isScrollableAreaLayer(&graphicsLayer) ? true |
| : m_owningLayer.needsRepaint(); |
| } |
| |
| void CompositedLayerMapping::adjustForCompositedScrolling( |
| const GraphicsLayer* graphicsLayer, |
| IntSize& offset) const { |
| if (graphicsLayer == m_scrollingContentsLayer.get() || |
| graphicsLayer == m_foregroundLayer.get()) { |
| if (PaintLayerScrollableArea* scrollableArea = |
| m_owningLayer.getScrollableArea()) { |
| if (scrollableArea->usesCompositedScrolling()) { |
| // Note: this is the offset from the beginning of flow of the block, not |
| // the offset from the top/left of the overflow rect. |
| // offsetFromLayoutObject adds the origin offset from top/left to the |
| // beginning of flow. |
| ScrollOffset scrollOffset = scrollableArea->getScrollOffset(); |
| offset.expand(-scrollOffset.width(), -scrollOffset.height()); |
| } |
| } |
| } |
| } |
| |
| void CompositedLayerMapping::paintContents( |
| const GraphicsLayer* graphicsLayer, |
| GraphicsContext& context, |
| GraphicsLayerPaintingPhase graphicsLayerPaintingPhase, |
| const IntRect& interestRect) const { |
| // https://code.google.com/p/chromium/issues/detail?id=343772 |
| DisableCompositingQueryAsserts disabler; |
| // Allow throttling to make sure no painting paths (e.g., |
| // ContentLayerDelegate::paintContents) try to paint throttled content. |
| DocumentLifecycle::AllowThrottlingScope allowThrottling( |
| m_owningLayer.layoutObject().document().lifecycle()); |
| #if DCHECK_IS_ON() |
| // FIXME: once the state machine is ready, this can be removed and we can |
| // refer to that instead. |
| if (Page* page = layoutObject().frame()->page()) |
| page->setIsPainting(true); |
| #endif |
| |
| TRACE_EVENT1( |
| "devtools.timeline,rail", "Paint", "data", |
| InspectorPaintEvent::data(&m_owningLayer.layoutObject(), |
| LayoutRect(interestRect), graphicsLayer)); |
| |
| PaintLayerFlags paintLayerFlags = 0; |
| if (graphicsLayerPaintingPhase & GraphicsLayerPaintBackground) |
| paintLayerFlags |= PaintLayerPaintingCompositingBackgroundPhase; |
| else |
| paintLayerFlags |= PaintLayerPaintingSkipRootBackground; |
| if (graphicsLayerPaintingPhase & GraphicsLayerPaintForeground) |
| paintLayerFlags |= PaintLayerPaintingCompositingForegroundPhase; |
| if (graphicsLayerPaintingPhase & GraphicsLayerPaintMask) |
| paintLayerFlags |= PaintLayerPaintingCompositingMaskPhase; |
| if (graphicsLayerPaintingPhase & GraphicsLayerPaintChildClippingMask) |
| paintLayerFlags |= PaintLayerPaintingChildClippingMaskPhase; |
| if (graphicsLayerPaintingPhase & GraphicsLayerPaintAncestorClippingMask) |
| paintLayerFlags |= PaintLayerPaintingAncestorClippingMaskPhase; |
| if (graphicsLayerPaintingPhase & GraphicsLayerPaintOverflowContents) |
| paintLayerFlags |= PaintLayerPaintingOverflowContents; |
| if (graphicsLayerPaintingPhase & GraphicsLayerPaintCompositedScroll) |
| paintLayerFlags |= PaintLayerPaintingCompositingScrollingPhase; |
| if (graphicsLayerPaintingPhase & GraphicsLayerPaintDecoration) |
| paintLayerFlags |= PaintLayerPaintingCompositingDecorationPhase; |
| |
| if (graphicsLayer == m_backgroundLayer.get()) |
| paintLayerFlags |= PaintLayerPaintingRootBackgroundOnly; |
| else if (compositor()->fixedRootBackgroundLayer() && |
| m_owningLayer.isRootLayer()) |
| paintLayerFlags |= PaintLayerPaintingSkipRootBackground; |
| |
| if (graphicsLayer == m_graphicsLayer.get() || |
| graphicsLayer == m_foregroundLayer.get() || |
| graphicsLayer == m_backgroundLayer.get() || |
| graphicsLayer == m_maskLayer.get() || |
| graphicsLayer == m_childClippingMaskLayer.get() || |
| graphicsLayer == m_scrollingContentsLayer.get() || |
| graphicsLayer == m_decorationOutlineLayer.get() || |
| graphicsLayer == m_ancestorClippingMaskLayer.get()) { |
| bool paintRootBackgroundOntoScrollingContentsLayer = |
| m_backgroundPaintsOntoScrollingContentsLayer; |
| DCHECK(!paintRootBackgroundOntoScrollingContentsLayer || |
| (!m_backgroundLayer && !m_foregroundLayer)); |
| if (paintRootBackgroundOntoScrollingContentsLayer) { |
| if (graphicsLayer == m_scrollingContentsLayer.get()) |
| paintLayerFlags &= ~PaintLayerPaintingSkipRootBackground; |
| else if (!m_backgroundPaintsOntoGraphicsLayer) |
| paintLayerFlags |= PaintLayerPaintingSkipRootBackground; |
| } |
| |
| GraphicsLayerPaintInfo paintInfo; |
| paintInfo.paintLayer = &m_owningLayer; |
| paintInfo.compositedBounds = compositedBounds(); |
| paintInfo.offsetFromLayoutObject = graphicsLayer->offsetFromLayoutObject(); |
| adjustForCompositedScrolling(graphicsLayer, |
| paintInfo.offsetFromLayoutObject); |
| |
| // We have to use the same root as for hit testing, because both methods can |
| // compute and cache clipRects. |
| doPaintTask(paintInfo, *graphicsLayer, paintLayerFlags, context, |
| interestRect); |
| } else if (graphicsLayer == m_squashingLayer.get()) { |
| for (size_t i = 0; i < m_squashedLayers.size(); ++i) |
| doPaintTask(m_squashedLayers[i], *graphicsLayer, paintLayerFlags, context, |
| interestRect); |
| } else if (isScrollableAreaLayer(graphicsLayer)) { |
| paintScrollableArea(graphicsLayer, context, interestRect); |
| } |
| probe::didPaint(m_owningLayer.layoutObject().frame(), graphicsLayer, context, |
| LayoutRect(interestRect)); |
| #if DCHECK_IS_ON() |
| if (Page* page = layoutObject().frame()->page()) |
| page->setIsPainting(false); |
| #endif |
| } |
| |
| void CompositedLayerMapping::paintScrollableArea( |
| const GraphicsLayer* graphicsLayer, |
| GraphicsContext& context, |
| const IntRect& interestRect) const { |
| // Note the composited scrollable area painted here is never associated with a |
| // frame. For painting frame ScrollableAreas, see |
| // PaintLayerCompositor::paintContents. |
| |
| if (DrawingRecorder::useCachedDrawingIfPossible( |
| context, *graphicsLayer, DisplayItem::kScrollbarCompositedScrollbar)) |
| return; |
| |
| FloatRect layerBounds(FloatPoint(), graphicsLayer->size()); |
| PaintRecordBuilder builder(layerBounds, nullptr, &context); |
| PaintLayerScrollableArea* scrollableArea = m_owningLayer.getScrollableArea(); |
| if (graphicsLayer == layerForHorizontalScrollbar()) { |
| paintScrollbar(scrollableArea->horizontalScrollbar(), builder.context(), |
| interestRect); |
| } else if (graphicsLayer == layerForVerticalScrollbar()) { |
| paintScrollbar(scrollableArea->verticalScrollbar(), builder.context(), |
| interestRect); |
| } else if (graphicsLayer == layerForScrollCorner()) { |
| // Note that scroll corners always paint into local space, whereas |
| // scrollbars paint in the space of their containing frame. |
| IntPoint scrollCornerAndResizerLocation = |
| scrollableArea->scrollCornerAndResizerRect().location(); |
| CullRect cullRect(enclosingIntRect(interestRect)); |
| ScrollableAreaPainter(*scrollableArea) |
| .paintScrollCorner(builder.context(), -scrollCornerAndResizerLocation, |
| cullRect); |
| ScrollableAreaPainter(*scrollableArea) |
| .paintResizer(builder.context(), -scrollCornerAndResizerLocation, |
| cullRect); |
| } |
| // Replay the painted scrollbar content with the GraphicsLayer backing as the |
| // DisplayItemClient in order for the resulting DrawingDisplayItem to produce |
| // the correct visualRect (i.e., the bounds of the involved GraphicsLayer). |
| DrawingRecorder drawingRecorder(context, *graphicsLayer, |
| DisplayItem::kScrollbarCompositedScrollbar, |
| layerBounds); |
| context.canvas()->PlaybackPaintRecord(builder.endRecording()); |
| } |
| |
| bool CompositedLayerMapping::isScrollableAreaLayer( |
| const GraphicsLayer* graphicsLayer) const { |
| return graphicsLayer == layerForHorizontalScrollbar() || |
| graphicsLayer == layerForVerticalScrollbar() || |
| graphicsLayer == layerForScrollCorner(); |
| } |
| |
| bool CompositedLayerMapping::isTrackingRasterInvalidations() const { |
| GraphicsLayerClient* client = compositor(); |
| return client ? client->isTrackingRasterInvalidations() : false; |
| } |
| |
| #if DCHECK_IS_ON() |
| void CompositedLayerMapping::verifyNotPainting() { |
| DCHECK(!layoutObject().frame()->page() || |
| !layoutObject().frame()->page()->isPainting()); |
| } |
| #endif |
| |
| // Only used for performance benchmark testing. Intended to be a |
| // sufficiently-unique element id name to allow picking out the target element |
| // for invalidation. |
| static const char* kTestPaintInvalidationTargetName = |
| "blinkPaintInvalidationTarget"; |
| |
| void CompositedLayerMapping::invalidateTargetElementForTesting() { |
| // The below is an artificial construct formed intentionally to focus a |
| // microbenchmark on the cost of paint with a partial invalidation. |
| Element* targetElement = |
| m_owningLayer.layoutObject().document().getElementById( |
| AtomicString(kTestPaintInvalidationTargetName)); |
| // TODO(wkorman): If we don't find the expected target element, we could |
| // consider walking to the first leaf node so that the partial-invalidation |
| // benchmark mode still provides some value when running on generic pages. |
| if (!targetElement) |
| return; |
| LayoutObject* targetObject = targetElement->layoutObject(); |
| if (!targetObject) |
| return; |
| targetObject->enclosingLayer()->setNeedsRepaint(); |
| // TODO(wkorman): Consider revising the below to invalidate all |
| // non-compositing descendants as well. |
| targetObject->invalidateDisplayItemClients(PaintInvalidationForTesting); |
| } |
| |
| IntRect CompositedLayerMapping::pixelSnappedCompositedBounds() const { |
| LayoutRect bounds = m_compositedBounds; |
| bounds.move(m_owningLayer.subpixelAccumulation()); |
| return pixelSnappedIntRect(bounds); |
| } |
| |
| bool CompositedLayerMapping::invalidateLayerIfNoPrecedingEntry( |
| size_t indexToClear) { |
| PaintLayer* layerToRemove = m_squashedLayers[indexToClear].paintLayer; |
| size_t previousIndex = 0; |
| for (; previousIndex < indexToClear; ++previousIndex) { |
| if (m_squashedLayers[previousIndex].paintLayer == layerToRemove) |
| break; |
| } |
| if (previousIndex == indexToClear && |
| layerToRemove->groupedMapping() == this) { |
| compositor()->paintInvalidationOnCompositingChange(layerToRemove); |
| return true; |
| } |
| return false; |
| } |
| |
| bool CompositedLayerMapping::updateSquashingLayerAssignment( |
| PaintLayer* squashedLayer, |
| size_t nextSquashedLayerIndex) { |
| GraphicsLayerPaintInfo paintInfo; |
| paintInfo.paintLayer = squashedLayer; |
| // NOTE: composited bounds are updated elsewhere |
| // NOTE: offsetFromLayoutObject is updated elsewhere |
| |
| // Change tracking on squashing layers: at the first sign of something |
| // changed, just invalidate the layer. |
| // FIXME: Perhaps we can find a tighter more clever mechanism later. |
| if (nextSquashedLayerIndex < m_squashedLayers.size()) { |
| if (paintInfo.paintLayer == |
| m_squashedLayers[nextSquashedLayerIndex].paintLayer) |
| return false; |
| |
| // Must invalidate before adding the squashed layer to the mapping. |
| compositor()->paintInvalidationOnCompositingChange(squashedLayer); |
| |
| // If the layer which was previously at |nextSquashedLayerIndex| is not |
| // earlier in the grouped mapping, invalidate its current backing now, since |
| // it will move later or be removed from the squashing layer. |
| invalidateLayerIfNoPrecedingEntry(nextSquashedLayerIndex); |
| |
| m_squashedLayers.insert(nextSquashedLayerIndex, paintInfo); |
| } else { |
| // Must invalidate before adding the squashed layer to the mapping. |
| compositor()->paintInvalidationOnCompositingChange(squashedLayer); |
| m_squashedLayers.push_back(paintInfo); |
| } |
| squashedLayer->setGroupedMapping( |
| this, PaintLayer::InvalidateLayerAndRemoveFromMapping); |
| |
| return true; |
| } |
| |
| void CompositedLayerMapping::removeLayerFromSquashingGraphicsLayer( |
| const PaintLayer* layer) { |
| size_t layerIndex = 0; |
| for (; layerIndex < m_squashedLayers.size(); ++layerIndex) { |
| if (m_squashedLayers[layerIndex].paintLayer == layer) |
| break; |
| } |
| |
| // Assert on incorrect mappings between layers and groups |
| ASSERT(layerIndex < m_squashedLayers.size()); |
| if (layerIndex == m_squashedLayers.size()) |
| return; |
| |
| m_squashedLayers.remove(layerIndex); |
| } |
| |
| #if DCHECK_IS_ON() |
| bool CompositedLayerMapping::verifyLayerInSquashingVector( |
| const PaintLayer* layer) { |
| for (size_t layerIndex = 0; layerIndex < m_squashedLayers.size(); |
| ++layerIndex) { |
| if (m_squashedLayers[layerIndex].paintLayer == layer) |
| return true; |
| } |
| |
| return false; |
| } |
| #endif |
| |
| void CompositedLayerMapping::finishAccumulatingSquashingLayers( |
| size_t nextSquashedLayerIndex, |
| Vector<PaintLayer*>& layersNeedingPaintInvalidation) { |
| if (nextSquashedLayerIndex < m_squashedLayers.size()) { |
| // Any additional squashed Layers in the array no longer belong here, but |
| // they might have been added already at an earlier index. Clear pointers on |
| // those that do not appear in the valid set before removing all the extra |
| // entries. |
| for (size_t i = nextSquashedLayerIndex; i < m_squashedLayers.size(); ++i) { |
| if (invalidateLayerIfNoPrecedingEntry(i)) |
| m_squashedLayers[i].paintLayer->setGroupedMapping( |
| nullptr, PaintLayer::DoNotInvalidateLayerAndRemoveFromMapping); |
| layersNeedingPaintInvalidation.push_back(m_squashedLayers[i].paintLayer); |
| } |
| |
| m_squashedLayers.remove(nextSquashedLayerIndex, |
| m_squashedLayers.size() - nextSquashedLayerIndex); |
| } |
| } |
| |
| String CompositedLayerMapping::debugName( |
| const GraphicsLayer* graphicsLayer) const { |
| String name; |
| if (graphicsLayer == m_graphicsLayer.get()) { |
| name = m_owningLayer.debugName(); |
| } else if (graphicsLayer == m_squashingContainmentLayer.get()) { |
| name = "Squashing Containment Layer"; |
| } else if (graphicsLayer == m_squashingLayer.get()) { |
| name = "Squashing Layer (first squashed layer: " + |
| (m_squashedLayers.size() > 0 |
| ? m_squashedLayers[0].paintLayer->debugName() |
| : "") + |
| ")"; |
| } else if (graphicsLayer == m_ancestorClippingLayer.get()) { |
| name = "Ancestor Clipping Layer"; |
| } else if (graphicsLayer == m_ancestorClippingMaskLayer.get()) { |
| name = "Ancestor Clipping Mask Layer"; |
| } else if (graphicsLayer == m_foregroundLayer.get()) { |
| name = m_owningLayer.debugName() + " (foreground) Layer"; |
| } else if (graphicsLayer == m_backgroundLayer.get()) { |
| name = m_owningLayer.debugName() + " (background) Layer"; |
| } else if (graphicsLayer == m_childContainmentLayer.get()) { |
| name = "Child Containment Layer"; |
| } else if (graphicsLayer == m_childTransformLayer.get()) { |
| name = "Child Transform Layer"; |
| } else if (graphicsLayer == m_maskLayer.get()) { |
| name = "Mask Layer"; |
| } else if (graphicsLayer == m_childClippingMaskLayer.get()) { |
| name = "Child Clipping Mask Layer"; |
| } else if (graphicsLayer == m_layerForHorizontalScrollbar.get()) { |
| name = "Horizontal Scrollbar Layer"; |
| } else if (graphicsLayer == m_layerForVerticalScrollbar.get()) { |
| name = "Vertical Scrollbar Layer"; |
| } else if (graphicsLayer == m_layerForScrollCorner.get()) { |
| name = "Scroll Corner Layer"; |
| } else if (graphicsLayer == m_overflowControlsHostLayer.get()) { |
| name = "Overflow Controls Host Layer"; |
| } else if (graphicsLayer == m_overflowControlsAncestorClippingLayer.get()) { |
| name = "Overflow Controls Ancestor Clipping Layer"; |
| } else if (graphicsLayer == m_scrollingLayer.get()) { |
| name = "Scrolling Layer"; |
| } else if (graphicsLayer == m_scrollingContentsLayer.get()) { |
| name = "Scrolling Contents Layer"; |
| } else if (graphicsLayer == m_decorationOutlineLayer.get()) { |
| name = "Decoration Layer"; |
| } else { |
| NOTREACHED(); |
| } |
| |
| return name; |
| } |
| |
| } // namespace blink |