Bug 16332 - stop propagation and canceled flags
stop propagation and canceled flags
Status: RESOLVED FIXED
Product: WebAppsWG
Classification: Unclassified
Component: DOM3 Events
unspecified
PC Windows NT
: P2 normal
: ---
Assigned To: Travis Leithead [MSFT]
public-webapps-bugzilla
http://lists.w3.org/Archives/Public/w...
: LC, needsReview
Depends on:
Blocks:
  Show dependency treegraph
 
Reported: 2012-03-12 20:41 UTC by Travis Leithead [MSFT]
Modified: 2012-04-20 00:31 UTC (History)
3 users (show)

See Also:


Attachments
Tests re-dispatching a trusted event and whether the stop-propagation flags are reset (1019 bytes, text/html)
2012-04-19 18:39 UTC, Travis Leithead [MSFT]
Details
Tests re-dispatching a trusted event and whether the default-action-prevention state flags are reset (1.08 KB, text/html)
2012-04-19 18:49 UTC, Travis Leithead [MSFT]
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Travis Leithead [MSFT] 2012-03-12 20:41:32 UTC
PORTING separate issues from TRACKER, issue 186...
[Copied from http://lists.w3.org/Archives/Public/www-dom/2011JanMar/0054.html]

http://dev.w3.org/2006/webapi/DOM-Level-3-Events/html/DOM3-Events.html#event-flow  
says "As the final step of the event dispatch, for reasons of backwards  
compatibility, the implementation must reset the event object's  
internal-propagation and default-action-prevention states."

As far as I can tell this is not implemented this way. Propagation is not  
reset in either Opera or WebKit (it is in Gecko).

Load the following in  
http://software.hixie.ch/utilities/js/live-dom-viewer/ to test:

<!DOCTYPE html>
...<script>
   var e = document.createEvent("Event")
   e.initEvent("test", false, true)
   document.addEventListener("test", function() { w("invoked") }, false)
   e.stopPropagation();
   w("d1: " + document.dispatchEvent(e))
   w("d2: " + document.dispatchEvent(e))
</script>

Similarly, in Opera, WebKit or Gecko the canceled flag does not appear to  
be reset. Load the following in the same viewer to test:

<!DOCTYPE html>
...<script>
   function callback(ev) {
     if("defaultPrevented" in ev)
       w("ev: " + ev.defaultPrevented)
     else
       w("ev: " + ev.getPreventDefault())
   }
   var e = document.createEvent("Event")
   e.initEvent("test", false, true)
   document.addEventListener("test", callback, false)
   e.preventDefault();
   w("d1: " + document.dispatchEvent(e))
   w("d2: " + document.dispatchEvent(e))
</script>

So it appears that there is no backwards compatibility issue here.

Having said that, something to reset these flags might be nice so event  
objects can be reused. Or do we always want people to create new event  
objects? Maybe invoking initEvent() should reset these flags? As invoking  
initEvent() can also change the nature of it being cancelable.

Kind regards,


-- 
Anne van Kesteren
http://annevankesteren.nl/
Comment 1 Travis Leithead [MSFT] 2012-03-12 20:48:14 UTC
[see http://lists.w3.org/Archives/Public/www-dom/2011JanMar/0063.html]

Tue, 01 Mar 2011 21:01:04 +0100, Bjoern Hoehrmann <derhoermi@gmx.net>  
wrote:
> * Anne van Kesteren wrote:
>> http://dev.w3.org/2006/webapi/DOM-Level-3-Events/html/DOM3-Events.html#event-flow
>> says "As the final step of the event dispatch, for reasons of backwards
>> compatibility, the implementation must reset the event object's
>> internal-propagation and default-action-prevention states."
>
> This is the result of issues 20 and 35 in the former WebAPI Working
> Group where you will find test cases and data and opinions on this.

http://www.w3.org/2006/webapi/track/issues/35 has a public view and does  
seem to reach a similar conclusion. Namely that behavior is not very much  
the same and we can probably do "whatever". It does not explain, as far as  
I can tell, "for reasons of backwards compatibility".

I have also read through  
http://www.w3.org/2005/06/tracker/webapi/issues/20 and  
http://www.w3.org/2005/06/tracker/webapi/issues/35 and the few emails  
associated with them. They also seem to reach similar conclusions.


> I do note that in order to adequately test this you would also have
> to check, say, implementation-generated events versus synthesized
> ones, and modifying these states from within handlers rather than
> before dispatching them the first time. Feel free to be inspired by
> the member-only test code I submitted for these issues if they are
> useful.

Both Glenn and I have played around with that as well, getting much the  
same result as you back then.


>> As far as I can tell this is not implemented this way. Propagation is  
>> not reset in either Opera or WebKit (it is in Gecko).
>
>> So it appears that there is no backwards compatibility issue here.
>
> Clearly if Firefox behaves in a certain way here then doing some-
> thing else might break deployed code that works today in Firefox,
> so it would seem there is indeed a compatibility issue there.

Between browsers, yes. But "backwards compatibility" is generally used as  
a term meaning the "wider web relies on this behavior" which I do not  
think is established here, especially since Gecko does not do this for the  
"canceled flag".

The "canceled flag" is the most problematic I think. I would prefer it if  
defaultPrevented remains to make sense after the event is dispatched. But  
given that most browsers do not reset these flags it seems we can get away  
with only unsetting them when initEvent() is invoked.


>> Having said that, something to reset these flags might be nice so event
>> objects can be reused. Or do we always want people to create new event
>> objects? Maybe invoking initEvent() should reset these flags? As  
>> invoking initEvent() can also change the nature of it being cancelable.
>
> I would regard it as a bad practise to use the same event object
> multiple times; semantically event objects are event instances,
> and an instance of an event is unique. And you would be relying on
> hidden state, which is unreliable, as you can see here. Do note
> that these questions have also been discussed in the issues above,
> at least as I recall them.

Unsetting these flags would not just be for re-dispatching, but would also  
allow you to reconfigure it before dispatching has taken place at all. I  
agree re-dispatching is dubious, but we have to define how it works.


I think based on the information in the older issues that we should change  
the model so that these three flags are only ever reset for initEvent()  
and otherwise never. I will change DOM Core to match that. I hope that  
works for everyone.


-- 
Anne van Kesteren
http://annevankesteren.nl/
Comment 2 Travis Leithead [MSFT] 2012-03-24 00:12:24 UTC
(In reply to comment #0)

> Load the following in  
> http://software.hixie.ch/utilities/js/live-dom-viewer/ to test:
> 
> <!DOCTYPE html>
> ...<script>
>    var e = document.createEvent("Event")
>    e.initEvent("test", false, true)
>    document.addEventListener("test", function() { w("invoked") }, false)
>    e.stopPropagation();
>    w("d1: " + document.dispatchEvent(e))
>    w("d2: " + document.dispatchEvent(e))
> </script>

Consistent in all browsrs but Firefox...

> 
> Similarly, in Opera, WebKit or Gecko the canceled flag does not appear to  
> be reset. Load the following in the same viewer to test:
> 
> <!DOCTYPE html>
> ...<script>
>    function callback(ev) {
>      if("defaultPrevented" in ev)
>        w("ev: " + ev.defaultPrevented)
>      else
>        w("ev: " + ev.getPreventDefault())
>    }
>    var e = document.createEvent("Event")
>    e.initEvent("test", false, true)
>    document.addEventListener("test", callback, false)
>    e.preventDefault();
>    w("d1: " + document.dispatchEvent(e))
>    w("d2: " + document.dispatchEvent(e))
> </script>

IE has true/false values flipped, all other browsers are the same.
Comment 3 Travis Leithead [MSFT] 2012-04-19 18:39:30 UTC
Created attachment 1120 [details]
Tests re-dispatching a trusted event and whether the stop-propagation flags are reset
Comment 4 Travis Leithead [MSFT] 2012-04-19 18:49:53 UTC
Created attachment 1121 [details]
Tests re-dispatching a trusted event and whether the default-action-prevention state flags are reset
Comment 5 Travis Leithead [MSFT] 2012-04-19 18:50:21 UTC
(In reply to comment #2)
> (In reply to comment #0)
> 
> > Load the following in  
> > http://software.hixie.ch/utilities/js/live-dom-viewer/ to test:
> > 
> > <!DOCTYPE html>
> > ...<script>
> >    var e = document.createEvent("Event")
> >    e.initEvent("test", false, true)
> >    document.addEventListener("test", function() { w("invoked") }, false)
> >    e.stopPropagation();
> >    w("d1: " + document.dispatchEvent(e))
> >    w("d2: " + document.dispatchEvent(e))
> > </script>
> 
> Consistent in all browsrs but Firefox...

Meaning that in all browsers but Firefox, the object's "internal-propagation" flag is _not_ reset between dispatches, contrary to what DOM3 Events states. It also means that stopPropagation() can take effect before the event has been initially dispatched.

Note: When re-dispatching trusted events (see attached "TestingTrustedDispatchAndStopPropagation") I see the following results:
IE: (untestable) since it throws NotSupportedError as required by DOM3 Events under "dispatchEvent" (the Event was not created using document.createEvent).
Chrome/Safari/Opera: dispatches the event, and the state was not reset, just like the un-trusted scenario.
Firefox: dispatches the event, and the state is reset (just as it behaved before).

> > Similarly, in Opera, WebKit or Gecko the canceled flag does not appear to  
> > be reset. Load the following in the same viewer to test:
> > 
> > <!DOCTYPE html>
> > ...<script>
> >    function callback(ev) {
> >      if("defaultPrevented" in ev)
> >        w("ev: " + ev.defaultPrevented)
> >      else
> >        w("ev: " + ev.getPreventDefault())
> >    }
> >    var e = document.createEvent("Event")
> >    e.initEvent("test", false, true)
> >    document.addEventListener("test", callback, false)
> >    e.preventDefault();
> >    w("d1: " + document.dispatchEvent(e))
> >    w("d2: " + document.dispatchEvent(e))
> > </script>
> 
> IE has true/false values flipped, all other browsers are the same.

(Meaning that in all browsers the "default-action-prevention" state is _not_ reset between dispatches, contrary to what is stated by DOM3 Events. It also means that IE has a bug wherein it does not honor preventDefault() prior to dispatching an event.)

[The "true" return value for dispatchEvent in IE can be partially explained by a critical reading of the prose under "Return Value" for dispatch event (** emphasis mine**):

| "Indicates whether **any of the listeners which handled the event**
|  called Event.preventDefault(). If Event.preventDefault() was called
|  the returned value must be false, else it must be true."

...however, the value of defaultPrevented inside of the handler being "false" is clearly a bug without any backing from existing prose in DOM3 Events. :-)]

Note: When re-dispatching trusted events (see attached "TestingTrustedDispatchAndPreventDefault") I see the following results:
IE: (untestable) since it throws NotSupportedError as required by DOM3 Events under "dispatchEvent" (the Event was not created using document.createEvent).
Firefox/Chrome/Safari/Opera: dispatches the event, and the state was not reset, just like the un-trusted scenario.
Comment 6 Travis Leithead [MSFT] 2012-04-19 18:56:26 UTC
Based on the result from Comment 5, DOM3 Events needs to be updated in two places for clarification:

(In reply to comment #0)
> says "As the final step of the event dispatch, for reasons of backwards  
> compatibility, the implementation must reset the event object's  
> internal-propagation and default-action-prevention states."
> 
> As far as I can tell this is not implemented this way. Propagation is not  
> reset in either Opera or WebKit (it is in Gecko).

The above spec comment needs to be removed from the event dispatch algorithm. Something to its effect should be placed in the requirements for initEvent which will be the only time that these internal state values will be reset.

(In reply to comment #5)
> [The "true" return value for dispatchEvent in IE can be partially explained by
> a critical reading of the prose under "Return Value" for dispatch event (**
> emphasis mine**):
> 
> | "Indicates whether **any of the listeners which handled the event**
> |  called Event.preventDefault(). If Event.preventDefault() was called
> |  the returned value must be false, else it must be true."
> 
> ...however, the value of defaultPrevented inside of the handler being "false"
> is clearly a bug without any backing from existing prose in DOM3 Events. :-)]

The prose under the Return Value for dispatchEvent will be tweaked to indicate that preventDefault should be honored even if the caller used preventDefault _before_ the event was dispatched (which is what implementations allow).
Comment 7 Anne 2012-04-19 22:57:50 UTC
Do you know what the new text will be like relative to DOM4? The same effect?
Comment 8 Travis Leithead [MSFT] 2012-04-20 00:31:24 UTC
I've made the changes. I updated initEvent to describe the process of resetting the internal flags. This should match DOM4's behavior.