Source/WebCore/ChangeLog

 12015-05-20 Chris Fleizach <cfleizach@apple.com>
 2
 3 AX: improve list heuristics (presentational use versus actual lists)
 4 https://bugs.webkit.org/show_bug.cgi?id=134187
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 Improve heuristics for list detection by:
 9 1. Not exposing lists without list markers (unless explicitly marked as lists)
 10 2. Recognizing css: before-content as list markers
 11
 12 Test: accessibility/list-detection2.html
 13
 14 * accessibility/AccessibilityList.cpp:
 15 (WebCore::AccessibilityList::isDescriptionList):
 16 (WebCore::AccessibilityList::childHasPseudoVisibleListItemMarkers):
 17 (WebCore::AccessibilityList::determineAccessibilityRole):
 18 * accessibility/AccessibilityList.h:
 19
1202015-05-20 Eric Carlson <eric.carlson@apple.com>
221
322 [Mac] inform media session of all state changes
184665

Source/WebCore/accessibility/AccessibilityList.cpp

3232#include "AXObjectCache.h"
3333#include "HTMLElement.h"
3434#include "HTMLNames.h"
 35#include "PseudoElement.h"
3536#include "RenderListItem.h"
3637#include "RenderObject.h"
3738#include "RenderStyle.h"

9798 return node && node->hasTagName(dlTag);
9899}
99100
 101bool AccessibilityList::childHasPseudoVisibleListItemMarkers(RenderObject* listItem)
 102{
 103 // Check if the list item has a pseudo-element that should be accessible (e.g. an image or text)
 104 Element* listItemElement = is<Element>(listItem->node()) ? downcast<Element>(listItem->node()) : nullptr;
 105 if (!listItemElement->beforePseudoElement())
 106 return false;
 107
 108 AccessibilityObject* axObj = axObjectCache()->getOrCreate(listItemElement->beforePseudoElement()->renderer());
 109 if (!axObj)
 110 return false;
 111
 112 if (!axObj->accessibilityIsIgnored())
 113 return true;
 114
 115 for (const auto& child : axObj->children()) {
 116 if (!child->accessibilityIsIgnored())
 117 return true;
 118 }
 119
 120 return false;
 121}
 122
100123AccessibilityRole AccessibilityList::determineAccessibilityRole()
101124{
102125 m_ariaRole = determineAriaRoleAttribute();

131154 listItemCount++;
132155 else if (child->roleValue() == ListItemRole) {
133156 RenderObject* listItem = child->renderer();
134  if (listItem && listItem->isListItem()) {
135  if (listItem->style().listStyleType() != NoneListStyle || listItem->style().listStyleImage())
 157 if (!listItem)
 158 continue;
 159
 160 // Rendered list items always count.
 161 if (listItem->isListItem()) {
 162 if (!hasVisibleMarkers && (listItem->style().listStyleType() != NoneListStyle || listItem->style().listStyleImage() || childHasPseudoVisibleListItemMarkers(listItem)))
136163 hasVisibleMarkers = true;
137164 listItemCount++;
 165 } else if (listItem->node() && listItem->node()->hasTagName(liTag)) {
 166 // Inline elements that are in a list with an explicit role should also count.
 167 if (m_ariaRole == ListRole)
 168 listItemCount++;
 169
 170 if (childHasPseudoVisibleListItemMarkers(listItem)) {
 171 hasVisibleMarkers = true;
 172 listItemCount++;
 173 }
138174 }
139175 }
140176 }
141177
142  bool unorderedList = isUnorderedList();
143178 // Non <ul> lists and ARIA lists only need to have one child.
144  // <ul> lists need to have 1 child, or visible markers.
145  if (!unorderedList || ariaRoleAttribute() != UnknownRole) {
 179 // <ul>, <ol> lists need to have visible markers.
 180 if (ariaRoleAttribute() != UnknownRole) {
146181 if (!listItemCount)
147182 role = GroupRole;
148  } else if (unorderedList && listItemCount <= 1 && !hasVisibleMarkers)
 183 } else if (!hasVisibleMarkers)
149184 role = GroupRole;
150185
151186 return role;
184562

Source/WebCore/accessibility/AccessibilityList.h

4949 virtual bool isList() const override { return true; }
5050 virtual bool computeAccessibilityIsIgnored() const override;
5151 virtual AccessibilityRole determineAccessibilityRole() override;
 52 bool childHasPseudoVisibleListItemMarkers(RenderObject*);
5253};
5354
5455} // namespace WebCore
184562

