1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/events/EventListenerManager.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1364 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +// Microsoft's API Name hackery sucks 1.10 +#undef CreateEvent 1.11 + 1.12 +#include "mozilla/BasicEvents.h" 1.13 +#include "mozilla/EventDispatcher.h" 1.14 +#include "mozilla/EventListenerManager.h" 1.15 +#ifdef MOZ_B2G 1.16 +#include "mozilla/Hal.h" 1.17 +#endif // #ifdef MOZ_B2G 1.18 +#include "mozilla/HalSensor.h" 1.19 +#include "mozilla/InternalMutationEvent.h" 1.20 +#include "mozilla/JSEventHandler.h" 1.21 +#include "mozilla/MemoryReporting.h" 1.22 +#include "mozilla/dom/BindingUtils.h" 1.23 +#include "mozilla/dom/Element.h" 1.24 +#include "mozilla/dom/Event.h" 1.25 + 1.26 +#include "EventListenerService.h" 1.27 +#include "nsCOMArray.h" 1.28 +#include "nsCOMPtr.h" 1.29 +#include "nsContentUtils.h" 1.30 +#include "nsDOMCID.h" 1.31 +#include "nsError.h" 1.32 +#include "nsGkAtoms.h" 1.33 +#include "nsIContent.h" 1.34 +#include "nsIContentSecurityPolicy.h" 1.35 +#include "nsIDocument.h" 1.36 +#include "nsIDOMEventListener.h" 1.37 +#include "nsIScriptGlobalObject.h" 1.38 +#include "nsISupports.h" 1.39 +#include "nsIXPConnect.h" 1.40 +#include "nsJSUtils.h" 1.41 +#include "nsNameSpaceManager.h" 1.42 +#include "nsPIDOMWindow.h" 1.43 +#include "nsSandboxFlags.h" 1.44 +#include "xpcpublic.h" 1.45 + 1.46 +namespace mozilla { 1.47 + 1.48 +using namespace dom; 1.49 +using namespace hal; 1.50 + 1.51 +#define EVENT_TYPE_EQUALS(ls, type, userType, typeString, allEvents) \ 1.52 + ((ls->mEventType == type && \ 1.53 + (ls->mEventType != NS_USER_DEFINED_EVENT || \ 1.54 + (mIsMainThreadELM && ls->mTypeAtom == userType) || \ 1.55 + (!mIsMainThreadELM && ls->mTypeString.Equals(typeString)))) || \ 1.56 + (allEvents && ls->mAllEvents)) 1.57 + 1.58 +static const uint32_t kAllMutationBits = 1.59 + NS_EVENT_BITS_MUTATION_SUBTREEMODIFIED | 1.60 + NS_EVENT_BITS_MUTATION_NODEINSERTED | 1.61 + NS_EVENT_BITS_MUTATION_NODEREMOVED | 1.62 + NS_EVENT_BITS_MUTATION_NODEREMOVEDFROMDOCUMENT | 1.63 + NS_EVENT_BITS_MUTATION_NODEINSERTEDINTODOCUMENT | 1.64 + NS_EVENT_BITS_MUTATION_ATTRMODIFIED | 1.65 + NS_EVENT_BITS_MUTATION_CHARACTERDATAMODIFIED; 1.66 + 1.67 +static uint32_t 1.68 +MutationBitForEventType(uint32_t aEventType) 1.69 +{ 1.70 + switch (aEventType) { 1.71 + case NS_MUTATION_SUBTREEMODIFIED: 1.72 + return NS_EVENT_BITS_MUTATION_SUBTREEMODIFIED; 1.73 + case NS_MUTATION_NODEINSERTED: 1.74 + return NS_EVENT_BITS_MUTATION_NODEINSERTED; 1.75 + case NS_MUTATION_NODEREMOVED: 1.76 + return NS_EVENT_BITS_MUTATION_NODEREMOVED; 1.77 + case NS_MUTATION_NODEREMOVEDFROMDOCUMENT: 1.78 + return NS_EVENT_BITS_MUTATION_NODEREMOVEDFROMDOCUMENT; 1.79 + case NS_MUTATION_NODEINSERTEDINTODOCUMENT: 1.80 + return NS_EVENT_BITS_MUTATION_NODEINSERTEDINTODOCUMENT; 1.81 + case NS_MUTATION_ATTRMODIFIED: 1.82 + return NS_EVENT_BITS_MUTATION_ATTRMODIFIED; 1.83 + case NS_MUTATION_CHARACTERDATAMODIFIED: 1.84 + return NS_EVENT_BITS_MUTATION_CHARACTERDATAMODIFIED; 1.85 + default: 1.86 + break; 1.87 + } 1.88 + return 0; 1.89 +} 1.90 + 1.91 +uint32_t EventListenerManager::sMainThreadCreatedCount = 0; 1.92 + 1.93 +EventListenerManager::EventListenerManager(EventTarget* aTarget) 1.94 + : mMayHavePaintEventListener(false) 1.95 + , mMayHaveMutationListeners(false) 1.96 + , mMayHaveCapturingListeners(false) 1.97 + , mMayHaveSystemGroupListeners(false) 1.98 + , mMayHaveTouchEventListener(false) 1.99 + , mMayHaveMouseEnterLeaveEventListener(false) 1.100 + , mMayHavePointerEnterLeaveEventListener(false) 1.101 + , mClearingListeners(false) 1.102 + , mIsMainThreadELM(NS_IsMainThread()) 1.103 + , mNoListenerForEvent(0) 1.104 + , mTarget(aTarget) 1.105 +{ 1.106 + NS_ASSERTION(aTarget, "unexpected null pointer"); 1.107 + 1.108 + if (mIsMainThreadELM) { 1.109 + ++sMainThreadCreatedCount; 1.110 + } 1.111 +} 1.112 + 1.113 +EventListenerManager::~EventListenerManager() 1.114 +{ 1.115 + // If your code fails this assertion, a possible reason is that 1.116 + // a class did not call our Disconnect() manually. Note that 1.117 + // this class can have Disconnect called in one of two ways: 1.118 + // if it is part of a cycle, then in Unlink() (such a cycle 1.119 + // would be with one of the listeners, not mTarget which is weak). 1.120 + // If not part of a cycle, then Disconnect must be called manually, 1.121 + // typically from the destructor of the owner class (mTarget). 1.122 + // XXX azakai: Is there any reason to not just call Disconnect 1.123 + // from right here, if not previously called? 1.124 + NS_ASSERTION(!mTarget, "didn't call Disconnect"); 1.125 + RemoveAllListeners(); 1.126 +} 1.127 + 1.128 +void 1.129 +EventListenerManager::RemoveAllListeners() 1.130 +{ 1.131 + if (mClearingListeners) { 1.132 + return; 1.133 + } 1.134 + mClearingListeners = true; 1.135 + mListeners.Clear(); 1.136 + mClearingListeners = false; 1.137 +} 1.138 + 1.139 +void 1.140 +EventListenerManager::Shutdown() 1.141 +{ 1.142 + Event::Shutdown(); 1.143 +} 1.144 + 1.145 +NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(EventListenerManager, AddRef) 1.146 +NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(EventListenerManager, Release) 1.147 + 1.148 +inline void 1.149 +ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback, 1.150 + EventListenerManager::Listener& aField, 1.151 + const char* aName, 1.152 + unsigned aFlags) 1.153 +{ 1.154 + if (MOZ_UNLIKELY(aCallback.WantDebugInfo())) { 1.155 + nsAutoCString name; 1.156 + name.AppendASCII(aName); 1.157 + if (aField.mTypeAtom) { 1.158 + name.AppendASCII(" event="); 1.159 + name.Append(nsAtomCString(aField.mTypeAtom)); 1.160 + name.AppendASCII(" listenerType="); 1.161 + name.AppendInt(aField.mListenerType); 1.162 + name.AppendASCII(" "); 1.163 + } 1.164 + CycleCollectionNoteChild(aCallback, aField.mListener.GetISupports(), name.get(), 1.165 + aFlags); 1.166 + } else { 1.167 + CycleCollectionNoteChild(aCallback, aField.mListener.GetISupports(), aName, 1.168 + aFlags); 1.169 + } 1.170 +} 1.171 + 1.172 +NS_IMPL_CYCLE_COLLECTION_CLASS(EventListenerManager) 1.173 + 1.174 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(EventListenerManager) 1.175 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListeners) 1.176 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 1.177 + 1.178 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(EventListenerManager) 1.179 + tmp->Disconnect(); 1.180 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END 1.181 + 1.182 + 1.183 +nsPIDOMWindow* 1.184 +EventListenerManager::GetInnerWindowForTarget() 1.185 +{ 1.186 + nsCOMPtr<nsINode> node = do_QueryInterface(mTarget); 1.187 + if (node) { 1.188 + // XXX sXBL/XBL2 issue -- do we really want the owner here? What 1.189 + // if that's the XBL document? 1.190 + return node->OwnerDoc()->GetInnerWindow(); 1.191 + } 1.192 + 1.193 + nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow(); 1.194 + return window; 1.195 +} 1.196 + 1.197 +already_AddRefed<nsPIDOMWindow> 1.198 +EventListenerManager::GetTargetAsInnerWindow() const 1.199 +{ 1.200 + nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mTarget); 1.201 + if (!window) { 1.202 + return nullptr; 1.203 + } 1.204 + 1.205 + NS_ASSERTION(window->IsInnerWindow(), "Target should not be an outer window"); 1.206 + return window.forget(); 1.207 +} 1.208 + 1.209 +void 1.210 +EventListenerManager::AddEventListenerInternal( 1.211 + const EventListenerHolder& aListenerHolder, 1.212 + uint32_t aType, 1.213 + nsIAtom* aTypeAtom, 1.214 + const nsAString& aTypeString, 1.215 + const EventListenerFlags& aFlags, 1.216 + bool aHandler, 1.217 + bool aAllEvents) 1.218 +{ 1.219 + MOZ_ASSERT((NS_IsMainThread() && aType && aTypeAtom) || // Main thread 1.220 + (!NS_IsMainThread() && aType && !aTypeString.IsEmpty()) || // non-main-thread 1.221 + aAllEvents, "Missing type"); // all-events listener 1.222 + 1.223 + if (!aListenerHolder || mClearingListeners) { 1.224 + return; 1.225 + } 1.226 + 1.227 + // Since there is no public API to call us with an EventListenerHolder, we 1.228 + // know that there's an EventListenerHolder on the stack holding a strong ref 1.229 + // to the listener. 1.230 + 1.231 + Listener* listener; 1.232 + uint32_t count = mListeners.Length(); 1.233 + for (uint32_t i = 0; i < count; i++) { 1.234 + listener = &mListeners.ElementAt(i); 1.235 + // mListener == aListenerHolder is the last one, since it can be a bit slow. 1.236 + if (listener->mListenerIsHandler == aHandler && 1.237 + listener->mFlags == aFlags && 1.238 + EVENT_TYPE_EQUALS(listener, aType, aTypeAtom, aTypeString, 1.239 + aAllEvents) && 1.240 + listener->mListener == aListenerHolder) { 1.241 + return; 1.242 + } 1.243 + } 1.244 + 1.245 + mNoListenerForEvent = NS_EVENT_NULL; 1.246 + mNoListenerForEventAtom = nullptr; 1.247 + 1.248 + listener = aAllEvents ? mListeners.InsertElementAt(0) : 1.249 + mListeners.AppendElement(); 1.250 + listener->mListener = aListenerHolder; 1.251 + MOZ_ASSERT(aType < PR_UINT16_MAX); 1.252 + listener->mEventType = aType; 1.253 + listener->mTypeString = aTypeString; 1.254 + listener->mTypeAtom = aTypeAtom; 1.255 + listener->mFlags = aFlags; 1.256 + listener->mListenerIsHandler = aHandler; 1.257 + listener->mHandlerIsString = false; 1.258 + listener->mAllEvents = aAllEvents; 1.259 + 1.260 + // Detect the type of event listener. 1.261 + nsCOMPtr<nsIXPConnectWrappedJS> wjs; 1.262 + if (aFlags.mListenerIsJSListener) { 1.263 + MOZ_ASSERT(!aListenerHolder.HasWebIDLCallback()); 1.264 + listener->mListenerType = Listener::eJSEventListener; 1.265 + } else if (aListenerHolder.HasWebIDLCallback()) { 1.266 + listener->mListenerType = Listener::eWebIDLListener; 1.267 + } else if ((wjs = do_QueryInterface(aListenerHolder.GetXPCOMCallback()))) { 1.268 + listener->mListenerType = Listener::eWrappedJSListener; 1.269 + } else { 1.270 + listener->mListenerType = Listener::eNativeListener; 1.271 + } 1.272 + 1.273 + 1.274 + if (aFlags.mInSystemGroup) { 1.275 + mMayHaveSystemGroupListeners = true; 1.276 + } 1.277 + if (aFlags.mCapture) { 1.278 + mMayHaveCapturingListeners = true; 1.279 + } 1.280 + 1.281 + if (aType == NS_AFTERPAINT) { 1.282 + mMayHavePaintEventListener = true; 1.283 + nsPIDOMWindow* window = GetInnerWindowForTarget(); 1.284 + if (window) { 1.285 + window->SetHasPaintEventListeners(); 1.286 + } 1.287 + } else if (aType >= NS_MUTATION_START && aType <= NS_MUTATION_END) { 1.288 + // For mutation listeners, we need to update the global bit on the DOM window. 1.289 + // Otherwise we won't actually fire the mutation event. 1.290 + mMayHaveMutationListeners = true; 1.291 + // Go from our target to the nearest enclosing DOM window. 1.292 + nsPIDOMWindow* window = GetInnerWindowForTarget(); 1.293 + if (window) { 1.294 + nsCOMPtr<nsIDocument> doc = window->GetExtantDoc(); 1.295 + if (doc) { 1.296 + doc->WarnOnceAbout(nsIDocument::eMutationEvent); 1.297 + } 1.298 + // If aType is NS_MUTATION_SUBTREEMODIFIED, we need to listen all 1.299 + // mutations. nsContentUtils::HasMutationListeners relies on this. 1.300 + window->SetMutationListeners((aType == NS_MUTATION_SUBTREEMODIFIED) ? 1.301 + kAllMutationBits : 1.302 + MutationBitForEventType(aType)); 1.303 + } 1.304 + } else if (aTypeAtom == nsGkAtoms::ondeviceorientation) { 1.305 + EnableDevice(NS_DEVICE_ORIENTATION); 1.306 + } else if (aTypeAtom == nsGkAtoms::ondeviceproximity || aTypeAtom == nsGkAtoms::onuserproximity) { 1.307 + EnableDevice(NS_DEVICE_PROXIMITY); 1.308 + } else if (aTypeAtom == nsGkAtoms::ondevicelight) { 1.309 + EnableDevice(NS_DEVICE_LIGHT); 1.310 + } else if (aTypeAtom == nsGkAtoms::ondevicemotion) { 1.311 + EnableDevice(NS_DEVICE_MOTION); 1.312 +#ifdef MOZ_B2G 1.313 + } else if (aTypeAtom == nsGkAtoms::onmoztimechange) { 1.314 + nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow(); 1.315 + if (window) { 1.316 + window->EnableTimeChangeNotifications(); 1.317 + } 1.318 + } else if (aTypeAtom == nsGkAtoms::onmoznetworkupload) { 1.319 + nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow(); 1.320 + if (window) { 1.321 + window->EnableNetworkEvent(NS_NETWORK_UPLOAD_EVENT); 1.322 + } 1.323 + } else if (aTypeAtom == nsGkAtoms::onmoznetworkdownload) { 1.324 + nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow(); 1.325 + if (window) { 1.326 + window->EnableNetworkEvent(NS_NETWORK_DOWNLOAD_EVENT); 1.327 + } 1.328 +#endif // MOZ_B2G 1.329 + } else if (aTypeAtom == nsGkAtoms::ontouchstart || 1.330 + aTypeAtom == nsGkAtoms::ontouchend || 1.331 + aTypeAtom == nsGkAtoms::ontouchmove || 1.332 + aTypeAtom == nsGkAtoms::ontouchenter || 1.333 + aTypeAtom == nsGkAtoms::ontouchleave || 1.334 + aTypeAtom == nsGkAtoms::ontouchcancel) { 1.335 + mMayHaveTouchEventListener = true; 1.336 + nsPIDOMWindow* window = GetInnerWindowForTarget(); 1.337 + // we don't want touchevent listeners added by scrollbars to flip this flag 1.338 + // so we ignore listeners created with system event flag 1.339 + if (window && !aFlags.mInSystemGroup) { 1.340 + window->SetHasTouchEventListeners(); 1.341 + } 1.342 + } else if (aType >= NS_POINTER_EVENT_START && aType <= NS_POINTER_LOST_CAPTURE) { 1.343 + nsPIDOMWindow* window = GetInnerWindowForTarget(); 1.344 + if (aTypeAtom == nsGkAtoms::onpointerenter || 1.345 + aTypeAtom == nsGkAtoms::onpointerleave) { 1.346 + mMayHavePointerEnterLeaveEventListener = true; 1.347 + if (window) { 1.348 +#ifdef DEBUG 1.349 + nsCOMPtr<nsIDocument> d = window->GetExtantDoc(); 1.350 + NS_WARN_IF_FALSE(!nsContentUtils::IsChromeDoc(d), 1.351 + "Please do not use pointerenter/leave events in chrome. " 1.352 + "They are slower than pointerover/out!"); 1.353 +#endif 1.354 + window->SetHasPointerEnterLeaveEventListeners(); 1.355 + } 1.356 + } 1.357 + } else if (aTypeAtom == nsGkAtoms::onmouseenter || 1.358 + aTypeAtom == nsGkAtoms::onmouseleave) { 1.359 + mMayHaveMouseEnterLeaveEventListener = true; 1.360 + nsPIDOMWindow* window = GetInnerWindowForTarget(); 1.361 + if (window) { 1.362 +#ifdef DEBUG 1.363 + nsCOMPtr<nsIDocument> d = window->GetExtantDoc(); 1.364 + NS_WARN_IF_FALSE(!nsContentUtils::IsChromeDoc(d), 1.365 + "Please do not use mouseenter/leave events in chrome. " 1.366 + "They are slower than mouseover/out!"); 1.367 +#endif 1.368 + window->SetHasMouseEnterLeaveEventListeners(); 1.369 + } 1.370 +#ifdef MOZ_GAMEPAD 1.371 + } else if (aType >= NS_GAMEPAD_START && 1.372 + aType <= NS_GAMEPAD_END) { 1.373 + nsPIDOMWindow* window = GetInnerWindowForTarget(); 1.374 + if (window) { 1.375 + window->SetHasGamepadEventListener(); 1.376 + } 1.377 +#endif 1.378 + } 1.379 + if (aTypeAtom && mTarget) { 1.380 + mTarget->EventListenerAdded(aTypeAtom); 1.381 + } 1.382 +} 1.383 + 1.384 +bool 1.385 +EventListenerManager::IsDeviceType(uint32_t aType) 1.386 +{ 1.387 + switch (aType) { 1.388 + case NS_DEVICE_ORIENTATION: 1.389 + case NS_DEVICE_MOTION: 1.390 + case NS_DEVICE_LIGHT: 1.391 + case NS_DEVICE_PROXIMITY: 1.392 + case NS_USER_PROXIMITY: 1.393 + return true; 1.394 + default: 1.395 + break; 1.396 + } 1.397 + return false; 1.398 +} 1.399 + 1.400 +void 1.401 +EventListenerManager::EnableDevice(uint32_t aType) 1.402 +{ 1.403 + nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow(); 1.404 + if (!window) { 1.405 + return; 1.406 + } 1.407 + 1.408 + switch (aType) { 1.409 + case NS_DEVICE_ORIENTATION: 1.410 + window->EnableDeviceSensor(SENSOR_ORIENTATION); 1.411 + break; 1.412 + case NS_DEVICE_PROXIMITY: 1.413 + case NS_USER_PROXIMITY: 1.414 + window->EnableDeviceSensor(SENSOR_PROXIMITY); 1.415 + break; 1.416 + case NS_DEVICE_LIGHT: 1.417 + window->EnableDeviceSensor(SENSOR_LIGHT); 1.418 + break; 1.419 + case NS_DEVICE_MOTION: 1.420 + window->EnableDeviceSensor(SENSOR_ACCELERATION); 1.421 + window->EnableDeviceSensor(SENSOR_LINEAR_ACCELERATION); 1.422 + window->EnableDeviceSensor(SENSOR_GYROSCOPE); 1.423 + break; 1.424 + default: 1.425 + NS_WARNING("Enabling an unknown device sensor."); 1.426 + break; 1.427 + } 1.428 +} 1.429 + 1.430 +void 1.431 +EventListenerManager::DisableDevice(uint32_t aType) 1.432 +{ 1.433 + nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow(); 1.434 + if (!window) { 1.435 + return; 1.436 + } 1.437 + 1.438 + switch (aType) { 1.439 + case NS_DEVICE_ORIENTATION: 1.440 + window->DisableDeviceSensor(SENSOR_ORIENTATION); 1.441 + break; 1.442 + case NS_DEVICE_MOTION: 1.443 + window->DisableDeviceSensor(SENSOR_ACCELERATION); 1.444 + window->DisableDeviceSensor(SENSOR_LINEAR_ACCELERATION); 1.445 + window->DisableDeviceSensor(SENSOR_GYROSCOPE); 1.446 + break; 1.447 + case NS_DEVICE_PROXIMITY: 1.448 + case NS_USER_PROXIMITY: 1.449 + window->DisableDeviceSensor(SENSOR_PROXIMITY); 1.450 + break; 1.451 + case NS_DEVICE_LIGHT: 1.452 + window->DisableDeviceSensor(SENSOR_LIGHT); 1.453 + break; 1.454 + default: 1.455 + NS_WARNING("Disabling an unknown device sensor."); 1.456 + break; 1.457 + } 1.458 +} 1.459 + 1.460 +void 1.461 +EventListenerManager::RemoveEventListenerInternal( 1.462 + const EventListenerHolder& aListenerHolder, 1.463 + uint32_t aType, 1.464 + nsIAtom* aUserType, 1.465 + const nsAString& aTypeString, 1.466 + const EventListenerFlags& aFlags, 1.467 + bool aAllEvents) 1.468 +{ 1.469 + if (!aListenerHolder || !aType || mClearingListeners) { 1.470 + return; 1.471 + } 1.472 + 1.473 + Listener* listener; 1.474 + 1.475 + uint32_t count = mListeners.Length(); 1.476 + uint32_t typeCount = 0; 1.477 + bool deviceType = IsDeviceType(aType); 1.478 +#ifdef MOZ_B2G 1.479 + bool timeChangeEvent = (aType == NS_MOZ_TIME_CHANGE_EVENT); 1.480 + bool networkEvent = (aType == NS_NETWORK_UPLOAD_EVENT || 1.481 + aType == NS_NETWORK_DOWNLOAD_EVENT); 1.482 +#endif // MOZ_B2G 1.483 + 1.484 + for (uint32_t i = 0; i < count; ++i) { 1.485 + listener = &mListeners.ElementAt(i); 1.486 + if (EVENT_TYPE_EQUALS(listener, aType, aUserType, aTypeString, 1.487 + aAllEvents)) { 1.488 + ++typeCount; 1.489 + if (listener->mListener == aListenerHolder && 1.490 + listener->mFlags.EqualsIgnoringTrustness(aFlags)) { 1.491 + nsRefPtr<EventListenerManager> kungFuDeathGrip(this); 1.492 + mListeners.RemoveElementAt(i); 1.493 + --count; 1.494 + mNoListenerForEvent = NS_EVENT_NULL; 1.495 + mNoListenerForEventAtom = nullptr; 1.496 + if (mTarget && aUserType) { 1.497 + mTarget->EventListenerRemoved(aUserType); 1.498 + } 1.499 + 1.500 + if (!deviceType 1.501 +#ifdef MOZ_B2G 1.502 + && !timeChangeEvent && !networkEvent 1.503 +#endif // MOZ_B2G 1.504 + ) { 1.505 + return; 1.506 + } 1.507 + --typeCount; 1.508 + } 1.509 + } 1.510 + } 1.511 + 1.512 + if (!aAllEvents && deviceType && typeCount == 0) { 1.513 + DisableDevice(aType); 1.514 +#ifdef MOZ_B2G 1.515 + } else if (timeChangeEvent && typeCount == 0) { 1.516 + nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow(); 1.517 + if (window) { 1.518 + window->DisableTimeChangeNotifications(); 1.519 + } 1.520 + } else if (!aAllEvents && networkEvent && typeCount == 0) { 1.521 + nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow(); 1.522 + if (window) { 1.523 + window->DisableNetworkEvent(aType); 1.524 + } 1.525 +#endif // MOZ_B2G 1.526 + } 1.527 +} 1.528 + 1.529 +bool 1.530 +EventListenerManager::ListenerCanHandle(Listener* aListener, 1.531 + WidgetEvent* aEvent) 1.532 +{ 1.533 + // This is slightly different from EVENT_TYPE_EQUALS in that it returns 1.534 + // true even when aEvent->message == NS_USER_DEFINED_EVENT and 1.535 + // aListener=>mEventType != NS_USER_DEFINED_EVENT as long as the atoms are 1.536 + // the same 1.537 + if (aListener->mAllEvents) { 1.538 + return true; 1.539 + } 1.540 + if (aEvent->message == NS_USER_DEFINED_EVENT) { 1.541 + if (mIsMainThreadELM) { 1.542 + return aListener->mTypeAtom == aEvent->userType; 1.543 + } 1.544 + return aListener->mTypeString.Equals(aEvent->typeString); 1.545 + } 1.546 + MOZ_ASSERT(mIsMainThreadELM); 1.547 + return aListener->mEventType == aEvent->message; 1.548 +} 1.549 + 1.550 +void 1.551 +EventListenerManager::AddEventListenerByType( 1.552 + const EventListenerHolder& aListenerHolder, 1.553 + const nsAString& aType, 1.554 + const EventListenerFlags& aFlags) 1.555 +{ 1.556 + nsCOMPtr<nsIAtom> atom = 1.557 + mIsMainThreadELM ? do_GetAtom(NS_LITERAL_STRING("on") + aType) : nullptr; 1.558 + uint32_t type = nsContentUtils::GetEventId(atom); 1.559 + AddEventListenerInternal(aListenerHolder, type, atom, aType, aFlags); 1.560 +} 1.561 + 1.562 +void 1.563 +EventListenerManager::RemoveEventListenerByType( 1.564 + const EventListenerHolder& aListenerHolder, 1.565 + const nsAString& aType, 1.566 + const EventListenerFlags& aFlags) 1.567 +{ 1.568 + nsCOMPtr<nsIAtom> atom = 1.569 + mIsMainThreadELM ? do_GetAtom(NS_LITERAL_STRING("on") + aType) : nullptr; 1.570 + uint32_t type = nsContentUtils::GetEventId(atom); 1.571 + RemoveEventListenerInternal(aListenerHolder, type, atom, aType, aFlags); 1.572 +} 1.573 + 1.574 +EventListenerManager::Listener* 1.575 +EventListenerManager::FindEventHandler(uint32_t aEventType, 1.576 + nsIAtom* aTypeAtom, 1.577 + const nsAString& aTypeString) 1.578 +{ 1.579 + // Run through the listeners for this type and see if a script 1.580 + // listener is registered 1.581 + Listener* listener; 1.582 + uint32_t count = mListeners.Length(); 1.583 + for (uint32_t i = 0; i < count; ++i) { 1.584 + listener = &mListeners.ElementAt(i); 1.585 + if (listener->mListenerIsHandler && 1.586 + EVENT_TYPE_EQUALS(listener, aEventType, aTypeAtom, aTypeString, 1.587 + false)) { 1.588 + return listener; 1.589 + } 1.590 + } 1.591 + return nullptr; 1.592 +} 1.593 + 1.594 +EventListenerManager::Listener* 1.595 +EventListenerManager::SetEventHandlerInternal( 1.596 + nsIAtom* aName, 1.597 + const nsAString& aTypeString, 1.598 + const TypedEventHandler& aTypedHandler, 1.599 + bool aPermitUntrustedEvents) 1.600 +{ 1.601 + MOZ_ASSERT(aName || !aTypeString.IsEmpty()); 1.602 + 1.603 + uint32_t eventType = nsContentUtils::GetEventId(aName); 1.604 + Listener* listener = FindEventHandler(eventType, aName, aTypeString); 1.605 + 1.606 + if (!listener) { 1.607 + // If we didn't find a script listener or no listeners existed 1.608 + // create and add a new one. 1.609 + EventListenerFlags flags; 1.610 + flags.mListenerIsJSListener = true; 1.611 + 1.612 + nsCOMPtr<JSEventHandler> jsEventHandler; 1.613 + NS_NewJSEventHandler(mTarget, aName, 1.614 + aTypedHandler, getter_AddRefs(jsEventHandler)); 1.615 + EventListenerHolder listenerHolder(jsEventHandler); 1.616 + AddEventListenerInternal(listenerHolder, eventType, aName, aTypeString, 1.617 + flags, true); 1.618 + 1.619 + listener = FindEventHandler(eventType, aName, aTypeString); 1.620 + } else { 1.621 + JSEventHandler* jsEventHandler = listener->GetJSEventHandler(); 1.622 + MOZ_ASSERT(jsEventHandler, 1.623 + "How can we have an event handler with no JSEventHandler?"); 1.624 + 1.625 + bool same = jsEventHandler->GetTypedEventHandler() == aTypedHandler; 1.626 + // Possibly the same listener, but update still the context and scope. 1.627 + jsEventHandler->SetHandler(aTypedHandler); 1.628 + if (mTarget && !same && aName) { 1.629 + mTarget->EventListenerRemoved(aName); 1.630 + mTarget->EventListenerAdded(aName); 1.631 + } 1.632 + } 1.633 + 1.634 + // Set flag to indicate possible need for compilation later 1.635 + listener->mHandlerIsString = !aTypedHandler.HasEventHandler(); 1.636 + if (aPermitUntrustedEvents) { 1.637 + listener->mFlags.mAllowUntrustedEvents = true; 1.638 + } 1.639 + 1.640 + return listener; 1.641 +} 1.642 + 1.643 +nsresult 1.644 +EventListenerManager::SetEventHandler(nsIAtom* aName, 1.645 + const nsAString& aBody, 1.646 + uint32_t aLanguage, 1.647 + bool aDeferCompilation, 1.648 + bool aPermitUntrustedEvents, 1.649 + Element* aElement) 1.650 +{ 1.651 + NS_PRECONDITION(aLanguage != nsIProgrammingLanguage::UNKNOWN, 1.652 + "Must know the language for the script event listener"); 1.653 + 1.654 + // |aPermitUntrustedEvents| is set to False for chrome - events 1.655 + // *generated* from an unknown source are not allowed. 1.656 + // However, for script languages with no 'sandbox', we want to reject 1.657 + // such scripts based on the source of their code, not just the source 1.658 + // of the event. 1.659 + if (aPermitUntrustedEvents && 1.660 + aLanguage != nsIProgrammingLanguage::JAVASCRIPT) { 1.661 + NS_WARNING("Discarding non-JS event listener from untrusted source"); 1.662 + return NS_ERROR_FAILURE; 1.663 + } 1.664 + 1.665 + nsCOMPtr<nsIDocument> doc; 1.666 + nsCOMPtr<nsIScriptGlobalObject> global = 1.667 + GetScriptGlobalAndDocument(getter_AddRefs(doc)); 1.668 + 1.669 + if (!global) { 1.670 + // This can happen; for example this document might have been 1.671 + // loaded as data. 1.672 + return NS_OK; 1.673 + } 1.674 + 1.675 +#ifdef DEBUG 1.676 + nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(global); 1.677 + if (win) { 1.678 + MOZ_ASSERT(win->IsInnerWindow(), "We should not have an outer window here!"); 1.679 + } 1.680 +#endif 1.681 + 1.682 + nsresult rv = NS_OK; 1.683 + // return early preventing the event listener from being added 1.684 + // 'doc' is fetched above 1.685 + if (doc) { 1.686 + // Don't allow adding an event listener if the document is sandboxed 1.687 + // without 'allow-scripts'. 1.688 + if (doc->GetSandboxFlags() & SANDBOXED_SCRIPTS) { 1.689 + return NS_ERROR_DOM_SECURITY_ERR; 1.690 + } 1.691 + 1.692 + nsCOMPtr<nsIContentSecurityPolicy> csp; 1.693 + rv = doc->NodePrincipal()->GetCsp(getter_AddRefs(csp)); 1.694 + NS_ENSURE_SUCCESS(rv, rv); 1.695 + 1.696 + if (csp) { 1.697 + bool inlineOK = true; 1.698 + bool reportViolations = false; 1.699 + rv = csp->GetAllowsInlineScript(&reportViolations, &inlineOK); 1.700 + NS_ENSURE_SUCCESS(rv, rv); 1.701 + 1.702 + if (reportViolations) { 1.703 + // gather information to log with violation report 1.704 + nsIURI* uri = doc->GetDocumentURI(); 1.705 + nsAutoCString asciiSpec; 1.706 + if (uri) 1.707 + uri->GetAsciiSpec(asciiSpec); 1.708 + nsAutoString scriptSample, attr, tagName(NS_LITERAL_STRING("UNKNOWN")); 1.709 + aName->ToString(attr); 1.710 + nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(mTarget)); 1.711 + if (domNode) 1.712 + domNode->GetNodeName(tagName); 1.713 + // build a "script sample" based on what we know about this element 1.714 + scriptSample.Assign(attr); 1.715 + scriptSample.AppendLiteral(" attribute on "); 1.716 + scriptSample.Append(tagName); 1.717 + scriptSample.AppendLiteral(" element"); 1.718 + csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_INLINE_SCRIPT, 1.719 + NS_ConvertUTF8toUTF16(asciiSpec), 1.720 + scriptSample, 1.721 + 0, 1.722 + EmptyString(), 1.723 + EmptyString()); 1.724 + } 1.725 + 1.726 + // return early if CSP wants us to block inline scripts 1.727 + if (!inlineOK) { 1.728 + return NS_OK; 1.729 + } 1.730 + } 1.731 + } 1.732 + 1.733 + // This might be the first reference to this language in the global 1.734 + // We must init the language before we attempt to fetch its context. 1.735 + if (NS_FAILED(global->EnsureScriptEnvironment())) { 1.736 + NS_WARNING("Failed to setup script environment for this language"); 1.737 + // but fall through and let the inevitable failure below handle it. 1.738 + } 1.739 + 1.740 + nsIScriptContext* context = global->GetScriptContext(); 1.741 + NS_ENSURE_TRUE(context, NS_ERROR_FAILURE); 1.742 + NS_ENSURE_STATE(global->GetGlobalJSObject()); 1.743 + 1.744 + Listener* listener = SetEventHandlerInternal(aName, 1.745 + EmptyString(), 1.746 + TypedEventHandler(), 1.747 + aPermitUntrustedEvents); 1.748 + 1.749 + if (!aDeferCompilation) { 1.750 + return CompileEventHandlerInternal(listener, &aBody, aElement); 1.751 + } 1.752 + 1.753 + return NS_OK; 1.754 +} 1.755 + 1.756 +void 1.757 +EventListenerManager::RemoveEventHandler(nsIAtom* aName, 1.758 + const nsAString& aTypeString) 1.759 +{ 1.760 + if (mClearingListeners) { 1.761 + return; 1.762 + } 1.763 + 1.764 + uint32_t eventType = nsContentUtils::GetEventId(aName); 1.765 + Listener* listener = FindEventHandler(eventType, aName, aTypeString); 1.766 + 1.767 + if (listener) { 1.768 + mListeners.RemoveElementAt(uint32_t(listener - &mListeners.ElementAt(0))); 1.769 + mNoListenerForEvent = NS_EVENT_NULL; 1.770 + mNoListenerForEventAtom = nullptr; 1.771 + if (mTarget && aName) { 1.772 + mTarget->EventListenerRemoved(aName); 1.773 + } 1.774 + } 1.775 +} 1.776 + 1.777 +nsresult 1.778 +EventListenerManager::CompileEventHandlerInternal(Listener* aListener, 1.779 + const nsAString* aBody, 1.780 + Element* aElement) 1.781 +{ 1.782 + MOZ_ASSERT(aListener->GetJSEventHandler()); 1.783 + MOZ_ASSERT(aListener->mHandlerIsString, "Why are we compiling a non-string JS listener?"); 1.784 + JSEventHandler* jsEventHandler = aListener->GetJSEventHandler(); 1.785 + MOZ_ASSERT(!jsEventHandler->GetTypedEventHandler().HasEventHandler(), 1.786 + "What is there to compile?"); 1.787 + 1.788 + nsresult result = NS_OK; 1.789 + nsCOMPtr<nsIDocument> doc; 1.790 + nsCOMPtr<nsIScriptGlobalObject> global = 1.791 + GetScriptGlobalAndDocument(getter_AddRefs(doc)); 1.792 + NS_ENSURE_STATE(global); 1.793 + 1.794 + nsIScriptContext* context = global->GetScriptContext(); 1.795 + NS_ENSURE_STATE(context); 1.796 + 1.797 + // Activate JSAPI, and make sure that exceptions are reported on the right 1.798 + // Window. 1.799 + AutoJSAPIWithErrorsReportedToWindow jsapi(context); 1.800 + JSContext* cx = jsapi.cx(); 1.801 + 1.802 + nsCOMPtr<nsIAtom> typeAtom = aListener->mTypeAtom; 1.803 + nsIAtom* attrName = typeAtom; 1.804 + 1.805 + // Flag us as not a string so we don't keep trying to compile strings which 1.806 + // can't be compiled. 1.807 + aListener->mHandlerIsString = false; 1.808 + 1.809 + // mTarget may not be an Element if it's a window and we're 1.810 + // getting an inline event listener forwarded from <html:body> or 1.811 + // <html:frameset> or <xul:window> or the like. 1.812 + // XXX I don't like that we have to reference content from 1.813 + // here. The alternative is to store the event handler string on 1.814 + // the JSEventHandler itself, and that still doesn't address 1.815 + // the arg names issue. 1.816 + nsCOMPtr<Element> element = do_QueryInterface(mTarget); 1.817 + MOZ_ASSERT(element || aBody, "Where will we get our body?"); 1.818 + nsAutoString handlerBody; 1.819 + const nsAString* body = aBody; 1.820 + if (!aBody) { 1.821 + if (aListener->mTypeAtom == nsGkAtoms::onSVGLoad) { 1.822 + attrName = nsGkAtoms::onload; 1.823 + } else if (aListener->mTypeAtom == nsGkAtoms::onSVGUnload) { 1.824 + attrName = nsGkAtoms::onunload; 1.825 + } else if (aListener->mTypeAtom == nsGkAtoms::onSVGResize) { 1.826 + attrName = nsGkAtoms::onresize; 1.827 + } else if (aListener->mTypeAtom == nsGkAtoms::onSVGScroll) { 1.828 + attrName = nsGkAtoms::onscroll; 1.829 + } else if (aListener->mTypeAtom == nsGkAtoms::onSVGZoom) { 1.830 + attrName = nsGkAtoms::onzoom; 1.831 + } else if (aListener->mTypeAtom == nsGkAtoms::onbeginEvent) { 1.832 + attrName = nsGkAtoms::onbegin; 1.833 + } else if (aListener->mTypeAtom == nsGkAtoms::onrepeatEvent) { 1.834 + attrName = nsGkAtoms::onrepeat; 1.835 + } else if (aListener->mTypeAtom == nsGkAtoms::onendEvent) { 1.836 + attrName = nsGkAtoms::onend; 1.837 + } 1.838 + 1.839 + element->GetAttr(kNameSpaceID_None, attrName, handlerBody); 1.840 + body = &handlerBody; 1.841 + aElement = element; 1.842 + } 1.843 + aListener = nullptr; 1.844 + 1.845 + uint32_t lineNo = 0; 1.846 + nsAutoCString url (NS_LITERAL_CSTRING("-moz-evil:lying-event-listener")); 1.847 + MOZ_ASSERT(body); 1.848 + MOZ_ASSERT(aElement); 1.849 + nsIURI *uri = aElement->OwnerDoc()->GetDocumentURI(); 1.850 + if (uri) { 1.851 + uri->GetSpec(url); 1.852 + lineNo = 1; 1.853 + } 1.854 + 1.855 + uint32_t argCount; 1.856 + const char **argNames; 1.857 + nsContentUtils::GetEventArgNames(aElement->GetNameSpaceID(), 1.858 + typeAtom, 1.859 + &argCount, &argNames); 1.860 + 1.861 + // Wrap the event target, so that we can use it as the scope for the event 1.862 + // handler. Note that mTarget is different from aElement in the <body> case, 1.863 + // where mTarget is a Window. 1.864 + // 1.865 + // The wrapScope doesn't really matter here, because the target will create 1.866 + // its reflector in the proper scope, and then we'll enter that compartment. 1.867 + JS::Rooted<JSObject*> wrapScope(cx, context->GetWindowProxy()); 1.868 + JS::Rooted<JS::Value> v(cx); 1.869 + { 1.870 + JSAutoCompartment ac(cx, wrapScope); 1.871 + nsresult rv = nsContentUtils::WrapNative(cx, mTarget, &v, 1.872 + /* aAllowWrapping = */ false); 1.873 + if (NS_WARN_IF(NS_FAILED(rv))) { 1.874 + return rv; 1.875 + } 1.876 + } 1.877 + JS::Rooted<JSObject*> target(cx, &v.toObject()); 1.878 + JSAutoCompartment ac(cx, target); 1.879 + 1.880 + nsDependentAtomString str(attrName); 1.881 + // Most of our names are short enough that we don't even have to malloc 1.882 + // the JS string stuff, so don't worry about playing games with 1.883 + // refcounting XPCOM stringbuffers. 1.884 + JS::Rooted<JSString*> jsStr(cx, JS_NewUCStringCopyN(cx, 1.885 + str.BeginReading(), 1.886 + str.Length())); 1.887 + NS_ENSURE_TRUE(jsStr, NS_ERROR_OUT_OF_MEMORY); 1.888 + 1.889 + // Get the reflector for |aElement|, so that we can pass to setElement. 1.890 + if (NS_WARN_IF(!WrapNewBindingObject(cx, target, aElement, &v))) { 1.891 + return NS_ERROR_FAILURE; 1.892 + } 1.893 + JS::CompileOptions options(cx); 1.894 + options.setIntroductionType("eventHandler") 1.895 + .setFileAndLine(url.get(), lineNo) 1.896 + .setVersion(SCRIPTVERSION_DEFAULT) 1.897 + .setElement(&v.toObject()) 1.898 + .setElementAttributeName(jsStr) 1.899 + .setDefineOnScope(false); 1.900 + 1.901 + JS::Rooted<JSObject*> handler(cx); 1.902 + result = nsJSUtils::CompileFunction(cx, target, options, 1.903 + nsAtomCString(typeAtom), 1.904 + argCount, argNames, *body, handler.address()); 1.905 + NS_ENSURE_SUCCESS(result, result); 1.906 + NS_ENSURE_TRUE(handler, NS_ERROR_FAILURE); 1.907 + 1.908 + nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(mTarget); 1.909 + if (jsEventHandler->EventName() == nsGkAtoms::onerror && win) { 1.910 + nsRefPtr<OnErrorEventHandlerNonNull> handlerCallback = 1.911 + new OnErrorEventHandlerNonNull(handler, /* aIncumbentGlobal = */ nullptr); 1.912 + jsEventHandler->SetHandler(handlerCallback); 1.913 + } else if (jsEventHandler->EventName() == nsGkAtoms::onbeforeunload && win) { 1.914 + nsRefPtr<OnBeforeUnloadEventHandlerNonNull> handlerCallback = 1.915 + new OnBeforeUnloadEventHandlerNonNull(handler, /* aIncumbentGlobal = */ nullptr); 1.916 + jsEventHandler->SetHandler(handlerCallback); 1.917 + } else { 1.918 + nsRefPtr<EventHandlerNonNull> handlerCallback = 1.919 + new EventHandlerNonNull(handler, /* aIncumbentGlobal = */ nullptr); 1.920 + jsEventHandler->SetHandler(handlerCallback); 1.921 + } 1.922 + 1.923 + return result; 1.924 +} 1.925 + 1.926 +nsresult 1.927 +EventListenerManager::HandleEventSubType(Listener* aListener, 1.928 + nsIDOMEvent* aDOMEvent, 1.929 + EventTarget* aCurrentTarget) 1.930 +{ 1.931 + nsresult result = NS_OK; 1.932 + EventListenerHolder listenerHolder(aListener->mListener); // strong ref 1.933 + 1.934 + // If this is a script handler and we haven't yet 1.935 + // compiled the event handler itself 1.936 + if ((aListener->mListenerType == Listener::eJSEventListener) && 1.937 + aListener->mHandlerIsString) { 1.938 + result = CompileEventHandlerInternal(aListener, nullptr, nullptr); 1.939 + aListener = nullptr; 1.940 + } 1.941 + 1.942 + if (NS_SUCCEEDED(result)) { 1.943 + if (mIsMainThreadELM) { 1.944 + nsContentUtils::EnterMicroTask(); 1.945 + } 1.946 + // nsIDOMEvent::currentTarget is set in EventDispatcher. 1.947 + if (listenerHolder.HasWebIDLCallback()) { 1.948 + ErrorResult rv; 1.949 + listenerHolder.GetWebIDLCallback()-> 1.950 + HandleEvent(aCurrentTarget, *(aDOMEvent->InternalDOMEvent()), rv); 1.951 + result = rv.ErrorCode(); 1.952 + } else { 1.953 + result = listenerHolder.GetXPCOMCallback()->HandleEvent(aDOMEvent); 1.954 + } 1.955 + if (mIsMainThreadELM) { 1.956 + nsContentUtils::LeaveMicroTask(); 1.957 + } 1.958 + } 1.959 + 1.960 + return result; 1.961 +} 1.962 + 1.963 +/** 1.964 +* Causes a check for event listeners and processing by them if they exist. 1.965 +* @param an event listener 1.966 +*/ 1.967 + 1.968 +void 1.969 +EventListenerManager::HandleEventInternal(nsPresContext* aPresContext, 1.970 + WidgetEvent* aEvent, 1.971 + nsIDOMEvent** aDOMEvent, 1.972 + EventTarget* aCurrentTarget, 1.973 + nsEventStatus* aEventStatus) 1.974 +{ 1.975 + //Set the value of the internal PreventDefault flag properly based on aEventStatus 1.976 + if (*aEventStatus == nsEventStatus_eConsumeNoDefault) { 1.977 + aEvent->mFlags.mDefaultPrevented = true; 1.978 + } 1.979 + 1.980 + nsAutoTObserverArray<Listener, 2>::EndLimitedIterator iter(mListeners); 1.981 + Maybe<nsAutoPopupStatePusher> popupStatePusher; 1.982 + if (mIsMainThreadELM) { 1.983 + popupStatePusher.construct(Event::GetEventPopupControlState(aEvent)); 1.984 + } 1.985 + 1.986 + bool hasListener = false; 1.987 + while (iter.HasMore()) { 1.988 + if (aEvent->mFlags.mImmediatePropagationStopped) { 1.989 + break; 1.990 + } 1.991 + Listener* listener = &iter.GetNext(); 1.992 + // Check that the phase is same in event and event listener. 1.993 + // Handle only trusted events, except when listener permits untrusted events. 1.994 + if (ListenerCanHandle(listener, aEvent)) { 1.995 + hasListener = true; 1.996 + if (listener->IsListening(aEvent) && 1.997 + (aEvent->mFlags.mIsTrusted || 1.998 + listener->mFlags.mAllowUntrustedEvents)) { 1.999 + if (!*aDOMEvent) { 1.1000 + // This is tiny bit slow, but happens only once per event. 1.1001 + nsCOMPtr<EventTarget> et = 1.1002 + do_QueryInterface(aEvent->originalTarget); 1.1003 + EventDispatcher::CreateEvent(et, aPresContext, 1.1004 + aEvent, EmptyString(), aDOMEvent); 1.1005 + } 1.1006 + if (*aDOMEvent) { 1.1007 + if (!aEvent->currentTarget) { 1.1008 + aEvent->currentTarget = aCurrentTarget->GetTargetForDOMEvent(); 1.1009 + if (!aEvent->currentTarget) { 1.1010 + break; 1.1011 + } 1.1012 + } 1.1013 + 1.1014 + if (NS_FAILED(HandleEventSubType(listener, *aDOMEvent, 1.1015 + aCurrentTarget))) { 1.1016 + aEvent->mFlags.mExceptionHasBeenRisen = true; 1.1017 + } 1.1018 + } 1.1019 + } 1.1020 + } 1.1021 + } 1.1022 + 1.1023 + aEvent->currentTarget = nullptr; 1.1024 + 1.1025 + if (mIsMainThreadELM && !hasListener) { 1.1026 + mNoListenerForEvent = aEvent->message; 1.1027 + mNoListenerForEventAtom = aEvent->userType; 1.1028 + } 1.1029 + 1.1030 + if (aEvent->mFlags.mDefaultPrevented) { 1.1031 + *aEventStatus = nsEventStatus_eConsumeNoDefault; 1.1032 + } 1.1033 +} 1.1034 + 1.1035 +void 1.1036 +EventListenerManager::Disconnect() 1.1037 +{ 1.1038 + mTarget = nullptr; 1.1039 + RemoveAllListeners(); 1.1040 +} 1.1041 + 1.1042 +void 1.1043 +EventListenerManager::AddEventListener( 1.1044 + const nsAString& aType, 1.1045 + const EventListenerHolder& aListenerHolder, 1.1046 + bool aUseCapture, 1.1047 + bool aWantsUntrusted) 1.1048 +{ 1.1049 + EventListenerFlags flags; 1.1050 + flags.mCapture = aUseCapture; 1.1051 + flags.mAllowUntrustedEvents = aWantsUntrusted; 1.1052 + return AddEventListenerByType(aListenerHolder, aType, flags); 1.1053 +} 1.1054 + 1.1055 +void 1.1056 +EventListenerManager::RemoveEventListener( 1.1057 + const nsAString& aType, 1.1058 + const EventListenerHolder& aListenerHolder, 1.1059 + bool aUseCapture) 1.1060 +{ 1.1061 + EventListenerFlags flags; 1.1062 + flags.mCapture = aUseCapture; 1.1063 + RemoveEventListenerByType(aListenerHolder, aType, flags); 1.1064 +} 1.1065 + 1.1066 +void 1.1067 +EventListenerManager::AddListenerForAllEvents(nsIDOMEventListener* aDOMListener, 1.1068 + bool aUseCapture, 1.1069 + bool aWantsUntrusted, 1.1070 + bool aSystemEventGroup) 1.1071 +{ 1.1072 + EventListenerFlags flags; 1.1073 + flags.mCapture = aUseCapture; 1.1074 + flags.mAllowUntrustedEvents = aWantsUntrusted; 1.1075 + flags.mInSystemGroup = aSystemEventGroup; 1.1076 + EventListenerHolder listenerHolder(aDOMListener); 1.1077 + AddEventListenerInternal(listenerHolder, NS_EVENT_ALL, nullptr, EmptyString(), 1.1078 + flags, false, true); 1.1079 +} 1.1080 + 1.1081 +void 1.1082 +EventListenerManager::RemoveListenerForAllEvents( 1.1083 + nsIDOMEventListener* aDOMListener, 1.1084 + bool aUseCapture, 1.1085 + bool aSystemEventGroup) 1.1086 +{ 1.1087 + EventListenerFlags flags; 1.1088 + flags.mCapture = aUseCapture; 1.1089 + flags.mInSystemGroup = aSystemEventGroup; 1.1090 + EventListenerHolder listenerHolder(aDOMListener); 1.1091 + RemoveEventListenerInternal(listenerHolder, NS_EVENT_ALL, nullptr, 1.1092 + EmptyString(), flags, true); 1.1093 +} 1.1094 + 1.1095 +bool 1.1096 +EventListenerManager::HasMutationListeners() 1.1097 +{ 1.1098 + if (mMayHaveMutationListeners) { 1.1099 + uint32_t count = mListeners.Length(); 1.1100 + for (uint32_t i = 0; i < count; ++i) { 1.1101 + Listener* listener = &mListeners.ElementAt(i); 1.1102 + if (listener->mEventType >= NS_MUTATION_START && 1.1103 + listener->mEventType <= NS_MUTATION_END) { 1.1104 + return true; 1.1105 + } 1.1106 + } 1.1107 + } 1.1108 + 1.1109 + return false; 1.1110 +} 1.1111 + 1.1112 +uint32_t 1.1113 +EventListenerManager::MutationListenerBits() 1.1114 +{ 1.1115 + uint32_t bits = 0; 1.1116 + if (mMayHaveMutationListeners) { 1.1117 + uint32_t count = mListeners.Length(); 1.1118 + for (uint32_t i = 0; i < count; ++i) { 1.1119 + Listener* listener = &mListeners.ElementAt(i); 1.1120 + if (listener->mEventType >= NS_MUTATION_START && 1.1121 + listener->mEventType <= NS_MUTATION_END) { 1.1122 + if (listener->mEventType == NS_MUTATION_SUBTREEMODIFIED) { 1.1123 + return kAllMutationBits; 1.1124 + } 1.1125 + bits |= MutationBitForEventType(listener->mEventType); 1.1126 + } 1.1127 + } 1.1128 + } 1.1129 + return bits; 1.1130 +} 1.1131 + 1.1132 +bool 1.1133 +EventListenerManager::HasListenersFor(const nsAString& aEventName) 1.1134 +{ 1.1135 + nsCOMPtr<nsIAtom> atom = do_GetAtom(NS_LITERAL_STRING("on") + aEventName); 1.1136 + return HasListenersFor(atom); 1.1137 +} 1.1138 + 1.1139 +bool 1.1140 +EventListenerManager::HasListenersFor(nsIAtom* aEventNameWithOn) 1.1141 +{ 1.1142 +#ifdef DEBUG 1.1143 + nsAutoString name; 1.1144 + aEventNameWithOn->ToString(name); 1.1145 +#endif 1.1146 + NS_ASSERTION(StringBeginsWith(name, NS_LITERAL_STRING("on")), 1.1147 + "Event name does not start with 'on'"); 1.1148 + uint32_t count = mListeners.Length(); 1.1149 + for (uint32_t i = 0; i < count; ++i) { 1.1150 + Listener* listener = &mListeners.ElementAt(i); 1.1151 + if (listener->mTypeAtom == aEventNameWithOn) { 1.1152 + return true; 1.1153 + } 1.1154 + } 1.1155 + return false; 1.1156 +} 1.1157 + 1.1158 +bool 1.1159 +EventListenerManager::HasListeners() 1.1160 +{ 1.1161 + return !mListeners.IsEmpty(); 1.1162 +} 1.1163 + 1.1164 +nsresult 1.1165 +EventListenerManager::GetListenerInfo(nsCOMArray<nsIEventListenerInfo>* aList) 1.1166 +{ 1.1167 + nsCOMPtr<EventTarget> target = do_QueryInterface(mTarget); 1.1168 + NS_ENSURE_STATE(target); 1.1169 + aList->Clear(); 1.1170 + uint32_t count = mListeners.Length(); 1.1171 + for (uint32_t i = 0; i < count; ++i) { 1.1172 + const Listener& listener = mListeners.ElementAt(i); 1.1173 + // If this is a script handler and we haven't yet 1.1174 + // compiled the event handler itself go ahead and compile it 1.1175 + if (listener.mListenerType == Listener::eJSEventListener && 1.1176 + listener.mHandlerIsString) { 1.1177 + CompileEventHandlerInternal(const_cast<Listener*>(&listener), nullptr, 1.1178 + nullptr); 1.1179 + } 1.1180 + nsAutoString eventType; 1.1181 + if (listener.mAllEvents) { 1.1182 + eventType.SetIsVoid(true); 1.1183 + } else { 1.1184 + eventType.Assign(Substring(nsDependentAtomString(listener.mTypeAtom), 2)); 1.1185 + } 1.1186 + // EventListenerInfo is defined in XPCOM, so we have to go ahead 1.1187 + // and convert to an XPCOM callback here... 1.1188 + nsRefPtr<EventListenerInfo> info = 1.1189 + new EventListenerInfo(eventType, listener.mListener.ToXPCOMCallback(), 1.1190 + listener.mFlags.mCapture, 1.1191 + listener.mFlags.mAllowUntrustedEvents, 1.1192 + listener.mFlags.mInSystemGroup); 1.1193 + aList->AppendObject(info); 1.1194 + } 1.1195 + return NS_OK; 1.1196 +} 1.1197 + 1.1198 +bool 1.1199 +EventListenerManager::HasUnloadListeners() 1.1200 +{ 1.1201 + uint32_t count = mListeners.Length(); 1.1202 + for (uint32_t i = 0; i < count; ++i) { 1.1203 + Listener* listener = &mListeners.ElementAt(i); 1.1204 + if (listener->mEventType == NS_PAGE_UNLOAD || 1.1205 + listener->mEventType == NS_BEFORE_PAGE_UNLOAD) { 1.1206 + return true; 1.1207 + } 1.1208 + } 1.1209 + return false; 1.1210 +} 1.1211 + 1.1212 +void 1.1213 +EventListenerManager::SetEventHandler(nsIAtom* aEventName, 1.1214 + const nsAString& aTypeString, 1.1215 + EventHandlerNonNull* aHandler) 1.1216 +{ 1.1217 + if (!aHandler) { 1.1218 + RemoveEventHandler(aEventName, aTypeString); 1.1219 + return; 1.1220 + } 1.1221 + 1.1222 + // Untrusted events are always permitted for non-chrome script 1.1223 + // handlers. 1.1224 + SetEventHandlerInternal(aEventName, aTypeString, TypedEventHandler(aHandler), 1.1225 + !mIsMainThreadELM || 1.1226 + !nsContentUtils::IsCallerChrome()); 1.1227 +} 1.1228 + 1.1229 +void 1.1230 +EventListenerManager::SetEventHandler(OnErrorEventHandlerNonNull* aHandler) 1.1231 +{ 1.1232 + if (mIsMainThreadELM) { 1.1233 + if (!aHandler) { 1.1234 + RemoveEventHandler(nsGkAtoms::onerror, EmptyString()); 1.1235 + return; 1.1236 + } 1.1237 + 1.1238 + // Untrusted events are always permitted for non-chrome script 1.1239 + // handlers. 1.1240 + SetEventHandlerInternal(nsGkAtoms::onerror, EmptyString(), 1.1241 + TypedEventHandler(aHandler), 1.1242 + !nsContentUtils::IsCallerChrome()); 1.1243 + } else { 1.1244 + if (!aHandler) { 1.1245 + RemoveEventHandler(nullptr, NS_LITERAL_STRING("error")); 1.1246 + return; 1.1247 + } 1.1248 + 1.1249 + // Untrusted events are always permitted. 1.1250 + SetEventHandlerInternal(nullptr, NS_LITERAL_STRING("error"), 1.1251 + TypedEventHandler(aHandler), true); 1.1252 + } 1.1253 +} 1.1254 + 1.1255 +void 1.1256 +EventListenerManager::SetEventHandler( 1.1257 + OnBeforeUnloadEventHandlerNonNull* aHandler) 1.1258 +{ 1.1259 + if (!aHandler) { 1.1260 + RemoveEventHandler(nsGkAtoms::onbeforeunload, EmptyString()); 1.1261 + return; 1.1262 + } 1.1263 + 1.1264 + // Untrusted events are always permitted for non-chrome script 1.1265 + // handlers. 1.1266 + SetEventHandlerInternal(nsGkAtoms::onbeforeunload, EmptyString(), 1.1267 + TypedEventHandler(aHandler), 1.1268 + !mIsMainThreadELM || 1.1269 + !nsContentUtils::IsCallerChrome()); 1.1270 +} 1.1271 + 1.1272 +const TypedEventHandler* 1.1273 +EventListenerManager::GetTypedEventHandler(nsIAtom* aEventName, 1.1274 + const nsAString& aTypeString) 1.1275 +{ 1.1276 + uint32_t eventType = nsContentUtils::GetEventId(aEventName); 1.1277 + Listener* listener = FindEventHandler(eventType, aEventName, aTypeString); 1.1278 + 1.1279 + if (!listener) { 1.1280 + return nullptr; 1.1281 + } 1.1282 + 1.1283 + JSEventHandler* jsEventHandler = listener->GetJSEventHandler(); 1.1284 + 1.1285 + if (listener->mHandlerIsString) { 1.1286 + CompileEventHandlerInternal(listener, nullptr, nullptr); 1.1287 + } 1.1288 + 1.1289 + const TypedEventHandler& typedHandler = 1.1290 + jsEventHandler->GetTypedEventHandler(); 1.1291 + return typedHandler.HasEventHandler() ? &typedHandler : nullptr; 1.1292 +} 1.1293 + 1.1294 +size_t 1.1295 +EventListenerManager::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const 1.1296 +{ 1.1297 + size_t n = aMallocSizeOf(this); 1.1298 + n += mListeners.SizeOfExcludingThis(aMallocSizeOf); 1.1299 + uint32_t count = mListeners.Length(); 1.1300 + for (uint32_t i = 0; i < count; ++i) { 1.1301 + JSEventHandler* jsEventHandler = 1.1302 + mListeners.ElementAt(i).GetJSEventHandler(); 1.1303 + if (jsEventHandler) { 1.1304 + n += jsEventHandler->SizeOfIncludingThis(aMallocSizeOf); 1.1305 + } 1.1306 + } 1.1307 + return n; 1.1308 +} 1.1309 + 1.1310 +void 1.1311 +EventListenerManager::MarkForCC() 1.1312 +{ 1.1313 + uint32_t count = mListeners.Length(); 1.1314 + for (uint32_t i = 0; i < count; ++i) { 1.1315 + const Listener& listener = mListeners.ElementAt(i); 1.1316 + JSEventHandler* jsEventHandler = listener.GetJSEventHandler(); 1.1317 + if (jsEventHandler) { 1.1318 + const TypedEventHandler& typedHandler = 1.1319 + jsEventHandler->GetTypedEventHandler(); 1.1320 + if (typedHandler.HasEventHandler()) { 1.1321 + JS::ExposeObjectToActiveJS(typedHandler.Ptr()->Callable()); 1.1322 + } 1.1323 + } else if (listener.mListenerType == Listener::eWrappedJSListener) { 1.1324 + xpc_TryUnmarkWrappedGrayObject(listener.mListener.GetXPCOMCallback()); 1.1325 + } else if (listener.mListenerType == Listener::eWebIDLListener) { 1.1326 + // Callback() unmarks gray 1.1327 + listener.mListener.GetWebIDLCallback()->Callback(); 1.1328 + } 1.1329 + } 1.1330 + if (mRefCnt.IsPurple()) { 1.1331 + mRefCnt.RemovePurple(); 1.1332 + } 1.1333 +} 1.1334 + 1.1335 +already_AddRefed<nsIScriptGlobalObject> 1.1336 +EventListenerManager::GetScriptGlobalAndDocument(nsIDocument** aDoc) 1.1337 +{ 1.1338 + nsCOMPtr<nsINode> node(do_QueryInterface(mTarget)); 1.1339 + nsCOMPtr<nsIDocument> doc; 1.1340 + nsCOMPtr<nsIScriptGlobalObject> global; 1.1341 + if (node) { 1.1342 + // Try to get context from doc 1.1343 + // XXX sXBL/XBL2 issue -- do we really want the owner here? What 1.1344 + // if that's the XBL document? 1.1345 + doc = node->OwnerDoc(); 1.1346 + if (doc->IsLoadedAsData()) { 1.1347 + return nullptr; 1.1348 + } 1.1349 + 1.1350 + // We want to allow compiling an event handler even in an unloaded 1.1351 + // document, so use GetScopeObject here, not GetScriptHandlingObject. 1.1352 + global = do_QueryInterface(doc->GetScopeObject()); 1.1353 + } else { 1.1354 + nsCOMPtr<nsPIDOMWindow> win = GetTargetAsInnerWindow(); 1.1355 + if (win) { 1.1356 + doc = win->GetExtantDoc(); 1.1357 + global = do_QueryInterface(win); 1.1358 + } else { 1.1359 + global = do_QueryInterface(mTarget); 1.1360 + } 1.1361 + } 1.1362 + 1.1363 + doc.forget(aDoc); 1.1364 + return global.forget(); 1.1365 +} 1.1366 + 1.1367 +} // namespace mozilla