Consider disabling overlay scrollbars if a `-webkit-scrollbar` style rule is specified with nonzero width and height
Categories
(Core :: Layout: Scrolling and Overflow, enhancement, P3)
Tracking
()
| Tracking | Status | |
|---|---|---|
| firefox151 | --- | fixed |
People
(Reporter: dholbert, Assigned: hiro)
References
(Blocks 5 open bugs, Regressed 1 open bug)
Details
(Keywords: dev-doc-needed, webcompat:platform-bug)
User Story
user-impact-score:166.5
Attachments
(9 files, 1 obsolete file)
|
567 bytes,
text/html
|
Details | |
|
568 bytes,
text/html
|
Details | |
|
661.60 KB,
video/mp4
|
Details | |
|
48 bytes,
text/x-phabricator-request
|
Details | Review | |
|
48 bytes,
text/x-phabricator-request
|
Details | Review | |
|
48 bytes,
text/x-phabricator-request
|
Details | Review | |
|
48 bytes,
text/x-phabricator-request
|
Details | Review | |
|
48 bytes,
text/x-phabricator-request
|
Details | Review | |
|
48 bytes,
text/x-phabricator-request
|
Details | Review |
We've got at least two cases where it seems like a Google site is trying to provide a thickness for scrollbars, which (in Safari and Edge with overlay scrollbars enabled) also successfully makes the scrollbars be "non-overlay-style", which happens to be a usability win in these cases:
bug 1836872
bug 1973196
I know we've already investigated and opted not to implement -webkit-scrollbar in general (bug 1432935). We might want to investigate doing a limited intervention for cases like this to at least take certain bundles of styles as a signal that we should switch to traditional scrollbars, though... Since that's how they end up working in Chromium and WebKit right now at least.
As an example, here's a testcase where the upper block uses scrollbars if they're enabled, and the lower two use ::-webkit-scrollbar to toggle non-overlay scrollbars with specified thicknesses in Chromium and WebKit:
https://jsfiddle.net/dholbert/L83ejpvd/
Updated•8 months ago
|
| Reporter | ||
Comment 1•8 months ago
|
||
(In reply to Daniel Holbert [:dholbert] from comment #0)
As an example, here's a testcase where the upper block uses scrollbars if they're enabled, and the lower two use
::-webkit-scrollbarto toggle non-overlay scrollbars with specified thicknesses in Chromium and WebKit:
https://jsfiddle.net/dholbert/L83ejpvd/
Here's that fiddle as a bugzilla attachment, for convenience/better-archival.
| Reporter | ||
Comment 2•8 months ago
|
||
Here's a testcase to demonstrate how this affects Gmail (bug 1973196).
A) In Firefox with overlay scrollbars, you can't scroll to reach SUCCESS. The inner area's overlay scrollbar isn't shown (it's covered up by the outer area's scrollbar, so you can't horizontally-scroll the inner area to see SUCCESS at all (unless you have some mechanism for touchpad/wheel-based horizontal scrolling).
B) In Firefox with legacy scrollbars, this is fine; you can scroll; you can see the two horizontal-scrollbars stacked and everything is fine.
C) In Chromium/WebKit, this is fine & you can scroll. This is true regardless of whether they're configured to use overlay scrollbars -- they react to the -webkit-scrollbar styles here by disabling overlay scrollbars and honoring the specified scrollbar width.
In this bug, I'm arguing that we should react to the prefixed scrollbar styles just like Chromium/WebKit do, and just activate legacy scrollbars (matching our (B) rendering).
We could do this on the level of the entire document, if that's easier to do (rather than turning overlay-scrollbars on/off for certain elements). Basically: whatever styles might deactivate overlay scrollbars in Chromium/WebKit should probably do the same for us (whether or not they also add additional fanciness in other browser-engines that we can ignore).
| Reporter | ||
Updated•8 months ago
|
| Reporter | ||
Comment 3•8 months ago
|
||
Here's a screencast showing the behaviors A/B/C from the previous comment.
Behavior (A) at the beginning is the problematic one -- that's where we're leaving content unreachable, basically.
Updated•6 months ago
|
Comment 4•6 months ago
|
||
testcase 2 shows different behaviors between OSes.
On macOS (with default settings), we're always showing both toolbars with both being reachable and not placed on top of each other (on the z-axis). There seems to be no "always show scrollbars" setting available in the settings.
On Windows 11, I'm seeing the behavior as shown in the screencast in comment #3 with the setting "always show scrollbars" not available.
On Linux (Ubuntu 24.04, X11), I'm seeing the behavior as shown in the screencast in comment #3 with the setting "always show scrollbars" available.
Comment 5•2 months ago
|
||
(In reply to Alex Jakobi [:ajakobi] from comment #4)
On macOS (with default settings), we're always showing both toolbars with both being reachable and not placed on top of each other (on the z-axis). There seems to be no "always show scrollbars" setting available in the settings.
On macOS this setting is in the OS settings: https://www.macrumors.com/how-to/make-scroll-bars-always-visible/
On Windows 11, I'm seeing the behavior as shown in the screencast in comment #3 with the setting "always show scrollbars" not available.
Same on Windows (https://www.windowscentral.com/how-always-show-scrollbars-windows-11)
| Assignee | ||
Comment 6•2 months ago
|
||
There are 6 test cases;
- webkit-scrollbar.html has
::-webkit-scrollbar { width: 8px; height: 8px; } - webkit-scrollbar-zero-width.html has
::-webkit-scrollbar { width: 0px; } - webkit-scrollbar-no-width-height.html has
::-webkit-scrollbar { background-color: #aaa; } - disable-horizontal-scrollbar-dynamically.html has no
::webkit-scrollbar styles, checks non-overlay scrollbars
properly disappear once after the contents in the scroll container
does no longer overflow - webkit-scrollbar-focus.html has
:focus::-webkit-scrollbar { width: 8px; height: 8px; } - webkit-scrollbar-display-none.html has
::webkit-scrollbar { width: 8px; display: none; }
As of now 6) doesn't work as expected.
| Assignee | ||
Comment 7•2 months ago
|
||
This change does;
- not make the current <scrollbar> element be a ::-webkit-scrollbar
pseudo element - rather, while traversing <scrollbar> element for its styling,
additionally collect ::-webkit-scrollbar rules and match them
as if the <scrollbar> is a ::-webkit-scrollbar pseudo - then if we found
width > 0orheight > 0styles on any
non-root <scrollbar> element, disable overlay scrollbar globally per
document, and kicks a media feature change evaluation
There are some restrictions;
- The machinery for property restrictions doesn't work for
::-webkit-scrollbar since it's not implemented as a genuine psuedo
element - ::-webkit-scrollbar is not applicable to the root scroll container
I haven't figured out how to obtain the originating element, i.e. the
<html> or <body>, while traversing the root <scrollbar> - Disabling overlay scrollbars does just work on the entire document,
it doesn't disable overlay on an individual scrollbar. We may be able
to do it by adding inline styles such asopacity: 1 ! importantto
each <scrollbar> to negate overlay styles defined in a UA style sheet
[1] with-moz-overlay-scrollbarmedia query.
| Assignee | ||
Comment 8•2 months ago
|
||
I think I am being stuck at finding ways to apply property restrictions to ::-webkit-scrollbar styles.
The current approach (written in D280698) is to not make <scrollbar> be a ::-webkit-scrollbar, it just additionally collects ::-webkit-scrollbar styles while traversing <scrollbar>. The current D280698 works in most cases, but if there's any CSS property other than width and height for ::-webkit-scrollbar, the property is also applicable to <scrollbar>, thus it will break <scrollbar>, one observable result I have seen is an assertion for cached scrollbar styles hits.
Emilio, do you have opinions on this issue? I'd personally like the original approach that is to make <scrollbar> be a ::-webkit-scrollbar pseudo element since the machinery for property restrictions works as-it-is. And the current approach also has a couple of hacks in selector matching.
Comment 9•2 months ago
|
||
Yeah, adding a SetPseudoElement(webkitScrollbar) to the scrollbar node seems more straight-forward, but that said, that's gonna break the scrollbar caching as well. also, we should disable overlay scrollbars only on that particular scroll container.
Updated•1 month ago
|
| Assignee | ||
Comment 10•1 month ago
|
||
Most of the remaining call sites of nsPresContext::UseOverlayScrollbars
for scrollbar parts' frames will be replaced by a new function in the
next change.
Updated•1 month ago
|
| Assignee | ||
Comment 11•1 month ago
|
||
There remain some direct call sites of
nsPresContext::UseOverlayScrollbars.
-
two call sites in nsCSSFrameConstructor::GetAnonymousContent [0]
These call sites for used for scrollbar style caching stuff,
thoguh I am not 100% sure whether these two call sites needs to be
replaced, in the next patch only width/height are applicable to
::-webkit-scrollbar pseudo elements and those properties are flagged
as has_effect_on_gecko_scrollbars=false, so it's okay-ish?
anyway the first one can not be replaced since it gets called before
styling the <scrollbar> itself -
in Element::GetClientAreaRect [1]
it's for an optimization to avoid layout flush on the root scroll
container, we will not apply ::-webkit-scrollbar to the root one, so it
doesn't matter -
in Gecko_GetScrollbarInlineSize [2]
it's used only for internal CSS env scrollbar-inline-size, so it
doesn't matter unless we use ::-webkit-scrollbar internally -
in nsTreeBodyFrame::Init [3]
it's XUL we won't use ::-webkit-scrollbar in chrome contents -
in Gecko_MediaFeatures_UseOverlayScrollbars [4]
it's used for-moz-overlay-scrollbarsmedia query, we leave the
styles in the media query, to disable overlay we will setactiveto
the scrollbar element.
[0] https://searchfox.org/firefox-main/rev/33fd6bd39c625067a29f153adce6a4646e45750f/layout/base/nsCSSFrameConstructor.cpp#3964,4035
[1] https://searchfox.org/firefox-main/rev/e480af4adea0d838f97b105d7efdd32fa18b73c1/dom/base/Element.cpp#1057
[2] https://searchfox.org/firefox-main/rev/e480af4adea0d838f97b105d7efdd32fa18b73c1/layout/style/GeckoBindings.cpp#317
[3] https://searchfox.org/firefox-main/rev/e480af4adea0d838f97b105d7efdd32fa18b73c1/layout/xul/tree/nsTreeBodyFrame.cpp#293
[4] https://searchfox.org/firefox-main/rev/e480af4adea0d838f97b105d7efdd32fa18b73c1/layout/style/nsMediaFeatures.cpp#112
| Assignee | ||
Comment 12•1 month ago
|
||
While traversing ::-webkit-scrollbar pseudo element for its styling,
additionally collect rules for <scrollbar> defined in UA style sheet
and match them.
There are two caveats;
- we tell a lie in
@supports selectoras if we don't handle
::-webkit-scrollbar at all - only width and height properties are applicable to ::webkit-scrollbar
pseudo elements, but these values are never used for <scrollbar>
elements becasue of the nature of our scrollbar implementation, i.e.
it rather respects scrollbar-width.
There's a restriction;
- ::-webkit-scrollbar is not applicable to the root scroll container
I haven't figured out how to obtain the originating element, i.e. the
<html> or <body>, while traversing the root <scrollbar>
| Assignee | ||
Comment 13•1 month ago
|
||
To disable scrollbars on a given scrollbar we set active attribute to
the vertical and horizontal scrollbar elements as if the user is
hovering over the scrollbars so that we don't specifically need to
cancel out styles in @media (-moz-overlay-scrollbars) query in
scrollbars.css even though the media query is document wide.
Updated•1 month ago
|
| Assignee | ||
Comment 14•1 month ago
•
|
||
Thank you, Emilio. I've somehow managed to make it happen. I am still not confident scrollbar style caching, I've written what I am thinks is in the commit message of D281571. I hope it does make sense to you!
Comment 15•1 month ago
|
||
Hmm, looking a bit closer at this and all the special cases that were needed, I wonder if it'd be easier to just add a RefPtr<ComputedStyle> ScrollContainerFrame::mWebKitScrollbarStyle; and update it in DidSetComputedStyle, via ProbePseudoElementStyle... Seems that'd work and wouldn't be a penalty for pages that don't use ::-webkit-scrollbar (we don't selector-match / compute a style at all in ProbePseudoElementStyle...)
It might be faster than breaking the scrollbar style caching perhaps? Hiro, that seems like a maybe easier approach? I think we didn't consider this approach when discussing it (only either "adding more NAC" or "turning scrollbar element into ::-webkit-scrollbar").
Assuming it'd be fast enough to probe on a page with no ::-webkit-scrollbar rules, and that we only probe one style (::-webkit-scrollbar really has some extra pseudo-classes too like ::-webkit-scrollbar:horizontal etc, not sure if they work now), it might be faster and less invasive.
Updated•1 month ago
|
| Assignee | ||
Comment 16•1 month ago
|
||
Thank you, Emilio! With the new approach the code change became much simpler!
Updated•1 month ago
|
Updated•1 month ago
|
| Assignee | ||
Comment 17•29 days ago
|
||
With supporting ::-webkit-scrollbar in @supports some wpts fail (here is a try run)
For example, scrollbar-width-010.html is one of the failure tests.
From the test title;
CSS Scrollbars: scrollbar-width auto on the root defers to ::-webkit-scrollbar on the root
From the relevant spec;
3.1. Interaction with non-standard features
On any element or pseudo-element where the computed value of scrollbar-width is anything other than the initial auto value, user agents must ignore any alternative non-standard means for authors to influence the rendering of scrollbars, such as the ::webkit-scrollbar family of pseudo-elements.
So I believe the test is invalid, the spec doesn't mention that ::-webkit-scrollbar needs to be respected in the case where scrollbar-width is auto.
Daniel, what do you think? What's a reasonable step to progress this bug? e.g., marking these wpts as FAIL in our tree and opening a PR to remove them from web-platform-tests?
Or, if we do a lie for @supports, these tests pass. Emilo, what do you think?
| Reporter | ||
Comment 18•29 days ago
•
|
||
(In reply to Hiroyuki Ikezoe (:hiro) from comment #17)
For example, scrollbar-width-010.html is one of the failure tests.
[ Explaining the test failure, largely for the benefit of future-me; I expect you already know the following: ]
For this specific test, and I imagine many of the others that you referenced -- the test is turning off scrollbars with a ::-webkit-scrollbar { display: none } rule, and it checks that they were in fact turned off (by comparing the root element's width against the viewport width). That should trivially fail in Firefox because we'll ignore the ::-webkit-scrollbar style, but the reason we currently pass the test is that it has this fallback style for browsers-that-lack-support-for-::-webkit-scrollbar which independently turns off the scrollbars:
@supports not selector(::-webkit-scrollbar) {
:root {
overflow: hidden;
}
This bug's current patch makes us skip that @supports clause (because we now claim to support ::-webkit-scrollbar), so we end up at the mercy of the ::-webkit-scrollbar{ display:none } rule to do the scrollbar-hiding; and IIUC the current patch doesn't actually implement any such display:none scrollbar-hiding behavior (right?), which is why we fail.
From the relevant spec;
[...]
So I believe the test is invalid, the spec doesn't mention that::-webkit-scrollbarneeds to be respected in the case wherescrollbar-widthisauto.
I think the test's expectations are fine, though probably the test should be renamed to have .tentative since it's testing something nonstandard -- it's expecting that ::-webkit-scrollbar DOES have a particular effect; and the relevant standards only make declarations about situations when ::-webkit-scrollbar should NOT have an effect. (i.e. the spec for the modern feature just says that the modern feature "wins" if the page specifies both, but it's not trying to define how ::-webkit-scrollbar behaves on its own)
So. Having said all that - I'm not super worried about the WPT failure in-and-of-itself (I don't think it's part of an interop score or anything, and I'm not entirely sure whether it's reflective of real-world content) -- but for the purposes of this bug here, I think this WPT failure raises two questions:
(A) As part of this work, maybe we should treat ::-webkit-scrollbar { display: none } as effectively the same as scrollbar-width: none? (when scrollbar-width is unspecified) I assume that's essentially what Chrome and Safari are doing, and that's what this one test at least is expecting. And I believe we've seen that styling in real-world sites before, so it'd probably be nice to support that.
(B) Should we worry about sites specifying scrollbar-styles using @supports conditions for ::-webkit-scrollbar styles, with graceful fallback that we're currently benefiting from, where we'll start losing out if we start pretending to support ::-webkit-scrollbar?
My thoughts on these questions:
- I tend to think we should do (A), though it could easily happen as a separate/followup work item. We just might want to consider it as something that blocks letting
layout.css.fake-webkit-scrollbar.enabledride the trains. - I'm not super worried about (B); I suspect the types of sites that bother to use @supports conditions to check for ::-webkit-scrollbar support will also be the types of sites that would default to using the standard properties. (which means partially-implemented
::-webkit-scrollbarwould still be fine). And in many cases maybe it won't end up mattering too much.
| Reporter | ||
Comment 19•29 days ago
|
||
(Also: before landing, it might be worth sending an intent-to-prototype email about this feature, since it'll be a new web-exposed API surface, even if really we're just having a minimal reaction to a legacy API -- e.g. disabling overlay scrollbars as proposed in this bug, and possibly turning off scrollbars if you agree that that's worth pursuing per (A), and responding to @supports since we do now parse the syntax even if we don't have full support for most aspects of the legacy feature.)
| Assignee | ||
Comment 20•29 days ago
|
||
Clearing NI to Emilio. I've started working on the (A) plan. Thank you Daniel!
| Reporter | ||
Comment 21•29 days ago
•
|
||
Thanks! I also found some confirmation that ::-webkit-scrollbar { display: none } is indeed a useful thing to support -- just a few weeks ago, we ran into bug 2016775 where Slack used that exact formulation to hide the "real" scrollbars (so that Slack can draw its own synthetic scrollbar). And because we don't support that syntax, Firefox showed two superimposed scrollbars: the real one and slack's synthetic one.
| Assignee | ||
Comment 22•28 days ago
|
||
Comment 23•8 days ago
|
||
Comment 24•7 days ago
|
||
| bugherder | ||
https://hg.mozilla.org/mozilla-central/rev/2ead425234a5
https://hg.mozilla.org/mozilla-central/rev/0c2657e75214
https://hg.mozilla.org/mozilla-central/rev/542767479d68
https://hg.mozilla.org/mozilla-central/rev/535df2422590
https://hg.mozilla.org/mozilla-central/rev/365ed3302c3c
https://hg.mozilla.org/mozilla-central/rev/3981bcd36b50
Comment 25•7 days ago
|
||
Comment 26•7 days ago
|
||
Backed out for causing a top crash in bug 2026487.
Backout link: https://hg.mozilla.org/mozilla-central/rev/305f77cef750
Updated•7 days ago
|
Comment 27•7 days ago
|
||
FWIW I think it's probably wise to enable on nightly for a while before letting it ride the trains since it's a significant change and also a partial implementation of ::-webkit-scrollbar. So I'm not super confident we won't find other issues (authors relying on the width being exactly the one they set, etc).
| Assignee | ||
Comment 28•7 days ago
|
||
(In reply to Emilio Cobos Álvarez [:emilio] from comment #27)
FWIW I think it's probably wise to enable on nightly for a while before letting it ride the trains since it's a significant change and also a partial implementation of
::-webkit-scrollbar. So I'm not super confident we won't find other issues (authors relying on the width being exactly the one they set, etc).
Yeah, that's a good idea. I changed the pref nightly only in D281572.
Comment 29•6 days ago
|
||
Comment 30•6 days ago
|
||
Comment 31•6 days ago
|
||
Backed out for causing build bustages on ScrollContainerFrame
| Assignee | ||
Comment 32•6 days ago
|
||
Geez.
ERROR - /builds/worker/checkouts/gecko/layout/generic/ScrollContainerFrame.cpp:5900:7: error: variable of type '(lambda at /builds/worker/checkouts/gecko/layout/generic/ScrollContainerFrame.cpp:5900:7)' only valid on the heap
What I originally wrote was violating it. Actually I didn't notice that WeakFrame is only used in heap. But with;
nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
"ScrollContainerFrame::DisableOverlayScrollbars",
[weakFrame = WeakFrame{this}] {
It's not allocated in heap along with the runnable instance? It's just the copied over one? WeakFrame{this} is still allocated on the stack of the call site?
| Assignee | ||
Comment 33•6 days ago
|
||
I've updated https://phabricator.services.mozilla.com/D289998, with wrapping the WeakFrame with unique_ptr and pushed a try run to see whether the compiler complains it or not.
| Assignee | ||
Comment 34•6 days ago
|
||
Geez, the try push didn't happen. Lando doesn't allows us pushing changes including fixes for security bugs. It does make sense in normal cases, but in this specific case it doesn't make any sense since the security bug (bug 2026430) in question was caused by the patches here in this bug.
| Assignee | ||
Comment 35•6 days ago
|
||
With ac_add_options --enable-clang-plugin option (RyanVM told me it on Matrix) I do see the compile error locally and with the revised change (with unique_ptr wrap), I don't see the error. I will go with it.
Comment 36•6 days ago
|
||
Comment 37•5 days ago
|
||
| bugherder | ||
https://hg.mozilla.org/mozilla-central/rev/1085ddb446f8
https://hg.mozilla.org/mozilla-central/rev/58196de66b55
https://hg.mozilla.org/mozilla-central/rev/9defa83e4762
https://hg.mozilla.org/mozilla-central/rev/d0f87d0f2b6a
https://hg.mozilla.org/mozilla-central/rev/e40a4b5768d0
https://hg.mozilla.org/mozilla-central/rev/decda03a4596
Description
•