Open Bug 1173474 Opened 6 years ago Updated 2 years ago

eBay search results loses the scroll position when press the Back Button

Categories

(Core :: DOM: Core & HTML, defect, P3)

38 Branch
defect

Tracking

()

UNCONFIRMED
Tracking Status
platform-rel --- -

People

(Reporter: spider_70, Unassigned)

Details

(Whiteboard: [platform-rel-eBay])

User Story

STR:
 1. Visit http://www.ebay.com/
 2. Type "tblet" into ebay's search field (no quotes) and press enter.
 3. Scroll down on the search results page and click some result.
 4. Use browser's back button (or alt+left) to navigate back to the search results.

EXPECTED RESULTS:
Your scroll position in the search results should be preserved.

ACTUAL RESULTS:
Your scroll position is clobbered - the page is scrolled to the top (and their "Showing results for tablet. Search instead for tblet" node is focused).
User Agent: Mozilla/5.0 (Windows NT 6.1; rv:38.0) Gecko/20100101 Firefox/38.0.5
Build ID: 20150525141253

Steps to reproduce:

I'm search for an article (example: head-liner) and scroll through the Items, approximately in the middle of the Page i press the Back Button in the Browser.




Actual results:

the scroll Position return at the Top of the Page, but that happens only when the ebay search show did you mean....? (see Screenshot)
 http://www.pic-upload.de/view-27323422/Bild-4.jpg.html


Expected results:

the back Button should not loses the scroll position when i'm not follow the "did you mean" Link.
tested with a new Profil without Addons.
IE and Chrome works fine.
(In reply to Kevin D. from comment #0)
> User Agent: Mozilla/5.0 (Windows NT 6.1; rv:38.0) Gecko/20100101
> Firefox/38.0.5
> Build ID: 20150525141253
> 
> Steps to reproduce:
> 
> I'm search for an article (example: head-liner) and scroll through the
> Items, approximately in the middle of the Page i select a article and press the Back Button in
> the Browser.
> 
> 
> 
> 
> Actual results:
> 
> the scroll Position return at the Top of the Page, but that happens only
> when the ebay search show did you mean....? (see Screenshot)
>  http://www.pic-upload.de/view-27323422/Bild-4.jpg.html
> 
> 
> Expected results:
> 
> the back Button should not loses the scroll position when i'm not follow the
> "did you mean" Link.
> tested with a new Profil without Addons.
> IE and Chrome works fine.
looks like there is a focus() call on that link and it's what causes the scroll to go to that link.
if($('p.sm-md').length){$('p.sm-md').attr('tabIndex', -1).focus();}
I'm not sure what's the expected behavior though, moving to Core / DOM where someone might know.
Component: Untriaged → DOM
Product: Firefox → Core
Calling focus() on a node needs to scroll that node into the viewport.  It's a bit unfortunate that ebay is doing that.  :(
platform-rel: --- → ?
Whiteboard: [platform-rel-eBay]
I can still reproduce this bug, FWIW, though the "did you mean" text has now changed to:
> "Showing results for [otherthing] Search instead for [thing-you-searched-for]"

I tried searching for "tblet" (intentionally misspelled) and the "Showing results for" box gets focused & scrolled-into-view whenever I navigate back to the search results page.
If the site is doing this intentionally I'm not sure there's anything we can do here.
User Story: (updated)
(In reply to Boris Zbarsky [:bz] (still a bit busy) from comment #3)
> Calling focus() on a node needs to scroll that node into the viewport.

FWIW: Edge and Chrome seem to disagree with you on this.  They don't scroll the node into the viewport when I perform the STR.  (In those browsers, the node *does* get focused, but the user's scroll position is still restored nicely.)  Maybe they're non-conforming on this, though?  [Perhaps via some special page-navigation-handling case that we should align on?]
platform-rel: ? → +
Boris, can you comment on comment 6? If this is a web compat wart, should we consider changing our behaviour?
Flags: needinfo?(bzbarsky)
More data needed.  In general, I'm pretty sure that calling focus() on a node in Chrome _does_ scroll to that node.  Simple testcase:

  data:text/html,<input type="button" onclick="document.querySelector('input:not([type])').focus()" value="click me"><div style="height: 4000px"></div><input>

So the question is which behavior actually differs here.  Some options:

1) The site is serving different scripts to Edge/Chrome.
2) The focus() call is not actually being reached in Edge/Chrome.
3) The focus() call is happening on a different node in Edge/Chrome.
4) The focus() call is happening before history scroll position restoration in Edge/Chrome.
5) The focus() call is being ignored due to some special page-navigation-handling case,
   as dholbert suggests.

