Title: [291747] trunk
Revision
291747
Author
tyle...@apple.com
Date
2022-03-23 10:00:33 -0700 (Wed, 23 Mar 2022)

Log Message

AccessibilityRenderObject::nextSibling should allow parent differences in the presence of display: contents
https://bugs.webkit.org/show_bug.cgi?id=238184

Reviewed by Andres Gonzalez.

Source/WebCore:

AccessibilityRenderObject::nextSibling currently has this logic to
return nullptr if the computed sibling has a parent different from `this`:

    auto* nextObject = objectCache->getOrCreate(nextSibling);
    if (nextObject && nextObject->parentObject() != this->parentObject())
        return nullptr;

This is problematic in the presence of display: contents since we expect
parent object differences due to the way this property affects the render tree.

Concretely, this breaks the firstChild(), nextSibling() iteration we do throughout
WebKit (e.g. in AccessibilityRenderObject::addChildren), as when we get to an element
with display: contents we get a parent mismatch and iteration stops unnecessarily.

This patch fixes the issue by allowing a parent mismatch when either
object has display: contents, as we account for this difference in the
appropriate places (e.g. AccessibilityObject::insertChild).

Test: accessibility/display-contents-search-traversal.html

* accessibility/AXLogger.cpp:
(WebCore::operator<<):
Log when an object has display: contents. This property affects the
render tree and AX tree, so it's useful to log.
* accessibility/AccessibilityObject.h:
(WebCore::AccessibilityObject::hasDisplayContents const): Added.
* accessibility/AccessibilityRenderObject.cpp:
(WebCore::AccessibilityRenderObject::nextSibling const):

LayoutTests:

* accessibility/display-contents-search-traversal-expected.txt: Added.
* accessibility/display-contents-search-traversal.html: Added.
* platform/glib/TestExpectations: Skip new test.
* platform/ios/TestExpectations: Enable new test.
* platform/ios/accessibility/display-contents-search-traversal-expected.txt: Added.
* platform/win/TestExpectations: Skip new test.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (291746 => 291747)


--- trunk/LayoutTests/ChangeLog	2022-03-23 16:40:09 UTC (rev 291746)
+++ trunk/LayoutTests/ChangeLog	2022-03-23 17:00:33 UTC (rev 291747)
@@ -1,3 +1,17 @@
+2022-03-23  Tyler Wilcock  <tyle...@apple.com>
+
+        AccessibilityRenderObject::nextSibling should allow parent differences in the presence of display: contents
+        https://bugs.webkit.org/show_bug.cgi?id=238184
+
+        Reviewed by Andres Gonzalez.
+
+        * accessibility/display-contents-search-traversal-expected.txt: Added.
+        * accessibility/display-contents-search-traversal.html: Added.
+        * platform/glib/TestExpectations: Skip new test.
+        * platform/ios/TestExpectations: Enable new test.
+        * platform/ios/accessibility/display-contents-search-traversal-expected.txt: Added.
+        * platform/win/TestExpectations: Skip new test.
+
 2022-03-23  Patrick Angle  <pan...@apple.com>
 
         No breakpoints hit on github.com, and some are invalid

Added: trunk/LayoutTests/accessibility/display-contents-search-traversal-expected.txt (0 => 291747)


--- trunk/LayoutTests/accessibility/display-contents-search-traversal-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/accessibility/display-contents-search-traversal-expected.txt	2022-03-23 17:00:33 UTC (rev 291747)
@@ -0,0 +1,68 @@
+This test ensures we can traverse through basic webpages with multiple display: contents elements via search.
+
+AXRole: AXGroup
+
+AXRole: AXStaticText
+AXValue: Content before main.
+
+AXRole: AXGroup
+
+AXRole: AXHeading
+
+AXRole: AXStaticText
+AXValue: No style changes
+
+AXRole: AXToolbar
+
+AXRole: AXGroup
+
+AXRole: AXButton
+
+AXRole: AXHeading
+
+AXRole: AXStaticText
+AXValue: display: contents; on the toolbar
+
+AXRole: AXToolbar
+
+AXRole: AXGroup
+
+AXRole: AXButton
+
+AXRole: AXHeading
+
+AXRole: AXStaticText
+AXValue: display: contents; on the generic div
+
+AXRole: AXToolbar
+
+AXRole: AXGroup
+
+AXRole: AXButton
+
+AXRole: AXHeading
+
+AXRole: AXStaticText
+AXValue: display: contents; on the button
+
+AXRole: AXToolbar
+
+AXRole: AXGroup
+
+AXRole: AXButton
+
+AXRole: AXGroup
+
+AXRole: AXStaticText
+AXValue: Content after main.
+
+Traversed 25 elements.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
+
+
+
+
+

