Bug 1214151 - If a sticky element does not consume all of the un-adjustment for an async transform, allow a descendant fixed or sticky element to consume it. r=mstange
MozReview-Commit-ID: HHGjs4GZGNX
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -500,42 +500,57 @@ AsyncCompositionManager::AlignFixedAndSt
// transform. This translation will apply on top of the layer's local
// transform, but |anchor| and |transformedAnchor| are in a coordinate space
// where the local transform isn't applied yet, so apply it and then subtract
// to get the desired translation.
auto localTransformTyped = ViewAs<LayerToParentLayerMatrix4x4>(localTransform);
ParentLayerPoint translation = TransformBy(localTransformTyped, transformedAnchor)
- TransformBy(localTransformTyped, anchor);
+ // A fixed layer will "consume" (be unadjusted by) the entire translation
+ // calculated above. A sticky layer may consume all, part, or none of it,
+ // depending on where we are relative to its sticky scroll range.
+ bool translationConsumed = true;
+
if (layer->GetIsStickyPosition()) {
// For sticky positioned layers, the difference between the two rectangles
// defines a pair of translation intervals in each dimension through which
// the layer should not move relative to the scroll container. To
// accomplish this, we limit each dimension of the |translation| to that
// part of it which overlaps those intervals.
const LayerRect& stickyOuter = layer->GetStickyScrollRangeOuter();
const LayerRect& stickyInner = layer->GetStickyScrollRangeInner();
// TODO: There's a unit mismatch here, as |translation| is in ParentLayer
// space while |stickyOuter| and |stickyInner| are in Layer space.
+ ParentLayerPoint originalTranslation = translation;
translation.y = IntervalOverlap(translation.y, stickyOuter.y, stickyOuter.YMost()) -
IntervalOverlap(translation.y, stickyInner.y, stickyInner.YMost());
translation.x = IntervalOverlap(translation.x, stickyOuter.x, stickyOuter.XMost()) -
IntervalOverlap(translation.x, stickyInner.x, stickyInner.XMost());
+ if (translation != originalTranslation) {
+ translationConsumed = false;
+ }
}
// Finally, apply the translation to the layer transform. Note that in cases
// where the async transform on |aTransformedSubtreeRoot| affects this layer's
// clip rect, we need to apply the same translation to said clip rect, so
// that the effective transform on the clip rect takes it back to where it was
// originally, had there been no async scroll.
TranslateShadowLayer(layer, ThebesPoint(translation.ToUnknownPoint()),
true, aClipPartsCache);
- return TraversalFlag::Skip;
+ // If we didn't consume the entire translation, continue the traversal
+ // to allow a descendant fixed or sticky layer to consume the rest.
+ // TODO: We curently don't handle the case where we consume part but not
+ // all of the translation correctly. In such a case,
+ // |a[Previous|Current]TransformForRoot| would need to be adjusted
+ // to reflect only the unconsumed part of the translation.
+ return translationConsumed ? TraversalFlag::Skip : TraversalFlag::Continue;
});
}
static void
SampleValue(float aPortion, Animation& aAnimation, StyleAnimationValue& aStart,
StyleAnimationValue& aEnd, Animatable* aValue, Layer* aLayer)
{
StyleAnimationValue interpolatedValue;