Skip to content

Commit c13b9f1

Browse files
committed
CustomElementRegistry.prototype.initialize should upgrade custom elements
https://bugs.webkit.org/show_bug.cgi?id=302534 Reviewed by Anne van Kesteren. Implement whatwg/html#11913. This PR also fixes a few bugs in the existing code, which were revealed by the newly introduced test. Test: imported/w3c/web-platform-tests/custom-elements/registries/scoped-registry-initialize-upgrades.html * LayoutTests/imported/w3c/web-platform-tests/custom-elements/registries/pseudo-class-defined.window-expected.txt: * LayoutTests/imported/w3c/web-platform-tests/custom-elements/registries/pseudo-class-defined.window.js: * LayoutTests/imported/w3c/web-platform-tests/custom-elements/registries/scoped-registry-initialize-upgrades-expected.txt: Added. * LayoutTests/imported/w3c/web-platform-tests/custom-elements/registries/scoped-registry-initialize-upgrades.html: Added. * Source/WebCore/bindings/js/JSCustomElementInterface.cpp: (WebCore::JSCustomElementInterface::tryToConstructCustomElement): Replaced the bad assertion with a code to get the global object through the script execution context associated with this custom element interface. * Source/WebCore/dom/CustomElementRegistry.cpp: (WebCore::CustomElementRegistry::initialize): Implement the new semantics. * Source/WebCore/dom/Document.cpp: (WebCore::createUpgradeCandidateElement): Call setUsesNullCustomElementRegistry in the callers of this function (createElementNS and createElementForBindings). (WebCore::Document::createElementForBindings): (WebCore::Document::createElementNS): Canonical link: https://commits.webkit.org/303250@main
1 parent bd68a87 commit c13b9f1

File tree

7 files changed

+126
-19
lines changed

7 files changed

+126
-19
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

22
PASS "uncustomized" :defined doesn't care about your registry'
33
PASS "custom" :defined doesn't care about your registry
4-
PASS pseudo-class-defined
4+
PASS "custom" :defined should apply after initialize
55

