Open Bug 43114 Opened 20 years ago Updated 6 months ago

Need better approach to store scroll position in session history

Categories

(Core :: Layout, defect, P3)

defect

Tracking

()

Future

People

(Reporter: pollmann, Assigned: dbaron)

References

(Depends on 1 open bug, Blocks 12 open bugs)

Details

(Keywords: parity-chrome, Whiteboard: [patch])

Attachments

(1 file, 1 obsolete file)

See details of other approaches outlined on bug 16806.

One thing to bear in mind is that we can not use raw pointers to content because 
all content will be destroyed when leaving the page.
Can be dealt with later.
Status: NEW → ASSIGNED
Target Milestone: --- → Future
Bulk reassigning form bugs to Alex
Assignee: pollmann → alexsavulov
Status: ASSIGNED → NEW
Blocks: 64926, 98895
Blocks: 19261
No longer blocks: 139832
Keywords: nsbeta1
nsbeta1-
Keywords: nsbeta1nsbeta1-
Flags: blocking1.4?
Flags: blocking1.4? → blocking1.4-
Blocks: 251784
No longer blocks: 98895
Taking bug.
Assignee: alexsavulov → dbaron
Attached patch some initial attempts (obsolete) — Splinter Review
Some initial work on this and bug 103279 -- still has a bunch of missing pieces.
Whiteboard: [patch]
Attachment #209533 - Attachment is obsolete: true
Please don't clutter bugs with comments that don't help in fixing them.
Here's a bunch more disorganized thoughts (to add to bug 103279 comment 9, etc.) after a conversation I just had with Zachary and Jesse.  I really need to organize these more coherently at some point.

