LCOV - code coverage report
Current view: top level - dom/base - CustomElementRegistry.h (source / functions) Hit Total Coverage
Test: output.info Lines: 12 52 23.1 %
Date: 2017-07-14 16:53:18 Functions: 6 36 16.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim:set ts=2 sw=2 sts=2 et cindent: */
       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/ErrorResult.h"
      14             : #include "mozilla/dom/BindingDeclarations.h"
      15             : #include "mozilla/dom/Element.h"
      16             : #include "mozilla/dom/FunctionBinding.h"
      17             : #include "nsCycleCollectionParticipant.h"
      18             : #include "nsWrapperCache.h"
      19             : 
      20             : class nsDocument;
      21             : 
      22             : namespace mozilla {
      23             : namespace dom {
      24             : 
      25             : struct CustomElementData;
      26             : struct ElementDefinitionOptions;
      27             : struct LifecycleCallbacks;
      28             : class CallbackFunction;
      29             : class CustomElementReaction;
      30             : class Function;
      31             : class Promise;
      32             : 
      33           0 : struct LifecycleCallbackArgs
      34             : {
      35             :   nsString name;
      36             :   nsString oldValue;
      37             :   nsString newValue;
      38             : };
      39             : 
      40           0 : class CustomElementCallback
      41             : {
      42             : public:
      43             :   CustomElementCallback(Element* aThisObject,
      44             :                         nsIDocument::ElementCallbackType aCallbackType,
      45             :                         CallbackFunction* aCallback,
      46             :                         CustomElementData* aOwnerData);
      47             :   void Traverse(nsCycleCollectionTraversalCallback& aCb) const;
      48             :   void Call();
      49           0 :   void SetArgs(LifecycleCallbackArgs& aArgs)
      50             :   {
      51           0 :     MOZ_ASSERT(mType == nsIDocument::eAttributeChanged,
      52             :                "Arguments are only used by attribute changed callback.");
      53           0 :     mArgs = aArgs;
      54           0 :   }
      55             : 
      56             : private:
      57             :   // The this value to use for invocation of the callback.
      58             :   RefPtr<Element> mThisObject;
      59             :   RefPtr<CallbackFunction> mCallback;
      60             :   // The type of callback (eCreated, eAttached, etc.)
      61             :   nsIDocument::ElementCallbackType mType;
      62             :   // Arguments to be passed to the callback,
      63             :   // used by the attribute changed callback.
      64             :   LifecycleCallbackArgs mArgs;
      65             :   // CustomElementData that contains this callback in the
      66             :   // callback queue.
      67             :   CustomElementData* mOwnerData;
      68             : };
      69             : 
      70             : // Each custom element has an associated callback queue and an element is
      71             : // being created flag.
      72             : struct CustomElementData
      73             : {
      74           0 :   NS_INLINE_DECL_REFCOUNTING(CustomElementData)
      75             : 
      76             :   // https://dom.spec.whatwg.org/#concept-element-custom-element-state
      77             :   // CustomElementData is only created on the element which is a custom element
      78             :   // or an upgrade candidate, so the state of an element without
      79             :   // CustomElementData is "uncustomized".
      80             :   enum class State {
      81             :     eUndefined,
      82             :     eFailed,
      83             :     eCustom
      84             :   };
      85             : 
      86             :   explicit CustomElementData(nsIAtom* aType);
      87             :   CustomElementData(nsIAtom* aType, State aState);
      88             :   // Objects in this array are transient and empty after each microtask
      89             :   // checkpoint.
      90             :   nsTArray<nsAutoPtr<CustomElementCallback>> mCallbackQueue;
      91             :   // Custom element type, for <button is="x-button"> or <x-button>
      92             :   // this would be x-button.
      93             :   nsCOMPtr<nsIAtom> mType;
      94             :   // The callback that is next to be processed upon calling RunCallbackQueue.
      95             :   int32_t mCurrentCallback;
      96             :   // Element is being created flag as described in the custom elements spec.
      97             :   bool mElementIsBeingCreated;
      98             :   // Flag to determine if the created callback has been invoked, thus it
      99             :   // determines if other callbacks can be enqueued.
     100             :   bool mCreatedCallbackInvoked;
     101             :   // The microtask level associated with the callbacks in the callback queue,
     102             :   // it is used to determine if a new queue needs to be pushed onto the
     103             :   // processing stack.
     104             :   int32_t mAssociatedMicroTask;
     105             :   // Custom element state as described in the custom element spec.
     106             :   State mState;
     107             :   // custom element reaction queue as described in the custom element spec.
     108             :   // There is 1 reaction in reaction queue, when 1) it becomes disconnected,
     109             :   // 2) it’s adopted into a new document, 3) its attributes are changed,
     110             :   // appended, removed, or replaced.
     111             :   // There are 3 reactions in reaction queue when doing upgrade operation,
     112             :   // e.g., create an element, insert a node.
     113             :   AutoTArray<nsAutoPtr<CustomElementReaction>, 3> mReactionQueue;
     114             : 
     115             :   // Empties the callback queue.
     116             :   void RunCallbackQueue();
     117             : 
     118             : private:
     119           0 :   virtual ~CustomElementData() {}
     120             : };
     121             : 
     122             : // The required information for a custom element as defined in:
     123             : // https://html.spec.whatwg.org/multipage/scripting.html#custom-element-definition
     124           0 : struct CustomElementDefinition
     125             : {
     126             :   CustomElementDefinition(nsIAtom* aType,
     127             :                           nsIAtom* aLocalName,
     128             :                           JSObject* aConstructor,
     129             :                           JSObject* aPrototype,
     130             :                           mozilla::dom::LifecycleCallbacks* aCallbacks,
     131             :                           uint32_t aDocOrder);
     132             : 
     133             :   // The type (name) for this custom element.
     134             :   nsCOMPtr<nsIAtom> mType;
     135             : 
     136             :   // The localname to (e.g. <button is=type> -- this would be button).
     137             :   nsCOMPtr<nsIAtom> mLocalName;
     138             : 
     139             :   // The custom element constructor.
     140             :   JS::Heap<JSObject *> mConstructor;
     141             : 
     142             :   // The prototype to use for new custom elements of this type.
     143             :   JS::Heap<JSObject *> mPrototype;
     144             : 
     145             :   // The lifecycle callbacks to call for this custom element.
     146             :   nsAutoPtr<mozilla::dom::LifecycleCallbacks> mCallbacks;
     147             : 
     148             :   // A construction stack.
     149             :   // TODO: Bug 1287348 - Implement construction stack for upgrading an element
     150             : 
     151             :   // The document custom element order.
     152             :   uint32_t mDocOrder;
     153             : 
     154           0 :   bool IsCustomBuiltIn() {
     155           0 :     return mType != mLocalName;
     156             :   }
     157             : };
     158             : 
     159             : class CustomElementReaction
     160             : {
     161             : public:
     162           0 :   explicit CustomElementReaction(CustomElementRegistry* aRegistry,
     163             :                                  CustomElementDefinition* aDefinition)
     164           0 :     : mRegistry(aRegistry)
     165           0 :     , mDefinition(aDefinition)
     166             :   {
     167           0 :   };
     168             : 
     169           0 :   virtual ~CustomElementReaction() = default;
     170             :   virtual void Invoke(Element* aElement) = 0;
     171             : 
     172             : protected:
     173             :   CustomElementRegistry* mRegistry;
     174             :   CustomElementDefinition* mDefinition;
     175             : };
     176             : 
     177           0 : class CustomElementUpgradeReaction final : public CustomElementReaction
     178             : {
     179             : public:
     180           0 :   explicit CustomElementUpgradeReaction(CustomElementRegistry* aRegistry,
     181             :                                         CustomElementDefinition* aDefinition)
     182           0 :     : CustomElementReaction(aRegistry, aDefinition)
     183             :   {
     184           0 :   }
     185             : 
     186             : private:
     187             :    virtual void Invoke(Element* aElement) override;
     188             : };
     189             : 
     190             : // https://html.spec.whatwg.org/multipage/scripting.html#custom-element-reactions-stack
     191             : class CustomElementReactionsStack
     192             : {
     193             : public:
     194        1206 :   NS_INLINE_DECL_REFCOUNTING(CustomElementReactionsStack)
     195             : 
     196           2 :   CustomElementReactionsStack()
     197           2 :     : mIsBackupQueueProcessing(false)
     198             :   {
     199           2 :   }
     200             : 
     201             :   // nsWeakPtr is a weak pointer of Element
     202             :   // The element reaction queues are stored in CustomElementData.
     203             :   // We need to lookup ElementReactionQueueMap again to get relevant reaction queue.
     204             :   // The choice of 1 for the auto size here is based on gut feeling.
     205             :   typedef AutoTArray<nsWeakPtr, 1> ElementQueue;
     206             : 
     207             :   /**
     208             :    * Enqueue a custom element upgrade reaction
     209             :    * https://html.spec.whatwg.org/multipage/scripting.html#enqueue-a-custom-element-upgrade-reaction
     210             :    */
     211             :   void EnqueueUpgradeReaction(CustomElementRegistry* aRegistry,
     212             :                               Element* aElement,
     213             :                               CustomElementDefinition* aDefinition);
     214             : 
     215             :   // [CEReactions] Before executing the algorithm's steps
     216             :   // Push a new element queue onto the custom element reactions stack.
     217             :   void CreateAndPushElementQueue();
     218             : 
     219             :   // [CEReactions] After executing the algorithm's steps
     220             :   // Pop the element queue from the custom element reactions stack,
     221             :   // and invoke custom element reactions in that queue.
     222             :   void PopAndInvokeElementQueue();
     223             : 
     224             : private:
     225           0 :   ~CustomElementReactionsStack() {};
     226             : 
     227             :   // The choice of 8 for the auto size here is based on gut feeling.
     228             :   AutoTArray<ElementQueue, 8> mReactionsStack;
     229             :   ElementQueue mBackupQueue;
     230             :   // https://html.spec.whatwg.org/#enqueue-an-element-on-the-appropriate-element-queue
     231             :   bool mIsBackupQueueProcessing;
     232             : 
     233             :   void InvokeBackupQueue();
     234             : 
     235             :   /**
     236             :    * Invoke custom element reactions
     237             :    * https://html.spec.whatwg.org/multipage/scripting.html#invoke-custom-element-reactions
     238             :    */
     239             :   void InvokeReactions(ElementQueue& aElementQueue);
     240             : 
     241             :   void Enqueue(Element* aElement, CustomElementReaction* aReaction);
     242             : 
     243             : private:
     244           0 :   class ProcessBackupQueueRunnable : public mozilla::Runnable {
     245             :     public:
     246           0 :       explicit ProcessBackupQueueRunnable(
     247             :         CustomElementReactionsStack* aReactionStack)
     248           0 :         : Runnable(
     249             :             "dom::CustomElementReactionsStack::ProcessBackupQueueRunnable")
     250           0 :         , mReactionStack(aReactionStack)
     251             :       {
     252           0 :         MOZ_ASSERT(!mReactionStack->mIsBackupQueueProcessing,
     253             :                    "mIsBackupQueueProcessing should be initially false");
     254           0 :         mReactionStack->mIsBackupQueueProcessing = true;
     255           0 :       }
     256             : 
     257           0 :       NS_IMETHOD Run() override
     258             :       {
     259           0 :         mReactionStack->InvokeBackupQueue();
     260           0 :         mReactionStack->mIsBackupQueueProcessing = false;
     261           0 :         return NS_OK;
     262             :       }
     263             : 
     264             :     private:
     265             :       RefPtr<CustomElementReactionsStack> mReactionStack;
     266             :   };
     267             : };
     268             : 
     269             : class CustomElementRegistry final : public nsISupports,
     270             :                                     public nsWrapperCache
     271             : {
     272             :   // Allow nsDocument to access mCustomDefinitions and mCandidatesMap.
     273             :   friend class ::nsDocument;
     274             : 
     275             : public:
     276             :   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
     277           1 :   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CustomElementRegistry)
     278             : 
     279             : public:
     280             :   static bool IsCustomElementEnabled(JSContext* aCx = nullptr,
     281             :                                      JSObject* aObject = nullptr);
     282             : 
     283             :   static void ProcessTopElementQueue();
     284             : 
     285             :   static void XPCOMShutdown();
     286             : 
     287             :   explicit CustomElementRegistry(nsPIDOMWindowInner* aWindow);
     288             : 
     289             :   /**
     290             :    * Looking up a custom element definition.
     291             :    * https://html.spec.whatwg.org/#look-up-a-custom-element-definition
     292             :    */
     293             :   CustomElementDefinition* LookupCustomElementDefinition(
     294             :     const nsAString& aLocalName, const nsAString* aIs = nullptr) const;
     295             : 
     296             :   CustomElementDefinition* LookupCustomElementDefinition(
     297             :     JSContext* aCx, JSObject *aConstructor) const;
     298             : 
     299             :   /**
     300             :    * Enqueue created callback or register upgrade candidate for
     301             :    * newly created custom elements, possibly extending an existing type.
     302             :    * ex. <x-button>, <button is="x-button> (type extension)
     303             :    */
     304             :   void SetupCustomElement(Element* aElement, const nsAString* aTypeExtension);
     305             : 
     306             :   void EnqueueLifecycleCallback(nsIDocument::ElementCallbackType aType,
     307             :                                 Element* aCustomElement,
     308             :                                 LifecycleCallbackArgs* aArgs,
     309             :                                 CustomElementDefinition* aDefinition);
     310             : 
     311             :   void GetCustomPrototype(nsIAtom* aAtom,
     312             :                           JS::MutableHandle<JSObject*> aPrototype);
     313             : 
     314             :   void Upgrade(Element* aElement, CustomElementDefinition* aDefinition);
     315             : 
     316             : private:
     317             :   ~CustomElementRegistry();
     318             : 
     319             :   /**
     320             :    * Registers an unresolved custom element that is a candidate for
     321             :    * upgrade when the definition is registered via registerElement.
     322             :    * |aTypeName| is the name of the custom element type, if it is not
     323             :    * provided, then element name is used. |aTypeName| should be provided
     324             :    * when registering a custom element that extends an existing
     325             :    * element. e.g. <button is="x-button">.
     326             :    */
     327             :   void RegisterUnresolvedElement(Element* aElement,
     328             :                                  nsIAtom* aTypeName = nullptr);
     329             : 
     330             :   void UpgradeCandidates(JSContext* aCx,
     331             :                          nsIAtom* aKey,
     332             :                          CustomElementDefinition* aDefinition,
     333             :                          ErrorResult& aRv);
     334             : 
     335             :   typedef nsClassHashtable<nsISupportsHashKey, CustomElementDefinition>
     336             :     DefinitionMap;
     337             :   typedef nsClassHashtable<nsISupportsHashKey, nsTArray<nsWeakPtr>>
     338             :     CandidateMap;
     339             :   typedef JS::GCHashMap<JS::Heap<JSObject*>,
     340             :                         nsCOMPtr<nsIAtom>,
     341             :                         js::MovableCellHasher<JS::Heap<JSObject*>>,
     342             :                         js::SystemAllocPolicy> ConstructorMap;
     343             : 
     344             :   // Hashtable for custom element definitions in web components.
     345             :   // Custom prototypes are stored in the compartment where
     346             :   // registerElement was called.
     347             :   DefinitionMap mCustomDefinitions;
     348             : 
     349             :   // Hashtable for looking up definitions by using constructor as key.
     350             :   // Custom elements' name are stored here and we need to lookup
     351             :   // mCustomDefinitions again to get definitions.
     352             :   ConstructorMap mConstructors;
     353             : 
     354             :   typedef nsRefPtrHashtable<nsISupportsHashKey, Promise>
     355             :     WhenDefinedPromiseMap;
     356             :   WhenDefinedPromiseMap mWhenDefinedPromiseMap;
     357             : 
     358             :   // The "upgrade candidates map" from the web components spec. Maps from a
     359             :   // namespace id and local name to a list of elements to upgrade if that
     360             :   // element is registered as a custom element.
     361             :   CandidateMap mCandidatesMap;
     362             : 
     363             :   nsCOMPtr<nsPIDOMWindowInner> mWindow;
     364             : 
     365             :   // Array representing the processing stack in the custom elements
     366             :   // specification. The processing stack is conceptually a stack of
     367             :   // element queues. Each queue is represented by a sequence of
     368             :   // CustomElementData in this array, separated by nullptr that
     369             :   // represent the boundaries of the items in the stack. The first
     370             :   // queue in the stack is the base element queue.
     371             :   static mozilla::Maybe<nsTArray<RefPtr<CustomElementData>>> sProcessingStack;
     372             : 
     373             :   // It is used to prevent reentrant invocations of element definition.
     374             :   bool mIsCustomDefinitionRunning;
     375             : 
     376             : private:
     377             :   class MOZ_RAII AutoSetRunningFlag final {
     378             :     public:
     379           0 :       explicit AutoSetRunningFlag(CustomElementRegistry* aRegistry)
     380           0 :         : mRegistry(aRegistry)
     381             :       {
     382           0 :         MOZ_ASSERT(!mRegistry->mIsCustomDefinitionRunning,
     383             :                    "IsCustomDefinitionRunning flag should be initially false");
     384           0 :         mRegistry->mIsCustomDefinitionRunning = true;
     385           0 :       }
     386             : 
     387           0 :       ~AutoSetRunningFlag() {
     388           0 :         mRegistry->mIsCustomDefinitionRunning = false;
     389           0 :       }
     390             : 
     391             :     private:
     392             :       CustomElementRegistry* mRegistry;
     393             :   };
     394             : 
     395             : public:
     396             :   nsISupports* GetParentObject() const;
     397             : 
     398             :   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
     399             : 
     400             :   void Define(const nsAString& aName, Function& aFunctionConstructor,
     401             :               const ElementDefinitionOptions& aOptions, ErrorResult& aRv);
     402             : 
     403             :   void Get(JSContext* cx, const nsAString& name,
     404             :            JS::MutableHandle<JS::Value> aRetVal);
     405             : 
     406             :   already_AddRefed<Promise> WhenDefined(const nsAString& aName, ErrorResult& aRv);
     407             : };
     408             : 
     409             : class MOZ_RAII AutoCEReaction final {
     410             :   public:
     411         602 :     explicit AutoCEReaction(CustomElementReactionsStack* aReactionsStack)
     412         602 :       : mReactionsStack(aReactionsStack) {
     413         602 :       mReactionsStack->CreateAndPushElementQueue();
     414         602 :     }
     415        1204 :     ~AutoCEReaction() {
     416         602 :       mReactionsStack->PopAndInvokeElementQueue();
     417         602 :     }
     418             :   private:
     419             :     RefPtr<CustomElementReactionsStack> mReactionsStack;
     420             : };
     421             : 
     422             : } // namespace dom
     423             : } // namespace mozilla
     424             : 
     425             : 
     426             : #endif // mozilla_dom_CustomElementRegistry_h

Generated by: LCOV version 1.13