michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 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: // Microsoft's API Name hackery sucks michael@0: #undef CreateEvent michael@0: michael@0: #include "mozilla/BasicEvents.h" michael@0: #include "mozilla/EventDispatcher.h" michael@0: #include "mozilla/EventListenerManager.h" michael@0: #ifdef MOZ_B2G michael@0: #include "mozilla/Hal.h" michael@0: #endif // #ifdef MOZ_B2G michael@0: #include "mozilla/HalSensor.h" michael@0: #include "mozilla/InternalMutationEvent.h" michael@0: #include "mozilla/JSEventHandler.h" michael@0: #include "mozilla/MemoryReporting.h" michael@0: #include "mozilla/dom/BindingUtils.h" michael@0: #include "mozilla/dom/Element.h" michael@0: #include "mozilla/dom/Event.h" michael@0: michael@0: #include "EventListenerService.h" michael@0: #include "nsCOMArray.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsDOMCID.h" michael@0: #include "nsError.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsIContent.h" michael@0: #include "nsIContentSecurityPolicy.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsIDOMEventListener.h" michael@0: #include "nsIScriptGlobalObject.h" michael@0: #include "nsISupports.h" michael@0: #include "nsIXPConnect.h" michael@0: #include "nsJSUtils.h" michael@0: #include "nsNameSpaceManager.h" michael@0: #include "nsPIDOMWindow.h" michael@0: #include "nsSandboxFlags.h" michael@0: #include "xpcpublic.h" michael@0: michael@0: namespace mozilla { michael@0: michael@0: using namespace dom; michael@0: using namespace hal; michael@0: michael@0: #define EVENT_TYPE_EQUALS(ls, type, userType, typeString, allEvents) \ michael@0: ((ls->mEventType == type && \ michael@0: (ls->mEventType != NS_USER_DEFINED_EVENT || \ michael@0: (mIsMainThreadELM && ls->mTypeAtom == userType) || \ michael@0: (!mIsMainThreadELM && ls->mTypeString.Equals(typeString)))) || \ michael@0: (allEvents && ls->mAllEvents)) michael@0: michael@0: static const uint32_t kAllMutationBits = michael@0: NS_EVENT_BITS_MUTATION_SUBTREEMODIFIED | michael@0: NS_EVENT_BITS_MUTATION_NODEINSERTED | michael@0: NS_EVENT_BITS_MUTATION_NODEREMOVED | michael@0: NS_EVENT_BITS_MUTATION_NODEREMOVEDFROMDOCUMENT | michael@0: NS_EVENT_BITS_MUTATION_NODEINSERTEDINTODOCUMENT | michael@0: NS_EVENT_BITS_MUTATION_ATTRMODIFIED | michael@0: NS_EVENT_BITS_MUTATION_CHARACTERDATAMODIFIED; michael@0: michael@0: static uint32_t michael@0: MutationBitForEventType(uint32_t aEventType) michael@0: { michael@0: switch (aEventType) { michael@0: case NS_MUTATION_SUBTREEMODIFIED: michael@0: return NS_EVENT_BITS_MUTATION_SUBTREEMODIFIED; michael@0: case NS_MUTATION_NODEINSERTED: michael@0: return NS_EVENT_BITS_MUTATION_NODEINSERTED; michael@0: case NS_MUTATION_NODEREMOVED: michael@0: return NS_EVENT_BITS_MUTATION_NODEREMOVED; michael@0: case NS_MUTATION_NODEREMOVEDFROMDOCUMENT: michael@0: return NS_EVENT_BITS_MUTATION_NODEREMOVEDFROMDOCUMENT; michael@0: case NS_MUTATION_NODEINSERTEDINTODOCUMENT: michael@0: return NS_EVENT_BITS_MUTATION_NODEINSERTEDINTODOCUMENT; michael@0: case NS_MUTATION_ATTRMODIFIED: michael@0: return NS_EVENT_BITS_MUTATION_ATTRMODIFIED; michael@0: case NS_MUTATION_CHARACTERDATAMODIFIED: michael@0: return NS_EVENT_BITS_MUTATION_CHARACTERDATAMODIFIED; michael@0: default: michael@0: break; michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: uint32_t EventListenerManager::sMainThreadCreatedCount = 0; michael@0: michael@0: EventListenerManager::EventListenerManager(EventTarget* aTarget) michael@0: : mMayHavePaintEventListener(false) michael@0: , mMayHaveMutationListeners(false) michael@0: , mMayHaveCapturingListeners(false) michael@0: , mMayHaveSystemGroupListeners(false) michael@0: , mMayHaveTouchEventListener(false) michael@0: , mMayHaveMouseEnterLeaveEventListener(false) michael@0: , mMayHavePointerEnterLeaveEventListener(false) michael@0: , mClearingListeners(false) michael@0: , mIsMainThreadELM(NS_IsMainThread()) michael@0: , mNoListenerForEvent(0) michael@0: , mTarget(aTarget) michael@0: { michael@0: NS_ASSERTION(aTarget, "unexpected null pointer"); michael@0: michael@0: if (mIsMainThreadELM) { michael@0: ++sMainThreadCreatedCount; michael@0: } michael@0: } michael@0: michael@0: EventListenerManager::~EventListenerManager() michael@0: { michael@0: // If your code fails this assertion, a possible reason is that michael@0: // a class did not call our Disconnect() manually. Note that michael@0: // this class can have Disconnect called in one of two ways: michael@0: // if it is part of a cycle, then in Unlink() (such a cycle michael@0: // would be with one of the listeners, not mTarget which is weak). michael@0: // If not part of a cycle, then Disconnect must be called manually, michael@0: // typically from the destructor of the owner class (mTarget). michael@0: // XXX azakai: Is there any reason to not just call Disconnect michael@0: // from right here, if not previously called? michael@0: NS_ASSERTION(!mTarget, "didn't call Disconnect"); michael@0: RemoveAllListeners(); michael@0: } michael@0: michael@0: void michael@0: EventListenerManager::RemoveAllListeners() michael@0: { michael@0: if (mClearingListeners) { michael@0: return; michael@0: } michael@0: mClearingListeners = true; michael@0: mListeners.Clear(); michael@0: mClearingListeners = false; michael@0: } michael@0: michael@0: void michael@0: EventListenerManager::Shutdown() michael@0: { michael@0: Event::Shutdown(); michael@0: } michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(EventListenerManager, AddRef) michael@0: NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(EventListenerManager, Release) michael@0: michael@0: inline void michael@0: ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback, michael@0: EventListenerManager::Listener& aField, michael@0: const char* aName, michael@0: unsigned aFlags) michael@0: { michael@0: if (MOZ_UNLIKELY(aCallback.WantDebugInfo())) { michael@0: nsAutoCString name; michael@0: name.AppendASCII(aName); michael@0: if (aField.mTypeAtom) { michael@0: name.AppendASCII(" event="); michael@0: name.Append(nsAtomCString(aField.mTypeAtom)); michael@0: name.AppendASCII(" listenerType="); michael@0: name.AppendInt(aField.mListenerType); michael@0: name.AppendASCII(" "); michael@0: } michael@0: CycleCollectionNoteChild(aCallback, aField.mListener.GetISupports(), name.get(), michael@0: aFlags); michael@0: } else { michael@0: CycleCollectionNoteChild(aCallback, aField.mListener.GetISupports(), aName, michael@0: aFlags); michael@0: } michael@0: } michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CLASS(EventListenerManager) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(EventListenerManager) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListeners) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(EventListenerManager) michael@0: tmp->Disconnect(); michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_END michael@0: michael@0: michael@0: nsPIDOMWindow* michael@0: EventListenerManager::GetInnerWindowForTarget() michael@0: { michael@0: nsCOMPtr node = do_QueryInterface(mTarget); michael@0: if (node) { michael@0: // XXX sXBL/XBL2 issue -- do we really want the owner here? What michael@0: // if that's the XBL document? michael@0: return node->OwnerDoc()->GetInnerWindow(); michael@0: } michael@0: michael@0: nsCOMPtr window = GetTargetAsInnerWindow(); michael@0: return window; michael@0: } michael@0: michael@0: already_AddRefed michael@0: EventListenerManager::GetTargetAsInnerWindow() const michael@0: { michael@0: nsCOMPtr window = do_QueryInterface(mTarget); michael@0: if (!window) { michael@0: return nullptr; michael@0: } michael@0: michael@0: NS_ASSERTION(window->IsInnerWindow(), "Target should not be an outer window"); michael@0: return window.forget(); michael@0: } michael@0: michael@0: void michael@0: EventListenerManager::AddEventListenerInternal( michael@0: const EventListenerHolder& aListenerHolder, michael@0: uint32_t aType, michael@0: nsIAtom* aTypeAtom, michael@0: const nsAString& aTypeString, michael@0: const EventListenerFlags& aFlags, michael@0: bool aHandler, michael@0: bool aAllEvents) michael@0: { michael@0: MOZ_ASSERT((NS_IsMainThread() && aType && aTypeAtom) || // Main thread michael@0: (!NS_IsMainThread() && aType && !aTypeString.IsEmpty()) || // non-main-thread michael@0: aAllEvents, "Missing type"); // all-events listener michael@0: michael@0: if (!aListenerHolder || mClearingListeners) { michael@0: return; michael@0: } michael@0: michael@0: // Since there is no public API to call us with an EventListenerHolder, we michael@0: // know that there's an EventListenerHolder on the stack holding a strong ref michael@0: // to the listener. michael@0: michael@0: Listener* listener; michael@0: uint32_t count = mListeners.Length(); michael@0: for (uint32_t i = 0; i < count; i++) { michael@0: listener = &mListeners.ElementAt(i); michael@0: // mListener == aListenerHolder is the last one, since it can be a bit slow. michael@0: if (listener->mListenerIsHandler == aHandler && michael@0: listener->mFlags == aFlags && michael@0: EVENT_TYPE_EQUALS(listener, aType, aTypeAtom, aTypeString, michael@0: aAllEvents) && michael@0: listener->mListener == aListenerHolder) { michael@0: return; michael@0: } michael@0: } michael@0: michael@0: mNoListenerForEvent = NS_EVENT_NULL; michael@0: mNoListenerForEventAtom = nullptr; michael@0: michael@0: listener = aAllEvents ? mListeners.InsertElementAt(0) : michael@0: mListeners.AppendElement(); michael@0: listener->mListener = aListenerHolder; michael@0: MOZ_ASSERT(aType < PR_UINT16_MAX); michael@0: listener->mEventType = aType; michael@0: listener->mTypeString = aTypeString; michael@0: listener->mTypeAtom = aTypeAtom; michael@0: listener->mFlags = aFlags; michael@0: listener->mListenerIsHandler = aHandler; michael@0: listener->mHandlerIsString = false; michael@0: listener->mAllEvents = aAllEvents; michael@0: michael@0: // Detect the type of event listener. michael@0: nsCOMPtr wjs; michael@0: if (aFlags.mListenerIsJSListener) { michael@0: MOZ_ASSERT(!aListenerHolder.HasWebIDLCallback()); michael@0: listener->mListenerType = Listener::eJSEventListener; michael@0: } else if (aListenerHolder.HasWebIDLCallback()) { michael@0: listener->mListenerType = Listener::eWebIDLListener; michael@0: } else if ((wjs = do_QueryInterface(aListenerHolder.GetXPCOMCallback()))) { michael@0: listener->mListenerType = Listener::eWrappedJSListener; michael@0: } else { michael@0: listener->mListenerType = Listener::eNativeListener; michael@0: } michael@0: michael@0: michael@0: if (aFlags.mInSystemGroup) { michael@0: mMayHaveSystemGroupListeners = true; michael@0: } michael@0: if (aFlags.mCapture) { michael@0: mMayHaveCapturingListeners = true; michael@0: } michael@0: michael@0: if (aType == NS_AFTERPAINT) { michael@0: mMayHavePaintEventListener = true; michael@0: nsPIDOMWindow* window = GetInnerWindowForTarget(); michael@0: if (window) { michael@0: window->SetHasPaintEventListeners(); michael@0: } michael@0: } else if (aType >= NS_MUTATION_START && aType <= NS_MUTATION_END) { michael@0: // For mutation listeners, we need to update the global bit on the DOM window. michael@0: // Otherwise we won't actually fire the mutation event. michael@0: mMayHaveMutationListeners = true; michael@0: // Go from our target to the nearest enclosing DOM window. michael@0: nsPIDOMWindow* window = GetInnerWindowForTarget(); michael@0: if (window) { michael@0: nsCOMPtr doc = window->GetExtantDoc(); michael@0: if (doc) { michael@0: doc->WarnOnceAbout(nsIDocument::eMutationEvent); michael@0: } michael@0: // If aType is NS_MUTATION_SUBTREEMODIFIED, we need to listen all michael@0: // mutations. nsContentUtils::HasMutationListeners relies on this. michael@0: window->SetMutationListeners((aType == NS_MUTATION_SUBTREEMODIFIED) ? michael@0: kAllMutationBits : michael@0: MutationBitForEventType(aType)); michael@0: } michael@0: } else if (aTypeAtom == nsGkAtoms::ondeviceorientation) { michael@0: EnableDevice(NS_DEVICE_ORIENTATION); michael@0: } else if (aTypeAtom == nsGkAtoms::ondeviceproximity || aTypeAtom == nsGkAtoms::onuserproximity) { michael@0: EnableDevice(NS_DEVICE_PROXIMITY); michael@0: } else if (aTypeAtom == nsGkAtoms::ondevicelight) { michael@0: EnableDevice(NS_DEVICE_LIGHT); michael@0: } else if (aTypeAtom == nsGkAtoms::ondevicemotion) { michael@0: EnableDevice(NS_DEVICE_MOTION); michael@0: #ifdef MOZ_B2G michael@0: } else if (aTypeAtom == nsGkAtoms::onmoztimechange) { michael@0: nsCOMPtr window = GetTargetAsInnerWindow(); michael@0: if (window) { michael@0: window->EnableTimeChangeNotifications(); michael@0: } michael@0: } else if (aTypeAtom == nsGkAtoms::onmoznetworkupload) { michael@0: nsCOMPtr window = GetTargetAsInnerWindow(); michael@0: if (window) { michael@0: window->EnableNetworkEvent(NS_NETWORK_UPLOAD_EVENT); michael@0: } michael@0: } else if (aTypeAtom == nsGkAtoms::onmoznetworkdownload) { michael@0: nsCOMPtr window = GetTargetAsInnerWindow(); michael@0: if (window) { michael@0: window->EnableNetworkEvent(NS_NETWORK_DOWNLOAD_EVENT); michael@0: } michael@0: #endif // MOZ_B2G michael@0: } else if (aTypeAtom == nsGkAtoms::ontouchstart || michael@0: aTypeAtom == nsGkAtoms::ontouchend || michael@0: aTypeAtom == nsGkAtoms::ontouchmove || michael@0: aTypeAtom == nsGkAtoms::ontouchenter || michael@0: aTypeAtom == nsGkAtoms::ontouchleave || michael@0: aTypeAtom == nsGkAtoms::ontouchcancel) { michael@0: mMayHaveTouchEventListener = true; michael@0: nsPIDOMWindow* window = GetInnerWindowForTarget(); michael@0: // we don't want touchevent listeners added by scrollbars to flip this flag michael@0: // so we ignore listeners created with system event flag michael@0: if (window && !aFlags.mInSystemGroup) { michael@0: window->SetHasTouchEventListeners(); michael@0: } michael@0: } else if (aType >= NS_POINTER_EVENT_START && aType <= NS_POINTER_LOST_CAPTURE) { michael@0: nsPIDOMWindow* window = GetInnerWindowForTarget(); michael@0: if (aTypeAtom == nsGkAtoms::onpointerenter || michael@0: aTypeAtom == nsGkAtoms::onpointerleave) { michael@0: mMayHavePointerEnterLeaveEventListener = true; michael@0: if (window) { michael@0: #ifdef DEBUG michael@0: nsCOMPtr d = window->GetExtantDoc(); michael@0: NS_WARN_IF_FALSE(!nsContentUtils::IsChromeDoc(d), michael@0: "Please do not use pointerenter/leave events in chrome. " michael@0: "They are slower than pointerover/out!"); michael@0: #endif michael@0: window->SetHasPointerEnterLeaveEventListeners(); michael@0: } michael@0: } michael@0: } else if (aTypeAtom == nsGkAtoms::onmouseenter || michael@0: aTypeAtom == nsGkAtoms::onmouseleave) { michael@0: mMayHaveMouseEnterLeaveEventListener = true; michael@0: nsPIDOMWindow* window = GetInnerWindowForTarget(); michael@0: if (window) { michael@0: #ifdef DEBUG michael@0: nsCOMPtr d = window->GetExtantDoc(); michael@0: NS_WARN_IF_FALSE(!nsContentUtils::IsChromeDoc(d), michael@0: "Please do not use mouseenter/leave events in chrome. " michael@0: "They are slower than mouseover/out!"); michael@0: #endif michael@0: window->SetHasMouseEnterLeaveEventListeners(); michael@0: } michael@0: #ifdef MOZ_GAMEPAD michael@0: } else if (aType >= NS_GAMEPAD_START && michael@0: aType <= NS_GAMEPAD_END) { michael@0: nsPIDOMWindow* window = GetInnerWindowForTarget(); michael@0: if (window) { michael@0: window->SetHasGamepadEventListener(); michael@0: } michael@0: #endif michael@0: } michael@0: if (aTypeAtom && mTarget) { michael@0: mTarget->EventListenerAdded(aTypeAtom); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: EventListenerManager::IsDeviceType(uint32_t aType) michael@0: { michael@0: switch (aType) { michael@0: case NS_DEVICE_ORIENTATION: michael@0: case NS_DEVICE_MOTION: michael@0: case NS_DEVICE_LIGHT: michael@0: case NS_DEVICE_PROXIMITY: michael@0: case NS_USER_PROXIMITY: michael@0: return true; michael@0: default: michael@0: break; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: EventListenerManager::EnableDevice(uint32_t aType) michael@0: { michael@0: nsCOMPtr window = GetTargetAsInnerWindow(); michael@0: if (!window) { michael@0: return; michael@0: } michael@0: michael@0: switch (aType) { michael@0: case NS_DEVICE_ORIENTATION: michael@0: window->EnableDeviceSensor(SENSOR_ORIENTATION); michael@0: break; michael@0: case NS_DEVICE_PROXIMITY: michael@0: case NS_USER_PROXIMITY: michael@0: window->EnableDeviceSensor(SENSOR_PROXIMITY); michael@0: break; michael@0: case NS_DEVICE_LIGHT: michael@0: window->EnableDeviceSensor(SENSOR_LIGHT); michael@0: break; michael@0: case NS_DEVICE_MOTION: michael@0: window->EnableDeviceSensor(SENSOR_ACCELERATION); michael@0: window->EnableDeviceSensor(SENSOR_LINEAR_ACCELERATION); michael@0: window->EnableDeviceSensor(SENSOR_GYROSCOPE); michael@0: break; michael@0: default: michael@0: NS_WARNING("Enabling an unknown device sensor."); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: void michael@0: EventListenerManager::DisableDevice(uint32_t aType) michael@0: { michael@0: nsCOMPtr window = GetTargetAsInnerWindow(); michael@0: if (!window) { michael@0: return; michael@0: } michael@0: michael@0: switch (aType) { michael@0: case NS_DEVICE_ORIENTATION: michael@0: window->DisableDeviceSensor(SENSOR_ORIENTATION); michael@0: break; michael@0: case NS_DEVICE_MOTION: michael@0: window->DisableDeviceSensor(SENSOR_ACCELERATION); michael@0: window->DisableDeviceSensor(SENSOR_LINEAR_ACCELERATION); michael@0: window->DisableDeviceSensor(SENSOR_GYROSCOPE); michael@0: break; michael@0: case NS_DEVICE_PROXIMITY: michael@0: case NS_USER_PROXIMITY: michael@0: window->DisableDeviceSensor(SENSOR_PROXIMITY); michael@0: break; michael@0: case NS_DEVICE_LIGHT: michael@0: window->DisableDeviceSensor(SENSOR_LIGHT); michael@0: break; michael@0: default: michael@0: NS_WARNING("Disabling an unknown device sensor."); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: void michael@0: EventListenerManager::RemoveEventListenerInternal( michael@0: const EventListenerHolder& aListenerHolder, michael@0: uint32_t aType, michael@0: nsIAtom* aUserType, michael@0: const nsAString& aTypeString, michael@0: const EventListenerFlags& aFlags, michael@0: bool aAllEvents) michael@0: { michael@0: if (!aListenerHolder || !aType || mClearingListeners) { michael@0: return; michael@0: } michael@0: michael@0: Listener* listener; michael@0: michael@0: uint32_t count = mListeners.Length(); michael@0: uint32_t typeCount = 0; michael@0: bool deviceType = IsDeviceType(aType); michael@0: #ifdef MOZ_B2G michael@0: bool timeChangeEvent = (aType == NS_MOZ_TIME_CHANGE_EVENT); michael@0: bool networkEvent = (aType == NS_NETWORK_UPLOAD_EVENT || michael@0: aType == NS_NETWORK_DOWNLOAD_EVENT); michael@0: #endif // MOZ_B2G michael@0: michael@0: for (uint32_t i = 0; i < count; ++i) { michael@0: listener = &mListeners.ElementAt(i); michael@0: if (EVENT_TYPE_EQUALS(listener, aType, aUserType, aTypeString, michael@0: aAllEvents)) { michael@0: ++typeCount; michael@0: if (listener->mListener == aListenerHolder && michael@0: listener->mFlags.EqualsIgnoringTrustness(aFlags)) { michael@0: nsRefPtr kungFuDeathGrip(this); michael@0: mListeners.RemoveElementAt(i); michael@0: --count; michael@0: mNoListenerForEvent = NS_EVENT_NULL; michael@0: mNoListenerForEventAtom = nullptr; michael@0: if (mTarget && aUserType) { michael@0: mTarget->EventListenerRemoved(aUserType); michael@0: } michael@0: michael@0: if (!deviceType michael@0: #ifdef MOZ_B2G michael@0: && !timeChangeEvent && !networkEvent michael@0: #endif // MOZ_B2G michael@0: ) { michael@0: return; michael@0: } michael@0: --typeCount; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (!aAllEvents && deviceType && typeCount == 0) { michael@0: DisableDevice(aType); michael@0: #ifdef MOZ_B2G michael@0: } else if (timeChangeEvent && typeCount == 0) { michael@0: nsCOMPtr window = GetTargetAsInnerWindow(); michael@0: if (window) { michael@0: window->DisableTimeChangeNotifications(); michael@0: } michael@0: } else if (!aAllEvents && networkEvent && typeCount == 0) { michael@0: nsCOMPtr window = GetTargetAsInnerWindow(); michael@0: if (window) { michael@0: window->DisableNetworkEvent(aType); michael@0: } michael@0: #endif // MOZ_B2G michael@0: } michael@0: } michael@0: michael@0: bool michael@0: EventListenerManager::ListenerCanHandle(Listener* aListener, michael@0: WidgetEvent* aEvent) michael@0: { michael@0: // This is slightly different from EVENT_TYPE_EQUALS in that it returns michael@0: // true even when aEvent->message == NS_USER_DEFINED_EVENT and michael@0: // aListener=>mEventType != NS_USER_DEFINED_EVENT as long as the atoms are michael@0: // the same michael@0: if (aListener->mAllEvents) { michael@0: return true; michael@0: } michael@0: if (aEvent->message == NS_USER_DEFINED_EVENT) { michael@0: if (mIsMainThreadELM) { michael@0: return aListener->mTypeAtom == aEvent->userType; michael@0: } michael@0: return aListener->mTypeString.Equals(aEvent->typeString); michael@0: } michael@0: MOZ_ASSERT(mIsMainThreadELM); michael@0: return aListener->mEventType == aEvent->message; michael@0: } michael@0: michael@0: void michael@0: EventListenerManager::AddEventListenerByType( michael@0: const EventListenerHolder& aListenerHolder, michael@0: const nsAString& aType, michael@0: const EventListenerFlags& aFlags) michael@0: { michael@0: nsCOMPtr atom = michael@0: mIsMainThreadELM ? do_GetAtom(NS_LITERAL_STRING("on") + aType) : nullptr; michael@0: uint32_t type = nsContentUtils::GetEventId(atom); michael@0: AddEventListenerInternal(aListenerHolder, type, atom, aType, aFlags); michael@0: } michael@0: michael@0: void michael@0: EventListenerManager::RemoveEventListenerByType( michael@0: const EventListenerHolder& aListenerHolder, michael@0: const nsAString& aType, michael@0: const EventListenerFlags& aFlags) michael@0: { michael@0: nsCOMPtr atom = michael@0: mIsMainThreadELM ? do_GetAtom(NS_LITERAL_STRING("on") + aType) : nullptr; michael@0: uint32_t type = nsContentUtils::GetEventId(atom); michael@0: RemoveEventListenerInternal(aListenerHolder, type, atom, aType, aFlags); michael@0: } michael@0: michael@0: EventListenerManager::Listener* michael@0: EventListenerManager::FindEventHandler(uint32_t aEventType, michael@0: nsIAtom* aTypeAtom, michael@0: const nsAString& aTypeString) michael@0: { michael@0: // Run through the listeners for this type and see if a script michael@0: // listener is registered michael@0: Listener* listener; michael@0: uint32_t count = mListeners.Length(); michael@0: for (uint32_t i = 0; i < count; ++i) { michael@0: listener = &mListeners.ElementAt(i); michael@0: if (listener->mListenerIsHandler && michael@0: EVENT_TYPE_EQUALS(listener, aEventType, aTypeAtom, aTypeString, michael@0: false)) { michael@0: return listener; michael@0: } michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: EventListenerManager::Listener* michael@0: EventListenerManager::SetEventHandlerInternal( michael@0: nsIAtom* aName, michael@0: const nsAString& aTypeString, michael@0: const TypedEventHandler& aTypedHandler, michael@0: bool aPermitUntrustedEvents) michael@0: { michael@0: MOZ_ASSERT(aName || !aTypeString.IsEmpty()); michael@0: michael@0: uint32_t eventType = nsContentUtils::GetEventId(aName); michael@0: Listener* listener = FindEventHandler(eventType, aName, aTypeString); michael@0: michael@0: if (!listener) { michael@0: // If we didn't find a script listener or no listeners existed michael@0: // create and add a new one. michael@0: EventListenerFlags flags; michael@0: flags.mListenerIsJSListener = true; michael@0: michael@0: nsCOMPtr jsEventHandler; michael@0: NS_NewJSEventHandler(mTarget, aName, michael@0: aTypedHandler, getter_AddRefs(jsEventHandler)); michael@0: EventListenerHolder listenerHolder(jsEventHandler); michael@0: AddEventListenerInternal(listenerHolder, eventType, aName, aTypeString, michael@0: flags, true); michael@0: michael@0: listener = FindEventHandler(eventType, aName, aTypeString); michael@0: } else { michael@0: JSEventHandler* jsEventHandler = listener->GetJSEventHandler(); michael@0: MOZ_ASSERT(jsEventHandler, michael@0: "How can we have an event handler with no JSEventHandler?"); michael@0: michael@0: bool same = jsEventHandler->GetTypedEventHandler() == aTypedHandler; michael@0: // Possibly the same listener, but update still the context and scope. michael@0: jsEventHandler->SetHandler(aTypedHandler); michael@0: if (mTarget && !same && aName) { michael@0: mTarget->EventListenerRemoved(aName); michael@0: mTarget->EventListenerAdded(aName); michael@0: } michael@0: } michael@0: michael@0: // Set flag to indicate possible need for compilation later michael@0: listener->mHandlerIsString = !aTypedHandler.HasEventHandler(); michael@0: if (aPermitUntrustedEvents) { michael@0: listener->mFlags.mAllowUntrustedEvents = true; michael@0: } michael@0: michael@0: return listener; michael@0: } michael@0: michael@0: nsresult michael@0: EventListenerManager::SetEventHandler(nsIAtom* aName, michael@0: const nsAString& aBody, michael@0: uint32_t aLanguage, michael@0: bool aDeferCompilation, michael@0: bool aPermitUntrustedEvents, michael@0: Element* aElement) michael@0: { michael@0: NS_PRECONDITION(aLanguage != nsIProgrammingLanguage::UNKNOWN, michael@0: "Must know the language for the script event listener"); michael@0: michael@0: // |aPermitUntrustedEvents| is set to False for chrome - events michael@0: // *generated* from an unknown source are not allowed. michael@0: // However, for script languages with no 'sandbox', we want to reject michael@0: // such scripts based on the source of their code, not just the source michael@0: // of the event. michael@0: if (aPermitUntrustedEvents && michael@0: aLanguage != nsIProgrammingLanguage::JAVASCRIPT) { michael@0: NS_WARNING("Discarding non-JS event listener from untrusted source"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsCOMPtr doc; michael@0: nsCOMPtr global = michael@0: GetScriptGlobalAndDocument(getter_AddRefs(doc)); michael@0: michael@0: if (!global) { michael@0: // This can happen; for example this document might have been michael@0: // loaded as data. michael@0: return NS_OK; michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: nsCOMPtr win = do_QueryInterface(global); michael@0: if (win) { michael@0: MOZ_ASSERT(win->IsInnerWindow(), "We should not have an outer window here!"); michael@0: } michael@0: #endif michael@0: michael@0: nsresult rv = NS_OK; michael@0: // return early preventing the event listener from being added michael@0: // 'doc' is fetched above michael@0: if (doc) { michael@0: // Don't allow adding an event listener if the document is sandboxed michael@0: // without 'allow-scripts'. michael@0: if (doc->GetSandboxFlags() & SANDBOXED_SCRIPTS) { michael@0: return NS_ERROR_DOM_SECURITY_ERR; michael@0: } michael@0: michael@0: nsCOMPtr csp; michael@0: rv = doc->NodePrincipal()->GetCsp(getter_AddRefs(csp)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (csp) { michael@0: bool inlineOK = true; michael@0: bool reportViolations = false; michael@0: rv = csp->GetAllowsInlineScript(&reportViolations, &inlineOK); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (reportViolations) { michael@0: // gather information to log with violation report michael@0: nsIURI* uri = doc->GetDocumentURI(); michael@0: nsAutoCString asciiSpec; michael@0: if (uri) michael@0: uri->GetAsciiSpec(asciiSpec); michael@0: nsAutoString scriptSample, attr, tagName(NS_LITERAL_STRING("UNKNOWN")); michael@0: aName->ToString(attr); michael@0: nsCOMPtr domNode(do_QueryInterface(mTarget)); michael@0: if (domNode) michael@0: domNode->GetNodeName(tagName); michael@0: // build a "script sample" based on what we know about this element michael@0: scriptSample.Assign(attr); michael@0: scriptSample.AppendLiteral(" attribute on "); michael@0: scriptSample.Append(tagName); michael@0: scriptSample.AppendLiteral(" element"); michael@0: csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_INLINE_SCRIPT, michael@0: NS_ConvertUTF8toUTF16(asciiSpec), michael@0: scriptSample, michael@0: 0, michael@0: EmptyString(), michael@0: EmptyString()); michael@0: } michael@0: michael@0: // return early if CSP wants us to block inline scripts michael@0: if (!inlineOK) { michael@0: return NS_OK; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // This might be the first reference to this language in the global michael@0: // We must init the language before we attempt to fetch its context. michael@0: if (NS_FAILED(global->EnsureScriptEnvironment())) { michael@0: NS_WARNING("Failed to setup script environment for this language"); michael@0: // but fall through and let the inevitable failure below handle it. michael@0: } michael@0: michael@0: nsIScriptContext* context = global->GetScriptContext(); michael@0: NS_ENSURE_TRUE(context, NS_ERROR_FAILURE); michael@0: NS_ENSURE_STATE(global->GetGlobalJSObject()); michael@0: michael@0: Listener* listener = SetEventHandlerInternal(aName, michael@0: EmptyString(), michael@0: TypedEventHandler(), michael@0: aPermitUntrustedEvents); michael@0: michael@0: if (!aDeferCompilation) { michael@0: return CompileEventHandlerInternal(listener, &aBody, aElement); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: EventListenerManager::RemoveEventHandler(nsIAtom* aName, michael@0: const nsAString& aTypeString) michael@0: { michael@0: if (mClearingListeners) { michael@0: return; michael@0: } michael@0: michael@0: uint32_t eventType = nsContentUtils::GetEventId(aName); michael@0: Listener* listener = FindEventHandler(eventType, aName, aTypeString); michael@0: michael@0: if (listener) { michael@0: mListeners.RemoveElementAt(uint32_t(listener - &mListeners.ElementAt(0))); michael@0: mNoListenerForEvent = NS_EVENT_NULL; michael@0: mNoListenerForEventAtom = nullptr; michael@0: if (mTarget && aName) { michael@0: mTarget->EventListenerRemoved(aName); michael@0: } michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: EventListenerManager::CompileEventHandlerInternal(Listener* aListener, michael@0: const nsAString* aBody, michael@0: Element* aElement) michael@0: { michael@0: MOZ_ASSERT(aListener->GetJSEventHandler()); michael@0: MOZ_ASSERT(aListener->mHandlerIsString, "Why are we compiling a non-string JS listener?"); michael@0: JSEventHandler* jsEventHandler = aListener->GetJSEventHandler(); michael@0: MOZ_ASSERT(!jsEventHandler->GetTypedEventHandler().HasEventHandler(), michael@0: "What is there to compile?"); michael@0: michael@0: nsresult result = NS_OK; michael@0: nsCOMPtr doc; michael@0: nsCOMPtr global = michael@0: GetScriptGlobalAndDocument(getter_AddRefs(doc)); michael@0: NS_ENSURE_STATE(global); michael@0: michael@0: nsIScriptContext* context = global->GetScriptContext(); michael@0: NS_ENSURE_STATE(context); michael@0: michael@0: // Activate JSAPI, and make sure that exceptions are reported on the right michael@0: // Window. michael@0: AutoJSAPIWithErrorsReportedToWindow jsapi(context); michael@0: JSContext* cx = jsapi.cx(); michael@0: michael@0: nsCOMPtr typeAtom = aListener->mTypeAtom; michael@0: nsIAtom* attrName = typeAtom; michael@0: michael@0: // Flag us as not a string so we don't keep trying to compile strings which michael@0: // can't be compiled. michael@0: aListener->mHandlerIsString = false; michael@0: michael@0: // mTarget may not be an Element if it's a window and we're michael@0: // getting an inline event listener forwarded from or michael@0: // or or the like. michael@0: // XXX I don't like that we have to reference content from michael@0: // here. The alternative is to store the event handler string on michael@0: // the JSEventHandler itself, and that still doesn't address michael@0: // the arg names issue. michael@0: nsCOMPtr element = do_QueryInterface(mTarget); michael@0: MOZ_ASSERT(element || aBody, "Where will we get our body?"); michael@0: nsAutoString handlerBody; michael@0: const nsAString* body = aBody; michael@0: if (!aBody) { michael@0: if (aListener->mTypeAtom == nsGkAtoms::onSVGLoad) { michael@0: attrName = nsGkAtoms::onload; michael@0: } else if (aListener->mTypeAtom == nsGkAtoms::onSVGUnload) { michael@0: attrName = nsGkAtoms::onunload; michael@0: } else if (aListener->mTypeAtom == nsGkAtoms::onSVGResize) { michael@0: attrName = nsGkAtoms::onresize; michael@0: } else if (aListener->mTypeAtom == nsGkAtoms::onSVGScroll) { michael@0: attrName = nsGkAtoms::onscroll; michael@0: } else if (aListener->mTypeAtom == nsGkAtoms::onSVGZoom) { michael@0: attrName = nsGkAtoms::onzoom; michael@0: } else if (aListener->mTypeAtom == nsGkAtoms::onbeginEvent) { michael@0: attrName = nsGkAtoms::onbegin; michael@0: } else if (aListener->mTypeAtom == nsGkAtoms::onrepeatEvent) { michael@0: attrName = nsGkAtoms::onrepeat; michael@0: } else if (aListener->mTypeAtom == nsGkAtoms::onendEvent) { michael@0: attrName = nsGkAtoms::onend; michael@0: } michael@0: michael@0: element->GetAttr(kNameSpaceID_None, attrName, handlerBody); michael@0: body = &handlerBody; michael@0: aElement = element; michael@0: } michael@0: aListener = nullptr; michael@0: michael@0: uint32_t lineNo = 0; michael@0: nsAutoCString url (NS_LITERAL_CSTRING("-moz-evil:lying-event-listener")); michael@0: MOZ_ASSERT(body); michael@0: MOZ_ASSERT(aElement); michael@0: nsIURI *uri = aElement->OwnerDoc()->GetDocumentURI(); michael@0: if (uri) { michael@0: uri->GetSpec(url); michael@0: lineNo = 1; michael@0: } michael@0: michael@0: uint32_t argCount; michael@0: const char **argNames; michael@0: nsContentUtils::GetEventArgNames(aElement->GetNameSpaceID(), michael@0: typeAtom, michael@0: &argCount, &argNames); michael@0: michael@0: // Wrap the event target, so that we can use it as the scope for the event michael@0: // handler. Note that mTarget is different from aElement in the case, michael@0: // where mTarget is a Window. michael@0: // michael@0: // The wrapScope doesn't really matter here, because the target will create michael@0: // its reflector in the proper scope, and then we'll enter that compartment. michael@0: JS::Rooted wrapScope(cx, context->GetWindowProxy()); michael@0: JS::Rooted v(cx); michael@0: { michael@0: JSAutoCompartment ac(cx, wrapScope); michael@0: nsresult rv = nsContentUtils::WrapNative(cx, mTarget, &v, michael@0: /* aAllowWrapping = */ false); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) { michael@0: return rv; michael@0: } michael@0: } michael@0: JS::Rooted target(cx, &v.toObject()); michael@0: JSAutoCompartment ac(cx, target); michael@0: michael@0: nsDependentAtomString str(attrName); michael@0: // Most of our names are short enough that we don't even have to malloc michael@0: // the JS string stuff, so don't worry about playing games with michael@0: // refcounting XPCOM stringbuffers. michael@0: JS::Rooted jsStr(cx, JS_NewUCStringCopyN(cx, michael@0: str.BeginReading(), michael@0: str.Length())); michael@0: NS_ENSURE_TRUE(jsStr, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: // Get the reflector for |aElement|, so that we can pass to setElement. michael@0: if (NS_WARN_IF(!WrapNewBindingObject(cx, target, aElement, &v))) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: JS::CompileOptions options(cx); michael@0: options.setIntroductionType("eventHandler") michael@0: .setFileAndLine(url.get(), lineNo) michael@0: .setVersion(SCRIPTVERSION_DEFAULT) michael@0: .setElement(&v.toObject()) michael@0: .setElementAttributeName(jsStr) michael@0: .setDefineOnScope(false); michael@0: michael@0: JS::Rooted handler(cx); michael@0: result = nsJSUtils::CompileFunction(cx, target, options, michael@0: nsAtomCString(typeAtom), michael@0: argCount, argNames, *body, handler.address()); michael@0: NS_ENSURE_SUCCESS(result, result); michael@0: NS_ENSURE_TRUE(handler, NS_ERROR_FAILURE); michael@0: michael@0: nsCOMPtr win = do_QueryInterface(mTarget); michael@0: if (jsEventHandler->EventName() == nsGkAtoms::onerror && win) { michael@0: nsRefPtr handlerCallback = michael@0: new OnErrorEventHandlerNonNull(handler, /* aIncumbentGlobal = */ nullptr); michael@0: jsEventHandler->SetHandler(handlerCallback); michael@0: } else if (jsEventHandler->EventName() == nsGkAtoms::onbeforeunload && win) { michael@0: nsRefPtr handlerCallback = michael@0: new OnBeforeUnloadEventHandlerNonNull(handler, /* aIncumbentGlobal = */ nullptr); michael@0: jsEventHandler->SetHandler(handlerCallback); michael@0: } else { michael@0: nsRefPtr handlerCallback = michael@0: new EventHandlerNonNull(handler, /* aIncumbentGlobal = */ nullptr); michael@0: jsEventHandler->SetHandler(handlerCallback); michael@0: } michael@0: michael@0: return result; michael@0: } michael@0: michael@0: nsresult michael@0: EventListenerManager::HandleEventSubType(Listener* aListener, michael@0: nsIDOMEvent* aDOMEvent, michael@0: EventTarget* aCurrentTarget) michael@0: { michael@0: nsresult result = NS_OK; michael@0: EventListenerHolder listenerHolder(aListener->mListener); // strong ref michael@0: michael@0: // If this is a script handler and we haven't yet michael@0: // compiled the event handler itself michael@0: if ((aListener->mListenerType == Listener::eJSEventListener) && michael@0: aListener->mHandlerIsString) { michael@0: result = CompileEventHandlerInternal(aListener, nullptr, nullptr); michael@0: aListener = nullptr; michael@0: } michael@0: michael@0: if (NS_SUCCEEDED(result)) { michael@0: if (mIsMainThreadELM) { michael@0: nsContentUtils::EnterMicroTask(); michael@0: } michael@0: // nsIDOMEvent::currentTarget is set in EventDispatcher. michael@0: if (listenerHolder.HasWebIDLCallback()) { michael@0: ErrorResult rv; michael@0: listenerHolder.GetWebIDLCallback()-> michael@0: HandleEvent(aCurrentTarget, *(aDOMEvent->InternalDOMEvent()), rv); michael@0: result = rv.ErrorCode(); michael@0: } else { michael@0: result = listenerHolder.GetXPCOMCallback()->HandleEvent(aDOMEvent); michael@0: } michael@0: if (mIsMainThreadELM) { michael@0: nsContentUtils::LeaveMicroTask(); michael@0: } michael@0: } michael@0: michael@0: return result; michael@0: } michael@0: michael@0: /** michael@0: * Causes a check for event listeners and processing by them if they exist. michael@0: * @param an event listener michael@0: */ michael@0: michael@0: void michael@0: EventListenerManager::HandleEventInternal(nsPresContext* aPresContext, michael@0: WidgetEvent* aEvent, michael@0: nsIDOMEvent** aDOMEvent, michael@0: EventTarget* aCurrentTarget, michael@0: nsEventStatus* aEventStatus) michael@0: { michael@0: //Set the value of the internal PreventDefault flag properly based on aEventStatus michael@0: if (*aEventStatus == nsEventStatus_eConsumeNoDefault) { michael@0: aEvent->mFlags.mDefaultPrevented = true; michael@0: } michael@0: michael@0: nsAutoTObserverArray::EndLimitedIterator iter(mListeners); michael@0: Maybe popupStatePusher; michael@0: if (mIsMainThreadELM) { michael@0: popupStatePusher.construct(Event::GetEventPopupControlState(aEvent)); michael@0: } michael@0: michael@0: bool hasListener = false; michael@0: while (iter.HasMore()) { michael@0: if (aEvent->mFlags.mImmediatePropagationStopped) { michael@0: break; michael@0: } michael@0: Listener* listener = &iter.GetNext(); michael@0: // Check that the phase is same in event and event listener. michael@0: // Handle only trusted events, except when listener permits untrusted events. michael@0: if (ListenerCanHandle(listener, aEvent)) { michael@0: hasListener = true; michael@0: if (listener->IsListening(aEvent) && michael@0: (aEvent->mFlags.mIsTrusted || michael@0: listener->mFlags.mAllowUntrustedEvents)) { michael@0: if (!*aDOMEvent) { michael@0: // This is tiny bit slow, but happens only once per event. michael@0: nsCOMPtr et = michael@0: do_QueryInterface(aEvent->originalTarget); michael@0: EventDispatcher::CreateEvent(et, aPresContext, michael@0: aEvent, EmptyString(), aDOMEvent); michael@0: } michael@0: if (*aDOMEvent) { michael@0: if (!aEvent->currentTarget) { michael@0: aEvent->currentTarget = aCurrentTarget->GetTargetForDOMEvent(); michael@0: if (!aEvent->currentTarget) { michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (NS_FAILED(HandleEventSubType(listener, *aDOMEvent, michael@0: aCurrentTarget))) { michael@0: aEvent->mFlags.mExceptionHasBeenRisen = true; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: aEvent->currentTarget = nullptr; michael@0: michael@0: if (mIsMainThreadELM && !hasListener) { michael@0: mNoListenerForEvent = aEvent->message; michael@0: mNoListenerForEventAtom = aEvent->userType; michael@0: } michael@0: michael@0: if (aEvent->mFlags.mDefaultPrevented) { michael@0: *aEventStatus = nsEventStatus_eConsumeNoDefault; michael@0: } michael@0: } michael@0: michael@0: void michael@0: EventListenerManager::Disconnect() michael@0: { michael@0: mTarget = nullptr; michael@0: RemoveAllListeners(); michael@0: } michael@0: michael@0: void michael@0: EventListenerManager::AddEventListener( michael@0: const nsAString& aType, michael@0: const EventListenerHolder& aListenerHolder, michael@0: bool aUseCapture, michael@0: bool aWantsUntrusted) michael@0: { michael@0: EventListenerFlags flags; michael@0: flags.mCapture = aUseCapture; michael@0: flags.mAllowUntrustedEvents = aWantsUntrusted; michael@0: return AddEventListenerByType(aListenerHolder, aType, flags); michael@0: } michael@0: michael@0: void michael@0: EventListenerManager::RemoveEventListener( michael@0: const nsAString& aType, michael@0: const EventListenerHolder& aListenerHolder, michael@0: bool aUseCapture) michael@0: { michael@0: EventListenerFlags flags; michael@0: flags.mCapture = aUseCapture; michael@0: RemoveEventListenerByType(aListenerHolder, aType, flags); michael@0: } michael@0: michael@0: void michael@0: EventListenerManager::AddListenerForAllEvents(nsIDOMEventListener* aDOMListener, michael@0: bool aUseCapture, michael@0: bool aWantsUntrusted, michael@0: bool aSystemEventGroup) michael@0: { michael@0: EventListenerFlags flags; michael@0: flags.mCapture = aUseCapture; michael@0: flags.mAllowUntrustedEvents = aWantsUntrusted; michael@0: flags.mInSystemGroup = aSystemEventGroup; michael@0: EventListenerHolder listenerHolder(aDOMListener); michael@0: AddEventListenerInternal(listenerHolder, NS_EVENT_ALL, nullptr, EmptyString(), michael@0: flags, false, true); michael@0: } michael@0: michael@0: void michael@0: EventListenerManager::RemoveListenerForAllEvents( michael@0: nsIDOMEventListener* aDOMListener, michael@0: bool aUseCapture, michael@0: bool aSystemEventGroup) michael@0: { michael@0: EventListenerFlags flags; michael@0: flags.mCapture = aUseCapture; michael@0: flags.mInSystemGroup = aSystemEventGroup; michael@0: EventListenerHolder listenerHolder(aDOMListener); michael@0: RemoveEventListenerInternal(listenerHolder, NS_EVENT_ALL, nullptr, michael@0: EmptyString(), flags, true); michael@0: } michael@0: michael@0: bool michael@0: EventListenerManager::HasMutationListeners() michael@0: { michael@0: if (mMayHaveMutationListeners) { michael@0: uint32_t count = mListeners.Length(); michael@0: for (uint32_t i = 0; i < count; ++i) { michael@0: Listener* listener = &mListeners.ElementAt(i); michael@0: if (listener->mEventType >= NS_MUTATION_START && michael@0: listener->mEventType <= NS_MUTATION_END) { michael@0: return true; michael@0: } michael@0: } michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: uint32_t michael@0: EventListenerManager::MutationListenerBits() michael@0: { michael@0: uint32_t bits = 0; michael@0: if (mMayHaveMutationListeners) { michael@0: uint32_t count = mListeners.Length(); michael@0: for (uint32_t i = 0; i < count; ++i) { michael@0: Listener* listener = &mListeners.ElementAt(i); michael@0: if (listener->mEventType >= NS_MUTATION_START && michael@0: listener->mEventType <= NS_MUTATION_END) { michael@0: if (listener->mEventType == NS_MUTATION_SUBTREEMODIFIED) { michael@0: return kAllMutationBits; michael@0: } michael@0: bits |= MutationBitForEventType(listener->mEventType); michael@0: } michael@0: } michael@0: } michael@0: return bits; michael@0: } michael@0: michael@0: bool michael@0: EventListenerManager::HasListenersFor(const nsAString& aEventName) michael@0: { michael@0: nsCOMPtr atom = do_GetAtom(NS_LITERAL_STRING("on") + aEventName); michael@0: return HasListenersFor(atom); michael@0: } michael@0: michael@0: bool michael@0: EventListenerManager::HasListenersFor(nsIAtom* aEventNameWithOn) michael@0: { michael@0: #ifdef DEBUG michael@0: nsAutoString name; michael@0: aEventNameWithOn->ToString(name); michael@0: #endif michael@0: NS_ASSERTION(StringBeginsWith(name, NS_LITERAL_STRING("on")), michael@0: "Event name does not start with 'on'"); michael@0: uint32_t count = mListeners.Length(); michael@0: for (uint32_t i = 0; i < count; ++i) { michael@0: Listener* listener = &mListeners.ElementAt(i); michael@0: if (listener->mTypeAtom == aEventNameWithOn) { michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: EventListenerManager::HasListeners() michael@0: { michael@0: return !mListeners.IsEmpty(); michael@0: } michael@0: michael@0: nsresult michael@0: EventListenerManager::GetListenerInfo(nsCOMArray* aList) michael@0: { michael@0: nsCOMPtr target = do_QueryInterface(mTarget); michael@0: NS_ENSURE_STATE(target); michael@0: aList->Clear(); michael@0: uint32_t count = mListeners.Length(); michael@0: for (uint32_t i = 0; i < count; ++i) { michael@0: const Listener& listener = mListeners.ElementAt(i); michael@0: // If this is a script handler and we haven't yet michael@0: // compiled the event handler itself go ahead and compile it michael@0: if (listener.mListenerType == Listener::eJSEventListener && michael@0: listener.mHandlerIsString) { michael@0: CompileEventHandlerInternal(const_cast(&listener), nullptr, michael@0: nullptr); michael@0: } michael@0: nsAutoString eventType; michael@0: if (listener.mAllEvents) { michael@0: eventType.SetIsVoid(true); michael@0: } else { michael@0: eventType.Assign(Substring(nsDependentAtomString(listener.mTypeAtom), 2)); michael@0: } michael@0: // EventListenerInfo is defined in XPCOM, so we have to go ahead michael@0: // and convert to an XPCOM callback here... michael@0: nsRefPtr info = michael@0: new EventListenerInfo(eventType, listener.mListener.ToXPCOMCallback(), michael@0: listener.mFlags.mCapture, michael@0: listener.mFlags.mAllowUntrustedEvents, michael@0: listener.mFlags.mInSystemGroup); michael@0: aList->AppendObject(info); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: EventListenerManager::HasUnloadListeners() michael@0: { michael@0: uint32_t count = mListeners.Length(); michael@0: for (uint32_t i = 0; i < count; ++i) { michael@0: Listener* listener = &mListeners.ElementAt(i); michael@0: if (listener->mEventType == NS_PAGE_UNLOAD || michael@0: listener->mEventType == NS_BEFORE_PAGE_UNLOAD) { michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: EventListenerManager::SetEventHandler(nsIAtom* aEventName, michael@0: const nsAString& aTypeString, michael@0: EventHandlerNonNull* aHandler) michael@0: { michael@0: if (!aHandler) { michael@0: RemoveEventHandler(aEventName, aTypeString); michael@0: return; michael@0: } michael@0: michael@0: // Untrusted events are always permitted for non-chrome script michael@0: // handlers. michael@0: SetEventHandlerInternal(aEventName, aTypeString, TypedEventHandler(aHandler), michael@0: !mIsMainThreadELM || michael@0: !nsContentUtils::IsCallerChrome()); michael@0: } michael@0: michael@0: void michael@0: EventListenerManager::SetEventHandler(OnErrorEventHandlerNonNull* aHandler) michael@0: { michael@0: if (mIsMainThreadELM) { michael@0: if (!aHandler) { michael@0: RemoveEventHandler(nsGkAtoms::onerror, EmptyString()); michael@0: return; michael@0: } michael@0: michael@0: // Untrusted events are always permitted for non-chrome script michael@0: // handlers. michael@0: SetEventHandlerInternal(nsGkAtoms::onerror, EmptyString(), michael@0: TypedEventHandler(aHandler), michael@0: !nsContentUtils::IsCallerChrome()); michael@0: } else { michael@0: if (!aHandler) { michael@0: RemoveEventHandler(nullptr, NS_LITERAL_STRING("error")); michael@0: return; michael@0: } michael@0: michael@0: // Untrusted events are always permitted. michael@0: SetEventHandlerInternal(nullptr, NS_LITERAL_STRING("error"), michael@0: TypedEventHandler(aHandler), true); michael@0: } michael@0: } michael@0: michael@0: void michael@0: EventListenerManager::SetEventHandler( michael@0: OnBeforeUnloadEventHandlerNonNull* aHandler) michael@0: { michael@0: if (!aHandler) { michael@0: RemoveEventHandler(nsGkAtoms::onbeforeunload, EmptyString()); michael@0: return; michael@0: } michael@0: michael@0: // Untrusted events are always permitted for non-chrome script michael@0: // handlers. michael@0: SetEventHandlerInternal(nsGkAtoms::onbeforeunload, EmptyString(), michael@0: TypedEventHandler(aHandler), michael@0: !mIsMainThreadELM || michael@0: !nsContentUtils::IsCallerChrome()); michael@0: } michael@0: michael@0: const TypedEventHandler* michael@0: EventListenerManager::GetTypedEventHandler(nsIAtom* aEventName, michael@0: const nsAString& aTypeString) michael@0: { michael@0: uint32_t eventType = nsContentUtils::GetEventId(aEventName); michael@0: Listener* listener = FindEventHandler(eventType, aEventName, aTypeString); michael@0: michael@0: if (!listener) { michael@0: return nullptr; michael@0: } michael@0: michael@0: JSEventHandler* jsEventHandler = listener->GetJSEventHandler(); michael@0: michael@0: if (listener->mHandlerIsString) { michael@0: CompileEventHandlerInternal(listener, nullptr, nullptr); michael@0: } michael@0: michael@0: const TypedEventHandler& typedHandler = michael@0: jsEventHandler->GetTypedEventHandler(); michael@0: return typedHandler.HasEventHandler() ? &typedHandler : nullptr; michael@0: } michael@0: michael@0: size_t michael@0: EventListenerManager::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const michael@0: { michael@0: size_t n = aMallocSizeOf(this); michael@0: n += mListeners.SizeOfExcludingThis(aMallocSizeOf); michael@0: uint32_t count = mListeners.Length(); michael@0: for (uint32_t i = 0; i < count; ++i) { michael@0: JSEventHandler* jsEventHandler = michael@0: mListeners.ElementAt(i).GetJSEventHandler(); michael@0: if (jsEventHandler) { michael@0: n += jsEventHandler->SizeOfIncludingThis(aMallocSizeOf); michael@0: } michael@0: } michael@0: return n; michael@0: } michael@0: michael@0: void michael@0: EventListenerManager::MarkForCC() michael@0: { michael@0: uint32_t count = mListeners.Length(); michael@0: for (uint32_t i = 0; i < count; ++i) { michael@0: const Listener& listener = mListeners.ElementAt(i); michael@0: JSEventHandler* jsEventHandler = listener.GetJSEventHandler(); michael@0: if (jsEventHandler) { michael@0: const TypedEventHandler& typedHandler = michael@0: jsEventHandler->GetTypedEventHandler(); michael@0: if (typedHandler.HasEventHandler()) { michael@0: JS::ExposeObjectToActiveJS(typedHandler.Ptr()->Callable()); michael@0: } michael@0: } else if (listener.mListenerType == Listener::eWrappedJSListener) { michael@0: xpc_TryUnmarkWrappedGrayObject(listener.mListener.GetXPCOMCallback()); michael@0: } else if (listener.mListenerType == Listener::eWebIDLListener) { michael@0: // Callback() unmarks gray michael@0: listener.mListener.GetWebIDLCallback()->Callback(); michael@0: } michael@0: } michael@0: if (mRefCnt.IsPurple()) { michael@0: mRefCnt.RemovePurple(); michael@0: } michael@0: } michael@0: michael@0: already_AddRefed michael@0: EventListenerManager::GetScriptGlobalAndDocument(nsIDocument** aDoc) michael@0: { michael@0: nsCOMPtr node(do_QueryInterface(mTarget)); michael@0: nsCOMPtr doc; michael@0: nsCOMPtr global; michael@0: if (node) { michael@0: // Try to get context from doc michael@0: // XXX sXBL/XBL2 issue -- do we really want the owner here? What michael@0: // if that's the XBL document? michael@0: doc = node->OwnerDoc(); michael@0: if (doc->IsLoadedAsData()) { michael@0: return nullptr; michael@0: } michael@0: michael@0: // We want to allow compiling an event handler even in an unloaded michael@0: // document, so use GetScopeObject here, not GetScriptHandlingObject. michael@0: global = do_QueryInterface(doc->GetScopeObject()); michael@0: } else { michael@0: nsCOMPtr win = GetTargetAsInnerWindow(); michael@0: if (win) { michael@0: doc = win->GetExtantDoc(); michael@0: global = do_QueryInterface(win); michael@0: } else { michael@0: global = do_QueryInterface(mTarget); michael@0: } michael@0: } michael@0: michael@0: doc.forget(aDoc); michael@0: return global.forget(); michael@0: } michael@0: michael@0: } // namespace mozilla