This is an archived snapshot of W3C's public bugzilla bug tracker, decommissioned in April 2019. Please see the home page for more details.

Bug 12230 - Some synthetic events cause actions
Summary: Some synthetic events cause actions
Status: RESOLVED MOVED
Alias: None
Product: WHATWG
Classification: Unclassified
Component: HTML (show other bugs)
Version: unspecified
Hardware: Other All
: P3 normal
Target Milestone: Unsorted
Assignee: Ian 'Hixie' Hickson
QA Contact: contributor
URL: http://www.whatwg.org/specs/web-apps/...
Whiteboard: blocked awaiting data; see comment 38...
Keywords:
: 8672 10897 (view as bug list)
Depends on:
Blocks:
 
Reported: 2011-03-03 15:42 UTC by contributor
Modified: 2016-10-12 14:43 UTC (History)
22 users (show)

See Also:


Attachments

Description contributor 2011-03-03 15:42:11 UTC
Specification: http://www.whatwg.org/specs/web-apps/current-work/complete.html
Section: http://www.whatwg.org/specs/web-apps/current-work/#form-submission-algorithm

Comment:
From this algorithm it does not seem to follow that dispatching a synthetic
non-canceled submit event at a form causes it to be submitted. Yet that is
what browsers implement. So that forms have a default handler for submit
events should probably be split from steps that cause such an event to be
submitted.

Posted from: 88.131.66.80 by annevk@opera.com
User agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; en-us) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4
Comment 1 Anne 2011-03-04 09:43:32 UTC
The implication of the above is that the "trusted flag" of this event is irrelevant by the way.
Comment 2 Ian 'Hixie' Hickson 2011-03-04 20:54:59 UTC
Woah. That's weird. Why do browsers do that? That makes no sense.
Comment 3 Anne 2011-03-05 09:20:14 UTC
http://software.hixie.ch/utilities/js/live-dom-viewer/saved/866 also seems to be true for <a> (though not in Gecko). I am not sure why this works this way, but it makes sense to me. Adding some people that can maybe explain it.
Comment 4 Anne 2011-03-05 09:49:16 UTC
Ian pointed out that the reason it makes no sense is that the default action of an event is decided by the code that dispatches the event. The behavior here goes contrary to that.
Comment 5 Anne 2011-03-05 10:05:14 UTC
http://software.hixie.ch/utilities/js/live-dom-viewer/saved/867 is the same test for <form>. I tested both in IE9 and it seems that IE does do it for <a> but not for <form>. I guess that indicates that if everyone is willing this can still be changed.
Comment 6 Boris Zbarsky 2011-03-05 15:20:53 UTC
In Gecko, I think the submission is the default action of the submit event, because that makes it easy to handle onsubmit: you fire the "submit" event, the handlers run, then if the event's default is not prevented you submit the form.  You use your normal event handling codepaths, with no special magic needed.

This code predates the existence of trusted event flags in Gecko, and since we added them largely for security reasons there was no reason to check them here: the page can already submit the form programmatically whenever it wants by calling submit().  So preventing it from doing the same by dispatching a "submit" event really gains you nothing security-wise.

I doubt that web sites depend on this stuff, except insofar as the actual form submission should _look_ like the default action of the "submit" event in terms of how it's affected by preventDefault and in terms of when it happens.
Comment 7 Jacob Rossi [MSFT] 2011-03-07 18:13:44 UTC
DOM L3 Events states that "Most untrusted events should not trigger default actions, with the exception of click..."[1].

IE9 and Webkit match this behavior:

* synthetic click events on links cause navigation
* synthetic submit events on forms do not cause form submission

The HTML5 form submission algorithm matches D3E, IE9, and Webkit. 

In general, I think D3E is right that untrusted events should not induce default actions. The exception for click is mostly historical (because click is often used as an activation event).

I don't think this change is advised.

[1] http://dev.w3.org/2006/webapi/DOM-Level-3-Events/html/DOM3-Events.html#trusted-events
Comment 8 Anne 2011-03-07 18:26:39 UTC
So a) that is not really saying anything at all in a normative way and b) the way the events model works is that the code that dispatches the event also invokes the default handler. Otherwise why would dispatchEvent() have a return value?

But here the code that dispatches the event (author-provided code) is completely disjoint from the code with the default handler (browser-provided code).

I.e. from the events model you expect code to work like this:

if(target.dispatchEvent(ev))
  defaultHandler()
Comment 9 Boris Zbarsky 2011-03-07 18:47:23 UTC
At least in the case of Gecko, there are lots of things that can cause dispatch of a submit event, fwiw.
Comment 10 Jacob Rossi [MSFT] 2011-03-07 19:02:36 UTC
In general, yes, code that dispatches an event should also be the performer of the default actions. This is true for submit:

* If the browser is preparing to perform form submission, it dispatches a submit event with the default action of submitting the form. If preventDefault() is not called, then it completes the form submission.

* If script synthesizes a submit event dispatch, then it's up to the script to perform the default action. This can be acheived as you described:

if(myform.dispatchEvent(ev))
    myform.submit(); //HTML5 form submit alg says this doesn't cause another submit event

This is how D3E defines it and how Webkit/IE9 behave. A recurssive submit event doesn't occur b/c of the scripted-submit flag. So I don't see the issue with having the submit event as a step in the form submission algorithm since it accounts for scripted submits.

