Bug 1885602 - Part 5: Implement navigating to the SUMO help topic from the menu heade...
[gecko.git] / dom / events / AsyncEventDispatcher.h
blobd17c0e700caf9eacacc259606935e459607e163e
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/. */
7 #ifndef mozilla_AsyncEventDispatcher_h_
8 #define mozilla_AsyncEventDispatcher_h_
10 #include "mozilla/Attributes.h"
11 #include "mozilla/EventForwards.h"
12 #include "mozilla/RefPtr.h"
13 #include "mozilla/dom/Event.h"
14 #include "nsCOMPtr.h"
15 #include "mozilla/dom/Document.h"
16 #include "nsString.h"
17 #include "nsThreadUtils.h"
19 class nsINode;
21 namespace mozilla {
23 /**
24 * Use AsyncEventDispatcher to fire a DOM event that requires safe a stable DOM.
25 * For example, you may need to fire an event from within layout, but
26 * want to ensure that the event handler doesn't mutate the DOM at
27 * the wrong time, in order to avoid resulting instability.
30 class AsyncEventDispatcher : public CancelableRunnable {
31 public:
32 /**
33 * If aOnlyChromeDispatch is true, the event is dispatched to only
34 * chrome node. In that case, if aTarget is already a chrome node,
35 * the event is dispatched to it, otherwise the dispatch path starts
36 * at the first chrome ancestor of that target.
38 AsyncEventDispatcher(
39 dom::EventTarget* aTarget, const nsAString& aEventType,
40 CanBubble aCanBubble,
41 ChromeOnlyDispatch aOnlyChromeDispatch = ChromeOnlyDispatch::eNo,
42 Composed aComposed = Composed::eDefault)
43 : CancelableRunnable("AsyncEventDispatcher"),
44 mTarget(aTarget),
45 mEventType(aEventType),
46 mEventMessage(eUnidentifiedEvent),
47 mCanBubble(aCanBubble),
48 mOnlyChromeDispatch(aOnlyChromeDispatch),
49 mComposed(aComposed) {}
51 /**
52 * If aOnlyChromeDispatch is true, the event is dispatched to only
53 * chrome node. In that case, if aTarget is already a chrome node,
54 * the event is dispatched to it, otherwise the dispatch path starts
55 * at the first chrome ancestor of that target.
57 AsyncEventDispatcher(nsINode* aTarget, mozilla::EventMessage aEventMessage,
58 CanBubble aCanBubble,
59 ChromeOnlyDispatch aOnlyChromeDispatch)
60 : CancelableRunnable("AsyncEventDispatcher"),
61 mTarget(aTarget),
62 mEventMessage(aEventMessage),
63 mCanBubble(aCanBubble),
64 mOnlyChromeDispatch(aOnlyChromeDispatch) {
65 mEventType.SetIsVoid(true);
66 MOZ_ASSERT(mEventMessage != eUnidentifiedEvent);
69 AsyncEventDispatcher(dom::EventTarget* aTarget,
70 mozilla::EventMessage aEventMessage,
71 CanBubble aCanBubble)
72 : CancelableRunnable("AsyncEventDispatcher"),
73 mTarget(aTarget),
74 mEventMessage(aEventMessage),
75 mCanBubble(aCanBubble) {
76 mEventType.SetIsVoid(true);
77 MOZ_ASSERT(mEventMessage != eUnidentifiedEvent);
80 /**
81 * aEvent must have been created without Widget*Event and Internal*Event
82 * because this constructor assumes that it's safe to use aEvent
83 * asynchronously (i.e., after all objects allocated in the stack are
84 * destroyed).
86 AsyncEventDispatcher(
87 dom::EventTarget* aTarget, already_AddRefed<dom::Event> aEvent,
88 ChromeOnlyDispatch aOnlyChromeDispatch = ChromeOnlyDispatch::eNo)
89 : CancelableRunnable("AsyncEventDispatcher"),
90 mTarget(aTarget),
91 mEvent(aEvent),
92 mEventMessage(eUnidentifiedEvent),
93 mOnlyChromeDispatch(aOnlyChromeDispatch) {
94 MOZ_ASSERT(
95 mEvent->IsSafeToBeDispatchedAsynchronously(),
96 "The DOM event should be created without Widget*Event and "
97 "Internal*Event "
98 "because if it needs to be safe to be dispatched asynchronously");
101 AsyncEventDispatcher(dom::EventTarget* aTarget, WidgetEvent& aEvent);
103 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD Run() override;
104 nsresult Cancel() override;
105 nsresult PostDOMEvent();
108 * Dispatch event immediately if it's safe to dispatch the event.
109 * Otherwise, posting the event into the queue to dispatch it when it's safe.
111 * Note that this method allows callers to call itself with unsafe aTarget
112 * because its lifetime is guaranteed by this method (in the case of
113 * synchronous dispatching) or AsyncEventDispatcher (in the case of
114 * asynchronous dispatching).
116 MOZ_CAN_RUN_SCRIPT_BOUNDARY static void RunDOMEventWhenSafe(
117 dom::EventTarget& aTarget, const nsAString& aEventType,
118 CanBubble aCanBubble,
119 ChromeOnlyDispatch aOnlyChromeDispatch = ChromeOnlyDispatch::eNo,
120 Composed aComposed = Composed::eDefault);
123 * Dispatch event immediately if it's safe to dispatch the event.
124 * Otherwise, posting the event into the queue to dispatch it when it's safe.
126 * NOTE: Only this is now marked as MOZ_CAN_RUN_SCRIPT because all callers
127 * have already had safe smart pointers for both aTarget and aEvent.
128 * If you need to use this in a hot path without smart pointers, you may need
129 * to create unsafe version of this method for avoiding the extra add/release
130 * refcount cost in the case of asynchronous dispatching.
132 MOZ_CAN_RUN_SCRIPT static void RunDOMEventWhenSafe(
133 dom::EventTarget& aTarget, dom::Event& aEvent,
134 ChromeOnlyDispatch aOnlyChromeDispatch = ChromeOnlyDispatch::eNo);
137 * Dispatch event immediately if it's safe to dispatch the event.
138 * Otherwise, posting the event into the queue to dispatch it when it's safe.
140 * Note that this method allows callers to call itself with unsafe aTarget
141 * because its lifetime is guaranteed by EventDispatcher (in the case of
142 * synchronous dispatching) or AsyncEventDispatcher (in the case of
143 * asynchronous dispatching).
145 MOZ_CAN_RUN_SCRIPT_BOUNDARY static nsresult RunDOMEventWhenSafe(
146 nsINode& aTarget, WidgetEvent& aEvent,
147 nsEventStatus* aEventStatus = nullptr);
149 // Calling this causes the Run() method to check that
150 // mTarget->IsInComposedDoc(). mTarget must be an nsINode or else we'll
151 // assert.
152 void RequireNodeInDocument();
154 // NOTE: The static version of this should be preferred when possible, because
155 // it avoids the allocation but this is useful when used in combination with
156 // LoadBlockingAsyncEventDispatcher.
157 void RunDOMEventWhenSafe();
159 protected:
160 MOZ_CAN_RUN_SCRIPT static void DispatchEventOnTarget(
161 dom::EventTarget* aTarget, dom::Event* aEvent,
162 ChromeOnlyDispatch aOnlyChromeDispatch, Composed aComposed);
163 MOZ_CAN_RUN_SCRIPT static void DispatchEventOnTarget(
164 dom::EventTarget* aTarget, const nsAString& aEventType,
165 CanBubble aCanBubble, ChromeOnlyDispatch aOnlyChromeDispatch,
166 Composed aComposed);
168 public:
169 nsCOMPtr<dom::EventTarget> mTarget;
170 RefPtr<dom::Event> mEvent;
171 // If mEventType is set, mEventMessage will be eUnidentifiedEvent.
172 // If mEventMessage is set, mEventType will be void.
173 // They can never both be set at the same time.
174 nsString mEventType;
175 EventMessage mEventMessage;
176 CanBubble mCanBubble = CanBubble::eNo;
177 ChromeOnlyDispatch mOnlyChromeDispatch = ChromeOnlyDispatch::eNo;
178 Composed mComposed = Composed::eDefault;
179 bool mCanceled = false;
180 bool mCheckStillInDoc = false;
183 class LoadBlockingAsyncEventDispatcher final : public AsyncEventDispatcher {
184 public:
185 LoadBlockingAsyncEventDispatcher(nsINode* aEventNode,
186 const nsAString& aEventType,
187 CanBubble aBubbles,
188 ChromeOnlyDispatch aDispatchChromeOnly)
189 : AsyncEventDispatcher(aEventNode, aEventType, aBubbles,
190 aDispatchChromeOnly),
191 mBlockedDoc(aEventNode->OwnerDoc()) {
192 mBlockedDoc->BlockOnload();
195 LoadBlockingAsyncEventDispatcher(nsINode* aEventNode,
196 already_AddRefed<dom::Event> aEvent)
197 : AsyncEventDispatcher(aEventNode, std::move(aEvent)),
198 mBlockedDoc(aEventNode->OwnerDoc()) {
199 mBlockedDoc->BlockOnload();
202 ~LoadBlockingAsyncEventDispatcher() { mBlockedDoc->UnblockOnload(true); }
204 private:
205 RefPtr<dom::Document> mBlockedDoc;
208 } // namespace mozilla
210 #endif // mozilla_AsyncEventDispatcher_h_