Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(66)

Side by Side Diff: third_party/WebKit/Source/modules/accessibility/AXNodeObject.cpp

Issue 1435113003: Make use of new AX name calc in Chromium. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix ChromeVox and Automation API tests Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2012, Google Inc. All rights reserved. 2 * Copyright (C) 2012, Google Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions 5 * modification, are permitted provided that the following conditions
6 * are met: 6 * are met:
7 * 7 *
8 * 1. Redistributions of source code must retain the above copyright 8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer. 9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright 10 * 2. Redistributions in binary form must reproduce the above copyright
(...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after
150 return ariaLabelledby; 150 return ariaLabelledby;
151 151
152 const AtomicString& ariaLabel = getAttribute(aria_labelAttr); 152 const AtomicString& ariaLabel = getAttribute(aria_labelAttr);
153 if (!ariaLabel.isEmpty()) 153 if (!ariaLabel.isEmpty())
154 return ariaLabel; 154 return ariaLabel;
155 155
156 return String(); 156 return String();
157 } 157 }
158 158
159 159
160 void AXNodeObject::ariaLabelledbyElements(WillBeHeapVector<RawPtrWillBeMember<El ement>>& elements) const 160 void AXNodeObject::ariaLabelledbyElementVector(WillBeHeapVector<RawPtrWillBeMemb er<Element>>& elements) const
161 { 161 {
162 // Try both spellings, but prefer aria-labelledby, which is the official spe c. 162 // Try both spellings, but prefer aria-labelledby, which is the official spe c.
163 elementsFromAttribute(elements, aria_labelledbyAttr); 163 elementsFromAttribute(elements, aria_labelledbyAttr);
164 if (!elements.size()) 164 if (!elements.size())
165 elementsFromAttribute(elements, aria_labeledbyAttr); 165 elementsFromAttribute(elements, aria_labeledbyAttr);
166 } 166 }
167 167
168 bool AXNodeObject::computeAccessibilityIsIgnored(IgnoredReasons* ignoredReasons) const 168 bool AXNodeObject::computeAccessibilityIsIgnored(IgnoredReasons* ignoredReasons) const
169 { 169 {
170 #if ENABLE(ASSERT) 170 #if ENABLE(ASSERT)
171 // Double-check that an AXObject is never accessed before 171 // Double-check that an AXObject is never accessed before
172 // it's been initialized. 172 // it's been initialized.
173 ASSERT(m_initialized); 173 ASSERT(m_initialized);
174 #endif 174 #endif
175 175
176 // If this element is within a parent that cannot have children, it should n ot be exposed. 176 // If this element is within a parent that cannot have children, it should n ot be exposed.
177 if (isDescendantOfLeafNode()) { 177 if (isDescendantOfLeafNode()) {
178 if (ignoredReasons) 178 if (ignoredReasons)
179 ignoredReasons->append(IgnoredReason(AXAncestorIsLeafNode, leafNodeA ncestor())); 179 ignoredReasons->append(IgnoredReason(AXAncestorIsLeafNode, leafNodeA ncestor()));
180 return true; 180 return true;
181 } 181 }
182 182
183 // Ignore labels that are already referenced by a control's title UI element . 183 // Ignore labels that are already referenced by a control.
184 AXObject* controlObject = correspondingControlForLabelElement(); 184 AXObject* controlObject = correspondingControlForLabelElement();
185 if (controlObject && !controlObject->deprecatedExposesTitleUIElement() && co ntrolObject->isCheckboxOrRadio()) { 185 if (controlObject) {
186 if (ignoredReasons) { 186 AXNameFrom controlNameFrom;
187 HTMLLabelElement* label = labelElementContainer(); 187 AXObject::AXObjectVector controlNameObjects;
188 if (label && !label->isSameNode(node())) { 188 controlObject->name(controlNameFrom, &controlNameObjects);
189 AXObject* labelAXObject = axObjectCache().getOrCreate(label); 189 if (controlNameFrom != AXNameFromRelatedElement && controlObject->isChec kboxOrRadio()) {
190 ignoredReasons->append(IgnoredReason(AXLabelContainer, labelAXOb ject)); 190 if (ignoredReasons) {
191 HTMLLabelElement* label = labelElementContainer();
192 if (label && !label->isSameNode(node())) {
193 AXObject* labelAXObject = axObjectCache().getOrCreate(label) ;
194 ignoredReasons->append(IgnoredReason(AXLabelContainer, label AXObject));
195 }
196
197 ignoredReasons->append(IgnoredReason(AXLabelFor, controlObject)) ;
191 } 198 }
192 199 return true;
193 ignoredReasons->append(IgnoredReason(AXLabelFor, controlObject));
194 } 200 }
195 return true;
196 } 201 }
197 202
198 Element* element = node()->isElementNode() ? toElement(node()) : node()->par entElement(); 203 Element* element = node()->isElementNode() ? toElement(node()) : node()->par entElement();
199 if (!layoutObject() 204 if (!layoutObject()
200 && (!element || !element->isInCanvasSubtree()) 205 && (!element || !element->isInCanvasSubtree())
201 && !equalIgnoringCase(getAttribute(aria_hiddenAttr), "false")) { 206 && !equalIgnoringCase(getAttribute(aria_hiddenAttr), "false")) {
202 if (ignoredReasons) 207 if (ignoredReasons)
203 ignoredReasons->append(IgnoredReason(AXNotRendered)); 208 ignoredReasons->append(IgnoredReason(AXNotRendered));
204 return true; 209 return true;
205 } 210 }
(...skipping 926 matching lines...) Expand 10 before | Expand all | Expand 10 after
1132 Node* node = this->node(); 1137 Node* node = this->node();
1133 if (!isHTMLCanvasElement(node)) 1138 if (!isHTMLCanvasElement(node))
1134 return false; 1139 return false;
1135 1140
1136 // If it has any children that are elements, we'll assume it might be fallba ck 1141 // If it has any children that are elements, we'll assume it might be fallba ck
1137 // content. If it has no children or its only children are not elements 1142 // content. If it has no children or its only children are not elements
1138 // (e.g. just text nodes), it doesn't have fallback content. 1143 // (e.g. just text nodes), it doesn't have fallback content.
1139 return ElementTraversal::firstChild(*node); 1144 return ElementTraversal::firstChild(*node);
1140 } 1145 }
1141 1146
1142 bool AXNodeObject::deprecatedExposesTitleUIElement() const
1143 {
1144 if (!isControl())
1145 return false;
1146
1147 // If this control is ignored (because it's invisible),
1148 // then the label needs to be exposed so it can be visible to accessibility.
1149 if (accessibilityIsIgnored())
1150 return true;
1151
1152 // ARIA: section 2A, bullet #3 says if aria-labelledby or aria-label appears , it should
1153 // override the "label" element association.
1154 bool hasTextAlternative = (!ariaLabelledbyAttribute().isEmpty() || !getAttri bute(aria_labelAttr).isEmpty());
1155
1156 // Checkboxes and radio buttons use the text of their title ui element as th eir own AXTitle.
1157 // This code controls whether the title ui element should appear in the AX t ree (usually, no).
1158 // It should appear if the control already has a label (which will be used a s the AXTitle instead).
1159 if (isCheckboxOrRadio())
1160 return hasTextAlternative;
1161
1162 // When controls have their own descriptions, the title element should be ig nored.
1163 if (hasTextAlternative)
1164 return false;
1165
1166 return true;
1167 }
1168
1169 int AXNodeObject::headingLevel() const 1147 int AXNodeObject::headingLevel() const
1170 { 1148 {
1171 // headings can be in block flow and non-block flow 1149 // headings can be in block flow and non-block flow
1172 Node* node = this->node(); 1150 Node* node = this->node();
1173 if (!node) 1151 if (!node)
1174 return 0; 1152 return 0;
1175 1153
1176 if (roleValue() == HeadingRole && hasAttribute(aria_levelAttr)) { 1154 if (roleValue() == HeadingRole && hasAttribute(aria_levelAttr)) {
1177 int level = getAttribute(aria_levelAttr).toInt(); 1155 int level = getAttribute(aria_levelAttr).toInt();
1178 if (level >= 1 && level <= 9) 1156 if (level >= 1 && level <= 9)
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
1239 1217
1240 const AtomicString& ariaAutoComplete = getAttribute(aria_autocompleteAttr).l ower(); 1218 const AtomicString& ariaAutoComplete = getAttribute(aria_autocompleteAttr).l ower();
1241 1219
1242 if (ariaAutoComplete == "inline" || ariaAutoComplete == "list" 1220 if (ariaAutoComplete == "inline" || ariaAutoComplete == "list"
1243 || ariaAutoComplete == "both") 1221 || ariaAutoComplete == "both")
1244 return ariaAutoComplete; 1222 return ariaAutoComplete;
1245 1223
1246 return String(); 1224 return String();
1247 } 1225 }
1248 1226
1249 String AXNodeObject::deprecatedPlaceholder() const
1250 {
1251 String placeholder;
1252 if (node()) {
1253 if (isHTMLInputElement(*node())) {
1254 HTMLInputElement* inputElement = toHTMLInputElement(node());
1255 placeholder = inputElement->strippedPlaceholder();
1256 } else if (isHTMLTextAreaElement(*node())) {
1257 HTMLTextAreaElement* textAreaElement = toHTMLTextAreaElement(node()) ;
1258 placeholder = textAreaElement->strippedPlaceholder();
1259 }
1260 }
1261 return placeholder;
1262 }
1263
1264 AccessibilityOrientation AXNodeObject::orientation() const 1227 AccessibilityOrientation AXNodeObject::orientation() const
1265 { 1228 {
1266 const AtomicString& ariaOrientation = getAttribute(aria_orientationAttr); 1229 const AtomicString& ariaOrientation = getAttribute(aria_orientationAttr);
1267 AccessibilityOrientation orientation = AccessibilityOrientationUndefined; 1230 AccessibilityOrientation orientation = AccessibilityOrientationUndefined;
1268 if (equalIgnoringCase(ariaOrientation, "horizontal")) 1231 if (equalIgnoringCase(ariaOrientation, "horizontal"))
1269 orientation = AccessibilityOrientationHorizontal; 1232 orientation = AccessibilityOrientationHorizontal;
1270 else if (equalIgnoringCase(ariaOrientation, "vertical")) 1233 else if (equalIgnoringCase(ariaOrientation, "vertical"))
1271 orientation = AccessibilityOrientationVertical; 1234 orientation = AccessibilityOrientationVertical;
1272 1235
1273 switch (roleValue()) { 1236 switch (roleValue()) {
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
1314 1277
1315 if (isNativeTextControl() && (isHTMLTextAreaElement(*node) || isHTMLInputEle ment(*node))) 1278 if (isNativeTextControl() && (isHTMLTextAreaElement(*node) || isHTMLInputEle ment(*node)))
1316 return toHTMLTextFormControlElement(*node).value(); 1279 return toHTMLTextFormControlElement(*node).value();
1317 1280
1318 if (!node->isElementNode()) 1281 if (!node->isElementNode())
1319 return String(); 1282 return String();
1320 1283
1321 return toElement(node)->innerText(); 1284 return toElement(node)->innerText();
1322 } 1285 }
1323 1286
1324 AXObject* AXNodeObject::deprecatedTitleUIElement() const
1325 {
1326 if (!node() || !node()->isElementNode())
1327 return 0;
1328
1329 if (isFieldset())
1330 return axObjectCache().getOrCreate(toHTMLFieldSetElement(node())->legend ());
1331
1332 HTMLLabelElement* label = labelForElement(toElement(node()));
1333 if (label)
1334 return axObjectCache().getOrCreate(label);
1335
1336 return 0;
1337 }
1338
1339 AccessibilityButtonState AXNodeObject::checkboxOrRadioValue() const 1287 AccessibilityButtonState AXNodeObject::checkboxOrRadioValue() const
1340 { 1288 {
1341 if (isNativeCheckboxOrRadio()) 1289 if (isNativeCheckboxOrRadio())
1342 return isChecked() ? ButtonStateOn : ButtonStateOff; 1290 return isChecked() ? ButtonStateOn : ButtonStateOff;
1343 1291
1344 return AXObject::checkboxOrRadioValue(); 1292 return AXObject::checkboxOrRadioValue();
1345 } 1293 }
1346 1294
1347 RGBA32 AXNodeObject::colorValue() const 1295 RGBA32 AXNodeObject::colorValue() const
1348 { 1296 {
(...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after
1483 Decimal step = toHTMLInputElement(*node()).createStepRange(RejectAny).step() ; 1431 Decimal step = toHTMLInputElement(*node()).createStepRange(RejectAny).step() ;
1484 return step.toString().toFloat(); 1432 return step.toString().toFloat();
1485 } 1433 }
1486 1434
1487 String AXNodeObject::stringValue() const 1435 String AXNodeObject::stringValue() const
1488 { 1436 {
1489 Node* node = this->node(); 1437 Node* node = this->node();
1490 if (!node) 1438 if (!node)
1491 return String(); 1439 return String();
1492 1440
1493 if (ariaRoleAttribute() == StaticTextRole) {
1494 String staticText = text();
1495 if (!staticText.length())
1496 staticText = deprecatedTextUnderElement(TextUnderElementAll);
1497 return staticText;
1498 }
1499
1500 if (node->isTextNode())
1501 return deprecatedTextUnderElement(TextUnderElementAll);
1502
1503 return stringValueOfControl();
1504 }
1505
1506 String AXNodeObject::stringValueOfControl() const
1507 {
1508 Node* node = this->node();
1509 if (!node)
1510 return String();
1511
1512 if (isHTMLSelectElement(*node)) { 1441 if (isHTMLSelectElement(*node)) {
1513 HTMLSelectElement& selectElement = toHTMLSelectElement(*node); 1442 HTMLSelectElement& selectElement = toHTMLSelectElement(*node);
1514 int selectedIndex = selectElement.selectedIndex(); 1443 int selectedIndex = selectElement.selectedIndex();
1515 const WillBeHeapVector<RawPtrWillBeMember<HTMLElement>>& listItems = sel ectElement.listItems(); 1444 const WillBeHeapVector<RawPtrWillBeMember<HTMLElement>>& listItems = sel ectElement.listItems();
1516 if (selectedIndex >= 0 && static_cast<size_t>(selectedIndex) < listItems .size()) { 1445 if (selectedIndex >= 0 && static_cast<size_t>(selectedIndex) < listItems .size()) {
1517 const AtomicString& overriddenDescription = listItems[selectedIndex] ->fastGetAttribute(aria_labelAttr); 1446 const AtomicString& overriddenDescription = listItems[selectedIndex] ->fastGetAttribute(aria_labelAttr);
1518 if (!overriddenDescription.isNull()) 1447 if (!overriddenDescription.isNull())
1519 return overriddenDescription; 1448 return overriddenDescription;
1520 } 1449 }
1521 if (!selectElement.multiple()) 1450 if (!selectElement.multiple())
(...skipping 20 matching lines...) Expand all
1542 { 1471 {
1543 WillBeHeapVector<RawPtrWillBeMember<Element>> elements; 1472 WillBeHeapVector<RawPtrWillBeMember<Element>> elements;
1544 elementsFromAttribute(elements, aria_describedbyAttr); 1473 elementsFromAttribute(elements, aria_describedbyAttr);
1545 1474
1546 return accessibilityDescriptionForElements(elements); 1475 return accessibilityDescriptionForElements(elements);
1547 } 1476 }
1548 1477
1549 String AXNodeObject::ariaLabelledbyAttribute() const 1478 String AXNodeObject::ariaLabelledbyAttribute() const
1550 { 1479 {
1551 WillBeHeapVector<RawPtrWillBeMember<Element>> elements; 1480 WillBeHeapVector<RawPtrWillBeMember<Element>> elements;
1552 ariaLabelledbyElements(elements); 1481 ariaLabelledbyElementVector(elements);
1553 1482
1554 return accessibilityDescriptionForElements(elements); 1483 return accessibilityDescriptionForElements(elements);
1555 } 1484 }
1556 1485
1557 AccessibilityRole AXNodeObject::ariaRoleAttribute() const 1486 AccessibilityRole AXNodeObject::ariaRoleAttribute() const
1558 { 1487 {
1559 return m_ariaRole; 1488 return m_ariaRole;
1560 } 1489 }
1561 1490
1562 // When building the textUnderElement for an object, determine whether or not
1563 // we should include the inner text of this given descendant object or skip it.
1564 static bool shouldUseAccessibilityObjectInnerText(AXObject* obj)
1565 {
1566 // Consider this hypothetical example:
1567 // <div tabindex=0>
1568 // <h2>
1569 // Table of contents
1570 // </h2>
1571 // <a href="#start">Jump to start of book</a>
1572 // <ul>
1573 // <li><a href="#1">Chapter 1</a></li>
1574 // <li><a href="#1">Chapter 2</a></li>
1575 // </ul>
1576 // </div>
1577 //
1578 // The goal is to return a reasonable title for the outer container div, bec ause
1579 // it's focusable - but without making its title be the full inner text, whi ch is
1580 // quite long. As a heuristic, skip links, controls, and elements that are u sually
1581 // containers with lots of children.
1582
1583 // Skip hidden children
1584 if (obj->isInertOrAriaHidden())
1585 return false;
1586
1587 // If something doesn't expose any children, then we can always take the inn er text content.
1588 // This is what we want when someone puts an <a> inside a <button> for examp le.
1589 if (obj->isDescendantOfLeafNode())
1590 return true;
1591
1592 // Skip focusable children, so we don't include the text of links and contro ls.
1593 if (obj->canSetFocusAttribute())
1594 return false;
1595
1596 // Skip big container elements like lists, tables, etc.
1597 if (obj->isList() || obj->isAXTable() || obj->isTree() || obj->isCanvas())
1598 return false;
1599
1600 return true;
1601 }
1602
1603 // Returns the nearest LayoutBlockFlow ancestor which does not have an 1491 // Returns the nearest LayoutBlockFlow ancestor which does not have an
1604 // inlineBoxWrapper - i.e. is not itself an inline object. 1492 // inlineBoxWrapper - i.e. is not itself an inline object.
1605 static LayoutBlockFlow* nonInlineBlockFlow(LayoutObject* object) 1493 static LayoutBlockFlow* nonInlineBlockFlow(LayoutObject* object)
1606 { 1494 {
1607 LayoutObject* current = object; 1495 LayoutObject* current = object;
1608 while (current) { 1496 while (current) {
1609 if (current->isLayoutBlockFlow()) { 1497 if (current->isLayoutBlockFlow()) {
1610 LayoutBlockFlow* blockFlow = toLayoutBlockFlow(current); 1498 LayoutBlockFlow* blockFlow = toLayoutBlockFlow(current);
1611 if (!blockFlow->inlineBoxWrapper()) 1499 if (!blockFlow->inlineBoxWrapper())
1612 return blockFlow; 1500 return blockFlow;
(...skipping 11 matching lines...) Expand all
1624 { 1512 {
1625 if (!r1 || !r2) 1513 if (!r1 || !r2)
1626 return false; 1514 return false;
1627 if (!r1->isInline() || !r2->isInline()) 1515 if (!r1->isInline() || !r2->isInline())
1628 return false; 1516 return false;
1629 LayoutBlockFlow* b1 = nonInlineBlockFlow(r1); 1517 LayoutBlockFlow* b1 = nonInlineBlockFlow(r1);
1630 LayoutBlockFlow* b2 = nonInlineBlockFlow(r2); 1518 LayoutBlockFlow* b2 = nonInlineBlockFlow(r2);
1631 return b1 && b2 && b1 == b2; 1519 return b1 && b2 && b1 == b2;
1632 } 1520 }
1633 1521
1634 String AXNodeObject::deprecatedTextUnderElement(TextUnderElementMode mode) const
1635 {
1636 Node* node = this->node();
1637 if (node && node->isTextNode())
1638 return toText(node)->wholeText();
1639
1640 StringBuilder builder;
1641 AXObject* previous = nullptr;
1642 for (AXObject* child = firstChild(); child; child = child->nextSibling()) {
1643 if (!shouldUseAccessibilityObjectInnerText(child))
1644 continue;
1645
1646 if (child->isAXNodeObject()) {
1647 HeapVector<Member<AccessibilityText>> textOrder;
1648 toAXNodeObject(child)->deprecatedAlternativeText(textOrder);
1649 if (textOrder.size() > 0) {
1650 builder.append(textOrder[0]->text());
1651 if (mode == TextUnderElementAny)
1652 break;
1653 continue;
1654 }
1655 }
1656
1657 // If we're going between two layoutObjects that are in separate LayoutB oxes, add
1658 // whitespace if it wasn't there already. Intuitively if you have
1659 // <span>Hello</span><span>World</span>, those are part of the same Layo utBox
1660 // so we should return "HelloWorld", but given <div>Hello</div><div>Worl d</div> the
1661 // strings are in separate boxes so we should return "Hello World".
1662 if (previous && builder.length() && !isHTMLSpace(builder[builder.length( ) - 1])) {
1663 if (!isInSameNonInlineBlockFlow(child->layoutObject(), previous->lay outObject()))
1664 builder.append(' ');
1665 }
1666
1667 builder.append(child->deprecatedTextUnderElement(mode));
1668 previous = child;
1669
1670 if (mode == TextUnderElementAny && !builder.isEmpty())
1671 break;
1672 }
1673
1674 return builder.toString();
1675 }
1676
1677 AXObject* AXNodeObject::findChildWithTagName(const HTMLQualifiedName& tagName) c onst 1522 AXObject* AXNodeObject::findChildWithTagName(const HTMLQualifiedName& tagName) c onst
1678 { 1523 {
1679 for (AXObject* child = firstChild(); child; child = child->nextSibling()) { 1524 for (AXObject* child = firstChild(); child; child = child->nextSibling()) {
1680 Node* childNode = child->node(); 1525 Node* childNode = child->node();
1681 if (childNode && childNode->hasTagName(tagName)) 1526 if (childNode && childNode->hasTagName(tagName))
1682 return child; 1527 return child;
1683 } 1528 }
1684 return 0; 1529 return 0;
1685 } 1530 }
1686 1531
1687 String AXNodeObject::deprecatedAccessibilityDescription() const
1688 {
1689 // Static text should not have a description, it should only have a stringVa lue.
1690 if (roleValue() == StaticTextRole)
1691 return String();
1692
1693 String ariaDescription = ariaAccessibilityDescription();
1694 if (!ariaDescription.isEmpty())
1695 return ariaDescription;
1696
1697 if (isImage() || isInputImage() || isNativeImage() || isCanvas()) {
1698 // Images should use alt as long as the attribute is present, even if em pty.
1699 // Otherwise, it should fallback to other methods, like the title attrib ute.
1700 const AtomicString& alt = getAttribute(altAttr);
1701 if (!alt.isNull())
1702 return alt;
1703 }
1704
1705 // An element's descriptive text is comprised of deprecatedTitle() (what's v isible on the screen) and deprecatedAccessibilityDescription() (other descriptiv e text).
1706 // Both are used to generate what a screen reader speaks.
1707 // If this point is reached (i.e. there's no accessibilityDescription) and t here's no deprecatedTitle(), we should fallback to using the title attribute.
1708 // The title attribute is normally used as help text (because it is a toolti p), but if there is nothing else available, this should be used (according to AR IA).
1709 if (deprecatedTitle(TextUnderElementAny).isEmpty())
1710 return getAttribute(titleAttr);
1711
1712 if (roleValue() == FigureRole) {
1713 AXObject* figcaption = findChildWithTagName(figcaptionTag);
1714 if (figcaption)
1715 return figcaption->deprecatedAccessibilityDescription();
1716 }
1717
1718 return String();
1719 }
1720
1721 String AXNodeObject::deprecatedTitle(TextUnderElementMode mode) const
1722 {
1723 Node* node = this->node();
1724 if (!node)
1725 return String();
1726
1727 bool isInputElement = isHTMLInputElement(*node);
1728 if (isInputElement) {
1729 HTMLInputElement& input = toHTMLInputElement(*node);
1730 if (input.isTextButton())
1731 return input.valueWithDefault();
1732 }
1733
1734 if (isInputElement || AXObject::isARIAInput(ariaRoleAttribute()) || isContro l()) {
1735 HTMLLabelElement* label = labelForElement(toElement(node));
1736 if (label && !deprecatedExposesTitleUIElement())
1737 return label->innerText();
1738 }
1739
1740 // If this node isn't laid out, there's no inner text we can extract from a select element.
1741 if (!isAXLayoutObject() && isHTMLSelectElement(*node))
1742 return String();
1743
1744 switch (roleValue()) {
1745 case PopUpButtonRole:
1746 // Native popup buttons should not use their button children's text as a title. That value is retrieved through stringValue().
1747 if (isHTMLSelectElement(*node))
1748 return String();
1749 case ButtonRole:
1750 case ToggleButtonRole:
1751 case CheckBoxRole:
1752 case LineBreakRole:
1753 case ListBoxOptionRole:
1754 case ListItemRole:
1755 case MenuButtonRole:
1756 case MenuItemRole:
1757 case MenuItemCheckBoxRole:
1758 case MenuItemRadioRole:
1759 case RadioButtonRole:
1760 case SwitchRole:
1761 case TabRole:
1762 return deprecatedTextUnderElement(mode);
1763 // SVGRoots should not use the text under itself as a title. That could incl ude the text of objects like <text>.
1764 case SVGRootRole:
1765 return String();
1766 case FigureRole: {
1767 AXObject* figcaption = findChildWithTagName(figcaptionTag);
1768 if (figcaption)
1769 return figcaption->deprecatedTextUnderElement();
1770 }
1771 default:
1772 break;
1773 }
1774
1775 if (isHeading() || isLink())
1776 return deprecatedTextUnderElement(mode);
1777
1778 // If it's focusable but it's not content editable or a known control type, then it will appear to
1779 // the user as a single atomic object, so we should use its text as the defa ult title.
1780 if (isGenericFocusableElement())
1781 return deprecatedTextUnderElement(mode);
1782
1783 return String();
1784 }
1785
1786 String AXNodeObject::deprecatedHelpText() const
1787 {
1788 Node* node = this->node();
1789 if (!node)
1790 return String();
1791
1792 const AtomicString& ariaHelp = getAttribute(aria_helpAttr);
1793 if (!ariaHelp.isEmpty())
1794 return ariaHelp;
1795
1796 String describedBy = ariaDescribedByAttribute();
1797 if (!describedBy.isEmpty())
1798 return describedBy;
1799
1800 String description = deprecatedAccessibilityDescription();
1801 for (Node* curr = node; curr; curr = curr->parentNode()) {
1802 if (curr->isHTMLElement()) {
1803 const AtomicString& summary = toElement(curr)->getAttribute(summaryA ttr);
1804 if (!summary.isEmpty())
1805 return summary;
1806
1807 // The title attribute should be used as help text unless it is alre ady being used as descriptive text.
1808 const AtomicString& title = toElement(curr)->getAttribute(titleAttr) ;
1809 if (!title.isEmpty() && description != title)
1810 return title;
1811 }
1812
1813 // Only take help text from an ancestor element if its a group or an unk nown role. If help was
1814 // added to those kinds of elements, it is likely it was meant for a chi ld element.
1815 AXObject* axObj = axObjectCache().getOrCreate(curr);
1816 if (axObj) {
1817 AccessibilityRole role = axObj->roleValue();
1818 if (role != GroupRole && role != UnknownRole)
1819 break;
1820 }
1821 }
1822
1823 return String();
1824 }
1825
1826 String AXNodeObject::computedName() const
1827 {
1828 String title = this->deprecatedTitle(TextUnderElementAll);
1829
1830 String titleUIText;
1831 if (title.isEmpty()) {
1832 AXObject* titleUIElement = this->deprecatedTitleUIElement();
1833 if (titleUIElement) {
1834 titleUIText = titleUIElement->deprecatedTextUnderElement();
1835 if (!titleUIText.isEmpty())
1836 return titleUIText;
1837 }
1838 }
1839
1840 String description = deprecatedAccessibilityDescription();
1841 if (!description.isEmpty())
1842 return description;
1843
1844 if (!title.isEmpty())
1845 return title;
1846
1847 String placeholder;
1848 if (isHTMLInputElement(node())) {
1849 HTMLInputElement* element = toHTMLInputElement(node());
1850 placeholder = element->strippedPlaceholder();
1851 if (!placeholder.isEmpty())
1852 return placeholder;
1853 }
1854
1855 return String();
1856 }
1857
1858 // 1532 //
1859 // New AX name calculation. 1533 // New AX name calculation.
1860 // 1534 //
1861 1535
1862 String AXNodeObject::textAlternative(bool recursive, bool inAriaLabelledByTraver sal, AXObjectSet& visited, AXNameFrom& nameFrom, AXRelatedObjectVector* relatedO bjects, NameSources* nameSources) const 1536 String AXNodeObject::textAlternative(bool recursive, bool inAriaLabelledByTraver sal, AXObjectSet& visited, AXNameFrom& nameFrom, AXRelatedObjectVector* relatedO bjects, NameSources* nameSources) const
1863 { 1537 {
1864 // If nameSources is non-null, relatedObjects is used in filling it in, so i t must be non-null as well. 1538 // If nameSources is non-null, relatedObjects is used in filling it in, so i t must be non-null as well.
1865 if (nameSources) 1539 if (nameSources)
1866 ASSERT(relatedObjects); 1540 ASSERT(relatedObjects);
1867 1541
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after
1940 // Step 2E from: http://www.w3.org/TR/accname-aam-1.1 1614 // Step 2E from: http://www.w3.org/TR/accname-aam-1.1
1941 if (recursive && !inAriaLabelledByTraversal && isControl()) { 1615 if (recursive && !inAriaLabelledByTraversal && isControl()) {
1942 // No need to set any name source info in a recursive call. 1616 // No need to set any name source info in a recursive call.
1943 if (isRange()) { 1617 if (isRange()) {
1944 const AtomicString& ariaValuetext = getAttribute(aria_valuetextAttr) ; 1618 const AtomicString& ariaValuetext = getAttribute(aria_valuetextAttr) ;
1945 if (!ariaValuetext.isNull()) 1619 if (!ariaValuetext.isNull())
1946 return ariaValuetext.string(); 1620 return ariaValuetext.string();
1947 return String::number(valueForRange()); 1621 return String::number(valueForRange());
1948 } 1622 }
1949 1623
1950 return stringValueOfControl(); 1624 return stringValue();
1951 } 1625 }
1952 1626
1953 // Step 2F / 2G from: http://www.w3.org/TR/accname-aam-1.1 1627 // Step 2F / 2G from: http://www.w3.org/TR/accname-aam-1.1
1954 if (recursive || nameFromContents()) { 1628 if (recursive || nameFromContents()) {
1955 nameFrom = AXNameFromContents; 1629 nameFrom = AXNameFromContents;
1956 if (nameSources) { 1630 if (nameSources) {
1957 nameSources->append(NameSource(foundTextAlternative)); 1631 nameSources->append(NameSource(foundTextAlternative));
1958 nameSources->last().type = nameFrom; 1632 nameSources->last().type = nameFrom;
1959 } 1633 }
1960 1634
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after
2060 if (!foundValidElement) 1734 if (!foundValidElement)
2061 return String(); 1735 return String();
2062 if (relatedObjects) 1736 if (relatedObjects)
2063 *relatedObjects = localRelatedObjects; 1737 *relatedObjects = localRelatedObjects;
2064 return accumulatedText.toString(); 1738 return accumulatedText.toString();
2065 } 1739 }
2066 1740
2067 String AXNodeObject::textFromAriaLabelledby(AXObjectSet& visited, AXRelatedObjec tVector* relatedObjects) const 1741 String AXNodeObject::textFromAriaLabelledby(AXObjectSet& visited, AXRelatedObjec tVector* relatedObjects) const
2068 { 1742 {
2069 WillBeHeapVector<RawPtrWillBeMember<Element>> elements; 1743 WillBeHeapVector<RawPtrWillBeMember<Element>> elements;
2070 ariaLabelledbyElements(elements); 1744 ariaLabelledbyElementVector(elements);
2071 return textFromElements(true, visited, elements, relatedObjects); 1745 return textFromElements(true, visited, elements, relatedObjects);
2072 } 1746 }
2073 1747
2074 String AXNodeObject::textFromAriaDescribedby(AXRelatedObjectVector* relatedObjec ts) const 1748 String AXNodeObject::textFromAriaDescribedby(AXRelatedObjectVector* relatedObjec ts) const
2075 { 1749 {
2076 AXObjectSet visited; 1750 AXObjectSet visited;
2077 WillBeHeapVector<RawPtrWillBeMember<Element>> elements; 1751 WillBeHeapVector<RawPtrWillBeMember<Element>> elements;
2078 elementsFromAttribute(elements, aria_describedbyAttr); 1752 elementsFromAttribute(elements, aria_describedbyAttr);
2079 return textFromElements(true, visited, elements, relatedObjects); 1753 return textFromElements(true, visited, elements, relatedObjects);
2080 } 1754 }
(...skipping 420 matching lines...) Expand 10 before | Expand all | Expand 10 after
2501 { 2175 {
2502 if (!hasAttribute(aria_ownsAttr)) 2176 if (!hasAttribute(aria_ownsAttr))
2503 return; 2177 return;
2504 2178
2505 Vector<String> idVector; 2179 Vector<String> idVector;
2506 tokenVectorFromAttribute(idVector, aria_ownsAttr); 2180 tokenVectorFromAttribute(idVector, aria_ownsAttr);
2507 2181
2508 axObjectCache().updateAriaOwns(this, idVector, ownedChildren); 2182 axObjectCache().updateAriaOwns(this, idVector, ownedChildren);
2509 } 2183 }
2510 2184
2511 String AXNodeObject::deprecatedAlternativeTextForWebArea() const
2512 {
2513 // The WebArea description should follow this order:
2514 // aria-label on the <html>
2515 // title on the <html>
2516 // <title> inside the <head> (of it was set through JS)
2517 // name on the <html>
2518 // For iframes:
2519 // aria-label on the <iframe>
2520 // title on the <iframe>
2521 // name on the <iframe>
2522
2523 Document* document = this->document();
2524 if (!document)
2525 return String();
2526
2527 // Check if the HTML element has an aria-label for the webpage.
2528 if (Element* documentElement = document->documentElement()) {
2529 const AtomicString& ariaLabel = documentElement->getAttribute(aria_label Attr);
2530 if (!ariaLabel.isEmpty())
2531 return ariaLabel;
2532 }
2533
2534 if (HTMLFrameOwnerElement* owner = document->ownerElement()) {
2535 if (isHTMLFrameElementBase(*owner)) {
2536 const AtomicString& title = owner->getAttribute(titleAttr);
2537 if (!title.isEmpty())
2538 return title;
2539 }
2540 return owner->getNameAttribute();
2541 }
2542
2543 String documentTitle = document->title();
2544 if (!documentTitle.isEmpty())
2545 return documentTitle;
2546
2547 if (HTMLElement* body = document->body())
2548 return body->getNameAttribute();
2549
2550 return String();
2551 }
2552
2553 void AXNodeObject::deprecatedAlternativeText(HeapVector<Member<AccessibilityText >>& textOrder) const
2554 {
2555 if (isWebArea()) {
2556 String webAreaText = deprecatedAlternativeTextForWebArea();
2557 if (!webAreaText.isEmpty())
2558 textOrder.append(AccessibilityText::create(webAreaText, AlternativeT ext));
2559 return;
2560 }
2561
2562 deprecatedAriaLabelledbyText(textOrder);
2563
2564 const AtomicString& ariaLabel = getAttribute(aria_labelAttr);
2565 if (!ariaLabel.isEmpty())
2566 textOrder.append(AccessibilityText::create(ariaLabel, AlternativeText));
2567
2568 if (isImage() || isInputImage() || isNativeImage() || isCanvas()) {
2569 // Images should use alt as long as the attribute is present, even if em pty.
2570 // Otherwise, it should fallback to other methods, like the title attrib ute.
2571 const AtomicString& alt = getAttribute(altAttr);
2572 if (!alt.isNull())
2573 textOrder.append(AccessibilityText::create(alt, AlternativeText));
2574 }
2575 }
2576
2577 void AXNodeObject::deprecatedAriaLabelledbyText(HeapVector<Member<AccessibilityT ext>>& textOrder) const
2578 {
2579 String ariaLabelledby = ariaLabelledbyAttribute();
2580 if (!ariaLabelledby.isEmpty()) {
2581 WillBeHeapVector<RawPtrWillBeMember<Element>> elements;
2582 ariaLabelledbyElements(elements);
2583
2584 for (const auto& element : elements) {
2585 AXObject* axElement = axObjectCache().getOrCreate(element);
2586 textOrder.append(AccessibilityText::create(ariaLabelledby, Alternati veText, axElement));
2587 }
2588 }
2589 }
2590
2591 // Based on http://rawgit.com/w3c/aria/master/html-aam/html-aam.html#accessible- name-and-description-calculation 2185 // Based on http://rawgit.com/w3c/aria/master/html-aam/html-aam.html#accessible- name-and-description-calculation
2592 String AXNodeObject::nativeTextAlternative(AXObjectSet& visited, AXNameFrom& nam eFrom, AXRelatedObjectVector* relatedObjects, NameSources* nameSources, bool* fo undTextAlternative) const 2186 String AXNodeObject::nativeTextAlternative(AXObjectSet& visited, AXNameFrom& nam eFrom, AXRelatedObjectVector* relatedObjects, NameSources* nameSources, bool* fo undTextAlternative) const
2593 { 2187 {
2594 if (!node()) 2188 if (!node())
2595 return String(); 2189 return String();
2596 2190
2597 // If nameSources is non-null, relatedObjects is used in filling it in, so i t must be non-null as well. 2191 // If nameSources is non-null, relatedObjects is used in filling it in, so i t must be non-null as well.
2598 if (nameSources) 2192 if (nameSources)
2599 ASSERT(relatedObjects); 2193 ASSERT(relatedObjects);
2600 2194
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after
2701 if (nameSources) { 2295 if (nameSources) {
2702 NameSource& source = nameSources->last(); 2296 NameSource& source = nameSources->last();
2703 source.text = textAlternative; 2297 source.text = textAlternative;
2704 *foundTextAlternative = true; 2298 *foundTextAlternative = true;
2705 } else { 2299 } else {
2706 return textAlternative; 2300 return textAlternative;
2707 } 2301 }
2708 } 2302 }
2709 2303
2710 // localised default value ("Submit") 2304 // localised default value ("Submit")
2711 nameFrom = AXNameFromAttribute; 2305 nameFrom = AXNameFromValue;
2712 textAlternative = inputElement->locale().queryString(WebLocalizedString: :SubmitButtonDefaultLabel); 2306 textAlternative = inputElement->locale().queryString(WebLocalizedString: :SubmitButtonDefaultLabel);
2713 if (nameSources) { 2307 if (nameSources) {
2714 nameSources->append(NameSource(*foundTextAlternative, typeAttr)); 2308 nameSources->append(NameSource(*foundTextAlternative, typeAttr));
2715 NameSource& source = nameSources->last(); 2309 NameSource& source = nameSources->last();
2716 source.attributeValue = inputElement->getAttribute(typeAttr); 2310 source.attributeValue = inputElement->getAttribute(typeAttr);
2717 source.type = nameFrom; 2311 source.type = nameFrom;
2718 source.text = textAlternative; 2312 source.text = textAlternative;
2719 *foundTextAlternative = true; 2313 *foundTextAlternative = true;
2720 } else { 2314 } else {
2721 return textAlternative; 2315 return textAlternative;
(...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after
2890 source.relatedObjects = *relatedObjects; 2484 source.relatedObjects = *relatedObjects;
2891 source.text = textAlternative; 2485 source.text = textAlternative;
2892 *foundTextAlternative = true; 2486 *foundTextAlternative = true;
2893 } else { 2487 } else {
2894 return textAlternative; 2488 return textAlternative;
2895 } 2489 }
2896 } 2490 }
2897 } 2491 }
2898 } 2492 }
2899 2493
2494 // Document.
2495 if (isWebArea()) {
2496 Document* document = this->document();
2497 if (document) {
2498 nameFrom = AXNameFromAttribute;
2499 if (nameSources) {
2500 nameSources->append(NameSource(foundTextAlternative, aria_labelA ttr));
2501 nameSources->last().type = nameFrom;
2502 }
2503 if (Element* documentElement = document->documentElement()) {
2504 const AtomicString& ariaLabel = documentElement->getAttribute(ar ia_labelAttr);
2505 if (!ariaLabel.isEmpty()) {
2506 textAlternative = ariaLabel;
2507
2508 if (nameSources) {
2509 NameSource& source = nameSources->last();
2510 source.text = textAlternative;
2511 source.attributeValue = ariaLabel;
2512 *foundTextAlternative = true;
2513 } else {
2514 return textAlternative;
2515 }
2516 }
2517 }
2518
2519 nameFrom = AXNameFromRelatedElement;
2520 if (nameSources) {
2521 nameSources->append(NameSource(*foundTextAlternative));
2522 nameSources->last().type = nameFrom;
2523 nameSources->last().nativeSource = AXTextFromNativeHTMLTitleElem ent;
2524 }
2525
2526 textAlternative = document->title();
2527
2528 Element* titleElement = document->titleElement();
2529 AXObject* titleAXObject = axObjectCache().getOrCreate(titleElement);
2530 if (titleAXObject) {
2531 if (relatedObjects) {
2532 localRelatedObjects.append(new NameSourceRelatedObject(title AXObject, textAlternative));
2533 *relatedObjects = localRelatedObjects;
2534 localRelatedObjects.clear();
2535 }
2536
2537 if (nameSources) {
2538 NameSource& source = nameSources->last();
2539 source.relatedObjects = *relatedObjects;
2540 source.text = textAlternative;
2541 *foundTextAlternative = true;
2542 } else {
2543 return textAlternative;
2544 }
2545 }
2546 }
2547 }
2548
2900 return textAlternative; 2549 return textAlternative;
2901 } 2550 }
2902 2551
2903 String AXNodeObject::description(AXNameFrom nameFrom, AXDescriptionFrom& descrip tionFrom, AXObjectVector* descriptionObjects) const 2552 String AXNodeObject::description(AXNameFrom nameFrom, AXDescriptionFrom& descrip tionFrom, AXObjectVector* descriptionObjects) const
2904 { 2553 {
2905 AXRelatedObjectVector relatedObjects; 2554 AXRelatedObjectVector relatedObjects;
2906 String result = description(nameFrom, descriptionFrom, nullptr, &relatedObje cts); 2555 String result = description(nameFrom, descriptionFrom, nullptr, &relatedObje cts);
2907 if (descriptionObjects) { 2556 if (descriptionObjects) {
2908 descriptionObjects->clear(); 2557 descriptionObjects->clear();
2909 for (size_t i = 0; i < relatedObjects.size(); i++) 2558 for (size_t i = 0; i < relatedObjects.size(); i++)
(...skipping 194 matching lines...) Expand 10 before | Expand all | Expand 10 after
3104 if (!descriptionSource.relatedObjects.isEmpty()) 2753 if (!descriptionSource.relatedObjects.isEmpty())
3105 *relatedObjects = descriptionSource.relatedObjects; 2754 *relatedObjects = descriptionSource.relatedObjects;
3106 return descriptionSource.text; 2755 return descriptionSource.text;
3107 } 2756 }
3108 } 2757 }
3109 } 2758 }
3110 2759
3111 return String(); 2760 return String();
3112 } 2761 }
3113 2762
2763 String AXNodeObject::placeholder(AXNameFrom nameFrom, AXDescriptionFrom descript ionFrom) const
2764 {
2765 if (nameFrom == AXNameFromPlaceholder)
2766 return String();
2767
2768 if (descriptionFrom == AXDescriptionFromPlaceholder)
2769 return String();
2770
2771 if (!node())
2772 return String();
2773
2774 String placeholder;
2775 if (isHTMLInputElement(*node())) {
2776 HTMLInputElement* inputElement = toHTMLInputElement(node());
2777 placeholder = inputElement->strippedPlaceholder();
2778 } else if (isHTMLTextAreaElement(*node())) {
2779 HTMLTextAreaElement* textAreaElement = toHTMLTextAreaElement(node());
2780 placeholder = textAreaElement->strippedPlaceholder();
2781 }
2782 return placeholder;
2783 }
2784
3114 DEFINE_TRACE(AXNodeObject) 2785 DEFINE_TRACE(AXNodeObject)
3115 { 2786 {
3116 visitor->trace(m_node); 2787 visitor->trace(m_node);
3117 AXObject::trace(visitor); 2788 AXObject::trace(visitor);
3118 } 2789 }
3119 2790
3120 } // namespace blin 2791 } // namespace blin
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698