NS_IMPL_CYCLE_COLLECTION_UNLINK_END nsPIDOMWindow* EventListenerManager::GetInnerWindowForTarget() { nsCOMPtr<nsINode> node = do_QueryInterface(mTarget); if (node) { // XXX sXBL/XBL2 issue -- do we really want the owner here? What // if that's the XBL document? return node->OwnerDoc()->GetInnerWindow(); } nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow(); return window; }
nsIDocShell* EventListenerManager::GetDocShellForTarget() { nsCOMPtr<nsINode> node(do_QueryInterface(mTarget)); nsIDocument* doc = nullptr; nsIDocShell* docShell = nullptr; if (node) { doc = node->OwnerDoc(); if (!doc->GetDocShell()) { bool ignore; nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(doc->GetScriptHandlingObject(ignore)); if (window) { doc = window->GetExtantDoc(); } } } else { nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow(); if (window) { doc = window->GetExtantDoc(); } } if (!doc) { nsCOMPtr<DOMEventTargetHelper> helper(do_QueryInterface(mTarget)); if (helper) { nsPIDOMWindow* window = helper->GetOwner(); if (window) { doc = window->GetExtantDoc(); } } } if (doc) { docShell = doc->GetDocShell(); } return docShell; }
void EventListenerManager::RemoveEventListenerInternal( const EventListenerHolder& aListenerHolder, uint32_t aType, nsIAtom* aUserType, const nsAString& aTypeString, const EventListenerFlags& aFlags, bool aAllEvents) { if (!aListenerHolder || !aType || mClearingListeners) { return; } Listener* listener; uint32_t count = mListeners.Length(); uint32_t typeCount = 0; bool deviceType = IsDeviceType(aType); #ifdef MOZ_B2G bool timeChangeEvent = (aType == NS_MOZ_TIME_CHANGE_EVENT); bool networkEvent = (aType == NS_NETWORK_UPLOAD_EVENT || aType == NS_NETWORK_DOWNLOAD_EVENT); #endif // MOZ_B2G for (uint32_t i = 0; i < count; ++i) { listener = &mListeners.ElementAt(i); if (EVENT_TYPE_EQUALS(listener, aType, aUserType, aTypeString, aAllEvents)) { ++typeCount; if (listener->mListener == aListenerHolder && listener->mFlags.EqualsIgnoringTrustness(aFlags)) { nsRefPtr<EventListenerManager> kungFuDeathGrip(this); mListeners.RemoveElementAt(i); --count; mNoListenerForEvent = NS_EVENT_NULL; mNoListenerForEventAtom = nullptr; if (mTarget && aUserType) { mTarget->EventListenerRemoved(aUserType); } if (!deviceType #ifdef MOZ_B2G && !timeChangeEvent && !networkEvent #endif // MOZ_B2G ) { return; } --typeCount; } } } if (!aAllEvents && deviceType && typeCount == 0) { DisableDevice(aType); #ifdef MOZ_B2G } else if (timeChangeEvent && typeCount == 0) { nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow(); if (window) { window->DisableTimeChangeNotifications(); } } else if (!aAllEvents && networkEvent && typeCount == 0) { nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow(); if (window) { window->DisableNetworkEvent(aType); } #endif // MOZ_B2G } }
void EventListenerManager::AddEventListenerInternal( const EventListenerHolder& aListenerHolder, uint32_t aType, nsIAtom* aTypeAtom, const nsAString& aTypeString, const EventListenerFlags& aFlags, bool aHandler, bool aAllEvents) { MOZ_ASSERT((NS_IsMainThread() && aType && aTypeAtom) || // Main thread (!NS_IsMainThread() && aType && !aTypeString.IsEmpty()) || // non-main-thread aAllEvents, "Missing type"); // all-events listener if (!aListenerHolder || mClearingListeners) { return; } // Since there is no public API to call us with an EventListenerHolder, we // know that there's an EventListenerHolder on the stack holding a strong ref // to the listener. Listener* listener; uint32_t count = mListeners.Length(); for (uint32_t i = 0; i < count; i++) { listener = &mListeners.ElementAt(i); // mListener == aListenerHolder is the last one, since it can be a bit slow. if (listener->mListenerIsHandler == aHandler && listener->mFlags == aFlags && EVENT_TYPE_EQUALS(listener, aType, aTypeAtom, aTypeString, aAllEvents) && listener->mListener == aListenerHolder) { return; } } mNoListenerForEvent = NS_EVENT_NULL; mNoListenerForEventAtom = nullptr; listener = aAllEvents ? mListeners.InsertElementAt(0) : mListeners.AppendElement(); listener->mListener = aListenerHolder; MOZ_ASSERT(aType < PR_UINT16_MAX); listener->mEventType = aType; listener->mTypeString = aTypeString; listener->mTypeAtom = aTypeAtom; listener->mFlags = aFlags; listener->mListenerIsHandler = aHandler; listener->mHandlerIsString = false; listener->mAllEvents = aAllEvents; // Detect the type of event listener. nsCOMPtr<nsIXPConnectWrappedJS> wjs; if (aFlags.mListenerIsJSListener) { MOZ_ASSERT(!aListenerHolder.HasWebIDLCallback()); listener->mListenerType = Listener::eJSEventListener; } else if (aListenerHolder.HasWebIDLCallback()) { listener->mListenerType = Listener::eWebIDLListener; } else if ((wjs = do_QueryInterface(aListenerHolder.GetXPCOMCallback()))) { listener->mListenerType = Listener::eWrappedJSListener; } else { listener->mListenerType = Listener::eNativeListener; } if (aFlags.mInSystemGroup) { mMayHaveSystemGroupListeners = true; } if (aFlags.mCapture) { mMayHaveCapturingListeners = true; } if (aType == NS_AFTERPAINT) { mMayHavePaintEventListener = true; nsPIDOMWindow* window = GetInnerWindowForTarget(); if (window) { window->SetHasPaintEventListeners(); } } else if (aType == NS_MOZAUDIOAVAILABLE) { mMayHaveAudioAvailableEventListener = true; nsPIDOMWindow* window = GetInnerWindowForTarget(); if (window) { window->SetHasAudioAvailableEventListeners(); } } else if (aType >= NS_MUTATION_START && aType <= NS_MUTATION_END) { // For mutation listeners, we need to update the global bit on the DOM window. // Otherwise we won't actually fire the mutation event. mMayHaveMutationListeners = true; // Go from our target to the nearest enclosing DOM window. nsPIDOMWindow* window = GetInnerWindowForTarget(); if (window) { nsCOMPtr<nsIDocument> doc = window->GetExtantDoc(); if (doc) { doc->WarnOnceAbout(nsIDocument::eMutationEvent); } // If aType is NS_MUTATION_SUBTREEMODIFIED, we need to listen all // mutations. nsContentUtils::HasMutationListeners relies on this. window->SetMutationListeners((aType == NS_MUTATION_SUBTREEMODIFIED) ? kAllMutationBits : MutationBitForEventType(aType)); } } else if (aTypeAtom == nsGkAtoms::ondeviceorientation) { EnableDevice(NS_DEVICE_ORIENTATION); } else if (aTypeAtom == nsGkAtoms::ondeviceproximity || aTypeAtom == nsGkAtoms::onuserproximity) { EnableDevice(NS_DEVICE_PROXIMITY); } else if (aTypeAtom == nsGkAtoms::ondevicelight) { EnableDevice(NS_DEVICE_LIGHT); } else if (aTypeAtom == nsGkAtoms::ondevicemotion) { EnableDevice(NS_DEVICE_MOTION); #ifdef MOZ_B2G } else if (aTypeAtom == nsGkAtoms::onmoztimechange) { nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow(); if (window) { window->EnableTimeChangeNotifications(); } } else if (aTypeAtom == nsGkAtoms::onmoznetworkupload) { nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow(); if (window) { window->EnableNetworkEvent(NS_NETWORK_UPLOAD_EVENT); } } else if (aTypeAtom == nsGkAtoms::onmoznetworkdownload) { nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow(); if (window) { window->EnableNetworkEvent(NS_NETWORK_DOWNLOAD_EVENT); } #endif // MOZ_B2G } else if (aTypeAtom == nsGkAtoms::ontouchstart || aTypeAtom == nsGkAtoms::ontouchend || aTypeAtom == nsGkAtoms::ontouchmove || aTypeAtom == nsGkAtoms::ontouchenter || aTypeAtom == nsGkAtoms::ontouchleave || aTypeAtom == nsGkAtoms::ontouchcancel) { mMayHaveTouchEventListener = true; nsPIDOMWindow* window = GetInnerWindowForTarget(); // we don't want touchevent listeners added by scrollbars to flip this flag // so we ignore listeners created with system event flag if (window && !aFlags.mInSystemGroup) { window->SetHasTouchEventListeners(); } } else if (aType >= NS_POINTER_EVENT_START && aType <= NS_POINTER_LOST_CAPTURE) { nsPIDOMWindow* window = GetInnerWindowForTarget(); if (aTypeAtom == nsGkAtoms::onpointerenter || aTypeAtom == nsGkAtoms::onpointerleave) { mMayHavePointerEnterLeaveEventListener = true; if (window) { #ifdef DEBUG nsCOMPtr<nsIDocument> d = window->GetExtantDoc(); NS_WARN_IF_FALSE(!nsContentUtils::IsChromeDoc(d), "Please do not use pointerenter/leave events in chrome. " "They are slower than pointerover/out!"); #endif window->SetHasPointerEnterLeaveEventListeners(); } } } else if (aTypeAtom == nsGkAtoms::onmouseenter || aTypeAtom == nsGkAtoms::onmouseleave) { mMayHaveMouseEnterLeaveEventListener = true; nsPIDOMWindow* window = GetInnerWindowForTarget(); if (window) { #ifdef DEBUG nsCOMPtr<nsIDocument> d = window->GetExtantDoc(); NS_WARN_IF_FALSE(!nsContentUtils::IsChromeDoc(d), "Please do not use mouseenter/leave events in chrome. " "They are slower than mouseover/out!"); #endif window->SetHasMouseEnterLeaveEventListeners(); } #ifdef MOZ_GAMEPAD } else if (aType >= NS_GAMEPAD_START && aType <= NS_GAMEPAD_END) { nsPIDOMWindow* window = GetInnerWindowForTarget(); if (window) { window->SetHasGamepadEventListener(); } #endif } if (aTypeAtom && mTarget) { mTarget->EventListenerAdded(aTypeAtom); } }