This is true for most events. However, click is an exception. Ideally, it wouldn't be, but changing it may have a compat impact. Hence why I say it's for historical reasons.

The only confusing point I see here is that D3E says "untrusted events must behave as if the Event.preventDefault() method had been called on that event."  This should not be interpreted that the return value of dispatchEvent() should always be false. Rather, it just means the UA doesn't invoke its default actions--that is left for the scripted dispatcher to implement for untrusted events.
Comment 11 Anne 2011-03-08 08:32:40 UTC
Given that Gecko does not follow links for synthetic click events dispatched on <a> I am skeptical it is an exception. Or needs to be one anyway.

(The language in DOM Level 3 Events is just confusing this issue in my opinion. It does not need to say anything here. This is up to specifications that define form submission and following links.)
Comment 12 Boris Zbarsky 2011-03-08 14:54:16 UTC
Click events on <a> are one of the few places where Gecko actually checks the trusted flag right now before performing a default action.
Comment 13 Jacob Rossi [MSFT] 2011-03-08 18:16:41 UTC
My understanding is that certain ATs use synthetic click events to activate elements. Compat w.r.t. accessibility is a good reason to have this exception. The fact that Gecko does not do this is not enough evidence to remove this exception. 

D3E says nothing of form submission or how link navigation occurs (contrary to what you suggest). In fact, the submit event isn't even in D3E:

"The change, submit, and reset events were removed [from D3E], since they were specific to HTML forms, and are specified in HTML5."

While it does cover the existance of UI events, it simply notes that such events will have UA specific default actions (form submission and link navigation are non-normative examples).

The rule of thumb that untrusted events do not invoke UA default actions is something intrinsic to events and thus does belong in D3E. 

The exception for click is purely an acknowledgement that this behavior exists and is used today. If you'd like a more comprehensive explanation, look back at the www-dom threads about why DOMActivate isn't typically used by ATs.
Comment 14 Boris Zbarsky 2011-03-08 18:36:44 UTC
Do they synthesize these click events via directly calling the DOM APIs, or via the browser's accessibility apis?
Comment 15 Jonas Sicking (Not reading bugmail) 2011-03-18 22:33:33 UTC
For what it's worth, I'd be willing to try to change the fact that we submit on untrusted events in Gecko. I agree that it is very strange behavior.
Comment 16 Ian 'Hixie' Hickson 2011-05-06 21:58:47 UTC
EDITOR'S RESPONSE: This is an Editor's Response to your comment. If you are satisfied with this response, please change the state of this bug to CLOSED. If you have additional information and would like the editor to reconsider, please reopen this bug. If you would like to escalate the issue to the full HTML Working Group, please add the TrackerRequest keyword to this bug, and suggest title and text for the tracker issue; or you may create a tracker issue yourself, if you are able to do so. For more details, see this document:
   http://dev.w3.org/html5/decision-policy/decision-policy.html

Status: Rejected
Change Description: no spec change
Rationale: 

Synthetic events dispatched using the DOM API should never cause browsers to trigger default behaviour. That is contrary to the DOM Events model.
Comment 17 Olli Pettay 2011-07-05 11:18:06 UTC
FYI, nowadays all major browser engines follow the link when
untrusted click is dispatched to <a>.
Comment 18 Michael[tm] Smith 2011-08-04 05:03:39 UTC
mass-moved component to LC1
Comment 19 Jonas Sicking (Not reading bugmail) 2011-08-22 20:24:49 UTC
I still think that events should be fired in response to actions. Firing events isn't what causes actions.

It would have been a much better and consistent solution for Gecko to make <a>.click() dispatch an untrusted event, but have the default action for that event be to follow the link.

That way calling .click() would follow the link, but calling .dispatchEvent would not.


