Skip to content

Commit

Permalink
Implement revamped scoped custom element registry
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=286870

Reviewed by NOBODY (OOPS!).

This PR implements a number of API changes we proposed in whatwg/html#10854.

Since the new API's behavior is sufficiently different from the old proposal, this PR opts to write a new set of tests
instead of retrofitting the old tests to match the new behavior.

* LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/Construct.tentative-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/Construct.tentative.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/CustomElementRegistry-define.tentative-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/CustomElementRegistry-define.tentative.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/CustomElementRegistry-initialize.tentative-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/CustomElementRegistry-initialize.tentative.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/CustomElementRegistry-upgrade.tentative-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/CustomElementRegistry-upgrade.tentative.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/Document-createElement.tentative-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/Document-createElement.tentative.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/Document-importNode.tentative-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/Document-importNode.tentative.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/Element-customElements-exceptions.tentative-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/Element-customElements-exceptions.tentative.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/Element-customElements.tentative-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/Element-customElements.tentative.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/Element-innerHTML.tentative-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/Element-innerHTML.tentative.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/ShadowRoot-init-customElements.tentative-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/ShadowRoot-init-customElements.tentative.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/ShadowRoot-innerHTML.tentative-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/custom-elements/revamped-scoped-registry/ShadowRoot-innerHTML.tentative.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/custom-elements/scoped-registry/ShadowRoot-createElement.tentative-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/custom-elements/scoped-registry/ShadowRoot-importNode.tentative-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/custom-elements/scoped-registry/ShadowRoot-init-registry.tentative-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/custom-elements/scoped-registry/ShadowRoot-innerHTML-upgrade.tentative-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/custom-elements/scoped-registry/ShadowRoot-innerHTML.tentative-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/custom-elements/scoped-registry/constructor-reentry-with-different-definition.tentative-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/custom-elements/scoped-registry/scoped-registry-define-upgrade-criteria.tentative-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/custom-elements/scoped-registry/scoped-registry-define-upgrade-order.tentative-expected.txt:
* Source/WebCore/CMakeLists.txt:
* Source/WebCore/DerivedSources-input.xcfilelist:
* Source/WebCore/DerivedSources-output.xcfilelist:
* Source/WebCore/DerivedSources.make:
* Source/WebCore/Headers.cmake:
* Source/WebCore/Sources.txt:
* Source/WebCore/WebCore.xcodeproj/project.pbxproj:
* Source/WebCore/dom/Attr.cpp:
(WebCore::Attr::cloneNodeInternal):
* Source/WebCore/dom/Attr.h:
* Source/WebCore/dom/CDATASection.cpp:
(WebCore::CDATASection::cloneNodeInternal):
* Source/WebCore/dom/CDATASection.h:
* Source/WebCore/dom/Comment.cpp:
(WebCore::Comment::cloneNodeInternal):
* Source/WebCore/dom/Comment.h:
* Source/WebCore/dom/ContainerNode.cpp:
(WebCore::ContainerNode::cloneChildNodes):
* Source/WebCore/dom/ContainerNode.h:
* Source/WebCore/dom/CustomElementReactionQueue.cpp:
(WebCore::CustomElementReactionQueue::tryToUpgradeElement):
* Source/WebCore/dom/CustomElementRegistry.cpp:
(WebCore::upgradeElementsInShadowIncludingDescendants):
(WebCore::CustomElementRegistry::upgrade):
(WebCore::CustomElementRegistry::initialize):
(WebCore::CustomElementRegistry::addToScopedCustomElementRegistryMap):
* Source/WebCore/dom/CustomElementRegistry.h:
(WebCore::CustomElementRegistry::registryForElement):
(WebCore::CustomElementRegistry::registryForNodeOrTreeScope):
* Source/WebCore/dom/CustomElementRegistry.idl:
* Source/WebCore/dom/Document.cpp:
(WebCore::createUpgradeCandidateElement):
(WebCore::createHTMLElementWithNameValidation):
(WebCore::Document::createElementForBindings):
(WebCore::Document::importNode):
(WebCore::createFallbackHTMLElement):
(WebCore::Document::createElement):
(WebCore::Document::createElementNS):
(WebCore::Document::cloneNodeInternal):
(WebCore::TreeScope::createElementForBindings): Deleted.
(WebCore::TreeScope::createElement): Deleted.
(WebCore::TreeScope::createElementNS): Deleted.
* Source/WebCore/dom/Document.h:
(WebCore::Document::setSawElementsInKnownNamespaces): Deleted.
* Source/WebCore/dom/Document.idl:
* Source/WebCore/dom/DocumentFragment.cpp:
(WebCore::DocumentFragment::cloneNodeInternal):
* Source/WebCore/dom/DocumentFragment.h:
* Source/WebCore/dom/DocumentType.cpp:
(WebCore::DocumentType::cloneNodeInternal):
* Source/WebCore/dom/DocumentType.h:
* Source/WebCore/dom/Element.cpp:
(WebCore::Element::cloneNodeInternal):
(WebCore::Element::cloneShadowTreeIfPossible):
(WebCore::Element::cloneElementWithChildren):
(WebCore::Element::cloneElementWithoutChildren):
(WebCore::Element::cloneElementWithoutAttributesAndChildren):
(WebCore::Element::insertedIntoAncestor):
(WebCore::Element::removedFromAncestor):
(WebCore::Element::customElementRegistry const):
* Source/WebCore/dom/Element.h:
* Source/WebCore/dom/Element.idl:
* Source/WebCore/dom/ElementCreationOptions.h: Copied from Source/WebCore/dom/ShadowRootInit.h.
* Source/WebCore/dom/ElementCreationOptions.idl: Copied from Source/WebCore/dom/ShadowRootInit.idl.
* Source/WebCore/dom/ImportNodeOptions.h: Copied from Source/WebCore/dom/ShadowRootInit.h.
* Source/WebCore/dom/ImportNodeOptions.idl: Copied from Source/WebCore/dom/ShadowRootInit.idl.
* Source/WebCore/dom/Node.cpp:
(WebCore::Node::cloneNode):
* Source/WebCore/dom/Node.h:
(WebCore::Node::usesNullCustomElementRegistry const):
(WebCore::Node::setUsesNullCustomElementRegistry const):
(WebCore::Node::clearUsesNullCustomElementRegistry const):
* Source/WebCore/dom/ProcessingInstruction.cpp:
(WebCore::ProcessingInstruction::cloneNodeInternal):
* Source/WebCore/dom/ProcessingInstruction.h:
* Source/WebCore/dom/ShadowRoot.cpp:
(WebCore::ShadowRoot::insertedIntoAncestor):
(WebCore::ShadowRoot::removedFromAncestor):
(WebCore::ShadowRoot::registryForBindings const):
(WebCore::ShadowRoot::cloneNodeInternal):
* Source/WebCore/dom/ShadowRoot.h:
* Source/WebCore/dom/ShadowRoot.idl:
* Source/WebCore/dom/ShadowRootInit.h:
* Source/WebCore/dom/ShadowRootInit.idl:
* Source/WebCore/dom/Text.cpp:
(WebCore::Text::cloneNodeInternal):
* Source/WebCore/dom/Text.h:
* Source/WebCore/dom/TreeScope.cpp:
(WebCore::TreeScope::setCustomElementRegistry):
(WebCore::TreeScope::importNode): Deleted.
* Source/WebCore/dom/TreeScope.h:
(WebCore::TreeScope::customElementRegistry const):
* Source/WebCore/editing/ApplyStyleCommand.cpp:
(WebCore::ApplyStyleCommand::pushDownInlineStyleAroundNode):
(WebCore::ApplyStyleCommand::applyInlineStyleChange):
* Source/WebCore/editing/BreakBlockquoteCommand.cpp:
(WebCore::BreakBlockquoteCommand::doApply):
* Source/WebCore/editing/InsertParagraphSeparatorCommand.cpp:
(WebCore::InsertParagraphSeparatorCommand::cloneHierarchyUnderNewBlock):
(WebCore::InsertParagraphSeparatorCommand::doApply):
* Source/WebCore/editing/MarkupAccumulator.cpp:
(WebCore::MarkupAccumulator::startAppendingNode):
* Source/WebCore/editing/ModifySelectionListLevel.cpp:
(WebCore::IncreaseSelectionListLevelCommand::doApply):
* Source/WebCore/editing/SplitElementCommand.cpp:
(WebCore::SplitElementCommand::doApply):
* Source/WebCore/editing/TextManipulationController.cpp:
(WebCore::TextManipulationController::updateInsertions):
* Source/WebCore/editing/markup.cpp:
(WebCore::createFragmentFromText):
* Source/WebCore/html/AttachmentAssociatedElement.cpp:
(WebCore::AttachmentAssociatedElement::cloneAttachmentAssociatedElementWithoutAttributesAndChildren):
* Source/WebCore/html/AttachmentAssociatedElement.h:
* Source/WebCore/html/HTMLAttributeNames.in:
* Source/WebCore/html/HTMLImageElement.cpp:
(WebCore::HTMLImageElement::cloneElementWithoutAttributesAndChildren):
* Source/WebCore/html/HTMLImageElement.h:
* Source/WebCore/html/HTMLInputElement.cpp:
(WebCore::HTMLInputElement::cloneElementWithoutAttributesAndChildren):
* Source/WebCore/html/HTMLInputElement.h:
* Source/WebCore/html/HTMLScriptElement.cpp:
(WebCore::HTMLScriptElement::cloneElementWithoutAttributesAndChildren):
* Source/WebCore/html/HTMLScriptElement.h:
* Source/WebCore/html/HTMLSourceElement.cpp:
(WebCore::HTMLSourceElement::cloneElementWithoutAttributesAndChildren):
* Source/WebCore/html/HTMLSourceElement.h:
* Source/WebCore/html/HTMLTemplateElement.cpp:
(WebCore::HTMLTemplateElement::cloneNodeInternal):
(WebCore::HTMLTemplateElement::attachAsDeclarativeShadowRootIfNeeded): Deleted used code.
* Source/WebCore/html/HTMLTemplateElement.h:
* Source/WebCore/html/HTMLTemplateElement.idl:
* Source/WebCore/html/parser/HTMLConstructionSite.cpp:
(WebCore::HTMLConstructionSite::insertHTMLTemplateElement):
(WebCore::HTMLConstructionSite::createElement):
(WebCore::HTMLConstructionSite::createHTMLElementOrFindCustomElementInterface):
* Source/WebCore/html/shadow/SliderThumbElement.cpp:
(WebCore::SliderThumbElement::cloneElementWithoutAttributesAndChildren):
* Source/WebCore/html/shadow/SliderThumbElement.h:
* Source/WebCore/html/track/TextTrackCue.cpp:
(WebCore::TextTrackCue::create):
(WebCore::TextTrackCue::getCueAsHTML):
(WebCore::TextTrackCue::rebuildDisplayTree):
* Source/WebCore/html/track/VTTCue.cpp:
(WebCore::VTTCue::createCueRenderingTree):
* Source/WebCore/html/track/WebVTTElement.cpp:
(WebCore::WebVTTElement::cloneElementWithoutAttributesAndChildren):
* Source/WebCore/html/track/WebVTTElement.h:
* Source/WebCore/inspector/agents/InspectorDOMAgent.cpp:
* Source/WebCore/page/LocalDOMWindow.cpp:
(WebCore::LocalDOMWindow::ensureCustomElementRegistry):
* Source/WebCore/svg/SVGScriptElement.cpp:
(WebCore::SVGScriptElement::cloneElementWithoutAttributesAndChildren):
* Source/WebCore/svg/SVGScriptElement.h:
* Source/WebCore/svg/SVGUseElement.cpp:
(WebCore::SVGUseElement::cloneTarget const):
(WebCore::cloneDataAndChildren):
* Source/WebCore/xml/parser/XMLDocumentParserLibxml2.cpp:
(WebCore::XMLDocumentParser::startElementNs):
* Source/WebKitLegacy/mac/DOM/DOMDocument.mm:
  • Loading branch information