LayoutTests/imported/w3c/web-platform-tests/custom-elements/registries/pseudo-class-defined.window.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,5 @@ test(() => {
2323
const registry = new CustomElementRegistry();
2424
registry.define("sw-r2d2", class extends HTMLElement {});
2525
registry.initialize(element);
26-
assert_false(element.matches(":defined"));
27-
registry.upgrade(element);
2826
assert_true(element.matches(":defined"));
29-
});
27+
}, `"custom" :defined should apply after initialize`);
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
2+
PASS Document: customElementRegistry.prototype.initialize should upgrade the element given to the first argument
3+
PASS Document: customElementRegistry.prototype.initialize should upgrade elements in tree order
4+
PASS Document: customElementRegistry.prototype.initialize only upgrades elements beloning to the registry
5+
PASS HTMLDocument: customElementRegistry.prototype.initialize should upgrade the element given to the first argument
6+
PASS HTMLDocument: customElementRegistry.prototype.initialize should upgrade elements in tree order
7+
PASS HTMLDocument: customElementRegistry.prototype.initialize only upgrades elements beloning to the registry
8+
PASS XHTMLDocument: customElementRegistry.prototype.initialize should upgrade the element given to the first argument
9+
PASS XHTMLDocument: customElementRegistry.prototype.initialize should upgrade elements in tree order
10+
PASS XHTMLDocument: customElementRegistry.prototype.initialize only upgrades elements beloning to the registry
11+
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta name="author" title="Ryosuke Niwa" href="mailto:[email protected]">
5+
<link rel="help" href="https://html.spec.whatwg.org/multipage/custom-elements.html#dom-customelementregistry-initialize">
6+
<script src="/resources/testharness.js"></script>
7+
<script src="/resources/testharnessreport.js"></script>
8+
</head>
9+
<body>
10+
<script>
11+
12+
function runTest(title, makeDocument, makeCustomElementRegistry) {
13+
test(() => {
14+
const element = makeDocument().createElement('a-b');
15+
assert_equals(element.customElementRegistry, null);
16+
const registry = new CustomElementRegistry;
17+
registry.define('a-b', class ABElement extends HTMLElement { });
18+
assert_equals(element.customElementRegistry, null);
19+
registry.initialize(element);
20+
assert_equals(element.customElementRegistry, registry);
21+
}, `${title}: customElementRegistry.prototype.initialize should upgrade the element given to the first argument`);
22+
23+
test(() => {
24+
const doc = makeDocument();
25+
const container = doc.createElementNS('http://www.w3.org/1999/xhtml', 'div');
26+
container.innerHTML = '<a-b id="ab1"></a-b><a-b id="ab2"></a-b>';
27+
const elements = Array.from(container.querySelectorAll('a-b'));
28+
assert_equals(elements[0].id, 'ab1');
29+
assert_equals(elements[0].customElementRegistry, null);
30+
assert_equals(elements[1].id, 'ab2');
31+
assert_equals(elements[1].customElementRegistry, null);
32+
const registry = new CustomElementRegistry;
33+
let registryInConstructor = [];
34+
class ABElement extends HTMLElement {
35+
constructor() {
36+
super();
37+
registryInConstructor[elements.indexOf(this)] = this.customElementRegistry;
38+
}
39+
};
40+
registry.define('a-b', ABElement);
41+
assert_false(elements[0] instanceof ABElement);
42+
assert_false(elements[1] instanceof ABElement);
43+
assert_equals(registryInConstructor.length, 0);
44+
registry.initialize(container);
45+
assert_equals(elements[0].customElementRegistry, registry);
46+
assert_true(elements[0] instanceof ABElement);
47+
assert_equals(elements[1].customElementRegistry, registry);
48+
assert_true(elements[1] instanceof ABElement);
49+
assert_equals(registryInConstructor.length, 2);
50+
assert_equals(registryInConstructor[0], registry);
51+
assert_equals(registryInConstructor[1], registry);
52+
}, `${title}: customElementRegistry.prototype.initialize should upgrade elements in tree order`);
53+
54+
test(() => {
55+
const doc1 = makeDocument();
56+
const htmlNS = 'http://www.w3.org/1999/xhtml';
57+
if (!doc1.documentElement)
58+
doc1.appendChild(doc1.createElementNS(htmlNS, 'html')).appendChild(doc1.createElementNS(htmlNS, 'body'));
59+
60+
const undefinedElement1 = doc1.createElementNS(htmlNS, 'a-b');
61+
62+
const registry1 = new CustomElementRegistry;
63+
class ABElement extends HTMLElement { };
64+
registry1.define('a-b', ABElement);
65+
66+
const registry2 = new CustomElementRegistry;
67+
undefinedElement2 = doc1.createElementNS(htmlNS, 'a-b', {customElementRegistry: registry2});
68+
69+
undefinedElement1.appendChild(undefinedElement2);
70+
71+
assert_equals(undefinedElement1.customElementRegistry, null);
72+
assert_equals(undefinedElement1.__proto__.constructor.name, 'HTMLElement');
73+
assert_equals(undefinedElement2.customElementRegistry, registry2);
74+
assert_equals(undefinedElement2.__proto__.constructor.name, 'HTMLElement');
75+
76+
registry1.initialize(undefinedElement1);
77+
assert_equals(undefinedElement1.customElementRegistry, registry1);
78+
assert_true(undefinedElement1 instanceof ABElement);
79+
assert_equals(undefinedElement2.customElementRegistry, registry2);
80+
assert_equals(undefinedElement2.__proto__.constructor.name, 'HTMLElement');
81+
}, `${title}: customElementRegistry.prototype.initialize only upgrades elements beloning to the registry`);
82+
}
83+
84+
runTest('Document', () => new Document);
85+
runTest('HTMLDocument', () => document.implementation.createHTMLDocument());
86+
runTest('XHTMLDocument', () => document.implementation.createDocument('http://www.w3.org/1999/xhtml', 'html', null));
87+
88+
</script>
89+
</body>
90+
</html>

