Closed Bug 521460 Opened 15 years ago Closed 15 years ago

After pushState(), navigation back to parent shows iframe contents.

Categories

(Firefox :: Bookmarks & History, defect)

defect
Not set
normal

Tracking

()

RESOLVED WORKSFORME

People

(Reporter: kyle.scholz, Unassigned)

References

()

Details

User-Agent:       Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.207.0 Safari/532.0
Build Identifier: Minefield/3.7a1pre w/Patch v1.2.1 from Bug #500328

When a window at location A contains an <iframe> at location B, if the parent's location is set to B using pushState(), when the browser navigates away and back, the parent window displays B instead of A.

Reproducible: Always

Steps to Reproduce:
See demo URL.
1. Click "set state".
2. Click "Google"
3. Navigate back.
Actual Results:  
The contents of somepage.html (the <iframe> src are displayed.

Expected Results:  
The contents of pushState_iframe.html should be displayed.
I think we're seeing desired behavior here.

I.e. if you navigate to A
then pushstate to B
then navigate to C
then go back

Shouldn't you be at B again? And if you go one more back you should arrive at A again.

One of two things can happen when going back to B.

Either we'll still have the history entry still in memory (in the bfcache), in this case we'll just display it.

Or it no longer exists in memory. Here we decided (and i think the spec says) to load the B url.

In either case, if you go back once more to get to A, we'll simply fire popstate rather than hit the network.


What I think you're seeing is that when you stick an <iframe> on the page, gecko won't put B into the bfcache (due to limitations in the bfcache). This has nothing to do with where that <iframe> is pointing. Also, the same thing could happen if you don't have an iframe on B, but if you load a bunch of other pages in some other tab while viewing C, since that will evict B from the bfcache eventually.
Okay, I see that the presence of the iframe only seeds bfcache. It's not necessary to demonstrate the bug. Now let's dicuss whether it is a bug or issue for clarification in the specification:

Now that we've made it possible to change the URI of a Document, I think we've introduced a question about what the History stack is supposed to represent. I've made the assumption that it's a stack of Documents but it seems to be implemented as a stack of URIs. Until now, these have been equivalent so it didn't matter.

I think the "stack of Documents" model corresponds better to the user's
understanding of the feature, because it ensures that they can return to a state they've seen before, regardless of how pushState() has manipulated URIs. Thoughts?
Interestingly, I can't reproduce the indicated behavior on my machine.  But I'm always starting a fresh copy of FF and navigating to the given page immediately.  Perhaps your bfcache is a little fuller than mine.

I think what we have here conforms about as well as we can to users' expectations.  When we can, we keep Documents around.  But eventually, we run out of memory and we have to forget about some Documents.  If the user goes back to a page whose Document we've forgotten, we have no choice but to re-fetch the page.  The real question is, which page should we fetch?

We could introduce a notion of the "original URI" of a page.  Then when we went back to somepage.html and had to re-fetch, we'd fetch pushState_iframe.html.  But I think this is the wrong thing to do.

The whole notion of push/replaceState is that the new URI *is* the URI of the history entry; you're just not pulling that URI when you navigate to it initially.  But you should be able to bookmark that URI, or e-mail that URI to a friend, or even refresh the page, and everything should work as though that URI is *the* URI of the page.  Adding an "original URI" property would break all these use-cases.
We're in agreement that pushed URIs should be portable over bookmarks, sharing, and refresh. One of the desirable features of pushState is that it will enable further portability by enabling developers to use the same URI scheme for applications that require a modern browser but offer legacy HTML implementations.

My concern is that in the present implementation, returning to a state in history that has a pushed URI may cause a new fetch and will certainly lose any cached state from the Document that the user was viewing. As a result, the user may not see the same content they were viewing when they navigated away from the Document.

For example, imagine an Ajaxy implementation of Bugzilla. I visit http://bugzilla and perform a search for "History". The application pushes a URI that exposes my query, making it sharable and present in HTTP-Referer: http://bugzilla/search?History. Clearly, any visitor to this URL should perform the same query, but the results are dynamic so the content may vary in each response. When I perform the query, suppose there are 3 results. I scan the list of results and see that #1 and #3 are interesting, so I click on #1. In the time I'm reading #1, someone modifies #3 so it no longer matches my query. When I click Back to return to the results, since the URI was pushed with pushState and doesn't match the entry in bfcache, the Document is refetched and #3 is no longer in the result list. As a user, this perplexing and frustrating, because I expect to see the same Document (or application state) I just viewed.

If this isn't compelling, I can provide more cases.

Additionally, the refetch introduces new latency and server load to deliver content that the user may have in bfcache. It breaks form persistence and loses any DOM state changes the user may have triggered in an application that are not somehow serialized to the pushed URI.
(In reply to comment #4)
I think we may be talking past each other here.

We keep Documents in bfcache on a best-effort basis, and pushState doesn't (or, at least, shouldn't) interact any differently with the cache than clicking a regular link.

If you visit page A, make some modifications to the DOM, click a link to B, then go back to A, we may or may not re-fetch A, depending on whether A was evicted from the bfcache.  I agree that in many cases, it's better not to re-fetch A.  But we can't keep every Document in the cache forever; at some point we have to evict from the cache.  Those eviction decisions are (or, again, should be) made in exactly the same fashion whether or not pushState is involved.

In other words, you should be able to pushState to http://bugzilla.com/search?History, click a link, and then go back and see exactly your old search results.  We don't force a re-fetch in this scenario; we use exactly the same policy here as we'd use if no pushState was involved.  If the policy is substantially different when we use pushState, then I think that's a bug.
Okay, sorry for the confusion here. I think we're missing each other because the issue I'm seeing really does exhibit ONLY when there is an iframe on the page. Here's a demo without the iframe:

http://kylescholz.com/projects/firefox/bugs/pushState_noiframe.html

1. Navigate to page
2. Click set state (URI is changed to somepage.html)
3. Navigate away
4. Navigate back (displayed URI is somepage.html but contents are from pushState_noiframe.html. Great!)

When the iframe is present in:

http://kylescholz.com/projects/firefox/bugs/pushState_iframe.html

At 4, the location reads somepage.html and contents of somepage.html are displayed in the window. You mentioned this isn't repeatable for you in Comment #3. Is there no difference in the behavior of these demo cases? My build is a few days old. Are you synced to head?
I've been working off Jonas's comment #1:
> What I think you're seeing is that when you stick an <iframe> on the page,
> gecko won't put B into the bfcache (due to limitations in the bfcache).

My understanding is that he thinks the behavior you're seeing has to do with limitations of the browser's bfcache, and has nothing to do with pushstate.  But perhaps that doesn't match with your experience.

Can you see what bfcache behavior you get when you do something similar without pushstate?  e.g. Visit page A which contains an iframe, modify the DOM somehow, click link to Google, then go back.  Is the state of A saved as expected, or is A re-fetched?

I don't see the behavior you're describing on on pushState_iframe.html on my build.  I'm based off revision fe12a3c05c39 (from Friday), and I'm running Ubuntu 9.04.  But bfcache could be doing different things on our systems depending on the amount of available RAM, etc.
My effort to reduce confusion:

There seems to be two separate issues debated here. First, what URL should be loaded if the following steps happen:

1. User goes to page A
2. pushState is called to change the page-url to B (for example in response to
   user clicking somewhere on the page)
3. User navigate to page C
4. User clicks 'back' button

Should we load page A and popstate with the uri from B, or should we load B.


The second issue is, why should having an <iframe> on the page affect anything at all.


So, dealing with the first question first. I think we should load page B. The reason is that that is the page that we'd load if the user created a bookmark while on page B and then loaded the bookmark. And it's the url that would send if the user used the "Send Link..." feature (in the Edit menu). And of course it's the URL that would be loaded if the user copied the URL in the URL-bar and IMed to a friend.

Another reason is performance. Say that A is the URL
"http://shop.com/producList/shoes", and B "http://shop.com/producList/hats". If we loaded A first and then "popstated" to B, then that would mean that we'd first load the page with the list of shoes, and then the page would make an AJAX request to get the list of hats. It would be better if we instead loaded a page with a list of hats directly.


So to the second issue, why does having an <iframe> in the page affect anything. This is actually totally unrelated to pushState. Consider the following set of actions:

1. User navigates page A
2. User navigates to page B
3. User clicks 'back' button.

In step 3, Firefox will do one of two things. Either we'll load page A, i.e. reload and reparse the page. Or, we we'll bring back the in-memory representation of page A as it was when you left it. When leaving a page, we usually don't actually unload it, instead we just hide it. If the user then goes back we simply unhide it. No loading from the network, no parsing. See [1] for details. One way to see if we unhide or reload a page is by modifying the DOM. For example the following page:

<html>
<body>
<input type=button value="click me"
 onclick="document.body.appendChild(document.createTextNode('this was added'))">
</body>
</html>

Load that page and click the button and then navigate elsewhere. When you click the back you'll still see the "this was added" text if we unhid the page. If the page is reloaded the text won't be there.

If a page contains an <iframe> we are always forced to unload it from memory. I.e. if you add an <iframe> to the above example you'll never see a "this was added" text when you go back to the page.

[1] https://developer.mozilla.org/en/Using_Firefox_1.5_caching


So, putting this all together, in your original example in comment 0, if you don't have an <iframe> we don't actually load page A. We in fact don't load anything. We simply unhide the page you just left.

Makes sense?
> If a page contains an <iframe> we are always forced to unload it from memory.
> I.e. if you add an <iframe> to the above example you'll never see a "this was
> added" text when you go back to the page.

This does not appear to be true.

See:
http://www.douglips.com/foo/bugs/ffnoiframe.html
and
http://www.douglips.com/foo/bugs/ffiframe.html
and
http://www.douglips.com/foo/bugs/ffiframe2.html

All exhibit the "this was added" after you "exit stage left" and come back.
FYI: I've determined that the behavior I initially reported only occurs when Firebug is present, so I don't think there is any issue with the patch. Without Firebug, the Document is unhidden as Jonas describes when I navigate back (even when it contains an iframe), and includes any DOM manipulations. Thanks for digging in guys and I'll try to address this in Firebug.
Status: UNCONFIRMED → RESOLVED
Closed: 15 years ago
Resolution: --- → WORKSFORME
Thanks for the update. I wonder if what happens is that Firebug registers an "unload" event handler. Such documents are for sure never put in the bfcache.

Looked at the bfcache code and indeed we are indeed able to cache documents with <iframe>s in them.
You need to log in before you can comment on or make changes to this bug.