Added: trunk/LayoutTests/accessibility/display-contents-search-traversal.html (0 => 291747)


--- trunk/LayoutTests/accessibility/display-contents-search-traversal.html	                        (rev 0)
+++ trunk/LayoutTests/accessibility/display-contents-search-traversal.html	2022-03-23 17:00:33 UTC (rev 291747)
@@ -0,0 +1,76 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src=""
+<script src=""
+</head>
+<body>
+
+<div id="container" role="group">
+    <p>Content before main.</p>
+
+    <main>
+
+    <h1>No style changes</h1>
+    <div role="toolbar">
+        <div>
+            <button>Foo</button>
+        </div>
+    </div>
+
+    <h1>display: contents; on the toolbar</h1>
+    <div role="toolbar" style="display: contents;">
+        <div>
+            <button>Foo</button>
+        </div>
+    </div>
+
+    <h1>display: contents; on the generic div</h1>
+    <div role="toolbar">
+        <div style="display: contents;">
+            <button>Foo</button>
+        </div>
+    </div>
+
+    <h1>display: contents; on the button</h1>
+    <div role="toolbar">
+        <div>
+            <button style="display: contents;">Foo</button>
+        </div>
+    </div>
+
+    </main>
+
+    <p>Content after main.</p>
+</div>
+
+<script>
+    var testOutput = "This test ensures we can traverse through basic webpages with multiple display: contents elements via search.\n";
+
+    if (window.accessibilityController) {
+        const containerElement = accessibilityController.accessibleElementById("container");
+
+        let elementCount = 0;
+        let searchResult = null;
+        while (true) {
+            searchResult = containerElement.uiElementForSearchPredicate(searchResult, true, "AXAnyTypeSearchKey", "", false);
+            if (!searchResult)
+                break;
+            const role = searchResult.role;
+            testOutput += `\n${role}`;
+            if (role.includes("StaticText")) {
+                let textContent = accessibilityController.platformName === "ios" ? searchResult.description : searchResult.stringValue;
+                testOutput += `\n${textContent}`;
+            }
+
+            testOutput += "\n";
+            elementCount += 1;
+        }
+        testOutput += `\nTraversed ${elementCount} elements.`;
+        document.getElementById("container").style.visibility = "hidden";
+        debug(testOutput);
+    }
+</script>
+</body>
+</html>
+

Modified: trunk/LayoutTests/platform/glib/TestExpectations (291746 => 291747)


--- trunk/LayoutTests/platform/glib/TestExpectations	2022-03-23 16:40:09 UTC (rev 291746)
+++ trunk/LayoutTests/platform/glib/TestExpectations	2022-03-23 17:00:33 UTC (rev 291747)
@@ -342,6 +342,7 @@
 accessibility/focusable-div.html [ Skip ]
 
 # Missing AccessibilityUIElement::uiElementForSearchPredicate implementation.
+accessibility/display-contents-search-traversal.html [ Skip ]
 accessibility/table-search-traversal.html [ Skip ]
 accessibility/dynamically-changing-iframe-remains-accessible.html [ Skip ]
 accessibility/ignore-modals-without-any-content.html [ Skip ]

Modified: trunk/LayoutTests/platform/ios/TestExpectations (291746 => 291747)


--- trunk/LayoutTests/platform/ios/TestExpectations	2022-03-23 16:40:09 UTC (rev 291746)
+++ trunk/LayoutTests/platform/ios/TestExpectations	2022-03-23 17:00:33 UTC (rev 291747)
@@ -2114,6 +2114,7 @@
 # Enable "aria-table-attributes" test for iOS
 webkit.org/b/150366 accessibility/aria-table-attributes.html [ Pass ]
 
