Bug 1399505 - Generate WR sticky frames from nsDisplayStickyPosition display items. r?mstange
MozReview-Commit-ID: 4ZCcdlBtEGi
--- a/gfx/layers/wr/StackingContextHelper.cpp
+++ b/gfx/layers/wr/StackingContextHelper.cpp
@@ -99,16 +99,22 @@ StackingContextHelper::StackingContextHe
StackingContextHelper::~StackingContextHelper()
{
if (mBuilder) {
mBuilder->PopStackingContext();
}
}
+void
+StackingContextHelper::AdjustOrigin(const LayerPoint& aDelta)
+{
+ mOrigin += aDelta;
+}
+
wr::LayoutRect
StackingContextHelper::ToRelativeLayoutRect(const LayerRect& aRect) const
{
return wr::ToLayoutRect(RoundedToInt(aRect - mOrigin));
}
wr::LayoutRect
StackingContextHelper::ToRelativeLayoutRect(const LayoutDeviceRect& aRect) const
--- a/gfx/layers/wr/StackingContextHelper.h
+++ b/gfx/layers/wr/StackingContextHelper.h
@@ -64,16 +64,18 @@ public:
// of the tree, so that we have a StackingContextHelper to pass down into
// the RenderLayer traversal, but don't actually want it to push a stacking
// context on the display list builder.
StackingContextHelper();
// Pops the stacking context, if one was pushed during the constructor.
~StackingContextHelper();
+ void AdjustOrigin(const LayerPoint& aDelta);
+
// When this StackingContextHelper is in scope, this function can be used
// to convert a rect from the layer system's coordinate space to a LayoutRect
// that is relative to the stacking context. This is useful because most
// things that are pushed inside the stacking context need to be relative
// to the stacking context.
// We allow passing in a LayoutDeviceRect for convenience because in a lot of
// cases with WebRender display item generate the layout device space is the
// same as the layer space. (TODO: try to make this more explicit somehow).
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -7037,16 +7037,124 @@ nsDisplayStickyPosition::BuildLayer(nsDi
aContainerParameters.mXScale,
NSAppUnitsToFloatPixels(inner.height, factor) *
aContainerParameters.mYScale);
layer->SetStickyPositionData(scrollId, stickyOuter, stickyInner);
return layer.forget();
}
+bool
+nsDisplayStickyPosition::CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
+ mozilla::wr::IpcResourceUpdateQueue& aResources,
+ const StackingContextHelper& aSc,
+ WebRenderLayerManager* aManager,
+ nsDisplayListBuilder* aDisplayListBuilder)
+{
+ LayerPoint scTranslation;
+ StickyScrollContainer* stickyScrollContainer = StickyScrollContainer::GetStickyScrollContainerForFrame(mFrame);
+ if (stickyScrollContainer) {
+ float auPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
+
+ bool snap;
+ nsRect itemBounds = GetBounds(aDisplayListBuilder, &snap);
+
+ // The itemBounds here already take into account the main-thread
+ // position:sticky implementation, so we need to unapply that.
+ nsIFrame* firstCont = nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame);
+ nsPoint translation = stickyScrollContainer->ComputePosition(firstCont) - firstCont->GetNormalPosition();
+ itemBounds.MoveBy(-translation);
+ scTranslation = ViewAs<LayerPixel>(
+ LayoutDevicePoint::FromAppUnits(translation, auPerDevPixel),
+ PixelCastJustification::WebRenderHasUnitResolution);
+
+ LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(itemBounds, auPerDevPixel);
+
+ Maybe<wr::StickySideConstraint> top;
+ Maybe<wr::StickySideConstraint> right;
+ Maybe<wr::StickySideConstraint> bottom;
+ Maybe<wr::StickySideConstraint> left;
+
+ nsRect outer;
+ nsRect inner;
+ stickyScrollContainer->GetScrollRanges(mFrame, &outer, &inner);
+
+ nsRect scrollPort = stickyScrollContainer->ScrollFrame()->GetScrollPortRect();
+
+ // The following computations make more sense upon understanding the
+ // semantics of "inner" and "outer", which is explained in the comment on
+ // SetStickyPositionData in Layers.h.
+
+ if (outer.YMost() != inner.YMost()) {
+ // Question: How far will itemBounds.y be from the top of the scrollport
+ // when we have scrolled down from the current scroll position of "0" to a
+ // scroll position of "inner.YMost()" (which is >= 0 since we are
+ // scrolling down)?
+ // Answer: (itemBounds.y - 0) - (inner.YMost() - 0)
+ // == itemBounds.y - inner.YMost()
+ float margin = NSAppUnitsToFloatPixels(itemBounds.y - inner.YMost(), auPerDevPixel);
+ // The scroll distance during which the item should remain "stuck"
+ float maxOffset = NSAppUnitsToFloatPixels(outer.YMost() - inner.YMost(), auPerDevPixel);
+ top = Some(wr::StickySideConstraint { margin, maxOffset });
+ }
+ if (outer.y != inner.y) {
+ // Question: How far will itemBounds.YMost() be from the bottom of the
+ // scrollport when we have scrolled up from the current scroll position of
+ // "0" to a scroll position of "inner.y" (which is <= 0 since we are
+ // scrolling up)?
+ // Answer: (scrollPort.height - itemBounds.YMost()) - (0 - inner.y)
+ // == scrollPort.height - itemBounds.YMost() + inner.y
+ float margin = NSAppUnitsToFloatPixels(scrollPort.height - itemBounds.YMost() + inner.y, auPerDevPixel);
+ // The scroll distance during which the item should remain "stuck"
+ float maxOffset = NSAppUnitsToFloatPixels(outer.y - inner.y, auPerDevPixel);
+ bottom = Some(wr::StickySideConstraint { margin, maxOffset });
+ }
+ // Same as above, but for the x-axis
+ if (outer.XMost() != inner.XMost()) {
+ float margin = NSAppUnitsToFloatPixels(itemBounds.x - inner.XMost(), auPerDevPixel);
+ float maxOffset = NSAppUnitsToFloatPixels(outer.XMost() - inner.XMost(), auPerDevPixel);
+ left = Some(wr::StickySideConstraint { margin, maxOffset });
+ }
+ if (outer.x != inner.x) {
+ float margin = NSAppUnitsToFloatPixels(scrollPort.width - itemBounds.XMost() + inner.x, auPerDevPixel);
+ float maxOffset = NSAppUnitsToFloatPixels(outer.x - inner.x, auPerDevPixel);
+ right = Some(wr::StickySideConstraint { margin, maxOffset });
+ }
+
+ wr::WrStickyId id = aBuilder.DefineStickyFrame(aSc.ToRelativeLayoutRect(bounds),
+ top.ptrOr(nullptr), right.ptrOr(nullptr), bottom.ptrOr(nullptr), left.ptrOr(nullptr));
+
+ aBuilder.PushStickyFrame(id);
+ }
+
+ // All the things inside this position:sticky item also have the main-thread
+ // translation already applied, so we need to make sure that gets unapplied.
+ // The easiest way to do it is to just create a new stacking context with an
+ // adjusted origin and use that for the nested items. This way all the
+ // ToRelativeLayoutRect calls on this StackingContextHelper object will
+ // include the necessary adjustment.
+ StackingContextHelper sc(aSc, aBuilder, aDisplayListBuilder, this,
+ &mList, nullptr, 0, nullptr, nullptr);
+ sc.AdjustOrigin(scTranslation);
+
+ // TODO: if, inside this nested command builder, we try to turn a gecko clip
+ // chain into a WR clip chain, we might end up repushing the clip stack
+ // without `id` which effectively throws out the sticky behaviour. The
+ // repushing can happen because of the need to define a new clip while
+ // particular things are on the stack
+ nsDisplayOwnLayer::CreateWebRenderCommands(aBuilder, aResources, sc,
+ aManager, aDisplayListBuilder);
+
+ if (stickyScrollContainer) {
+ aBuilder.PopStickyFrame();
+ }
+
+ return true;
+}
+
nsDisplayScrollInfoLayer::nsDisplayScrollInfoLayer(
nsDisplayListBuilder* aBuilder,
nsIFrame* aScrolledFrame,
nsIFrame* aScrollFrame)
: nsDisplayWrapList(aBuilder, aScrollFrame)
, mScrollFrame(aScrollFrame)
, mScrolledFrame(aScrolledFrame)
, mScrollParentId(aBuilder->GetCurrentScrollParentId())
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -4561,16 +4561,22 @@ public:
return mozilla::LAYER_ACTIVE;
}
virtual bool CanMerge(const nsDisplayItem* aItem) const override
{
// Items with the same fixed position frame can be merged.
return HasSameTypeAndClip(aItem) && mFrame == aItem->Frame();
}
+
+ virtual bool CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
+ mozilla::wr::IpcResourceUpdateQueue& aResources,
+ const StackingContextHelper& aSc,
+ mozilla::layers::WebRenderLayerManager* aManager,
+ nsDisplayListBuilder* aDisplayListBuilder) override;
};
class nsDisplayFixedPosition : public nsDisplayOwnLayer {
public:
nsDisplayFixedPosition(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
nsDisplayList* aList,
const ActiveScrolledRoot* aActiveScrolledRoot);
--- a/layout/reftests/async-scrolling/reftest.list
+++ b/layout/reftests/async-scrolling/reftest.list
@@ -27,17 +27,17 @@ skip-if(!asyncPan) == position-fixed-cov
skip-if(!asyncPan) == position-fixed-cover-2.html position-fixed-cover-2-ref.html
skip-if(!asyncPan) == position-fixed-cover-3.html position-fixed-cover-3-ref.html
fuzzy-if(Android,5,4) skip-if(!asyncPan) == position-fixed-transformed-1.html position-fixed-transformed-1-ref.html
skip-if(!asyncPan) == split-layers-1.html split-layers-1-ref.html
skip-if(!asyncPan) == split-layers-multi-scrolling-1.html split-layers-multi-scrolling-1-ref.html
fuzzy-if(skiaContent,2,240000) fuzzy-if(browserIsRemote&&!skiaContent&&(cocoaWidget||winWidget),1,240000) skip-if(!asyncPan) == split-opacity-layers-1.html split-opacity-layers-1-ref.html
skip-if(!asyncPan) == sticky-pos-scrollable-1.html sticky-pos-scrollable-1-ref.html
fails-if(webrender) skip-if(!asyncPan) == sticky-pos-scrollable-2.html sticky-pos-scrollable-2-ref.html # bug 1366295 for webrender
-fails-if(webrender) skip-if(!asyncPan) == sticky-pos-scrollable-3.html sticky-pos-scrollable-3-ref.html # bug 1366295 for webrender
+skip-if(!asyncPan) == sticky-pos-scrollable-3.html sticky-pos-scrollable-3-ref.html
skip-if(!asyncPan) == fixed-pos-scrollable-1.html fixed-pos-scrollable-1-ref.html
skip-if(!asyncPan) == culling-1.html culling-1-ref.html
skip-if(!asyncPan) == position-fixed-iframe-1.html position-fixed-iframe-1-ref.html
skip-if(!asyncPan) == position-fixed-iframe-2.html position-fixed-iframe-2-ref.html
fuzzy-if(skiaContent,1,11300) skip-if(!asyncPan) == position-fixed-in-scroll-container.html position-fixed-in-scroll-container-ref.html
skip-if(!asyncPan) == position-fixed-inside-sticky-1.html position-fixed-inside-sticky-1-ref.html
skip-if(!asyncPan) == position-fixed-inside-sticky-2.html position-fixed-inside-sticky-2-ref.html
fuzzy(1,60000) skip-if(!asyncPan) == group-opacity-surface-size-1.html group-opacity-surface-size-1-ref.html
@@ -54,17 +54,17 @@ fuzzy-if(Android,7,4) skip-if(!asyncPan)
pref(apz.disable_for_scroll_linked_effects,true) skip-if(!asyncPan) == disable-apz-for-sle-pages.html disable-apz-for-sle-pages-ref.html
fuzzy-if(browserIsRemote&&d2d,1,19) skip-if(!asyncPan) == background-blend-mode-1.html background-blend-mode-1-ref.html
random-if(webrender) skip-if(Android||!asyncPan) != opaque-fractional-displayport-1.html about:blank # test is specific to "layers" and not valid with webrender
random-if(webrender) skip-if(Android||!asyncPan) != opaque-fractional-displayport-2.html about:blank # test is specific to "layers" and not valid with webrender
fuzzy-if(Android,6,4) fails-if(webrender) skip-if(!asyncPan) == fixed-pos-scrolled-clip-1.html fixed-pos-scrolled-clip-1-ref.html # bug 1373802 for webrender
fuzzy-if(Android,6,8) fails-if(webrender) skip-if(!asyncPan) == fixed-pos-scrolled-clip-2.html fixed-pos-scrolled-clip-2-ref.html # bug 1373802 for webrender
fuzzy-if(Android,6,8) fails-if(webrender) skip-if(!asyncPan) == fixed-pos-scrolled-clip-3.html fixed-pos-scrolled-clip-3-ref.html # bug 1373802 for webrender
fuzzy-if(Android,6,8) fails-if(webrender) skip-if(!asyncPan) == fixed-pos-scrolled-clip-4.html fixed-pos-scrolled-clip-4-ref.html # bug 1373802 for webrender
-fuzzy-if(Android,6,4) fails-if(webrender) skip-if(!asyncPan) == position-sticky-scrolled-clip-1.html position-sticky-scrolled-clip-1-ref.html # bug 1366295 for webrender
+fuzzy-if(Android,6,4) skip-if(!asyncPan) == position-sticky-scrolled-clip-1.html position-sticky-scrolled-clip-1-ref.html
fuzzy-if(Android,6,4) skip == position-sticky-scrolled-clip-2.html position-sticky-scrolled-clip-2-ref.html # bug ?????? - incorrectly applying clip to sticky contents
# for the following tests, we want to disable the low-precision buffer
# as it will expand the displayport beyond what the test specifies in
# its reftest-displayport attributes, and interfere with where we expect
# checkerboarding to occur
default-preferences pref(layers.low-precision-buffer,false)
skip-if(!asyncPan) == checkerboard-1.html checkerboard-1-ref.html