Bug 1301024 - Part 2: Implement create an element steps. r=smaug
authorJohn Dai <jdai@mozilla.com>
Mon, 25 Sep 2017 01:33:00 -0400
changeset 382693 a6fdd6eae58340d86c716dca6a0be8aeedfb3573
parent 382692 861b7c93c156abc54546d6a2af547f4847c99d80
child 382694 a47aacfccdb2bbd11ddcdb62bc07f010b0ac4bfa
push id95398
push userryanvm@gmail.com
push dateMon, 25 Sep 2017 13:07:35 +0000
treeherdermozilla-inbound@2d14b96108c7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1301024
milestone58.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1301024 - Part 2: Implement create an element steps. r=smaug
dom/base/CustomElementRegistry.cpp
dom/base/CustomElementRegistry.h
dom/base/nsContentUtils.cpp
dom/base/nsContentUtils.h
dom/html/nsHTMLContentSink.cpp
--- a/dom/base/CustomElementRegistry.cpp
+++ b/dom/base/CustomElementRegistry.cpp
@@ -934,17 +934,17 @@ DoUpgrade(Element* aElement,
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 }
 
 } // anonymous namespace
 
 // https://html.spec.whatwg.org/multipage/scripting.html#upgrades
-void
+/* static */ void
 CustomElementRegistry::Upgrade(Element* aElement,
                                CustomElementDefinition* aDefinition,
                                ErrorResult& aRv)
 {
   aElement->RemoveStates(NS_EVENT_STATE_UNRESOLVED);
 
   RefPtr<CustomElementData> data = aElement->GetCustomElementData();
   MOZ_ASSERT(data, "CustomElementData should exist");
@@ -972,18 +972,20 @@ CustomElementRegistry::Upgrade(Element* 
                                                             namespaceURI);
 
         LifecycleCallbackArgs args = {
           nsDependentAtomString(attrName),
           VoidString(),
           (attrValue.IsEmpty() ? VoidString() : attrValue),
           (namespaceURI.IsEmpty() ? VoidString() : namespaceURI)
         };
-        EnqueueLifecycleCallback(nsIDocument::eAttributeChanged, aElement,
-                                 &args, aDefinition);
+        nsContentUtils::EnqueueLifecycleCallback(aElement->OwnerDoc(),
+                                                 nsIDocument::eAttributeChanged,
+                                                 aElement,
+                                                 &args, aDefinition);
       }
     }
   }
 
   // Step 4.
   // TODO: Bug 1334043 - Implement connected lifecycle callbacks for custom elements
 
   // Step 5.
@@ -998,17 +1000,19 @@ CustomElementRegistry::Upgrade(Element* 
     data->mReactionQueue.Clear();
     return;
   }
 
   // Step 8.
   data->mState = CustomElementData::State::eCustom;
 
   // This is for old spec.
-  EnqueueLifecycleCallback(nsIDocument::eCreated, aElement, nullptr, aDefinition);
+  nsContentUtils::EnqueueLifecycleCallback(aElement->OwnerDoc(),
+                                           nsIDocument::eCreated,
+                                           aElement, nullptr, aDefinition);
 }
 
 //-----------------------------------------------------
 // CustomElementReactionsStack
 
 void
 CustomElementReactionsStack::CreateAndPushElementQueue()
 {
--- a/dom/base/CustomElementRegistry.h
+++ b/dom/base/CustomElementRegistry.h
@@ -357,32 +357,33 @@ public:
   void EnqueueLifecycleCallback(nsIDocument::ElementCallbackType aType,
                                 Element* aCustomElement,
                                 LifecycleCallbackArgs* aArgs,
                                 CustomElementDefinition* aDefinition);
 
   void GetCustomPrototype(nsIAtom* aAtom,
                           JS::MutableHandle<JSObject*> aPrototype);
 
+  void SyncInvokeReactions(nsIDocument::ElementCallbackType aType,
+                           Element* aCustomElement,
+                           CustomElementDefinition* aDefinition);
+
   /**
    * Upgrade an element.
    * https://html.spec.whatwg.org/multipage/scripting.html#upgrades
    */
-  void Upgrade(Element* aElement, CustomElementDefinition* aDefinition, ErrorResult& aRv);
+  static void Upgrade(Element* aElement, CustomElementDefinition* aDefinition, ErrorResult& aRv);
 
 private:
   ~CustomElementRegistry();
 
   UniquePtr<CustomElementCallback> CreateCustomElementCallback(
     nsIDocument::ElementCallbackType aType, Element* aCustomElement,
     LifecycleCallbackArgs* aArgs, CustomElementDefinition* aDefinition);
 
-  void SyncInvokeReactions(nsIDocument::ElementCallbackType aType,
-                           Element* aCustomElement,
-                           CustomElementDefinition* aDefinition);
   /**
    * Registers an unresolved custom element that is a candidate for
    * upgrade when the definition is registered via registerElement.
    * |aTypeName| is the name of the custom element type, if it is not
    * provided, then element name is used. |aTypeName| should be provided
    * when registering a custom element that extends an existing
    * element. e.g. <button is="x-button">.
    */
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -10150,16 +10150,59 @@ nsContentUtils::GetElementDefinitionIfOb
   if (!definition || !definition->IsInObservedAttributeList(aAttrName)) {
     return nullptr;
   }
 
   return definition;
 }
 
 /* static */ void
