mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 10:44:56 +00:00
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 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
2022970c76
commit
fd8d6b1590
@ -10,6 +10,7 @@
|
||||
#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"
|
||||
@ -356,6 +357,7 @@ CustomElementRegistry::RunCustomElementCreationCallback::Run()
|
||||
|
||||
CustomElementDefinition*
|
||||
CustomElementRegistry::LookupCustomElementDefinition(nsAtom* aNameAtom,
|
||||
int32_t aNameSpaceID,
|
||||
nsAtom* aTypeAtom)
|
||||
{
|
||||
CustomElementDefinition* data = mCustomDefinitions.GetWeak(aTypeAtom);
|
||||
@ -373,7 +375,7 @@ CustomElementRegistry::LookupCustomElementDefinition(nsAtom* aNameAtom,
|
||||
}
|
||||
}
|
||||
|
||||
if (data && data->mLocalName == aNameAtom) {
|
||||
if (data && data->mLocalName == aNameAtom && data->mNamespaceID == aNameSpaceID) {
|
||||
return data;
|
||||
}
|
||||
|
||||
@ -688,6 +690,24 @@ 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,
|
||||
@ -720,12 +740,13 @@ CustomElementRegistry::Define(JSContext* aCx,
|
||||
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);
|
||||
@ -945,6 +966,7 @@ CustomElementRegistry::Define(JSContext* aCx,
|
||||
RefPtr<CustomElementDefinition> definition =
|
||||
new CustomElementDefinition(nameAtom,
|
||||
localNameAtom,
|
||||
nameSpaceID,
|
||||
&aFunctionConstructor,
|
||||
std::move(observedAttributes),
|
||||
std::move(callbacksHolder));
|
||||
@ -1471,11 +1493,13 @@ 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))
|
||||
|
@ -157,6 +157,7 @@ struct CustomElementDefinition
|
||||
|
||||
CustomElementDefinition(nsAtom* aType,
|
||||
nsAtom* aLocalName,
|
||||
int32_t aNamespaceID,
|
||||
Function* aConstructor,
|
||||
nsTArray<RefPtr<nsAtom>>&& aObservedAttributes,
|
||||
UniquePtr<LifecycleCallbacks>&& aCallbacks);
|
||||
@ -168,6 +169,9 @@ struct CustomElementDefinition
|
||||
// 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;
|
||||
|
||||
@ -399,7 +403,7 @@ public:
|
||||
* 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;
|
||||
@ -555,6 +559,8 @@ private:
|
||||
CustomElementRegistry* mRegistry;
|
||||
};
|
||||
|
||||
int32_t InferNamespace(JSContext* aCx, JS::Handle<JSObject*> constructor);
|
||||
|
||||
public:
|
||||
nsISupports* GetParentObject() const;
|
||||
|
||||
|
@ -10002,6 +10002,15 @@ nsContentUtils::NewXULOrHTMLElement(Element** aResult, mozilla::dom::NodeInfo* a
|
||||
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();
|
||||
}
|
||||
@ -10122,7 +10131,7 @@ nsContentUtils::LookupCustomElementDefinition(nsIDocument* aDoc,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return registry->LookupCustomElementDefinition(aNameAtom, aTypeAtom);
|
||||
return registry->LookupCustomElementDefinition(aNameAtom, aNameSpaceID, aTypeAtom);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
|
@ -3811,10 +3811,7 @@ HTMLConstructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp,
|
||||
// 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) {
|
||||
|
@ -3,3 +3,6 @@ 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]
|
||||
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -382,7 +382,7 @@ nsHtml5TreeOperation::CreateHTMLElement(
|
||||
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) {
|
||||
|
Loading…
Reference in New Issue
Block a user