diff --git a/dom/base/FragmentOrElement.cpp b/dom/base/FragmentOrElement.cpp index f75bb0b490ad..372788bb41e6 100644 --- a/dom/base/FragmentOrElement.cpp +++ b/dom/base/FragmentOrElement.cpp @@ -1091,7 +1091,11 @@ nsIContent::GetEventTargetParent(EventChainPreVisitor& aVisitor) // point to a node in Shadow DOM. aVisitor.mEvent->mTarget = aVisitor.mTargetInKnownToBeHandledScope; return; - } else { + } else if (targetInKnownToBeHandledScope) { + // Note, if targetInKnownToBeHandledScope is null, + // mTargetInKnownToBeHandledScope could be Window object in content + // page and we're in chrome document in the same process. + // Step 11.6 aVisitor.mRetargetedRelatedTarget = retargetedRelatedTarget; } diff --git a/dom/events/EventDispatcher.cpp b/dom/events/EventDispatcher.cpp index eda2a5f31a5c..255bf616fe9f 100644 --- a/dom/events/EventDispatcher.cpp +++ b/dom/events/EventDispatcher.cpp @@ -656,6 +656,26 @@ MayRetargetToChromeIfCanNotHandleEvent( return nullptr; } +static bool +ShouldClearTargets(WidgetEvent* aEvent) +{ + nsCOMPtr finalTarget; + nsCOMPtr finalRelatedTarget; + if ((finalTarget = do_QueryInterface(aEvent->mTarget)) && + finalTarget->SubtreeRoot()->IsShadowRoot()) { + return true; + } + + if ((finalRelatedTarget = + do_QueryInterface(aEvent->mRelatedTarget)) && + finalRelatedTarget->SubtreeRoot()->IsShadowRoot()) { + return true; + } + //XXXsmaug Check also all the touch objects. + + return false; +} + /* static */ nsresult EventDispatcher::Dispatch(nsISupports* aTarget, nsPresContext* aPresContext, @@ -836,6 +856,8 @@ EventDispatcher::Dispatch(nsISupports* aTarget, aEvent->mOriginalRelatedTarget = aEvent->mRelatedTarget; + bool clearTargets = false; + nsCOMPtr content = do_QueryInterface(aEvent->mOriginalTarget); bool isInAnon = content && content->IsInAnonymousSubtree(); @@ -859,6 +881,8 @@ EventDispatcher::Dispatch(nsISupports* aTarget, for (uint32_t i = 0; i < chain.Length(); ++i) { chain[i].PreHandleEvent(preVisitor); } + + clearTargets = ShouldClearTargets(aEvent); } else { // At least the original target can handle the event. // Setting the retarget to the |target| simplifies retargeting code. @@ -927,6 +951,9 @@ EventDispatcher::Dispatch(nsISupports* aTarget, for (uint32_t i = 0; i < chain.Length(); ++i) { chain[i].PreHandleEvent(preVisitor); } + + clearTargets = ShouldClearTargets(aEvent); + // Handle the chain. EventChainPostVisitor postVisitor(preVisitor); MOZ_RELEASE_ASSERT(!aEvent->mPath); @@ -951,15 +978,16 @@ EventDispatcher::Dispatch(nsISupports* aTarget, aEvent->mFlags.mDispatchedAtLeastOnce = true; // https://dom.spec.whatwg.org/#concept-event-dispatch - // Step 18 - // "If target's root is a shadow root, then set event's target attribute and - // event's relatedTarget to null." - nsCOMPtr finalTarget = do_QueryInterface(aEvent->mTarget); - if (finalTarget && finalTarget->SubtreeRoot()->IsShadowRoot()) { + // step 10. If clearTargets, then: + // 1. Set event's target to null. + // 2. Set event's relatedTarget to null. + // 3. Set event's touch target list to the empty list. + if (clearTargets) { aEvent->mTarget = nullptr; aEvent->mOriginalTarget = nullptr; aEvent->mRelatedTarget = nullptr; aEvent->mOriginalRelatedTarget = nullptr; + //XXXsmaug Check also all the touch objects. } if (!externalDOMEvent && preVisitor.mDOMEvent) { diff --git a/testing/web-platform/meta/dom/events/relatedTarget.window.js.ini b/testing/web-platform/meta/dom/events/relatedTarget.window.js.ini index cbe3c35452cd..222634b6cc93 100644 --- a/testing/web-platform/meta/dom/events/relatedTarget.window.js.ini +++ b/testing/web-platform/meta/dom/events/relatedTarget.window.js.ini @@ -1,4 +1,5 @@ [relatedTarget.window.html] - [Untitled] + prefs: [dom.webcomponents.shadowdom.enabled:true] + [Reset targets before activation behavior] expected: FAIL diff --git a/testing/web-platform/tests/dom/events/relatedTarget.window.js b/testing/web-platform/tests/dom/events/relatedTarget.window.js index d3632bb9e040..4f68550ad513 100644 --- a/testing/web-platform/tests/dom/events/relatedTarget.window.js +++ b/testing/web-platform/tests/dom/events/relatedTarget.window.js @@ -41,13 +41,15 @@ async_test(t => { async_test(t => { const shadowChild = shadow.appendChild(document.createElement("div")); - shadowChild.addEventListener("demo", t.step_func(() => document.body.appendChild(shadowChild))); + const shadowChild2 = shadow.appendChild(document.createElement("div")); + shadowChild2.addEventListener("demo", t.step_func(() => document.body.appendChild(shadowChild))); const event = new FocusEvent("demo", { relatedTarget: shadowChild }); - document.body.dispatchEvent(event); + shadowChild2.dispatchEvent(event); assert_equals(shadowChild.parentNode, document.body); assert_equals(event.target, null); assert_equals(event.relatedTarget, null); shadowChild.remove(); + shadowChild2.remove(); t.done(); }, "Reset if relatedTarget pointed to a shadow tree pre-dispatch");