tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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_