Open Bug 1791232 Opened 2 years ago Updated 2 months ago

Element with position:sticky inside a container with clip-path or overflow:clip is not painting correctly on scroll

Categories

(Core :: Web Painting, defect)

Firefox 104
defect

Tracking

()

Tracking Status
firefox-esr102 --- wontfix
firefox110 --- wontfix
firefox111 --- wontfix
firefox112 --- wontfix
firefox113 --- fix-optional

People

(Reporter: maggotfish, Unassigned)

References

(Depends on 1 open bug)

Details

Attachments

(4 files, 2 obsolete files)

User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:104.0) Gecko/20100101 Firefox/104.0

Steps to reproduce:

See example: https://codepen.io/tombigel/pen/xxEPxvg?editors=0100

I have an element with position: sticky inside a parent with clip-path: inset(0), or same with overflow: clip.

Actual results:

when scrolling it's not painting it correctly as I scroll.

Expected results:

Should always paint the clipped area of the sticky positioned element.

Flags: needinfo?(emilio)

Seems to date back to our initial implementation of sticky.

Attached file More reduced test-case. (obsolete) —
Status: UNCONFIRMED → NEW
Ever confirmed: true

In the reduced test-case, something like this helps make the bug not reproduce:

diff --git a/layout/painting/nsDisplayList.cpp b/layout/painting/nsDisplayList.cpp
index 6949daddfe8c8..e47cb5a929103 100644
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -1585,10 +1585,7 @@ static bool IsStickyFrameActive(nsDisplayListBuilder* aBuilder,
   MOZ_ASSERT(aFrame->StyleDisplay()->mPosition ==
              StylePositionProperty::Sticky);
 
-  StickyScrollContainer* stickyScrollContainer =
-      StickyScrollContainer::GetStickyScrollContainerForFrame(aFrame);
-  return stickyScrollContainer &&
-         stickyScrollContainer->ScrollFrame()->IsMaybeAsynchronouslyScrolled();
+  return true;
 }
 
 bool nsDisplayListBuilder::IsAnimatedGeometryRoot(nsIFrame* aFrame,

I'm unsure what that means in practice tho, not familiar with AGRs... Even with that the bug still repros when the window is unfocused. Tim / Botond (when you're back, no rush), do you know what is the right fix here?

Flags: needinfo?(tnikkel)
Flags: needinfo?(emilio)
Flags: needinfo?(botond)

In bug 1382534, the problem was that a mask display item had an incorrect ASR (active scrolled root), and the fix was to give it a different ASR (the rest of the work in that bug was about computing a correct clip to go with the ASR).

However, the codepath modified in comment 5 seems not to be related to ASR assignments, but rather only used by retained-DL? Which makes me wonder what the behaviour is with retained-DL disabled.

It reproduces with retained DL disabled. The code in comment 5 doesn't seem only related to retained DL?

The only call chain of IsStickyFrameActive that I could find using Searchfox is:

RetainedDisplayListBuilder::ProcessFrame
  nsDisplayListBuilder::FindAnimatedGeometryRootFrameFor
    nsDisplayListBuilder::IsAnimatedGeometryRoot

pin @emilio any update on this?

What I'm seeing in local testing is:

  • The change in comment 5 does not fix the bug (or change anything about when in reproduces, as far as I could tell) for me.
  • The bug does reproduce with retained DL disabled for me.

One note: the bug reproduces reliably if you click on the page (presumably causing a repaint) after scrolling down and before scrolling back up.

Attached file Further reduced test case (obsolete) —

Here is a testcase that's further reduced still.

One note: the bug reproduces reliably if you click on the page (presumably causing a repaint) after scrolling down and before scrolling back up.

Slight clarification here: the bug reproduces reliably if you click below the green area after scrolling down and before scrolling back up.

Attachment #9295132 - Attachment is obsolete: true
Attachment #9311163 - Attachment mime type: text/plain → text/html
Flags: needinfo?(botond)

Here is the Gecko display list when the page is scrolled to the top after initially loading the page (rendering is correct).

Here is the display list with the page scrolled to the top after first scrolling down and clicking below the green area, i.e. the last time the page was painted was when it was scrolled down. Here, rendering is incorrect.

I'm not spotting anything particularly surprising about the display lists, the only things that differ are vertical offsets reflecting the fact that in the second case we were scrolled down at the time of the paint. There are no differences related to ASRs or clips (beyond their offsets).

One thing I've noticed is that async scrolling after the repaint does scroll the green content, but how much it scrolls it is limited, with the limit related to the value of the margin-bottom: as the margin-bottom approaches 0, it's less limited (such that at 0 the problem goes away completely), whereas as the margin-bottom approaches -90vh (90vh being the height of the parent comp element), it's more limited (such that at 90vh you can cause the green area to not be rendered at all even when scrolled to the top, if you've scrolled it completely out of view at the time of the repaint).

This is making me think there is a clip that should be scrolling in the compositor but isn't.

The previous comment was based on a slight misunderstanding of what the page is doing.

I'm uploading a revised testcase which uses a gradient rather than a solid color as the sticky element's background, which makes it more obvious whether the sticky element is itself being scrolled vs. staying in place and just having its clip be scrolled.

