Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Capturing event listeners are called during bubbling phase for shadow hosts #685

Closed
rniwa opened this issue Sep 6, 2018 · 46 comments
Closed
Labels
topic: events topic: shadow Relates to shadow trees (as defined in DOM)

Comments

@rniwa
Copy link
Collaborator

rniwa commented Sep 6, 2018

We have a bug report saying that WebKit fires event listeners with capture flag set to true during bubbling phase.

Consider the following DOM:

const hostParent = document.createElement('section');
hostParent.id = 'hostParent';
hostParent.innerHTML = '<div id="host"></div>';
const host = hostParent.firstChild;
const shadowRoot = host.attachShadow({mode: 'closed'});
shadowRoot.innerHTML = '<p id="parent"><span id="target"></span></p>';
...
target.dispatchEvent(new CustomEvent('test', {detail: {}, bubbles: true, composed: true}));

Then the event listeners on target, parent, host, and hostParent are invoked in the following order on WebKit & Chrome:

  • hostParent, capturing, eventPhase: CAPTURING_PHASE
  • parent, capturing, eventPhase: CAPTURING_PHASE
  • target, capturing, eventPhase: AT_TARGET
  • target, non-capturing, eventPhase: AT_TARGET
  • parent, non-capturing, eventPhase: BUBBLING_PHASE
  • host, capturing, eventPhase: AT_TARGET
  • host, non-capturing, eventPhase: AT_TARGET
  • hostParent, non-capturing, eventPhase: BUBBLING_PHASE

As far as I can tell, the current behavior of WebKit & Chrome is in accordance with the latest DOM specification. What happens is that step 5 (event path construction) in dispatching an event ends up appending an tuple/struct (we should probably stick with either term, btw) with target set to null. In step 14, we only invoke event listeners with capture flag set if target is null, meaning that shadow hosts' event listeners with capture flag set would NOT be called. Then in step 15, we set eventPhase to AT_TARGET and invoke event listeners. Because the concept to inner invoke an event listener doesn't skip event listeners with capture flag set to true when the event phase is AT_TARGET, we end up firing capturing event listeners during bubbling.

@rniwa
Copy link
Collaborator Author

rniwa commented Sep 6, 2018

@annevk @hayatoito @smaug----

@rniwa
Copy link
Collaborator Author

rniwa commented Sep 6, 2018

I think we should update the spec to match what Gecko implemented which ends up invoking event listeners in the following order for the example:

  • hostParent, capturing, eventPhase: CAPTURING_PHASE
  • host, capturing, eventPhase: AT_TARGET
  • parent, capturing, eventPhase: CAPTURING_PHASE
  • target, capturing, eventPhase: AT_TARGET
  • target, non-capturing, eventPhase: AT_TARGET
  • parent, non-capturing, eventPhase: BUBBLING_PHASE
  • host, non-capturing, eventPhase: AT_TARGET
  • hostParent, non-capturing, eventPhase: BUBBLING_PHASE

@annevk annevk added topic: shadow Relates to shadow trees (as defined in DOM) topic: events labels Sep 6, 2018
annevk added a commit that referenced this issue Sep 6, 2018
As pointed out in #685 this was a bit confusing.
annevk added a commit that referenced this issue Sep 6, 2018
Instead of shoehorning all target handling into the bubbling iteration, this separates "capturing" iteration from "bubbling" iteration and the Event object's phase is set to target as appropriate in both.

This also invokes the event listeners in a more natural order.

Tests: ...

Fixes #685.
@annevk
Copy link
Member

annevk commented Sep 6, 2018

I agree that what Gecko does looks a lot better and I've created #686 to align with it. @stramel, I acknowledged you as "Michael Stramel". Please let me know if you'd prefer a different name there.

I'm somewhat surprised we don't have good test coverage for this.

@stramel
Copy link

stramel commented Sep 6, 2018

@annevk That works for me! Thanks!

@rniwa Thank you for the detailed explanation of what was happening and looking into it!

@hayatoito
Copy link
Member

hayatoito commented Sep 7, 2018

I remember there were some discussions around this area between @annevk and Dimitri (and maybe including me?), however, I don't recall it.

