OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/browser/accessibility/browser_accessibility_win.h" | 5 #include "content/browser/accessibility/browser_accessibility_win.h" |
6 | 6 |
7 #include <UIAutomationClient.h> | 7 #include <UIAutomationClient.h> |
8 #include <UIAutomationCoreApi.h> | 8 #include <UIAutomationCoreApi.h> |
9 | 9 |
10 #include <algorithm> | 10 #include <algorithm> |
(...skipping 2046 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2057 STDMETHODIMP BrowserAccessibilityWin::get_nSelections(LONG* n_selections) { | 2057 STDMETHODIMP BrowserAccessibilityWin::get_nSelections(LONG* n_selections) { |
2058 if (!instance_active()) | 2058 if (!instance_active()) |
2059 return E_FAIL; | 2059 return E_FAIL; |
2060 | 2060 |
2061 if (!n_selections) | 2061 if (!n_selections) |
2062 return E_INVALIDARG; | 2062 return E_INVALIDARG; |
2063 | 2063 |
2064 *n_selections = 0; | 2064 *n_selections = 0; |
2065 int selection_start, selection_end; | 2065 int selection_start, selection_end; |
2066 GetSelectionOffsets(&selection_start, &selection_end); | 2066 GetSelectionOffsets(&selection_start, &selection_end); |
2067 if (selection_start >= 0 && selection_end >= 0 && | 2067 |
2068 // A selection exists either: when both selection endpoints are inside this | |
2069 // object but their values are not equal, or when one endpoint is inside and | |
2070 // the other outside this object. | |
2071 if ((selection_start >= 0 || selection_end >= 0) && | |
2068 selection_start != selection_end) | 2072 selection_start != selection_end) |
2069 *n_selections = 1; | 2073 *n_selections = 1; |
2070 | 2074 |
2071 return S_OK; | 2075 return S_OK; |
2072 } | 2076 } |
2073 | 2077 |
2074 STDMETHODIMP BrowserAccessibilityWin::get_selection(LONG selection_index, | 2078 STDMETHODIMP BrowserAccessibilityWin::get_selection(LONG selection_index, |
2075 LONG* start_offset, | 2079 LONG* start_offset, |
2076 LONG* end_offset) { | 2080 LONG* end_offset) { |
2077 if (!instance_active()) | 2081 if (!instance_active()) |
(...skipping 1563 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
3641 int value; | 3645 int value; |
3642 if (GetIntAttribute(attribute, &value)) { | 3646 if (GetIntAttribute(attribute, &value)) { |
3643 win_attributes_->ia2_attributes.push_back( | 3647 win_attributes_->ia2_attributes.push_back( |
3644 base::ASCIIToUTF16(ia2_attr) + L":" + | 3648 base::ASCIIToUTF16(ia2_attr) + L":" + |
3645 base::IntToString16(value)); | 3649 base::IntToString16(value)); |
3646 } | 3650 } |
3647 } | 3651 } |
3648 | 3652 |
3649 int32 BrowserAccessibilityWin::GetHyperlinkIndexFromChild( | 3653 int32 BrowserAccessibilityWin::GetHyperlinkIndexFromChild( |
3650 const BrowserAccessibilityWin& child) const { | 3654 const BrowserAccessibilityWin& child) const { |
3655 if (hyperlinks().empty()) | |
3656 return -1; | |
3657 | |
3651 auto iterator = std::find( | 3658 auto iterator = std::find( |
3652 hyperlinks().begin(), hyperlinks().end(), child.GetId()); | 3659 hyperlinks().begin(), hyperlinks().end(), child.GetId()); |
3653 if (iterator == hyperlinks().end()) | 3660 if (iterator == hyperlinks().end()) |
3654 return -1; | 3661 return -1; |
3655 | 3662 |
3656 return static_cast<int32>(iterator - hyperlinks().begin()); | 3663 return static_cast<int32>(iterator - hyperlinks().begin()); |
3657 } | 3664 } |
3658 | 3665 |
3659 int32 BrowserAccessibilityWin::GetHypertextOffsetFromHyperlinkIndex( | 3666 int32 BrowserAccessibilityWin::GetHypertextOffsetFromHyperlinkIndex( |
3660 int32 hyperlink_index) const { | 3667 int32 hyperlink_index) const { |
3661 auto& offsets_map = hyperlink_offset_to_index(); | 3668 for (auto& offset_index : hyperlink_offset_to_index()) { |
3662 for (auto& offset_index : offsets_map) { | |
3663 if (offset_index.second == hyperlink_index) | 3669 if (offset_index.second == hyperlink_index) |
3664 return offset_index.first; | 3670 return offset_index.first; |
3665 } | 3671 } |
3666 | 3672 |
3667 return -1; | 3673 return -1; |
3668 } | 3674 } |
3669 | 3675 |
3670 int32 BrowserAccessibilityWin::GetHypertextOffsetFromChild( | 3676 int32 BrowserAccessibilityWin::GetHypertextOffsetFromChild( |
3671 const BrowserAccessibilityWin& child) const { | 3677 const BrowserAccessibilityWin& child) const { |
3672 int32 hyperlink_index = GetHyperlinkIndexFromChild(child); | 3678 int32 hyperlink_index = GetHyperlinkIndexFromChild(child); |
(...skipping 10 matching lines...) Expand all Loading... | |
3683 while (parent_object && parent_object != this) { | 3689 while (parent_object && parent_object != this) { |
3684 current_object = parent_object; | 3690 current_object = parent_object; |
3685 parent_object = current_object->GetParent()->ToBrowserAccessibilityWin(); | 3691 parent_object = current_object->GetParent()->ToBrowserAccessibilityWin(); |
3686 } | 3692 } |
3687 if (!parent_object) | 3693 if (!parent_object) |
3688 return -1; | 3694 return -1; |
3689 | 3695 |
3690 return parent_object->GetHypertextOffsetFromChild(*current_object); | 3696 return parent_object->GetHypertextOffsetFromChild(*current_object); |
3691 } | 3697 } |
3692 | 3698 |
3699 int BrowserAccessibilityWin::GetHypertextOffsetFromEndpoint( | |
3700 const BrowserAccessibilityWin& endpoint_object, int endpoint_offset) const { | |
3701 // There are three cases: | |
3702 // 1. Either the selection endpoint is inside this object or is an ancestor of | |
3703 // of this object. endpoint_offset should be returned. | |
3704 // 2. The selection endpoint is a pure descendant of this object. The offset | |
3705 // of the embedded object character corresponding to the subtree in which | |
3706 // the endpoint is located should be returned. | |
3707 // 3. The selection endpoint is in a completely different part of the tree. | |
3708 // Either 0 or text_length should be returned depending on the direction that | |
3709 // one needs to travel to find the endpoint. | |
3710 | |
3711 | |
3712 // Case 1. | |
3713 | |
3714 // IsDescendantOf includes the case when endpoint_object == this. | |
3715 if (IsDescendantOf(&endpoint_object) || | |
3716 // Handle the case when the endpoint is a direct text-only child. | |
3717 // The offset should still be valid on the parent. | |
3718 (endpoint_object.IsTextOnlyObject() && | |
3719 endpoint_object.GetParent() == this)) { | |
3720 return endpoint_offset; | |
3721 } | |
3722 | |
dmazzoni
2015/09/30 20:09:56
Please remove the double blank lines within a func
| |
3723 | |
3724 const BrowserAccessibility* common_parent = this; | |
3725 while (common_parent && !endpoint_object.IsDescendantOf(common_parent)) { | |
3726 common_parent = common_parent->GetParent(); | |
3727 } | |
3728 if (!common_parent) | |
3729 return -1; | |
3730 | |
3731 // TODO(nektar): Add a const version of ToBrowserAccessibilityWin. | |
dmazzoni
2015/09/30 20:09:56
Please just do this as part of this changelist
| |
3732 auto common_parent_win = static_cast<const BrowserAccessibilityWin*>( | |
3733 common_parent); | |
3734 | |
3735 // Text only objects must have a parent. | |
3736 DCHECK(!IsTextOnlyObject() || GetParent()); | |
3737 DCHECK(!endpoint_object.IsTextOnlyObject() || endpoint_object.GetParent()); | |
3738 // Text only objects that are direct descendants should behave as if they | |
3739 // are part of their parent when computing hyperlink offsets. | |
3740 const BrowserAccessibilityWin* non_text_this = IsTextOnlyObject() ? | |
dmazzoni
2015/09/30 20:09:56
how about nearest_non_text_ancestor
| |
3741 GetParent()->ToBrowserAccessibilityWin() : this; | |
3742 const BrowserAccessibilityWin& non_text_endpoint = | |
3743 endpoint_object.IsTextOnlyObject() ? | |
3744 *(endpoint_object.GetParent()->ToBrowserAccessibilityWin()) : | |
dmazzoni
2015/09/30 20:09:56
I'd indent this and the next line 4 more spaces be
| |
3745 endpoint_object; | |
3746 | |
3747 | |
3748 // Case 2. | |
3749 | |
3750 // We already checked in case 1 if our endpoint is inside this object. | |
3751 // We can safely assume that it is a descendant or in a completely different | |
3752 // part of the tree. | |
3753 if (common_parent_win == non_text_this) | |
3754 return non_text_this->GetHypertextOffsetFromDescendant(non_text_endpoint); | |
3755 | |
3756 | |
3757 // Case 3. | |
3758 | |
3759 // We can safely assume that the endpoint is in another part of the tree or | |
3760 // at common parent, and that this object is a descendant of common parent. | |
3761 int32 current_offset = static_cast<int>( | |
dmazzoni
2015/09/30 20:09:56
nit: <int32> to match the type you're assigning to
| |
3762 common_parent_win->GetHypertextOffsetFromDescendant(*non_text_this)); | |
3763 DCHECK_GE(current_offset, 0); | |
3764 if (common_parent_win != &non_text_endpoint) { | |
3765 endpoint_offset = static_cast<int>( | |
3766 common_parent_win->GetHypertextOffsetFromDescendant(non_text_endpoint)); | |
3767 DCHECK_GE(endpoint_offset, 0); | |
3768 } | |
3769 | |
3770 if (endpoint_offset < current_offset) | |
3771 return 0; | |
3772 if (endpoint_offset > current_offset) | |
3773 return TextForIAccessibleText().length(); | |
3774 | |
3775 NOTREACHED(); | |
3776 return -1; | |
3777 } | |
3778 | |
3693 int BrowserAccessibilityWin::GetSelectionAnchor() const { | 3779 int BrowserAccessibilityWin::GetSelectionAnchor() const { |
3694 BrowserAccessibility* root = manager()->GetRoot(); | 3780 BrowserAccessibility* root = manager()->GetRoot(); |
3695 int32 anchor_id; | 3781 int32 anchor_id; |
3696 if (!root || !root->GetIntAttribute(ui::AX_ATTR_ANCHOR_OBJECT_ID, &anchor_id)) | 3782 if (!root || !root->GetIntAttribute(ui::AX_ATTR_ANCHOR_OBJECT_ID, &anchor_id)) |
3697 return -1; | 3783 return -1; |
3698 | 3784 |
3699 BrowserAccessibilityWin* anchor_object = manager()->GetFromID( | 3785 BrowserAccessibilityWin* anchor_object = manager()->GetFromID( |
3700 anchor_id)->ToBrowserAccessibilityWin(); | 3786 anchor_id)->ToBrowserAccessibilityWin(); |
3701 if (!anchor_object) | 3787 if (!anchor_object) |
3702 return -1; | 3788 return -1; |
3703 | 3789 |
3704 // Includes the case when anchor_object == this. | 3790 int anchor_offset; |
3705 if (IsDescendantOf(anchor_object) || | 3791 if (!root->GetIntAttribute(ui::AX_ATTR_ANCHOR_OFFSET, &anchor_offset)) |
3706 // Text only objects that are direct descendants should behave as if they | 3792 return -1; |
3707 // are part of this object when computing hypertext. | |
3708 (anchor_object->GetParent() == this && | |
3709 anchor_object->IsTextOnlyObject())) { | |
3710 int anchor_offset; | |
3711 if (!root->GetIntAttribute(ui::AX_ATTR_ANCHOR_OFFSET, &anchor_offset)) | |
3712 return -1; | |
3713 | 3793 |
3714 return anchor_offset; | 3794 return GetHypertextOffsetFromEndpoint(*anchor_object, anchor_offset); |
3715 } | |
3716 | |
3717 if (anchor_object->IsDescendantOf(this)) | |
3718 return GetHypertextOffsetFromDescendant(*anchor_object); | |
3719 | |
3720 return -1; | |
3721 } | 3795 } |
3722 | 3796 |
3723 int BrowserAccessibilityWin::GetSelectionFocus() const { | 3797 int BrowserAccessibilityWin::GetSelectionFocus() const { |
3724 BrowserAccessibility* root = manager()->GetRoot(); | 3798 BrowserAccessibility* root = manager()->GetRoot(); |
3725 int32 focus_id; | 3799 int32 focus_id; |
3726 if (!root || !root->GetIntAttribute(ui::AX_ATTR_FOCUS_OBJECT_ID, &focus_id)) | 3800 if (!root || !root->GetIntAttribute(ui::AX_ATTR_FOCUS_OBJECT_ID, &focus_id)) |
3727 return -1; | 3801 return -1; |
3728 | 3802 |
3729 BrowserAccessibilityWin* focus_object = manager()->GetFromID( | 3803 BrowserAccessibilityWin* focus_object = manager()->GetFromID( |
3730 focus_id)->ToBrowserAccessibilityWin(); | 3804 focus_id)->ToBrowserAccessibilityWin(); |
3731 if (!focus_object) | 3805 if (!focus_object) |
3732 return -1; | 3806 return -1; |
3733 | 3807 |
3734 // Includes the case when focus_object == this. | 3808 int focus_offset; |
3735 if (IsDescendantOf(focus_object) || | 3809 if (!root->GetIntAttribute(ui::AX_ATTR_FOCUS_OFFSET, &focus_offset)) |
3736 // Text only objects that are direct descendants should behave as if they | 3810 return -1; |
3737 // are part of this object when computing hypertext. | |
3738 (focus_object->GetParent() == this && focus_object->IsTextOnlyObject())) { | |
3739 int focus_offset; | |
3740 if (!root->GetIntAttribute(ui::AX_ATTR_FOCUS_OFFSET, &focus_offset)) | |
3741 return -1; | |
3742 | 3811 |
3743 return focus_offset; | 3812 return GetHypertextOffsetFromEndpoint(*focus_object, focus_offset); |
3744 } | |
3745 | |
3746 if (focus_object->IsDescendantOf(this)) | |
3747 return GetHypertextOffsetFromDescendant(*focus_object); | |
3748 | |
3749 return -1; | |
3750 } | 3813 } |
3751 | 3814 |
3752 void BrowserAccessibilityWin::GetSelectionOffsets( | 3815 void BrowserAccessibilityWin::GetSelectionOffsets( |
3753 int* selection_start, int* selection_end) const { | 3816 int* selection_start, int* selection_end) const { |
3754 DCHECK(selection_start && selection_end); | 3817 DCHECK(selection_start && selection_end); |
3755 | 3818 |
3756 if (IsEditableText() && !HasState(ui::AX_STATE_RICHLY_EDITABLE) && | 3819 if (IsEditableText() && !HasState(ui::AX_STATE_RICHLY_EDITABLE) && |
3757 GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START, selection_start) && | 3820 GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START, selection_start) && |
3758 GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, selection_end)) { | 3821 GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, selection_end)) { |
3759 return; | 3822 return; |
3760 } | 3823 } |
3761 | 3824 |
3762 *selection_start = GetSelectionAnchor(); | 3825 *selection_start = GetSelectionAnchor(); |
3763 *selection_end = GetSelectionFocus(); | 3826 *selection_end = GetSelectionFocus(); |
3764 if (*selection_start < 0 || *selection_end < 0) | 3827 if (*selection_start < 0 || *selection_end < 0) |
3765 return; | 3828 return; |
3766 | 3829 |
3830 // If the selection is collapsed, then return the caret position only if the | |
3831 // caret is active on this object. | |
3832 // The focus object indicates the caret position, but we will use the anchor | |
3833 // for consistency with Firefox. | |
3834 // TODO(nektar): Investigate why Firefox uses the anchor and not the focus. | |
3835 if (*selection_start == *selection_end) { | |
3836 BrowserAccessibility* root = manager()->GetRoot(); | |
3837 int32 anchor_id; | |
3838 if (!root || !root->GetIntAttribute( | |
3839 ui::AX_ATTR_ANCHOR_OBJECT_ID, &anchor_id)) { | |
3840 return; | |
3841 } | |
3842 | |
3843 BrowserAccessibilityWin* anchor_object = manager()->GetFromID( | |
3844 anchor_id)->ToBrowserAccessibilityWin(); | |
3845 if (!anchor_object) | |
3846 return; | |
3847 | |
3848 if (!anchor_object->IsDescendantOf(this) && | |
3849 (IsTextOnlyObject() && GetParent() != anchor_object)) { | |
3850 *selection_start = -1; | |
3851 *selection_end = -1; | |
3852 return; | |
3853 } | |
3854 } | |
3855 | |
3767 if (*selection_end < *selection_start) | 3856 if (*selection_end < *selection_start) |
3768 std::swap(*selection_start, *selection_end); | 3857 std::swap(*selection_start, *selection_end); |
3769 | 3858 |
3770 // IA2 Spec says that the end of the selection should be after the last | 3859 // The IA2 Spec says that if a selection is present and if the end of the |
3771 // embedded object character that is part of the selection, if there is one. | 3860 // selection falls on an embedded object character, it should be incremented |
3772 if (hyperlink_offset_to_index().find(*selection_end) != | 3861 // by one so that it points after the embedded object character. |
3773 hyperlink_offset_to_index().end()) { | 3862 LONG hyperlink_index; |
3863 auto current_object = const_cast<BrowserAccessibilityWin*>(this); | |
3864 HRESULT hr = current_object->get_hyperlinkIndex( | |
3865 *selection_end, &hyperlink_index); | |
3866 if (hr != S_OK) | |
3867 return; | |
3868 | |
3869 base::win::ScopedComPtr<IAccessibleHyperlink> hyperlink; | |
3870 hr = current_object->get_hyperlink( | |
3871 hyperlink_index, hyperlink.Receive()); | |
3872 DCHECK(SUCCEEDED(hr)); | |
3873 base::win::ScopedComPtr<IAccessibleText> ax_hyperlink; | |
3874 hr = hyperlink.QueryInterface(ax_hyperlink.Receive()); | |
3875 DCHECK(SUCCEEDED(hr)); | |
3876 LONG nSelections = -1; | |
3877 hr = ax_hyperlink->get_nSelections(&nSelections); | |
3878 DCHECK(SUCCEEDED(hr)); | |
3879 | |
3880 if (nSelections > 0) | |
3774 ++(*selection_end); | 3881 ++(*selection_end); |
3775 } | |
3776 } | 3882 } |
3777 | 3883 |
3778 base::string16 BrowserAccessibilityWin::GetNameRecursive() const { | 3884 base::string16 BrowserAccessibilityWin::GetNameRecursive() const { |
3779 if (!name().empty()) { | 3885 if (!name().empty()) { |
3780 return name(); | 3886 return name(); |
3781 } | 3887 } |
3782 | 3888 |
3783 base::string16 result; | 3889 base::string16 result; |
3784 for (uint32 i = 0; i < PlatformChildCount(); ++i) { | 3890 for (uint32 i = 0; i < PlatformChildCount(); ++i) { |
3785 result += PlatformGetChild(i)->ToBrowserAccessibilityWin()-> | 3891 result += PlatformGetChild(i)->ToBrowserAccessibilityWin()-> |
3786 GetNameRecursive(); | 3892 GetNameRecursive(); |
3787 } | 3893 } |
3788 return result; | 3894 return result; |
3789 } | 3895 } |
3790 | 3896 |
3791 base::string16 BrowserAccessibilityWin::GetValueText() { | 3897 base::string16 BrowserAccessibilityWin::GetValueText() { |
3792 float fval; | 3898 float fval; |
3793 base::string16 value = this->value(); | 3899 base::string16 value = this->value(); |
3794 | 3900 |
3795 if (value.empty() && | 3901 if (value.empty() && |
3796 GetFloatAttribute(ui::AX_ATTR_VALUE_FOR_RANGE, &fval)) { | 3902 GetFloatAttribute(ui::AX_ATTR_VALUE_FOR_RANGE, &fval)) { |
3797 value = base::UTF8ToUTF16(base::DoubleToString(fval)); | 3903 value = base::UTF8ToUTF16(base::DoubleToString(fval)); |
3798 } | 3904 } |
3799 return value; | 3905 return value; |
3800 } | 3906 } |
3801 | 3907 |
3802 base::string16 BrowserAccessibilityWin::TextForIAccessibleText() { | 3908 base::string16 BrowserAccessibilityWin::TextForIAccessibleText() const { |
3803 switch (GetRole()) { | 3909 switch (GetRole()) { |
3804 case ui::AX_ROLE_TEXT_FIELD: | 3910 case ui::AX_ROLE_TEXT_FIELD: |
3805 case ui::AX_ROLE_MENU_LIST_OPTION: | 3911 case ui::AX_ROLE_MENU_LIST_OPTION: |
3806 return value(); | 3912 return value(); |
3807 default: | 3913 default: |
3808 return hypertext(); | 3914 return hypertext(); |
3809 } | 3915 } |
3810 } | 3916 } |
3811 | 3917 |
3812 bool BrowserAccessibilityWin::IsSameHypertextCharacter(size_t old_char_index, | 3918 bool BrowserAccessibilityWin::IsSameHypertextCharacter(size_t old_char_index, |
(...skipping 686 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
4499 ia2_role = ia_role; | 4605 ia2_role = ia_role; |
4500 | 4606 |
4501 win_attributes_->ia_role = ia_role; | 4607 win_attributes_->ia_role = ia_role; |
4502 win_attributes_->ia_state = ia_state; | 4608 win_attributes_->ia_state = ia_state; |
4503 win_attributes_->role_name = role_name; | 4609 win_attributes_->role_name = role_name; |
4504 win_attributes_->ia2_role = ia2_role; | 4610 win_attributes_->ia2_role = ia2_role; |
4505 win_attributes_->ia2_state = ia2_state; | 4611 win_attributes_->ia2_state = ia2_state; |
4506 } | 4612 } |
4507 | 4613 |
4508 } // namespace content | 4614 } // namespace content |
OLD | NEW |