rniwa committed Feb 1, 2025
1 parent 8881c5e commit ea3200b
Show file tree
Hide file tree
Showing 112 changed files with 1,498 additions and 295 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

PASS A constructor with only a scoped custom element registry definition should fail upon construction
PASS A constructor uses the global registry to create an element
PASS A constructor creating an element from another registry before or after super call should work

Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<!DOCTYPE html>
<html>
<head>
<meta name="author" title="Ryosuke Niwa" href="mailto:[email protected]">
<link rel="help" href="https://github.com/whatwg/html/issues/10854">
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<script>

test(() => {
class ABElement extends HTMLElement { };
const scopedRegistry = new CustomElementRegistry;
scopedRegistry.define('a-b', ABElement);
assert_throws_js(TypeError, () => new ABElement);
}, 'A constructor with only a scoped custom element registry definition should fail upon construction');

test(() => {
class CElement extends HTMLElement { };
const scopedRegistry = new CustomElementRegistry;
scopedRegistry.define('scoped-c', CElement);
customElements.define('global-c', CElement);
const cElement = new CElement;
assert_equals(cElement.localName, 'global-c');
}, 'A constructor uses the global registry to create an element');

test(() => {
let fgElement;
let hiElement;
class DEElement extends HTMLElement {
constructor() {
fgElement = document.createElement('f-g', {customElements: scopedRegistry2});
super();
hiElement = document.createElement('h-i', {customElements: scopedRegistry2});
}
};
class FGElement extends HTMLElement { }
class HIElement extends HTMLElement { }
const scopedRegistry1 = new CustomElementRegistry;
scopedRegistry1.define('d-e', DEElement);
const scopedRegistry2 = new CustomElementRegistry;
scopedRegistry2.define('f-g', FGElement);
scopedRegistry2.define('h-i', HIElement);

const deElement = document.createElement('d-e', {customElements: scopedRegistry1});
assert_true(deElement instanceof DEElement);
assert_equals(deElement.customElements, scopedRegistry1);
assert_true(fgElement instanceof FGElement);
assert_equals(fgElement.customElements, scopedRegistry2);
assert_true(hiElement instanceof HIElement);
assert_equals(hiElement.customElements, scopedRegistry2);
}, 'A constructor creating an element from another registry before or after super call should work');

