Title: [285740] trunk
Revision
285740
Author
[email protected]
Date
2021-11-12 14:11:13 -0800 (Fri, 12 Nov 2021)

Log Message

Implement custom element definition's *disable shadow* flag
https://bugs.webkit.org/show_bug.cgi?id=233023

Patch by Alexey Shvayka <[email protected]> on 2021-11-12
Reviewed by Geoffrey Garen.

LayoutTests/imported/w3c:

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:

Source/WebCore:

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):

Modified Paths

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)
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to