Open Bug 1595791 Opened 2 years ago Updated 1 month ago

overscroll-behavior: contain not honored on first interaction in some cases

Categories

(Core :: Layout: Scrolling and Overflow, defect, P3)

70 Branch
defect

Tracking

()

UNCONFIRMED

People

(Reporter: danburzo, Unassigned)

References

(Blocks 2 open bugs)

Details

Attachments

(2 files)

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

Steps to reproduce:

In some cases, the first two-finger swipe gesture on a horizontally-scrolling element does not honor its overscroll-behavior-x: contain CSS rule, and causes a navigation in browser history.

A reduced test, with instructions, is attached, as well as a video documenting the behavior.

Actual results:

The first swipe to the left (as if to go Back in history) immediately following a vertical scroll ignores the overscroll-behavior-x: contain declaration and causes a history navigation.

Expected results:

overscroll-behavior should be honored.

Component: Untriaged → Layout
Product: Firefox → Core
Component: Layout → Layout: Scrolling and Overflow
Priority: -- → P3

Does this issue reproduce on Windows or Linux?

Firefox 70.0.1 on Windows 10 Pro does not seem to support a two-finger swipe gesture to navigate history on a Microsoft Surface device. The gesture is available, however, in Edge and Chrome.

Blocks: apz-mac

I see something similar in Firefox 89 on Windows 10. I have a page that can scroll and it contains a fixed element of which a child element can scroll, too. Both vertically. The scrollable child element is created dynamically. When I create that and move the mouse cursor over it, then turn the mouse wheel, the page is always scrolled down instead of the child element. Mostly only for the first time. This is very annoying because the overscroll-behavior seems completely useless initially. The mouse cursor is over a scrollable element that can scroll (is not at the end) but something else scolls instead. Very unexpected.

The same operation works reliably in Chrome.

Given the original description mentions some touch gesture (on-screen?) it seems like all input methods are affected. The scroll target selection just doesn't seem to consider all targets and pick the right one to scroll. I haven't tested touch input (on-screen, Windows and Android) but could do if that's helpful.

Yves, could you share your page (upload it an an html attachment to this bug) please?

Assuming the underlying issue is the same, having a page that shows the problem using scrolling (which can then be investigated on any platform, not just Mac) should make it easier to investigate and track down.

Thanks!

I'm trying to get a testcase. I can't reproduce it yet with a simple scenario. There's also nested flex boxes in my app. Let's see how much of it I need.

BTW, it doesn't only happen the first time. Leave the page open for a while (in the background) and when you come back, it scrolls the wrong element again (once).

Sorry, I'm unable to create a testcase. The bug only appears in my application. Unfortunately I can't share that, and it would be really complex (thousands of lines of code).

Sorry for another message, I can't edit my last one.

The bug is no longer repoducible in my app either. I haven't changed anything to it, just played with my testcase. Looks like it's a random failure. Those might never be found. The only solution would be to decrease Firefox' market share to 0%. :-(

Thanks for trying. If you do end up getting a reproducible test case at a later time, please feel free to circle back here and post it.

I'd like to add that my observation is probably not related to overscroll-behavior. I can see the entire page scroll down when the mouse cursor is directly over a scrollable child that could also be scrolled down. That CSS property does not apply and the behaviour should also occur without it. If I place the mouse cursor over a scrollable element, wait a moment and scroll in a valid direction, it must always scoll the child, not any of its parents. So this is actually a broader bug.

In fact I've just removed the overscroll-behavior properties from my CSS, verified that their effect is gone, and could still observe this. My steps were simple:

  1. Make sure the page is scrolled to the top
  2. Make sure the child element is scrolled to the top
  3. Scroll the child down using the mouse wheel
  4. Scroll the child up again
  5. Move the mouse cursor out of the child, wait ~2 seconds
  6. Move the mouse cursor back onto the child, wait ~10 seconds
  7. Scroll down using the wheel

Instead of the child, now the page scrolls.

The behaviour described in comment 10 is a known issue that affects "heavy" pages (that is, pages that run a lot of script on the main thread or otherwise tie up the main thread with e.g. complex rendering). The reason for it is a bit subtle, but I can try to explain:

  • For performance reasons, scrollable elements can be in one of two states: "active" and "inactive". The "active" state is more resource-intensive, so the browser only keeps a scrollable element in the active state when it's currently being scrolled, or has recently been scrolled.
  • When a scroll frame is in the inactive state, its representation in the compositor (which handles scrolling among other things) is fairly minimal. It's basically "there's something going on in this area, we need to ask the main thread for more information before we can scroll".
  • That request for information has a timeout, set at 400 ms. If the main thread does not respond within the timeout (which can sometimes happen on heavy pages), we scroll anyways based on the limited information we have (which can e.g. mean scrolling the page even if, with the additional info, we would have scrolled the child).
  • The "~10 seconds" wait you mention (it's actually 15) is how long we keep a scrollable element active after it stops scrolling, before returning it to the inactive state to conserve resources.

The 400 ms timeout is basically a correctness/responsiveness tradeoff. In many cases, the answer from the main thread would be "go ahead and scroll the thing you were going to scroll anyways", in which case not waiting longer than 400 ms for the answer is a responsiveness win. In other cases, the answer is "no, scroll this other thing instead" or "don't scroll at all", in which case we have a correctness bug.


For overscroll-behavior, however, we decided that not honouring it would be an unacceptable correctness bug, so in bug 1425573 we took steps to ensure the "fallback behaviour" would be to not scroll anything in that case.

So, while the behaviour described is expected without overscroll-behavior, it's not expected with overscroll-behavior -- so we're still interested in a testcase that demonstrates bad behaviour with overscroll-behavior.


One final note: as part of our Site Isolation project (also called "Fission"), we've had to rearchitect our handling of inactive scrollable elements in a way that ends up avoiding the correctness issue described above, regardless of overscroll-behavior. This is not released yet, but it can be tried on the Nightly channel by opting into the "Fission (Site Isolation)" nightly experiment in about:preferences.

(It's also possible to opt into the new scrolling architecture without opting into Fission by setting the pref apz.wr.activate_all_scroll_frames=true. However, please be aware that this is not a well-tested configuration.)

I'm not nearly waiting 400 ms and there's not much going on on my page, so that can't be the cause of the incorrect scrolling here. It's several nested flex-boxes but not much computational work. The existing scripts just manipulate the DOM directly from local data.

But my bug may be resolved by a changed application behaviour. My use case is this: I have a div that may or may not scroll. Inside it, there's a button to show additional list entries. When the user clicks it, the div content gets longer and then may or may not scroll. This is where the mouse wheel would often scroll the page instead of the newly scrollable div – except when I clicked inside the div once directly before turning the wheel. That was a good (but annoying) workaround.

Now when the list expands, I let the div automatically scroll down so that the newly added list entries become visible, with the "smooth" option of the scroll function. I'm not seeing the incorrect scrolling anymore now. It might still occur later from time to time (I have to monitor this), but not directly after interacting with the list view. Maybe this automatic scrolling has made the browser more aware of what it has there, which would fix things according to your explanation (which I still think doesn't fully apply because of the non-existent UI thread block of 400 ms).

Not as resolved as desired. That wrong scrolling bug became so bad that I had to implement my own scrolling handling. The wheel event is now entirely blocked on the fixed side panel and only those elements that may scroll handle the event for themselves. It's not as smooth, and Chrome animates the scrolling faster than Firefox, but it comes with the originally intended effect that no inadvertent scrolling of the full page is ever possible over the side panel, whether or not it has a scrollable element itself.

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