Let me try to find the discussions in the past. Maybe there is a reason for the current behavior.

So please don't proceed this until we could get more insights from the past discussions.

@annevk
Copy link
Member

annevk commented Sep 7, 2018

@hayatoito ah yes, #237 discusses it. It does seem this needs some more research.

@hayatoito
Copy link
Member

@annevk Thanks! That's exactly what I've tried to find. Let me take a look.

@rniwa
Copy link
Collaborator Author

rniwa commented Sep 7, 2018

Okay according to #237, we want to be invoking both capturing & bubbling event listeners during pebble phase.

Presumably, this was agreed to in order to let non-capturing event listeners inside the shadow tree to do the necessary work before the event dispatched outside the shadow tree.

However, it makes no sense whatsoever that the capturing event listeners on ancestors of a shadow host are getting invoked BEFORE capturing event listeners are invoked on the target inside a shadow tree.

@hayatoito
Copy link
Member

hayatoito commented Sep 7, 2018

It looks I've changed the behavior here, which was originally requested by @azakus (Daniel Freedman).

Maybe we would like to hear more opinions or feedback from web authors before changing the behavior because the current behavior has been there for years, and the change would be a breaking change for us, especially existing web components frameworks.

@azakus, we'd appreciate if you could share your insights.

@annevk
Copy link
Member

annevk commented Sep 7, 2018

So the reason we went with the current model as outlined in #237 is because once you reach a target, all registered listeners get invoked in order, regardless of their capture boolean value. Due to shadow trees creating multiple targets, targets outside the shadow tree would first get their capture listeners invoked, and then their bubble listeners. So you could observe there is a shadow tree underneath if an earlier registered bubble listener is invoked before a capture listener.

It would make some sense to me if we changed the model and always invoked capture listeners before bubble listeners, but I don't know how compatible that would be.

@annevk
Copy link
Member

annevk commented Sep 7, 2018

I.e., change the order in which these are invoked:

t = document.createElement("hi");
t.addEventListener("test", e => w("bubble"), false);
t.addEventListener("test", e => w("capture"), true);
t.dispatchEvent(new Event("test"));

Given that all browsers agree that might be somewhat unrealistic.

@annevk
Copy link
Member

annevk commented Sep 7, 2018

And if we don't want to change how that simple scenario works and we want to avoid leaking the existence of shadow trees, we're stuck with the model described in OP, however weird that may seem. (It's weird enough to warrant an explanation in the DOM Standard, at least.)

@hayatoito
Copy link
Member

I agree that new one is more natural. It looks I had the same opinion before.

I guess the only remaining concern here is how compatible that would be. We need to evaluate it somehow. I am now asking feedback for Polymer team.

From the implementation perspective, I don't see any difficulty to change the Blink's behavior.

@rniwa
Copy link
Collaborator Author

rniwa commented Sep 7, 2018

I agree this is a slight Web compatibility concern here given WebKit & Blink have been shipping like this for years but I think it's worth an attempt given how bad the current behavior is.

FWIW, if there is a rough consensus that the new behavior Anne specified in #686 is reasonable, then I can implement it in WebKit. People can play with it in the next STP, and we may find out any web compatibility issues that may come out.

@smaug----
Copy link
Collaborator

FWIW, Gecko has used the model where capturing listeners get call before bubble listeners on host
(where host can be for example input element) at least for 12 years.

@rniwa
Copy link
Collaborator Author

rniwa commented Sep 7, 2018

FWIW, Gecko has used the model where capturing listeners get call before bubble listeners on host
(where host can be for example input element) at least for 12 years.

As far as I can tell, this oddly ordered invocation of capturing event listener isn't observable when there is no author-defined shadow tree. So I think the compat risk with content which uses attachShadow is real since WebKit and Blink always shipped this odd event dispatching behavior.

@smaug----
Copy link
Collaborator

input element and such have native anonymous content in Gecko, so effectively behaving as host.
(it wasn't called host before shadow DOM, but that doesn't matter. XBL bound element is also host.)

@annevk
Copy link
Member

annevk commented Sep 7, 2018

That, coupled with the fact that I haven't seen any bug reports about that behavior, makes me somewhat hopeful we can actually ship capture-listeners-and-then-later-on-bubble-listeners for targets. 😊

@rniwa
Copy link
Collaborator Author

rniwa commented Sep 7, 2018

input element and such have native anonymous content in Gecko, so effectively behaving as host.
(it wasn't called host before shadow DOM, but that doesn't matter. XBL bound element is also host.)

Right, but if you can't see what's happening inside the shadow tree, the proposed new behavior & what WebKit & Blink implement are identical. Otherwise, we would have caught this difference much earlier.

@rniwa
Copy link
Collaborator Author

rniwa commented Sep 8, 2018

Okay, I finally understand what @annevk and @smaug---- are talking about. Yes, the order of invocations of event listeners on a given element would change from the registration order to capturing event listeners then bubbling event listeners.

But I'm a lot less concerned about that (especially now that you state Gecko has been doing this for years, and scripts don't typically register both capturing & bubbling event listeners on a single element) than the fact order of event dispatching on different elements would change. It's a lot more likely that some event listeners on different elements are relying on event listeners on another element getting events before/after them by stopping propagation, etc...