</script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

PASS Create a CustomElementRegistry not identically equal to window.customElements
PASS Defining an element in the global registry does not add a definition to a scoped CustomElementRegistry
PASS Defining an element in a scoped global registry does not add a definition to the global registry

Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<!DOCTYPE html>
<html>
<head>
<meta name="author" title="Ryosuke Niwa" href="mailto:[email protected]">
<link rel="help" href="https://github.com/whatwg/html/issues/10854">
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<script>
test(() => {
let registry = new CustomElementRegistry();
assert_not_equals(registry, window.customElements);
}, 'Create a CustomElementRegistry not identically equal to window.customElements');

test(() => {
let registry = new CustomElementRegistry();
window.customElements.define('a-b', class extends HTMLElement {});
assert_equals(registry.get('a-b'), undefined);
}, 'Defining an element in the global registry does not add a definition to a scoped CustomElementRegistry');

test(() => {
let registry = new CustomElementRegistry();
registry.define('b-c', class extends HTMLElement {});
assert_equals(window.customElements.get('b-c'), undefined);
}, 'Defining an element in a scoped global registry does not add a definition to the global registry');

</script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

PASS initialize is a function on both global and scoped CustomElementRegistry
PASS initialize sets element.customElements to the global registry
PASS initialize does not set the registry of nested shadow tree to the global registry
PASS initialize sets element.customElements to a scoped registry
PASS initialize does not set the registry of nested shadow tree to a scoped registry
PASS initialize sets element.customElements permantently
PASS initialize is no-op on a subtree with a non-null registry

Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
<!DOCTYPE html>
<html>
<head>
<meta name="author" title="Ryosuke Niwa" href="mailto:[email protected]">
<link rel="help" href="https://github.com/whatwg/html/issues/10854">
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<div id="host">
<template shadowrootmode="open" shadowrootclonable="true" shadowrootcustomelements>
<a-b>
<template shadowrootmode="open" shadowrootclonable="true" shadowrootcustomelements>
<c-d/><c-d>
</template>
</a-b>
<ef></ef>
</template>
</div>
<div id="host-with-registry">
<template shadowrootmode="open" shadowrootclonable="true">
<a-b></a-b>
<ef></ef>
</template>
</div>
<script>

