diff options
author | FranklinDM <mrmineshafter17@gmail.com> | 2023-03-03 21:22:02 +0800 |
---|---|---|
committer | FranklinDM <mrmineshafter17@gmail.com> | 2023-03-04 21:32:09 +0800 |
commit | 3d82baddf33f6951140b17e0c01c3901b733f041 (patch) | |
tree | a53e8bd9246db8e95e4abf6c69889d90ae58ab68 | |
parent | 60d420fe715cbdd8e2db5bad0a390312383ca0a3 (diff) | |
download | uxp-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.cpp | 129 | ||||
-rw-r--r-- | dom/base/ShadowRoot.cpp | 12 | ||||
-rw-r--r-- | dom/base/nsContentUtils.cpp | 52 | ||||
-rw-r--r-- | dom/base/nsContentUtils.h | 14 | ||||
-rwxr-xr-x | dom/events/Event.cpp | 40 | ||||
-rwxr-xr-x | dom/events/Event.h | 8 | ||||
-rw-r--r-- | dom/events/EventDispatcher.cpp | 74 | ||||
-rw-r--r-- | dom/events/EventDispatcher.h | 37 | ||||
-rw-r--r-- | widget/BasicEvents.h | 3 |
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; } /** |