tor-browser

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

CustomElementRegistry.h (21765B)


      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_dom_CustomElementRegistry_h
      8 #define mozilla_dom_CustomElementRegistry_h
      9 
     10 #include "js/GCHashTable.h"
     11 #include "js/TypeDecls.h"
     12 #include "mozilla/Attributes.h"
     13 #include "mozilla/CycleCollectedJSContext.h"  // for MicroTaskRunnable
     14 #include "mozilla/RefPtr.h"
     15 #include "mozilla/dom/BindingDeclarations.h"
     16 #include "mozilla/dom/CustomElementRegistryBinding.h"
     17 #include "mozilla/dom/Document.h"
     18 #include "mozilla/dom/Element.h"
     19 #include "mozilla/dom/ElementInternals.h"
     20 #include "mozilla/dom/ElementInternalsBinding.h"
     21 #include "mozilla/dom/HTMLFormElement.h"
     22 #include "nsAtomHashKeys.h"
     23 #include "nsCycleCollectionParticipant.h"
     24 #include "nsTHashSet.h"
     25 #include "nsWrapperCache.h"
     26 
     27 namespace mozilla {
     28 class ErrorResult;
     29 
     30 namespace dom {
     31 
     32 struct CustomElementData;
     33 struct ElementDefinitionOptions;
     34 class CallbackFunction;
     35 class CustomElementCallback;
     36 class CustomElementReaction;
     37 class DocGroup;
     38 class Promise;
     39 
     40 enum class ElementCallbackType {
     41  eConnected,
     42  eDisconnected,
     43  eAdopted,
     44  eConnectedMove,
     45  eAttributeChanged,
     46  eFormAssociated,
     47  eFormReset,
     48  eFormDisabled,
     49  eFormStateRestore,
     50  eGetCustomInterface
     51 };
     52 
     53 struct LifecycleCallbackArgs {
     54  // Used by the attribute changed callback.
     55  RefPtr<nsAtom> mName;
     56  nsString mOldValue;
     57  nsString mNewValue;
     58  nsString mNamespaceURI;
     59 
     60  // Used by the adopted callback.
     61  RefPtr<Document> mOldDocument;
     62  RefPtr<Document> mNewDocument;
     63 
     64  // Used by the form associated callback.
     65  RefPtr<HTMLFormElement> mForm;
     66 
     67  // Used by the form disabled callback.
     68  bool mDisabled;
     69 
     70  // Used by the form state restore callback.
     71  Nullable<OwningFileOrUSVStringOrFormData> mState;
     72  RestoreReason mReason;
     73 
     74  size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const;
     75 };
     76 
     77 // Each custom element has an associated callback queue and an element is
     78 // being created flag.
     79 struct CustomElementData {
     80  // https://dom.spec.whatwg.org/#concept-element-custom-element-state
     81  // CustomElementData is only created on the element which is a custom element
     82  // or an upgrade candidate, so the state of an element without
     83  // CustomElementData is "uncustomized".
     84  enum class State { eUndefined, eFailed, eCustom, ePrecustomized };
     85 
     86  explicit CustomElementData(nsAtom* aType);
     87  CustomElementData(nsAtom* aType, State aState);
     88  ~CustomElementData() = default;
     89 
     90  // Custom element state as described in the custom element spec.
     91  State mState;
     92  // custom element reaction queue as described in the custom element spec.
     93  // There is 1 reaction in reaction queue, when 1) it becomes disconnected,
     94  // 2) it’s adopted into a new document, 3) its attributes are changed,
     95  // appended, removed, or replaced.
     96  // There are 3 reactions in reaction queue when doing upgrade operation,
     97  // e.g., create an element, insert a node.
     98  AutoTArray<UniquePtr<CustomElementReaction>, 3> mReactionQueue;
     99 
    100  void SetCustomElementDefinition(CustomElementDefinition* aDefinition);
    101  CustomElementDefinition* GetCustomElementDefinition() const;
    102  nsAtom* GetCustomElementType() const { return mType; }
    103  void AttachedInternals();
    104  bool HasAttachedInternals() const { return mIsAttachedInternals; }
    105 
    106  bool IsFormAssociated() const;
    107 
    108  void Traverse(nsCycleCollectionTraversalCallback& aCb) const;
    109  void Unlink();
    110  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
    111 
    112  nsAtom* GetIs(const Element* aElement) const {
    113    // If mType isn't the same as name atom, this is a customized built-in
    114    // element, which has 'is' value set.
    115    return aElement->NodeInfo()->NameAtom() == mType ? nullptr : mType.get();
    116  }
    117 
    118  ElementInternals* GetElementInternals() const { return mElementInternals; }
    119 
    120  ElementInternals* GetOrCreateElementInternals(HTMLElement* aTarget) {
    121    if (!mElementInternals) {
    122      mElementInternals = MakeAndAddRef<ElementInternals>(aTarget);
    123    }
    124    return mElementInternals;
    125  }
    126 
    127 private:
    128  // Custom element type, for <button is="x-button"> or <x-button>
    129  // this would be x-button.
    130  RefPtr<nsAtom> mType;
    131  RefPtr<CustomElementDefinition> mCustomElementDefinition;
    132  RefPtr<ElementInternals> mElementInternals;
    133  bool mIsAttachedInternals = false;
    134 };
    135 
    136 #define ALREADY_CONSTRUCTED_MARKER nullptr
    137 
    138 // The required information for a custom element as defined in:
    139 // https://html.spec.whatwg.org/multipage/scripting.html#custom-element-definition
    140 struct CustomElementDefinition {
    141  NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(CustomElementDefinition)
    142  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(CustomElementDefinition)
    143 
    144  CustomElementDefinition(
    145      nsAtom* aType, nsAtom* aLocalName, int32_t aNamespaceID,
    146      CustomElementConstructor* aConstructor,
    147      nsTArray<RefPtr<nsAtom>>&& aObservedAttributes,
    148      UniquePtr<LifecycleCallbacks>&& aCallbacks,
    149      UniquePtr<FormAssociatedLifecycleCallbacks>&& aFormAssociatedCallbacks,
    150      bool aFormAssociated, bool aDisableInternals, bool aDisableShadow);
    151 
    152  // The type (name) for this custom element, for <button is="x-foo"> or <x-foo>
    153  // this would be x-foo.
    154  RefPtr<nsAtom> mType;
    155 
    156  // The localname to (e.g. <button is=type> -- this would be button).
    157  RefPtr<nsAtom> mLocalName;
    158 
    159  // The namespace for this custom element
    160  int32_t mNamespaceID;
    161 
    162  // The custom element constructor.
    163  RefPtr<CustomElementConstructor> mConstructor;
    164 
    165  // The list of attributes that this custom element observes.
    166  nsTArray<RefPtr<nsAtom>> mObservedAttributes;
    167 
    168  // The lifecycle callbacks to call for this custom element.
    169  UniquePtr<LifecycleCallbacks> mCallbacks;
    170  UniquePtr<FormAssociatedLifecycleCallbacks> mFormAssociatedCallbacks;
    171 
    172  // If this is true, user agent treats elements associated to this custom
    173  // element definition as form-associated custom elements.
    174  bool mFormAssociated = false;
    175 
    176  // Determine whether to allow to attachInternals() for this custom element.
    177  bool mDisableInternals = false;
    178 
    179  // Determine whether to allow to attachShadow() for this custom element.
    180  bool mDisableShadow = false;
    181 
    182  // A construction stack. Use nullptr to represent an "already constructed
    183  // marker".
    184  nsTArray<RefPtr<Element>> mConstructionStack;
    185 
    186  // See step 6.1.10 of https://dom.spec.whatwg.org/#concept-create-element
    187  // which set up the prefix after a custom element is created. However, In
    188  // Gecko, the prefix isn't allowed to be changed in NodeInfo, so we store the
    189  // prefix information here and propagate to where NodeInfo is assigned to a
    190  // custom element instead.
    191  nsTArray<RefPtr<nsAtom>> mPrefixStack;
    192 
    193  // This basically is used for distinguishing the custom element constructor
    194  // is invoked from document.createElement or directly from JS, i.e.
    195  // `new CustomElementConstructor()`.
    196  uint32_t mConstructionDepth = 0;
    197 
    198  bool IsCustomBuiltIn() { return mType != mLocalName; }
    199 
    200  bool IsInObservedAttributeList(nsAtom* aName) {
    201    if (mObservedAttributes.IsEmpty()) {
    202      return false;
    203    }
    204 
    205    return mObservedAttributes.Contains(aName);
    206  }
    207 
    208 private:
    209  ~CustomElementDefinition() = default;
    210 };
    211 
    212 class CustomElementReaction {
    213 public:
    214  virtual ~CustomElementReaction() = default;
    215  MOZ_CAN_RUN_SCRIPT
    216  virtual void Invoke(Element* aElement, ErrorResult& aRv) = 0;
    217  virtual void Traverse(nsCycleCollectionTraversalCallback& aCb) const = 0;
    218  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const = 0;
    219 
    220  bool IsUpgradeReaction() { return mIsUpgradeReaction; }
    221 
    222 protected:
    223  bool mIsUpgradeReaction = false;
    224 };
    225 
    226 // https://html.spec.whatwg.org/multipage/scripting.html#custom-element-reactions-stack
    227 class CustomElementReactionsStack {
    228 public:
    229  NS_INLINE_DECL_REFCOUNTING(CustomElementReactionsStack)
    230 
    231  CustomElementReactionsStack()
    232      : mIsBackupQueueProcessing(false),
    233        mRecursionDepth(0),
    234        mIsElementQueuePushedForCurrentRecursionDepth(false) {}
    235 
    236  // Hold a strong reference of Element so that it does not get cycle collected
    237  // before the reactions in its reaction queue are invoked.
    238  // The element reaction queues are stored in CustomElementData.
    239  // We need to lookup ElementReactionQueueMap again to get relevant reaction
    240  // queue. The choice of 3 for the auto size here is based on running Custom
    241  // Elements wpt tests.
    242  typedef AutoTArray<RefPtr<Element>, 3> ElementQueue;
    243 
    244  /**
    245   * Enqueue a custom element upgrade reaction
    246   * https://html.spec.whatwg.org/multipage/scripting.html#enqueue-a-custom-element-upgrade-reaction
    247   */
    248  void EnqueueUpgradeReaction(Element* aElement,
    249                              CustomElementDefinition* aDefinition);
    250 
    251  /**
    252   * Enqueue a custom element callback reaction
    253   * https://html.spec.whatwg.org/multipage/scripting.html#enqueue-a-custom-element-callback-reaction
    254   */
    255  void EnqueueCallbackReaction(
    256      Element* aElement,
    257      UniquePtr<CustomElementCallback> aCustomElementCallback);
    258 
    259  /**
    260   * [CEReactions] Before executing the algorithm's steps.
    261   * Increase the current recursion depth, and the element queue is pushed
    262   * lazily when we really enqueue reactions.
    263   *
    264   * @return true if the element queue is pushed for "previous" recursion depth.
    265   */
    266  bool EnterCEReactions() {
    267    bool temp = mIsElementQueuePushedForCurrentRecursionDepth;
    268    mRecursionDepth++;
    269    // The is-element-queue-pushed flag is initially false when entering a new
    270    // recursion level. The original value will be cached in AutoCEReaction
    271    // and restored after leaving this recursion level.
    272    mIsElementQueuePushedForCurrentRecursionDepth = false;
    273    return temp;
    274  }
    275 
    276  /**
    277   * [CEReactions] After executing the algorithm's steps.
    278   * Pop and invoke the element queue if it is created and pushed for current
    279   * recursion depth, then decrease the current recursion depth.
    280   *
    281   * @param aCx JSContext used for handling exception thrown by algorithm's
    282   *            steps, this could be a nullptr.
    283   *        aWasElementQueuePushed used for restoring status after leaving
    284   *                               current recursion.
    285   */
    286  MOZ_CAN_RUN_SCRIPT
    287  void LeaveCEReactions(JSContext* aCx, bool aWasElementQueuePushed) {
    288    MOZ_ASSERT(mRecursionDepth);
    289 
    290    if (mIsElementQueuePushedForCurrentRecursionDepth) {
    291      Maybe<JS::AutoSaveExceptionState> ases;
    292      if (aCx) {
    293        ases.emplace(aCx);
    294      }
    295      PopAndInvokeElementQueue();
    296    }
    297    mRecursionDepth--;
    298    // Restore the is-element-queue-pushed flag cached in AutoCEReaction when
    299    // leaving the recursion level.
    300    mIsElementQueuePushedForCurrentRecursionDepth = aWasElementQueuePushed;
    301 
    302    MOZ_ASSERT_IF(!mRecursionDepth, mReactionsStack.IsEmpty());
    303  }
    304 
    305  bool IsElementQueuePushedForCurrentRecursionDepth() {
    306    MOZ_ASSERT_IF(mIsElementQueuePushedForCurrentRecursionDepth,
    307                  !mReactionsStack.IsEmpty() &&
    308                      !mReactionsStack.LastElement()->IsEmpty());
    309    return mIsElementQueuePushedForCurrentRecursionDepth;
    310  }
    311 
    312 private:
    313  ~CustomElementReactionsStack() = default;
    314  ;
    315 
    316  /**
    317   * Push a new element queue onto the custom element reactions stack.
    318   */
    319  void CreateAndPushElementQueue();
    320 
    321  /**
    322   * Pop the element queue from the custom element reactions stack, and invoke
    323   * custom element reactions in that queue.
    324   */
    325  MOZ_CAN_RUN_SCRIPT void PopAndInvokeElementQueue();
    326 
    327  // The choice of 8 for the auto size here is based on gut feeling.
    328  AutoTArray<UniquePtr<ElementQueue>, 8> mReactionsStack;
    329  ElementQueue mBackupQueue;
    330  // https://html.spec.whatwg.org/#enqueue-an-element-on-the-appropriate-element-queue
    331  bool mIsBackupQueueProcessing;
    332 
    333  MOZ_CAN_RUN_SCRIPT void InvokeBackupQueue();
    334 
    335  /**
    336   * Invoke custom element reactions
    337   * https://html.spec.whatwg.org/multipage/scripting.html#invoke-custom-element-reactions
    338   */
    339  MOZ_CAN_RUN_SCRIPT
    340  void InvokeReactions(ElementQueue* aElementQueue, nsIGlobalObject* aGlobal);
    341 
    342  void Enqueue(Element* aElement, CustomElementReaction* aReaction);
    343 
    344  // Current [CEReactions] recursion depth.
    345  uint32_t mRecursionDepth;
    346  // True if the element queue is pushed into reaction stack for current
    347  // recursion depth. This will be cached in AutoCEReaction when entering a new
    348  // CEReaction recursion and restored after leaving the recursion.
    349  bool mIsElementQueuePushedForCurrentRecursionDepth;
    350 
    351 private:
    352  class BackupQueueMicroTask final : public mozilla::MicroTaskRunnable {
    353   public:
    354    explicit BackupQueueMicroTask(CustomElementReactionsStack* aReactionStack)
    355        : MicroTaskRunnable(), mReactionStack(aReactionStack) {
    356      MOZ_ASSERT(!mReactionStack->mIsBackupQueueProcessing,
    357                 "mIsBackupQueueProcessing should be initially false");
    358      mReactionStack->mIsBackupQueueProcessing = true;
    359    }
    360 
    361    MOZ_CAN_RUN_SCRIPT virtual void Run(AutoSlowOperation& aAso) override {
    362      mReactionStack->InvokeBackupQueue();
    363      mReactionStack->mIsBackupQueueProcessing = false;
    364    }
    365 
    366   private:
    367    const RefPtr<CustomElementReactionsStack> mReactionStack;
    368  };
    369 };
    370 
    371 class CustomElementRegistry final : public nsISupports, public nsWrapperCache {
    372 public:
    373  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
    374  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CustomElementRegistry)
    375 
    376 public:
    377  explicit CustomElementRegistry(nsPIDOMWindowInner* aWindow);
    378 
    379 private:
    380  class RunCustomElementCreationCallback : public mozilla::Runnable {
    381   public:
    382    // MOZ_CAN_RUN_SCRIPT_BOUNDARY until Runnable::Run is MOZ_CAN_RUN_SCRIPT.
    383    // See bug 1535398.
    384    MOZ_CAN_RUN_SCRIPT_BOUNDARY
    385    NS_DECL_NSIRUNNABLE
    386 
    387    explicit RunCustomElementCreationCallback(
    388        CustomElementRegistry* aRegistry, nsAtom* aAtom,
    389        CustomElementCreationCallback* aCallback)
    390        : mozilla::Runnable(
    391              "CustomElementRegistry::RunCustomElementCreationCallback"),
    392          mRegistry(aRegistry),
    393          mAtom(aAtom),
    394          mCallback(aCallback) {}
    395 
    396   private:
    397    RefPtr<CustomElementRegistry> mRegistry;
    398    RefPtr<nsAtom> mAtom;
    399    RefPtr<CustomElementCreationCallback> mCallback;
    400  };
    401 
    402 public:
    403  /**
    404   * Looking up a custom element definition.
    405   * https://html.spec.whatwg.org/#look-up-a-custom-element-definition
    406   */
    407  CustomElementDefinition* LookupCustomElementDefinition(nsAtom* aNameAtom,
    408                                                         int32_t aNameSpaceID,
    409                                                         nsAtom* aTypeAtom);
    410 
    411  CustomElementDefinition* LookupCustomElementDefinition(
    412      JSContext* aCx, JSObject* aConstructor) const;
    413 
    414  static void EnqueueLifecycleCallback(ElementCallbackType aType,
    415                                       Element* aCustomElement,
    416                                       const LifecycleCallbackArgs& aArgs,
    417                                       CustomElementDefinition* aDefinition);
    418 
    419  /**
    420   * Upgrade an element.
    421   * https://html.spec.whatwg.org/multipage/scripting.html#upgrades
    422   */
    423  MOZ_CAN_RUN_SCRIPT
    424  static void Upgrade(Element* aElement, CustomElementDefinition* aDefinition,
    425                      ErrorResult& aRv);
    426 
    427  /**
    428   * To allow native code to call methods of chrome-implemented custom elements,
    429   * a helper method may be defined in the custom element called
    430   * 'getCustomInterfaceCallback'. This method takes an IID and returns an
    431   * object which implements an XPCOM interface.
    432   *
    433   * This returns null if aElement is not from a chrome document.
    434   */
    435  static already_AddRefed<nsISupports> CallGetCustomInterface(
    436      Element* aElement, const nsIID& aIID);
    437 
    438  /**
    439   * Registers an unresolved custom element that is a candidate for
    440   * upgrade. |aTypeName| is the name of the custom element type, if it is not
    441   * provided, then element name is used. |aTypeName| should be provided
    442   * when registering a custom element that extends an existing
    443   * element. e.g. <button is="x-button">.
    444   */
    445  void RegisterUnresolvedElement(Element* aElement,
    446                                 nsAtom* aTypeName = nullptr);
    447 
    448  /**
    449   * Unregister an unresolved custom element that is a candidate for
    450   * upgrade when a custom element is removed from tree.
    451   */
    452  void UnregisterUnresolvedElement(Element* aElement,
    453                                   nsAtom* aTypeName = nullptr);
    454 
    455  /**
    456   * Register an element to be upgraded when the custom element creation
    457   * callback is executed.
    458   *
    459   * To be used when LookupCustomElementDefinition() didn't return a definition,
    460   * but with the callback scheduled to be run.
    461   */
    462  inline void RegisterCallbackUpgradeElement(Element* aElement,
    463                                             nsAtom* aTypeName = nullptr) {
    464    if (mElementCreationCallbacksUpgradeCandidatesMap.IsEmpty()) {
    465      return;
    466    }
    467 
    468    RefPtr<nsAtom> typeName = aTypeName;
    469    if (!typeName) {
    470      typeName = aElement->NodeInfo()->NameAtom();
    471    }
    472 
    473    nsTHashSet<RefPtr<nsIWeakReference>>* elements =
    474        mElementCreationCallbacksUpgradeCandidatesMap.Get(typeName);
    475 
    476    // If there isn't a table, there won't be a definition added by the
    477    // callback.
    478    if (!elements) {
    479      return;
    480    }
    481 
    482    nsWeakPtr elem = do_GetWeakReference(aElement);
    483    elements->Insert(elem);
    484  }
    485 
    486  void TraceDefinitions(JSTracer* aTrc);
    487 
    488 private:
    489  ~CustomElementRegistry();
    490 
    491  bool JSObjectToAtomArray(JSContext* aCx, JS::Handle<JSObject*> aConstructor,
    492                           const nsString& aName,
    493                           nsTArray<RefPtr<nsAtom>>& aArray, ErrorResult& aRv);
    494 
    495  void UpgradeCandidates(nsAtom* aKey, CustomElementDefinition* aDefinition,
    496                         ErrorResult& aRv);
    497 
    498  using DefinitionMap =
    499      nsRefPtrHashtable<nsAtomHashKey, CustomElementDefinition>;
    500  using ElementCreationCallbackMap =
    501      nsRefPtrHashtable<nsAtomHashKey, CustomElementCreationCallback>;
    502  using CandidateMap =
    503      nsClassHashtable<nsAtomHashKey, nsTHashSet<RefPtr<nsIWeakReference>>>;
    504  using ConstructorMap =
    505      JS::GCHashMap<JS::Heap<JSObject*>, RefPtr<nsAtom>,
    506                    js::StableCellHasher<JS::Heap<JSObject*>>,
    507                    js::SystemAllocPolicy>;
    508 
    509  // Hashtable for custom element definitions in web components.
    510  // Custom prototypes are stored in the compartment where definition was
    511  // defined.
    512  DefinitionMap mCustomDefinitions;
    513 
    514  // Hashtable for chrome-only callbacks that is called *before* we return
    515  // a CustomElementDefinition, when the typeAtom matches.
    516  // The callbacks are registered with the setElementCreationCallback method.
    517  ElementCreationCallbackMap mElementCreationCallbacks;
    518 
    519  // Hashtable for looking up definitions by using constructor as key.
    520  // Custom elements' name are stored here and we need to lookup
    521  // mCustomDefinitions again to get definitions.
    522  ConstructorMap mConstructors;
    523 
    524  using WhenDefinedPromiseMap = nsRefPtrHashtable<nsAtomHashKey, Promise>;
    525  WhenDefinedPromiseMap mWhenDefinedPromiseMap;
    526 
    527  // The "upgrade candidates map" from the web components spec. Maps from a
    528  // namespace id and local name to a list of elements to upgrade if that
    529  // element is registered as a custom element.
    530  CandidateMap mCandidatesMap;
    531 
    532  // If an element creation callback is found, the nsTHashtable for the
    533  // type is created here, and elements will later be upgraded.
    534  CandidateMap mElementCreationCallbacksUpgradeCandidatesMap;
    535 
    536  nsCOMPtr<nsPIDOMWindowInner> mWindow;
    537 
    538  // It is used to prevent reentrant invocations of element definition.
    539  bool mIsCustomDefinitionRunning;
    540 
    541 private:
    542  int32_t InferNamespace(JSContext* aCx, JS::Handle<JSObject*> constructor);
    543 
    544 public:
    545  nsISupports* GetParentObject() const;
    546 
    547  DocGroup* GetDocGroup() const;
    548 
    549  virtual JSObject* WrapObject(JSContext* aCx,
    550                               JS::Handle<JSObject*> aGivenProto) override;
    551 
    552  void Define(JSContext* aCx, const nsAString& aName,
    553              CustomElementConstructor& aFunctionConstructor,
    554              const ElementDefinitionOptions& aOptions, ErrorResult& aRv);
    555 
    556  void Get(const nsAString& name,
    557           OwningCustomElementConstructorOrUndefined& aRetVal);
    558 
    559  void GetName(JSContext* aCx, CustomElementConstructor& aConstructor,
    560               nsAString& aResult);
    561 
    562  already_AddRefed<Promise> WhenDefined(const nsAString& aName,
    563                                        ErrorResult& aRv);
    564 
    565  // Chrome-only method that give JS an opportunity to only load the custom
    566  // element definition script when needed.
    567  void SetElementCreationCallback(const nsAString& aName,
    568                                  CustomElementCreationCallback& aCallback,
    569                                  ErrorResult& aRv);
    570 
    571  void Upgrade(nsINode& aRoot);
    572 };
    573 
    574 class MOZ_RAII AutoCEReaction final {
    575 public:
    576  // JSContext is allowed to be a nullptr if we are guaranteeing that we're
    577  // not doing something that might throw but not finish reporting a JS
    578  // exception during the lifetime of the AutoCEReaction.
    579  AutoCEReaction(CustomElementReactionsStack* aReactionsStack, JSContext* aCx)
    580      : mReactionsStack(aReactionsStack), mCx(aCx) {
    581    mIsElementQueuePushedForPreviousRecursionDepth =
    582        mReactionsStack->EnterCEReactions();
    583  }
    584 
    585  // MOZ_CAN_RUN_SCRIPT_BOUNDARY because this is called from Maybe<>.reset().
    586  MOZ_CAN_RUN_SCRIPT_BOUNDARY ~AutoCEReaction() {
    587    mReactionsStack->LeaveCEReactions(
    588        mCx, mIsElementQueuePushedForPreviousRecursionDepth);
    589  }
    590 
    591 private:
    592  const RefPtr<CustomElementReactionsStack> mReactionsStack;
    593  JSContext* mCx;
    594  bool mIsElementQueuePushedForPreviousRecursionDepth;
    595 };
    596 
    597 }  // namespace dom
    598 }  // namespace mozilla
    599 
    600 #endif  // mozilla_dom_CustomElementRegistry_h