Bug 1168263 - Propagate the scroll-clip of a descendant of a layer with a perspective transform up to the layer itself. r=kats,mstange
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -741,23 +741,26 @@ IntersectMaybeRects(const Maybe<IntRectT
return a;
} else {
return Some(a->Intersect(*b));
}
}
bool
AsyncCompositionManager::ApplyAsyncContentTransformToTree(Layer *aLayer,
- bool* aOutFoundRoot)
+ bool* aOutFoundRoot,
+ Maybe<ParentLayerIntRect>& aClipDeferredToParent)
{
+ Maybe<ParentLayerIntRect> clipDeferredFromChildren;
bool appliedTransform = false;
for (Layer* child = aLayer->GetFirstChild();
child; child = child->GetNextSibling()) {
appliedTransform |=
- ApplyAsyncContentTransformToTree(child, aOutFoundRoot);
+ ApplyAsyncContentTransformToTree(child, aOutFoundRoot,
+ clipDeferredFromChildren);
}
Matrix4x4 oldTransform = aLayer->GetTransform();
Matrix4x4 combinedAsyncTransform;
bool hasAsyncTransform = false;
ScreenMargin fixedLayerMargins;
@@ -852,17 +855,26 @@ AsyncCompositionManager::ApplyAsyncConte
asyncClip = Some(TransformTo<ParentLayerPixel>(asyncTransform, *asyncClip));
}
// Combine the local clip with the ancestor scrollframe clip. This is not
// included in the async transform above, since the ancestor clip should not
// move with this APZC.
if (metrics.HasClipRect()) {
ParentLayerIntRect clip = metrics.ClipRect();
- asyncClip = IntersectMaybeRects(Some(clip), asyncClip);
+ if (aLayer->GetParent() && aLayer->GetParent()->GetTransformIsPerspective()) {
+ // If our parent layer has a perspective transform, we want to apply
+ // our scroll clip to it instead of to this layer (see bug 1168263).
+ // A layer with a perspective transform shouldn't have multiple
+ // children with FrameMetrics, nor a child with multiple FrameMetrics.
+ MOZ_ASSERT(!aClipDeferredToParent);
+ aClipDeferredToParent = Some(clip);
+ } else {
+ asyncClip = IntersectMaybeRects(Some(clip), asyncClip);
+ }
}
// Do the same for the ancestor mask layers: ancestorMaskLayers contains
// the ancestor mask layers for scroll frames *inside* the current scroll
// frame, so these are the ones we need to shift by our async transform.
for (Layer* ancestorMaskLayer : ancestorMaskLayers) {
SetShadowTransform(ancestorMaskLayer,
ancestorMaskLayer->GetLocalTransform() * asyncTransform);
@@ -888,21 +900,22 @@ AsyncCompositionManager::ApplyAsyncConte
AdjustForClip(asyncTransformWithoutOverscroll, aLayer);
// Since fixed/sticky layers are relative to their nearest scrolling ancestor,
// we use the ViewID from the bottommost scrollable metrics here.
AlignFixedAndStickyLayers(aLayer, aLayer, metrics.GetScrollId(), oldTransform,
transformWithoutOverscrollOrOmta, fixedLayerMargins);
}
+ if (hasAsyncTransform || clipDeferredFromChildren) {
+ aLayer->AsLayerComposite()->SetShadowClipRect(
+ IntersectMaybeRects(asyncClip, clipDeferredFromChildren));
+ }
+
if (hasAsyncTransform) {
- if (asyncClip) {
- aLayer->AsLayerComposite()->SetShadowClipRect(asyncClip);
- }
-
// Apply the APZ transform on top of GetLocalTransform() here (rather than
// GetTransform()) in case the OMTA code in SampleAnimations already set a
// shadow transform; in that case we want to apply ours on top of that one
// rather than clobber it.
SetShadowTransform(aLayer,
aLayer->GetLocalTransform() * AdjustForClip(combinedAsyncTransform, aLayer));
// Do the same for the layer's own mask layer, if it has one.
@@ -1310,17 +1323,18 @@ AsyncCompositionManager::TransformShadow
// async using Gecko). If this fails, fall back to transforming the
// primary scrollable layer. "Failing" here means that we don't
// find a frame that is async scrollable. Note that the fallback
// code also includes Fennec which is rendered async. Fennec uses
// its own platform-specific async rendering that is done partially
// in Gecko and partially in Java.
wantNextFrame |= SampleAPZAnimations(LayerMetricsWrapper(root), aCurrentFrame);
bool foundRoot = false;
- if (ApplyAsyncContentTransformToTree(root, &foundRoot)) {
+ Maybe<ParentLayerIntRect> clipDeferredFromChildren;
+ if (ApplyAsyncContentTransformToTree(root, &foundRoot, clipDeferredFromChildren)) {
#if defined(MOZ_ANDROID_APZ)
MOZ_ASSERT(foundRoot);
#endif
} else {
nsAutoTArray<Layer*,1> scrollableLayers;
#ifdef MOZ_WIDGET_ANDROID
mLayerManager->GetRootScrollableLayers(scrollableLayers);
#else
--- a/gfx/layers/composite/AsyncCompositionManager.h
+++ b/gfx/layers/composite/AsyncCompositionManager.h
@@ -127,18 +127,21 @@ public:
void GetFrameUniformity(FrameUniformityData* aFrameUniformityData);
private:
void TransformScrollableLayer(Layer* aLayer);
// Return true if an AsyncPanZoomController content transform was
// applied for |aLayer|. |*aOutFoundRoot| is set to true on Android only, if
// one of the metrics on one of the layers was determined to be the "root"
// and its state was synced to the Java front-end. |aOutFoundRoot| must be
- // non-null.
- bool ApplyAsyncContentTransformToTree(Layer* aLayer, bool* aOutFoundRoot);
+ // non-null. As the function recurses over the layer tree, a layer may
+ // populate |aClipDeferredToParent| a clip rect it wants to set on its parent.
+ bool ApplyAsyncContentTransformToTree(Layer* aLayer,
+ bool* aOutFoundRoot,
+ Maybe<ParentLayerIntRect>& aClipDeferredToParent);
/**
* Update the shadow transform for aLayer assuming that is a scrollbar,
* so that it stays in sync with the content that is being scrolled by APZ.
*/
void ApplyAsyncTransformToScrollbar(Layer* aLayer);
void SetFirstPaintViewport(const LayerIntPoint& aOffset,
const CSSToLayerScale& aZoom,