+accessibility/display-contents-search-traversal.html [ Pass ]
 accessibility/table-search-traversal.html [ Pass ]
 accessibility/dynamically-changing-iframe-remains-accessible.html [ Pass ]
 accessibility/ignore-modals-without-any-content.html [ Pass ]

Added: trunk/LayoutTests/platform/ios/accessibility/display-contents-search-traversal-expected.txt (0 => 291747)


--- trunk/LayoutTests/platform/ios/accessibility/display-contents-search-traversal-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/platform/ios/accessibility/display-contents-search-traversal-expected.txt	2022-03-23 17:00:33 UTC (rev 291747)
@@ -0,0 +1,38 @@
+This test ensures we can traverse through basic webpages with multiple display: contents elements via search.
+
+StaticText
+AXLabel: Content before main.
+
+StaticText
+AXLabel: No style changes
+
+Button
+
+StaticText
+AXLabel: display: contents; on the toolbar
+
+Button
+
+StaticText
+AXLabel: display: contents; on the generic div
+
+Button
+
+StaticText
+AXLabel: display: contents; on the button
+
+Button
+
+StaticText
+AXLabel: Content after main.
+
+Traversed 10 elements.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
+
+
+
+
+

Modified: trunk/LayoutTests/platform/win/TestExpectations (291746 => 291747)


--- trunk/LayoutTests/platform/win/TestExpectations	2022-03-23 16:40:09 UTC (rev 291746)
+++ trunk/LayoutTests/platform/win/TestExpectations	2022-03-23 17:00:33 UTC (rev 291747)
@@ -478,6 +478,7 @@
 accessibility/ancestor-computation.html [ Skip ]
 
 # Missing AccessibilityUIElement::uiElementForSearchPredicate implementation.
+accessibility/display-contents-search-traversal.html [ Skip ]
 accessibility/table-search-traversal.html [ Skip ]
 accessibility/dynamically-changing-iframe-remains-accessible.html [ Skip ]
 accessibility/ignore-modals-without-any-content.html [ Skip ]

Modified: trunk/Source/WebCore/ChangeLog (291746 => 291747)


--- trunk/Source/WebCore/ChangeLog	2022-03-23 16:40:09 UTC (rev 291746)
+++ trunk/Source/WebCore/ChangeLog	2022-03-23 17:00:33 UTC (rev 291747)
@@ -1,3 +1,39 @@
+2022-03-23  Tyler Wilcock  <tyle...@apple.com>
+
+        AccessibilityRenderObject::nextSibling should allow parent differences in the presence of display: contents
+        https://bugs.webkit.org/show_bug.cgi?id=238184
+
+        Reviewed by Andres Gonzalez.
+
+        AccessibilityRenderObject::nextSibling currently has this logic to
+        return nullptr if the computed sibling has a parent different from `this`:
+
+            auto* nextObject = objectCache->getOrCreate(nextSibling);
+            if (nextObject && nextObject->parentObject() != this->parentObject())
+                return nullptr;
+
+        This is problematic in the presence of display: contents since we expect
+        parent object differences due to the way this property affects the render tree.
+
+        Concretely, this breaks the firstChild(), nextSibling() iteration we do throughout
+        WebKit (e.g. in AccessibilityRenderObject::addChildren), as when we get to an element
+        with display: contents we get a parent mismatch and iteration stops unnecessarily.
+
+        This patch fixes the issue by allowing a parent mismatch when either
+        object has display: contents, as we account for this difference in the
+        appropriate places (e.g. AccessibilityObject::insertChild).
+
+        Test: accessibility/display-contents-search-traversal.html
+
+        * accessibility/AXLogger.cpp:
+        (WebCore::operator<<):
+        Log when an object has display: contents. This property affects the
+        render tree and AX tree, so it's useful to log.
+        * accessibility/AccessibilityObject.h:
+        (WebCore::AccessibilityObject::hasDisplayContents const): Added.
+        * accessibility/AccessibilityRenderObject.cpp:
+        (WebCore::AccessibilityRenderObject::nextSibling const):
+
 2022-03-23  Youenn Fablet  <you...@apple.com>
 
         VideoFrame does not need to inherit from MediaSample

