Open Bug 301307 Opened 19 years ago Updated 2 years ago

location.replace behaves badly, making accessible Ajax applications impossible

Categories

(Core :: DOM: Navigation, defect)

defect

Tracking

()

People

(Reporter: lon, Assigned: smaug)

References

(Blocks 1 open bug, )

Details

(Keywords: helpwanted)

User-Agent:       Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.1.4322)
Build Identifier: Mozilla/5.0

See http://www.q42.nl/FireFix/firesox.html, full explanation is in there, 
including steps to reproduce

Reproducible: Always

Steps to Reproduce:
See http://www.q42.nl/FireFix/firesox.html, steps to include are in there
Actual Results:  
location in address bar was wrong, content in iframe was wrong, state of back-
button was wrong

Expected Results:  
different. Address, iframe and history should work correctly so we can build 
Ajax apps that support navigating using history and are bookmarkable.
IE supports all of this correctly.
It seems invalid to me. location.replace is designed to **replace** the current
page. Of course the back button shouldn't work.

Use location.href if you need history.
(In reply to comment #1)
> It seems invalid to me. location.replace is designed to **replace** the 
current
> page. Of course the back button shouldn't work.
> Use location.href if you need history.

I disagree.

Sample situation:
1. I have a page having an iframe.
2. I load a new page inside the iframe.
3. After that I replace the main URL by just adding a hash to it.
4. Now I press Back:

Firefox behavior:
1. the hash is removed
2. the iframe is NOT rolled back to the previous page

Expected behavior:
1. the hash stays
2. the iframe is rolled back to the previous page

Why:
the main url has been replaced. It should not be possible to roll that back 
using the back button.
the iframe has been changed, it should be possible to roll it back using the 
back button.

IE does it this, the correct, way. And whichever way you look at it: FiriFox is 
wrong in doing what it does.

We must be able to change the URL without influencing the history for 
bookmarkability purposes (using replace) and at the same time trigger history-
additions for navigation purposes (using a hidden iframe) for Ajax applications.

What's wrong with that?
Assignee: nobody → general
Component: History → DOM: Level 0
Product: Firefox → Core
QA Contact: history → ian
Version: unspecified → 1.0 Branch
Not a DOM bug....  Also not likely to get fixed unless someone steps up to do it.

The basic problem is in the fundamental architecture of session history -- the way we determine which navigation actually took place to take us between two adjacent session history trees just grabs the rootmost docshell in which something interesting happened.  In this case that's the toplevel page, unfortunately.
Assignee: general → nobody
Status: UNCONFIRMED → NEW
Component: DOM: Level 0 → History: Session
Ever confirmed: true
Keywords: helpwanted
OS: Windows 2000 → All
QA Contact: ian → history.session
Hardware: PC → All
Version: 1.0 Branch → Trunk
I agree wholeheartedly with the comments.

I have an ajax app that uses one url to control a number of iframes. Only the one url is remembered in history - the rest are synchronised on the fly.

Whilst IE's behaviour works well, FF's back button just does not work. Another mild annoyance is that while IE holds the title of the memorised page as reference in the back button, FF holds the title of the top page, which always stays the same.

We have large University FF customers and we can't show the navigation of the app off to its best without using IE.
Blocks: 311337
FWIW, I agree that this is a bug, and I believe the HTML5 spec supports that.
Component: History: Session → Document Navigation
QA Contact: history.session → docshell
bz, sounds like this is something we need to deal with, and it's hurting several apps. From my limited understanding of what we'd need to do it sounds like something we don't want to do 1.9.1. bz, do you have a gut feeling for how much work it would be for someone to fix this bug?
Flags: blocking1.9.2?
That really depends on the behavior that's desired here...

Right now, history navigation is performed essentially by diffing the before and after state and undoing (or redoing) whatever the change was.

The problem here is that two changes were made; one is "undoable" and one is "not".  I say "not" in quotes, because if you have just a single window, no subframes, and you load A, then load B, then replace with C, then navigating back from C should load A.

So say I load <http://example.com/#x> which has a subframe, then do a load in the subframe, then navigate the toplevel page to <http://example.com/#y>, then replace with <http://example.com/#z>.  Back should now go to <http://example.com/#x> in the toplevel, not navigate the subframe.

In other words, as far as I can see the behavior needs to depend not only on the state of what's loaded where, as now, but on the history of how we got there...  Note that this is only a problem for anchor scrolls, because in all other cases doing a load in the parent automatically gets rid of the kids so there is no problem.

It might be possible to get the "right" behavior by somehow flagging, when replace() gets called, which frame is the right one to navigate.  Basically, calling replace() should replace the session history tree at the current index, but not change which docshell would be navigated when one goes back.  Or something along those lines.  At least for anchor scrolls, maybe.  If this approach works, then I think 2-3 days should be enough to implement; testing is another matter.  We don't have good session history regression tests yet.

If the above approach doesn't work, then gut feeling is a week or two; it might be longer if it turns out that the history navigation algorithm needs a complete revamp of some sort, but I doubt that will end up being the case here.
Smaug, do you think you could have a stab at what bz discusses in the above comment? This is important enough, it seems, that if it's feasable we'd even consider taking this for 1.9.1, *assuming* of course that this turns out to be a safe enough change. And that's an overly optimistic assumption I'm sure, but it's IMO still worth a shot if you think you have cycles to look into this (regardless of whether we can take this for 1.9.1 or not).
Assignee: nobody → Olli.Pettay
Flags: wanted1.9.1+
Any testcases for this?

(In reply to comment #7)
> That really depends on the behavior that's desired here...
Exactly

> So say I load <http://example.com/#x> which has a subframe, then do a load in
> the subframe, then navigate the toplevel page to <http://example.com/#y>, then
> replace with <http://example.com/#z>.  Back should now go to
> <http://example.com/#x> in the toplevel, not navigate the subframe.
I would expect <http://example.com/#z> in toplevel and subframe navigated back.
> Any testcases for this?

Naturally not.  :(

> I would expect <http://example.com/#z> in toplevel and subframe navigated back.

Why?  Basically, replace() should not affect what back would do.  Before the replace(), back would have navigated the toplevel, no?
(In reply to comment #10) 
> Why?  Basically, replace() should not affect what back would do.  Before the
> replace(), back would have navigated the toplevel, no?
Ah, my mistake.
I feel like I'm missing some bits here. Why this suddenly became so important?
Mostly because of comment 6, apparently.  Several different applications all hitting this issue recently.
Flags: blocking1.9.2? → blocking1.9.2-
Here's a simple app that fails running in an iframe:

    <script>alert("loading")</script>
    <button onclick="location.hash='#a'">goto a<button>
    <button onclick="location.hash='#b'">goto b<button>
    <button onclick="location.replace('#c')">replace with c<button>
    <button onclick="history.back()">back<button>

Press the buttons in sequence. The last button would be expected to take the iframe back to #a, and this is the behavior in all other browsers. Instead, the iframe reloads itself. 

This bug manifested itself in an Ember app, where the `Route#replaceWith` API translates into `1ocation.replace`. It is standard to use this approach to keep intermediate pages out of the history, and make `history.back()` do what we want it to do. We could hack `history.back()` to do something else, but we cannot hack the browser's back button.

Maybe this bug could be addressed before its 10th anniversary?

Is there some workaround?
(In reply to Not doing reviews right now from comment #7)
> That really depends on the behavior that's desired here...
> 
> Right now, history navigation is performed essentially by diffing the before
> and after state and undoing (or redoing) whatever the change was.
> 
> The problem here is that two changes were made; one is "undoable" and one is
> "not".  I say "not" in quotes, because if you have just a single window, no
> subframes, and you load A, then load B, then replace with C, then navigating
> back from C should load A.
> 
> So say I load <http://example.com/#x> which has a subframe, then do a load
> in the subframe, then navigate the toplevel page to <http://example.com/#y>,
> then replace with <http://example.com/#z>.  Back should now go to
> <http://example.com/#x> in the toplevel, not navigate the subframe.
> 
> In other words, as far as I can see the behavior needs to depend not only on
> the state of what's loaded where, as now, but on the history of how we got
> there...  Note that this is only a problem for anchor scrolls, because in
> all other cases doing a load in the parent automatically gets rid of the
> kids so there is no problem.
> 
> It might be possible to get the "right" behavior by somehow flagging, when
> replace() gets called, which frame is the right one to navigate.  Basically,
> calling replace() should replace the session history tree at the current
> index, but not change which docshell would be navigated when one goes back. 
> Or something along those lines.  At least for anchor scrolls, maybe.  If
> this approach works, then I think 2-3 days should be enough to implement;
> testing is another matter.  We don't have good session history regression
> tests yet.
> 
> If the above approach doesn't work, then gut feeling is a week or two; it
> might be longer if it turns out that the history navigation algorithm needs
> a complete revamp of some sort, but I doubt that will end up being the case
> here.

I don't know the implementation details, but whatever they are, Chrome, IE, and all other browsers got them right.

Hey rtm,
Can you still reproduce this or should we close it?

Flags: needinfo?(rtm)

Redirect a needinfo that is pending on an inactive user to the triage owner.
:hsinyi, since the bug has high severity, could you have a look please?

For more information, please visit auto_nag documentation.

Flags: needinfo?(rtm) → needinfo?(htsai)
Flags: needinfo?(htsai)

Still reproducible with Comment 17

Severity: major → S3
You need to log in before you can comment on or make changes to this bug.