|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
|
3 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #include "nsPresContext.h" |
|
8 #include "nsContentUtils.h" |
|
9 #include "nsError.h" |
|
10 #include <new> |
|
11 #include "nsIContent.h" |
|
12 #include "nsIDocument.h" |
|
13 #include "nsINode.h" |
|
14 #include "nsPIDOMWindow.h" |
|
15 #include "GeckoProfiler.h" |
|
16 #include "GeneratedEvents.h" |
|
17 #include "mozilla/ContentEvents.h" |
|
18 #include "mozilla/dom/EventTarget.h" |
|
19 #include "mozilla/dom/TouchEvent.h" |
|
20 #include "mozilla/EventDispatcher.h" |
|
21 #include "mozilla/EventListenerManager.h" |
|
22 #include "mozilla/InternalMutationEvent.h" |
|
23 #include "mozilla/MiscEvents.h" |
|
24 #include "mozilla/MouseEvents.h" |
|
25 #include "mozilla/TextEvents.h" |
|
26 #include "mozilla/TouchEvents.h" |
|
27 #include "mozilla/unused.h" |
|
28 |
|
29 namespace mozilla { |
|
30 |
|
31 using namespace dom; |
|
32 |
|
33 class ELMCreationDetector |
|
34 { |
|
35 public: |
|
36 ELMCreationDetector() |
|
37 // We can do this optimization only in the main thread. |
|
38 : mNonMainThread(!NS_IsMainThread()) |
|
39 , mInitialCount(mNonMainThread ? |
|
40 0 : EventListenerManager::sMainThreadCreatedCount) |
|
41 { |
|
42 } |
|
43 |
|
44 bool MayHaveNewListenerManager() |
|
45 { |
|
46 return mNonMainThread || |
|
47 mInitialCount != EventListenerManager::sMainThreadCreatedCount; |
|
48 } |
|
49 |
|
50 bool IsMainThread() |
|
51 { |
|
52 return !mNonMainThread; |
|
53 } |
|
54 |
|
55 private: |
|
56 bool mNonMainThread; |
|
57 uint32_t mInitialCount; |
|
58 }; |
|
59 |
|
60 #define NS_TARGET_CHAIN_FORCE_CONTENT_DISPATCH (1 << 0) |
|
61 #define NS_TARGET_CHAIN_WANTS_WILL_HANDLE_EVENT (1 << 1) |
|
62 #define NS_TARGET_CHAIN_MAY_HAVE_MANAGER (1 << 2) |
|
63 |
|
64 // EventTargetChainItem represents a single item in the event target chain. |
|
65 class EventTargetChainItem |
|
66 { |
|
67 private: |
|
68 EventTargetChainItem(EventTarget* aTarget); |
|
69 public: |
|
70 EventTargetChainItem() |
|
71 : mFlags(0) |
|
72 , mItemFlags(0) |
|
73 { |
|
74 } |
|
75 |
|
76 static EventTargetChainItem* Create(nsTArray<EventTargetChainItem>& aChain, |
|
77 EventTarget* aTarget, |
|
78 EventTargetChainItem* aChild = nullptr) |
|
79 { |
|
80 MOZ_ASSERT(!aChild || &aChain.ElementAt(aChain.Length() - 1) == aChild); |
|
81 return new (aChain.AppendElement()) EventTargetChainItem(aTarget); |
|
82 } |
|
83 |
|
84 static void DestroyLast(nsTArray<EventTargetChainItem>& aChain, |
|
85 EventTargetChainItem* aItem) |
|
86 { |
|
87 uint32_t lastIndex = aChain.Length() - 1; |
|
88 MOZ_ASSERT(&aChain[lastIndex] == aItem); |
|
89 aChain.RemoveElementAt(lastIndex); |
|
90 } |
|
91 |
|
92 bool IsValid() |
|
93 { |
|
94 NS_WARN_IF_FALSE(!!(mTarget), "Event target is not valid!"); |
|
95 return !!(mTarget); |
|
96 } |
|
97 |
|
98 EventTarget* GetNewTarget() |
|
99 { |
|
100 return mNewTarget; |
|
101 } |
|
102 |
|
103 void SetNewTarget(EventTarget* aNewTarget) |
|
104 { |
|
105 mNewTarget = aNewTarget; |
|
106 } |
|
107 |
|
108 void SetForceContentDispatch(bool aForce) |
|
109 { |
|
110 if (aForce) { |
|
111 mFlags |= NS_TARGET_CHAIN_FORCE_CONTENT_DISPATCH; |
|
112 } else { |
|
113 mFlags &= ~NS_TARGET_CHAIN_FORCE_CONTENT_DISPATCH; |
|
114 } |
|
115 } |
|
116 |
|
117 bool ForceContentDispatch() |
|
118 { |
|
119 return !!(mFlags & NS_TARGET_CHAIN_FORCE_CONTENT_DISPATCH); |
|
120 } |
|
121 |
|
122 void SetWantsWillHandleEvent(bool aWants) |
|
123 { |
|
124 if (aWants) { |
|
125 mFlags |= NS_TARGET_CHAIN_WANTS_WILL_HANDLE_EVENT; |
|
126 } else { |
|
127 mFlags &= ~NS_TARGET_CHAIN_WANTS_WILL_HANDLE_EVENT; |
|
128 } |
|
129 } |
|
130 |
|
131 bool WantsWillHandleEvent() |
|
132 { |
|
133 return !!(mFlags & NS_TARGET_CHAIN_WANTS_WILL_HANDLE_EVENT); |
|
134 } |
|
135 |
|
136 void SetMayHaveListenerManager(bool aMayHave) |
|
137 { |
|
138 if (aMayHave) { |
|
139 mFlags |= NS_TARGET_CHAIN_MAY_HAVE_MANAGER; |
|
140 } else { |
|
141 mFlags &= ~NS_TARGET_CHAIN_MAY_HAVE_MANAGER; |
|
142 } |
|
143 } |
|
144 |
|
145 bool MayHaveListenerManager() |
|
146 { |
|
147 return !!(mFlags & NS_TARGET_CHAIN_MAY_HAVE_MANAGER); |
|
148 } |
|
149 |
|
150 EventTarget* CurrentTarget() |
|
151 { |
|
152 return mTarget; |
|
153 } |
|
154 |
|
155 /** |
|
156 * Dispatches event through the event target chain. |
|
157 * Handles capture, target and bubble phases both in default |
|
158 * and system event group and calls also PostHandleEvent for each |
|
159 * item in the chain. |
|
160 */ |
|
161 static void HandleEventTargetChain(nsTArray<EventTargetChainItem>& aChain, |
|
162 EventChainPostVisitor& aVisitor, |
|
163 EventDispatchingCallback* aCallback, |
|
164 ELMCreationDetector& aCd); |
|
165 |
|
166 /** |
|
167 * Resets aVisitor object and calls PreHandleEvent. |
|
168 * Copies mItemFlags and mItemData to the current EventTargetChainItem. |
|
169 */ |
|
170 void PreHandleEvent(EventChainPreVisitor& aVisitor); |
|
171 |
|
172 /** |
|
173 * If the current item in the event target chain has an event listener |
|
174 * manager, this method calls EventListenerManager::HandleEvent(). |
|
175 */ |
|
176 void HandleEvent(EventChainPostVisitor& aVisitor, |
|
177 ELMCreationDetector& aCd) |
|
178 { |
|
179 if (WantsWillHandleEvent()) { |
|
180 mTarget->WillHandleEvent(aVisitor); |
|
181 } |
|
182 if (aVisitor.mEvent->mFlags.mPropagationStopped) { |
|
183 return; |
|
184 } |
|
185 if (!mManager) { |
|
186 if (!MayHaveListenerManager() && !aCd.MayHaveNewListenerManager()) { |
|
187 return; |
|
188 } |
|
189 mManager = mTarget->GetExistingListenerManager(); |
|
190 } |
|
191 if (mManager) { |
|
192 NS_ASSERTION(aVisitor.mEvent->currentTarget == nullptr, |
|
193 "CurrentTarget should be null!"); |
|
194 mManager->HandleEvent(aVisitor.mPresContext, aVisitor.mEvent, |
|
195 &aVisitor.mDOMEvent, |
|
196 CurrentTarget(), |
|
197 &aVisitor.mEventStatus); |
|
198 NS_ASSERTION(aVisitor.mEvent->currentTarget == nullptr, |
|
199 "CurrentTarget should be null!"); |
|
200 } |
|
201 } |
|
202 |
|
203 /** |
|
204 * Copies mItemFlags and mItemData to aVisitor and calls PostHandleEvent. |
|
205 */ |
|
206 void PostHandleEvent(EventChainPostVisitor& aVisitor); |
|
207 |
|
208 nsCOMPtr<EventTarget> mTarget; |
|
209 uint16_t mFlags; |
|
210 uint16_t mItemFlags; |
|
211 nsCOMPtr<nsISupports> mItemData; |
|
212 // Event retargeting must happen whenever mNewTarget is non-null. |
|
213 nsCOMPtr<EventTarget> mNewTarget; |
|
214 // Cache mTarget's event listener manager. |
|
215 nsRefPtr<EventListenerManager> mManager; |
|
216 }; |
|
217 |
|
218 EventTargetChainItem::EventTargetChainItem(EventTarget* aTarget) |
|
219 : mTarget(aTarget) |
|
220 , mFlags(0) |
|
221 , mItemFlags(0) |
|
222 { |
|
223 MOZ_ASSERT(!aTarget || mTarget == aTarget->GetTargetForEventTargetChain()); |
|
224 } |
|
225 |
|
226 void |
|
227 EventTargetChainItem::PreHandleEvent(EventChainPreVisitor& aVisitor) |
|
228 { |
|
229 aVisitor.Reset(); |
|
230 unused << mTarget->PreHandleEvent(aVisitor); |
|
231 SetForceContentDispatch(aVisitor.mForceContentDispatch); |
|
232 SetWantsWillHandleEvent(aVisitor.mWantsWillHandleEvent); |
|
233 SetMayHaveListenerManager(aVisitor.mMayHaveListenerManager); |
|
234 mItemFlags = aVisitor.mItemFlags; |
|
235 mItemData = aVisitor.mItemData; |
|
236 } |
|
237 |
|
238 void |
|
239 EventTargetChainItem::PostHandleEvent(EventChainPostVisitor& aVisitor) |
|
240 { |
|
241 aVisitor.mItemFlags = mItemFlags; |
|
242 aVisitor.mItemData = mItemData; |
|
243 mTarget->PostHandleEvent(aVisitor); |
|
244 } |
|
245 |
|
246 void |
|
247 EventTargetChainItem::HandleEventTargetChain( |
|
248 nsTArray<EventTargetChainItem>& aChain, |
|
249 EventChainPostVisitor& aVisitor, |
|
250 EventDispatchingCallback* aCallback, |
|
251 ELMCreationDetector& aCd) |
|
252 { |
|
253 // Save the target so that it can be restored later. |
|
254 nsCOMPtr<EventTarget> firstTarget = aVisitor.mEvent->target; |
|
255 uint32_t chainLength = aChain.Length(); |
|
256 |
|
257 // Capture |
|
258 aVisitor.mEvent->mFlags.mInCapturePhase = true; |
|
259 aVisitor.mEvent->mFlags.mInBubblingPhase = false; |
|
260 for (uint32_t i = chainLength - 1; i > 0; --i) { |
|
261 EventTargetChainItem& item = aChain[i]; |
|
262 if ((!aVisitor.mEvent->mFlags.mNoContentDispatch || |
|
263 item.ForceContentDispatch()) && |
|
264 !aVisitor.mEvent->mFlags.mPropagationStopped) { |
|
265 item.HandleEvent(aVisitor, aCd); |
|
266 } |
|
267 |
|
268 if (item.GetNewTarget()) { |
|
269 // item is at anonymous boundary. Need to retarget for the child items. |
|
270 for (uint32_t j = i; j > 0; --j) { |
|
271 uint32_t childIndex = j - 1; |
|
272 EventTarget* newTarget = aChain[childIndex].GetNewTarget(); |
|
273 if (newTarget) { |
|
274 aVisitor.mEvent->target = newTarget; |
|
275 break; |
|
276 } |
|
277 } |
|
278 } |
|
279 } |
|
280 |
|
281 // Target |
|
282 aVisitor.mEvent->mFlags.mInBubblingPhase = true; |
|
283 EventTargetChainItem& targetItem = aChain[0]; |
|
284 if (!aVisitor.mEvent->mFlags.mPropagationStopped && |
|
285 (!aVisitor.mEvent->mFlags.mNoContentDispatch || |
|
286 targetItem.ForceContentDispatch())) { |
|
287 targetItem.HandleEvent(aVisitor, aCd); |
|
288 } |
|
289 if (aVisitor.mEvent->mFlags.mInSystemGroup) { |
|
290 targetItem.PostHandleEvent(aVisitor); |
|
291 } |
|
292 |
|
293 // Bubble |
|
294 aVisitor.mEvent->mFlags.mInCapturePhase = false; |
|
295 for (uint32_t i = 1; i < chainLength; ++i) { |
|
296 EventTargetChainItem& item = aChain[i]; |
|
297 EventTarget* newTarget = item.GetNewTarget(); |
|
298 if (newTarget) { |
|
299 // Item is at anonymous boundary. Need to retarget for the current item |
|
300 // and for parent items. |
|
301 aVisitor.mEvent->target = newTarget; |
|
302 } |
|
303 |
|
304 if (aVisitor.mEvent->mFlags.mBubbles || newTarget) { |
|
305 if ((!aVisitor.mEvent->mFlags.mNoContentDispatch || |
|
306 item.ForceContentDispatch()) && |
|
307 !aVisitor.mEvent->mFlags.mPropagationStopped) { |
|
308 item.HandleEvent(aVisitor, aCd); |
|
309 } |
|
310 if (aVisitor.mEvent->mFlags.mInSystemGroup) { |
|
311 item.PostHandleEvent(aVisitor); |
|
312 } |
|
313 } |
|
314 } |
|
315 aVisitor.mEvent->mFlags.mInBubblingPhase = false; |
|
316 |
|
317 if (!aVisitor.mEvent->mFlags.mInSystemGroup) { |
|
318 // Dispatch to the system event group. Make sure to clear the |
|
319 // STOP_DISPATCH flag since this resets for each event group. |
|
320 aVisitor.mEvent->mFlags.mPropagationStopped = false; |
|
321 aVisitor.mEvent->mFlags.mImmediatePropagationStopped = false; |
|
322 |
|
323 // Setting back the original target of the event. |
|
324 aVisitor.mEvent->target = aVisitor.mEvent->originalTarget; |
|
325 |
|
326 // Special handling if PresShell (or some other caller) |
|
327 // used a callback object. |
|
328 if (aCallback) { |
|
329 aCallback->HandleEvent(aVisitor); |
|
330 } |
|
331 |
|
332 // Retarget for system event group (which does the default handling too). |
|
333 // Setting back the target which was used also for default event group. |
|
334 aVisitor.mEvent->target = firstTarget; |
|
335 aVisitor.mEvent->mFlags.mInSystemGroup = true; |
|
336 HandleEventTargetChain(aChain, |
|
337 aVisitor, |
|
338 aCallback, |
|
339 aCd); |
|
340 aVisitor.mEvent->mFlags.mInSystemGroup = false; |
|
341 |
|
342 // After dispatch, clear all the propagation flags so that |
|
343 // system group listeners don't affect to the event. |
|
344 aVisitor.mEvent->mFlags.mPropagationStopped = false; |
|
345 aVisitor.mEvent->mFlags.mImmediatePropagationStopped = false; |
|
346 } |
|
347 } |
|
348 |
|
349 static nsTArray<EventTargetChainItem>* sCachedMainThreadChain = nullptr; |
|
350 |
|
351 /* static */ void |
|
352 EventDispatcher::Shutdown() |
|
353 { |
|
354 delete sCachedMainThreadChain; |
|
355 sCachedMainThreadChain = nullptr; |
|
356 } |
|
357 |
|
358 EventTargetChainItem* |
|
359 EventTargetChainItemForChromeTarget(nsTArray<EventTargetChainItem>& aChain, |
|
360 nsINode* aNode, |
|
361 EventTargetChainItem* aChild = nullptr) |
|
362 { |
|
363 if (!aNode->IsInDoc()) { |
|
364 return nullptr; |
|
365 } |
|
366 nsPIDOMWindow* win = aNode->OwnerDoc()->GetInnerWindow(); |
|
367 EventTarget* piTarget = win ? win->GetParentTarget() : nullptr; |
|
368 NS_ENSURE_TRUE(piTarget, nullptr); |
|
369 |
|
370 EventTargetChainItem* etci = |
|
371 EventTargetChainItem::Create(aChain, |
|
372 piTarget->GetTargetForEventTargetChain(), |
|
373 aChild); |
|
374 if (!etci->IsValid()) { |
|
375 EventTargetChainItem::DestroyLast(aChain, etci); |
|
376 return nullptr; |
|
377 } |
|
378 return etci; |
|
379 } |
|
380 |
|
381 /* static */ nsresult |
|
382 EventDispatcher::Dispatch(nsISupports* aTarget, |
|
383 nsPresContext* aPresContext, |
|
384 WidgetEvent* aEvent, |
|
385 nsIDOMEvent* aDOMEvent, |
|
386 nsEventStatus* aEventStatus, |
|
387 EventDispatchingCallback* aCallback, |
|
388 nsCOMArray<EventTarget>* aTargets) |
|
389 { |
|
390 PROFILER_LABEL("EventDispatcher", "Dispatch"); |
|
391 NS_ASSERTION(aEvent, "Trying to dispatch without WidgetEvent!"); |
|
392 NS_ENSURE_TRUE(!aEvent->mFlags.mIsBeingDispatched, |
|
393 NS_ERROR_DOM_INVALID_STATE_ERR); |
|
394 NS_ASSERTION(!aTargets || !aEvent->message, "Wrong parameters!"); |
|
395 |
|
396 // If we're dispatching an already created DOMEvent object, make |
|
397 // sure it is initialized! |
|
398 // If aTargets is non-null, the event isn't going to be dispatched. |
|
399 NS_ENSURE_TRUE(aEvent->message || !aDOMEvent || aTargets, |
|
400 NS_ERROR_DOM_INVALID_STATE_ERR); |
|
401 |
|
402 nsCOMPtr<EventTarget> target = do_QueryInterface(aTarget); |
|
403 |
|
404 bool retargeted = false; |
|
405 |
|
406 if (aEvent->mFlags.mRetargetToNonNativeAnonymous) { |
|
407 nsCOMPtr<nsIContent> content = do_QueryInterface(target); |
|
408 if (content && content->IsInNativeAnonymousSubtree()) { |
|
409 nsCOMPtr<EventTarget> newTarget = |
|
410 do_QueryInterface(content->FindFirstNonChromeOnlyAccessContent()); |
|
411 NS_ENSURE_STATE(newTarget); |
|
412 |
|
413 aEvent->originalTarget = target; |
|
414 target = newTarget; |
|
415 retargeted = true; |
|
416 } |
|
417 } |
|
418 |
|
419 if (aEvent->mFlags.mOnlyChromeDispatch) { |
|
420 nsCOMPtr<nsINode> node = do_QueryInterface(aTarget); |
|
421 if (!node) { |
|
422 nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(aTarget); |
|
423 if (win) { |
|
424 node = win->GetExtantDoc(); |
|
425 } |
|
426 } |
|
427 |
|
428 NS_ENSURE_STATE(node); |
|
429 nsIDocument* doc = node->OwnerDoc(); |
|
430 if (!nsContentUtils::IsChromeDoc(doc)) { |
|
431 nsPIDOMWindow* win = doc ? doc->GetInnerWindow() : nullptr; |
|
432 // If we can't dispatch the event to chrome, do nothing. |
|
433 EventTarget* piTarget = win ? win->GetParentTarget() : nullptr; |
|
434 NS_ENSURE_TRUE(piTarget, NS_OK); |
|
435 |
|
436 // Set the target to be the original dispatch target, |
|
437 aEvent->target = target; |
|
438 // but use chrome event handler or TabChildGlobal for event target chain. |
|
439 target = piTarget; |
|
440 } |
|
441 } |
|
442 |
|
443 #ifdef DEBUG |
|
444 if (!nsContentUtils::IsSafeToRunScript()) { |
|
445 nsresult rv = NS_ERROR_FAILURE; |
|
446 if (target->GetContextForEventHandlers(&rv) || |
|
447 NS_FAILED(rv)) { |
|
448 nsCOMPtr<nsINode> node = do_QueryInterface(target); |
|
449 if (node && nsContentUtils::IsChromeDoc(node->OwnerDoc())) { |
|
450 NS_WARNING("Fix the caller!"); |
|
451 } else { |
|
452 NS_ERROR("This is unsafe! Fix the caller!"); |
|
453 } |
|
454 } |
|
455 } |
|
456 |
|
457 if (aDOMEvent) { |
|
458 WidgetEvent* innerEvent = aDOMEvent->GetInternalNSEvent(); |
|
459 NS_ASSERTION(innerEvent == aEvent, |
|
460 "The inner event of aDOMEvent is not the same as aEvent!"); |
|
461 } |
|
462 #endif |
|
463 |
|
464 nsresult rv = NS_OK; |
|
465 bool externalDOMEvent = !!(aDOMEvent); |
|
466 |
|
467 // If we have a PresContext, make sure it doesn't die before |
|
468 // event dispatching is finished. |
|
469 nsRefPtr<nsPresContext> kungFuDeathGrip(aPresContext); |
|
470 |
|
471 ELMCreationDetector cd; |
|
472 nsTArray<EventTargetChainItem> chain; |
|
473 if (cd.IsMainThread()) { |
|
474 if (!sCachedMainThreadChain) { |
|
475 sCachedMainThreadChain = new nsTArray<EventTargetChainItem>(); |
|
476 } |
|
477 chain.SwapElements(*sCachedMainThreadChain); |
|
478 chain.SetCapacity(128); |
|
479 } |
|
480 |
|
481 // Create the event target chain item for the event target. |
|
482 EventTargetChainItem* targetEtci = |
|
483 EventTargetChainItem::Create(chain, target->GetTargetForEventTargetChain()); |
|
484 MOZ_ASSERT(&chain[0] == targetEtci); |
|
485 if (!targetEtci->IsValid()) { |
|
486 EventTargetChainItem::DestroyLast(chain, targetEtci); |
|
487 return NS_ERROR_FAILURE; |
|
488 } |
|
489 |
|
490 // Make sure that nsIDOMEvent::target and nsIDOMEvent::originalTarget |
|
491 // point to the last item in the chain. |
|
492 if (!aEvent->target) { |
|
493 // Note, CurrentTarget() points always to the object returned by |
|
494 // GetTargetForEventTargetChain(). |
|
495 aEvent->target = targetEtci->CurrentTarget(); |
|
496 } else { |
|
497 // XXX But if the target is already set, use that. This is a hack |
|
498 // for the 'load', 'beforeunload' and 'unload' events, |
|
499 // which are dispatched to |window| but have document as their target. |
|
500 // |
|
501 // Make sure that the event target points to the right object. |
|
502 aEvent->target = aEvent->target->GetTargetForEventTargetChain(); |
|
503 NS_ENSURE_STATE(aEvent->target); |
|
504 } |
|
505 |
|
506 if (retargeted) { |
|
507 aEvent->originalTarget = |
|
508 aEvent->originalTarget->GetTargetForEventTargetChain(); |
|
509 NS_ENSURE_STATE(aEvent->originalTarget); |
|
510 } |
|
511 else { |
|
512 aEvent->originalTarget = aEvent->target; |
|
513 } |
|
514 |
|
515 nsCOMPtr<nsIContent> content = do_QueryInterface(aEvent->originalTarget); |
|
516 bool isInAnon = (content && content->IsInAnonymousSubtree()); |
|
517 |
|
518 aEvent->mFlags.mIsBeingDispatched = true; |
|
519 |
|
520 // Create visitor object and start event dispatching. |
|
521 // PreHandleEvent for the original target. |
|
522 nsEventStatus status = aEventStatus ? *aEventStatus : nsEventStatus_eIgnore; |
|
523 EventChainPreVisitor preVisitor(aPresContext, aEvent, aDOMEvent, status, |
|
524 isInAnon); |
|
525 targetEtci->PreHandleEvent(preVisitor); |
|
526 |
|
527 if (!preVisitor.mCanHandle && preVisitor.mAutomaticChromeDispatch && content) { |
|
528 // Event target couldn't handle the event. Try to propagate to chrome. |
|
529 EventTargetChainItem::DestroyLast(chain, targetEtci); |
|
530 targetEtci = EventTargetChainItemForChromeTarget(chain, content); |
|
531 NS_ENSURE_STATE(targetEtci); |
|
532 MOZ_ASSERT(&chain[0] == targetEtci); |
|
533 targetEtci->PreHandleEvent(preVisitor); |
|
534 } |
|
535 if (preVisitor.mCanHandle) { |
|
536 // At least the original target can handle the event. |
|
537 // Setting the retarget to the |target| simplifies retargeting code. |
|
538 nsCOMPtr<EventTarget> t = do_QueryInterface(aEvent->target); |
|
539 targetEtci->SetNewTarget(t); |
|
540 EventTargetChainItem* topEtci = targetEtci; |
|
541 targetEtci = nullptr; |
|
542 while (preVisitor.mParentTarget) { |
|
543 EventTarget* parentTarget = preVisitor.mParentTarget; |
|
544 EventTargetChainItem* parentEtci = |
|
545 EventTargetChainItem::Create(chain, preVisitor.mParentTarget, topEtci); |
|
546 if (!parentEtci->IsValid()) { |
|
547 EventTargetChainItem::DestroyLast(chain, parentEtci); |
|
548 rv = NS_ERROR_FAILURE; |
|
549 break; |
|
550 } |
|
551 |
|
552 // Item needs event retargetting. |
|
553 if (preVisitor.mEventTargetAtParent) { |
|
554 // Need to set the target of the event |
|
555 // so that also the next retargeting works. |
|
556 preVisitor.mEvent->target = preVisitor.mEventTargetAtParent; |
|
557 parentEtci->SetNewTarget(preVisitor.mEventTargetAtParent); |
|
558 } |
|
559 |
|
560 parentEtci->PreHandleEvent(preVisitor); |
|
561 if (preVisitor.mCanHandle) { |
|
562 topEtci = parentEtci; |
|
563 } else { |
|
564 EventTargetChainItem::DestroyLast(chain, parentEtci); |
|
565 parentEtci = nullptr; |
|
566 if (preVisitor.mAutomaticChromeDispatch && content) { |
|
567 // Even if the current target can't handle the event, try to |
|
568 // propagate to chrome. |
|
569 nsCOMPtr<nsINode> disabledTarget = do_QueryInterface(parentTarget); |
|
570 if (disabledTarget) { |
|
571 parentEtci = EventTargetChainItemForChromeTarget(chain, |
|
572 disabledTarget, |
|
573 topEtci); |
|
574 if (parentEtci) { |
|
575 parentEtci->PreHandleEvent(preVisitor); |
|
576 if (preVisitor.mCanHandle) { |
|
577 chain[0].SetNewTarget(parentTarget); |
|
578 topEtci = parentEtci; |
|
579 continue; |
|
580 } |
|
581 } |
|
582 } |
|
583 } |
|
584 break; |
|
585 } |
|
586 } |
|
587 if (NS_SUCCEEDED(rv)) { |
|
588 if (aTargets) { |
|
589 aTargets->Clear(); |
|
590 aTargets->SetCapacity(chain.Length()); |
|
591 for (uint32_t i = 0; i < chain.Length(); ++i) { |
|
592 aTargets->AppendObject(chain[i].CurrentTarget()->GetTargetForDOMEvent()); |
|
593 } |
|
594 } else { |
|
595 // Event target chain is created. Handle the chain. |
|
596 EventChainPostVisitor postVisitor(preVisitor); |
|
597 EventTargetChainItem::HandleEventTargetChain(chain, postVisitor, |
|
598 aCallback, cd); |
|
599 |
|
600 preVisitor.mEventStatus = postVisitor.mEventStatus; |
|
601 // If the DOM event was created during event flow. |
|
602 if (!preVisitor.mDOMEvent && postVisitor.mDOMEvent) { |
|
603 preVisitor.mDOMEvent = postVisitor.mDOMEvent; |
|
604 } |
|
605 } |
|
606 } |
|
607 } |
|
608 |
|
609 // Note, EventTargetChainItem objects are deleted when the chain goes out of |
|
610 // the scope. |
|
611 |
|
612 aEvent->mFlags.mIsBeingDispatched = false; |
|
613 aEvent->mFlags.mDispatchedAtLeastOnce = true; |
|
614 |
|
615 if (!externalDOMEvent && preVisitor.mDOMEvent) { |
|
616 // An dom::Event was created while dispatching the event. |
|
617 // Duplicate private data if someone holds a pointer to it. |
|
618 nsrefcnt rc = 0; |
|
619 NS_RELEASE2(preVisitor.mDOMEvent, rc); |
|
620 if (preVisitor.mDOMEvent) { |
|
621 preVisitor.mDOMEvent->DuplicatePrivateData(); |
|
622 } |
|
623 } |
|
624 |
|
625 if (aEventStatus) { |
|
626 *aEventStatus = preVisitor.mEventStatus; |
|
627 } |
|
628 |
|
629 if (cd.IsMainThread() && chain.Capacity() == 128 && sCachedMainThreadChain) { |
|
630 chain.ClearAndRetainStorage(); |
|
631 chain.SwapElements(*sCachedMainThreadChain); |
|
632 } |
|
633 |
|
634 return rv; |
|
635 } |
|
636 |
|
637 /* static */ nsresult |
|
638 EventDispatcher::DispatchDOMEvent(nsISupports* aTarget, |
|
639 WidgetEvent* aEvent, |
|
640 nsIDOMEvent* aDOMEvent, |
|
641 nsPresContext* aPresContext, |
|
642 nsEventStatus* aEventStatus) |
|
643 { |
|
644 if (aDOMEvent) { |
|
645 WidgetEvent* innerEvent = aDOMEvent->GetInternalNSEvent(); |
|
646 NS_ENSURE_TRUE(innerEvent, NS_ERROR_ILLEGAL_VALUE); |
|
647 |
|
648 bool dontResetTrusted = false; |
|
649 if (innerEvent->mFlags.mDispatchedAtLeastOnce) { |
|
650 innerEvent->target = nullptr; |
|
651 innerEvent->originalTarget = nullptr; |
|
652 } else { |
|
653 aDOMEvent->GetIsTrusted(&dontResetTrusted); |
|
654 } |
|
655 |
|
656 if (!dontResetTrusted) { |
|
657 //Check security state to determine if dispatcher is trusted |
|
658 aDOMEvent->SetTrusted(nsContentUtils::ThreadsafeIsCallerChrome()); |
|
659 } |
|
660 |
|
661 return EventDispatcher::Dispatch(aTarget, aPresContext, innerEvent, |
|
662 aDOMEvent, aEventStatus); |
|
663 } else if (aEvent) { |
|
664 return EventDispatcher::Dispatch(aTarget, aPresContext, aEvent, |
|
665 aDOMEvent, aEventStatus); |
|
666 } |
|
667 return NS_ERROR_ILLEGAL_VALUE; |
|
668 } |
|
669 |
|
670 /* static */ nsresult |
|
671 EventDispatcher::CreateEvent(EventTarget* aOwner, |
|
672 nsPresContext* aPresContext, |
|
673 WidgetEvent* aEvent, |
|
674 const nsAString& aEventType, |
|
675 nsIDOMEvent** aDOMEvent) |
|
676 { |
|
677 *aDOMEvent = nullptr; |
|
678 |
|
679 if (aEvent) { |
|
680 switch(aEvent->eventStructType) { |
|
681 case NS_MUTATION_EVENT: |
|
682 return NS_NewDOMMutationEvent(aDOMEvent, aOwner, aPresContext, |
|
683 aEvent->AsMutationEvent()); |
|
684 case NS_GUI_EVENT: |
|
685 case NS_SCROLLPORT_EVENT: |
|
686 case NS_UI_EVENT: |
|
687 return NS_NewDOMUIEvent(aDOMEvent, aOwner, aPresContext, |
|
688 aEvent->AsGUIEvent()); |
|
689 case NS_SCROLLAREA_EVENT: |
|
690 return NS_NewDOMScrollAreaEvent(aDOMEvent, aOwner, aPresContext, |
|
691 aEvent->AsScrollAreaEvent()); |
|
692 case NS_KEY_EVENT: |
|
693 return NS_NewDOMKeyboardEvent(aDOMEvent, aOwner, aPresContext, |
|
694 aEvent->AsKeyboardEvent()); |
|
695 case NS_COMPOSITION_EVENT: |
|
696 return NS_NewDOMCompositionEvent(aDOMEvent, aOwner, aPresContext, |
|
697 aEvent->AsCompositionEvent()); |
|
698 case NS_MOUSE_EVENT: |
|
699 return NS_NewDOMMouseEvent(aDOMEvent, aOwner, aPresContext, |
|
700 aEvent->AsMouseEvent()); |
|
701 case NS_FOCUS_EVENT: |
|
702 return NS_NewDOMFocusEvent(aDOMEvent, aOwner, aPresContext, |
|
703 aEvent->AsFocusEvent()); |
|
704 case NS_MOUSE_SCROLL_EVENT: |
|
705 return NS_NewDOMMouseScrollEvent(aDOMEvent, aOwner, aPresContext, |
|
706 aEvent->AsMouseScrollEvent()); |
|
707 case NS_WHEEL_EVENT: |
|
708 return NS_NewDOMWheelEvent(aDOMEvent, aOwner, aPresContext, |
|
709 aEvent->AsWheelEvent()); |
|
710 case NS_EDITOR_INPUT_EVENT: |
|
711 return NS_NewDOMInputEvent(aDOMEvent, aOwner, aPresContext, |
|
712 aEvent->AsEditorInputEvent()); |
|
713 case NS_DRAG_EVENT: |
|
714 return NS_NewDOMDragEvent(aDOMEvent, aOwner, aPresContext, |
|
715 aEvent->AsDragEvent()); |
|
716 case NS_TEXT_EVENT: |
|
717 return NS_NewDOMUIEvent(aDOMEvent, aOwner, aPresContext, |
|
718 aEvent->AsTextEvent()); |
|
719 case NS_CLIPBOARD_EVENT: |
|
720 return NS_NewDOMClipboardEvent(aDOMEvent, aOwner, aPresContext, |
|
721 aEvent->AsClipboardEvent()); |
|
722 case NS_SVGZOOM_EVENT: |
|
723 return NS_NewDOMSVGZoomEvent(aDOMEvent, aOwner, aPresContext, |
|
724 aEvent->AsGUIEvent()); |
|
725 case NS_SMIL_TIME_EVENT: |
|
726 return NS_NewDOMTimeEvent(aDOMEvent, aOwner, aPresContext, aEvent); |
|
727 |
|
728 case NS_COMMAND_EVENT: |
|
729 return NS_NewDOMCommandEvent(aDOMEvent, aOwner, aPresContext, |
|
730 aEvent->AsCommandEvent()); |
|
731 case NS_SIMPLE_GESTURE_EVENT: |
|
732 return NS_NewDOMSimpleGestureEvent(aDOMEvent, aOwner, aPresContext, |
|
733 aEvent->AsSimpleGestureEvent()); |
|
734 case NS_POINTER_EVENT: |
|
735 return NS_NewDOMPointerEvent(aDOMEvent, aOwner, aPresContext, |
|
736 aEvent->AsPointerEvent()); |
|
737 case NS_TOUCH_EVENT: |
|
738 return NS_NewDOMTouchEvent(aDOMEvent, aOwner, aPresContext, |
|
739 aEvent->AsTouchEvent()); |
|
740 case NS_TRANSITION_EVENT: |
|
741 return NS_NewDOMTransitionEvent(aDOMEvent, aOwner, aPresContext, |
|
742 aEvent->AsTransitionEvent()); |
|
743 case NS_ANIMATION_EVENT: |
|
744 return NS_NewDOMAnimationEvent(aDOMEvent, aOwner, aPresContext, |
|
745 aEvent->AsAnimationEvent()); |
|
746 default: |
|
747 // For all other types of events, create a vanilla event object. |
|
748 return NS_NewDOMEvent(aDOMEvent, aOwner, aPresContext, aEvent); |
|
749 } |
|
750 } |
|
751 |
|
752 // And if we didn't get an event, check the type argument. |
|
753 |
|
754 if (aEventType.LowerCaseEqualsLiteral("mouseevent") || |
|
755 aEventType.LowerCaseEqualsLiteral("mouseevents") || |
|
756 aEventType.LowerCaseEqualsLiteral("popupevents")) |
|
757 return NS_NewDOMMouseEvent(aDOMEvent, aOwner, aPresContext, nullptr); |
|
758 if (aEventType.LowerCaseEqualsLiteral("mousescrollevents")) |
|
759 return NS_NewDOMMouseScrollEvent(aDOMEvent, aOwner, aPresContext, nullptr); |
|
760 if (aEventType.LowerCaseEqualsLiteral("dragevent") || |
|
761 aEventType.LowerCaseEqualsLiteral("dragevents")) |
|
762 return NS_NewDOMDragEvent(aDOMEvent, aOwner, aPresContext, nullptr); |
|
763 if (aEventType.LowerCaseEqualsLiteral("keyboardevent") || |
|
764 aEventType.LowerCaseEqualsLiteral("keyevents")) |
|
765 return NS_NewDOMKeyboardEvent(aDOMEvent, aOwner, aPresContext, nullptr); |
|
766 if (aEventType.LowerCaseEqualsLiteral("compositionevent")) |
|
767 return NS_NewDOMCompositionEvent(aDOMEvent, aOwner, aPresContext, nullptr); |
|
768 if (aEventType.LowerCaseEqualsLiteral("mutationevent") || |
|
769 aEventType.LowerCaseEqualsLiteral("mutationevents")) |
|
770 return NS_NewDOMMutationEvent(aDOMEvent, aOwner, aPresContext, nullptr); |
|
771 if (aEventType.LowerCaseEqualsLiteral("textevent") || |
|
772 aEventType.LowerCaseEqualsLiteral("textevents")) |
|
773 return NS_NewDOMUIEvent(aDOMEvent, aOwner, aPresContext, nullptr); |
|
774 if (aEventType.LowerCaseEqualsLiteral("popupblockedevents")) |
|
775 return NS_NewDOMPopupBlockedEvent(aDOMEvent, aOwner, aPresContext, nullptr); |
|
776 if (aEventType.LowerCaseEqualsLiteral("deviceorientationevent")) |
|
777 return NS_NewDOMDeviceOrientationEvent(aDOMEvent, aOwner, aPresContext, nullptr); |
|
778 if (aEventType.LowerCaseEqualsLiteral("devicemotionevent")) |
|
779 return NS_NewDOMDeviceMotionEvent(aDOMEvent, aOwner, aPresContext, nullptr); |
|
780 if (aEventType.LowerCaseEqualsLiteral("uievent") || |
|
781 aEventType.LowerCaseEqualsLiteral("uievents")) |
|
782 return NS_NewDOMUIEvent(aDOMEvent, aOwner, aPresContext, nullptr); |
|
783 if (aEventType.LowerCaseEqualsLiteral("event") || |
|
784 aEventType.LowerCaseEqualsLiteral("events") || |
|
785 aEventType.LowerCaseEqualsLiteral("htmlevents") || |
|
786 aEventType.LowerCaseEqualsLiteral("svgevent") || |
|
787 aEventType.LowerCaseEqualsLiteral("svgevents")) |
|
788 return NS_NewDOMEvent(aDOMEvent, aOwner, aPresContext, nullptr); |
|
789 if (aEventType.LowerCaseEqualsLiteral("svgzoomevent") || |
|
790 aEventType.LowerCaseEqualsLiteral("svgzoomevents")) |
|
791 return NS_NewDOMSVGZoomEvent(aDOMEvent, aOwner, aPresContext, nullptr); |
|
792 if (aEventType.LowerCaseEqualsLiteral("timeevent") || |
|
793 aEventType.LowerCaseEqualsLiteral("timeevents")) |
|
794 return NS_NewDOMTimeEvent(aDOMEvent, aOwner, aPresContext, nullptr); |
|
795 if (aEventType.LowerCaseEqualsLiteral("xulcommandevent") || |
|
796 aEventType.LowerCaseEqualsLiteral("xulcommandevents")) |
|
797 return NS_NewDOMXULCommandEvent(aDOMEvent, aOwner, aPresContext, nullptr); |
|
798 if (aEventType.LowerCaseEqualsLiteral("commandevent") || |
|
799 aEventType.LowerCaseEqualsLiteral("commandevents")) |
|
800 return NS_NewDOMCommandEvent(aDOMEvent, aOwner, aPresContext, nullptr); |
|
801 if (aEventType.LowerCaseEqualsLiteral("elementreplace")) |
|
802 return NS_NewDOMElementReplaceEvent(aDOMEvent, aOwner, aPresContext, nullptr); |
|
803 if (aEventType.LowerCaseEqualsLiteral("datacontainerevent") || |
|
804 aEventType.LowerCaseEqualsLiteral("datacontainerevents")) |
|
805 return NS_NewDOMDataContainerEvent(aDOMEvent, aOwner, aPresContext, nullptr); |
|
806 if (aEventType.LowerCaseEqualsLiteral("messageevent")) |
|
807 return NS_NewDOMMessageEvent(aDOMEvent, aOwner, aPresContext, nullptr); |
|
808 if (aEventType.LowerCaseEqualsLiteral("notifypaintevent")) |
|
809 return NS_NewDOMNotifyPaintEvent(aDOMEvent, aOwner, aPresContext, nullptr); |
|
810 if (aEventType.LowerCaseEqualsLiteral("simplegestureevent")) |
|
811 return NS_NewDOMSimpleGestureEvent(aDOMEvent, aOwner, aPresContext, nullptr); |
|
812 if (aEventType.LowerCaseEqualsLiteral("beforeunloadevent")) |
|
813 return NS_NewDOMBeforeUnloadEvent(aDOMEvent, aOwner, aPresContext, nullptr); |
|
814 if (aEventType.LowerCaseEqualsLiteral("pagetransition")) |
|
815 return NS_NewDOMPageTransitionEvent(aDOMEvent, aOwner, aPresContext, nullptr); |
|
816 if (aEventType.LowerCaseEqualsLiteral("domtransaction")) |
|
817 return NS_NewDOMDOMTransactionEvent(aDOMEvent, aOwner, aPresContext, nullptr); |
|
818 if (aEventType.LowerCaseEqualsLiteral("scrollareaevent")) |
|
819 return NS_NewDOMScrollAreaEvent(aDOMEvent, aOwner, aPresContext, nullptr); |
|
820 if (aEventType.LowerCaseEqualsLiteral("popstateevent")) |
|
821 return NS_NewDOMPopStateEvent(aDOMEvent, aOwner, aPresContext, nullptr); |
|
822 if (aEventType.LowerCaseEqualsLiteral("closeevent")) |
|
823 return NS_NewDOMCloseEvent(aDOMEvent, aOwner, aPresContext, nullptr); |
|
824 if (aEventType.LowerCaseEqualsLiteral("touchevent") && |
|
825 TouchEvent::PrefEnabled()) |
|
826 return NS_NewDOMTouchEvent(aDOMEvent, aOwner, aPresContext, nullptr); |
|
827 if (aEventType.LowerCaseEqualsLiteral("hashchangeevent")) |
|
828 return NS_NewDOMHashChangeEvent(aDOMEvent, aOwner, aPresContext, nullptr); |
|
829 if (aEventType.LowerCaseEqualsLiteral("customevent")) |
|
830 return NS_NewDOMCustomEvent(aDOMEvent, aOwner, aPresContext, nullptr); |
|
831 if (aEventType.LowerCaseEqualsLiteral("mozsmsevent")) |
|
832 return NS_NewDOMMozSmsEvent(aDOMEvent, aOwner, aPresContext, nullptr); |
|
833 if (aEventType.LowerCaseEqualsLiteral("mozmmsevent")) |
|
834 return NS_NewDOMMozMmsEvent(aDOMEvent, aOwner, aPresContext, nullptr); |
|
835 if (aEventType.LowerCaseEqualsLiteral("storageevent")) { |
|
836 return NS_NewDOMStorageEvent(aDOMEvent, aOwner, aPresContext, nullptr); |
|
837 } |
|
838 // NEW EVENT TYPES SHOULD NOT BE ADDED HERE; THEY SHOULD USE ONLY EVENT |
|
839 // CONSTRUCTORS |
|
840 |
|
841 return NS_ERROR_DOM_NOT_SUPPORTED_ERR; |
|
842 } |
|
843 |
|
844 } // namespace mozilla |