michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=8 sts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsPresContext.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsError.h" michael@0: #include michael@0: #include "nsIContent.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsINode.h" michael@0: #include "nsPIDOMWindow.h" michael@0: #include "GeckoProfiler.h" michael@0: #include "GeneratedEvents.h" michael@0: #include "mozilla/ContentEvents.h" michael@0: #include "mozilla/dom/EventTarget.h" michael@0: #include "mozilla/dom/TouchEvent.h" michael@0: #include "mozilla/EventDispatcher.h" michael@0: #include "mozilla/EventListenerManager.h" michael@0: #include "mozilla/InternalMutationEvent.h" michael@0: #include "mozilla/MiscEvents.h" michael@0: #include "mozilla/MouseEvents.h" michael@0: #include "mozilla/TextEvents.h" michael@0: #include "mozilla/TouchEvents.h" michael@0: #include "mozilla/unused.h" michael@0: michael@0: namespace mozilla { michael@0: michael@0: using namespace dom; michael@0: michael@0: class ELMCreationDetector michael@0: { michael@0: public: michael@0: ELMCreationDetector() michael@0: // We can do this optimization only in the main thread. michael@0: : mNonMainThread(!NS_IsMainThread()) michael@0: , mInitialCount(mNonMainThread ? michael@0: 0 : EventListenerManager::sMainThreadCreatedCount) michael@0: { michael@0: } michael@0: michael@0: bool MayHaveNewListenerManager() michael@0: { michael@0: return mNonMainThread || michael@0: mInitialCount != EventListenerManager::sMainThreadCreatedCount; michael@0: } michael@0: michael@0: bool IsMainThread() michael@0: { michael@0: return !mNonMainThread; michael@0: } michael@0: michael@0: private: michael@0: bool mNonMainThread; michael@0: uint32_t mInitialCount; michael@0: }; michael@0: michael@0: #define NS_TARGET_CHAIN_FORCE_CONTENT_DISPATCH (1 << 0) michael@0: #define NS_TARGET_CHAIN_WANTS_WILL_HANDLE_EVENT (1 << 1) michael@0: #define NS_TARGET_CHAIN_MAY_HAVE_MANAGER (1 << 2) michael@0: michael@0: // EventTargetChainItem represents a single item in the event target chain. michael@0: class EventTargetChainItem michael@0: { michael@0: private: michael@0: EventTargetChainItem(EventTarget* aTarget); michael@0: public: michael@0: EventTargetChainItem() michael@0: : mFlags(0) michael@0: , mItemFlags(0) michael@0: { michael@0: } michael@0: michael@0: static EventTargetChainItem* Create(nsTArray& aChain, michael@0: EventTarget* aTarget, michael@0: EventTargetChainItem* aChild = nullptr) michael@0: { michael@0: MOZ_ASSERT(!aChild || &aChain.ElementAt(aChain.Length() - 1) == aChild); michael@0: return new (aChain.AppendElement()) EventTargetChainItem(aTarget); michael@0: } michael@0: michael@0: static void DestroyLast(nsTArray& aChain, michael@0: EventTargetChainItem* aItem) michael@0: { michael@0: uint32_t lastIndex = aChain.Length() - 1; michael@0: MOZ_ASSERT(&aChain[lastIndex] == aItem); michael@0: aChain.RemoveElementAt(lastIndex); michael@0: } michael@0: michael@0: bool IsValid() michael@0: { michael@0: NS_WARN_IF_FALSE(!!(mTarget), "Event target is not valid!"); michael@0: return !!(mTarget); michael@0: } michael@0: michael@0: EventTarget* GetNewTarget() michael@0: { michael@0: return mNewTarget; michael@0: } michael@0: michael@0: void SetNewTarget(EventTarget* aNewTarget) michael@0: { michael@0: mNewTarget = aNewTarget; michael@0: } michael@0: michael@0: void SetForceContentDispatch(bool aForce) michael@0: { michael@0: if (aForce) { michael@0: mFlags |= NS_TARGET_CHAIN_FORCE_CONTENT_DISPATCH; michael@0: } else { michael@0: mFlags &= ~NS_TARGET_CHAIN_FORCE_CONTENT_DISPATCH; michael@0: } michael@0: } michael@0: michael@0: bool ForceContentDispatch() michael@0: { michael@0: return !!(mFlags & NS_TARGET_CHAIN_FORCE_CONTENT_DISPATCH); michael@0: } michael@0: michael@0: void SetWantsWillHandleEvent(bool aWants) michael@0: { michael@0: if (aWants) { michael@0: mFlags |= NS_TARGET_CHAIN_WANTS_WILL_HANDLE_EVENT; michael@0: } else { michael@0: mFlags &= ~NS_TARGET_CHAIN_WANTS_WILL_HANDLE_EVENT; michael@0: } michael@0: } michael@0: michael@0: bool WantsWillHandleEvent() michael@0: { michael@0: return !!(mFlags & NS_TARGET_CHAIN_WANTS_WILL_HANDLE_EVENT); michael@0: } michael@0: michael@0: void SetMayHaveListenerManager(bool aMayHave) michael@0: { michael@0: if (aMayHave) { michael@0: mFlags |= NS_TARGET_CHAIN_MAY_HAVE_MANAGER; michael@0: } else { michael@0: mFlags &= ~NS_TARGET_CHAIN_MAY_HAVE_MANAGER; michael@0: } michael@0: } michael@0: michael@0: bool MayHaveListenerManager() michael@0: { michael@0: return !!(mFlags & NS_TARGET_CHAIN_MAY_HAVE_MANAGER); michael@0: } michael@0: michael@0: EventTarget* CurrentTarget() michael@0: { michael@0: return mTarget; michael@0: } michael@0: michael@0: /** michael@0: * Dispatches event through the event target chain. michael@0: * Handles capture, target and bubble phases both in default michael@0: * and system event group and calls also PostHandleEvent for each michael@0: * item in the chain. michael@0: */ michael@0: static void HandleEventTargetChain(nsTArray& aChain, michael@0: EventChainPostVisitor& aVisitor, michael@0: EventDispatchingCallback* aCallback, michael@0: ELMCreationDetector& aCd); michael@0: michael@0: /** michael@0: * Resets aVisitor object and calls PreHandleEvent. michael@0: * Copies mItemFlags and mItemData to the current EventTargetChainItem. michael@0: */ michael@0: void PreHandleEvent(EventChainPreVisitor& aVisitor); michael@0: michael@0: /** michael@0: * If the current item in the event target chain has an event listener michael@0: * manager, this method calls EventListenerManager::HandleEvent(). michael@0: */ michael@0: void HandleEvent(EventChainPostVisitor& aVisitor, michael@0: ELMCreationDetector& aCd) michael@0: { michael@0: if (WantsWillHandleEvent()) { michael@0: mTarget->WillHandleEvent(aVisitor); michael@0: } michael@0: if (aVisitor.mEvent->mFlags.mPropagationStopped) { michael@0: return; michael@0: } michael@0: if (!mManager) { michael@0: if (!MayHaveListenerManager() && !aCd.MayHaveNewListenerManager()) { michael@0: return; michael@0: } michael@0: mManager = mTarget->GetExistingListenerManager(); michael@0: } michael@0: if (mManager) { michael@0: NS_ASSERTION(aVisitor.mEvent->currentTarget == nullptr, michael@0: "CurrentTarget should be null!"); michael@0: mManager->HandleEvent(aVisitor.mPresContext, aVisitor.mEvent, michael@0: &aVisitor.mDOMEvent, michael@0: CurrentTarget(), michael@0: &aVisitor.mEventStatus); michael@0: NS_ASSERTION(aVisitor.mEvent->currentTarget == nullptr, michael@0: "CurrentTarget should be null!"); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Copies mItemFlags and mItemData to aVisitor and calls PostHandleEvent. michael@0: */ michael@0: void PostHandleEvent(EventChainPostVisitor& aVisitor); michael@0: michael@0: nsCOMPtr mTarget; michael@0: uint16_t mFlags; michael@0: uint16_t mItemFlags; michael@0: nsCOMPtr mItemData; michael@0: // Event retargeting must happen whenever mNewTarget is non-null. michael@0: nsCOMPtr mNewTarget; michael@0: // Cache mTarget's event listener manager. michael@0: nsRefPtr mManager; michael@0: }; michael@0: michael@0: EventTargetChainItem::EventTargetChainItem(EventTarget* aTarget) michael@0: : mTarget(aTarget) michael@0: , mFlags(0) michael@0: , mItemFlags(0) michael@0: { michael@0: MOZ_ASSERT(!aTarget || mTarget == aTarget->GetTargetForEventTargetChain()); michael@0: } michael@0: michael@0: void michael@0: EventTargetChainItem::PreHandleEvent(EventChainPreVisitor& aVisitor) michael@0: { michael@0: aVisitor.Reset(); michael@0: unused << mTarget->PreHandleEvent(aVisitor); michael@0: SetForceContentDispatch(aVisitor.mForceContentDispatch); michael@0: SetWantsWillHandleEvent(aVisitor.mWantsWillHandleEvent); michael@0: SetMayHaveListenerManager(aVisitor.mMayHaveListenerManager); michael@0: mItemFlags = aVisitor.mItemFlags; michael@0: mItemData = aVisitor.mItemData; michael@0: } michael@0: michael@0: void michael@0: EventTargetChainItem::PostHandleEvent(EventChainPostVisitor& aVisitor) michael@0: { michael@0: aVisitor.mItemFlags = mItemFlags; michael@0: aVisitor.mItemData = mItemData; michael@0: mTarget->PostHandleEvent(aVisitor); michael@0: } michael@0: michael@0: void michael@0: EventTargetChainItem::HandleEventTargetChain( michael@0: nsTArray& aChain, michael@0: EventChainPostVisitor& aVisitor, michael@0: EventDispatchingCallback* aCallback, michael@0: ELMCreationDetector& aCd) michael@0: { michael@0: // Save the target so that it can be restored later. michael@0: nsCOMPtr firstTarget = aVisitor.mEvent->target; michael@0: uint32_t chainLength = aChain.Length(); michael@0: michael@0: // Capture michael@0: aVisitor.mEvent->mFlags.mInCapturePhase = true; michael@0: aVisitor.mEvent->mFlags.mInBubblingPhase = false; michael@0: for (uint32_t i = chainLength - 1; i > 0; --i) { michael@0: EventTargetChainItem& item = aChain[i]; michael@0: if ((!aVisitor.mEvent->mFlags.mNoContentDispatch || michael@0: item.ForceContentDispatch()) && michael@0: !aVisitor.mEvent->mFlags.mPropagationStopped) { michael@0: item.HandleEvent(aVisitor, aCd); michael@0: } michael@0: michael@0: if (item.GetNewTarget()) { michael@0: // item is at anonymous boundary. Need to retarget for the child items. michael@0: for (uint32_t j = i; j > 0; --j) { michael@0: uint32_t childIndex = j - 1; michael@0: EventTarget* newTarget = aChain[childIndex].GetNewTarget(); michael@0: if (newTarget) { michael@0: aVisitor.mEvent->target = newTarget; michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Target michael@0: aVisitor.mEvent->mFlags.mInBubblingPhase = true; michael@0: EventTargetChainItem& targetItem = aChain[0]; michael@0: if (!aVisitor.mEvent->mFlags.mPropagationStopped && michael@0: (!aVisitor.mEvent->mFlags.mNoContentDispatch || michael@0: targetItem.ForceContentDispatch())) { michael@0: targetItem.HandleEvent(aVisitor, aCd); michael@0: } michael@0: if (aVisitor.mEvent->mFlags.mInSystemGroup) { michael@0: targetItem.PostHandleEvent(aVisitor); michael@0: } michael@0: michael@0: // Bubble michael@0: aVisitor.mEvent->mFlags.mInCapturePhase = false; michael@0: for (uint32_t i = 1; i < chainLength; ++i) { michael@0: EventTargetChainItem& item = aChain[i]; michael@0: EventTarget* newTarget = item.GetNewTarget(); michael@0: if (newTarget) { michael@0: // Item is at anonymous boundary. Need to retarget for the current item michael@0: // and for parent items. michael@0: aVisitor.mEvent->target = newTarget; michael@0: } michael@0: michael@0: if (aVisitor.mEvent->mFlags.mBubbles || newTarget) { michael@0: if ((!aVisitor.mEvent->mFlags.mNoContentDispatch || michael@0: item.ForceContentDispatch()) && michael@0: !aVisitor.mEvent->mFlags.mPropagationStopped) { michael@0: item.HandleEvent(aVisitor, aCd); michael@0: } michael@0: if (aVisitor.mEvent->mFlags.mInSystemGroup) { michael@0: item.PostHandleEvent(aVisitor); michael@0: } michael@0: } michael@0: } michael@0: aVisitor.mEvent->mFlags.mInBubblingPhase = false; michael@0: michael@0: if (!aVisitor.mEvent->mFlags.mInSystemGroup) { michael@0: // Dispatch to the system event group. Make sure to clear the michael@0: // STOP_DISPATCH flag since this resets for each event group. michael@0: aVisitor.mEvent->mFlags.mPropagationStopped = false; michael@0: aVisitor.mEvent->mFlags.mImmediatePropagationStopped = false; michael@0: michael@0: // Setting back the original target of the event. michael@0: aVisitor.mEvent->target = aVisitor.mEvent->originalTarget; michael@0: michael@0: // Special handling if PresShell (or some other caller) michael@0: // used a callback object. michael@0: if (aCallback) { michael@0: aCallback->HandleEvent(aVisitor); michael@0: } michael@0: michael@0: // Retarget for system event group (which does the default handling too). michael@0: // Setting back the target which was used also for default event group. michael@0: aVisitor.mEvent->target = firstTarget; michael@0: aVisitor.mEvent->mFlags.mInSystemGroup = true; michael@0: HandleEventTargetChain(aChain, michael@0: aVisitor, michael@0: aCallback, michael@0: aCd); michael@0: aVisitor.mEvent->mFlags.mInSystemGroup = false; michael@0: michael@0: // After dispatch, clear all the propagation flags so that michael@0: // system group listeners don't affect to the event. michael@0: aVisitor.mEvent->mFlags.mPropagationStopped = false; michael@0: aVisitor.mEvent->mFlags.mImmediatePropagationStopped = false; michael@0: } michael@0: } michael@0: michael@0: static nsTArray* sCachedMainThreadChain = nullptr; michael@0: michael@0: /* static */ void michael@0: EventDispatcher::Shutdown() michael@0: { michael@0: delete sCachedMainThreadChain; michael@0: sCachedMainThreadChain = nullptr; michael@0: } michael@0: michael@0: EventTargetChainItem* michael@0: EventTargetChainItemForChromeTarget(nsTArray& aChain, michael@0: nsINode* aNode, michael@0: EventTargetChainItem* aChild = nullptr) michael@0: { michael@0: if (!aNode->IsInDoc()) { michael@0: return nullptr; michael@0: } michael@0: nsPIDOMWindow* win = aNode->OwnerDoc()->GetInnerWindow(); michael@0: EventTarget* piTarget = win ? win->GetParentTarget() : nullptr; michael@0: NS_ENSURE_TRUE(piTarget, nullptr); michael@0: michael@0: EventTargetChainItem* etci = michael@0: EventTargetChainItem::Create(aChain, michael@0: piTarget->GetTargetForEventTargetChain(), michael@0: aChild); michael@0: if (!etci->IsValid()) { michael@0: EventTargetChainItem::DestroyLast(aChain, etci); michael@0: return nullptr; michael@0: } michael@0: return etci; michael@0: } michael@0: michael@0: /* static */ nsresult michael@0: EventDispatcher::Dispatch(nsISupports* aTarget, michael@0: nsPresContext* aPresContext, michael@0: WidgetEvent* aEvent, michael@0: nsIDOMEvent* aDOMEvent, michael@0: nsEventStatus* aEventStatus, michael@0: EventDispatchingCallback* aCallback, michael@0: nsCOMArray* aTargets) michael@0: { michael@0: PROFILER_LABEL("EventDispatcher", "Dispatch"); michael@0: NS_ASSERTION(aEvent, "Trying to dispatch without WidgetEvent!"); michael@0: NS_ENSURE_TRUE(!aEvent->mFlags.mIsBeingDispatched, michael@0: NS_ERROR_DOM_INVALID_STATE_ERR); michael@0: NS_ASSERTION(!aTargets || !aEvent->message, "Wrong parameters!"); michael@0: michael@0: // If we're dispatching an already created DOMEvent object, make michael@0: // sure it is initialized! michael@0: // If aTargets is non-null, the event isn't going to be dispatched. michael@0: NS_ENSURE_TRUE(aEvent->message || !aDOMEvent || aTargets, michael@0: NS_ERROR_DOM_INVALID_STATE_ERR); michael@0: michael@0: nsCOMPtr target = do_QueryInterface(aTarget); michael@0: michael@0: bool retargeted = false; michael@0: michael@0: if (aEvent->mFlags.mRetargetToNonNativeAnonymous) { michael@0: nsCOMPtr content = do_QueryInterface(target); michael@0: if (content && content->IsInNativeAnonymousSubtree()) { michael@0: nsCOMPtr newTarget = michael@0: do_QueryInterface(content->FindFirstNonChromeOnlyAccessContent()); michael@0: NS_ENSURE_STATE(newTarget); michael@0: michael@0: aEvent->originalTarget = target; michael@0: target = newTarget; michael@0: retargeted = true; michael@0: } michael@0: } michael@0: michael@0: if (aEvent->mFlags.mOnlyChromeDispatch) { michael@0: nsCOMPtr node = do_QueryInterface(aTarget); michael@0: if (!node) { michael@0: nsCOMPtr win = do_QueryInterface(aTarget); michael@0: if (win) { michael@0: node = win->GetExtantDoc(); michael@0: } michael@0: } michael@0: michael@0: NS_ENSURE_STATE(node); michael@0: nsIDocument* doc = node->OwnerDoc(); michael@0: if (!nsContentUtils::IsChromeDoc(doc)) { michael@0: nsPIDOMWindow* win = doc ? doc->GetInnerWindow() : nullptr; michael@0: // If we can't dispatch the event to chrome, do nothing. michael@0: EventTarget* piTarget = win ? win->GetParentTarget() : nullptr; michael@0: NS_ENSURE_TRUE(piTarget, NS_OK); michael@0: michael@0: // Set the target to be the original dispatch target, michael@0: aEvent->target = target; michael@0: // but use chrome event handler or TabChildGlobal for event target chain. michael@0: target = piTarget; michael@0: } michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: if (!nsContentUtils::IsSafeToRunScript()) { michael@0: nsresult rv = NS_ERROR_FAILURE; michael@0: if (target->GetContextForEventHandlers(&rv) || michael@0: NS_FAILED(rv)) { michael@0: nsCOMPtr node = do_QueryInterface(target); michael@0: if (node && nsContentUtils::IsChromeDoc(node->OwnerDoc())) { michael@0: NS_WARNING("Fix the caller!"); michael@0: } else { michael@0: NS_ERROR("This is unsafe! Fix the caller!"); michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (aDOMEvent) { michael@0: WidgetEvent* innerEvent = aDOMEvent->GetInternalNSEvent(); michael@0: NS_ASSERTION(innerEvent == aEvent, michael@0: "The inner event of aDOMEvent is not the same as aEvent!"); michael@0: } michael@0: #endif michael@0: michael@0: nsresult rv = NS_OK; michael@0: bool externalDOMEvent = !!(aDOMEvent); michael@0: michael@0: // If we have a PresContext, make sure it doesn't die before michael@0: // event dispatching is finished. michael@0: nsRefPtr kungFuDeathGrip(aPresContext); michael@0: michael@0: ELMCreationDetector cd; michael@0: nsTArray chain; michael@0: if (cd.IsMainThread()) { michael@0: if (!sCachedMainThreadChain) { michael@0: sCachedMainThreadChain = new nsTArray(); michael@0: } michael@0: chain.SwapElements(*sCachedMainThreadChain); michael@0: chain.SetCapacity(128); michael@0: } michael@0: michael@0: // Create the event target chain item for the event target. michael@0: EventTargetChainItem* targetEtci = michael@0: EventTargetChainItem::Create(chain, target->GetTargetForEventTargetChain()); michael@0: MOZ_ASSERT(&chain[0] == targetEtci); michael@0: if (!targetEtci->IsValid()) { michael@0: EventTargetChainItem::DestroyLast(chain, targetEtci); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // Make sure that nsIDOMEvent::target and nsIDOMEvent::originalTarget michael@0: // point to the last item in the chain. michael@0: if (!aEvent->target) { michael@0: // Note, CurrentTarget() points always to the object returned by michael@0: // GetTargetForEventTargetChain(). michael@0: aEvent->target = targetEtci->CurrentTarget(); michael@0: } else { michael@0: // XXX But if the target is already set, use that. This is a hack michael@0: // for the 'load', 'beforeunload' and 'unload' events, michael@0: // which are dispatched to |window| but have document as their target. michael@0: // michael@0: // Make sure that the event target points to the right object. michael@0: aEvent->target = aEvent->target->GetTargetForEventTargetChain(); michael@0: NS_ENSURE_STATE(aEvent->target); michael@0: } michael@0: michael@0: if (retargeted) { michael@0: aEvent->originalTarget = michael@0: aEvent->originalTarget->GetTargetForEventTargetChain(); michael@0: NS_ENSURE_STATE(aEvent->originalTarget); michael@0: } michael@0: else { michael@0: aEvent->originalTarget = aEvent->target; michael@0: } michael@0: michael@0: nsCOMPtr content = do_QueryInterface(aEvent->originalTarget); michael@0: bool isInAnon = (content && content->IsInAnonymousSubtree()); michael@0: michael@0: aEvent->mFlags.mIsBeingDispatched = true; michael@0: michael@0: // Create visitor object and start event dispatching. michael@0: // PreHandleEvent for the original target. michael@0: nsEventStatus status = aEventStatus ? *aEventStatus : nsEventStatus_eIgnore; michael@0: EventChainPreVisitor preVisitor(aPresContext, aEvent, aDOMEvent, status, michael@0: isInAnon); michael@0: targetEtci->PreHandleEvent(preVisitor); michael@0: michael@0: if (!preVisitor.mCanHandle && preVisitor.mAutomaticChromeDispatch && content) { michael@0: // Event target couldn't handle the event. Try to propagate to chrome. michael@0: EventTargetChainItem::DestroyLast(chain, targetEtci); michael@0: targetEtci = EventTargetChainItemForChromeTarget(chain, content); michael@0: NS_ENSURE_STATE(targetEtci); michael@0: MOZ_ASSERT(&chain[0] == targetEtci); michael@0: targetEtci->PreHandleEvent(preVisitor); michael@0: } michael@0: if (preVisitor.mCanHandle) { michael@0: // At least the original target can handle the event. michael@0: // Setting the retarget to the |target| simplifies retargeting code. michael@0: nsCOMPtr t = do_QueryInterface(aEvent->target); michael@0: targetEtci->SetNewTarget(t); michael@0: EventTargetChainItem* topEtci = targetEtci; michael@0: targetEtci = nullptr; michael@0: while (preVisitor.mParentTarget) { michael@0: EventTarget* parentTarget = preVisitor.mParentTarget; michael@0: EventTargetChainItem* parentEtci = michael@0: EventTargetChainItem::Create(chain, preVisitor.mParentTarget, topEtci); michael@0: if (!parentEtci->IsValid()) { michael@0: EventTargetChainItem::DestroyLast(chain, parentEtci); michael@0: rv = NS_ERROR_FAILURE; michael@0: break; michael@0: } michael@0: michael@0: // Item needs event retargetting. michael@0: if (preVisitor.mEventTargetAtParent) { michael@0: // Need to set the target of the event michael@0: // so that also the next retargeting works. michael@0: preVisitor.mEvent->target = preVisitor.mEventTargetAtParent; michael@0: parentEtci->SetNewTarget(preVisitor.mEventTargetAtParent); michael@0: } michael@0: michael@0: parentEtci->PreHandleEvent(preVisitor); michael@0: if (preVisitor.mCanHandle) { michael@0: topEtci = parentEtci; michael@0: } else { michael@0: EventTargetChainItem::DestroyLast(chain, parentEtci); michael@0: parentEtci = nullptr; michael@0: if (preVisitor.mAutomaticChromeDispatch && content) { michael@0: // Even if the current target can't handle the event, try to michael@0: // propagate to chrome. michael@0: nsCOMPtr disabledTarget = do_QueryInterface(parentTarget); michael@0: if (disabledTarget) { michael@0: parentEtci = EventTargetChainItemForChromeTarget(chain, michael@0: disabledTarget, michael@0: topEtci); michael@0: if (parentEtci) { michael@0: parentEtci->PreHandleEvent(preVisitor); michael@0: if (preVisitor.mCanHandle) { michael@0: chain[0].SetNewTarget(parentTarget); michael@0: topEtci = parentEtci; michael@0: continue; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: break; michael@0: } michael@0: } michael@0: if (NS_SUCCEEDED(rv)) { michael@0: if (aTargets) { michael@0: aTargets->Clear(); michael@0: aTargets->SetCapacity(chain.Length()); michael@0: for (uint32_t i = 0; i < chain.Length(); ++i) { michael@0: aTargets->AppendObject(chain[i].CurrentTarget()->GetTargetForDOMEvent()); michael@0: } michael@0: } else { michael@0: // Event target chain is created. Handle the chain. michael@0: EventChainPostVisitor postVisitor(preVisitor); michael@0: EventTargetChainItem::HandleEventTargetChain(chain, postVisitor, michael@0: aCallback, cd); michael@0: michael@0: preVisitor.mEventStatus = postVisitor.mEventStatus; michael@0: // If the DOM event was created during event flow. michael@0: if (!preVisitor.mDOMEvent && postVisitor.mDOMEvent) { michael@0: preVisitor.mDOMEvent = postVisitor.mDOMEvent; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Note, EventTargetChainItem objects are deleted when the chain goes out of michael@0: // the scope. michael@0: michael@0: aEvent->mFlags.mIsBeingDispatched = false; michael@0: aEvent->mFlags.mDispatchedAtLeastOnce = true; michael@0: michael@0: if (!externalDOMEvent && preVisitor.mDOMEvent) { michael@0: // An dom::Event was created while dispatching the event. michael@0: // Duplicate private data if someone holds a pointer to it. michael@0: nsrefcnt rc = 0; michael@0: NS_RELEASE2(preVisitor.mDOMEvent, rc); michael@0: if (preVisitor.mDOMEvent) { michael@0: preVisitor.mDOMEvent->DuplicatePrivateData(); michael@0: } michael@0: } michael@0: michael@0: if (aEventStatus) { michael@0: *aEventStatus = preVisitor.mEventStatus; michael@0: } michael@0: michael@0: if (cd.IsMainThread() && chain.Capacity() == 128 && sCachedMainThreadChain) { michael@0: chain.ClearAndRetainStorage(); michael@0: chain.SwapElements(*sCachedMainThreadChain); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: /* static */ nsresult michael@0: EventDispatcher::DispatchDOMEvent(nsISupports* aTarget, michael@0: WidgetEvent* aEvent, michael@0: nsIDOMEvent* aDOMEvent, michael@0: nsPresContext* aPresContext, michael@0: nsEventStatus* aEventStatus) michael@0: { michael@0: if (aDOMEvent) { michael@0: WidgetEvent* innerEvent = aDOMEvent->GetInternalNSEvent(); michael@0: NS_ENSURE_TRUE(innerEvent, NS_ERROR_ILLEGAL_VALUE); michael@0: michael@0: bool dontResetTrusted = false; michael@0: if (innerEvent->mFlags.mDispatchedAtLeastOnce) { michael@0: innerEvent->target = nullptr; michael@0: innerEvent->originalTarget = nullptr; michael@0: } else { michael@0: aDOMEvent->GetIsTrusted(&dontResetTrusted); michael@0: } michael@0: michael@0: if (!dontResetTrusted) { michael@0: //Check security state to determine if dispatcher is trusted michael@0: aDOMEvent->SetTrusted(nsContentUtils::ThreadsafeIsCallerChrome()); michael@0: } michael@0: michael@0: return EventDispatcher::Dispatch(aTarget, aPresContext, innerEvent, michael@0: aDOMEvent, aEventStatus); michael@0: } else if (aEvent) { michael@0: return EventDispatcher::Dispatch(aTarget, aPresContext, aEvent, michael@0: aDOMEvent, aEventStatus); michael@0: } michael@0: return NS_ERROR_ILLEGAL_VALUE; michael@0: } michael@0: michael@0: /* static */ nsresult michael@0: EventDispatcher::CreateEvent(EventTarget* aOwner, michael@0: nsPresContext* aPresContext, michael@0: WidgetEvent* aEvent, michael@0: const nsAString& aEventType, michael@0: nsIDOMEvent** aDOMEvent) michael@0: { michael@0: *aDOMEvent = nullptr; michael@0: michael@0: if (aEvent) { michael@0: switch(aEvent->eventStructType) { michael@0: case NS_MUTATION_EVENT: michael@0: return NS_NewDOMMutationEvent(aDOMEvent, aOwner, aPresContext, michael@0: aEvent->AsMutationEvent()); michael@0: case NS_GUI_EVENT: michael@0: case NS_SCROLLPORT_EVENT: michael@0: case NS_UI_EVENT: michael@0: return NS_NewDOMUIEvent(aDOMEvent, aOwner, aPresContext, michael@0: aEvent->AsGUIEvent()); michael@0: case NS_SCROLLAREA_EVENT: michael@0: return NS_NewDOMScrollAreaEvent(aDOMEvent, aOwner, aPresContext, michael@0: aEvent->AsScrollAreaEvent()); michael@0: case NS_KEY_EVENT: michael@0: return NS_NewDOMKeyboardEvent(aDOMEvent, aOwner, aPresContext, michael@0: aEvent->AsKeyboardEvent()); michael@0: case NS_COMPOSITION_EVENT: michael@0: return NS_NewDOMCompositionEvent(aDOMEvent, aOwner, aPresContext, michael@0: aEvent->AsCompositionEvent()); michael@0: case NS_MOUSE_EVENT: michael@0: return NS_NewDOMMouseEvent(aDOMEvent, aOwner, aPresContext, michael@0: aEvent->AsMouseEvent()); michael@0: case NS_FOCUS_EVENT: michael@0: return NS_NewDOMFocusEvent(aDOMEvent, aOwner, aPresContext, michael@0: aEvent->AsFocusEvent()); michael@0: case NS_MOUSE_SCROLL_EVENT: michael@0: return NS_NewDOMMouseScrollEvent(aDOMEvent, aOwner, aPresContext, michael@0: aEvent->AsMouseScrollEvent()); michael@0: case NS_WHEEL_EVENT: michael@0: return NS_NewDOMWheelEvent(aDOMEvent, aOwner, aPresContext, michael@0: aEvent->AsWheelEvent()); michael@0: case NS_EDITOR_INPUT_EVENT: michael@0: return NS_NewDOMInputEvent(aDOMEvent, aOwner, aPresContext, michael@0: aEvent->AsEditorInputEvent()); michael@0: case NS_DRAG_EVENT: michael@0: return NS_NewDOMDragEvent(aDOMEvent, aOwner, aPresContext, michael@0: aEvent->AsDragEvent()); michael@0: case NS_TEXT_EVENT: michael@0: return NS_NewDOMUIEvent(aDOMEvent, aOwner, aPresContext, michael@0: aEvent->AsTextEvent()); michael@0: case NS_CLIPBOARD_EVENT: michael@0: return NS_NewDOMClipboardEvent(aDOMEvent, aOwner, aPresContext, michael@0: aEvent->AsClipboardEvent()); michael@0: case NS_SVGZOOM_EVENT: michael@0: return NS_NewDOMSVGZoomEvent(aDOMEvent, aOwner, aPresContext, michael@0: aEvent->AsGUIEvent()); michael@0: case NS_SMIL_TIME_EVENT: michael@0: return NS_NewDOMTimeEvent(aDOMEvent, aOwner, aPresContext, aEvent); michael@0: michael@0: case NS_COMMAND_EVENT: michael@0: return NS_NewDOMCommandEvent(aDOMEvent, aOwner, aPresContext, michael@0: aEvent->AsCommandEvent()); michael@0: case NS_SIMPLE_GESTURE_EVENT: michael@0: return NS_NewDOMSimpleGestureEvent(aDOMEvent, aOwner, aPresContext, michael@0: aEvent->AsSimpleGestureEvent()); michael@0: case NS_POINTER_EVENT: michael@0: return NS_NewDOMPointerEvent(aDOMEvent, aOwner, aPresContext, michael@0: aEvent->AsPointerEvent()); michael@0: case NS_TOUCH_EVENT: michael@0: return NS_NewDOMTouchEvent(aDOMEvent, aOwner, aPresContext, michael@0: aEvent->AsTouchEvent()); michael@0: case NS_TRANSITION_EVENT: michael@0: return NS_NewDOMTransitionEvent(aDOMEvent, aOwner, aPresContext, michael@0: aEvent->AsTransitionEvent()); michael@0: case NS_ANIMATION_EVENT: michael@0: return NS_NewDOMAnimationEvent(aDOMEvent, aOwner, aPresContext, michael@0: aEvent->AsAnimationEvent()); michael@0: default: michael@0: // For all other types of events, create a vanilla event object. michael@0: return NS_NewDOMEvent(aDOMEvent, aOwner, aPresContext, aEvent); michael@0: } michael@0: } michael@0: michael@0: // And if we didn't get an event, check the type argument. michael@0: michael@0: if (aEventType.LowerCaseEqualsLiteral("mouseevent") || michael@0: aEventType.LowerCaseEqualsLiteral("mouseevents") || michael@0: aEventType.LowerCaseEqualsLiteral("popupevents")) michael@0: return NS_NewDOMMouseEvent(aDOMEvent, aOwner, aPresContext, nullptr); michael@0: if (aEventType.LowerCaseEqualsLiteral("mousescrollevents")) michael@0: return NS_NewDOMMouseScrollEvent(aDOMEvent, aOwner, aPresContext, nullptr); michael@0: if (aEventType.LowerCaseEqualsLiteral("dragevent") || michael@0: aEventType.LowerCaseEqualsLiteral("dragevents")) michael@0: return NS_NewDOMDragEvent(aDOMEvent, aOwner, aPresContext, nullptr); michael@0: if (aEventType.LowerCaseEqualsLiteral("keyboardevent") || michael@0: aEventType.LowerCaseEqualsLiteral("keyevents")) michael@0: return NS_NewDOMKeyboardEvent(aDOMEvent, aOwner, aPresContext, nullptr); michael@0: if (aEventType.LowerCaseEqualsLiteral("compositionevent")) michael@0: return NS_NewDOMCompositionEvent(aDOMEvent, aOwner, aPresContext, nullptr); michael@0: if (aEventType.LowerCaseEqualsLiteral("mutationevent") || michael@0: aEventType.LowerCaseEqualsLiteral("mutationevents")) michael@0: return NS_NewDOMMutationEvent(aDOMEvent, aOwner, aPresContext, nullptr); michael@0: if (aEventType.LowerCaseEqualsLiteral("textevent") || michael@0: aEventType.LowerCaseEqualsLiteral("textevents")) michael@0: return NS_NewDOMUIEvent(aDOMEvent, aOwner, aPresContext, nullptr); michael@0: if (aEventType.LowerCaseEqualsLiteral("popupblockedevents")) michael@0: return NS_NewDOMPopupBlockedEvent(aDOMEvent, aOwner, aPresContext, nullptr); michael@0: if (aEventType.LowerCaseEqualsLiteral("deviceorientationevent")) michael@0: return NS_NewDOMDeviceOrientationEvent(aDOMEvent, aOwner, aPresContext, nullptr); michael@0: if (aEventType.LowerCaseEqualsLiteral("devicemotionevent")) michael@0: return NS_NewDOMDeviceMotionEvent(aDOMEvent, aOwner, aPresContext, nullptr); michael@0: if (aEventType.LowerCaseEqualsLiteral("uievent") || michael@0: aEventType.LowerCaseEqualsLiteral("uievents")) michael@0: return NS_NewDOMUIEvent(aDOMEvent, aOwner, aPresContext, nullptr); michael@0: if (aEventType.LowerCaseEqualsLiteral("event") || michael@0: aEventType.LowerCaseEqualsLiteral("events") || michael@0: aEventType.LowerCaseEqualsLiteral("htmlevents") || michael@0: aEventType.LowerCaseEqualsLiteral("svgevent") || michael@0: aEventType.LowerCaseEqualsLiteral("svgevents")) michael@0: return NS_NewDOMEvent(aDOMEvent, aOwner, aPresContext, nullptr); michael@0: if (aEventType.LowerCaseEqualsLiteral("svgzoomevent") || michael@0: aEventType.LowerCaseEqualsLiteral("svgzoomevents")) michael@0: return NS_NewDOMSVGZoomEvent(aDOMEvent, aOwner, aPresContext, nullptr); michael@0: if (aEventType.LowerCaseEqualsLiteral("timeevent") || michael@0: aEventType.LowerCaseEqualsLiteral("timeevents")) michael@0: return NS_NewDOMTimeEvent(aDOMEvent, aOwner, aPresContext, nullptr); michael@0: if (aEventType.LowerCaseEqualsLiteral("xulcommandevent") || michael@0: aEventType.LowerCaseEqualsLiteral("xulcommandevents")) michael@0: return NS_NewDOMXULCommandEvent(aDOMEvent, aOwner, aPresContext, nullptr); michael@0: if (aEventType.LowerCaseEqualsLiteral("commandevent") || michael@0: aEventType.LowerCaseEqualsLiteral("commandevents")) michael@0: return NS_NewDOMCommandEvent(aDOMEvent, aOwner, aPresContext, nullptr); michael@0: if (aEventType.LowerCaseEqualsLiteral("elementreplace")) michael@0: return NS_NewDOMElementReplaceEvent(aDOMEvent, aOwner, aPresContext, nullptr); michael@0: if (aEventType.LowerCaseEqualsLiteral("datacontainerevent") || michael@0: aEventType.LowerCaseEqualsLiteral("datacontainerevents")) michael@0: return NS_NewDOMDataContainerEvent(aDOMEvent, aOwner, aPresContext, nullptr); michael@0: if (aEventType.LowerCaseEqualsLiteral("messageevent")) michael@0: return NS_NewDOMMessageEvent(aDOMEvent, aOwner, aPresContext, nullptr); michael@0: if (aEventType.LowerCaseEqualsLiteral("notifypaintevent")) michael@0: return NS_NewDOMNotifyPaintEvent(aDOMEvent, aOwner, aPresContext, nullptr); michael@0: if (aEventType.LowerCaseEqualsLiteral("simplegestureevent")) michael@0: return NS_NewDOMSimpleGestureEvent(aDOMEvent, aOwner, aPresContext, nullptr); michael@0: if (aEventType.LowerCaseEqualsLiteral("beforeunloadevent")) michael@0: return NS_NewDOMBeforeUnloadEvent(aDOMEvent, aOwner, aPresContext, nullptr); michael@0: if (aEventType.LowerCaseEqualsLiteral("pagetransition")) michael@0: return NS_NewDOMPageTransitionEvent(aDOMEvent, aOwner, aPresContext, nullptr); michael@0: if (aEventType.LowerCaseEqualsLiteral("domtransaction")) michael@0: return NS_NewDOMDOMTransactionEvent(aDOMEvent, aOwner, aPresContext, nullptr); michael@0: if (aEventType.LowerCaseEqualsLiteral("scrollareaevent")) michael@0: return NS_NewDOMScrollAreaEvent(aDOMEvent, aOwner, aPresContext, nullptr); michael@0: if (aEventType.LowerCaseEqualsLiteral("popstateevent")) michael@0: return NS_NewDOMPopStateEvent(aDOMEvent, aOwner, aPresContext, nullptr); michael@0: if (aEventType.LowerCaseEqualsLiteral("closeevent")) michael@0: return NS_NewDOMCloseEvent(aDOMEvent, aOwner, aPresContext, nullptr); michael@0: if (aEventType.LowerCaseEqualsLiteral("touchevent") && michael@0: TouchEvent::PrefEnabled()) michael@0: return NS_NewDOMTouchEvent(aDOMEvent, aOwner, aPresContext, nullptr); michael@0: if (aEventType.LowerCaseEqualsLiteral("hashchangeevent")) michael@0: return NS_NewDOMHashChangeEvent(aDOMEvent, aOwner, aPresContext, nullptr); michael@0: if (aEventType.LowerCaseEqualsLiteral("customevent")) michael@0: return NS_NewDOMCustomEvent(aDOMEvent, aOwner, aPresContext, nullptr); michael@0: if (aEventType.LowerCaseEqualsLiteral("mozsmsevent")) michael@0: return NS_NewDOMMozSmsEvent(aDOMEvent, aOwner, aPresContext, nullptr); michael@0: if (aEventType.LowerCaseEqualsLiteral("mozmmsevent")) michael@0: return NS_NewDOMMozMmsEvent(aDOMEvent, aOwner, aPresContext, nullptr); michael@0: if (aEventType.LowerCaseEqualsLiteral("storageevent")) { michael@0: return NS_NewDOMStorageEvent(aDOMEvent, aOwner, aPresContext, nullptr); michael@0: } michael@0: // NEW EVENT TYPES SHOULD NOT BE ADDED HERE; THEY SHOULD USE ONLY EVENT michael@0: // CONSTRUCTORS michael@0: michael@0: return NS_ERROR_DOM_NOT_SUPPORTED_ERR; michael@0: } michael@0: michael@0: } // namespace mozilla