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 |