Title: [294141] trunk
Revision
294141
Author
r...@igalia.com
Date
2022-05-12 22:04:23 -0700 (Thu, 12 May 2022)

Log Message

ARIA reflection for FrozenArray<Element> attributes
https://bugs.webkit.org/show_bug.cgi?id=239853
<rdar://problem/92797836>

Reviewed by Chris Dumez.

LayoutTests/imported/w3c:

Update expectations with the new PASS lines.

* web-platform-tests/dom/nodes/aria-element-reflection.tentative-expected.txt:

Source/WebCore:

Implement ARIA reflection for attributes that refer to a list of Elements:
aria-controls, aria-describedby, aria-details, aria-flowto,
aria-labelledby and aria-owns.
For the properties names this patch uses "Elements" suffix:
ariaControlsElements, ariaDescribedByElements, ariaDescribedByElements,
ariaFlowToElements, ariaLabelledByElements, ariaOwnsElements
this matches Chromium implementation and AOM explainer, but not AOM spec:
https://github.com/w3c/aria/issues/1732

* accessibility/AriaAttributes.idl: Add the new properties under
AriaReflectionForElementReferencesEnabled runtime flag.
* bindings/scripts/CodeGenerator.pm:
(GetterExpression): Add function for FrozenArray<Element> properties.
(SetterExpression): Ditto.
* bindings/scripts/test/JS/JSTestObj.cpp: Add tests.
(WebCore::JSTestObjDOMConstructor::construct):
(WebCore::jsTestObj_reflectedElementsArrayAttrGetter):
(WebCore::JSC_DEFINE_CUSTOM_GETTER):
(WebCore::setJSTestObj_reflectedElementsArrayAttrSetter):
(WebCore::JSC_DEFINE_CUSTOM_SETTER):
* bindings/scripts/test/TestObj.idl: Add example attribute.
* dom/Element.cpp:
(WebCore::isElementsArrayReflectionAttribute): New utility method to
identify the attributes that refer to a list of Elements.
(WebCore::Element::attributeChanged): Include check for elements
array.
(WebCore::Element::getElementsArrayAttribute const): Implement getter.
(WebCore::Element::setElementsArrayAttribute): Implement setter.
* dom/Element.h: Remove FIXME in ExplicitlySetAttrElementsMap as now
it stores more than one element. Add headers for getter and setter.

LayoutTests:

Update test so it identifies the FrozenArray<Element> attributes.

* accessibility/ARIA-reflection-expected.txt:
* accessibility/ARIA-reflection.html:

Modified Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (294140 => 294141)


--- trunk/LayoutTests/ChangeLog	2022-05-13 03:38:28 UTC (rev 294140)
+++ trunk/LayoutTests/ChangeLog	2022-05-13 05:04:23 UTC (rev 294141)
@@ -1,3 +1,16 @@
+2022-05-12  Manuel Rego Casasnovas  <r...@igalia.com>
+
+        ARIA reflection for FrozenArray<Element> attributes
+        https://bugs.webkit.org/show_bug.cgi?id=239853
+        <rdar://problem/92797836>
+
+        Reviewed by Chris Dumez.
+
+        Update test so it identifies the FrozenArray<Element> attributes.
+
+        * accessibility/ARIA-reflection-expected.txt:
+        * accessibility/ARIA-reflection.html:
+
 2022-05-12  Diego Pino Garcia  <dp...@igalia.com>
 
         [GTK] Unreviewed test gardening, update tests expected to fail but passed

Modified: trunk/LayoutTests/accessibility/ARIA-reflection-expected.txt (294140 => 294141)


--- trunk/LayoutTests/accessibility/ARIA-reflection-expected.txt	2022-05-13 03:38:28 UTC (rev 294140)
+++ trunk/LayoutTests/accessibility/ARIA-reflection-expected.txt	2022-05-13 05:04:23 UTC (rev 294141)
@@ -67,6 +67,13 @@
 element.setAttribute("aria-colspan", otherData);
 PASS element[currentProperty] is otherDataProperty
 
+Test ariaControlsElements < - > aria-controls
+PASS element[currentProperty] is null
+PASS element.getAttribute(currentAttribute) is null
+PASS element.getAttribute(currentAttribute) is dataAttribute
+element.setAttribute("aria-controls", otherData);
+PASS element[currentProperty] is otherDataProperty
+
 Test ariaCurrent < - > aria-current
 PASS element[currentProperty] is null
 PASS element.getAttribute(currentAttribute) is null