Having some idea of which it is would be nice.

To start us off on that investigation, here is another testcase:

  data:text/html,<div style="height: 4000px"></div><input><script>document.querySelector('input:not([type])').focus()</script>Scroll to the top of this page, navigate away from it, then navigate back

When I follow the directions on this testcase, I end up scrolled to the bottom, due to the focus() call, in Chrome dev...
Flags: needinfo?(bzbarsky)
Priority: -- → P2
platform-rel: + → -
(In reply to Boris Zbarsky [:bz] (still a bit busy) (if a patch has no decent message, automatic r-) from comment #8)
> To start us off on that investigation, here is another testcase:
> 
>   data:text/html,<div style="height:
> 4000px"></div><input><script>document.querySelector('input:not([type])').
> focus()</script>Scroll to the top of this page, navigate away from it, then
> navigate back
> 
> When I follow the directions on this testcase, I end up scrolled to the
> bottom, due to the focus() call, in Chrome dev...

If we change this testcase to use <p> instead of <input>: (just like the original webpage does)

data:text/html,<div style="height: 4000px"></div><p tabindex="-1">Test</p><script>document.querySelector('p').focus()</script>

Chrome does not scroll to the bottom anymore, it restores the scroll position. I'm not sure whether it's on purpose or a bug? Haven't test Edge yet.
Ah, I didn't realize the thing being focused on the original page wasn't a form control.

So I just tested, and the behavior for something like <a href> matches that for <p tabindex="-1"> or <p tabindex=5"> in Chrome, but does NOT match <input>.  <select> behaves like <a>.  <p contenteditable> behaves like <input>...
Looking at our implementation, in this case, .focus() is called way before scroll position is restored. But since .focus() changes the scroll position, ScrollPosition.collect()[1] seems to return the new scroll position when restoring the session.
What if we _not_ scrollIntoView() on focus() if document is still loading?


[1] http://searchfox.org/mozilla-central/rev/eace920a0372051a11d8d275bd9b8f14f3024ecd/toolkit/modules/sessionstore/ScrollPosition.jsm#40
> What if we _not_ scrollIntoView() on focus() if document is still loading?

That would break user expectations, in simple cases like "user clicks something, webpage transfers focus to this other thing that now needs to be in view", no?

I would really like to figure out exactly what other UAs do around this stuff before we start making changes.  And ideally we would get the spec updates to reflect reality...
(In reply to Boris Zbarsky [:bz] (still a bit busy) (if a patch has no decent message, automatic r-) from comment #12)
> > What if we _not_ scrollIntoView() on focus() if document is still loading?
> 
> That would break user expectations, in simple cases like "user clicks
> something, webpage transfers focus to this other thing that now needs to be
> in view", no?

Well, that's only when document is still loading...

> 
> I would really like to figure out exactly what other UAs do around this
> stuff before we start making changes.  And ideally we would get the spec
> updates to reflect reality...

Sure. I ran some tests on Edge, Safari and Chrome.

Testing with the STR in the "User Story", Edge, Safari and Chrome all restore the scroll position.

Testing with "data:text/html,<div style="height: 4000px"></div><input><script>document.querySelector('input:not([type])').focus()</script>":
Only Safari restores the scroll position, Chrome and Edge jumps to the focused element.

Testing with "data:text/html,<div style="height: 4000px"></div><p tabindex="-1">Test</p><script>document.querySelector('p').focus()</script>", 
Safari and Chrome restores the scroll position, Edge jumps to the focused element.

In summary, Safari always restores the scroll position, Edge jumps to the focused element with simple test cases, but restores the scroll position with the STR in User Story (maybe it's timing related), Chrome seems to jump to the focused element only if the element is editable.
> Well, that's only when document is still loading...

Yes, sure.  But that basically means that depending on whether some slow ad is loading or not clicking on the page may or may not behave correctly.  That's a horrible user experience.

As far as testing, here are the sorts of questions I want answered:

1) What happens if the focus() call happens from DOMContentLoaded?
2) What happens if the focus() call happens from onload?
3) What happens if the focus() call happens from a 0-length timeout from onload?
4) What happens if the focus() call happens from a 1-second timeout from onload?
5) If you have a slow-loading page (e.g. a <script> pointing to <http://software.hixie.ch/utilities/cgi/test-tools/delayed-file?pause=10&mime=application%2Fjavascript&text=document.write%28%27slow+load+done%27%29> at the very bottom of the page) when does scroll position restoration happen in various browsers?  Before or after that script loads?  How do focus() calls behave while waiting for that script load?

(That's off the top of my head, after thinking about this for ~30 seconds; there are probably more things to be tested here.)

Of course for Safari and Chrome we could also try looking up their code for scroll state restoration; going from code to observed behavior may not be trivial, though.
Here is the result of my testing...

1) What happens if the focus() call happens from DOMContentLoaded?

Firefox:  scroll position starts at the saved scroll state (sometimes there is scroll event, sometimes not), then scrolls to the focused element
Chrome: scrolls to focused element directly
Safari: scrolls to the saved scroll state
Edge: scrolls to focused element directly

2) What happens if the focus() call happens from onload?

