mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
This CL remove the body of Sky's TraceEvent.h in favor of the version in base. There's still some more of the system to unwind before we can remove all the other boilerplate. R=eseidel@chromium.org Review URL: https://codereview.chromium.org/691663002
2220 lines
96 KiB
C++
2220 lines
96 KiB
C++
/*
|
|
* 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 "config.h"
|
|
|
|
#include "core/rendering/compositing/CompositedLayerMapping.h"
|
|
|
|
#include "core/fetch/ImageResource.h"
|
|
#include "core/frame/FrameView.h"
|
|
#include "core/html/HTMLCanvasElement.h"
|
|
#include "core/html/HTMLMediaElement.h"
|
|
#include "core/html/canvas/CanvasRenderingContext.h"
|
|
#include "core/inspector/InspectorNodeIds.h"
|
|
#include "core/inspector/InspectorTraceEvents.h"
|
|
#include "core/page/Chrome.h"
|
|
#include "core/page/ChromeClient.h"
|
|
#include "core/page/Page.h"
|
|
#include "core/page/scrolling/ScrollingCoordinator.h"
|
|
#include "core/rendering/FilterEffectRenderer.h"
|
|
#include "core/rendering/RenderImage.h"
|
|
#include "core/rendering/RenderLayerStackingNodeIterator.h"
|
|
#include "core/rendering/RenderVideo.h"
|
|
#include "core/rendering/RenderView.h"
|
|
#include "core/rendering/compositing/RenderLayerCompositor.h"
|
|
#include "core/rendering/style/KeyframeList.h"
|
|
#include "platform/LengthFunctions.h"
|
|
#include "platform/RuntimeEnabledFeatures.h"
|
|
#include "platform/fonts/FontCache.h"
|
|
#include "platform/geometry/TransformState.h"
|
|
#include "platform/graphics/GraphicsContext.h"
|
|
#include "wtf/CurrentTime.h"
|
|
#include "wtf/text/StringBuilder.h"
|
|
|
|
namespace blink {
|
|
|
|
static IntRect clipBox(RenderBox* renderer);
|
|
|
|
static IntRect contentsRect(const RenderObject* renderer)
|
|
{
|
|
if (!renderer->isBox())
|
|
return IntRect();
|
|
|
|
return renderer->isVideo() ?
|
|
toRenderVideo(renderer)->videoBox() :
|
|
pixelSnappedIntRect(toRenderBox(renderer)->contentBoxRect());
|
|
}
|
|
|
|
static IntRect backgroundRect(const RenderObject* renderer)
|
|
{
|
|
if (!renderer->isBox())
|
|
return IntRect();
|
|
|
|
LayoutRect rect;
|
|
const RenderBox* box = toRenderBox(renderer);
|
|
EFillBox clip = box->style()->backgroundClip();
|
|
switch (clip) {
|
|
case BorderFillBox:
|
|
rect = box->borderBoxRect();
|
|
break;
|
|
case PaddingFillBox:
|
|
rect = box->paddingBoxRect();
|
|
break;
|
|
case ContentFillBox:
|
|
rect = box->contentBoxRect();
|
|
break;
|
|
case TextFillBox:
|
|
break;
|
|
}
|
|
|
|
return pixelSnappedIntRect(rect);
|
|
}
|
|
|
|
static inline bool isAcceleratedCanvas(const RenderObject* renderer)
|
|
{
|
|
if (renderer->isCanvas()) {
|
|
HTMLCanvasElement* canvas = toHTMLCanvasElement(renderer->node());
|
|
if (CanvasRenderingContext* context = canvas->renderingContext())
|
|
return context->isAccelerated();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool hasBoxDecorationsOrBackgroundImage(const RenderStyle* style)
|
|
{
|
|
return style->hasBoxDecorations() || style->hasBackgroundImage();
|
|
}
|
|
|
|
static bool contentLayerSupportsDirectBackgroundComposition(const RenderObject* renderer)
|
|
{
|
|
// No support for decorations - border, border-radius or outline.
|
|
// Only simple background - solid color or transparent.
|
|
if (hasBoxDecorationsOrBackgroundImage(renderer->style()))
|
|
return false;
|
|
|
|
// If there is no background, there is nothing to support.
|
|
if (!renderer->style()->hasBackground())
|
|
return true;
|
|
|
|
// Simple background that is contained within the contents rect.
|
|
return contentsRect(renderer).contains(backgroundRect(renderer));
|
|
}
|
|
|
|
static inline bool isAcceleratedContents(RenderObject* renderer)
|
|
{
|
|
return isAcceleratedCanvas(renderer)
|
|
|| renderer->isVideo();
|
|
}
|
|
|
|
// Get the scrolling coordinator in a way that works inside CompositedLayerMapping's destructor.
|
|
static ScrollingCoordinator* scrollingCoordinatorFromLayer(RenderLayer& layer)
|
|
{
|
|
Page* page = layer.renderer()->frame()->page();
|
|
if (!page)
|
|
return 0;
|
|
|
|
return page->scrollingCoordinator();
|
|
}
|
|
|
|
CompositedLayerMapping::CompositedLayerMapping(RenderLayer& layer)
|
|
: m_owningLayer(layer)
|
|
, m_contentOffsetInCompositingLayerDirty(false)
|
|
, m_pendingUpdateScope(GraphicsLayerUpdateNone)
|
|
, m_requiresOwnBackingStoreForIntrinsicReasons(false)
|
|
, m_requiresOwnBackingStoreForAncestorReasons(false)
|
|
, m_backgroundLayerPaintsFixedRootBackground(false)
|
|
, m_scrollingContentsAreEmpty(false)
|
|
{
|
|
createPrimaryGraphicsLayer();
|
|
}
|
|
|
|
CompositedLayerMapping::~CompositedLayerMapping()
|
|
{
|
|
// Hits in compositing/squashing/squash-onto-nephew.html.
|
|
DisableCompositingQueryAsserts disabler;
|
|
|
|
// Do not leave the destroyed pointer dangling on any RenderLayers that painted to this mapping's squashing layer.
|
|
for (size_t i = 0; i < m_squashedLayers.size(); ++i) {
|
|
RenderLayer* oldSquashedLayer = m_squashedLayers[i].renderLayer;
|
|
if (oldSquashedLayer->groupedMapping() == this) {
|
|
oldSquashedLayer->setGroupedMapping(0, true);
|
|
oldSquashedLayer->setLostGroupedMapping(true);
|
|
}
|
|
}
|
|
|
|
updateClippingLayers(false, false);
|
|
updateOverflowControlsLayers(false, false, false, false);
|
|
updateChildTransformLayer(false);
|
|
updateForegroundLayer(false);
|
|
updateBackgroundLayer(false);
|
|
updateMaskLayer(false);
|
|
updateClippingMaskLayers(false);
|
|
updateScrollingLayers(false);
|
|
updateSquashingLayers(false);
|
|
destroyGraphicsLayers();
|
|
}
|
|
|
|
PassOwnPtr<GraphicsLayer> CompositedLayerMapping::createGraphicsLayer(CompositingReasons reasons)
|
|
{
|
|
GraphicsLayerFactory* graphicsLayerFactory = 0;
|
|
if (Page* page = renderer()->frame()->page())
|
|
graphicsLayerFactory = page->chrome().client().graphicsLayerFactory();
|
|
|
|
OwnPtr<GraphicsLayer> graphicsLayer = GraphicsLayer::create(graphicsLayerFactory, this);
|
|
|
|
graphicsLayer->setCompositingReasons(reasons);
|
|
if (Node* owningNode = m_owningLayer.renderer()->generatingNode())
|
|
graphicsLayer->setOwnerNodeId(InspectorNodeIds::idForNode(owningNode));
|
|
|
|
return graphicsLayer.release();
|
|
}
|
|
|
|
void CompositedLayerMapping::createPrimaryGraphicsLayer()
|
|
{
|
|
m_graphicsLayer = createGraphicsLayer(m_owningLayer.compositingReasons());
|
|
|
|
updateOpacity(renderer()->style());
|
|
updateTransform(renderer()->style());
|
|
updateFilters(renderer()->style());
|
|
|
|
if (RuntimeEnabledFeatures::cssCompositingEnabled()) {
|
|
updateLayerBlendMode(renderer()->style());
|
|
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;
|
|
m_scrollingBlockSelectionLayer = nullptr;
|
|
}
|
|
|
|
void CompositedLayerMapping::updateOpacity(const RenderStyle* style)
|
|
{
|
|
m_graphicsLayer->setOpacity(compositingOpacity(style->opacity()));
|
|
}
|
|
|
|
void CompositedLayerMapping::updateTransform(const RenderStyle* 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.hasTransform()) {
|
|
style->applyTransform(t, toRenderBox(renderer())->pixelSnappedBorderBoxRect().size(), RenderStyle::ExcludeTransformOrigin);
|
|
makeMatrixRenderable(t, compositor()->hasAcceleratedCompositing());
|
|
}
|
|
|
|
m_graphicsLayer->setTransform(t);
|
|
}
|
|
|
|
void CompositedLayerMapping::updateFilters(const RenderStyle* style)
|
|
{
|
|
m_graphicsLayer->setFilters(owningLayer().computeFilterOperations(style));
|
|
}
|
|
|
|
void CompositedLayerMapping::updateLayerBlendMode(const RenderStyle* 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::updateContentsOpaque()
|
|
{
|
|
// For non-root layers, background is always painted by the primary graphics layer.
|
|
ASSERT(!m_backgroundLayer);
|
|
if (m_backgroundLayer) {
|
|
m_graphicsLayer->setContentsOpaque(false);
|
|
m_backgroundLayer->setContentsOpaque(m_owningLayer.backgroundIsKnownToBeOpaqueInRect(compositedBounds()));
|
|
} else {
|
|
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 RenderLayer.
|
|
m_compositedBounds = m_owningLayer.boundingBoxForCompositing();
|
|
m_contentOffsetInCompositingLayerDirty = true;
|
|
}
|
|
|
|
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.compositingReasons());
|
|
}
|
|
|
|
bool CompositedLayerMapping::owningLayerClippedByLayerNotAboveCompositedAncestor()
|
|
{
|
|
if (!m_owningLayer.parent())
|
|
return false;
|
|
|
|
const RenderLayer* compositingAncestor = m_owningLayer.enclosingLayerWithCompositedLayerMapping(ExcludeSelf);
|
|
if (!compositingAncestor)
|
|
return false;
|
|
|
|
const RenderObject* clippingContainer = m_owningLayer.clippingContainer();
|
|
if (!clippingContainer)
|
|
return false;
|
|
|
|
if (compositingAncestor->renderer()->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 RenderLayerClipper).
|
|
ClipRectsContext clipRectsContext(compositingAncestor, UncachedClipRects, IgnoreOverlayScrollbarSize);
|
|
clipRectsContext.setIgnoreOverflowClip();
|
|
IntRect parentClipRect = pixelSnappedIntRect(m_owningLayer.clipper().backgroundClipRect(clipRectsContext).rect());
|
|
return parentClipRect != PaintInfo::infiniteRect();
|
|
}
|
|
|
|
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();
|
|
|
|
RenderLayerCompositor* compositor = this->compositor();
|
|
RenderObject* renderer = this->renderer();
|
|
|
|
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;
|
|
|
|
RenderLayer* scrollParent = compositor->preferCompositingToLCDTextEnabled() ? m_owningLayer.scrollParent() : 0;
|
|
|
|
// This is required because compositing layers are parented
|
|
// according to the z-order hierarchy, yet clipping goes down the renderer hierarchy.
|
|
// Thus, a RenderLayer can be clipped by a RenderLayer that is an ancestor in the renderer 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();
|
|
|
|
if (scrollParent) {
|
|
// If our containing block is our ancestor scrolling layer, then we'll already be clipped
|
|
// to it via our scroll parent and we don't need an ancestor clipping layer.
|
|
if (m_owningLayer.renderer()->containingBlock()->enclosingLayer() == m_owningLayer.ancestorScrollingLayer())
|
|
needsAncestorClip = false;
|
|
}
|
|
|
|
if (updateClippingLayers(needsAncestorClip, needsDescendantsClippingLayer))
|
|
layerConfigChanged = true;
|
|
|
|
if (updateOverflowControlsLayers(requiresHorizontalScrollbarLayer(), requiresVerticalScrollbarLayer(), requiresScrollCornerLayer(), needsAncestorClip))
|
|
layerConfigChanged = true;
|
|
|
|
bool scrollingConfigChanged = false;
|
|
if (updateScrollingLayers(m_owningLayer.needsCompositedScrolling())) {
|
|
layerConfigChanged = true;
|
|
scrollingConfigChanged = true;
|
|
}
|
|
|
|
bool hasPerspective = false;
|
|
if (RenderStyle* style = renderer->style())
|
|
hasPerspective = style->hasPerspective();
|
|
bool needsChildTransformLayer = hasPerspective && (layerForChildrenTransform() == m_childTransformLayer.get()) && renderer->isBox();
|
|
if (updateChildTransformLayer(needsChildTransformLayer))
|
|
layerConfigChanged = true;
|
|
|
|
updateScrollParent(scrollParent);
|
|
updateClipParent();
|
|
|
|
if (updateSquashingLayers(!m_squashedLayers.isEmpty()))
|
|
layerConfigChanged = true;
|
|
|
|
if (layerConfigChanged)
|
|
updateInternalHierarchy();
|
|
|
|
if (scrollingConfigChanged) {
|
|
if (renderer->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 = false;
|
|
if (updateMaskLayer(renderer->hasMask())) {
|
|
maskLayerChanged = true;
|
|
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 = renderer->style()->clipPath();
|
|
bool needsChildClippingMask = (hasClipPath || renderer->style()->hasBorderRadius()) && (hasChildClippingLayer || isAcceleratedContents(renderer) || hasScrollingLayer());
|
|
if (updateClippingMaskLayers(needsChildClippingMask)) {
|
|
// Clip path clips the entire subtree, including scrollbars. It must be attached directly onto
|
|
// the main m_graphicsLayer.
|
|
if (hasClipPath)
|
|
m_graphicsLayer->setMaskLayer(m_childClippingMaskLayer.get());
|
|
else if (hasClippingLayer())
|
|
clippingLayer()->setMaskLayer(m_childClippingMaskLayer.get());
|
|
else if (hasScrollingLayer())
|
|
scrollingLayer()->setMaskLayer(m_childClippingMaskLayer.get());
|
|
else if (isAcceleratedContents(renderer))
|
|
m_graphicsLayer->setContentsClippingMaskLayer(m_childClippingMaskLayer.get());
|
|
}
|
|
|
|
updateBackgroundColor();
|
|
|
|
if (renderer->isImage()) {
|
|
if (isDirectlyCompositedImage()) {
|
|
updateImageContents();
|
|
} else if (m_graphicsLayer->hasContentsLayer()) {
|
|
m_graphicsLayer->setContentsToImage(0);
|
|
}
|
|
}
|
|
|
|
if (renderer->isVideo()) {
|
|
HTMLMediaElement* mediaElement = toHTMLMediaElement(renderer->node());
|
|
m_graphicsLayer->setContentsToPlatformLayer(mediaElement->platformLayer());
|
|
} else if (isAcceleratedCanvas(renderer)) {
|
|
HTMLCanvasElement* canvas = toHTMLCanvasElement(renderer->node());
|
|
if (CanvasRenderingContext* context = canvas->renderingContext())
|
|
m_graphicsLayer->setContentsToPlatformLayer(context->platformLayer());
|
|
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();
|
|
|
|
return layerConfigChanged;
|
|
}
|
|
|
|
static IntRect clipBox(RenderBox* renderer)
|
|
{
|
|
LayoutRect result = PaintInfo::infiniteRect();
|
|
if (renderer->hasOverflowClip())
|
|
result = renderer->overflowClipRect(LayoutPoint());
|
|
|
|
if (renderer->hasClip())
|
|
result.intersect(renderer->clipRect(LayoutPoint()));
|
|
|
|
return pixelSnappedIntRect(result);
|
|
}
|
|
|
|
static LayoutPoint computeOffsetFromCompositedAncestor(const RenderLayer* layer, const RenderLayer* compositedAncestor)
|
|
{
|
|
LayoutPoint offset;
|
|
layer->convertToLayerCoords(compositedAncestor, offset);
|
|
if (compositedAncestor)
|
|
offset.move(compositedAncestor->compositedLayerMapping()->owningLayer().subpixelAccumulation());
|
|
return offset;
|
|
}
|
|
|
|
void CompositedLayerMapping::computeBoundsOfOwningLayer(const RenderLayer* 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 LayoutPoint& offsetFromCompositedAncestor, const IntPoint& graphicsLayerParentLocation, const RenderLayer& referenceLayer,
|
|
Vector<GraphicsLayerPaintInfo>& layers, GraphicsLayer* squashingLayer, LayoutPoint* offsetFromTransformedAncestor, Vector<RenderLayer*>& layersNeedingPaintInvalidation)
|
|
{
|
|
if (!squashingLayer)
|
|
return;
|
|
ASSERT(compositor()->layerSquashingEnabled());
|
|
|
|
LayoutPoint offsetFromReferenceLayerToParentGraphicsLayer(offsetFromCompositedAncestor);
|
|
offsetFromReferenceLayerToParentGraphicsLayer.moveBy(-graphicsLayerParentLocation);
|
|
|
|
// FIXME: Cache these offsets.
|
|
LayoutPoint referenceOffsetFromTransformedAncestor = referenceLayer.computeOffsetFromTransformedAncestor();
|
|
|
|
LayoutRect totalSquashBounds;
|
|
for (size_t i = 0; i < layers.size(); ++i) {
|
|
LayoutRect squashedBounds = layers[i].renderLayer->boundingBoxForCompositing();
|
|
|
|
// Store the local bounds of the RenderLayer subtree before applying the offset.
|
|
layers[i].compositedBounds = squashedBounds;
|
|
|
|
LayoutPoint offsetFromTransformedAncestorForSquashedLayer = layers[i].renderLayer->computeOffsetFromTransformedAncestor();
|
|
LayoutSize offsetFromSquashingLayer = offsetFromTransformedAncestorForSquashedLayer - referenceOffsetFromTransformedAncestor;
|
|
|
|
squashedBounds.move(offsetFromSquashingLayer);
|
|
totalSquashBounds.unite(squashedBounds);
|
|
}
|
|
|
|
// The totalSquashBounds is positioned with respect to referenceLayer of this CompositedLayerMapping.
|
|
// But the squashingLayer needs to be positioned with respect to the ancestor CompositedLayerMapping.
|
|
// The conversion between referenceLayer and the ancestor CLM is already computed as
|
|
// offsetFromReferenceLayerToParentGraphicsLayer.
|
|
totalSquashBounds.moveBy(offsetFromReferenceLayerToParentGraphicsLayer);
|
|
IntRect squashLayerBounds = enclosingIntRect(totalSquashBounds);
|
|
IntPoint squashLayerOrigin = squashLayerBounds.location();
|
|
LayoutSize squashLayerOriginInOwningLayerSpace = squashLayerOrigin - offsetFromReferenceLayerToParentGraphicsLayer;
|
|
|
|
// Now that the squashing bounds are known, we can convert the RenderLayer painting offsets
|
|
// from CLM owning layer space to the squashing layer space.
|
|
//
|
|
// The painting offset we want to compute for each squashed RenderLayer is essentially the position of
|
|
// the squashed RenderLayer described w.r.t. referenceLayer's origin. For this purpose we already cached
|
|
// offsetFromSquashingCLM before, which describes where the squashed RenderLayer is located w.r.t.
|
|
// referenceLayer. So we just need to convert that point from referenceLayer space to referenceLayer
|
|
// space. This is simply done by subtracing squashLayerOriginInOwningLayerSpace, 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) {
|
|
LayoutPoint offsetFromTransformedAncestorForSquashedLayer = layers[i].renderLayer->computeOffsetFromTransformedAncestor();
|
|
LayoutSize offsetFromSquashLayerOrigin = (offsetFromTransformedAncestorForSquashedLayer - referenceOffsetFromTransformedAncestor) - squashLayerOriginInOwningLayerSpace;
|
|
|
|
// It is ok to issue paint invalidation here, because all of the geometry needed to correctly invalidate paint is computed by this point.
|
|
IntSize newOffsetFromRenderer = -IntSize(offsetFromSquashLayerOrigin.width().round(), offsetFromSquashLayerOrigin.height().round());
|
|
LayoutSize subpixelAccumulation = offsetFromSquashLayerOrigin + newOffsetFromRenderer;
|
|
if (layers[i].offsetFromRendererSet && layers[i].offsetFromRenderer != newOffsetFromRenderer) {
|
|
layers[i].renderLayer->paintInvalidator().paintInvalidationIncludingNonCompositingDescendants();
|
|
layersNeedingPaintInvalidation.append(layers[i].renderLayer);
|
|
}
|
|
layers[i].offsetFromRenderer = newOffsetFromRenderer;
|
|
layers[i].offsetFromRendererSet = true;
|
|
|
|
layers[i].renderLayer->setSubpixelAccumulation(subpixelAccumulation);
|
|
}
|
|
|
|
squashingLayer->setPosition(squashLayerBounds.location());
|
|
squashingLayer->setSize(squashLayerBounds.size());
|
|
|
|
*offsetFromTransformedAncestor = referenceOffsetFromTransformedAncestor;
|
|
offsetFromTransformedAncestor->move(squashLayerOriginInOwningLayerSpace);
|
|
|
|
for (size_t i = 0; i < layers.size(); ++i)
|
|
layers[i].localClipRectForSquashedLayer = localClipRectForSquashedLayer(referenceLayer, layers[i], layers);
|
|
}
|
|
|
|
void CompositedLayerMapping::updateGraphicsLayerGeometry(const RenderLayer* compositingContainer, const RenderLayer* compositingStackingContext, Vector<RenderLayer*>& 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 (!renderer()->style()->isRunningTransformAnimationOnCompositor())
|
|
updateTransform(renderer()->style());
|
|
|
|
// Set opacity, if it is not animating.
|
|
if (!renderer()->style()->isRunningOpacityAnimationOnCompositor())
|
|
updateOpacity(renderer()->style());
|
|
|
|
if (!renderer()->style()->isRunningFilterAnimationOnCompositor())
|
|
updateFilters(renderer()->style());
|
|
|
|
// 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);
|
|
updateOverflowControlsHostLayerGeometry(compositingStackingContext);
|
|
|
|
FloatSize contentsSize = relativeCompositingBounds.size();
|
|
|
|
updateMainGraphicsLayerGeometry(relativeCompositingBounds, localCompositingBounds, graphicsLayerParentLocation);
|
|
updateContentsOffsetInCompositingLayer(snappedOffsetFromCompositedAncestor, graphicsLayerParentLocation);
|
|
updateSquashingLayerGeometry(offsetFromCompositedAncestor, graphicsLayerParentLocation, m_owningLayer, 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(toRenderBox(renderer()));
|
|
|
|
updateChildContainmentLayerGeometry(clippingBox, localCompositingBounds);
|
|
updateChildTransformLayerGeometry();
|
|
|
|
updateMaskLayerGeometry();
|
|
updateTransformGeometry(snappedOffsetFromCompositedAncestor, relativeCompositingBounds);
|
|
updateForegroundLayerGeometry(contentsSize, clippingBox);
|
|
updateBackgroundLayerGeometry(contentsSize);
|
|
updateScrollingLayerGeometry(localCompositingBounds);
|
|
updateChildClippingMaskLayerGeometry();
|
|
|
|
if (m_owningLayer.scrollableArea() && m_owningLayer.scrollableArea()->scrollsOverflow())
|
|
m_owningLayer.scrollableArea()->positionOverflowControls(IntSize());
|
|
|
|
if (RuntimeEnabledFeatures::cssCompositingEnabled()) {
|
|
updateLayerBlendMode(renderer()->style());
|
|
updateIsRootForIsolatedGroup();
|
|
}
|
|
|
|
updateContentsRect();
|
|
updateBackgroundColor();
|
|
updateDrawsContent();
|
|
updateContentsOpaque();
|
|
updateRenderingContext();
|
|
updateShouldFlattenTransform();
|
|
updateChildrenTransform();
|
|
updateScrollParent(compositor()->preferCompositingToLCDTextEnabled() ? m_owningLayer.scrollParent() : 0);
|
|
|
|
updateCompositingReasons();
|
|
}
|
|
|
|
void CompositedLayerMapping::updateMainGraphicsLayerGeometry(const IntRect& relativeCompositingBounds, const IntRect& localCompositingBounds, const IntPoint& graphicsLayerParentLocation)
|
|
{
|
|
m_graphicsLayer->setPosition(FloatPoint(relativeCompositingBounds.location() - graphicsLayerParentLocation));
|
|
m_graphicsLayer->setOffsetFromRenderer(toIntSize(localCompositingBounds.location()));
|
|
|
|
FloatSize oldSize = m_graphicsLayer->size();
|
|
const IntSize& contentsSize = relativeCompositingBounds.size();
|
|
if (oldSize != contentsSize)
|
|
m_graphicsLayer->setSize(contentsSize);
|
|
|
|
m_graphicsLayer->setContentsVisible(true);
|
|
m_graphicsLayer->setBackfaceVisibility(renderer()->style()->backfaceVisibility() == BackfaceVisibilityVisible);
|
|
}
|
|
|
|
void CompositedLayerMapping::computeGraphicsLayerParentLocation(const RenderLayer* 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(toRenderBox(compositingContainer->renderer()));
|
|
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 = renderer()->view()->documentRect().location();
|
|
}
|
|
|
|
if (compositingContainer && compositingContainer->needsCompositedScrolling()) {
|
|
RenderBox* renderBox = toRenderBox(compositingContainer->renderer());
|
|
IntSize scrollOffset = renderBox->scrolledContentOffset();
|
|
IntPoint scrollOrigin(renderBox->borderLeft(), renderBox->borderTop());
|
|
graphicsLayerParentLocation = scrollOrigin - scrollOffset;
|
|
}
|
|
}
|
|
|
|
void CompositedLayerMapping::updateAncestorClippingLayerGeometry(const RenderLayer* 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 != PaintInfo::infiniteRect());
|
|
m_ancestorClippingLayer->setPosition(FloatPoint(parentClipRect.location() - graphicsLayerParentLocation));
|
|
m_ancestorClippingLayer->setSize(parentClipRect.size());
|
|
|
|
// backgroundRect is relative to compositingContainer, so subtract snappedOffsetFromCompositedAncestor.X/snappedOffsetFromCompositedAncestor.Y to get back to local coords.
|
|
m_ancestorClippingLayer->setOffsetFromRenderer(parentClipRect.location() - snappedOffsetFromCompositedAncestor);
|
|
|
|
// The primary layer is then parented in, and positioned relative to this clipping layer.
|
|
graphicsLayerParentLocation = parentClipRect.location();
|
|
}
|
|
|
|
void CompositedLayerMapping::updateOverflowControlsHostLayerGeometry(const RenderLayer* compositingStackingContext)
|
|
{
|
|
if (!m_overflowControlsHostLayer)
|
|
return;
|
|
|
|
if (needsToReparentOverflowControls()) {
|
|
if (m_overflowControlsClippingLayer) {
|
|
m_overflowControlsClippingLayer->setPosition(m_ancestorClippingLayer->position());
|
|
m_overflowControlsClippingLayer->setSize(m_ancestorClippingLayer->size());
|
|
m_overflowControlsClippingLayer->setOffsetFromRenderer(m_ancestorClippingLayer->offsetFromRenderer());
|
|
m_overflowControlsClippingLayer->setMasksToBounds(true);
|
|
|
|
m_overflowControlsHostLayer->setPosition(IntPoint(-m_overflowControlsClippingLayer->offsetFromRenderer()));
|
|
} 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.renderer()->mapLocalToContainer(compositingStackingContext->renderer(), transformState, ApplyContainerFlip);
|
|
transformState.flatten();
|
|
LayoutPoint offsetFromStackingContainer = LayoutPoint(transformState.lastPlanarPoint());
|
|
m_overflowControlsHostLayer->setPosition(FloatPoint(offsetFromStackingContainer));
|
|
}
|
|
} else {
|
|
m_overflowControlsHostLayer->setPosition(FloatPoint());
|
|
}
|
|
}
|
|
|
|
void CompositedLayerMapping::updateChildContainmentLayerGeometry(const IntRect& clippingBox, const IntRect& localCompositingBounds)
|
|
{
|
|
if (!m_childContainmentLayer)
|
|
return;
|
|
|
|
m_childContainmentLayer->setPosition(FloatPoint(clippingBox.location() - localCompositingBounds.location() + roundedIntSize(m_owningLayer.subpixelAccumulation())));
|
|
m_childContainmentLayer->setSize(clippingBox.size());
|
|
m_childContainmentLayer->setOffsetFromRenderer(toIntSize(clippingBox.location()));
|
|
if (m_childClippingMaskLayer && !m_scrollingLayer && !renderer()->style()->clipPath()) {
|
|
m_childClippingMaskLayer->setPosition(m_childContainmentLayer->position());
|
|
m_childClippingMaskLayer->setSize(m_childContainmentLayer->size());
|
|
m_childClippingMaskLayer->setOffsetFromRenderer(m_childContainmentLayer->offsetFromRenderer());
|
|
}
|
|
}
|
|
|
|
void CompositedLayerMapping::updateChildTransformLayerGeometry()
|
|
{
|
|
if (!m_childTransformLayer)
|
|
return;
|
|
const IntRect borderBox = toRenderBox(m_owningLayer.renderer())->pixelSnappedBorderBoxRect();
|
|
m_childTransformLayer->setSize(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->setOffsetFromRenderer(m_graphicsLayer->offsetFromRenderer());
|
|
}
|
|
|
|
void CompositedLayerMapping::updateTransformGeometry(const IntPoint& snappedOffsetFromCompositedAncestor, const IntRect& relativeCompositingBounds)
|
|
{
|
|
if (m_owningLayer.hasTransform()) {
|
|
const LayoutRect borderBox = toRenderBox(renderer())->borderBoxRect();
|
|
|
|
// Get layout bounds in the coords of compositingContainer to match relativeCompositingBounds.
|
|
IntRect layerBounds = pixelSnappedIntRect(toLayoutPoint(m_owningLayer.subpixelAccumulation()), borderBox.size());
|
|
layerBounds.moveBy(snappedOffsetFromCompositedAncestor);
|
|
|
|
// Update properties that depend on layer dimensions
|
|
FloatPoint3D transformOrigin = computeTransformOrigin(IntRect(IntPoint(), layerBounds.size()));
|
|
|
|
// |transformOrigin| is in the local space of this layer. layerBounds - relativeCompositingBounds converts to the space of the
|
|
// compositing bounds relative to the composited ancestor. This does not apply to the z direction, since the page is 2D.
|
|
FloatPoint3D compositedTransformOrigin(
|
|
layerBounds.x() - relativeCompositingBounds.x() + transformOrigin.x(),
|
|
layerBounds.y() - relativeCompositingBounds.y() + transformOrigin.y(),
|
|
transformOrigin.z());
|
|
m_graphicsLayer->setTransformOrigin(compositedTransformOrigin);
|
|
} else {
|
|
FloatPoint3D compositedTransformOrigin(
|
|
relativeCompositingBounds.width() * 0.5f,
|
|
relativeCompositingBounds.height() * 0.5f,
|
|
0.f);
|
|
m_graphicsLayer->setTransformOrigin(compositedTransformOrigin);
|
|
}
|
|
}
|
|
|
|
void CompositedLayerMapping::updateScrollingLayerGeometry(const IntRect& localCompositingBounds)
|
|
{
|
|
if (!m_scrollingLayer)
|
|
return;
|
|
|
|
ASSERT(m_scrollingContentsLayer);
|
|
RenderBox* renderBox = toRenderBox(renderer());
|
|
IntRect clientBox = enclosingIntRect(renderBox->clientBoxRect());
|
|
|
|
IntSize adjustedScrollOffset = m_owningLayer.scrollableArea()->adjustedScrollOffset();
|
|
m_scrollingLayer->setPosition(FloatPoint(clientBox.location() - localCompositingBounds.location() + roundedIntSize(m_owningLayer.subpixelAccumulation())));
|
|
m_scrollingLayer->setSize(clientBox.size());
|
|
|
|
IntSize oldScrollingLayerOffset = m_scrollingLayer->offsetFromRenderer();
|
|
m_scrollingLayer->setOffsetFromRenderer(-toIntSize(clientBox.location()));
|
|
|
|
if (m_childClippingMaskLayer && !renderer()->style()->clipPath()) {
|
|
m_childClippingMaskLayer->setPosition(m_scrollingLayer->position());
|
|
m_childClippingMaskLayer->setSize(m_scrollingLayer->size());
|
|
m_childClippingMaskLayer->setOffsetFromRenderer(toIntSize(clientBox.location()));
|
|
}
|
|
|
|
bool clientBoxOffsetChanged = oldScrollingLayerOffset != m_scrollingLayer->offsetFromRenderer();
|
|
|
|
IntSize scrollSize(renderBox->scrollWidth(), renderBox->scrollHeight());
|
|
if (scrollSize != m_scrollingContentsLayer->size() || clientBoxOffsetChanged)
|
|
m_scrollingContentsLayer->setNeedsDisplay();
|
|
|
|
IntSize scrollingContentsOffset = toIntSize(clientBox.location() - adjustedScrollOffset);
|
|
if (scrollingContentsOffset != m_scrollingContentsLayer->offsetFromRenderer() || scrollSize != m_scrollingContentsLayer->size()) {
|
|
bool coordinatorHandlesOffset = compositor()->scrollingLayerDidChange(&m_owningLayer);
|
|
m_scrollingContentsLayer->setPosition(coordinatorHandlesOffset ? FloatPoint() : FloatPoint(-adjustedScrollOffset));
|
|
}
|
|
|
|
m_scrollingContentsLayer->setSize(scrollSize);
|
|
// FIXME: The paint offset and the scroll offset should really be separate concepts.
|
|
m_scrollingContentsLayer->setOffsetFromRenderer(scrollingContentsOffset, GraphicsLayer::DontSetNeedsDisplay);
|
|
|
|
if (m_foregroundLayer) {
|
|
if (m_foregroundLayer->size() != m_scrollingContentsLayer->size())
|
|
m_foregroundLayer->setSize(m_scrollingContentsLayer->size());
|
|
m_foregroundLayer->setNeedsDisplay();
|
|
m_foregroundLayer->setOffsetFromRenderer(m_scrollingContentsLayer->offsetFromRenderer());
|
|
}
|
|
|
|
updateScrollingBlockSelection();
|
|
}
|
|
|
|
void CompositedLayerMapping::updateChildClippingMaskLayerGeometry()
|
|
{
|
|
if (!m_childClippingMaskLayer || !renderer()->style()->clipPath())
|
|
return;
|
|
RenderBox* renderBox = toRenderBox(renderer());
|
|
IntRect clientBox = enclosingIntRect(renderBox->clientBoxRect());
|
|
|
|
m_childClippingMaskLayer->setPosition(m_graphicsLayer->position());
|
|
m_childClippingMaskLayer->setSize(m_graphicsLayer->size());
|
|
m_childClippingMaskLayer->setOffsetFromRenderer(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->offsetFromRenderer();
|
|
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 layer's position) rather than leave the position the
|
|
// same and use offset-from-renderer + 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->setOffsetFromRenderer(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 = toRenderView(renderer())->frameView();
|
|
backgroundSize = frameView->visibleContentRect().size();
|
|
}
|
|
m_backgroundLayer->setPosition(FloatPoint());
|
|
if (backgroundSize != m_backgroundLayer->size()) {
|
|
m_backgroundLayer->setSize(backgroundSize);
|
|
m_backgroundLayer->setNeedsDisplay();
|
|
}
|
|
m_backgroundLayer->setOffsetFromRenderer(m_graphicsLayer->offsetFromRenderer());
|
|
}
|
|
|
|
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());
|
|
|
|
if (m_childContainmentLayer)
|
|
m_graphicsLayer->addChild(m_childContainmentLayer.get());
|
|
else if (m_childTransformLayer)
|
|
m_graphicsLayer->addChild(m_childTransformLayer.get());
|
|
|
|
if (m_scrollingLayer) {
|
|
GraphicsLayer* superLayer = m_graphicsLayer.get();
|
|
|
|
if (m_childContainmentLayer)
|
|
superLayer = m_childContainmentLayer.get();
|
|
|
|
if (m_childTransformLayer)
|
|
superLayer = m_childTransformLayer.get();
|
|
|
|
superLayer->addChild(m_scrollingLayer.get());
|
|
}
|
|
|
|
// The clip for child layers does not include space for overflow controls, so they exist as
|
|
// siblings of the clipping layer if we have one. Normal children of this layer are set as
|
|
// children of the clipping layer.
|
|
if (m_overflowControlsClippingLayer) {
|
|
ASSERT(m_overflowControlsHostLayer);
|
|
m_graphicsLayer->addChild(m_overflowControlsClippingLayer.get());
|
|
m_overflowControlsClippingLayer->addChild(m_overflowControlsHostLayer.get());
|
|
} else if (m_overflowControlsHostLayer) {
|
|
m_graphicsLayer->addChild(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(compositor()->layerSquashingEnabled());
|
|
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);
|
|
m_scrollingBlockSelectionLayer->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
|
|
// RenderLayer, 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::updateScrollingBlockSelection()
|
|
{
|
|
if (!m_scrollingBlockSelectionLayer)
|
|
return;
|
|
|
|
if (!m_scrollingContentsAreEmpty) {
|
|
// In this case, the selection will be painted directly into m_scrollingContentsLayer.
|
|
m_scrollingBlockSelectionLayer->setDrawsContent(false);
|
|
return;
|
|
}
|
|
|
|
const IntRect blockSelectionGapsBounds = m_owningLayer.blockSelectionGapsBounds();
|
|
const bool shouldDrawContent = !blockSelectionGapsBounds.isEmpty();
|
|
m_scrollingBlockSelectionLayer->setDrawsContent(shouldDrawContent);
|
|
if (!shouldDrawContent)
|
|
return;
|
|
|
|
const IntPoint position = blockSelectionGapsBounds.location() + m_owningLayer.scrollableArea()->adjustedScrollOffset();
|
|
if (m_scrollingBlockSelectionLayer->size() == blockSelectionGapsBounds.size() && m_scrollingBlockSelectionLayer->position() == position)
|
|
return;
|
|
|
|
m_scrollingBlockSelectionLayer->setPosition(position);
|
|
m_scrollingBlockSelectionLayer->setSize(blockSelectionGapsBounds.size());
|
|
m_scrollingBlockSelectionLayer->setOffsetFromRenderer(toIntSize(blockSelectionGapsBounds.location()), GraphicsLayer::SetNeedsDisplay);
|
|
}
|
|
|
|
void CompositedLayerMapping::updateDrawsContent()
|
|
{
|
|
if (m_scrollingLayer) {
|
|
// We don't have to consider overflow controls, because we know that the scrollbars are drawn elsewhere.
|
|
// m_graphicsLayer only needs backing store if the non-scrolling parts (background, outlines, borders, shadows etc) need to paint.
|
|
// m_scrollingLayer never has backing store.
|
|
// m_scrollingContentsLayer only needs backing store if the scrolled contents need to paint.
|
|
bool hasNonScrollingPaintedContent = m_owningLayer.hasBoxDecorationsOrBackground();
|
|
m_graphicsLayer->setDrawsContent(hasNonScrollingPaintedContent);
|
|
|
|
m_scrollingContentsAreEmpty = !(renderer()->hasBackground() || paintsChildren());
|
|
m_scrollingContentsLayer->setDrawsContent(!m_scrollingContentsAreEmpty);
|
|
|
|
updateScrollingBlockSelection();
|
|
return;
|
|
}
|
|
|
|
bool hasPaintedContent = containsPaintedContent();
|
|
if (hasPaintedContent && isAcceleratedCanvas(renderer())) {
|
|
CanvasRenderingContext* context = toHTMLCanvasElement(renderer()->node())->renderingContext();
|
|
// Content layer may be null if context is lost.
|
|
if (WebLayer* contentLayer = context->platformLayer()) {
|
|
Color bgColor(Color::transparent);
|
|
if (contentLayerSupportsDirectBackgroundComposition(renderer())) {
|
|
bgColor = rendererBackgroundColor();
|
|
}
|
|
contentLayer->setBackgroundColor(bgColor.rgb());
|
|
}
|
|
}
|
|
|
|
// FIXME: we could refine this to only allocate backings for one of these layers if possible.
|
|
m_graphicsLayer->setDrawsContent(hasPaintedContent);
|
|
if (m_foregroundLayer)
|
|
m_foregroundLayer->setDrawsContent(hasPaintedContent);
|
|
|
|
if (m_backgroundLayer)
|
|
m_backgroundLayer->setDrawsContent(hasPaintedContent);
|
|
}
|
|
|
|
void CompositedLayerMapping::updateChildrenTransform()
|
|
{
|
|
if (GraphicsLayer* childTransformLayer = layerForChildrenTransform()) {
|
|
childTransformLayer->setTransform(owningLayer().perspectiveTransform());
|
|
childTransformLayer->setTransformOrigin(FloatPoint3D(childTransformLayer->size().width() * 0.5f, childTransformLayer->size().height() * 0.5f, 0.f));
|
|
}
|
|
|
|
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);
|
|
layersChanged = true;
|
|
}
|
|
} else if (m_ancestorClippingLayer) {
|
|
m_ancestorClippingLayer->removeFromParent();
|
|
m_ancestorClippingLayer = nullptr;
|
|
layersChanged = true;
|
|
}
|
|
|
|
if (needsDescendantClip) {
|
|
// FIXME(sky): remove
|
|
} 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;
|
|
}
|
|
|
|
// Only a member function so it can call createGraphicsLayer.
|
|
bool CompositedLayerMapping::toggleScrollbarLayerIfNeeded(OwnPtr<GraphicsLayer>& layer, bool needsLayer, CompositingReasons reason)
|
|
{
|
|
if (needsLayer == !!layer)
|
|
return false;
|
|
layer = needsLayer ? createGraphicsLayer(reason) : nullptr;
|
|
return true;
|
|
}
|
|
|
|
bool CompositedLayerMapping::updateOverflowControlsLayers(bool needsHorizontalScrollbarLayer, bool needsVerticalScrollbarLayer, bool needsScrollCornerLayer, bool needsAncestorClip)
|
|
{
|
|
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 needsOverflowClipLayer = needsOverflowControlsHostLayer && needsAncestorClip;
|
|
toggleScrollbarLayerIfNeeded(m_overflowControlsClippingLayer, needsOverflowClipLayer, CompositingReasonLayerForOverflowControlsHost);
|
|
|
|
if (ScrollingCoordinator* scrollingCoordinator = scrollingCoordinatorFromLayer(m_owningLayer)) {
|
|
if (horizontalScrollbarLayerChanged)
|
|
scrollingCoordinator->scrollableAreaScrollbarLayerDidChange(m_owningLayer.scrollableArea(), HorizontalScrollbar);
|
|
if (verticalScrollbarLayerChanged)
|
|
scrollingCoordinator->scrollableAreaScrollbarLayerDidChange(m_owningLayer.scrollableArea(), VerticalScrollbar);
|
|
}
|
|
|
|
return horizontalScrollbarLayerChanged || verticalScrollbarLayerChanged || scrollCornerLayerChanged;
|
|
}
|
|
|
|
void CompositedLayerMapping::positionOverflowControlsLayers(const IntSize& offsetFromRoot)
|
|
{
|
|
IntSize offsetFromRenderer = m_graphicsLayer->offsetFromRenderer() - roundedIntSize(m_owningLayer.subpixelAccumulation());
|
|
if (GraphicsLayer* layer = layerForHorizontalScrollbar()) {
|
|
Scrollbar* hBar = m_owningLayer.scrollableArea()->horizontalScrollbar();
|
|
if (hBar) {
|
|
layer->setPosition(hBar->frameRect().location() - offsetFromRoot - offsetFromRenderer);
|
|
layer->setSize(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.scrollableArea()->verticalScrollbar();
|
|
if (vBar) {
|
|
layer->setPosition(vBar->frameRect().location() - offsetFromRoot - offsetFromRenderer);
|
|
layer->setSize(vBar->frameRect().size());
|
|
if (layer->hasContentsLayer())
|
|
layer->setContentsRect(IntRect(IntPoint(), vBar->frameRect().size()));
|
|
}
|
|
layer->setDrawsContent(vBar && !layer->hasContentsLayer());
|
|
}
|
|
|
|
if (GraphicsLayer* layer = layerForScrollCorner()) {
|
|
const LayoutRect& scrollCornerAndResizer = m_owningLayer.scrollableArea()->scrollCornerAndResizerRect();
|
|
layer->setPosition(scrollCornerAndResizer.location() - offsetFromRenderer);
|
|
layer->setSize(scrollCornerAndResizer.size());
|
|
layer->setDrawsContent(!scrollCornerAndResizer.isEmpty());
|
|
}
|
|
}
|
|
|
|
bool CompositedLayerMapping::hasUnpositionedOverflowControlsLayers() const
|
|
{
|
|
if (GraphicsLayer* layer = layerForHorizontalScrollbar()) {
|
|
if (!layer->drawsContent())
|
|
return true;
|
|
}
|
|
|
|
if (GraphicsLayer* layer = layerForVerticalScrollbar()) {
|
|
if (!layer->drawsContent())
|
|
return true;
|
|
}
|
|
|
|
if (GraphicsLayer* layer = layerForScrollCorner()) {
|
|
if (!layer->drawsContent())
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
enum ApplyToGraphicsLayersModeFlags {
|
|
ApplyToCoreLayers = (1 << 0),
|
|
ApplyToSquashingLayer = (1 << 1),
|
|
ApplyToScrollbarLayers = (1 << 2),
|
|
ApplyToBackgroundLayer = (1 << 3),
|
|
ApplyToMaskLayers = (1 << 4),
|
|
ApplyToContentLayers = (1 << 5),
|
|
ApplyToAllGraphicsLayers = (ApplyToSquashingLayer | ApplyToScrollbarLayers | ApplyToBackgroundLayer | ApplyToMaskLayers | ApplyToCoreLayers | ApplyToContentLayers)
|
|
};
|
|
typedef unsigned ApplyToGraphicsLayersMode;
|
|
|
|
template <typename Func>
|
|
static void ApplyToGraphicsLayers(const CompositedLayerMapping* mapping, const Func& f, ApplyToGraphicsLayersMode mode)
|
|
{
|
|
ASSERT(mode);
|
|
|
|
if ((mode & ApplyToCoreLayers) && mapping->squashingContainmentLayer())
|
|
f(mapping->squashingContainmentLayer());
|
|
if ((mode & ApplyToCoreLayers) && mapping->childTransformLayer())
|
|
f(mapping->childTransformLayer());
|
|
if ((mode & ApplyToCoreLayers) && mapping->ancestorClippingLayer())
|
|
f(mapping->ancestorClippingLayer());
|
|
if (((mode & ApplyToCoreLayers) || (mode & ApplyToContentLayers)) && mapping->mainGraphicsLayer())
|
|
f(mapping->mainGraphicsLayer());
|
|
if ((mode & ApplyToCoreLayers) && mapping->clippingLayer())
|
|
f(mapping->clippingLayer());
|
|
if ((mode & ApplyToCoreLayers) && mapping->scrollingLayer())
|
|
f(mapping->scrollingLayer());
|
|
if ((mode & ApplyToCoreLayers) && mapping->scrollingBlockSelectionLayer())
|
|
f(mapping->scrollingBlockSelectionLayer());
|
|
if (((mode & ApplyToCoreLayers) || (mode & ApplyToContentLayers)) && mapping->scrollingContentsLayer())
|
|
f(mapping->scrollingContentsLayer());
|
|
if (((mode & ApplyToCoreLayers) || (mode & ApplyToContentLayers)) && mapping->foregroundLayer())
|
|
f(mapping->foregroundLayer());
|
|
|
|
if ((mode & ApplyToSquashingLayer) && mapping->squashingLayer())
|
|
f(mapping->squashingLayer());
|
|
|
|
if (((mode & ApplyToMaskLayers) || (mode & ApplyToContentLayers)) && mapping->maskLayer())
|
|
f(mapping->maskLayer());
|
|
if (((mode & ApplyToMaskLayers) || (mode & ApplyToContentLayers)) && mapping->childClippingMaskLayer())
|
|
f(mapping->childClippingMaskLayer());
|
|
|
|
if (((mode & ApplyToBackgroundLayer) || (mode & ApplyToContentLayers)) && 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 (RenderLayer* root = m_owningLayer.renderingContextRoot()) {
|
|
if (Node* node = root->renderer()->node())
|
|
id = static_cast<int>(WTF::PtrHash<Node*>::hash(node));
|
|
}
|
|
|
|
UpdateRenderingContextFunctor functor = { id };
|
|
ApplyToGraphicsLayersMode mode = ApplyToAllGraphicsLayers & ~ApplyToSquashingLayer;
|
|
ApplyToGraphicsLayers<UpdateRenderingContextFunctor>(this, functor, mode);
|
|
}
|
|
|
|
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 = ApplyToCoreLayers;
|
|
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 (GraphicsLayer* childTransformLayer = layerForChildrenTransform()) {
|
|
bool hasPerspective = false;
|
|
if (RenderStyle* style = m_owningLayer.renderer()->style())
|
|
hasPerspective = style->hasPerspective();
|
|
if (hasPerspective)
|
|
childTransformLayer->setShouldFlattenTransform(false);
|
|
|
|
// Note, if the target is the scrolling layer, we need to ensure that the
|
|
// scrolling content layer doesn't flatten the transform. (It would be nice
|
|
// if we could apply transform to the scrolling content layer, but that's
|
|
// too late, we need the children transform to be applied _before_ the
|
|
// scrolling offset.)
|
|
if (childTransformLayer == m_scrollingLayer.get()) {
|
|
m_scrollingContentsLayer->setShouldFlattenTransform(false);
|
|
m_scrollingBlockSelectionLayer->setShouldFlattenTransform(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CompositedLayerMapping::updateForegroundLayer(bool needsForegroundLayer)
|
|
{
|
|
bool layerChanged = false;
|
|
if (needsForegroundLayer) {
|
|
if (!m_foregroundLayer) {
|
|
m_foregroundLayer = createGraphicsLayer(CompositingReasonLayerForForeground);
|
|
m_foregroundLayer->setDrawsContent(true);
|
|
m_foregroundLayer->setPaintingPhase(GraphicsLayerPaintForeground);
|
|
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->setDrawsContent(true);
|
|
m_backgroundLayer->setTransformOrigin(FloatPoint3D());
|
|
m_backgroundLayer->setPaintingPhase(GraphicsLayerPaintBackground);
|
|
#if !OS(ANDROID)
|
|
m_backgroundLayer->contentLayer()->setDrawCheckerboardForMissingTiles(true);
|
|
m_graphicsLayer->contentLayer()->setDrawCheckerboardForMissingTiles(false);
|
|
#endif
|
|
layerChanged = true;
|
|
}
|
|
} else {
|
|
if (m_backgroundLayer) {
|
|
m_backgroundLayer->removeFromParent();
|
|
m_backgroundLayer = nullptr;
|
|
#if !OS(ANDROID)
|
|
m_graphicsLayer->contentLayer()->setDrawCheckerboardForMissingTiles(true);
|
|
#endif
|
|
layerChanged = true;
|
|
}
|
|
}
|
|
|
|
if (layerChanged && !m_owningLayer.renderer()->documentBeingDestroyed())
|
|
compositor()->rootFixedBackgroundsChanged();
|
|
|
|
return layerChanged;
|
|
}
|
|
|
|
bool CompositedLayerMapping::updateMaskLayer(bool needsMaskLayer)
|
|
{
|
|
bool layerChanged = false;
|
|
if (needsMaskLayer) {
|
|
if (!m_maskLayer) {
|
|
m_maskLayer = createGraphicsLayer(CompositingReasonLayerForMask);
|
|
m_maskLayer->setDrawsContent(true);
|
|
m_maskLayer->setPaintingPhase(GraphicsLayerPaintMask);
|
|
layerChanged = true;
|
|
}
|
|
} else if (m_maskLayer) {
|
|
m_maskLayer = nullptr;
|
|
layerChanged = true;
|
|
}
|
|
|
|
return layerChanged;
|
|
}
|
|
|
|
bool CompositedLayerMapping::updateClippingMaskLayers(bool needsChildClippingMaskLayer)
|
|
{
|
|
bool layerChanged = false;
|
|
if (needsChildClippingMaskLayer) {
|
|
if (!m_childClippingMaskLayer) {
|
|
m_childClippingMaskLayer = createGraphicsLayer(CompositingReasonLayerForClippingMask);
|
|
m_childClippingMaskLayer->setDrawsContent(true);
|
|
m_childClippingMaskLayer->setPaintingPhase(GraphicsLayerPaintChildClippingMask);
|
|
layerChanged = true;
|
|
}
|
|
} else if (m_childClippingMaskLayer) {
|
|
m_childClippingMaskLayer = nullptr;
|
|
layerChanged = true;
|
|
}
|
|
return layerChanged;
|
|
}
|
|
|
|
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);
|
|
m_scrollingContentsLayer->setDrawsContent(true);
|
|
m_scrollingLayer->addChild(m_scrollingContentsLayer.get());
|
|
|
|
m_scrollingBlockSelectionLayer = createGraphicsLayer(CompositingReasonLayerForScrollingBlockSelection);
|
|
m_scrollingBlockSelectionLayer->setDrawsContent(true);
|
|
m_scrollingContentsLayer->addChild(m_scrollingBlockSelectionLayer.get());
|
|
|
|
layerChanged = true;
|
|
if (scrollingCoordinator)
|
|
scrollingCoordinator->scrollableAreaScrollLayerDidChange(m_owningLayer.scrollableArea());
|
|
}
|
|
} else if (m_scrollingLayer) {
|
|
m_scrollingLayer = nullptr;
|
|
m_scrollingContentsLayer = nullptr;
|
|
m_scrollingBlockSelectionLayer = nullptr;
|
|
layerChanged = true;
|
|
if (scrollingCoordinator)
|
|
scrollingCoordinator->scrollableAreaScrollLayerDidChange(m_owningLayer.scrollableArea());
|
|
}
|
|
|
|
return layerChanged;
|
|
}
|
|
|
|
static void updateScrollParentForGraphicsLayer(GraphicsLayer* layer, GraphicsLayer* topmostLayer, RenderLayer* 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(RenderLayer* 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);
|
|
}
|
|
}
|
|
|
|
void CompositedLayerMapping::updateClipParent()
|
|
{
|
|
if (owningLayerClippedByLayerNotAboveCompositedAncestor())
|
|
return;
|
|
|
|
RenderLayer* clipParent = m_owningLayer.clipParent();
|
|
if (clipParent)
|
|
clipParent = clipParent->enclosingLayerWithCompositedLayerMapping(IncludeSelf);
|
|
|
|
if (ScrollingCoordinator* scrollingCoordinator = scrollingCoordinatorFromLayer(m_owningLayer))
|
|
scrollingCoordinator->updateClipParentForGraphicsLayer(m_graphicsLayer.get(), clipParent);
|
|
}
|
|
|
|
bool CompositedLayerMapping::updateSquashingLayers(bool needsSquashingLayers)
|
|
{
|
|
bool layersChanged = false;
|
|
|
|
if (needsSquashingLayers) {
|
|
ASSERT(compositor()->layerSquashingEnabled());
|
|
|
|
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);
|
|
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 rendererOpacity) const
|
|
{
|
|
float finalOpacity = rendererOpacity;
|
|
|
|
for (RenderLayer* 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 || curr->compositingState() == HasOwnBackingButPaintsIntoAncestor)
|
|
break;
|
|
|
|
finalOpacity *= curr->renderer()->opacity();
|
|
}
|
|
|
|
return finalOpacity;
|
|
}
|
|
|
|
Color CompositedLayerMapping::rendererBackgroundColor() const
|
|
{
|
|
return renderer()->resolveColor(CSSPropertyBackgroundColor);
|
|
}
|
|
|
|
void CompositedLayerMapping::updateBackgroundColor()
|
|
{
|
|
m_graphicsLayer->setBackgroundColor(rendererBackgroundColor());
|
|
}
|
|
|
|
bool CompositedLayerMapping::paintsChildren() const
|
|
{
|
|
if (m_owningLayer.hasNonEmptyChildRenderers())
|
|
return true;
|
|
|
|
// FIXME: We shouldn't be called with a stale z-order lists. See bug 85512.
|
|
m_owningLayer.stackingNode()->updateLayerListsIfNeeded();
|
|
|
|
#if ENABLE(ASSERT)
|
|
LayerListMutationDetector mutationChecker(m_owningLayer.stackingNode());
|
|
#endif
|
|
|
|
RenderLayerStackingNodeIterator it(*m_owningLayer.stackingNode(), AllChildren);
|
|
while (RenderLayerStackingNode* stackingNode = it.next()) {
|
|
if (stackingNode->layer()->hasCompositedLayerMapping())
|
|
continue;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CompositedLayerMapping::containsPaintedContent() const
|
|
{
|
|
if (paintsIntoCompositedAncestor())
|
|
return false;
|
|
|
|
if (renderer()->isImage() && isDirectlyCompositedImage())
|
|
return false;
|
|
|
|
RenderObject* renderObject = renderer();
|
|
// 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 (renderObject->isVideo() && toRenderVideo(renderer())->shouldDisplayVideo())
|
|
return m_owningLayer.hasBoxDecorationsOrBackground();
|
|
|
|
if (m_owningLayer.hasVisibleBoxDecorations())
|
|
return true;
|
|
|
|
if (renderObject->hasMask()) // masks require special treatment
|
|
return true;
|
|
|
|
if (renderObject->isReplaced())
|
|
return true;
|
|
|
|
if (renderObject->node() && renderObject->node()->isDocumentNode()) {
|
|
// Look to see if the root object has a non-simple background
|
|
RenderObject* rootObject = renderObject->document().documentElement() ? renderObject->document().documentElement()->renderer() : 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->style()))
|
|
return true;
|
|
}
|
|
|
|
// FIXME: it's O(n^2). A better solution is needed.
|
|
return paintsChildren();
|
|
}
|
|
|
|
// An image can be directly compositing if it's the sole content of the layer, and has no box decorations
|
|
// that require painting. Direct compositing saves backing store.
|
|
bool CompositedLayerMapping::isDirectlyCompositedImage() const
|
|
{
|
|
ASSERT(renderer()->isImage());
|
|
|
|
RenderObject* renderObject = renderer();
|
|
if (m_owningLayer.hasBoxDecorationsOrBackground() || renderObject->hasClip() || renderObject->hasClipPath())
|
|
return false;
|
|
|
|
RenderImage* imageRenderer = toRenderImage(renderObject);
|
|
if (ImageResource* cachedImage = imageRenderer->cachedImage()) {
|
|
if (!cachedImage->hasImage())
|
|
return false;
|
|
|
|
Image* image = cachedImage->imageForRenderer(imageRenderer);
|
|
return image->isBitmapImage();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void CompositedLayerMapping::contentChanged(ContentChangeType changeType)
|
|
{
|
|
if ((changeType == ImageChanged) && renderer()->isImage() && isDirectlyCompositedImage()) {
|
|
updateImageContents();
|
|
return;
|
|
}
|
|
|
|
if (changeType == CanvasChanged && isAcceleratedCanvas(renderer())) {
|
|
m_graphicsLayer->setContentsNeedsDisplay();
|
|
return;
|
|
}
|
|
}
|
|
|
|
void CompositedLayerMapping::updateImageContents()
|
|
{
|
|
ASSERT(renderer()->isImage());
|
|
RenderImage* imageRenderer = toRenderImage(renderer());
|
|
|
|
ImageResource* cachedImage = imageRenderer->cachedImage();
|
|
if (!cachedImage)
|
|
return;
|
|
|
|
Image* image = cachedImage->imageForRenderer(imageRenderer);
|
|
if (!image)
|
|
return;
|
|
|
|
// We have to wait until the image is fully loaded before setting it on the layer.
|
|
if (!cachedImage->isLoaded())
|
|
return;
|
|
|
|
// This is a no-op if the layer doesn't have an inner layer for the image.
|
|
m_graphicsLayer->setContentsToImage(image);
|
|
|
|
// 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
|
|
{
|
|
RenderStyle* style = renderer()->style();
|
|
|
|
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
|
|
// renderer's contents are painted.
|
|
LayoutSize CompositedLayerMapping::contentOffsetInCompositingLayer() const
|
|
{
|
|
ASSERT(!m_contentOffsetInCompositingLayerDirty);
|
|
return m_contentOffsetInCompositingLayer;
|
|
}
|
|
|
|
LayoutRect CompositedLayerMapping::contentsBox() const
|
|
{
|
|
LayoutRect contentsBox = contentsRect(renderer());
|
|
contentsBox.move(contentOffsetInCompositingLayer());
|
|
return contentsBox;
|
|
}
|
|
|
|
bool CompositedLayerMapping::needsToReparentOverflowControls() const
|
|
{
|
|
return m_owningLayer.scrollableArea()
|
|
&& m_owningLayer.scrollableArea()->hasOverlayScrollbars()
|
|
&& m_owningLayer.scrollableArea()->topmostScrollChild();
|
|
}
|
|
|
|
GraphicsLayer* CompositedLayerMapping::detachLayerForOverflowControls(const RenderLayer& enclosingLayer)
|
|
{
|
|
GraphicsLayer* host = m_overflowControlsClippingLayer.get();
|
|
if (!host)
|
|
host = m_overflowControlsHostLayer.get();
|
|
host->removeFromParent();
|
|
return host;
|
|
}
|
|
|
|
GraphicsLayer* CompositedLayerMapping::parentForSublayers() const
|
|
{
|
|
if (m_scrollingBlockSelectionLayer)
|
|
return m_scrollingBlockSelectionLayer.get();
|
|
|
|
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();
|
|
}
|
|
|
|
GraphicsLayer* CompositedLayerMapping::childForSuperlayers() const
|
|
{
|
|
if (m_squashingContainmentLayer)
|
|
return m_squashingContainmentLayer.get();
|
|
|
|
if (m_ancestorClippingLayer)
|
|
return m_ancestorClippingLayer.get();
|
|
|
|
return m_graphicsLayer.get();
|
|
}
|
|
|
|
GraphicsLayer* CompositedLayerMapping::layerForChildrenTransform() const
|
|
{
|
|
if (GraphicsLayer* clipLayer = clippingLayer())
|
|
return clipLayer;
|
|
if (m_scrollingLayer)
|
|
return m_scrollingLayer.get();
|
|
return m_childTransformLayer.get();
|
|
}
|
|
|
|
bool CompositedLayerMapping::updateRequiresOwnBackingStoreForAncestorReasons(const RenderLayer* compositingAncestorLayer)
|
|
{
|
|
unsigned previousRequiresOwnBackingStoreForAncestorReasons = m_requiresOwnBackingStoreForAncestorReasons;
|
|
bool previousPaintsIntoCompositedAncestor = paintsIntoCompositedAncestor();
|
|
bool canPaintIntoAncestor = compositingAncestorLayer
|
|
&& (compositingAncestorLayer->compositedLayerMapping()->mainGraphicsLayer()->drawsContent()
|
|
|| compositingAncestorLayer->compositedLayerMapping()->paintsIntoCompositedAncestor());
|
|
|
|
m_requiresOwnBackingStoreForAncestorReasons = !canPaintIntoAncestor;
|
|
if (paintsIntoCompositedAncestor() != previousPaintsIntoCompositedAncestor) {
|
|
// Back out the change temporarily while invalidating with respect to the old container.
|
|
m_requiresOwnBackingStoreForAncestorReasons = !m_requiresOwnBackingStoreForAncestorReasons;
|
|
compositor()->paintInvalidationOnCompositingChange(&m_owningLayer);
|
|
m_requiresOwnBackingStoreForAncestorReasons = !m_requiresOwnBackingStoreForAncestorReasons;
|
|
}
|
|
|
|
return m_requiresOwnBackingStoreForAncestorReasons != previousRequiresOwnBackingStoreForAncestorReasons;
|
|
}
|
|
|
|
bool CompositedLayerMapping::updateRequiresOwnBackingStoreForIntrinsicReasons()
|
|
{
|
|
unsigned previousRequiresOwnBackingStoreForIntrinsicReasons = m_requiresOwnBackingStoreForIntrinsicReasons;
|
|
bool previousPaintsIntoCompositedAncestor = paintsIntoCompositedAncestor();
|
|
RenderObject* renderer = m_owningLayer.renderer();
|
|
m_requiresOwnBackingStoreForIntrinsicReasons = m_owningLayer.isRootLayer()
|
|
|| (m_owningLayer.compositingReasons() & CompositingReasonComboReasonsThatRequireOwnBacking)
|
|
|| m_owningLayer.transform()
|
|
|| m_owningLayer.clipsCompositingDescendantsWithBorderRadius() // FIXME: Revisit this if the paintsIntoCompositedAncestor state is removed.
|
|
|| renderer->isTransparent()
|
|
|| renderer->hasMask();
|
|
|
|
if (paintsIntoCompositedAncestor() != previousPaintsIntoCompositedAncestor) {
|
|
// Back out the change temporarily while invalidating with respect to the old container.
|
|
m_requiresOwnBackingStoreForIntrinsicReasons = !m_requiresOwnBackingStoreForIntrinsicReasons;
|
|
compositor()->paintInvalidationOnCompositingChange(&m_owningLayer);
|
|
m_requiresOwnBackingStoreForIntrinsicReasons = !m_requiresOwnBackingStoreForIntrinsicReasons;
|
|
}
|
|
|
|
return m_requiresOwnBackingStoreForIntrinsicReasons != previousRequiresOwnBackingStoreForIntrinsicReasons;
|
|
}
|
|
|
|
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.
|
|
ASSERT(!paintsIntoCompositedAncestor());
|
|
ApplyToGraphicsLayers(this, SetContentsNeedsDisplayFunctor(), ApplyToContentLayers);
|
|
}
|
|
|
|
struct SetContentsNeedsDisplayInRectFunctor {
|
|
void operator() (GraphicsLayer* layer) const
|
|
{
|
|
if (layer->drawsContent()) {
|
|
IntRect layerDirtyRect = r;
|
|
layerDirtyRect.move(-layer->offsetFromRenderer());
|
|
layer->setNeedsDisplayInRect(layerDirtyRect);
|
|
}
|
|
}
|
|
|
|
IntRect r;
|
|
};
|
|
|
|
// r is in the coordinate space of the layer's render object
|
|
void CompositedLayerMapping::setContentsNeedDisplayInRect(const LayoutRect& r)
|
|
{
|
|
// FIXME: need to split out paint invalidations for the background.
|
|
ASSERT(!paintsIntoCompositedAncestor());
|
|
|
|
SetContentsNeedsDisplayInRectFunctor functor = {
|
|
pixelSnappedIntRect(r.location() + m_owningLayer.subpixelAccumulation(), r.size())
|
|
};
|
|
ApplyToGraphicsLayers(this, functor, ApplyToContentLayers);
|
|
}
|
|
|
|
const GraphicsLayerPaintInfo* CompositedLayerMapping::containingSquashedLayer(const RenderObject* renderObject, const Vector<GraphicsLayerPaintInfo>& layers)
|
|
{
|
|
for (size_t i = 0; i < layers.size(); ++i) {
|
|
if (renderObject->isDescendantOf(layers[i].renderLayer->renderer()))
|
|
return &layers[i];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const GraphicsLayerPaintInfo* CompositedLayerMapping::containingSquashedLayer(const RenderObject* renderObject)
|
|
{
|
|
return CompositedLayerMapping::containingSquashedLayer(renderObject, m_squashedLayers);
|
|
}
|
|
|
|
IntRect CompositedLayerMapping::localClipRectForSquashedLayer(const RenderLayer& referenceLayer, const GraphicsLayerPaintInfo& paintInfo, const Vector<GraphicsLayerPaintInfo>& layers)
|
|
{
|
|
const RenderObject* clippingContainer = paintInfo.renderLayer->clippingContainer();
|
|
if (clippingContainer == referenceLayer.clippingContainer())
|
|
return PaintInfo::infiniteRect();
|
|
|
|
ASSERT(clippingContainer);
|
|
|
|
const GraphicsLayerPaintInfo* ancestorPaintInfo = containingSquashedLayer(clippingContainer, layers);
|
|
// Must be there, otherwise CompositingLayerAssigner::canSquashIntoCurrentSquashingOwner would have disallowed squashing.
|
|
ASSERT(ancestorPaintInfo);
|
|
|
|
// FIXME: this is a potential performance issue. We shoudl consider caching these clip rects or otherwise optimizing.
|
|
ClipRectsContext clipRectsContext(ancestorPaintInfo->renderLayer, UncachedClipRects);
|
|
IntRect parentClipRect = pixelSnappedIntRect(paintInfo.renderLayer->clipper().backgroundClipRect(clipRectsContext).rect());
|
|
ASSERT(parentClipRect != PaintInfo::infiniteRect());
|
|
|
|
// Convert from ancestor to local coordinates.
|
|
IntSize ancestorToLocalOffset = paintInfo.offsetFromRenderer - ancestorPaintInfo->offsetFromRenderer;
|
|
parentClipRect.move(ancestorToLocalOffset);
|
|
return parentClipRect;
|
|
}
|
|
|
|
void CompositedLayerMapping::doPaintTask(const GraphicsLayerPaintInfo& paintInfo, const PaintLayerFlags& paintLayerFlags, GraphicsContext* context,
|
|
const IntRect& clip) // In the coords of rootLayer.
|
|
{
|
|
RELEASE_ASSERT(paintInfo.renderLayer->compositingState() == PaintsIntoGroupedBacking || !paintsIntoCompositedAncestor());
|
|
|
|
FontCachePurgePreventer fontCachePurgePreventer;
|
|
|
|
// Note carefully: in theory it is appropriate to invoke context->save() here
|
|
// and restore the context after painting. For efficiency, we are assuming that
|
|
// it is equivalent to manually undo this offset translation, which means we are
|
|
// assuming that the context's space was not affected by the RenderLayer
|
|
// painting code.
|
|
|
|
IntSize offset = paintInfo.offsetFromRenderer;
|
|
context->translate(-offset.width(), -offset.height());
|
|
|
|
// 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.renderLayer->subpixelAccumulation());
|
|
dirtyRect.intersect(pixelSnappedIntRect(bounds));
|
|
} else {
|
|
dirtyRect.move(roundedIntSize(paintInfo.renderLayer->subpixelAccumulation()));
|
|
}
|
|
|
|
#if ENABLE(ASSERT)
|
|
paintInfo.renderLayer->renderer()->assertSubtreeIsLaidOut();
|
|
#endif
|
|
|
|
if (paintInfo.renderLayer->compositingState() != PaintsIntoGroupedBacking) {
|
|
// FIXME: GraphicsLayers need a way to split for RenderRegions.
|
|
LayerPaintingInfo paintingInfo(paintInfo.renderLayer, dirtyRect, PaintBehaviorNormal, paintInfo.renderLayer->subpixelAccumulation());
|
|
paintInfo.renderLayer->paintLayerContents(context, paintingInfo, paintLayerFlags);
|
|
|
|
if (paintInfo.renderLayer->containsDirtyOverlayScrollbars())
|
|
paintInfo.renderLayer->paintLayerContents(context, paintingInfo, paintLayerFlags | PaintLayerPaintingOverlayScrollbars);
|
|
} else {
|
|
ASSERT(compositor()->layerSquashingEnabled());
|
|
LayerPaintingInfo paintingInfo(paintInfo.renderLayer, dirtyRect, PaintBehaviorNormal, paintInfo.renderLayer->subpixelAccumulation());
|
|
|
|
// RenderLayer::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()).
|
|
context->save();
|
|
dirtyRect.intersect(paintInfo.localClipRectForSquashedLayer);
|
|
context->clip(dirtyRect);
|
|
paintInfo.renderLayer->paintLayer(context, paintingInfo, paintLayerFlags);
|
|
context->restore();
|
|
}
|
|
|
|
ASSERT(!paintInfo.renderLayer->usedTransparency());
|
|
|
|
// Manually restore the context to its original state by applying the opposite translation.
|
|
context->translate(offset.width(), offset.height());
|
|
}
|
|
|
|
static void paintScrollbar(Scrollbar* scrollbar, GraphicsContext& context, const IntRect& clip)
|
|
{
|
|
if (!scrollbar)
|
|
return;
|
|
|
|
context.save();
|
|
const IntRect& scrollbarRect = scrollbar->frameRect();
|
|
context.translate(-scrollbarRect.x(), -scrollbarRect.y());
|
|
IntRect transformedClip = clip;
|
|
transformedClip.moveBy(scrollbarRect.location());
|
|
scrollbar->paint(&context, transformedClip);
|
|
context.restore();
|
|
}
|
|
|
|
// Up-call from compositing layer drawing callback.
|
|
void CompositedLayerMapping::paintContents(const GraphicsLayer* graphicsLayer, GraphicsContext& context, GraphicsLayerPaintingPhase graphicsLayerPaintingPhase, const IntRect& clip)
|
|
{
|
|
// https://code.google.com/p/chromium/issues/detail?id=343772
|
|
DisableCompositingQueryAsserts disabler;
|
|
#if ENABLE(ASSERT)
|
|
// FIXME: once the state machine is ready, this can be removed and we can refer to that instead.
|
|
if (Page* page = renderer()->frame()->page())
|
|
page->setIsPainting(true);
|
|
#endif
|
|
TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "Paint", "data", InspectorPaintEvent::data(m_owningLayer.renderer(), clip, graphicsLayer));
|
|
TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline.stack"), "CallStack", TRACE_EVENT_SCOPE_PROCESS, "stack", InspectorCallStackEvent::currentCallStack());
|
|
|
|
PaintLayerFlags paintLayerFlags = 0;
|
|
if (graphicsLayerPaintingPhase & GraphicsLayerPaintBackground)
|
|
paintLayerFlags |= PaintLayerPaintingCompositingBackgroundPhase;
|
|
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)
|
|
paintLayerFlags |= (PaintLayerPaintingRootBackgroundOnly | PaintLayerPaintingCompositingForegroundPhase); // Need PaintLayerPaintingCompositingForegroundPhase to walk child layers.
|
|
else if (compositor()->fixedRootBackgroundLayer())
|
|
paintLayerFlags |= PaintLayerPaintingSkipRootBackground;
|
|
|
|
if (graphicsLayer == m_graphicsLayer.get()
|
|
|| graphicsLayer == m_foregroundLayer.get()
|
|
|| graphicsLayer == m_backgroundLayer.get()
|
|
|| graphicsLayer == m_maskLayer.get()
|
|
|| graphicsLayer == m_childClippingMaskLayer.get()
|
|
|| graphicsLayer == m_scrollingContentsLayer.get()
|
|
|| graphicsLayer == m_scrollingBlockSelectionLayer.get()) {
|
|
|
|
GraphicsLayerPaintInfo paintInfo;
|
|
paintInfo.renderLayer = &m_owningLayer;
|
|
paintInfo.compositedBounds = compositedBounds();
|
|
paintInfo.offsetFromRenderer = graphicsLayer->offsetFromRenderer();
|
|
|
|
// We have to use the same root as for hit testing, because both methods can compute and cache clipRects.
|
|
doPaintTask(paintInfo, paintLayerFlags, &context, clip);
|
|
} else if (graphicsLayer == m_squashingLayer.get()) {
|
|
ASSERT(compositor()->layerSquashingEnabled());
|
|
for (size_t i = 0; i < m_squashedLayers.size(); ++i)
|
|
doPaintTask(m_squashedLayers[i], paintLayerFlags, &context, clip);
|
|
} else if (graphicsLayer == layerForHorizontalScrollbar()) {
|
|
paintScrollbar(m_owningLayer.scrollableArea()->horizontalScrollbar(), context, clip);
|
|
} else if (graphicsLayer == layerForVerticalScrollbar()) {
|
|
paintScrollbar(m_owningLayer.scrollableArea()->verticalScrollbar(), context, clip);
|
|
} else if (graphicsLayer == layerForScrollCorner()) {
|
|
const IntRect& scrollCornerAndResizer = m_owningLayer.scrollableArea()->scrollCornerAndResizerRect();
|
|
context.save();
|
|
context.translate(-scrollCornerAndResizer.x(), -scrollCornerAndResizer.y());
|
|
IntRect transformedClip = clip;
|
|
transformedClip.moveBy(scrollCornerAndResizer.location());
|
|
m_owningLayer.scrollableArea()->paintScrollCorner(&context, IntPoint(), transformedClip);
|
|
m_owningLayer.scrollableArea()->paintResizer(&context, IntPoint(), transformedClip);
|
|
context.restore();
|
|
}
|
|
#if ENABLE(ASSERT)
|
|
if (Page* page = renderer()->frame()->page())
|
|
page->setIsPainting(false);
|
|
#endif
|
|
}
|
|
|
|
bool CompositedLayerMapping::isTrackingPaintInvalidations() const
|
|
{
|
|
GraphicsLayerClient* client = compositor();
|
|
return client ? client->isTrackingPaintInvalidations() : false;
|
|
}
|
|
|
|
#if ENABLE(ASSERT)
|
|
void CompositedLayerMapping::verifyNotPainting()
|
|
{
|
|
ASSERT(!renderer()->frame()->page() || !renderer()->frame()->page()->isPainting());
|
|
}
|
|
#endif
|
|
|
|
void CompositedLayerMapping::notifyAnimationStarted(const GraphicsLayer*, double monotonicTime)
|
|
{
|
|
renderer()->node()->document().compositorPendingAnimations().notifyCompositorAnimationStarted(monotonicTime);
|
|
}
|
|
|
|
IntRect CompositedLayerMapping::pixelSnappedCompositedBounds() const
|
|
{
|
|
LayoutRect bounds = m_compositedBounds;
|
|
bounds.move(m_owningLayer.subpixelAccumulation());
|
|
return pixelSnappedIntRect(bounds);
|
|
}
|
|
|
|
bool CompositedLayerMapping::updateSquashingLayerAssignment(RenderLayer* squashedLayer, const RenderLayer& owningLayer, size_t nextSquashedLayerIndex)
|
|
{
|
|
ASSERT(compositor()->layerSquashingEnabled());
|
|
|
|
GraphicsLayerPaintInfo paintInfo;
|
|
paintInfo.renderLayer = squashedLayer;
|
|
// NOTE: composited bounds are updated elsewhere
|
|
// NOTE: offsetFromRenderer 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.
|
|
bool updatedAssignment = false;
|
|
if (nextSquashedLayerIndex < m_squashedLayers.size()) {
|
|
if (paintInfo.renderLayer != m_squashedLayers[nextSquashedLayerIndex].renderLayer) {
|
|
compositor()->paintInvalidationOnCompositingChange(squashedLayer);
|
|
updatedAssignment = true;
|
|
m_squashedLayers[nextSquashedLayerIndex] = paintInfo;
|
|
}
|
|
} else {
|
|
compositor()->paintInvalidationOnCompositingChange(squashedLayer);
|
|
m_squashedLayers.append(paintInfo);
|
|
updatedAssignment = true;
|
|
}
|
|
squashedLayer->setGroupedMapping(this);
|
|
return updatedAssignment;
|
|
}
|
|
|
|
void CompositedLayerMapping::removeRenderLayerFromSquashingGraphicsLayer(const RenderLayer* layer)
|
|
{
|
|
size_t layerIndex = kNotFound;
|
|
|
|
for (size_t i = 0; i < m_squashedLayers.size(); ++i) {
|
|
if (m_squashedLayers[i].renderLayer == layer) {
|
|
layerIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (layerIndex == kNotFound)
|
|
return;
|
|
|
|
m_squashedLayers.remove(layerIndex);
|
|
}
|
|
|
|
void CompositedLayerMapping::finishAccumulatingSquashingLayers(size_t nextSquashedLayerIndex)
|
|
{
|
|
ASSERT(compositor()->layerSquashingEnabled());
|
|
|
|
// Any additional squashed RenderLayers in the array no longer exist, and removing invalidates the squashingLayer contents.
|
|
if (nextSquashedLayerIndex < m_squashedLayers.size())
|
|
m_squashedLayers.remove(nextSquashedLayerIndex, m_squashedLayers.size() - nextSquashedLayerIndex);
|
|
}
|
|
|
|
String CompositedLayerMapping::debugName(const GraphicsLayer* graphicsLayer)
|
|
{
|
|
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";
|
|
} 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_overflowControlsClippingLayer.get()) {
|
|
name = "Overflow Controls ClipLayer Layer";
|
|
} else if (graphicsLayer == m_scrollingLayer.get()) {
|
|
name = "Scrolling Layer";
|
|
} else if (graphicsLayer == m_scrollingContentsLayer.get()) {
|
|
name = "Scrolling Contents Layer";
|
|
} else if (graphicsLayer == m_scrollingBlockSelectionLayer.get()) {
|
|
name = "Scrolling Block Selection Layer";
|
|
} else {
|
|
ASSERT_NOT_REACHED();
|
|
}
|
|
|
|
return name;
|
|
}
|
|
|
|
} // namespace blink
|