AsyncEventDispatcher.h (8803B)
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 #ifndef mozilla_AsyncEventDispatcher_h_ 8 #define mozilla_AsyncEventDispatcher_h_ 9 10 #include "mozilla/Attributes.h" 11 #include "mozilla/EventForwards.h" 12 #include "mozilla/RefPtr.h" 13 #include "mozilla/dom/Document.h" 14 #include "mozilla/dom/Event.h" 15 #include "nsCOMPtr.h" 16 #include "nsString.h" 17 #include "nsThreadUtils.h" 18 19 class nsINode; 20 21 namespace mozilla { 22 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. 28 */ 29 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. 37 */ 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) {} 50 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. 56 */ 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); 67 } 68 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); 78 } 79 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). 85 */ 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"); 99 } 100 101 AsyncEventDispatcher(dom::EventTarget* aTarget, WidgetEvent& aEvent); 102 103 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD Run() override; 104 nsresult Cancel() override; 105 nsresult PostDOMEvent(); 106 107 /** 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. 110 * 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). 115 */ 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); 121 122 /** 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. 125 * 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. 131 */ 132 MOZ_CAN_RUN_SCRIPT static void RunDOMEventWhenSafe( 133 dom::EventTarget& aTarget, dom::Event& aEvent, 134 ChromeOnlyDispatch aOnlyChromeDispatch = ChromeOnlyDispatch::eNo); 135 136 /** 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. 139 * 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). 144 */ 145 MOZ_CAN_RUN_SCRIPT_BOUNDARY static nsresult RunDOMEventWhenSafe( 146 nsINode& aTarget, WidgetEvent& aEvent, 147 nsEventStatus* aEventStatus = nullptr); 148 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(); 153 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(); 158 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); 167 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; 181 }; 182 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(); 193 } 194 195 LoadBlockingAsyncEventDispatcher(nsINode* aEventNode, 196 already_AddRefed<dom::Event> aEvent) 197 : AsyncEventDispatcher(aEventNode, std::move(aEvent)), 198 mBlockedDoc(aEventNode->OwnerDoc()) { 199 mBlockedDoc->BlockOnload(); 200 } 201 202 ~LoadBlockingAsyncEventDispatcher() { mBlockedDoc->UnblockOnload(true); } 203 204 private: 205 RefPtr<dom::Document> mBlockedDoc; 206 }; 207 208 class AsyncSelectionChangeEventDispatcher : public AsyncEventDispatcher { 209 public: 210 AsyncSelectionChangeEventDispatcher(dom::EventTarget* aTarget, 211 EventMessage aEventMessage, 212 CanBubble aCanBubble) 213 : AsyncEventDispatcher(aTarget, aEventMessage, aCanBubble) {} 214 215 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD Run() override { 216 mTarget->GetAsNode()->ClearHasScheduledSelectionChangeEvent(); 217 return AsyncEventDispatcher::Run(); 218 } 219 }; 220 221 } // namespace mozilla 222 223 #endif // mozilla_AsyncEventDispatcher_h_