| OLD | NEW |
| 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 166 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 177 } | 177 } |
| 178 | 178 |
| 179 bool AXNodeObject::computeAccessibilityIsIgnored( | 179 bool AXNodeObject::computeAccessibilityIsIgnored( |
| 180 IgnoredReasons* ignoredReasons) const { | 180 IgnoredReasons* ignoredReasons) const { |
| 181 #if ENABLE(ASSERT) | 181 #if ENABLE(ASSERT) |
| 182 // Double-check that an AXObject is never accessed before | 182 // Double-check that an AXObject is never accessed before |
| 183 // it's been initialized. | 183 // it's been initialized. |
| 184 ASSERT(m_initialized); | 184 ASSERT(m_initialized); |
| 185 #endif | 185 #endif |
| 186 | 186 |
| 187 // If this element is within a parent that cannot have children, it should not
be exposed. | 187 // If this element is within a parent that cannot have children, it should not |
| 188 // be exposed. |
| 188 if (isDescendantOfLeafNode()) { | 189 if (isDescendantOfLeafNode()) { |
| 189 if (ignoredReasons) | 190 if (ignoredReasons) |
| 190 ignoredReasons->append( | 191 ignoredReasons->append( |
| 191 IgnoredReason(AXAncestorIsLeafNode, leafNodeAncestor())); | 192 IgnoredReason(AXAncestorIsLeafNode, leafNodeAncestor())); |
| 192 return true; | 193 return true; |
| 193 } | 194 } |
| 194 | 195 |
| 195 // Ignore labels that are already referenced by a control. | 196 // Ignore labels that are already referenced by a control. |
| 196 AXObject* controlObject = correspondingControlForLabelElement(); | 197 AXObject* controlObject = correspondingControlForLabelElement(); |
| 197 if (controlObject && controlObject->isCheckboxOrRadio() && | 198 if (controlObject && controlObject->isCheckboxOrRadio() && |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 233 static bool isPresentationalInTable(AXObject* parent, | 234 static bool isPresentationalInTable(AXObject* parent, |
| 234 HTMLElement* currentElement) { | 235 HTMLElement* currentElement) { |
| 235 if (!currentElement) | 236 if (!currentElement) |
| 236 return false; | 237 return false; |
| 237 | 238 |
| 238 Node* parentNode = parent->getNode(); | 239 Node* parentNode = parent->getNode(); |
| 239 if (!parentNode || !parentNode->isHTMLElement()) | 240 if (!parentNode || !parentNode->isHTMLElement()) |
| 240 return false; | 241 return false; |
| 241 | 242 |
| 242 // AXTable determines the role as checking isTableXXX. | 243 // AXTable determines the role as checking isTableXXX. |
| 243 // If Table has explicit role including presentation, AXTable doesn't assign i
mplicit Role | 244 // If Table has explicit role including presentation, AXTable doesn't assign |
| 244 // to a whole Table. That's why we should check it based on node. | 245 // implicit Role to a whole Table. That's why we should check it based on |
| 246 // node. |
| 245 // Normal Table Tree is that | 247 // Normal Table Tree is that |
| 246 // cell(its role)-> tr(tr role)-> tfoot, tbody, thead(ignored role) -> table(t
able role). | 248 // cell(its role)-> tr(tr role)-> tfoot, tbody, thead(ignored role) -> |
| 249 // table(table role). |
| 247 // If table has presentation role, it will be like | 250 // If table has presentation role, it will be like |
| 248 // cell(group)-> tr(unknown) -> tfoot, tbody, thead(ignored) -> table(presenta
tion). | 251 // cell(group)-> tr(unknown) -> tfoot, tbody, thead(ignored) -> |
| 252 // table(presentation). |
| 249 if (isHTMLTableCellElement(*currentElement) && | 253 if (isHTMLTableCellElement(*currentElement) && |
| 250 isHTMLTableRowElement(*parentNode)) | 254 isHTMLTableRowElement(*parentNode)) |
| 251 return parent->hasInheritedPresentationalRole(); | 255 return parent->hasInheritedPresentationalRole(); |
| 252 | 256 |
| 253 if (isHTMLTableRowElement(*currentElement) && | 257 if (isHTMLTableRowElement(*currentElement) && |
| 254 isHTMLTableSectionElement(toHTMLElement(*parentNode))) { | 258 isHTMLTableSectionElement(toHTMLElement(*parentNode))) { |
| 255 // Because TableSections have ignored role, presentation should be checked w
ith its parent node | 259 // Because TableSections have ignored role, presentation should be checked |
| 260 // with its parent node. |
| 256 AXObject* tableObject = parent->parentObject(); | 261 AXObject* tableObject = parent->parentObject(); |
| 257 Node* tableNode = tableObject ? tableObject->getNode() : 0; | 262 Node* tableNode = tableObject ? tableObject->getNode() : 0; |
| 258 return isHTMLTableElement(tableNode) && | 263 return isHTMLTableElement(tableNode) && |
| 259 tableObject->hasInheritedPresentationalRole(); | 264 tableObject->hasInheritedPresentationalRole(); |
| 260 } | 265 } |
| 261 return false; | 266 return false; |
| 262 } | 267 } |
| 263 | 268 |
| 264 static bool isRequiredOwnedElement(AXObject* parent, | 269 static bool isRequiredOwnedElement(AXObject* parent, |
| 265 AccessibilityRole currentRole, | 270 AccessibilityRole currentRole, |
| (...skipping 10 matching lines...) Expand all Loading... |
| 276 currentRole == MenuItemRadioRole) | 281 currentRole == MenuItemRadioRole) |
| 277 return isHTMLMenuElement(*parentNode); | 282 return isHTMLMenuElement(*parentNode); |
| 278 | 283 |
| 279 if (!currentElement) | 284 if (!currentElement) |
| 280 return false; | 285 return false; |
| 281 if (isHTMLTableCellElement(*currentElement)) | 286 if (isHTMLTableCellElement(*currentElement)) |
| 282 return isHTMLTableRowElement(*parentNode); | 287 return isHTMLTableRowElement(*parentNode); |
| 283 if (isHTMLTableRowElement(*currentElement)) | 288 if (isHTMLTableRowElement(*currentElement)) |
| 284 return isHTMLTableSectionElement(toHTMLElement(*parentNode)); | 289 return isHTMLTableSectionElement(toHTMLElement(*parentNode)); |
| 285 | 290 |
| 286 // In case of ListboxRole and it's child, ListBoxOptionRole, | 291 // In case of ListboxRole and its child, ListBoxOptionRole, inheritance of |
| 287 // Inheritance of presentation role is handled in AXListBoxOption | 292 // presentation role is handled in AXListBoxOption because ListBoxOption Role |
| 288 // Because ListBoxOption Role doesn't have any child. | 293 // doesn't have any child. |
| 289 // If it's just ignored because of presentation, we can't see any AX tree rela
ted to ListBoxOption. | 294 // If it's just ignored because of presentation, we can't see any AX tree |
| 295 // related to ListBoxOption. |
| 290 return false; | 296 return false; |
| 291 } | 297 } |
| 292 | 298 |
| 293 const AXObject* AXNodeObject::inheritsPresentationalRoleFrom() const { | 299 const AXObject* AXNodeObject::inheritsPresentationalRoleFrom() const { |
| 294 // ARIA states if an item can get focus, it should not be presentational. | 300 // ARIA states if an item can get focus, it should not be presentational. |
| 295 if (canSetFocusAttribute()) | 301 if (canSetFocusAttribute()) |
| 296 return 0; | 302 return 0; |
| 297 | 303 |
| 298 if (isPresentational()) | 304 if (isPresentational()) |
| 299 return this; | 305 return this; |
| 300 | 306 |
| 301 // http://www.w3.org/TR/wai-aria/complete#presentation | 307 // http://www.w3.org/TR/wai-aria/complete#presentation |
| 302 // ARIA spec says that the user agent MUST apply an inherited role of presenta
tion | 308 // ARIA spec says that the user agent MUST apply an inherited role of |
| 309 // presentation |
| 303 // to any owned elements that do not have an explicit role defined. | 310 // to any owned elements that do not have an explicit role defined. |
| 304 if (ariaRoleAttribute() != UnknownRole) | 311 if (ariaRoleAttribute() != UnknownRole) |
| 305 return 0; | 312 return 0; |
| 306 | 313 |
| 307 AXObject* parent = parentObject(); | 314 AXObject* parent = parentObject(); |
| 308 if (!parent) | 315 if (!parent) |
| 309 return 0; | 316 return 0; |
| 310 | 317 |
| 311 HTMLElement* element = nullptr; | 318 HTMLElement* element = nullptr; |
| 312 if (getNode() && getNode()->isHTMLElement()) | 319 if (getNode() && getNode()->isHTMLElement()) |
| 313 element = toHTMLElement(getNode()); | 320 element = toHTMLElement(getNode()); |
| 314 if (!parent->hasInheritedPresentationalRole()) { | 321 if (!parent->hasInheritedPresentationalRole()) { |
| 315 if (!getLayoutObject() || !getLayoutObject()->isBoxModelObject()) | 322 if (!getLayoutObject() || !getLayoutObject()->isBoxModelObject()) |
| 316 return 0; | 323 return 0; |
| 317 | 324 |
| 318 LayoutBoxModelObject* cssBox = toLayoutBoxModelObject(getLayoutObject()); | 325 LayoutBoxModelObject* cssBox = toLayoutBoxModelObject(getLayoutObject()); |
| 319 if (!cssBox->isTableCell() && !cssBox->isTableRow()) | 326 if (!cssBox->isTableCell() && !cssBox->isTableRow()) |
| 320 return 0; | 327 return 0; |
| 321 | 328 |
| 322 if (!isPresentationalInTable(parent, element)) | 329 if (!isPresentationalInTable(parent, element)) |
| 323 return 0; | 330 return 0; |
| 324 } | 331 } |
| 325 // ARIA spec says that when a parent object is presentational and this object | 332 // ARIA spec says that when a parent object is presentational and this object |
| 326 // is a required owned element of that parent, then this object is also presen
tational. | 333 // is a required owned element of that parent, then this object is also |
| 334 // presentational. |
| 327 if (isRequiredOwnedElement(parent, roleValue(), element)) | 335 if (isRequiredOwnedElement(parent, roleValue(), element)) |
| 328 return parent; | 336 return parent; |
| 329 return 0; | 337 return 0; |
| 330 } | 338 } |
| 331 | 339 |
| 332 bool AXNodeObject::isDescendantOfElementType( | 340 bool AXNodeObject::isDescendantOfElementType( |
| 333 const HTMLQualifiedName& tagName) const { | 341 const HTMLQualifiedName& tagName) const { |
| 334 if (!getNode()) | 342 if (!getNode()) |
| 335 return false; | 343 return false; |
| 336 | 344 |
| 337 for (Element* parent = getNode()->parentElement(); parent; | 345 for (Element* parent = getNode()->parentElement(); parent; |
| 338 parent = parent->parentElement()) { | 346 parent = parent->parentElement()) { |
| 339 if (parent->hasTagName(tagName)) | 347 if (parent->hasTagName(tagName)) |
| 340 return true; | 348 return true; |
| 341 } | 349 } |
| 342 return false; | 350 return false; |
| 343 } | 351 } |
| 344 | 352 |
| 345 AccessibilityRole AXNodeObject::nativeAccessibilityRoleIgnoringAria() const { | 353 AccessibilityRole AXNodeObject::nativeAccessibilityRoleIgnoringAria() const { |
| 346 if (!getNode()) | 354 if (!getNode()) |
| 347 return UnknownRole; | 355 return UnknownRole; |
| 348 | 356 |
| 349 // HTMLAnchorElement sets isLink only when it has hrefAttr. | 357 // HTMLAnchorElement sets isLink only when it has hrefAttr. |
| 350 // We assume that it is also LinkRole if it has event listners even though it
doesn't have hrefAttr. | 358 // We assume that it is also LinkRole if it has event listners even though it |
| 359 // doesn't have hrefAttr. |
| 351 if (getNode()->isLink() || (isHTMLAnchorElement(*getNode()) && isClickable())) | 360 if (getNode()->isLink() || (isHTMLAnchorElement(*getNode()) && isClickable())) |
| 352 return LinkRole; | 361 return LinkRole; |
| 353 | 362 |
| 354 if (isHTMLButtonElement(*getNode())) | 363 if (isHTMLButtonElement(*getNode())) |
| 355 return buttonRoleType(); | 364 return buttonRoleType(); |
| 356 | 365 |
| 357 if (isHTMLDetailsElement(*getNode())) | 366 if (isHTMLDetailsElement(*getNode())) |
| 358 return DetailsRole; | 367 return DetailsRole; |
| 359 | 368 |
| 360 if (isHTMLSummaryElement(*getNode())) { | 369 if (isHTMLSummaryElement(*getNode())) { |
| (...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 489 | 498 |
| 490 if (getNode()->hasTagName(sectionTag)) | 499 if (getNode()->hasTagName(sectionTag)) |
| 491 return RegionRole; | 500 return RegionRole; |
| 492 | 501 |
| 493 if (getNode()->hasTagName(addressTag)) | 502 if (getNode()->hasTagName(addressTag)) |
| 494 return ContentInfoRole; | 503 return ContentInfoRole; |
| 495 | 504 |
| 496 if (isHTMLDialogElement(*getNode())) | 505 if (isHTMLDialogElement(*getNode())) |
| 497 return DialogRole; | 506 return DialogRole; |
| 498 | 507 |
| 499 // The HTML element should not be exposed as an element. That's what the Layou
tView element does. | 508 // The HTML element should not be exposed as an element. That's what the |
| 509 // LayoutView element does. |
| 500 if (isHTMLHtmlElement(*getNode())) | 510 if (isHTMLHtmlElement(*getNode())) |
| 501 return IgnoredRole; | 511 return IgnoredRole; |
| 502 | 512 |
| 503 if (isHTMLIFrameElement(*getNode())) { | 513 if (isHTMLIFrameElement(*getNode())) { |
| 504 const AtomicString& ariaRole = getAttribute(roleAttr); | 514 const AtomicString& ariaRole = getAttribute(roleAttr); |
| 505 if (ariaRole == "none" || ariaRole == "presentation") | 515 if (ariaRole == "none" || ariaRole == "presentation") |
| 506 return IframePresentationalRole; | 516 return IframePresentationalRole; |
| 507 return IframeRole; | 517 return IframeRole; |
| 508 } | 518 } |
| 509 | 519 |
| 510 // There should only be one banner/contentInfo per page. If header/footer are
being used within an article or section | 520 // There should only be one banner/contentInfo per page. If header/footer are |
| 511 // then it should not be exposed as whole page's banner/contentInfo but as a g
roup role. | 521 // being used within an article or section then it should not be exposed as |
| 522 // whole page's banner/contentInfo but as a group role. |
| 512 if (getNode()->hasTagName(headerTag)) { | 523 if (getNode()->hasTagName(headerTag)) { |
| 513 if (isDescendantOfElementType(articleTag) || | 524 if (isDescendantOfElementType(articleTag) || |
| 514 isDescendantOfElementType(sectionTag) || | 525 isDescendantOfElementType(sectionTag) || |
| 515 (getNode()->parentElement() && | 526 (getNode()->parentElement() && |
| 516 getNode()->parentElement()->hasTagName(mainTag))) { | 527 getNode()->parentElement()->hasTagName(mainTag))) { |
| 517 return GroupRole; | 528 return GroupRole; |
| 518 } | 529 } |
| 519 return BannerRole; | 530 return BannerRole; |
| 520 } | 531 } |
| 521 | 532 |
| (...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 655 | 666 |
| 656 // If it's a control, it's not generic. | 667 // If it's a control, it's not generic. |
| 657 if (isControl()) | 668 if (isControl()) |
| 658 return false; | 669 return false; |
| 659 | 670 |
| 660 // If it has an aria role, it's not generic. | 671 // If it has an aria role, it's not generic. |
| 661 if (m_ariaRole != UnknownRole) | 672 if (m_ariaRole != UnknownRole) |
| 662 return false; | 673 return false; |
| 663 | 674 |
| 664 // If the content editable attribute is set on this element, that's the reason | 675 // If the content editable attribute is set on this element, that's the reason |
| 665 // it's focusable, and existing logic should handle this case already - so it'
s not a | 676 // it's focusable, and existing logic should handle this case already - so |
| 666 // generic focusable element. | 677 // it's not a generic focusable element. |
| 667 | 678 |
| 668 if (hasContentEditableAttributeSet()) | 679 if (hasContentEditableAttributeSet()) |
| 669 return false; | 680 return false; |
| 670 | 681 |
| 671 // The web area and body element are both focusable, but existing logic handle
s these | 682 // The web area and body element are both focusable, but existing logic |
| 672 // cases already, so we don't need to include them here. | 683 // handles these cases already, so we don't need to include them here. |
| 673 if (roleValue() == WebAreaRole) | 684 if (roleValue() == WebAreaRole) |
| 674 return false; | 685 return false; |
| 675 if (isHTMLBodyElement(getNode())) | 686 if (isHTMLBodyElement(getNode())) |
| 676 return false; | 687 return false; |
| 677 | 688 |
| 678 // An SVG root is focusable by default, but it's probably not interactive, so
don't | 689 // An SVG root is focusable by default, but it's probably not interactive, so |
| 679 // include it. It can still be made accessible by giving it an ARIA role. | 690 // don't include it. It can still be made accessible by giving it an ARIA |
| 691 // role. |
| 680 if (roleValue() == SVGRootRole) | 692 if (roleValue() == SVGRootRole) |
| 681 return false; | 693 return false; |
| 682 | 694 |
| 683 return true; | 695 return true; |
| 684 } | 696 } |
| 685 | 697 |
| 686 AXObject* AXNodeObject::menuButtonForMenu() const { | 698 AXObject* AXNodeObject::menuButtonForMenu() const { |
| 687 Element* menuItem = menuItemElementForMenu(); | 699 Element* menuItem = menuItemElementForMenu(); |
| 688 | 700 |
| 689 if (menuItem) { | 701 if (menuItem) { |
| 690 // ARIA just has generic menu items. AppKit needs to know if this is a top l
evel items like MenuBarButton or MenuBarItem | 702 // ARIA just has generic menu items. AppKit needs to know if this is a top |
| 703 // level items like MenuBarButton or MenuBarItem |
| 691 AXObject* menuItemAX = axObjectCache().getOrCreate(menuItem); | 704 AXObject* menuItemAX = axObjectCache().getOrCreate(menuItem); |
| 692 if (menuItemAX && menuItemAX->isMenuButton()) | 705 if (menuItemAX && menuItemAX->isMenuButton()) |
| 693 return menuItemAX; | 706 return menuItemAX; |
| 694 } | 707 } |
| 695 return 0; | 708 return 0; |
| 696 } | 709 } |
| 697 | 710 |
| 698 static Element* siblingWithAriaRole(String role, Node* node) { | 711 static Element* siblingWithAriaRole(String role, Node* node) { |
| 699 Node* parent = node->parentNode(); | 712 Node* parent = node->parentNode(); |
| 700 if (!parent) | 713 if (!parent) |
| (...skipping 22 matching lines...) Expand all Loading... |
| 723 return 0; | 736 return 0; |
| 724 | 737 |
| 725 if (!node->isElementNode()) | 738 if (!node->isElementNode()) |
| 726 node = node->parentElement(); | 739 node = node->parentElement(); |
| 727 | 740 |
| 728 if (!node) | 741 if (!node) |
| 729 return 0; | 742 return 0; |
| 730 | 743 |
| 731 for (Element* element = toElement(node); element; | 744 for (Element* element = toElement(node); element; |
| 732 element = element->parentElement()) { | 745 element = element->parentElement()) { |
| 733 // It's a pretty common practice to put click listeners on the body or docum
ent, but that's | 746 // It's a pretty common practice to put click listeners on the body or |
| 734 // almost never what the user wants when clicking on an accessible element. | 747 // document, but that's almost never what the user wants when clicking on an |
| 748 // accessible element. |
| 735 if (isHTMLBodyElement(element)) | 749 if (isHTMLBodyElement(element)) |
| 736 break; | 750 break; |
| 737 | 751 |
| 738 if (element->hasEventListeners(EventTypeNames::click) || | 752 if (element->hasEventListeners(EventTypeNames::click) || |
| 739 element->hasEventListeners(EventTypeNames::mousedown) || | 753 element->hasEventListeners(EventTypeNames::mousedown) || |
| 740 element->hasEventListeners(EventTypeNames::mouseup) || | 754 element->hasEventListeners(EventTypeNames::mouseup) || |
| 741 element->hasEventListeners(EventTypeNames::DOMActivate)) | 755 element->hasEventListeners(EventTypeNames::DOMActivate)) |
| 742 return element; | 756 return element; |
| 743 } | 757 } |
| 744 | 758 |
| 745 return 0; | 759 return 0; |
| 746 } | 760 } |
| 747 | 761 |
| 748 AccessibilityRole AXNodeObject::remapAriaRoleDueToParent( | 762 AccessibilityRole AXNodeObject::remapAriaRoleDueToParent( |
| 749 AccessibilityRole role) const { | 763 AccessibilityRole role) const { |
| 750 // Some objects change their role based on their parent. | 764 // Some objects change their role based on their parent. |
| 751 // However, asking for the unignoredParent calls accessibilityIsIgnored(), whi
ch can trigger a loop. | 765 // However, asking for the unignoredParent calls accessibilityIsIgnored(), |
| 752 // While inside the call stack of creating an element, we need to avoid access
ibilityIsIgnored(). | 766 // which can trigger a loop. While inside the call stack of creating an |
| 767 // element, we need to avoid accessibilityIsIgnored(). |
| 753 // https://bugs.webkit.org/show_bug.cgi?id=65174 | 768 // https://bugs.webkit.org/show_bug.cgi?id=65174 |
| 754 | 769 |
| 755 if (role != ListBoxOptionRole && role != MenuItemRole) | 770 if (role != ListBoxOptionRole && role != MenuItemRole) |
| 756 return role; | 771 return role; |
| 757 | 772 |
| 758 for (AXObject* parent = parentObject(); | 773 for (AXObject* parent = parentObject(); |
| 759 parent && !parent->accessibilityIsIgnored(); | 774 parent && !parent->accessibilityIsIgnored(); |
| 760 parent = parent->parentObject()) { | 775 parent = parent->parentObject()) { |
| 761 AccessibilityRole parentAriaRole = parent->ariaRoleAttribute(); | 776 AccessibilityRole parentAriaRole = parent->ariaRoleAttribute(); |
| 762 | 777 |
| 763 // Selects and listboxes both have options as child roles, but they map to d
ifferent roles within WebCore. | 778 // Selects and listboxes both have options as child roles, but they map to |
| 779 // different roles within WebCore. |
| 764 if (role == ListBoxOptionRole && parentAriaRole == MenuRole) | 780 if (role == ListBoxOptionRole && parentAriaRole == MenuRole) |
| 765 return MenuItemRole; | 781 return MenuItemRole; |
| 766 // An aria "menuitem" may map to MenuButton or MenuItem depending on its par
ent. | 782 // An aria "menuitem" may map to MenuButton or MenuItem depending on its |
| 783 // parent. |
| 767 if (role == MenuItemRole && parentAriaRole == GroupRole) | 784 if (role == MenuItemRole && parentAriaRole == GroupRole) |
| 768 return MenuButtonRole; | 785 return MenuButtonRole; |
| 769 | 786 |
| 770 // If the parent had a different role, then we don't need to continue search
ing up the chain. | 787 // If the parent had a different role, then we don't need to continue |
| 788 // searching up the chain. |
| 771 if (parentAriaRole) | 789 if (parentAriaRole) |
| 772 break; | 790 break; |
| 773 } | 791 } |
| 774 | 792 |
| 775 return role; | 793 return role; |
| 776 } | 794 } |
| 777 | 795 |
| 778 void AXNodeObject::init() { | 796 void AXNodeObject::init() { |
| 779 #if ENABLE(ASSERT) | 797 #if ENABLE(ASSERT) |
| 780 ASSERT(!m_initialized); | 798 ASSERT(!m_initialized); |
| (...skipping 208 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 989 // Otherwise it's not checked | 1007 // Otherwise it's not checked |
| 990 return false; | 1008 return false; |
| 991 } | 1009 } |
| 992 | 1010 |
| 993 bool AXNodeObject::isClickable() const { | 1011 bool AXNodeObject::isClickable() const { |
| 994 if (getNode()) { | 1012 if (getNode()) { |
| 995 if (getNode()->isElementNode() && | 1013 if (getNode()->isElementNode() && |
| 996 toElement(getNode())->isDisabledFormControl()) | 1014 toElement(getNode())->isDisabledFormControl()) |
| 997 return false; | 1015 return false; |
| 998 | 1016 |
| 999 // Note: we can't call getNode()->willRespondToMouseClickEvents() because th
at triggers a style recalc and can delete this. | 1017 // Note: we can't call getNode()->willRespondToMouseClickEvents() because |
| 1018 // that triggers a style recalc and can delete this. |
| 1000 if (getNode()->hasEventListeners(EventTypeNames::mouseup) || | 1019 if (getNode()->hasEventListeners(EventTypeNames::mouseup) || |
| 1001 getNode()->hasEventListeners(EventTypeNames::mousedown) || | 1020 getNode()->hasEventListeners(EventTypeNames::mousedown) || |
| 1002 getNode()->hasEventListeners(EventTypeNames::click) || | 1021 getNode()->hasEventListeners(EventTypeNames::click) || |
| 1003 getNode()->hasEventListeners(EventTypeNames::DOMActivate)) | 1022 getNode()->hasEventListeners(EventTypeNames::DOMActivate)) |
| 1004 return true; | 1023 return true; |
| 1005 } | 1024 } |
| 1006 | 1025 |
| 1007 return AXObject::isClickable(); | 1026 return AXObject::isClickable(); |
| 1008 } | 1027 } |
| 1009 | 1028 |
| (...skipping 27 matching lines...) Expand all Loading... |
| 1037 } | 1056 } |
| 1038 | 1057 |
| 1039 bool AXNodeObject::isPressed() const { | 1058 bool AXNodeObject::isPressed() const { |
| 1040 if (!isButton()) | 1059 if (!isButton()) |
| 1041 return false; | 1060 return false; |
| 1042 | 1061 |
| 1043 Node* node = this->getNode(); | 1062 Node* node = this->getNode(); |
| 1044 if (!node) | 1063 if (!node) |
| 1045 return false; | 1064 return false; |
| 1046 | 1065 |
| 1047 // ARIA button with aria-pressed not undefined, then check for aria-pressed at
tribute rather than getNode()->active() | 1066 // ARIA button with aria-pressed not undefined, then check for aria-pressed |
| 1067 // attribute rather than getNode()->active() |
| 1048 if (ariaRoleAttribute() == ToggleButtonRole) { | 1068 if (ariaRoleAttribute() == ToggleButtonRole) { |
| 1049 if (equalIgnoringCase(getAttribute(aria_pressedAttr), "true") || | 1069 if (equalIgnoringCase(getAttribute(aria_pressedAttr), "true") || |
| 1050 equalIgnoringCase(getAttribute(aria_pressedAttr), "mixed")) | 1070 equalIgnoringCase(getAttribute(aria_pressedAttr), "mixed")) |
| 1051 return true; | 1071 return true; |
| 1052 return false; | 1072 return false; |
| 1053 } | 1073 } |
| 1054 | 1074 |
| 1055 return node->active(); | 1075 return node->active(); |
| 1056 } | 1076 } |
| 1057 | 1077 |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1091 | 1111 |
| 1092 if (isWebArea()) | 1112 if (isWebArea()) |
| 1093 return true; | 1113 return true; |
| 1094 | 1114 |
| 1095 // Children of elements with an aria-activedescendant attribute should be | 1115 // Children of elements with an aria-activedescendant attribute should be |
| 1096 // focusable if they have a (non-presentational) ARIA role. | 1116 // focusable if they have a (non-presentational) ARIA role. |
| 1097 if (!isPresentational() && ariaRoleAttribute() != UnknownRole && | 1117 if (!isPresentational() && ariaRoleAttribute() != UnknownRole && |
| 1098 ancestorExposesActiveDescendant()) | 1118 ancestorExposesActiveDescendant()) |
| 1099 return true; | 1119 return true; |
| 1100 | 1120 |
| 1101 // NOTE: It would be more accurate to ask the document whether setFocusedNode(
) would | 1121 // NOTE: It would be more accurate to ask the document whether |
| 1102 // do anything. For example, setFocusedNode() will do nothing if the current f
ocused | 1122 // setFocusedNode() would do anything. For example, setFocusedNode() will do |
| 1103 // node will not relinquish the focus. | 1123 // nothing if the current focused node will not relinquish the focus. |
| 1104 if (isDisabledFormControl(node)) | 1124 if (isDisabledFormControl(node)) |
| 1105 return false; | 1125 return false; |
| 1106 | 1126 |
| 1107 return node->isElementNode() && toElement(node)->supportsFocus(); | 1127 return node->isElementNode() && toElement(node)->supportsFocus(); |
| 1108 } | 1128 } |
| 1109 | 1129 |
| 1110 bool AXNodeObject::canSetValueAttribute() const { | 1130 bool AXNodeObject::canSetValueAttribute() const { |
| 1111 if (equalIgnoringCase(getAttribute(aria_readonlyAttr), "true")) | 1131 if (equalIgnoringCase(getAttribute(aria_readonlyAttr), "true")) |
| 1112 return false; | 1132 return false; |
| 1113 | 1133 |
| (...skipping 395 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1509 return overriddenDescription; | 1529 return overriddenDescription; |
| 1510 } | 1530 } |
| 1511 if (!selectElement.multiple()) | 1531 if (!selectElement.multiple()) |
| 1512 return selectElement.value(); | 1532 return selectElement.value(); |
| 1513 return String(); | 1533 return String(); |
| 1514 } | 1534 } |
| 1515 | 1535 |
| 1516 if (isNativeTextControl()) | 1536 if (isNativeTextControl()) |
| 1517 return text(); | 1537 return text(); |
| 1518 | 1538 |
| 1519 // Handle other HTML input elements that aren't text controls, like date and t
ime | 1539 // Handle other HTML input elements that aren't text controls, like date and |
| 1520 // controls, by returning the string value, with the exception of checkboxes | 1540 // time controls, by returning the string value, with the exception of |
| 1521 // and radio buttons (which would return "on"). | 1541 // checkboxes and radio buttons (which would return "on"). |
| 1522 if (isHTMLInputElement(node)) { | 1542 if (isHTMLInputElement(node)) { |
| 1523 HTMLInputElement* input = toHTMLInputElement(node); | 1543 HTMLInputElement* input = toHTMLInputElement(node); |
| 1524 if (input->type() != InputTypeNames::checkbox && | 1544 if (input->type() != InputTypeNames::checkbox && |
| 1525 input->type() != InputTypeNames::radio) | 1545 input->type() != InputTypeNames::radio) |
| 1526 return input->value(); | 1546 return input->value(); |
| 1527 } | 1547 } |
| 1528 | 1548 |
| 1529 return String(); | 1549 return String(); |
| 1530 } | 1550 } |
| 1531 | 1551 |
| (...skipping 25 matching lines...) Expand all Loading... |
| 1557 if (!blockFlow->inlineBoxWrapper()) | 1577 if (!blockFlow->inlineBoxWrapper()) |
| 1558 return blockFlow; | 1578 return blockFlow; |
| 1559 } | 1579 } |
| 1560 current = current->parent(); | 1580 current = current->parent(); |
| 1561 } | 1581 } |
| 1562 | 1582 |
| 1563 ASSERT_NOT_REACHED(); | 1583 ASSERT_NOT_REACHED(); |
| 1564 return nullptr; | 1584 return nullptr; |
| 1565 } | 1585 } |
| 1566 | 1586 |
| 1567 // Returns true if |r1| and |r2| are both non-null, both inline, and are contain
ed | 1587 // Returns true if |r1| and |r2| are both non-null, both inline, and are |
| 1568 // within the same non-inline LayoutBlockFlow. | 1588 // contained within the same non-inline LayoutBlockFlow. |
| 1569 static bool isInSameNonInlineBlockFlow(LayoutObject* r1, LayoutObject* r2) { | 1589 static bool isInSameNonInlineBlockFlow(LayoutObject* r1, LayoutObject* r2) { |
| 1570 if (!r1 || !r2) | 1590 if (!r1 || !r2) |
| 1571 return false; | 1591 return false; |
| 1572 if (!r1->isInline() || !r2->isInline()) | 1592 if (!r1->isInline() || !r2->isInline()) |
| 1573 return false; | 1593 return false; |
| 1574 LayoutBlockFlow* b1 = nonInlineBlockFlow(r1); | 1594 LayoutBlockFlow* b1 = nonInlineBlockFlow(r1); |
| 1575 LayoutBlockFlow* b2 = nonInlineBlockFlow(r2); | 1595 LayoutBlockFlow* b2 = nonInlineBlockFlow(r2); |
| 1576 return b1 && b2 && b1 == b2; | 1596 return b1 && b2 && b1 == b2; |
| 1577 } | 1597 } |
| 1578 | 1598 |
| 1579 bool AXNodeObject::isNativeCheckboxInMixedState() const { | 1599 bool AXNodeObject::isNativeCheckboxInMixedState() const { |
| 1580 if (!isHTMLInputElement(m_node)) | 1600 if (!isHTMLInputElement(m_node)) |
| 1581 return false; | 1601 return false; |
| 1582 | 1602 |
| 1583 HTMLInputElement* input = toHTMLInputElement(m_node); | 1603 HTMLInputElement* input = toHTMLInputElement(m_node); |
| 1584 return input->type() == InputTypeNames::checkbox && | 1604 return input->type() == InputTypeNames::checkbox && |
| 1585 input->shouldAppearIndeterminate(); | 1605 input->shouldAppearIndeterminate(); |
| 1586 } | 1606 } |
| 1587 | 1607 |
| 1588 // | 1608 // |
| 1589 // New AX name calculation. | 1609 // New AX name calculation. |
| 1590 // | 1610 // |
| 1591 | 1611 |
| 1592 String AXNodeObject::textAlternative(bool recursive, | 1612 String AXNodeObject::textAlternative(bool recursive, |
| 1593 bool inAriaLabelledByTraversal, | 1613 bool inAriaLabelledByTraversal, |
| 1594 AXObjectSet& visited, | 1614 AXObjectSet& visited, |
| 1595 AXNameFrom& nameFrom, | 1615 AXNameFrom& nameFrom, |
| 1596 AXRelatedObjectVector* relatedObjects, | 1616 AXRelatedObjectVector* relatedObjects, |
| 1597 NameSources* nameSources) const { | 1617 NameSources* nameSources) const { |
| 1598 // If nameSources is non-null, relatedObjects is used in filling it in, so it
must be non-null as well. | 1618 // If nameSources is non-null, relatedObjects is used in filling it in, so it |
| 1619 // must be non-null as well. |
| 1599 if (nameSources) | 1620 if (nameSources) |
| 1600 ASSERT(relatedObjects); | 1621 ASSERT(relatedObjects); |
| 1601 | 1622 |
| 1602 bool foundTextAlternative = false; | 1623 bool foundTextAlternative = false; |
| 1603 | 1624 |
| 1604 if (!getNode() && !getLayoutObject()) | 1625 if (!getNode() && !getLayoutObject()) |
| 1605 return String(); | 1626 return String(); |
| 1606 | 1627 |
| 1607 String textAlternative = ariaTextAlternative( | 1628 String textAlternative = ariaTextAlternative( |
| 1608 recursive, inAriaLabelledByTraversal, visited, nameFrom, relatedObjects, | 1629 recursive, inAriaLabelledByTraversal, visited, nameFrom, relatedObjects, |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1706 computeAriaOwnsChildren(ownedChildren); | 1727 computeAriaOwnsChildren(ownedChildren); |
| 1707 for (AXObject* obj = rawFirstChild(); obj; obj = obj->rawNextSibling()) { | 1728 for (AXObject* obj = rawFirstChild(); obj; obj = obj->rawNextSibling()) { |
| 1708 if (!axObjectCache().isAriaOwned(obj)) | 1729 if (!axObjectCache().isAriaOwned(obj)) |
| 1709 children.append(obj); | 1730 children.append(obj); |
| 1710 } | 1731 } |
| 1711 for (const auto& ownedChild : ownedChildren) | 1732 for (const auto& ownedChild : ownedChildren) |
| 1712 children.append(ownedChild); | 1733 children.append(ownedChild); |
| 1713 | 1734 |
| 1714 for (AXObject* child : children) { | 1735 for (AXObject* child : children) { |
| 1715 // Don't recurse into children that are explicitly marked as aria-hidden. | 1736 // Don't recurse into children that are explicitly marked as aria-hidden. |
| 1716 // Note that we don't call isInertOrAriaHidden because that would return tru
e | 1737 // Note that we don't call isInertOrAriaHidden because that would return |
| 1717 // if any ancestor is hidden, but we need to be able to compute the accessib
le | 1738 // true if any ancestor is hidden, but we need to be able to compute the |
| 1718 // name of object inside hidden subtrees (for example, if aria-labelledby po
ints | 1739 // accessible name of object inside hidden subtrees (for example, if |
| 1719 // to an object that's hidden). | 1740 // aria-labelledby points to an object that's hidden). |
| 1720 if (equalIgnoringCase(child->getAttribute(aria_hiddenAttr), "true")) | 1741 if (equalIgnoringCase(child->getAttribute(aria_hiddenAttr), "true")) |
| 1721 continue; | 1742 continue; |
| 1722 | 1743 |
| 1723 // If we're going between two layoutObjects that are in separate LayoutBoxes
, add | 1744 // If we're going between two layoutObjects that are in separate |
| 1724 // whitespace if it wasn't there already. Intuitively if you have | 1745 // LayoutBoxes, add whitespace if it wasn't there already. Intuitively if |
| 1725 // <span>Hello</span><span>World</span>, those are part of the same LayoutBo
x | 1746 // you have <span>Hello</span><span>World</span>, those are part of the same |
| 1726 // so we should return "HelloWorld", but given <div>Hello</div><div>World</d
iv> the | 1747 // LayoutBox so we should return "HelloWorld", but given |
| 1727 // strings are in separate boxes so we should return "Hello World". | 1748 // <div>Hello</div><div>World</div> the strings are in separate boxes so we |
| 1749 // should return "Hello World". |
| 1728 if (previous && accumulatedText.length() && | 1750 if (previous && accumulatedText.length() && |
| 1729 !isHTMLSpace(accumulatedText[accumulatedText.length() - 1])) { | 1751 !isHTMLSpace(accumulatedText[accumulatedText.length() - 1])) { |
| 1730 if (!isInSameNonInlineBlockFlow(child->getLayoutObject(), | 1752 if (!isInSameNonInlineBlockFlow(child->getLayoutObject(), |
| 1731 previous->getLayoutObject())) | 1753 previous->getLayoutObject())) |
| 1732 accumulatedText.append(' '); | 1754 accumulatedText.append(' '); |
| 1733 } | 1755 } |
| 1734 | 1756 |
| 1735 String result; | 1757 String result; |
| 1736 if (child->isPresentational()) | 1758 if (child->isPresentational()) |
| 1737 result = child->textFromDescendants(visited, true); | 1759 result = child->textFromDescendants(visited, true); |
| 1738 else | 1760 else |
| 1739 result = recursiveTextAlternative(*child, false, visited); | 1761 result = recursiveTextAlternative(*child, false, visited); |
| 1740 accumulatedText.append(result); | 1762 accumulatedText.append(result); |
| 1741 previous = child; | 1763 previous = child; |
| 1742 } | 1764 } |
| 1743 | 1765 |
| 1744 return accumulatedText.toString(); | 1766 return accumulatedText.toString(); |
| 1745 } | 1767 } |
| 1746 | 1768 |
| 1747 bool AXNodeObject::nameFromLabelElement() const { | 1769 bool AXNodeObject::nameFromLabelElement() const { |
| 1748 // This unfortunately duplicates a bit of logic from textAlternative and nativ
eTextAlternative, | 1770 // This unfortunately duplicates a bit of logic from textAlternative and |
| 1749 // but it's necessary because nameFromLabelElement needs to be called from | 1771 // nativeTextAlternative, but it's necessary because nameFromLabelElement |
| 1750 // computeAccessibilityIsIgnored, which isn't allowed to call axObjectCache->g
etOrCreate. | 1772 // needs to be called from computeAccessibilityIsIgnored, which isn't allowed |
| 1773 // to call axObjectCache->getOrCreate. |
| 1751 | 1774 |
| 1752 if (!getNode() && !getLayoutObject()) | 1775 if (!getNode() && !getLayoutObject()) |
| 1753 return false; | 1776 return false; |
| 1754 | 1777 |
| 1755 // Step 2A from: http://www.w3.org/TR/accname-aam-1.1 | 1778 // Step 2A from: http://www.w3.org/TR/accname-aam-1.1 |
| 1756 if (isHiddenForTextAlternativeCalculation()) | 1779 if (isHiddenForTextAlternativeCalculation()) |
| 1757 return false; | 1780 return false; |
| 1758 | 1781 |
| 1759 // Step 2B from: http://www.w3.org/TR/accname-aam-1.1 | 1782 // Step 2B from: http://www.w3.org/TR/accname-aam-1.1 |
| 1760 HeapVector<Member<Element>> elements; | 1783 HeapVector<Member<Element>> elements; |
| 1761 ariaLabelledbyElementVector(elements); | 1784 ariaLabelledbyElementVector(elements); |
| 1762 if (elements.size() > 0) | 1785 if (elements.size() > 0) |
| 1763 return false; | 1786 return false; |
| 1764 | 1787 |
| 1765 // Step 2C from: http://www.w3.org/TR/accname-aam-1.1 | 1788 // Step 2C from: http://www.w3.org/TR/accname-aam-1.1 |
| 1766 const AtomicString& ariaLabel = getAttribute(aria_labelAttr); | 1789 const AtomicString& ariaLabel = getAttribute(aria_labelAttr); |
| 1767 if (!ariaLabel.isEmpty()) | 1790 if (!ariaLabel.isEmpty()) |
| 1768 return false; | 1791 return false; |
| 1769 | 1792 |
| 1770 // Based on http://rawgit.com/w3c/aria/master/html-aam/html-aam.html#accessibl
e-name-and-description-calculation | 1793 // Based on |
| 1794 // http://rawgit.com/w3c/aria/master/html-aam/html-aam.html#accessible-name-an
d-description-calculation |
| 1771 // 5.1/5.5 Text inputs, Other labelable Elements | 1795 // 5.1/5.5 Text inputs, Other labelable Elements |
| 1772 HTMLElement* htmlElement = nullptr; | 1796 HTMLElement* htmlElement = nullptr; |
| 1773 if (getNode()->isHTMLElement()) | 1797 if (getNode()->isHTMLElement()) |
| 1774 htmlElement = toHTMLElement(getNode()); | 1798 htmlElement = toHTMLElement(getNode()); |
| 1775 if (htmlElement && isLabelableElement(htmlElement)) { | 1799 if (htmlElement && isLabelableElement(htmlElement)) { |
| 1776 if (toLabelableElement(htmlElement)->labels() && | 1800 if (toLabelableElement(htmlElement)->labels() && |
| 1777 toLabelableElement(htmlElement)->labels()->length() > 0) | 1801 toLabelableElement(htmlElement)->labels()->length() > 0) |
| 1778 return true; | 1802 return true; |
| 1779 } | 1803 } |
| 1780 | 1804 |
| 1781 return false; | 1805 return false; |
| 1782 } | 1806 } |
| 1783 | 1807 |
| 1784 void AXNodeObject::getRelativeBounds(AXObject** outContainer, | 1808 void AXNodeObject::getRelativeBounds(AXObject** outContainer, |
| 1785 FloatRect& outBoundsInContainer, | 1809 FloatRect& outBoundsInContainer, |
| 1786 SkMatrix44& outContainerTransform) const { | 1810 SkMatrix44& outContainerTransform) const { |
| 1787 if (layoutObjectForRelativeBounds()) { | 1811 if (layoutObjectForRelativeBounds()) { |
| 1788 AXObject::getRelativeBounds(outContainer, outBoundsInContainer, | 1812 AXObject::getRelativeBounds(outContainer, outBoundsInContainer, |
| 1789 outContainerTransform); | 1813 outContainerTransform); |
| 1790 return; | 1814 return; |
| 1791 } | 1815 } |
| 1792 | 1816 |
| 1793 *outContainer = nullptr; | 1817 *outContainer = nullptr; |
| 1794 outBoundsInContainer = FloatRect(); | 1818 outBoundsInContainer = FloatRect(); |
| 1795 outContainerTransform.setIdentity(); | 1819 outContainerTransform.setIdentity(); |
| 1796 | 1820 |
| 1797 // First check if it has explicit bounds, for example if this element is tied
to a | 1821 // First check if it has explicit bounds, for example if this element is tied |
| 1798 // canvas path. When explicit coordinates are provided, the ID of the explicit
container | 1822 // to a canvas path. When explicit coordinates are provided, the ID of the |
| 1799 // element that the coordinates are relative to must be provided too. | 1823 // explicit container element that the coordinates are relative to must be |
| 1824 // provided too. |
| 1800 if (!m_explicitElementRect.isEmpty()) { | 1825 if (!m_explicitElementRect.isEmpty()) { |
| 1801 *outContainer = axObjectCache().objectFromAXID(m_explicitContainerID); | 1826 *outContainer = axObjectCache().objectFromAXID(m_explicitContainerID); |
| 1802 if (*outContainer) { | 1827 if (*outContainer) { |
| 1803 outBoundsInContainer = FloatRect(m_explicitElementRect); | 1828 outBoundsInContainer = FloatRect(m_explicitElementRect); |
| 1804 return; | 1829 return; |
| 1805 } | 1830 } |
| 1806 } | 1831 } |
| 1807 | 1832 |
| 1808 // If it's in a canvas but doesn't have an explicit rect, get the bounding rec
t of its children. | 1833 // If it's in a canvas but doesn't have an explicit rect, get the bounding |
| 1834 // rect of its children. |
| 1809 if (getNode()->parentElement()->isInCanvasSubtree()) { | 1835 if (getNode()->parentElement()->isInCanvasSubtree()) { |
| 1810 Vector<FloatRect> rects; | 1836 Vector<FloatRect> rects; |
| 1811 for (Node& child : NodeTraversal::childrenOf(*getNode())) { | 1837 for (Node& child : NodeTraversal::childrenOf(*getNode())) { |
| 1812 if (child.isHTMLElement()) { | 1838 if (child.isHTMLElement()) { |
| 1813 if (AXObject* obj = axObjectCache().get(&child)) { | 1839 if (AXObject* obj = axObjectCache().get(&child)) { |
| 1814 AXObject* container; | 1840 AXObject* container; |
| 1815 FloatRect bounds; | 1841 FloatRect bounds; |
| 1816 obj->getRelativeBounds(&container, bounds, outContainerTransform); | 1842 obj->getRelativeBounds(&container, bounds, outContainerTransform); |
| 1817 if (container) { | 1843 if (container) { |
| 1818 *outContainer = container; | 1844 *outContainer = container; |
| 1819 rects.append(bounds); | 1845 rects.append(bounds); |
| 1820 } | 1846 } |
| 1821 } | 1847 } |
| 1822 } | 1848 } |
| 1823 } | 1849 } |
| 1824 | 1850 |
| 1825 if (*outContainer) { | 1851 if (*outContainer) { |
| 1826 outBoundsInContainer = unionRect(rects); | 1852 outBoundsInContainer = unionRect(rects); |
| 1827 return; | 1853 return; |
| 1828 } | 1854 } |
| 1829 } | 1855 } |
| 1830 | 1856 |
| 1831 // If this object doesn't have an explicit element rect or computable from its
children, | 1857 // If this object doesn't have an explicit element rect or computable from its |
| 1832 // for now, let's return the position of the ancestor that does have a positio
n, | 1858 // children, for now, let's return the position of the ancestor that does have |
| 1833 // and make it the width of that parent, and about the height of a line of tex
t, so that | 1859 // a position, and make it the width of that parent, and about the height of a |
| 1834 // it's clear the object is a child of the parent. | 1860 // line of text, so that it's clear the object is a child of the parent. |
| 1835 for (AXObject* positionProvider = parentObject(); positionProvider; | 1861 for (AXObject* positionProvider = parentObject(); positionProvider; |
| 1836 positionProvider = positionProvider->parentObject()) { | 1862 positionProvider = positionProvider->parentObject()) { |
| 1837 if (positionProvider->isAXLayoutObject()) { | 1863 if (positionProvider->isAXLayoutObject()) { |
| 1838 positionProvider->getRelativeBounds(outContainer, outBoundsInContainer, | 1864 positionProvider->getRelativeBounds(outContainer, outBoundsInContainer, |
| 1839 outContainerTransform); | 1865 outContainerTransform); |
| 1840 if (*outContainer) | 1866 if (*outContainer) |
| 1841 outBoundsInContainer.setSize( | 1867 outBoundsInContainer.setSize( |
| 1842 FloatSize(outBoundsInContainer.width(), | 1868 FloatSize(outBoundsInContainer.width(), |
| 1843 std::min(10.0f, outBoundsInContainer.height()))); | 1869 std::min(10.0f, outBoundsInContainer.height()))); |
| 1844 break; | 1870 break; |
| 1845 } | 1871 } |
| 1846 } | 1872 } |
| 1847 } | 1873 } |
| 1848 | 1874 |
| 1849 static Node* getParentNodeForComputeParent(Node* node) { | 1875 static Node* getParentNodeForComputeParent(Node* node) { |
| 1850 if (!node) | 1876 if (!node) |
| 1851 return nullptr; | 1877 return nullptr; |
| 1852 | 1878 |
| 1853 Node* parentNode = nullptr; | 1879 Node* parentNode = nullptr; |
| 1854 | 1880 |
| 1855 // Skip over <optgroup> and consider the <select> the immediate parent of an <
option>. | 1881 // Skip over <optgroup> and consider the <select> the immediate parent of an |
| 1882 // <option>. |
| 1856 if (isHTMLOptionElement(node)) | 1883 if (isHTMLOptionElement(node)) |
| 1857 parentNode = toHTMLOptionElement(node)->ownerSelectElement(); | 1884 parentNode = toHTMLOptionElement(node)->ownerSelectElement(); |
| 1858 | 1885 |
| 1859 if (!parentNode) | 1886 if (!parentNode) |
| 1860 parentNode = node->parentNode(); | 1887 parentNode = node->parentNode(); |
| 1861 | 1888 |
| 1862 return parentNode; | 1889 return parentNode; |
| 1863 } | 1890 } |
| 1864 | 1891 |
| 1865 AXObject* AXNodeObject::computeParent() const { | 1892 AXObject* AXNodeObject::computeParent() const { |
| (...skipping 30 matching lines...) Expand all Loading... |
| 1896 Node* nextSibling = getNode()->nextSibling(); | 1923 Node* nextSibling = getNode()->nextSibling(); |
| 1897 if (!nextSibling) | 1924 if (!nextSibling) |
| 1898 return 0; | 1925 return 0; |
| 1899 | 1926 |
| 1900 return axObjectCache().getOrCreate(nextSibling); | 1927 return axObjectCache().getOrCreate(nextSibling); |
| 1901 } | 1928 } |
| 1902 | 1929 |
| 1903 void AXNodeObject::addChildren() { | 1930 void AXNodeObject::addChildren() { |
| 1904 ASSERT(!isDetached()); | 1931 ASSERT(!isDetached()); |
| 1905 // If the need to add more children in addition to existing children arises, | 1932 // If the need to add more children in addition to existing children arises, |
| 1906 // childrenChanged should have been called, leaving the object with no childre
n. | 1933 // childrenChanged should have been called, leaving the object with no |
| 1934 // children. |
| 1907 ASSERT(!m_haveChildren); | 1935 ASSERT(!m_haveChildren); |
| 1908 | 1936 |
| 1909 if (!m_node) | 1937 if (!m_node) |
| 1910 return; | 1938 return; |
| 1911 | 1939 |
| 1912 m_haveChildren = true; | 1940 m_haveChildren = true; |
| 1913 | 1941 |
| 1914 // The only time we add children from the DOM tree to a node with a layoutObje
ct is when it's a canvas. | 1942 // The only time we add children from the DOM tree to a node with a |
| 1943 // layoutObject is when it's a canvas. |
| 1915 if (getLayoutObject() && !isHTMLCanvasElement(*m_node)) | 1944 if (getLayoutObject() && !isHTMLCanvasElement(*m_node)) |
| 1916 return; | 1945 return; |
| 1917 | 1946 |
| 1918 HeapVector<Member<AXObject>> ownedChildren; | 1947 HeapVector<Member<AXObject>> ownedChildren; |
| 1919 computeAriaOwnsChildren(ownedChildren); | 1948 computeAriaOwnsChildren(ownedChildren); |
| 1920 | 1949 |
| 1921 for (Node& child : NodeTraversal::childrenOf(*m_node)) { | 1950 for (Node& child : NodeTraversal::childrenOf(*m_node)) { |
| 1922 AXObject* childObj = axObjectCache().getOrCreate(&child); | 1951 AXObject* childObj = axObjectCache().getOrCreate(&child); |
| 1923 if (childObj && !axObjectCache().isAriaOwned(childObj)) | 1952 if (childObj && !axObjectCache().isAriaOwned(childObj)) |
| 1924 addChild(childObj); | 1953 addChild(childObj); |
| 1925 } | 1954 } |
| 1926 | 1955 |
| 1927 for (const auto& ownedChild : ownedChildren) | 1956 for (const auto& ownedChild : ownedChildren) |
| 1928 addChild(ownedChild); | 1957 addChild(ownedChild); |
| 1929 | 1958 |
| 1930 for (const auto& child : m_children) | 1959 for (const auto& child : m_children) |
| 1931 child->setParent(this); | 1960 child->setParent(this); |
| 1932 } | 1961 } |
| 1933 | 1962 |
| 1934 void AXNodeObject::addChild(AXObject* child) { | 1963 void AXNodeObject::addChild(AXObject* child) { |
| 1935 insertChild(child, m_children.size()); | 1964 insertChild(child, m_children.size()); |
| 1936 } | 1965 } |
| 1937 | 1966 |
| 1938 void AXNodeObject::insertChild(AXObject* child, unsigned index) { | 1967 void AXNodeObject::insertChild(AXObject* child, unsigned index) { |
| 1939 if (!child) | 1968 if (!child) |
| 1940 return; | 1969 return; |
| 1941 | 1970 |
| 1942 // If the parent is asking for this child's children, then either it's the fir
st time (and clearing is a no-op), | 1971 // If the parent is asking for this child's children, then either it's the |
| 1943 // or its visibility has changed. In the latter case, this child may have a st
ale child cached. | 1972 // first time (and clearing is a no-op), or its visibility has changed. In the |
| 1944 // This can prevent aria-hidden changes from working correctly. Hence, wheneve
r a parent is getting children, ensure data is not stale. | 1973 // latter case, this child may have a stale child cached. This can prevent |
| 1974 // aria-hidden changes from working correctly. Hence, whenever a parent is |
| 1975 // getting children, ensure data is not stale. |
| 1945 child->clearChildren(); | 1976 child->clearChildren(); |
| 1946 | 1977 |
| 1947 if (child->accessibilityIsIgnored()) { | 1978 if (child->accessibilityIsIgnored()) { |
| 1948 const auto& children = child->children(); | 1979 const auto& children = child->children(); |
| 1949 size_t length = children.size(); | 1980 size_t length = children.size(); |
| 1950 for (size_t i = 0; i < length; ++i) | 1981 for (size_t i = 0; i < length; ++i) |
| 1951 m_children.insert(index + i, children[i]); | 1982 m_children.insert(index + i, children[i]); |
| 1952 } else { | 1983 } else { |
| 1953 ASSERT(child->parentObject() == this); | 1984 ASSERT(child->parentObject() == this); |
| 1954 m_children.insert(index, child); | 1985 m_children.insert(index, child); |
| 1955 } | 1986 } |
| 1956 } | 1987 } |
| 1957 | 1988 |
| 1958 bool AXNodeObject::canHaveChildren() const { | 1989 bool AXNodeObject::canHaveChildren() const { |
| 1959 // If this is an AXLayoutObject, then it's okay if this object | 1990 // If this is an AXLayoutObject, then it's okay if this object |
| 1960 // doesn't have a node - there are some layoutObjects that don't have associat
ed | 1991 // doesn't have a node - there are some layoutObjects that don't have |
| 1961 // nodes, like scroll areas and css-generated text. | 1992 // associated nodes, like scroll areas and css-generated text. |
| 1962 if (!getNode() && !isAXLayoutObject()) | 1993 if (!getNode() && !isAXLayoutObject()) |
| 1963 return false; | 1994 return false; |
| 1964 | 1995 |
| 1965 if (getNode() && isHTMLMapElement(getNode())) | 1996 if (getNode() && isHTMLMapElement(getNode())) |
| 1966 return false; | 1997 return false; |
| 1967 | 1998 |
| 1968 AccessibilityRole role = roleValue(); | 1999 AccessibilityRole role = roleValue(); |
| 1969 | 2000 |
| 1970 // If an element has an ARIA role of presentation, we need to consider the nat
ive | 2001 // If an element has an ARIA role of presentation, we need to consider the |
| 1971 // role when deciding whether it can have children or not - otherwise giving s
omething | 2002 // native role when deciding whether it can have children or not - otherwise |
| 1972 // a role of presentation could expose inner implementation details. | 2003 // giving something a role of presentation could expose inner implementation |
| 2004 // details. |
| 1973 if (isPresentational()) | 2005 if (isPresentational()) |
| 1974 role = nativeAccessibilityRoleIgnoringAria(); | 2006 role = nativeAccessibilityRoleIgnoringAria(); |
| 1975 | 2007 |
| 1976 switch (role) { | 2008 switch (role) { |
| 1977 case ImageRole: | 2009 case ImageRole: |
| 1978 case ButtonRole: | 2010 case ButtonRole: |
| 1979 case PopUpButtonRole: | 2011 case PopUpButtonRole: |
| 1980 case CheckBoxRole: | 2012 case CheckBoxRole: |
| 1981 case RadioButtonRole: | 2013 case RadioButtonRole: |
| 1982 case SwitchRole: | 2014 case SwitchRole: |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2038 } | 2070 } |
| 2039 | 2071 |
| 2040 Element* AXNodeObject::anchorElement() const { | 2072 Element* AXNodeObject::anchorElement() const { |
| 2041 Node* node = this->getNode(); | 2073 Node* node = this->getNode(); |
| 2042 if (!node) | 2074 if (!node) |
| 2043 return 0; | 2075 return 0; |
| 2044 | 2076 |
| 2045 AXObjectCacheImpl& cache = axObjectCache(); | 2077 AXObjectCacheImpl& cache = axObjectCache(); |
| 2046 | 2078 |
| 2047 // search up the DOM tree for an anchor element | 2079 // search up the DOM tree for an anchor element |
| 2048 // NOTE: this assumes that any non-image with an anchor is an HTMLAnchorElemen
t | 2080 // NOTE: this assumes that any non-image with an anchor is an |
| 2081 // HTMLAnchorElement |
| 2049 for (; node; node = node->parentNode()) { | 2082 for (; node; node = node->parentNode()) { |
| 2050 if (isHTMLAnchorElement(*node) || | 2083 if (isHTMLAnchorElement(*node) || |
| 2051 (node->layoutObject() && | 2084 (node->layoutObject() && |
| 2052 cache.getOrCreate(node->layoutObject())->isAnchor())) | 2085 cache.getOrCreate(node->layoutObject())->isAnchor())) |
| 2053 return toElement(node); | 2086 return toElement(node); |
| 2054 } | 2087 } |
| 2055 | 2088 |
| 2056 return 0; | 2089 return 0; |
| 2057 } | 2090 } |
| 2058 | 2091 |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2103 void AXNodeObject::setFocused(bool on) { | 2136 void AXNodeObject::setFocused(bool on) { |
| 2104 if (!canSetFocusAttribute()) | 2137 if (!canSetFocusAttribute()) |
| 2105 return; | 2138 return; |
| 2106 | 2139 |
| 2107 Document* document = this->getDocument(); | 2140 Document* document = this->getDocument(); |
| 2108 if (!on) { | 2141 if (!on) { |
| 2109 document->clearFocusedElement(); | 2142 document->clearFocusedElement(); |
| 2110 } else { | 2143 } else { |
| 2111 Node* node = this->getNode(); | 2144 Node* node = this->getNode(); |
| 2112 if (node && node->isElementNode()) { | 2145 if (node && node->isElementNode()) { |
| 2113 // If this node is already the currently focused node, then calling focus(
) won't do anything. | 2146 // If this node is already the currently focused node, then calling |
| 2114 // That is a problem when focus is removed from the webpage to chrome, and
then returns. | 2147 // focus() won't do anything. That is a problem when focus is removed |
| 2115 // In these cases, we need to do what keyboard and mouse focus do, which i
s reset focus first. | 2148 // from the webpage to chrome, and then returns. In these cases, we need |
| 2149 // to do what keyboard and mouse focus do, which is reset focus first. |
| 2116 if (document->focusedElement() == node) | 2150 if (document->focusedElement() == node) |
| 2117 document->clearFocusedElement(); | 2151 document->clearFocusedElement(); |
| 2118 | 2152 |
| 2119 toElement(node)->focus(); | 2153 toElement(node)->focus(); |
| 2120 } else { | 2154 } else { |
| 2121 document->clearFocusedElement(); | 2155 document->clearFocusedElement(); |
| 2122 } | 2156 } |
| 2123 } | 2157 } |
| 2124 } | 2158 } |
| 2125 | 2159 |
| 2126 void AXNodeObject::increment() { | 2160 void AXNodeObject::increment() { |
| 2127 UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture); | 2161 UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture); |
| 2128 alterSliderValue(true); | 2162 alterSliderValue(true); |
| 2129 } | 2163 } |
| 2130 | 2164 |
| 2131 void AXNodeObject::decrement() { | 2165 void AXNodeObject::decrement() { |
| 2132 UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture); | 2166 UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture); |
| 2133 alterSliderValue(false); | 2167 alterSliderValue(false); |
| 2134 } | 2168 } |
| 2135 | 2169 |
| 2136 void AXNodeObject::childrenChanged() { | 2170 void AXNodeObject::childrenChanged() { |
| 2137 // This method is meant as a quick way of marking a portion of the accessibili
ty tree dirty. | 2171 // This method is meant as a quick way of marking a portion of the |
| 2172 // accessibility tree dirty. |
| 2138 if (!getNode() && !getLayoutObject()) | 2173 if (!getNode() && !getLayoutObject()) |
| 2139 return; | 2174 return; |
| 2140 | 2175 |
| 2141 // If this is not part of the accessibility tree because an ancestor | 2176 // If this is not part of the accessibility tree because an ancestor |
| 2142 // has only presentational children, invalidate this object's children but | 2177 // has only presentational children, invalidate this object's children but |
| 2143 // skip sending a notification and skip walking up the ancestors. | 2178 // skip sending a notification and skip walking up the ancestors. |
| 2144 if (ancestorForWhichThisIsAPresentationalChild()) { | 2179 if (ancestorForWhichThisIsAPresentationalChild()) { |
| 2145 setNeedsToUpdateChildren(); | 2180 setNeedsToUpdateChildren(); |
| 2146 return; | 2181 return; |
| 2147 } | 2182 } |
| 2148 | 2183 |
| 2149 axObjectCache().postNotification(this, AXObjectCacheImpl::AXChildrenChanged); | 2184 axObjectCache().postNotification(this, AXObjectCacheImpl::AXChildrenChanged); |
| 2150 | 2185 |
| 2151 // Go up the accessibility parent chain, but only if the element already exist
s. This method is | 2186 // Go up the accessibility parent chain, but only if the element already |
| 2152 // called during layout, minimal work should be done. | 2187 // exists. This method is called during layout, minimal work should be done. |
| 2153 // If AX elements are created now, they could interrogate the layout tree whil
e it's in a funky state. | 2188 // If AX elements are created now, they could interrogate the layout tree |
| 2154 // At the same time, process ARIA live region changes. | 2189 // while it's in a funky state. At the same time, process ARIA live region |
| 2190 // changes. |
| 2155 for (AXObject* parent = this; parent; | 2191 for (AXObject* parent = this; parent; |
| 2156 parent = parent->parentObjectIfExists()) { | 2192 parent = parent->parentObjectIfExists()) { |
| 2157 parent->setNeedsToUpdateChildren(); | 2193 parent->setNeedsToUpdateChildren(); |
| 2158 | 2194 |
| 2159 // These notifications always need to be sent because screenreaders are reli
ant on them to perform. | 2195 // These notifications always need to be sent because screenreaders are |
| 2160 // In other words, they need to be sent even when the screen reader has not
accessed this live region since the last update. | 2196 // reliant on them to perform. In other words, they need to be sent even |
| 2197 // when the screen reader has not accessed this live region since the last |
| 2198 // update. |
| 2161 | 2199 |
| 2162 // If this element supports ARIA live regions, then notify the AT of changes
. | 2200 // If this element supports ARIA live regions, then notify the AT of |
| 2201 // changes. |
| 2163 if (parent->isLiveRegion()) | 2202 if (parent->isLiveRegion()) |
| 2164 axObjectCache().postNotification(parent, | 2203 axObjectCache().postNotification(parent, |
| 2165 AXObjectCacheImpl::AXLiveRegionChanged); | 2204 AXObjectCacheImpl::AXLiveRegionChanged); |
| 2166 | 2205 |
| 2167 // If this element is an ARIA text box or content editable, post a "value ch
anged" notification on it | 2206 // If this element is an ARIA text box or content editable, post a "value |
| 2168 // so that it behaves just like a native input element or textarea. | 2207 // changed" notification on it so that it behaves just like a native input |
| 2208 // element or textarea. |
| 2169 if (isNonNativeTextControl()) | 2209 if (isNonNativeTextControl()) |
| 2170 axObjectCache().postNotification(parent, | 2210 axObjectCache().postNotification(parent, |
| 2171 AXObjectCacheImpl::AXValueChanged); | 2211 AXObjectCacheImpl::AXValueChanged); |
| 2172 } | 2212 } |
| 2173 } | 2213 } |
| 2174 | 2214 |
| 2175 void AXNodeObject::selectionChanged() { | 2215 void AXNodeObject::selectionChanged() { |
| 2176 // Post the selected text changed event on the first ancestor that's | 2216 // Post the selected text changed event on the first ancestor that's |
| 2177 // focused (to handle form controls, ARIA text boxes and contentEditable), | 2217 // focused (to handle form controls, ARIA text boxes and contentEditable), |
| 2178 // or the web area if the selection is just in the document somewhere. | 2218 // or the web area if the selection is just in the document somewhere. |
| 2179 if (isFocused() || isWebArea()) { | 2219 if (isFocused() || isWebArea()) { |
| 2180 axObjectCache().postNotification(this, | 2220 axObjectCache().postNotification(this, |
| 2181 AXObjectCacheImpl::AXSelectedTextChanged); | 2221 AXObjectCacheImpl::AXSelectedTextChanged); |
| 2182 if (getDocument()) { | 2222 if (getDocument()) { |
| 2183 AXObject* documentObject = axObjectCache().getOrCreate(getDocument()); | 2223 AXObject* documentObject = axObjectCache().getOrCreate(getDocument()); |
| 2184 axObjectCache().postNotification( | 2224 axObjectCache().postNotification( |
| 2185 documentObject, AXObjectCacheImpl::AXDocumentSelectionChanged); | 2225 documentObject, AXObjectCacheImpl::AXDocumentSelectionChanged); |
| 2186 } | 2226 } |
| 2187 } else { | 2227 } else { |
| 2188 AXObject::selectionChanged(); // Calls selectionChanged on parent. | 2228 AXObject::selectionChanged(); // Calls selectionChanged on parent. |
| 2189 } | 2229 } |
| 2190 } | 2230 } |
| 2191 | 2231 |
| 2192 void AXNodeObject::textChanged() { | 2232 void AXNodeObject::textChanged() { |
| 2193 // If this element supports ARIA live regions, or is part of a region with an
ARIA editable role, | 2233 // If this element supports ARIA live regions, or is part of a region with an |
| 2194 // then notify the AT of changes. | 2234 // ARIA editable role, then notify the AT of changes. |
| 2195 AXObjectCacheImpl& cache = axObjectCache(); | 2235 AXObjectCacheImpl& cache = axObjectCache(); |
| 2196 for (Node* parentNode = getNode(); parentNode; | 2236 for (Node* parentNode = getNode(); parentNode; |
| 2197 parentNode = parentNode->parentNode()) { | 2237 parentNode = parentNode->parentNode()) { |
| 2198 AXObject* parent = cache.get(parentNode); | 2238 AXObject* parent = cache.get(parentNode); |
| 2199 if (!parent) | 2239 if (!parent) |
| 2200 continue; | 2240 continue; |
| 2201 | 2241 |
| 2202 if (parent->isLiveRegion()) | 2242 if (parent->isLiveRegion()) |
| 2203 cache.postNotification(parentNode, | 2243 cache.postNotification(parentNode, |
| 2204 AXObjectCacheImpl::AXLiveRegionChanged); | 2244 AXObjectCacheImpl::AXLiveRegionChanged); |
| 2205 | 2245 |
| 2206 // If this element is an ARIA text box or content editable, post a "value ch
anged" notification on it | 2246 // If this element is an ARIA text box or content editable, post a "value |
| 2207 // so that it behaves just like a native input element or textarea. | 2247 // changed" notification on it so that it behaves just like a native input |
| 2248 // element or textarea. |
| 2208 if (parent->isNonNativeTextControl()) | 2249 if (parent->isNonNativeTextControl()) |
| 2209 cache.postNotification(parentNode, AXObjectCacheImpl::AXValueChanged); | 2250 cache.postNotification(parentNode, AXObjectCacheImpl::AXValueChanged); |
| 2210 } | 2251 } |
| 2211 } | 2252 } |
| 2212 | 2253 |
| 2213 void AXNodeObject::updateAccessibilityRole() { | 2254 void AXNodeObject::updateAccessibilityRole() { |
| 2214 bool ignoredStatus = accessibilityIsIgnored(); | 2255 bool ignoredStatus = accessibilityIsIgnored(); |
| 2215 m_role = determineAccessibilityRole(); | 2256 m_role = determineAccessibilityRole(); |
| 2216 | 2257 |
| 2217 // The AX hierarchy only needs to be updated if the ignored status of an eleme
nt has changed. | 2258 // The AX hierarchy only needs to be updated if the ignored status of an |
| 2259 // element has changed. |
| 2218 if (ignoredStatus != accessibilityIsIgnored()) | 2260 if (ignoredStatus != accessibilityIsIgnored()) |
| 2219 childrenChanged(); | 2261 childrenChanged(); |
| 2220 } | 2262 } |
| 2221 | 2263 |
| 2222 void AXNodeObject::computeAriaOwnsChildren( | 2264 void AXNodeObject::computeAriaOwnsChildren( |
| 2223 HeapVector<Member<AXObject>>& ownedChildren) const { | 2265 HeapVector<Member<AXObject>>& ownedChildren) const { |
| 2224 if (!hasAttribute(aria_ownsAttr)) | 2266 if (!hasAttribute(aria_ownsAttr)) |
| 2225 return; | 2267 return; |
| 2226 | 2268 |
| 2227 Vector<String> idVector; | 2269 Vector<String> idVector; |
| 2228 if (canHaveChildren() && !isNativeTextControl() && | 2270 if (canHaveChildren() && !isNativeTextControl() && |
| 2229 !hasContentEditableAttributeSet()) | 2271 !hasContentEditableAttributeSet()) |
| 2230 tokenVectorFromAttribute(idVector, aria_ownsAttr); | 2272 tokenVectorFromAttribute(idVector, aria_ownsAttr); |
| 2231 | 2273 |
| 2232 axObjectCache().updateAriaOwns(this, idVector, ownedChildren); | 2274 axObjectCache().updateAriaOwns(this, idVector, ownedChildren); |
| 2233 } | 2275 } |
| 2234 | 2276 |
| 2235 // Based on http://rawgit.com/w3c/aria/master/html-aam/html-aam.html#accessible-
name-and-description-calculation | 2277 // Based on |
| 2278 // http://rawgit.com/w3c/aria/master/html-aam/html-aam.html#accessible-name-and-
description-calculation |
| 2236 String AXNodeObject::nativeTextAlternative( | 2279 String AXNodeObject::nativeTextAlternative( |
| 2237 AXObjectSet& visited, | 2280 AXObjectSet& visited, |
| 2238 AXNameFrom& nameFrom, | 2281 AXNameFrom& nameFrom, |
| 2239 AXRelatedObjectVector* relatedObjects, | 2282 AXRelatedObjectVector* relatedObjects, |
| 2240 NameSources* nameSources, | 2283 NameSources* nameSources, |
| 2241 bool* foundTextAlternative) const { | 2284 bool* foundTextAlternative) const { |
| 2242 if (!getNode()) | 2285 if (!getNode()) |
| 2243 return String(); | 2286 return String(); |
| 2244 | 2287 |
| 2245 // If nameSources is non-null, relatedObjects is used in filling it in, so it
must be non-null as well. | 2288 // If nameSources is non-null, relatedObjects is used in filling it in, so it |
| 2289 // must be non-null as well. |
| 2246 if (nameSources) | 2290 if (nameSources) |
| 2247 ASSERT(relatedObjects); | 2291 ASSERT(relatedObjects); |
| 2248 | 2292 |
| 2249 String textAlternative; | 2293 String textAlternative; |
| 2250 AXRelatedObjectVector localRelatedObjects; | 2294 AXRelatedObjectVector localRelatedObjects; |
| 2251 | 2295 |
| 2252 const HTMLInputElement* inputElement = nullptr; | 2296 const HTMLInputElement* inputElement = nullptr; |
| 2253 if (isHTMLInputElement(getNode())) | 2297 if (isHTMLInputElement(getNode())) |
| 2254 inputElement = toHTMLInputElement(getNode()); | 2298 inputElement = toHTMLInputElement(getNode()); |
| 2255 | 2299 |
| (...skipping 407 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2663 description(nameFrom, descriptionFrom, nullptr, &relatedObjects); | 2707 description(nameFrom, descriptionFrom, nullptr, &relatedObjects); |
| 2664 if (descriptionObjects) { | 2708 if (descriptionObjects) { |
| 2665 descriptionObjects->clear(); | 2709 descriptionObjects->clear(); |
| 2666 for (size_t i = 0; i < relatedObjects.size(); i++) | 2710 for (size_t i = 0; i < relatedObjects.size(); i++) |
| 2667 descriptionObjects->append(relatedObjects[i]->object); | 2711 descriptionObjects->append(relatedObjects[i]->object); |
| 2668 } | 2712 } |
| 2669 | 2713 |
| 2670 return collapseWhitespace(result); | 2714 return collapseWhitespace(result); |
| 2671 } | 2715 } |
| 2672 | 2716 |
| 2673 // Based on http://rawgit.com/w3c/aria/master/html-aam/html-aam.html#accessible-
name-and-description-calculation | 2717 // Based on |
| 2718 // http://rawgit.com/w3c/aria/master/html-aam/html-aam.html#accessible-name-and-
description-calculation |
| 2674 String AXNodeObject::description(AXNameFrom nameFrom, | 2719 String AXNodeObject::description(AXNameFrom nameFrom, |
| 2675 AXDescriptionFrom& descriptionFrom, | 2720 AXDescriptionFrom& descriptionFrom, |
| 2676 DescriptionSources* descriptionSources, | 2721 DescriptionSources* descriptionSources, |
| 2677 AXRelatedObjectVector* relatedObjects) const { | 2722 AXRelatedObjectVector* relatedObjects) const { |
| 2678 // If descriptionSources is non-null, relatedObjects is used in filling it in,
so it must be non-null as well. | 2723 // If descriptionSources is non-null, relatedObjects is used in filling it in, |
| 2724 // so it must be non-null as well. |
| 2679 if (descriptionSources) | 2725 if (descriptionSources) |
| 2680 ASSERT(relatedObjects); | 2726 ASSERT(relatedObjects); |
| 2681 | 2727 |
| 2682 if (!getNode()) | 2728 if (!getNode()) |
| 2683 return String(); | 2729 return String(); |
| 2684 | 2730 |
| 2685 String description; | 2731 String description; |
| 2686 bool foundDescription = false; | 2732 bool foundDescription = false; |
| 2687 | 2733 |
| 2688 descriptionFrom = AXDescriptionFromRelatedElement; | 2734 descriptionFrom = AXDescriptionFromRelatedElement; |
| 2689 if (descriptionSources) { | 2735 if (descriptionSources) { |
| 2690 descriptionSources->append( | 2736 descriptionSources->append( |
| 2691 DescriptionSource(foundDescription, aria_describedbyAttr)); | 2737 DescriptionSource(foundDescription, aria_describedbyAttr)); |
| 2692 descriptionSources->last().type = descriptionFrom; | 2738 descriptionSources->last().type = descriptionFrom; |
| 2693 } | 2739 } |
| 2694 | 2740 |
| 2695 // aria-describedby overrides any other accessible description, from: http://r
awgit.com/w3c/aria/master/html-aam/html-aam.html | 2741 // aria-describedby overrides any other accessible description, from: |
| 2742 // http://rawgit.com/w3c/aria/master/html-aam/html-aam.html |
| 2696 const AtomicString& ariaDescribedby = getAttribute(aria_describedbyAttr); | 2743 const AtomicString& ariaDescribedby = getAttribute(aria_describedbyAttr); |
| 2697 if (!ariaDescribedby.isNull()) { | 2744 if (!ariaDescribedby.isNull()) { |
| 2698 if (descriptionSources) | 2745 if (descriptionSources) |
| 2699 descriptionSources->last().attributeValue = ariaDescribedby; | 2746 descriptionSources->last().attributeValue = ariaDescribedby; |
| 2700 | 2747 |
| 2701 description = textFromAriaDescribedby(relatedObjects); | 2748 description = textFromAriaDescribedby(relatedObjects); |
| 2702 | 2749 |
| 2703 if (!description.isNull()) { | 2750 if (!description.isNull()) { |
| 2704 if (descriptionSources) { | 2751 if (descriptionSources) { |
| 2705 DescriptionSource& source = descriptionSources->last(); | 2752 DescriptionSource& source = descriptionSources->last(); |
| 2706 source.type = descriptionFrom; | 2753 source.type = descriptionFrom; |
| 2707 source.relatedObjects = *relatedObjects; | 2754 source.relatedObjects = *relatedObjects; |
| 2708 source.text = description; | 2755 source.text = description; |
| 2709 foundDescription = true; | 2756 foundDescription = true; |
| 2710 } else { | 2757 } else { |
| 2711 return description; | 2758 return description; |
| 2712 } | 2759 } |
| 2713 } else if (descriptionSources) { | 2760 } else if (descriptionSources) { |
| 2714 descriptionSources->last().invalid = true; | 2761 descriptionSources->last().invalid = true; |
| 2715 } | 2762 } |
| 2716 } | 2763 } |
| 2717 | 2764 |
| 2718 HTMLElement* htmlElement = nullptr; | 2765 HTMLElement* htmlElement = nullptr; |
| 2719 if (getNode()->isHTMLElement()) | 2766 if (getNode()->isHTMLElement()) |
| 2720 htmlElement = toHTMLElement(getNode()); | 2767 htmlElement = toHTMLElement(getNode()); |
| 2721 | 2768 |
| 2722 // placeholder, 5.1.2 from: http://rawgit.com/w3c/aria/master/html-aam/html-aa
m.html | 2769 // placeholder, 5.1.2 from: |
| 2770 // http://rawgit.com/w3c/aria/master/html-aam/html-aam.html |
| 2723 if (nameFrom != AXNameFromPlaceholder && htmlElement && | 2771 if (nameFrom != AXNameFromPlaceholder && htmlElement && |
| 2724 htmlElement->isTextFormControl()) { | 2772 htmlElement->isTextFormControl()) { |
| 2725 descriptionFrom = AXDescriptionFromPlaceholder; | 2773 descriptionFrom = AXDescriptionFromPlaceholder; |
| 2726 if (descriptionSources) { | 2774 if (descriptionSources) { |
| 2727 descriptionSources->append( | 2775 descriptionSources->append( |
| 2728 DescriptionSource(foundDescription, placeholderAttr)); | 2776 DescriptionSource(foundDescription, placeholderAttr)); |
| 2729 DescriptionSource& source = descriptionSources->last(); | 2777 DescriptionSource& source = descriptionSources->last(); |
| 2730 source.type = descriptionFrom; | 2778 source.type = descriptionFrom; |
| 2731 } | 2779 } |
| 2732 HTMLElement* element = toHTMLElement(getNode()); | 2780 HTMLElement* element = toHTMLElement(getNode()); |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2764 if (descriptionSources) { | 2812 if (descriptionSources) { |
| 2765 DescriptionSource& source = descriptionSources->last(); | 2813 DescriptionSource& source = descriptionSources->last(); |
| 2766 source.text = description; | 2814 source.text = description; |
| 2767 foundDescription = true; | 2815 foundDescription = true; |
| 2768 } else { | 2816 } else { |
| 2769 return description; | 2817 return description; |
| 2770 } | 2818 } |
| 2771 } | 2819 } |
| 2772 } | 2820 } |
| 2773 | 2821 |
| 2774 // table caption, 5.9.2 from: http://rawgit.com/w3c/aria/master/html-aam/html-
aam.html | 2822 // table caption, 5.9.2 from: |
| 2823 // http://rawgit.com/w3c/aria/master/html-aam/html-aam.html |
| 2775 if (nameFrom != AXNameFromCaption && isHTMLTableElement(getNode())) { | 2824 if (nameFrom != AXNameFromCaption && isHTMLTableElement(getNode())) { |
| 2776 HTMLTableElement* tableElement = toHTMLTableElement(getNode()); | 2825 HTMLTableElement* tableElement = toHTMLTableElement(getNode()); |
| 2777 | 2826 |
| 2778 descriptionFrom = AXDescriptionFromRelatedElement; | 2827 descriptionFrom = AXDescriptionFromRelatedElement; |
| 2779 if (descriptionSources) { | 2828 if (descriptionSources) { |
| 2780 descriptionSources->append(DescriptionSource(foundDescription)); | 2829 descriptionSources->append(DescriptionSource(foundDescription)); |
| 2781 descriptionSources->last().type = descriptionFrom; | 2830 descriptionSources->last().type = descriptionFrom; |
| 2782 descriptionSources->last().nativeSource = | 2831 descriptionSources->last().nativeSource = |
| 2783 AXTextFromNativeHTMLTableCaption; | 2832 AXTextFromNativeHTMLTableCaption; |
| 2784 } | 2833 } |
| (...skipping 13 matching lines...) Expand all Loading... |
| 2798 source.relatedObjects = *relatedObjects; | 2847 source.relatedObjects = *relatedObjects; |
| 2799 source.text = description; | 2848 source.text = description; |
| 2800 foundDescription = true; | 2849 foundDescription = true; |
| 2801 } else { | 2850 } else { |
| 2802 return description; | 2851 return description; |
| 2803 } | 2852 } |
| 2804 } | 2853 } |
| 2805 } | 2854 } |
| 2806 } | 2855 } |
| 2807 | 2856 |
| 2808 // summary, 5.6.2 from: http://rawgit.com/w3c/aria/master/html-aam/html-aam.ht
ml | 2857 // summary, 5.6.2 from: |
| 2858 // http://rawgit.com/w3c/aria/master/html-aam/html-aam.html |
| 2809 if (nameFrom != AXNameFromContents && isHTMLSummaryElement(getNode())) { | 2859 if (nameFrom != AXNameFromContents && isHTMLSummaryElement(getNode())) { |
| 2810 descriptionFrom = AXDescriptionFromContents; | 2860 descriptionFrom = AXDescriptionFromContents; |
| 2811 if (descriptionSources) { | 2861 if (descriptionSources) { |
| 2812 descriptionSources->append(DescriptionSource(foundDescription)); | 2862 descriptionSources->append(DescriptionSource(foundDescription)); |
| 2813 descriptionSources->last().type = descriptionFrom; | 2863 descriptionSources->last().type = descriptionFrom; |
| 2814 } | 2864 } |
| 2815 | 2865 |
| 2816 AXObjectSet visited; | 2866 AXObjectSet visited; |
| 2817 description = textFromDescendants(visited, false); | 2867 description = textFromDescendants(visited, false); |
| 2818 | 2868 |
| 2819 if (!description.isEmpty()) { | 2869 if (!description.isEmpty()) { |
| 2820 if (descriptionSources) { | 2870 if (descriptionSources) { |
| 2821 foundDescription = true; | 2871 foundDescription = true; |
| 2822 descriptionSources->last().text = description; | 2872 descriptionSources->last().text = description; |
| 2823 } else { | 2873 } else { |
| 2824 return description; | 2874 return description; |
| 2825 } | 2875 } |
| 2826 } | 2876 } |
| 2827 } | 2877 } |
| 2828 | 2878 |
| 2829 // title attribute, from: http://rawgit.com/w3c/aria/master/html-aam/html-aam.
html | 2879 // title attribute, from: |
| 2880 // http://rawgit.com/w3c/aria/master/html-aam/html-aam.html |
| 2830 if (nameFrom != AXNameFromTitle) { | 2881 if (nameFrom != AXNameFromTitle) { |
| 2831 descriptionFrom = AXDescriptionFromAttribute; | 2882 descriptionFrom = AXDescriptionFromAttribute; |
| 2832 if (descriptionSources) { | 2883 if (descriptionSources) { |
| 2833 descriptionSources->append( | 2884 descriptionSources->append( |
| 2834 DescriptionSource(foundDescription, titleAttr)); | 2885 DescriptionSource(foundDescription, titleAttr)); |
| 2835 descriptionSources->last().type = descriptionFrom; | 2886 descriptionSources->last().type = descriptionFrom; |
| 2836 } | 2887 } |
| 2837 const AtomicString& title = getAttribute(titleAttr); | 2888 const AtomicString& title = getAttribute(titleAttr); |
| 2838 if (!title.isEmpty()) { | 2889 if (!title.isEmpty()) { |
| 2839 description = title; | 2890 description = title; |
| 2840 if (descriptionSources) { | 2891 if (descriptionSources) { |
| 2841 foundDescription = true; | 2892 foundDescription = true; |
| 2842 descriptionSources->last().text = description; | 2893 descriptionSources->last().text = description; |
| 2843 } else { | 2894 } else { |
| 2844 return description; | 2895 return description; |
| 2845 } | 2896 } |
| 2846 } | 2897 } |
| 2847 } | 2898 } |
| 2848 | 2899 |
| 2849 // aria-help. | 2900 // aria-help. |
| 2850 // FIXME: this is not part of the official standard, but it's needed because t
he built-in date/time controls use it. | 2901 // FIXME: this is not part of the official standard, but it's needed because |
| 2902 // the built-in date/time controls use it. |
| 2851 descriptionFrom = AXDescriptionFromAttribute; | 2903 descriptionFrom = AXDescriptionFromAttribute; |
| 2852 if (descriptionSources) { | 2904 if (descriptionSources) { |
| 2853 descriptionSources->append( | 2905 descriptionSources->append( |
| 2854 DescriptionSource(foundDescription, aria_helpAttr)); | 2906 DescriptionSource(foundDescription, aria_helpAttr)); |
| 2855 descriptionSources->last().type = descriptionFrom; | 2907 descriptionSources->last().type = descriptionFrom; |
| 2856 } | 2908 } |
| 2857 const AtomicString& help = getAttribute(aria_helpAttr); | 2909 const AtomicString& help = getAttribute(aria_helpAttr); |
| 2858 if (!help.isEmpty()) { | 2910 if (!help.isEmpty()) { |
| 2859 description = help; | 2911 description = help; |
| 2860 if (descriptionSources) { | 2912 if (descriptionSources) { |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2904 } | 2956 } |
| 2905 return placeholder; | 2957 return placeholder; |
| 2906 } | 2958 } |
| 2907 | 2959 |
| 2908 DEFINE_TRACE(AXNodeObject) { | 2960 DEFINE_TRACE(AXNodeObject) { |
| 2909 visitor->trace(m_node); | 2961 visitor->trace(m_node); |
| 2910 AXObject::trace(visitor); | 2962 AXObject::trace(visitor); |
| 2911 } | 2963 } |
| 2912 | 2964 |
| 2913 } // namespace blink | 2965 } // namespace blink |
| OLD | NEW |