Diff
Modified: trunk/LayoutTests/ChangeLog (291569 => 291570)
--- trunk/LayoutTests/ChangeLog 2022-03-21 18:01:29 UTC (rev 291569)
+++ trunk/LayoutTests/ChangeLog 2022-03-21 18:48:25 UTC (rev 291570)
@@ -1,3 +1,22 @@
+2022-03-21 Tyler Wilcock <[email protected]>
+
+ AX: Include display: contents elements in the AX tree
+ https://bugs.webkit.org/show_bug.cgi?id=237834
+
+ Reviewed by Chris Fleizach.
+
+ * accessibility/aria-hidden-display-contents-element-expected.txt: Added.
+ * accessibility/aria-hidden-display-contents-element.html: Added.
+ * accessibility/display-contents-element-roles-expected.txt: Added.
+ * accessibility/display-contents-element-roles.html: Added.
+ * platform/glib/accessibility/aria-hidden-display-contents-element-expected.txt: Added.
+ * platform/glib/accessibility/display-contents-element-roles-expected.txt: Added.
+ * platform/ios/TestExpectations: Enable new tests.
+ * platform/ios/accessibility/aria-hidden-display-contents-element-expected.txt: Added.
+ * platform/ios/accessibility/display-contents-element-roles-expected.txt: Added.
+ * platform/win/TestExpectations: Skip display-contents-element-roles.html.
+ * platform/win/accessibility/aria-hidden-display-contents-element-expected.txt: Added.
+
2022-03-21 Ziran Sun <[email protected]>
[selection] HTMLTextFormControlElement::subtreeHasChanged() shouldn't be called in setRangeText
Added: trunk/LayoutTests/accessibility/aria-hidden-display-contents-element-expected.txt (0 => 291570)
--- trunk/LayoutTests/accessibility/aria-hidden-display-contents-element-expected.txt (rev 0)
+++ trunk/LayoutTests/accessibility/aria-hidden-display-contents-element-expected.txt 2022-03-21 18:48:25 UTC (rev 291570)
@@ -0,0 +1,23 @@
+This test ensures that an aria-hidden display:contents element behaves as expected.
+
+Testing element #main
+AXRole: AXGroup
+computedRoleString: main
+AXSubrole: AXLandmarkMain
+
+Testing element #list
+AXRole: AXList
+computedRoleString: list
+AXSubrole: AXContentList
+
+Testing element #paragraph
+AXRole: AXGroup
+computedRoleString: paragraph
+
+Hiding #main with aria-hidden='true'.
+PASS: All elements are now hidden.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
+
Added: trunk/LayoutTests/accessibility/aria-hidden-display-contents-element.html (0 => 291570)
--- trunk/LayoutTests/accessibility/aria-hidden-display-contents-element.html (rev 0)
+++ trunk/LayoutTests/accessibility/aria-hidden-display-contents-element.html 2022-03-21 18:48:25 UTC (rev 291570)
@@ -0,0 +1,71 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src=""
+<script src=""
+</head>
+<body id="body">
+
+<main id="main" style="display: contents">
+ <ol id="list">
+ <li>List item one</li>
+ <li>List item two</li>
+ </ol>
+ <p id="paragraph">Content inside main > p tag.<p>
+</main>
+
+<script>
+ var testOutput = "This test ensures that an aria-hidden display:contents element behaves as expected.";
+
+ function debugElement(id) {
+ testOutput += `\n\nTesting element #${id}\n`;
+ const axElement = accessibilityController.accessibleElementById(id);
+ if (!axElement) {
+ debug(`\nFAIL: Couldn't get AX element for #${id}.`);
+ return;
+ }
+
+ testOutput += axElement.role;
+ const computedRoleString = axElement.computedRoleString;
+ if (computedRoleString)
+ testOutput += `\ncomputedRoleString: ${computedRoleString}`;
+
+ let subrole = axElement.subrole;
+ if (subrole.replace("AXSubrole: ", ""))
+ testOutput += `\n${subrole}`;
+ }
+
+ if (window.accessibilityController) {
+ window.jsTestIsAsync = true;
+
+ debugElement("main");
+ debugElement("list");
+ debugElement("paragraph");
+
+ testOutput += "\n\nHiding #main with aria-hidden='true'.";
+ document.getElementById("main").ariaHidden = "true";
+ setTimeout(async function() {
+ await waitFor(() => {
+ // Wait for the main to be hidden.
+ return !accessibilityController.accessibleElementById("main");
+ });
+ const list = accessibilityController.accessibleElementById("list");
+ const paragraph = accessibilityController.accessibleElementById("paragraph");
+ if (!list && !paragraph)
+ testOutput += "\nPASS: All elements are now hidden.";
+ else {
+ if (list)
+ testOutput += "\nFAIL: <ol> was not hidden by aria-hidden.";
+ if (paragraph)
+ testOutput += "\nFAIL: <p> was not hidden by aria-hidden.";
+ }
+
+ document.getElementById("main").style.visibility = "hidden";
+ debug(testOutput);
+ finishJSTest();
+ }, 0);
+ }
+</script>
+</body>
+</html>
+
Added: trunk/LayoutTests/accessibility/display-contents-element-roles-expected.txt (0 => 291570)
--- trunk/LayoutTests/accessibility/display-contents-element-roles-expected.txt (rev 0)
+++ trunk/LayoutTests/accessibility/display-contents-element-roles-expected.txt 2022-03-21 18:48:25 UTC (rev 291570)
@@ -0,0 +1,166 @@
+This test ensures elements with CSS display: contents have the correct role.
+
+<a href="" id="link" class="testcase"></a>
+ AXRole: AXLink
+ computedRoleString: link
+
+<article class="testcase" id="article"></article>
+ AXRole: AXGroup
+ computedRoleString: article
+ AXSubrole: AXDocumentArticle
+
+<aside class="testcase" id="aside"></aside>
+ AXRole: AXGroup
+ computedRoleString: complementary
+ AXSubrole: AXLandmarkComplementary
+
+<blockquote class="testcase" id="blockquote"></blockquote>
+ AXRole: AXGroup
+ computedRoleString: blockquote
+
+<button class="testcase" id="button"></button>
+ AXRole: AXButton
+ computedRoleString: button
+
+<code class="testcase" id="code"></code>
+ AXRole: AXGroup
+ AXSubrole: AXCodeStyleGroup
+
+<del class="testcase" id="del"></del>
+ AXRole: AXGroup
+ computedRoleString: deletion
+ AXSubrole: AXDeleteStyleGroup
+
+<details class="testcase" id="details"></details>
+ AXRole: AXGroup
+ AXSubrole: AXDetails
+
+<summary class="testcase" id="summary"></summary>
+ AXRole: AXButton
+ AXSubrole: AXSummary
+
+<dfn class="testcase" id="dfn"></dfn>
+ AXRole: AXGroup
+ computedRoleString: definition
+ AXSubrole: AXDefinition
+
+<div class="testcase" id="div"></div>
+ AXRole: AXGroup
+
+<dl class="testcase" id="dl"></dl>
+ AXRole: AXList
+
+<dt class="testcase" id="dt"></dt>
+ AXRole: AXGroup
+ AXSubrole: AXTerm
+
+<dd class="testcase" id="dd"></dd>
+ AXRole: AXGroup
+ AXSubrole: AXDescription
+
+<legend class="testcase" id="legend"></legend>
+ AXRole: AXGroup
+
+<figure class="testcase" id="figure"></figure>
+ AXRole: AXGroup
+ computedRoleString: figure
+ AXSubrole: AXEmptyGroup
+
+<form class="testcase" id="form"></form>
+ AXRole: AXGroup
+ computedRoleString: form
+ AXSubrole: AXEmptyGroup
+
+<h2 class="testcase" id="h2"></h2>
+ AXRole: AXHeading
+ computedRoleString: heading
+
+<hr class="testcase" id="hr">
+ AXRole: AXSplitter
+ computedRoleString: separator
+ AXSubrole: AXContentSeparator
+
+<ins class="testcase" id="ins"></ins>
+ AXRole: AXGroup
+ computedRoleString: insertion
+ AXSubrole: AXInsertStyleGroup
+
+<label class="testcase" id="label"></label>
+ AXRole: AXGroup
+
+<main class="testcase" id="main"></main>
+ AXRole: AXGroup
+ computedRoleString: main
+ AXSubrole: AXLandmarkMain
+
+<mark class="testcase" id="mark"></mark>
+ AXRole: AXGroup
+
+<menu class="testcase" type="toolbar" id="menu-toolbar"></menu>
+ AXRole: AXToolbar
+ computedRoleString: toolbar
+
+<nav class="testcase" id="nav"></nav>
+ AXRole: AXGroup
+ computedRoleString: navigation
+ AXSubrole: AXLandmarkNavigation
+
+<ol class="testcase" id="ol"></ol>
+ AXRole: AXList
+ computedRoleString: list
+
+<li class="testcase" id="ol-li-element"></li>
+ AXRole: AXGroup
+ computedRoleString: listitem
+
+<output class="testcase" id="output"></output>
+ AXRole: AXGroup
+ computedRoleString: status
+ AXSubrole: AXApplicationStatus
+
+<p class="testcase" id="p"></p>
+ AXRole: AXGroup
+ computedRoleString: paragraph
+
+<pre class="testcase" id="pre"></pre>
+ AXRole: AXGroup
+ AXSubrole: AXPreformattedStyleGroup
+
+<section class="testcase" id="section-with-name" aria-label="Section name"></section>
+ AXRole: AXGroup
+ computedRoleString: region
+ AXSubrole: AXLandmarkRegion
+
+<section class="testcase" id="section-without-name"></section>
+ AXRole: AXGroup
+
+<sub class="testcase" id="sub"></sub>
+ AXRole: AXGroup
+ computedRoleString: subscript
+ AXSubrole: AXSubscriptStyleGroup
+
+<sup class="testcase" id="sup"></sup>
+ AXRole: AXGroup
+ computedRoleString: superscript
+ AXSubrole: AXSuperscriptStyleGroup
+
+<time class="testcase" id="time"></time>
+ AXRole: AXGroup
+ computedRoleString: time
+ AXSubrole: AXEmptyGroup
+
+<ul class="testcase" id="ul"></ul>
+ AXRole: AXList
+ computedRoleString: list
+
+<li class="testcase" id="ul-li-element"></li>
+ AXRole: AXGroup
+ computedRoleString: listitem
+
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
+
+
Added: trunk/LayoutTests/accessibility/display-contents-element-roles.html (0 => 291570)
--- trunk/LayoutTests/accessibility/display-contents-element-roles.html (rev 0)
+++ trunk/LayoutTests/accessibility/display-contents-element-roles.html 2022-03-21 18:48:25 UTC (rev 291570)
@@ -0,0 +1,129 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src=""
+<script src=""
+<style>
+.testcase { display: contents };
+</style>
+</head>
+<body>
+
+<div id="content">
+ <a href="" id="link" class="testcase">apple.com</a>
+
+ <article class="testcase" id="article">Article content</article>
+
+ <aside class="testcase" id="aside">Aside content</aside>
+
+ <blockquote class="testcase" id="blockquote">To be or not to be, that is the question</blockquote>
+
+ <button class="testcase" id="button">Click me</button>
+
+ <code class="testcase" id="code">Hello world</code>
+
+ <del class="testcase" id="del">Hello world</del>
+
+ <details class="testcase" id="details">
+ <summary class="testcase" id="summary">Summary text</summary>
+ </details>
+
+ <dfn class="testcase" id="dfn">Some definition</dfn>
+
+ <div class="testcase" id="div">Some text</div>
+
+ <dl class="testcase" id="dl">
+ <dt class="testcase" id="dt">dt element</dt>
+ <dd class="testcase" id="dd">dd element</dd>
+ </dl>
+
+ <fieldset>
+ <legend class="testcase" id="legend">Choose your favorite monster</legend>
+
+ <input type="radio" id="radio-button" name="monster">
+ <label for=""
+
+ <input type="radio" id="sasquatch" name="monster">
+ <label for=""
+ </fieldset>
+
+ <figure class="testcase" id="figure"></figure>
+
+ <form class="testcase" id="form"></form>
+
+ <h2 class="testcase" id="h2">Hello world</h2>
+
+ <hr class="testcase" id="hr"></hr>
+
+ <ins class="testcase" id="ins">Hello world</ins>
+
+ <label class="testcase" id="label">Label</label>
+
+ <main class="testcase" id="main">Main content</main>
+
+ <mark class="testcase" id="mark">Marked text</mark>
+
+ <menu class="testcase" type="toolbar" id="menu-toolbar"></menu>
+
+ <nav class="testcase" id="nav">Nav</nav>
+
+ <ol class="testcase" id="ol">
+ <li class="testcase" id="ol-li-element">Hello world</li>
+ </ol>
+
+ <output class="testcase" id="output">Output</output>
+
+ <p class="testcase" id="p">Paragraph</p>
+
+ <pre class="testcase" id="pre">Pre-text</pre>
+
+ <section class="testcase" id="section-with-name" aria-label="Section name">Section</section>
+
+ <section class="testcase" id="section-without-name">Section</section>
+
+ <sub class="testcase" id="sub">Hello world</sub>
+
+ <sup class="testcase" id="sup">Hello world</sup>
+
+ <time class="testcase" id="time"></time>
+
+ <ul class="testcase" id="ul">
+ <li class="testcase" id="ul-li-element">Hello world</li>
+ </ul>
+</div>
+
+<script>
+ // Buffer test output and dump it at the end to make the test run faster vs. individual `debug` calls.
+ var testOutput = "This test ensures elements with CSS display: contents have the correct role.\n\n";
+
+ var axElement;
+ function verifyRoles() {
+ const testcases = document.getElementsByClassName("testcase");
+ for (const domElement of testcases) {
+ const outerHTML = escapeHTML(domElement.cloneNode().outerHTML);
+ axElement = accessibilityController.accessibleElementById(domElement.id);
+ if (!axElement) {
+ testOutput += `FAIL. Couldn't get AX element for #${domElement.id}: ${outerHTML}\n`;
+ return;
+ }
+ testOutput += `${outerHTML}\n`;
+ testOutput += ` ${axElement.role}`;
+
+ let computedRoleString = axElement.computedRoleString;
+ if (computedRoleString)
+ testOutput += `\n computedRoleString: ${computedRoleString}`;
+
+ let subrole = axElement.subrole;
+ if (subrole.replace("AXSubrole: ", ""))
+ testOutput += `\n ${subrole}`;
+ testOutput += `\n\n`;
+ }
+ }
+
+ if (window.accessibilityController) {
+ verifyRoles();
+ document.getElementById("content").style.visibility = "hidden";
+ debug(testOutput);
+ }
+</script>
+</body>
Added: trunk/LayoutTests/platform/glib/accessibility/aria-hidden-display-contents-element-expected.txt (0 => 291570)
--- trunk/LayoutTests/platform/glib/accessibility/aria-hidden-display-contents-element-expected.txt (rev 0)
+++ trunk/LayoutTests/platform/glib/accessibility/aria-hidden-display-contents-element-expected.txt 2022-03-21 18:48:25 UTC (rev 291570)
@@ -0,0 +1,21 @@
+This test ensures that an aria-hidden display:contents element behaves as expected.
+
+Testing element #main
+AXRole: AXLandmarkMain
+computedRoleString: main
+
+Testing element #list
+AXRole: AXList
+computedRoleString: list
+
+Testing element #paragraph
+AXRole: AXParagraph
+computedRoleString: paragraph
+
+Hiding #main with aria-hidden='true'.
+PASS: All elements are now hidden.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
+
Added: trunk/LayoutTests/platform/glib/accessibility/display-contents-element-roles-expected.txt (0 => 291570)
--- trunk/LayoutTests/platform/glib/accessibility/display-contents-element-roles-expected.txt (rev 0)
+++ trunk/LayoutTests/platform/glib/accessibility/display-contents-element-roles-expected.txt 2022-03-21 18:48:25 UTC (rev 291570)
@@ -0,0 +1,145 @@
+This test ensures elements with CSS display: contents have the correct role.
+
+<a href="" id="link" class="testcase"></a>
+ AXRole: AXLink
+ computedRoleString: link
+
+<article class="testcase" id="article"></article>
+ AXRole: AXArticle
+ computedRoleString: article
+
+<aside class="testcase" id="aside"></aside>
+ AXRole: AXLandmarkComplementary
+ computedRoleString: complementary
+
+<blockquote class="testcase" id="blockquote"></blockquote>
+ AXRole: AXBlockquote
+ computedRoleString: blockquote
+
+<button class="testcase" id="button"></button>
+ AXRole: AXButton
+ computedRoleString: button
+
+<code class="testcase" id="code"></code>
+ AXRole: AXSection
+
+<del class="testcase" id="del"></del>
+ AXRole: AXDeletion
+ computedRoleString: deletion
+
+<details class="testcase" id="details"></details>
+ AXRole: AXUnknown
+
+<summary class="testcase" id="summary"></summary>
+ AXRole: AXUnknown
+
+<dfn class="testcase" id="dfn"></dfn>
+ AXRole: AXDefinition
+ computedRoleString: definition
+
+<div class="testcase" id="div"></div>
+ AXRole: AXSection
+
+<dl class="testcase" id="dl"></dl>
+ AXRole: AXDescriptionList
+
+<dt class="testcase" id="dt"></dt>
+ AXRole: AXDescriptionTerm
+
+<dd class="testcase" id="dd"></dd>
+ AXRole: AXDescriptionValue
+
+<legend class="testcase" id="legend"></legend>
+ AXRole: AXLabel
+
+<figure class="testcase" id="figure"></figure>
+ AXRole: AXGroup
+ computedRoleString: figure
+
+<form class="testcase" id="form"></form>
+ AXRole: AXForm
+ computedRoleString: form
+
+<h2 class="testcase" id="h2"></h2>
+ AXRole: AXHeading
+ computedRoleString: heading
+
+<hr class="testcase" id="hr">
+ AXRole: AXSeparator
+ computedRoleString: separator
+
+<ins class="testcase" id="ins"></ins>
+ AXRole: AXInsertion
+ computedRoleString: insertion
+
+<label class="testcase" id="label"></label>
+ AXRole: AXLabel
+
+<main class="testcase" id="main"></main>
+ AXRole: AXLandmarkMain
+ computedRoleString: main
+
+<mark class="testcase" id="mark"></mark>
+
+
+<menu class="testcase" type="toolbar" id="menu-toolbar"></menu>
+ AXRole: AXToolbar
+ computedRoleString: toolbar
+
+<nav class="testcase" id="nav"></nav>
+ AXRole: AXLandmarkNavigation
+ computedRoleString: navigation
+
+<ol class="testcase" id="ol"></ol>
+ AXRole: AXList
+ computedRoleString: list
+
+<li class="testcase" id="ol-li-element"></li>
+ AXRole: AXListItem
+ computedRoleString: listitem
+
+<output class="testcase" id="output"></output>
+ AXRole: AXStatusBar
+ computedRoleString: status
+
+<p class="testcase" id="p"></p>
+ AXRole: AXParagraph
+ computedRoleString: paragraph
+
+<pre class="testcase" id="pre"></pre>
+ AXRole: AXSection
+
+<section class="testcase" id="section-with-name" aria-label="Section name"></section>
+ AXRole: AXLandmarkRegion
+ computedRoleString: region
+
+<section class="testcase" id="section-without-name"></section>
+ AXRole: AXSection
+
+<sub class="testcase" id="sub"></sub>
+ AXRole: AXSubscript
+ computedRoleString: subscript
+
+<sup class="testcase" id="sup"></sup>
+ AXRole: AXSuperscript
+ computedRoleString: superscript
+
+<time class="testcase" id="time"></time>
+ AXRole: AXStatic
+ computedRoleString: time
+
+<ul class="testcase" id="ul"></ul>
+ AXRole: AXList
+ computedRoleString: list
+
+<li class="testcase" id="ul-li-element"></li>
+ AXRole: AXListItem
+ computedRoleString: listitem
+
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
+
+
Modified: trunk/LayoutTests/platform/ios/TestExpectations (291569 => 291570)
--- trunk/LayoutTests/platform/ios/TestExpectations 2022-03-21 18:01:29 UTC (rev 291569)
+++ trunk/LayoutTests/platform/ios/TestExpectations 2022-03-21 18:48:25 UTC (rev 291570)
@@ -2088,6 +2088,8 @@
# Enable "phone number linkifying" test for iOS
fast/dom/linkify-phone-numbers.html [ Pass ]
+accessibility/aria-hidden-display-contents-element.html [ Pass ]
+accessibility/display-contents-element-roles.html [ Pass ]
accessibility/video-element-url-attribute.html [ Pass ]
accessibility/visible-character-range.html [ Pass ]
accessibility/ancestor-computation.html [ Pass ]
Added: trunk/LayoutTests/platform/ios/accessibility/aria-hidden-display-contents-element-expected.txt (0 => 291570)
--- trunk/LayoutTests/platform/ios/accessibility/aria-hidden-display-contents-element-expected.txt (rev 0)
+++ trunk/LayoutTests/platform/ios/accessibility/aria-hidden-display-contents-element-expected.txt 2022-03-21 18:48:25 UTC (rev 291570)
@@ -0,0 +1,18 @@
+This test ensures that an aria-hidden display:contents element behaves as expected.
+
+Testing element #main
+LandmarkMain
+
+Testing element #list
+List
+
+Testing element #paragraph
+Paragraph
+
+Hiding #main with aria-hidden='true'.
+PASS: All elements are now hidden.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
+
Added: trunk/LayoutTests/platform/ios/accessibility/display-contents-element-roles-expected.txt (0 => 291570)
--- trunk/LayoutTests/platform/ios/accessibility/display-contents-element-roles-expected.txt (rev 0)
+++ trunk/LayoutTests/platform/ios/accessibility/display-contents-element-roles-expected.txt 2022-03-21 18:48:25 UTC (rev 291570)
@@ -0,0 +1,120 @@
+This test ensures elements with CSS display: contents have the correct role.
+
+<a href="" id="link" class="testcase"></a>
+ WebCoreLink
+
+<article class="testcase" id="article"></article>
+ DocumentArticle
+
+<aside class="testcase" id="aside"></aside>
+ LandmarkComplementary
+
+<blockquote class="testcase" id="blockquote"></blockquote>
+ Blockquote
+
+<button class="testcase" id="button"></button>
+ Button
+
+<code class="testcase" id="code"></code>
+ TextGroup
+
+<del class="testcase" id="del"></del>
+ Deletion
+
+<details class="testcase" id="details"></details>
+ Details
+
+<summary class="testcase" id="summary"></summary>
+ Summary
+
+<dfn class="testcase" id="dfn"></dfn>
+ Definition
+
+<div class="testcase" id="div"></div>
+ Div
+
+<dl class="testcase" id="dl"></dl>
+ DescriptionList
+
+<dt class="testcase" id="dt"></dt>
+ DescriptionListTerm
+
+<dd class="testcase" id="dd"></dd>
+ DescriptionListDetail
+
+<legend class="testcase" id="legend"></legend>
+ Legend
+
+<figure class="testcase" id="figure"></figure>
+ Figure
+
+<form class="testcase" id="form"></form>
+ Form
+
+<h2 class="testcase" id="h2"></h2>
+ Heading
+
+<hr class="testcase" id="hr">
+ HorizontalRule
+
+<ins class="testcase" id="ins"></ins>
+ Insertion
+
+<label class="testcase" id="label"></label>
+ Label
+
+<main class="testcase" id="main"></main>
+ LandmarkMain
+
+<mark class="testcase" id="mark"></mark>
+ Mark
+
+<menu class="testcase" type="toolbar" id="menu-toolbar"></menu>
+ Toolbar
+
+<nav class="testcase" id="nav"></nav>
+ LandmarkNavigation
+
+<ol class="testcase" id="ol"></ol>
+ List
+
+<li class="testcase" id="ol-li-element"></li>
+ ListItem
+
+<output class="testcase" id="output"></output>
+ ApplicationStatus
+
+<p class="testcase" id="p"></p>
+ Paragraph
+
+<pre class="testcase" id="pre"></pre>
+ TextGroup
+
+<section class="testcase" id="section-with-name" aria-label="Section name"></section>
+ LandmarkRegion
+
+<section class="testcase" id="section-without-name"></section>
+ TextGroup
+
+<sub class="testcase" id="sub"></sub>
+ Subscript
+
+<sup class="testcase" id="sup"></sup>
+ Superscript
+
+<time class="testcase" id="time"></time>
+ Time
+
+<ul class="testcase" id="ul"></ul>
+ List
+
+<li class="testcase" id="ul-li-element"></li>
+ ListItem
+
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
+
+
Modified: trunk/LayoutTests/platform/win/TestExpectations (291569 => 291570)
--- trunk/LayoutTests/platform/win/TestExpectations 2022-03-21 18:01:29 UTC (rev 291569)
+++ trunk/LayoutTests/platform/win/TestExpectations 2022-03-21 18:48:25 UTC (rev 291570)
@@ -1540,6 +1540,8 @@
################## Accessibility Issues ####################
################################################################################
+accessibility/display-contents-element-roles.html [ Skip ]
+
# Missing 'press' support
accessibility/axpress-on-aria-button.html [ Skip ]
accessibility/svg-element-press.html [ Skip ]
Added: trunk/LayoutTests/platform/win/accessibility/aria-hidden-display-contents-element-expected.txt (0 => 291570)
--- trunk/LayoutTests/platform/win/accessibility/aria-hidden-display-contents-element-expected.txt (rev 0)
+++ trunk/LayoutTests/platform/win/accessibility/aria-hidden-display-contents-element-expected.txt 2022-03-21 18:48:25 UTC (rev 291570)
@@ -0,0 +1,18 @@
+This test ensures that an aria-hidden display:contents element behaves as expected.
+
+Testing element #main
+AXRole: AXGroup
+
+Testing element #list
+AXRole: AXList
+
+Testing element #paragraph
+AXRole: AXGroup
+
+Hiding #main with aria-hidden='true'.
+PASS: All elements are now hidden.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
+
Modified: trunk/Source/WebCore/ChangeLog (291569 => 291570)
--- trunk/Source/WebCore/ChangeLog 2022-03-21 18:01:29 UTC (rev 291569)
+++ trunk/Source/WebCore/ChangeLog 2022-03-21 18:48:25 UTC (rev 291570)
@@ -1,3 +1,57 @@
+2022-03-21 Tyler Wilcock <[email protected]>
+
+ AX: Include display: contents elements in the AX tree
+ https://bugs.webkit.org/show_bug.cgi?id=237834
+
+ Reviewed by Chris Fleizach.
+
+ Because display: contents intentionally prevents a render object from being
+ generated for the element it's applied to, we don't add it to the AX tree as
+ part of our normal render tree walk, making these elements inaccessible.
+
+ This patch includes these elements as part of the DOM walk that
+ addHiddenChildren (now renamed to addNodeOnlyChildren) already does.
+
+ Also, because display: contents moves the affected element's children up a
+ level in the render tree, this patch also special cases:
+
+ 1. AccessibilityRenderObject::parentObject and similar methods to
+ return their display: contents parent instead of their render tree
+ parent
+ 2. AccessibilityObject::insertChild to only insert display: contents
+ children to their display: contents parent, rather than their render
+ tree parent
+
+ Test: accessibility/display-contents-element-roles.html, accessibility/aria-hidden-display-contents-element.html
+
+ * accessibility/AXObjectCache.cpp:
+ (WebCore::AXObjectCache::getOrCreate):
+ Allow creation of AX objects from display: contents `Node`s.
+ Also, don't create an object for a renderer that is in the process of
+ being destroyed (prevents display-contents-element-roles.html from crashing in ITM)
+ * accessibility/AccessibilityObject.cpp:
+ (WebCore::AccessibilityObject::displayContentsParent const):
+ (WebCore::AccessibilityObject::insertChild):
+ If an object has a display: contents parent, and that parent isn't
+ `this`, return early.
+ * accessibility/AccessibilityObject.h:
+ * accessibility/AccessibilityRenderObject.cpp:
+ (WebCore::AccessibilityRenderObject::parentObjectIfExists const):
+ (WebCore::AccessibilityRenderObject::parentObject const):
+ (WebCore::AccessibilityRenderObject::parentObjectUnignored const):
+ If an object has a display: contents parent, return that instead of
+ its render tree parent.
+ (WebCore::AccessibilityRenderObject::addNodeOnlyChildren):
+ Renamed from addHiddenChildren.
+ (WebCore::AccessibilityRenderObject::addChildren):
+ Don't clear m_subtreeDirty until after all children have been added.
+ Necessary to make aria-hidden-display-contents-element.html pass, as
+ we were clearing this too early, causing display: contents subtrees to
+ not be updated after aria-hidden changes.
+ (WebCore::AccessibilityRenderObject::addHiddenChildren):
+ Renamed to addNodeOnlyChildren.
+ * accessibility/AccessibilityRenderObject.h:
+
2022-03-21 Oriol Brufau <[email protected]>
[css-cascade] Don't defer applying text decoration properties
Modified: trunk/Source/WebCore/accessibility/AXObjectCache.cpp (291569 => 291570)
--- trunk/Source/WebCore/accessibility/AXObjectCache.cpp 2022-03-21 18:01:29 UTC (rev 291569)
+++ trunk/Source/WebCore/accessibility/AXObjectCache.cpp 2022-03-21 18:48:25 UTC (rev 291570)
@@ -720,14 +720,10 @@
return object.get();
}
- // It's only allowed to create an AccessibilityObject from a Node if it's in a canvas subtree.
- // Or if it's a hidden element, but we still want to expose it because of other ARIA attributes.
bool inCanvasSubtree = lineageOfType<HTMLCanvasElement>(*node->parentElement()).first();
- bool isHidden = isNodeAriaVisible(node);
-
bool insideMeterElement = is<HTMLMeterElement>(*node->parentElement());
-
- if (!inCanvasSubtree && !isHidden && !insideMeterElement)
+ bool hasDisplayContents = is<Element>(*node) && downcast<Element>(*node).hasDisplayContents();
+ if (!inCanvasSubtree && !insideMeterElement && !hasDisplayContents && !isNodeAriaVisible(node))
return nullptr;
Ref protectedNode { *node };
@@ -760,6 +756,9 @@
if (AccessibilityObject* obj = get(renderer))
return obj;
+ // Don't create an object for this renderer if it's being destroyed.
+ if (renderer->beingDestroyed())
+ return nullptr;
RefPtr<AccessibilityObject> newObj = createFromRenderer(renderer);
// Will crash later if we have two objects for the same renderer.
Modified: trunk/Source/WebCore/accessibility/AccessibilityObject.cpp (291569 => 291570)
--- trunk/Source/WebCore/accessibility/AccessibilityObject.cpp 2022-03-21 18:01:29 UTC (rev 291569)
+++ trunk/Source/WebCore/accessibility/AccessibilityObject.cpp 2022-03-21 18:48:25 UTC (rev 291570)
@@ -448,6 +448,16 @@
});
}
+AccessibilityObject* AccessibilityObject::displayContentsParent() const
+{
+ auto* parentNode = node() ? node()->parentNode() : nullptr;
+ if (!is<Element>(parentNode) || !downcast<Element>(parentNode)->hasDisplayContents())
+ return nullptr;
+
+ auto* cache = axObjectCache();
+ return cache ? cache->getOrCreate(parentNode) : nullptr;
+}
+
AccessibilityObject* AccessibilityObject::previousSiblingUnignored(int limit) const
{
AccessibilityObject* previous;
@@ -575,7 +585,7 @@
{
if (!newChild)
return;
-
+
ASSERT(is<AccessibilityObject>(newChild));
if (!is<AccessibilityObject>(newChild))
return;
@@ -600,7 +610,12 @@
}
}
}
-
+
+ auto* displayContentsParent = child->displayContentsParent();
+ // To avoid double-inserting a child of a `display: contents` element, only insert if `this` is the rightful parent.
+ if (displayContentsParent && displayContentsParent != this)
+ return;
+
auto thisAncestorFlags = computeAncestorFlags();
child->initializeAncestorFlags(thisAncestorFlags);
setIsIgnoredFromParentDataForChild(child);
@@ -625,7 +640,7 @@
} else {
// Table component child-parent relationships often don't line up properly, hence the need for methods
// like parentTable() and parentRow(). Exclude them from this ASSERT.
- ASSERT((!isTableComponent(*child) && !isTableComponent(*this)) ? child->parentObject() == this : true);
+ ASSERT(isTableComponent(*child) || isTableComponent(*this) || child->parentObject() == this);
m_children.insert(index, child);
}
Modified: trunk/Source/WebCore/accessibility/AccessibilityObject.h (291569 => 291570)
--- trunk/Source/WebCore/accessibility/AccessibilityObject.h 2022-03-21 18:01:29 UTC (rev 291569)
+++ trunk/Source/WebCore/accessibility/AccessibilityObject.h 2022-03-21 18:48:25 UTC (rev 291570)
@@ -373,6 +373,7 @@
AccessibilityObject* nextSiblingUnignored(int limit) const override;
AccessibilityObject* previousSiblingUnignored(int limit) const override;
AccessibilityObject* parentObject() const override { return nullptr; }
+ AccessibilityObject* displayContentsParent() const;
AXCoreObject* parentObjectUnignored() const override;
AccessibilityObject* parentObjectIfExists() const override { return nullptr; }
static AccessibilityObject* firstAccessibleObjectFromNode(const Node*);
Modified: trunk/Source/WebCore/accessibility/AccessibilityRenderObject.cpp (291569 => 291570)
--- trunk/Source/WebCore/accessibility/AccessibilityRenderObject.cpp 2022-03-21 18:01:29 UTC (rev 291569)
+++ trunk/Source/WebCore/accessibility/AccessibilityRenderObject.cpp 2022-03-21 18:48:25 UTC (rev 291570)
@@ -491,11 +491,17 @@
if (m_renderer && isWebArea())
return cache->get(&m_renderer->view().frameView());
+ if (auto* displayContentsParent = this->displayContentsParent())
+ return displayContentsParent;
+
return cache->get(renderParentObject());
}
AccessibilityObject* AccessibilityRenderObject::parentObject() const
{
+ if (auto* displayContentsParent = this->displayContentsParent())
+ return displayContentsParent;
+
if (!m_renderer)
return nullptr;
@@ -508,15 +514,14 @@
if (parent)
return parent;
}
-
+
AXObjectCache* cache = axObjectCache();
if (!cache)
return nullptr;
+
+ if (auto* parentObject = renderParentObject())
+ return cache->getOrCreate(parentObject);
- RenderObject* parentObj = renderParentObject();
- if (parentObj)
- return cache->getOrCreate(parentObj);
-
// WebArea's parent should be the scroll view containing it.
if (isWebArea())
return cache->getOrCreate(&m_renderer->view().frameView());
@@ -537,7 +542,7 @@
}
#endif
- return AccessibilityNodeObject::parentObjectUnignored();
+ return AccessibilityObject::parentObjectUnignored();
}
bool AccessibilityRenderObject::isAttachment() const
@@ -3198,29 +3203,36 @@
}
#endif
-// Hidden children are those that are not rendered or visible, but are specifically marked as aria-hidden=false,
-// meaning that they should be exposed to the AX hierarchy.
-void AccessibilityRenderObject::addHiddenChildren()
+// Some elements don't have an associated render object, meaning they won't be picked up by a walk of the render tree.
+// For example, nodes that are `aria-hidden="false"` and `hidden`, or elements with `display: contents`.
+// This function will find and add these elements to the AX tree.
+void AccessibilityRenderObject::addNodeOnlyChildren()
{
Node* node = this->node();
if (!node)
return;
-
- // First do a quick run through to determine if we have any hidden nodes (most often we will not).
- // If we do have hidden nodes, we need to determine where to insert them so they match DOM order as close as possible.
- bool shouldInsertHiddenNodes = false;
+
+ auto nodeHasDisplayContents = [] (Node& node) {
+ return is<Element>(node) && downcast<Element>(node).hasDisplayContents();
+ };
+ // First do a quick run through to determine if we have any interesting nodes (most often we will not).
+ // If we do have any interesting nodes, we need to determine where to insert them so they match DOM order as close as possible.
+ bool hasNodeOnlyChildren = false;
for (Node* child = node->firstChild(); child; child = child->nextSibling()) {
- if (!child->renderer() && isNodeAriaVisible(child)) {
- shouldInsertHiddenNodes = true;
+ if (child->renderer())
+ continue;
+
+ if (nodeHasDisplayContents(*child) || isNodeAriaVisible(child)) {
+ hasNodeOnlyChildren = true;
break;
}
}
- if (!shouldInsertHiddenNodes)
+ if (!hasNodeOnlyChildren)
return;
// Iterate through all of the children, including those that may have already been added, and
- // try to insert hidden nodes in the correct place in the DOM order.
+ // try to insert the nodes in the correct place in the DOM order.
unsigned insertionIndex = 0;
for (Node* child = node->firstChild(); child; child = child->nextSibling()) {
if (child->renderer()) {
@@ -3239,7 +3251,7 @@
continue;
}
- if (!isNodeAriaVisible(child))
+ if (!nodeHasDisplayContents(*child) && !isNodeAriaVisible(child))
continue;
unsigned previousSize = m_children.size();
@@ -3311,9 +3323,7 @@
for (RefPtr<AccessibilityObject> object = firstChild(); object; object = object->nextSibling())
addChildIfNeeded(*object);
- m_subtreeDirty = false;
-
- addHiddenChildren();
+ addNodeOnlyChildren();
addAttachmentChildren();
addImageMapChildren();
addTextFieldChildren();
@@ -3322,11 +3332,11 @@
#if USE(ATSPI)
addListItemMarker();
#endif
-
#if PLATFORM(COCOA)
updateAttachmentViewParents();
#endif
-
+
+ m_subtreeDirty = false;
updateRoleAfterChildrenCreation();
}
Modified: trunk/Source/WebCore/accessibility/AccessibilityRenderObject.h (291569 => 291570)
--- trunk/Source/WebCore/accessibility/AccessibilityRenderObject.h 2022-03-21 18:01:29 UTC (rev 291569)
+++ trunk/Source/WebCore/accessibility/AccessibilityRenderObject.h 2022-03-21 18:48:25 UTC (rev 291570)
@@ -248,7 +248,7 @@
void offsetBoundingBoxForRemoteSVGElement(LayoutRect&) const;
bool supportsPath() const override;
- void addHiddenChildren();
+ void addNodeOnlyChildren();
void addTextFieldChildren();
void addImageMapChildren();
void addCanvasChildren();