Open Bammuri opened 4 years ago
@emilio @smfr since I came across this recently again while fixing a bug in a related area.
I've written a test case to help show the different behaviors: https://mparch-bokan.glitch.me/bug/scrollIntoView-fixed.html
Each orange box is position: fixed with a child target that gets scrollIntoView({block:'start', inline:'start'})
called on it:
Box A: Is unscrollable Box B: Is scrollable Box C: Is unscrollable and inside an iframe Box D: Is scrollable and inside an iframe
Box A: No scroll - all engines agree Box B: Container scrolls, no window scroll - all engines agree
Box C:
Box D:
IMO Chrome's behavior is clearly a bug in the C/D cases. I also think the Firefox behavior in C seems like a bug; the fact that the container is scrollable shouldn't make the different of whether to bubble the scrollIntoView up to the outer window. I'd propose adopting Safari's behavior in the spec, WDYT?
If you tick the checkbox it'll shift the position: fixed elements so that the target, after being scrolled into view in the container, is outside of the frame.
Box A: No scroll - all engines agree Box B: Container scrolls - all engines agree
Box C:
Box D:
When I fix the bug for the bubbling behavior Chrome has the same behavior as Safari but it seems unintuitive to me to scroll something into view that isn't visible.
I wonder though if changing that is web-compatible. In Chrome we intersect the target's rect to the scroll port rect each time we bubble up but we have an edge-case that sets an empty rect dimension to 1px that IIRC we had to add for web compat.
I'll try to put together a more exhaustive test set for how scrollIntoView behaves w.r.t. overflow clips but any thoughts you may have here would be helpful.
Yeah, I think the Firefox behavior is because this code doesn't look across document boundaries, and that looks like a bug.
My only concern with scrolling the outer window is whether <iframe>
s (in particular cross-origin ones) should really do that. Is there any information they can infer information that they couldn't / shouldn't (e.g., via IntersectionObserver
)?
But we right now are scrolling stuff into view on them when they have a scrollable ancestor, if I'm reading the code correctly, so it seems it wouldn't really make stuff worse.
So assuming the cross-origin info leaks are not a worry, I think safari's behavior makes sense in the unclipped case.
For the clipping case: I don't think we do any intersection shenanigans in Gecko. It's weird to special-case clipping but not other ways of occlusion / hiding content, I think?
If we don't intersect, then we get the Safari behavior, right? I think that may be the simplest one to spec and explain to developers (and I don't think I've seen any compat issue with this).
Yeah, can confirm that with a simple fix like this:
diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp
index bb5313563e624..a2e0f47232050 100644
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -3669,8 +3669,7 @@ void PresShell::DoScrollContentIntoView() {
// Make sure we skip 'frame' ... if it's scrollable, we should use its
// scrollable ancestor as the container.
- nsIFrame* container = nsLayoutUtils::GetClosestFrameOfType(
- frame->GetParent(), LayoutFrameType::Scroll);
+ nsIFrame* container = frame->GetParent();
if (!container) {
// nothing can be scrolled
return;
I get the expected behavior for box C.
The real patch happened to be a little bit more subtle, but I attached it to https://bugzilla.mozilla.org/show_bug.cgi?id=1763743. If we agree on that behavior I'm happy to land that.
My only concern with scrolling the outer window is whether
Yeah, sorry, I should have mentioned: this is only for same-origin cases. We block bubbling scrolls across origins and I'm not suggesting we stop doing that.
For the clipping case: I don't think we do any intersection shenanigans in Gecko. It's weird to special-case clipping but not other ways of occlusion / hiding content, I think?
I think the only downside is you get some weird behaviors in some cases. E.g:
https://mparch-bokan.glitch.me/bug/sIV-clipping.html
In Gecko, if you first ScrollIntoView - end
(so that the target overflows far to the top/left), then scroll the main window back and click ScrollIntoView - nearest
, nothing happens because Gecko thinks the target element is already in view. Similarly, ScrollIntoView - center
also doesn't quite center the visible scroller.
WebKit seems to do the same thing as Gecko in the nearest
case but just scrolls to clearly the wrong place in the center
case which I don't understand.
IMHO the Blink behavior is more intuitive here.
If we don't intersect, then we get the Safari behavior, right? I think that may be the simplest one to spec and explain to developers (and I don't think I've seen any compat issue with this).
Yeah, (I hope) these are fairly obscure edge cases so I don't feel strongly; having the implementations agree is more important than the ideal behavior in these cases. I wouldn't be opposed to changing Blink's behavior to match Gecko/WebKit if it's web compatible (hopefully since the behavior is in Gecko/WebKit it should be) but it does seem like there's some differences even between Gecko/Webkit.
The real patch happened to be a little bit more subtle, but I attached it to https://bugzilla.mozilla.org/show_bug.cgi?id=1763743. If we agree on that behavior I'm happy to land that.
Nice, thanks. I have a similar patch for Blink with a WPT based on my test case in the first message which I should be able to land in the next day or two.
In Gecko, if you first
ScrollIntoView - end
(so that the target overflows far to the top/left), then scroll the main window back and clickScrollIntoView - nearest
, nothing happens because Gecko thinks the target element is already in view. Similarly,ScrollIntoView - center
also doesn't quite center the visible scroller.WebKit seems to do the same thing as Gecko in the
nearest
case but just scrolls to clearly the wrong place in thecenter
case which I don't understand.
Yeah that seems like a WebKit bug to me too, fwiw.
IMHO the Blink behavior is more intuitive here.
That is true, but it feels a bit weird to do some very limited amount of occlusion tracking but not others. I think specifying the simpler thing is better unless there's a need for the more complex thing.
Nice, thanks. I have a similar patch for Blink with a WPT based on my test case in the first message which I should be able to land in the next day or two.
Ah, sweet! if you have a WPT ready I don't need to write one :-)
Can you cc me on the relevant crbug / review request so I'm aware of when it lands and I don't need to land the Firefox patch without test coverage or forget about it? Thanks!
Can you cc me on the relevant crbug / review request so I'm aware of when it lands and I don't need to land the Firefox patch without test coverage or forget about it? Thanks!
Done: CL.
Can you cc me on the relevant crbug / review request so I'm aware of when it lands
It's landed and the test should be upstream soon: https://github.com/web-platform-tests/wpt/pull/33572.
Thanks, I sent a quick fix to the test: https://github.com/web-platform-tests/wpt/pull/33633
With that my patch passes the test on Firefox, so will send it for review, thanks so much!
Update: Chrome 102 shipped the change to bubble scrollIntoView from position: fixed elements to parent frames. However, I ran into some web-compat issues in 1334265 which, curiously, doesn't affect WebKit. I think I narrowed the reason down to: WebKit doesn't bubble a scrollIntoView across origins whereas Blink and Gecko do, see this reduced repro: https://reproduce.glitch.me/iframe-fixed-scrollintoview.html.
IIUC, WebKit sets ShouldAllowCrossOriginScrolling::No here (and here from focus() calls) so the scrollRectToVisible doesn't cross origins.
@smfr - Can you confirm?
Preventing a scroll from crossing origin boundaries seems like a good idea to me. Given that WebKit already ships this behavior it'd hopefully be web compatible. What do you all think of aligning Gecko/Blink to also avoid scrolls crossing origin boundaries in these cases?
I just noticed step 1 in the scroll an element into view steps is to terminate the iteration when crossing an origin boundary so it sounds like the WebKit behavior is intended. I'm going to give it a try in Blink and see if we can land it.
While contributing to Chromium, I discovered an interesting issue. Element.scrollIntoView() does not scroll to a position:fixed element in an iframe
There is something to be discussed before addressing the issue.
How should we treat elements that are offscreen and can't be scrolled on screen? e.g. https://jsbin.com/qehegek
Bokan@ said, if, after scrolling the immediate-most parent the element isn't visible in the parent, should we keep recursing up the scroll chain? I'm not sure what the right answer is (my hunch would be "no" but could easily be convinced)
I agree, and I think we should handle exceptions in these cases. If there are other cases, please tell me:)