dom/events/EventListenerManager.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 // Microsoft's API Name hackery sucks
michael@0 7 #undef CreateEvent
michael@0 8
michael@0 9 #include "mozilla/BasicEvents.h"
michael@0 10 #include "mozilla/EventDispatcher.h"
michael@0 11 #include "mozilla/EventListenerManager.h"
michael@0 12 #ifdef MOZ_B2G
michael@0 13 #include "mozilla/Hal.h"
michael@0 14 #endif // #ifdef MOZ_B2G
michael@0 15 #include "mozilla/HalSensor.h"
michael@0 16 #include "mozilla/InternalMutationEvent.h"
michael@0 17 #include "mozilla/JSEventHandler.h"
michael@0 18 #include "mozilla/MemoryReporting.h"
michael@0 19 #include "mozilla/dom/BindingUtils.h"
michael@0 20 #include "mozilla/dom/Element.h"
michael@0 21 #include "mozilla/dom/Event.h"
michael@0 22
michael@0 23 #include "EventListenerService.h"
michael@0 24 #include "nsCOMArray.h"
michael@0 25 #include "nsCOMPtr.h"
michael@0 26 #include "nsContentUtils.h"
michael@0 27 #include "nsDOMCID.h"
michael@0 28 #include "nsError.h"
michael@0 29 #include "nsGkAtoms.h"
michael@0 30 #include "nsIContent.h"
michael@0 31 #include "nsIContentSecurityPolicy.h"
michael@0 32 #include "nsIDocument.h"
michael@0 33 #include "nsIDOMEventListener.h"
michael@0 34 #include "nsIScriptGlobalObject.h"
michael@0 35 #include "nsISupports.h"
michael@0 36 #include "nsIXPConnect.h"
michael@0 37 #include "nsJSUtils.h"
michael@0 38 #include "nsNameSpaceManager.h"
michael@0 39 #include "nsPIDOMWindow.h"
michael@0 40 #include "nsSandboxFlags.h"
michael@0 41 #include "xpcpublic.h"
michael@0 42
michael@0 43 namespace mozilla {
michael@0 44
michael@0 45 using namespace dom;
michael@0 46 using namespace hal;
michael@0 47
michael@0 48 #define EVENT_TYPE_EQUALS(ls, type, userType, typeString, allEvents) \
michael@0 49 ((ls->mEventType == type && \
michael@0 50 (ls->mEventType != NS_USER_DEFINED_EVENT || \
michael@0 51 (mIsMainThreadELM && ls->mTypeAtom == userType) || \
michael@0 52 (!mIsMainThreadELM && ls->mTypeString.Equals(typeString)))) || \
michael@0 53 (allEvents && ls->mAllEvents))
michael@0 54
michael@0 55 static const uint32_t kAllMutationBits =
michael@0 56 NS_EVENT_BITS_MUTATION_SUBTREEMODIFIED |
michael@0 57 NS_EVENT_BITS_MUTATION_NODEINSERTED |
michael@0 58 NS_EVENT_BITS_MUTATION_NODEREMOVED |
michael@0 59 NS_EVENT_BITS_MUTATION_NODEREMOVEDFROMDOCUMENT |
michael@0 60 NS_EVENT_BITS_MUTATION_NODEINSERTEDINTODOCUMENT |
michael@0 61 NS_EVENT_BITS_MUTATION_ATTRMODIFIED |
michael@0 62 NS_EVENT_BITS_MUTATION_CHARACTERDATAMODIFIED;
michael@0 63
michael@0 64 static uint32_t
michael@0 65 MutationBitForEventType(uint32_t aEventType)
michael@0 66 {
michael@0 67 switch (aEventType) {
michael@0 68 case NS_MUTATION_SUBTREEMODIFIED:
michael@0 69 return NS_EVENT_BITS_MUTATION_SUBTREEMODIFIED;
michael@0 70 case NS_MUTATION_NODEINSERTED:
michael@0 71 return NS_EVENT_BITS_MUTATION_NODEINSERTED;
michael@0 72 case NS_MUTATION_NODEREMOVED:
michael@0 73 return NS_EVENT_BITS_MUTATION_NODEREMOVED;
michael@0 74 case NS_MUTATION_NODEREMOVEDFROMDOCUMENT:
michael@0 75 return NS_EVENT_BITS_MUTATION_NODEREMOVEDFROMDOCUMENT;
michael@0 76 case NS_MUTATION_NODEINSERTEDINTODOCUMENT:
michael@0 77 return NS_EVENT_BITS_MUTATION_NODEINSERTEDINTODOCUMENT;
michael@0 78 case NS_MUTATION_ATTRMODIFIED:
michael@0 79 return NS_EVENT_BITS_MUTATION_ATTRMODIFIED;
michael@0 80 case NS_MUTATION_CHARACTERDATAMODIFIED:
michael@0 81 return NS_EVENT_BITS_MUTATION_CHARACTERDATAMODIFIED;
michael@0 82 default:
michael@0 83 break;
michael@0 84 }
michael@0 85 return 0;
michael@0 86 }
michael@0 87
michael@0 88 uint32_t EventListenerManager::sMainThreadCreatedCount = 0;
michael@0 89
michael@0 90 EventListenerManager::EventListenerManager(EventTarget* aTarget)
michael@0 91 : mMayHavePaintEventListener(false)
michael@0 92 , mMayHaveMutationListeners(false)
michael@0 93 , mMayHaveCapturingListeners(false)
michael@0 94 , mMayHaveSystemGroupListeners(false)
michael@0 95 , mMayHaveTouchEventListener(false)
michael@0 96 , mMayHaveMouseEnterLeaveEventListener(false)
michael@0 97 , mMayHavePointerEnterLeaveEventListener(false)
michael@0 98 , mClearingListeners(false)
michael@0 99 , mIsMainThreadELM(NS_IsMainThread())
michael@0 100 , mNoListenerForEvent(0)
michael@0 101 , mTarget(aTarget)
michael@0 102 {
michael@0 103 NS_ASSERTION(aTarget, "unexpected null pointer");
michael@0 104
michael@0 105 if (mIsMainThreadELM) {
michael@0 106 ++sMainThreadCreatedCount;
michael@0 107 }
michael@0 108 }
michael@0 109
michael@0 110 EventListenerManager::~EventListenerManager()
michael@0 111 {
michael@0 112 // If your code fails this assertion, a possible reason is that
michael@0 113 // a class did not call our Disconnect() manually. Note that
michael@0 114 // this class can have Disconnect called in one of two ways:
michael@0 115 // if it is part of a cycle, then in Unlink() (such a cycle
michael@0 116 // would be with one of the listeners, not mTarget which is weak).
michael@0 117 // If not part of a cycle, then Disconnect must be called manually,
michael@0 118 // typically from the destructor of the owner class (mTarget).
michael@0 119 // XXX azakai: Is there any reason to not just call Disconnect
michael@0 120 // from right here, if not previously called?
michael@0 121 NS_ASSERTION(!mTarget, "didn't call Disconnect");
michael@0 122 RemoveAllListeners();
michael@0 123 }
michael@0 124
michael@0 125 void
michael@0 126 EventListenerManager::RemoveAllListeners()
michael@0 127 {
michael@0 128 if (mClearingListeners) {
michael@0 129 return;
michael@0 130 }
michael@0 131 mClearingListeners = true;
michael@0 132 mListeners.Clear();
michael@0 133 mClearingListeners = false;
michael@0 134 }
michael@0 135
michael@0 136 void
michael@0 137 EventListenerManager::Shutdown()
michael@0 138 {
michael@0 139 Event::Shutdown();
michael@0 140 }
michael@0 141
michael@0 142 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(EventListenerManager, AddRef)
michael@0 143 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(EventListenerManager, Release)
michael@0 144
michael@0 145 inline void
michael@0 146 ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
michael@0 147 EventListenerManager::Listener& aField,
michael@0 148 const char* aName,
michael@0 149 unsigned aFlags)
michael@0 150 {
michael@0 151 if (MOZ_UNLIKELY(aCallback.WantDebugInfo())) {
michael@0 152 nsAutoCString name;
michael@0 153 name.AppendASCII(aName);
michael@0 154 if (aField.mTypeAtom) {
michael@0 155 name.AppendASCII(" event=");
michael@0 156 name.Append(nsAtomCString(aField.mTypeAtom));
michael@0 157 name.AppendASCII(" listenerType=");
michael@0 158 name.AppendInt(aField.mListenerType);
michael@0 159 name.AppendASCII(" ");
michael@0 160 }
michael@0 161 CycleCollectionNoteChild(aCallback, aField.mListener.GetISupports(), name.get(),
michael@0 162 aFlags);
michael@0 163 } else {
michael@0 164 CycleCollectionNoteChild(aCallback, aField.mListener.GetISupports(), aName,
michael@0 165 aFlags);
michael@0 166 }
michael@0 167 }
michael@0 168
michael@0 169 NS_IMPL_CYCLE_COLLECTION_CLASS(EventListenerManager)
michael@0 170
michael@0 171 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(EventListenerManager)
michael@0 172 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListeners)
michael@0 173 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
michael@0 174
michael@0 175 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(EventListenerManager)
michael@0 176 tmp->Disconnect();
michael@0 177 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
michael@0 178
michael@0 179
michael@0 180 nsPIDOMWindow*
michael@0 181 EventListenerManager::GetInnerWindowForTarget()
michael@0 182 {
michael@0 183 nsCOMPtr<nsINode> node = do_QueryInterface(mTarget);
michael@0 184 if (node) {
michael@0 185 // XXX sXBL/XBL2 issue -- do we really want the owner here? What
michael@0 186 // if that's the XBL document?
michael@0 187 return node->OwnerDoc()->GetInnerWindow();
michael@0 188 }
michael@0 189
michael@0 190 nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
michael@0 191 return window;
michael@0 192 }
michael@0 193
michael@0 194 already_AddRefed<nsPIDOMWindow>
michael@0 195 EventListenerManager::GetTargetAsInnerWindow() const
michael@0 196 {
michael@0 197 nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mTarget);
michael@0 198 if (!window) {
michael@0 199 return nullptr;
michael@0 200 }
michael@0 201
michael@0 202 NS_ASSERTION(window->IsInnerWindow(), "Target should not be an outer window");
michael@0 203 return window.forget();
michael@0 204 }
michael@0 205
michael@0 206 void
michael@0 207 EventListenerManager::AddEventListenerInternal(
michael@0 208 const EventListenerHolder& aListenerHolder,
michael@0 209 uint32_t aType,
michael@0 210 nsIAtom* aTypeAtom,
michael@0 211 const nsAString& aTypeString,
michael@0 212 const EventListenerFlags& aFlags,
michael@0 213 bool aHandler,
michael@0 214 bool aAllEvents)
michael@0 215 {
michael@0 216 MOZ_ASSERT((NS_IsMainThread() && aType && aTypeAtom) || // Main thread
michael@0 217 (!NS_IsMainThread() && aType && !aTypeString.IsEmpty()) || // non-main-thread
michael@0 218 aAllEvents, "Missing type"); // all-events listener
michael@0 219
michael@0 220 if (!aListenerHolder || mClearingListeners) {
michael@0 221 return;
michael@0 222 }
michael@0 223
michael@0 224 // Since there is no public API to call us with an EventListenerHolder, we
michael@0 225 // know that there's an EventListenerHolder on the stack holding a strong ref
michael@0 226 // to the listener.
michael@0 227
michael@0 228 Listener* listener;
michael@0 229 uint32_t count = mListeners.Length();
michael@0 230 for (uint32_t i = 0; i < count; i++) {
michael@0 231 listener = &mListeners.ElementAt(i);
michael@0 232 // mListener == aListenerHolder is the last one, since it can be a bit slow.
michael@0 233 if (listener->mListenerIsHandler == aHandler &&
michael@0 234 listener->mFlags == aFlags &&
michael@0 235 EVENT_TYPE_EQUALS(listener, aType, aTypeAtom, aTypeString,
michael@0 236 aAllEvents) &&
michael@0 237 listener->mListener == aListenerHolder) {
michael@0 238 return;
michael@0 239 }
michael@0 240 }
michael@0 241
michael@0 242 mNoListenerForEvent = NS_EVENT_NULL;
michael@0 243 mNoListenerForEventAtom = nullptr;
michael@0 244
michael@0 245 listener = aAllEvents ? mListeners.InsertElementAt(0) :
michael@0 246 mListeners.AppendElement();
michael@0 247 listener->mListener = aListenerHolder;
michael@0 248 MOZ_ASSERT(aType < PR_UINT16_MAX);
michael@0 249 listener->mEventType = aType;
michael@0 250 listener->mTypeString = aTypeString;
michael@0 251 listener->mTypeAtom = aTypeAtom;
michael@0 252 listener->mFlags = aFlags;
michael@0 253 listener->mListenerIsHandler = aHandler;
michael@0 254 listener->mHandlerIsString = false;
michael@0 255 listener->mAllEvents = aAllEvents;
michael@0 256
michael@0 257 // Detect the type of event listener.
michael@0 258 nsCOMPtr<nsIXPConnectWrappedJS> wjs;
michael@0 259 if (aFlags.mListenerIsJSListener) {
michael@0 260 MOZ_ASSERT(!aListenerHolder.HasWebIDLCallback());
michael@0 261 listener->mListenerType = Listener::eJSEventListener;
michael@0 262 } else if (aListenerHolder.HasWebIDLCallback()) {
michael@0 263 listener->mListenerType = Listener::eWebIDLListener;
michael@0 264 } else if ((wjs = do_QueryInterface(aListenerHolder.GetXPCOMCallback()))) {
michael@0 265 listener->mListenerType = Listener::eWrappedJSListener;
michael@0 266 } else {
michael@0 267 listener->mListenerType = Listener::eNativeListener;
michael@0 268 }
michael@0 269
michael@0 270
michael@0 271 if (aFlags.mInSystemGroup) {
michael@0 272 mMayHaveSystemGroupListeners = true;
michael@0 273 }
michael@0 274 if (aFlags.mCapture) {
michael@0 275 mMayHaveCapturingListeners = true;
michael@0 276 }
michael@0 277
michael@0 278 if (aType == NS_AFTERPAINT) {
michael@0 279 mMayHavePaintEventListener = true;
michael@0 280 nsPIDOMWindow* window = GetInnerWindowForTarget();
michael@0 281 if (window) {
michael@0 282 window->SetHasPaintEventListeners();
michael@0 283 }
michael@0 284 } else if (aType >= NS_MUTATION_START && aType <= NS_MUTATION_END) {
michael@0 285 // For mutation listeners, we need to update the global bit on the DOM window.
michael@0 286 // Otherwise we won't actually fire the mutation event.
michael@0 287 mMayHaveMutationListeners = true;
michael@0 288 // Go from our target to the nearest enclosing DOM window.
michael@0 289 nsPIDOMWindow* window = GetInnerWindowForTarget();
michael@0 290 if (window) {
michael@0 291 nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
michael@0 292 if (doc) {
michael@0 293 doc->WarnOnceAbout(nsIDocument::eMutationEvent);
michael@0 294 }
michael@0 295 // If aType is NS_MUTATION_SUBTREEMODIFIED, we need to listen all
michael@0 296 // mutations. nsContentUtils::HasMutationListeners relies on this.
michael@0 297 window->SetMutationListeners((aType == NS_MUTATION_SUBTREEMODIFIED) ?
michael@0 298 kAllMutationBits :
michael@0 299 MutationBitForEventType(aType));
michael@0 300 }
michael@0 301 } else if (aTypeAtom == nsGkAtoms::ondeviceorientation) {
michael@0 302 EnableDevice(NS_DEVICE_ORIENTATION);
michael@0 303 } else if (aTypeAtom == nsGkAtoms::ondeviceproximity || aTypeAtom == nsGkAtoms::onuserproximity) {
michael@0 304 EnableDevice(NS_DEVICE_PROXIMITY);
michael@0 305 } else if (aTypeAtom == nsGkAtoms::ondevicelight) {
michael@0 306 EnableDevice(NS_DEVICE_LIGHT);
michael@0 307 } else if (aTypeAtom == nsGkAtoms::ondevicemotion) {
michael@0 308 EnableDevice(NS_DEVICE_MOTION);
michael@0 309 #ifdef MOZ_B2G
michael@0 310 } else if (aTypeAtom == nsGkAtoms::onmoztimechange) {
michael@0 311 nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
michael@0 312 if (window) {
michael@0 313 window->EnableTimeChangeNotifications();
michael@0 314 }
michael@0 315 } else if (aTypeAtom == nsGkAtoms::onmoznetworkupload) {
michael@0 316 nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
michael@0 317 if (window) {
michael@0 318 window->EnableNetworkEvent(NS_NETWORK_UPLOAD_EVENT);
michael@0 319 }
michael@0 320 } else if (aTypeAtom == nsGkAtoms::onmoznetworkdownload) {
michael@0 321 nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
michael@0 322 if (window) {
michael@0 323 window->EnableNetworkEvent(NS_NETWORK_DOWNLOAD_EVENT);
michael@0 324 }
michael@0 325 #endif // MOZ_B2G
michael@0 326 } else if (aTypeAtom == nsGkAtoms::ontouchstart ||
michael@0 327 aTypeAtom == nsGkAtoms::ontouchend ||
michael@0 328 aTypeAtom == nsGkAtoms::ontouchmove ||
michael@0 329 aTypeAtom == nsGkAtoms::ontouchenter ||
michael@0 330 aTypeAtom == nsGkAtoms::ontouchleave ||
michael@0 331 aTypeAtom == nsGkAtoms::ontouchcancel) {
michael@0 332 mMayHaveTouchEventListener = true;
michael@0 333 nsPIDOMWindow* window = GetInnerWindowForTarget();
michael@0 334 // we don't want touchevent listeners added by scrollbars to flip this flag
michael@0 335 // so we ignore listeners created with system event flag
michael@0 336 if (window && !aFlags.mInSystemGroup) {
michael@0 337 window->SetHasTouchEventListeners();
michael@0 338 }
michael@0 339 } else if (aType >= NS_POINTER_EVENT_START && aType <= NS_POINTER_LOST_CAPTURE) {
michael@0 340 nsPIDOMWindow* window = GetInnerWindowForTarget();
michael@0 341 if (aTypeAtom == nsGkAtoms::onpointerenter ||
michael@0 342 aTypeAtom == nsGkAtoms::onpointerleave) {
michael@0 343 mMayHavePointerEnterLeaveEventListener = true;
michael@0 344 if (window) {
michael@0 345 #ifdef DEBUG
michael@0 346 nsCOMPtr<nsIDocument> d = window->GetExtantDoc();
michael@0 347 NS_WARN_IF_FALSE(!nsContentUtils::IsChromeDoc(d),
michael@0 348 "Please do not use pointerenter/leave events in chrome. "
michael@0 349 "They are slower than pointerover/out!");
michael@0 350 #endif
michael@0 351 window->SetHasPointerEnterLeaveEventListeners();
michael@0 352 }
michael@0 353 }
michael@0 354 } else if (aTypeAtom == nsGkAtoms::onmouseenter ||
michael@0 355 aTypeAtom == nsGkAtoms::onmouseleave) {
michael@0 356 mMayHaveMouseEnterLeaveEventListener = true;
michael@0 357 nsPIDOMWindow* window = GetInnerWindowForTarget();
michael@0 358 if (window) {
michael@0 359 #ifdef DEBUG
michael@0 360 nsCOMPtr<nsIDocument> d = window->GetExtantDoc();
michael@0 361 NS_WARN_IF_FALSE(!nsContentUtils::IsChromeDoc(d),
michael@0 362 "Please do not use mouseenter/leave events in chrome. "
michael@0 363 "They are slower than mouseover/out!");
michael@0 364 #endif
michael@0 365 window->SetHasMouseEnterLeaveEventListeners();
michael@0 366 }
michael@0 367 #ifdef MOZ_GAMEPAD
michael@0 368 } else if (aType >= NS_GAMEPAD_START &&
michael@0 369 aType <= NS_GAMEPAD_END) {
michael@0 370 nsPIDOMWindow* window = GetInnerWindowForTarget();
michael@0 371 if (window) {
michael@0 372 window->SetHasGamepadEventListener();
michael@0 373 }
michael@0 374 #endif
michael@0 375 }
michael@0 376 if (aTypeAtom && mTarget) {
michael@0 377 mTarget->EventListenerAdded(aTypeAtom);
michael@0 378 }
michael@0 379 }
michael@0 380
michael@0 381 bool
michael@0 382 EventListenerManager::IsDeviceType(uint32_t aType)
michael@0 383 {
michael@0 384 switch (aType) {
michael@0 385 case NS_DEVICE_ORIENTATION:
michael@0 386 case NS_DEVICE_MOTION:
michael@0 387 case NS_DEVICE_LIGHT:
michael@0 388 case NS_DEVICE_PROXIMITY:
michael@0 389 case NS_USER_PROXIMITY:
michael@0 390 return true;
michael@0 391 default:
michael@0 392 break;
michael@0 393 }
michael@0 394 return false;
michael@0 395 }
michael@0 396
michael@0 397 void
michael@0 398 EventListenerManager::EnableDevice(uint32_t aType)
michael@0 399 {
michael@0 400 nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
michael@0 401 if (!window) {
michael@0 402 return;
michael@0 403 }
michael@0 404
michael@0 405 switch (aType) {
michael@0 406 case NS_DEVICE_ORIENTATION:
michael@0 407 window->EnableDeviceSensor(SENSOR_ORIENTATION);
michael@0 408 break;
michael@0 409 case NS_DEVICE_PROXIMITY:
michael@0 410 case NS_USER_PROXIMITY:
michael@0 411 window->EnableDeviceSensor(SENSOR_PROXIMITY);
michael@0 412 break;
michael@0 413 case NS_DEVICE_LIGHT:
michael@0 414 window->EnableDeviceSensor(SENSOR_LIGHT);
michael@0 415 break;
michael@0 416 case NS_DEVICE_MOTION:
michael@0 417 window->EnableDeviceSensor(SENSOR_ACCELERATION);
michael@0 418 window->EnableDeviceSensor(SENSOR_LINEAR_ACCELERATION);
michael@0 419 window->EnableDeviceSensor(SENSOR_GYROSCOPE);
michael@0 420 break;
michael@0 421 default:
michael@0 422 NS_WARNING("Enabling an unknown device sensor.");
michael@0 423 break;
michael@0 424 }
michael@0 425 }
michael@0 426
michael@0 427 void
michael@0 428 EventListenerManager::DisableDevice(uint32_t aType)
michael@0 429 {
michael@0 430 nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
michael@0 431 if (!window) {
michael@0 432 return;
michael@0 433 }
michael@0 434
michael@0 435 switch (aType) {
michael@0 436 case NS_DEVICE_ORIENTATION:
michael@0 437 window->DisableDeviceSensor(SENSOR_ORIENTATION);
michael@0 438 break;
michael@0 439 case NS_DEVICE_MOTION:
michael@0 440 window->DisableDeviceSensor(SENSOR_ACCELERATION);
michael@0 441 window->DisableDeviceSensor(SENSOR_LINEAR_ACCELERATION);
michael@0 442 window->DisableDeviceSensor(SENSOR_GYROSCOPE);
michael@0 443 break;
michael@0 444 case NS_DEVICE_PROXIMITY:
michael@0 445 case NS_USER_PROXIMITY:
michael@0 446 window->DisableDeviceSensor(SENSOR_PROXIMITY);
michael@0 447 break;
michael@0 448 case NS_DEVICE_LIGHT:
michael@0 449 window->DisableDeviceSensor(SENSOR_LIGHT);
michael@0 450 break;
michael@0 451 default:
michael@0 452 NS_WARNING("Disabling an unknown device sensor.");
michael@0 453 break;
michael@0 454 }
michael@0 455 }
michael@0 456
michael@0 457 void
michael@0 458 EventListenerManager::RemoveEventListenerInternal(
michael@0 459 const EventListenerHolder& aListenerHolder,
michael@0 460 uint32_t aType,
michael@0 461 nsIAtom* aUserType,
michael@0 462 const nsAString& aTypeString,
michael@0 463 const EventListenerFlags& aFlags,
michael@0 464 bool aAllEvents)
michael@0 465 {
michael@0 466 if (!aListenerHolder || !aType || mClearingListeners) {
michael@0 467 return;
michael@0 468 }
michael@0 469
michael@0 470 Listener* listener;
michael@0 471
michael@0 472 uint32_t count = mListeners.Length();
michael@0 473 uint32_t typeCount = 0;
michael@0 474 bool deviceType = IsDeviceType(aType);
michael@0 475 #ifdef MOZ_B2G
michael@0 476 bool timeChangeEvent = (aType == NS_MOZ_TIME_CHANGE_EVENT);
michael@0 477 bool networkEvent = (aType == NS_NETWORK_UPLOAD_EVENT ||
michael@0 478 aType == NS_NETWORK_DOWNLOAD_EVENT);
michael@0 479 #endif // MOZ_B2G
michael@0 480
michael@0 481 for (uint32_t i = 0; i < count; ++i) {
michael@0 482 listener = &mListeners.ElementAt(i);
michael@0 483 if (EVENT_TYPE_EQUALS(listener, aType, aUserType, aTypeString,
michael@0 484 aAllEvents)) {
michael@0 485 ++typeCount;
michael@0 486 if (listener->mListener == aListenerHolder &&
michael@0 487 listener->mFlags.EqualsIgnoringTrustness(aFlags)) {
michael@0 488 nsRefPtr<EventListenerManager> kungFuDeathGrip(this);
michael@0 489 mListeners.RemoveElementAt(i);
michael@0 490 --count;
michael@0 491 mNoListenerForEvent = NS_EVENT_NULL;
michael@0 492 mNoListenerForEventAtom = nullptr;
michael@0 493 if (mTarget && aUserType) {
michael@0 494 mTarget->EventListenerRemoved(aUserType);
michael@0 495 }
michael@0 496
michael@0 497 if (!deviceType
michael@0 498 #ifdef MOZ_B2G
michael@0 499 && !timeChangeEvent && !networkEvent
michael@0 500 #endif // MOZ_B2G
michael@0 501 ) {
michael@0 502 return;
michael@0 503 }
michael@0 504 --typeCount;
michael@0 505 }
michael@0 506 }
michael@0 507 }
michael@0 508
michael@0 509 if (!aAllEvents && deviceType && typeCount == 0) {
michael@0 510 DisableDevice(aType);
michael@0 511 #ifdef MOZ_B2G
michael@0 512 } else if (timeChangeEvent && typeCount == 0) {
michael@0 513 nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
michael@0 514 if (window) {
michael@0 515 window->DisableTimeChangeNotifications();
michael@0 516 }
michael@0 517 } else if (!aAllEvents && networkEvent && typeCount == 0) {
michael@0 518 nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
michael@0 519 if (window) {
michael@0 520 window->DisableNetworkEvent(aType);
michael@0 521 }
michael@0 522 #endif // MOZ_B2G
michael@0 523 }
michael@0 524 }
michael@0 525
michael@0 526 bool
michael@0 527 EventListenerManager::ListenerCanHandle(Listener* aListener,
michael@0 528 WidgetEvent* aEvent)
michael@0 529 {
michael@0 530 // This is slightly different from EVENT_TYPE_EQUALS in that it returns
michael@0 531 // true even when aEvent->message == NS_USER_DEFINED_EVENT and
michael@0 532 // aListener=>mEventType != NS_USER_DEFINED_EVENT as long as the atoms are
michael@0 533 // the same
michael@0 534 if (aListener->mAllEvents) {
michael@0 535 return true;
michael@0 536 }
michael@0 537 if (aEvent->message == NS_USER_DEFINED_EVENT) {
michael@0 538 if (mIsMainThreadELM) {
michael@0 539 return aListener->mTypeAtom == aEvent->userType;
michael@0 540 }
michael@0 541 return aListener->mTypeString.Equals(aEvent->typeString);
michael@0 542 }
michael@0 543 MOZ_ASSERT(mIsMainThreadELM);
michael@0 544 return aListener->mEventType == aEvent->message;
michael@0 545 }
michael@0 546
michael@0 547 void
michael@0 548 EventListenerManager::AddEventListenerByType(
michael@0 549 const EventListenerHolder& aListenerHolder,
michael@0 550 const nsAString& aType,
michael@0 551 const EventListenerFlags& aFlags)
michael@0 552 {
michael@0 553 nsCOMPtr<nsIAtom> atom =
michael@0 554 mIsMainThreadELM ? do_GetAtom(NS_LITERAL_STRING("on") + aType) : nullptr;
michael@0 555 uint32_t type = nsContentUtils::GetEventId(atom);
michael@0 556 AddEventListenerInternal(aListenerHolder, type, atom, aType, aFlags);
michael@0 557 }
michael@0 558
michael@0 559 void
michael@0 560 EventListenerManager::RemoveEventListenerByType(
michael@0 561 const EventListenerHolder& aListenerHolder,
michael@0 562 const nsAString& aType,
michael@0 563 const EventListenerFlags& aFlags)
michael@0 564 {
michael@0 565 nsCOMPtr<nsIAtom> atom =
michael@0 566 mIsMainThreadELM ? do_GetAtom(NS_LITERAL_STRING("on") + aType) : nullptr;
michael@0 567 uint32_t type = nsContentUtils::GetEventId(atom);
michael@0 568 RemoveEventListenerInternal(aListenerHolder, type, atom, aType, aFlags);
michael@0 569 }
michael@0 570
michael@0 571 EventListenerManager::Listener*
michael@0 572 EventListenerManager::FindEventHandler(uint32_t aEventType,
michael@0 573 nsIAtom* aTypeAtom,
michael@0 574 const nsAString& aTypeString)
michael@0 575 {
michael@0 576 // Run through the listeners for this type and see if a script
michael@0 577 // listener is registered
michael@0 578 Listener* listener;
michael@0 579 uint32_t count = mListeners.Length();
michael@0 580 for (uint32_t i = 0; i < count; ++i) {
michael@0 581 listener = &mListeners.ElementAt(i);
michael@0 582 if (listener->mListenerIsHandler &&
michael@0 583 EVENT_TYPE_EQUALS(listener, aEventType, aTypeAtom, aTypeString,
michael@0 584 false)) {
michael@0 585 return listener;
michael@0 586 }
michael@0 587 }
michael@0 588 return nullptr;
michael@0 589 }
michael@0 590
michael@0 591 EventListenerManager::Listener*
michael@0 592 EventListenerManager::SetEventHandlerInternal(
michael@0 593 nsIAtom* aName,
michael@0 594 const nsAString& aTypeString,
michael@0 595 const TypedEventHandler& aTypedHandler,
michael@0 596 bool aPermitUntrustedEvents)
michael@0 597 {
michael@0 598 MOZ_ASSERT(aName || !aTypeString.IsEmpty());
michael@0 599
michael@0 600 uint32_t eventType = nsContentUtils::GetEventId(aName);
michael@0 601 Listener* listener = FindEventHandler(eventType, aName, aTypeString);
michael@0 602
michael@0 603 if (!listener) {
michael@0 604 // If we didn't find a script listener or no listeners existed
michael@0 605 // create and add a new one.
michael@0 606 EventListenerFlags flags;
michael@0 607 flags.mListenerIsJSListener = true;
michael@0 608
michael@0 609 nsCOMPtr<JSEventHandler> jsEventHandler;
michael@0 610 NS_NewJSEventHandler(mTarget, aName,
michael@0 611 aTypedHandler, getter_AddRefs(jsEventHandler));
michael@0 612 EventListenerHolder listenerHolder(jsEventHandler);
michael@0 613 AddEventListenerInternal(listenerHolder, eventType, aName, aTypeString,
michael@0 614 flags, true);
michael@0 615
michael@0 616 listener = FindEventHandler(eventType, aName, aTypeString);
michael@0 617 } else {
michael@0 618 JSEventHandler* jsEventHandler = listener->GetJSEventHandler();
michael@0 619 MOZ_ASSERT(jsEventHandler,
michael@0 620 "How can we have an event handler with no JSEventHandler?");
michael@0 621
michael@0 622 bool same = jsEventHandler->GetTypedEventHandler() == aTypedHandler;
michael@0 623 // Possibly the same listener, but update still the context and scope.
michael@0 624 jsEventHandler->SetHandler(aTypedHandler);
michael@0 625 if (mTarget && !same && aName) {
michael@0 626 mTarget->EventListenerRemoved(aName);
michael@0 627 mTarget->EventListenerAdded(aName);
michael@0 628 }
michael@0 629 }
michael@0 630
michael@0 631 // Set flag to indicate possible need for compilation later
michael@0 632 listener->mHandlerIsString = !aTypedHandler.HasEventHandler();
michael@0 633 if (aPermitUntrustedEvents) {
michael@0 634 listener->mFlags.mAllowUntrustedEvents = true;
michael@0 635 }
michael@0 636
michael@0 637 return listener;
michael@0 638 }
michael@0 639
michael@0 640 nsresult
michael@0 641 EventListenerManager::SetEventHandler(nsIAtom* aName,
michael@0 642 const nsAString& aBody,
michael@0 643 uint32_t aLanguage,
michael@0 644 bool aDeferCompilation,
michael@0 645 bool aPermitUntrustedEvents,
michael@0 646 Element* aElement)
michael@0 647 {
michael@0 648 NS_PRECONDITION(aLanguage != nsIProgrammingLanguage::UNKNOWN,
michael@0 649 "Must know the language for the script event listener");
michael@0 650
michael@0 651 // |aPermitUntrustedEvents| is set to False for chrome - events
michael@0 652 // *generated* from an unknown source are not allowed.
michael@0 653 // However, for script languages with no 'sandbox', we want to reject
michael@0 654 // such scripts based on the source of their code, not just the source
michael@0 655 // of the event.
michael@0 656 if (aPermitUntrustedEvents &&
michael@0 657 aLanguage != nsIProgrammingLanguage::JAVASCRIPT) {
michael@0 658 NS_WARNING("Discarding non-JS event listener from untrusted source");
michael@0 659 return NS_ERROR_FAILURE;
michael@0 660 }
michael@0 661
michael@0 662 nsCOMPtr<nsIDocument> doc;
michael@0 663 nsCOMPtr<nsIScriptGlobalObject> global =
michael@0 664 GetScriptGlobalAndDocument(getter_AddRefs(doc));
michael@0 665
michael@0 666 if (!global) {
michael@0 667 // This can happen; for example this document might have been
michael@0 668 // loaded as data.
michael@0 669 return NS_OK;
michael@0 670 }
michael@0 671
michael@0 672 #ifdef DEBUG
michael@0 673 nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(global);
michael@0 674 if (win) {
michael@0 675 MOZ_ASSERT(win->IsInnerWindow(), "We should not have an outer window here!");
michael@0 676 }
michael@0 677 #endif
michael@0 678
michael@0 679 nsresult rv = NS_OK;
michael@0 680 // return early preventing the event listener from being added
michael@0 681 // 'doc' is fetched above
michael@0 682 if (doc) {
michael@0 683 // Don't allow adding an event listener if the document is sandboxed
michael@0 684 // without 'allow-scripts'.
michael@0 685 if (doc->GetSandboxFlags() & SANDBOXED_SCRIPTS) {
michael@0 686 return NS_ERROR_DOM_SECURITY_ERR;
michael@0 687 }
michael@0 688
michael@0 689 nsCOMPtr<nsIContentSecurityPolicy> csp;
michael@0 690 rv = doc->NodePrincipal()->GetCsp(getter_AddRefs(csp));
michael@0 691 NS_ENSURE_SUCCESS(rv, rv);
michael@0 692
michael@0 693 if (csp) {
michael@0 694 bool inlineOK = true;
michael@0 695 bool reportViolations = false;
michael@0 696 rv = csp->GetAllowsInlineScript(&reportViolations, &inlineOK);
michael@0 697 NS_ENSURE_SUCCESS(rv, rv);
michael@0 698
michael@0 699 if (reportViolations) {
michael@0 700 // gather information to log with violation report
michael@0 701 nsIURI* uri = doc->GetDocumentURI();
michael@0 702 nsAutoCString asciiSpec;
michael@0 703 if (uri)
michael@0 704 uri->GetAsciiSpec(asciiSpec);
michael@0 705 nsAutoString scriptSample, attr, tagName(NS_LITERAL_STRING("UNKNOWN"));
michael@0 706 aName->ToString(attr);
michael@0 707 nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(mTarget));
michael@0 708 if (domNode)
michael@0 709 domNode->GetNodeName(tagName);
michael@0 710 // build a "script sample" based on what we know about this element
michael@0 711 scriptSample.Assign(attr);
michael@0 712 scriptSample.AppendLiteral(" attribute on ");
michael@0 713 scriptSample.Append(tagName);
michael@0 714 scriptSample.AppendLiteral(" element");
michael@0 715 csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_INLINE_SCRIPT,
michael@0 716 NS_ConvertUTF8toUTF16(asciiSpec),
michael@0 717 scriptSample,
michael@0 718 0,
michael@0 719 EmptyString(),
michael@0 720 EmptyString());
michael@0 721 }
michael@0 722
michael@0 723 // return early if CSP wants us to block inline scripts
michael@0 724 if (!inlineOK) {
michael@0 725 return NS_OK;
michael@0 726 }
michael@0 727 }
michael@0 728 }
michael@0 729
michael@0 730 // This might be the first reference to this language in the global
michael@0 731 // We must init the language before we attempt to fetch its context.
michael@0 732 if (NS_FAILED(global->EnsureScriptEnvironment())) {
michael@0 733 NS_WARNING("Failed to setup script environment for this language");
michael@0 734 // but fall through and let the inevitable failure below handle it.
michael@0 735 }
michael@0 736
michael@0 737 nsIScriptContext* context = global->GetScriptContext();
michael@0 738 NS_ENSURE_TRUE(context, NS_ERROR_FAILURE);
michael@0 739 NS_ENSURE_STATE(global->GetGlobalJSObject());
michael@0 740
michael@0 741 Listener* listener = SetEventHandlerInternal(aName,
michael@0 742 EmptyString(),
michael@0 743 TypedEventHandler(),
michael@0 744 aPermitUntrustedEvents);
michael@0 745
michael@0 746 if (!aDeferCompilation) {
michael@0 747 return CompileEventHandlerInternal(listener, &aBody, aElement);
michael@0 748 }
michael@0 749
michael@0 750 return NS_OK;
michael@0 751 }
michael@0 752
michael@0 753 void
michael@0 754 EventListenerManager::RemoveEventHandler(nsIAtom* aName,
michael@0 755 const nsAString& aTypeString)
michael@0 756 {
michael@0 757 if (mClearingListeners) {
michael@0 758 return;
michael@0 759 }
michael@0 760
michael@0 761 uint32_t eventType = nsContentUtils::GetEventId(aName);
michael@0 762 Listener* listener = FindEventHandler(eventType, aName, aTypeString);
michael@0 763
michael@0 764 if (listener) {
michael@0 765 mListeners.RemoveElementAt(uint32_t(listener - &mListeners.ElementAt(0)));
michael@0 766 mNoListenerForEvent = NS_EVENT_NULL;
michael@0 767 mNoListenerForEventAtom = nullptr;
michael@0 768 if (mTarget && aName) {
michael@0 769 mTarget->EventListenerRemoved(aName);
michael@0 770 }
michael@0 771 }
michael@0 772 }
michael@0 773
michael@0 774 nsresult
michael@0 775 EventListenerManager::CompileEventHandlerInternal(Listener* aListener,
michael@0 776 const nsAString* aBody,
michael@0 777 Element* aElement)
michael@0 778 {
michael@0 779 MOZ_ASSERT(aListener->GetJSEventHandler());
michael@0 780 MOZ_ASSERT(aListener->mHandlerIsString, "Why are we compiling a non-string JS listener?");
michael@0 781 JSEventHandler* jsEventHandler = aListener->GetJSEventHandler();
michael@0 782 MOZ_ASSERT(!jsEventHandler->GetTypedEventHandler().HasEventHandler(),
michael@0 783 "What is there to compile?");
michael@0 784
michael@0 785 nsresult result = NS_OK;
michael@0 786 nsCOMPtr<nsIDocument> doc;
michael@0 787 nsCOMPtr<nsIScriptGlobalObject> global =
michael@0 788 GetScriptGlobalAndDocument(getter_AddRefs(doc));
michael@0 789 NS_ENSURE_STATE(global);
michael@0 790
michael@0 791 nsIScriptContext* context = global->GetScriptContext();
michael@0 792 NS_ENSURE_STATE(context);
michael@0 793
michael@0 794 // Activate JSAPI, and make sure that exceptions are reported on the right
michael@0 795 // Window.
michael@0 796 AutoJSAPIWithErrorsReportedToWindow jsapi(context);
michael@0 797 JSContext* cx = jsapi.cx();
michael@0 798
michael@0 799 nsCOMPtr<nsIAtom> typeAtom = aListener->mTypeAtom;
michael@0 800 nsIAtom* attrName = typeAtom;
michael@0 801
michael@0 802 // Flag us as not a string so we don't keep trying to compile strings which
michael@0 803 // can't be compiled.
michael@0 804 aListener->mHandlerIsString = false;
michael@0 805
michael@0 806 // mTarget may not be an Element if it's a window and we're
michael@0 807 // getting an inline event listener forwarded from <html:body> or
michael@0 808 // <html:frameset> or <xul:window> or the like.
michael@0 809 // XXX I don't like that we have to reference content from
michael@0 810 // here. The alternative is to store the event handler string on
michael@0 811 // the JSEventHandler itself, and that still doesn't address
michael@0 812 // the arg names issue.
michael@0 813 nsCOMPtr<Element> element = do_QueryInterface(mTarget);
michael@0 814 MOZ_ASSERT(element || aBody, "Where will we get our body?");
michael@0 815 nsAutoString handlerBody;
michael@0 816 const nsAString* body = aBody;
michael@0 817 if (!aBody) {
michael@0 818 if (aListener->mTypeAtom == nsGkAtoms::onSVGLoad) {
michael@0 819 attrName = nsGkAtoms::onload;
michael@0 820 } else if (aListener->mTypeAtom == nsGkAtoms::onSVGUnload) {
michael@0 821 attrName = nsGkAtoms::onunload;
michael@0 822 } else if (aListener->mTypeAtom == nsGkAtoms::onSVGResize) {
michael@0 823 attrName = nsGkAtoms::onresize;
michael@0 824 } else if (aListener->mTypeAtom == nsGkAtoms::onSVGScroll) {
michael@0 825 attrName = nsGkAtoms::onscroll;
michael@0 826 } else if (aListener->mTypeAtom == nsGkAtoms::onSVGZoom) {
michael@0 827 attrName = nsGkAtoms::onzoom;
michael@0 828 } else if (aListener->mTypeAtom == nsGkAtoms::onbeginEvent) {
michael@0 829 attrName = nsGkAtoms::onbegin;
michael@0 830 } else if (aListener->mTypeAtom == nsGkAtoms::onrepeatEvent) {
michael@0 831 attrName = nsGkAtoms::onrepeat;
michael@0 832 } else if (aListener->mTypeAtom == nsGkAtoms::onendEvent) {
michael@0 833 attrName = nsGkAtoms::onend;
michael@0 834 }
michael@0 835
michael@0 836 element->GetAttr(kNameSpaceID_None, attrName, handlerBody);
michael@0 837 body = &handlerBody;
michael@0 838 aElement = element;
michael@0 839 }
michael@0 840 aListener = nullptr;
michael@0 841
michael@0 842 uint32_t lineNo = 0;
michael@0 843 nsAutoCString url (NS_LITERAL_CSTRING("-moz-evil:lying-event-listener"));
michael@0 844 MOZ_ASSERT(body);
michael@0 845 MOZ_ASSERT(aElement);
michael@0 846 nsIURI *uri = aElement->OwnerDoc()->GetDocumentURI();
michael@0 847 if (uri) {
michael@0 848 uri->GetSpec(url);
michael@0 849 lineNo = 1;
michael@0 850 }
michael@0 851
michael@0 852 uint32_t argCount;
michael@0 853 const char **argNames;
michael@0 854 nsContentUtils::GetEventArgNames(aElement->GetNameSpaceID(),
michael@0 855 typeAtom,
michael@0 856 &argCount, &argNames);
michael@0 857
michael@0 858 // Wrap the event target, so that we can use it as the scope for the event
michael@0 859 // handler. Note that mTarget is different from aElement in the <body> case,
michael@0 860 // where mTarget is a Window.
michael@0 861 //
michael@0 862 // The wrapScope doesn't really matter here, because the target will create
michael@0 863 // its reflector in the proper scope, and then we'll enter that compartment.
michael@0 864 JS::Rooted<JSObject*> wrapScope(cx, context->GetWindowProxy());
michael@0 865 JS::Rooted<JS::Value> v(cx);
michael@0 866 {
michael@0 867 JSAutoCompartment ac(cx, wrapScope);
michael@0 868 nsresult rv = nsContentUtils::WrapNative(cx, mTarget, &v,
michael@0 869 /* aAllowWrapping = */ false);
michael@0 870 if (NS_WARN_IF(NS_FAILED(rv))) {
michael@0 871 return rv;
michael@0 872 }
michael@0 873 }
michael@0 874 JS::Rooted<JSObject*> target(cx, &v.toObject());
michael@0 875 JSAutoCompartment ac(cx, target);
michael@0 876
michael@0 877 nsDependentAtomString str(attrName);
michael@0 878 // Most of our names are short enough that we don't even have to malloc
michael@0 879 // the JS string stuff, so don't worry about playing games with
michael@0 880 // refcounting XPCOM stringbuffers.
michael@0 881 JS::Rooted<JSString*> jsStr(cx, JS_NewUCStringCopyN(cx,
michael@0 882 str.BeginReading(),
michael@0 883 str.Length()));
michael@0 884 NS_ENSURE_TRUE(jsStr, NS_ERROR_OUT_OF_MEMORY);
michael@0 885
michael@0 886 // Get the reflector for |aElement|, so that we can pass to setElement.
michael@0 887 if (NS_WARN_IF(!WrapNewBindingObject(cx, target, aElement, &v))) {
michael@0 888 return NS_ERROR_FAILURE;
michael@0 889 }
michael@0 890 JS::CompileOptions options(cx);
michael@0 891 options.setIntroductionType("eventHandler")
michael@0 892 .setFileAndLine(url.get(), lineNo)
michael@0 893 .setVersion(SCRIPTVERSION_DEFAULT)
michael@0 894 .setElement(&v.toObject())
michael@0 895 .setElementAttributeName(jsStr)
michael@0 896 .setDefineOnScope(false);
michael@0 897
michael@0 898 JS::Rooted<JSObject*> handler(cx);
michael@0 899 result = nsJSUtils::CompileFunction(cx, target, options,
michael@0 900 nsAtomCString(typeAtom),
michael@0 901 argCount, argNames, *body, handler.address());
michael@0 902 NS_ENSURE_SUCCESS(result, result);
michael@0 903 NS_ENSURE_TRUE(handler, NS_ERROR_FAILURE);
michael@0 904
michael@0 905 nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(mTarget);
michael@0 906 if (jsEventHandler->EventName() == nsGkAtoms::onerror && win) {
michael@0 907 nsRefPtr<OnErrorEventHandlerNonNull> handlerCallback =
michael@0 908 new OnErrorEventHandlerNonNull(handler, /* aIncumbentGlobal = */ nullptr);
michael@0 909 jsEventHandler->SetHandler(handlerCallback);
michael@0 910 } else if (jsEventHandler->EventName() == nsGkAtoms::onbeforeunload && win) {
michael@0 911 nsRefPtr<OnBeforeUnloadEventHandlerNonNull> handlerCallback =
michael@0 912 new OnBeforeUnloadEventHandlerNonNull(handler, /* aIncumbentGlobal = */ nullptr);
michael@0 913 jsEventHandler->SetHandler(handlerCallback);
michael@0 914 } else {
michael@0 915 nsRefPtr<EventHandlerNonNull> handlerCallback =
michael@0 916 new EventHandlerNonNull(handler, /* aIncumbentGlobal = */ nullptr);
michael@0 917 jsEventHandler->SetHandler(handlerCallback);
michael@0 918 }
michael@0 919
michael@0 920 return result;
michael@0 921 }
michael@0 922
michael@0 923 nsresult
michael@0 924 EventListenerManager::HandleEventSubType(Listener* aListener,
michael@0 925 nsIDOMEvent* aDOMEvent,
michael@0 926 EventTarget* aCurrentTarget)
michael@0 927 {
michael@0 928 nsresult result = NS_OK;
michael@0 929 EventListenerHolder listenerHolder(aListener->mListener); // strong ref
michael@0 930
michael@0 931 // If this is a script handler and we haven't yet
michael@0 932 // compiled the event handler itself
michael@0 933 if ((aListener->mListenerType == Listener::eJSEventListener) &&
michael@0 934 aListener->mHandlerIsString) {
michael@0 935 result = CompileEventHandlerInternal(aListener, nullptr, nullptr);
michael@0 936 aListener = nullptr;
michael@0 937 }
michael@0 938
michael@0 939 if (NS_SUCCEEDED(result)) {
michael@0 940 if (mIsMainThreadELM) {
michael@0 941 nsContentUtils::EnterMicroTask();
michael@0 942 }
michael@0 943 // nsIDOMEvent::currentTarget is set in EventDispatcher.
michael@0 944 if (listenerHolder.HasWebIDLCallback()) {
michael@0 945 ErrorResult rv;
michael@0 946 listenerHolder.GetWebIDLCallback()->
michael@0 947 HandleEvent(aCurrentTarget, *(aDOMEvent->InternalDOMEvent()), rv);
michael@0 948 result = rv.ErrorCode();
michael@0 949 } else {
michael@0 950 result = listenerHolder.GetXPCOMCallback()->HandleEvent(aDOMEvent);
michael@0 951 }
michael@0 952 if (mIsMainThreadELM) {
michael@0 953 nsContentUtils::LeaveMicroTask();
michael@0 954 }
michael@0 955 }
michael@0 956
michael@0 957 return result;
michael@0 958 }
michael@0 959
michael@0 960 /**
michael@0 961 * Causes a check for event listeners and processing by them if they exist.
michael@0 962 * @param an event listener
michael@0 963 */
michael@0 964
michael@0 965 void
michael@0 966 EventListenerManager::HandleEventInternal(nsPresContext* aPresContext,
michael@0 967 WidgetEvent* aEvent,
michael@0 968 nsIDOMEvent** aDOMEvent,
michael@0 969 EventTarget* aCurrentTarget,
michael@0 970 nsEventStatus* aEventStatus)
michael@0 971 {
michael@0 972 //Set the value of the internal PreventDefault flag properly based on aEventStatus
michael@0 973 if (*aEventStatus == nsEventStatus_eConsumeNoDefault) {
michael@0 974 aEvent->mFlags.mDefaultPrevented = true;
michael@0 975 }
michael@0 976
michael@0 977 nsAutoTObserverArray<Listener, 2>::EndLimitedIterator iter(mListeners);
michael@0 978 Maybe<nsAutoPopupStatePusher> popupStatePusher;
michael@0 979 if (mIsMainThreadELM) {
michael@0 980 popupStatePusher.construct(Event::GetEventPopupControlState(aEvent));
michael@0 981 }
michael@0 982
michael@0 983 bool hasListener = false;
michael@0 984 while (iter.HasMore()) {
michael@0 985 if (aEvent->mFlags.mImmediatePropagationStopped) {
michael@0 986 break;
michael@0 987 }
michael@0 988 Listener* listener = &iter.GetNext();
michael@0 989 // Check that the phase is same in event and event listener.
michael@0 990 // Handle only trusted events, except when listener permits untrusted events.
michael@0 991 if (ListenerCanHandle(listener, aEvent)) {
michael@0 992 hasListener = true;
michael@0 993 if (listener->IsListening(aEvent) &&
michael@0 994 (aEvent->mFlags.mIsTrusted ||
michael@0 995 listener->mFlags.mAllowUntrustedEvents)) {
michael@0 996 if (!*aDOMEvent) {
michael@0 997 // This is tiny bit slow, but happens only once per event.
michael@0 998 nsCOMPtr<EventTarget> et =
michael@0 999 do_QueryInterface(aEvent->originalTarget);
michael@0 1000 EventDispatcher::CreateEvent(et, aPresContext,
michael@0 1001 aEvent, EmptyString(), aDOMEvent);
michael@0 1002 }
michael@0 1003 if (*aDOMEvent) {
michael@0 1004 if (!aEvent->currentTarget) {
michael@0 1005 aEvent->currentTarget = aCurrentTarget->GetTargetForDOMEvent();
michael@0 1006 if (!aEvent->currentTarget) {
michael@0 1007 break;
michael@0 1008 }
michael@0 1009 }
michael@0 1010
michael@0 1011 if (NS_FAILED(HandleEventSubType(listener, *aDOMEvent,
michael@0 1012 aCurrentTarget))) {
michael@0 1013 aEvent->mFlags.mExceptionHasBeenRisen = true;
michael@0 1014 }
michael@0 1015 }
michael@0 1016 }
michael@0 1017 }
michael@0 1018 }
michael@0 1019
michael@0 1020 aEvent->currentTarget = nullptr;
michael@0 1021
michael@0 1022 if (mIsMainThreadELM && !hasListener) {
michael@0 1023 mNoListenerForEvent = aEvent->message;
michael@0 1024 mNoListenerForEventAtom = aEvent->userType;
michael@0 1025 }
michael@0 1026
michael@0 1027 if (aEvent->mFlags.mDefaultPrevented) {
michael@0 1028 *aEventStatus = nsEventStatus_eConsumeNoDefault;
michael@0 1029 }
michael@0 1030 }
michael@0 1031
michael@0 1032 void
michael@0 1033 EventListenerManager::Disconnect()
michael@0 1034 {
michael@0 1035 mTarget = nullptr;
michael@0 1036 RemoveAllListeners();
michael@0 1037 }
michael@0 1038
michael@0 1039 void
michael@0 1040 EventListenerManager::AddEventListener(
michael@0 1041 const nsAString& aType,
michael@0 1042 const EventListenerHolder& aListenerHolder,
michael@0 1043 bool aUseCapture,
michael@0 1044 bool aWantsUntrusted)
michael@0 1045 {
michael@0 1046 EventListenerFlags flags;
michael@0 1047 flags.mCapture = aUseCapture;
michael@0 1048 flags.mAllowUntrustedEvents = aWantsUntrusted;
michael@0 1049 return AddEventListenerByType(aListenerHolder, aType, flags);
michael@0 1050 }
michael@0 1051
michael@0 1052 void
michael@0 1053 EventListenerManager::RemoveEventListener(
michael@0 1054 const nsAString& aType,
michael@0 1055 const EventListenerHolder& aListenerHolder,
michael@0 1056 bool aUseCapture)
michael@0 1057 {
michael@0 1058 EventListenerFlags flags;
michael@0 1059 flags.mCapture = aUseCapture;
michael@0 1060 RemoveEventListenerByType(aListenerHolder, aType, flags);
michael@0 1061 }
michael@0 1062
michael@0 1063 void
michael@0 1064 EventListenerManager::AddListenerForAllEvents(nsIDOMEventListener* aDOMListener,
michael@0 1065 bool aUseCapture,
michael@0 1066 bool aWantsUntrusted,
michael@0 1067 bool aSystemEventGroup)
michael@0 1068 {
michael@0 1069 EventListenerFlags flags;
michael@0 1070 flags.mCapture = aUseCapture;
michael@0 1071 flags.mAllowUntrustedEvents = aWantsUntrusted;
michael@0 1072 flags.mInSystemGroup = aSystemEventGroup;
michael@0 1073 EventListenerHolder listenerHolder(aDOMListener);
michael@0 1074 AddEventListenerInternal(listenerHolder, NS_EVENT_ALL, nullptr, EmptyString(),
michael@0 1075 flags, false, true);
michael@0 1076 }
michael@0 1077
michael@0 1078 void
michael@0 1079 EventListenerManager::RemoveListenerForAllEvents(
michael@0 1080 nsIDOMEventListener* aDOMListener,
michael@0 1081 bool aUseCapture,
michael@0 1082 bool aSystemEventGroup)
michael@0 1083 {
michael@0 1084 EventListenerFlags flags;
michael@0 1085 flags.mCapture = aUseCapture;
michael@0 1086 flags.mInSystemGroup = aSystemEventGroup;
michael@0 1087 EventListenerHolder listenerHolder(aDOMListener);
michael@0 1088 RemoveEventListenerInternal(listenerHolder, NS_EVENT_ALL, nullptr,
michael@0 1089 EmptyString(), flags, true);
michael@0 1090 }
michael@0 1091
michael@0 1092 bool
michael@0 1093 EventListenerManager::HasMutationListeners()
michael@0 1094 {
michael@0 1095 if (mMayHaveMutationListeners) {
michael@0 1096 uint32_t count = mListeners.Length();
michael@0 1097 for (uint32_t i = 0; i < count; ++i) {
michael@0 1098 Listener* listener = &mListeners.ElementAt(i);
michael@0 1099 if (listener->mEventType >= NS_MUTATION_START &&
michael@0 1100 listener->mEventType <= NS_MUTATION_END) {
michael@0 1101 return true;
michael@0 1102 }
michael@0 1103 }
michael@0 1104 }
michael@0 1105
michael@0 1106 return false;
michael@0 1107 }
michael@0 1108
michael@0 1109 uint32_t
michael@0 1110 EventListenerManager::MutationListenerBits()
michael@0 1111 {
michael@0 1112 uint32_t bits = 0;
michael@0 1113 if (mMayHaveMutationListeners) {
michael@0 1114 uint32_t count = mListeners.Length();
michael@0 1115 for (uint32_t i = 0; i < count; ++i) {
michael@0 1116 Listener* listener = &mListeners.ElementAt(i);
michael@0 1117 if (listener->mEventType >= NS_MUTATION_START &&
michael@0 1118 listener->mEventType <= NS_MUTATION_END) {
michael@0 1119 if (listener->mEventType == NS_MUTATION_SUBTREEMODIFIED) {
michael@0 1120 return kAllMutationBits;
michael@0 1121 }
michael@0 1122 bits |= MutationBitForEventType(listener->mEventType);
michael@0 1123 }
michael@0 1124 }
michael@0 1125 }
michael@0 1126 return bits;
michael@0 1127 }
michael@0 1128
michael@0 1129 bool
michael@0 1130 EventListenerManager::HasListenersFor(const nsAString& aEventName)
michael@0 1131 {
michael@0 1132 nsCOMPtr<nsIAtom> atom = do_GetAtom(NS_LITERAL_STRING("on") + aEventName);
michael@0 1133 return HasListenersFor(atom);
michael@0 1134 }
michael@0 1135
michael@0 1136 bool
michael@0 1137 EventListenerManager::HasListenersFor(nsIAtom* aEventNameWithOn)
michael@0 1138 {
michael@0 1139 #ifdef DEBUG
michael@0 1140 nsAutoString name;
michael@0 1141 aEventNameWithOn->ToString(name);
michael@0 1142 #endif
michael@0 1143 NS_ASSERTION(StringBeginsWith(name, NS_LITERAL_STRING("on")),
michael@0 1144 "Event name does not start with 'on'");
michael@0 1145 uint32_t count = mListeners.Length();
michael@0 1146 for (uint32_t i = 0; i < count; ++i) {
michael@0 1147 Listener* listener = &mListeners.ElementAt(i);
michael@0 1148 if (listener->mTypeAtom == aEventNameWithOn) {
michael@0 1149 return true;
michael@0 1150 }
michael@0 1151 }
michael@0 1152 return false;
michael@0 1153 }
michael@0 1154
michael@0 1155 bool
michael@0 1156 EventListenerManager::HasListeners()
michael@0 1157 {
michael@0 1158 return !mListeners.IsEmpty();
michael@0 1159 }
michael@0 1160
michael@0 1161 nsresult
michael@0 1162 EventListenerManager::GetListenerInfo(nsCOMArray<nsIEventListenerInfo>* aList)
michael@0 1163 {
michael@0 1164 nsCOMPtr<EventTarget> target = do_QueryInterface(mTarget);
michael@0 1165 NS_ENSURE_STATE(target);
michael@0 1166 aList->Clear();
michael@0 1167 uint32_t count = mListeners.Length();
michael@0 1168 for (uint32_t i = 0; i < count; ++i) {
michael@0 1169 const Listener& listener = mListeners.ElementAt(i);
michael@0 1170 // If this is a script handler and we haven't yet
michael@0 1171 // compiled the event handler itself go ahead and compile it
michael@0 1172 if (listener.mListenerType == Listener::eJSEventListener &&
michael@0 1173 listener.mHandlerIsString) {
michael@0 1174 CompileEventHandlerInternal(const_cast<Listener*>(&listener), nullptr,
michael@0 1175 nullptr);
michael@0 1176 }
michael@0 1177 nsAutoString eventType;
michael@0 1178 if (listener.mAllEvents) {
michael@0 1179 eventType.SetIsVoid(true);
michael@0 1180 } else {
michael@0 1181 eventType.Assign(Substring(nsDependentAtomString(listener.mTypeAtom), 2));
michael@0 1182 }
michael@0 1183 // EventListenerInfo is defined in XPCOM, so we have to go ahead
michael@0 1184 // and convert to an XPCOM callback here...
michael@0 1185 nsRefPtr<EventListenerInfo> info =
michael@0 1186 new EventListenerInfo(eventType, listener.mListener.ToXPCOMCallback(),
michael@0 1187 listener.mFlags.mCapture,
michael@0 1188 listener.mFlags.mAllowUntrustedEvents,
michael@0 1189 listener.mFlags.mInSystemGroup);
michael@0 1190 aList->AppendObject(info);
michael@0 1191 }
michael@0 1192 return NS_OK;
michael@0 1193 }
michael@0 1194
michael@0 1195 bool
michael@0 1196 EventListenerManager::HasUnloadListeners()
michael@0 1197 {
michael@0 1198 uint32_t count = mListeners.Length();
michael@0 1199 for (uint32_t i = 0; i < count; ++i) {
michael@0 1200 Listener* listener = &mListeners.ElementAt(i);
michael@0 1201 if (listener->mEventType == NS_PAGE_UNLOAD ||
michael@0 1202 listener->mEventType == NS_BEFORE_PAGE_UNLOAD) {
michael@0 1203 return true;
michael@0 1204 }
michael@0 1205 }
michael@0 1206 return false;
michael@0 1207 }
michael@0 1208
michael@0 1209 void
michael@0 1210 EventListenerManager::SetEventHandler(nsIAtom* aEventName,
michael@0 1211 const nsAString& aTypeString,
michael@0 1212 EventHandlerNonNull* aHandler)
michael@0 1213 {
michael@0 1214 if (!aHandler) {
michael@0 1215 RemoveEventHandler(aEventName, aTypeString);
michael@0 1216 return;
michael@0 1217 }
michael@0 1218
michael@0 1219 // Untrusted events are always permitted for non-chrome script
michael@0 1220 // handlers.
michael@0 1221 SetEventHandlerInternal(aEventName, aTypeString, TypedEventHandler(aHandler),
michael@0 1222 !mIsMainThreadELM ||
michael@0 1223 !nsContentUtils::IsCallerChrome());
michael@0 1224 }
michael@0 1225
michael@0 1226 void
michael@0 1227 EventListenerManager::SetEventHandler(OnErrorEventHandlerNonNull* aHandler)
michael@0 1228 {
michael@0 1229 if (mIsMainThreadELM) {
michael@0 1230 if (!aHandler) {
michael@0 1231 RemoveEventHandler(nsGkAtoms::onerror, EmptyString());
michael@0 1232 return;
michael@0 1233 }
michael@0 1234
michael@0 1235 // Untrusted events are always permitted for non-chrome script
michael@0 1236 // handlers.
michael@0 1237 SetEventHandlerInternal(nsGkAtoms::onerror, EmptyString(),
michael@0 1238 TypedEventHandler(aHandler),
michael@0 1239 !nsContentUtils::IsCallerChrome());
michael@0 1240 } else {
michael@0 1241 if (!aHandler) {
michael@0 1242 RemoveEventHandler(nullptr, NS_LITERAL_STRING("error"));
michael@0 1243 return;
michael@0 1244 }
michael@0 1245
michael@0 1246 // Untrusted events are always permitted.
michael@0 1247 SetEventHandlerInternal(nullptr, NS_LITERAL_STRING("error"),
michael@0 1248 TypedEventHandler(aHandler), true);
michael@0 1249 }
michael@0 1250 }
michael@0 1251
michael@0 1252 void
michael@0 1253 EventListenerManager::SetEventHandler(
michael@0 1254 OnBeforeUnloadEventHandlerNonNull* aHandler)
michael@0 1255 {
michael@0 1256 if (!aHandler) {
michael@0 1257 RemoveEventHandler(nsGkAtoms::onbeforeunload, EmptyString());
michael@0 1258 return;
michael@0 1259 }
michael@0 1260
michael@0 1261 // Untrusted events are always permitted for non-chrome script
michael@0 1262 // handlers.
michael@0 1263 SetEventHandlerInternal(nsGkAtoms::onbeforeunload, EmptyString(),
michael@0 1264 TypedEventHandler(aHandler),
michael@0 1265 !mIsMainThreadELM ||
michael@0 1266 !nsContentUtils::IsCallerChrome());
michael@0 1267 }
michael@0 1268
michael@0 1269 const TypedEventHandler*
michael@0 1270 EventListenerManager::GetTypedEventHandler(nsIAtom* aEventName,
michael@0 1271 const nsAString& aTypeString)
michael@0 1272 {
michael@0 1273 uint32_t eventType = nsContentUtils::GetEventId(aEventName);
michael@0 1274 Listener* listener = FindEventHandler(eventType, aEventName, aTypeString);
michael@0 1275
michael@0 1276 if (!listener) {
michael@0 1277 return nullptr;
michael@0 1278 }
michael@0 1279
michael@0 1280 JSEventHandler* jsEventHandler = listener->GetJSEventHandler();
michael@0 1281
michael@0 1282 if (listener->mHandlerIsString) {
michael@0 1283 CompileEventHandlerInternal(listener, nullptr, nullptr);
michael@0 1284 }
michael@0 1285
michael@0 1286 const TypedEventHandler& typedHandler =
michael@0 1287 jsEventHandler->GetTypedEventHandler();
michael@0 1288 return typedHandler.HasEventHandler() ? &typedHandler : nullptr;
michael@0 1289 }
michael@0 1290
michael@0 1291 size_t
michael@0 1292 EventListenerManager::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
michael@0 1293 {
michael@0 1294 size_t n = aMallocSizeOf(this);
michael@0 1295 n += mListeners.SizeOfExcludingThis(aMallocSizeOf);
michael@0 1296 uint32_t count = mListeners.Length();
michael@0 1297 for (uint32_t i = 0; i < count; ++i) {
michael@0 1298 JSEventHandler* jsEventHandler =
michael@0 1299 mListeners.ElementAt(i).GetJSEventHandler();
michael@0 1300 if (jsEventHandler) {
michael@0 1301 n += jsEventHandler->SizeOfIncludingThis(aMallocSizeOf);
michael@0 1302 }
michael@0 1303 }
michael@0 1304 return n;
michael@0 1305 }
michael@0 1306
michael@0 1307 void
michael@0 1308 EventListenerManager::MarkForCC()
michael@0 1309 {
michael@0 1310 uint32_t count = mListeners.Length();
michael@0 1311 for (uint32_t i = 0; i < count; ++i) {
michael@0 1312 const Listener& listener = mListeners.ElementAt(i);
michael@0 1313 JSEventHandler* jsEventHandler = listener.GetJSEventHandler();
michael@0 1314 if (jsEventHandler) {
michael@0 1315 const TypedEventHandler& typedHandler =
michael@0 1316 jsEventHandler->GetTypedEventHandler();
michael@0 1317 if (typedHandler.HasEventHandler()) {
michael@0 1318 JS::ExposeObjectToActiveJS(typedHandler.Ptr()->Callable());
michael@0 1319 }
michael@0 1320 } else if (listener.mListenerType == Listener::eWrappedJSListener) {
michael@0 1321 xpc_TryUnmarkWrappedGrayObject(listener.mListener.GetXPCOMCallback());
michael@0 1322 } else if (listener.mListenerType == Listener::eWebIDLListener) {
michael@0 1323 // Callback() unmarks gray
michael@0 1324 listener.mListener.GetWebIDLCallback()->Callback();
michael@0 1325 }
michael@0 1326 }
michael@0 1327 if (mRefCnt.IsPurple()) {
michael@0 1328 mRefCnt.RemovePurple();
michael@0 1329 }
michael@0 1330 }
michael@0 1331
michael@0 1332 already_AddRefed<nsIScriptGlobalObject>
michael@0 1333 EventListenerManager::GetScriptGlobalAndDocument(nsIDocument** aDoc)
michael@0 1334 {
michael@0 1335 nsCOMPtr<nsINode> node(do_QueryInterface(mTarget));
michael@0 1336 nsCOMPtr<nsIDocument> doc;
michael@0 1337 nsCOMPtr<nsIScriptGlobalObject> global;
michael@0 1338 if (node) {
michael@0 1339 // Try to get context from doc
michael@0 1340 // XXX sXBL/XBL2 issue -- do we really want the owner here? What
michael@0 1341 // if that's the XBL document?
michael@0 1342 doc = node->OwnerDoc();
michael@0 1343 if (doc->IsLoadedAsData()) {
michael@0 1344 return nullptr;
michael@0 1345 }
michael@0 1346
michael@0 1347 // We want to allow compiling an event handler even in an unloaded
michael@0 1348 // document, so use GetScopeObject here, not GetScriptHandlingObject.
michael@0 1349 global = do_QueryInterface(doc->GetScopeObject());
michael@0 1350 } else {
michael@0 1351 nsCOMPtr<nsPIDOMWindow> win = GetTargetAsInnerWindow();
michael@0 1352 if (win) {
michael@0 1353 doc = win->GetExtantDoc();
michael@0 1354 global = do_QueryInterface(win);
michael@0 1355 } else {
michael@0 1356 global = do_QueryInterface(mTarget);
michael@0 1357 }
michael@0 1358 }
michael@0 1359
michael@0 1360 doc.forget(aDoc);
michael@0 1361 return global.forget();
michael@0 1362 }
michael@0 1363
michael@0 1364 } // namespace mozilla

mercurial