(In reply to comment #9)
> target:  either session history, anchor, or none.  After *each* reflow during
> page load we should scroll to the target if:

Note that we should also do this for other reflows, e.g., resizing the page, or for other dynamic changes.

We should also have a "target" mechanism corresponding to "maintain the user's scroll position" that picks a relevant piece of content by some algorithm and holds onto it until the next time there is scrolling (so we don't have drift of that piece of content across resize reflows).

One key point is that we want to remember what caused the last scrolling operation ... and if it was the user, the first time we run the algorithm to figure out what piece (or pieces) of content we want to keep visible, we should remember those results until the next scrolling operation so we don't have drift across resizes or other causes of reflow.

 * we should keep that state (cause of the last scrolling operation) in session history so that we restore it on restore

We should restore the current scroll position on *every* reflow.

The types of "reasons for current scroll position" are:
 * at top of page
 * scroll to position
 * scroll to named anchor
... I suspect these 

When we restore from session history, the reason in the session history should always override the named anchor.  When we're not restoring from session history, the initial current position should be "scroll to named anchor" if there is one, and "at top of page" if there isn't.

A few use cases:

 * user loads the page, scrolls down.  We should keep the thing the user scrolled to visible as they resize (and it shouldn't drift due to repeated recomputation)

 * user loads page with named anchor target (and doesn't resize page), then resizes window.  We should just keep rescrolling to that named anchor target.

(we had a whole bunch more... I should find time to list them)
One other fun complication here -- we need to consider horizontal scrolling as well.  There's the question of whether to consider it separately or together.  (I'm pretty sure scrolling to a named anchor should reset both scrolling directions.  The question is whether scrolling on one axis using the scrollbars should stop targetting the named anchor completely and fall back to the scrollbar algorithm for both directions, or whether it should only do so for one direction.  It's probably much easier to just do so for both directions, and probably not much gain.)

One other point is that I think we should still try to scroll to a named anchor in the first reflow after we've constructed frames for the content containing that anchor -- even if the user has scrolled elsewhere.  But if the user scrolls again, we should stop trying to maintain that position.

One other thought I had about the "maintain the scrolled-to position" algorithm is that it could sample a bunch of points across the screen, and the figure out the new positions of *all* of those pieces of content after the scroll.


Another testable use case:

 * user loads a page with a named anchor in the URL.  The content with the anchor is initially too close to the bottom of the page to prevent it from appearing at the top, but within a few hundred milliseconds enough additional content comes in that it could be at the top.  It should immediately jump to the top of the page (i.e., before the page finishes loading).
Yet another type of scroll destination is scrolling a range into view, as we do when finding text.  See also bug 171237.
One other question is whether to store the targets for different scrollable things together or separately.  I suspect the answer is together, since some scrolls (e.g., scroll-to-anchor) can affect multiple scrollable areas (e.g., scrolling the viewport and a div with overflow) and some affect only one.
QA Contact: chrispetersen → layout
One more thought: when the initial named anchor scrolling happens, we should perhaps consider using scrolling within the first few hundred milliseconds as not intending to overwrite the scroll to the named anchor, but user scrolling later as intended to overwrite it.

(In reply to comment #12)
> One other question is whether to store the targets for different scrollable
> things together or separately.  I suspect the answer is together, since some
> scrolls (e.g., scroll-to-anchor) can affect multiple scrollable areas (e.g.,
> scrolling the viewport and a div with overflow) and some affect only one.

I think together... and all the sub-areas should be indexed by the content node containing the sub-area.  There could be some interesting interactions with session history, though.
To continue the thought about comment 12 and comment 13 -- I think things in the same document should be stored together, but things in iframes should be separate.  This probably eliminates interesting session history issues.
(In reply to comment #14)
> To continue the thought about comment 12 and comment 13 -- I think things in
> the same document should be stored together, but things in iframes should be
> separate.  This probably eliminates interesting session history issues.

Although... to continue a little more... maybe the answer could be everything being stored separately.

An action for "scroll X into view" or "scroll to anchor Y" could simply lead to a scroll being done on everything from X/Y up to the top of the tree.
Microsoft describes IE8's approach to maintaining scroll position across zooms:

http://blogs.msdn.com/ie/archive/2009/05/18/making-the-web-bigger-part-1-improvements-to-zoom.aspx
One other thing I'd been thinking about:  I had been thinking before that in some cases we might want to store a sequence of scrolling operations (e.g., scroll to align named anchor with top, then scroll text "foo" into view).  However, the resizing behavior in such a case might be too confusing to the user (i.e., too many possible modes) to make such behavior worthwhile.
How about computing current scroll position a named anchor plus offset from the named anchor where offset is a ratio of document width/height.

Rationale:
1) It's easy to accurately determine the location of named anchor even after reloading the document.
2) After originally scrolling to the named anchor the user had scrolled away from it (offset)
3) If this offset is stored as a ratio (or percentage) of document width and a ratio of document height, then the offset should be usable even after reflow (ratio offset is scaled to real pixel offset after computing the document width and height after reflow). Note that simple pixel (or twips) offsets do not work after a reflow because narrowing the window to half of its original width will usually more than double the length of the document in pixels.

This still does not work if the named anchor is e.g. inside an element with position:absolute and the user has scrolled looking at other text.

Basically the problem is that the browser should be able to guess what part of the page the user is focused in and try to keep that part of the page in constant location.

Perhaps the browser should (logically) compare screenshots of the page before and after every scroll operation and use largest continuous area that changed during that scroll as the focus area and the focus point would be the (weighted?) middle point of that area? (Guessing that because user changed this area most, the interesting stuff must be in this area.) Basically the history mechanism should store that this focus point should be in some constant location inside the viewport (again, using ratio of viewport width and height). The focus point could be stored as 105th element + 5th word.

The algorithm used in IE8 (described on page linked from comment 16) seems to be only about user focus and as such does not work with history - only on the currently visible page. The algorithm is basically as follows: try to keep pixel under pointer in constant location inside the viewport, in case of multiple pointers (e.g. two fingers on multitouch screen) use middle point of pointer locations as the focus point. I consider this as a zoom+pan feature, not a history scroll mechanism (still a good feature to have, though). Note the serious restrictions described in IE8 implementation, too.
Blocks: 755770
I am not sure if what I am experiencing is related to this or not, but I wanted to throw another data point into the discussion.

I am using 28.0.

I am writing a web app that uses history manipulations functions (e.g., pushState(), etc.) to fake browser history. My app effectively allows you to browse a database. I am seeing that if I drill down to a database record page and scroll an internal div to an arbitrary position, then go back in my browser history and drill down a similar path, then the scroll position of the internal div is restored to where it was when I was at the same place in my history.

To put it in more concrete terms, say I have a query result for a list of albums and I click on an album to view the album page, then on that album page I click on the artist to view the artist page, and then finally on the artist page scroll an internal div containing all albums by that artists. If I go back to the original query result (via the back button) and then drill down following the same path doing pushState() to generate "new pages" along the way, then it doesn't matter if I end up at the same artist page or a different one, the scroll position of the internal div is restored.

This doesn't make sense to me. First, I have a single page web app, so I all of my content is completely dynamic. Second, I am using pushState() to instigate "new page" generation as I drill down and the URL does change. I would expect that pushState() is treated as a new page load and not a re-load from history; thus, I would expect all scroll positions on the subsequently loaded "new pages" to be zero.

For further information, if I drill down further from the page with the improperly remembered scroll positions so that I come back to the very same record, then the scroll positions are set to zero as I would expect. This leads me to believe that the scroll positions get re-loaded relative to browser history even though technically I am loading a "new page" via pushState().

I think this behavior is incorrect, but perhaps I'm missing something. It works the way I would expect in Chrome.

I hope you could understand my example.
dbaron: you took this bug on 1/24/06.  are you still planning to work on it?
Flags: needinfo?(dbaron)
Probably not in the next few months, at least, though maybe beyond that.
Flags: needinfo?(dbaron)
As a followup to my comment from last year, this bug 706792 is also related to the issue I was trying to report.
dbaron: if you're unlikely to work on this in the foreseeable future, is it helpful for you to be the owner?
Flags: needinfo?(dbaron)
I'm probably not, but I'd prefer to stay the owner, since I'd at least like to have a detailed discussion with anybody else who wants to work on it instead.
Flags: needinfo?(dbaron)
i guess you don't don't think it's significantly less likely for someone to be inspired to work on a bug that already has an owner :P
Sorry if my comment misses the point, I don't know enough about FF or about this issue to be sure of what I'm saying - just ignore it if it's dum.

This bug really bugs me on Firefox Mobile. Any page resize will make FF lose position, and resizes happen accidentally because some movement causes the phone to switch from portrait to landscape mode, or simply because the screen lock kicks in while I'm using the phone in landscape mode, and the screen lock forces a portrait mode... I tried using Firefox to keep reference materials open when I'm speaking in public, giving classes, and it's virtually impossible...

I understand these reflow issues are complex but I ask if solving it just for these mobile transitions would make it easier, as a partial, temporary solution? I mean, if solving the whole issue is too complex, can anybody at least make the browser accurately reposition by saving the scroll position for 480x800 pixels when the page is fully loaded, and set it back correctly if the screen goes to 800x480 and then back to 480x800?

The rationale is that for a user, it is incomprehensible why Firefox cannot take us back to where it was only instants ago. Maybe we can't solve it now for the tough cases, when the page is changing shape and size while loading, or with serial window resizes as a user drags the corner of a window on a Desktop. But fixing the simple resizes on stable pages would already be a big help.
I proposed a partial-solution strategy for this 16 year-old bug. I still don't know if it is a valid proposal because nobody commented on it and I admit my knowledge of the matter is insufficient to speak with any certainties.

Besides the technical proposal, I would like to add a human/managerial proposal. If none of the people here are able to tackle the bug, for so long, could any of them take a stand for it, talking to their boss, their colleague, making people notice the bug and recognize its renewed relevance (with the migration to mobile devices everywhere)? 

This is not going to be noticed because of votes on Bugzilla, it's too complicated to get here and vote. We just have to feel the anger of people all over the world getting their scroll position robbed from them all the time.

Maybe somebody managing this project will decide to give up on some new feature for the next release and just put their best developer on this until it is fixed. Maybe. It's a big maybe, I know. Either way, I would respect their decision as more knowledgeable than mine. But I wish there was an actual decision taken to their consideration...

I apologize if I am being inopportune and I thank everyone involved in this discussion.
Agreed. Stuff like this is why Firefox is dying a slow death.
There is a draft proposal on how to do this, based on the Blink implementation of scroll anchoring:

https://discourse.wicg.io/t/proposal-scroll-anchoring/1752
Scroll Anchoring spec https://wicg.github.io/ScrollAnchoring/
Landed in Chrome 56 and Opera 43 https://www.chromestatus.com/feature/5700102471548928
Whiteboard: [patch] → [parity-chrome][patch]
Mass bug change to replace various 'parity' whiteboard flags with the new canonical keywords. (See bug 1443764 comment 13.)
Keywords: parity-chrome
Whiteboard: [parity-chrome][patch] → [patch]
The Scroll Anchoring spec has been moved into CSSWG. https://drafts.csswg.org/css-scroll-anchoring/

This can be closed after 1305957 ?

No; there are significant pieces proposed here that haven't been done yet.

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