Bug 1480465: Infer the namespace for custom elements at definition time by following the class hierarchy. r=smaug
authorDave Townsend <dtownsend@oxymoronical.com>
Wed, 15 Aug 2018 10:31:16 +0000
changeset 829432 413c0f58374c3fd8af375ea1b680cb86c4a1492b
parent 829431 d08617053829ae9ea37b55c410768a1b07cde51d
child 829433 b6f1266dd6c5bf04a6dc68a5e7a19dd5f5230f51
push id118781
push userktomlinson@mozilla.com
push dateThu, 16 Aug 2018 02:46:06 +0000
reviewerssmaug
bugs1480465
milestone63.0a1
Bug 1480465: Infer the namespace for custom elements at definition time by following the class hierarchy. r=smaug When a custom element is defined we can check whether its class is an instance of XULElement or HTMLElement and tag the defintion with a namespace accordingly. This allows us to know the correct namespace for the custom element when created. Differential Revision: https://phabricator.services.mozilla.com/D2680
dom/base/CustomElementRegistry.cpp
dom/base/CustomElementRegistry.h
dom/base/nsContentUtils.cpp
dom/bindings/BindingUtils.cpp
dom/tests/mochitest/webcomponents/chrome_disabled.ini
dom/tests/mochitest/webcomponents/test_custom_element_namespace.html
dom/tests/mochitest/webcomponents/test_custom_element_namespace.xhtml
dom/tests/mochitest/webcomponents/test_custom_element_namespace.xul
parser/html/nsHtml5TreeOperation.cpp
--- a/dom/base/CustomElementRegistry.cpp
+++ b/dom/base/CustomElementRegistry.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/CustomElementRegistry.h"
 
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/CycleCollectedJSContext.h"
 #include "mozilla/dom/CustomElementRegistryBinding.h"
 #include "mozilla/dom/HTMLElementBinding.h"
+#include "mozilla/dom/XULElementBinding.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/WebComponentsBinding.h"
 #include "mozilla/dom/DocGroup.h"
 #include "mozilla/dom/CustomEvent.h"
 #include "mozilla/dom/ShadowRoot.h"
 #include "nsHTMLTags.h"
 #include "jsapi.h"
 #include "xpcprivate.h"
