/** * If the current item in the event target chain has an event listener * manager, this method calls nsEventListenerManager::HandleEvent(). */ void HandleEvent(nsEventChainPostVisitor& aVisitor, ELMCreationDetector& aCd) { if (WantsWillHandleEvent()) { mTarget->WillHandleEvent(aVisitor); } if (aVisitor.mEvent->mFlags.mPropagationStopped) { return; } if (!mManager) { if (!MayHaveListenerManager() && !aCd.MayHaveNewListenerManager()) { return; } mManager = mTarget->GetExistingListenerManager(); } if (mManager) { NS_ASSERTION(aVisitor.mEvent->currentTarget == nullptr, "CurrentTarget should be null!"); mManager->HandleEvent(aVisitor.mPresContext, aVisitor.mEvent, &aVisitor.mDOMEvent, CurrentTarget(), &aVisitor.mEventStatus); NS_ASSERTION(aVisitor.mEvent->currentTarget == nullptr, "CurrentTarget should be null!"); } }
/** * If the current item in the event target chain has an event listener * manager, this method calls nsEventListenerManager::HandleEvent(). */ nsresult HandleEvent(nsEventChainPostVisitor& aVisitor, ELMCreationDetector& aCd, nsCxPusher* aPusher) { if (WantsWillHandleEvent()) { mTarget->WillHandleEvent(aVisitor); } if (aVisitor.mEvent->mFlags.mPropagationStopped) { return NS_OK; } if (!mManager) { if (!MayHaveListenerManager() && !aCd.MayHaveNewListenerManager()) { return NS_OK; } mManager = static_cast<nsEventListenerManager*>(mTarget->GetListenerManager(false)); } if (mManager) { NS_ASSERTION(aVisitor.mEvent->currentTarget == nullptr, "CurrentTarget should be null!"); mManager->HandleEvent(aVisitor.mPresContext, aVisitor.mEvent, &aVisitor.mDOMEvent, CurrentTarget(), &aVisitor.mEventStatus, aPusher); NS_ASSERTION(aVisitor.mEvent->currentTarget == nullptr, "CurrentTarget should be null!"); } return NS_OK; }
/* static */ nsresult nsEventDispatcher::Dispatch(nsISupports* aTarget, nsPresContext* aPresContext, WidgetEvent* aEvent, nsIDOMEvent* aDOMEvent, nsEventStatus* aEventStatus, nsDispatchingCallback* aCallback, nsCOMArray<EventTarget>* aTargets) { PROFILER_LABEL("nsEventDispatcher", "Dispatch"); NS_ASSERTION(aEvent, "Trying to dispatch without WidgetEvent!"); NS_ENSURE_TRUE(!aEvent->mFlags.mIsBeingDispatched, NS_ERROR_DOM_INVALID_STATE_ERR); NS_ASSERTION(!aTargets || !aEvent->message, "Wrong parameters!"); // If we're dispatching an already created DOMEvent object, make // sure it is initialized! // If aTargets is non-null, the event isn't going to be dispatched. NS_ENSURE_TRUE(aEvent->message || !aDOMEvent || aTargets, NS_ERROR_DOM_INVALID_STATE_ERR); nsCOMPtr<EventTarget> target = do_QueryInterface(aTarget); bool retargeted = false; if (aEvent->mFlags.mRetargetToNonNativeAnonymous) { nsCOMPtr<nsIContent> content = do_QueryInterface(target); if (content && content->IsInNativeAnonymousSubtree()) { nsCOMPtr<EventTarget> newTarget = do_QueryInterface(content->FindFirstNonChromeOnlyAccessContent()); NS_ENSURE_STATE(newTarget); aEvent->originalTarget = target; target = newTarget; retargeted = true; } } if (aEvent->mFlags.mOnlyChromeDispatch) { nsCOMPtr<nsINode> node = do_QueryInterface(aTarget); if (!node) { nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(aTarget); if (win) { node = win->GetExtantDoc(); } } NS_ENSURE_STATE(node); nsIDocument* doc = node->OwnerDoc(); if (!nsContentUtils::IsChromeDoc(doc)) { nsPIDOMWindow* win = doc ? doc->GetInnerWindow() : nullptr; // If we can't dispatch the event to chrome, do nothing. EventTarget* piTarget = win ? win->GetParentTarget() : nullptr; NS_ENSURE_TRUE(piTarget, NS_OK); // Set the target to be the original dispatch target, aEvent->target = target; // but use chrome event handler or TabChildGlobal for event target chain. target = piTarget; } } #ifdef DEBUG if (!nsContentUtils::IsSafeToRunScript()) { nsresult rv = NS_ERROR_FAILURE; if (target->GetContextForEventHandlers(&rv) || NS_FAILED(rv)) { nsCOMPtr<nsINode> node = do_QueryInterface(target); if (node && nsContentUtils::IsChromeDoc(node->OwnerDoc())) { NS_WARNING("Fix the caller!"); } else { NS_ERROR("This is unsafe! Fix the caller!"); } } } if (aDOMEvent) { WidgetEvent* innerEvent = aDOMEvent->GetInternalNSEvent(); NS_ASSERTION(innerEvent == aEvent, "The inner event of aDOMEvent is not the same as aEvent!"); } #endif nsresult rv = NS_OK; bool externalDOMEvent = !!(aDOMEvent); // If we have a PresContext, make sure it doesn't die before // event dispatching is finished. nsRefPtr<nsPresContext> kungFuDeathGrip(aPresContext); ELMCreationDetector cd; nsTArray<nsEventTargetChainItem> chain; if (cd.IsMainThread()) { if (!sCachedMainThreadChain) { sCachedMainThreadChain = new nsTArray<nsEventTargetChainItem>(); } chain.SwapElements(*sCachedMainThreadChain); chain.SetCapacity(128); } // Create the event target chain item for the event target. nsEventTargetChainItem* targetEtci = nsEventTargetChainItem::Create(chain, target->GetTargetForEventTargetChain()); MOZ_ASSERT(&chain[0] == targetEtci); if (!targetEtci->IsValid()) { nsEventTargetChainItem::DestroyLast(chain, targetEtci); return NS_ERROR_FAILURE; } // Make sure that nsIDOMEvent::target and nsIDOMEvent::originalTarget // point to the last item in the chain. if (!aEvent->target) { // Note, CurrentTarget() points always to the object returned by // GetTargetForEventTargetChain(). aEvent->target = targetEtci->CurrentTarget(); } else { // XXX But if the target is already set, use that. This is a hack // for the 'load', 'beforeunload' and 'unload' events, // which are dispatched to |window| but have document as their target. // // Make sure that the event target points to the right object. aEvent->target = aEvent->target->GetTargetForEventTargetChain(); NS_ENSURE_STATE(aEvent->target); } if (retargeted) { aEvent->originalTarget = aEvent->originalTarget->GetTargetForEventTargetChain(); NS_ENSURE_STATE(aEvent->originalTarget); } else { aEvent->originalTarget = aEvent->target; } nsCOMPtr<nsIContent> content = do_QueryInterface(aEvent->originalTarget); bool isInAnon = (content && content->IsInAnonymousSubtree()); aEvent->mFlags.mIsBeingDispatched = true; // Create visitor object and start event dispatching. // PreHandleEvent for the original target. nsEventStatus status = aEventStatus ? *aEventStatus : nsEventStatus_eIgnore; nsEventChainPreVisitor 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. nsEventTargetChainItem::DestroyLast(chain, targetEtci); targetEtci = EventTargetChainItemForChromeTarget(chain, content); NS_ENSURE_STATE(targetEtci); MOZ_ASSERT(&chain[0] == targetEtci); targetEtci->PreHandleEvent(preVisitor); } if (preVisitor.mCanHandle) { // At least the original target can handle the event. // Setting the retarget to the |target| simplifies retargeting code. nsCOMPtr<EventTarget> t = do_QueryInterface(aEvent->target); targetEtci->SetNewTarget(t); nsEventTargetChainItem* topEtci = targetEtci; targetEtci = nullptr; while (preVisitor.mParentTarget) { EventTarget* parentTarget = preVisitor.mParentTarget; nsEventTargetChainItem* parentEtci = nsEventTargetChainItem::Create(chain, preVisitor.mParentTarget, topEtci); if (!parentEtci->IsValid()) { nsEventTargetChainItem::DestroyLast(chain, parentEtci); rv = NS_ERROR_FAILURE; break; } // Item needs event retargetting. if (preVisitor.mEventTargetAtParent) { // Need to set the target of the event // so that also the next retargeting works. preVisitor.mEvent->target = preVisitor.mEventTargetAtParent; parentEtci->SetNewTarget(preVisitor.mEventTargetAtParent); } parentEtci->PreHandleEvent(preVisitor); if (preVisitor.mCanHandle) { topEtci = parentEtci; } else { nsEventTargetChainItem::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; } } } } break; } } if (NS_SUCCEEDED(rv)) { if (aTargets) { aTargets->Clear(); aTargets->SetCapacity(chain.Length()); for (uint32_t i = 0; i < chain.Length(); ++i) { aTargets->AppendObject(chain[i].CurrentTarget()->GetTargetForDOMEvent()); } } else { // Event target chain is created. Handle the chain. nsEventChainPostVisitor postVisitor(preVisitor); nsEventTargetChainItem::HandleEventTargetChain(chain, postVisitor, aCallback, cd); preVisitor.mEventStatus = postVisitor.mEventStatus; // If the DOM event was created during event flow. if (!preVisitor.mDOMEvent && postVisitor.mDOMEvent) { preVisitor.mDOMEvent = postVisitor.mDOMEvent; } } } } // Note, nsEventTargetChainItem objects are deleted when the chain goes out of // the scope. aEvent->mFlags.mIsBeingDispatched = false; aEvent->mFlags.mDispatchedAtLeastOnce = true; if (!externalDOMEvent && preVisitor.mDOMEvent) { // An nsDOMEvent was created while dispatching the event. // Duplicate private data if someone holds a pointer to it. nsrefcnt rc = 0; NS_RELEASE2(preVisitor.mDOMEvent, rc); if (preVisitor.mDOMEvent) { preVisitor.mDOMEvent->DuplicatePrivateData(); } } if (aEventStatus) { *aEventStatus = preVisitor.mEventStatus; } if (cd.IsMainThread() && chain.Capacity() == 128 && sCachedMainThreadChain) { chain.ClearAndRetainStorage(); chain.SwapElements(*sCachedMainThreadChain); } return rv; }