Bug 1404420: Add custom element support to XUL. r?edgar, r?smaug draft
authorDave Townsend <dtownsend@oxymoronical.com>
Tue, 10 Oct 2017 15:25:10 -0700
changeset 699871 08300a82bf3b92aaa185f5dc81b671d84e8f4d5e
parent 699870 12d7e455b355cc68131aaf9f28f88bfd6226b828
child 699872 1a47f4e09b667953cc65a8752853ec026410857c
push id89707
push userdtownsend@mozilla.com
push dateFri, 17 Nov 2017 20:49:13 +0000
reviewersedgar, smaug
bugs1404420
milestone59.0a1
Bug 1404420: Add custom element support to XUL. r?edgar, r?smaug MozReview-Commit-ID: BietEX2gOoG
dom/base/CustomElementRegistry.cpp
dom/base/CustomElementRegistry.h
dom/base/nsContentCreatorFunctions.h
dom/base/nsContentUtils.cpp
dom/base/nsContentUtils.h
dom/base/nsDocument.cpp
dom/base/nsNameSpaceManager.cpp
dom/base/nsNodeUtils.cpp
dom/bindings/BindingUtils.cpp
dom/bindings/BindingUtils.h
dom/bindings/Codegen.py
dom/html/nsHTMLContentSink.cpp
dom/tests/mochitest/webcomponents/chrome.ini
dom/tests/mochitest/webcomponents/test_xul_custom_element.xul
dom/webidl/XULElement.webidl
dom/xbl/nsXBLService.cpp
dom/xml/XMLDocument.cpp
dom/xul/moz.build
dom/xul/nsXULElement.cpp
dom/xul/nsXULElement.h
layout/xul/nsDocElementBoxFrame.cpp
--- a/dom/base/CustomElementRegistry.cpp
+++ b/dom/base/CustomElementRegistry.cpp
@@ -188,33 +188,35 @@ CustomElementData::Unlink()
 //-----------------------------------------------------
 // CustomElementRegistry
 
 namespace {
 
 class MOZ_RAII AutoConstructionStackEntry final
 {
 public:
-  AutoConstructionStackEntry(nsTArray<RefPtr<nsGenericHTMLElement>>& aStack,
-                             nsGenericHTMLElement* aElement)
+  AutoConstructionStackEntry(nsTArray<RefPtr<Element>>& aStack,
+                             Element* aElement)
     : mStack(aStack)
   {
+    MOZ_ASSERT(aElement->IsHTMLElement() || aElement->IsXULElement());
+
     mIndex = mStack.Length();
     mStack.AppendElement(aElement);
   }
 
   ~AutoConstructionStackEntry()
   {
     MOZ_ASSERT(mIndex == mStack.Length() - 1,
                "Removed element should be the last element");
     mStack.RemoveElementAt(mIndex);
   }
 
 private:
-  nsTArray<RefPtr<nsGenericHTMLElement>>& mStack;
+  nsTArray<RefPtr<Element>>& mStack;
   uint32_t mIndex;
 };
 
 } // namespace anonymous
 
 // Only needed for refcounted objects.
 NS_IMPL_CYCLE_COLLECTION_CLASS(CustomElementRegistry)
 
