| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2008 Apple Inc. All rights reserved. | 2 * Copyright (C) 2008 Apple 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 450 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 461 } | 461 } |
| 462 | 462 |
| 463 // | 463 // |
| 464 // Whether objects are ignored, i.e. not included in the tree. | 464 // Whether objects are ignored, i.e. not included in the tree. |
| 465 // | 465 // |
| 466 | 466 |
| 467 AXObjectInclusion AXLayoutObject::defaultObjectInclusion(IgnoredReasons* ignored
Reasons) const | 467 AXObjectInclusion AXLayoutObject::defaultObjectInclusion(IgnoredReasons* ignored
Reasons) const |
| 468 { | 468 { |
| 469 // The following cases can apply to any element that's a subclass of AXLayou
tObject. | 469 // The following cases can apply to any element that's a subclass of AXLayou
tObject. |
| 470 | 470 |
| 471 if (!m_layoutObject) | 471 if (!m_layoutObject) { |
| 472 if (ignoredReasons) |
| 473 ignoredReasons->append(IgnoredReason(AXNotRendered)); |
| 472 return IgnoreObject; | 474 return IgnoreObject; |
| 475 } |
| 473 | 476 |
| 474 if (m_layoutObject->style()->visibility() != VISIBLE) { | 477 if (m_layoutObject->style()->visibility() != VISIBLE) { |
| 475 // aria-hidden is meant to override visibility as the determinant in AX
hierarchy inclusion. | 478 // aria-hidden is meant to override visibility as the determinant in AX
hierarchy inclusion. |
| 476 if (equalIgnoringCase(getAttribute(aria_hiddenAttr), "false")) | 479 if (equalIgnoringCase(getAttribute(aria_hiddenAttr), "false")) |
| 477 return DefaultBehavior; | 480 return DefaultBehavior; |
| 478 | 481 |
| 482 if (ignoredReasons) |
| 483 ignoredReasons->append(IgnoredReason(AXNotVisible)); |
| 479 return IgnoreObject; | 484 return IgnoreObject; |
| 480 } | 485 } |
| 481 | 486 |
| 482 return AXObject::defaultObjectInclusion(ignoredReasons); | 487 return AXObject::defaultObjectInclusion(ignoredReasons); |
| 483 } | 488 } |
| 484 | 489 |
| 485 bool AXLayoutObject::computeAccessibilityIsIgnored(IgnoredReasons* ignoredReason
s) const | 490 bool AXLayoutObject::computeAccessibilityIsIgnored(IgnoredReasons* ignoredReason
s) const |
| 486 { | 491 { |
| 487 #if ENABLE(ASSERT) | 492 #if ENABLE(ASSERT) |
| 488 ASSERT(m_initialized); | 493 ASSERT(m_initialized); |
| 489 #endif | 494 #endif |
| 490 | 495 |
| 491 // Check first if any of the common reasons cause this element to be ignored
. | 496 // Check first if any of the common reasons cause this element to be ignored
. |
| 492 // Then process other use cases that need to be applied to all the various r
oles | 497 // Then process other use cases that need to be applied to all the various r
oles |
| 493 // that AXLayoutObjects take on. | 498 // that AXLayoutObjects take on. |
| 494 AXObjectInclusion decision = defaultObjectInclusion(ignoredReasons); | 499 AXObjectInclusion decision = defaultObjectInclusion(ignoredReasons); |
| 495 if (decision == IncludeObject) | 500 if (decision == IncludeObject) |
| 496 return false; | 501 return false; |
| 497 if (decision == IgnoreObject) | 502 if (decision == IgnoreObject) |
| 498 return true; | 503 return true; |
| 499 | 504 |
| 500 // If this element is within a parent that cannot have children, it should n
ot be exposed. | 505 // If this element is within a parent that cannot have children, it should n
ot be exposed |
| 501 if (isDescendantOfLeafNode()) | 506 if (isDescendantOfLeafNode()) { |
| 507 if (ignoredReasons) |
| 508 ignoredReasons->append(IgnoredReason(AXAncestorIsLeafNode, leafNodeA
ncestor())); |
| 502 return true; | 509 return true; |
| 510 } |
| 503 | 511 |
| 504 if (roleValue() == IgnoredRole) | 512 if (roleValue() == IgnoredRole) { |
| 513 if (ignoredReasons) |
| 514 ignoredReasons->append(IgnoredReason(AXUninteresting)); |
| 505 return true; | 515 return true; |
| 516 } |
| 506 | 517 |
| 507 if (hasInheritedPresentationalRole()) | 518 if (hasInheritedPresentationalRole()) { |
| 519 if (ignoredReasons) { |
| 520 const AXObject* inheritsFrom = inheritsPresentationalRoleFrom(); |
| 521 if (inheritsFrom == this) |
| 522 ignoredReasons->append(IgnoredReason(AXPresentationalRole)); |
| 523 else |
| 524 ignoredReasons->append(IgnoredReason(AXInheritsPresentation, inh
eritsFrom)); |
| 525 } |
| 508 return true; | 526 return true; |
| 527 } |
| 509 | 528 |
| 510 // An ARIA tree can only have tree items and static text as children. | 529 // An ARIA tree can only have tree items and static text as children. |
| 511 if (treeAncestorDisallowingChild()) | 530 if (AXObject* treeAncestor = treeAncestorDisallowingChild()) { |
| 531 if (ignoredReasons) |
| 532 ignoredReasons->append(IgnoredReason(AXAncestorDisallowsChild, treeA
ncestor)); |
| 512 return true; | 533 return true; |
| 534 } |
| 513 | 535 |
| 514 // TODO: we should refactor this - but right now this is necessary to make | 536 // TODO: we should refactor this - but right now this is necessary to make |
| 515 // sure scroll areas stay in the tree. | 537 // sure scroll areas stay in the tree. |
| 516 if (isAttachment()) | 538 if (isAttachment()) |
| 517 return false; | 539 return false; |
| 518 | 540 |
| 519 // ignore popup menu items because AppKit does | |
| 520 for (LayoutObject* parent = m_layoutObject->parent(); parent; parent = paren
t->parent()) { | |
| 521 if (parent->isBoxModelObject() && toLayoutBoxModelObject(parent)->isMenu
List()) | |
| 522 return true; | |
| 523 } | |
| 524 | |
| 525 // find out if this element is inside of a label element. | 541 // find out if this element is inside of a label element. |
| 526 // if so, it may be ignored because it's the label for a checkbox or radio b
utton | 542 // if so, it may be ignored because it's the label for a checkbox or radio b
utton |
| 527 AXObject* controlObject = correspondingControlForLabelElement(); | 543 AXObject* controlObject = correspondingControlForLabelElement(); |
| 528 if (controlObject && !controlObject->deprecatedExposesTitleUIElement() && co
ntrolObject->isCheckboxOrRadio()) | 544 if (controlObject && !controlObject->deprecatedExposesTitleUIElement() && co
ntrolObject->isCheckboxOrRadio()) { |
| 545 if (ignoredReasons) { |
| 546 HTMLLabelElement* label = labelElementContainer(); |
| 547 if (label && !label->isSameNode(node())) { |
| 548 AXObject* labelAXObject = axObjectCache()->getOrCreate(label); |
| 549 ignoredReasons->append(IgnoredReason(AXLabelContainer, labelAXOb
ject)); |
| 550 } |
| 551 |
| 552 ignoredReasons->append(IgnoredReason(AXLabelFor, controlObject)); |
| 553 } |
| 529 return true; | 554 return true; |
| 555 } |
| 530 | 556 |
| 531 if (m_layoutObject->isBR()) | 557 if (m_layoutObject->isBR()) |
| 532 return false; | 558 return false; |
| 533 | 559 |
| 534 // NOTE: BRs always have text boxes now, so the text box check here can be r
emoved | |
| 535 if (m_layoutObject->isText()) { | 560 if (m_layoutObject->isText()) { |
| 536 // static text beneath MenuItems and MenuButtons are just reported along
with the menu item, so it's ignored on an individual level | 561 // static text beneath MenuItems and MenuButtons are just reported along
with the menu item, so it's ignored on an individual level |
| 537 AXObject* parent = parentObjectUnignored(); | 562 AXObject* parent = parentObjectUnignored(); |
| 538 if (parent && (parent->ariaRoleAttribute() == MenuItemRole || parent->ar
iaRoleAttribute() == MenuButtonRole)) | 563 if (parent && (parent->ariaRoleAttribute() == MenuItemRole || parent->ar
iaRoleAttribute() == MenuButtonRole)) { |
| 564 if (ignoredReasons) |
| 565 ignoredReasons->append(IgnoredReason(AXStaticTextUsedAsNameFor,
parent)); |
| 539 return true; | 566 return true; |
| 567 } |
| 540 LayoutText* layoutText = toLayoutText(m_layoutObject); | 568 LayoutText* layoutText = toLayoutText(m_layoutObject); |
| 541 if (m_layoutObject->isBR() || !layoutText->firstTextBox()) | 569 if (!layoutText->firstTextBox()) { |
| 570 if (ignoredReasons) |
| 571 ignoredReasons->append(IgnoredReason(AXEmptyText)); |
| 542 return true; | 572 return true; |
| 573 } |
| 543 | 574 |
| 544 // Don't ignore static text in editable text controls. | 575 // Don't ignore static text in editable text controls. |
| 545 for (AXObject* parent = parentObject(); parent; parent = parent->parentO
bject()) { | 576 for (AXObject* parent = parentObject(); parent; parent = parent->parentO
bject()) { |
| 546 if (parent->roleValue() == TextFieldRole) | 577 if (parent->roleValue() == TextFieldRole) |
| 547 return false; | 578 return false; |
| 548 } | 579 } |
| 549 | 580 |
| 550 // text elements that are just empty whitespace should not be returned | 581 // text elements that are just empty whitespace should not be returned |
| 551 // FIXME(dmazzoni): we probably shouldn't ignore this if the style is 'p
re', or similar... | 582 // FIXME(dmazzoni): we probably shouldn't ignore this if the style is 'p
re', or similar... |
| 552 return layoutText->text().impl()->containsOnlyWhitespace(); | 583 if (layoutText->text().impl()->containsOnlyWhitespace()) { |
| 584 if (ignoredReasons) |
| 585 ignoredReasons->append(IgnoredReason(AXEmptyText)); |
| 586 return true; |
| 587 } |
| 588 return false; |
| 553 } | 589 } |
| 554 | |
| 555 if (isHeading()) | 590 if (isHeading()) |
| 556 return false; | 591 return false; |
| 557 | 592 |
| 558 if (isLandmarkRelated()) | 593 if (isLandmarkRelated()) |
| 559 return false; | 594 return false; |
| 560 | 595 |
| 561 if (isLink()) | 596 if (isLink()) |
| 562 return false; | 597 return false; |
| 563 | 598 |
| 564 // all controls are accessible | 599 // all controls are accessible |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 610 // if this element has aria attributes on it, it should not be ignored. | 645 // if this element has aria attributes on it, it should not be ignored. |
| 611 if (supportsARIAAttributes()) | 646 if (supportsARIAAttributes()) |
| 612 return false; | 647 return false; |
| 613 | 648 |
| 614 // <span> tags are inline tags and not meant to convey information if they h
ave no other aria | 649 // <span> tags are inline tags and not meant to convey information if they h
ave no other aria |
| 615 // information on them. If we don't ignore them, they may emit signals expec
ted to come from | 650 // information on them. If we don't ignore them, they may emit signals expec
ted to come from |
| 616 // their parent. In addition, because included spans are GroupRole objects,
and GroupRole | 651 // their parent. In addition, because included spans are GroupRole objects,
and GroupRole |
| 617 // objects are often containers with meaningful information, the inclusion o
f a span can have | 652 // objects are often containers with meaningful information, the inclusion o
f a span can have |
| 618 // the side effect of causing the immediate parent accessible to be ignored.
This is especially | 653 // the side effect of causing the immediate parent accessible to be ignored.
This is especially |
| 619 // problematic for platforms which have distinct roles for textual block ele
ments. | 654 // problematic for platforms which have distinct roles for textual block ele
ments. |
| 620 if (isHTMLSpanElement(node)) | 655 if (isHTMLSpanElement(node)) { |
| 656 if (ignoredReasons) |
| 657 ignoredReasons->append(IgnoredReason(AXUninteresting)); |
| 621 return true; | 658 return true; |
| 659 } |
| 622 | 660 |
| 623 if (m_layoutObject->isLayoutBlockFlow() && m_layoutObject->childrenInline()
&& !canSetFocusAttribute()) | 661 if (m_layoutObject->isLayoutBlockFlow() && m_layoutObject->childrenInline()
&& !canSetFocusAttribute()) { |
| 624 return !toLayoutBlockFlow(m_layoutObject)->firstLineBox() && !mouseButto
nListener(); | 662 if (toLayoutBlockFlow(m_layoutObject)->firstLineBox() || mouseButtonList
ener()) |
| 663 return false; |
| 664 |
| 665 if (ignoredReasons) |
| 666 ignoredReasons->append(IgnoredReason(AXUninteresting)); |
| 667 return true; |
| 668 } |
| 625 | 669 |
| 626 // ignore images seemingly used as spacers | 670 // ignore images seemingly used as spacers |
| 627 if (isImage()) { | 671 if (isImage()) { |
| 628 | |
| 629 // If the image can take focus, it should not be ignored, lest the user
not be able to interact with something important. | 672 // If the image can take focus, it should not be ignored, lest the user
not be able to interact with something important. |
| 630 if (canSetFocusAttribute()) | 673 if (canSetFocusAttribute()) |
| 631 return false; | 674 return false; |
| 632 | 675 |
| 633 if (node && node->isElementNode()) { | 676 if (node && node->isElementNode()) { |
| 634 Element* elt = toElement(node); | 677 Element* elt = toElement(node); |
| 635 const AtomicString& alt = elt->getAttribute(altAttr); | 678 const AtomicString& alt = elt->getAttribute(altAttr); |
| 636 // don't ignore an image that has an alt tag | 679 // don't ignore an image that has an alt tag |
| 637 if (!alt.string().containsOnlyWhitespace()) | 680 if (!alt.string().containsOnlyWhitespace()) |
| 638 return false; | 681 return false; |
| 639 // informal standard is to ignore images with zero-length alt string
s | 682 // informal standard is to ignore images with zero-length alt string
s |
| 640 if (!alt.isNull()) | 683 if (!alt.isNull()) { |
| 684 if (ignoredReasons) |
| 685 ignoredReasons->append(IgnoredReason(AXEmptyAlt)); |
| 641 return true; | 686 return true; |
| 687 } |
| 642 } | 688 } |
| 643 | 689 |
| 644 if (isNativeImage() && m_layoutObject->isImage()) { | 690 if (isNativeImage() && m_layoutObject->isImage()) { |
| 645 // check for one-dimensional image | 691 // check for one-dimensional image |
| 646 LayoutImage* image = toLayoutImage(m_layoutObject); | 692 LayoutImage* image = toLayoutImage(m_layoutObject); |
| 647 if (image->size().height() <= 1 || image->size().width() <= 1) | 693 if (image->size().height() <= 1 || image->size().width() <= 1) { |
| 694 if (ignoredReasons) |
| 695 ignoredReasons->append(IgnoredReason(AXProbablyPresentationa
l)); |
| 648 return true; | 696 return true; |
| 697 } |
| 649 | 698 |
| 650 // check whether laid out image was stretched from one-dimensional f
ile image | 699 // check whether laid out image was stretched from one-dimensional f
ile image |
| 651 if (image->cachedImage()) { | 700 if (image->cachedImage()) { |
| 652 LayoutSize imageSize = image->cachedImage()->imageSizeForLayoutO
bject(m_layoutObject, image->view()->zoomFactor()); | 701 LayoutSize imageSize = image->cachedImage()->imageSizeForLayoutO
bject(m_layoutObject, image->view()->zoomFactor()); |
| 653 return imageSize.height() <= 1 || imageSize.width() <= 1; | 702 if (imageSize.height() <= 1 || imageSize.width() <= 1) { |
| 703 if (ignoredReasons) |
| 704 ignoredReasons->append(IgnoredReason(AXProbablyPresentat
ional)); |
| 705 return true; |
| 706 } |
| 707 return false; |
| 654 } | 708 } |
| 655 } | 709 } |
| 656 return false; | 710 return false; |
| 657 } | 711 } |
| 658 | 712 |
| 659 if (isCanvas()) { | 713 if (isCanvas()) { |
| 660 if (canvasHasFallbackContent()) | 714 if (canvasHasFallbackContent()) |
| 661 return false; | 715 return false; |
| 662 LayoutHTMLCanvas* canvas = toLayoutHTMLCanvas(m_layoutObject); | 716 LayoutHTMLCanvas* canvas = toLayoutHTMLCanvas(m_layoutObject); |
| 663 if (canvas->size().height() <= 1 || canvas->size().width() <= 1) | 717 if (canvas->size().height() <= 1 || canvas->size().width() <= 1) { |
| 718 if (ignoredReasons) |
| 719 ignoredReasons->append(IgnoredReason(AXProbablyPresentational)); |
| 664 return true; | 720 return true; |
| 721 } |
| 665 // Otherwise fall through; use presence of help text, title, or descript
ion to decide. | 722 // Otherwise fall through; use presence of help text, title, or descript
ion to decide. |
| 666 } | 723 } |
| 667 | 724 |
| 668 if (isWebArea() || m_layoutObject->isListMarker()) | 725 if (isWebArea() || m_layoutObject->isListMarker()) |
| 669 return false; | 726 return false; |
| 670 | 727 |
| 671 // Using the help text, title or accessibility description (so we | 728 // Using the help text, title or accessibility description (so we |
| 672 // check if there's some kind of accessible name for the element) | 729 // check if there's some kind of accessible name for the element) |
| 673 // to decide an element's visibility is not as definitive as | 730 // to decide an element's visibility is not as definitive as |
| 674 // previous checks, so this should remain as one of the last. | 731 // previous checks, so this should remain as one of the last. |
| 675 // | 732 // |
| 676 // These checks are simplified in the interest of execution speed; | 733 // These checks are simplified in the interest of execution speed; |
| 677 // for example, any element having an alt attribute will make it | 734 // for example, any element having an alt attribute will make it |
| 678 // not ignored, rather than just images. | 735 // not ignored, rather than just images. |
| 679 if (!getAttribute(aria_helpAttr).isEmpty() || !getAttribute(aria_describedby
Attr).isEmpty() || !getAttribute(altAttr).isEmpty() || !getAttribute(titleAttr).
isEmpty()) | 736 if (!getAttribute(aria_helpAttr).isEmpty() || !getAttribute(aria_describedby
Attr).isEmpty() || !getAttribute(altAttr).isEmpty() || !getAttribute(titleAttr).
isEmpty()) |
| 680 return false; | 737 return false; |
| 681 | 738 |
| 682 // Don't ignore generic focusable elements like <div tabindex=0> | 739 // Don't ignore generic focusable elements like <div tabindex=0> |
| 683 // unless they're completely empty, with no children. | 740 // unless they're completely empty, with no children. |
| 684 if (isGenericFocusableElement() && node->hasChildren()) | 741 if (isGenericFocusableElement() && node->hasChildren()) |
| 685 return false; | 742 return false; |
| 686 | 743 |
| 687 if (!ariaAccessibilityDescription().isEmpty()) | 744 if (!ariaAccessibilityDescription().isEmpty()) |
| 688 return false; | 745 return false; |
| 689 | 746 |
| 690 // By default, objects should be ignored so that the AX hierarchy is not | 747 // By default, objects should be ignored so that the AX hierarchy is not |
| 691 // filled with unnecessary items. | 748 // filled with unnecessary items. |
| 749 if (ignoredReasons) |
| 750 ignoredReasons->append(IgnoredReason(AXUninteresting)); |
| 692 return true; | 751 return true; |
| 693 } | 752 } |
| 694 | 753 |
| 695 // | 754 // |
| 696 // Properties of static elements. | 755 // Properties of static elements. |
| 697 // | 756 // |
| 698 | 757 |
| 699 const AtomicString& AXLayoutObject::accessKey() const | 758 const AtomicString& AXLayoutObject::accessKey() const |
| 700 { | 759 { |
| 701 Node* node = m_layoutObject->node(); | 760 Node* node = m_layoutObject->node(); |
| (...skipping 1576 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2278 if (label && label->layoutObject()) { | 2337 if (label && label->layoutObject()) { |
| 2279 LayoutRect labelRect = axObjectCache()->getOrCreate(label)->elementR
ect(); | 2338 LayoutRect labelRect = axObjectCache()->getOrCreate(label)->elementR
ect(); |
| 2280 result.unite(labelRect); | 2339 result.unite(labelRect); |
| 2281 } | 2340 } |
| 2282 } | 2341 } |
| 2283 | 2342 |
| 2284 return result; | 2343 return result; |
| 2285 } | 2344 } |
| 2286 | 2345 |
| 2287 } // namespace blink | 2346 } // namespace blink |
| OLD | NEW |