|
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/. */ |
|
5 |
|
6 // Microsoft's API Name hackery sucks |
|
7 #undef CreateEvent |
|
8 |
|
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" |
|
22 |
|
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" |
|
42 |
|
43 namespace mozilla { |
|
44 |
|
45 using namespace dom; |
|
46 using namespace hal; |
|
47 |
|
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)) |
|
54 |
|
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; |
|
63 |
|
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 } |
|
87 |
|
88 uint32_t EventListenerManager::sMainThreadCreatedCount = 0; |
|
89 |
|
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"); |
|
104 |
|
105 if (mIsMainThreadELM) { |
|
106 ++sMainThreadCreatedCount; |
|
107 } |
|
108 } |
|
109 |
|
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 } |
|
124 |
|
125 void |
|
126 EventListenerManager::RemoveAllListeners() |
|
127 { |
|
128 if (mClearingListeners) { |
|
129 return; |
|
130 } |
|
131 mClearingListeners = true; |
|
132 mListeners.Clear(); |
|
133 mClearingListeners = false; |
|
134 } |
|
135 |
|
136 void |
|
137 EventListenerManager::Shutdown() |
|
138 { |
|
139 Event::Shutdown(); |
|
140 } |
|
141 |
|
142 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(EventListenerManager, AddRef) |
|
143 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(EventListenerManager, Release) |
|
144 |
|
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 } |
|
168 |
|
169 NS_IMPL_CYCLE_COLLECTION_CLASS(EventListenerManager) |
|
170 |
|
171 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(EventListenerManager) |
|
172 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListeners) |
|
173 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
|
174 |
|
175 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(EventListenerManager) |
|
176 tmp->Disconnect(); |
|
177 NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
|
178 |
|
179 |
|
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 } |
|
189 |
|
190 nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow(); |
|
191 return window; |
|
192 } |
|
193 |
|
194 already_AddRefed<nsPIDOMWindow> |
|
195 EventListenerManager::GetTargetAsInnerWindow() const |
|
196 { |
|
197 nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mTarget); |
|
198 if (!window) { |
|
199 return nullptr; |
|
200 } |
|
201 |
|
202 NS_ASSERTION(window->IsInnerWindow(), "Target should not be an outer window"); |
|
203 return window.forget(); |
|
204 } |
|
205 |
|
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 |
|
219 |
|
220 if (!aListenerHolder || mClearingListeners) { |
|
221 return; |
|
222 } |
|
223 |
|
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. |
|
227 |
|
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 } |
|
241 |
|
242 mNoListenerForEvent = NS_EVENT_NULL; |
|
243 mNoListenerForEventAtom = nullptr; |
|
244 |
|
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; |
|
256 |
|
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 } |
|
269 |
|
270 |
|
271 if (aFlags.mInSystemGroup) { |
|
272 mMayHaveSystemGroupListeners = true; |
|
273 } |
|
274 if (aFlags.mCapture) { |
|
275 mMayHaveCapturingListeners = true; |
|
276 } |
|
277 |
|
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 } |
|
380 |
|
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 } |
|
396 |
|
397 void |
|
398 EventListenerManager::EnableDevice(uint32_t aType) |
|
399 { |
|
400 nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow(); |
|
401 if (!window) { |
|
402 return; |
|
403 } |
|
404 |
|
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 } |
|
426 |
|
427 void |
|
428 EventListenerManager::DisableDevice(uint32_t aType) |
|
429 { |
|
430 nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow(); |
|
431 if (!window) { |
|
432 return; |
|
433 } |
|
434 |
|
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 } |
|
456 |
|
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 } |
|
469 |
|
470 Listener* listener; |
|
471 |
|
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 |
|
480 |
|
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 } |
|
496 |
|
497 if (!deviceType |
|
498 #ifdef MOZ_B2G |
|
499 && !timeChangeEvent && !networkEvent |
|
500 #endif // MOZ_B2G |
|
501 ) { |
|
502 return; |
|
503 } |
|
504 --typeCount; |
|
505 } |
|
506 } |
|
507 } |
|
508 |
|
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 } |
|
525 |
|
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 } |
|
546 |
|
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 } |
|
558 |
|
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 } |
|
570 |
|
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 } |
|
590 |
|
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()); |
|
599 |
|
600 uint32_t eventType = nsContentUtils::GetEventId(aName); |
|
601 Listener* listener = FindEventHandler(eventType, aName, aTypeString); |
|
602 |
|
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; |
|
608 |
|
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); |
|
615 |
|
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?"); |
|
621 |
|
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 } |
|
630 |
|
631 // Set flag to indicate possible need for compilation later |
|
632 listener->mHandlerIsString = !aTypedHandler.HasEventHandler(); |
|
633 if (aPermitUntrustedEvents) { |
|
634 listener->mFlags.mAllowUntrustedEvents = true; |
|
635 } |
|
636 |
|
637 return listener; |
|
638 } |
|
639 |
|
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"); |
|
650 |
|
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 } |
|
661 |
|
662 nsCOMPtr<nsIDocument> doc; |
|
663 nsCOMPtr<nsIScriptGlobalObject> global = |
|
664 GetScriptGlobalAndDocument(getter_AddRefs(doc)); |
|
665 |
|
666 if (!global) { |
|
667 // This can happen; for example this document might have been |
|
668 // loaded as data. |
|
669 return NS_OK; |
|
670 } |
|
671 |
|
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 |
|
678 |
|
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 } |
|
688 |
|
689 nsCOMPtr<nsIContentSecurityPolicy> csp; |
|
690 rv = doc->NodePrincipal()->GetCsp(getter_AddRefs(csp)); |
|
691 NS_ENSURE_SUCCESS(rv, rv); |
|
692 |
|
693 if (csp) { |
|
694 bool inlineOK = true; |
|
695 bool reportViolations = false; |
|
696 rv = csp->GetAllowsInlineScript(&reportViolations, &inlineOK); |
|
697 NS_ENSURE_SUCCESS(rv, rv); |
|
698 |
|
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 } |
|
722 |
|
723 // return early if CSP wants us to block inline scripts |
|
724 if (!inlineOK) { |
|
725 return NS_OK; |
|
726 } |
|
727 } |
|
728 } |
|
729 |
|
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 } |
|
736 |
|
737 nsIScriptContext* context = global->GetScriptContext(); |
|
738 NS_ENSURE_TRUE(context, NS_ERROR_FAILURE); |
|
739 NS_ENSURE_STATE(global->GetGlobalJSObject()); |
|
740 |
|
741 Listener* listener = SetEventHandlerInternal(aName, |
|
742 EmptyString(), |
|
743 TypedEventHandler(), |
|
744 aPermitUntrustedEvents); |
|
745 |
|
746 if (!aDeferCompilation) { |
|
747 return CompileEventHandlerInternal(listener, &aBody, aElement); |
|
748 } |
|
749 |
|
750 return NS_OK; |
|
751 } |
|
752 |
|
753 void |
|
754 EventListenerManager::RemoveEventHandler(nsIAtom* aName, |
|
755 const nsAString& aTypeString) |
|
756 { |
|
757 if (mClearingListeners) { |
|
758 return; |
|
759 } |
|
760 |
|
761 uint32_t eventType = nsContentUtils::GetEventId(aName); |
|
762 Listener* listener = FindEventHandler(eventType, aName, aTypeString); |
|
763 |
|
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 } |
|
773 |
|
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?"); |
|
784 |
|
785 nsresult result = NS_OK; |
|
786 nsCOMPtr<nsIDocument> doc; |
|
787 nsCOMPtr<nsIScriptGlobalObject> global = |
|
788 GetScriptGlobalAndDocument(getter_AddRefs(doc)); |
|
789 NS_ENSURE_STATE(global); |
|
790 |
|
791 nsIScriptContext* context = global->GetScriptContext(); |
|
792 NS_ENSURE_STATE(context); |
|
793 |
|
794 // Activate JSAPI, and make sure that exceptions are reported on the right |
|
795 // Window. |
|
796 AutoJSAPIWithErrorsReportedToWindow jsapi(context); |
|
797 JSContext* cx = jsapi.cx(); |
|
798 |
|
799 nsCOMPtr<nsIAtom> typeAtom = aListener->mTypeAtom; |
|
800 nsIAtom* attrName = typeAtom; |
|
801 |
|
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; |
|
805 |
|
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 } |
|
835 |
|
836 element->GetAttr(kNameSpaceID_None, attrName, handlerBody); |
|
837 body = &handlerBody; |
|
838 aElement = element; |
|
839 } |
|
840 aListener = nullptr; |
|
841 |
|
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 } |
|
851 |
|
852 uint32_t argCount; |
|
853 const char **argNames; |
|
854 nsContentUtils::GetEventArgNames(aElement->GetNameSpaceID(), |
|
855 typeAtom, |
|
856 &argCount, &argNames); |
|
857 |
|
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); |
|
876 |
|
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); |
|
885 |
|
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); |
|
897 |
|
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); |
|
904 |
|
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 } |
|
919 |
|
920 return result; |
|
921 } |
|
922 |
|
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 |
|
930 |
|
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 } |
|
938 |
|
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 } |
|
956 |
|
957 return result; |
|
958 } |
|
959 |
|
960 /** |
|
961 * Causes a check for event listeners and processing by them if they exist. |
|
962 * @param an event listener |
|
963 */ |
|
964 |
|
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 } |
|
976 |
|
977 nsAutoTObserverArray<Listener, 2>::EndLimitedIterator iter(mListeners); |
|
978 Maybe<nsAutoPopupStatePusher> popupStatePusher; |
|
979 if (mIsMainThreadELM) { |
|
980 popupStatePusher.construct(Event::GetEventPopupControlState(aEvent)); |
|
981 } |
|
982 |
|
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 } |
|
1010 |
|
1011 if (NS_FAILED(HandleEventSubType(listener, *aDOMEvent, |
|
1012 aCurrentTarget))) { |
|
1013 aEvent->mFlags.mExceptionHasBeenRisen = true; |
|
1014 } |
|
1015 } |
|
1016 } |
|
1017 } |
|
1018 } |
|
1019 |
|
1020 aEvent->currentTarget = nullptr; |
|
1021 |
|
1022 if (mIsMainThreadELM && !hasListener) { |
|
1023 mNoListenerForEvent = aEvent->message; |
|
1024 mNoListenerForEventAtom = aEvent->userType; |
|
1025 } |
|
1026 |
|
1027 if (aEvent->mFlags.mDefaultPrevented) { |
|
1028 *aEventStatus = nsEventStatus_eConsumeNoDefault; |
|
1029 } |
|
1030 } |
|
1031 |
|
1032 void |
|
1033 EventListenerManager::Disconnect() |
|
1034 { |
|
1035 mTarget = nullptr; |
|
1036 RemoveAllListeners(); |
|
1037 } |
|
1038 |
|
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 } |
|
1051 |
|
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 } |
|
1062 |
|
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 } |
|
1077 |
|
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 } |
|
1091 |
|
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 } |
|
1105 |
|
1106 return false; |
|
1107 } |
|
1108 |
|
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 } |
|
1128 |
|
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 } |
|
1135 |
|
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 } |
|
1154 |
|
1155 bool |
|
1156 EventListenerManager::HasListeners() |
|
1157 { |
|
1158 return !mListeners.IsEmpty(); |
|
1159 } |
|
1160 |
|
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 } |
|
1194 |
|
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 } |
|
1208 |
|
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 } |
|
1218 |
|
1219 // Untrusted events are always permitted for non-chrome script |
|
1220 // handlers. |
|
1221 SetEventHandlerInternal(aEventName, aTypeString, TypedEventHandler(aHandler), |
|
1222 !mIsMainThreadELM || |
|
1223 !nsContentUtils::IsCallerChrome()); |
|
1224 } |
|
1225 |
|
1226 void |
|
1227 EventListenerManager::SetEventHandler(OnErrorEventHandlerNonNull* aHandler) |
|
1228 { |
|
1229 if (mIsMainThreadELM) { |
|
1230 if (!aHandler) { |
|
1231 RemoveEventHandler(nsGkAtoms::onerror, EmptyString()); |
|
1232 return; |
|
1233 } |
|
1234 |
|
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 } |
|
1245 |
|
1246 // Untrusted events are always permitted. |
|
1247 SetEventHandlerInternal(nullptr, NS_LITERAL_STRING("error"), |
|
1248 TypedEventHandler(aHandler), true); |
|
1249 } |
|
1250 } |
|
1251 |
|
1252 void |
|
1253 EventListenerManager::SetEventHandler( |
|
1254 OnBeforeUnloadEventHandlerNonNull* aHandler) |
|
1255 { |
|
1256 if (!aHandler) { |
|
1257 RemoveEventHandler(nsGkAtoms::onbeforeunload, EmptyString()); |
|
1258 return; |
|
1259 } |
|
1260 |
|
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 } |
|
1268 |
|
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); |
|
1275 |
|
1276 if (!listener) { |
|
1277 return nullptr; |
|
1278 } |
|
1279 |
|
1280 JSEventHandler* jsEventHandler = listener->GetJSEventHandler(); |
|
1281 |
|
1282 if (listener->mHandlerIsString) { |
|
1283 CompileEventHandlerInternal(listener, nullptr, nullptr); |
|
1284 } |
|
1285 |
|
1286 const TypedEventHandler& typedHandler = |
|
1287 jsEventHandler->GetTypedEventHandler(); |
|
1288 return typedHandler.HasEventHandler() ? &typedHandler : nullptr; |
|
1289 } |
|
1290 |
|
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 } |
|
1306 |
|
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 } |
|
1331 |
|
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 } |
|
1346 |
|
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 } |
|
1359 |
|
1360 doc.forget(aDoc); |
|
1361 return global.forget(); |
|
1362 } |
|
1363 |
|
1364 } // namespace mozilla |