@hayatoito
Copy link
Member

If we all agree on the new behavior, and the spec is updated, I'll try to align the Blink's behavior to the new one on Chrome Canary.

Given it would be really difficult for us to predict what would be broken with this change, it would be worth trying this change on Chrome Canary as well as WebKit, and seeing what would be broken in wild.
If someone knows any better way to evaluate the impact of this change, please let us know that.

I'll add chromestatus entry for this change so that web developers can be noticed with this change.

It's a lot more likely that some event listeners on different elements are relying on event listeners on another element getting events before/after them by stopping propagation, etc...

Yes, that could be more likely.

@annevk
Copy link
Member

annevk commented Sep 10, 2018

I'd prefer to not update the specification until we've tried it at least via Safari Technology Preview or Chrome Canary. It doesn't seem worth making the change if we have to revert it months later. I do think we have reached agreement on this new model and by keeping the capture listeners split from the bubble listeners there's also no way to observe shadow trees (which is why we ended up with the weirder model the last time around; I guess we didn't consider changing the normal dispatch behavior enough).

@hayatoito
Copy link
Member

Okay, I think that's fine. Usually, Blink needs the (updated) spec to ship any web facing changes, however, this discussion can be used as justification to ship this change, I think.

However, at least, we might want WPT so that we can be sure our implementations are interoperable.
Can we have WPT without updating the spec?

@annevk
Copy link
Member

annevk commented Sep 10, 2018

I see, if we need to update tests, we should probably change the standard as well (they're best kept in sync), and then revert or change it to something else if we hit a roadblock.

@smaug---- @rniwa @dstorey any concerns?

@domenic
Copy link
Member

domenic commented Sep 10, 2018

You could also do a spec PR + .tentative.html tests.

@hayatoito
Copy link
Member

  • Chrome Canary will get this change in a few days.
  • Chrome M71 will get this change, roughly in the following schedule:
    • Chrome M71 beta will be out around the end of Oct
    • Chrome M71 Stable will be released around earlier in Dec

@annevk
Copy link
Member

annevk commented Sep 19, 2018

Filed bugs against the remaining browsers given how likely this is to happen:

moz-v2v-gh pushed a commit to mozilla/gecko-dev that referenced this issue Sep 20, 2018
…g phase at shadow hosts, a=testonly

Automatic update from web-platform-testsCall capture event listeners in capturing phase at shadow hosts

Chromestatus entry is here: https://www.chromestatus.com/feature/5636327009681408

Per the discussion of whatwg/dom#685, Blink will try to align the event
dispatch behavior with other browsers; Call capture event listeners in capturing phase at shadow
hosts.

So far, Blink and WebKit call capture event listeners in *bubbling* phase, instead of
*capturing* phase, at shadow hosts.

Other browsers:
- Safari: Will try to change the behavior in the next Safari Technical Preview.
- Firefox: Already implemented the new behavior
- Edge: Strong public support for the new behavior.

This change is guard by CallCaptureListenersAtCapturePhaseAtShadowHosts flag, which is disabled
at this moment, to confirm that this CL doesn't cause any behavior change when the flag is disabled.

This CL adds a wpt for new behavior, which is now marked as [Failure] in Blink.

After this CL lands, I will flip the flag in a follow-up CL, with rebasing a very few existing
tests.

BUG=883650

Change-Id: I29938840aed4f3430d8b749cd4843176b8668b5d
Reviewed-on: https://chromium-review.googlesource.com/1212255
Commit-Queue: Hayato Ito <hayato@chromium.org>
Reviewed-by: Kent Tamura <tkent@chromium.org>
Cr-Commit-Position: refs/heads/master@{#591939}

--

wpt-commits: 9d63cfd82f428a999fb3cacffb2f940faa6a6b64
wpt-pr: 13002
xeonchen pushed a commit to xeonchen/gecko-cinnabar that referenced this issue Sep 20, 2018
…g phase at shadow hosts, a=testonly

Automatic update from web-platform-testsCall capture event listeners in capturing phase at shadow hosts

Chromestatus entry is here: https://www.chromestatus.com/feature/5636327009681408

Per the discussion of whatwg/dom#685, Blink will try to align the event
dispatch behavior with other browsers; Call capture event listeners in capturing phase at shadow
hosts.

So far, Blink and WebKit call capture event listeners in *bubbling* phase, instead of
*capturing* phase, at shadow hosts.

Other browsers:
- Safari: Will try to change the behavior in the next Safari Technical Preview.
- Firefox: Already implemented the new behavior
- Edge: Strong public support for the new behavior.

This change is guard by CallCaptureListenersAtCapturePhaseAtShadowHosts flag, which is disabled
at this moment, to confirm that this CL doesn't cause any behavior change when the flag is disabled.

This CL adds a wpt for new behavior, which is now marked as [Failure] in Blink.

After this CL lands, I will flip the flag in a follow-up CL, with rebasing a very few existing
tests.

BUG=883650

Change-Id: I29938840aed4f3430d8b749cd4843176b8668b5d
Reviewed-on: https://chromium-review.googlesource.com/1212255
Commit-Queue: Hayato Ito <hayato@chromium.org>
Reviewed-by: Kent Tamura <tkent@chromium.org>
Cr-Commit-Position: refs/heads/master@{#591939}

--

wpt-commits: 9d63cfd82f428a999fb3cacffb2f940faa6a6b64
wpt-pr: 13002
@rniwa
Copy link
Collaborator Author

rniwa commented Nov 13, 2018

FWIW, we haven't gotten any bugs reports from this change.

@rniwa
Copy link
Collaborator Author

rniwa commented Feb 27, 2019

We've "shipped" this behavior in iOS 12.2 & macOS 10.14.4 betas, and I haven't heard of any issues yet. Chrome stable is also at M72. It seems like we can just merge this spec change now? The fact we have two browser engines successfully adopted this new behavior seems to indicate that the new behavior is quite Web compatible.

@hayatoito
Copy link
Member

+1 for that.

annevk added a commit that referenced this issue Mar 1, 2019
Instead of shoehorning all target handling into the bubbling iteration, this separates "capturing" iteration from "bubbling" iteration and the Event object's phase is set to target as appropriate in both.

This also invokes the event listeners in a more natural order.

Tests: ...

Fixes #685.
annevk added a commit that referenced this issue Mar 1, 2019
Instead of shoehorning all target handling into the bubbling iteration, this separates "capturing" iteration from "bubbling" iteration and the Event object's phase is set to target as appropriate in both.

This also invokes the event listeners in a more natural order.

Tests: web-platform-tests/wpt#13030 & web-platform-tests/wpt#13031.

Fixes #685.
@annevk
Copy link
Member

annevk commented Mar 1, 2019

I'll make it so, there's one wpt PR I pinged you both on that still needs review though.

annevk added a commit that referenced this issue Mar 1, 2019
As pointed out in #685 this was a bit confusing.
annevk added a commit that referenced this issue Mar 1, 2019
Instead of shoehorning all target handling into the bubbling iteration, this separates "capturing" iteration from "bubbling" iteration and the Event object's phase is set to target as appropriate in both.

This also invokes the event listeners in a more natural order.

Tests: web-platform-tests/wpt#13030 & web-platform-tests/wpt#13031.

Fixes #685.
@rniwa
Copy link
Collaborator Author

rniwa commented Mar 1, 2019

@annevk which PR needs reviewing??

@annevk
Copy link
Member

annevk commented Mar 1, 2019

@rniwa web-platform-tests/wpt#13030.

@rniwa
Copy link
Collaborator Author

rniwa commented Mar 1, 2019

Reviewed.

cdumez pushed a commit to web-platform-tests/wpt that referenced this issue Apr 5, 2019
moz-v2v-gh pushed a commit to mozilla/gecko-dev that referenced this issue Jun 5, 2019
…ed before non capturing ones, a=testonly

Automatic update from web-platform-tests
Capturing event listeners should be called before non capturing ones (#16230)

as per whatwg/dom#685.
--

wpt-commits: e4a98db0bb16cdbddeb2ae4a58d4bea99237cd42
wpt-pr: 16230
mykmelez pushed a commit to mykmelez/gecko that referenced this issue Jun 6, 2019
…ed before non capturing ones, a=testonly

Automatic update from web-platform-tests
Capturing event listeners should be called before non capturing ones (#16230)

as per whatwg/dom#685.
--

wpt-commits: e4a98db0bb16cdbddeb2ae4a58d4bea99237cd42
wpt-pr: 16230
marcoscaceres pushed a commit to web-platform-tests/wpt that referenced this issue Jul 23, 2019
gecko-dev-updater pushed a commit to marco-c/gecko-dev-wordified-and-comments-removed that referenced this issue Oct 3, 2019
…g phase at shadow hosts, a=testonly

Automatic update from web-platform-testsCall capture event listeners in capturing phase at shadow hosts

Chromestatus entry is here: https://www.chromestatus.com/feature/5636327009681408

Per the discussion of whatwg/dom#685, Blink will try to align the event
dispatch behavior with other browsers; Call capture event listeners in capturing phase at shadow
hosts.

So far, Blink and WebKit call capture event listeners in *bubbling* phase, instead of
*capturing* phase, at shadow hosts.

Other browsers:
- Safari: Will try to change the behavior in the next Safari Technical Preview.
- Firefox: Already implemented the new behavior
- Edge: Strong public support for the new behavior.

This change is guard by CallCaptureListenersAtCapturePhaseAtShadowHosts flag, which is disabled
at this moment, to confirm that this CL doesn't cause any behavior change when the flag is disabled.

This CL adds a wpt for new behavior, which is now marked as [Failure] in Blink.

After this CL lands, I will flip the flag in a follow-up CL, with rebasing a very few existing
tests.

BUG=883650

Change-Id: I29938840aed4f3430d8b749cd4843176b8668b5d
Reviewed-on: https://chromium-review.googlesource.com/1212255
Commit-Queue: Hayato Ito <hayatochromium.org>
Reviewed-by: Kent Tamura <tkentchromium.org>
Cr-Commit-Position: refs/heads/master{#591939}

--

wpt-commits: 9d63cfd82f428a999fb3cacffb2f940faa6a6b64
wpt-pr: 13002

UltraBlame original commit: 662dc6a1623435428099a27a8f4afcaac10d2776
gecko-dev-updater pushed a commit to marco-c/gecko-dev-comments-removed that referenced this issue Oct 3, 2019
…g phase at shadow hosts, a=testonly

Automatic update from web-platform-testsCall capture event listeners in capturing phase at shadow hosts

Chromestatus entry is here: https://www.chromestatus.com/feature/5636327009681408

Per the discussion of whatwg/dom#685, Blink will try to align the event
dispatch behavior with other browsers; Call capture event listeners in capturing phase at shadow
hosts.

So far, Blink and WebKit call capture event listeners in *bubbling* phase, instead of
*capturing* phase, at shadow hosts.

Other browsers:
- Safari: Will try to change the behavior in the next Safari Technical Preview.
- Firefox: Already implemented the new behavior
- Edge: Strong public support for the new behavior.

This change is guard by CallCaptureListenersAtCapturePhaseAtShadowHosts flag, which is disabled
at this moment, to confirm that this CL doesn't cause any behavior change when the flag is disabled.

This CL adds a wpt for new behavior, which is now marked as [Failure] in Blink.

After this CL lands, I will flip the flag in a follow-up CL, with rebasing a very few existing
tests.

BUG=883650

Change-Id: I29938840aed4f3430d8b749cd4843176b8668b5d
Reviewed-on: https://chromium-review.googlesource.com/1212255
Commit-Queue: Hayato Ito <hayatochromium.org>
Reviewed-by: Kent Tamura <tkentchromium.org>
Cr-Commit-Position: refs/heads/master{#591939}

--

wpt-commits: 9d63cfd82f428a999fb3cacffb2f940faa6a6b64
wpt-pr: 13002

UltraBlame original commit: 662dc6a1623435428099a27a8f4afcaac10d2776
gecko-dev-updater pushed a commit to marco-c/gecko-dev-wordified that referenced this issue Oct 3, 2019
…g phase at shadow hosts, a=testonly

Automatic update from web-platform-testsCall capture event listeners in capturing phase at shadow hosts

Chromestatus entry is here: https://www.chromestatus.com/feature/5636327009681408

Per the discussion of whatwg/dom#685, Blink will try to align the event
dispatch behavior with other browsers; Call capture event listeners in capturing phase at shadow
hosts.

So far, Blink and WebKit call capture event listeners in *bubbling* phase, instead of
*capturing* phase, at shadow hosts.

Other browsers:
- Safari: Will try to change the behavior in the next Safari Technical Preview.
- Firefox: Already implemented the new behavior
- Edge: Strong public support for the new behavior.

This change is guard by CallCaptureListenersAtCapturePhaseAtShadowHosts flag, which is disabled
at this moment, to confirm that this CL doesn't cause any behavior change when the flag is disabled.

This CL adds a wpt for new behavior, which is now marked as [Failure] in Blink.

After this CL lands, I will flip the flag in a follow-up CL, with rebasing a very few existing
tests.

BUG=883650

Change-Id: I29938840aed4f3430d8b749cd4843176b8668b5d
Reviewed-on: https://chromium-review.googlesource.com/1212255
Commit-Queue: Hayato Ito <hayatochromium.org>
Reviewed-by: Kent Tamura <tkentchromium.org>
Cr-Commit-Position: refs/heads/master{#591939}

--

wpt-commits: 9d63cfd82f428a999fb3cacffb2f940faa6a6b64
wpt-pr: 13002

UltraBlame original commit: 662dc6a1623435428099a27a8f4afcaac10d2776
gecko-dev-updater pushed a commit to marco-c/gecko-dev-comments-removed that referenced this issue Oct 4, 2019
…ed before non capturing ones, a=testonly

Automatic update from web-platform-tests
Capturing event listeners should be called before non capturing ones (#16230)

as per whatwg/dom#685.
--

wpt-commits: e4a98db0bb16cdbddeb2ae4a58d4bea99237cd42
wpt-pr: 16230

UltraBlame original commit: de9c8a0db6ca877075bcae553f872eff273cdc66
gecko-dev-updater pushed a commit to marco-c/gecko-dev-wordified that referenced this issue Oct 4, 2019
…ed before non capturing ones, a=testonly

Automatic update from web-platform-tests
Capturing event listeners should be called before non capturing ones (#16230)

as per whatwg/dom#685.
--

wpt-commits: e4a98db0bb16cdbddeb2ae4a58d4bea99237cd42
wpt-pr: 16230

UltraBlame original commit: de9c8a0db6ca877075bcae553f872eff273cdc66
gecko-dev-updater pushed a commit to marco-c/gecko-dev-wordified-and-comments-removed that referenced this issue Oct 4, 2019
…ed before non capturing ones, a=testonly

Automatic update from web-platform-tests
Capturing event listeners should be called before non capturing ones (#16230)

as per whatwg/dom#685.
--

wpt-commits: e4a98db0bb16cdbddeb2ae4a58d4bea99237cd42
wpt-pr: 16230

UltraBlame original commit: de9c8a0db6ca877075bcae553f872eff273cdc66
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
topic: events topic: shadow Relates to shadow trees (as defined in DOM)
Development

No branches or pull requests

7 participants