test(() => {
assert_equals(typeof(window.customElements.initialize), 'function');
assert_equals(typeof((new CustomElementRegistry).initialize), 'function');
}, 'initialize is a function on both global and scoped CustomElementRegistry');

test(() => {
const clone = host.cloneNode(true);
const shadowRoot = clone.shadowRoot;
assert_equals(shadowRoot.customElements, null);
assert_equals(shadowRoot.querySelector('a-b').customElements, null);
assert_equals(shadowRoot.querySelector('ef').customElements, null);
window.customElements.initialize(shadowRoot);
assert_equals(shadowRoot.customElements, window.customElements);
assert_equals(shadowRoot.querySelector('a-b').customElements, window.customElements);
assert_equals(shadowRoot.querySelector('ef').customElements, window.customElements);
}, 'initialize sets element.customElements to the global registry');

test(() => {
const clone = host.cloneNode(true);
const shadowRoot = clone.shadowRoot;
assert_equals(shadowRoot.customElements, null);
assert_equals(shadowRoot.querySelector('a-b').customElements, null);
assert_equals(shadowRoot.querySelector('ef').customElements, null);
window.customElements.initialize(shadowRoot);
assert_equals(shadowRoot.customElements, window.customElements);
assert_equals(shadowRoot.querySelector('a-b').customElements, window.customElements);
assert_equals(shadowRoot.querySelector('ef').customElements, window.customElements);
assert_equals(shadowRoot.querySelector('a-b').shadowRoot.customElements, null);
assert_equals(shadowRoot.querySelector('a-b').shadowRoot.querySelector('c-d').customElements, null);
}, 'initialize does not set the registry of nested shadow tree to the global registry');


