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