If you scroll this page, you can see there are two distinct phases: initially, only the clip scrolls (the background stays in place), and then at one point (once the margin-box of the sticky element, which is only 40vh high due to the margin-bottom: -50vh, reaches the bottom edge of the #comp element which is the sticky element's containing block), the sticky element itself starts to scroll out of view.

Similarly, when scrolling back up, first the sticky element scrolls partially into view, and then the clip starts scrolling to reveal more of it.

The rendering bug occurs if a repaint was last triggered at the time the clip was partially clipping away the sticky element. After such a repaint, async scrolling successfully moves the clip but any additional area revealed in this way is blank (until the next repaint).

So, given that, I think a revised diagnosis is more along the lines of us applying a clip to a display item prematurely during painting, not recognizing that the clip can scroll asynchronously and we should paint the entire item.

Attachment #9311163 - Attachment is obsolete: true

Discussed this with Markus today.

The problem here is that the StickyPosition display item's ASR is the root scroll frame's ASR (this is how we currently model position:sticky in the ASR model), and the clip that applies to this item from the overflow:clip is also the root scroll frame's ASR (since that clip scrolls with the root scroll frame).

As a result, we think that the clip and item scroll together, and so if the clip obscures part of the item at display list building time, it will continue to obscure that part of the item even after async scrolling.

However, this is not in fact the case -- while the clip scrolls with the root scroll frame, the sticky element only does so when it's not in its "stuck" position, and therefore it doesn't really scroll together with the clip.

Unfortunately this does not seem like an easy fix -- it seems like fixing this would require the refactor described in bug 1730749.

Depends on: 1730749

I'll also mention that I discovered two reftests in our test suite that exercise a very similar scenario:

position-sticky-scrolled-clip-1.html
position-sticky-scrolled-clip-2.html

They use the clip property rather than overflow:clip, but the effect at the display list level is the same: we have a sticky display item with a scrolled clip that sometimes moves relative to the item.

The first test is only testing that the clip can in fact be scrolled in the compositor, and it passes. The second test is basically testing the same scenario discussed in this bug, and it's marked as a known failure, and has been ever since it was added in bug 1298218, the bug that introduced the ASR model.

Canceling needinfo on Timothy as the original question has been substantially answered.

(But perhaps you might find the diagnosis here, and the additional motivation for the refactor described in bug 1730749, an interesting consideration to keep in mind as part of the ongoing/planned display list refactoring work?)

Flags: needinfo?(tnikkel)

Thanks so much for this Botond! We are watching this with great anticipation (:

(In reply to maggotfish from comment #20)

We are watching this with great anticipation (:

As mentioned at the end of comment 17, this is unfortunately not likely to be an easy fix as it depends on a refactor (bug 1730749) that will require a larger amount of work.

To help inform the prioritization of this work, I have a few questions for you:

  • Does this issue affect a website currently in production? If so, can you identify it, and/or give a sense of its size (order-of-magnitude estimate of # of users or # of visits)?
  • Are you aware of other affected websites?
  • Are you aware of any workarounds you can employ?

Thanks!

Flags: needinfo?(maggotfish)

As mentioned at the end of comment 17, this is unfortunately not likely to be an easy fix as it depends on a refactor (bug 1730749) that will require a larger amount of work.

Yes, it's understood (: but still wanted to add an optimistic tone.

Does this issue affect a website currently in production? If so, can you identify it, and/or give a sense of its size (order-of-magnitude estimate of # of users or # of visits)?

No that we know of. This is mostly blocking us from replacing our current scroll effects technique with a better one that should allow us adding more features and possibly improve the effects' performance.

Are you aware of other affected websites?

No.

Are you aware of any workarounds you can employ?

Alternatively, if Gecko ships the new scroll-animations per spec we may be able to avoid resorting to this technique.

Thanks!

Flags: needinfo?(maggotfish)

(In reply to maggotfish from comment #22)

Alternatively, if Gecko ships the new scroll-animations per spec we may be able to avoid resorting to this technique.

Support for scroll-driven animations is definitely planned, with significant implementation work done already. The tracking issue for that is bug 1676779.

Severity: -- → S3
Duplicate of this bug: 1820221

The following field has been copied from a duplicate bug:

Field Value Source
Regressed by bug 1382534 bug 1820221

For more information, please visit auto_nag documentation.

Keywords: regression
Regressed by: 1382534

Set release status flags based on info from the regressing bug 1382534

Set release status flags based on info from the regressing bug 1382534

Hey guys, it's been over 10 months now, is there any progress?
We really need this fixed as we plan on rolling out a feature that depends on this scenario.
See example here: https://codepen.io/ydaniv/pen/vYbpxrr
For now our implementation uses fixedpos which is problematic since it breaks on different scenarios where a containing block is forced on an ancestor of that element.

Currently this is working smoothly on Blink and WebKit.
Gecko's paint is still lagging.

Thanks!

Flags: needinfo?(botond)

I have successfully advocated for adding the refactoring that this depends on, bug 1730749, to our team's roadmap. However, we have not gotten to it yet, and there are a few more items ahead of it. I'm hopeful that we'll get to it in 2024 H1.

Flags: needinfo?(botond)

(Also, just correcting the bug flags, as this is not a regression; this has never worked. The relationship to bug 1382534 is described in bug 1820221 comment 5.)

Keywords: regression
No longer regressed by: 1382534

Also cross-commenting here, since this was my original issue:
Just want to point out that we managed to fix this issue in one case by slapping a will-change: transform on a wrapper element that's under the clipping element and parent of the sticky element: https://jsbin.com/wonopujena/edit?css,output

You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: