Closed Bug 16806 Opened 20 years ago Closed 20 years ago

Save scroll position in session history

Categories

(Core :: DOM: Navigation, defect, P3)

defect

Tracking

()

VERIFIED FIXED

People

(Reporter: pollmann, Assigned: pollmann)

References

(Blocks 1 open bug)

Details

(Whiteboard: [nsbeta2+][Will be minus on 6/15] fix in hand)

Need to implement nsIStatefulFrame for the scroll frame.
Status: NEW → ASSIGNED
Will just implementing nsIStatefulFrame provide adequate results for named
anchors?  That is, if you move from one named anchor to another, and back, will
you go to the remembered position?  (Or was this bug about scroll position in
textareas and iframes, etc.?)


See my comments on bug 16379 and bug 3891.
*** Bug 3891 has been marked as a duplicate of this bug. ***
*** Bug 16379 has been marked as a duplicate of this bug. ***
The way I'm attacking it now is to store scroll position for each
nsScrollPortFrame.  I don't see how moving to named anchors will be any
different than just preserving the session history - when a link is clicked, you
are going to a different URL, even if that happens to be a named anchor on the
same page.  This should work now, no?

There is one known problem if I store the scroll position by pixel offset, as I
am currently implementing.  If the width of the window is resized, the position
in the document may be different, due to a reflow.  I don't see an easy fix for
this yet.
Played around with the resizing issue in a few browsers, and based on
what I saw, there may not be an easy fix for the resizing known issue
at all, based on pixel offsets. The best you could do would be to apply
two different factors based on height and width change. Two factors would be
needed because changes in height have a much greater effect than changes
in width. Also, this would have to based on canvas size, not window size.
The sidebar also complicates things. To do this, it would probably be
necessary to save the canvas size for each entry in the session history.

If the canvas size were saved, a simple workaround (albeit one that may strike
many people as weird) would be to resize the browser as the session history
is moved through, so that the canvas size is the same that it was when each
page was last viewed. The pixel offsets would be correct then for sure!
But this is probably too weird.

There is another workaround for this issue: store the page position based on
an integer pixel offest from the top of the topmost DOM node on the page
(integer because it could be off the top of the page). I'm guessing this
would be harder, but who knows? Note that the scroll position would still
need to be stored as a pixel offset as a backup, in case the page in the
session history has changed and that node on the DOM tree no longer exists!
How is the offset remembered for when we do reflows? When we change to an
alternate stylesheet, the entire window is redrawn but the position is not
reset to the top -- is the way that is done even remotely portable to the
session history?
I think it's remembered as a pixel offset from the top - that's how it looks,
anyway.

There's existing (but somewhat buggy) code (for event handling) to get the frame
at a given point, and existing code to scroll to a given frame.  The problem
with a better solution is knowing what point will give the correct frame (or
probably finding the frame some other way).
We could:
1) Get the current scroll offset
2) Get the frame at those coordinates (top left corner of view)
3) Find coordinates of that frame.
4) Store 2 and 1 minus 3 (offset into that frame) as the scroll state

When restoring we set the scroll offset to the coordinates of frame 2 (which may
be different due to a resize) plus the offset into that frame.

Another possibility would be to get the frame in the center of the view, and
scroll that into the center of the view when restoring.

These seems more user friendly than pixel offsets, but also could require a lot
of computation.  This logic would be used for every scrollbar visible on the web
page when it was left, including the scrollbars for listboxes and textareas.

For a first pass, I'm going to just store pixel offsets. Once that is working,
I'll look at optimizing the offset logic - perhaps to what I just mentioned, or
a rough approximation that requires less computation.
The problem with what you suggest is that you can easily hit a gap between
frames and end up with the BODY frame when you ask for the frame at a given
point.  Furthermore, you don't want to be messed up by floaters or fixed
positioned elements or other such things.
Thanks.  Yes, there are several issues now that I think about that - especially
the first suggestion I wrote is flawed - what if it got an inline frame that was
at the beginning of a line but then reflowed to the far right side of the line.
The user would be viewing a big blank document.  More thinking required on this.

Is pixel offsets is acceptable to everyone as a first pass?  :)
It's certainly better than nothing! :-)
Blocks: 19261
*** Bug 18658 has been marked as a duplicate of this bug. ***
*** Bug 19548 has been marked as a duplicate of this bug. ***
*** Bug 21739 has been marked as a duplicate of this bug. ***
Target Milestone: M14
Marking this feature for M14.
*** Bug 23079 has been marked as a duplicate of this bug. ***
Blocks: 24206
*** Bug 25304 has been marked as a duplicate of this bug. ***
*** Bug 26122 has been marked as a duplicate of this bug. ***
Keywords: beta1
Can we live without this for beta1?
Whiteboard: [PDT-]
Do you want beta1 to impress people?
*** Bug 27372 has been marked as a duplicate of this bug. ***
I honestly could deal without this for beta1, but it definitely should not slip 
beta2.
Moving non PDT+ bugs out.
Target Milestone: M14 → M15
*** Bug 24852 has been marked as a duplicate of this bug. ***
*** Bug 29967 has been marked as a duplicate of this bug. ***
*** Bug 30263 has been marked as a duplicate of this bug. ***
Updating QA contact.
Component: Browser-General → History
QA Contact: leger → claudius
*** Bug 30111 has been marked as a duplicate of this bug. ***
*** Bug 33704 has been marked as a duplicate of this bug. ***
ftp/file expantion state also needs saving, might be common code: see bug 33703
*** Bug 33965 has been marked as a duplicate of this bug. ***
*** Bug 34141 has been marked as a duplicate of this bug. ***
*** Bug 34523 has been marked as a duplicate of this bug. ***
Moving out to M17.
Target Milestone: M15 → M17
I really missed seeing this in PR1. I surely hope it doesn't get bumped 
indefinitely. At HotBot.com we gets lots of user feedback about this and 
only can tell them to use IE5 if this feature is important to them.
Rescheduling
Target Milestone: M17 → M16
*** Bug 35729 has been marked as a duplicate of this bug. ***
What about storing a certain amount of the source file at the position in the
history.  Then when the going back, search for the stored text in the source and
set the scroll position accordingly?  The hard part would be writing the code to
translate a linenumber in the source to a pixel coridinate for the scroll frame.
That doesn't explain which position in the document to use.  I was thinking a
bit about an algorithm for this, and I wrote down some notes somewhere.  (If
you're interested, I can try and find the notes.)  But the basic idea was this:
 * pick n points (say n=10) across the top of the scrolling view (or maybe a
hair below the top), kinda like this:
   *       *       *       *       *       *       *       *       *       *
and then find the frame that is the closest common ancestor of at least a*n of
them (say, a=0.5), and store the position in twips relative to that frame (or,
really, the a way of finding the content node for that frame).
 * have special cases for being at the top or the bottom (and of course hitting
it)
 * I know I have some other things to say here...

This algorithm could also be used during a resize-reflow.  It would be good to
use the original remembered position for a series of resizes - that is, on a
drag-resize (or even a series thereof), one would want to use the original
remembered position throughout, rather than continually recalculate.  This could
be done by clearing the remembered position when the view is scrolled.

One might also need to make special exceptions for when scrolling views were
within scrolling views (e.g., IFRAME), since the reference frame (probably)
shouldn't be within another scrolling view (or perhaps it's not a problem?).
*** Bug 36070 has been marked as a duplicate of this bug. ***
*** Bug 36477 has been marked as a duplicate of this bug. ***
*** Bug 36543 has been marked as a duplicate of this bug. ***
Removing 'beta1' keyword since beta 1 is long gone.
Keywords: beta1
Oops, I noticed there's a 'nsbeta2' keyword as well.  Putting that in.
Keywords: nsbeta2
*** Bug 37390 has been marked as a duplicate of this bug. ***
Depends on: 35566
Clearing Status Whiteboard since `beta1' has been replaced by `nsbeta2'.
I really hope that [PDT-] was left over from `beta1'...
Whiteboard: [PDT-]
*** Bug 38452 has been marked as a duplicate of this bug. ***
From a usability point of view, I think it's most obvious if the top of the 
content frame is kept constant, rather than the middle. Therefore, I suggest ...

* As soon as the frame either {starts to go to a new location} or {is resized,
  whether because of window resizing, sidebar opening, or whatever}, define an
  anchor point for the original layout at (0 + left margin of BODY in pixels,
  0 + top margin of BODY in pixels).

* Find the object boundary in the same layout frame which is nearest to the
  anchor point in the northwest direction. `Object boundary' here could be the
  border between two words, or between two images, or a plugin and a word, or
  whatever.

* Record both the identity of the object boundary (however you like -- the number
  of the relevant character in the source code might work, unless of course
  you're viewing an image) and the x and y offsets from that object boundary to
  the anchor point. So for example, I might end up with an anchor point that was
  `3 pixels south and 5 pixels east of the boundary between the 785th and 786th
  elements in the page'.

* Whenever that location is returned to, or the window is resized, position the
  layout so that, in order of priority:
  - the vertical position of the anchor point is exactly the same as it was
    originally
  - the horizontal position of the anchor point is as close as possible to what
    it was originally.

* Do this for any frames and iframes in the page, as well as for the document as
  a whole.

* Only define the anchor point just before the *first* time the frame is resized
  while it is shown, not every time. That way you will avoid the possibility of
  the position drifting away from the original after repeated resizing.

* Exactly how this method should deal with languages which aren't read in the
  {left-to-right, top-to-bottom} direction is left as an exercise for the reader.

Example: let's say I'm in an HTML page, and the first completely visible line at 
the top of the screen reads
|
| razed and they shall be scorched to the earth. Their tags shall

When I resized the window, the content would always be scrolled so that the word 
`razed' had exactly the same vertical offset from the top of the scrollport as it 
did originally. (The horizontal position would obviously differ because of the 
different wrapping of the paragraph.)

Does this make any sense?
To some degree, but focusing on just the NW corner will mess things up when
there are left-floating elements involved.  I proposed that we worry about the
entire top of the viewport.
Putting on [nsbeta2+][6/01] radar.  This work must be done by 06/01 or we may 
pull this for PR2.
Whiteboard: [nsbeta2+][6/01]
Target Milestone: M16 → M17
Not going in before M16 closes tonight.
*** Bug 40104 has been marked as a duplicate of this bug. ***
With 24 dups, this thing should be mostfreq.

*SPAM* - adding mostfreq keyword to bugs with loads of dupes. Please aid this 
effort by adding this keyword to any bugs with more than 15 DUPEs.
Keywords: mostfreq
*** Bug 40505 has been marked as a duplicate of this bug. ***
*** Bug 40749 has been marked as a duplicate of this bug. ***
I have an implementation idea for this. When we need to store the scroll 
position, could we store the XPointer to the topmost (as in the visible area) 
DOM element? Of course we do not yet have any XPointer support, but suppose we 
did, would this work?

I know that if we have scripts involved, the script may change the document 
between each load so much that that might break almost any attempt, but are 
there any other issues?
I have a fix in my tree for this that works for both Gfx and Native scrollbars.  
So far, all I've implemented is very basic scroll positioning (x,y pixel 
offsets).

The fix was:

 Implement nsIStatefulFrame for nsScrollPortFrame for the gfx scrollbar case and 
nsScrollFrame for the native scrollbar case (implementations are identical)

 Expose nsFrameManager's CatureFrameStateFor and RestoreFrameStateFor as public 
methods and add an additional parameter to accept a fixed content id (scroll 
frames don't have any content associated with them.

 Capture frame state for the root scroll frame in PresShell::CaptureHistoryState 
and restore it in PresShell::EndLoad.  Use a fixed content id.  (I chose 1, but 
should probably come up with an enum or something like that)

Not sure this will be approved for beta2 now that the deadline is passed, keep 
your fingers crossed!  :)
Summary: Saving scroll position in session history → Save scroll position in session history
Whiteboard: [nsbeta2+][6/01] → [nsbeta2+][6/01] fix in hand
Due to slip in schedule, moving this bug from [6/01] to [Will be minus on 6/15] 
for fix deadline.
Whiteboard: [nsbeta2+][6/01] fix in hand → [nsbeta2+][Will be minus on 6/15] fix in hand
> So far, all I've implemented is very basic scroll positioning (x,y pixel...

I made a slight improvement on this today.  Instead of storing just x and y, 
also stored height and width of the frame *inside* the scroll frame.  When 
returning to the page, I scale x and y proportionally to the new height and 
width:
  newx = oldx * newwidth / oldwidth
  newy = oldy * newheight / oldheight

This gets us to within a few lines of where we were on even tough pages like:
http://www.w3.org/MarkUp/ (Try scrolling to a choice place in Nav or IE, then 
going somewhere else, radically changing window width and returning - both 
apparently store pixel offsets, and you'll have to scroll to find your place!)

Ideally, we should store a content pointer, but I don't have any ideas on how to 
easily implement this.  (I don't think I'll have time to go the xpointer 
route...)
Just checked in the fix.  To verify, go to any page.  Scroll part way down.  Go 
to any other page.  Click back.  You should see that you are at the same part of 
the page that you were at when you left the page.  Thanks!
Status: ASSIGNED → RESOLVED
Closed: 20 years ago
Resolution: --- → FIXED
*** Bug 42914 has been marked as a duplicate of this bug. ***
VERIFIED Fixed in the 2000061908 builds on all platforms. note this fixes saving scroll position for back/forwqard but nor for 
reload. That issue is beingtracked separately in bug 40104

note: the username t8m@centrum.com is being truncated to t8m@centrum.c on the cc list by 4.x and bugzilla won't let me submit 
this bug as such so I had to remove it, sorry, I'll try to correct later.
Status: RESOLVED → VERIFIED
No longer blocks: 24206
This has now regressed and been reborn as bug 46877. I suggest we use our voting
power to try and get this fixed again.
*** Bug 276007 has been marked as a duplicate of this bug. ***
Component: History: Session → Document Navigation
QA Contact: claudius → docshell
You need to log in before you can comment on or make changes to this bug.