Modified: trunk/Source/WebCore/accessibility/AXLogger.cpp (291746 => 291747)


--- trunk/Source/WebCore/accessibility/AXLogger.cpp	2022-03-23 16:40:09 UTC (rev 291746)
+++ trunk/Source/WebCore/accessibility/AXLogger.cpp	2022-03-23 17:00:33 UTC (rev 291747)
@@ -532,6 +532,8 @@
     if (objectWithInterestingHTML)
         stream.dumpProperty("outerHTML", objectWithInterestingHTML->outerHTML());
 
+    if (auto* axObject = dynamicDowncast<AccessibilityObject>(&object); axObject && axObject->hasDisplayContents())
+        stream.dumpProperty("hasDisplayContents", true);
     stream.dumpProperty("address", &object);
     stream.dumpProperty("wrapper", object.wrapper());
 

Modified: trunk/Source/WebCore/accessibility/AccessibilityObject.h (291746 => 291747)


--- trunk/Source/WebCore/accessibility/AccessibilityObject.h	2022-03-23 16:40:09 UTC (rev 291746)
+++ trunk/Source/WebCore/accessibility/AccessibilityObject.h	2022-03-23 17:00:33 UTC (rev 291747)
@@ -544,6 +544,7 @@
     int getIntegralAttribute(const QualifiedName&) const;
     bool hasTagName(const QualifiedName&) const override;
     String tagName() const override;
+    bool hasDisplayContents() const;
 
     VisiblePositionRange visiblePositionRange() const override { return VisiblePositionRange(); }
     VisiblePositionRange visiblePositionRangeForLine(unsigned) const override { return VisiblePositionRange(); }
@@ -859,6 +860,11 @@
 };
 
 #if ENABLE(ACCESSIBILITY)
+inline bool AccessibilityObject::hasDisplayContents() const
+{
+    return is<Element>(node()) && downcast<Element>(node())->hasDisplayContents();
+}
+
 inline std::optional<BoundaryPoint> AccessibilityObject::lastBoundaryPointContainedInRect(const Vector<BoundaryPoint>& boundaryPoints, const BoundaryPoint& startBoundaryPoint, const FloatRect& targetRect) const
 {
     return lastBoundaryPointContainedInRect(boundaryPoints, startBoundaryPoint, targetRect, 0, boundaryPoints.size() - 1);
@@ -869,6 +875,7 @@
     return previousLineStartPositionInternal(position).value_or(VisiblePosition());
 }
 #else
+inline bool AccessibilityObject::hasDisplayContents() const { return false; }
 inline std::optional<BoundaryPoint> AccessibilityObject::lastBoundaryPointContainedInRect(const Vector<BoundaryPoint>&, const BoundaryPoint&, const FloatRect&) const { return std::nullopt; }
 inline VisiblePosition AccessibilityObject::previousLineStartPosition(const VisiblePosition& position) const { return { }; }
 #endif

Modified: trunk/Source/WebCore/accessibility/AccessibilityRenderObject.cpp (291746 => 291747)


--- trunk/Source/WebCore/accessibility/AccessibilityRenderObject.cpp	2022-03-23 16:40:09 UTC (rev 291746)
+++ trunk/Source/WebCore/accessibility/AccessibilityRenderObject.cpp	2022-03-23 17:00:33 UTC (rev 291747)
@@ -421,11 +421,17 @@
     if (!objectCache)
         return nullptr;
 
+    auto* nextObject = objectCache->getOrCreate(nextSibling);
+    auto* nextObjectParent = nextObject ? nextObject->parentObject() : nullptr;
+    auto* thisParent = parentObject();
     // Make sure next sibling has the same parent.
-    auto* nextObject = objectCache->getOrCreate(nextSibling);
-    if (nextObject && nextObject->parentObject() != this->parentObject())
+    if (nextObjectParent && nextObjectParent != thisParent) {
+        // Unless either object has a parent with display: contents, as display: contents can cause parent differences
+        // that we properly account for elsewhere.
+        if (nextObjectParent->hasDisplayContents() || (thisParent && thisParent->hasDisplayContents()))
+            return nextObject;
         return nullptr;
-
+    }
     return nextObject;
 }
 
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to