LayoutTests/ChangeLog

 12015-05-20 Chris Fleizach <cfleizach@apple.com>
 2
 3 AX: improve list heuristics (presentational use versus actual lists)
 4 https://bugs.webkit.org/show_bug.cgi?id=134187
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 * accessibility/list-detection-expected.txt:
 9 * accessibility/list-detection.html:
 10 * accessibility/list-detection2-expected.txt: Added.
 11 * accessibility/list-detection2.html: Added.
 12
1132015-05-20 Daniel Bates <dabates@apple.com>
214
315 AX: AutoFill button is not accessible with VoiceOver
184665

LayoutTests/accessibility/list-detection-expected.txt

3535PASS axElement.role == 'AXRole: AXList' is true
3636
3737
38 Unordered list with more than 1 item and no style is a list.
39 PASS axElement.role == 'AXRole: AXList' is true
 38Unordered list with more than 1 item and no style is not a list.
 39PASS axElement.role == 'AXRole: AXList' is false
4040
4141
4242Unordered list with 1 item and no style is not list.
184562

LayoutTests/accessibility/list-detection.html

4040<li>item</li>
4141</ul>
4242
43 <ul id="list9" style="list-style-type: none;" test-description="Unordered list with more than 1 item and no style is a list." is-list="true">
 43<ul id="list9" style="list-style-type: none;" test-description="Unordered list with more than 1 item and no style is not a list." is-list="false">
4444<li>item</li>
4545<li>item</li>
4646</ul>
184562

LayoutTests/accessibility/list-detection2-expected.txt

 1This tests that heurisitics for determining meaningful lists versus presentational lists result in the same computed role, regardless of platform.
 2
 3On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 4
 5
 6PASS successfullyParsed is true
 7
 8TEST COMPLETE
 9PASS: ul[role="list"] w/ explicit role and displayed inline -> list.
 10PASS: ul[role="list"] w/ explicit role but no markers -> list.
 11PASS: ul w/ default list markers -> list.
 12PASS: ol w/ default list counters -> list.
 13PASS: ul w/ list-style-image -> list.
 14PASS: ul w/ image content on ::before -> list.
 15PASS: ul w/ image content on inline ::before -> list.
 16PASS: ul w/ bullet content on ::before -> list.
 17PASS: ul w/ bullet content on inline ::before -> list.
 18PASS: ol w/ counter content on ::before -> list.
 19PASS: ol w/ counter content on inline ::before -> list.
 20PASS: ul w/ background image (NOT A LIST) -> group.
 21PASS: ul w/ background on ::before (NOT A LIST) -> group.
 22PASS: ul w/o explicit role and displayed inline, which defaults to no markers (NOT A LIST) -> group.
 23PASS: ol w/o explicit role and displayed inline, which defaults to no markers (NOT A LIST) -> group.
 24PASS: ul w/o explicit role and no markers (NOT A LIST) -> group.
 25
0