@@ -351,16 +352,17 @@ CustomElementRegistry::RunCustomElementC
       "chrome JavaScript error in custom element construction.");
   }
 
   return NS_OK;
 }
 
 CustomElementDefinition*
 CustomElementRegistry::LookupCustomElementDefinition(nsAtom* aNameAtom,
+                                                     int32_t aNameSpaceID,
                                                      nsAtom* aTypeAtom)
 {
   CustomElementDefinition* data = mCustomDefinitions.GetWeak(aTypeAtom);
 
   if (!data) {
     RefPtr<CustomElementCreationCallback> callback;
     mElementCreationCallbacks.Get(aTypeAtom, getter_AddRefs(callback));
     if (callback) {
@@ -368,17 +370,17 @@ CustomElementRegistry::LookupCustomEleme
       mElementCreationCallbacksUpgradeCandidatesMap.LookupOrAdd(aTypeAtom);
       RefPtr<Runnable> runnable =
         new RunCustomElementCreationCallback(this, aTypeAtom, callback);
       nsContentUtils::AddScriptRunner(runnable);
       data = mCustomDefinitions.GetWeak(aTypeAtom);
     }
   }
 
-  if (data && data->mLocalName == aNameAtom) {
+  if (data && data->mLocalName == aNameAtom && data->mNamespaceID == aNameSpaceID) {
     return data;
   }
 
   return nullptr;
 }
 
 CustomElementDefinition*
 CustomElementRegistry::LookupCustomElementDefinition(JSContext* aCx,
@@ -683,16 +685,34 @@ nsISupports* CustomElementRegistry::GetP
 }
 
 DocGroup*
 CustomElementRegistry::GetDocGroup() const
 {
   return mWindow ? mWindow->GetDocGroup() : nullptr;
 }
 
+int32_t
+CustomElementRegistry::InferNamespace(JSContext* aCx,
+                                      JS::Handle<JSObject*> constructor)
+{
+  JSObject* XULConstructor = XULElement_Binding::GetConstructorObject(aCx);
+
+  JS::Rooted<JSObject*> proto(aCx, constructor);
+  while (proto) {
+    if (proto == XULConstructor) {
+      return kNameSpaceID_XUL;
+    }
+
+    JS_GetPrototype(aCx, proto, &proto);
+  }
+
+  return kNameSpaceID_XHTML;
+}
+
 // https://html.spec.whatwg.org/multipage/scripting.html#element-definition
 void
 CustomElementRegistry::Define(JSContext* aCx,
                               const nsAString& aName,
                               Function& aFunctionConstructor,
                               const ElementDefinitionOptions& aOptions,
                               ErrorResult& aRv)
 {
@@ -715,22 +735,23 @@ CustomElementRegistry::Define(JSContext*
     return;
   }
 
   if (!JS::IsConstructor(constructorUnwrapped)) {
     aRv.ThrowTypeError<MSG_NOT_CONSTRUCTOR>(NS_LITERAL_STRING("Argument 2 of CustomElementRegistry.define"));
     return;
   }
 
+  int32_t nameSpaceID = InferNamespace(aCx, constructor);
+
   /**
    * 2. If name is not a valid custom element name, then throw a "SyntaxError"
    *    DOMException and abort these steps.
    */
   nsIDocument* doc = mWindow->GetExtantDoc();
-  uint32_t nameSpaceID = doc ? doc->GetDefaultNamespaceID() : kNameSpaceID_XHTML;
   RefPtr<nsAtom> nameAtom(NS_Atomize(aName));
   if (!nsContentUtils::IsCustomElementName(nameAtom, nameSpaceID)) {
     aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
     return;
   }
 
   /**
    * 3. If this CustomElementRegistry contains an entry with name name, then
@@ -940,16 +961,17 @@ CustomElementRegistry::Define(JSContext*
   if (!mConstructors.put(constructorUnwrapped, nameAtom)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
   RefPtr<CustomElementDefinition> definition =
     new CustomElementDefinition(nameAtom,
                                 localNameAtom,
+                                nameSpaceID,
                                 &aFunctionConstructor,
                                 std::move(observedAttributes),
                                 std::move(callbacksHolder));
 
   CustomElementDefinition* def = definition.get();
   mCustomDefinitions.Put(nameAtom, definition.forget());
 
   MOZ_ASSERT(mCustomDefinitions.Count() == mConstructors.count(),
@@ -1466,21 +1488,23 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Cus
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(CustomElementDefinition, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(CustomElementDefinition, Release)
 
 
 CustomElementDefinition::CustomElementDefinition(nsAtom* aType,
                                                  nsAtom* aLocalName,
+                                                 int32_t aNamespaceID,
                                                  Function* aConstructor,
                                                  nsTArray<RefPtr<nsAtom>>&& aObservedAttributes,
                                                  UniquePtr<LifecycleCallbacks>&& aCallbacks)
   : mType(aType),
     mLocalName(aLocalName),
+    mNamespaceID(aNamespaceID),
     mConstructor(new CustomElementConstructor(aConstructor)),
     mObservedAttributes(std::move(aObservedAttributes)),
     mCallbacks(std::move(aCallbacks))
 {
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/base/CustomElementRegistry.h
+++ b/dom/base/CustomElementRegistry.h
@@ -152,27 +152,31 @@ private:
 // https://html.spec.whatwg.org/multipage/scripting.html#custom-element-definition
 struct CustomElementDefinition
 {
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(CustomElementDefinition)
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(CustomElementDefinition)
 
   CustomElementDefinition(nsAtom* aType,
                           nsAtom* aLocalName,
+                          int32_t aNamespaceID,
                           Function* aConstructor,
                           nsTArray<RefPtr<nsAtom>>&& aObservedAttributes,
                           UniquePtr<LifecycleCallbacks>&& aCallbacks);
 
   // The type (name) for this custom element, for <button is="x-foo"> or <x-foo>
   // this would be x-foo.
   RefPtr<nsAtom> mType;
 
   // The localname to (e.g. <button is=type> -- this would be button).
   RefPtr<nsAtom> mLocalName;
 
+  // The namespace for this custom element
+  int32_t mNamespaceID;
+
   // The custom element constructor.
   RefPtr<CustomElementConstructor> mConstructor;
 
   // The list of attributes that this custom element observes.
   nsTArray<RefPtr<nsAtom>> mObservedAttributes;
 
   // The lifecycle callbacks to call for this custom element.
   UniquePtr<LifecycleCallbacks> mCallbacks;
@@ -394,17 +398,17 @@ private:
   };
 
 public:
   /**
    * Looking up a custom element definition.
    * https://html.spec.whatwg.org/#look-up-a-custom-element-definition
    */
   CustomElementDefinition* LookupCustomElementDefinition(
-    nsAtom* aNameAtom, nsAtom* aTypeAtom);
+    nsAtom* aNameAtom, int32_t aNameSpaceID, nsAtom* aTypeAtom);
 
   CustomElementDefinition* LookupCustomElementDefinition(
     JSContext* aCx, JSObject *aConstructor) const;
 
   static void EnqueueLifecycleCallback(nsIDocument::ElementCallbackType aType,
                                        Element* aCustomElement,
                                        LifecycleCallbackArgs* aArgs,
                                        LifecycleAdoptedCallbackArgs* aAdoptedCallbackArgs,
@@ -550,16 +554,18 @@ private:
       ~AutoSetRunningFlag() {
         mRegistry->mIsCustomDefinitionRunning = false;
       }
 
     private:
       CustomElementRegistry* mRegistry;
   };
 
+  int32_t InferNamespace(JSContext* aCx, JS::Handle<JSObject*> constructor);
+
 public:
   nsISupports* GetParentObject() const;
 
   DocGroup* GetDocGroup() const;
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   void Define(JSContext* aCx, const nsAString& aName,
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -9997,16 +9997,25 @@ nsContentUtils::NewXULOrHTMLElement(Elem
     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();
+
+      // XUL documents always use NOT_FROM_PARSER for non-XUL elements. We can
+      // get the global from the document in that case.
+      if (!global) {
+        nsIDocument* doc = nodeInfo->GetDocument();
+        if (doc && doc->IsXULDocument()) {
+          global = doc->GetScopeObject();
+        }
+      }
     } 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;
     }
@@ -10117,17 +10126,17 @@ nsContentUtils::LookupCustomElementDefin
     return nullptr;
   }
 
   RefPtr<CustomElementRegistry> registry(GetCustomElementRegistry(aDoc));
   if (!registry) {
     return nullptr;
   }
 