@@ -985,18 +987,17 @@ CustomElementRegistry::Upgrade(Element* 
 
   // Step 4.
   if (aElement->IsInComposedDoc()) {
     nsContentUtils::EnqueueLifecycleCallback(nsIDocument::eConnected, aElement,
       nullptr, nullptr, aDefinition);
   }
 
   // Step 5.
-  AutoConstructionStackEntry acs(aDefinition->mConstructionStack,
-                                 nsGenericHTMLElement::FromContent(aElement));
+  AutoConstructionStackEntry acs(aDefinition->mConstructionStack, aElement);
 
   // Step 6 and step 7.
   DoUpgrade(aElement, aDefinition->mConstructor, aRv);
   if (aRv.Failed()) {
     data->mState = CustomElementData::State::eFailed;
     // Empty element's custom element reaction queue.
     data->mReactionQueue.Clear();
     return;
--- a/dom/base/CustomElementRegistry.h
+++ b/dom/base/CustomElementRegistry.h
@@ -178,17 +178,17 @@ struct CustomElementDefinition
 
   // The prototype to use for new custom elements of this type.
   JS::Heap<JSObject *> mPrototype;
 
   // The lifecycle callbacks to call for this custom element.
   UniquePtr<mozilla::dom::LifecycleCallbacks> mCallbacks;
 
   // A construction stack. Use nullptr to represent an "already constructed marker".
-  nsTArray<RefPtr<nsGenericHTMLElement>> mConstructionStack;
+  nsTArray<RefPtr<Element>> mConstructionStack;
 
   // The document custom element order.
   uint32_t mDocOrder;
 
   bool IsCustomBuiltIn()
   {
     return mType != mLocalName;
   }
--- a/dom/base/nsContentCreatorFunctions.h
+++ b/dom/base/nsContentCreatorFunctions.h
@@ -54,17 +54,19 @@ CreateHTMLElement(uint32_t aNodeType,
 
 nsresult
 NS_NewMathMLElement(mozilla::dom::Element** aResult,
                     already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 
 #ifdef MOZ_XUL
 nsresult
 NS_NewXULElement(mozilla::dom::Element** aResult,
-                 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
+                 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
+                 mozilla::dom::FromParser aFromParser,
+                 const nsAString* aIs);
 
 void
 NS_TrustedNewXULElement(nsIContent** aResult,
                         already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 #endif
 
 nsresult
 NS_NewSVGElement(mozilla::dom::Element** aResult,
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -221,16 +221,17 @@
 #include "mozilla/BloomFilter.h"
 #include "TabChild.h"
 #include "mozilla/dom/DocGroup.h"
 #include "mozilla/dom/TabGroup.h"
 #include "nsIWebNavigationInfo.h"
 #include "nsPluginHost.h"
 #include "mozilla/HangAnnotations.h"
 #include "mozilla/Encoding.h"
+#include "nsXULElement.h"
 
 #include "nsIBidiKeyboard.h"
 
 #if defined(XP_WIN)
 // Undefine LoadImage to prevent naming conflict with Windows.
 #undef LoadImage
 #endif
 
@@ -10136,25 +10137,206 @@ nsContentUtils::TryToUpgradeElement(Elem
   } else {
     // Add an unresolved custom element that is a candidate for
     // upgrade when a custom element is connected to the document.
     // We will make sure it's shadow-including tree order in bug 1326028.
     nsContentUtils::RegisterUnresolvedElement(aElement, typeAtom);
   }
 }
 
+static void
+DoCustomElementCreate(Element** aElement, nsIDocument* aDoc, NodeInfo* aNodeInfo,
+                      CustomElementConstructor* aConstructor, ErrorResult& aRv)
+{
+  RefPtr<Element> element =
+    aConstructor->Construct("Custom Element Create", aRv);
+  if (aRv.Failed()) {
+    return;
+  }
+
+  if (aNodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
+    if (!element || !element->IsHTMLElement()) {
+      aRv.ThrowTypeError<MSG_THIS_DOES_NOT_IMPLEMENT_INTERFACE>(NS_LITERAL_STRING("HTMLElement"));
+      return;
+    }
+  } else {
+    if (!element || !element->IsXULElement()) {
+      aRv.ThrowTypeError<MSG_THIS_DOES_NOT_IMPLEMENT_INTERFACE>(NS_LITERAL_STRING("XULElement"));
+      return;
+    }
+  }
+
+  nsAtom* localName = aNodeInfo->NameAtom();
+
+  if (aDoc != element->OwnerDoc() || element->GetParentNode() ||
+      element->HasChildren() || element->GetAttrCount() ||
+      element->NodeInfo()->NameAtom() != localName) {
+    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return;
+  }
+
+  element.forget(aElement);
+}
+
+/* static */ nsresult
+nsContentUtils::NewXULOrHTMLElement(Element** aResult, mozilla::dom::NodeInfo* aNodeInfo,
+                                    FromParser aFromParser, const nsAString* aIs,
+                                    mozilla::dom::CustomElementDefinition* aDefinition)
+{
+  RefPtr<mozilla::dom::NodeInfo> nodeInfo = aNodeInfo;
+  NS_ASSERTION(nodeInfo->NamespaceEquals(kNameSpaceID_XHTML) ||
+               nodeInfo->NamespaceEquals(kNameSpaceID_XUL),
+               "Can only create XUL or XHTML elements.");
+
+  nsAtom *name = nodeInfo->NameAtom();
+  RefPtr<nsAtom> tagAtom = nodeInfo->NameAtom();
+  RefPtr<nsAtom> typeAtom = aIs ? NS_Atomize(*aIs) : tagAtom;
+
+  int32_t tag = eHTMLTag_unknown;
+  if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
+    tag = nsHTMLTags::CaseSensitiveAtomTagToId(name);
+  }
+
+  nsIDocument* doc = nodeInfo->GetDocument();
+
+  // 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 = aDefinition;
+  if (!definition && CustomElementRegistry::IsCustomElementEnabled()) {
+    definition =
+      nsContentUtils::LookupCustomElementDefinition(doc,
+                                                    nodeInfo->LocalName(),
+                                                    nodeInfo->NamespaceID(),
+                                                    typeAtom);
+  }
+
+  // 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 and use the
+    // node document's global object if it is called from parser.
+    nsIGlobalObject* global;
+    if (aFromParser == dom::NOT_FROM_PARSER) {
+      global = GetEntryGlobal();
+    } else {
+      global = nodeInfo->GetDocument()->GetScopeObject();
+    }
+    if (!global) {
+      // In browser chrome code, one may have access to a document which doesn't
+      // have scope object anymore.
+      return NS_ERROR_FAILURE;
+    }
+
+    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().
+      // Built-in element
+      if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
+        *aResult = CreateHTMLElement(tag, nodeInfo.forget(), aFromParser).take();
+      } else {
+        *aResult = new nsXULElement(nodeInfo.forget());
+      }
+      (*aResult)->SetCustomElementData(new CustomElementData(typeAtom));
+      if (synchronousCustomElements) {
+        CustomElementRegistry::Upgrade(*aResult, definition, rv);
+        if (rv.MaybeSetPendingException(cx)) {
+          aes.ReportException();
+        }
+      } else {
+        nsContentUtils::EnqueueUpgradeReaction(*aResult, definition);
+      }
+
+      return NS_OK;
+    }
+
+    // Step 6.1.
+    if (synchronousCustomElements) {
+      DoCustomElementCreate(aResult, doc, nodeInfo, definition->mConstructor, rv);
+      if (rv.MaybeSetPendingException(cx)) {
+        if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
+          NS_IF_ADDREF(*aResult = NS_NewHTMLUnknownElement(nodeInfo.forget(), aFromParser));
+        } else {
+          NS_IF_ADDREF(*aResult = new nsXULElement(nodeInfo.forget()));
+        }
+      }
+      return NS_OK;
+    }
+
+    // Step 6.2.
+    if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
+      NS_IF_ADDREF(*aResult = NS_NewHTMLElement(nodeInfo.forget(), aFromParser));
+    } else {
+      NS_IF_ADDREF(*aResult = new nsXULElement(nodeInfo.forget()));
+    }
+    (*aResult)->SetCustomElementData(new CustomElementData(definition->mType));
+    nsContentUtils::EnqueueUpgradeReaction(*aResult, definition);
+    return NS_OK;
+  }
+
+  bool isCustomElementName = false;
+
+  if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
+    // Per the Custom Element specification, unknown tags that are valid custom
+    // element names should be HTMLElement instead of HTMLUnknownElement.
+    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();
+    }
+  } else {
+    isCustomElementName = nsContentUtils::IsCustomElementName(name);
+    NS_IF_ADDREF(*aResult = new nsXULElement(nodeInfo.forget()));
+  }
+
+  if (!*aResult) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  if (CustomElementRegistry::IsCustomElementEnabled() &&
+      (isCustomElementName || aIs)) {
+    (*aResult)->SetCustomElementData(new CustomElementData(typeAtom));
+  }
+
+  return NS_OK;
+}
+
 /* static */ CustomElementDefinition*
 nsContentUtils::LookupCustomElementDefinition(nsIDocument* aDoc,
                                               const nsAString& aLocalName,
                                               uint32_t aNameSpaceID,
                                               nsAtom* aTypeAtom)
 {
   MOZ_ASSERT(aDoc);
 
-  if (aNameSpaceID != kNameSpaceID_XHTML ||
+  if ((aNameSpaceID != kNameSpaceID_XUL &&
+       aNameSpaceID != kNameSpaceID_XHTML) ||
       !aDoc->GetDocShell()) {
     return nullptr;
   }
 
   nsCOMPtr<nsPIDOMWindowInner> window(aDoc->GetInnerWindow());
   if (!window) {
     return nullptr;
   }
@@ -10283,17 +10465,18 @@ nsContentUtils::EnqueueLifecycleCallback
 /* static */ void
 nsContentUtils::GetCustomPrototype(nsIDocument* aDoc,
                                    int32_t aNamespaceID,
                                    nsAtom* aAtom,
                                    JS::MutableHandle<JSObject*> aPrototype)
 {
   MOZ_ASSERT(aDoc);
 
-  if (aNamespaceID != kNameSpaceID_XHTML ||
+  if ((aNamespaceID != kNameSpaceID_XHTML &&
+       aNamespaceID != kNameSpaceID_XUL) ||
       !aDoc->GetDocShell()) {
     return;
   }
 
   nsCOMPtr<nsPIDOMWindowInner> window(aDoc->GetInnerWindow());
   if (!window) {
     return;
   }
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -15,16 +15,17 @@
 
 #if defined(SOLARIS)
 #include <ieeefp.h>
 #endif
 
 #include "js/TypeDecls.h"
 #include "js/Value.h"
 #include "js/RootingAPI.h"
+#include "mozilla/dom/FromParser.h"
 #include "mozilla/BasicEvents.h"
 #include "mozilla/EventForwards.h"
 #include "mozilla/GuardObjects.h"
 #include "mozilla/TaskCategory.h"
 #include "mozilla/TimeStamp.h"
 #include "nsContentListDeclarations.h"
 #include "nsMathUtils.h"
 #include "nsTArrayForwardDeclare.h"
@@ -3031,16 +3032,24 @@ public:
 
   /**
    * Try to upgrade an element.
    * https://html.spec.whatwg.org/multipage/custom-elements.html#concept-try-upgrade
    */
   static void TryToUpgradeElement(Element* aElement);
 
   /**
+   * Creates a new XUL or XHTML element applying any appropriate custom element
+   * definition.
+   */
+  static nsresult NewXULOrHTMLElement(Element** aResult, mozilla::dom::NodeInfo* aNodeInfo,
+                                      mozilla::dom::FromParser aFromParser, const nsAString* aIs,
+                                      mozilla::dom::CustomElementDefinition* aDefinition);
+
+  /**
    * Looking up a custom element definition.
    * https://html.spec.whatwg.org/#look-up-a-custom-element-definition
    */
   static mozilla::dom::CustomElementDefinition*
     LookupCustomElementDefinition(nsIDocument* aDoc,
                                   const nsAString& aLocalName,
                                   uint32_t aNameSpaceID,
                                   nsAtom* aTypeAtom);
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -6382,17 +6382,17 @@ nsDocument::CustomElementConstructor(JSC
   }
 
   RefPtr<Element> element;
 
   // We integrate with construction stack and do prototype swizzling here, so
   // that old upgrade behavior could also share the new upgrade steps.
   // And this old upgrade will be remove at some point (when everything is
   // switched to latest custom element spec).
-  nsTArray<RefPtr<nsGenericHTMLElement>>& constructionStack =
+  nsTArray<RefPtr<Element>>& constructionStack =
     definition->mConstructionStack;
   if (constructionStack.Length()) {
     element = constructionStack.LastElement();
     NS_ENSURE_TRUE(element != ALEADY_CONSTRUCTED_MARKER, false);
 
     // Do prototype swizzling if dom reflector exists.
     JS::Rooted<JSObject*> reflector(aCx, element->GetWrapper());
     if (reflector) {
--- a/dom/base/nsNameSpaceManager.cpp
+++ b/dom/base/nsNameSpaceManager.cpp
@@ -178,17 +178,17 @@ NS_NewElement(Element** aResult,
 {
   RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
   int32_t ns = ni->NamespaceID();
   if (ns == kNameSpaceID_XHTML) {
     return NS_NewHTMLElement(aResult, ni.forget(), aFromParser, aIs);
   }
 #ifdef MOZ_XUL
   if (ns == kNameSpaceID_XUL) {
-    return NS_NewXULElement(aResult, ni.forget());
+    return NS_NewXULElement(aResult, ni.forget(), aFromParser, aIs);
   }
 #endif
   if (ns == kNameSpaceID_MathML) {
     // If the mathml.disabled pref. is true, convert all MathML nodes into
     // disabled MathML nodes by swapping the namespace.
     if (ni->NodeInfoManager()->MathMLEnabled()) {
       return NS_NewMathMLElement(aResult, ni.forget());
     }
--- a/dom/base/nsNodeUtils.cpp
+++ b/dom/base/nsNodeUtils.cpp
@@ -459,17 +459,17 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNod
   if (aClone) {
     nsresult rv = aNode->Clone(nodeInfo, getter_AddRefs(clone), aDeep);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       aError.Throw(rv);
       return nullptr;
     }
 
     if (CustomElementRegistry::IsCustomElementEnabled() &&
-        clone->IsHTMLElement()) {
+        clone->IsElement()) {
       // The cloned node may be a custom element that may require
       // enqueing upgrade reaction.
       Element* cloneElem = clone->AsElement();
       RefPtr<nsAtom> tagAtom = nodeInfo->NameAtom();
       CustomElementData* data = elem->GetCustomElementData();
 
       // Check if node may be custom element by type extension.
       // ex. <button is="x-button">
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -41,27 +41,29 @@
 #include "mozilla/dom/CustomElementRegistry.h"
 #include "mozilla/dom/DOMException.h"
 #include "mozilla/dom/ElementBinding.h"
 #include "mozilla/dom/HTMLObjectElement.h"
 #include "mozilla/dom/HTMLObjectElementBinding.h"
 #include "mozilla/dom/HTMLEmbedElement.h"
 #include "mozilla/dom/HTMLElementBinding.h"
 #include "mozilla/dom/HTMLEmbedElementBinding.h"
+#include "mozilla/dom/XULElementBinding.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ResolveSystemBinding.h"
 #include "mozilla/dom/WebIDLGlobalNameHash.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/dom/WorkerScope.h"
 #include "mozilla/dom/XrayExpandoClass.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
 #include "nsDOMClassInfo.h"
 #include "ipc/ErrorIPCUtils.h"
 #include "mozilla/UseCounter.h"
 #include "mozilla/dom/DocGroup.h"
+#include "nsXULElement.h"
 
 namespace mozilla {
 namespace dom {
 
 using namespace workers;
 
 // Forward declare GetConstructorObject methods.
 #define HTML_TAG(_tag, _classname, _interfacename)                             \
@@ -3543,33 +3545,38 @@ GetCustomElementReactionsStack(JS::Handl
   if (!docGroup) {
     return nullptr;
   }
 
   return docGroup->CustomElementReactionsStack();
 }
 
 // https://html.spec.whatwg.org/multipage/dom.html#htmlconstructor
-already_AddRefed<nsGenericHTMLElement>
-CreateHTMLElement(const GlobalObject& aGlobal, const JS::CallArgs& aCallArgs,
-                  JS::Handle<JSObject*> aGivenProto, ErrorResult& aRv)
+already_AddRefed<Element>
+CreateXULOrHTMLElement(const GlobalObject& aGlobal, const JS::CallArgs& aCallArgs,
+                       JS::Handle<JSObject*> aGivenProto, ErrorResult& aRv)
 {
   // Step 1.
   nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal.GetAsSupports());
   if (!window) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
   nsIDocument* doc = window->GetExtantDoc();
   if (!doc) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
+  int32_t ns = doc->GetDefaultNamespaceID();
+  if (ns != kNameSpaceID_XUL) {
+    ns = kNameSpaceID_XHTML;
+  }
+
   RefPtr<mozilla::dom::CustomElementRegistry> registry(window->CustomElements());
   if (!registry) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
   // Step 2 is in the code output by CGClassConstructor.
   // Step 3.
@@ -3592,31 +3599,44 @@ CreateHTMLElement(const GlobalObject& aG
   // And the actual callee might be in different compartment, so enter its
   // compartment before getting the standard constructor object to compare to,
   // so we get it from the same global as callee itself.
   JSAutoCompartment ac(cx, callee);
   int32_t tag = eHTMLTag_userdefined;
   if (!definition->IsCustomBuiltIn()) {
     // Step 4.
     // If the definition is for an autonomous custom element, the active
-    // function should be HTMLElement.
-    JS::Rooted<JSObject*> constructor(cx, HTMLElementBinding::GetConstructorObject(cx));
+    // function should be HTMLElement or XULElement
+    JS::Rooted<JSObject*> constructor(cx);
+    if (ns == kNameSpaceID_XUL) {
+      constructor = XULElementBinding::GetConstructorObject(cx);
+    } else {
+      constructor = HTMLElementBinding::GetConstructorObject(cx);
+    }
+
     if (!constructor) {
       aRv.NoteJSContextException(cx);
       return nullptr;
     }
 
     if (callee != constructor) {
       aRv.ThrowTypeError<MSG_ILLEGAL_CONSTRUCTOR>();
       return nullptr;
     }
   } else {
     // Step 5.
     // If the definition is for a customized built-in element, the localName
     // should be defined in the specification.
+
+    // Customized built-in elements are not supported for XUL yet.
+    if (ns == kNameSpaceID_XUL) {
+      aRv.Throw(NS_ERROR_UNEXPECTED);
+      return nullptr;
+    }
+
     tag = nsHTMLTags::CaseSensitiveAtomTagToId(definition->mLocalName);
     if (tag == eHTMLTag_userdefined) {
       aRv.ThrowTypeError<MSG_ILLEGAL_CONSTRUCTOR>();
       return nullptr;
     }
 
     MOZ_ASSERT(tag <= NS_HTML_TAG_MAX, "tag is out of bounds");
 
@@ -3638,47 +3658,51 @@ CreateHTMLElement(const GlobalObject& aG
       aRv.ThrowTypeError<MSG_ILLEGAL_CONSTRUCTOR>();
       return nullptr;
     }
   }
 
   RefPtr<mozilla::dom::NodeInfo> nodeInfo =
     doc->NodeInfoManager()->GetNodeInfo(definition->mLocalName,
                                         nullptr,
-                                        kNameSpaceID_XHTML,
+                                        ns,
                                         nsIDOMNode::ELEMENT_NODE);
   if (!nodeInfo) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
   // Step 6 and Step 7 are in the code output by CGClassConstructor.
   // Step 8.
-  nsTArray<RefPtr<nsGenericHTMLElement>>& constructionStack =
+  nsTArray<RefPtr<Element>>& constructionStack =
     definition->mConstructionStack;
   if (constructionStack.IsEmpty()) {
-    RefPtr<nsGenericHTMLElement> newElement;
-    if (tag == eHTMLTag_userdefined) {
-      // Autonomous custom element.
-      newElement = NS_NewHTMLElement(nodeInfo.forget());
+    RefPtr<Element> newElement;
+    if (ns == kNameSpaceID_XUL) {
+      newElement = new nsXULElement(nodeInfo.forget());
     } else {
-      // Customized built-in element.
-      newElement = CreateHTMLElement(tag, nodeInfo.forget(), NOT_FROM_PARSER);
+      if (tag == eHTMLTag_userdefined) {
+        // Autonomous custom element.
+        newElement = NS_NewHTMLElement(nodeInfo.forget());
+      } else {
+        // Customized built-in element.
+        newElement = CreateHTMLElement(tag, nodeInfo.forget(), NOT_FROM_PARSER);
+      }
     }
 
     newElement->SetCustomElementData(
       new CustomElementData(definition->mType, CustomElementData::State::eCustom));
 
     newElement->SetCustomElementDefinition(definition);
 
     return newElement.forget();
   }
 
   // Step 9.
-  RefPtr<nsGenericHTMLElement>& element = constructionStack.LastElement();
+  RefPtr<Element>& element = constructionStack.LastElement();
 
   // Step 10.
   if (element == ALEADY_CONSTRUCTED_MARKER) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return nullptr;
   }
 
   // Step 11.
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -3386,21 +3386,21 @@ GetDesiredProto(JSContext* aCx, const JS
 
 // Get the CustomElementReactionsStack for the docgroup of the global
 // of the underlying object of aObj.  This can be null if aObj can't
 // be CheckUnwrapped, or if the global of the result has no docgroup
 // (e.g. because it's not a Window global).
 CustomElementReactionsStack*
 GetCustomElementReactionsStack(JS::Handle<JSObject*> aObj);
 // This function is expected to be called from the constructor function for an
-// HTML element interface; the global/callargs need to be whatever was passed to
-// that constructor function.
-already_AddRefed<nsGenericHTMLElement>
-CreateHTMLElement(const GlobalObject& aGlobal, const JS::CallArgs& aCallArgs,
-                  JS::Handle<JSObject*> aGivenProto, ErrorResult& aRv);
+// HTML or XUL element interface; the global/callargs need to be whatever was
+// passed to that constructor function.
+already_AddRefed<Element>
+CreateXULOrHTMLElement(const GlobalObject& aGlobal, const JS::CallArgs& aCallArgs,
+                       JS::Handle<JSObject*> aGivenProto, ErrorResult& aRv);
 
 void
 SetDocumentAndPageUseCounter(JSObject* aObject, UseCounter aUseCounter);
 
 // Warnings
 void
 DeprecationWarning(JSContext* aCx, JSObject* aObject,
                    nsIDocument::DeprecatedOperations aOperation);
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -1902,21 +1902,21 @@ class CGClassConstructor(CGAbstractStati
             ctorName=ctorName,
             htmlConstructorSanityCheck=htmlConstructorSanityCheck,
             htmlConstructorFallback=htmlConstructorFallback)
 
         if  self._ctor.isHTMLConstructor():
             signatures = self._ctor.signatures()
             assert len(signatures) == 1
             # Given that HTMLConstructor takes no args, we can just codegen a
-            # call to CreateHTMLElement() in BindingUtils which reuses the
+            # call to CreateXULOrHTMLElement() in BindingUtils which reuses the
             # factory thing in HTMLContentSink. Then we don't have to implement
             # Constructor on all the HTML elements.
             callGenerator = CGPerSignatureCall(signatures[0][0], signatures[0][1],
-                                               "CreateHTMLElement", True,
+                                               "CreateXULOrHTMLElement", True,
                                                self.descriptor, self._ctor,
                                                isConstructor=True)
         else:
             name = self._ctor.identifier.name
             nativeName = MakeNativeName(self.descriptor.binaryNameFor(name))
             callGenerator = CGMethodCall(nativeName, True, self.descriptor,
                                          self._ctor, isConstructor=True,
                                          constructorName=ctorName)
--- a/dom/html/nsHTMLContentSink.cpp
+++ b/dom/html/nsHTMLContentSink.cpp
@@ -219,165 +219,27 @@ public:
     nsIContent *Add(nsIContent *child);
   };
 
   Node* mStack;
   int32_t mStackSize;
   int32_t mStackPos;
 };
 
-static void
-DoCustomElementCreate(Element** aElement, nsIDocument* aDoc, nsAtom* aLocalName,
-                      CustomElementConstructor* aConstructor, ErrorResult& aRv)
-{
-  RefPtr<Element> element =
-    aConstructor->Construct("Custom Element Create", aRv);
-  if (aRv.Failed()) {
-    return;
-  }
-
-  if (!element || !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() ||
-      element->NodeInfo()->NameAtom() != aLocalName) {
-    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,
                   mozilla::dom::CustomElementDefinition* aDefinition)
 {
-  *aResult = nullptr;
-
   RefPtr<mozilla::dom::NodeInfo> nodeInfo = aNodeInfo;
 
-  nsAtom *name = nodeInfo->NameAtom();
-  RefPtr<nsAtom> tagAtom = nodeInfo->NameAtom();
-  RefPtr<nsAtom> typeAtom = aIs ? NS_Atomize(*aIs) : tagAtom;
-
   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 = aDefinition;
-  if (!definition && CustomElementRegistry::IsCustomElementEnabled()) {
-    definition =
-      nsContentUtils::LookupCustomElementDefinition(nodeInfo->GetDocument(),
-                                                    nodeInfo->LocalName(),
-                                                    nodeInfo->NamespaceID(),
-                                                    typeAtom);
-  }
-
-  // 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 and use the
-    // node document's global object if it is called from parser.
-    nsIGlobalObject* global;
-    if (aFromParser == dom::NOT_FROM_PARSER) {
-      global = GetEntryGlobal();
-    } else {
-      global = nodeInfo->GetDocument()->GetScopeObject();
-    }
-    if (!global) {
-      // In browser chrome code, one may have access to a document which doesn't
-      // have scope object anymore.
-      return NS_ERROR_FAILURE;
-    }
-
-    AutoEntryScript aes(global, "create custom elements");
-    JSContext* cx = aes.cx();
-    ErrorResult rv;
+               "Trying to create HTML elements that don't have the XHTML namespace");
 
-    // 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().
-      // Built-in element
-      *aResult = CreateHTMLElement(tag, nodeInfo.forget(), aFromParser).take();
-      (*aResult)->SetCustomElementData(new CustomElementData(typeAtom));
-      if (synchronousCustomElements) {
-        CustomElementRegistry::Upgrade(*aResult, definition, rv);
-        if (rv.MaybeSetPendingException(cx)) {
-          aes.ReportException();
-        }
-      } else {
-        nsContentUtils::EnqueueUpgradeReaction(*aResult, definition);
-      }
-
-      return NS_OK;
-    }
-
-    // Step 6.1.
-    if (synchronousCustomElements) {
-      DoCustomElementCreate(aResult, nodeInfo->GetDocument(),
-                            nodeInfo->NameAtom(),
-                            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));
-    (*aResult)->SetCustomElementData(new CustomElementData(definition->mType));
-    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();
-  }
-
-  if (!*aResult) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  if (CustomElementRegistry::IsCustomElementEnabled() &&
-      (isCustomElementName || aIs)) {
-    (*aResult)->SetCustomElementData(new CustomElementData(typeAtom));
-  }
-
-  return NS_OK;
+  return nsContentUtils::NewXULOrHTMLElement(aResult, nodeInfo, aFromParser, aIs, aDefinition);
 }
 
 already_AddRefed<nsGenericHTMLElement>
 CreateHTMLElement(uint32_t aNodeType,
                   already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
                   FromParser aFromParser)
 {
   NS_ASSERTION(aNodeType <= NS_HTML_TAG_MAX ||
--- a/dom/tests/mochitest/webcomponents/chrome.ini
+++ b/dom/tests/mochitest/webcomponents/chrome.ini
@@ -5,9 +5,10 @@ support-files =
 [test_custom_element_htmlconstructor_chrome.html]
 skip-if = os == 'android' # bug 1323645
 support-files =
   htmlconstructor_autonomous_tests.js
   htmlconstructor_builtin_tests.js
 [test_custom_element_upgrade_chrome.html]
 support-files =
   test_upgrade_page.html
-  upgrade_tests.js
\ No newline at end of file
+  upgrade_tests.js
+[test_xul_custom_element.xul]
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/webcomponents/test_xul_custom_element.xul
@@ -0,0 +1,68 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="XUL Custom Elements"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        onload="runTest();">
+  <title>XUL Custom Elements</title>
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+  <script type="application/javascript">
+  <![CDATA[
+    SimpleTest.waitForExplicitFinish();
+
+    class TestCustomElement extends XULElement {
+      constructor() {
+        super();
+      }
+
+      connectedCallback() {
+        this.textContent = "foo";
+      }
+    }
+
+    customElements.define("test-custom-element", TestCustomElement);
+
+    function runTest() {
+      const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+      let element = document.createElementNS(XUL_NS, "test-custom-element");
+      document.querySelector("#content").appendChild(element);
+      is(element.textContent, "foo", "Should have set the textContent");
+      ok(element instanceof TestCustomElement, "Should be an instance of TestCustomElement");
+
+      let element2 = element.cloneNode(false);
+      is(element2.textContent, "", "Shouldn't have cloned the textContent");
+      document.querySelector("#content").appendChild(element2);
+      is(element2.textContent, "foo", "Should have set the textContent");
+      ok(element2 instanceof TestCustomElement, "Should be an instance of TestCustomElement");
+
+      let element3 = new TestCustomElement();
+      is(element3.localName, "test-custom-element", "Should see the right tag");
+      is(element3.textContent, "", "Shouldn't have been inserted yet");
+      is(element3.namespaceURI, XUL_NS, "Should have set the right namespace");
+      document.querySelector("#content").appendChild(element3);
+      is(element3.textContent, "foo", "Should have set the textContent");
+      ok(element3 instanceof TestCustomElement, "Should be an instance of TestCustomElement");
+
+      let element4 = document.getElementById("element4");
+      is(element4.textContent, "foo",
+         "Parser should have instantiated the custom element.");
+      ok(element4 instanceof TestCustomElement, "Should be an instance of TestCustomElement");
+
+      SimpleTest.finish();
+    }
+  ]]>
+  </script>
+
+  <body xmlns="http://www.w3.org/1999/xhtml">
+    <p id="display"></p>
+    <div id="content" style="display: none">
+      <test-custom-element id="element4" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"/>
+    </div>
+    <pre id="test"></pre>
+  </body>
+</window>
--- a/dom/webidl/XULElement.webidl
+++ b/dom/webidl/XULElement.webidl
@@ -3,17 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
 interface XULControllers;
 interface MozRDFCompositeDataSource;
 interface MozRDFResource;
 
-[Func="IsChromeOrXBL"]
+[HTMLConstructor, Func="IsChromeOrXBL"]
 interface XULElement : Element {
   // Layout properties
   [SetterThrows]
   attribute DOMString align;
   [SetterThrows]
   attribute DOMString dir;
   [SetterThrows]
   attribute DOMString flex;
--- a/dom/xbl/nsXBLService.cpp
+++ b/dom/xbl/nsXBLService.cpp
@@ -1025,16 +1025,21 @@ nsXBLService::FetchBindingDocument(nsICo
   if (IsChromeOrResourceURI(aDocumentURI))
     aForceSyncLoad = true;
 
   // Create document and contentsink and set them up.
   nsCOMPtr<nsIDocument> doc;
   rv = NS_NewXMLDocument(getter_AddRefs(doc));
   NS_ENSURE_SUCCESS(rv, rv);
 
+  // XBL documents must allow XUL and XBL elements in them but the usual check
+  // only checks if the document is loaded in the system principal which is
+  // sometimes not the case.
+  doc->ForceEnableXULXBL();
+
   // Set the style backend type before loading the XBL document. Assume
   // gecko if there's no bound document.
   doc->SetStyleBackendType(aBoundDocument ? aBoundDocument->GetStyleBackendType()
                                           : StyleBackendType::Gecko);
 
   nsCOMPtr<nsIXMLContentSink> xblSink;
   rv = NS_NewXBLContentSink(getter_AddRefs(xblSink), doc, aDocumentURI, nullptr);
   NS_ENSURE_SUCCESS(rv, rv);
--- a/dom/xml/XMLDocument.cpp
+++ b/dom/xml/XMLDocument.cpp
@@ -231,16 +231,22 @@ NS_NewXBLDocument(nsIDOMDocument** aInst
                                   NS_LITERAL_STRING("http://www.mozilla.org/xbl"),
                                   NS_LITERAL_STRING("bindings"), nullptr,
                                   aDocumentURI, aBaseURI, aPrincipal, false,
                                   nullptr, DocumentFlavorLegacyGuess,
                                   aStyleBackend);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIDocument> idoc = do_QueryInterface(*aInstancePtrResult);
+
+  // XBL documents must allow XUL and XBL elements in them but the usual check
+  // only checks if the document is loaded in the system principal which is
+  // sometimes not the case.
+  idoc->ForceEnableXULXBL();
+
   nsDocument* doc = static_cast<nsDocument*>(idoc.get());
   doc->SetLoadedAsInteractiveData(true);
   doc->SetReadyStateInternal(nsIDocument::READYSTATE_COMPLETE);
 
   return NS_OK;
 }
 
 namespace mozilla {
--- a/dom/xul/moz.build
+++ b/dom/xul/moz.build
@@ -15,16 +15,17 @@ if CONFIG['MOZ_XUL']:
     DIRS += ['templates']
 
     XPIDL_SOURCES += [
         'nsIXULOverlayProvider.idl',
     ]
 
     EXPORTS += [
         'nsIXULDocument.h',
+        'nsXULElement.h',
     ]
 
     UNIFIED_SOURCES += [
         'nsXULCommandDispatcher.cpp',
         'nsXULContentSink.cpp',
         'nsXULElement.cpp',
         'nsXULPopupListener.cpp',
         'nsXULPrototypeCache.cpp',
--- a/dom/xul/nsXULElement.cpp
+++ b/dom/xul/nsXULElement.cpp
@@ -181,18 +181,22 @@ nsXULElement::MaybeUpdatePrivateLifetime
 }
 
 /* static */
 already_AddRefed<nsXULElement>
 nsXULElement::Create(nsXULPrototypeElement* aPrototype, mozilla::dom::NodeInfo *aNodeInfo,
                      bool aIsScriptable, bool aIsRoot)
 {
     RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
-    RefPtr<nsXULElement> element = new nsXULElement(ni.forget());
-    if (element) {
+    nsCOMPtr<Element> baseElement;
+    NS_NewXULElement(getter_AddRefs(baseElement), ni.forget(), dom::FROM_PARSER_NETWORK, nullptr);
+
+    if (baseElement) {
+        nsXULElement* element = FromContent(baseElement);
+
         if (aPrototype->mHasIdAttribute) {
             element->SetHasID();
         }
         if (aPrototype->mHasClassAttribute) {
             element->SetMayHaveClass();
         }
         if (aPrototype->mHasStyleAttribute) {
             element->SetMayHaveStyle();
@@ -211,19 +215,21 @@ nsXULElement::Create(nsXULPrototypeEleme
 
         if (aIsRoot && aPrototype->mNodeInfo->Equals(nsGkAtoms::window)) {
             for (uint32_t i = 0; i < aPrototype->mNumAttributes; ++i) {
                 if (aPrototype->mAttributes[i].mName.Equals(nsGkAtoms::windowtype)) {
                     element->MaybeUpdatePrivateLifetime();
                 }
             }
         }
+
+        return baseElement.forget().downcast<nsXULElement>();
     }
 
-    return element.forget();
+    return nullptr;
 }
 
 nsresult
 nsXULElement::Create(nsXULPrototypeElement* aPrototype,
                      nsIDocument* aDocument,
                      bool aIsScriptable,
                      bool aIsRoot,
                      Element** aResult)
@@ -250,30 +256,32 @@ nsXULElement::Create(nsXULPrototypeEleme
     RefPtr<nsXULElement> element = Create(aPrototype, nodeInfo,
                                             aIsScriptable, aIsRoot);
     element.forget(aResult);
 
     return NS_OK;
 }
 
 nsresult
-NS_NewXULElement(Element** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
+NS_NewXULElement(Element** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
+                 FromParser aFromParser, const nsAString* aIs)
 {
-    RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
-
-    NS_PRECONDITION(ni, "need nodeinfo for non-proto Create");
-
-    nsIDocument* doc = ni->GetDocument();
+    RefPtr<mozilla::dom::NodeInfo> nodeInfo = aNodeInfo;
+
+    NS_PRECONDITION(nodeInfo, "need nodeinfo for non-proto Create");
+
+    NS_ASSERTION(nodeInfo->NamespaceEquals(kNameSpaceID_XUL),
+                 "Trying to create XUL elements that don't have the XUL namespace");
+
+    nsIDocument* doc = nodeInfo->GetDocument();
     if (doc && !doc->AllowXULXBL()) {
         return NS_ERROR_NOT_AVAILABLE;
     }
 
-    NS_ADDREF(*aResult = new nsXULElement(ni.forget()));
-
-    return NS_OK;
+    return nsContentUtils::NewXULOrHTMLElement(aResult, nodeInfo, aFromParser, aIs, nullptr);
 }
 
 void
 NS_TrustedNewXULElement(nsIContent** aResult,
                         already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
 {
     RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
     NS_PRECONDITION(ni, "need nodeinfo for non-proto Create");
--- a/dom/xul/nsXULElement.h
+++ b/dom/xul/nsXULElement.h
@@ -35,16 +35,17 @@
 #include "nsFrameLoader.h" // Needed because we return an
                            // already_AddRefed<nsFrameLoader> where bindings
                            // want an already_AddRefed<nsIFrameLoader> and hence
                            // bindings need to know that the former can cast to
                            // the latter.
 #include "mozilla/dom/DOMRect.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/DOMString.h"
+#include "mozilla/dom/FromParser.h"
 
 class nsIDocument;
 class nsXULPrototypeDocument;
 
 class nsIObjectInputStream;
 class nsIObjectOutputStream;
 class nsIOffThreadScriptReceiver;
 class nsXULPrototypeNode;
@@ -794,17 +795,18 @@ protected:
       nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
       return slots ? slots->mControllers.get() : nullptr;
     }
 
     void UnregisterAccessKey(const nsAString& aOldValue);
     bool BoolAttrIsTrue(nsAtom* aName) const;
 
     friend nsresult
-    NS_NewXULElement(mozilla::dom::Element** aResult, mozilla::dom::NodeInfo *aNodeInfo);
+    NS_NewXULElement(mozilla::dom::Element** aResult, mozilla::dom::NodeInfo *aNodeInfo,
+                     mozilla::dom::FromParser aFromParser, const nsAString* aIs);
     friend void
     NS_TrustedNewXULElement(nsIContent** aResult, mozilla::dom::NodeInfo *aNodeInfo);
 
     static already_AddRefed<nsXULElement>
     Create(nsXULPrototypeElement* aPrototype, mozilla::dom::NodeInfo *aNodeInfo,
            bool aIsScriptable, bool aIsRoot);
 
     bool IsReadWriteTextElement() const
--- a/layout/xul/nsDocElementBoxFrame.cpp
+++ b/layout/xul/nsDocElementBoxFrame.cpp
@@ -16,16 +16,17 @@
 #include "nsStackLayout.h"
 #include "nsIAnonymousContentCreator.h"
 #include "mozilla/dom/NodeInfo.h"
 #include "nsIServiceManager.h"
 #include "nsNodeInfoManager.h"
 #include "nsContentCreatorFunctions.h"
 #include "nsContentUtils.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/FromParser.h"
 
 //#define DEBUG_REFLOW
 
 using namespace mozilla::dom;
 
 class nsDocElementBoxFrame final : public nsBoxFrame
                                  , public nsIAnonymousContentCreator
 {
@@ -93,29 +94,31 @@ nsDocElementBoxFrame::CreateAnonymousCon
   // create the top-secret popupgroup node. shhhhh!
   RefPtr<NodeInfo> nodeInfo;
   nodeInfo = nodeInfoManager->GetNodeInfo(nsGkAtoms::popupgroup,
                                           nullptr, kNameSpaceID_XUL,
                                           nsIDOMNode::ELEMENT_NODE);
   NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
 
   nsresult rv = NS_NewXULElement(getter_AddRefs(mPopupgroupContent),
-                                 nodeInfo.forget());
+                                 nodeInfo.forget(), dom::NOT_FROM_PARSER,
+                                 nullptr);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (!aElements.AppendElement(mPopupgroupContent))
     return NS_ERROR_OUT_OF_MEMORY;
 
   // create the top-secret default tooltip node. shhhhh!
   nodeInfo = nodeInfoManager->GetNodeInfo(nsGkAtoms::tooltip, nullptr,
                                           kNameSpaceID_XUL,
                                           nsIDOMNode::ELEMENT_NODE);
   NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
 
-  rv = NS_NewXULElement(getter_AddRefs(mTooltipContent), nodeInfo.forget());
+  rv = NS_NewXULElement(getter_AddRefs(mTooltipContent), nodeInfo.forget(),
+                        dom::NOT_FROM_PARSER, nullptr);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mTooltipContent->SetAttr(kNameSpaceID_None, nsGkAtoms::_default,
                            NS_LITERAL_STRING("true"), false);
 
   if (!aElements.AppendElement(mTooltipContent))
     return NS_ERROR_OUT_OF_MEMORY;