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/Event.h" 14 #include "nsCOMPtr.h" 15 #include "mozilla/dom/Document.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(nsINode* aTarget, const nsAString& aEventType, 39 CanBubble aCanBubble, 40 ChromeOnlyDispatch aOnlyChromeDispatch, 41 Composed aComposed = Composed::eDefault) 42 : CancelableRunnable("AsyncEventDispatcher"), 43 mTarget(aTarget), 44 mEventType(aEventType), 45 mEventMessage(eUnidentifiedEvent), 46 mCanBubble(aCanBubble), 47 mOnlyChromeDispatch(aOnlyChromeDispatch), 48 mComposed(aComposed) {} 49 50 /** 51 * If aOnlyChromeDispatch is true, the event is dispatched to only 52 * chrome node. In that case, if aTarget is already a chrome node, 53 * the event is dispatched to it, otherwise the dispatch path starts 54 * at the first chrome ancestor of that target. 55 */ AsyncEventDispatcher(nsINode * aTarget,mozilla::EventMessage aEventMessage,CanBubble aCanBubble,ChromeOnlyDispatch aOnlyChromeDispatch)56 AsyncEventDispatcher(nsINode* aTarget, mozilla::EventMessage aEventMessage, 57 CanBubble aCanBubble, 58 ChromeOnlyDispatch aOnlyChromeDispatch) 59 : CancelableRunnable("AsyncEventDispatcher"), 60 mTarget(aTarget), 61 mEventMessage(aEventMessage), 62 mCanBubble(aCanBubble), 63 mOnlyChromeDispatch(aOnlyChromeDispatch) { 64 mEventType.SetIsVoid(true); 65 MOZ_ASSERT(mEventMessage != eUnidentifiedEvent); 66 } 67 AsyncEventDispatcher(dom::EventTarget * aTarget,const nsAString & aEventType,CanBubble aCanBubble)68 AsyncEventDispatcher(dom::EventTarget* aTarget, const nsAString& aEventType, 69 CanBubble aCanBubble) 70 : CancelableRunnable("AsyncEventDispatcher"), 71 mTarget(aTarget), 72 mEventType(aEventType), 73 mEventMessage(eUnidentifiedEvent), 74 mCanBubble(aCanBubble) {} 75 AsyncEventDispatcher(dom::EventTarget * aTarget,mozilla::EventMessage aEventMessage,CanBubble aCanBubble)76 AsyncEventDispatcher(dom::EventTarget* aTarget, 77 mozilla::EventMessage aEventMessage, 78 CanBubble aCanBubble) 79 : CancelableRunnable("AsyncEventDispatcher"), 80 mTarget(aTarget), 81 mEventMessage(aEventMessage), 82 mCanBubble(aCanBubble) { 83 mEventType.SetIsVoid(true); 84 MOZ_ASSERT(mEventMessage != eUnidentifiedEvent); 85 } 86 87 /** 88 * aEvent must have been created without Widget*Event and Internal*Event 89 * because this constructor assumes that it's safe to use aEvent 90 * asynchronously (i.e., after all objects allocated in the stack are 91 * destroyed). 92 */ AsyncEventDispatcher(dom::EventTarget * aTarget,dom::Event * aEvent)93 AsyncEventDispatcher(dom::EventTarget* aTarget, dom::Event* aEvent) 94 : CancelableRunnable("AsyncEventDispatcher"), 95 mTarget(aTarget), 96 mEvent(aEvent), 97 mEventMessage(eUnidentifiedEvent) { 98 MOZ_ASSERT( 99 aEvent->IsSafeToBeDispatchedAsynchronously(), 100 "The DOM event should be created without Widget*Event and " 101 "Internal*Event " 102 "because if it needs to be safe to be dispatched asynchronously"); 103 } 104 105 AsyncEventDispatcher(dom::EventTarget* aTarget, WidgetEvent& aEvent); 106 107 NS_IMETHOD Run() override; 108 nsresult Cancel() override; 109 nsresult PostDOMEvent(); 110 void RunDOMEventWhenSafe(); 111 112 // Calling this causes the Run() method to check that 113 // mTarget->IsInComposedDoc(). mTarget must be an nsINode or else we'll 114 // assert. 115 void RequireNodeInDocument(); 116 117 nsCOMPtr<dom::EventTarget> mTarget; 118 RefPtr<dom::Event> mEvent; 119 // If mEventType is set, mEventMessage will be eUnidentifiedEvent. 120 // If mEventMessage is set, mEventType will be void. 121 // They can never both be set at the same time. 122 nsString mEventType; 123 EventMessage mEventMessage; 124 CanBubble mCanBubble = CanBubble::eNo; 125 ChromeOnlyDispatch mOnlyChromeDispatch = ChromeOnlyDispatch::eNo; 126 Composed mComposed = Composed::eDefault; 127 bool mCanceled = false; 128 bool mCheckStillInDoc = false; 129 }; 130 131 class LoadBlockingAsyncEventDispatcher final : public AsyncEventDispatcher { 132 public: LoadBlockingAsyncEventDispatcher(nsINode * aEventNode,const nsAString & aEventType,CanBubble aBubbles,ChromeOnlyDispatch aDispatchChromeOnly)133 LoadBlockingAsyncEventDispatcher(nsINode* aEventNode, 134 const nsAString& aEventType, 135 CanBubble aBubbles, 136 ChromeOnlyDispatch aDispatchChromeOnly) 137 : AsyncEventDispatcher(aEventNode, aEventType, aBubbles, 138 aDispatchChromeOnly), 139 mBlockedDoc(aEventNode->OwnerDoc()) { 140 if (mBlockedDoc) { 141 mBlockedDoc->BlockOnload(); 142 } 143 } 144 LoadBlockingAsyncEventDispatcher(nsINode * aEventNode,dom::Event * aEvent)145 LoadBlockingAsyncEventDispatcher(nsINode* aEventNode, dom::Event* aEvent) 146 : AsyncEventDispatcher(aEventNode, aEvent), 147 mBlockedDoc(aEventNode->OwnerDoc()) { 148 if (mBlockedDoc) { 149 mBlockedDoc->BlockOnload(); 150 } 151 } 152 153 ~LoadBlockingAsyncEventDispatcher(); 154 155 private: 156 RefPtr<dom::Document> mBlockedDoc; 157 }; 158 159 } // namespace mozilla 160 161 #endif // mozilla_AsyncEventDispatcher_h_ 162