-  return registry->LookupCustomElementDefinition(aNameAtom, aTypeAtom);
+  return registry->LookupCustomElementDefinition(aNameAtom, aNameSpaceID, aTypeAtom);
 }
 
 /* static */ void
 nsContentUtils::RegisterCallbackUpgradeElement(Element* aElement,
                                                nsAtom* aTypeName)
 {
   MOZ_ASSERT(aElement);
 
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -3806,20 +3806,17 @@ HTMLConstructor(JSContext* aCx, unsigned
   if (!definition) {
     return ThrowErrorMessage(aCx, MSG_ILLEGAL_CONSTRUCTOR);
   }
 
   // Steps 4 and 5 do some sanity checks on our callee.  We add to those a
   // determination of what sort of element we're planning to construct.
   // Technically, this should happen (implicitly) in step 8, but this
   // determination is side-effect-free, so it's OK.
-  int32_t ns = doc->GetDefaultNamespaceID();
-  if (ns != kNameSpaceID_XUL) {
-    ns = kNameSpaceID_XHTML;
-  }
+  int32_t ns = definition->mNamespaceID;
 
   constructorGetterCallback cb = nullptr;
   if (ns == kNameSpaceID_XUL) {
     if (definition->mLocalName == nsGkAtoms::menupopup ||
         definition->mLocalName == nsGkAtoms::popup ||
         definition->mLocalName == nsGkAtoms::panel ||
         definition->mLocalName == nsGkAtoms::tooltip) {
       cb = XULPopupElement_Binding::GetConstructorObject;
--- a/dom/tests/mochitest/webcomponents/chrome_disabled.ini
+++ b/dom/tests/mochitest/webcomponents/chrome_disabled.ini
@@ -1,5 +1,8 @@
 [DEFAULT]
 prefs =
   dom.webcomponents.customelements.enabled=false
 
 [test_xul_custom_element.xul]
+[test_custom_element_namespace.html]
+[test_custom_element_namespace.xhtml]
+[test_custom_element_namespace.xul]
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/webcomponents/test_custom_element_namespace.html
@@ -0,0 +1,95 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Custom Elements in an HTML document</title>
+  <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+  <script>
+    SimpleTest.waitForExplicitFinish();
+
+    const HTML_NS = "http://www.w3.org/1999/xhtml";
+    const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+    class TestXULCustomElement extends XULElement {
+      constructor() {
+        super();
+      }
+
+      get connected() {
+        return true;
+      }
+    }
+
+    customElements.define("test-xul-element", TestXULCustomElement);
+
+    class TestHTMLCustomElement extends HTMLElement {
+      constructor() {
+        super();
+      }
+
+      get connected() {
+        return true;
+      }
+    }
+
+    customElements.define("test-html-element", TestHTMLCustomElement);
+
+    function checkElement(element, ns, connected, type) {
+      is(element.namespaceURI, ns, `${type} should have the correct namespace`);
+      if (connected) {
+        ok(element.connected, `${type} should have applied the class`);
+      } else {
+        is(element.connected, undefined, `${type} should not have applied the class`);
+      }
+    }
+
+    function runTest() {
+      let element = new TestXULCustomElement();
+      checkElement(element, XUL_NS, true, "instantiated XUL");
+
+      element = document.getElementById("xul2");
+      checkElement(element, HTML_NS, false, "parsed XUL as HTML");
+
+      element = document.createElement("test-xul-element");
+      checkElement(element, HTML_NS, false, "document.createElement(XUL)");
+
+      element = document.createXULElement("test-xul-element");
+      checkElement(element, XUL_NS, true, "document.createXULElement(XUL)");
+
+      element = document.createElementNS(XUL_NS, "test-xul-element");
+      checkElement(element, XUL_NS, true, "document.createElementNS(XUL, XUL)");
+
+      element = document.createElementNS(HTML_NS, "test-xul-element");
+      checkElement(element, HTML_NS, false, "document.createElementNS(HTML, XUL)");
+
+      element = new TestHTMLCustomElement();
+      checkElement(element, HTML_NS, true, "instantiated HTML");
+
+      element = document.getElementById("html2");
+      checkElement(element, HTML_NS, true, "parsed HTML as HTML");
+
+      element = document.createElement("test-html-element");
+      checkElement(element, HTML_NS, true, "document.createElement(HTML)");
+
+      element = document.createXULElement("test-html-element");
+      checkElement(element, XUL_NS, false, "document.createXULElement(HTML)");
+
+      element = document.createElementNS(XUL_NS, "test-html-element");
+      checkElement(element, XUL_NS, false, "document.createElementNS(XUL, HTML)");
+
+      element = document.createElementNS(HTML_NS, "test-html-element");
+      checkElement(element, HTML_NS, true, "document.createElementNS(HTML, HTML)");
+
+      SimpleTest.finish();
+    }
+  </script>
+</head>
+<body onload="runTest();">
+  <p id="display"></p>
+  <div id="content">
+    <test-xul-element id="xul2"/>
+    <test-html-element id="html2"/>
+  </div>
+  <pre id="test"></pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/webcomponents/test_custom_element_namespace.xhtml
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+  <title>Custom Elements in an XHTML document</title>
+  <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+  <script>
+    SimpleTest.waitForExplicitFinish();
+
+    const HTML_NS = "http://www.w3.org/1999/xhtml";
+    const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+    class TestXULCustomElement extends XULElement {
+      constructor() {
+        super();
+      }
+
+      get connected() {
+        return true;
+      }
+    }
+
+    customElements.define("test-xul-element", TestXULCustomElement);
+
+    class TestHTMLCustomElement extends HTMLElement {
+      constructor() {
+        super();
+      }
+
+      get connected() {
+        return true;
+      }
+    }
+
+    customElements.define("test-html-element", TestHTMLCustomElement);
+
+    function checkElement(element, ns, connected, type) {
+      is(element.namespaceURI, ns, `${type} should have the correct namespace`);
+      if (connected) {
+        ok(element.connected, `${type} should have applied the class`);
+      } else {
+        is(element.connected, undefined, `${type} should not have applied the class`);
+      }
+    }
+
+    function runTest() {
+      let element = new TestXULCustomElement();
+      checkElement(element, XUL_NS, true, "instantiated XUL");
+
+      element = document.getElementById("xul1");
+      checkElement(element, XUL_NS, true, "parsed XUL as XUL");
+
+      element = document.getElementById("xul2");
+      checkElement(element, HTML_NS, false, "parsed XUL as HTML");
+
+      element = document.createElement("test-xul-element");
+      checkElement(element, HTML_NS, false, "document.createElement(XUL)");
+
+      element = document.createXULElement("test-xul-element");
+      checkElement(element, XUL_NS, true, "document.createXULElement(XUL)");
+
+      element = document.createElementNS(XUL_NS, "test-xul-element");
+      checkElement(element, XUL_NS, true, "document.createElementNS(XUL, XUL)");
+
+      element = document.createElementNS(HTML_NS, "test-xul-element");
+      checkElement(element, HTML_NS, false, "document.createElementNS(HTML, XUL)");
+
+      element = new TestHTMLCustomElement();
+      checkElement(element, HTML_NS, true, "instantiated HTML");
+
+      element = document.getElementById("html1");
+      checkElement(element, XUL_NS, false, "parsed HTML as XUL");
+
+      element = document.getElementById("html2");
+      checkElement(element, HTML_NS, true, "parsed HTML as HTML");
+
+      element = document.createElement("test-html-element");
+      checkElement(element, HTML_NS, true, "document.createElement(HTML)");
+
+      element = document.createXULElement("test-html-element");
+      checkElement(element, XUL_NS, false, "document.createXULElement(HTML)");
+
+      element = document.createElementNS(XUL_NS, "test-html-element");
+      checkElement(element, XUL_NS, false, "document.createElementNS(XUL, HTML)");
+
+      element = document.createElementNS(HTML_NS, "test-html-element");
+      checkElement(element, HTML_NS, true, "document.createElementNS(HTML, HTML)");
+
+      SimpleTest.finish();
+    }
+  </script>
+</head>
+<body onload="runTest();">
+  <p id="display"></p>
+  <div id="content">
+    <test-xul-element xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="xul1"/>
+    <test-html-element xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="html1"/>
+    <test-xul-element id="xul2"/>
+    <test-html-element id="html2"/>
+  </div>
+  <pre id="test"></pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/webcomponents/test_custom_element_namespace.xul
@@ -0,0 +1,112 @@
+<?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>Custom Elements in a XUL document</title>
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+  <script type="application/javascript">
+  <![CDATA[
+    SimpleTest.waitForExplicitFinish();
+
+    const HTML_NS = "http://www.w3.org/1999/xhtml";
+    const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+    class TestXULCustomElement extends XULElement {
+      constructor() {
+        super();
+      }
+
+      get connected() {
+        return true;
+      }
+    }
+
+    customElements.define("test-xul-element", TestXULCustomElement);
+
+    class TestHTMLCustomElement extends HTMLElement {
+      constructor() {
+        super();
+      }
+
+      get connected() {
+        return true;
+      }
+    }
+
+    customElements.define("test-html-element", TestHTMLCustomElement);
+
+    function checkElement(element, ns, connected, type) {
+      is(element.namespaceURI, ns, `${type} should have the correct namespace`);
+      if (connected) {
+        ok(element.connected, `${type} should have applied the class`);
+      } else {
+        is(element.connected, undefined, `${type} should not have applied the class`);
+      }
+    }
+
+    function runTest() {
+      let element = new TestXULCustomElement();
+      checkElement(element, XUL_NS, true, "instantiated XUL");
+
+      element = document.getElementById("xul1");
+      checkElement(element, XUL_NS, true, "parsed XUL as XUL");
+
+      element = document.getElementById("xul2");
+      checkElement(element, HTML_NS, false, "parsed XUL as HTML");
+
+      element = document.createElement("test-xul-element");
+      checkElement(element, XUL_NS, true, "document.createElement(XUL)");
+
+      element = document.createXULElement("test-xul-element");
+      checkElement(element, XUL_NS, true, "document.createXULElement(XUL)");
+
+      element = document.createElementNS(XUL_NS, "test-xul-element");
+      checkElement(element, XUL_NS, true, "document.createElementNS(XUL, XUL)");
+
+      element = document.createElementNS(HTML_NS, "test-xul-element");
+      checkElement(element, HTML_NS, false, "document.createElementNS(HTML, XUL)");
+
+      element = new TestHTMLCustomElement();
+      checkElement(element, HTML_NS, true, "instantiated HTML");
+
+      element = document.getElementById("html1");
+      checkElement(element, XUL_NS, false, "parsed HTML as XUL");
+
+      element = document.getElementById("html2");
+      checkElement(element, HTML_NS, true, "parsed HTML as HTML");
+
+      element = document.createElement("test-html-element");
+      checkElement(element, XUL_NS, false, "document.createElement(HTML)");
+
+      element = document.createXULElement("test-html-element");
+      checkElement(element, XUL_NS, false, "document.createXULElement(HTML)");
+
+      element = document.createElementNS(XUL_NS, "test-html-element");
+      checkElement(element, XUL_NS, false, "document.createElementNS(XUL, HTML)");
+
+      element = document.createElementNS(HTML_NS, "test-html-element");
+      checkElement(element, HTML_NS, true, "document.createElementNS(HTML, HTML)");
+
+      SimpleTest.finish();
+    }
+  ]]>
+  </script>
+
+  <test-xul-element id="xul1"/>
+  <test-html-element id="html1"/>
+
+  <body xmlns="http://www.w3.org/1999/xhtml">
+    <p id="display"></p>
+    <div id="content" style="display: none">
+      <test-xul-element id="xul2"/>
+      <test-html-element id="html2"/>
+    </div>
+    <pre id="test"></pre>
+  </body>
+</window>
--- a/parser/html/nsHtml5TreeOperation.cpp
+++ b/parser/html/nsHtml5TreeOperation.cpp
@@ -377,17 +377,17 @@ nsHtml5TreeOperation::CreateHTMLElement(
   dom::Element* newContent = nullptr;
   nsIDocument* document = nodeInfo->GetDocument();
   bool willExecuteScript = false;
   bool isCustomElement = false;
   RefPtr<nsAtom> isAtom;
   dom::CustomElementDefinition* definition = nullptr;
 
   // Avoid overhead by checking if custom elements pref is enabled or not.
-  if (nsContentUtils::IsCustomElementsEnabled()) {
+  if (dom::CustomElementRegistry::IsCustomElementEnabled(document)) {
     if (aAttributes) {
       nsHtml5String is = aAttributes->getValue(nsHtml5AttributeName::ATTR_IS);
       if (is) {
         nsAutoString isValue;
         is.ToString(isValue);
         isAtom = NS_Atomize(isValue);
       }
     }