+nsContentUtils::SyncInvokeReactions(nsIDocument::ElementCallbackType aType,
+                                    Element* aElement,
+                                    CustomElementDefinition* aDefinition)
+{
+  MOZ_ASSERT(aElement);
+
+  nsIDocument* doc = aElement->OwnerDoc();
+  nsPIDOMWindowInner* window(doc->GetInnerWindow());
+  if (!window) {
+    return;
+  }
+
+  RefPtr<CustomElementRegistry> registry(window->CustomElements());
+  if (!registry) {
+    return;
+  }
+
+  registry->SyncInvokeReactions(aType, aElement, aDefinition);
+}
+
+/* static */ void
+nsContentUtils::EnqueueUpgradeReaction(Element* aElement,
+                                       CustomElementDefinition* aDefinition)
+{
+  MOZ_ASSERT(aElement);
+
+  nsIDocument* doc = aElement->OwnerDoc();
+  nsPIDOMWindowInner* window(doc->GetInnerWindow());
+  if (!window) {
+    return;
+  }
+
+  RefPtr<CustomElementRegistry> registry(window->CustomElements());
+  if (!registry) {
+    return;
+  }
+
+  CustomElementReactionsStack* stack =
+    doc->GetDocGroup()->CustomElementReactionsStack();
+  stack->EnqueueUpgradeReaction(registry, aElement, aDefinition);
+}
+
+/* static */ void
 nsContentUtils::EnqueueLifecycleCallback(nsIDocument* aDoc,
                                          nsIDocument::ElementCallbackType aType,
                                          Element* aCustomElement,
                                          LifecycleCallbackArgs* aArgs,
                                          CustomElementDefinition* aDefinition)
 {
   MOZ_ASSERT(aDoc);
 
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -2993,16 +2993,23 @@ public:
   static void SetupCustomElement(Element* aElement,
                                  const nsAString* aTypeExtension = nullptr);
 
   static mozilla::dom::CustomElementDefinition*
   GetElementDefinitionIfObservingAttr(Element* aCustomElement,
                                       nsIAtom* aExtensionType,
                                       nsIAtom* aAttrName);
 
+  static void SyncInvokeReactions(nsIDocument::ElementCallbackType aType,
+                                  Element* aCustomElement,
+                                  mozilla::dom::CustomElementDefinition* aDefinition);
+
+  static void EnqueueUpgradeReaction(Element* aElement,
+                                     mozilla::dom::CustomElementDefinition* aDefinition);
+
   static void EnqueueLifecycleCallback(nsIDocument* aDoc,
                                        nsIDocument::ElementCallbackType aType,
                                        Element* aCustomElement,
                                        mozilla::dom::LifecycleCallbackArgs* aArgs = nullptr,
                                        mozilla::dom::CustomElementDefinition* aDefinition = nullptr);
 
   static void GetCustomPrototype(nsIDocument* aDoc,
                                  int32_t aNamespaceID,
--- a/dom/html/nsHTMLContentSink.cpp
+++ b/dom/html/nsHTMLContentSink.cpp
@@ -218,31 +218,126 @@ public:
     nsIContent *Add(nsIContent *child);
   };
 
   Node* mStack;
   int32_t mStackSize;
   int32_t mStackPos;
 };
 