test(() => {
const clone = host.cloneNode(true);
const shadowRoot = clone.shadowRoot;
assert_equals(shadowRoot.customElements, null);
assert_equals(shadowRoot.querySelector('a-b').customElements, null);
assert_equals(shadowRoot.querySelector('ef').customElements, null);
const registry = new CustomElementRegistry;
registry.initialize(shadowRoot);
assert_equals(shadowRoot.customElements, registry);
assert_equals(shadowRoot.querySelector('a-b').customElements, registry);
assert_equals(shadowRoot.querySelector('ef').customElements, registry);
}, 'initialize sets element.customElements to a scoped registry');

test(() => {
const clone = host.cloneNode(true);
const shadowRoot = clone.shadowRoot;
assert_equals(shadowRoot.customElements, null);
assert_equals(shadowRoot.querySelector('a-b').customElements, null);
assert_equals(shadowRoot.querySelector('ef').customElements, null);
const registry = new CustomElementRegistry;
registry.initialize(shadowRoot);
assert_equals(shadowRoot.customElements, registry);
assert_equals(shadowRoot.querySelector('a-b').customElements, registry);
assert_equals(shadowRoot.querySelector('ef').customElements, registry);
assert_equals(shadowRoot.querySelector('a-b').shadowRoot.customElements, null);
assert_equals(shadowRoot.querySelector('a-b').shadowRoot.querySelector('c-d').customElements, null);
}, 'initialize does not set the registry of nested shadow tree to a scoped registry');

test(() => {
const clone = host.cloneNode(true);
const shadowRoot = clone.shadowRoot;
assert_equals(shadowRoot.customElements, null);
assert_equals(shadowRoot.querySelector('a-b').customElements, null);
assert_equals(shadowRoot.querySelector('ef').customElements, null);
const registry = new CustomElementRegistry;
registry.initialize(shadowRoot);
assert_equals(shadowRoot.customElements, registry);
assert_equals(shadowRoot.querySelector('a-b').customElements, registry);
assert_equals(shadowRoot.querySelector('ef').customElements, registry);
document.body.appendChild(clone);
assert_equals(shadowRoot.customElements, registry);
assert_equals(shadowRoot.querySelector('a-b').customElements, registry);
assert_equals(shadowRoot.querySelector('ef').customElements, registry);
}, 'initialize sets element.customElements permantently');

test(() => {
const clone = document.getElementById('host-with-registry').cloneNode(true);
const shadowRoot = clone.shadowRoot;
assert_equals(shadowRoot.customElements, window.customElements);
assert_equals(shadowRoot.querySelector('a-b').customElements, window.customElements);
assert_equals(shadowRoot.querySelector('ef').customElements, window.customElements);
const registry = new CustomElementRegistry;
registry.initialize(shadowRoot);
assert_equals(shadowRoot.customElements, window.customElements);
assert_equals(shadowRoot.querySelector('a-b').customElements, window.customElements);
assert_equals(shadowRoot.querySelector('ef').customElements, window.customElements);
}, 'initialize is no-op on a subtree with a non-null registry');

</script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
CONSOLE MESSAGE: [object HTMLElement]