@@ -74,6 +81,20 @@
 element.setAttribute("aria-current", otherData);
 PASS element[currentProperty] is otherDataProperty
 
+Test ariaDescribedByElements < - > aria-describedby
+PASS element[currentProperty] is null
+PASS element.getAttribute(currentAttribute) is null
+PASS element.getAttribute(currentAttribute) is dataAttribute
+element.setAttribute("aria-describedby", otherData);
+PASS element[currentProperty] is otherDataProperty
+
+Test ariaDetailsElements < - > aria-details
+PASS element[currentProperty] is null
+PASS element.getAttribute(currentAttribute) is null
+PASS element.getAttribute(currentAttribute) is dataAttribute
+element.setAttribute("aria-details", otherData);
+PASS element[currentProperty] is otherDataProperty
+
 Test ariaDisabled < - > aria-disabled
 PASS element[currentProperty] is null
 PASS element.getAttribute(currentAttribute) is null
@@ -95,6 +116,13 @@
 element.setAttribute("aria-expanded", otherData);
 PASS element[currentProperty] is otherDataProperty
 
+Test ariaFlowToElements < - > aria-flowto
+PASS element[currentProperty] is null
+PASS element.getAttribute(currentAttribute) is null
+PASS element.getAttribute(currentAttribute) is dataAttribute
+element.setAttribute("aria-flowto", otherData);
+PASS element[currentProperty] is otherDataProperty
+
 Test ariaHasPopup < - > aria-haspopup
 PASS element[currentProperty] is null
 PASS element.getAttribute(currentAttribute) is null
@@ -130,6 +158,13 @@
 element.setAttribute("aria-label", otherData);
 PASS element[currentProperty] is otherDataProperty
 
+Test ariaLabelledByElements < - > aria-labelledby
+PASS element[currentProperty] is null
+PASS element.getAttribute(currentAttribute) is null
+PASS element.getAttribute(currentAttribute) is dataAttribute
+element.setAttribute("aria-labelledby", otherData);
+PASS element[currentProperty] is otherDataProperty
+
 Test ariaLevel < - > aria-level
 PASS element[currentProperty] is null
 PASS element.getAttribute(currentAttribute) is null
@@ -172,6 +207,13 @@
 element.setAttribute("aria-orientation", otherData);
 PASS element[currentProperty] is otherDataProperty
 
+Test ariaOwnsElements < - > aria-owns
+PASS element[currentProperty] is null
+PASS element.getAttribute(currentAttribute) is null
+PASS element.getAttribute(currentAttribute) is dataAttribute
+element.setAttribute("aria-owns", otherData);
+PASS element[currentProperty] is otherDataProperty
+
 Test ariaPlaceholder < - > aria-placeholder
 PASS element[currentProperty] is null
 PASS element.getAttribute(currentAttribute) is null
@@ -292,7 +334,7 @@
 PASS element[currentProperty] is otherDataProperty
 
 
-PASS count is 40
+PASS count is 46
 PASS successfullyParsed is true
 
 TEST COMPLETE

Modified: trunk/LayoutTests/accessibility/ARIA-reflection.html (294140 => 294141)


--- trunk/LayoutTests/accessibility/ARIA-reflection.html	2022-05-13 03:38:28 UTC (rev 294140)
+++ trunk/LayoutTests/accessibility/ARIA-reflection.html	2022-05-13 05:04:23 UTC (rev 294141)
@@ -7,6 +7,7 @@
 <div id="test" data="" data">
   <div id="firstChild"></div>
   <div id="secondChild"></div>
+  <div id="thirdChild"></div>
 </div>
 
 <p id="description"></p>
@@ -31,12 +32,59 @@
         return false;
     }
 
+    function isElementsArrayReflectionProperty(property) {
+        switch (property) {
+            case "ariaControlsElements":
+            case "ariaDescribedByElements":
+            case "ariaDetailsElements":
+            case "ariaFlowToElements":
+            case "ariaLabelledByElements":
+            case "ariaOwnsElements":
+                return true;
+        }
+        return false;
+    }
+
     function convertPropertyToAttribute(property) {
         if (isElementReflectionProperty(property))
           property = property.replace("Element", "");
+        else if (isElementsArrayReflectionProperty(property))
+          property = property.replace("Elements", "");
         return property.replace("aria", "aria-").toLowerCase();
     }
 