Firefox: scroll position starts at the saved scroll state (sometimes there is scroll event, sometimes not), then scrolls to the focused element
Chrome: scrolls to focused element directly
Safari: scrolls to the saved scroll state
Edge: scrolls to focused element directly

3) What happens if the focus() call happens from a 0-length timeout from onload?

Firefox: scrolls to saved scroll state then scrolls to the focused element on timeout
Chrome: scrolls to saved scroll state then scrolls to the focused element on timeout
Safari: scrolls to saved scroll state then scrolls to the focused element on timeout
Edge: scrolls to focused element on timeout

4) What happens if the focus() call happens from a 1-second timeout from onload?

Firefox: scrolls to saved scroll state then scrolls to the focused element on timeout
Chrome: scrolls to saved scroll state then scrolls to the focused element on timeout
Safari: scrolls to saved scroll state then scrolls to the focused element on timeout
Edge: scrolls to saved scroll state then scrolls to the focused element on timeout

5) If you have a slow-loading page (e.g. a <script> pointing to <http://software.hixie.ch/utilities/cgi/test-tools/delayed-file?pause=10&mime=application%2Fjavascript&text=document.write%28%27slow+load+done%27%29> at the very bottom of the page) when does scroll position restoration happen in various browsers?  Before or after that script loads?  

Firefox: scroll position restoration happens before and after the script is loaded (2 scroll events received)
Chrome: scroll position restoration happens before the script is loaded
Safari: scroll position restoration happens after the script is loaded
Edge: scroll position restoration happens after the script is loaded

How do focus() calls behave while waiting for that script load?

Firefox: scrolls to the focused element, then scrolls to the focused element again after the script is loaded (2 scroll events received)
Chrome: scrolls to focused element, then nothing happens after the script is loaded
Safari: scrolls to focused element, then nothing happens after the script is loaded
Edge: scrolls to focused element, then nothing happens after the script is loaded

6) If you have a page with large image (30MB+), when does scroll position restoration happen in various browsers?  Before or after that image loads?

Firefox: scroll position is restored before and after the image is loaded
Chrome: scroll position is restored before image is loaded
Safari: scroll position is restored after image is loaded
Edge: scroll position is restored before image is loaded

How do focus() calls behave while waiting for that image load?

Firefox: scrolls to the focused element, then scrolls to the focused element again after the image is loaded (2 scroll events received)
Chrome: scrolls to focused element, then nothing happens after the image is loaded
Safari:  scrolls to focused element, if image is loaded from cache, the saved scroll state is restored after image is loaded, otherwise, nothing happens after image is loaded
Edge: scrolls to focused element, then nothing happens after the image is loaded

---


Since some of the tests above differ from the result of testing with the original STR, I made a small change to the page in the original STR. Originally, the focused element is on the top of the page, so calling .focus() on the element doesn't "change" the scroll position, but if I move the element to be focused to the middle of the page, I noticed that Safari and Edge will result in having the scroll position on the focused element, not the saved scroll state (Chrome still restores the saved scroll state because it does not "scroll into view" on p element).
That's ... exciting.  I'm not sure how to reconcile the data from test #5 and test #2 for Chrome, unless it has some sort of "restore scroll position onload or if some time fires" thing or something... :(
(In reply to Boris Zbarsky [:bz] (still a bit busy) (if a patch has no decent message, automatic r-) from comment #16)
> That's ... exciting.  I'm not sure how to reconcile the data from test #5
> and test #2 for Chrome, unless it has some sort of "restore scroll position
> onload or if some time fires" thing or something... :(

Sorry, let me correct the results of #1 and #2 (I was checking on scroll events only). So, Chrome does start at the saved scroll position (no scroll event fired), then scrolls to the focused element.

1) What happens if the focus() call happens from DOMContentLoaded?

Firefox:  scroll position starts at the saved scroll state (sometimes there is scroll event, sometimes not), then scrolls to the focused element
Chrome: scroll position starts at the saved scroll state, then scrolls to the focused element
Safari: scrolls to the saved scroll state
Edge: scrolls to focused element 

