| Summary: | pushState and navigation sequence causes document to be requested when it shouldn't | ||
|---|---|---|---|
| Product: | WebKit | Reporter: | Iraê <irae> |
| Component: | History | Assignee: | Nobody <webkit-unassigned> |
| Status: | NEW --- | ||
| Severity: | Normal | CC: | beidson, irae, sleroux, stefan |
| Priority: | P2 | ||
| Version: | 528+ (Nightly build) | ||
| Hardware: | Unspecified | ||
| OS: | Unspecified | ||
|
Description
Iraê
2015-06-12 19:09:51 PDT
After more time of looking into this after the lab, and thinking about it while *not* tired and busy, I think this behavior is correct. At least that's what I concluded with the concocted example we were considering in the lab. I don't have time to look closely at this test case ATM, but will either this evening or tomorrow. I still think there is something wrong here. I see diferences when binding 'onpopstate' before and after 'onLoad' have fired. How binding the pop state event later cause the page to not be re-requested? This has been one of the top crashers for Firefox for iOS. We narrowed our crash down to our usage of history.pushState() to simulate history restore for tabs. (Which is not an awesome hack and a workaround for the lack of a mutable BackForwardList) Here is a simple POC that makes both Safari and Firefox for iOS crash: https://people.mozilla.org/~sarentz/t/boom.html Tested on an iPad Mini running 9.1. Has been reported on 9.2 too. Including high end devices like 6s. May depend on memory usage in general. It is more reliable to reproduce if you open a lot of apps first. This causes a memory pressure on the device that results in the OS killing us. (People have been reporting this bug to us as 'my whole device reboots' because they see the white apple on black screen appear, but now we know that due to the high memory pressure Springboard is also simply killed, which looks like a reboot) I guess this might explain why it is reproducible before onLoad fires and there is no issue after onLoad. I would guess the loading process is memory intensive. Also would explain why in complex scenarios, like previous iterations of Yahoo Search, this was 100% reproducible, while I had trouble creating local POCs. (In reply to comment #3) > This has been one of the top crashers for Firefox for iOS. > > We narrowed our crash down to our usage of history.pushState() to simulate > history restore for tabs. (Which is not an awesome hack and a workaround for > the lack of a mutable BackForwardList) > > Here is a simple POC that makes both Safari and Firefox for iOS crash: > > https://people.mozilla.org/~sarentz/t/boom.html > > Tested on an iPad Mini running 9.1. Has been reported on 9.2 too. Including > high end devices like 6s. May depend on memory usage in general. It is more > reliable to reproduce if you open a lot of apps first. > > This causes a memory pressure on the device that results in the OS killing > us. > > (People have been reporting this bug to us as 'my whole device reboots' > because they see the white apple on black screen appear, but now we know > that due to the high memory pressure Springboard is also simply killed, > which looks like a reboot) This bug report is about a server request that is unexpected. You are describing a crash, which is a completely different issue from what is being reported. Please file a new bug with steps to reproduce and, ideally, a crashlog. Iraé, here's why I don't think this is a bug. > 1. Go to page A, that implements history.pushState and history.replaceState. Let's call URL-A "http://foo.com/index.html" The WebView has only ever loaded a single document - DOC-A The back/forward list has one entry: URL-A (DOC-A) > 2. Page A uses replaceState to store current page, creating PageA-state1 The WebView has still only ever loaded a single document - DOC-A The back/forward list now has one entry: URL-A (DOC-A, with state object A1) > 3. Navigate internally using a link, that uses pushState... Let's say the pushState is to URL-B "http://foo.com/bar.html" The WebView has still only ever loaded a single document - DOC-A The back/forward list now has two entries: URL-A (DOC-A, with state object A1), URL-B (DOC-A, with state object B1) >...does Ajax request then uses replaceState to create PageA-state2 The WebView has still only ever loaded a single document - DOC-A The back/forward list now has two entries: URL-A (DOC-A, with state object A1), URL-B (DOC-A, with state object B2) > 4. Navigate to a different document. Let's call the navigation to URL-C "http://webkit.org/" The WebView has now navigated to TWO different documents, DOC-A and DOC-C The back/forward list now has three entries: URL-A (DOC-A, with state object A1), URL-B (DOC-A, with state object B2), URL-C (DOC-C) At this point in time, DOC-A might no longer exist. If DOC-A was eligible to go into the page cache, then it still exists in a suspended state. But if something about DOC-A rendered it ineligible, it is gone. Forever. > After a while, or after "minimizing Safari"... "After awhile" might mean, say, 3 hours. At this point, even if DOC-A were still suspended in the page cache, we would've invalidated it. It is too old for us to confidently resume. Or, if you minimize Safari and then go launch some other apps that use memory on your device, WebKit would get a low memory warning and purge DOC-A, since it is not obviously usable anymore. > ...press back. You are viewing URL-C (DOC-C), and you press back. If DOC-A still exists, the "back" command is telling WebKit "Resume DOC-A at URL-B, popping state object B2" But if anything has happened to purge DOC-A, then the "back" command is telling WebKit "Load URL-B, then pop state object B2" Your server is likely to actually get pinged for URL-B, since WebKit never actually loaded a URL-B - Remember the user originally visited "http://foo.com/index.html" yet now WebKit is being asked to load "http://foo.com/bar.html", which has never actually been loaded. This is why when people use pushState/replaceState it is important that they use real world URLs. Those URLs are not meant to be some app-internal placeholder, but rather actually URLs that might actually be hit in a network request. Does this explanation make sense to you? Is there anything else I can help clarify? |