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 "base/string_number_conversions.h" | 10 #include "base/string_number_conversions.h" |
(...skipping 2708 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2719 } | 2719 } |
2720 if (index >= 0) { | 2720 if (index >= 0) { |
2721 ia2_attributes_.push_back(string16(L"table-cell-index:") + | 2721 ia2_attributes_.push_back(string16(L"table-cell-index:") + |
2722 base::IntToString16(index)); | 2722 base::IntToString16(index)); |
2723 } | 2723 } |
2724 } else { | 2724 } else { |
2725 NOTREACHED(); | 2725 NOTREACHED(); |
2726 } | 2726 } |
2727 } | 2727 } |
2728 | 2728 |
| 2729 // The calculation of the accessible name of an element has been |
| 2730 // standardized in the HTML to Platform Accessibility APIs Implementation |
| 2731 // Guide (http://www.w3.org/TR/html-aapi/). In order to return the |
| 2732 // appropriate accessible name on Windows, we need to apply some logic |
| 2733 // to the fields we get from WebKit. |
| 2734 // |
| 2735 // TODO(dmazzoni): move most of this logic into WebKit. |
| 2736 // |
| 2737 // WebKit gives us: |
| 2738 // |
| 2739 // name: the default name, e.g. inner text |
| 2740 // title ui element: a reference to a <label> element on the same |
| 2741 // page that labels this node. |
| 2742 // description: accessible labels that override the default name: |
| 2743 // aria-label or aria-labelledby or aria-describedby |
| 2744 // help: the value of the "title" attribute |
| 2745 // |
| 2746 // On Windows, the logic we apply lets some fields take precedence and |
| 2747 // always returns the primary name in "name" and the secondary name, |
| 2748 // if any, in "description". |
| 2749 |
| 2750 string16 description, help, title_attr; |
| 2751 int title_elem_id = 0; |
| 2752 GetIntAttribute(AccessibilityNodeData::ATTR_TITLE_UI_ELEMENT, &title_elem_id); |
| 2753 GetStringAttribute(AccessibilityNodeData::ATTR_DESCRIPTION, &description); |
| 2754 GetStringAttribute(AccessibilityNodeData::ATTR_HELP, &help); |
| 2755 |
| 2756 // WebKit annoyingly puts the title in the description if there's no other |
| 2757 // description, which just confuses the rest of the logic. Put it back. |
| 2758 // Now "help" is always the value of the "title" attribute, if present. |
| 2759 if (GetHtmlAttribute("title", &title_attr) && |
| 2760 description == title_attr && |
| 2761 help.empty()) { |
| 2762 help = description; |
| 2763 description = L""; |
| 2764 string_attributes_[AccessibilityNodeData::ATTR_DESCRIPTION] = L""; |
| 2765 string_attributes_[AccessibilityNodeData::ATTR_HELP] = help; |
| 2766 } |
| 2767 |
| 2768 // Now implement the main logic: the descripion should become the name if |
| 2769 // it's nonempty, and the help should become the description if |
| 2770 // there's no description - or the name if there's no name or description. |
| 2771 if (!description.empty()) { |
| 2772 name_ = description; |
| 2773 description = L""; |
| 2774 string_attributes_[AccessibilityNodeData::ATTR_DESCRIPTION] = description; |
| 2775 } |
| 2776 if (!help.empty() && description.empty()) { |
| 2777 description = help; |
| 2778 string_attributes_[AccessibilityNodeData::ATTR_DESCRIPTION] = help; |
| 2779 string_attributes_[AccessibilityNodeData::ATTR_HELP] = L""; |
| 2780 } |
| 2781 if (!description.empty() && name_.empty() && !title_elem_id) { |
| 2782 name_ = description; |
| 2783 description = L""; |
| 2784 string_attributes_[AccessibilityNodeData::ATTR_DESCRIPTION] = L""; |
| 2785 } |
| 2786 |
| 2787 // If it's a text field, also consider the placeholder. |
| 2788 string16 placeholder; |
| 2789 if (role_ == AccessibilityNodeData::ROLE_TEXT_FIELD && |
| 2790 HasState(AccessibilityNodeData::STATE_FOCUSABLE) && |
| 2791 GetHtmlAttribute("placeholder", &placeholder)) { |
| 2792 if (name_.empty() && !title_elem_id) { |
| 2793 name_ = placeholder; |
| 2794 } else if (description.empty()) { |
| 2795 description = placeholder; |
| 2796 string_attributes_[AccessibilityNodeData::ATTR_DESCRIPTION] = description; |
| 2797 } |
| 2798 } |
| 2799 |
| 2800 // For certain roles (listbox option, static text, and list marker) |
| 2801 // WebKit stores the main accessible text in the "value" - swap it so |
| 2802 // that it's the "name". |
2729 if (name_.empty() && | 2803 if (name_.empty() && |
2730 (role_ == AccessibilityNodeData::ROLE_LISTBOX_OPTION || | 2804 (role_ == AccessibilityNodeData::ROLE_LISTBOX_OPTION || |
2731 role_ == AccessibilityNodeData::ROLE_STATIC_TEXT || | 2805 role_ == AccessibilityNodeData::ROLE_STATIC_TEXT || |
2732 role_ == AccessibilityNodeData::ROLE_LIST_MARKER)) { | 2806 role_ == AccessibilityNodeData::ROLE_LIST_MARKER)) { |
2733 name_.swap(value_); | 2807 name_.swap(value_); |
2734 } | 2808 } |
2735 | 2809 |
2736 // If this object doesn't have a name but it does have a description, | |
2737 // use the description as its name - because some screen readers only | |
2738 // announce the name. | |
2739 if (name_.empty()) | |
2740 GetStringAttribute(AccessibilityNodeData::ATTR_DESCRIPTION, &name_); | |
2741 | |
2742 // If this doesn't have a value and is linked then set its value to the url | 2810 // If this doesn't have a value and is linked then set its value to the url |
2743 // attribute. This allows screen readers to read an empty link's destination. | 2811 // attribute. This allows screen readers to read an empty link's destination. |
2744 string16 url; | 2812 string16 url; |
2745 if (value_.empty() && (ia_state_ & STATE_SYSTEM_LINKED)) | 2813 if (value_.empty() && (ia_state_ & STATE_SYSTEM_LINKED)) |
2746 GetStringAttribute(AccessibilityNodeData::ATTR_URL, &value_); | 2814 GetStringAttribute(AccessibilityNodeData::ATTR_URL, &value_); |
2747 | 2815 |
2748 // Clear any old relationships between this node and other nodes. | 2816 // Clear any old relationships between this node and other nodes. |
2749 for (size_t i = 0; i < relations_.size(); ++i) | 2817 for (size_t i = 0; i < relations_.size(); ++i) |
2750 relations_[i]->Release(); | 2818 relations_[i]->Release(); |
2751 relations_.clear(); | 2819 relations_.clear(); |
2752 | 2820 |
2753 // Handle title UI element. | 2821 // Handle title UI element. |
2754 int title_elem_id; | 2822 if (title_elem_id) { |
2755 if (GetIntAttribute(AccessibilityNodeData::ATTR_TITLE_UI_ELEMENT, | |
2756 &title_elem_id)) { | |
2757 // Add a labelled by relationship. | 2823 // Add a labelled by relationship. |
2758 CComObject<BrowserAccessibilityRelation>* relation; | 2824 CComObject<BrowserAccessibilityRelation>* relation; |
2759 HRESULT hr = CComObject<BrowserAccessibilityRelation>::CreateInstance( | 2825 HRESULT hr = CComObject<BrowserAccessibilityRelation>::CreateInstance( |
2760 &relation); | 2826 &relation); |
2761 DCHECK(SUCCEEDED(hr)); | 2827 DCHECK(SUCCEEDED(hr)); |
2762 relation->AddRef(); | 2828 relation->AddRef(); |
2763 relation->Initialize(this, IA2_RELATION_LABELLED_BY); | 2829 relation->Initialize(this, IA2_RELATION_LABELLED_BY); |
2764 relation->AddTarget(title_elem_id); | 2830 relation->AddTarget(title_elem_id); |
2765 relations_.push_back(relation); | 2831 relations_.push_back(relation); |
2766 } | 2832 } |
(...skipping 644 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3411 ia_state_ |= STATE_SYSTEM_READONLY; | 3477 ia_state_ |= STATE_SYSTEM_READONLY; |
3412 | 3478 |
3413 // The role should always be set. | 3479 // The role should always be set. |
3414 DCHECK(!role_name_.empty() || ia_role_); | 3480 DCHECK(!role_name_.empty() || ia_role_); |
3415 | 3481 |
3416 // If we didn't explicitly set the IAccessible2 role, make it the same | 3482 // If we didn't explicitly set the IAccessible2 role, make it the same |
3417 // as the MSAA role. | 3483 // as the MSAA role. |
3418 if (!ia2_role_) | 3484 if (!ia2_role_) |
3419 ia2_role_ = ia_role_; | 3485 ia2_role_ = ia_role_; |
3420 } | 3486 } |
OLD | NEW |