+    function getDataProperty(property) {
+        if (isElementReflectionProperty(property))
+          return firstChild;
+        else if (isElementsArrayReflectionProperty(property))
+          return [firstChild, secondChild];
+        return data;
+    }
+
+    function getDataAttribute(property) {
+        if (isElementReflectionProperty(property))
+          return firstChild.id;
+        else if (isElementsArrayReflectionProperty(property))
+          return firstChild.id + " " + secondChild.id;
+        return data;
+    }
+
+    function getOtherDataProperty(property) {
+        if (isElementReflectionProperty(property))
+          return secondChild;
+        else if (isElementsArrayReflectionProperty(property))
+          return [thirdChild];
+        return otherData;
+    }
+
+    function getOtherDataAttribute(property) {
+        if (isElementReflectionProperty(property))
+          return secondChild.id;
+        else if (isElementsArrayReflectionProperty(property))
+          return thirdChild.id;
+        return otherData;
+    }
+
     function testElement() {
         currentAttribute = convertPropertyToAttribute(currentProperty);
 
@@ -45,14 +93,14 @@
         shouldBeNull("element.getAttribute(currentAttribute)");
 
         // Set the property value
-        dataProperty = isElementReflectionProperty(currentProperty) ? firstChild : data;
-        dataAttribute = isElementReflectionProperty(currentProperty) ? firstChild.id : data;
+        dataProperty = getDataProperty(currentProperty);
+        dataAttribute = getDataAttribute(currentProperty);
         element[currentProperty] = dataProperty;
         shouldBe("element.getAttribute(currentAttribute)", "dataAttribute");
 
         // Set the attribute value
-        otherDataProperty = isElementReflectionProperty(currentProperty) ? secondChild : otherData;
-        otherDataAttribute = isElementReflectionProperty(currentProperty) ? secondChild.id : otherData;
+        otherDataProperty = getOtherDataProperty(currentProperty);
+        otherDataAttribute = getOtherDataAttribute(currentProperty);
         debug("element.setAttribute(\"" + currentAttribute + "\", otherData);");
         element.setAttribute(currentAttribute, otherDataAttribute);
         shouldBe("element[currentProperty]", "otherDataProperty");
@@ -78,7 +126,7 @@
         }
 
         debug("\n");
