summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFranklinDM <mrmineshafter17@gmail.com>2023-03-03 21:22:02 +0800
committerFranklinDM <mrmineshafter17@gmail.com>2023-03-04 21:32:09 +0800
commit3d82baddf33f6951140b17e0c01c3901b733f041 (patch)
treea53e8bd9246db8e95e4abf6c69889d90ae58ab68
parent60d420fe715cbdd8e2db5bad0a390312383ca0a3 (diff)
downloaduxp-3d82baddf33f6951140b17e0c01c3901b733f041.tar.gz
Issue #2135 - Bug 1413102: Ensure Shadow DOM boundaries are dealt properly in event handling
* RE: BasicEvents.h - our WidgetEvent is not movable (yet), so the change that requires that wasn't included. * Parts of this use code that was introduced in bug 1427511. For now, they were replaced with their equivalents.
-rw-r--r--dom/base/FragmentOrElement.cpp129
-rw-r--r--dom/base/ShadowRoot.cpp12
-rw-r--r--dom/base/nsContentUtils.cpp52
-rw-r--r--dom/base/nsContentUtils.h14
-rwxr-xr-xdom/events/Event.cpp40
-rwxr-xr-xdom/events/Event.h8
-rw-r--r--dom/events/EventDispatcher.cpp74
-rw-r--r--dom/events/EventDispatcher.h37
-rw-r--r--widget/BasicEvents.h3
9 files changed, 299 insertions, 70 deletions
diff --git a/dom/base/FragmentOrElement.cpp b/dom/base/FragmentOrElement.cpp
index 8cd4bafa70..7f287cb58d 100644
--- a/dom/base/FragmentOrElement.cpp
+++ b/dom/base/FragmentOrElement.cpp
@@ -797,6 +797,22 @@ FindChromeAccessOnlySubtreeOwner(nsIContent* aContent)
return aContent;
}
+already_AddRefed<nsINode>
+FindChromeAccessOnlySubtreeOwner(EventTarget* aTarget)
+{
+ nsCOMPtr<nsINode> node = do_QueryInterface(aTarget);
+ if (!node || !node->ChromeOnlyAccess()) {
+ return node.forget();
+ }
+
+ if (!node->IsContent()) {
+ return nullptr;
+ }
+
+ node = FindChromeAccessOnlySubtreeOwner(node->AsContent());
+ return node.forget();
+}
+
nsresult
nsIContent::GetEventTargetParent(EventChainPreVisitor& aVisitor)
{
@@ -817,24 +833,11 @@ nsIContent::GetEventTargetParent(EventChainPreVisitor& aVisitor)
// chrome access only subtree or if we are about to propagate out of
// a shadow root to a shadow root host.
((this == aVisitor.mEvent->mOriginalTarget &&
- !ChromeOnlyAccess()) || isAnonForEvents || GetShadowRoot())) {
+ !ChromeOnlyAccess()) || isAnonForEvents)) {
nsCOMPtr<nsIContent> relatedTarget =
do_QueryInterface(aVisitor.mEvent->AsMouseEvent()->mRelatedTarget);
if (relatedTarget &&
relatedTarget->OwnerDoc() == OwnerDoc()) {
-
- // In the web components case, we may need to stop propagation of events
- // at shadow root host.
- if (GetShadowRoot()) {
- nsIContent* adjustedTarget =
- Event::GetShadowRelatedTarget(this, relatedTarget);
- if (this == adjustedTarget) {
- aVisitor.SetParentTarget(nullptr, false);
- aVisitor.mCanHandle = false;
- return NS_OK;
- }
- }
-
// If current target is anonymous for events or we know that related
// target is descendant of an element which is anonymous for events,
// we may want to stop event propagation.
@@ -950,6 +953,104 @@ nsIContent::GetEventTargetParent(EventChainPreVisitor& aVisitor)
} else {
aVisitor.SetParentTarget(GetComposedDoc(), false);
}
+
+ if (!ChromeOnlyAccess() && !aVisitor.mRelatedTargetRetargetedInCurrentScope) {
+ // We don't support Shadow DOM in native anonymous content yet.
+ aVisitor.mRelatedTargetRetargetedInCurrentScope = true;
+ if (aVisitor.mEvent->mOriginalRelatedTarget) {
+ // https://dom.spec.whatwg.org/#concept-event-dispatch
+ // Step 3.
+ // "Let relatedTarget be the result of retargeting event's relatedTarget
+ // against target if event's relatedTarget is non-null, and null
+ // otherwise."
+ //
+ // This is a bit complicated because the event might be from native
+ // anonymous content, but we need to deal with non-native anonymous
+ // content there.
+ bool initialTarget = this == aVisitor.mEvent->mOriginalTarget;
+ nsCOMPtr<nsINode> originalTargetAsNode;
+ // Use of mOriginalTargetIsInAnon is an optimization here.
+ if (!initialTarget && aVisitor.mOriginalTargetIsInAnon) {
+ originalTargetAsNode =
+ FindChromeAccessOnlySubtreeOwner(aVisitor.mEvent->mOriginalTarget);
+ initialTarget = originalTargetAsNode == this;
+ }
+ if (initialTarget) {
+ nsCOMPtr<nsINode> relatedTargetAsNode =
+ FindChromeAccessOnlySubtreeOwner(aVisitor.mEvent->mOriginalRelatedTarget);
+ if (!originalTargetAsNode) {
+ originalTargetAsNode =
+ do_QueryInterface(aVisitor.mEvent->mOriginalTarget);
+ }
+
+ if (relatedTargetAsNode && originalTargetAsNode) {
+ nsINode* retargetedRelatedTarget =
+ nsContentUtils::Retarget(relatedTargetAsNode, originalTargetAsNode);
+ if (originalTargetAsNode == retargetedRelatedTarget &&
+ retargetedRelatedTarget != relatedTargetAsNode) {
+ // Step 4.
+ // "If target is relatedTarget and target is not event's
+ // relatedTarget, then return true."
+ aVisitor.IgnoreCurrentTarget();
+ // Old code relies on mTarget to point to the first element which
+ // was not added to the event target chain because of mCanHandle
+ // being false, but in Shadow DOM case mTarget really should
+ // point to a node in Shadow DOM.
+ aVisitor.mEvent->mTarget = aVisitor.mTargetInKnownToBeHandledScope;
+ return NS_OK;
+ }
+
+ // Part of step 5. Retargeting target has happened already higher
+ // up in this method.
+ // "Append to an event path with event, target, targetOverride,
+ // relatedTarget, and false."
+ aVisitor.mRetargetedRelatedTarget = retargetedRelatedTarget;
+ }
+ } else {
+ nsCOMPtr<nsINode> relatedTargetAsNode =
+ FindChromeAccessOnlySubtreeOwner(aVisitor.mEvent->mOriginalRelatedTarget);
+ if (relatedTargetAsNode) {
+ // Step 11.3.
+ // "Let relatedTarget be the result of retargeting event's
+ // relatedTarget against parent if event's relatedTarget is non-null,
+ // and null otherwise.".
+ nsINode* retargetedRelatedTarget =
+ nsContentUtils::Retarget(relatedTargetAsNode, this);
+ nsCOMPtr<nsINode> targetInKnownToBeHandledScope =
+ FindChromeAccessOnlySubtreeOwner(aVisitor.mTargetInKnownToBeHandledScope);
+ if (nsContentUtils::ContentIsShadowIncludingDescendantOf(
+ this, targetInKnownToBeHandledScope->SubtreeRoot())) {
+ // Part of step 11.4.
+ // "If target's root is a shadow-including inclusive ancestor of
+ // parent, then"
+ // "...Append to an event path with event, parent, null, relatedTarget,
+ // " and slot-in-closed-tree."
+ aVisitor.mRetargetedRelatedTarget = retargetedRelatedTarget;
+ } else if (this == retargetedRelatedTarget) {
+ // Step 11.5
+ // "Otherwise, if parent and relatedTarget are identical, then set
+ // parent to null."
+ aVisitor.IgnoreCurrentTarget();
+ // Old code relies on mTarget to point to the first element which
+ // was not added to the event target chain because of mCanHandle
+ // being false, but in Shadow DOM case mTarget really should
+ // point to a node in Shadow DOM.
+ aVisitor.mEvent->mTarget = aVisitor.mTargetInKnownToBeHandledScope;
+ return NS_OK;
+ } else {
+ // Step 11.6
+ aVisitor.mRetargetedRelatedTarget = retargetedRelatedTarget;
+ }
+ }
+ }
+ }
+ }
+
+ if (slot) {
+ // Inform that we're about to exit the current scope.
+ aVisitor.mRelatedTargetRetargetedInCurrentScope = false;
+ }
+
return NS_OK;
}
diff --git a/dom/base/ShadowRoot.cpp b/dom/base/ShadowRoot.cpp
index 1f7e8264d6..d4b63004ae 100644
--- a/dom/base/ShadowRoot.cpp
+++ b/dom/base/ShadowRoot.cpp
@@ -311,6 +311,8 @@ ShadowRoot::GetEventTargetParent(EventChainPreVisitor& aVisitor)
{
aVisitor.mCanHandle = true;
aVisitor.mRootOfClosedTree = IsClosed();
+ // Inform that we're about to exit the current scope.
+ aVisitor.mRelatedTargetRetargetedInCurrentScope = false;
// https://dom.spec.whatwg.org/#ref-for-get-the-parent%E2%91%A6
if (!aVisitor.mEvent->mFlags.mComposed) {
@@ -333,12 +335,10 @@ ShadowRoot::GetEventTargetParent(EventChainPreVisitor& aVisitor)
nsIContent* shadowHost = GetHost();
aVisitor.SetParentTarget(shadowHost, false);
- if (aVisitor.mOriginalTargetIsInAnon) {
- nsCOMPtr<nsIContent> content(do_QueryInterface(aVisitor.mEvent->mTarget));
- if (content && content->GetBindingParent() == shadowHost) {
- aVisitor.mEventTargetAtParent = shadowHost;
- }
- }
+ nsCOMPtr<nsIContent> content(do_QueryInterface(aVisitor.mEvent->mTarget));
+ if (content && content->GetBindingParent() == shadowHost) {
+ aVisitor.mEventTargetAtParent = shadowHost;
+ }
return NS_OK;
}
diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp
index c964f0ce29..12ce07ec6b 100644
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -2248,6 +2248,34 @@ nsContentUtils::ContentIsHostIncludingDescendantOf(
return false;
}
+bool
+nsContentUtils::ContentIsShadowIncludingDescendantOf(
+ const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor)
+{
+ MOZ_ASSERT(aPossibleDescendant, "The possible descendant is null!");
+ MOZ_ASSERT(aPossibleAncestor, "The possible ancestor is null!");
+
+ if (aPossibleAncestor == aPossibleDescendant->GetComposedDoc()) {
+ return true;
+ }
+
+ do {
+ if (aPossibleDescendant == aPossibleAncestor) {
+ return true;
+ }
+
+ if (aPossibleDescendant->NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
+ ShadowRoot* shadowRoot =
+ ShadowRoot::FromNode(const_cast<nsINode*>(aPossibleDescendant));
+ aPossibleDescendant = shadowRoot ? shadowRoot->GetHost() : nullptr;
+ } else {
+ aPossibleDescendant = aPossibleDescendant->GetParentNode();
+ }
+ } while (aPossibleDescendant);
+
+ return false;
+}
+
// static
bool
nsContentUtils::ContentIsCrossDocDescendantOf(nsINode* aPossibleDescendant,
@@ -2266,6 +2294,30 @@ nsContentUtils::ContentIsCrossDocDescendantOf(nsINode* aPossibleDescendant,
return false;
}
+// static
+nsINode*
+nsContentUtils::Retarget(nsINode* aTargetA, nsINode* aTargetB)
+{
+ while (true && aTargetA) {
+ // If A's root is not a shadow root...
+ nsINode* root = aTargetA->SubtreeRoot();
+ // TODO: replace with IsShadowRoot() check once bug 1427511 lands.
+ if (!ShadowRoot::FromNode(root)) {
+ // ...then return A.
+ return aTargetA;
+ }
+
+ // or A's root is a shadow-including inclusive ancestor of B...
+ if (nsContentUtils::ContentIsShadowIncludingDescendantOf(aTargetB, root)) {
+ // ...then return A.
+ return aTargetA;
+ }
+
+ aTargetA = ShadowRoot::FromNode(root)->GetHost();
+ }
+
+ return nullptr;
+}
// static
nsresult
diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h
index eb67ff6a6a..a0c602f61a 100644
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -280,6 +280,13 @@ public:
const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor);
/**
+ * Similar to above, but does special case only ShadowRoot,
+ * not HTMLTemplateElement.
+ */
+ static bool ContentIsShadowIncludingDescendantOf(
+ const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor);
+
+ /**
* Similar to ContentIsDescendantOf except it crosses document boundaries,
* this function uses ancestor/descendant relations in the composed document
* (see shadow DOM spec).
@@ -287,6 +294,13 @@ public:
static bool ContentIsCrossDocDescendantOf(nsINode* aPossibleDescendant,
nsINode* aPossibleAncestor);
+ /**
+ * Retarget an object A against an object B
+ * @see https://dom.spec.whatwg.org/#retarget
+ */
+ static nsINode*
+ Retarget(nsINode* aTargetA, nsINode* aTargetB);
+
/*
* This method fills the |aArray| with all ancestor nodes of |aNode|
* including |aNode| at the zero index.
diff --git a/dom/events/Event.cpp b/dom/events/Event.cpp
index 864678dd66..7d7acb5e0a 100755
--- a/dom/events/Event.cpp
+++ b/dom/events/Event.cpp
@@ -148,6 +148,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Event)
tmp->mEvent->mCurrentTarget = nullptr;
tmp->mEvent->mOriginalTarget = nullptr;
tmp->mEvent->mRelatedTarget = nullptr;
+ tmp->mEvent->mOriginalRelatedTarget = nullptr;
switch (tmp->mEvent->mClass) {
case eDragEventClass: {
WidgetDragEvent* dragEvent = tmp->mEvent->AsDragEvent();
@@ -176,6 +177,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Event)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->mCurrentTarget)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->mOriginalTarget)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->mRelatedTarget)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->mOriginalRelatedTarget)
switch (tmp->mEvent->mClass) {
case eDragEventClass: {
WidgetDragEvent* dragEvent = tmp->mEvent->AsDragEvent();
@@ -1227,44 +1229,6 @@ Event::SetOwner(EventTarget* aOwner)
#endif
}
-// static
-nsIContent*
-Event::GetShadowRelatedTarget(nsIContent* aCurrentTarget,
- nsIContent* aRelatedTarget)
-{
- if (!aCurrentTarget || !aRelatedTarget) {
- return nullptr;
- }
-
- // Walk up the ancestor node trees of the related target until
- // we encounter the node tree of the current target in order
- // to find the adjusted related target. Walking up the tree may
- // not find a common ancestor node tree if the related target is in
- // an ancestor tree, but in that case it does not need to be adjusted.
- ShadowRoot* currentTargetShadow = aCurrentTarget->GetContainingShadow();
- if (!currentTargetShadow) {
- return nullptr;
- }
-
- nsIContent* relatedTarget = aCurrentTarget;
- while (relatedTarget) {
- ShadowRoot* ancestorShadow = relatedTarget->GetContainingShadow();
- if (currentTargetShadow == ancestorShadow) {
- return relatedTarget;
- }
-
- // Didn't find the ancestor tree, thus related target does not have to
- // adjusted.
- if (!ancestorShadow) {
- return nullptr;
- }
-
- relatedTarget = ancestorShadow->GetHost();
- }
-
- return nullptr;
-}
-
NS_IMETHODIMP
Event::GetCancelBubble(bool* aCancelBubble)
{
diff --git a/dom/events/Event.h b/dom/events/Event.h
index 7e461a3f89..5e7678b356 100755
--- a/dom/events/Event.h
+++ b/dom/events/Event.h
@@ -251,14 +251,6 @@ public:
return mIsMainThreadEvent;
}
- /**
- * For a given current target, returns the related target adjusted with
- * shadow DOM retargeting rules. Returns nullptr if related target
- * is not adjusted.
- */
- static nsIContent* GetShadowRelatedTarget(nsIContent* aCurrentTarget,
- nsIContent* aRelatedTarget);
-
void MarkUninitialized()
{
mEvent->mMessage = eVoidEvent;
diff --git a/dom/events/EventDispatcher.cpp b/dom/events/EventDispatcher.cpp
index b708b03e97..9a48ca2a90 100644
--- a/dom/events/EventDispatcher.cpp
+++ b/dom/events/EventDispatcher.cpp
@@ -204,6 +204,16 @@ public:
mNewTarget = aNewTarget;
}
+ EventTarget* GetRetargetedRelatedTarget()
+ {
+ return mRetargetedRelatedTarget;
+ }
+
+ void SetRetargetedRelatedTarget(EventTarget* aTarget)
+ {
+ mRetargetedRelatedTarget = aTarget;
+ }
+
void SetForceContentDispatch(bool aForce)
{
mFlags.mForceContentDispatch = aForce;
@@ -354,6 +364,7 @@ public:
private:
nsCOMPtr<EventTarget> mTarget;
+ nsCOMPtr<EventTarget> mRetargetedRelatedTarget;
class EventTargetChainFlags
{
@@ -422,6 +433,7 @@ EventTargetChainItem::GetEventTargetParent(EventChainPreVisitor& aVisitor)
SetWantsPreHandleEvent(aVisitor.mWantsPreHandleEvent);
SetPreHandleEventOnly(aVisitor.mWantsPreHandleEvent && !aVisitor.mCanHandle);
SetRootOfClosedTree(aVisitor.mRootOfClosedTree);
+ SetRetargetedRelatedTarget(aVisitor.mRetargetedRelatedTarget);
mItemFlags = aVisitor.mItemFlags;
mItemData = aVisitor.mItemData;
}
@@ -454,6 +466,7 @@ EventTargetChainItem::HandleEventTargetChain(
{
// Save the target so that it can be restored later.
nsCOMPtr<EventTarget> firstTarget = aVisitor.mEvent->mTarget;
+ nsCOMPtr<EventTarget> firstRelatedTarget = aVisitor.mEvent->mRelatedTarget;
uint32_t chainLength = aChain.Length();
uint32_t firstCanHandleEventTargetIdx =
EventTargetChainItem::GetFirstCanHandleEventTargetIdx(aChain);
@@ -483,6 +496,30 @@ EventTargetChainItem::HandleEventTargetChain(
}
}
}
+
+ // https://dom.spec.whatwg.org/#dispatching-events
+ // Step 14.2
+ // "Set event's relatedTarget to tuple's relatedTarget."
+ // Note, the initial retargeting was done already when creating
+ // event target chain, so we need to do this only after calling
+ // HandleEvent, not before, like in the specification.
+ if (item.GetRetargetedRelatedTarget()) {
+ bool found = false;
+ for (uint32_t j = i; j > 0; --j) {
+ uint32_t childIndex = j - 1;
+ EventTarget* relatedTarget =
+ aChain[childIndex].GetRetargetedRelatedTarget();
+ if (relatedTarget) {
+ found = true;
+ aVisitor.mEvent->mRelatedTarget = relatedTarget;
+ break;
+ }
+ }
+ if (!found) {
+ aVisitor.mEvent->mRelatedTarget =
+ aVisitor.mEvent->mOriginalRelatedTarget;
+ }
+ }
}
// Target
@@ -511,6 +548,14 @@ EventTargetChainItem::HandleEventTargetChain(
aVisitor.mEvent->mTarget = newTarget;
}
+ // https://dom.spec.whatwg.org/#dispatching-events
+ // Step 15.2
+ // "Set event's relatedTarget to tuple's relatedTarget."
+ EventTarget* relatedTarget = item.GetRetargetedRelatedTarget();
+ if (relatedTarget) {
+ aVisitor.mEvent->mRelatedTarget = relatedTarget;
+ }
+
if (aVisitor.mEvent->mFlags.mBubbles || newTarget) {
if ((!aVisitor.mEvent->mFlags.mNoContentDispatch ||
item.ForceContentDispatch()) &&
@@ -542,6 +587,7 @@ EventTargetChainItem::HandleEventTargetChain(
// Retarget for system event group (which does the default handling too).
// Setting back the target which was used also for default event group.
aVisitor.mEvent->mTarget = firstTarget;
+ aVisitor.mEvent->mRelatedTarget = aVisitor.mEvent->mOriginalRelatedTarget;
aVisitor.mEvent->mFlags.mInSystemGroup = true;
HandleEventTargetChain(aChain,
aVisitor,
@@ -599,6 +645,7 @@ MayRetargetToChromeIfCanNotHandleEvent(
EventTargetChainItem::DestroyLast(aChain, aTargetEtci);
}
if (aPreVisitor.mAutomaticChromeDispatch && aContent) {
+ aPreVisitor.mRelatedTargetRetargetedInCurrentScope = false;
// Event target couldn't handle the event. Try to propagate to chrome.
EventTargetChainItem* chromeTargetEtci =
EventTargetChainItemForChromeTarget(aChain, aContent, aChildEtci);
@@ -762,9 +809,10 @@ EventDispatcher::Dispatch(nsISupports* aTarget,
aEvent->mOriginalTarget = aEvent->mTarget;
}
+ aEvent->mOriginalRelatedTarget = aEvent->mRelatedTarget;
+
nsCOMPtr<nsIContent> content = do_QueryInterface(aEvent->mOriginalTarget);
- bool isInAnon = (content && (content->IsInAnonymousSubtree() ||
- content->IsInShadowTree()));
+ bool isInAnon = (content && content->IsInAnonymousSubtree());
aEvent->mFlags.mIsBeingDispatched = true;
@@ -772,7 +820,7 @@ EventDispatcher::Dispatch(nsISupports* aTarget,
// GetEventTargetParent for the original target.
nsEventStatus status = aEventStatus ? *aEventStatus : nsEventStatus_eIgnore;
EventChainPreVisitor preVisitor(aPresContext, aEvent, aDOMEvent, status,
- isInAnon);
+ isInAnon, aEvent->mTarget);
targetEtci->GetEventTargetParent(preVisitor);
if (!preVisitor.mCanHandle) {
@@ -810,12 +858,18 @@ EventDispatcher::Dispatch(nsISupports* aTarget,
if (preVisitor.mEventTargetAtParent) {
// Need to set the target of the event
// so that also the next retargeting works.
+ preVisitor.mTargetInKnownToBeHandledScope = preVisitor.mEvent->mTarget;
preVisitor.mEvent->mTarget = preVisitor.mEventTargetAtParent;
parentEtci->SetNewTarget(preVisitor.mEventTargetAtParent);
}
+ if (preVisitor.mRetargetedRelatedTarget) {
+ preVisitor.mEvent->mRelatedTarget = preVisitor.mRetargetedRelatedTarget;
+ }
+
parentEtci->GetEventTargetParent(preVisitor);
if (preVisitor.mCanHandle) {
+ preVisitor.mTargetInKnownToBeHandledScope = preVisitor.mEvent->mTarget;
topEtci = parentEtci;
} else {
nsCOMPtr<nsINode> disabledTarget = do_QueryInterface(parentTarget);
@@ -825,6 +879,7 @@ EventDispatcher::Dispatch(nsISupports* aTarget,
topEtci,
disabledTarget);
if (parentEtci && preVisitor.mCanHandle) {
+ preVisitor.mTargetInKnownToBeHandledScope = preVisitor.mEvent->mTarget;
EventTargetChainItem* item =
EventTargetChainItem::GetFirstCanHandleEventTarget(chain);
item->SetNewTarget(parentTarget);
@@ -871,6 +926,19 @@ EventDispatcher::Dispatch(nsISupports* aTarget,
aEvent->mFlags.mIsBeingDispatched = false;
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<nsIContent> finalTarget = do_QueryInterface(aEvent->mTarget);
+ // TODO: replace with IsShadowRoot() check once bug 1427511 lands.
+ if (finalTarget && ShadowRoot::FromNode(finalTarget->SubtreeRoot())) {
+ aEvent->mTarget = nullptr;
+ aEvent->mOriginalTarget = nullptr;
+ aEvent->mRelatedTarget = nullptr;
+ aEvent->mOriginalRelatedTarget = nullptr;
+ }
+
if (!externalDOMEvent && preVisitor.mDOMEvent) {
// An dom::Event was created while dispatching the event.
// Duplicate private data if someone holds a pointer to it.
diff --git a/dom/events/EventDispatcher.h b/dom/events/EventDispatcher.h
index dc11536eb6..529240d0ac 100644
--- a/dom/events/EventDispatcher.h
+++ b/dom/events/EventDispatcher.h
@@ -113,7 +113,8 @@ public:
WidgetEvent* aEvent,
nsIDOMEvent* aDOMEvent,
nsEventStatus aEventStatus,
- bool aIsInAnon)
+ bool aIsInAnon,
+ dom::EventTarget* aTargetInKnownToBeHandledScope)
: EventChainVisitor(aPresContext, aEvent, aDOMEvent, aEventStatus)
, mCanHandle(true)
, mAutomaticChromeDispatch(true)
@@ -126,8 +127,11 @@ public:
, mRootOfClosedTree(false)
, mParentIsSlotInClosedTree(false)
, mParentIsChromeHandler(false)
+ , mRelatedTargetRetargetedInCurrentScope(false)
, mParentTarget(nullptr)
, mEventTargetAtParent(nullptr)
+ , mRetargetedRelatedTarget(nullptr)
+ , mTargetInKnownToBeHandledScope(aTargetInKnownToBeHandledScope)
{
}
@@ -144,8 +148,12 @@ public:
mRootOfClosedTree = false;
mParentIsSlotInClosedTree = false;
mParentIsChromeHandler = false;
+ // Note, we don't clear mRelatedTargetRetargetedInCurrentScope explicitly,
+ // since it is used during event path creation to indicate whether
+ // relatedTarget may need to be retargeted.
mParentTarget = nullptr;
mEventTargetAtParent = nullptr;
+ mRetargetedRelatedTarget = nullptr;
}
dom::EventTarget* GetParentTarget()
@@ -161,6 +169,13 @@ public:
}
}
+ void IgnoreCurrentTarget()
+ {
+ mCanHandle = false;
+ SetParentTarget(nullptr, false);
+ mEventTargetAtParent = nullptr;
+ }
+
/**
* Member that must be set in GetEventTargetParent by event targets. If set to
* false, indicates that this event target will not be handling the event and
@@ -229,6 +244,13 @@ public:
*/
bool mParentIsChromeHandler;
+ /**
+ * True if event's related target has been already retargeted in the
+ * current 'scope'. This should be set to false initially and whenever
+ * event path creation crosses shadow boundary.
+ */
+ bool mRelatedTargetRetargetedInCurrentScope;
+
private:
/**
* Parent item in the event target chain.
@@ -241,6 +263,19 @@ public:
* which should be used when the event is handled at mParentTarget.
*/
dom::EventTarget* mEventTargetAtParent;
+
+ /**
+ * If the related target of the event needs to be retargeted, set this
+ * to a new EventTarget.
+ */
+ dom::EventTarget* mRetargetedRelatedTarget;
+
+ /**
+ * Set to the value of mEvent->mTarget of the previous scope in case of
+ * Shadow DOM or such, and if there is no anonymous content this just points
+ * to the initial target.
+ */
+ dom::EventTarget* mTargetInKnownToBeHandledScope;
};
class EventChainPostVisitor : public mozilla::EventChainVisitor
diff --git a/widget/BasicEvents.h b/widget/BasicEvents.h
index 9631dfd257..14977bdc24 100644
--- a/widget/BasicEvents.h
+++ b/widget/BasicEvents.h
@@ -427,6 +427,7 @@ public:
/// The possible related target
nsCOMPtr<dom::EventTarget> mRelatedTarget;
+ nsCOMPtr<dom::EventTarget> mOriginalRelatedTarget;
nsTArray<EventTargetChainItem>* mPath;
@@ -448,6 +449,8 @@ public:
mCurrentTarget = aCopyTargets ? aEvent.mCurrentTarget : nullptr;
mOriginalTarget = aCopyTargets ? aEvent.mOriginalTarget : nullptr;
mRelatedTarget = aCopyTargets ? aEvent.mRelatedTarget : nullptr;
+ mOriginalRelatedTarget =
+ aCopyTargets ? aEvent.mOriginalRelatedTarget : nullptr;
}
/**