Closed Bug 1084399 Opened 5 years ago Closed 5 years ago

XMLHttpRequests aborted when they should not be

Categories

(Core :: DOM: Core & HTML, defect)

36 Branch
defect
Not set

Tracking

()

RESOLVED INVALID

People

(Reporter: mirko.tschaeni, Unassigned)

References

()

Details

Attachments

(1 file, 1 obsolete file)

Attached file Isolated reproduction of the issue (obsolete) —
User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.78.2 (KHTML, like Gecko) Version/7.0.6 Safari/537.78.2

Steps to reproduce:

* start a  XMLHttpRequest request to a url that will only respond after a while (i.e. 3 seconds, will also work with fast response but is harder to reproduce)
* attach an error listener
* assign document.location.href to an url that will send a response that  is not rendered in the browser window but a save as dialog is shown to the user (i.e. content-type application/x-tar or with a content-disposition: attachment header)


Actual results:

The still pending ajax request is aborted by the browser. My error listener is called and the status of the request is 0 (request canceled).


Expected results:

The request should not be aborted because the page is not unloading. Also any succeeding  ajax requests work normally, only the ones that are pending at the time the document.location.href assignment takes place are canceled.
Firefox should wait until it the response reaches the browser, check whether the content will be rendered in the browser or if a save as dialog will be shown. In the browser-> unload the current page, abort all pending requests... ; save as -> do oohing in the current browsing context
Component: Untriaged → DOM
Product: Firefox → Core
Per spec, https://html.spec.whatwg.org/multipage/browsers.html#navigating-across-documents step 12 does https://html.spec.whatwg.org/multipage/browsers.html#abort-a-document before any request is even sent.  Step 2 of that algorithm cancels the XHR.
Status: UNCONFIRMED → RESOLVED
Closed: 5 years ago
Resolution: --- → INVALID
This case should be treated according to the spec, https://html.spec.whatwg.org/multipage/browsers.html#navigating-across-documents step 10 which then does https://html.spec.whatwg.org/multipage/browsers.html#hand-off-to-external-software.

A resource that is not rendered in the browser window (i.e. content-disposition: attachment) does not affect the browsing context. The save as dialog should be regarded as a hand off to external software.

@Boris: If this case was treated according to #navigating-across-documents step 12 and then #abort-a-document, then the current browsing context should actually be aborted. This is NOT the case however.
Only pending ajax requests (and possibly other instances of the fetch algorithm, not tested) are aborted. Besides from that the browsing context remains unaffected (new ajax requests are processed normally, no unloading takes place).

I think that the abortion of pending ajax requests is premature in this situation. The user agent can not yet decide whether the process will actually result in a navigation (-> abort the document) or in a hand off to external software (leave the current browsing context untouched).
Status: RESOLVED → UNCONFIRMED
Resolution: INVALID → ---
Step 10 is for when you know _before_ you've done the HTTP request that you won't be handling the result yourself. Something like <a download> for example.

But in this case, we don't know that, because you explicitly said that the response is what allows us to determine that we can't render it, not the request context.  So we do reach step 12 of the algorithm.

The actual HTTP request for the new page happens in step 15, which is very much after step 12.

> then the current browsing context should actually be aborted

Yes.  And it is.

> new ajax requests are processed normally, no unloading takes place

Yes.  Please read the definition of "abort" that I explicitly linked to in comment 1.  It just says to stop any fetch instances in progress at the time of the abort and to terminate parsing if the document is still being parsed.  It doesn't affect fetch instances that start _after_ the abort.

Basically, "abort" is what happens when the user hits the "stop" button in their browser UI.

> I think that the abortion of pending ajax requests is premature in this situation.

If you think the spec should change, please do feel free to raise a spec issue here.  But it's worth checking what other browsers do here and why the spec says what it does first.