-        shouldBe("count", "40");
+        shouldBe("count", "46");
 
     } else {
         testFailed("Could not load accessibility controller");

Modified: trunk/LayoutTests/imported/w3c/ChangeLog (294140 => 294141)


--- trunk/LayoutTests/imported/w3c/ChangeLog	2022-05-13 03:38:28 UTC (rev 294140)
+++ trunk/LayoutTests/imported/w3c/ChangeLog	2022-05-13 05:04:23 UTC (rev 294141)
@@ -1,3 +1,15 @@
+2022-05-12  Manuel Rego Casasnovas  <r...@igalia.com>
+
+        ARIA reflection for FrozenArray<Element> attributes
+        https://bugs.webkit.org/show_bug.cgi?id=239853
+        <rdar://problem/92797836>
+
+        Reviewed by Chris Dumez.
+
+        Update expectations with the new PASS lines.
+
+        * web-platform-tests/dom/nodes/aria-element-reflection.tentative-expected.txt:
+
 2022-05-12  Oriol Brufau  <obru...@igalia.com>
 
         [cssom] Serialize computed '-webkit-text-combine: none'

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/dom/nodes/aria-element-reflection.tentative-expected.txt (294140 => 294141)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/dom/nodes/aria-element-reflection.tentative-expected.txt	2022-05-13 03:38:28 UTC (rev 294140)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/dom/nodes/aria-element-reflection.tentative-expected.txt	2022-05-13 05:04:23 UTC (rev 294141)
@@ -36,12 +36,7 @@
 
 Light DOM text
 
-Delicious
-Nutritious
-
 Misspelling
-Wonderful
-Fantastic
 
 
 PASS aria-activedescendant element reflection
@@ -49,20 +44,20 @@
 PASS Setting the IDL attribute to an element which is not the first element in DOM order with its ID causes the content attribute to be an empty string
 PASS Setting an element reference that crosses into a shadow tree is disallowed, but setting one that is in a shadow inclusive ancestor is allowed.
 PASS aria-errormessage
-FAIL aria-details assert_array_equals: value is undefined, expected array
+PASS aria-details
 PASS Deleting a reflected element should return null for the IDL attribute and cause the content attribute to become stale.
 PASS Changing the ID of an element causes the content attribute to become out of sync.
 PASS Reparenting an element into a descendant shadow scope hides the element reference.
 PASS Reparenting referenced element cannot cause retargeting of reference.
 PASS Element reference set in invalid scope remains intact throughout move to valid scope.
-FAIL aria-labelledby. assert_array_equals: parsed content attribute sets element references. value is undefined, expected array
-FAIL aria-controls. assert_array_equals: value is undefined, expected array
-FAIL aria-describedby. assert_array_equals: value is undefined, expected array
-FAIL aria-flowto. assert_array_equals: value is undefined, expected array
-FAIL aria-owns. assert_array_equals: value is undefined, expected array
-FAIL shadow DOM behaviour for FrozenArray element reflection. assert_array_equals: value is undefined, expected array
-FAIL Moving explicitly set elements across shadow DOM boundaries. assert_equals: expected (string) "buttonDescription1 buttonDescription2" but got (object) null
-FAIL Moving explicitly set elements around within the same scope, and removing from the DOM. assert_array_equals: aria-labeled by is supported by IDL getter. value is undefined, expected array
+PASS aria-labelledby.
+PASS aria-controls.
+PASS aria-describedby.
+PASS aria-flowto.
+PASS aria-owns.
+PASS shadow DOM behaviour for FrozenArray element reflection.
+PASS Moving explicitly set elements across shadow DOM boundaries.
+PASS Moving explicitly set elements around within the same scope, and removing from the DOM.
 PASS Reparenting.
 PASS Attaching element reference before it's inserted into the DOM.
 PASS Cross-document references and moves.

Modified: trunk/Source/WebCore/ChangeLog (294140 => 294141)


--- trunk/Source/WebCore/ChangeLog	2022-05-13 03:38:28 UTC (rev 294140)
+++ trunk/Source/WebCore/ChangeLog	2022-05-13 05:04:23 UTC (rev 294141)
@@ -1,3 +1,42 @@
+2022-05-12  Manuel Rego Casasnovas  <r...@igalia.com>
+
+        ARIA reflection for FrozenArray<Element> attributes
+        https://bugs.webkit.org/show_bug.cgi?id=239853
+        <rdar://problem/92797836>
+
+        Reviewed by Chris Dumez.
+
+        Implement ARIA reflection for attributes that refer to a list of Elements:
+        aria-controls, aria-describedby, aria-details, aria-flowto,
+        aria-labelledby and aria-owns.
+        For the properties names this patch uses "Elements" suffix:
+        ariaControlsElements, ariaDescribedByElements, ariaDescribedByElements,
+        ariaFlowToElements, ariaLabelledByElements, ariaOwnsElements
+        this matches Chromium implementation and AOM explainer, but not AOM spec:
+        https://github.com/w3c/aria/issues/1732
+
+        * accessibility/AriaAttributes.idl: Add the new properties under
+        AriaReflectionForElementReferencesEnabled runtime flag.
+        * bindings/scripts/CodeGenerator.pm:
+        (GetterExpression): Add function for FrozenArray<Element> properties.
+        (SetterExpression): Ditto.
+        * bindings/scripts/test/JS/JSTestObj.cpp: Add tests.
+        (WebCore::JSTestObjDOMConstructor::construct):
+        (WebCore::jsTestObj_reflectedElementsArrayAttrGetter):
+        (WebCore::JSC_DEFINE_CUSTOM_GETTER):
+        (WebCore::setJSTestObj_reflectedElementsArrayAttrSetter):
+        (WebCore::JSC_DEFINE_CUSTOM_SETTER):
+        * bindings/scripts/test/TestObj.idl: Add example attribute.
+        * dom/Element.cpp:
+        (WebCore::isElementsArrayReflectionAttribute): New utility method to
+        identify the attributes that refer to a list of Elements.
+        (WebCore::Element::attributeChanged): Include check for elements
+        array.
+        (WebCore::Element::getElementsArrayAttribute const): Implement getter.
+        (WebCore::Element::setElementsArrayAttribute): Implement setter.
+        * dom/Element.h: Remove FIXME in ExplicitlySetAttrElementsMap as now
+        it stores more than one element. Add headers for getter and setter.
+
 2022-05-12  Matt Woodrow  <mattwood...@apple.com>
 
         Quirk Flightaware.com to use the older number serialization path.

Modified: trunk/Source/WebCore/accessibility/AriaAttributes.idl (294140 => 294141)


--- trunk/Source/WebCore/accessibility/AriaAttributes.idl	2022-05-13 03:38:28 UTC (rev 294140)
+++ trunk/Source/WebCore/accessibility/AriaAttributes.idl	2022-05-13 05:04:23 UTC (rev 294141)
@@ -33,15 +33,20 @@
     [Reflect=aria_colcount]         attribute DOMString? ariaColCount;
     [Reflect=aria_colindex]         attribute DOMString? ariaColIndex;
     [Reflect=aria_colspan]          attribute DOMString? ariaColSpan;
+    [Reflect=aria_controls, EnabledBySetting=AriaReflectionForElementReferencesEnabled] attribute FrozenArray<Element>? ariaControlsElements;
     [Reflect=aria_current]          attribute DOMString? ariaCurrent;
+    [Reflect=aria_describedby, EnabledBySetting=AriaReflectionForElementReferencesEnabled] attribute FrozenArray<Element>? ariaDescribedByElements;
+    [Reflect=aria_details, EnabledBySetting=AriaReflectionForElementReferencesEnabled] attribute FrozenArray<Element>? ariaDetailsElements;
     [Reflect=aria_disabled]         attribute DOMString? ariaDisabled;
     [Reflect=aria_errormessage, EnabledBySetting=AriaReflectionForElementReferencesEnabled] attribute Element? ariaErrorMessageElement;
     [Reflect=aria_expanded]         attribute DOMString? ariaExpanded;
+    [Reflect=aria_flowto, EnabledBySetting=AriaReflectionForElementReferencesEnabled] attribute FrozenArray<Element>? ariaFlowToElements;
     [Reflect=aria_haspopup]         attribute DOMString? ariaHasPopup;
     [Reflect=aria_hidden]           attribute DOMString? ariaHidden;
     [Reflect=aria_invalid]          attribute DOMString? ariaInvalid;
     [Reflect=aria_keyshortcuts]     attribute DOMString? ariaKeyShortcuts;
     [Reflect=aria_label]            attribute DOMString? ariaLabel;
+    [Reflect=aria_labelledby, EnabledBySetting=AriaReflectionForElementReferencesEnabled] attribute FrozenArray<Element>? ariaLabelledByElements;
     [Reflect=aria_level]            attribute DOMString? ariaLevel;
     [Reflect=aria_live]             attribute DOMString? ariaLive;
     [Reflect=aria_modal]            attribute DOMString? ariaModal;
@@ -48,6 +53,7 @@
     [Reflect=aria_multiline]        attribute DOMString? ariaMultiLine;
     [Reflect=aria_multiselectable]  attribute DOMString? ariaMultiSelectable;
     [Reflect=aria_orientation]      attribute DOMString? ariaOrientation;
+    [Reflect=aria_owns, EnabledBySetting=AriaReflectionForElementReferencesEnabled] attribute FrozenArray<Element>? ariaOwnsElements;
     [Reflect=aria_placeholder]      attribute DOMString? ariaPlaceholder;
     [Reflect=aria_posinset]         attribute DOMString? ariaPosInSet;
     [Reflect=aria_pressed]          attribute DOMString? ariaPressed;

Modified: trunk/Source/WebCore/bindings/scripts/CodeGenerator.pm (294140 => 294141)


--- trunk/Source/WebCore/bindings/scripts/CodeGenerator.pm	2022-05-13 03:38:28 UTC (rev 294140)
+++ trunk/Source/WebCore/bindings/scripts/CodeGenerator.pm	2022-05-13 05:04:23 UTC (rev 294141)
@@ -1110,6 +1110,8 @@
         $functionName = "getUnsignedIntegralAttribute";
     } elsif ($attributeType->name eq "Element") {
         $functionName = "getElementAttribute";
+    } elsif ($attributeType->name eq "FrozenArray" && scalar @{$attributeType->subtypes} == 1 && @{$attributeType->subtypes}[0]->name eq "Element") {
+        $functionName = "getElementsArrayAttribute";
     } else {
         if ($contentAttributeName eq "WebCore::HTMLNames::idAttr") {
             $functionName = "getIdAttribute";
@@ -1149,6 +1151,8 @@
         $functionName = "setUnsignedIntegralAttribute";
     } elsif ($attributeType->name eq "Element") {
         $functionName = "setElementAttribute";
+    } elsif ($attributeType->name eq "FrozenArray" && scalar @{$attributeType->subtypes} == 1 && @{$attributeType->subtypes}[0]->name eq "Element") {
+        $functionName = "setElementsArrayAttribute";
     } elsif ($generator->IsSVGAnimatedType($attributeType)) {
         $functionName = "setAttribute";
     } else {

Modified: trunk/Source/WebCore/bindings/scripts/test/JS/JSTestObj.cpp (294140 => 294141)


--- trunk/Source/WebCore/bindings/scripts/test/JS/JSTestObj.cpp	2022-05-13 03:38:28 UTC (rev 294140)
+++ trunk/Source/WebCore/bindings/scripts/test/JS/JSTestObj.cpp	2022-05-13 05:04:23 UTC (rev 294141)
@@ -1805,6 +1805,8 @@
 static JSC_DECLARE_CUSTOM_SETTER(setJSTestObj_reflectedBooleanAttr);
 static JSC_DECLARE_CUSTOM_GETTER(jsTestObj_reflectedElementAttr);
 static JSC_DECLARE_CUSTOM_SETTER(setJSTestObj_reflectedElementAttr);
+static JSC_DECLARE_CUSTOM_GETTER(jsTestObj_reflectedElementsArrayAttr);
+static JSC_DECLARE_CUSTOM_SETTER(setJSTestObj_reflectedElementsArrayAttr);
 static JSC_DECLARE_CUSTOM_GETTER(jsTestObj_reflectedURLAttr);
 static JSC_DECLARE_CUSTOM_SETTER(setJSTestObj_reflectedURLAttr);
 static JSC_DECLARE_CUSTOM_GETTER(jsTestObj_reflectedUSVURLAttr);
@@ -2178,6 +2180,7 @@
     { "reflectedUnsignedIntegralAttr", static_cast<unsigned>(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsTestObj_reflectedUnsignedIntegralAttr), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(setJSTestObj_reflectedUnsignedIntegralAttr) } },
     { "reflectedBooleanAttr", static_cast<unsigned>(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsTestObj_reflectedBooleanAttr), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(setJSTestObj_reflectedBooleanAttr) } },
     { "reflectedElementAttr", static_cast<unsigned>(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsTestObj_reflectedElementAttr), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(setJSTestObj_reflectedElementAttr) } },
