| /* |
| * 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 "core/HTMLNames.h" |
| #include "core/dom/DOMNodeIds.h" |
| #include "core/fetch/ImageResource.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/page/ChromeClient.h" |
| #include "core/page/Page.h" |
| #include "core/page/scrolling/ScrollingCoordinator.h" |
| #include "core/paint/ObjectPaintInvalidator.h" |
| #include "core/paint/PaintInfo.h" |
| #include "core/paint/PaintLayerPainter.h" |
| #include "core/paint/PaintLayerStackingNodeIterator.h" |
| #include "core/paint/PaintTiming.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/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/SkPictureBuilder.h" |
| #include "platform/graphics/paint/TransformDisplayItem.h" |
| #include "wtf/CurrentTime.h" |
| #include "wtf/text/StringBuilder.h" |
| #include <memory> |
| |
| 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 isCanvasControlledByOffscreen(const LayoutObject* layoutObject) |
| { |
| if (layoutObject->isCanvas()) { |
| HTMLCanvasElement* canvas = toHTMLCanvasElement(layoutObject->node()); |
| if (canvas->surfaceLayerBridge()) |
| return true; |
| } |
| 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; |
| Widget* widget = toLayoutEmbeddedObject(layoutObject)->widget(); |
| if (!widget || !widget->isPluginView()) |
| return nullptr; |
| return toPluginView(widget)->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) |
| { |
| 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); |
| 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_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().computeFilterOperations(style)); |
| } |
| |
| void CompositedLayerMapping::updateBackdropFilters(const ComputedStyle& style) |
| { |
| m_graphicsLayer->setBackdropFilters(owningLayer().computeBackdropFilterOperations(style)); |
| } |
| |
| 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() |
| { |
| bool shouldPaintOntoScrollingContentsLayer = m_owningLayer.shouldPaintBackgroundOntoScrollingContentsLayer(); |
| if (shouldPaintOntoScrollingContentsLayer != backgroundPaintsOntoScrollingContentsLayer()) { |
| m_backgroundPaintsOntoScrollingContentsLayer = shouldPaintOntoScrollingContentsLayer; |
| // If the background is no longer painted onto the scrolling contents |
| // layer the scrolling contents layer needs to be updated. If it is |
| // going to be painted onto the scrolling contents layer this update |
| // will be triggered by LayoutBoxModelObject::setBackingNeedsPaintInvalidationInRect |
| if (hasScrollingLayer() && !shouldPaintOntoScrollingContentsLayer) |
| m_scrollingContentsLayer->setNeedsDisplay(); |
| } |
| } |
| |
| void CompositedLayerMapping::updateContentsOpaque() |
| { |
| if (isAcceleratedCanvas(layoutObject())) { |
| // Determine whether the rendering context's external texture layer is opaque. |
| CanvasRenderingContext* context = toHTMLCanvasElement(layoutObject()->node())->renderingContext(); |
| if (!context->creationAttributes().alpha()) |
| m_graphicsLayer->setContentsOpaque(true); |
| else if (WebLayer* layer = context->platformLayer()) |
| 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 { |
| // 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())); |
| |
| // When we 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::owningLayerClippedByLayerNotAboveCompositedAncestor(const PaintLayer* scrollParent) |
| { |
| if (!m_owningLayer.parent()) |
| return false; |
| |
| const PaintLayer* compositingAncestor = m_owningLayer.enclosingLayerWithCompositedLayerMapping(ExcludeSelf); |
| if (!compositingAncestor) |
| return false; |
| |
| const LayoutObject* clippingContainer = m_owningLayer.clippingContainer(); |
| if (!clippingContainer) |
| return false; |
| |
| if (clippingContainer->enclosingLayer() == scrollParent) |
| return false; |
| |
| if (compositingAncestor->layoutObject()->isDescendantOf(clippingContainer)) |
| return false; |
| |
| // 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, IgnoreOverlayScrollbarSize); |
| clipRectsContext.setIgnoreOverflowClip(); |
| IntRect parentClipRect = pixelSnappedIntRect(m_owningLayer.clipper().backgroundClipRect(clipRectsContext).rect()); |
| return parentClipRect != LayoutRect::infiniteIntRect(); |
| } |
| |
| 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(); |
| |
| if (PaintLayerReflectionInfo* reflection = m_owningLayer.reflectionInfo()) { |
| if (reflection->reflectionLayer()->hasCompositedLayerMapping()) |
| reflection->reflectionLayer()->compositedLayerMapping()->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 = owningLayerClippedByLayerNotAboveCompositedAncestor(scrollParent); |
| |
| if (updateClippingLayers(needsAncestorClip, needsDescendantsClippingLayer)) |
| layerConfigChanged = true; |
| |
| bool scrollingConfigChanged = false; |
| if (updateScrollingLayers(m_owningLayer.needsCompositedScrolling())) { |
| layerConfigChanged = true; |
| scrollingConfigChanged = 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); |
| |
| if (m_owningLayer.reflectionInfo() && !RuntimeEnabledFeatures::cssBoxReflectFilterEnabled()) { |
| if (m_owningLayer.reflectionInfo()->reflectionLayer()->hasCompositedLayerMapping()) { |
| GraphicsLayer* reflectionLayer = m_owningLayer.reflectionInfo()->reflectionLayer()->compositedLayerMapping()->mainGraphicsLayer(); |
| m_graphicsLayer->setReplicatedByLayer(reflectionLayer); |
| UseCounter::count(m_owningLayer.layoutObject()->document(), UseCounter::CompositedReplication); |
| } |
| } else { |
| m_graphicsLayer->setReplicatedByLayer(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)->remotePlatformLayer(); |
| m_graphicsLayer->setContentsToPlatformLayer(layer); |
| } |
| } else if (layoutObject->isVideo()) { |
| HTMLMediaElement* mediaElement = toHTMLMediaElement(layoutObject->node()); |
| m_graphicsLayer->setContentsToPlatformLayer(mediaElement->platformLayer()); |
| } else if (isCanvasControlledByOffscreen(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()); |
| |
| m_owningLayer.update3DTransformedDescendantStatus(); |
| 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) |
| { |
| LayoutPoint offset = layer->visualOffsetFromAncestor(compositedAncestor); |
| if (compositedAncestor) |
| offset.move(compositedAncestor->compositedLayerMapping()->owningLayer().subpixelAccumulation()); |
| return offset; |
| } |
| |
| void CompositedLayerMapping::computeBoundsOfOwningLayer(const PaintLayer* compositedAncestor, IntRect& localBounds, IntRect& compositingBoundsRelativeToCompositedAncestor, LayoutPoint& offsetFromCompositedAncestor, |
| IntPoint& snappedOffsetFromCompositedAncestor) |
| { |
| LayoutRect localRawCompositingBounds = compositedBounds(); |
| offsetFromCompositedAncestor = computeOffsetFromCompositedAncestor(&m_owningLayer, compositedAncestor); |
| 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 DCHECK_IS_ON() |
| 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 DCHECK_IS_ON() |
| 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.append(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); |
| updateContentsOffsetInCompositingLayer(snappedOffsetFromCompositedAncestor, graphicsLayerParentLocation); |
| 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) |
| clippingBox = clipBox(toLayoutBox(layoutObject())); |
| |
| updateChildTransformLayerGeometry(); |
| updateChildContainmentLayerGeometry(clippingBox, localCompositingBounds); |
| |
| updateMaskLayerGeometry(); |
| updateTransformGeometry(snappedOffsetFromCompositedAncestor, relativeCompositingBounds); |
| updateForegroundLayerGeometry(contentsSize, clippingBox); |
| updateBackgroundLayerGeometry(contentsSize); |
| updateReflectionLayerGeometry(layersNeedingPaintInvalidation); |
| 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()) { |
| // 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(), -layoutBox->borderTop()); |
| graphicsLayerParentLocation = -(scrollOrigin + scrollOffset); |
| } |
| } |
| |
| void CompositedLayerMapping::updateAncestorClippingLayerGeometry(const PaintLayer* compositingContainer, const IntPoint& snappedOffsetFromCompositedAncestor, IntPoint& graphicsLayerParentLocation) |
| { |
| if (!compositingContainer || !m_ancestorClippingLayer) |
| return; |
| |
| ClipRectsContext clipRectsContext(compositingContainer, PaintingClipRectsIgnoringOverflowClip, IgnoreOverlayScrollbarSize); |
| IntRect parentClipRect = pixelSnappedIntRect(m_owningLayer.clipper().backgroundClipRect(clipRectsContext).rect()); |
| ASSERT(parentClipRect != LayoutRect::infiniteIntRect()); |
| m_ancestorClippingLayer->setPosition(FloatPoint(parentClipRect.location() - graphicsLayerParentLocation)); |
| m_ancestorClippingLayer->setSize(FloatSize(parentClipRect.size())); |
| |
| // backgroundRect is relative to compositingContainer, so subtract snappedOffsetFromCompositedAncestor.X/snappedOffsetFromCompositedAncestor.Y to get back to local coords. |
| m_ancestorClippingLayer->setOffsetFromLayoutObject(parentClipRect.location() - snappedOffsetFromCompositedAncestor); |
| |
| // The primary layer is then parented in, and positioned relative to this clipping layer. |
| graphicsLayerParentLocation = parentClipRect.location(); |
| } |
| |
| void CompositedLayerMapping::updateOverflowControlsHostLayerGeometry(const PaintLayer* compositingStackingContext, const PaintLayer* compositingContainer) |
| { |
| if (!m_overflowControlsHostLayer) |
| return; |
| |
| LayoutPoint hostLayerPosition; |
| |
| if (needsToReparentOverflowControls()) { |
| if (m_overflowControlsAncestorClippingLayer) { |
| m_overflowControlsAncestorClippingLayer->setSize(m_ancestorClippingLayer->size()); |
| m_overflowControlsAncestorClippingLayer->setOffsetFromLayoutObject(m_ancestorClippingLayer->offsetFromLayoutObject()); |
| m_overflowControlsAncestorClippingLayer->setMasksToBounds(true); |
| hostLayerPosition = toLayoutPoint(LayoutSize(-m_overflowControlsAncestorClippingLayer->offsetFromLayoutObject())); |
| |
| FloatPoint position = m_ancestorClippingLayer->position(); |
| if (compositingStackingContext != compositingContainer) { |
| LayoutPoint offset; |
| compositingContainer->convertToLayerCoords(compositingStackingContext, offset); |
| FloatSize offsetFromStackingContainer = toFloatSize(FloatPoint(offset)); |
| position += offsetFromStackingContainer; |
| } |
| |
| m_overflowControlsAncestorClippingLayer->setPosition(position); |
| } 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(scrollableArea->adjustedScrollOffset())); |
| } |
| } |
| |
| // To clip correctly, m_overflowControlsHostLayer should match the border box rect, which is at |
| // the origin of the LayoutObject. The parent is m_graphicsLayer, so we must adjust the position |
| // by the distance from m_graphicsLayer to the LayoutObject. |
| |
| IntSize offsetFromLayoutObject = m_graphicsLayer->offsetFromLayoutObject() - roundedIntSize(m_owningLayer.subpixelAccumulation()); |
| hostLayerPosition.move(-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::updateReflectionLayerGeometry(Vector<PaintLayer*>& layersNeedingPaintInvalidation) |
| { |
| if (!m_owningLayer.reflectionInfo() || !m_owningLayer.reflectionInfo()->reflectionLayer()->hasCompositedLayerMapping()) |
| return; |
| |
| CompositedLayerMapping* reflectionCompositedLayerMapping = m_owningLayer.reflectionInfo()->reflectionLayer()->compositedLayerMapping(); |
| reflectionCompositedLayerMapping->updateGraphicsLayerGeometry(&m_owningLayer, &m_owningLayer, layersNeedingPaintInvalidation); |
| } |
| |
| void CompositedLayerMapping::updateScrollingLayerGeometry(const IntRect& localCompositingBounds) |
| { |
| if (!m_scrollingLayer) |
| return; |
| |
| ASSERT(m_scrollingContentsLayer); |
| LayoutBox* layoutBox = toLayoutBox(layoutObject()); |
| IntRect overflowClipRect = enclosingIntRect(layoutBox->overflowClipRect(LayoutPoint())); |
| DoubleSize adjustedScrollOffset = m_owningLayer.getScrollableArea()->adjustedScrollOffset(); |
| m_scrollingLayer->setPosition(FloatPoint(overflowClipRect.location() - localCompositingBounds.location() + roundedIntSize(m_owningLayer.subpixelAccumulation()))); |
| 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->scrollWidth().toInt(), layoutBox->scrollHeight().toInt()); |
| if (scrollSize != m_scrollingContentsLayer->size() || overflowClipRectOffsetChanged) |
| m_scrollingContentsLayer->setNeedsDisplay(); |
| |
| DoubleSize scrollingContentsOffset(overflowClipRect.location().x() - adjustedScrollOffset.width(), overflowClipRect.location().y() - adjustedScrollOffset.height()); |
| // 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(adjustedScrollOffset))); |
| } |
| 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()) |
| 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::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()->style()->canContainFixedPositionObjects() && !m_owningLayer.isRootLayer(); |
| // FIXME: we should make certain that childForSuperLayers will never be the m_squashingContainmentLayer here |
| scrollingCoordinator->setLayerIsContainerForFixedPositionLayers(childForSuperlayers(), 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()->frameHost().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()); |
| |
| // 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); |
| } |
| |
| 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; |
| } |
| 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); |
| |
| if (m_backgroundLayer) |
| m_backgroundLayer->setDrawsContent(hasPaintedContent); |
| |
| 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 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) { |
| m_ancestorClippingLayer->removeFromParent(); |
| m_ancestorClippingLayer = 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. |
| 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), |
| ApplyToAllGraphicsLayers = (ApplyToSquashingLayer | ApplyToScrollbarLayers | ApplyToBackgroundLayer | ApplyToMaskLayers | ApplyToLayersAffectedByPreserve3D | ApplyToContentLayers | ApplyToScrollingContentLayers) |
| }; |
| 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 & 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()); |
| } |
| |
| 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 past 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.scrollingElement(); |
| 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) |
| animatingStyle = scrollingElement->layoutObject()->style(); |
| } |
| } |
| |
| if (RuntimeEnabledFeatures::compositorWorkerEnabled() && animatingElement && 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::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) |
| { |
| if (owningLayerClippedByLayerNotAboveCompositedAncestor(scrollParent)) |
| return; |
| |
| const PaintLayer* 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_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 ENABLE(ASSERT) |
| 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 (m_owningLayer.isReflection()) |
| return false; |
| |
| 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 |
| { |
| ASSERT(layoutObject()->isImage()); |
| LayoutImage* imageLayoutObject = toLayoutImage(layoutObject()); |
| |
| if (m_owningLayer.hasBoxDecorationsOrBackground() |
| || imageLayoutObject->hasClip() |
| || imageLayoutObject->hasClipPath() |
| || imageLayoutObject->hasObjectFit()) |
| return false; |
| |
| if (ImageResource* 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() |
| { |
| ASSERT(layoutObject()->isImage()); |
| LayoutImage* imageLayoutObject = toLayoutImage(layoutObject()); |
| |
| ImageResource* 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(const PaintLayer& enclosingLayer) |
| { |
| 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) |
| { |
| 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); |
| IntRect parentClipRect = pixelSnappedIntRect(paintInfo.paintLayer->clipper().backgroundClipRect(clipRectsContext).rect()); |
| ASSERT(parentClipRect != LayoutRect::infiniteIntRect()); |
| |
| // Convert from ancestor to local coordinates. |
| IntSize ancestorToLocalOffset = paintInfo.offsetFromLayoutObject - ancestorPaintInfo->offsetFromLayoutObject; |
| parentClipRect.move(ancestorToLocalOffset); |
| return parentClipRect; |
| } |
| |
| 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)) { |
| LayoutRect bounds = paintInfo.compositedBounds; |
| bounds.move(paintInfo.paintLayer->subpixelAccumulation()); |
| dirtyRect.intersect(pixelSnappedIntRect(bounds)); |
| } else { |
| dirtyRect.move(roundedIntSize(paintInfo.paintLayer->subpixelAccumulation())); |
| } |
| |
| #if ENABLE(ASSERT) |
| if (!layoutObject()->view()->frame() || !layoutObject()->view()->frame()->shouldThrottleRendering()) |
| paintInfo.paintLayer->layoutObject()->assertSubtreeIsLaidOut(); |
| #endif |
| |
| float deviceScaleFactor = blink::deviceScaleFactor(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).paintLayer(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)); |
| } |
| |
| 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 screen space, including applying clips along the way. |
| LayoutRect visibleContentRect(graphicsLayerBoundsInObjectSpace); |
| LayoutView* rootView = anchorLayoutObject->view(); |
| while (!rootView->frame()->ownerLayoutItem().isNull()) |
| rootView = LayoutAPIShim::layoutObjectFrom(rootView->frame()->ownerLayoutItem())->view(); |
| anchorLayoutObject->mapToVisualRectInAncestorSpace(rootView, visibleContentRect); |
| visibleContentRect.intersect(LayoutRect(rootView->frameView()->visibleContentRect())); |
| |
| IntRect enclosingGraphicsLayerBounds(enclosingIntRect(graphicsLayerBounds)); |
| |
| // Map the visible content rect from screen 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(FloatRect(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; |
| |
| // Paint the whole layer if "mainFrameClipsContent" is false, meaning that WebPreferences::record_whole_document is true. |
| bool shouldPaintWholePage = !m_owningLayer.layoutObject()->document().settings()->mainFrameClipsContent(); |
| if (shouldPaintWholePage |
| || (graphicsLayer != m_graphicsLayer.get() && graphicsLayer != m_squashingLayer.get() && graphicsLayer != m_scrollingContentsLayer.get())) |
| return wholeLayerRect; |
| |
| IntRect newInterestRect = recomputeInterestRect(graphicsLayer); |
| if (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()) { |
| // Note: this is just the scroll offset, *not* the "adjusted scroll offset". Scroll offset |
| // does not include the origin adjustment. That is instead baked already into offsetFromLayoutObject. |
| DoubleSize scrollOffset = scrollableArea->scrollOffset(); |
| 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 ENABLE(ASSERT) |
| // 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 & GraphicsLayerPaintOverflowContents) |
| paintLayerFlags |= PaintLayerPaintingOverflowContents; |
| if (graphicsLayerPaintingPhase & GraphicsLayerPaintCompositedScroll) |
| paintLayerFlags |= PaintLayerPaintingCompositingScrollingPhase; |
| |
| 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()) { |
| |
| bool paintRootBackgroundOntoScrollingContentsLayer = m_backgroundPaintsOntoScrollingContentsLayer; |
| DCHECK(!paintRootBackgroundOntoScrollingContentsLayer || (!m_backgroundLayer && !m_foregroundLayer)); |
| if (paintRootBackgroundOntoScrollingContentsLayer) { |
| if (graphicsLayer == m_scrollingContentsLayer.get()) |
| paintLayerFlags &= ~PaintLayerPaintingSkipRootBackground; |
| else |
| 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); |
| } |
| InspectorInstrumentation::didPaint(m_owningLayer.layoutObject()->frame(), graphicsLayer, context, LayoutRect(interestRect)); |
| #if ENABLE(ASSERT) |
| 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()); |
| SkPictureBuilder pictureBuilder(layerBounds, nullptr, &context); |
| PaintLayerScrollableArea* scrollableArea = m_owningLayer.getScrollableArea(); |
| if (graphicsLayer == layerForHorizontalScrollbar()) { |
| paintScrollbar(scrollableArea->horizontalScrollbar(), pictureBuilder.context(), interestRect); |
| } else if (graphicsLayer == layerForVerticalScrollbar()) { |
| paintScrollbar(scrollableArea->verticalScrollbar(), pictureBuilder.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(pictureBuilder.context(), -scrollCornerAndResizerLocation, cullRect); |
| ScrollableAreaPainter(*scrollableArea).paintResizer(pictureBuilder.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); |
| pictureBuilder.endRecording()->playback(context.canvas()); |
| } |
| |
| bool CompositedLayerMapping::isScrollableAreaLayer(const GraphicsLayer* graphicsLayer) const |
| { |
| return graphicsLayer == layerForHorizontalScrollbar() |
| || graphicsLayer == layerForVerticalScrollbar() |
| || graphicsLayer == layerForScrollCorner(); |
| } |
| |
| bool CompositedLayerMapping::isTrackingPaintInvalidations() const |
| { |
| GraphicsLayerClient* client = compositor(); |
| return client ? client->isTrackingPaintInvalidations() : false; |
| } |
| |
| #if ENABLE(ASSERT) |
| void CompositedLayerMapping::verifyNotPainting() |
| { |
| ASSERT(!layoutObject()->frame()->page() || !layoutObject()->frame()->page()->isPainting()); |
| } |
| #endif |
| |
| void CompositedLayerMapping::notifyPaint(bool isFirstPaint, bool textPainted, bool imagePainted) |
| { |
| if (PaintTiming* timing = m_owningLayer.paintTiming()) |
| timing->notifyPaint(isFirstPaint, textPainted, imagePainted); |
| } |
| |
| 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.append(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 ENABLE(ASSERT) |
| 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.append(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_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 { |
| ASSERT_NOT_REACHED(); |
| } |
| |
| return name; |
| } |
| |
| } // namespace blink |