diff options
author | Matt A. Tobin <email@mattatobin.com> | 2020-04-17 07:30:43 -0400 |
---|---|---|
committer | Matt A. Tobin <email@mattatobin.com> | 2020-04-17 07:30:43 -0400 |
commit | 7e506bd98dab604062bfe12a44c096eb287721bf (patch) | |
tree | ebea23264b1fa0f9c23935b253c23bdc0b41c25d /dom | |
parent | 010f37f47b9c15935a6113cd82e43f0673122016 (diff) | |
download | UXP-7e506bd98dab604062bfe12a44c096eb287721bf.tar UXP-7e506bd98dab604062bfe12a44c096eb287721bf.tar.gz UXP-7e506bd98dab604062bfe12a44c096eb287721bf.tar.lz UXP-7e506bd98dab604062bfe12a44c096eb287721bf.tar.xz UXP-7e506bd98dab604062bfe12a44c096eb287721bf.zip |
Bug 1412775 - Implement Event.composedPath
Tag #1375
Diffstat (limited to 'dom')
-rw-r--r-- | dom/archivereader/ArchiveRequest.cpp | 2 | ||||
-rw-r--r-- | dom/base/FragmentOrElement.cpp | 17 | ||||
-rw-r--r-- | dom/base/ShadowRoot.cpp | 5 | ||||
-rw-r--r-- | dom/base/nsDocument.cpp | 4 | ||||
-rw-r--r-- | dom/base/nsGlobalWindow.cpp | 2 | ||||
-rw-r--r-- | dom/base/nsInProcessTabChildGlobal.cpp | 8 | ||||
-rw-r--r-- | dom/base/nsWindowRoot.cpp | 2 | ||||
-rw-r--r-- | dom/events/DOMEventTargetHelper.cpp | 2 | ||||
-rwxr-xr-x | dom/events/Event.cpp | 7 | ||||
-rwxr-xr-x | dom/events/Event.h | 2 | ||||
-rw-r--r-- | dom/events/EventDispatcher.cpp | 107 | ||||
-rw-r--r-- | dom/events/EventDispatcher.h | 41 | ||||
-rw-r--r-- | dom/indexedDB/IDBFileHandle.cpp | 2 | ||||
-rw-r--r-- | dom/indexedDB/IDBFileRequest.cpp | 2 | ||||
-rw-r--r-- | dom/indexedDB/IDBRequest.cpp | 2 | ||||
-rw-r--r-- | dom/indexedDB/IDBTransaction.cpp | 2 | ||||
-rw-r--r-- | dom/webidl/Event.webidl | 2 | ||||
-rw-r--r-- | dom/workers/SharedWorker.cpp | 2 | ||||
-rw-r--r-- | dom/xul/nsXULElement.cpp | 2 |
19 files changed, 188 insertions, 25 deletions
diff --git a/dom/archivereader/ArchiveRequest.cpp b/dom/archivereader/ArchiveRequest.cpp index c4b19f56b..832dec3fa 100644 --- a/dom/archivereader/ArchiveRequest.cpp +++ b/dom/archivereader/ArchiveRequest.cpp @@ -72,7 +72,7 @@ nsresult ArchiveRequest::GetEventTargetParent(EventChainPreVisitor& aVisitor) { aVisitor.mCanHandle = true; - aVisitor.mParentTarget = nullptr; + aVisitor.SetParentTarget(nullptr, false); return NS_OK; } diff --git a/dom/base/FragmentOrElement.cpp b/dom/base/FragmentOrElement.cpp index ecb18798f..5b2fabd8d 100644 --- a/dom/base/FragmentOrElement.cpp +++ b/dom/base/FragmentOrElement.cpp @@ -813,6 +813,7 @@ nsIContent::GetEventTargetParent(EventChainPreVisitor& aVisitor) // Don't propagate mouseover and mouseout events when mouse is moving // inside chrome access only content. bool isAnonForEvents = IsRootOfChromeAccessOnlySubtree(); + aVisitor.mRootOfClosedTree = isAnonForEvents; if ((aVisitor.mEvent->mMessage == eMouseOver || aVisitor.mEvent->mMessage == eMouseOut || aVisitor.mEvent->mMessage == ePointerOver || @@ -834,7 +835,7 @@ nsIContent::GetEventTargetParent(EventChainPreVisitor& aVisitor) nsIContent* adjustedTarget = Event::GetShadowRelatedTarget(this, relatedTarget); if (this == adjustedTarget) { - aVisitor.mParentTarget = nullptr; + aVisitor.SetParentTarget(nullptr, false); aVisitor.mCanHandle = false; return NS_OK; } @@ -891,7 +892,7 @@ nsIContent::GetEventTargetParent(EventChainPreVisitor& aVisitor) originalTarget->FindFirstNonChromeOnlyAccessContent()) ? "" : "Wrong event propagation!?!\n"); #endif - aVisitor.mParentTarget = nullptr; + aVisitor.SetParentTarget(nullptr, false); // Event should not propagate to non-anon content. aVisitor.mCanHandle = isAnonForEvents; return NS_OK; @@ -943,11 +944,17 @@ nsIContent::GetEventTargetParent(EventChainPreVisitor& aVisitor) if (!aVisitor.mEvent->mFlags.mComposedInNativeAnonymousContent && IsRootOfNativeAnonymousSubtree() && OwnerDoc() && OwnerDoc()->GetWindow()) { - aVisitor.mParentTarget = OwnerDoc()->GetWindow()->GetParentTarget(); + aVisitor.SetParentTarget(OwnerDoc()->GetWindow()->GetParentTarget(), true); } else if (parent) { - aVisitor.mParentTarget = parent; + aVisitor.SetParentTarget(parent, false); + if (slot) { + ShadowRoot* root = slot->GetContainingShadow(); + if (root && root->IsClosed()) { + aVisitor.mParentIsSlotInClosedTree = true; + } + } } else { - aVisitor.mParentTarget = GetComposedDoc(); + aVisitor.SetParentTarget(GetComposedDoc(), false); } return NS_OK; } diff --git a/dom/base/ShadowRoot.cpp b/dom/base/ShadowRoot.cpp index 308f57cf7..e7f7ae93c 100644 --- a/dom/base/ShadowRoot.cpp +++ b/dom/base/ShadowRoot.cpp @@ -309,6 +309,7 @@ nsresult ShadowRoot::GetEventTargetParent(EventChainPreVisitor& aVisitor) { aVisitor.mCanHandle = true; + aVisitor.mRootOfClosedTree = IsClosed(); // https://dom.spec.whatwg.org/#ref-for-get-the-parent%E2%91%A6 if (!aVisitor.mEvent->mFlags.mComposed) { @@ -323,13 +324,13 @@ ShadowRoot::GetEventTargetParent(EventChainPreVisitor& aVisitor) EventTarget* parentTarget = win && aVisitor.mEvent->mMessage != eLoad ? win->GetParentTarget() : nullptr; - aVisitor.mParentTarget = parentTarget; + aVisitor.SetParentTarget(parentTarget, true); return NS_OK; } } nsIContent* shadowHost = GetHost(); - aVisitor.mParentTarget = shadowHost; + aVisitor.SetParentTarget(shadowHost, false); if (aVisitor.mOriginalTargetIsInAnon) { nsCOMPtr<nsIContent> content(do_QueryInterface(aVisitor.mEvent->mTarget)); diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index 459ad6bdd..d2763eddd 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -7531,8 +7531,8 @@ nsDocument::GetEventTargetParent(EventChainPreVisitor& aVisitor) // Load events must not propagate to |window| object, see bug 335251. if (aVisitor.mEvent->mMessage != eLoad) { nsGlobalWindow* window = nsGlobalWindow::Cast(GetWindow()); - aVisitor.mParentTarget = - window ? window->GetTargetForEventTargetChain() : nullptr; + aVisitor.SetParentTarget( + window ? window->GetTargetForEventTargetChain() : nullptr, false); } return NS_OK; } diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 802b44f38..69643762c 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -3608,7 +3608,7 @@ nsGlobalWindow::GetEventTargetParent(EventChainPreVisitor& aVisitor) } } - aVisitor.mParentTarget = GetParentTarget(); + aVisitor.SetParentTarget(GetParentTarget(), true); // Handle 'active' event. if (!mIdleObservers.IsEmpty() && diff --git a/dom/base/nsInProcessTabChildGlobal.cpp b/dom/base/nsInProcessTabChildGlobal.cpp index 547bb8d36..9885b41a8 100644 --- a/dom/base/nsInProcessTabChildGlobal.cpp +++ b/dom/base/nsInProcessTabChildGlobal.cpp @@ -270,7 +270,7 @@ nsInProcessTabChildGlobal::GetEventTargetParent(EventChainPreVisitor& aVisitor) #endif if (mPreventEventsEscaping) { - aVisitor.mParentTarget = nullptr; + aVisitor.SetParentTarget(nullptr, false); return NS_OK; } @@ -278,11 +278,13 @@ nsInProcessTabChildGlobal::GetEventTargetParent(EventChainPreVisitor& aVisitor) (!mOwner || !nsContentUtils::IsInChromeDocshell(mOwner->OwnerDoc()))) { if (mOwner) { if (nsPIDOMWindowInner* innerWindow = mOwner->OwnerDoc()->GetInnerWindow()) { - aVisitor.mParentTarget = innerWindow->GetParentTarget(); + // 'this' is already a "chrome handler", so we consider window's + // parent target to be part of that same part of the event path. + aVisitor.SetParentTarget(innerWindow->GetParentTarget(), false); } } } else { - aVisitor.mParentTarget = mOwner; + aVisitor.SetParentTarget(mOwner, false); } return NS_OK; diff --git a/dom/base/nsWindowRoot.cpp b/dom/base/nsWindowRoot.cpp index b629ab86f..710ecd88f 100644 --- a/dom/base/nsWindowRoot.cpp +++ b/dom/base/nsWindowRoot.cpp @@ -186,7 +186,7 @@ nsWindowRoot::GetEventTargetParent(EventChainPreVisitor& aVisitor) aVisitor.mForceContentDispatch = true; //FIXME! Bug 329119 // To keep mWindow alive aVisitor.mItemData = static_cast<nsISupports *>(mWindow); - aVisitor.mParentTarget = mParent; + aVisitor.SetParentTarget(mParent, false); return NS_OK; } 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<RefPtr<EventTarget>>& 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<RefPtr<EventTarget>>& 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<RefPtr<EventTarget>>& aPath) +{ + nsTArray<EventTargetChainItem>* path = aEvent->mPath; + if (!path || path->IsEmpty() || !aEvent->mCurrentTarget) { + return; + } + + EventTarget* currentTarget = + aEvent->mCurrentTarget->GetTargetForEventTargetChain(); + if (!currentTarget) { + return; + } + + AutoTArray<EventTarget*, 128> 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 @@ -195,10 +214,29 @@ 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<RefPtr<dom::EventTarget>>& aPath); + /** * Called at shutting down. */ diff --git a/dom/indexedDB/IDBFileHandle.cpp b/dom/indexedDB/IDBFileHandle.cpp index 55dd22ce9..93e163c11 100644 --- a/dom/indexedDB/IDBFileHandle.cpp +++ b/dom/indexedDB/IDBFileHandle.cpp @@ -132,7 +132,7 @@ IDBFileHandle::GetEventTargetParent(EventChainPreVisitor& aVisitor) AssertIsOnOwningThread(); aVisitor.mCanHandle = true; - aVisitor.mParentTarget = mMutableFile; + aVisitor.SetParentTarget(mMutableFile, false); return NS_OK; } diff --git a/dom/indexedDB/IDBFileRequest.cpp b/dom/indexedDB/IDBFileRequest.cpp index 7d93f24de..1401f7c76 100644 --- a/dom/indexedDB/IDBFileRequest.cpp +++ b/dom/indexedDB/IDBFileRequest.cpp @@ -69,7 +69,7 @@ IDBFileRequest::GetEventTargetParent(EventChainPreVisitor& aVisitor) AssertIsOnOwningThread(); aVisitor.mCanHandle = true; - aVisitor.mParentTarget = mFileHandle; + aVisitor.SetParentTarget(mFileHandle, false); return NS_OK; } diff --git a/dom/indexedDB/IDBRequest.cpp b/dom/indexedDB/IDBRequest.cpp index b74725954..5ce077e33 100644 --- a/dom/indexedDB/IDBRequest.cpp +++ b/dom/indexedDB/IDBRequest.cpp @@ -454,7 +454,7 @@ IDBRequest::GetEventTargetParent(EventChainPreVisitor& aVisitor) AssertIsOnOwningThread(); aVisitor.mCanHandle = true; - aVisitor.mParentTarget = mTransaction; + aVisitor.SetParentTarget(mTransaction, false); return NS_OK; } diff --git a/dom/indexedDB/IDBTransaction.cpp b/dom/indexedDB/IDBTransaction.cpp index ec27c51d8..0a10e2ca0 100644 --- a/dom/indexedDB/IDBTransaction.cpp +++ b/dom/indexedDB/IDBTransaction.cpp @@ -1001,7 +1001,7 @@ IDBTransaction::GetEventTargetParent(EventChainPreVisitor& aVisitor) AssertIsOnOwningThread(); aVisitor.mCanHandle = true; - aVisitor.mParentTarget = mDatabase; + aVisitor.SetParentTarget(mDatabase, false); return NS_OK; } diff --git a/dom/webidl/Event.webidl b/dom/webidl/Event.webidl index a5d7da7d4..8a8e71c43 100644 --- a/dom/webidl/Event.webidl +++ b/dom/webidl/Event.webidl @@ -22,6 +22,8 @@ interface Event { [Pure] readonly attribute EventTarget? currentTarget; + sequence<EventTarget> composedPath(); + const unsigned short NONE = 0; const unsigned short CAPTURING_PHASE = 1; const unsigned short AT_TARGET = 2; diff --git a/dom/workers/SharedWorker.cpp b/dom/workers/SharedWorker.cpp index 750dc9237..71c644405 100644 --- a/dom/workers/SharedWorker.cpp +++ b/dom/workers/SharedWorker.cpp @@ -196,7 +196,7 @@ SharedWorker::GetEventTargetParent(EventChainPreVisitor& aVisitor) QueueEvent(event); aVisitor.mCanHandle = false; - aVisitor.mParentTarget = nullptr; + aVisitor.SetParentTarget(nullptr, false); return NS_OK; } diff --git a/dom/xul/nsXULElement.cpp b/dom/xul/nsXULElement.cpp index af050d9f8..2ae03e0b1 100644 --- a/dom/xul/nsXULElement.cpp +++ b/dom/xul/nsXULElement.cpp @@ -1303,7 +1303,7 @@ nsXULElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) if (IsEventStoppedFromAnonymousScrollbar(aVisitor.mEvent->mMessage)) { // Don't propagate these events from native anonymous scrollbar. aVisitor.mCanHandle = true; - aVisitor.mParentTarget = nullptr; + aVisitor.SetParentTarget(nullptr, false); return NS_OK; } if (aVisitor.mEvent->mMessage == eXULCommand && |