+    { "reflectedElementsArrayAttr", static_cast<unsigned>(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsTestObj_reflectedElementsArrayAttr), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(setJSTestObj_reflectedElementsArrayAttr) } },
     { "reflectedURLAttr", static_cast<unsigned>(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsTestObj_reflectedURLAttr), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(setJSTestObj_reflectedURLAttr) } },
     { "reflectedUSVURLAttr", static_cast<unsigned>(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsTestObj_reflectedUSVURLAttr), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(setJSTestObj_reflectedUSVURLAttr) } },
     { "reflectedStringAttr", static_cast<unsigned>(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { (intptr_t)static_cast<PropertySlot::GetValueFunc>(jsTestObj_reflectedStringAttr), (intptr_t) static_cast<PutPropertySlot::PutValueFunc>(setJSTestObj_reflectedStringAttr) } },
@@ -3952,6 +3955,37 @@
     return IDLAttribute<JSTestObj>::set<setJSTestObj_reflectedElementAttrSetter>(*lexicalGlobalObject, thisValue, encodedValue, attributeName);
 }
 
+static inline JSValue jsTestObj_reflectedElementsArrayAttrGetter(JSGlobalObject& lexicalGlobalObject, JSTestObj& thisObject)
+{
+    auto& vm = JSC::getVM(&lexicalGlobalObject);
+    auto throwScope = DECLARE_THROW_SCOPE(vm);
+    auto& impl = thisObject.wrapped();
+    RELEASE_AND_RETURN(throwScope, (toJS<IDLFrozenArray<IDLInterface<Element>>>(lexicalGlobalObject, *thisObject.globalObject(), throwScope, impl.getElementsArrayAttribute(WebCore::HTMLNames::reflectedelementsarrayattrAttr))));
+}
+
+JSC_DEFINE_CUSTOM_GETTER(jsTestObj_reflectedElementsArrayAttr, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName))
+{
+    return IDLAttribute<JSTestObj>::get<jsTestObj_reflectedElementsArrayAttrGetter, CastedThisErrorBehavior::Assert>(*lexicalGlobalObject, thisValue, attributeName);
+}
+
+static inline bool setJSTestObj_reflectedElementsArrayAttrSetter(JSGlobalObject& lexicalGlobalObject, JSTestObj& thisObject, JSValue value)
+{
+    auto& vm = JSC::getVM(&lexicalGlobalObject);
+    auto throwScope = DECLARE_THROW_SCOPE(vm);
+    auto& impl = thisObject.wrapped();
+    auto nativeValue = convert<IDLFrozenArray<IDLInterface<Element>>>(lexicalGlobalObject, value);
+    RETURN_IF_EXCEPTION(throwScope, false);
+    invokeFunctorPropagatingExceptionIfNecessary(lexicalGlobalObject, throwScope, [&] {
+        return impl.setElementsArrayAttribute(WebCore::HTMLNames::reflectedelementsarrayattrAttr, WTFMove(nativeValue));
+    });
+    return true;
+}
+
+JSC_DEFINE_CUSTOM_SETTER(setJSTestObj_reflectedElementsArrayAttr, (JSGlobalObject* lexicalGlobalObject, EncodedJSValue thisValue, EncodedJSValue encodedValue, PropertyName attributeName))
+{
+    return IDLAttribute<JSTestObj>::set<setJSTestObj_reflectedElementsArrayAttrSetter>(*lexicalGlobalObject, thisValue, encodedValue, attributeName);
+}
+
 static inline JSValue jsTestObj_reflectedURLAttrGetter(JSGlobalObject& lexicalGlobalObject, JSTestObj& thisObject)
 {
     auto& vm = JSC::getVM(&lexicalGlobalObject);

Modified: trunk/Source/WebCore/bindings/scripts/test/TestObj.idl (294140 => 294141)


--- trunk/Source/WebCore/bindings/scripts/test/TestObj.idl	2022-05-13 03:38:28 UTC (rev 294140)
+++ trunk/Source/WebCore/bindings/scripts/test/TestObj.idl	2022-05-13 05:04:23 UTC (rev 294141)
@@ -105,6 +105,7 @@
     [Reflect] attribute unsigned long reflectedUnsignedIntegralAttr;
     [Reflect] attribute boolean reflectedBooleanAttr;
     [Reflect] attribute Element reflectedElementAttr;
+    [Reflect] attribute FrozenArray<Element> reflectedElementsArrayAttr;
     [Reflect, URL] attribute DOMString reflectedURLAttr;
     [Reflect, URL] attribute USVString reflectedUSVURLAttr;
     [Reflect=customContentStringAttr] attribute DOMString reflectedStringAttr;

Modified: trunk/Source/WebCore/dom/Element.cpp (294140 => 294141)


--- trunk/Source/WebCore/dom/Element.cpp	2022-05-13 03:38:28 UTC (rev 294140)
+++ trunk/Source/WebCore/dom/Element.cpp	2022-05-13 05:04:23 UTC (rev 294141)
@@ -1906,6 +1906,16 @@
     return name == HTMLNames::aria_activedescendantAttr || name == HTMLNames::aria_errormessageAttr;
 }
 