PASS upgrade is a function on both global and scoped CustomElementRegistry
PASS upgrade is a no-op when called on a shadow root with no association
PASS upgrade should upgrade a candidate element when called on a shadow root with an association
PASS upgrade should not upgrade a candidate element not associated with the registry

Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
<!DOCTYPE html>
<html>
<head>
<meta name="author" title="Ryosuke Niwa" href="mailto:[email protected]">
<link rel="help" href="https://github.com/whatwg/html/issues/10854">
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<some-host id="host">
<template shadowrootmode="closed" shadowrootclonable="true" shadowrootcustomelements>
<a-b>
<template shadowrootmode="closed" shadowrootclonable="true" shadowrootcustomelements>
<c-d/>
<template shadowrootmode="closed" shadowrootclonable="true">
<a-b></a-b>
</template>
<c-d>
</template>
</a-b>
</template>
</some-host>
<script>

customElements.define('some-host', class SomeHost extends HTMLElement {
internals;

constructor() {
super();
this.internals = this.attachInternals();
}
});
customElements.define('a-b', class GlobalABElement extends HTMLElement { });
customElements.define('c-d', class GlobalCDElement extends HTMLElement { });

test(() => {
assert_equals(typeof(window.customElements.upgrade), 'function');
assert_equals(typeof((new CustomElementRegistry).upgrade), 'function');
}, 'upgrade is a function on both global and scoped CustomElementRegistry');

test(() => {
const registry = new CustomElementRegistry;
registry.define('a-b', class ABElement extends HTMLElement { });

const clone = host.cloneNode(true);
registry.upgrade(clone.internals.shadowRoot);
assert_equals(clone.internals.shadowRoot.querySelector('a-b').__proto__.constructor.name, 'HTMLElement');
}, 'upgrade is a no-op when called on a shadow root with no association');

test(() => {
const registry = new CustomElementRegistry;
registry.define('a-b', class ABElement extends HTMLElement {
internals;

constructor() {
super();
this.internals = this.attachInternals();
}
});

const clone = host.cloneNode(true);
registry.initialize(clone.internals.shadowRoot);
registry.upgrade(clone.internals.shadowRoot);
const abElement = clone.internals.shadowRoot.querySelector('a-b');
assert_equals(abElement.__proto__.constructor.name, 'ABElement');
assert_equals(abElement.internals.shadowRoot.customElements, null);
const cdElement = abElement.internals.shadowRoot.querySelector('c-d');
assert_equals(cdElement.__proto__.constructor.name, 'HTMLElement');
assert_equals(cdElement.customElements, null);
}, 'upgrade should upgrade a candidate element when called on a shadow root with an association');

test(() => {
const registry = new CustomElementRegistry;
registry.define('a-b', class ScopedABElement extends HTMLElement {
internals;

constructor() {
super();
this.internals = this.attachInternals();
}
});

const clone = host.cloneNode(true);
console.log(clone);
registry.initialize(clone.internals.shadowRoot);
registry.upgrade(clone.internals.shadowRoot);
const abElement = clone.internals.shadowRoot.querySelector('a-b');
assert_equals(abElement.__proto__.constructor.name, 'ScopedABElement');
registry.initialize(abElement.internals.shadowRoot);
assert_equals(abElement.internals.shadowRoot.customElements, registry);
const cdElement = abElement.internals.shadowRoot.querySelector('c-d');
assert_equals(cdElement.customElements, registry);

registry.define('c-d', class ScopedCDElement extends HTMLElement {
internals;

constructor() {
super();
this.internals = this.attachInternals();
}
});
assert_equals(cdElement.__proto__.constructor.name, 'HTMLElement');
registry.upgrade(abElement.internals.shadowRoot);
assert_equals(cdElement.__proto__.constructor.name, 'ScopedCDElement');
assert_equals(cdElement.customElements, registry);

assert_equals(cdElement.internals.shadowRoot.customElements, window.customElements);
const innerAB = cdElement.internals.shadowRoot.querySelector('a-b');
assert_equals(innerAB.customElements, window.customElements);
assert_equals(innerAB.__proto__.constructor.name, 'HTMLElement');
}, 'upgrade should not upgrade a candidate element not associated with the registry');

</script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

PASS createElement should use the global registry by default
PASS createElement should use the specified scoped registry
PASS createElement should create a builtin element regardles of a custom element registry specified
PASS createElement should use the specified global registry
PASS createElement should create an upgrade candidate when there is no matching definition in the specified registry
PASS createElement should create an upgrade candidate and the candidate should be upgraded when the element is defined

Loading

0 comments on commit ea3200b

Please sign in to comment.