diff options
author | Matt A. Tobin <email@mattatobin.com> | 2020-04-14 21:24:51 -0400 |
---|---|---|
committer | Matt A. Tobin <email@mattatobin.com> | 2020-04-14 21:25:54 -0400 |
commit | f2e872884b4a014bacc5a6be744dd7dec16afb24 (patch) | |
tree | 07b5c3969b8be3992d9442b58cf925869576e34e /dom | |
parent | 368c9a433edafbebef9797a8c7c1cbe281e29d95 (diff) | |
download | aura-central-f2e872884b4a014bacc5a6be744dd7dec16afb24.tar.gz |
Bug 1305458 - Changing -moz-appearence on hover breaks change event
* Rename nsIDOMEventTarget::PreHandleEvent to nsIDOMEventTarget::GetEventTargetParent
* Add nsIDOMEventTarget::PreHandleEvent
* Add EventTargetChainItem::GetFirstEventTarget
* Call EventTargetChainItem::PreHandleEvent even it sets mCanHandle=false
* Move form control frame focus/blur from nsGenericHTMLFormElement::GetEventTargetParent to PreHandleEvent
* Move fire change event from HTMLTextAreaElement::GetEventTargetParent to PreHandleEvent
* Refine nsXULElement::GetEventTargetParent
* Move dispatch XUL command from nsXULElement::GetEventTargetParent to PreHandleEvent
* Move fire events and set value from HTMLInputElement::GetEventTargetParent to PreHandleEvent
* Add test case
* Let HTMLInputElement delegate event handling to it's parent class
* Refine EventTargetChain flags to reduce overheads
* Refine event target chain creation
* Refine assertion in EventTargetChainItem::Create
Tag mcp-graveyard/UXP%1375
Diffstat (limited to 'dom')
70 files changed, 547 insertions, 258 deletions
diff --git a/dom/archivereader/ArchiveRequest.cpp b/dom/archivereader/ArchiveRequest.cpp index ec1686804..c4b19f56b 100644 --- a/dom/archivereader/ArchiveRequest.cpp +++ b/dom/archivereader/ArchiveRequest.cpp @@ -69,7 +69,7 @@ ArchiveRequest::~ArchiveRequest() } nsresult -ArchiveRequest::PreHandleEvent(EventChainPreVisitor& aVisitor) +ArchiveRequest::GetEventTargetParent(EventChainPreVisitor& aVisitor) { aVisitor.mCanHandle = true; aVisitor.mParentTarget = nullptr; diff --git a/dom/archivereader/ArchiveRequest.h b/dom/archivereader/ArchiveRequest.h index 3988f1aa1..3081a3e42 100644 --- a/dom/archivereader/ArchiveRequest.h +++ b/dom/archivereader/ArchiveRequest.h @@ -38,7 +38,8 @@ public: ArchiveReader* aReader); // nsIDOMEventTarget - virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override; + virtual nsresult GetEventTargetParent( + EventChainPreVisitor& aVisitor) override; public: // This is called by the DOMArchiveRequestEvent diff --git a/dom/base/Attr.cpp b/dom/base/Attr.cpp index 71b559392..df05ef5cb 100644 --- a/dom/base/Attr.cpp +++ b/dom/base/Attr.cpp @@ -344,7 +344,7 @@ Attr::RemoveChildAt(uint32_t aIndex, bool aNotify) } nsresult -Attr::PreHandleEvent(EventChainPreVisitor& aVisitor) +Attr::GetEventTargetParent(EventChainPreVisitor& aVisitor) { aVisitor.mCanHandle = true; return NS_OK; diff --git a/dom/base/Attr.h b/dom/base/Attr.h index 3f80030d2..39ac8b195 100644 --- a/dom/base/Attr.h +++ b/dom/base/Attr.h @@ -54,7 +54,7 @@ public: // nsIDOMAttr interface NS_DECL_NSIDOMATTR - virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override; + virtual nsresult GetEventTargetParent(EventChainPreVisitor& aVisitor) override; // nsIAttribute interface void SetMap(nsDOMAttributeMap *aMap) override; diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp index c8467e036..5a92853ae 100644 --- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -3129,7 +3129,7 @@ Element::CheckHandleEventForLinksPrecondition(EventChainVisitor& aVisitor, } nsresult -Element::PreHandleEventForLinks(EventChainPreVisitor& aVisitor) +Element::GetEventTargetParentForLinks(EventChainPreVisitor& aVisitor) { // Optimisation: return early if this event doesn't interest us. // IMPORTANT: this switch and the switch below it must be kept in sync! @@ -3151,8 +3151,9 @@ Element::PreHandleEventForLinks(EventChainPreVisitor& aVisitor) nsresult rv = NS_OK; - // We do the status bar updates in PreHandleEvent so that the status bar gets - // updated even if the event is consumed before we have a chance to set it. + // We do the status bar updates in GetEventTargetParent so that the status bar + // gets updated even if the event is consumed before we have a chance to set + // it. switch (aVisitor.mEvent->mMessage) { // Set the status bar similarly for mouseover and focus case eMouseOver: diff --git a/dom/base/Element.h b/dom/base/Element.h index 782004703..0104d795c 100644 --- a/dom/base/Element.h +++ b/dom/base/Element.h @@ -1400,7 +1400,7 @@ protected: /** * Handle status bar updates before they can be cancelled. */ - nsresult PreHandleEventForLinks(EventChainPreVisitor& aVisitor); + nsresult GetEventTargetParentForLinks(EventChainPreVisitor& aVisitor); /** * Handle default actions for link event if the event isn't consumed yet. diff --git a/dom/base/FragmentOrElement.cpp b/dom/base/FragmentOrElement.cpp index 526c3c9d4..ca00a49a5 100644 --- a/dom/base/FragmentOrElement.cpp +++ b/dom/base/FragmentOrElement.cpp @@ -718,7 +718,7 @@ FindChromeAccessOnlySubtreeOwner(nsIContent* aContent) } nsresult -nsIContent::PreHandleEvent(EventChainPreVisitor& aVisitor) +nsIContent::GetEventTargetParent(EventChainPreVisitor& aVisitor) { //FIXME! Document how this event retargeting works, Bug 329124. aVisitor.mCanHandle = true; diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index b05bf827b..45c80ca7f 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -7712,7 +7712,7 @@ nsDocument::GetExistingListenerManager() const } nsresult -nsDocument::PreHandleEvent(EventChainPreVisitor& aVisitor) +nsDocument::GetEventTargetParent(EventChainPreVisitor& aVisitor) { aVisitor.mCanHandle = true; // FIXME! This is a hack to make middle mouse paste working also in Editor. diff --git a/dom/base/nsDocument.h b/dom/base/nsDocument.h index 90e511dcb..79b7f18f4 100644 --- a/dom/base/nsDocument.h +++ b/dom/base/nsDocument.h @@ -810,7 +810,7 @@ public: NS_DECL_NSIDOMDOCUMENTXBL // nsIDOMEventTarget - virtual nsresult PreHandleEvent( + virtual nsresult GetEventTargetParent( mozilla::EventChainPreVisitor& aVisitor) override; virtual mozilla::EventListenerManager* GetOrCreateListenerManager() override; diff --git a/dom/base/nsGenConImageContent.cpp b/dom/base/nsGenConImageContent.cpp index e1b1f5a17..af3b186bd 100644 --- a/dom/base/nsGenConImageContent.cpp +++ b/dom/base/nsGenConImageContent.cpp @@ -46,7 +46,7 @@ public: virtual void UnbindFromTree(bool aDeep, bool aNullParent) override; virtual EventStates IntrinsicState() const override; - virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override + virtual nsresult GetEventTargetParent(EventChainPreVisitor& aVisitor) override { MOZ_ASSERT(IsInNativeAnonymousSubtree()); if (aVisitor.mEvent->mMessage == eLoad || @@ -54,7 +54,7 @@ public: // Don't propagate the events to the parent. return NS_OK; } - return nsXMLElement::PreHandleEvent(aVisitor); + return nsXMLElement::GetEventTargetParent(aVisitor); } private: diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index c965d5b97..802b44f38 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -3577,9 +3577,10 @@ nsGlobalWindow::WillHandleEvent(EventChainPostVisitor& aVisitor) } nsresult -nsGlobalWindow::PreHandleEvent(EventChainPreVisitor& aVisitor) +nsGlobalWindow::GetEventTargetParent(EventChainPreVisitor& aVisitor) { - NS_PRECONDITION(IsInnerWindow(), "PreHandleEvent is used on outer window!?"); + NS_PRECONDITION(IsInnerWindow(), + "GetEventTargetParent is used on outer window!?"); EventMessage msg = aVisitor.mEvent->mMessage; aVisitor.mCanHandle = true; @@ -3812,7 +3813,7 @@ nsGlobalWindow::PostHandleEvent(EventChainPostVisitor& aVisitor) } else if (aVisitor.mEvent->mMessage == eLoad && aVisitor.mEvent->IsTrusted()) { // This is page load event since load events don't propagate to |window|. - // @see nsDocument::PreHandleEvent. + // @see nsDocument::GetEventTargetParent. mIsDocumentLoaded = true; nsCOMPtr<Element> element = GetOuterWindow()->GetFrameElementInternal(); diff --git a/dom/base/nsIContent.h b/dom/base/nsIContent.h index dcdc632b4..e179d6ebc 100644 --- a/dom/base/nsIContent.h +++ b/dom/base/nsIContent.h @@ -947,7 +947,7 @@ public: // Overloaded from nsINode virtual already_AddRefed<nsIURI> GetBaseURI(bool aTryUseXHRDocBaseURI = false) const override; - virtual nsresult PreHandleEvent( + virtual nsresult GetEventTargetParent( mozilla::EventChainPreVisitor& aVisitor) override; virtual bool IsPurple() = 0; diff --git a/dom/base/nsINode.cpp b/dom/base/nsINode.cpp index 212110b72..0b56f519d 100644 --- a/dom/base/nsINode.cpp +++ b/dom/base/nsINode.cpp @@ -1243,7 +1243,7 @@ nsINode::RemoveEventListener(const nsAString& aType, NS_IMPL_REMOVE_SYSTEM_EVENT_LISTENER(nsINode) nsresult -nsINode::PreHandleEvent(EventChainPreVisitor& aVisitor) +nsINode::GetEventTargetParent(EventChainPreVisitor& aVisitor) { // This is only here so that we can use the NS_DECL_NSIDOMTARGET macro NS_ABORT(); diff --git a/dom/base/nsInProcessTabChildGlobal.cpp b/dom/base/nsInProcessTabChildGlobal.cpp index 10ccf4aec..547bb8d36 100644 --- a/dom/base/nsInProcessTabChildGlobal.cpp +++ b/dom/base/nsInProcessTabChildGlobal.cpp @@ -97,7 +97,7 @@ nsInProcessTabChildGlobal::nsInProcessTabChildGlobal(nsIDocShell* aShell, mozilla::HoldJSObjects(this); // If owner corresponds to an <iframe mozbrowser> or <iframe mozapp>, we'll - // have to tweak our PreHandleEvent implementation. + // GetEventTargetParent implementation. nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwner); if (browserFrame) { mIsBrowserOrAppFrame = browserFrame->GetReallyIsBrowserOrApp(); @@ -251,7 +251,7 @@ nsInProcessTabChildGlobal::GetOwnerContent() } nsresult -nsInProcessTabChildGlobal::PreHandleEvent(EventChainPreVisitor& aVisitor) +nsInProcessTabChildGlobal::GetEventTargetParent(EventChainPreVisitor& aVisitor) { aVisitor.mForceContentDispatch = true; aVisitor.mCanHandle = true; diff --git a/dom/base/nsInProcessTabChildGlobal.h b/dom/base/nsInProcessTabChildGlobal.h index e7dd9cb5a..55ffc5965 100644 --- a/dom/base/nsInProcessTabChildGlobal.h +++ b/dom/base/nsInProcessTabChildGlobal.h @@ -96,7 +96,7 @@ public: JS::Handle<JSObject *> aCpows, nsIPrincipal* aPrincipal) override; - virtual nsresult PreHandleEvent( + virtual nsresult GetEventTargetParent( mozilla::EventChainPreVisitor& aVisitor) override; NS_IMETHOD AddEventListener(const nsAString& aType, nsIDOMEventListener* aListener, @@ -168,7 +168,7 @@ protected: // Is this the message manager for an in-process <iframe mozbrowser> or // <iframe mozapp>? This affects where events get sent, so it affects - // PreHandleEvent. + // GetEventTargetParent. bool mIsBrowserOrAppFrame; bool mPreventEventsEscaping; diff --git a/dom/base/nsWindowRoot.cpp b/dom/base/nsWindowRoot.cpp index b839a0ee5..b629ab86f 100644 --- a/dom/base/nsWindowRoot.cpp +++ b/dom/base/nsWindowRoot.cpp @@ -180,7 +180,7 @@ nsWindowRoot::GetContextForEventHandlers(nsresult* aRv) } nsresult -nsWindowRoot::PreHandleEvent(EventChainPreVisitor& aVisitor) +nsWindowRoot::GetEventTargetParent(EventChainPreVisitor& aVisitor) { aVisitor.mCanHandle = true; aVisitor.mForceContentDispatch = true; //FIXME! Bug 329119 diff --git a/dom/events/DOMEventTargetHelper.cpp b/dom/events/DOMEventTargetHelper.cpp index dd9a01d8d..ee03463ef 100644 --- a/dom/events/DOMEventTargetHelper.cpp +++ b/dom/events/DOMEventTargetHelper.cpp @@ -328,7 +328,7 @@ DOMEventTargetHelper::GetEventHandler(nsIAtom* aType, } nsresult -DOMEventTargetHelper::PreHandleEvent(EventChainPreVisitor& aVisitor) +DOMEventTargetHelper::GetEventTargetParent(EventChainPreVisitor& aVisitor) { aVisitor.mCanHandle = true; aVisitor.mParentTarget = nullptr; diff --git a/dom/events/DOMEventTargetHelper.h b/dom/events/DOMEventTargetHelper.h index c5a0611c9..b14f05428 100644 --- a/dom/events/DOMEventTargetHelper.h +++ b/dom/events/DOMEventTargetHelper.h @@ -248,10 +248,10 @@ NS_DEFINE_STATIC_IID_ACCESSOR(DOMEventTargetHelper, /* Use this macro to declare functions that forward the behavior of this * interface to another object. - * This macro doesn't forward PreHandleEvent because sometimes subclasses + * This macro doesn't forward GetEventTargetParent because sometimes subclasses * want to override it. */ -#define NS_FORWARD_NSIDOMEVENTTARGET_NOPREHANDLEEVENT(_to) \ +#define NS_FORWARD_NSIDOMEVENTTARGET_NOGETEVENTTARGETPARENT(_to) \ NS_IMETHOD AddEventListener(const nsAString & type, nsIDOMEventListener *listener, bool useCapture, bool wantsUntrusted, uint8_t _argc) { \ return _to AddEventListener(type, listener, useCapture, wantsUntrusted, _argc); \ } \ diff --git a/dom/events/EventDispatcher.cpp b/dom/events/EventDispatcher.cpp index 1d4dfd7d9..740e611e4 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,52 @@ 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 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 +273,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 +326,34 @@ 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; + 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 +363,49 @@ 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); 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 +423,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 +455,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 +467,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 +556,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 +700,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,21 +737,24 @@ 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); @@ -670,29 +779,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,7 +808,11 @@ 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); EventTargetChainItem::HandleEventTargetChain(chain, postVisitor, aCallback, cd); diff --git a/dom/events/EventDispatcher.h b/dom/events/EventDispatcher.h index 3c754033d..db7b47dbf 100644 --- a/dom/events/EventDispatcher.h +++ b/dom/events/EventDispatcher.h @@ -31,14 +31,14 @@ class EventTarget; * About event dispatching: * When either EventDispatcher::Dispatch or * EventDispatcher::DispatchDOMEvent is called an event target chain is - * created. EventDispatcher creates the chain by calling PreHandleEvent + * created. EventDispatcher creates the chain by calling GetEventTargetParent * on each event target and the creation continues until either the mCanHandle * member of the EventChainPreVisitor object is false or the mParentTarget * does not point to a new target. The event target chain is created in the * heap. * * If the event needs retargeting, mEventTargetAtParent must be set in - * PreHandleEvent. + * GetEventTargetParent. * * The capture, target and bubble phases of the event dispatch are handled * by iterating through the event target chain. Iteration happens twice, @@ -86,7 +86,7 @@ public: /** * Bits for items in the event target chain. - * Set in PreHandleEvent() and used in PostHandleEvent(). + * Set in GetEventTargetParent() and used in PostHandleEvent(). * * @note These bits are different for each item in the event target chain. * It is up to the Pre/PostHandleEvent implementation to decide how to @@ -98,7 +98,7 @@ public: /** * Data for items in the event target chain. - * Set in PreHandleEvent() and used in PostHandleEvent(). + * Set in GetEventTargetParent() and used in PostHandleEvent(). * * @note This data is different for each item in the event target chain. * It is up to the Pre/PostHandleEvent implementation to decide how to @@ -123,6 +123,7 @@ public: , mOriginalTargetIsInAnon(aIsInAnon) , mWantsWillHandleEvent(false) , mMayHaveListenerManager(true) + , mWantsPreHandleEvent(false) , mParentTarget(nullptr) , mEventTargetAtParent(nullptr) { @@ -137,13 +138,14 @@ public: mForceContentDispatch = false; mWantsWillHandleEvent = false; mMayHaveListenerManager = true; + mWantsPreHandleEvent = false; mParentTarget = nullptr; mEventTargetAtParent = nullptr; } /** - * Member that must be set in PreHandleEvent by event targets. If set to false, - * indicates that this event target will not be handling the event and + * 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 * construction of the event target chain is complete. The target that sets * mCanHandle to false is NOT included in the event target chain. */ @@ -170,7 +172,7 @@ public: /** * true if the original target of the event is inside anonymous content. - * This is set before calling PreHandleEvent on event targets. + * This is set before calling GetEventTargetParent on event targets. */ bool mOriginalTargetIsInAnon; @@ -182,11 +184,17 @@ public: /** * If it is known that the current target doesn't have a listener manager - * when PreHandleEvent is called, set this to false. + * when GetEventTargetParent is called, set this to false. */ bool mMayHaveListenerManager; /** + * Whether or not nsIDOMEventTarget::PreHandleEvent will be called. Default is + * false; + */ + bool mWantsPreHandleEvent; + + /** * Parent item in the event target chain. */ dom::EventTarget* mParentTarget; diff --git a/dom/events/test/mochitest.ini b/dom/events/test/mochitest.ini index 0397487bb..27e8e7150 100644 --- a/dom/events/test/mochitest.ini +++ b/dom/events/test/mochitest.ini @@ -185,3 +185,4 @@ skip-if = toolkit == 'android' #CRASH_DUMP, RANDOM [test_bug687787.html] [test_bug1298970.html] [test_bug1304044.html] +[test_bug1305458.html] diff --git a/dom/events/test/test_bug1305458.html b/dom/events/test/test_bug1305458.html new file mode 100644 index 000000000..df65959a9 --- /dev/null +++ b/dom/events/test/test_bug1305458.html @@ -0,0 +1,50 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1305458 +--> +<head> + <title>Test for Bug 1305458</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <style> + input[type=number] { + -moz-appearance: textfield; + } + input[type=number]:focus, + input[type=number]:hover { + -moz-appearance: number-input; + } + </style> +</head> +<body onload="doTest()"> + <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1305458">Mozilla Bug 1305458</a> + <input id="test_input" type="number"> + <div id="test_div">bar</div> + <script> + SimpleTest.waitForExplicitFinish(); + var change_count = 0; + function doTest() { + let input = document.getElementById("test_input"); + let div = document.getElementById("test_div"); + input.addEventListener("change", () => { + ++change_count; + }, false); + // mouse hover + input.focus(); + synthesizeMouse(input, 1, 1, {type: "mousemove"}); + synthesizeKey("1", {}); + input.blur(); + is(change_count, 1, "input should fire change when blur"); + + input.focus(); + synthesizeMouse(div, 1, 1, {type: "mousemove"}); + synthesizeKey("1", {}); + input.blur(); + is(change_count, 2, "input should fire change when blur"); + SimpleTest.finish(); + } + </script> +</body> +</html> diff --git a/dom/html/HTMLAnchorElement.cpp b/dom/html/HTMLAnchorElement.cpp index a6cfacc53..501d6e3d0 100644 --- a/dom/html/HTMLAnchorElement.cpp +++ b/dom/html/HTMLAnchorElement.cpp @@ -252,9 +252,9 @@ HTMLAnchorElement::IsHTMLFocusable(bool aWithMouse, } nsresult -HTMLAnchorElement::PreHandleEvent(EventChainPreVisitor& aVisitor) +HTMLAnchorElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) { - return PreHandleEventForAnchors(aVisitor); + return GetEventTargetParentForAnchors(aVisitor); } nsresult diff --git a/dom/html/HTMLAnchorElement.h b/dom/html/HTMLAnchorElement.h index 2cb04ad93..027d8d689 100644 --- a/dom/html/HTMLAnchorElement.h +++ b/dom/html/HTMLAnchorElement.h @@ -58,7 +58,8 @@ public: bool aNullParent = true) override; virtual bool IsHTMLFocusable(bool aWithMouse, bool *aIsFocusable, int32_t *aTabIndex) override; - virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override; + virtual nsresult GetEventTargetParent( + EventChainPreVisitor& aVisitor) override; virtual nsresult PostHandleEvent( EventChainPostVisitor& aVisitor) override; virtual bool IsLink(nsIURI** aURI) const override; diff --git a/dom/html/HTMLAreaElement.cpp b/dom/html/HTMLAreaElement.cpp index 098081b8b..213d4831d 100644 --- a/dom/html/HTMLAreaElement.cpp +++ b/dom/html/HTMLAreaElement.cpp @@ -81,9 +81,9 @@ HTMLAreaElement::SetTarget(const nsAString& aValue) } nsresult -HTMLAreaElement::PreHandleEvent(EventChainPreVisitor& aVisitor) +HTMLAreaElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) { - return PreHandleEventForAnchors(aVisitor); + return GetEventTargetParentForAnchors(aVisitor); } nsresult diff --git a/dom/html/HTMLAreaElement.h b/dom/html/HTMLAreaElement.h index 650c0fd8f..c2ed7a928 100644 --- a/dom/html/HTMLAreaElement.h +++ b/dom/html/HTMLAreaElement.h @@ -44,7 +44,8 @@ public: // nsIDOMHTMLAreaElement NS_DECL_NSIDOMHTMLAREAELEMENT - virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override; + virtual nsresult GetEventTargetParent( + EventChainPreVisitor& aVisitor) override; virtual nsresult PostHandleEvent(EventChainPostVisitor& aVisitor) override; virtual bool IsLink(nsIURI** aURI) const override; virtual void GetLinkTarget(nsAString& aTarget) override; diff --git a/dom/html/HTMLButtonElement.cpp b/dom/html/HTMLButtonElement.cpp index 435aa9f7f..aad1c412b 100644 --- a/dom/html/HTMLButtonElement.cpp +++ b/dom/html/HTMLButtonElement.cpp @@ -207,7 +207,7 @@ HTMLButtonElement::IsDisabledForEvents(EventMessage aMessage) } nsresult -HTMLButtonElement::PreHandleEvent(EventChainPreVisitor& aVisitor) +HTMLButtonElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) { aVisitor.mCanHandle = false; if (IsDisabledForEvents(aVisitor.mEvent->mMessage)) { @@ -235,7 +235,7 @@ HTMLButtonElement::PreHandleEvent(EventChainPreVisitor& aVisitor) } } - return nsGenericHTMLElement::PreHandleEvent(aVisitor); + return nsGenericHTMLElement::GetEventTargetParent(aVisitor); } nsresult diff --git a/dom/html/HTMLButtonElement.h b/dom/html/HTMLButtonElement.h index ecd9e03d7..a8d206dd6 100644 --- a/dom/html/HTMLButtonElement.h +++ b/dom/html/HTMLButtonElement.h @@ -57,7 +57,8 @@ public: virtual void FieldSetDisabledChanged(bool aNotify) override; // nsIDOMEventTarget - virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override; + virtual nsresult GetEventTargetParent( + EventChainPreVisitor& aVisitor) override; virtual nsresult PostHandleEvent( EventChainPostVisitor& aVisitor) override; diff --git a/dom/html/HTMLCanvasElement.cpp b/dom/html/HTMLCanvasElement.cpp index 4b5deab18..803c60e8a 100644 --- a/dom/html/HTMLCanvasElement.cpp +++ b/dom/html/HTMLCanvasElement.cpp @@ -574,7 +574,7 @@ HTMLCanvasElement::CopyInnerTo(Element* aDest) return rv; } -nsresult HTMLCanvasElement::PreHandleEvent(EventChainPreVisitor& aVisitor) +nsresult HTMLCanvasElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) { if (aVisitor.mEvent->mClass == eMouseEventClass) { WidgetMouseEventBase* evt = (WidgetMouseEventBase*)aVisitor.mEvent; @@ -592,7 +592,7 @@ nsresult HTMLCanvasElement::PreHandleEvent(EventChainPreVisitor& aVisitor) aVisitor.mCanHandle = true; } } - return nsGenericHTMLElement::PreHandleEvent(aVisitor); + return nsGenericHTMLElement::GetEventTargetParent(aVisitor); } nsChangeHint diff --git a/dom/html/HTMLCanvasElement.h b/dom/html/HTMLCanvasElement.h index e77db6ff1..de26c475a 100644 --- a/dom/html/HTMLCanvasElement.h +++ b/dom/html/HTMLCanvasElement.h @@ -312,7 +312,8 @@ public: virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; nsresult CopyInnerTo(mozilla::dom::Element* aDest); - virtual nsresult PreHandleEvent(mozilla::EventChainPreVisitor& aVisitor) override; + virtual nsresult GetEventTargetParent( + mozilla::EventChainPreVisitor& aVisitor) override; /* * Helpers called by various users of Canvas diff --git a/dom/html/HTMLFieldSetElement.cpp b/dom/html/HTMLFieldSetElement.cpp index d72fd1061..c008b9b6d 100644 --- a/dom/html/HTMLFieldSetElement.cpp +++ b/dom/html/HTMLFieldSetElement.cpp @@ -70,7 +70,7 @@ HTMLFieldSetElement::IsDisabledForEvents(EventMessage aMessage) // nsIContent nsresult -HTMLFieldSetElement::PreHandleEvent(EventChainPreVisitor& aVisitor) +HTMLFieldSetElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) { // Do not process any DOM events if the element is disabled. aVisitor.mCanHandle = false; @@ -78,7 +78,7 @@ HTMLFieldSetElement::PreHandleEvent(EventChainPreVisitor& aVisitor) return NS_OK; } - return nsGenericHTMLFormElement::PreHandleEvent(aVisitor); + return nsGenericHTMLFormElement::GetEventTargetParent(aVisitor); } nsresult diff --git a/dom/html/HTMLFieldSetElement.h b/dom/html/HTMLFieldSetElement.h index 96fff4582..1e9f0214f 100644 --- a/dom/html/HTMLFieldSetElement.h +++ b/dom/html/HTMLFieldSetElement.h @@ -40,7 +40,8 @@ public: NS_DECL_NSIDOMHTMLFIELDSETELEMENT // nsIContent - virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override; + virtual nsresult GetEventTargetParent( + EventChainPreVisitor& aVisitor) override; virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, const nsAttrValue* aValue, bool aNotify) override; diff --git a/dom/html/HTMLFormElement.cpp b/dom/html/HTMLFormElement.cpp index 6bea19a2b..bdc05b053 100644 --- a/dom/html/HTMLFormElement.cpp +++ b/dom/html/HTMLFormElement.cpp @@ -489,7 +489,7 @@ HTMLFormElement::UnbindFromTree(bool aDeep, bool aNullParent) } nsresult -HTMLFormElement::PreHandleEvent(EventChainPreVisitor& aVisitor) +HTMLFormElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) { aVisitor.mWantsWillHandleEvent = true; if (aVisitor.mEvent->mOriginalTarget == static_cast<nsIContent*>(this)) { @@ -513,7 +513,7 @@ HTMLFormElement::PreHandleEvent(EventChainPreVisitor& aVisitor) mGeneratingReset = true; } } - return nsGenericHTMLElement::PreHandleEvent(aVisitor); + return nsGenericHTMLElement::GetEventTargetParent(aVisitor); } nsresult diff --git a/dom/html/HTMLFormElement.h b/dom/html/HTMLFormElement.h index b3e836f5f..e45d4c10d 100644 --- a/dom/html/HTMLFormElement.h +++ b/dom/html/HTMLFormElement.h @@ -93,7 +93,8 @@ public: nsIAtom* aAttribute, const nsAString& aValue, nsAttrValue& aResult) override; - virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override; + virtual nsresult GetEventTargetParent( + EventChainPreVisitor& aVisitor) override; virtual nsresult WillHandleEvent( EventChainPostVisitor& aVisitor) override; virtual nsresult PostHandleEvent( diff --git a/dom/html/HTMLImageElement.cpp b/dom/html/HTMLImageElement.cpp index fab1cdef4..49cbebc5a 100644 --- a/dom/html/HTMLImageElement.cpp +++ b/dom/html/HTMLImageElement.cpp @@ -437,7 +437,7 @@ HTMLImageElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, } nsresult -HTMLImageElement::PreHandleEvent(EventChainPreVisitor& aVisitor) +HTMLImageElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) { // We handle image element with attribute ismap in its corresponding frame // element. Set mMultipleActionsPrevented here to prevent the click event @@ -446,7 +446,7 @@ HTMLImageElement::PreHandleEvent(EventChainPreVisitor& aVisitor) if (mouseEvent && mouseEvent->IsLeftClickEvent() && IsMap()) { mouseEvent->mFlags.mMultipleActionsPrevented = true; } - return nsGenericHTMLElement::PreHandleEvent(aVisitor); + return nsGenericHTMLElement::GetEventTargetParent(aVisitor); } bool diff --git a/dom/html/HTMLImageElement.h b/dom/html/HTMLImageElement.h index 62323e801..41730c2e5 100644 --- a/dom/html/HTMLImageElement.h +++ b/dom/html/HTMLImageElement.h @@ -70,7 +70,8 @@ public: NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const override; virtual nsMapRuleToAttributesFunc GetAttributeMappingFunction() const override; - virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override; + virtual nsresult GetEventTargetParent( + EventChainPreVisitor& aVisitor) override; bool IsHTMLFocusable(bool aWithMouse, bool *aIsFocusable, int32_t *aTabIndex) override; diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp index 0b879bb9b..d5fca28f6 100644 --- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -147,6 +147,8 @@ namespace dom { #define NS_CONTROL_TYPE(bits) ((bits) & ~( \ NS_OUTER_ACTIVATE_EVENT | NS_ORIGINAL_CHECKED_VALUE | NS_NO_CONTENT_DISPATCH | \ NS_ORIGINAL_INDETERMINATE_VALUE)) +#define NS_PRE_HANDLE_BLUR_EVENT (1 << 13) +#define NS_PRE_HANDLE_INPUT_EVENT (1 << 14) // whether textfields should be selected once focused: // -1: no, 1: yes, 0: uninitialized @@ -3738,7 +3740,7 @@ HTMLInputElement::IsDisabledForEvents(EventMessage aMessage) } nsresult -HTMLInputElement::PreHandleEvent(EventChainPreVisitor& aVisitor) +HTMLInputElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) { // Do not process any DOM events if the element is disabled aVisitor.mCanHandle = false; @@ -3755,7 +3757,7 @@ HTMLInputElement::PreHandleEvent(EventChainPreVisitor& aVisitor) //FIXME Allow submission etc. also when there is no prescontext, Bug 329509. if (!aVisitor.mPresContext) { - return nsGenericHTMLElement::PreHandleEvent(aVisitor); + return nsGenericHTMLFormElementWithState::GetEventTargetParent(aVisitor); } // // Web pages expect the value of a radio button or checkbox to be set @@ -3865,23 +3867,16 @@ HTMLInputElement::PreHandleEvent(EventChainPreVisitor& aVisitor) // Fire onchange (if necessary), before we do the blur, bug 357684. if (aVisitor.mEvent->mMessage == eBlur) { - // Experimental mobile types rely on the system UI to prevent users to not - // set invalid values but we have to be extra-careful. Especially if the - // option has been enabled on desktop. - if (IsExperimentalMobileType(mType)) { - nsAutoString aValue; - GetValueInternal(aValue); - nsresult rv = - SetValueInternal(aValue, nsTextEditorState::eSetValue_Internal); - NS_ENSURE_SUCCESS(rv, rv); - } - FireChangeEventIfNeeded(); + // We set NS_PRE_HANDLE_BLUR_EVENT here and handle it in PreHandleEvent to + // prevent breaking event target chain creation. + aVisitor.mWantsPreHandleEvent = true; + aVisitor.mItemFlags |= NS_PRE_HANDLE_BLUR_EVENT; } if (mType == NS_FORM_INPUT_RANGE && (aVisitor.mEvent->mMessage == eFocus || aVisitor.mEvent->mMessage == eBlur)) { - // Just as nsGenericHTMLFormElementWithState::PreHandleEvent calls + // Just as nsGenericHTMLFormElementWithState::GetEventTargetParent calls // nsIFormControlFrame::SetFocus, we handle focus here. nsIFrame* frame = GetPrimaryFrame(); if (frame) { @@ -3969,10 +3964,11 @@ HTMLInputElement::PreHandleEvent(EventChainPreVisitor& aVisitor) } } - nsresult rv = nsGenericHTMLFormElementWithState::PreHandleEvent(aVisitor); + nsresult rv = nsGenericHTMLFormElementWithState::GetEventTargetParent(aVisitor); - // We do this after calling the base class' PreHandleEvent so that - // nsIContent::PreHandleEvent doesn't reset any change we make to mCanHandle. + // We do this after calling the base class' GetEventTargetParent so that + // nsIContent::GetEventTargetParent doesn't reset any change we make to + // mCanHandle. if (mType == NS_FORM_INPUT_NUMBER && aVisitor.mEvent->IsTrusted() && aVisitor.mEvent->mOriginalTarget != this) { @@ -3987,18 +3983,10 @@ HTMLInputElement::PreHandleEvent(EventChainPreVisitor& aVisitor) } if (textControl && aVisitor.mEvent->mOriginalTarget == textControl) { if (aVisitor.mEvent->mMessage == eEditorInput) { - // Propogate the anon text control's new value to our HTMLInputElement: - nsAutoString value; - numberControlFrame->GetValueOfAnonTextControl(value); - numberControlFrame->HandlingInputEvent(true); - nsWeakFrame weakNumberControlFrame(numberControlFrame); - rv = SetValueInternal(value, - nsTextEditorState::eSetValue_BySetUserInput | - nsTextEditorState::eSetValue_Notify); - NS_ENSURE_SUCCESS(rv, rv); - if (weakNumberControlFrame.IsAlive()) { - numberControlFrame->HandlingInputEvent(false); - } + aVisitor.mWantsPreHandleEvent = true; + // We set NS_PRE_HANDLE_INPUT_EVENT here and handle it in PreHandleEvent + // to prevent breaking event target chain creation. + aVisitor.mItemFlags |= NS_PRE_HANDLE_INPUT_EVENT; } else if (aVisitor.mEvent->mMessage == eFormChange) { // We cancel the DOM 'change' event that is fired for any change to our @@ -4037,6 +4025,50 @@ HTMLInputElement::PreHandleEvent(EventChainPreVisitor& aVisitor) return rv; } +nsresult +HTMLInputElement::PreHandleEvent(EventChainVisitor& aVisitor) +{ + if (!aVisitor.mPresContext) { + return nsGenericHTMLFormElementWithState::PreHandleEvent(aVisitor); + } + nsresult rv; + if (aVisitor.mItemFlags & NS_PRE_HANDLE_BLUR_EVENT) { + MOZ_ASSERT(aVisitor.mEvent->mMessage == eBlur); + // Experimental mobile types rely on the system UI to prevent users to not + // set invalid values but we have to be extra-careful. Especially if the + // option has been enabled on desktop. + if (IsExperimentalMobileType(mType)) { + nsAutoString aValue; + GetValueInternal(aValue); + nsresult rv = + SetValueInternal(aValue, nsTextEditorState::eSetValue_Internal); + NS_ENSURE_SUCCESS(rv, rv); + } + FireChangeEventIfNeeded(); + } + rv = nsGenericHTMLFormElementWithState::PreHandleEvent(aVisitor); + if (aVisitor.mItemFlags & NS_PRE_HANDLE_INPUT_EVENT) { + nsNumberControlFrame* numberControlFrame = do_QueryFrame(GetPrimaryFrame()); + MOZ_ASSERT(aVisitor.mEvent->mMessage == eEditorInput); + MOZ_ASSERT(numberControlFrame); + MOZ_ASSERT(numberControlFrame->GetAnonTextControl() == + aVisitor.mEvent->mOriginalTarget); + // Propogate the anon text control's new value to our HTMLInputElement: + nsAutoString value; + numberControlFrame->GetValueOfAnonTextControl(value); + numberControlFrame->HandlingInputEvent(true); + nsWeakFrame weakNumberControlFrame(numberControlFrame); + rv = SetValueInternal(value, + nsTextEditorState::eSetValue_BySetUserInput | + nsTextEditorState::eSetValue_Notify); + NS_ENSURE_SUCCESS(rv, rv); + if (weakNumberControlFrame.IsAlive()) { + numberControlFrame->HandlingInputEvent(false); + } + } + return rv; +} + void HTMLInputElement::StartRangeThumbDrag(WidgetGUIEvent* aEvent) { diff --git a/dom/html/HTMLInputElement.h b/dom/html/HTMLInputElement.h index 98a590443..98c74893e 100644 --- a/dom/html/HTMLInputElement.h +++ b/dom/html/HTMLInputElement.h @@ -185,7 +185,9 @@ public: NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const override; virtual nsMapRuleToAttributesFunc GetAttributeMappingFunction() const override; - virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override; + virtual nsresult GetEventTargetParent( + EventChainPreVisitor& aVisitor) override; + virtual nsresult PreHandleEvent(EventChainVisitor& aVisitor) override; virtual nsresult PostHandleEvent( EventChainPostVisitor& aVisitor) override; void PostHandleEventForRangeThumb(EventChainPostVisitor& aVisitor); diff --git a/dom/html/HTMLLinkElement.cpp b/dom/html/HTMLLinkElement.cpp index 8afe767bd..0a2cdaaf4 100644 --- a/dom/html/HTMLLinkElement.cpp +++ b/dom/html/HTMLLinkElement.cpp @@ -421,9 +421,9 @@ HTMLLinkElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, } nsresult -HTMLLinkElement::PreHandleEvent(EventChainPreVisitor& aVisitor) +HTMLLinkElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) { - return PreHandleEventForAnchors(aVisitor); + return GetEventTargetParentForAnchors(aVisitor); } nsresult diff --git a/dom/html/HTMLLinkElement.h b/dom/html/HTMLLinkElement.h index 421b149e9..1fa154f41 100644 --- a/dom/html/HTMLLinkElement.h +++ b/dom/html/HTMLLinkElement.h @@ -46,7 +46,8 @@ public: void UpdateImport(); // nsIDOMEventTarget - virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override; + virtual nsresult GetEventTargetParent( + EventChainPreVisitor& aVisitor) override; virtual nsresult PostHandleEvent( EventChainPostVisitor& aVisitor) override; diff --git a/dom/html/HTMLMenuItemElement.cpp b/dom/html/HTMLMenuItemElement.cpp index ca8bb4feb..6a8ad8174 100644 --- a/dom/html/HTMLMenuItemElement.cpp +++ b/dom/html/HTMLMenuItemElement.cpp @@ -252,7 +252,7 @@ HTMLMenuItemElement::SetChecked(bool aChecked) } nsresult -HTMLMenuItemElement::PreHandleEvent(EventChainPreVisitor& aVisitor) +HTMLMenuItemElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) { if (aVisitor.mEvent->mMessage == eMouseClick) { @@ -283,7 +283,7 @@ HTMLMenuItemElement::PreHandleEvent(EventChainPreVisitor& aVisitor) aVisitor.mItemFlags |= mType; } - return nsGenericHTMLElement::PreHandleEvent(aVisitor); + return nsGenericHTMLElement::GetEventTargetParent(aVisitor); } nsresult diff --git a/dom/html/HTMLMenuItemElement.h b/dom/html/HTMLMenuItemElement.h index 3c19b1170..1da63edd0 100644 --- a/dom/html/HTMLMenuItemElement.h +++ b/dom/html/HTMLMenuItemElement.h @@ -36,7 +36,8 @@ public: // nsIDOMHTMLMenuItemElement NS_DECL_NSIDOMHTMLMENUITEMELEMENT - virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override; + virtual nsresult GetEventTargetParent( + EventChainPreVisitor& aVisitor) override; virtual nsresult PostHandleEvent( EventChainPostVisitor& aVisitor) override; diff --git a/dom/html/HTMLOptGroupElement.cpp b/dom/html/HTMLOptGroupElement.cpp index 9e738961d..0424ccef3 100644 --- a/dom/html/HTMLOptGroupElement.cpp +++ b/dom/html/HTMLOptGroupElement.cpp @@ -48,7 +48,7 @@ NS_IMPL_STRING_ATTR(HTMLOptGroupElement, Label, label) nsresult -HTMLOptGroupElement::PreHandleEvent(EventChainPreVisitor& aVisitor) +HTMLOptGroupElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) { aVisitor.mCanHandle = false; // Do not process any DOM events if the element is disabled @@ -65,7 +65,7 @@ HTMLOptGroupElement::PreHandleEvent(EventChainPreVisitor& aVisitor) } } - return nsGenericHTMLElement::PreHandleEvent(aVisitor); + return nsGenericHTMLElement::GetEventTargetParent(aVisitor); } Element* diff --git a/dom/html/HTMLOptGroupElement.h b/dom/html/HTMLOptGroupElement.h index e46a6a953..c669f43b4 100644 --- a/dom/html/HTMLOptGroupElement.h +++ b/dom/html/HTMLOptGroupElement.h @@ -35,7 +35,8 @@ public: virtual void RemoveChildAt(uint32_t aIndex, bool aNotify) override; // nsIContent - virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override; + virtual nsresult GetEventTargetParent( + EventChainPreVisitor& aVisitor) override; virtual EventStates IntrinsicState() const override; diff --git a/dom/html/HTMLSelectElement.cpp b/dom/html/HTMLSelectElement.cpp index 9ba0a1efe..272a16578 100644 --- a/dom/html/HTMLSelectElement.cpp +++ b/dom/html/HTMLSelectElement.cpp @@ -1442,14 +1442,14 @@ HTMLSelectElement::IsDisabledForEvents(EventMessage aMessage) } nsresult -HTMLSelectElement::PreHandleEvent(EventChainPreVisitor& aVisitor) +HTMLSelectElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) { aVisitor.mCanHandle = false; if (IsDisabledForEvents(aVisitor.mEvent->mMessage)) { return NS_OK; } - return nsGenericHTMLFormElementWithState::PreHandleEvent(aVisitor); + return nsGenericHTMLFormElementWithState::GetEventTargetParent(aVisitor); } nsresult diff --git a/dom/html/HTMLSelectElement.h b/dom/html/HTMLSelectElement.h index dc1075cd7..4cb5f90c9 100644 --- a/dom/html/HTMLSelectElement.h +++ b/dom/html/HTMLSelectElement.h @@ -278,7 +278,8 @@ public: virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; // nsIContent - virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override; + virtual nsresult GetEventTargetParent( + EventChainPreVisitor& aVisitor) override; virtual nsresult PostHandleEvent( EventChainPostVisitor& aVisitor) override; diff --git a/dom/html/HTMLTextAreaElement.cpp b/dom/html/HTMLTextAreaElement.cpp index 42bc02435..796d8a49e 100644 --- a/dom/html/HTMLTextAreaElement.cpp +++ b/dom/html/HTMLTextAreaElement.cpp @@ -506,7 +506,7 @@ HTMLTextAreaElement::IsDisabledForEvents(EventMessage aMessage) } nsresult -HTMLTextAreaElement::PreHandleEvent(EventChainPreVisitor& aVisitor) +HTMLTextAreaElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) { aVisitor.mCanHandle = false; if (IsDisabledForEvents(aVisitor.mEvent->mMessage)) { @@ -534,11 +534,22 @@ HTMLTextAreaElement::PreHandleEvent(EventChainPreVisitor& aVisitor) aVisitor.mEvent->mFlags.mNoContentDispatch = false; } - // Fire onchange (if necessary), before we do the blur, bug 370521. if (aVisitor.mEvent->mMessage == eBlur) { - FireChangeEventIfNeeded(); + // Set mWantsPreHandleEvent and fire change event in PreHandleEvent to + // prevent it breaks event target chain creation. + aVisitor.mWantsPreHandleEvent = true; } + return nsGenericHTMLFormElementWithState::GetEventTargetParent(aVisitor); +} + +nsresult +HTMLTextAreaElement::PreHandleEvent(EventChainVisitor& aVisitor) +{ + if (aVisitor.mEvent->mMessage == eBlur) { + // Fire onchange (if necessary), before we do the blur, bug 370521. + FireChangeEventIfNeeded(); + } return nsGenericHTMLFormElementWithState::PreHandleEvent(aVisitor); } diff --git a/dom/html/HTMLTextAreaElement.h b/dom/html/HTMLTextAreaElement.h index 039082818..bfc7d203d 100644 --- a/dom/html/HTMLTextAreaElement.h +++ b/dom/html/HTMLTextAreaElement.h @@ -125,7 +125,9 @@ public: int32_t aModType) const override; NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const override; - virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override; + virtual nsresult GetEventTargetParent( + EventChainPreVisitor& aVisitor) override; + virtual nsresult PreHandleEvent(EventChainVisitor& aVisitor) override; virtual nsresult PostHandleEvent( EventChainPostVisitor& aVisitor) override; diff --git a/dom/html/nsGenericHTMLElement.cpp b/dom/html/nsGenericHTMLElement.cpp index 78e4d5b95..394a77a59 100644 --- a/dom/html/nsGenericHTMLElement.cpp +++ b/dom/html/nsGenericHTMLElement.cpp @@ -607,16 +607,16 @@ nsGenericHTMLElement::CheckHandleEventForAnchorsPreconditions( } nsresult -nsGenericHTMLElement::PreHandleEventForAnchors(EventChainPreVisitor& aVisitor) +nsGenericHTMLElement::GetEventTargetParentForAnchors(EventChainPreVisitor& aVisitor) { - nsresult rv = nsGenericHTMLElementBase::PreHandleEvent(aVisitor); + nsresult rv = nsGenericHTMLElementBase::GetEventTargetParent(aVisitor); NS_ENSURE_SUCCESS(rv, rv); if (!CheckHandleEventForAnchorsPreconditions(aVisitor)) { return NS_OK; } - return PreHandleEventForLinks(aVisitor); + return GetEventTargetParentForLinks(aVisitor); } nsresult @@ -2081,7 +2081,19 @@ nsGenericHTMLFormElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, } nsresult -nsGenericHTMLFormElement::PreHandleEvent(EventChainPreVisitor& aVisitor) +nsGenericHTMLFormElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) +{ + if (aVisitor.mEvent->IsTrusted() && (aVisitor.mEvent->mMessage == eFocus || + aVisitor.mEvent->mMessage == eBlur)) { + // We have to handle focus/blur event to change focus states in + // PreHandleEvent to prevent it breaks event target chain creation. + aVisitor.mWantsPreHandleEvent = true; + } + return nsGenericHTMLElement::GetEventTargetParent(aVisitor); +} + +nsresult +nsGenericHTMLFormElement::PreHandleEvent(EventChainVisitor& aVisitor) { if (aVisitor.mEvent->IsTrusted()) { switch (aVisitor.mEvent->mMessage) { @@ -2105,7 +2117,6 @@ nsGenericHTMLFormElement::PreHandleEvent(EventChainPreVisitor& aVisitor) break; } } - return nsGenericHTMLElement::PreHandleEvent(aVisitor); } diff --git a/dom/html/nsGenericHTMLElement.h b/dom/html/nsGenericHTMLElement.h index 2b8b608b9..ab4391aa1 100644 --- a/dom/html/nsGenericHTMLElement.h +++ b/dom/html/nsGenericHTMLElement.h @@ -498,7 +498,8 @@ public: */ bool CheckHandleEventForAnchorsPreconditions( mozilla::EventChainVisitor& aVisitor); - nsresult PreHandleEventForAnchors(mozilla::EventChainPreVisitor& aVisitor); + nsresult GetEventTargetParentForAnchors( + mozilla::EventChainPreVisitor& aVisitor); nsresult PostHandleEventForAnchors(mozilla::EventChainPostVisitor& aVisitor); bool IsHTMLLink(nsIURI** aURI) const; @@ -1219,8 +1220,10 @@ public: virtual IMEState GetDesiredIMEState() override; virtual mozilla::EventStates IntrinsicState() const override; - virtual nsresult PreHandleEvent( + virtual nsresult GetEventTargetParent( mozilla::EventChainPreVisitor& aVisitor) override; + virtual nsresult PreHandleEvent( + mozilla::EventChainVisitor& aVisitor) override; /** * This callback is called by a fieldest on all its elements whenever its @@ -1305,7 +1308,7 @@ protected: static bool FormIdUpdated(Element* aOldElement, Element* aNewElement, void* aData); - // Returns true if the event should not be handled from PreHandleEvent + // Returns true if the event should not be handled from GetEventTargetParent bool IsElementDisabledForEvents(mozilla::EventMessage aMessage, nsIFrame* aFrame); diff --git a/dom/indexedDB/IDBFileHandle.cpp b/dom/indexedDB/IDBFileHandle.cpp index 8b88e1722..55dd22ce9 100644 --- a/dom/indexedDB/IDBFileHandle.cpp +++ b/dom/indexedDB/IDBFileHandle.cpp @@ -127,7 +127,7 @@ IDBFileHandle::Run() } nsresult -IDBFileHandle::PreHandleEvent(EventChainPreVisitor& aVisitor) +IDBFileHandle::GetEventTargetParent(EventChainPreVisitor& aVisitor) { AssertIsOnOwningThread(); diff --git a/dom/indexedDB/IDBFileHandle.h b/dom/indexedDB/IDBFileHandle.h index 574524090..17206e09c 100644 --- a/dom/indexedDB/IDBFileHandle.h +++ b/dom/indexedDB/IDBFileHandle.h @@ -117,7 +117,7 @@ public: // nsIDOMEventTarget virtual nsresult - PreHandleEvent(EventChainPreVisitor& aVisitor) override; + GetEventTargetParent(EventChainPreVisitor& aVisitor) override; // WrapperCache virtual JSObject* diff --git a/dom/indexedDB/IDBFileRequest.cpp b/dom/indexedDB/IDBFileRequest.cpp index 066b2b24a..7d93f24de 100644 --- a/dom/indexedDB/IDBFileRequest.cpp +++ b/dom/indexedDB/IDBFileRequest.cpp @@ -64,7 +64,7 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED(IDBFileRequest, DOMRequest, mFileHandle) nsresult -IDBFileRequest::PreHandleEvent(EventChainPreVisitor& aVisitor) +IDBFileRequest::GetEventTargetParent(EventChainPreVisitor& aVisitor) { AssertIsOnOwningThread(); diff --git a/dom/indexedDB/IDBFileRequest.h b/dom/indexedDB/IDBFileRequest.h index 4f4252dc9..3d8d282fd 100644 --- a/dom/indexedDB/IDBFileRequest.h +++ b/dom/indexedDB/IDBFileRequest.h @@ -58,7 +58,7 @@ public: // nsIDOMEventTarget virtual nsresult - PreHandleEvent(EventChainPreVisitor& aVisitor) override; + GetEventTargetParent(EventChainPreVisitor& aVisitor) override; // nsWrapperCache virtual JSObject* diff --git a/dom/indexedDB/IDBRequest.cpp b/dom/indexedDB/IDBRequest.cpp index e0e318059..b74725954 100644 --- a/dom/indexedDB/IDBRequest.cpp +++ b/dom/indexedDB/IDBRequest.cpp @@ -449,7 +449,7 @@ NS_IMPL_ADDREF_INHERITED(IDBRequest, IDBWrapperCache) NS_IMPL_RELEASE_INHERITED(IDBRequest, IDBWrapperCache) nsresult -IDBRequest::PreHandleEvent(EventChainPreVisitor& aVisitor) +IDBRequest::GetEventTargetParent(EventChainPreVisitor& aVisitor) { AssertIsOnOwningThread(); diff --git a/dom/indexedDB/IDBRequest.h b/dom/indexedDB/IDBRequest.h index 1c7fca756..e6cbaab9e 100644 --- a/dom/indexedDB/IDBRequest.h +++ b/dom/indexedDB/IDBRequest.h @@ -90,7 +90,7 @@ public: // nsIDOMEventTarget virtual nsresult - PreHandleEvent(EventChainPreVisitor& aVisitor) override; + GetEventTargetParent(EventChainPreVisitor& aVisitor) override; void GetSource(Nullable<OwningIDBObjectStoreOrIDBIndexOrIDBCursor>& aSource) const; diff --git a/dom/indexedDB/IDBTransaction.cpp b/dom/indexedDB/IDBTransaction.cpp index a50489898..ec27c51d8 100644 --- a/dom/indexedDB/IDBTransaction.cpp +++ b/dom/indexedDB/IDBTransaction.cpp @@ -996,7 +996,7 @@ IDBTransaction::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) } nsresult -IDBTransaction::PreHandleEvent(EventChainPreVisitor& aVisitor) +IDBTransaction::GetEventTargetParent(EventChainPreVisitor& aVisitor) { AssertIsOnOwningThread(); diff --git a/dom/indexedDB/IDBTransaction.h b/dom/indexedDB/IDBTransaction.h index 1c3e8be99..5a893ca20 100644 --- a/dom/indexedDB/IDBTransaction.h +++ b/dom/indexedDB/IDBTransaction.h @@ -314,7 +314,7 @@ public: // nsIDOMEventTarget virtual nsresult - PreHandleEvent(EventChainPreVisitor& aVisitor) override; + GetEventTargetParent(EventChainPreVisitor& aVisitor) override; private: IDBTransaction(IDBDatabase* aDatabase, diff --git a/dom/interfaces/events/nsIDOMEventTarget.idl b/dom/interfaces/events/nsIDOMEventTarget.idl index a7e3aae5a..9e00c598c 100644 --- a/dom/interfaces/events/nsIDOMEventTarget.idl +++ b/dom/interfaces/events/nsIDOMEventTarget.idl @@ -13,6 +13,7 @@ using mozilla::dom::Nullable; namespace mozilla { +class EventChainVisitor; class EventChainPostVisitor; class EventChainPreVisitor; class EventListenerManager; @@ -224,7 +225,19 @@ interface nsIDOMEventTarget : nsISupports * @note Only EventDispatcher should call this method. */ [noscript, nostdcall] - void PreHandleEvent(in EventChainPreVisitorRef aVisitor); + void GetEventTargetParent(in EventChainPreVisitorRef aVisitor); + + /** + * Called before the capture phase of the event flow and after event target + * chain creation. This is used to handle those stuffs must be executed before + * dispatch event to DOM + */ +%{C++ + virtual nsresult PreHandleEvent(mozilla::EventChainVisitor& aVisitor) + { + return NS_OK; + } +%} /** * If EventChainPreVisitor.mWantsWillHandleEvent is set PR_TRUE, diff --git a/dom/ipc/TabChild.h b/dom/ipc/TabChild.h index d9988a596..004067355 100644 --- a/dom/ipc/TabChild.h +++ b/dom/ipc/TabChild.h @@ -135,7 +135,7 @@ public: } nsresult - PreHandleEvent(EventChainPreVisitor& aVisitor) override + GetEventTargetParent(EventChainPreVisitor& aVisitor) override { aVisitor.mForceContentDispatch = true; return NS_OK; diff --git a/dom/mathml/nsMathMLElement.cpp b/dom/mathml/nsMathMLElement.cpp index 2be931682..e2bc4f008 100644 --- a/dom/mathml/nsMathMLElement.cpp +++ b/dom/mathml/nsMathMLElement.cpp @@ -919,12 +919,12 @@ nsMathMLElement::MapMathMLAttributesInto(const nsMappedAttributes* aAttributes, } nsresult -nsMathMLElement::PreHandleEvent(EventChainPreVisitor& aVisitor) +nsMathMLElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) { - nsresult rv = Element::PreHandleEvent(aVisitor); + nsresult rv = Element::GetEventTargetParent(aVisitor); NS_ENSURE_SUCCESS(rv, rv); - return PreHandleEventForLinks(aVisitor); + return GetEventTargetParentForLinks(aVisitor); } nsresult diff --git a/dom/mathml/nsMathMLElement.h b/dom/mathml/nsMathMLElement.h index 47ed8b165..bbcdb9771 100644 --- a/dom/mathml/nsMathMLElement.h +++ b/dom/mathml/nsMathMLElement.h @@ -74,7 +74,7 @@ public: static void MapMathMLAttributesInto(const nsMappedAttributes* aAttributes, nsRuleData* aRuleData); - virtual nsresult PreHandleEvent( + virtual nsresult GetEventTargetParent( mozilla::EventChainPreVisitor& aVisitor) override; virtual nsresult PostHandleEvent( mozilla::EventChainPostVisitor& aVisitor) override; diff --git a/dom/svg/SVGAElement.cpp b/dom/svg/SVGAElement.cpp index a69c60ee4..95c3d6979 100644 --- a/dom/svg/SVGAElement.cpp +++ b/dom/svg/SVGAElement.cpp @@ -84,12 +84,12 @@ SVGAElement::Href() // nsINode methods nsresult -SVGAElement::PreHandleEvent(EventChainPreVisitor& aVisitor) +SVGAElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) { - nsresult rv = Element::PreHandleEvent(aVisitor); + nsresult rv = Element::GetEventTargetParent(aVisitor); NS_ENSURE_SUCCESS(rv, rv); - return PreHandleEventForLinks(aVisitor); + return GetEventTargetParentForLinks(aVisitor); } nsresult diff --git a/dom/svg/SVGAElement.h b/dom/svg/SVGAElement.h index 4f7df9e50..776c96f53 100644 --- a/dom/svg/SVGAElement.h +++ b/dom/svg/SVGAElement.h @@ -37,7 +37,8 @@ public: NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(SVGAElement, SVGAElementBase) // nsINode interface methods - virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override; + virtual nsresult GetEventTargetParent( + EventChainPreVisitor& aVisitor) override; virtual nsresult PostHandleEvent( EventChainPostVisitor& aVisitor) override; virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; diff --git a/dom/svg/SVGSVGElement.cpp b/dom/svg/SVGSVGElement.cpp index 90c3c3fff..bb3aaccd2 100644 --- a/dom/svg/SVGSVGElement.cpp +++ b/dom/svg/SVGSVGElement.cpp @@ -589,7 +589,7 @@ SVGSVGElement::IsAttributeMapped(const nsIAtom* name) const // nsIContent methods: nsresult -SVGSVGElement::PreHandleEvent(EventChainPreVisitor& aVisitor) +SVGSVGElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) { if (aVisitor.mEvent->mMessage == eSVGLoad) { if (mTimedDocumentRoot) { @@ -600,7 +600,7 @@ SVGSVGElement::PreHandleEvent(EventChainPreVisitor& aVisitor) AnimationNeedsResample(); } } - return SVGSVGElementBase::PreHandleEvent(aVisitor); + return SVGSVGElementBase::GetEventTargetParent(aVisitor); } bool diff --git a/dom/svg/SVGSVGElement.h b/dom/svg/SVGSVGElement.h index da08ad770..0145ae8fa 100644 --- a/dom/svg/SVGSVGElement.h +++ b/dom/svg/SVGSVGElement.h @@ -152,7 +152,8 @@ public: // nsIContent interface NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const override; - virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override; + virtual nsresult GetEventTargetParent( + EventChainPreVisitor& aVisitor) override; virtual bool IsEventAttributeName(nsIAtom* aName) override; diff --git a/dom/workers/SharedWorker.cpp b/dom/workers/SharedWorker.cpp index 99bb50339..750dc9237 100644 --- a/dom/workers/SharedWorker.cpp +++ b/dom/workers/SharedWorker.cpp @@ -181,7 +181,7 @@ SharedWorker::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) } nsresult -SharedWorker::PreHandleEvent(EventChainPreVisitor& aVisitor) +SharedWorker::GetEventTargetParent(EventChainPreVisitor& aVisitor) { AssertIsOnMainThread(); @@ -200,5 +200,5 @@ SharedWorker::PreHandleEvent(EventChainPreVisitor& aVisitor) return NS_OK; } - return DOMEventTargetHelper::PreHandleEvent(aVisitor); + return DOMEventTargetHelper::GetEventTargetParent(aVisitor); } diff --git a/dom/workers/SharedWorker.h b/dom/workers/SharedWorker.h index 220436e4d..4b50d39e8 100644 --- a/dom/workers/SharedWorker.h +++ b/dom/workers/SharedWorker.h @@ -76,7 +76,7 @@ public: WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; virtual nsresult - PreHandleEvent(EventChainPreVisitor& aVisitor) override; + GetEventTargetParent(EventChainPreVisitor& aVisitor) override; WorkerPrivate* GetWorkerPrivate() const diff --git a/dom/xul/nsXULElement.cpp b/dom/xul/nsXULElement.cpp index a854f53ec..bc9215086 100644 --- a/dom/xul/nsXULElement.cpp +++ b/dom/xul/nsXULElement.cpp @@ -126,6 +126,8 @@ uint32_t nsXULPrototypeAttribute::gNumCacheSets; uint32_t nsXULPrototypeAttribute::gNumCacheFills; #endif +#define NS_DISPATCH_XUL_COMMAND (1 << 0) + class nsXULElementTearoff final : public nsIFrameLoaderOwner { ~nsXULElementTearoff() {} @@ -1236,17 +1238,65 @@ nsXULElement::List(FILE* out, int32_t aIndent) const } #endif +bool +nsXULElement::IsEventStoppedFromAnonymousScrollbar(EventMessage aMessage) +{ + return (IsRootOfNativeAnonymousSubtree() && + IsAnyOfXULElements(nsGkAtoms::scrollbar, nsGkAtoms::scrollcorner) && + (aMessage == eMouseClick || aMessage == eMouseDoubleClick || + aMessage == eXULCommand || aMessage == eContextMenu || + aMessage == eDragStart)); +} + nsresult -nsXULElement::PreHandleEvent(EventChainPreVisitor& aVisitor) +nsXULElement::DispatchXULCommand(const EventChainVisitor& aVisitor, + nsAutoString& aCommand) +{ + // XXX sXBL/XBL2 issue! Owner or current document? + nsCOMPtr<nsIDOMDocument> domDoc(do_QueryInterface(GetUncomposedDoc())); + NS_ENSURE_STATE(domDoc); + nsCOMPtr<nsIDOMElement> commandElt; + domDoc->GetElementById(aCommand, getter_AddRefs(commandElt)); + nsCOMPtr<nsIContent> commandContent(do_QueryInterface(commandElt)); + if (commandContent) { + // Create a new command event to dispatch to the element + // pointed to by the command attribute. The new event's + // sourceEvent will be the original command event that we're + // handling. + nsCOMPtr<nsIDOMEvent> domEvent = aVisitor.mDOMEvent; + while (domEvent) { + Event* event = domEvent->InternalDOMEvent(); + NS_ENSURE_STATE(!SameCOMIdentity(event->GetOriginalTarget(), + commandContent)); + nsCOMPtr<nsIDOMXULCommandEvent> commandEvent = + do_QueryInterface(domEvent); + if (commandEvent) { + commandEvent->GetSourceEvent(getter_AddRefs(domEvent)); + } else { + domEvent = nullptr; + } + } + WidgetInputEvent* orig = aVisitor.mEvent->AsInputEvent(); + nsContentUtils::DispatchXULCommand( + commandContent, + orig->IsTrusted(), + aVisitor.mDOMEvent, + nullptr, + orig->IsControl(), + orig->IsAlt(), + orig->IsShift(), + orig->IsMeta()); + } else { + NS_WARNING("A XUL element is attached to a command that doesn't exist!\n"); + } + return NS_OK; +} + +nsresult +nsXULElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) { aVisitor.mForceContentDispatch = true; //FIXME! Bug 329119 - if (IsRootOfNativeAnonymousSubtree() && - (IsAnyOfXULElements(nsGkAtoms::scrollbar, nsGkAtoms::scrollcorner)) && - (aVisitor.mEvent->mMessage == eMouseClick || - aVisitor.mEvent->mMessage == eMouseDoubleClick || - aVisitor.mEvent->mMessage == eXULCommand || - aVisitor.mEvent->mMessage == eContextMenu || - aVisitor.mEvent->mMessage == eDragStart)) { + if (IsEventStoppedFromAnonymousScrollbar(aVisitor.mEvent->mMessage)) { // Don't propagate these events from native anonymous scrollbar. aVisitor.mCanHandle = true; aVisitor.mParentTarget = nullptr; @@ -1263,55 +1313,33 @@ nsXULElement::PreHandleEvent(EventChainPreVisitor& aVisitor) // See if we have a command elt. If so, we execute on the command // instead of on our content element. nsAutoString command; - if (xulEvent && GetAttr(kNameSpaceID_None, nsGkAtoms::command, command) && + if (xulEvent && + GetAttr(kNameSpaceID_None, nsGkAtoms::command, command) && !command.IsEmpty()) { // Stop building the event target chain for the original event. // We don't want it to propagate to any DOM nodes. aVisitor.mCanHandle = false; aVisitor.mAutomaticChromeDispatch = false; - - // XXX sXBL/XBL2 issue! Owner or current document? - nsCOMPtr<nsIDOMDocument> domDoc(do_QueryInterface(GetUncomposedDoc())); - NS_ENSURE_STATE(domDoc); - nsCOMPtr<nsIDOMElement> commandElt; - domDoc->GetElementById(command, getter_AddRefs(commandElt)); - nsCOMPtr<nsIContent> commandContent(do_QueryInterface(commandElt)); - if (commandContent) { - // Create a new command event to dispatch to the element - // pointed to by the command attribute. The new event's - // sourceEvent will be the original command event that we're - // handling. - nsCOMPtr<nsIDOMEvent> domEvent = aVisitor.mDOMEvent; - while (domEvent) { - Event* event = domEvent->InternalDOMEvent(); - NS_ENSURE_STATE(!SameCOMIdentity(event->GetOriginalTarget(), - commandContent)); - nsCOMPtr<nsIDOMXULCommandEvent> commandEvent = - do_QueryInterface(domEvent); - if (commandEvent) { - commandEvent->GetSourceEvent(getter_AddRefs(domEvent)); - } else { - domEvent = nullptr; - } - } - - WidgetInputEvent* orig = aVisitor.mEvent->AsInputEvent(); - nsContentUtils::DispatchXULCommand( - commandContent, - aVisitor.mEvent->IsTrusted(), - aVisitor.mDOMEvent, - nullptr, - orig->IsControl(), - orig->IsAlt(), - orig->IsShift(), - orig->IsMeta()); - } else { - NS_WARNING("A XUL element is attached to a command that doesn't exist!\n"); - } + // Dispatch XUL command in PreHandleEvent to prevent it breaks event + // target chain creation + aVisitor.mWantsPreHandleEvent = true; + aVisitor.mItemFlags |= NS_DISPATCH_XUL_COMMAND; return NS_OK; } } + return nsStyledElement::GetEventTargetParent(aVisitor); +} + +nsresult +nsXULElement::PreHandleEvent(EventChainVisitor& aVisitor) +{ + if (aVisitor.mItemFlags & NS_DISPATCH_XUL_COMMAND) { + nsAutoString command; + GetAttr(kNameSpaceID_None, nsGkAtoms::command, command); + MOZ_ASSERT(!command.IsEmpty()); + return DispatchXULCommand(aVisitor, command); + } return nsStyledElement::PreHandleEvent(aVisitor); } diff --git a/dom/xul/nsXULElement.h b/dom/xul/nsXULElement.h index 80424412f..109f6c2e7 100644 --- a/dom/xul/nsXULElement.h +++ b/dom/xul/nsXULElement.h @@ -355,9 +355,10 @@ public: NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsXULElement, nsStyledElement) // nsINode - virtual nsresult PreHandleEvent( + virtual nsresult GetEventTargetParent( mozilla::EventChainPreVisitor& aVisitor) override; - + virtual nsresult PreHandleEvent( + mozilla::EventChainVisitor& aVisitor) override; // nsIContent virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent, nsIContent* aBindingParent, @@ -689,6 +690,11 @@ protected: virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override; void MaybeUpdatePrivateLifetime(); + + bool IsEventStoppedFromAnonymousScrollbar(mozilla::EventMessage aMessage); + + nsresult DispatchXULCommand(const mozilla::EventChainVisitor& aVisitor, + nsAutoString& aCommand); }; #endif // nsXULElement_h__ |