+static inline bool isElementsArrayReflectionAttribute(const QualifiedName& name)
+{
+    return name == HTMLNames::aria_controlsAttr
+        || name == HTMLNames::aria_describedbyAttr
+        || name == HTMLNames::aria_detailsAttr
+        || name == HTMLNames::aria_flowtoAttr
+        || name == HTMLNames::aria_labelledbyAttr
+        || name == HTMLNames::aria_ownsAttr;
+}
+
 void Element::attributeChanged(const QualifiedName& name, const AtomString& oldValue, const AtomString& newValue, AttributeModificationReason)
 {
     bool valueIsSameAsBefore = oldValue == newValue;
@@ -1942,7 +1952,7 @@
             }
         } else if (name == HTMLNames::partAttr)
             partAttributeChanged(newValue);
-        else if (document().settings().ariaReflectionForElementReferencesEnabled() && isElementReflectionAttribute(name)) {
+        else if (document().settings().ariaReflectionForElementReferencesEnabled() && (isElementReflectionAttribute(name) || isElementsArrayReflectionAttribute(name))) {
             if (auto* map = explicitlySetAttrElementsMapIfExists())
                 map->remove(name);
         } else if (name == HTMLNames::exportpartsAttr) {
@@ -2023,6 +2033,70 @@
     explicitlySetAttrElementsMap().set(attributeName, Vector<WeakPtr<Element>> { element });
 }
 
+std::optional<Vector<RefPtr<Element>>> Element::getElementsArrayAttribute(const QualifiedName& attributeName) const
+{
+    ASSERT(document().settings().ariaReflectionForElementReferencesEnabled());
+    ASSERT(isElementsArrayReflectionAttribute(attributeName));
+
+    if (auto* map = explicitlySetAttrElementsMapIfExists()) {
+        if (auto it = map->find(attributeName); it != map->end()) {
+            return compactMap(it->value, [&](auto& element) -> std::optional<RefPtr<Element>> {
+                if (element && isDescendantOrShadowDescendantOf(element->rootNode()))
+                    return element.get();
+                return std::nullopt;
+            });
+        }
+    }
+
+    auto attr = attributeName;
+    if (attr == HTMLNames::aria_labelledbyAttr && !hasAttribute(HTMLNames::aria_labelledbyAttr) && hasAttribute(HTMLNames::aria_labeledbyAttr))
+        attr = HTMLNames::aria_labeledbyAttr;
+
+    if (!hasAttribute(attr))
+        return std::nullopt;
+
+    SpaceSplitString ids(getAttribute(attr), SpaceSplitString::ShouldFoldCase::No);
+    Vector<RefPtr<Element>> elements;
+    for (unsigned i = 0; i < ids.size(); ++i) {
+        if (auto* element = treeScope().getElementById(ids[i]))
+            elements.append(element);
+    }
+    return elements;
+}
+
+void Element::setElementsArrayAttribute(const QualifiedName& attributeName, std::optional<Vector<RefPtr<Element>>>&& elements)
+{
+    ASSERT(document().settings().ariaReflectionForElementReferencesEnabled());
+    ASSERT(isElementsArrayReflectionAttribute(attributeName));
+
+    if (!elements) {
+        if (auto* map = explicitlySetAttrElementsMapIfExists())
+            map->remove(attributeName);
+        removeAttribute(attributeName);
+        return;
+    }
+
+    Vector<WeakPtr<Element>> newElements;
+    newElements.reserveInitialCapacity(elements->size());
+    StringBuilder value;
+    for (auto element : elements.value()) {
+        newElements.uncheckedAppend(element);
+        if (value.isEmpty() && newElements.size() > 1)
+            continue;
+
+        auto id = element->getIdAttribute();
+        if (!id.isNull() && &rootNode() == &element->rootNode() && treeScope().getElementById(id) == element) {
+            if (!value.isEmpty())
+                value.append(' ');
+            value.append(id);
+        } else
+            value.clear();
+    }
+    setAttribute(attributeName, value.toAtomString());
+
+    explicitlySetAttrElementsMap().set(attributeName, WTFMove(newElements));
+}
+
 template <typename CharacterType>
 static inline bool isNonEmptyTokenList(const CharacterType* characters, unsigned length)
 {

Modified: trunk/Source/WebCore/dom/Element.h (294140 => 294141)


--- trunk/Source/WebCore/dom/Element.h	2022-05-13 03:38:28 UTC (rev 294140)
+++ trunk/Source/WebCore/dom/Element.h	2022-05-13 05:04:23 UTC (rev 294141)
@@ -93,8 +93,6 @@
 struct SecurityPolicyViolationEventInit;
 struct ShadowRootInit;
 
-// FIXME: This only stores one Element in the Vector right now, though this will change
-// once reflection for FrozenArray<Element> is implemented in https://webkit.org/b/239853.
 using ExplicitlySetAttrElementsMap = HashMap<QualifiedName, Vector<WeakPtr<Element>>>;
 
 namespace Style {
@@ -127,6 +125,8 @@
     WEBCORE_EXPORT void setUnsignedIntegralAttribute(const QualifiedName& attributeName, unsigned value);
     WEBCORE_EXPORT Element* getElementAttribute(const QualifiedName& attributeName) const;
     WEBCORE_EXPORT void setElementAttribute(const QualifiedName& attributeName, Element* value);
+    WEBCORE_EXPORT std::optional<Vector<RefPtr<Element>>> getElementsArrayAttribute(const QualifiedName& attributeName) const;
+    WEBCORE_EXPORT void setElementsArrayAttribute(const QualifiedName& attributeName, std::optional<Vector<RefPtr<Element>>>&& value);
 
     // Call this to get the value of an attribute that is known not to be the style
     // attribute or one of the SVG animatable attributes.
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to