From 7e506bd98dab604062bfe12a44c096eb287721bf Mon Sep 17 00:00:00 2001 From: "Matt A. Tobin" Date: Fri, 17 Apr 2020 07:30:43 -0400 Subject: Bug 1412775 - Implement Event.composedPath Tag #1375 --- dom/events/DOMEventTargetHelper.cpp | 2 +- dom/events/Event.cpp | 7 +++ dom/events/Event.h | 2 + dom/events/EventDispatcher.cpp | 107 +++++++++++++++++++++++++++++++++++- dom/events/EventDispatcher.h | 41 ++++++++++++++ 5 files changed, 155 insertions(+), 4 deletions(-) (limited to 'dom/events') diff --git a/dom/events/DOMEventTargetHelper.cpp b/dom/events/DOMEventTargetHelper.cpp index ee03463ef..90ee61059 100644 --- a/dom/events/DOMEventTargetHelper.cpp +++ b/dom/events/DOMEventTargetHelper.cpp @@ -331,7 +331,7 @@ nsresult DOMEventTargetHelper::GetEventTargetParent(EventChainPreVisitor& aVisitor) { aVisitor.mCanHandle = true; - aVisitor.mParentTarget = nullptr; + aVisitor.SetParentTarget(nullptr, false); return NS_OK; } diff --git a/dom/events/Event.cpp b/dom/events/Event.cpp index 280e40ad5..def298228 100755 --- a/dom/events/Event.cpp +++ b/dom/events/Event.cpp @@ -11,6 +11,7 @@ #include "mozilla/dom/ShadowRoot.h" #include "mozilla/ContentEvents.h" #include "mozilla/DOMEventTargetHelper.h" +#include "mozilla/EventDispatcher.h" #include "mozilla/EventStateManager.h" #include "mozilla/InternalMutationEvent.h" #include "mozilla/dom/Performance.h" @@ -298,6 +299,12 @@ Event::GetCurrentTarget() const return mEvent->GetCurrentDOMEventTarget(); } +void +Event::ComposedPath(nsTArray>& aPath) +{ + EventDispatcher::GetComposedPathFor(mEvent, aPath); +} + NS_IMETHODIMP Event::GetCurrentTarget(nsIDOMEventTarget** aCurrentTarget) { diff --git a/dom/events/Event.h b/dom/events/Event.h index 0817aa809..4f1fcc827 100755 --- a/dom/events/Event.h +++ b/dom/events/Event.h @@ -157,6 +157,8 @@ public: EventTarget* GetTarget() const; EventTarget* GetCurrentTarget() const; + void ComposedPath(nsTArray>& aPath); + uint16_t EventPhase() const; // xpidl implementation diff --git a/dom/events/EventDispatcher.cpp b/dom/events/EventDispatcher.cpp index 740e611e4..1094c08c2 100644 --- a/dom/events/EventDispatcher.cpp +++ b/dom/events/EventDispatcher.cpp @@ -246,6 +246,36 @@ public: return mFlags.mPreHandleEventOnly; } + void SetRootOfClosedTree(bool aSet) + { + mFlags.mRootOfClosedTree = aSet; + } + + bool IsRootOfClosedTree() + { + return mFlags.mRootOfClosedTree; + } + + void SetIsSlotInClosedTree(bool aSet) + { + mFlags.mIsSlotInClosedTree = aSet; + } + + bool IsSlotInClosedTree() + { + return mFlags.mIsSlotInClosedTree; + } + + void SetIsChromeHandler(bool aSet) + { + mFlags.mIsChromeHandler = aSet; + } + + bool IsChromeHandler() + { + return mFlags.mIsChromeHandler; + } + void SetMayHaveListenerManager(bool aMayHave) { mFlags.mMayHaveManager = aMayHave; @@ -344,6 +374,9 @@ private: bool mIsChromeContent : 1; bool mWantsPreHandleEvent : 1; bool mPreHandleEventOnly : 1; + bool mRootOfClosedTree : 1; + bool mIsSlotInClosedTree : 1; + bool mIsChromeHandler : 1; private: typedef uint32_t RawFlags; void SetRawFlags(RawFlags aRawFlags) @@ -390,6 +423,7 @@ EventTargetChainItem::GetEventTargetParent(EventChainPreVisitor& aVisitor) SetMayHaveListenerManager(aVisitor.mMayHaveListenerManager); SetWantsPreHandleEvent(aVisitor.mWantsPreHandleEvent); SetPreHandleEventOnly(aVisitor.mWantsPreHandleEvent && !aVisitor.mCanHandle); + SetRootOfClosedTree(aVisitor.mRootOfClosedTree); mItemFlags = aVisitor.mItemFlags; mItemData = aVisitor.mItemData; } @@ -761,16 +795,19 @@ EventDispatcher::Dispatch(nsISupports* aTarget, targetEtci->SetNewTarget(t); EventTargetChainItem* topEtci = targetEtci; targetEtci = nullptr; - while (preVisitor.mParentTarget) { - EventTarget* parentTarget = preVisitor.mParentTarget; + while (preVisitor.GetParentTarget()) { + EventTarget* parentTarget = preVisitor.GetParentTarget(); EventTargetChainItem* parentEtci = - EventTargetChainItem::Create(chain, preVisitor.mParentTarget, topEtci); + EventTargetChainItem::Create(chain, parentTarget, topEtci); if (!parentEtci->IsValid()) { EventTargetChainItem::DestroyLast(chain, parentEtci); rv = NS_ERROR_FAILURE; break; } + parentEtci->SetIsSlotInClosedTree(preVisitor.mParentIsSlotInClosedTree); + parentEtci->SetIsChromeHandler(preVisitor.mParentIsChromeHandler); + // Item needs event retargetting. if (preVisitor.mEventTargetAtParent) { // Need to set the target of the event @@ -814,9 +851,13 @@ EventDispatcher::Dispatch(nsISupports* aTarget, } // Handle the chain. EventChainPostVisitor postVisitor(preVisitor); + MOZ_RELEASE_ASSERT(!aEvent->mPath); + aEvent->mPath = &chain; EventTargetChainItem::HandleEventTargetChain(chain, postVisitor, aCallback, cd); + aEvent->mPath = nullptr; + preVisitor.mEventStatus = postVisitor.mEventStatus; // If the DOM event was created during event flow. if (!preVisitor.mDOMEvent && postVisitor.mDOMEvent) { @@ -1128,4 +1169,64 @@ EventDispatcher::CreateEvent(EventTarget* aOwner, return nullptr; } +// static +void +EventDispatcher::GetComposedPathFor(WidgetEvent* aEvent, + nsTArray>& aPath) +{ + nsTArray* path = aEvent->mPath; + if (!path || path->IsEmpty() || !aEvent->mCurrentTarget) { + return; + } + + EventTarget* currentTarget = + aEvent->mCurrentTarget->GetTargetForEventTargetChain(); + if (!currentTarget) { + return; + } + + AutoTArray reversedComposedPath; + bool hasSeenCurrentTarget = false; + uint32_t hiddenSubtreeLevel = 0; + for (uint32_t i = path->Length(); i; ) { + --i; + + EventTargetChainItem& item = path->ElementAt(i); + if (item.PreHandleEventOnly()) { + continue; + } + + if (!hasSeenCurrentTarget && currentTarget == item.CurrentTarget()) { + hasSeenCurrentTarget = true; + } else if (hasSeenCurrentTarget && item.IsRootOfClosedTree()) { + ++hiddenSubtreeLevel; + } + + if (hiddenSubtreeLevel == 0) { + reversedComposedPath.AppendElement(item.CurrentTarget()); + } + + if (item.IsSlotInClosedTree() && hiddenSubtreeLevel > 0) { + --hiddenSubtreeLevel; + } + + if (item.IsChromeHandler()) { + if (hasSeenCurrentTarget) { + // The current behavior is to include only EventTargets from + // either chrome side of event path or content side, not from both. + break; + } + + // Need to start all over to collect the composed path on content side. + reversedComposedPath.Clear(); + } + } + + aPath.SetCapacity(reversedComposedPath.Length()); + for (uint32_t i = reversedComposedPath.Length(); i; ) { + --i; + aPath.AppendElement(reversedComposedPath[i]->GetTargetForDOMEvent()); + } +} + } // namespace mozilla diff --git a/dom/events/EventDispatcher.h b/dom/events/EventDispatcher.h index 618c863d5..8a34e6bf7 100644 --- a/dom/events/EventDispatcher.h +++ b/dom/events/EventDispatcher.h @@ -124,6 +124,9 @@ public: , mWantsWillHandleEvent(false) , mMayHaveListenerManager(true) , mWantsPreHandleEvent(false) + , mRootOfClosedTree(false) + , mParentIsSlotInClosedTree(false) + , mParentIsChromeHandler(false) , mParentTarget(nullptr) , mEventTargetAtParent(nullptr) { @@ -139,10 +142,26 @@ public: mWantsWillHandleEvent = false; mMayHaveListenerManager = true; mWantsPreHandleEvent = false; + mRootOfClosedTree = false; + mParentIsSlotInClosedTree = false; + mParentIsChromeHandler = false; mParentTarget = nullptr; mEventTargetAtParent = nullptr; } + dom::EventTarget* GetParentTarget() + { + return mParentTarget; + } + + void SetParentTarget(dom::EventTarget* aParentTarget, bool aIsChromeHandler) + { + mParentTarget = aParentTarget; + if (mParentTarget) { + mParentIsChromeHandler = aIsChromeHandler; + } + } + /** * 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 @@ -194,11 +213,30 @@ public: */ bool mWantsPreHandleEvent; + /** + * True if the current target is either closed ShadowRoot or root of + * chrome only access tree (for example native anonymous content). + */ + bool mRootOfClosedTree; + + /** + * True if mParentTarget is HTMLSlotElement in a closed shadow tree and the + * current target is assigned to that slot. + */ + bool mParentIsSlotInClosedTree; + + /** + * True if mParentTarget is a chrome handler in the event path. + */ + bool mParentIsChromeHandler; + +private: /** * Parent item in the event target chain. */ dom::EventTarget* mParentTarget; +public: /** * If the event needs to be retargeted, this is the event target, * which should be used when the event is handled at mParentTarget. @@ -281,6 +319,9 @@ public: WidgetEvent* aEvent, const nsAString& aEventType); + static void GetComposedPathFor(WidgetEvent* aEvent, + nsTArray>& aPath); + /** * Called at shutting down. */ -- cgit v1.2.3