| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 <limits> | 7 #include <limits> |
| 8 | 8 |
| 9 #include "base/string_number_conversions.h" | 9 #include "base/string_number_conversions.h" |
| 10 #include "base/string_util.h" | 10 #include "base/string_util.h" |
| 11 #include "base/utf_string_conversions.h" | 11 #include "base/utf_string_conversions.h" |
| 12 #include "base/win/enum_variant.h" | 12 #include "base/win/enum_variant.h" |
| 13 #include "base/win/scoped_comptr.h" | 13 #include "base/win/scoped_comptr.h" |
| 14 #include "content/browser/accessibility/browser_accessibility_manager_win.h" | 14 #include "content/browser/accessibility/browser_accessibility_manager_win.h" |
| 15 #include "content/common/view_messages.h" | 15 #include "content/common/view_messages.h" |
| 16 #include "net/base/escape.h" | 16 #include "net/base/escape.h" |
| 17 #include "ui/base/accessibility/accessible_text_utils.h" | 17 #include "ui/base/accessibility/accessible_text_utils.h" |
| 18 | 18 |
| 19 using webkit_glue::WebAccessibility; | 19 using webkit_glue::WebAccessibility; |
| 20 | 20 |
| 21 // The GUID for the ISimpleDOM service is not defined in the IDL files. | 21 // The GUID for the ISimpleDOM service is not defined in the IDL files. |
| 22 // This is taken directly from the Mozilla sources | 22 // This is taken directly from the Mozilla sources |
| 23 // (accessible/src/msaa/nsAccessNodeWrap.cpp) and it's also documented at: | 23 // (accessible/src/msaa/nsAccessNodeWrap.cpp) and it's also documented at: |
| 24 // http://developer.mozilla.org/en/Accessibility/AT-APIs/ImplementationFeatures/
MSAA | 24 // http://developer.mozilla.org/en/Accessibility/AT-APIs/ImplementationFeatures/
MSAA |
| 25 | 25 |
| 26 const GUID GUID_ISimpleDOM = { | 26 const GUID GUID_ISimpleDOM = { |
| 27 0x0c539790, 0x12e4, 0x11cf, | 27 0x0c539790, 0x12e4, 0x11cf, |
| 28 0xb6, 0x61, 0x00, 0xaa, 0x00, 0x4c, 0xd6, 0xd8}; | 28 0xb6, 0x61, 0x00, 0xaa, 0x00, 0x4c, 0xd6, 0xd8}; |
| 29 | 29 |
| 30 const char16 BrowserAccessibilityWin::kEmbeddedCharacter[] = L"\xfffc"; | |
| 31 | |
| 32 // | 30 // |
| 33 // BrowserAccessibilityRelation | 31 // BrowserAccessibilityRelation |
| 34 // | 32 // |
| 35 // A simple implementation of IAccessibleRelation, used to represent | 33 // A simple implementation of IAccessibleRelation, used to represent |
| 36 // a relationship between two accessible nodes in the tree. | 34 // a relationship between two accessible nodes in the tree. |
| 37 // | 35 // |
| 38 | 36 |
| 39 class BrowserAccessibilityRelation | 37 class BrowserAccessibilityRelation |
| 40 : public CComObjectRootEx<CComMultiThreadModel>, | 38 : public CComObjectRootEx<CComMultiThreadModel>, |
| 41 public IAccessibleRelation { | 39 public IAccessibleRelation { |
| (...skipping 1705 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1747 // IAccessibleText methods. | 1745 // IAccessibleText methods. |
| 1748 // | 1746 // |
| 1749 | 1747 |
| 1750 STDMETHODIMP BrowserAccessibilityWin::get_nCharacters(LONG* n_characters) { | 1748 STDMETHODIMP BrowserAccessibilityWin::get_nCharacters(LONG* n_characters) { |
| 1751 if (!instance_active_) | 1749 if (!instance_active_) |
| 1752 return E_FAIL; | 1750 return E_FAIL; |
| 1753 | 1751 |
| 1754 if (!n_characters) | 1752 if (!n_characters) |
| 1755 return E_INVALIDARG; | 1753 return E_INVALIDARG; |
| 1756 | 1754 |
| 1757 *n_characters = TextForIAccessibleText().length(); | 1755 if (role_ == WebAccessibility::ROLE_TEXT_FIELD || |
| 1756 role_ == WebAccessibility::ROLE_TEXTAREA) { |
| 1757 *n_characters = value_.length(); |
| 1758 } else { |
| 1759 *n_characters = name_.length(); |
| 1760 } |
| 1761 |
| 1758 return S_OK; | 1762 return S_OK; |
| 1759 } | 1763 } |
| 1760 | 1764 |
| 1761 STDMETHODIMP BrowserAccessibilityWin::get_caretOffset(LONG* offset) { | 1765 STDMETHODIMP BrowserAccessibilityWin::get_caretOffset(LONG* offset) { |
| 1762 if (!instance_active_) | 1766 if (!instance_active_) |
| 1763 return E_FAIL; | 1767 return E_FAIL; |
| 1764 | 1768 |
| 1765 if (!offset) | 1769 if (!offset) |
| 1766 return E_INVALIDARG; | 1770 return E_INVALIDARG; |
| 1767 | 1771 |
| (...skipping 294 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2062 | 2066 |
| 2063 const string16& text_str = TextForIAccessibleText(); | 2067 const string16& text_str = TextForIAccessibleText(); |
| 2064 HandleSpecialTextOffset(text_str, &start_offset); | 2068 HandleSpecialTextOffset(text_str, &start_offset); |
| 2065 HandleSpecialTextOffset(text_str, &end_offset); | 2069 HandleSpecialTextOffset(text_str, &end_offset); |
| 2066 | 2070 |
| 2067 manager_->SetTextSelection(*this, start_offset, end_offset); | 2071 manager_->SetTextSelection(*this, start_offset, end_offset); |
| 2068 return S_OK; | 2072 return S_OK; |
| 2069 } | 2073 } |
| 2070 | 2074 |
| 2071 // | 2075 // |
| 2072 // IAccessibleHypertext methods. | |
| 2073 // | |
| 2074 | |
| 2075 STDMETHODIMP BrowserAccessibilityWin::get_nHyperlinks(long* hyperlink_count) { | |
| 2076 if (!instance_active_) | |
| 2077 return E_FAIL; | |
| 2078 | |
| 2079 if (!hyperlink_count) | |
| 2080 return E_INVALIDARG; | |
| 2081 | |
| 2082 *hyperlink_count = hyperlink_offset_to_index_.size(); | |
| 2083 return S_OK; | |
| 2084 } | |
| 2085 | |
| 2086 STDMETHODIMP BrowserAccessibilityWin::get_hyperlink( | |
| 2087 long index, | |
| 2088 IAccessibleHyperlink** hyperlink) { | |
| 2089 if (!instance_active_) | |
| 2090 return E_FAIL; | |
| 2091 | |
| 2092 if (!hyperlink || | |
| 2093 index < 0 || | |
| 2094 index >= static_cast<long>(hyperlinks_.size())) { | |
| 2095 return E_INVALIDARG; | |
| 2096 } | |
| 2097 | |
| 2098 BrowserAccessibilityWin* child = | |
| 2099 children_[hyperlinks_[index]]->toBrowserAccessibilityWin(); | |
| 2100 *hyperlink = static_cast<IAccessibleHyperlink*>(child->NewReference()); | |
| 2101 return S_OK; | |
| 2102 } | |
| 2103 | |
| 2104 STDMETHODIMP BrowserAccessibilityWin::get_hyperlinkIndex( | |
| 2105 long char_index, | |
| 2106 long* hyperlink_index) { | |
| 2107 if (!instance_active_) | |
| 2108 return E_FAIL; | |
| 2109 | |
| 2110 if (!hyperlink_index) | |
| 2111 return E_INVALIDARG; | |
| 2112 | |
| 2113 *hyperlink_index = -1; | |
| 2114 | |
| 2115 if (char_index < 0 || char_index >= static_cast<long>(hypertext_.size())) | |
| 2116 return E_INVALIDARG; | |
| 2117 | |
| 2118 std::map<int32, int32>::iterator it = | |
| 2119 hyperlink_offset_to_index_.find(char_index); | |
| 2120 if (it == hyperlink_offset_to_index_.end()) | |
| 2121 return E_FAIL; | |
| 2122 | |
| 2123 *hyperlink_index = it->second; | |
| 2124 return S_OK; | |
| 2125 } | |
| 2126 | |
| 2127 // | |
| 2128 // IAccessibleValue methods. | 2076 // IAccessibleValue methods. |
| 2129 // | 2077 // |
| 2130 | 2078 |
| 2131 STDMETHODIMP BrowserAccessibilityWin::get_currentValue(VARIANT* value) { | 2079 STDMETHODIMP BrowserAccessibilityWin::get_currentValue(VARIANT* value) { |
| 2132 if (!instance_active_) | 2080 if (!instance_active_) |
| 2133 return E_FAIL; | 2081 return E_FAIL; |
| 2134 | 2082 |
| 2135 if (!value) | 2083 if (!value) |
| 2136 return E_INVALIDARG; | 2084 return E_INVALIDARG; |
| 2137 | 2085 |
| (...skipping 375 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2513 // IServiceProvider methods. | 2461 // IServiceProvider methods. |
| 2514 // | 2462 // |
| 2515 | 2463 |
| 2516 STDMETHODIMP BrowserAccessibilityWin::QueryService( | 2464 STDMETHODIMP BrowserAccessibilityWin::QueryService( |
| 2517 REFGUID guidService, REFIID riid, void** object) { | 2465 REFGUID guidService, REFIID riid, void** object) { |
| 2518 if (!instance_active_) | 2466 if (!instance_active_) |
| 2519 return E_FAIL; | 2467 return E_FAIL; |
| 2520 | 2468 |
| 2521 if (guidService == IID_IAccessible || | 2469 if (guidService == IID_IAccessible || |
| 2522 guidService == IID_IAccessible2 || | 2470 guidService == IID_IAccessible2 || |
| 2523 guidService == IID_IAccessibleAction || | |
| 2524 guidService == IID_IAccessibleHyperlink || | |
| 2525 guidService == IID_IAccessibleHypertext || | |
| 2526 guidService == IID_IAccessibleImage || | 2471 guidService == IID_IAccessibleImage || |
| 2527 guidService == IID_IAccessibleTable || | 2472 guidService == IID_IAccessibleTable || |
| 2528 guidService == IID_IAccessibleTable2 || | 2473 guidService == IID_IAccessibleTable2 || |
| 2529 guidService == IID_IAccessibleTableCell || | 2474 guidService == IID_IAccessibleTableCell || |
| 2530 guidService == IID_IAccessibleText || | 2475 guidService == IID_IAccessibleText || |
| 2531 guidService == IID_IAccessibleValue || | 2476 guidService == IID_IAccessibleValue || |
| 2532 guidService == IID_ISimpleDOMDocument || | 2477 guidService == IID_ISimpleDOMDocument || |
| 2533 guidService == IID_ISimpleDOMNode || | 2478 guidService == IID_ISimpleDOMNode || |
| 2534 guidService == IID_ISimpleDOMText || | 2479 guidService == IID_ISimpleDOMText || |
| 2535 guidService == GUID_ISimpleDOM) { | 2480 guidService == GUID_ISimpleDOM) { |
| 2536 return QueryInterface(riid, object); | 2481 return QueryInterface(riid, object); |
| 2537 } | 2482 } |
| 2538 | 2483 |
| 2539 *object = NULL; | 2484 *object = NULL; |
| 2540 return E_FAIL; | 2485 return E_FAIL; |
| 2541 } | 2486 } |
| 2542 | 2487 |
| 2543 // | 2488 // |
| 2544 // CComObjectRootEx methods. | 2489 // CComObjectRootEx methods. |
| 2545 // | 2490 // |
| 2546 | 2491 |
| 2547 HRESULT WINAPI BrowserAccessibilityWin::InternalQueryInterface( | 2492 HRESULT WINAPI BrowserAccessibilityWin::InternalQueryInterface( |
| 2548 void* this_ptr, | 2493 void* this_ptr, |
| 2549 const _ATL_INTMAP_ENTRY* entries, | 2494 const _ATL_INTMAP_ENTRY* entries, |
| 2550 REFIID iid, | 2495 REFIID iid, |
| 2551 void** object) { | 2496 void** object) { |
| 2552 if (iid == IID_IAccessibleImage) { | 2497 if (iid == IID_IAccessibleText) { |
| 2498 if (ia_role_ != ROLE_SYSTEM_LINK && ia_role_ != ROLE_SYSTEM_TEXT) { |
| 2499 *object = NULL; |
| 2500 return E_NOINTERFACE; |
| 2501 } |
| 2502 } else if (iid == IID_IAccessibleImage) { |
| 2553 if (ia_role_ != ROLE_SYSTEM_GRAPHIC) { | 2503 if (ia_role_ != ROLE_SYSTEM_GRAPHIC) { |
| 2554 *object = NULL; | 2504 *object = NULL; |
| 2555 return E_NOINTERFACE; | 2505 return E_NOINTERFACE; |
| 2556 } | 2506 } |
| 2557 } else if (iid == IID_IAccessibleTable || iid == IID_IAccessibleTable2) { | 2507 } else if (iid == IID_IAccessibleTable || iid == IID_IAccessibleTable2) { |
| 2558 if (ia_role_ != ROLE_SYSTEM_TABLE) { | 2508 if (ia_role_ != ROLE_SYSTEM_TABLE) { |
| 2559 *object = NULL; | 2509 *object = NULL; |
| 2560 return E_NOINTERFACE; | 2510 return E_NOINTERFACE; |
| 2561 } | 2511 } |
| 2562 } else if (iid == IID_IAccessibleTableCell) { | 2512 } else if (iid == IID_IAccessibleTableCell) { |
| (...skipping 17 matching lines...) Expand all Loading... |
| 2580 | 2530 |
| 2581 return CComObjectRootBase::InternalQueryInterface( | 2531 return CComObjectRootBase::InternalQueryInterface( |
| 2582 this_ptr, entries, iid, object); | 2532 this_ptr, entries, iid, object); |
| 2583 } | 2533 } |
| 2584 | 2534 |
| 2585 // | 2535 // |
| 2586 // Private methods. | 2536 // Private methods. |
| 2587 // | 2537 // |
| 2588 | 2538 |
| 2589 // Initialize this object and mark it as active. | 2539 // Initialize this object and mark it as active. |
| 2590 void BrowserAccessibilityWin::PreInitialize() { | 2540 void BrowserAccessibilityWin::Initialize() { |
| 2591 BrowserAccessibility::PreInitialize(); | 2541 BrowserAccessibility::Initialize(); |
| 2592 | 2542 |
| 2593 InitRoleAndState(); | 2543 InitRoleAndState(); |
| 2594 | 2544 |
| 2595 // Expose headings levels with the "level" attribute. | 2545 // Expose headings levels with the "level" attribute. |
| 2596 if (role_ == WebAccessibility::ROLE_HEADING && role_name_.size() == 2 && | 2546 if (role_ == WebAccessibility::ROLE_HEADING && role_name_.size() == 2 && |
| 2597 IsAsciiDigit(role_name_[1])) { | 2547 IsAsciiDigit(role_name_[1])) { |
| 2598 ia2_attributes_.push_back(string16(L"level:") + role_name_.substr(1)); | 2548 ia2_attributes_.push_back(string16(L"level:") + role_name_.substr(1)); |
| 2599 } | 2549 } |
| 2600 | 2550 |
| 2601 // Expose the "display" and "tag" attributes. | 2551 // Expose the "display" and "tag" attributes. |
| (...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2700 HRESULT hr = CComObject<BrowserAccessibilityRelation>::CreateInstance( | 2650 HRESULT hr = CComObject<BrowserAccessibilityRelation>::CreateInstance( |
| 2701 &relation); | 2651 &relation); |
| 2702 DCHECK(SUCCEEDED(hr)); | 2652 DCHECK(SUCCEEDED(hr)); |
| 2703 relation->AddRef(); | 2653 relation->AddRef(); |
| 2704 relation->Initialize(this, IA2_RELATION_LABELLED_BY); | 2654 relation->Initialize(this, IA2_RELATION_LABELLED_BY); |
| 2705 relation->AddTarget(title_elem_id); | 2655 relation->AddTarget(title_elem_id); |
| 2706 relations_.push_back(relation); | 2656 relations_.push_back(relation); |
| 2707 } | 2657 } |
| 2708 } | 2658 } |
| 2709 | 2659 |
| 2710 void BrowserAccessibilityWin::PostInitialize() { | 2660 void BrowserAccessibilityWin::SendNodeUpdateEvents() { |
| 2711 BrowserAccessibility::PostInitialize(); | |
| 2712 | |
| 2713 // Construct the hypertext for this node. | |
| 2714 hyperlink_offset_to_index_.clear(); | |
| 2715 hyperlinks_.clear(); | |
| 2716 hypertext_.clear(); | |
| 2717 for (unsigned int i = 0; i < children().size(); ++i) { | |
| 2718 BrowserAccessibility* child = children()[i]; | |
| 2719 if (child->role() == WebAccessibility::ROLE_STATIC_TEXT) { | |
| 2720 hypertext_ += child->name(); | |
| 2721 } else { | |
| 2722 hyperlink_offset_to_index_[hypertext_.size()] = hyperlinks_.size(); | |
| 2723 hypertext_ += kEmbeddedCharacter; | |
| 2724 hyperlinks_.push_back(i); | |
| 2725 } | |
| 2726 } | |
| 2727 DCHECK_EQ(hyperlink_offset_to_index_.size(), hyperlinks_.size()); | |
| 2728 | |
| 2729 // Fire an event when an alert first appears. | 2661 // Fire an event when an alert first appears. |
| 2730 if (role_ == WebAccessibility::ROLE_ALERT && first_time_) | 2662 if (role_ == WebAccessibility::ROLE_ALERT && first_time_) |
| 2731 manager_->NotifyAccessibilityEvent(ViewHostMsg_AccEvent::ALERT, this); | 2663 manager_->NotifyAccessibilityEvent(ViewHostMsg_AccEvent::ALERT, this); |
| 2732 | 2664 |
| 2733 // Fire events if text has changed. | 2665 // Fire events if text has changed. |
| 2734 string16 text = TextForIAccessibleText(); | 2666 string16 text = TextForIAccessibleText(); |
| 2735 if (previous_text_ != text) { | 2667 if (previous_text_ != text) { |
| 2736 if (!previous_text_.empty() && !text.empty()) { | 2668 if (!previous_text_.empty() && !text.empty()) { |
| 2737 manager_->NotifyAccessibilityEvent( | 2669 manager_->NotifyAccessibilityEvent( |
| 2738 ViewHostMsg_AccEvent::OBJECT_SHOW, this); | 2670 ViewHostMsg_AccEvent::OBJECT_SHOW, this); |
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2849 base::IntToString16(value)); | 2781 base::IntToString16(value)); |
| 2850 } | 2782 } |
| 2851 | 2783 |
| 2852 string16 BrowserAccessibilityWin::Escape(const string16& str) { | 2784 string16 BrowserAccessibilityWin::Escape(const string16& str) { |
| 2853 return net::EscapeQueryParamValueUTF8(str, false); | 2785 return net::EscapeQueryParamValueUTF8(str, false); |
| 2854 } | 2786 } |
| 2855 | 2787 |
| 2856 const string16& BrowserAccessibilityWin::TextForIAccessibleText() { | 2788 const string16& BrowserAccessibilityWin::TextForIAccessibleText() { |
| 2857 if (IsEditableText()) { | 2789 if (IsEditableText()) { |
| 2858 return value_; | 2790 return value_; |
| 2859 } else if (role_ == WebAccessibility::ROLE_STATIC_TEXT) { | 2791 } else { |
| 2860 return name_; | 2792 return name_; |
| 2861 } else { | |
| 2862 return hypertext_; | |
| 2863 } | 2793 } |
| 2864 } | 2794 } |
| 2865 | 2795 |
| 2866 void BrowserAccessibilityWin::HandleSpecialTextOffset( | 2796 void BrowserAccessibilityWin::HandleSpecialTextOffset( |
| 2867 const string16& text, LONG* offset) { | 2797 const string16& text, LONG* offset) { |
| 2868 if (*offset == IA2_TEXT_OFFSET_LENGTH) { | 2798 if (*offset == IA2_TEXT_OFFSET_LENGTH) { |
| 2869 *offset = static_cast<LONG>(text.size()); | 2799 *offset = static_cast<LONG>(text.size()); |
| 2870 } else if (*offset == IA2_TEXT_OFFSET_CARET) { | 2800 } else if (*offset == IA2_TEXT_OFFSET_CARET) { |
| 2871 get_caretOffset(offset); | 2801 get_caretOffset(offset); |
| 2872 } | 2802 } |
| (...skipping 437 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3310 } | 3240 } |
| 3311 | 3241 |
| 3312 // The role should always be set. | 3242 // The role should always be set. |
| 3313 DCHECK(!role_name_.empty() || ia_role_); | 3243 DCHECK(!role_name_.empty() || ia_role_); |
| 3314 | 3244 |
| 3315 // If we didn't explicitly set the IAccessible2 role, make it the same | 3245 // If we didn't explicitly set the IAccessible2 role, make it the same |
| 3316 // as the MSAA role. | 3246 // as the MSAA role. |
| 3317 if (!ia2_role_) | 3247 if (!ia2_role_) |
| 3318 ia2_role_ = ia_role_; | 3248 ia2_role_ = ia_role_; |
| 3319 } | 3249 } |
| OLD | NEW |