For the vast majority of events we can't make calling .dispatchEvent execute the default action as .dispatchEvent simply doesn't provide enough data. Hence we should be consistent and never make .dispatchEvent execute the default action.
Comment 20 Olli Pettay 2011-08-23 12:03:58 UTC
(In reply to comment #19)
> It would have been a much better and consistent solution for Gecko to make
> <a>.click() dispatch an untrusted event, but have the default action for that
> event be to follow the link.
That is what Gecko does: click() dispatches mouse click event and the default
action is to follow the link. That is how all the browsers work.

> That way calling .click() would follow the link, but calling .dispatchEvent
> would not.
Er, what?


> 
> 
> For the vast majority of events we can't make calling .dispatchEvent execute
> the default action as .dispatchEvent simply doesn't provide enough data. Hence
> we should be consistent and never make .dispatchEvent execute the default
> action.
There are different kinds of events. Events which are notifications that
something has happened, like focus, and events which happen before default
handling happens, like click.
Comment 21 Jonas Sicking (Not reading bugmail) 2011-10-11 04:55:10 UTC
Actually, you're arguing that there should be three types of events:

1. Events which are strictly notifications
2. Events which are notifications that something is about to happen, but where
   the "something" can be cancelled. 
3. Events which carry a inherent default action which always happens when the
   event is submitted, but where the default action can be cancelled.

Examples of group 1 is things like "load" and "visibilitychange"

Examples of group 2 is things like "error" in indexedDB (cancels transaction unless cancelled), "downloading"/"checking" in AppCache (displays UI in the browser).

Examples of group 3 would be "click" and "submit".

I argue that we should abandon group 3 and let those events belong to group 2 instead. That way we have fewer types of events. Additionally all events would simply be some form of notification.
Comment 22 Anne 2014-05-20 15:10:46 UTC
*** Bug 8672 has been marked as a duplicate of this bug. ***
Comment 23 Anne 2014-05-20 15:10:49 UTC
*** Bug 10897 has been marked as a duplicate of this bug. ***
Comment 24 Anne 2014-05-20 15:14:32 UTC
This does not actually seem resolved.
Comment 25 Ian 'Hixie' Hickson 2014-05-20 17:01:39 UTC
The spec works as sicking describes in comment 19.

What's not resolved?

There's just two kinds of events. Events that can be canceled, and events that can't be canceled. Those that can be canceled are called from algorithms that then check if they were canceled, and act differently based on that information.

The concept of "default action" is just a very confusing way to describe this. Default actions aren't inherent to the actual dispatching of the event. They're inherent to the algorithm that dispatches the event. Something that happens in _response_ to an event isn't a default action, it's an event handler. UAs have no built-in event handlers.
Comment 26 Glenn Maynard 2014-05-20 17:09:54 UTC
(In reply to Ian 'Hixie' Hickson from comment #25)
> The spec works as sicking describes in comment 19.
> 
> What's not resolved?
> 
> There's just two kinds of events. Events that can be canceled, and events
> that can't be canceled. Those that can be canceled are called from
> algorithms that then check if they were canceled, and act differently based
> on that information.
> 
> The concept of "default action" is just a very confusing way to describe
> this. Default actions aren't inherent to the actual dispatching of the
> event. They're inherent to the algorithm that dispatches the event.
> Something that happens in _response_ to an event isn't a default action,
> it's an event handler. UAs have no built-in event handlers.

This is true, most of the platform behaves this way, and everything new on the platform should behave this way.  Like I said on IRC, we should never use the phrase "default action", since it leads to a lot of confusion.

But, click events don't behave this way, and at least in Firefox submit events don't either.  Dispatching a synthetic click event on an <a> does trigger navigation, and dispatchEvent needs a hook in the DOM spec to describe this.

We should call it something other than "default action". "Legacy support action"?  It should be a name that reflects the fact that it's for web compatibility use only, so if spec authors use this hook for new features, they can't claim they didn't realize it was only meant for web-compatibility...

I'm not sure about submit.  Firefox submits forms if you dispatch submit, but Chrome doesn't.
Comment 27 Anne 2014-05-21 08:23:16 UTC
Ian, either we need to remove the behavior from browsers or we need to enshrine it. I would like an open bug (e.g. this one) blocked on browsers figuring out whether it can be nuked. Otherwise it's going to come up in another two years and it'll take me time to find all the relevant information again.
Comment 28 Glenn Maynard 2014-05-21 14:07:14 UTC
I tried a couple things to see if this could be done in the current event model.  They failed, but I'll describe them to save others the time.

One way is to pretend that all HTMLAnchorElements create a listener on themselves on creation:

this.addEventListener("click", function(e) {
    setTimeout(function() {
        if(!e.defaultPrevented)
            this.click();
    }.bind(this), 0);
}.bind(this));

This fails if an earlier listener calls stopPropagation or stopImmediatePropagation, which shouldn't prevent the navigation.

Another is to do it early in capturing:

window.addEventListener("click", function(e) {
    if(!(e.target instanceof HTMLAnchorElement))
        return;
    setTimeout(function() {
        if(!e.defaultPrevented)
            e.target.click();
    }, 0);
}, true);

This can't be stopped by stopImmediatePropagation (as long as it's added by the UA before script has a chance to add any earlier listeners).  It fails if the <a> isn't inside the document.
Comment 29 Boris Zbarsky 2014-05-21 18:45:38 UTC
I think internally in Gecko it's basically a separate pass through the event target chain.  Here's what our docs/IDL say:

236   /**
237    * Called after the bubble phase of the system event group.
238    * The default handling of the event should happen here.
239    * @param aVisitor the visitor object which is used during post handling.
240    *
241    * @see EventDispatcher.h for documentation about aVisitor.
242    * @note Only EventDispatcher should call this method.
243    */
244   [noscript, nostdcall]
245   void PostHandleEvent(in EventChainPostVisitorRef aVisitor);
Comment 30 Ian 'Hixie' Hickson 2014-05-21 20:04:02 UTC
If you can follow a link by firing a synthetic event using dispatchEvent(), then that's a bug, and should be fixed in the browser. Same with form submission.

If there's a compat need to do this, we should seriously investigate how strong that need is. I'm not aware of any pages depending on this.
Comment 31 Glenn Maynard 2014-05-21 20:12:48 UTC
You know better than most that ugly intentional hacks for web-compatibility aren't bugs, they're just a reality of the platform.

I wrote pages that depended on this myself (before I understood why it was a bad idea), and I don't have much hope that I'm the only one.  But, if there's any chance that we can get rid of this, I'm all for it.
Comment 32 Olli Pettay 2014-05-21 20:18:00 UTC
(In reply to Ian 'Hixie' Hickson from comment #30)
> If you can follow a link by firing a synthetic event using dispatchEvent(),
> then that's a bug, and should be fixed in the browser.
Changing that behavior in browsers breaks sites,
and all the browsers have that behavior for "click"
Comment 33 Glenn Maynard 2014-05-21 20:46:12 UTC
(In reply to Boris Zbarsky from comment #29)
> I think internally in Gecko it's basically a separate pass through the event
> target chain.  Here's what our docs/IDL say:

I was hoping to find a way to express this in terms of something that can already be done in JS (which would avoid needing to make changes to DOM Events itself to handle this isolated case), but I don't see any way to do that.
Comment 34 Boris Zbarsky 2014-05-21 20:52:17 UTC
> but I don't see any way to do that.

Yeah, I don't either.
Comment 35 Ian 'Hixie' Hickson 2014-05-22 18:11:53 UTC
I'm surprised by the assertion that this is required for compat, because it has been my impression for some time that "dispatchEvent()" is a very esoteric feature that few people use.

Do you have any sample URLs of pages that depend on this?
Comment 36 Jonas Sicking (Not reading bugmail) 2014-05-22 18:42:58 UTC
It'd be good to get telemetry data from browsers on this. But we should make sure to separate "calls to .dispatchEvent()" from "calls to .click()/.submit()". In the gecko code I believe the two use the same code paths so we need to be careful to tell them apart as we only want to change the .dispatchEvent() behavior. Don't know if other implementations have the same setup.
Comment 37 Ian 'Hixie' Hickson 2014-06-10 00:04:15 UTC
What's the next step here? Should Anne and I just go ahead and spec this? Is a browser or other going to add telemetry?
Comment 38 Ian 'Hixie' Hickson 2014-08-28 23:14:06 UTC
smaug, foolip: any chance we can convince you to add telemetry here? This would be exclusively about catching instances where events triggered by dispatchEvent() subsequently cause direct action by the browser, such as following a link or submitting a form. (Ideally, separating each such action out into its own thing.)
Comment 39 Philip Jägenstedt 2014-08-29 13:16:52 UTC
The model in comment #8, "the code that dispatches the event also invokes the default handler", isn't exactly how things work in Blink/WebKit. Rather there's a (C++ virtual) defaultEventHandler that can be implemented on any type of node that may get invoked for any event that wasn't canceled, although I'm not sure about the details or exceptions.

I've verified that a.dispatchEvent(click) does the same things as a.click() in Blink, which is to follow the link. This is the problem I gather. In fact the script-exposed dispatchEvent() very quickly joins the same code path as the internal dispatchEvent(), so distinguishing them is hard. Blink hasn't implemented Event.isTrusted, maybe that would help in some cases.

I'm easily convinced when it comes to adding UseCounters, but I think Blink is quite far from the model in comment #8 so I'm not yet sure exactly which bits to measure in order to answer any questions meaningfully.

CC also Rick Byers, who knows much more about input events than I do.
Comment 40 Rick Byers 2014-08-29 15:21:22 UTC
I believe it's intentional that JS-dispatched events are handled almost exactly like real input-dispatched ones in blink (eg. I know libraries like fastClick used to struggle with some browsers where the 'click' they generate wasn't causing input fields to get focus).  Are there important differences with other browsers here?  We'd be open to changing blink in ways that improve compatibility.

The one restriction is that we identify sensitive actions (like opening popups, playing video) and do those only if the action is from real input.  Philip, this may not be immediately obvious from the code as we rely on static state (UserGestureIndicator) to tell us whether we're currently in the context of a true user action, as opposed to some 'trusted' bit that flows with the event.  I could see shifting this more to rely on isTrusted though.
Comment 41 Ian 'Hixie' Hickson 2014-08-29 19:03:02 UTC
Having dispatchEvent() cause UA actions is so, so far from the standard event
model. :-(
Comment 42 Philip Jägenstedt 2014-08-29 19:45:51 UTC
Right, what Blink/WebKit does is not very close to the spec. From comment #29 it seems like Gecko also does something not quite per spec.

Where does this leave us?
Comment 43 Glenn Maynard 2014-08-29 19:46:20 UTC
(In reply to Rick Byers from comment #40)
> I believe it's intentional that JS-dispatched events are handled almost
> exactly like real input-dispatched ones in blink (eg. I know libraries like
> fastClick used to struggle with some browsers where the 'click' they
> generate wasn't causing input fields to get focus).  Are there important
> differences with other browsers here?  We'd be open to changing blink in
> ways that improve compatibility.

Only a couple isolated events do this (click), though.

Ian was just asking for telemetry: how many pages are triggering UA behavior by synthesizing events, and for which events.  I'm guessing click is pretty common (I've written "fast click" workarounds for the Safari delayed click bug myself).  I don't remember what other events are like this, but maybe some of those are less common.
Comment 44 Rick Byers 2014-09-03 15:02:52 UTC
> Only a couple isolated events do this (click), though.

Ah, good - sorry for my misunderstanding.  I don't know the history here (largely we inherited it from WebKit).

> Ian was just asking for telemetry: how many pages are triggering UA behavior by
> synthesizing events, and for which events.  I'm guessing click is pretty common
> (I've written "fast click" workarounds for the Safari delayed click bug myself).
> I don't remember what other events are like this, but maybe some of those are
> less common.

I agree this would be good to know.  From a quick experiment it looks like we always go down the default event handling codepath (HTMLElement::defaultEventHandler), even for JS created events.  I'd guess we have lots of places where this triggers behavior accidentally, but I'm not sure how to separate the accidental behavior from intentional ones.  We could try an experiment with disabling this (except probably for click) and try to find sites that break.  Here are a few (or MANY) examples I see from the code (but haven't independently verified):
 - mouse events sent to slider thumb elements drag them
 - keypress events for the enter key trigger a click event
 - click events sent to a label element trigger a click on the corresponding control (eg. to toggle a checkbox).
Comment 45 Glenn Maynard 2014-09-03 16:24:09 UTC
(In reply to Rick Byers from comment #44)
> Ah, good - sorry for my misunderstanding.  I don't know the history here
> (largely we inherited it from WebKit).

My understanding could be wrong, too--from what you say below this probably needs a lot more testing.

> I agree this would be good to know.  From a quick experiment it looks like
> we always go down the default event handling codepath
> (HTMLElement::defaultEventHandler), even for JS created events.  I'd guess
> we have lots of places where this triggers behavior accidentally, but I'm
> not sure how to separate the accidental behavior from intentional ones.  We
> could try an experiment with disabling this (except probably for click) and
> try to find sites that break.

This might work for non-click, especially if Firefox/IE don't do it for these cases.

For click, you could instrument JS postMessage, and record when a "click" event is fired and not cancelled.  This would have some false positives (if it's fired on an element that doesn't do anything on click), but it'd probably be pretty close.

We could test what effect it has on fast-click scripts--if it breaks those that's probably a showstopper by itself for click.

(FWIW, I don't have much hope of removing this for click, but I agree with Ian that it's worth looking at.  It's a serious violation of the event model.  I'd be happy if we could remove all of the other ones, and make click the one special case.)

>  Here are a few (or MANY) examples I see from
> the code (but haven't independently verified):
>  - mouse events sent to slider thumb elements drag them
>  - keypress events for the enter key trigger a click event
>  - click events sent to a label element trigger a click on the corresponding
> control (eg. to toggle a checkbox).

If these do happen, it's a lot worse than I thought.  I'll try to test one or two of these tonight.
Comment 46 Rick Byers 2014-09-03 16:34:45 UTC
> If these do happen, it's a lot worse than I thought.  I'll try to test one or two of these tonight

Alright, let me know.  If you can't reproduce it then I can debug - maybe I missed something.  If you can reproduce it, then I can likely give you >30 more scenarios (I just picked these 3 mostly at random).  If Webkit/blink are different from other browsers here then it shouldn't be too hard to rip it out hopefully.
Comment 47 Ian 'Hixie' Hickson 2014-09-03 16:47:40 UTC
Fundamentally the problem is that "HTMLElement::defaultEventHandler" shouldn't exist in the first place.

One way to instrument this would be to flag events that are dispatched by dispatchEvent() with some internal bit, and then in HTMLElement::defaultEventHandler, if any action is actually taken, and the bit is set, log the event type and the element that was affected.
Comment 48 Boris Zbarsky 2014-09-03 17:04:50 UTC
> Fundamentally the problem is that "HTMLElement::defaultEventHandler" shouldn't
> exist in the first place.

In the DOM3 event model, default actions were modeled as event listeners in the system event group.  So a number of UAs implemented it pretty much like that; this includes Gecko and Blink apparently, and possibly others.

Then you unilaterally, as far as I can tell, decided to change what "default action" means in terms of processing model.  Which brings us to where we are today.
Comment 49 Olli Pettay 2014-09-03 17:09:22 UTC
(In reply to Ian 'Hixie' Hickson from comment #47)
> Fundamentally the problem is that "HTMLElement::defaultEventHandler"
> shouldn't exist in the first place.
That is really just an implementation detail (assuming it is still doing the same what was there ~10 years ago when I looked at webkit :)).
In Gecko EventTarget::PostHandleEvent is pretty similar, and
filtering out untrusted events there prevents them to trigger default action.
Comment 50 Alexey Proskuryakov 2014-09-03 18:09:36 UTC
In WebKit, the internal bit is essentially whether there is a native event inside the WebCore event. Keyboard event handling is mostly implemented in terms of platform events, so it's a no-op for simulated ones. Mouse event handling is a lot less uniform - we sometimes check for the platform event, and other times don't.

I do not understand what's wrong with defaultEventHandler - this model seems very clear and logical. Whether default event handlers respect synthetic events, and whether element.click is implemented in terms of events can be determined on a case by case basis without breaking the model.
Comment 51 Jonas Sicking (Not reading bugmail) 2014-09-03 21:38:48 UTC
(In reply to Boris Zbarsky from comment #48)
> Then you unilaterally, as far as I can tell, decided to change what "default
> action" means in terms of processing model.  Which brings us to where we are
> today.

Default action *should* mean "what the caller of dispatchEvent() does if dispatchEvent() returns true".

This hasn't always been spelled out well in the spec, and possibly even contradicted at some points, but it's the only thing that makes sense.

It's of course ok for implementations to have some form of HTMLElement::defaultEventHandler, as long as that's only triggered by implementation-calls to dispatchEvent() since then HTMLElement::defaultEventHandler effectively becomes an implementation detail.

This has been debated for quite a while in various forums. Maybe not the right ones, but this is definitely not a unilateral action.
Comment 52 Jonas Sicking (Not reading bugmail) 2014-09-03 21:44:52 UTC
(In reply to Alexey Proskuryakov from comment #50)
> I do not understand what's wrong with defaultEventHandler - this model seems
> very clear and logical. Whether default event handlers respect synthetic
> events, and whether element.click is implemented in terms of events can be
> determined on a case by case basis without breaking the model.

Determining it on a case-by-case basis is exactly what will cause inconsistent behavior which is very hard for authors to learn/remember.

It's much more simple if the model is "default actions is what the caller of dispatchEvent() does if dispatchEvent() returns true".

This way dispatchEvent() is *just* a notification mechanism, never an action mechanism. That is very easy to learn and remember for authors. It also enables them to implement their own events that work the same way that platform-provided events work.

Element.click() is a separate feature from event dispatch. It so happens that it causes events to be dispatched, just like XHR.send() does, however it is still a separate mechanism. This is why it's ok that Element.click() causes actions in addition to notifications.
Comment 53 Ian 'Hixie' Hickson 2014-09-03 21:51:14 UTC
(In reply to Boris Zbarsky from comment #48)
> 
> In the DOM3 event model, default actions were modeled as event listeners in
> the system event group.  So a number of UAs implemented it pretty much like
> that; this includes Gecko and Blink apparently, and possibly others.

Most of DOM3 Events was never really implemented. I'm surprised that this is where this comes from, if it is. (I don't think it is. I think it's more likely, given how it only affects a few specific events in certain specific scenarios, that it was the result of occasional misunderstandings by various engineers over the years, followed by the usual interop constraints leading other vendors to copy the behaviour.)


> Then you unilaterally, as far as I can tell, decided to change what "default
> action" means in terms of processing model.

I personally never specced anything to do with the events model as far as I recall. But in any case, my personal understanding of DOM Events is based on DOM1 and DOM2, which as far as I can tell works the same as Anne's DOM spec today.


(In reply to Jonas Sicking from comment #51)
> 
> Default action *should* mean "what the caller of dispatchEvent() does if
> dispatchEvent() returns true".
>
> This hasn't always been spelled out well in the spec, and possibly even
> contradicted at some points, but it's the only thing that makes sense.

Agreed.


Anyway, this is all mostly moot. My assumption is that we want to maintain the model described in the DOM spec (if we don't, then we should file a bug on the DOM spec to change the model). Given that assumption, and given the few deployed cases that don't match that model, we need to decide what to do going forward. There are two obvious directions we can go in:

 - spec the oddities in the specs
 - remove the oddities from the browsers

Which we do depends on how widespread the oddities are, and to determine that, we need data. Whence my question in comment 38.
Comment 54 Glenn Maynard 2014-09-03 23:05:51 UTC
(In reply to Glenn Maynard from comment #45)
> >  Here are a few (or MANY) examples I see from
> > the code (but haven't independently verified):
> >  - mouse events sent to slider thumb elements drag them
> >  - keypress events for the enter key trigger a click event
> >  - click events sent to a label element trigger a click on the corresponding
> > control (eg. to toggle a checkbox).

I couldn't produce any side-effects for the keydown case (https://zewt.org/~glenn/test-slider-script-events.html).  My test may be wrong, but comment #50 seems to say key events don't do this (assuming this is the same as in WebKit).  I wasn't really sure how to test the thumb case (fire mousemove while the user is holding the mouse button down).  Sorry, it's probably better for somebody referencing the source to find some test cases.

(I didn't test the click case since that's the big one we already know about.)

(In reply to Boris Zbarsky from comment #48)
> In the DOM3 event model, default actions were modeled as event listeners in
> the system event group.  So a number of UAs implemented it pretty much like
> that; this includes Gecko and Blink apparently, and possibly others.

Wouldn't that mean that stopPropagation/stopImmediatePropagation would prevent default behavior, since the system's event listener would never see it?

Anyhow, from what Alexey said, it sounds like WebKit only has issues with some mouse events.  Events that do this are the odd confusing exception, and I've had trouble finding any other than click.
Comment 55 Olli Pettay 2014-09-03 23:12:19 UTC
(In reply to Glenn Maynard from comment #54)

> (In reply to Boris Zbarsky from comment #48)
> > In the DOM3 event model, default actions were modeled as event listeners in
> > the system event group.  So a number of UAs implemented it pretty much like
> > that; this includes Gecko and Blink apparently, and possibly others.
> 
> Wouldn't that mean that stopPropagation/stopImmediatePropagation would
> prevent default behavior, since the system's event listener would never see
> it?

stop*Propagation works on group level.
So if web page calls stopPropagation, it affect event handling in default group only, not
in system group.
Comment 56 Boris Zbarsky 2014-09-03 23:44:10 UTC
> I'm surprised that this is where this comes from

Pretty sure it is in Gecko, unless I'm seriously misremembering the history of the code.
Comment 57 Olli Pettay 2014-09-04 00:17:25 UTC
System event group was added to Gecko 2002
https://bugzilla.mozilla.org/show_bug.cgi?id=124990#c0


(Though, before firefox 3, Gecko's event dispatching was really odd, but the
system group certainly was there.)
Comment 58 Rick Byers 2014-09-04 00:47:26 UTC
Sorry, the examples I gave were bad ones with more subtle interactions.

Here's a demo of a synthetic keydown Enter causing link navigation in Chrome (haven't tried other browsers yet - if they don't repro then we should obviously just fix this): http://jsbin.com/qaweq/1/edit

It looks like there's a few other similar cases where an enter keypress can cause click-like behavior (eg. on a <summary> element, on a form submit button).

Also here's a demo where a synthetic mousedown event over an input type=range element causes the slider thumb to move (and then respond to additional mouse movement events as if the mouse button was down): http://jsbin.com/tupewe/1.  This case definitely seems like a bug that is likely to be fixable.  But in general it's going to take a substantial amount of work to determine which of all these cases websites are actually relying on.
Comment 59 Glenn Maynard 2014-09-04 01:19:53 UTC
(In reply to Olli Pettay from comment #55)
> stop*Propagation works on group level.
> So if web page calls stopPropagation, it affect event handling in default
> group only, not
> in system group.

I see anything in the DOM3 Events spec about event groups and this is the first I've heard of it.  Anyway, events that act this way are the rare exceptions and the vast majority of events on the platform don't, so whatever the original intention, it's a poor description of the event model we have today.

(In reply to Rick Byers from comment #58)
> Sorry, the examples I gave were bad ones with more subtle interactions.
> 
> Here's a demo of a synthetic keydown Enter causing link navigation in Chrome
> (haven't tried other browsers yet - if they don't repro then we should
> obviously just fix this): http://jsbin.com/qaweq/1/edit

This doesn't work for me, but for other reasons: I get "Refused to display 'https://www.google.com/?gws_rd=ssl' in a frame because it set 'X-Frame-Options' to 'SAMEORIGIN'."  I tried changing the link to amazon.com, but then I get "Runner: Blocked a frame with origin "http://run.jsbin.com" from accessing a cross-origin frame."  I copied it here and it works: http://zewt.org/~glenn/test-synthetic-keydown.html

It doesn't do repro in Firefox (after changing initKeyboardEvent to initKeyEvent; I'm sure there's a great story behind how we interop failed at something that simple...).

> Also here's a demo where a synthetic mousedown event over an input
> type=range element causes the slider thumb to move (and then respond to
> additional mouse movement events as if the mouse button was down):
> http://jsbin.com/tupewe/1.  This case definitely seems like a bug that is
> likely to be fixable.

This doesn't repro for me in Firefox.

> But in general it's going to take a substantial
> amount of work to determine which of all these cases websites are actually
> relying on.

Hopefully things are a bit easier if there's zero interop on most of these, though.
Comment 60 Olli Pettay 2014-09-04 01:30:41 UTC
(In reply to Glenn Maynard from comment #59)
> I see anything in the DOM3 Events spec about event groups and this is the
> first I've heard of it.
You must be young or something ;) 
http://www.w3.org/TR/2003/NOTE-DOM-Level-3-Events-20031107/events.html#Event-groups
(Gecko never has had more than default and system groups, so it implemented a variant of what the old D3E draft had.)

> Anyway, events that act this way are the rare
> exceptions and the vast majority of events on the platform don't, so
> whatever the original intention, it's a poor description of the event model
> we have today.
I'm not saying several event groups is a good model for the web.
It works fine in Gecko but that is an internal implementation detail and 
doesn't affect to whatever we want to do with this bug.


(One thing to keep in mind is shadow dom. Will we want to let some shadow dom callbacks to
act as default action handlers? But even then, those could be called only in case of trusted events.)
Comment 61 Rick Byers 2014-09-04 01:53:39 UTC
> This doesn't work for me, but for other reasons: I get "Refused to display 'https://www.google.com/?gws_rd=ssl' in a frame because it set 'X-Frame-Options' to 'SAMEORIGIN'." 

Yes you can't use the "output" frame of jsbin, load the output in it's own window  (eg. by navigating to the output URL directly or clicking the pop-out button) and it should work.
Comment 62 Philip Jägenstedt 2014-09-25 08:17:42 UTC
This is currently blocked waiting for use counter data or similar, as requested in comment #38.

Rick, do you have a good grasp on which synthetic events will do something script-observable in Blink's defaultEventHandlers, and how to measure that?

I could figure out how to measure specifics like "dispatching a synthetic non-canceled submit event at a form causes it to be submitted" but I feel like there's a mismatch in model in spec and implementation and that only changing some instances could make things worse.
Comment 63 Rick Byers 2014-10-02 21:11:46 UTC
No, I'm sorry - I really don't have a good handle on this.  I just did a quick search to try to find a couple examples.

One possible next step would be for someone to do a more exhaustive/careful search of the blink code and make a list of possible cases, testing repros to confirm the behavior, and then either just trying to change it and see what breaks and/or adding use counters to try to see how often it's hit (depending on the specific scenario).

Another possible approach is to try refactoring the blink code to make this more explicitly and less likely to introduce new cases by accident.  That would be goodness in itself (even if we don't change any behavior, making it less likely to introduce new cases would be a big win I think).  Ideally that would leave us with a set of easily identifiable special cases that violate the spec, then we could incrementally try to tackle them one at a time (while locking in the wins).

Either way it'll require a bit of an investment.  I'm happy to review/approve CLs for this, but I'm afraid I don't have the time to drive it myself.


A related issue here is that we want to define some browser behavior to be like a  built-in event handler, not a default action.  Saying the browser behavior is the default action, and default actions are never invoked from synthetic events means we're explicitly putting the browser in a special place that's potentially inconsistent with the extensible web manifesto.  Ideally more browser behavior would be based on the same extensibility points available to JS frameworks (eg. so a browser default beahviour can be replaced by a custom version in JS without anyone noticing).  Of course there will always be 'trusted' cases that are special for security reasons, but I think that's the exception - not the common case.  See, for example, our beforescroll event proposal for an event where browser behavior is (roughly) defined as a handler, not a default action (of course much work is required to formally define this): https://docs.google.com/a/chromium.org/document/d/1oEVWIVdMZ2OlVZMvcZZ3IgaT6RAUNSKAzpzb9AlVeLw/edit#
Comment 64 Jonas Sicking (Not reading bugmail) 2014-10-02 23:31:07 UTC
Enabling pages to trigger actions that are triggered by other larger features is indeed usually a good idea, for the reasons enumerated in the extensible web manifest.

However enabling pages to trigger those actions by creating an event and calling dispatchEvent is very awkward.

Normally JS APIs are built using functions. Like "window.doStuff(5)". While possible to use a pattern like 

event = new CustomEvent("stuff");
event.detail = 5;
window.dispatchEvent(event);

it is quite awkward.

Instead define a JS function, and then define that this function is called as default action in response to some particular event.
Comment 65 Anne 2014-10-03 08:01:23 UTC
It's not just awkward, it doesn't match the prescribed events model. Which is that dispatchEvent() returns a boolean and based on that you might initiate a default action (or not). So the polyfill would always be along the lines of

  var ev = ...
  if(obj.dispatchEvent(ev))
    obj.defaultAction()

That just dispatching the event invokes obj.defaultAction() breaks the event model and is a longstanding bug for some events in browsers (this bug, indeed).
Comment 66 Philip Jägenstedt 2014-10-03 13:13:37 UTC
Thanks Rick! I also don't have time to devote to this, but if someone wants to do black-box testing and list which synthetic events can have observable side effects, I can try to add use counters for those. Refactoring Blink to internally use if (target->dispatchEvent(event)) { do_default_things(); } wherever possible sounds great, but I should really focus on fullscreen and media stuff :)
Comment 67 Rick Byers 2014-10-03 13:26:52 UTC
Anne/Jonas - Sorry, I didn't mean to pollute this important bug with a tangential discussion.  I'd really like your input here though - I've moved this discussion to www-style where we're discussing beforescroll: http://lists.w3.org/Archives/Public/www-style/2014Oct/0073.html
Comment 68 Travis Leithead [MSFT] 2014-10-15 17:58:04 UTC
(In reply to Philip Jägenstedt from comment #66)
> Thanks Rick! I also don't have time to devote to this, but if someone wants
> to do black-box testing and list which synthetic events can have observable
> side effects, I can try to add use counters for those. Refactoring Blink to
> internally use if (target->dispatchEvent(event)) { do_default_things(); }
> wherever possible sounds great, but I should really focus on fullscreen and
> media stuff :)

We found a case the other day in our site-compat testing for IE:

1. Open hbogo.com/activate on a phone with Chrome/webkit browser
2. Tap on the device choice prompt

Site dispatchEvent()'s a "mousedown" event to the <select> control, which causes it to open it's flyout. Doesn't happen on any other browser engine.

Note that <select>.click() doesn't do this either; should it?
Comment 69 Rick Byers 2015-08-14 15:03:33 UTC
We're now actively trying to fix blink to match the spec here (if we can convince ourself it's sufficiently web compatible): http://crbug.com/520519.

The main thing we're blocked on at the moment is having some other mechanism to allow select boxes to be opened from JS (since developers do sometimes rely on the default action synthetic of syntehtic mouse events for this in chromium).  If we can get some consensus on a simple API for that, then I think we can push this forward.  Please checkout the WICG thread and provide your input: http://discourse.wicg.io/t/htmlselectelement-add-ability-to-show-option-list-programmatically/1035
Comment 70 Rick Byers 2016-09-15 19:46:18 UTC
Chromium has now succeeded in shipping this breaking change to stable (in Chrome 53).  We now only run the default actions for trusted events, except for the case of 'click' which is special (see https://github.com/whatwg/dom/issues/325).
Comment 71 Travis Leithead [MSFT] 2016-09-16 15:12:28 UTC
(In reply to Rick Byers from comment #70)
> Chromium has now succeeded in shipping this breaking change to stable (in
> Chrome 53).  We now only run the default actions for trusted events, except
> for the case of 'click' which is special (see
> https://github.com/whatwg/dom/issues/325).

Nice! Hope this sticks. Thanks!
Comment 72 Rick Byers 2016-09-16 15:30:47 UTC
> Nice! Hope this sticks. Thanks!

It's been in Chrome stable now for a couple weeks, so it would take a major surprise for us to pull it out at this stage.
Comment 73 Anne 2016-10-12 14:43:57 UTC
Pretty close to fixing this in the standards, moving this to GitHub:

* https://github.com/whatwg/dom/issues/325
* https://github.com/whatwg/html/issues/1394

PR for DOM (HTML doesn't have a PR yet, just a plan):

* https://github.com/whatwg/dom/pull/342

We can continue the conversation there, to the extent this warrants further discussion.