If the spec does end up changing, please reopen the bug, of course.
Status: UNCONFIRMED → RESOLVED
Closed: 5 years ago5 years ago
Resolution: --- → INVALID
(In reply to Boris Zbarsky [:bz] from comment #3)
Thanks for your detailed answer. I understand your argumentation, however i still think some action is required here.

Reason1: Other browsers (Chrome starting with version 36 and IE at least since version 9) handle this situation differently (they will not abort the document until the response headers are known)
Reason2: This might actually be a spec issue

I think this behaviour (abort the document as soon as document.location.href is assigned) leads to situations that are hard to deal with properly and are impossible to detect. For a javascript inside the current browsing context the abort comes with no detectable reason, so it must either treat it like a random error or guess the reason. There is no event or state that lets the script detect that the abort is due to a (potentially) up coming navigation. 

In case of a download, the navigation will never happen (because the location.href assignment resulted in a save as dialog, not in a navigation). In this case the current browsing context will live on normally, any javascript inside this page (whether from the primary author of the page or from a third party) must "somehow" deal with the seemingly random XMLHttpRequest abort in the middle of document life cycle. 

> Step 10 is for when you know _before_ you've done the HTTP request that you
> won't be handling the result yourself. Something like <a download> for
> example.

You are right, that's before the request is sent. "Something like <a download> for example" is confusing however: 1. because whether a request will result in a download or not can only be told when the response is received. 2. because the problem we are facing are actually caused by downloads.

> Yes.  Please read the definition of "abort" that I explicitly linked to in
> comment 1.  It just says to stop any fetch instances in progress at the time
> of the abort and to terminate parsing if the document is still being parsed.
> It doesn't affect fetch instances that start _after_ the abort.

You are right, that's what's in the spec. I think this is a spec issue:
The specified abort algorithm might have made sense in traditional html pages that do not  use javascript excessively. With web sites that are mostly  built from javascript, this does not make sense (because canceling fetches and parsers will not acctually stop/abort the document). To the javascript this just looks like a random error during the execution of a XMLHttpRequest. There is no other indication that the document is aborted. I think for modern web applications the abort algorithm should be extended by some event and by stopping javascript execution. 
In the spec this algorithm is called "Aborting a document load" which COULD be read in a way that aborting is only possible for documents that have not yet fully loaded. In the case that i describe, the "Aborting a document load" algorithm is invoked also for documents that are fully loaded.


@Boris: what is your personal opinion about this?
> leads to situations that are hard to deal with properly and are impossible to detect.

How are these situations different from the user hitting the stop button?

> because the location.href assignment resulted in a save as dialog, not in a navigation

Note that we have a longstanding request to add a "go ahead and load in the browser tab where this load started" option to that "save as" dialog, by the way.  So you're not out of the woods even if that dialog has come up.

> 1. because whether a request will result in a download or not can only be told when the
> response is received

I think you misunderstood the example.  I'm talking about:

   <a download="myfile.txt" href="http://something/here">Click me to download</a>

which will always do a download, to filename myfile.txt.  This is known before you start the HTTP request, so would be covered by step 10.

> I think this is a spec issue:

Then I suggest raising it on the spec mailing list.

> @Boris: what is your personal opinion about this?

My personal opinion is that I refuse to make any changes to the already-complicated document unload procedure without at a minimum careful and exhaustive testing across browsers to determine what behavior they have, because adding a new behavior here that no browser has right now is pretty much guaranteed to lead to web compat issues.
OS: Mac OS X → All
Hardware: x86 → All
Attached file illegalAjaxAborts.tar
An improved and more detaild test application that tests request aborts (ajax, image and websocket) in different situation. The test application is also available online here: http://lab-browserillegalrequestabort-1.unblu.com/.
Attachment #8506930 - Attachment is obsolete: true
I have implemented a better test application that tests in detail, which requests are aborted and when. Using this test application (available as an attachment as well as online at http://lab-browserillegalrequestabort-1.unblu.com/) I have testet a selection of major browsers (Chrome, Internet Explorer, Firefox and Safari as well as other webkit based browsers) in the most recent versions as well as some older versions (i.e. Firefox 24).

The test application covers a wide range of variations and the results relevant for Firefox are consistent accross all variations, platforms and tested versions.

The application tests with different kinds of requests (XMLHttpRequest, image and WebSocket)
The application tests whether requests are aborted during step 12 of https://html.spec.whatwg.org/multipage/browsers.html#navigating-across-documents 
The application tests during different phases of the document life cycle (loading, loaded)
The application tests using same origin resources as well as x-origin resources


Firefox aborts all pending request in all variations as soon as a new value is assigned to the document location.

The majority of browsers (Chrome and InternetExplorer) DO NOT abort any requests in the same scenario.

WebKit does also abort requests just like firefox while webkit behaves less consistently (websockets are not aborted, it is not possible to start new requests in the time after the location assignment (which makes things even worse)).

Please also read trough: http://lab-browserillegalrequestabort-1.unblu.com/info.html (description of the test application).
Please note that the same issue has also been reported with webkit under https://bugs.webkit.org/show_bug.cgi?id=137817
Status: RESOLVED → UNCONFIRMED
Resolution: INVALID → ---
websockets are not considered fetch instances (nor is eventsource); the fact that we're aborting them in step 12 is bug 896666.

If you think the spec should change here, please raise a spec issue, so all stakeholders are able to participate.
Status: UNCONFIRMED → RESOLVED
Closed: 5 years ago5 years ago
Resolution: --- → INVALID
Ok, will file a spec bug then.
I have filed a spec issue here: https://www.w3.org/Bugs/Public/show_bug.cgi?id=27347.
The related spec issue (https://www.w3.org/Bugs/Public/show_bug.cgi?id=27347) has been updated with additional tests.

Now the test suite also tests whether a navigation inside a nested browsing context (iframe) is aborted when the parent browsing contexts starts a navigation.

Now the test suite can also run the tests cases inside an iframe, to test whether the navigation on the parent browsing context will have the same effect on the requests (ajax, image, iframe and websocket) inside a nested browsing context.
Component: DOM → DOM: Core & HTML
You need to log in before you can comment on or make changes to this bug.