LayoutTests/accessibility/list-detection2.html

 1<!DOCTYPE HTML>
 2<html>
 3<head>
 4 <title>List; presentation heuristics</title>
 5 <meta charset="utf-8">
 6 <style type="text/css">
 7 html, body {
 8 font-size: 100%;
 9 }
 10 h1 {
 11 font-size: 1em;
 12 }
 13 .inline, .inline li {
 14 display: inline;
 15 }
 16 .nomarkers {
 17 list-style: none;
 18 }
 19 .imagemarkers {
 20 list-style-image: url(data:image/gif;base64,R0lGODlhEAARAJECAOHh4UpKSgAAAAAAACH5BAEAAAIALAAAAAAQABEAAAIllB8Zx63b4otSUWcvyuz5D4biSD7AiZroWSXa5r7CJNOvra1RAQA7);
 21 }
 22 .imagemarkersbefore li::before {
 23 content: url(data:image/gif;base64,R0lGODlhEAARAJECAOHh4UpKSgAAAAAAACH5BAEAAAIALAAAAAAQABEAAAIllB8Zx63b4otSUWcvyuz5D4biSD7AiZroWSXa5r7CJNOvra1RAQA7) " ";
 24 }
 25 .bulletmarkersbefore li::before {
 26 content: "• ";
 27 }
 28 .countermarkersbefore {
 29 counter-reset: number;
 30 list-style-type: none;
 31 }
 32 .countermarkersbefore li::before {
 33 counter-increment: number;
 34 content: counters(number,".") ". ";
 35 }
 36 .backgroundmarkers li {
 37 padding-left: 30px;
 38 background: transparent url(data:image/gif;base64,R0lGODlhEAARAJECAOHh4UpKSgAAAAAAACH5BAEAAAIALAAAAAAQABEAAAIllB8Zx63b4otSUWcvyuz5D4biSD7AiZroWSXa5r7CJNOvra1RAQA7) left center no-repeat;
 39 }
 40 .backgroundmarkersbefore li::before {
 41 display: inline-block;
 42 content: " ";
 43 width: 17px;
 44 height: 17px;
 45 background: transparent url(data:image/gif;base64,R0lGODlhEAARAJECAOHh4UpKSgAAAAAAACH5BAEAAAIALAAAAAAQABEAAAIllB8Zx63b4otSUWcvyuz5D4biSD7AiZroWSXa5r7CJNOvra1RAQA7) left center no-repeat;
 46 }
 47</style>
 48</head>
 49<body onload="runTest();">
 50<script src="../resources/js-test-pre.js"></script>
 51
 52<div id="examples">
 53
 54 <!-- @data-role is the expectation -->
 55
 56 <h1>list because of explicit role, despite style and lack of list markers</h1>
 57 <ul role="list" data-role="list" class="ex inline" data-note=" w/ explicit role and displayed inline">
 58 <li>foo</li>
 59 <li>bar</li>
 60 <li>baz</li>
 61 </ul>
 62 <ul role="list" data-role="list" class="ex nomarkers" data-note=" w/ explicit role but no markers">
 63 <li>foo</li>
 64 <li>bar</li>
 65 <li>baz</li>
 66 </ul>
 67
 68 <h1>list because of rendered list markers (exposed as AXListMarker)</h1>
 69 <ul data-role="list" class="ex" data-note=" w/ default list markers">
 70 <li>foo</li>
 71 <li>bar</li>
 72 <li>baz</li>
 73 </ul>
 74 <ol data-role="list" class="ex" data-note=" w/ default list counters">
 75 <li>foo</li>
 76 <li>bar</li>
 77 <li>baz</li>
 78 </ol>
 79 <ul data-role="list" class="ex imagemarkers" data-note=" w/ list-style-image">
 80 <li>foo</li>
 81 <li>bar</li>
 82 <li>baz</li>
 83 </ul>
 84
 85 <h1>list because these list markers consist of CSS-generated content on ::before pseudo element. Note: WebKit is not exposing these as AXListMarker; need another bug for that.</h1>
 86 <ul data-role="list" class="ex nomarkers imagemarkersbefore" data-note=" w/ image content on ::before">
 87 <li>foo</li>
 88 <li>bar</li>
 89 <li>baz</li>
 90 </ul>
 91 <ul data-role="list" class="ex nomarkers inline imagemarkersbefore" data-note=" w/ image content on inline ::before">
 92 <li>foo</li>
 93 <li>bar</li>
 94 <li>baz</li>
 95 </ul>
 96 <ul data-role="list" class="ex nomarkers bulletmarkersbefore" data-note=" w/ bullet content on ::before">
 97 <li>foo</li>
 98 <li>bar</li>
 99 <li>baz</li>
 100 </ul>
 101 <ul data-role="list" class="ex nomarkers inline bulletmarkersbefore" data-note=" w/ bullet content on inline ::before">
 102 <li>foo</li>
 103 <li>bar</li>
 104 <li>baz</li>
 105 </ul>
 106 <ol data-role="list" class="ex nomarkers countermarkersbefore" data-note=" w/ counter content on ::before">
 107 <li>foo</li>
 108 <li>bar</li>
 109 <li>baz</li>
 110 </ol>
 111 <ol data-role="list" class="ex nomarkers inline countermarkersbefore" data-note=" w/ counter content on inline ::before">
 112 <li>foo</li>
 113 <li>bar</li>
 114 <li>baz</li>
 115 </ol>
 116
 117 <p>Since many web pages suffer from "list-itis" and some users have noted that they don't want to hear about so many lists, any UL or OL that does not match one of the above heuristics should not be exposed as a list. Chances are that they are just presentational lists using the elements for the sake of a styling hook.</p>
 118
 119
 120 <h1>EXPLICIT FAILURE CASES: These next examples are <strong>NOT</strong> lists because these "list markers" are faked as background images. One is even a background image on a ::before pseudo-element, but that does not count as a listmarker. List markers should only be inferred by the <code>content</code> property or the <code>list-style</code> properties.</h1>
 121 <ul data-role="group" class="ex nomarkers backgroundmarkers" data-note=" w/ background image (NOT A LIST)">
 122 <li>foo</li>
 123 <li>bar</li>
 124 <li>baz</li>
 125 </ul>
 126 <ul data-role="group" class="ex nomarkers backgroundmarkersbefore" data-note=" w/ background on ::before (NOT A LIST)">
 127 <li>foo</li>
 128 <li>bar</li>
 129 <li>baz</li>
 130 </ul>
 131
 132 <h1>EXPLICIT FAILURE CASES: These next examples are <strong>NOT</strong> there is no explicit role or any style or list markers to indicate this is actually intended as a list.</h1>
 133 <ul data-role="group" class="ex inline" data-note=" w/o explicit role and displayed inline, which defaults to no markers (NOT A LIST)">
 134 <li>foo</li>
 135 <li>bar</li>
 136 <li>baz</li>
 137 </ul>
 138 <ol data-role="group" class="ex inline" data-note=" w/o explicit role and displayed inline, which defaults to no markers (NOT A LIST)">
 139 <li>foo</li>
 140 <li>bar</li>
 141 <li>baz</li>
 142 </ol>
 143 <ul data-role="group" class="ex nomarkers" data-note=" w/o explicit role and no markers (NOT A LIST)">
 144 <li>foo</li>
 145 <li>bar</li>
 146 <li>baz</li>
 147 </ul>
 148
 149
 150</div>
 151
 152<div id="console"></div>
 153<script>
 154function runTest() {
 155 if (window.testRunner && window.accessibilityController) {
 156 description("This tests that heurisitics for determining meaningful lists versus presentational lists result in the same computed role, regardless of platform.")
 157 var examples = document.querySelectorAll(".ex");
 158 var el, contentAttrRoleString, axElement, computedAriaRole, output, expectedRole, expectation, result, note;
 159 for (var i = 0, c = examples.length; i < c; i++) {
 160 el = examples[i];
 161 el.id = "ex" + i
 162
 163 axElement = accessibilityController.accessibleElementById(el.id);
 164 if (!axElement)
 165 continue;
 166
 167 computedAriaRole = axElement.computedRoleString;
 168
 169 contentAttrRoleString = el.getAttribute("role");
 170 note = el.getAttribute("data-note")
 171 output = el.tagName.toLowerCase() + (contentAttrRoleString ? ("[role=\""+contentAttrRoleString+"\"]") : "") + (note ? note : "");
 172 output += " -> ";
 173 output += computedAriaRole;
 174 output += ". ";
 175
 176 expectedRole = "";
 177 if (el.hasAttribute("data-role")) {
 178 expectedRole = el.getAttribute("data-role");
 179 }
 180
 181 expectation = expectedRole;
 182 matchedResults = (computedAriaRole == expectedRole)
 183
 184 result = document.getElementById('console');
 185 if (matchedResults) {
 186 result.innerText += "PASS: " + output + "\n";
 187 } else {
 188 result.innerText += "FAIL: " + output + "Expected: " + expectation + ".\n";
 189 }
 190 }
 191
 192 // Once tests are complete, hide all the example markup.
 193 examples = document.querySelectorAll(".ex, #examples");
 194 for (var i = 0, c = examples.length; i < c; i++)
 195 examples[i].style.display = "none";
 196 }
 197}
 198</script>
 199<script src="../resources/js-test-post.js"></script>
 200</body>
 201</html>
 202
 203
0