Diff
Modified: trunk/LayoutTests/imported/w3c/ChangeLog (285739 => 285740)
--- trunk/LayoutTests/imported/w3c/ChangeLog 2021-11-12 21:23:59 UTC (rev 285739)
+++ trunk/LayoutTests/imported/w3c/ChangeLog 2021-11-12 22:11:13 UTC (rev 285740)
@@ -1,3 +1,16 @@
+2021-11-12 Alexey Shvayka <[email protected]>
+
+ Implement custom element definition's *disable shadow* flag
+ https://bugs.webkit.org/show_bug.cgi?id=233023
+
+ Reviewed by Geoffrey Garen.
+
+ Rebaseline WPT tests now that more checks are passing or failing later on.
+
+ * web-platform-tests/custom-elements/CustomElementRegistry-expected.txt:
+ * web-platform-tests/custom-elements/upgrading-expected.txt:
+ * web-platform-tests/shadow-dom/Element-interface-attachShadow-custom-element-expected.txt:
+
2021-11-12 Commit Queue <[email protected]>
Unreviewed, reverting r285639.
Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/custom-elements/CustomElementRegistry-expected.txt (285739 => 285740)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/custom-elements/CustomElementRegistry-expected.txt 2021-11-12 21:23:59 UTC (rev 285739)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/custom-elements/CustomElementRegistry-expected.txt 2021-11-12 22:11:13 UTC (rev 285740)
@@ -9,26 +9,26 @@
PASS customElements.define must check IsConstructor on the constructor before checking the element definition is running flag
PASS customElements.define must validate the custom element name before checking the element definition is running flag
PASS customElements.define unset the element definition is running flag before upgrading custom elements
-FAIL customElements.define must not throw when defining another custom element in a different global object during Get(constructor, "prototype") assert_array_equals: customElements.define must get "prototype", "disabledFeatures", and "formAssociated" on the constructor lengths differ, expected array ["prototype", "disabledFeatures", "formAssociated"] length 3, got ["prototype"] length 1
+FAIL customElements.define must not throw when defining another custom element in a different global object during Get(constructor, "prototype") assert_array_equals: customElements.define must get "prototype", "disabledFeatures", and "formAssociated" on the constructor lengths differ, expected array ["prototype", "disabledFeatures", "formAssociated"] length 3, got ["prototype", "disabledFeatures"] length 2
PASS Custom Elements: CustomElementRegistry interface
-FAIL customElements.define must get "prototype", "disabledFeatures", and "formAssociated" property of the constructor assert_array_equals: lengths differ, expected array ["prototype", "disabledFeatures", "formAssociated"] length 3, got ["prototype"] length 1
+FAIL customElements.define must get "prototype", "disabledFeatures", and "formAssociated" property of the constructor assert_array_equals: lengths differ, expected array ["prototype", "disabledFeatures", "formAssociated"] length 3, got ["prototype", "disabledFeatures"] length 2
PASS customElements.define must rethrow an exception thrown while getting "prototype" property of the constructor
PASS customElements.define must throw when "prototype" property of the constructor is not an object
PASS customElements.define must get callbacks of the constructor prototype
PASS customElements.define must rethrow an exception thrown while getting callbacks on the constructor prototype
PASS customElements.define must rethrow an exception thrown while converting a callback value to Function callback type
-FAIL customElements.define must get "observedAttributes" property on the constructor prototype when "attributeChangedCallback" is present assert_array_equals: lengths differ, expected array [0, "prototype", 5, "observedAttributes", 6, "disabledFeatures", 7, "formAssociated"] length 8, got [0, "prototype", 5, "observedAttributes"] length 4
+FAIL customElements.define must get "observedAttributes" property on the constructor prototype when "attributeChangedCallback" is present assert_array_equals: lengths differ, expected array [0, "prototype", 5, "observedAttributes", 6, "disabledFeatures", 7, "formAssociated"] length 8, got [0, "prototype", 5, "observedAttributes", 6, "disabledFeatures"] length 6
PASS customElements.define must rethrow an exception thrown while getting observedAttributes on the constructor prototype
PASS customElements.define must rethrow an exception thrown while converting the value of observedAttributes to sequence<DOMString>
PASS customElements.define must rethrow an exception thrown while iterating over observedAttributes to sequence<DOMString>
PASS customElements.define must rethrow an exception thrown while retrieving Symbol.iterator on observedAttributes
PASS customElements.define must not throw even if "observedAttributes" fails to convert if "attributeChangedCallback" is not defined
-FAIL customElements.define must rethrow an exception thrown while getting disabledFeatures on the constructor prototype assert_throws_exactly: function "() => customElements.define('element-with-throwing-disabled-features', proxy)" did not throw
-FAIL customElements.define must rethrow an exception thrown while converting the value of disabledFeatures to sequence<DOMString> assert_throws_js: function "() => customElements.define('element-with-invalid-disabled-features', proxy)" did not throw
-FAIL customElements.define must rethrow an exception thrown while iterating over disabledFeatures to sequence<DOMString> assert_throws_exactly: function "() => customElements.define('element-with-generator-disabled-features', constructor)" did not throw
-FAIL customElements.define must rethrow an exception thrown while retrieving Symbol.iterator on disabledFeatures assert_throws_js: function "() => customElements.define('element-with-disabled-features-with-uncallable-iterator', constructor)" did not throw
+PASS customElements.define must rethrow an exception thrown while getting disabledFeatures on the constructor prototype
+PASS customElements.define must rethrow an exception thrown while converting the value of disabledFeatures to sequence<DOMString>
+PASS customElements.define must rethrow an exception thrown while iterating over disabledFeatures to sequence<DOMString>
+PASS customElements.define must rethrow an exception thrown while retrieving Symbol.iterator on disabledFeatures
FAIL customElements.define must rethrow an exception thrown while getting formAssociated on the constructor prototype assert_throws_exactly: function "() => customElements.define('element-with-throwing-form-associated', proxy)" did not throw
-FAIL customElements.define must get four additional callbacks on the prototype if formAssociated is converted to true assert_array_equals: customElements.define must get "prototype", "disabledFeatures", and "formAssociated" on the constructor lengths differ, expected array ["prototype", "disabledFeatures", "formAssociated"] length 3, got ["prototype"] length 1
+FAIL customElements.define must get four additional callbacks on the prototype if formAssociated is converted to true assert_array_equals: customElements.define must get "prototype", "disabledFeatures", and "formAssociated" on the constructor lengths differ, expected array ["prototype", "disabledFeatures", "formAssociated"] length 3, got ["prototype", "disabledFeatures"] length 2
FAIL customElements.define must rethrow an exception thrown while getting additional formAssociated callbacks on the constructor prototype assert_throws_exactly: function "() => customElements.define('element-with-throwing-callback-2', proxy)" did not throw
PASS customElements.define must define an instantiatable custom element
PASS customElements.define must upgrade elements in the shadow-including tree order
Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/custom-elements/upgrading-expected.txt (285739 => 285740)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/custom-elements/upgrading-expected.txt 2021-11-12 21:23:59 UTC (rev 285739)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/custom-elements/upgrading-expected.txt 2021-11-12 22:11:13 UTC (rev 285740)
@@ -1,3 +1,4 @@
+CONSOLE MESSAGE: NotSupportedError: Failed to upgrade an element with shadow root: the custom element definition disallows shadow roots.
PASS Creating an element in the document of the template elements must not enqueue a custom element upgrade reaction because the document does not have a browsing context
PASS Creating an element in the document of the template elements and inserting into the document must not enqueue a custom element upgrade reaction
@@ -24,7 +25,7 @@
PASS "define" in the document of an iframe must enqueue a custom element upgrade reaction on a connected unresolved custom element
PASS Adopting (and leaving disconnceted) an unresolved custom element into the document of an iframe must not enqueue a custom element upgrade reaction
PASS Adopting and inserting an unresolved custom element into the document of an iframe must enqueue a custom element upgrade reaction
-FAIL If definition's disable shadow is true and element's shadow root is non-null, then throw a "NotSupportedError" DOMException. assert_false: Upgrading should fail. expected false got true
+PASS If definition's disable shadow is true and element's shadow root is non-null, then throw a "NotSupportedError" DOMException.
PASS Infinite constructor recursion with upgrade(this) should not be possible
PASS Infinite constructor recursion with appendChild should not be possible
Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/Element-interface-attachShadow-custom-element-expected.txt (285739 => 285740)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/Element-interface-attachShadow-custom-element-expected.txt 2021-11-12 21:23:59 UTC (rev 285739)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/Element-interface-attachShadow-custom-element-expected.txt 2021-11-12 22:11:13 UTC (rev 285740)
@@ -1,9 +1,7 @@
PASS Element.attachShadow must create an instance of ShadowRoot for autonomous custom elements
PASS Element.attachShadow must create an instance of ShadowRoot for undefined autonomous custom elements
-FAIL Element.attachShadow for an autonomous custom element with disabledFeatures=["shadow"] should throw a NotSupportedError assert_throws_dom: Definition, not a host function "() => {
- document.createElement('shadow-disabled-element').attachShadow({mode: 'closed'});
- }" did not throw
+PASS Element.attachShadow for an autonomous custom element with disabledFeatures=["shadow"] should throw a NotSupportedError
FAIL Element.attachShadow for a customized built-in element with disabledFeatures=["shadow"] should throw a NotSupportedError assert_throws_dom: Definition, not a host function "() => {
h2.attachShadow({mode: 'closed'});
}" did not throw
Modified: trunk/Source/WebCore/ChangeLog (285739 => 285740)
--- trunk/Source/WebCore/ChangeLog 2021-11-12 21:23:59 UTC (rev 285739)
+++ trunk/Source/WebCore/ChangeLog 2021-11-12 22:11:13 UTC (rev 285740)
@@ -1,3 +1,38 @@
+2021-11-12 Alexey Shvayka <[email protected]>
+
+ Implement custom element definition's *disable shadow* flag
+ https://bugs.webkit.org/show_bug.cgi?id=233023
+
+ Reviewed by Geoffrey Garen.
+
+ This change implements *disable shadow* flag that precludes attachShadow() [1]
+ as well as upgrading an element with already attached shadow root [2].
+
+ Aligns WebKit with Blink and Gecko.
+ Preserves the fast path for non-custom elements.
+
+ [1] https://dom.spec.whatwg.org/#ref-for-concept-custom-element-definition-disable-shadow
+ [2] https://html.spec.whatwg.org/multipage/custom-elements.html#upgrades:concept-custom-element-definition-disable-shadow
+
+ Tests: imported/w3c/web-platform-tests/custom-elements/CustomElementRegistry.html
+ imported/w3c/web-platform-tests/custom-elements/upgrading.html
+ imported/w3c/web-platform-tests/shadow-dom/Element-interface-attachShadow-custom-element.html
+
+ * bindings/js/JSCustomElementInterface.cpp:
+ (WebCore::JSCustomElementInterface::JSCustomElementInterface):
+ (WebCore::JSCustomElementInterface::upgradeElement):
+ * bindings/js/JSCustomElementInterface.h:
+ (WebCore::JSCustomElementInterface::disableShadow):
+ (WebCore::JSCustomElementInterface::isShadowDisabled const):
+ * bindings/js/JSCustomElementRegistryCustom.cpp:
+ (WebCore::JSCustomElementRegistry::define):
+ * dom/CustomElementRegistry.cpp:
+ (WebCore::CustomElementRegistry::addElementDefinition):
+ * dom/CustomElementRegistry.h:
+ (WebCore::CustomElementRegistry::isShadowDisabled const):
+ * dom/Element.cpp:
+ (WebCore::canAttachAuthorShadowRoot):
+
2021-11-12 Commit Queue <[email protected]>
Unreviewed, reverting r285639.
Modified: trunk/Source/WebCore/bindings/js/JSCustomElementInterface.cpp (285739 => 285740)
--- trunk/Source/WebCore/bindings/js/JSCustomElementInterface.cpp 2021-11-12 21:23:59 UTC (rev 285739)
+++ trunk/Source/WebCore/bindings/js/JSCustomElementInterface.cpp 2021-11-12 22:11:13 UTC (rev 285740)
@@ -50,6 +50,7 @@
, m_name(name)
, m_constructor(constructor)
, m_isolatedWorld(globalObject->world())
+ , m_isShadowDisabled(false)
{
}
@@ -207,6 +208,12 @@
m_constructionStack.append(&element);
+ if (m_isShadowDisabled && element.shadowRoot()) {
+ element.clearReactionQueueFromFailedCustomElement();
+ reportException(lexicalGlobalObject, createDOMException(lexicalGlobalObject, NotSupportedError, "Failed to upgrade an element with shadow root: the custom element definition disallows shadow roots."));
+ return;
+ }
+
MarkedArgumentBuffer args;
ASSERT(!args.hasOverflowed());
JSExecState::instrumentFunction(context, constructData);
Modified: trunk/Source/WebCore/bindings/js/JSCustomElementInterface.h (285739 => 285740)
--- trunk/Source/WebCore/bindings/js/JSCustomElementInterface.h 2021-11-12 21:23:59 UTC (rev 285739)
+++ trunk/Source/WebCore/bindings/js/JSCustomElementInterface.h 2021-11-12 22:11:13 UTC (rev 285740)
@@ -78,6 +78,9 @@
bool observesAttribute(const AtomString& name) const { return m_observedAttributes.contains(name); }
void invokeAttributeChangedCallback(Element&, const QualifiedName&, const AtomString& oldValue, const AtomString& newValue);
+ void disableShadow() { m_isShadowDisabled = true; }
+ bool isShadowDisabled() const { return m_isShadowDisabled; }
+
ScriptExecutionContext* scriptExecutionContext() const { return ContextDestructionObserver::scriptExecutionContext(); }
JSC::JSObject* constructor() { return m_constructor.get(); }
@@ -105,6 +108,7 @@
Ref<DOMWrapperWorld> m_isolatedWorld;
Vector<RefPtr<Element>, 1> m_constructionStack;
HashSet<AtomString> m_observedAttributes;
+ bool m_isShadowDisabled : 1;
};
} // namespace WebCore
Modified: trunk/Source/WebCore/bindings/js/JSCustomElementRegistryCustom.cpp (285739 => 285740)
--- trunk/Source/WebCore/bindings/js/JSCustomElementRegistryCustom.cpp 2021-11-12 21:23:59 UTC (rev 285739)
+++ trunk/Source/WebCore/bindings/js/JSCustomElementRegistryCustom.cpp 2021-11-12 22:11:13 UTC (rev 285740)
@@ -156,6 +156,15 @@
}
}
+ auto disabledFeaturesValue = constructor->get(&lexicalGlobalObject, Identifier::fromString(vm, "disabledFeatures"));
+ RETURN_IF_EXCEPTION(scope, { });
+ if (!disabledFeaturesValue.isUndefined()) {
+ auto disabledFeatures = convert<IDLSequence<IDLDOMString>>(lexicalGlobalObject, disabledFeaturesValue);
+ RETURN_IF_EXCEPTION(scope, { });
+ if (disabledFeatures.contains("shadow"_s))
+ elementInterface->disableShadow();
+ }
+
auto addToGlobalObjectWithPrivateName = [&] (JSObject* objectToAdd) {
if (objectToAdd) {
PrivateName uniquePrivateName;
Modified: trunk/Source/WebCore/dom/CustomElementRegistry.cpp (285739 => 285740)
--- trunk/Source/WebCore/dom/CustomElementRegistry.cpp 2021-11-12 21:23:59 UTC (rev 285739)
+++ trunk/Source/WebCore/dom/CustomElementRegistry.cpp 2021-11-12 22:11:13 UTC (rev 285740)
@@ -73,6 +73,9 @@
m_constructorMap.add(elementInterface->constructor(), elementInterface.ptr());
m_nameMap.add(localName, elementInterface.copyRef());
+ if (elementInterface->isShadowDisabled())
+ m_disabledShadowSet.add(localName);
+
if (auto* document = m_window.document())
enqueueUpgradeInShadowIncludingTreeOrder(*document, elementInterface.get());
Modified: trunk/Source/WebCore/dom/CustomElementRegistry.h (285739 => 285740)
--- trunk/Source/WebCore/dom/CustomElementRegistry.h 2021-11-12 21:23:59 UTC (rev 285739)
+++ trunk/Source/WebCore/dom/CustomElementRegistry.h 2021-11-12 22:11:13 UTC (rev 285740)
@@ -28,6 +28,7 @@
#include "ContextDestructionObserver.h"
#include "QualifiedName.h"
#include <wtf/HashMap.h>
+#include <wtf/HashSet.h>
#include <wtf/text/AtomString.h>
#include <wtf/text/AtomStringHash.h>
@@ -67,6 +68,7 @@
void upgrade(Node& root);
HashMap<AtomString, Ref<DeferredPromise>>& promiseMap() { return m_promiseMap; }
+ bool isShadowDisabled(const AtomString& name) const { return m_disabledShadowSet.contains(name); }
private:
CustomElementRegistry(DOMWindow&, ScriptExecutionContext*);
@@ -75,6 +77,7 @@
HashMap<AtomString, Ref<JSCustomElementInterface>> m_nameMap;
HashMap<const JSC::JSObject*, JSCustomElementInterface*> m_constructorMap;
HashMap<AtomString, Ref<DeferredPromise>> m_promiseMap;
+ HashSet<AtomString> m_disabledShadowSet;
bool m_elementDefinitionIsRunning { false };
Modified: trunk/Source/WebCore/dom/Element.cpp (285739 => 285740)
--- trunk/Source/WebCore/dom/Element.cpp 2021-11-12 21:23:59 UTC (rev 285739)
+++ trunk/Source/WebCore/dom/Element.cpp 2021-11-12 22:11:13 UTC (rev 285740)
@@ -2485,7 +2485,20 @@
return false;
const auto& localName = element.localName();
- return tagNames.get().contains(localName) || Document::validateCustomElementName(localName) == CustomElementNameValidationStatus::Valid;
+ if (tagNames.get().contains(localName))
+ return true;
+
+ if (Document::validateCustomElementName(localName) == CustomElementNameValidationStatus::Valid) {
+ if (auto* window = element.document().domWindow()) {
+ auto* registry = window->customElementRegistry();
+ if (registry && registry->isShadowDisabled(localName))
+ return false;
+ }
+
+ return true;
+ }
+
+ return false;
}
ExceptionOr<ShadowRoot&> Element::attachShadow(const ShadowRootInit& init)