/src/mozilla-central/dom/events/EventDispatcher.cpp
Line | Count | Source (jump to first uncovered line) |
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 "nsIContentInlines.h" |
13 | | #include "nsIDocument.h" |
14 | | #include "nsINode.h" |
15 | | #include "nsIScriptObjectPrincipal.h" |
16 | | #include "nsPIDOMWindow.h" |
17 | | #include "AnimationEvent.h" |
18 | | #include "BeforeUnloadEvent.h" |
19 | | #include "ClipboardEvent.h" |
20 | | #include "CommandEvent.h" |
21 | | #include "CompositionEvent.h" |
22 | | #include "DeviceMotionEvent.h" |
23 | | #include "DragEvent.h" |
24 | | #include "GeckoProfiler.h" |
25 | | #include "KeyboardEvent.h" |
26 | | #include "mozilla/ContentEvents.h" |
27 | | #include "mozilla/dom/CloseEvent.h" |
28 | | #include "mozilla/dom/CustomEvent.h" |
29 | | #include "mozilla/dom/DeviceOrientationEvent.h" |
30 | | #include "mozilla/dom/EventTarget.h" |
31 | | #include "mozilla/dom/FocusEvent.h" |
32 | | #include "mozilla/dom/HashChangeEvent.h" |
33 | | #include "mozilla/dom/InputEvent.h" |
34 | | #include "mozilla/dom/MessageEvent.h" |
35 | | #include "mozilla/dom/MouseScrollEvent.h" |
36 | | #include "mozilla/dom/MutationEvent.h" |
37 | | #include "mozilla/dom/NotifyPaintEvent.h" |
38 | | #include "mozilla/dom/PageTransitionEvent.h" |
39 | | #include "mozilla/dom/PointerEvent.h" |
40 | | #include "mozilla/dom/RootedDictionary.h" |
41 | | #include "mozilla/dom/ScrollAreaEvent.h" |
42 | | #include "mozilla/dom/SimpleGestureEvent.h" |
43 | | #include "mozilla/dom/ScriptSettings.h" |
44 | | #include "mozilla/dom/StorageEvent.h" |
45 | | #include "mozilla/dom/TimeEvent.h" |
46 | | #include "mozilla/dom/TouchEvent.h" |
47 | | #include "mozilla/dom/TransitionEvent.h" |
48 | | #include "mozilla/dom/WheelEvent.h" |
49 | | #include "mozilla/dom/WorkerPrivate.h" |
50 | | #include "mozilla/dom/XULCommandEvent.h" |
51 | | #include "mozilla/EventDispatcher.h" |
52 | | #include "mozilla/EventListenerManager.h" |
53 | | #include "mozilla/InternalMutationEvent.h" |
54 | | #include "mozilla/ipc/MessageChannel.h" |
55 | | #include "mozilla/MiscEvents.h" |
56 | | #include "mozilla/MouseEvents.h" |
57 | | #include "mozilla/Telemetry.h" |
58 | | #include "mozilla/TextEvents.h" |
59 | | #include "mozilla/TouchEvents.h" |
60 | | #include "mozilla/Unused.h" |
61 | | |
62 | | #ifdef MOZ_TASK_TRACER |
63 | | #include "GeckoTaskTracer.h" |
64 | | #include "mozilla/dom/Element.h" |
65 | | #include "mozilla/Likely.h" |
66 | | using namespace mozilla::tasktracer; |
67 | | #endif |
68 | | |
69 | | #ifdef MOZ_GECKO_PROFILER |
70 | | #include "ProfilerMarkerPayload.h" |
71 | | #endif |
72 | | |
73 | | namespace mozilla { |
74 | | |
75 | | using namespace dom; |
76 | | |
77 | | class ELMCreationDetector |
78 | | { |
79 | | public: |
80 | | ELMCreationDetector() |
81 | | // We can do this optimization only in the main thread. |
82 | | : mNonMainThread(!NS_IsMainThread()) |
83 | | , mInitialCount(mNonMainThread ? |
84 | | 0 : EventListenerManager::sMainThreadCreatedCount) |
85 | 0 | { |
86 | 0 | } |
87 | | |
88 | | bool MayHaveNewListenerManager() |
89 | 0 | { |
90 | 0 | return mNonMainThread || |
91 | 0 | mInitialCount != EventListenerManager::sMainThreadCreatedCount; |
92 | 0 | } |
93 | | |
94 | | bool IsMainThread() |
95 | 0 | { |
96 | 0 | return !mNonMainThread; |
97 | 0 | } |
98 | | |
99 | | private: |
100 | | bool mNonMainThread; |
101 | | uint32_t mInitialCount; |
102 | | }; |
103 | | |
104 | | static bool IsEventTargetChrome(EventTarget* aEventTarget, |
105 | | nsIDocument** aDocument = nullptr) |
106 | 0 | { |
107 | 0 | if (aDocument) { |
108 | 0 | *aDocument = nullptr; |
109 | 0 | } |
110 | 0 |
|
111 | 0 | nsIDocument* doc = nullptr; |
112 | 0 | if (nsCOMPtr<nsINode> node = do_QueryInterface(aEventTarget)) { |
113 | 0 | doc = node->OwnerDoc(); |
114 | 0 | } else if (nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aEventTarget)) { |
115 | 0 | doc = window->GetExtantDoc(); |
116 | 0 | } |
117 | 0 |
|
118 | 0 | // nsContentUtils::IsChromeDoc is null-safe. |
119 | 0 | bool isChrome = false; |
120 | 0 | if (doc) { |
121 | 0 | isChrome = nsContentUtils::IsChromeDoc(doc); |
122 | 0 | if (aDocument) { |
123 | 0 | nsCOMPtr<nsIDocument> retVal = doc; |
124 | 0 | retVal.swap(*aDocument); |
125 | 0 | } |
126 | 0 | } else if (nsCOMPtr<nsIScriptObjectPrincipal> sop = |
127 | 0 | do_QueryInterface(aEventTarget->GetOwnerGlobal())) { |
128 | 0 | isChrome = nsContentUtils::IsSystemPrincipal(sop->GetPrincipal()); |
129 | 0 | } |
130 | 0 | return isChrome; |
131 | 0 | } |
132 | | |
133 | | |
134 | | // EventTargetChainItem represents a single item in the event target chain. |
135 | | class EventTargetChainItem |
136 | | { |
137 | | private: |
138 | | explicit EventTargetChainItem(EventTarget* aTarget); |
139 | | public: |
140 | | EventTargetChainItem() |
141 | | : mItemFlags(0) |
142 | 0 | { |
143 | 0 | MOZ_COUNT_CTOR(EventTargetChainItem); |
144 | 0 | } |
145 | | |
146 | | ~EventTargetChainItem() |
147 | 0 | { |
148 | 0 | MOZ_COUNT_DTOR(EventTargetChainItem); |
149 | 0 | } |
150 | | |
151 | | static EventTargetChainItem* Create(nsTArray<EventTargetChainItem>& aChain, |
152 | | EventTarget* aTarget, |
153 | | EventTargetChainItem* aChild = nullptr) |
154 | 0 | { |
155 | 0 | // The last item which can handle the event must be aChild. |
156 | 0 | MOZ_ASSERT(GetLastCanHandleEventTarget(aChain) == aChild); |
157 | 0 | MOZ_ASSERT(!aTarget || aTarget == aTarget->GetTargetForEventTargetChain()); |
158 | 0 | EventTargetChainItem* etci = aChain.AppendElement(); |
159 | 0 | etci->mTarget = aTarget; |
160 | 0 | return etci; |
161 | 0 | } |
162 | | |
163 | | static void DestroyLast(nsTArray<EventTargetChainItem>& aChain, |
164 | | EventTargetChainItem* aItem) |
165 | 0 | { |
166 | 0 | uint32_t lastIndex = aChain.Length() - 1; |
167 | 0 | MOZ_ASSERT(&aChain[lastIndex] == aItem); |
168 | 0 | aChain.RemoveElementAt(lastIndex); |
169 | 0 | } |
170 | | |
171 | | static EventTargetChainItem* GetFirstCanHandleEventTarget( |
172 | | nsTArray<EventTargetChainItem>& aChain) |
173 | 0 | { |
174 | 0 | return &aChain[GetFirstCanHandleEventTargetIdx(aChain)]; |
175 | 0 | } |
176 | | |
177 | | static uint32_t GetFirstCanHandleEventTargetIdx(nsTArray<EventTargetChainItem>& aChain) |
178 | 0 | { |
179 | 0 | // aChain[i].PreHandleEventOnly() = true only when the target element wants |
180 | 0 | // PreHandleEvent and set mCanHandle=false. So we find the first element |
181 | 0 | // which can handle the event. |
182 | 0 | for (uint32_t i = 0; i < aChain.Length(); ++i) { |
183 | 0 | if (!aChain[i].PreHandleEventOnly()) { |
184 | 0 | return i; |
185 | 0 | } |
186 | 0 | } |
187 | 0 | MOZ_ASSERT(false); |
188 | 0 | return 0; |
189 | 0 | } |
190 | | |
191 | | static EventTargetChainItem* GetLastCanHandleEventTarget( |
192 | | nsTArray<EventTargetChainItem>& aChain) |
193 | 0 | { |
194 | 0 | // Fine the last item which can handle the event. |
195 | 0 | for (int32_t i = aChain.Length() - 1; i >= 0; --i) { |
196 | 0 | if (!aChain[i].PreHandleEventOnly()) { |
197 | 0 | return &aChain[i]; |
198 | 0 | } |
199 | 0 | } |
200 | 0 | return nullptr; |
201 | 0 | } |
202 | | |
203 | | bool IsValid() |
204 | 0 | { |
205 | 0 | NS_WARNING_ASSERTION(!!(mTarget), "Event target is not valid!"); |
206 | 0 | return !!(mTarget); |
207 | 0 | } |
208 | | |
209 | | EventTarget* GetNewTarget() |
210 | 0 | { |
211 | 0 | return mNewTarget; |
212 | 0 | } |
213 | | |
214 | | void SetNewTarget(EventTarget* aNewTarget) |
215 | 0 | { |
216 | 0 | mNewTarget = aNewTarget; |
217 | 0 | } |
218 | | |
219 | | EventTarget* GetRetargetedRelatedTarget() |
220 | 0 | { |
221 | 0 | return mRetargetedRelatedTarget; |
222 | 0 | } |
223 | | |
224 | | void SetRetargetedRelatedTarget(EventTarget* aTarget) |
225 | 0 | { |
226 | 0 | mRetargetedRelatedTarget = aTarget; |
227 | 0 | } |
228 | | |
229 | | void SetRetargetedTouchTarget(Maybe<nsTArray<RefPtr<EventTarget>>>&& aTargets) |
230 | 0 | { |
231 | 0 | mRetargetedTouchTargets = std::move(aTargets); |
232 | 0 | } |
233 | | |
234 | | bool HasRetargetTouchTargets() |
235 | 0 | { |
236 | 0 | return mRetargetedTouchTargets.isSome() || mInitialTargetTouches.isSome(); |
237 | 0 | } |
238 | | |
239 | | void RetargetTouchTargets(WidgetTouchEvent* aTouchEvent, Event* aDOMEvent) |
240 | 0 | { |
241 | 0 | MOZ_ASSERT(HasRetargetTouchTargets()); |
242 | 0 | MOZ_ASSERT(aTouchEvent, |
243 | 0 | "mRetargetedTouchTargets should be empty when dispatching non-touch events."); |
244 | 0 |
|
245 | 0 | if (mRetargetedTouchTargets.isSome()) { |
246 | 0 | WidgetTouchEvent::TouchArray& touches = aTouchEvent->mTouches; |
247 | 0 | MOZ_ASSERT(!touches.Length() || |
248 | 0 | touches.Length() == mRetargetedTouchTargets->Length()); |
249 | 0 | for (uint32_t i = 0; i < touches.Length(); ++i) { |
250 | 0 | touches[i]->mTarget = mRetargetedTouchTargets->ElementAt(i); |
251 | 0 | } |
252 | 0 | } |
253 | 0 |
|
254 | 0 | if (aDOMEvent) { |
255 | 0 | // The number of touch objects in targetTouches list may change depending |
256 | 0 | // on the retargeting. |
257 | 0 | TouchEvent* touchDOMEvent = static_cast<TouchEvent*>(aDOMEvent); |
258 | 0 | TouchList* targetTouches = touchDOMEvent->GetExistingTargetTouches(); |
259 | 0 | if (targetTouches) { |
260 | 0 | targetTouches->Clear(); |
261 | 0 | if (mInitialTargetTouches.isSome()) { |
262 | 0 | for (uint32_t i = 0; i < mInitialTargetTouches->Length(); ++i) { |
263 | 0 | Touch* touch = mInitialTargetTouches->ElementAt(i); |
264 | 0 | if (touch) { |
265 | 0 | touch->mTarget = touch->mOriginalTarget; |
266 | 0 | } |
267 | 0 | targetTouches->Append(touch); |
268 | 0 | } |
269 | 0 | } |
270 | 0 | } |
271 | 0 | } |
272 | 0 | } |
273 | | |
274 | | void SetInitialTargetTouches(Maybe<nsTArray<RefPtr<dom::Touch>>>&& |
275 | | aInitialTargetTouches) |
276 | 0 | { |
277 | 0 | mInitialTargetTouches = std::move(aInitialTargetTouches); |
278 | 0 | } |
279 | | |
280 | | void SetForceContentDispatch(bool aForce) |
281 | 0 | { |
282 | 0 | mFlags.mForceContentDispatch = aForce; |
283 | 0 | } |
284 | | |
285 | | bool ForceContentDispatch() |
286 | 0 | { |
287 | 0 | return mFlags.mForceContentDispatch; |
288 | 0 | } |
289 | | |
290 | | void SetWantsWillHandleEvent(bool aWants) |
291 | 0 | { |
292 | 0 | mFlags.mWantsWillHandleEvent = aWants; |
293 | 0 | } |
294 | | |
295 | | bool WantsWillHandleEvent() |
296 | 0 | { |
297 | 0 | return mFlags.mWantsWillHandleEvent; |
298 | 0 | } |
299 | | |
300 | | void SetWantsPreHandleEvent(bool aWants) |
301 | 0 | { |
302 | 0 | mFlags.mWantsPreHandleEvent = aWants; |
303 | 0 | } |
304 | | |
305 | | bool WantsPreHandleEvent() |
306 | 0 | { |
307 | 0 | return mFlags.mWantsPreHandleEvent; |
308 | 0 | } |
309 | | |
310 | | void SetPreHandleEventOnly(bool aWants) |
311 | 0 | { |
312 | 0 | mFlags.mPreHandleEventOnly = aWants; |
313 | 0 | } |
314 | | |
315 | | bool PreHandleEventOnly() |
316 | 0 | { |
317 | 0 | return mFlags.mPreHandleEventOnly; |
318 | 0 | } |
319 | | |
320 | | void SetRootOfClosedTree(bool aSet) |
321 | 0 | { |
322 | 0 | mFlags.mRootOfClosedTree = aSet; |
323 | 0 | } |
324 | | |
325 | | bool IsRootOfClosedTree() |
326 | 0 | { |
327 | 0 | return mFlags.mRootOfClosedTree; |
328 | 0 | } |
329 | | |
330 | | void SetItemInShadowTree(bool aSet) |
331 | 0 | { |
332 | 0 | mFlags.mItemInShadowTree = aSet; |
333 | 0 | } |
334 | | |
335 | | bool IsItemInShadowTree() |
336 | 0 | { |
337 | 0 | return mFlags.mItemInShadowTree; |
338 | 0 | } |
339 | | |
340 | | void SetIsSlotInClosedTree(bool aSet) |
341 | 0 | { |
342 | 0 | mFlags.mIsSlotInClosedTree = aSet; |
343 | 0 | } |
344 | | |
345 | | bool IsSlotInClosedTree() |
346 | 0 | { |
347 | 0 | return mFlags.mIsSlotInClosedTree; |
348 | 0 | } |
349 | | |
350 | | void SetIsChromeHandler(bool aSet) |
351 | 0 | { |
352 | 0 | mFlags.mIsChromeHandler = aSet; |
353 | 0 | } |
354 | | |
355 | | bool IsChromeHandler() |
356 | 0 | { |
357 | 0 | return mFlags.mIsChromeHandler; |
358 | 0 | } |
359 | | |
360 | | void SetMayHaveListenerManager(bool aMayHave) |
361 | 0 | { |
362 | 0 | mFlags.mMayHaveManager = aMayHave; |
363 | 0 | } |
364 | | |
365 | | bool MayHaveListenerManager() |
366 | 0 | { |
367 | 0 | return mFlags.mMayHaveManager; |
368 | 0 | } |
369 | | |
370 | | EventTarget* CurrentTarget() |
371 | 0 | { |
372 | 0 | return mTarget; |
373 | 0 | } |
374 | | |
375 | | /** |
376 | | * Dispatches event through the event target chain. |
377 | | * Handles capture, target and bubble phases both in default |
378 | | * and system event group and calls also PostHandleEvent for each |
379 | | * item in the chain. |
380 | | */ |
381 | | static void HandleEventTargetChain(nsTArray<EventTargetChainItem>& aChain, |
382 | | EventChainPostVisitor& aVisitor, |
383 | | EventDispatchingCallback* aCallback, |
384 | | ELMCreationDetector& aCd); |
385 | | |
386 | | /** |
387 | | * Resets aVisitor object and calls GetEventTargetParent. |
388 | | * Copies mItemFlags and mItemData to the current EventTargetChainItem. |
389 | | */ |
390 | | void GetEventTargetParent(EventChainPreVisitor& aVisitor); |
391 | | |
392 | | /** |
393 | | * Calls PreHandleEvent for those items which called SetWantsPreHandleEvent. |
394 | | */ |
395 | | void PreHandleEvent(EventChainVisitor& aVisitor); |
396 | | |
397 | | /** |
398 | | * If the current item in the event target chain has an event listener |
399 | | * manager, this method calls EventListenerManager::HandleEvent(). |
400 | | */ |
401 | | void HandleEvent(EventChainPostVisitor& aVisitor, |
402 | | ELMCreationDetector& aCd) |
403 | 0 | { |
404 | 0 | if (WantsWillHandleEvent()) { |
405 | 0 | mTarget->WillHandleEvent(aVisitor); |
406 | 0 | } |
407 | 0 | if (aVisitor.mEvent->PropagationStopped()) { |
408 | 0 | return; |
409 | 0 | } |
410 | 0 | if (aVisitor.mEvent->mFlags.mOnlySystemGroupDispatchInContent && |
411 | 0 | !aVisitor.mEvent->mFlags.mInSystemGroup && |
412 | 0 | !IsCurrentTargetChrome()) { |
413 | 0 | return; |
414 | 0 | } |
415 | 0 | if (!mManager) { |
416 | 0 | if (!MayHaveListenerManager() && !aCd.MayHaveNewListenerManager()) { |
417 | 0 | return; |
418 | 0 | } |
419 | 0 | mManager = mTarget->GetExistingListenerManager(); |
420 | 0 | } |
421 | 0 | if (mManager) { |
422 | 0 | NS_ASSERTION(aVisitor.mEvent->mCurrentTarget == nullptr, |
423 | 0 | "CurrentTarget should be null!"); |
424 | 0 | mManager->HandleEvent(aVisitor.mPresContext, aVisitor.mEvent, |
425 | 0 | &aVisitor.mDOMEvent, |
426 | 0 | CurrentTarget(), |
427 | 0 | &aVisitor.mEventStatus, |
428 | 0 | IsItemInShadowTree()); |
429 | 0 | NS_ASSERTION(aVisitor.mEvent->mCurrentTarget == nullptr, |
430 | 0 | "CurrentTarget should be null!"); |
431 | 0 | } |
432 | 0 | } |
433 | | |
434 | | /** |
435 | | * Copies mItemFlags and mItemData to aVisitor and calls PostHandleEvent. |
436 | | */ |
437 | | void PostHandleEvent(EventChainPostVisitor& aVisitor); |
438 | | |
439 | | private: |
440 | | nsCOMPtr<EventTarget> mTarget; |
441 | | nsCOMPtr<EventTarget> mRetargetedRelatedTarget; |
442 | | Maybe<nsTArray<RefPtr<EventTarget>>> mRetargetedTouchTargets; |
443 | | Maybe<nsTArray<RefPtr<dom::Touch>>> mInitialTargetTouches; |
444 | | |
445 | | class EventTargetChainFlags |
446 | | { |
447 | | public: |
448 | | explicit EventTargetChainFlags() |
449 | 0 | { |
450 | 0 | SetRawFlags(0); |
451 | 0 | } |
452 | | // Cached flags for each EventTargetChainItem which are set when calling |
453 | | // GetEventTargetParent to create event target chain. They are used to |
454 | | // manage or speedup event dispatching. |
455 | | bool mForceContentDispatch : 1; |
456 | | bool mWantsWillHandleEvent : 1; |
457 | | bool mMayHaveManager : 1; |
458 | | bool mChechedIfChrome : 1; |
459 | | bool mIsChromeContent : 1; |
460 | | bool mWantsPreHandleEvent : 1; |
461 | | bool mPreHandleEventOnly : 1; |
462 | | bool mRootOfClosedTree : 1; |
463 | | bool mItemInShadowTree : 1; |
464 | | bool mIsSlotInClosedTree : 1; |
465 | | bool mIsChromeHandler : 1; |
466 | | private: |
467 | | typedef uint32_t RawFlags; |
468 | | void SetRawFlags(RawFlags aRawFlags) |
469 | 0 | { |
470 | 0 | static_assert(sizeof(EventTargetChainFlags) <= sizeof(RawFlags), |
471 | 0 | "EventTargetChainFlags must not be bigger than the RawFlags"); |
472 | 0 | memcpy(this, &aRawFlags, sizeof(EventTargetChainFlags)); |
473 | 0 | } |
474 | | } mFlags; |
475 | | |
476 | | uint16_t mItemFlags; |
477 | | nsCOMPtr<nsISupports> mItemData; |
478 | | // Event retargeting must happen whenever mNewTarget is non-null. |
479 | | nsCOMPtr<EventTarget> mNewTarget; |
480 | | // Cache mTarget's event listener manager. |
481 | | RefPtr<EventListenerManager> mManager; |
482 | | |
483 | | bool IsCurrentTargetChrome() |
484 | 0 | { |
485 | 0 | if (!mFlags.mChechedIfChrome) { |
486 | 0 | mFlags.mChechedIfChrome = true; |
487 | 0 | if (IsEventTargetChrome(mTarget)) { |
488 | 0 | mFlags.mIsChromeContent = true; |
489 | 0 | } |
490 | 0 | } |
491 | 0 | return mFlags.mIsChromeContent; |
492 | 0 | } |
493 | | }; |
494 | | |
495 | | void |
496 | | EventTargetChainItem::GetEventTargetParent(EventChainPreVisitor& aVisitor) |
497 | 0 | { |
498 | 0 | aVisitor.Reset(); |
499 | 0 | mTarget->GetEventTargetParent(aVisitor); |
500 | 0 | SetForceContentDispatch(aVisitor.mForceContentDispatch); |
501 | 0 | SetWantsWillHandleEvent(aVisitor.mWantsWillHandleEvent); |
502 | 0 | SetMayHaveListenerManager(aVisitor.mMayHaveListenerManager); |
503 | 0 | SetWantsPreHandleEvent(aVisitor.mWantsPreHandleEvent); |
504 | 0 | SetPreHandleEventOnly(aVisitor.mWantsPreHandleEvent && !aVisitor.mCanHandle); |
505 | 0 | SetRootOfClosedTree(aVisitor.mRootOfClosedTree); |
506 | 0 | SetItemInShadowTree(aVisitor.mItemInShadowTree); |
507 | 0 | SetRetargetedRelatedTarget(aVisitor.mRetargetedRelatedTarget); |
508 | 0 | SetRetargetedTouchTarget(std::move(aVisitor.mRetargetedTouchTargets)); |
509 | 0 | mItemFlags = aVisitor.mItemFlags; |
510 | 0 | mItemData = aVisitor.mItemData; |
511 | 0 | } |
512 | | |
513 | | void |
514 | | EventTargetChainItem::PreHandleEvent(EventChainVisitor& aVisitor) |
515 | 0 | { |
516 | 0 | if (!WantsPreHandleEvent()) { |
517 | 0 | return; |
518 | 0 | } |
519 | 0 | aVisitor.mItemFlags = mItemFlags; |
520 | 0 | aVisitor.mItemData = mItemData; |
521 | 0 | Unused << mTarget->PreHandleEvent(aVisitor); |
522 | 0 | } |
523 | | |
524 | | void |
525 | | EventTargetChainItem::PostHandleEvent(EventChainPostVisitor& aVisitor) |
526 | 0 | { |
527 | 0 | aVisitor.mItemFlags = mItemFlags; |
528 | 0 | aVisitor.mItemData = mItemData; |
529 | 0 | mTarget->PostHandleEvent(aVisitor); |
530 | 0 | } |
531 | | |
532 | | void |
533 | | EventTargetChainItem::HandleEventTargetChain( |
534 | | nsTArray<EventTargetChainItem>& aChain, |
535 | | EventChainPostVisitor& aVisitor, |
536 | | EventDispatchingCallback* aCallback, |
537 | | ELMCreationDetector& aCd) |
538 | 0 | { |
539 | 0 | // Save the target so that it can be restored later. |
540 | 0 | nsCOMPtr<EventTarget> firstTarget = aVisitor.mEvent->mTarget; |
541 | 0 | nsCOMPtr<EventTarget> firstRelatedTarget = aVisitor.mEvent->mRelatedTarget; |
542 | 0 | Maybe<AutoTArray<nsCOMPtr<EventTarget>, 10>> firstTouchTargets; |
543 | 0 | WidgetTouchEvent* touchEvent = nullptr; |
544 | 0 | if (aVisitor.mEvent->mClass == eTouchEventClass) { |
545 | 0 | touchEvent = aVisitor.mEvent->AsTouchEvent(); |
546 | 0 | if (!aVisitor.mEvent->mFlags.mInSystemGroup) { |
547 | 0 | firstTouchTargets.emplace(); |
548 | 0 | WidgetTouchEvent* touchEvent = aVisitor.mEvent->AsTouchEvent(); |
549 | 0 | WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches; |
550 | 0 | for (uint32_t i = 0; i < touches.Length(); ++i) { |
551 | 0 | firstTouchTargets->AppendElement(touches[i]->mTarget); |
552 | 0 | } |
553 | 0 | } |
554 | 0 | } |
555 | 0 |
|
556 | 0 | uint32_t chainLength = aChain.Length(); |
557 | 0 | uint32_t firstCanHandleEventTargetIdx = |
558 | 0 | EventTargetChainItem::GetFirstCanHandleEventTargetIdx(aChain); |
559 | 0 |
|
560 | 0 | // Capture |
561 | 0 | aVisitor.mEvent->mFlags.mInCapturePhase = true; |
562 | 0 | aVisitor.mEvent->mFlags.mInBubblingPhase = false; |
563 | 0 | for (uint32_t i = chainLength - 1; i > firstCanHandleEventTargetIdx; --i) { |
564 | 0 | EventTargetChainItem& item = aChain[i]; |
565 | 0 | if (item.PreHandleEventOnly()) { |
566 | 0 | continue; |
567 | 0 | } |
568 | 0 | if ((!aVisitor.mEvent->mFlags.mNoContentDispatch || |
569 | 0 | item.ForceContentDispatch()) && |
570 | 0 | !aVisitor.mEvent->PropagationStopped()) { |
571 | 0 | item.HandleEvent(aVisitor, aCd); |
572 | 0 | } |
573 | 0 |
|
574 | 0 | if (item.GetNewTarget()) { |
575 | 0 | // item is at anonymous boundary. Need to retarget for the child items. |
576 | 0 | for (uint32_t j = i; j > 0; --j) { |
577 | 0 | uint32_t childIndex = j - 1; |
578 | 0 | EventTarget* newTarget = aChain[childIndex].GetNewTarget(); |
579 | 0 | if (newTarget) { |
580 | 0 | aVisitor.mEvent->mTarget = newTarget; |
581 | 0 | break; |
582 | 0 | } |
583 | 0 | } |
584 | 0 | } |
585 | 0 |
|
586 | 0 | // https://dom.spec.whatwg.org/#dispatching-events |
587 | 0 | // Step 14.2 |
588 | 0 | // "Set event's relatedTarget to tuple's relatedTarget." |
589 | 0 | // Note, the initial retargeting was done already when creating |
590 | 0 | // event target chain, so we need to do this only after calling |
591 | 0 | // HandleEvent, not before, like in the specification. |
592 | 0 | if (item.GetRetargetedRelatedTarget()) { |
593 | 0 | bool found = false; |
594 | 0 | for (uint32_t j = i; j > 0; --j) { |
595 | 0 | uint32_t childIndex = j - 1; |
596 | 0 | EventTarget* relatedTarget = |
597 | 0 | aChain[childIndex].GetRetargetedRelatedTarget(); |
598 | 0 | if (relatedTarget) { |
599 | 0 | found = true; |
600 | 0 | aVisitor.mEvent->mRelatedTarget = relatedTarget; |
601 | 0 | break; |
602 | 0 | } |
603 | 0 | } |
604 | 0 | if (!found) { |
605 | 0 | aVisitor.mEvent->mRelatedTarget = |
606 | 0 | aVisitor.mEvent->mOriginalRelatedTarget; |
607 | 0 | } |
608 | 0 | } |
609 | 0 |
|
610 | 0 | if (item.HasRetargetTouchTargets()) { |
611 | 0 | bool found = false; |
612 | 0 | for (uint32_t j = i; j > 0; --j) { |
613 | 0 | uint32_t childIndex = j - 1; |
614 | 0 | if (aChain[childIndex].HasRetargetTouchTargets()) { |
615 | 0 | found = true; |
616 | 0 | aChain[childIndex].RetargetTouchTargets(touchEvent, |
617 | 0 | aVisitor.mDOMEvent); |
618 | 0 | break; |
619 | 0 | } |
620 | 0 | } |
621 | 0 | if (!found) { |
622 | 0 | WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches; |
623 | 0 | for (uint32_t i = 0; i < touches.Length(); ++i) { |
624 | 0 | touches[i]->mTarget = touches[i]->mOriginalTarget; |
625 | 0 | } |
626 | 0 | } |
627 | 0 | } |
628 | 0 | } |
629 | 0 |
|
630 | 0 | // Target |
631 | 0 | aVisitor.mEvent->mFlags.mInBubblingPhase = true; |
632 | 0 | EventTargetChainItem& targetItem = aChain[firstCanHandleEventTargetIdx]; |
633 | 0 | // Need to explicitly retarget touch targets so that initial targets get set |
634 | 0 | // properly in case nothing else retargeted touches. |
635 | 0 | if (targetItem.HasRetargetTouchTargets()) { |
636 | 0 | targetItem.RetargetTouchTargets(touchEvent, aVisitor.mDOMEvent); |
637 | 0 | } |
638 | 0 | if (!aVisitor.mEvent->PropagationStopped() && |
639 | 0 | (!aVisitor.mEvent->mFlags.mNoContentDispatch || |
640 | 0 | targetItem.ForceContentDispatch())) { |
641 | 0 | targetItem.HandleEvent(aVisitor, aCd); |
642 | 0 | } |
643 | 0 | if (aVisitor.mEvent->mFlags.mInSystemGroup) { |
644 | 0 | targetItem.PostHandleEvent(aVisitor); |
645 | 0 | } |
646 | 0 |
|
647 | 0 | // Bubble |
648 | 0 | aVisitor.mEvent->mFlags.mInCapturePhase = false; |
649 | 0 | for (uint32_t i = firstCanHandleEventTargetIdx + 1; i < chainLength; ++i) { |
650 | 0 | EventTargetChainItem& item = aChain[i]; |
651 | 0 | if (item.PreHandleEventOnly()) { |
652 | 0 | continue; |
653 | 0 | } |
654 | 0 | EventTarget* newTarget = item.GetNewTarget(); |
655 | 0 | if (newTarget) { |
656 | 0 | // Item is at anonymous boundary. Need to retarget for the current item |
657 | 0 | // and for parent items. |
658 | 0 | aVisitor.mEvent->mTarget = newTarget; |
659 | 0 | } |
660 | 0 |
|
661 | 0 | // https://dom.spec.whatwg.org/#dispatching-events |
662 | 0 | // Step 15.2 |
663 | 0 | // "Set event's relatedTarget to tuple's relatedTarget." |
664 | 0 | EventTarget* relatedTarget = item.GetRetargetedRelatedTarget(); |
665 | 0 | if (relatedTarget) { |
666 | 0 | aVisitor.mEvent->mRelatedTarget = relatedTarget; |
667 | 0 | } |
668 | 0 |
|
669 | 0 | if (item.HasRetargetTouchTargets()) { |
670 | 0 | item.RetargetTouchTargets(touchEvent, aVisitor.mDOMEvent); |
671 | 0 | } |
672 | 0 |
|
673 | 0 | if (aVisitor.mEvent->mFlags.mBubbles || newTarget) { |
674 | 0 | if ((!aVisitor.mEvent->mFlags.mNoContentDispatch || |
675 | 0 | item.ForceContentDispatch()) && |
676 | 0 | !aVisitor.mEvent->PropagationStopped()) { |
677 | 0 | item.HandleEvent(aVisitor, aCd); |
678 | 0 | } |
679 | 0 | if (aVisitor.mEvent->mFlags.mInSystemGroup) { |
680 | 0 | item.PostHandleEvent(aVisitor); |
681 | 0 | } |
682 | 0 | } |
683 | 0 | } |
684 | 0 | aVisitor.mEvent->mFlags.mInBubblingPhase = false; |
685 | 0 |
|
686 | 0 | if (!aVisitor.mEvent->mFlags.mInSystemGroup && |
687 | 0 | aVisitor.mEvent->IsAllowedToDispatchInSystemGroup()) { |
688 | 0 | // Dispatch to the system event group. Make sure to clear the |
689 | 0 | // STOP_DISPATCH flag since this resets for each event group. |
690 | 0 | aVisitor.mEvent->mFlags.mPropagationStopped = false; |
691 | 0 | aVisitor.mEvent->mFlags.mImmediatePropagationStopped = false; |
692 | 0 |
|
693 | 0 | // Setting back the original target of the event. |
694 | 0 | aVisitor.mEvent->mTarget = aVisitor.mEvent->mOriginalTarget; |
695 | 0 | aVisitor.mEvent->mRelatedTarget = aVisitor.mEvent->mOriginalRelatedTarget; |
696 | 0 | if (firstTouchTargets) { |
697 | 0 | WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches; |
698 | 0 | for (uint32_t i = 0; i < touches.Length(); ++i) { |
699 | 0 | touches[i]->mTarget = touches[i]->mOriginalTarget; |
700 | 0 | } |
701 | 0 | } |
702 | 0 |
|
703 | 0 | // Special handling if PresShell (or some other caller) |
704 | 0 | // used a callback object. |
705 | 0 | if (aCallback) { |
706 | 0 | aCallback->HandleEvent(aVisitor); |
707 | 0 | } |
708 | 0 |
|
709 | 0 | // Retarget for system event group (which does the default handling too). |
710 | 0 | // Setting back the target which was used also for default event group. |
711 | 0 | aVisitor.mEvent->mTarget = firstTarget; |
712 | 0 | aVisitor.mEvent->mRelatedTarget = firstRelatedTarget; |
713 | 0 | if (firstTouchTargets) { |
714 | 0 | WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches; |
715 | 0 | for (uint32_t i = 0; i < firstTouchTargets->Length(); ++i) { |
716 | 0 | touches[i]->mTarget = firstTouchTargets->ElementAt(i); |
717 | 0 | } |
718 | 0 | } |
719 | 0 |
|
720 | 0 | aVisitor.mEvent->mFlags.mInSystemGroup = true; |
721 | 0 | HandleEventTargetChain(aChain, |
722 | 0 | aVisitor, |
723 | 0 | aCallback, |
724 | 0 | aCd); |
725 | 0 | aVisitor.mEvent->mFlags.mInSystemGroup = false; |
726 | 0 |
|
727 | 0 | // After dispatch, clear all the propagation flags so that |
728 | 0 | // system group listeners don't affect to the event. |
729 | 0 | aVisitor.mEvent->mFlags.mPropagationStopped = false; |
730 | 0 | aVisitor.mEvent->mFlags.mImmediatePropagationStopped = false; |
731 | 0 | } |
732 | 0 | } |
733 | | |
734 | | static nsTArray<EventTargetChainItem>* sCachedMainThreadChain = nullptr; |
735 | | |
736 | | /* static */ void |
737 | | EventDispatcher::Shutdown() |
738 | 0 | { |
739 | 0 | delete sCachedMainThreadChain; |
740 | 0 | sCachedMainThreadChain = nullptr; |
741 | 0 | } |
742 | | |
743 | | EventTargetChainItem* |
744 | | EventTargetChainItemForChromeTarget(nsTArray<EventTargetChainItem>& aChain, |
745 | | nsINode* aNode, |
746 | | EventTargetChainItem* aChild = nullptr) |
747 | 0 | { |
748 | 0 | if (!aNode->IsInComposedDoc()) { |
749 | 0 | return nullptr; |
750 | 0 | } |
751 | 0 | nsPIDOMWindowInner* win = aNode->OwnerDoc()->GetInnerWindow(); |
752 | 0 | EventTarget* piTarget = win ? win->GetParentTarget() : nullptr; |
753 | 0 | NS_ENSURE_TRUE(piTarget, nullptr); |
754 | 0 |
|
755 | 0 | EventTargetChainItem* etci = |
756 | 0 | EventTargetChainItem::Create(aChain, |
757 | 0 | piTarget->GetTargetForEventTargetChain(), |
758 | 0 | aChild); |
759 | 0 | if (!etci->IsValid()) { |
760 | 0 | EventTargetChainItem::DestroyLast(aChain, etci); |
761 | 0 | return nullptr; |
762 | 0 | } |
763 | 0 | return etci; |
764 | 0 | } |
765 | | |
766 | | /* static */ EventTargetChainItem* |
767 | | MayRetargetToChromeIfCanNotHandleEvent( |
768 | | nsTArray<EventTargetChainItem>& aChain, EventChainPreVisitor& aPreVisitor, |
769 | | EventTargetChainItem* aTargetEtci, EventTargetChainItem* aChildEtci, |
770 | | nsINode* aContent) |
771 | 0 | { |
772 | 0 | if (!aPreVisitor.mWantsPreHandleEvent) { |
773 | 0 | // Keep EventTargetChainItem if we need to call PreHandleEvent on it. |
774 | 0 | EventTargetChainItem::DestroyLast(aChain, aTargetEtci); |
775 | 0 | } |
776 | 0 | if (aPreVisitor.mAutomaticChromeDispatch && aContent) { |
777 | 0 | aPreVisitor.mRelatedTargetRetargetedInCurrentScope = false; |
778 | 0 | // Event target couldn't handle the event. Try to propagate to chrome. |
779 | 0 | EventTargetChainItem* chromeTargetEtci = |
780 | 0 | EventTargetChainItemForChromeTarget(aChain, aContent, aChildEtci); |
781 | 0 | if (chromeTargetEtci) { |
782 | 0 | // If we propagate to chrome, need to ensure we mark |
783 | 0 | // EventTargetChainItem to be chrome handler so that event.composedPath() |
784 | 0 | // can return the right value. |
785 | 0 | chromeTargetEtci->SetIsChromeHandler(true); |
786 | 0 | chromeTargetEtci->GetEventTargetParent(aPreVisitor); |
787 | 0 | return chromeTargetEtci; |
788 | 0 | } |
789 | 0 | } |
790 | 0 | return nullptr; |
791 | 0 | } |
792 | | |
793 | | static bool |
794 | | ShouldClearTargets(WidgetEvent* aEvent) |
795 | 0 | { |
796 | 0 | nsCOMPtr<nsIContent> finalTarget; |
797 | 0 | nsCOMPtr<nsIContent> finalRelatedTarget; |
798 | 0 | if ((finalTarget = do_QueryInterface(aEvent->mTarget)) && |
799 | 0 | finalTarget->SubtreeRoot()->IsShadowRoot()) { |
800 | 0 | return true; |
801 | 0 | } |
802 | 0 | |
803 | 0 | if ((finalRelatedTarget = |
804 | 0 | do_QueryInterface(aEvent->mRelatedTarget)) && |
805 | 0 | finalRelatedTarget->SubtreeRoot()->IsShadowRoot()) { |
806 | 0 | return true; |
807 | 0 | } |
808 | 0 | //XXXsmaug Check also all the touch objects. |
809 | 0 | |
810 | 0 | return false; |
811 | 0 | } |
812 | | |
813 | | /* static */ nsresult |
814 | | EventDispatcher::Dispatch(nsISupports* aTarget, |
815 | | nsPresContext* aPresContext, |
816 | | WidgetEvent* aEvent, |
817 | | Event* aDOMEvent, |
818 | | nsEventStatus* aEventStatus, |
819 | | EventDispatchingCallback* aCallback, |
820 | | nsTArray<EventTarget*>* aTargets) |
821 | 0 | { |
822 | 0 | AUTO_PROFILER_LABEL("EventDispatcher::Dispatch", OTHER); |
823 | 0 |
|
824 | 0 | NS_ASSERTION(aEvent, "Trying to dispatch without WidgetEvent!"); |
825 | 0 | NS_ENSURE_TRUE(!aEvent->mFlags.mIsBeingDispatched, |
826 | 0 | NS_ERROR_DOM_INVALID_STATE_ERR); |
827 | 0 | NS_ASSERTION(!aTargets || !aEvent->mMessage, "Wrong parameters!"); |
828 | 0 |
|
829 | 0 | // If we're dispatching an already created DOMEvent object, make |
830 | 0 | // sure it is initialized! |
831 | 0 | // If aTargets is non-null, the event isn't going to be dispatched. |
832 | 0 | NS_ENSURE_TRUE(aEvent->mMessage || !aDOMEvent || aTargets, |
833 | 0 | NS_ERROR_DOM_INVALID_STATE_ERR); |
834 | 0 |
|
835 | 0 | // Events shall not be fired while we are in stable state to prevent anything |
836 | 0 | // visible from the scripts. |
837 | 0 | MOZ_ASSERT(!nsContentUtils::IsInStableOrMetaStableState()); |
838 | 0 | NS_ENSURE_TRUE(!nsContentUtils::IsInStableOrMetaStableState(), |
839 | 0 | NS_ERROR_DOM_INVALID_STATE_ERR); |
840 | 0 |
|
841 | | #ifdef MOZ_TASK_TRACER |
842 | | if (MOZ_UNLIKELY(mozilla::tasktracer::IsStartLogging())) { |
843 | | nsAutoCString eventType; |
844 | | nsAutoString eventTypeU16; |
845 | | if (aDOMEvent) { |
846 | | aDOMEvent->GetType(eventTypeU16); |
847 | | } else { |
848 | | Event::GetWidgetEventType(aEvent, eventTypeU16); |
849 | | } |
850 | | eventType = NS_ConvertUTF16toUTF8(eventTypeU16); |
851 | | |
852 | | nsCOMPtr<Element> element = do_QueryInterface(aTarget); |
853 | | nsAutoString elementId; |
854 | | nsAutoString elementTagName; |
855 | | if (element) { |
856 | | element->GetId(elementId); |
857 | | element->GetTagName(elementTagName); |
858 | | } |
859 | | AddLabel("Event [%s] dispatched at target [id:%s tag:%s]", |
860 | | eventType.get(), |
861 | | NS_ConvertUTF16toUTF8(elementId).get(), |
862 | | NS_ConvertUTF16toUTF8(elementTagName).get()); |
863 | | } |
864 | | #endif |
865 | |
|
866 | 0 | nsCOMPtr<EventTarget> target = do_QueryInterface(aTarget); |
867 | 0 |
|
868 | 0 | bool retargeted = false; |
869 | 0 |
|
870 | 0 | if (aEvent->mFlags.mRetargetToNonNativeAnonymous) { |
871 | 0 | nsCOMPtr<nsIContent> content = do_QueryInterface(target); |
872 | 0 | if (content && content->IsInNativeAnonymousSubtree()) { |
873 | 0 | nsCOMPtr<EventTarget> newTarget = |
874 | 0 | do_QueryInterface(content->FindFirstNonChromeOnlyAccessContent()); |
875 | 0 | NS_ENSURE_STATE(newTarget); |
876 | 0 |
|
877 | 0 | aEvent->mOriginalTarget = target; |
878 | 0 | target = newTarget; |
879 | 0 | retargeted = true; |
880 | 0 | } |
881 | 0 | } |
882 | 0 |
|
883 | 0 | if (aEvent->mFlags.mOnlyChromeDispatch) { |
884 | 0 | nsCOMPtr<nsIDocument> doc; |
885 | 0 | if (!IsEventTargetChrome(target, getter_AddRefs(doc)) && doc) { |
886 | 0 | nsPIDOMWindowInner* win = doc->GetInnerWindow(); |
887 | 0 | // If we can't dispatch the event to chrome, do nothing. |
888 | 0 | EventTarget* piTarget = win ? win->GetParentTarget() : nullptr; |
889 | 0 | if (!piTarget) { |
890 | 0 | return NS_OK; |
891 | 0 | } |
892 | 0 | |
893 | 0 | // Set the target to be the original dispatch target, |
894 | 0 | aEvent->mTarget = target; |
895 | 0 | // but use chrome event handler or TabChildMessageManager for event target chain. |
896 | 0 | target = piTarget; |
897 | 0 | } else if (NS_WARN_IF(!doc)) { |
898 | 0 | return NS_ERROR_UNEXPECTED; |
899 | 0 | } |
900 | 0 | } |
901 | 0 | |
902 | | #ifdef DEBUG |
903 | | if (NS_IsMainThread() && |
904 | | aEvent->mMessage != eVoidEvent && |
905 | | !nsContentUtils::IsSafeToRunScript()) { |
906 | | nsCOMPtr<nsINode> node = do_QueryInterface(target); |
907 | | if (!node) { |
908 | | // If the target is not a node, just go ahead and crash. There really |
909 | | // shouldn't be any other event targets in documents that are not being |
910 | | // rendered or scripted. |
911 | | MOZ_CRASH("This is unsafe! Fix the caller!"); |
912 | | } else { |
913 | | // If this is a node, it's possible that this is some sort of DOM tree |
914 | | // that is never accessed by script (for example an SVG image or XBL |
915 | | // binding document or whatnot). We really only want to warn/assert here |
916 | | // if there might be actual scripted listeners for this event, so restrict |
917 | | // the warnings/asserts to the case when script can or once could touch |
918 | | // this node's document. |
919 | | nsIDocument* doc = node->OwnerDoc(); |
920 | | bool hasHadScriptHandlingObject; |
921 | | nsIGlobalObject* global = |
922 | | doc->GetScriptHandlingObject(hasHadScriptHandlingObject); |
923 | | if (global || hasHadScriptHandlingObject) { |
924 | | if (nsContentUtils::IsChromeDoc(doc)) { |
925 | | NS_WARNING("Fix the caller!"); |
926 | | } else { |
927 | | MOZ_CRASH("This is unsafe! Fix the caller!"); |
928 | | } |
929 | | } |
930 | | } |
931 | | } |
932 | | |
933 | | if (aDOMEvent) { |
934 | | WidgetEvent* innerEvent = aDOMEvent->WidgetEventPtr(); |
935 | | NS_ASSERTION(innerEvent == aEvent, |
936 | | "The inner event of aDOMEvent is not the same as aEvent!"); |
937 | | } |
938 | | #endif |
939 | | |
940 | 0 | nsresult rv = NS_OK; |
941 | 0 | bool externalDOMEvent = !!(aDOMEvent); |
942 | 0 |
|
943 | 0 | // If we have a PresContext, make sure it doesn't die before |
944 | 0 | // event dispatching is finished. |
945 | 0 | RefPtr<nsPresContext> kungFuDeathGrip(aPresContext); |
946 | 0 |
|
947 | 0 | ELMCreationDetector cd; |
948 | 0 | nsTArray<EventTargetChainItem> chain; |
949 | 0 | if (cd.IsMainThread()) { |
950 | 0 | if (!sCachedMainThreadChain) { |
951 | 0 | sCachedMainThreadChain = new nsTArray<EventTargetChainItem>(); |
952 | 0 | } |
953 | 0 | chain.SwapElements(*sCachedMainThreadChain); |
954 | 0 | chain.SetCapacity(128); |
955 | 0 | } |
956 | 0 |
|
957 | 0 | // Create the event target chain item for the event target. |
958 | 0 | EventTargetChainItem* targetEtci = |
959 | 0 | EventTargetChainItem::Create(chain, target->GetTargetForEventTargetChain()); |
960 | 0 | MOZ_ASSERT(&chain[0] == targetEtci); |
961 | 0 | if (!targetEtci->IsValid()) { |
962 | 0 | EventTargetChainItem::DestroyLast(chain, targetEtci); |
963 | 0 | return NS_ERROR_FAILURE; |
964 | 0 | } |
965 | 0 | |
966 | 0 | // Make sure that Event::target and Event::originalTarget |
967 | 0 | // point to the last item in the chain. |
968 | 0 | if (!aEvent->mTarget) { |
969 | 0 | // Note, CurrentTarget() points always to the object returned by |
970 | 0 | // GetTargetForEventTargetChain(). |
971 | 0 | aEvent->mTarget = targetEtci->CurrentTarget(); |
972 | 0 | } else { |
973 | 0 | // XXX But if the target is already set, use that. This is a hack |
974 | 0 | // for the 'load', 'beforeunload' and 'unload' events, |
975 | 0 | // which are dispatched to |window| but have document as their target. |
976 | 0 | // |
977 | 0 | // Make sure that the event target points to the right object. |
978 | 0 | aEvent->mTarget = aEvent->mTarget->GetTargetForEventTargetChain(); |
979 | 0 | NS_ENSURE_STATE(aEvent->mTarget); |
980 | 0 | } |
981 | 0 |
|
982 | 0 | if (retargeted) { |
983 | 0 | aEvent->mOriginalTarget = |
984 | 0 | aEvent->mOriginalTarget->GetTargetForEventTargetChain(); |
985 | 0 | NS_ENSURE_STATE(aEvent->mOriginalTarget); |
986 | 0 | } |
987 | 0 | else { |
988 | 0 | aEvent->mOriginalTarget = aEvent->mTarget; |
989 | 0 | } |
990 | 0 |
|
991 | 0 | aEvent->mOriginalRelatedTarget = aEvent->mRelatedTarget; |
992 | 0 |
|
993 | 0 | bool clearTargets = false; |
994 | 0 |
|
995 | 0 | nsCOMPtr<nsIContent> content = do_QueryInterface(aEvent->mOriginalTarget); |
996 | 0 | bool isInAnon = content && content->IsInAnonymousSubtree(); |
997 | 0 |
|
998 | 0 | aEvent->mFlags.mIsBeingDispatched = true; |
999 | 0 |
|
1000 | 0 | // Create visitor object and start event dispatching. |
1001 | 0 | // GetEventTargetParent for the original target. |
1002 | 0 | nsEventStatus status = aEventStatus ? *aEventStatus : nsEventStatus_eIgnore; |
1003 | 0 | EventChainPreVisitor preVisitor(aPresContext, aEvent, aDOMEvent, status, |
1004 | 0 | isInAnon, aEvent->mTarget); |
1005 | 0 | targetEtci->GetEventTargetParent(preVisitor); |
1006 | 0 |
|
1007 | 0 | if (!preVisitor.mCanHandle) { |
1008 | 0 | targetEtci = MayRetargetToChromeIfCanNotHandleEvent(chain, preVisitor, |
1009 | 0 | targetEtci, nullptr, |
1010 | 0 | content); |
1011 | 0 | } |
1012 | 0 | if (!preVisitor.mCanHandle) { |
1013 | 0 | // The original target and chrome target (mAutomaticChromeDispatch=true) |
1014 | 0 | // can not handle the event but we still have to call their PreHandleEvent. |
1015 | 0 | for (uint32_t i = 0; i < chain.Length(); ++i) { |
1016 | 0 | chain[i].PreHandleEvent(preVisitor); |
1017 | 0 | } |
1018 | 0 |
|
1019 | 0 | clearTargets = ShouldClearTargets(aEvent); |
1020 | 0 | } else { |
1021 | 0 | // At least the original target can handle the event. |
1022 | 0 | // Setting the retarget to the |target| simplifies retargeting code. |
1023 | 0 | nsCOMPtr<EventTarget> t = aEvent->mTarget; |
1024 | 0 | targetEtci->SetNewTarget(t); |
1025 | 0 | // In order to not change the targetTouches array passed to TouchEvents |
1026 | 0 | // when dispatching events from JS, we need to store the initial Touch |
1027 | 0 | // objects on the list. |
1028 | 0 | if (aEvent->mClass == eTouchEventClass && aDOMEvent) { |
1029 | 0 | TouchEvent* touchEvent = static_cast<TouchEvent*>(aDOMEvent); |
1030 | 0 | TouchList* targetTouches = touchEvent->GetExistingTargetTouches(); |
1031 | 0 | if (targetTouches) { |
1032 | 0 | Maybe<nsTArray<RefPtr<dom::Touch>>> initialTargetTouches; |
1033 | 0 | initialTargetTouches.emplace(); |
1034 | 0 | for (uint32_t i = 0; i < targetTouches->Length(); ++i) { |
1035 | 0 | initialTargetTouches->AppendElement(targetTouches->Item(i)); |
1036 | 0 | } |
1037 | 0 | targetEtci->SetInitialTargetTouches(std::move(initialTargetTouches)); |
1038 | 0 | targetTouches->Clear(); |
1039 | 0 | } |
1040 | 0 | } |
1041 | 0 | EventTargetChainItem* topEtci = targetEtci; |
1042 | 0 | targetEtci = nullptr; |
1043 | 0 | while (preVisitor.GetParentTarget()) { |
1044 | 0 | EventTarget* parentTarget = preVisitor.GetParentTarget(); |
1045 | 0 | EventTargetChainItem* parentEtci = |
1046 | 0 | EventTargetChainItem::Create(chain, parentTarget, topEtci); |
1047 | 0 | if (!parentEtci->IsValid()) { |
1048 | 0 | EventTargetChainItem::DestroyLast(chain, parentEtci); |
1049 | 0 | rv = NS_ERROR_FAILURE; |
1050 | 0 | break; |
1051 | 0 | } |
1052 | 0 | |
1053 | 0 | parentEtci->SetIsSlotInClosedTree(preVisitor.mParentIsSlotInClosedTree); |
1054 | 0 | parentEtci->SetIsChromeHandler(preVisitor.mParentIsChromeHandler); |
1055 | 0 |
|
1056 | 0 | // Item needs event retargetting. |
1057 | 0 | if (preVisitor.mEventTargetAtParent) { |
1058 | 0 | // Need to set the target of the event |
1059 | 0 | // so that also the next retargeting works. |
1060 | 0 | preVisitor.mTargetInKnownToBeHandledScope = preVisitor.mEvent->mTarget; |
1061 | 0 | preVisitor.mEvent->mTarget = preVisitor.mEventTargetAtParent; |
1062 | 0 | parentEtci->SetNewTarget(preVisitor.mEventTargetAtParent); |
1063 | 0 | } |
1064 | 0 |
|
1065 | 0 | if (preVisitor.mRetargetedRelatedTarget) { |
1066 | 0 | preVisitor.mEvent->mRelatedTarget = preVisitor.mRetargetedRelatedTarget; |
1067 | 0 | } |
1068 | 0 |
|
1069 | 0 | parentEtci->GetEventTargetParent(preVisitor); |
1070 | 0 | if (preVisitor.mCanHandle) { |
1071 | 0 | preVisitor.mTargetInKnownToBeHandledScope = preVisitor.mEvent->mTarget; |
1072 | 0 | topEtci = parentEtci; |
1073 | 0 | } else { |
1074 | 0 | bool ignoreBecauseOfShadowDOM = preVisitor.mIgnoreBecauseOfShadowDOM; |
1075 | 0 | nsCOMPtr<nsINode> disabledTarget = do_QueryInterface(parentTarget); |
1076 | 0 | parentEtci = MayRetargetToChromeIfCanNotHandleEvent(chain, |
1077 | 0 | preVisitor, |
1078 | 0 | parentEtci, |
1079 | 0 | topEtci, |
1080 | 0 | disabledTarget); |
1081 | 0 | if (parentEtci && preVisitor.mCanHandle) { |
1082 | 0 | preVisitor.mTargetInKnownToBeHandledScope = preVisitor.mEvent->mTarget; |
1083 | 0 | EventTargetChainItem* item = |
1084 | 0 | EventTargetChainItem::GetFirstCanHandleEventTarget(chain); |
1085 | 0 | if (!ignoreBecauseOfShadowDOM) { |
1086 | 0 | // If we ignored the target because of Shadow DOM retargeting, we |
1087 | 0 | // shouldn't treat the target to be in the event path at all. |
1088 | 0 | item->SetNewTarget(parentTarget); |
1089 | 0 | } |
1090 | 0 | topEtci = parentEtci; |
1091 | 0 | continue; |
1092 | 0 | } |
1093 | 0 | break; |
1094 | 0 | } |
1095 | 0 | } |
1096 | 0 | if (NS_SUCCEEDED(rv)) { |
1097 | 0 | if (aTargets) { |
1098 | 0 | aTargets->Clear(); |
1099 | 0 | uint32_t numTargets = chain.Length(); |
1100 | 0 | EventTarget** targets = aTargets->AppendElements(numTargets); |
1101 | 0 | for (uint32_t i = 0; i < numTargets; ++i) { |
1102 | 0 | targets[i] = chain[i].CurrentTarget()->GetTargetForDOMEvent(); |
1103 | 0 | } |
1104 | 0 | } else { |
1105 | 0 | // Event target chain is created. PreHandle the chain. |
1106 | 0 | for (uint32_t i = 0; i < chain.Length(); ++i) { |
1107 | 0 | chain[i].PreHandleEvent(preVisitor); |
1108 | 0 | } |
1109 | 0 |
|
1110 | 0 | clearTargets = ShouldClearTargets(aEvent); |
1111 | 0 |
|
1112 | 0 | // Handle the chain. |
1113 | 0 | EventChainPostVisitor postVisitor(preVisitor); |
1114 | 0 | MOZ_RELEASE_ASSERT(!aEvent->mPath); |
1115 | 0 | aEvent->mPath = &chain; |
1116 | 0 |
|
1117 | 0 | #ifdef MOZ_GECKO_PROFILER |
1118 | 0 | if (profiler_is_active()) { |
1119 | 0 | // Add a profiler label and a profiler marker for the actual |
1120 | 0 | // dispatch of the event. |
1121 | 0 | // This is a very hot code path, so we need to make sure not to |
1122 | 0 | // do this extra work when we're not profiling. |
1123 | 0 | if (!postVisitor.mDOMEvent) { |
1124 | 0 | // This is tiny bit slow, but happens only once per event. |
1125 | 0 | // Similar code also in EventListenerManager. |
1126 | 0 | nsCOMPtr<EventTarget> et = aEvent->mOriginalTarget; |
1127 | 0 | RefPtr<Event> event = EventDispatcher::CreateEvent(et, aPresContext, |
1128 | 0 | aEvent, |
1129 | 0 | EmptyString()); |
1130 | 0 | event.swap(postVisitor.mDOMEvent); |
1131 | 0 | } |
1132 | 0 | nsAutoString typeStr; |
1133 | 0 | postVisitor.mDOMEvent->GetType(typeStr); |
1134 | 0 | AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING( |
1135 | 0 | "EventDispatcher::Dispatch", OTHER, typeStr); |
1136 | 0 |
|
1137 | 0 | profiler_add_marker( |
1138 | 0 | "DOMEvent", |
1139 | 0 | MakeUnique<DOMEventMarkerPayload>(typeStr, |
1140 | 0 | aEvent->mTimeStamp, |
1141 | 0 | "DOMEvent", |
1142 | 0 | TRACING_INTERVAL_START)); |
1143 | 0 |
|
1144 | 0 | EventTargetChainItem::HandleEventTargetChain(chain, postVisitor, |
1145 | 0 | aCallback, cd); |
1146 | 0 |
|
1147 | 0 | profiler_add_marker( |
1148 | 0 | "DOMEvent", |
1149 | 0 | MakeUnique<DOMEventMarkerPayload>(typeStr, |
1150 | 0 | aEvent->mTimeStamp, |
1151 | 0 | "DOMEvent", |
1152 | 0 | TRACING_INTERVAL_END)); |
1153 | 0 | } else |
1154 | 0 | #endif |
1155 | 0 | { |
1156 | 0 | EventTargetChainItem::HandleEventTargetChain(chain, postVisitor, |
1157 | 0 | aCallback, cd); |
1158 | 0 | } |
1159 | 0 | aEvent->mPath = nullptr; |
1160 | 0 |
|
1161 | 0 | preVisitor.mEventStatus = postVisitor.mEventStatus; |
1162 | 0 | // If the DOM event was created during event flow. |
1163 | 0 | if (!preVisitor.mDOMEvent && postVisitor.mDOMEvent) { |
1164 | 0 | preVisitor.mDOMEvent = postVisitor.mDOMEvent; |
1165 | 0 | } |
1166 | 0 | } |
1167 | 0 | } |
1168 | 0 | } |
1169 | 0 |
|
1170 | 0 | // Note, EventTargetChainItem objects are deleted when the chain goes out of |
1171 | 0 | // the scope. |
1172 | 0 |
|
1173 | 0 | aEvent->mFlags.mIsBeingDispatched = false; |
1174 | 0 | aEvent->mFlags.mDispatchedAtLeastOnce = true; |
1175 | 0 |
|
1176 | 0 | // https://dom.spec.whatwg.org/#concept-event-dispatch |
1177 | 0 | // step 10. If clearTargets, then: |
1178 | 0 | // 1. Set event's target to null. |
1179 | 0 | // 2. Set event's relatedTarget to null. |
1180 | 0 | // 3. Set event's touch target list to the empty list. |
1181 | 0 | if (clearTargets) { |
1182 | 0 | aEvent->mTarget = nullptr; |
1183 | 0 | aEvent->mOriginalTarget = nullptr; |
1184 | 0 | aEvent->mRelatedTarget = nullptr; |
1185 | 0 | aEvent->mOriginalRelatedTarget = nullptr; |
1186 | 0 | //XXXsmaug Check also all the touch objects. |
1187 | 0 | } |
1188 | 0 |
|
1189 | 0 | if (!externalDOMEvent && preVisitor.mDOMEvent) { |
1190 | 0 | // An dom::Event was created while dispatching the event. |
1191 | 0 | // Duplicate private data if someone holds a pointer to it. |
1192 | 0 | nsrefcnt rc = 0; |
1193 | 0 | NS_RELEASE2(preVisitor.mDOMEvent, rc); |
1194 | 0 | if (preVisitor.mDOMEvent) { |
1195 | 0 | preVisitor.mDOMEvent->DuplicatePrivateData(); |
1196 | 0 | } |
1197 | 0 | } |
1198 | 0 |
|
1199 | 0 | if (aEventStatus) { |
1200 | 0 | *aEventStatus = preVisitor.mEventStatus; |
1201 | 0 | } |
1202 | 0 |
|
1203 | 0 | if (cd.IsMainThread() && chain.Capacity() == 128 && sCachedMainThreadChain) { |
1204 | 0 | chain.ClearAndRetainStorage(); |
1205 | 0 | chain.SwapElements(*sCachedMainThreadChain); |
1206 | 0 | } |
1207 | 0 |
|
1208 | 0 | return rv; |
1209 | 0 | } |
1210 | | |
1211 | | /* static */ nsresult |
1212 | | EventDispatcher::DispatchDOMEvent(nsISupports* aTarget, |
1213 | | WidgetEvent* aEvent, |
1214 | | Event* aDOMEvent, |
1215 | | nsPresContext* aPresContext, |
1216 | | nsEventStatus* aEventStatus) |
1217 | 0 | { |
1218 | 0 | if (aDOMEvent) { |
1219 | 0 | WidgetEvent* innerEvent = aDOMEvent->WidgetEventPtr(); |
1220 | 0 | NS_ENSURE_TRUE(innerEvent, NS_ERROR_ILLEGAL_VALUE); |
1221 | 0 |
|
1222 | 0 | bool dontResetTrusted = false; |
1223 | 0 | if (innerEvent->mFlags.mDispatchedAtLeastOnce) { |
1224 | 0 | innerEvent->mTarget = nullptr; |
1225 | 0 | innerEvent->mOriginalTarget = nullptr; |
1226 | 0 | } else { |
1227 | 0 | dontResetTrusted = aDOMEvent->IsTrusted(); |
1228 | 0 | } |
1229 | 0 |
|
1230 | 0 | if (!dontResetTrusted) { |
1231 | 0 | //Check security state to determine if dispatcher is trusted |
1232 | 0 | bool trusted = NS_IsMainThread() ? nsContentUtils::LegacyIsCallerChromeOrNativeCode() |
1233 | 0 | : IsCurrentThreadRunningChromeWorker(); |
1234 | 0 | aDOMEvent->SetTrusted(trusted); |
1235 | 0 | } |
1236 | 0 |
|
1237 | 0 | return EventDispatcher::Dispatch(aTarget, aPresContext, innerEvent, |
1238 | 0 | aDOMEvent, aEventStatus); |
1239 | 0 | } else if (aEvent) { |
1240 | 0 | return EventDispatcher::Dispatch(aTarget, aPresContext, aEvent, |
1241 | 0 | aDOMEvent, aEventStatus); |
1242 | 0 | } |
1243 | 0 | return NS_ERROR_ILLEGAL_VALUE; |
1244 | 0 | } |
1245 | | |
1246 | | /* static */ already_AddRefed<dom::Event> |
1247 | | EventDispatcher::CreateEvent(EventTarget* aOwner, |
1248 | | nsPresContext* aPresContext, |
1249 | | WidgetEvent* aEvent, |
1250 | | const nsAString& aEventType, |
1251 | | CallerType aCallerType) |
1252 | 0 | { |
1253 | 0 | if (aEvent) { |
1254 | 0 | switch(aEvent->mClass) { |
1255 | 0 | case eMutationEventClass: |
1256 | 0 | return NS_NewDOMMutationEvent(aOwner, aPresContext, |
1257 | 0 | aEvent->AsMutationEvent()); |
1258 | 0 | case eGUIEventClass: |
1259 | 0 | case eScrollPortEventClass: |
1260 | 0 | case eUIEventClass: |
1261 | 0 | return NS_NewDOMUIEvent(aOwner, aPresContext, aEvent->AsGUIEvent()); |
1262 | 0 | case eScrollAreaEventClass: |
1263 | 0 | return NS_NewDOMScrollAreaEvent(aOwner, aPresContext, |
1264 | 0 | aEvent->AsScrollAreaEvent()); |
1265 | 0 | case eKeyboardEventClass: |
1266 | 0 | return NS_NewDOMKeyboardEvent(aOwner, aPresContext, |
1267 | 0 | aEvent->AsKeyboardEvent()); |
1268 | 0 | case eCompositionEventClass: |
1269 | 0 | return NS_NewDOMCompositionEvent(aOwner, aPresContext, |
1270 | 0 | aEvent->AsCompositionEvent()); |
1271 | 0 | case eMouseEventClass: |
1272 | 0 | return NS_NewDOMMouseEvent(aOwner, aPresContext, aEvent->AsMouseEvent()); |
1273 | 0 | case eFocusEventClass: |
1274 | 0 | return NS_NewDOMFocusEvent(aOwner, aPresContext, aEvent->AsFocusEvent()); |
1275 | 0 | case eMouseScrollEventClass: |
1276 | 0 | return NS_NewDOMMouseScrollEvent(aOwner, aPresContext, |
1277 | 0 | aEvent->AsMouseScrollEvent()); |
1278 | 0 | case eWheelEventClass: |
1279 | 0 | return NS_NewDOMWheelEvent(aOwner, aPresContext, aEvent->AsWheelEvent()); |
1280 | 0 | case eEditorInputEventClass: |
1281 | 0 | return NS_NewDOMInputEvent(aOwner, aPresContext, |
1282 | 0 | aEvent->AsEditorInputEvent()); |
1283 | 0 | case eDragEventClass: |
1284 | 0 | return NS_NewDOMDragEvent(aOwner, aPresContext, aEvent->AsDragEvent()); |
1285 | 0 | case eClipboardEventClass: |
1286 | 0 | return NS_NewDOMClipboardEvent(aOwner, aPresContext, |
1287 | 0 | aEvent->AsClipboardEvent()); |
1288 | 0 | case eSMILTimeEventClass: |
1289 | 0 | return NS_NewDOMTimeEvent(aOwner, aPresContext, |
1290 | 0 | aEvent->AsSMILTimeEvent()); |
1291 | 0 | case eCommandEventClass: |
1292 | 0 | return NS_NewDOMCommandEvent(aOwner, aPresContext, |
1293 | 0 | aEvent->AsCommandEvent()); |
1294 | 0 | case eSimpleGestureEventClass: |
1295 | 0 | return NS_NewDOMSimpleGestureEvent(aOwner, aPresContext, |
1296 | 0 | aEvent->AsSimpleGestureEvent()); |
1297 | 0 | case ePointerEventClass: |
1298 | 0 | return NS_NewDOMPointerEvent(aOwner, aPresContext, |
1299 | 0 | aEvent->AsPointerEvent()); |
1300 | 0 | case eTouchEventClass: |
1301 | 0 | return NS_NewDOMTouchEvent(aOwner, aPresContext, aEvent->AsTouchEvent()); |
1302 | 0 | case eTransitionEventClass: |
1303 | 0 | return NS_NewDOMTransitionEvent(aOwner, aPresContext, |
1304 | 0 | aEvent->AsTransitionEvent()); |
1305 | 0 | case eAnimationEventClass: |
1306 | 0 | return NS_NewDOMAnimationEvent(aOwner, aPresContext, |
1307 | 0 | aEvent->AsAnimationEvent()); |
1308 | 0 | default: |
1309 | 0 | // For all other types of events, create a vanilla event object. |
1310 | 0 | return NS_NewDOMEvent(aOwner, aPresContext, aEvent); |
1311 | 0 | } |
1312 | 0 | } |
1313 | 0 | |
1314 | 0 | // And if we didn't get an event, check the type argument. |
1315 | 0 | |
1316 | 0 | if (aEventType.LowerCaseEqualsLiteral("mouseevent") || |
1317 | 0 | aEventType.LowerCaseEqualsLiteral("mouseevents")) { |
1318 | 0 | return NS_NewDOMMouseEvent(aOwner, aPresContext, nullptr); |
1319 | 0 | } |
1320 | 0 | if (aEventType.LowerCaseEqualsLiteral("mousescrollevents")) { |
1321 | 0 | return NS_NewDOMMouseScrollEvent(aOwner, aPresContext, nullptr); |
1322 | 0 | } |
1323 | 0 | if (aEventType.LowerCaseEqualsLiteral("dragevent")) { |
1324 | 0 | return NS_NewDOMDragEvent(aOwner, aPresContext, nullptr); |
1325 | 0 | } |
1326 | 0 | if (aEventType.LowerCaseEqualsLiteral("keyboardevent") || |
1327 | 0 | aEventType.LowerCaseEqualsLiteral("keyevents")) { |
1328 | 0 | return NS_NewDOMKeyboardEvent(aOwner, aPresContext, nullptr); |
1329 | 0 | } |
1330 | 0 | if (aEventType.LowerCaseEqualsLiteral("compositionevent") || |
1331 | 0 | aEventType.LowerCaseEqualsLiteral("textevent")) { |
1332 | 0 | return NS_NewDOMCompositionEvent(aOwner, aPresContext, nullptr); |
1333 | 0 | } |
1334 | 0 | if (aEventType.LowerCaseEqualsLiteral("mutationevent") || |
1335 | 0 | aEventType.LowerCaseEqualsLiteral("mutationevents")) { |
1336 | 0 | return NS_NewDOMMutationEvent(aOwner, aPresContext, nullptr); |
1337 | 0 | } |
1338 | 0 | if (aEventType.LowerCaseEqualsLiteral("deviceorientationevent")) { |
1339 | 0 | DeviceOrientationEventInit init; |
1340 | 0 | RefPtr<Event> event = |
1341 | 0 | DeviceOrientationEvent::Constructor(aOwner, EmptyString(), init); |
1342 | 0 | event->MarkUninitialized(); |
1343 | 0 | return event.forget(); |
1344 | 0 | } |
1345 | 0 | if (aEventType.LowerCaseEqualsLiteral("devicemotionevent")) { |
1346 | 0 | return NS_NewDOMDeviceMotionEvent(aOwner, aPresContext, nullptr); |
1347 | 0 | } |
1348 | 0 | if (aEventType.LowerCaseEqualsLiteral("uievent") || |
1349 | 0 | aEventType.LowerCaseEqualsLiteral("uievents")) { |
1350 | 0 | return NS_NewDOMUIEvent(aOwner, aPresContext, nullptr); |
1351 | 0 | } |
1352 | 0 | if (aEventType.LowerCaseEqualsLiteral("event") || |
1353 | 0 | aEventType.LowerCaseEqualsLiteral("events") || |
1354 | 0 | aEventType.LowerCaseEqualsLiteral("htmlevents") || |
1355 | 0 | aEventType.LowerCaseEqualsLiteral("svgevents")) { |
1356 | 0 | return NS_NewDOMEvent(aOwner, aPresContext, nullptr); |
1357 | 0 | } |
1358 | 0 | if (aEventType.LowerCaseEqualsLiteral("timeevent")) { |
1359 | 0 | return NS_NewDOMTimeEvent(aOwner, aPresContext, nullptr); |
1360 | 0 | } |
1361 | 0 | if (aEventType.LowerCaseEqualsLiteral("messageevent")) { |
1362 | 0 | RefPtr<Event> event = new MessageEvent(aOwner, aPresContext, nullptr); |
1363 | 0 | return event.forget(); |
1364 | 0 | } |
1365 | 0 | if (aEventType.LowerCaseEqualsLiteral("beforeunloadevent")) { |
1366 | 0 | return NS_NewDOMBeforeUnloadEvent(aOwner, aPresContext, nullptr); |
1367 | 0 | } |
1368 | 0 | if (aEventType.LowerCaseEqualsLiteral("scrollareaevent")) { |
1369 | 0 | return NS_NewDOMScrollAreaEvent(aOwner, aPresContext, nullptr); |
1370 | 0 | } |
1371 | 0 | if (aEventType.LowerCaseEqualsLiteral("touchevent") && |
1372 | 0 | TouchEvent::PrefEnabled(nsContentUtils::GetDocShellForEventTarget(aOwner))) { |
1373 | 0 | return NS_NewDOMTouchEvent(aOwner, aPresContext, nullptr); |
1374 | 0 | } |
1375 | 0 | if (aEventType.LowerCaseEqualsLiteral("hashchangeevent")) { |
1376 | 0 | HashChangeEventInit init; |
1377 | 0 | RefPtr<Event> event = |
1378 | 0 | HashChangeEvent::Constructor(aOwner, EmptyString(), init); |
1379 | 0 | event->MarkUninitialized(); |
1380 | 0 | return event.forget(); |
1381 | 0 | } |
1382 | 0 | if (aEventType.LowerCaseEqualsLiteral("customevent")) { |
1383 | 0 | return NS_NewDOMCustomEvent(aOwner, aPresContext, nullptr); |
1384 | 0 | } |
1385 | 0 | if (aEventType.LowerCaseEqualsLiteral("storageevent")) { |
1386 | 0 | RefPtr<Event> event = |
1387 | 0 | StorageEvent::Constructor(aOwner, EmptyString(), StorageEventInit()); |
1388 | 0 | event->MarkUninitialized(); |
1389 | 0 | return event.forget(); |
1390 | 0 | } |
1391 | 0 | if (aEventType.LowerCaseEqualsLiteral("focusevent")) { |
1392 | 0 | RefPtr<Event> event = NS_NewDOMFocusEvent(aOwner, aPresContext, nullptr); |
1393 | 0 | event->MarkUninitialized(); |
1394 | 0 | return event.forget(); |
1395 | 0 | } |
1396 | 0 | |
1397 | 0 | // Only allow these events for chrome |
1398 | 0 | if (aCallerType == CallerType::System) { |
1399 | 0 | if (aEventType.LowerCaseEqualsLiteral("simplegestureevent")) { |
1400 | 0 | return NS_NewDOMSimpleGestureEvent(aOwner, aPresContext, nullptr); |
1401 | 0 | } |
1402 | 0 | if (aEventType.LowerCaseEqualsLiteral("xulcommandevent") || |
1403 | 0 | aEventType.LowerCaseEqualsLiteral("xulcommandevents")) { |
1404 | 0 | return NS_NewDOMXULCommandEvent(aOwner, aPresContext, nullptr); |
1405 | 0 | } |
1406 | 0 | } |
1407 | 0 | |
1408 | 0 | // NEW EVENT TYPES SHOULD NOT BE ADDED HERE; THEY SHOULD USE ONLY EVENT |
1409 | 0 | // CONSTRUCTORS |
1410 | 0 | |
1411 | 0 | return nullptr; |
1412 | 0 | } |
1413 | | |
1414 | | // static |
1415 | | void |
1416 | | EventDispatcher::GetComposedPathFor(WidgetEvent* aEvent, |
1417 | | nsTArray<RefPtr<EventTarget>>& aPath) |
1418 | 0 | { |
1419 | 0 | nsTArray<EventTargetChainItem>* path = aEvent->mPath; |
1420 | 0 | if (!path || path->IsEmpty() || !aEvent->mCurrentTarget) { |
1421 | 0 | return; |
1422 | 0 | } |
1423 | 0 | |
1424 | 0 | EventTarget* currentTarget = |
1425 | 0 | aEvent->mCurrentTarget->GetTargetForEventTargetChain(); |
1426 | 0 | if (!currentTarget) { |
1427 | 0 | return; |
1428 | 0 | } |
1429 | 0 | |
1430 | 0 | AutoTArray<EventTarget*, 128> reversedComposedPath; |
1431 | 0 | bool hasSeenCurrentTarget = false; |
1432 | 0 | uint32_t hiddenSubtreeLevel = 0; |
1433 | 0 | for (uint32_t i = path->Length(); i; ) { |
1434 | 0 | --i; |
1435 | 0 |
|
1436 | 0 | EventTargetChainItem& item = path->ElementAt(i); |
1437 | 0 | if (item.PreHandleEventOnly()) { |
1438 | 0 | continue; |
1439 | 0 | } |
1440 | 0 | |
1441 | 0 | if (!hasSeenCurrentTarget && currentTarget == item.CurrentTarget()) { |
1442 | 0 | hasSeenCurrentTarget = true; |
1443 | 0 | } else if (hasSeenCurrentTarget && item.IsRootOfClosedTree()) { |
1444 | 0 | ++hiddenSubtreeLevel; |
1445 | 0 | } |
1446 | 0 |
|
1447 | 0 | if (hiddenSubtreeLevel == 0) { |
1448 | 0 | reversedComposedPath.AppendElement(item.CurrentTarget()); |
1449 | 0 | } |
1450 | 0 |
|
1451 | 0 | if (item.IsSlotInClosedTree() && hiddenSubtreeLevel > 0) { |
1452 | 0 | --hiddenSubtreeLevel; |
1453 | 0 | } |
1454 | 0 |
|
1455 | 0 | if (item.IsChromeHandler()) { |
1456 | 0 | if (hasSeenCurrentTarget) { |
1457 | 0 | // The current behavior is to include only EventTargets from |
1458 | 0 | // either chrome side of event path or content side, not from both. |
1459 | 0 | break; |
1460 | 0 | } |
1461 | 0 | |
1462 | 0 | // Need to start all over to collect the composed path on content side. |
1463 | 0 | reversedComposedPath.Clear(); |
1464 | 0 | } |
1465 | 0 | } |
1466 | 0 |
|
1467 | 0 | aPath.SetCapacity(reversedComposedPath.Length()); |
1468 | 0 | for (uint32_t i = reversedComposedPath.Length(); i; ) { |
1469 | 0 | --i; |
1470 | 0 | aPath.AppendElement(reversedComposedPath[i]->GetTargetForDOMEvent()); |
1471 | 0 | } |
1472 | 0 | } |
1473 | | |
1474 | | } // namespace mozilla |