2) What happens if the focus() call happens from onload?

Firefox: scroll position starts at the saved scroll state (sometimes there is scroll event, sometimes not), then scrolls to the focused element
Chrome: scroll position starts at the saved scroll state, then scrolls to the focused element
Safari: scrolls to the saved scroll state
Edge: scrolls to focused element
So, it seems that our behavior matches more with Chrome's in terms of scroll state restoration and on the behavior of focusing "editable elements".
The spec does mention a little bit about restoring persisted user state [1] in step 16.2, but I'm not really familiar with session history...

[1] https://html.spec.whatwg.org/multipage/browsers.html#history-traversal

It seems that all browsers agree on that if scroll position has changed (either by .focus() or manually), then the scroll position restoration is skipped. The difference is, if the focused element happens to be on top, that may mean "no change in scroll position", so scroll position restoration is still ran.
Based on comment 18, I don't think we need to change anything here. bz, may I have your opinion on this?
Flags: needinfo?(bzbarsky)
Ideally, we'd actually spec something here and get all browsers interoperable...  I mean, we still have a behavior difference between Firefox and other browsers on the original testcase, right?
Flags: needinfo?(bzbarsky)
(In reply to Boris Zbarsky [:bz] (still a bit busy) (if a patch has no decent message, automatic r-) from comment #20)
> Ideally, we'd actually spec something here and get all browsers
> interoperable...  I mean, we still have a behavior difference between
> Firefox and other browsers on the original testcase, right?

Sure. I have a few questions before filing a spec bug.

-  [1] specs when to restore persisted user state (which may include scroll position and form state) during history traversal, but I'm not sure where does page loading come into play here. Does page load begins at the end of all the steps in [1]? If so, we'd like to spec when to restore scroll position during page load, right? since during page load, content could incrementally be coming in, and user agent will keep trying to restore the scroll position.

- Will "reload" run the same steps as going forward and the going back? Since the experiments above were tested using "reload" and the original testcase can also be reproduced using "reload".


[1] https://html.spec.whatwg.org/multipage/browsers.html#history-traversal
> Does page load begins at the end of all the steps in [1]? 

No, it begins way before that, for normal page loads, assuming you mean the GET to get the HTML.  For history loads... it depends.  In Gecko terms, if the previous page is not in bfcache, then the initial history traversal will bail out in step 1 of the steps you cite, because "entry no longer holds a Document object" tests true.  Then navigation will proceed as usual and eventually call back into these steps once the Document for the post-navigation thing is created.  For the bfcache case, there is no "loading" per se, of course.

> If so, we'd like to spec when to restore scroll position during page load, right? 

Yes... We'd also like to spec what exactly focus() does in terms of scroll position.

> - Will "reload" run the same steps as going forward and the going back?

If you ignore bfcache and ignore the different revalidation policies, yes.
(In reply to Boris Zbarsky [:bz] (still a bit busy) (if a patch has no decent message, automatic r-) from comment #22)
> > Does page load begins at the end of all the steps in [1]? 
> 
> No, it begins way before that, for normal page loads, assuming you mean the
> GET to get the HTML.  For history loads... it depends.  In Gecko terms, if
> the previous page is not in bfcache, then the initial history traversal will
> bail out in step 1 of the steps you cite, because "entry no longer holds a
> Document object" tests true.  Then navigation will proceed as usual and
> eventually call back into these steps once the Document for the
> post-navigation thing is created.  For the bfcache case, there is no
> "loading" per se, of course.

Thanks for the explanation, I have a much clearer picture now.
For loading HTML files [1], it says: "After creating the Document object, but before any script execution, certainly before the parser stops, the user agent must update the session history with the new page."
And "update the session history with the new page" is where we traverse the history to the new entry.
So, it seems parsing and history traversal are two parallel steps.

> 
> > If so, we'd like to spec when to restore scroll position during page load, right? 
> 
> Yes... We'd also like to spec what exactly focus() does in terms of scroll
> position.

Ok, added this to the spec bug.

> 
> > - Will "reload" run the same steps as going forward and the going back?
> 
> If you ignore bfcache and ignore the different revalidation policies, yes.

I see, thanks again.

And here is the spec bug: https://github.com/whatwg/html/issues/2647
Moving to p3 because no activity for at least 1 year(s).
See https://github.com/mozilla/bug-handling/blob/master/policy/triage-bugzilla.md#how-do-you-triage for more information
Priority: P2 → P3
Component: DOM → DOM: Core & HTML
You need to log in before you can comment on or make changes to this bug.