diff options
author | Moonchild <moonchild@palemoon.org> | 2020-04-17 16:02:56 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-04-17 16:02:56 +0200 |
commit | d5102d6beafc2a2a0cec3cc3ee5f7ebde31ae7bf (patch) | |
tree | 2bfef192cbb748b675ce8308c242a376798e265d /dom/events/EventDispatcher.cpp | |
parent | 5caf99795aa81e1fc145b8e937b1ee8197ed2486 (diff) | |
parent | f35aa3e15fedf3cd4ad163d60ab74a9537ca5c82 (diff) | |
download | UXP-d5102d6beafc2a2a0cec3cc3ee5f7ebde31ae7bf.tar UXP-d5102d6beafc2a2a0cec3cc3ee5f7ebde31ae7bf.tar.gz UXP-d5102d6beafc2a2a0cec3cc3ee5f7ebde31ae7bf.tar.lz UXP-d5102d6beafc2a2a0cec3cc3ee5f7ebde31ae7bf.tar.xz UXP-d5102d6beafc2a2a0cec3cc3ee5f7ebde31ae7bf.zip |
Merge pull request #1518 from MoonchildProductions/shadowdom-merge
Incremental shadowdom-merge
Diffstat (limited to 'dom/events/EventDispatcher.cpp')
-rw-r--r-- | dom/events/EventDispatcher.cpp | 363 |
1 files changed, 285 insertions, 78 deletions
diff --git a/dom/events/EventDispatcher.cpp b/dom/events/EventDispatcher.cpp index 1d4dfd7d9..1094c08c2 100644 --- a/dom/events/EventDispatcher.cpp +++ b/dom/events/EventDispatcher.cpp @@ -130,13 +130,6 @@ static bool IsEventTargetChrome(EventTarget* aEventTarget, return isChrome; } - -#define NS_TARGET_CHAIN_FORCE_CONTENT_DISPATCH (1 << 0) -#define NS_TARGET_CHAIN_WANTS_WILL_HANDLE_EVENT (1 << 1) -#define NS_TARGET_CHAIN_MAY_HAVE_MANAGER (1 << 2) -#define NS_TARGET_CHAIN_CHECKED_IF_CHROME (1 << 3) -#define NS_TARGET_CHAIN_IS_CHROME_CONTENT (1 << 4) - // EventTargetChainItem represents a single item in the event target chain. class EventTargetChainItem { @@ -144,8 +137,7 @@ private: explicit EventTargetChainItem(EventTarget* aTarget); public: EventTargetChainItem() - : mFlags(0) - , mItemFlags(0) + : mItemFlags(0) { } @@ -153,7 +145,8 @@ public: EventTarget* aTarget, EventTargetChainItem* aChild = nullptr) { - MOZ_ASSERT(!aChild || &aChain.ElementAt(aChain.Length() - 1) == aChild); + // The last item which can handle the event must be aChild. + MOZ_ASSERT(GetLastCanHandleEventTarget(aChain) == aChild); return new (aChain.AppendElement()) EventTargetChainItem(aTarget); } @@ -165,6 +158,38 @@ public: aChain.RemoveElementAt(lastIndex); } + static EventTargetChainItem* GetFirstCanHandleEventTarget( + nsTArray<EventTargetChainItem>& aChain) + { + return &aChain[GetFirstCanHandleEventTargetIdx(aChain)]; + } + + static uint32_t GetFirstCanHandleEventTargetIdx(nsTArray<EventTargetChainItem>& aChain) + { + // aChain[i].PreHandleEventOnly() = true only when the target element wants + // PreHandleEvent and set mCanHandle=false. So we find the first element + // which can handle the event. + for (uint32_t i = 0; i < aChain.Length(); ++i) { + if (!aChain[i].PreHandleEventOnly()) { + return i; + } + } + MOZ_ASSERT(false); + return 0; + } + + static EventTargetChainItem* GetLastCanHandleEventTarget( + nsTArray<EventTargetChainItem>& aChain) + { + // Fine the last item which can handle the event. + for (int32_t i = aChain.Length() - 1; i >= 0; --i) { + if (!aChain[i].PreHandleEventOnly()) { + return &aChain[i]; + } + } + return nullptr; + } + bool IsValid() { NS_WARNING_ASSERTION(!!(mTarget), "Event target is not valid!"); @@ -183,44 +208,82 @@ public: void SetForceContentDispatch(bool aForce) { - if (aForce) { - mFlags |= NS_TARGET_CHAIN_FORCE_CONTENT_DISPATCH; - } else { - mFlags &= ~NS_TARGET_CHAIN_FORCE_CONTENT_DISPATCH; - } + mFlags.mForceContentDispatch = aForce; } bool ForceContentDispatch() { - return !!(mFlags & NS_TARGET_CHAIN_FORCE_CONTENT_DISPATCH); + return mFlags.mForceContentDispatch; } void SetWantsWillHandleEvent(bool aWants) { - if (aWants) { - mFlags |= NS_TARGET_CHAIN_WANTS_WILL_HANDLE_EVENT; - } else { - mFlags &= ~NS_TARGET_CHAIN_WANTS_WILL_HANDLE_EVENT; - } + mFlags.mWantsWillHandleEvent = aWants; } bool WantsWillHandleEvent() { - return !!(mFlags & NS_TARGET_CHAIN_WANTS_WILL_HANDLE_EVENT); + return mFlags.mWantsWillHandleEvent; + } + + void SetWantsPreHandleEvent(bool aWants) + { + mFlags.mWantsPreHandleEvent = aWants; + } + + bool WantsPreHandleEvent() + { + return mFlags.mWantsPreHandleEvent; + } + + void SetPreHandleEventOnly(bool aWants) + { + mFlags.mPreHandleEventOnly = aWants; + } + + bool PreHandleEventOnly() + { + 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) { - if (aMayHave) { - mFlags |= NS_TARGET_CHAIN_MAY_HAVE_MANAGER; - } else { - mFlags &= ~NS_TARGET_CHAIN_MAY_HAVE_MANAGER; - } + mFlags.mMayHaveManager = aMayHave; } bool MayHaveListenerManager() { - return !!(mFlags & NS_TARGET_CHAIN_MAY_HAVE_MANAGER); + return mFlags.mMayHaveManager; } EventTarget* CurrentTarget() @@ -240,10 +303,15 @@ public: ELMCreationDetector& aCd); /** - * Resets aVisitor object and calls PreHandleEvent. + * Resets aVisitor object and calls GetEventTargetParent. * Copies mItemFlags and mItemData to the current EventTargetChainItem. */ - void PreHandleEvent(EventChainPreVisitor& aVisitor); + void GetEventTargetParent(EventChainPreVisitor& aVisitor); + + /** + * Calls PreHandleEvent for those items which called SetWantsPreHandleEvent. + */ + void PreHandleEvent(EventChainVisitor& aVisitor); /** * If the current item in the event target chain has an event listener @@ -288,7 +356,37 @@ public: private: nsCOMPtr<EventTarget> mTarget; - uint16_t mFlags; + + class EventTargetChainFlags + { + public: + explicit EventTargetChainFlags() + { + SetRawFlags(0); + } + // Cached flags for each EventTargetChainItem which are set when calling + // GetEventTargetParent to create event target chain. They are used to + // manage or speedup event dispatching. + bool mForceContentDispatch : 1; + bool mWantsWillHandleEvent : 1; + bool mMayHaveManager : 1; + bool mChechedIfChrome : 1; + 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) + { + static_assert(sizeof(EventTargetChainFlags) <= sizeof(RawFlags), + "EventTargetChainFlags must not be bigger than the RawFlags"); + memcpy(this, &aRawFlags, sizeof(EventTargetChainFlags)); + } + } mFlags; + uint16_t mItemFlags; nsCOMPtr<nsISupports> mItemData; // Event retargeting must happen whenever mNewTarget is non-null. @@ -298,37 +396,50 @@ private: bool IsCurrentTargetChrome() { - if (!(mFlags & NS_TARGET_CHAIN_CHECKED_IF_CHROME)) { - mFlags |= NS_TARGET_CHAIN_CHECKED_IF_CHROME; + if (!mFlags.mChechedIfChrome) { + mFlags.mChechedIfChrome = true; if (IsEventTargetChrome(mTarget)) { - mFlags |= NS_TARGET_CHAIN_IS_CHROME_CONTENT; + mFlags.mIsChromeContent = true; } } - return !!(mFlags & NS_TARGET_CHAIN_IS_CHROME_CONTENT); + return mFlags.mIsChromeContent; } }; EventTargetChainItem::EventTargetChainItem(EventTarget* aTarget) : mTarget(aTarget) - , mFlags(0) , mItemFlags(0) { MOZ_ASSERT(!aTarget || mTarget == aTarget->GetTargetForEventTargetChain()); } void -EventTargetChainItem::PreHandleEvent(EventChainPreVisitor& aVisitor) +EventTargetChainItem::GetEventTargetParent(EventChainPreVisitor& aVisitor) { aVisitor.Reset(); - Unused << mTarget->PreHandleEvent(aVisitor); + Unused << mTarget->GetEventTargetParent(aVisitor); SetForceContentDispatch(aVisitor.mForceContentDispatch); SetWantsWillHandleEvent(aVisitor.mWantsWillHandleEvent); SetMayHaveListenerManager(aVisitor.mMayHaveListenerManager); + SetWantsPreHandleEvent(aVisitor.mWantsPreHandleEvent); + SetPreHandleEventOnly(aVisitor.mWantsPreHandleEvent && !aVisitor.mCanHandle); + SetRootOfClosedTree(aVisitor.mRootOfClosedTree); mItemFlags = aVisitor.mItemFlags; mItemData = aVisitor.mItemData; } void +EventTargetChainItem::PreHandleEvent(EventChainVisitor& aVisitor) +{ + if (!WantsPreHandleEvent()) { + return; + } + aVisitor.mItemFlags = mItemFlags; + aVisitor.mItemData = mItemData; + Unused << mTarget->PreHandleEvent(aVisitor); +} + +void EventTargetChainItem::PostHandleEvent(EventChainPostVisitor& aVisitor) { aVisitor.mItemFlags = mItemFlags; @@ -346,12 +457,17 @@ EventTargetChainItem::HandleEventTargetChain( // Save the target so that it can be restored later. nsCOMPtr<EventTarget> firstTarget = aVisitor.mEvent->mTarget; uint32_t chainLength = aChain.Length(); + uint32_t firstCanHandleEventTargetIdx = + EventTargetChainItem::GetFirstCanHandleEventTargetIdx(aChain); // Capture aVisitor.mEvent->mFlags.mInCapturePhase = true; aVisitor.mEvent->mFlags.mInBubblingPhase = false; - for (uint32_t i = chainLength - 1; i > 0; --i) { + for (uint32_t i = chainLength - 1; i > firstCanHandleEventTargetIdx; --i) { EventTargetChainItem& item = aChain[i]; + if (item.PreHandleEventOnly()) { + continue; + } if ((!aVisitor.mEvent->mFlags.mNoContentDispatch || item.ForceContentDispatch()) && !aVisitor.mEvent->PropagationStopped()) { @@ -373,7 +489,7 @@ EventTargetChainItem::HandleEventTargetChain( // Target aVisitor.mEvent->mFlags.mInBubblingPhase = true; - EventTargetChainItem& targetItem = aChain[0]; + EventTargetChainItem& targetItem = aChain[firstCanHandleEventTargetIdx]; if (!aVisitor.mEvent->PropagationStopped() && (!aVisitor.mEvent->mFlags.mNoContentDispatch || targetItem.ForceContentDispatch())) { @@ -385,8 +501,11 @@ EventTargetChainItem::HandleEventTargetChain( // Bubble aVisitor.mEvent->mFlags.mInCapturePhase = false; - for (uint32_t i = 1; i < chainLength; ++i) { + for (uint32_t i = firstCanHandleEventTargetIdx + 1; i < chainLength; ++i) { EventTargetChainItem& item = aChain[i]; + if (item.PreHandleEventOnly()) { + continue; + } EventTarget* newTarget = item.GetNewTarget(); if (newTarget) { // Item is at anonymous boundary. Need to retarget for the current item @@ -471,6 +590,28 @@ EventTargetChainItemForChromeTarget(nsTArray<EventTargetChainItem>& aChain, return etci; } +/* static */ EventTargetChainItem* +MayRetargetToChromeIfCanNotHandleEvent( + nsTArray<EventTargetChainItem>& aChain, EventChainPreVisitor& aPreVisitor, + EventTargetChainItem* aTargetEtci, EventTargetChainItem* aChildEtci, + nsINode* aContent) +{ + if (!aPreVisitor.mWantsPreHandleEvent) { + // Keep EventTargetChainItem if we need to call PreHandleEvent on it. + EventTargetChainItem::DestroyLast(aChain, aTargetEtci); + } + if (aPreVisitor.mAutomaticChromeDispatch && aContent) { + // Event target couldn't handle the event. Try to propagate to chrome. + EventTargetChainItem* chromeTargetEtci = + EventTargetChainItemForChromeTarget(aChain, aContent, aChildEtci); + if (chromeTargetEtci) { + chromeTargetEtci->GetEventTargetParent(aPreVisitor); + return chromeTargetEtci; + } + } + return nullptr; +} + /* static */ nsresult EventDispatcher::Dispatch(nsISupports* aTarget, nsPresContext* aPresContext, @@ -593,7 +734,6 @@ EventDispatcher::Dispatch(nsISupports* aTarget, // Create the event target chain item for the event target. EventTargetChainItem* targetEtci = EventTargetChainItem::Create(chain, target->GetTargetForEventTargetChain()); - MOZ_ASSERT(&chain[0] == targetEtci); if (!targetEtci->IsValid()) { EventTargetChainItem::DestroyLast(chain, targetEtci); return NS_ERROR_FAILURE; @@ -631,37 +771,43 @@ EventDispatcher::Dispatch(nsISupports* aTarget, aEvent->mFlags.mIsBeingDispatched = true; // Create visitor object and start event dispatching. - // PreHandleEvent for the original target. + // GetEventTargetParent for the original target. nsEventStatus status = aEventStatus ? *aEventStatus : nsEventStatus_eIgnore; EventChainPreVisitor preVisitor(aPresContext, aEvent, aDOMEvent, status, isInAnon); - targetEtci->PreHandleEvent(preVisitor); - - if (!preVisitor.mCanHandle && preVisitor.mAutomaticChromeDispatch && content) { - // Event target couldn't handle the event. Try to propagate to chrome. - EventTargetChainItem::DestroyLast(chain, targetEtci); - targetEtci = EventTargetChainItemForChromeTarget(chain, content); - NS_ENSURE_STATE(targetEtci); - MOZ_ASSERT(&chain[0] == targetEtci); - targetEtci->PreHandleEvent(preVisitor); - } - if (preVisitor.mCanHandle) { + targetEtci->GetEventTargetParent(preVisitor); + + if (!preVisitor.mCanHandle) { + targetEtci = MayRetargetToChromeIfCanNotHandleEvent(chain, preVisitor, + targetEtci, nullptr, + content); + } + if (!preVisitor.mCanHandle) { + // The original target and chrome target (mAutomaticChromeDispatch=true) + // can not handle the event but we still have to call their PreHandleEvent. + for (uint32_t i = 0; i < chain.Length(); ++i) { + chain[i].PreHandleEvent(preVisitor); + } + } else { // At least the original target can handle the event. // Setting the retarget to the |target| simplifies retargeting code. nsCOMPtr<EventTarget> t = do_QueryInterface(aEvent->mTarget); 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 @@ -670,29 +816,22 @@ EventDispatcher::Dispatch(nsISupports* aTarget, parentEtci->SetNewTarget(preVisitor.mEventTargetAtParent); } - parentEtci->PreHandleEvent(preVisitor); + parentEtci->GetEventTargetParent(preVisitor); if (preVisitor.mCanHandle) { topEtci = parentEtci; } else { - EventTargetChainItem::DestroyLast(chain, parentEtci); - parentEtci = nullptr; - if (preVisitor.mAutomaticChromeDispatch && content) { - // Even if the current target can't handle the event, try to - // propagate to chrome. - nsCOMPtr<nsINode> disabledTarget = do_QueryInterface(parentTarget); - if (disabledTarget) { - parentEtci = EventTargetChainItemForChromeTarget(chain, - disabledTarget, - topEtci); - if (parentEtci) { - parentEtci->PreHandleEvent(preVisitor); - if (preVisitor.mCanHandle) { - chain[0].SetNewTarget(parentTarget); - topEtci = parentEtci; - continue; - } - } - } + nsCOMPtr<nsINode> disabledTarget = do_QueryInterface(parentTarget); + parentEtci = MayRetargetToChromeIfCanNotHandleEvent(chain, + preVisitor, + parentEtci, + topEtci, + disabledTarget); + if (parentEtci && preVisitor.mCanHandle) { + EventTargetChainItem* item = + EventTargetChainItem::GetFirstCanHandleEventTarget(chain); + item->SetNewTarget(parentTarget); + topEtci = parentEtci; + continue; } break; } @@ -706,11 +845,19 @@ EventDispatcher::Dispatch(nsISupports* aTarget, targets[i] = chain[i].CurrentTarget()->GetTargetForDOMEvent(); } } else { - // Event target chain is created. Handle the chain. + // Event target chain is created. PreHandle the chain. + for (uint32_t i = 0; i < chain.Length(); ++i) { + chain[i].PreHandleEvent(preVisitor); + } + // 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) { @@ -1022,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 |