Source/WebCore/bindings/js/JSCustomElementInterface.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -120,15 +120,15 @@ RefPtr<Element> JSCustomElementInterface::tryToConstructCustomElement(Document&
120120
if (!m_constructor)
121121
return nullptr;
122122

123-
ASSERT(&document == scriptExecutionContext());
124-
auto* lexicalGlobalObject = document.globalObject();
123+
RefPtr contextDocument = downcast<Document>(scriptExecutionContext());
124+
auto* lexicalGlobalObject = scriptExecutionContext()->globalObject();
125125
ASSERT(lexicalGlobalObject);
126126
if (!lexicalGlobalObject)
127127
return nullptr;
128-
auto* oldRegistry = document.activeCustomElementRegistry();
129-
document.setActiveCustomElementRegistry(&registry);
128+
auto* oldRegistry = contextDocument->activeCustomElementRegistry();
129+
contextDocument->setActiveCustomElementRegistry(&registry);
130130
auto element = constructCustomElementSynchronously(document, vm, *lexicalGlobalObject, m_constructor.get(), localName, parserConstructElementWithEmptyStack);
131-
document.setActiveCustomElementRegistry(oldRegistry);
131+
contextDocument->setActiveCustomElementRegistry(oldRegistry);
132132
EXCEPTION_ASSERT(!!scope.exception() == !element);
133133
if (!element) {
134134
auto* exception = scope.exception();

Source/WebCore/dom/CustomElementRegistry.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,11 +219,19 @@ ExceptionOr<void> CustomElementRegistry::initialize(Node& root)
219219
if (this != registryOfTreeScope)
220220
addToScopedCustomElementRegistryMap(element, *this);
221221
};
222+
auto upgradeElementIfPossible = [&](Element& element) {
223+
if (element.isCustomElementUpgradeCandidate() && CustomElementRegistry::registryForElement(element) == this)
224+
CustomElementReactionQueue::tryToUpgradeElement(element);
225+
};
222226

223-
if (RefPtr element = dynamicDowncast<Element>(*containerRoot))
227+
if (RefPtr element = dynamicDowncast<Element>(*containerRoot)) {
224228
updateRegistryIfNeeded(*element);
225-
for (Ref element : descendantsOfType<Element>(*containerRoot))
229+
upgradeElementIfPossible(*element);
230+
}
231+
for (Ref element : descendantsOfType<Element>(*containerRoot)) {
226232
updateRegistryIfNeeded(element);
233+
upgradeElementIfPossible(element);
234+
}
227235
return { };
228236
}
229237

Source/WebCore/dom/Document.cpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1524,10 +1524,6 @@ static ALWAYS_INLINE Ref<HTMLElement> createUpgradeCandidateElement(Document& do
15241524
}
15251525

15261526
Ref element = HTMLMaybeFormAssociatedCustomElement::create(name, document);
1527-
1528-
if (!registry && document.usesNullCustomElementRegistry())
1529-
element->setUsesNullCustomElementRegistry();
1530-
15311527
element->setIsCustomElementUpgradeCandidate();
15321528

15331529
return element;
@@ -1597,12 +1593,14 @@ ExceptionOr<Ref<Element>> Document::createElementForBindings(const AtomString& n
15971593

15981594
if (result.hasException())
15991595
return result;
1596+
Ref element = result.releaseReturnValue();
16001597
if (registry && registry->isScoped()) [[unlikely]] {
1601-
Ref element = result.releaseReturnValue();
16021598
CustomElementRegistry::addToScopedCustomElementRegistryMap(element, *registry);
16031599
return element;
16041600
}
1605-
return result;
1601+
if (!registry && usesNullCustomElementRegistry())
1602+
element->setUsesNullCustomElementRegistry();
1603+
return element;
16061604
}
16071605

16081606
ExceptionOr<Ref<Element>> Document::createElementForBindings(const AtomString& name)
@@ -2035,12 +2033,14 @@ ExceptionOr<Ref<Element>> Document::createElementNS(const AtomString& namespaceU
20352033

20362034
if (result.hasException())
20372035
return result;
2036+
Ref element = result.releaseReturnValue();
20382037
if (registry && registry->isScoped()) [[unlikely]] {
2039-
Ref element = result.releaseReturnValue();
20402038
CustomElementRegistry::addToScopedCustomElementRegistryMap(element, *registry);
20412039
return element;
20422040
}
2043-
return result;
2041+
if (!registry && usesNullCustomElementRegistry())
2042+
element->setUsesNullCustomElementRegistry();
2043+
return element;
20442044
}
20452045

20462046
ExceptionOr<Ref<Element>> Document::createElementNS(const AtomString& namespaceURI, const AtomString& qualifiedName)

0 commit comments

Comments
 (0)