Re: [XMLHttpRequest] Request for Last Call 2

"Anne van Kesteren" <annevk@opera.com> wrote:

> On Tue, 08 May 2007 15:18:55 +0200, Stewart Brodie  
> <stewart.brodie@antplc.com> wrote:
> >>> The send() event seems to have changed considerably since the previous
> >>> drafts that I saw. I think that you need more explanation for the
> >>> bizarre readystatechange event during step 5 of the send() algorithm
> >>> since, as the note points out, the state hasn't changed.
> >>
> >> This is matches what implementations do.
> >
> > Which implementations?  It doesn't match what my implementation does.
> 
> Internet Explorer, for one. Can't break significantly with that.

If the purpose of the XHR specification is simply to document the Internet
Explorer implementation, as determined empirically, then that's
disappointing.  Please say that this isn't so.


> > > > I do not understand why the transition to state SENT is so complex.
> > > > In my current implementation, calling send() (in such a way that no
> > > > errors occur) results in the state being set to SENT immediately and
> > > > a single readystatechange event being raised.
> >> That would be in error.
> > This is major change from the draft of February 27th which clearly
> > stated that readystatechange events are ONLY raised when the readyState
> > changes value.
> 
> In http://www.w3.org/TR/2007/WD-XMLHttpRequest-20070227/#dfn-readystate it
 
> says:
> 
>    2 Sent
>       The user agent successfully acknowledged the request.
> 
> This same text is used in the current draft. I therefore disagree with  
> your assertion that it changes behavior.

That's not the sentence I was commenting on.  The behaviour that has changed
is the issuing of this extra event, contrary to what the previous
specification said in several (at least 3) places, including these:

"readystatechange: The readystatechange event must be dispatched when
readyState changes value. [...]"

and

"When readyState changes value a readystatechange event is to be dispatched
on the XMLHttpRequest object."


Neither of these sentences, or anywhere else in that document, is it
suggested that readystatechange events may be raised at arbitrary times,
even when the state hasn't changed - quite the opposite, in fact.

But since this spurious behaviour has been seen on IE, it appears that we
are stuck with it.  What a shame.  I can't believe that scripts actually
rely on this, do they?


> > Plus, it is extremely inconvenient to have to issue multiple events in
> > succession, as it introduces a whole raft of re-entrancy issues which
> > weren't present before, since assuming I send this new extra
> > readystatechange event in state OPEN, the event handler may well call
> > abort().
> 
> The event dispatching is based on careful reverse engineering of the most 

> popular XMLHttpRequest implementation. So scripts would break that  
> implementation if they did such things.

If the Internet Explorer implementation crashes when faced with that
scenario, then that's a poor quality implementation.  But IE7 does not crash
- I don't know about earlier versions.  I have a test program that reports
the states each time the listener is invoked.  I have modified it such that
the second time it sees state 1, it calls abort().  I haven't found any two
browsers that behave the same, yet:

IE7: 1, 1 (and it's now in state 0)
Firefox 2: 1, 1, 4 (and it's now in state 0 too)
Opera: 1, 2, 3, 4

Obviously the version of Opera that I have (9.20) isn't issuing the second
event yet and so proceeds normally (as does my browser, for the same
reason).

If I wish to copy this IE bug, then that means I would have to carry out a
large scale re-engineering of the current implementation.  I'm not going to
do that right now - I see no compelling reason to change my implementation
to match this specification.


> > Furthermore, the steps for send() permit that event handler to call  
> > send() again! This must be a bug in the algorithm, surely?
> 
> I split that step in two. Setting the send() flag now happens directly  
> after the INVALID_STATE_ERR checks.

Yes, anywhere between steps 2 and 5 would do, and it makes sense to keep it
near the other use for the flag.  Does that happen for synchronous as well
as asynchronous requests?


> > I think you have to move the setting of the "If async is true, the user
> > agent MUST set the send() flag" into a new step between steps 4 and 5,
> > and update step 6 to "If the send() flag is "true" or the state is not
> > OPEN, return from the send() method call".
> 
> This would break synchronous requests.

Yes, it would - some other solution would be required.

I think you need to specify what happens if you call abort() from an event
listener that is currently handling one of these OPEN-during-send()
readystatechange events.  Obviously, you need to permit it to call abort(),
so that the listener can cancel network activity (although I'm not sure on
what basis it might be making that decision, as there's no properties in the
object that it can read back!)

By the rules for the abort() call, the abort() call in this scenario will
put the object back into UNSENT state.  Obviously, after abort(), the open()
becomes available again, and from there, send() too.

When given the sequence abort(), open() in the event listener, Firefox goes
through the following sequence: 1, 1, 4 (and then state becomes 0 - no
event), 1, 2, 3, 4.  IE goes 1, 1 (then state becomes 0 - no event).

Adding the send() call in there too confuses Firefox no end: it tries to go
the DONE state on the abort(), but the properties have all been reset.  IE
actually continues and fetches the new page, but never calls the
readystatechange event listener until it reaches the DONE state.


> > Even that is probably insufficient, come to think of it.  The event  
> > handler may do abort(), and then open() before returning, which would  
> > leave it in a valid state, but a completely different request.
> 
> That would reset the send flag().

A minor editorial point: the specification doesn't actually say that.  It is
possible to interpret the existing text as meaning that the value of the
send() flag can be derived with this expression: "return state == OPEN &&
send_flag == true".  I think that either open() step 20 should explicitly
clear the send() flag, or that in section 2's introduction of the send()
flag, it should explicitly say that changing the readyState always forcibly
resets the flag to false.


> >> However, it's not really clear what SENT exactly represents.
> >
> > It is not clear what "has been successfully acknowledged" means either.
> > Does it mean that the "URI accessing mechanism" of the user agent has
> > accepted the request for processing?

Please can somebody supply an explanation of the meaning of the phrase "has
been successfully acknowledged".  I have no idea what it means.


> > Looking at it from a slightly different angle, what triggers the send()
> > algorithm to continue executing after step 6 in the async==true case?
> 
> Nothing "triggers" it.

In that case, how does step 7 ever occur?  In asynchronous mode, the send()
method finishes executing and returns to its caller in step 6.  Surely you
are not mandating that the application must be multi-threaded in order to
use XHR in async mode and that at step 6 you MUST create a new thread for
the asynchronous request?  That would be just unimplementable for some
platforms.


> >> Someone suggested renaming it to REQUESTING or something, but it seems
> >> some (maybe more than some?) implementations just make sure they don't
> >> skip the state and basically go straight from OPEN to LOADING making  
> >> sure they do SENT just before LOADING...
> >
> > Yes, that's fine - I'm pleased that that is spelt out.
> 
> I don't understand what you mean.

Nothing :-)  I was simply indicating that I liked the way that specification
spelled out that you must go through all the states and not jump from SENT
to DONE, as that was one of my concerns about it when I first implemented
XHR years ago.


-- 
Stewart Brodie
Software Engineer
ANT Software Limited

Received on Tuesday, 8 May 2007 16:47:27 UTC