+static void
+DoCustomElementCreate(Element** aElement, nsIDocument* aDoc,
+                      CustomElementConstructor* aConstructor, ErrorResult& aRv)
+{
+  RefPtr<Element> element =
+    aConstructor->Construct("Custom Element Create", aRv);
+  if (aRv.Failed() || !element->IsHTMLElement()) {
+    aRv.ThrowTypeError<MSG_THIS_DOES_NOT_IMPLEMENT_INTERFACE>(NS_LITERAL_STRING("HTMLElement"));
+    return;
+  }
+
+  if (aDoc != element->OwnerDoc() || element->GetParentNode() ||
+      element->HasChildren() || element->GetAttrCount()) {
+    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return;
+  }
+
+  element.forget(aElement);
+}
+
 nsresult
 NS_NewHTMLElement(Element** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
                   FromParser aFromParser, const nsAString* aIs)
 {
   *aResult = nullptr;
 
   RefPtr<mozilla::dom::NodeInfo> nodeInfo = aNodeInfo;
 
   nsIAtom *name = nodeInfo->NameAtom();
 
   NS_ASSERTION(nodeInfo->NamespaceEquals(kNameSpaceID_XHTML),
                "Trying to HTML elements that don't have the XHTML namespace");
 
   int32_t tag = nsHTMLTags::CaseSensitiveAtomTagToId(name);
 
+  // https://dom.spec.whatwg.org/#concept-create-element
+  // We only handle the "synchronous custom elements flag is set" now.
+  // For the unset case (e.g. cloning a node), see bug 1319342 for that.
+  // Step 4.
+  CustomElementDefinition* definition = nullptr;
+  if (CustomElementRegistry::IsCustomElementEnabled()) {
+    definition =
+      nsContentUtils::LookupCustomElementDefinition(nodeInfo->GetDocument(),
+                                                    nodeInfo->LocalName(),
+                                                    nodeInfo->NamespaceID(),
+                                                    aIs);
+  }
+
+  // It might be a problem that parser synchronously calls constructor, so filed
+  // bug 1378079 to figure out what we should do for parser case.
+  if (definition) {
+    /*
+     * Synchronous custom elements flag is determined by 3 places in spec,
+     * 1) create an element for a token, the flag is determined by
+     *    "will execute script" which is not originally created
+     *    for the HTML fragment parsing algorithm.
+     * 2) createElement and createElementNS, the flag is the same as
+     *    NOT_FROM_PARSER.
+     * 3) clone a node, our implementation will not go into this function.
+     * For the unset case which is non-synchronous only applied for
+     * inner/outerHTML.
+     */
+    bool synchronousCustomElements = aFromParser != dom::FROM_PARSER_FRAGMENT ||
+                                     aFromParser == dom::NOT_FROM_PARSER;
+    // Per discussion in https://github.com/w3c/webcomponents/issues/635,
+    // use entry global in those places that are called from JS APIs.
+    nsIGlobalObject* global = GetEntryGlobal();
+    MOZ_ASSERT(global);
+    AutoEntryScript aes(global, "create custom elements");
+    JSContext* cx = aes.cx();
+    ErrorResult rv;
+
+    // Step 5.
+    if (definition->IsCustomBuiltIn()) {
+      // SetupCustomElement() should be called with an element that don't have
+      // CustomElementData setup, if not we will hit the assertion in
+      // SetCustomElementData().
+      nsCOMPtr<nsIAtom> tagAtom = nodeInfo->NameAtom();
+      nsCOMPtr<nsIAtom> typeAtom = aIs ? NS_Atomize(*aIs) : tagAtom;
+      // Built-in element
+      *aResult = CreateHTMLElement(tag, nodeInfo.forget(), aFromParser).take();
+      (*aResult)->SetCustomElementData(new CustomElementData(typeAtom));
+      if (synchronousCustomElements) {
+        CustomElementRegistry::Upgrade(*aResult, definition, rv);
+      } else {
+        nsContentUtils::EnqueueUpgradeReaction(*aResult, definition);
+      }
+
+      if (rv.MaybeSetPendingException(cx)) {
+        aes.ReportException();
+      }
+      return NS_OK;
+    }
+
+    // Step 6.1.
+    if (synchronousCustomElements) {
+      DoCustomElementCreate(aResult, nodeInfo->GetDocument(),
+                            definition->mConstructor, rv);
+      if (rv.MaybeSetPendingException(cx)) {
+        NS_IF_ADDREF(*aResult = NS_NewHTMLUnknownElement(nodeInfo.forget(), aFromParser));
+      }
+      return NS_OK;
+    }
+
+    // Step 6.2.
+    NS_IF_ADDREF(*aResult = NS_NewHTMLElement(nodeInfo.forget(), aFromParser));
+    nsContentUtils::EnqueueUpgradeReaction(*aResult, definition);
+    return NS_OK;
+  }
+
   // Per the Custom Element specification, unknown tags that are valid custom
   // element names should be HTMLElement instead of HTMLUnknownElement.
   bool isCustomElementName = (tag == eHTMLTag_userdefined &&
                               nsContentUtils::IsCustomElementName(name));
   if (isCustomElementName) {
     NS_IF_ADDREF(*aResult = NS_NewHTMLElement(nodeInfo.forget(), aFromParser));
   } else {
     *aResult = CreateHTMLElement(tag, nodeInfo.forget(), aFromParser).take();