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
3 // found in the LICENSE file.
5 #include "ui/views/accessibility/native_view_accessibility_win.h"
8 #include <UIAutomationClient.h>
13 #include "base/memory/singleton.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/win/scoped_comptr.h"
16 #include "base/win/windows_version.h"
17 #include "third_party/iaccessible2/ia2_api_all.h"
18 #include "ui/accessibility/ax_enums.h"
19 #include "ui/accessibility/ax_text_utils.h"
20 #include "ui/accessibility/ax_view_state.h"
21 #include "ui/base/win/accessibility_ids_win.h"
22 #include "ui/base/win/accessibility_misc_utils.h"
23 #include "ui/base/win/atl_module.h"
24 #include "ui/views/controls/button/custom_button.h"
25 #include "ui/views/focus/focus_manager.h"
26 #include "ui/views/focus/view_storage.h"
27 #include "ui/views/widget/widget.h"
28 #include "ui/views/win/hwnd_util.h"
33 class AccessibleWebViewRegistry
{
35 static AccessibleWebViewRegistry
* GetInstance();
37 void RegisterWebView(View
* web_view
);
39 void UnregisterWebView(View
* web_view
);
41 // Given the view that received the request for the accessible
42 // id in |top_view|, and the child id requested, return the native
43 // accessible object with that child id from one of the WebViews in
44 // |top_view|'s view hierarchy, if any.
45 IAccessible
* GetAccessibleFromWebView(View
* top_view
, long child_id
);
47 // The system uses IAccessible APIs for many purposes, but only
48 // assistive technology like screen readers uses IAccessible2.
49 // Call this method to note that the IAccessible2 interface was queried and
50 // that WebViews should be proactively notified that this interface will be
51 // used. If this is enabled for the first time, this will explicitly call
52 // QueryService with an argument of IAccessible2 on all WebViews, otherwise
53 // it will just do it from now on.
54 void EnableIAccessible2Support();
57 friend struct DefaultSingletonTraits
<AccessibleWebViewRegistry
>;
58 AccessibleWebViewRegistry();
59 ~AccessibleWebViewRegistry() {}
61 IAccessible
* AccessibleObjectFromChildId(View
* web_view
, long child_id
);
63 void QueryIAccessible2Interface(View
* web_view
);
65 // Set of all web views. We check whether each one is contained in a
66 // top view dynamically rather than keeping track of a map.
67 std::set
<View
*> web_views_
;
69 // The most recent top view used in a call to GetAccessibleFromWebView.
72 // The most recent web view where an accessible object was found,
73 // corresponding to |last_top_view_|.
76 // If IAccessible2 support is enabled, we query the IAccessible2 interface
77 // of WebViews proactively when they're registered, so that they are
78 // aware that they need to support this interface.
79 bool iaccessible2_support_enabled_
;
81 DISALLOW_COPY_AND_ASSIGN(AccessibleWebViewRegistry
);
84 AccessibleWebViewRegistry::AccessibleWebViewRegistry()
85 : last_top_view_(NULL
),
87 iaccessible2_support_enabled_(false) {
90 AccessibleWebViewRegistry
* AccessibleWebViewRegistry::GetInstance() {
91 return Singleton
<AccessibleWebViewRegistry
>::get();
94 void AccessibleWebViewRegistry::RegisterWebView(View
* web_view
) {
95 DCHECK(web_views_
.find(web_view
) == web_views_
.end());
96 web_views_
.insert(web_view
);
98 if (iaccessible2_support_enabled_
)
99 QueryIAccessible2Interface(web_view
);
102 void AccessibleWebViewRegistry::UnregisterWebView(View
* web_view
) {
103 DCHECK(web_views_
.find(web_view
) != web_views_
.end());
104 web_views_
.erase(web_view
);
105 if (last_web_view_
== web_view
) {
106 last_top_view_
= NULL
;
107 last_web_view_
= NULL
;
111 IAccessible
* AccessibleWebViewRegistry::GetAccessibleFromWebView(
112 View
* top_view
, long child_id
) {
113 // This function gets called frequently, so try to avoid searching all
114 // of the web views if the notification is on the same web view that
115 // sent the last one.
116 if (last_top_view_
== top_view
) {
117 IAccessible
* accessible
=
118 AccessibleObjectFromChildId(last_web_view_
, child_id
);
123 // Search all web views. For each one, first ensure it's a descendant
124 // of this view where the event was posted - and if so, see if it owns
125 // an accessible object with that child id. If so, save the view to speed
126 // up the next notification.
127 for (std::set
<View
*>::iterator iter
= web_views_
.begin();
128 iter
!= web_views_
.end(); ++iter
) {
129 View
* web_view
= *iter
;
130 if (top_view
== web_view
|| !top_view
->Contains(web_view
))
132 IAccessible
* accessible
= AccessibleObjectFromChildId(web_view
, child_id
);
134 last_top_view_
= top_view
;
135 last_web_view_
= web_view
;
143 void AccessibleWebViewRegistry::EnableIAccessible2Support() {
144 if (iaccessible2_support_enabled_
)
146 iaccessible2_support_enabled_
= true;
147 for (std::set
<View
*>::iterator iter
= web_views_
.begin();
148 iter
!= web_views_
.end(); ++iter
) {
149 QueryIAccessible2Interface(*iter
);
153 IAccessible
* AccessibleWebViewRegistry::AccessibleObjectFromChildId(
156 IAccessible
* web_view_accessible
= web_view
->GetNativeViewAccessible();
157 if (web_view_accessible
== NULL
)
161 var_child
.vt
= VT_I4
;
162 var_child
.lVal
= child_id
;
163 IAccessible
* result
= NULL
;
164 if (S_OK
== web_view_accessible
->get_accChild(
165 var_child
, reinterpret_cast<IDispatch
**>(&result
))) {
172 void AccessibleWebViewRegistry::QueryIAccessible2Interface(View
* web_view
) {
173 IAccessible
* web_view_accessible
= web_view
->GetNativeViewAccessible();
174 if (!web_view_accessible
)
177 base::win::ScopedComPtr
<IServiceProvider
> service_provider
;
178 if (S_OK
!= web_view_accessible
->QueryInterface(service_provider
.Receive()))
180 base::win::ScopedComPtr
<IAccessible2
> iaccessible2
;
181 service_provider
->QueryService(
182 IID_IAccessible
, IID_IAccessible2
,
183 reinterpret_cast<void**>(iaccessible2
.Receive()));
186 } // anonymous namespace
189 long NativeViewAccessibilityWin::next_unique_id_
= 1;
190 int NativeViewAccessibilityWin::view_storage_ids_
[kMaxViewStorageIds
] = {0};
191 int NativeViewAccessibilityWin::next_view_storage_id_index_
= 0;
192 std::vector
<int> NativeViewAccessibilityWin::alert_target_view_storage_ids_
;
195 NativeViewAccessibility
* NativeViewAccessibility::Create(View
* view
) {
196 // Make sure ATL is initialized in this module.
197 ui::win::CreateATLModuleIfNeeded();
199 CComObject
<NativeViewAccessibilityWin
>* instance
= NULL
;
200 HRESULT hr
= CComObject
<NativeViewAccessibilityWin
>::CreateInstance(
202 DCHECK(SUCCEEDED(hr
));
203 instance
->set_view(view
);
208 NativeViewAccessibilityWin::NativeViewAccessibilityWin()
210 unique_id_(next_unique_id_
++) {
213 NativeViewAccessibilityWin::~NativeViewAccessibilityWin() {
217 void NativeViewAccessibilityWin::NotifyAccessibilityEvent(
218 ui::AXEvent event_type
) {
222 ViewStorage
* view_storage
= ViewStorage::GetInstance();
223 HWND hwnd
= HWNDForView(view_
);
224 int view_storage_id
= view_storage_ids_
[next_view_storage_id_index_
];
225 if (view_storage_id
== 0) {
226 view_storage_id
= view_storage
->CreateStorageID();
227 view_storage_ids_
[next_view_storage_id_index_
] = view_storage_id
;
229 view_storage
->RemoveView(view_storage_id
);
231 view_storage
->StoreView(view_storage_id
, view_
);
233 // Positive child ids are used for enumerating direct children,
234 // negative child ids can be used as unique ids to refer to a specific
235 // descendants. Make index into view_storage_ids_ into a negative child id.
237 base::win::kFirstViewsAccessibilityId
- next_view_storage_id_index_
;
238 ::NotifyWinEvent(MSAAEvent(event_type
), hwnd
, OBJID_CLIENT
, child_id
);
239 next_view_storage_id_index_
=
240 (next_view_storage_id_index_
+ 1) % kMaxViewStorageIds
;
242 // Keep track of views that are a target of an alert event.
243 if (event_type
== ui::AX_EVENT_ALERT
)
247 gfx::NativeViewAccessible
NativeViewAccessibilityWin::GetNativeObject() {
251 void NativeViewAccessibilityWin::Destroy() {
256 STDMETHODIMP
NativeViewAccessibilityWin::accHitTest(
257 LONG x_left
, LONG y_top
, VARIANT
* child
) {
261 if (!view_
|| !view_
->GetWidget())
264 // If this is a root view, our widget might have child widgets.
265 // Search child widgets first, since they're on top in the z-order.
266 if (view_
->GetWidget()->GetRootView() == view_
) {
267 std::vector
<Widget
*> child_widgets
;
268 PopulateChildWidgetVector(&child_widgets
);
269 for (size_t i
= 0; i
< child_widgets
.size(); ++i
) {
270 Widget
* child_widget
= child_widgets
[i
];
271 IAccessible
* child_accessible
=
272 child_widget
->GetRootView()->GetNativeViewAccessible();
273 HRESULT result
= child_accessible
->accHitTest(x_left
, y_top
, child
);
279 gfx::Point
point(x_left
, y_top
);
280 View::ConvertPointFromScreen(view_
, &point
);
282 // If the point is not inside this view, return false.
283 if (!view_
->HitTestPoint(point
)) {
284 child
->vt
= VT_EMPTY
;
288 // Check if the point is within any of the immediate children of this
290 View
* hit_child_view
= NULL
;
291 for (int i
= view_
->child_count() - 1; i
>= 0; --i
) {
292 View
* child_view
= view_
->child_at(i
);
293 if (!child_view
->visible())
296 gfx::Point
point_in_child_coords(point
);
297 view_
->ConvertPointToTarget(view_
, child_view
, &point_in_child_coords
);
298 if (child_view
->HitTestPoint(point_in_child_coords
)) {
299 hit_child_view
= child_view
;
304 // If the point was within one of this view's immediate children,
305 // call accHitTest recursively on that child's native view accessible -
306 // which may be a recursive call to this function or it may be overridden,
307 // for example in the case of a WebView.
308 if (hit_child_view
) {
309 HRESULT result
= hit_child_view
->GetNativeViewAccessible()->accHitTest(
310 x_left
, y_top
, child
);
312 // If the recursive call returned CHILDID_SELF, we have to convert that
313 // into a VT_DISPATCH for the return value to this call.
314 if (S_OK
== result
&& child
->vt
== VT_I4
&& child
->lVal
== CHILDID_SELF
) {
315 child
->vt
= VT_DISPATCH
;
316 child
->pdispVal
= hit_child_view
->GetNativeViewAccessible();
317 // Always increment ref when returning a reference to a COM object.
318 child
->pdispVal
->AddRef();
323 // This object is the best match, so return CHILDID_SELF. It's tempting to
324 // simplify the logic and use VT_DISPATCH everywhere, but the Windows
325 // call AccessibleObjectFromPoint will keep calling accHitTest until some
326 // object returns CHILDID_SELF.
328 child
->lVal
= CHILDID_SELF
;
332 HRESULT
NativeViewAccessibilityWin::accDoDefaultAction(VARIANT var_id
) {
333 if (!IsValidId(var_id
))
336 // The object does not support the method. This value is returned for
337 // controls that do not perform actions, such as edit fields.
338 return DISP_E_MEMBERNOTFOUND
;
341 STDMETHODIMP
NativeViewAccessibilityWin::accLocation(
342 LONG
* x_left
, LONG
* y_top
, LONG
* width
, LONG
* height
, VARIANT var_id
) {
343 if (!IsValidId(var_id
) || !x_left
|| !y_top
|| !width
|| !height
)
349 if (!view_
->bounds().IsEmpty()) {
350 *width
= view_
->width();
351 *height
= view_
->height();
352 gfx::Point
topleft(view_
->bounds().origin());
353 View::ConvertPointToScreen(
354 view_
->parent() ? view_
->parent() : view_
, &topleft
);
355 *x_left
= topleft
.x();
356 *y_top
= topleft
.y();
363 STDMETHODIMP
NativeViewAccessibilityWin::accNavigate(
364 LONG nav_dir
, VARIANT start
, VARIANT
* end
) {
365 if (start
.vt
!= VT_I4
|| !end
)
372 case NAVDIR_FIRSTCHILD
:
373 case NAVDIR_LASTCHILD
: {
374 if (start
.lVal
!= CHILDID_SELF
) {
375 // Start of navigation must be on the View itself.
377 } else if (!view_
->has_children()) {
378 // No children found.
382 // Set child_id based on first or last child.
384 if (nav_dir
== NAVDIR_LASTCHILD
)
385 child_id
= view_
->child_count() - 1;
387 View
* child
= view_
->child_at(child_id
);
388 end
->vt
= VT_DISPATCH
;
389 end
->pdispVal
= child
->GetNativeViewAccessible();
390 end
->pdispVal
->AddRef();
395 case NAVDIR_PREVIOUS
:
399 // Retrieve parent to access view index and perform bounds checking.
400 View
* parent
= view_
->parent();
405 if (start
.lVal
== CHILDID_SELF
) {
406 int view_index
= parent
->GetIndexOf(view_
);
407 // Check navigation bounds, adjusting for View child indexing (MSAA
408 // child indexing starts with 1, whereas View indexing starts with 0).
409 if (!IsValidNav(nav_dir
, view_index
, -1,
410 parent
->child_count() - 1)) {
411 // Navigation attempted to go out-of-bounds.
415 if (IsNavDirNext(nav_dir
)) {
422 View
* child
= parent
->child_at(view_index
);
423 end
->pdispVal
= child
->GetNativeViewAccessible();
424 end
->vt
= VT_DISPATCH
;
425 end
->pdispVal
->AddRef();
428 // Check navigation bounds, adjusting for MSAA child indexing (MSAA
429 // child indexing starts with 1, whereas View indexing starts with 0).
430 if (!IsValidNav(nav_dir
, start
.lVal
, 0, parent
->child_count() + 1)) {
431 // Navigation attempted to go out-of-bounds.
435 if (IsNavDirNext(nav_dir
)) {
442 HRESULT result
= this->get_accChild(start
, &end
->pdispVal
);
443 if (result
== S_FALSE
) {
446 end
->lVal
= start
.lVal
;
447 } else if (result
== E_INVALIDARG
) {
450 // Child is not a leaf.
451 end
->vt
= VT_DISPATCH
;
459 // Navigation performed correctly. Global return for this function, if no
460 // error triggered an escape earlier.
464 STDMETHODIMP
NativeViewAccessibilityWin::get_accChild(VARIANT var_child
,
465 IDispatch
** disp_child
) {
466 if (var_child
.vt
!= VT_I4
|| !disp_child
)
469 if (!view_
|| !view_
->GetWidget())
472 LONG child_id
= V_I4(&var_child
);
474 if (child_id
== CHILDID_SELF
) {
475 // Remain with the same dispatch.
479 // If this is a root view, our widget might have child widgets. Include
480 std::vector
<Widget
*> child_widgets
;
481 if (view_
->GetWidget()->GetRootView() == view_
)
482 PopulateChildWidgetVector(&child_widgets
);
483 int child_widget_count
= static_cast<int>(child_widgets
.size());
485 View
* child_view
= NULL
;
487 // Positive child ids are a 1-based child index, used by clients
488 // that want to enumerate all immediate children.
489 int child_id_as_index
= child_id
- 1;
490 if (child_id_as_index
< view_
->child_count()) {
491 child_view
= view_
->child_at(child_id_as_index
);
492 } else if (child_id_as_index
< view_
->child_count() + child_widget_count
) {
493 Widget
* child_widget
=
494 child_widgets
[child_id_as_index
- view_
->child_count()];
495 child_view
= child_widget
->GetRootView();
498 // Negative child ids can be used to map to any descendant.
499 // Check child widget first.
500 for (int i
= 0; i
< child_widget_count
; i
++) {
501 Widget
* child_widget
= child_widgets
[i
];
502 IAccessible
* child_accessible
=
503 child_widget
->GetRootView()->GetNativeViewAccessible();
504 HRESULT result
= child_accessible
->get_accChild(var_child
, disp_child
);
509 // We map child ids to a view storage id that can refer to a
510 // specific view (if that view still exists).
511 int view_storage_id_index
=
512 base::win::kFirstViewsAccessibilityId
- child_id
;
513 if (view_storage_id_index
>= 0 &&
514 view_storage_id_index
< kMaxViewStorageIds
) {
515 int view_storage_id
= view_storage_ids_
[view_storage_id_index
];
516 ViewStorage
* view_storage
= ViewStorage::GetInstance();
517 child_view
= view_storage
->RetrieveView(view_storage_id
);
519 *disp_child
= AccessibleWebViewRegistry::GetInstance()->
520 GetAccessibleFromWebView(view_
, child_id
);
532 *disp_child
= child_view
->GetNativeViewAccessible();
533 (*disp_child
)->AddRef();
537 STDMETHODIMP
NativeViewAccessibilityWin::get_accChildCount(LONG
* child_count
) {
541 if (!view_
|| !view_
->GetWidget())
544 *child_count
= view_
->child_count();
546 // If this is a root view, our widget might have child widgets. Include
548 if (view_
->GetWidget()->GetRootView() == view_
) {
549 std::vector
<Widget
*> child_widgets
;
550 PopulateChildWidgetVector(&child_widgets
);
551 *child_count
+= child_widgets
.size();
557 STDMETHODIMP
NativeViewAccessibilityWin::get_accDefaultAction(
558 VARIANT var_id
, BSTR
* def_action
) {
559 if (!IsValidId(var_id
) || !def_action
)
565 ui::AXViewState state
;
566 view_
->GetAccessibleState(&state
);
567 base::string16 temp_action
= state
.default_action
;
569 if (!temp_action
.empty()) {
570 *def_action
= SysAllocString(temp_action
.c_str());
578 STDMETHODIMP
NativeViewAccessibilityWin::get_accDescription(
579 VARIANT var_id
, BSTR
* desc
) {
580 if (!IsValidId(var_id
) || !desc
)
586 base::string16 temp_desc
;
588 view_
->GetTooltipText(gfx::Point(), &temp_desc
);
589 if (!temp_desc
.empty()) {
590 *desc
= SysAllocString(temp_desc
.c_str());
598 STDMETHODIMP
NativeViewAccessibilityWin::get_accFocus(VARIANT
* focus_child
) {
605 FocusManager
* focus_manager
= view_
->GetFocusManager();
606 View
* focus
= focus_manager
? focus_manager
->GetFocusedView() : NULL
;
607 if (focus
== view_
) {
608 // This view has focus.
609 focus_child
->vt
= VT_I4
;
610 focus_child
->lVal
= CHILDID_SELF
;
611 } else if (focus
&& view_
->Contains(focus
)) {
612 // Return the child object that has the keyboard focus.
613 focus_child
->vt
= VT_DISPATCH
;
614 focus_child
->pdispVal
= focus
->GetNativeViewAccessible();
615 focus_child
->pdispVal
->AddRef();
618 // Neither this object nor any of its children has the keyboard focus.
619 focus_child
->vt
= VT_EMPTY
;
624 STDMETHODIMP
NativeViewAccessibilityWin::get_accKeyboardShortcut(
625 VARIANT var_id
, BSTR
* acc_key
) {
626 if (!IsValidId(var_id
) || !acc_key
)
632 ui::AXViewState state
;
633 view_
->GetAccessibleState(&state
);
634 base::string16 temp_key
= state
.keyboard_shortcut
;
636 if (!temp_key
.empty()) {
637 *acc_key
= SysAllocString(temp_key
.c_str());
645 STDMETHODIMP
NativeViewAccessibilityWin::get_accName(
646 VARIANT var_id
, BSTR
* name
) {
647 if (!IsValidId(var_id
) || !name
)
653 // Retrieve the current view's name.
654 ui::AXViewState state
;
655 view_
->GetAccessibleState(&state
);
656 base::string16 temp_name
= state
.name
;
657 if (!temp_name
.empty()) {
658 // Return name retrieved.
659 *name
= SysAllocString(temp_name
.c_str());
661 // If view has no name, return S_FALSE.
668 STDMETHODIMP
NativeViewAccessibilityWin::get_accParent(
669 IDispatch
** disp_parent
) {
677 View
* parent_view
= view_
->parent();
680 HWND hwnd
= HWNDForView(view_
);
684 return ::AccessibleObjectFromWindow(
685 hwnd
, OBJID_WINDOW
, IID_IAccessible
,
686 reinterpret_cast<void**>(disp_parent
));
689 *disp_parent
= parent_view
->GetNativeViewAccessible();
690 (*disp_parent
)->AddRef();
694 STDMETHODIMP
NativeViewAccessibilityWin::get_accRole(
695 VARIANT var_id
, VARIANT
* role
) {
696 if (!IsValidId(var_id
) || !role
)
702 ui::AXViewState state
;
703 view_
->GetAccessibleState(&state
);
705 role
->lVal
= MSAARole(state
.role
);
709 STDMETHODIMP
NativeViewAccessibilityWin::get_accState(
710 VARIANT var_id
, VARIANT
* state
) {
711 // This returns MSAA states. See also the IAccessible2 interface
714 if (!IsValidId(var_id
) || !state
)
722 // Retrieve all currently applicable states of the parent.
723 SetState(state
, view_
);
725 // Make sure that state is not empty, and has the proper type.
726 if (state
->vt
== VT_EMPTY
)
732 STDMETHODIMP
NativeViewAccessibilityWin::get_accValue(VARIANT var_id
,
734 if (!IsValidId(var_id
) || !value
)
740 // Retrieve the current view's value.
741 ui::AXViewState state
;
742 view_
->GetAccessibleState(&state
);
743 base::string16 temp_value
= state
.value
;
745 if (!temp_value
.empty()) {
746 // Return value retrieved.
747 *value
= SysAllocString(temp_value
.c_str());
749 // If view has no value, fall back into the default implementation.
757 STDMETHODIMP
NativeViewAccessibilityWin::put_accValue(VARIANT var_id
,
759 if (!IsValidId(var_id
) || !new_value
)
765 // Return an error if the view can't set the value.
766 ui::AXViewState state
;
767 view_
->GetAccessibleState(&state
);
768 if (state
.set_value_callback
.is_null())
771 state
.set_value_callback
.Run(new_value
);
775 // IAccessible functions not supported.
777 STDMETHODIMP
NativeViewAccessibilityWin::get_accSelection(VARIANT
* selected
) {
779 selected
->vt
= VT_EMPTY
;
783 STDMETHODIMP
NativeViewAccessibilityWin::accSelect(
784 LONG flagsSelect
, VARIANT var_id
) {
788 STDMETHODIMP
NativeViewAccessibilityWin::get_accHelp(
789 VARIANT var_id
, BSTR
* help
) {
790 base::string16 temp
= base::UTF8ToUTF16(view_
->GetClassName());
791 *help
= SysAllocString(temp
.c_str());
795 STDMETHODIMP
NativeViewAccessibilityWin::get_accHelpTopic(
796 BSTR
* help_file
, VARIANT var_id
, LONG
* topic_id
) {
801 *topic_id
= static_cast<LONG
>(-1);
806 STDMETHODIMP
NativeViewAccessibilityWin::put_accName(
807 VARIANT var_id
, BSTR put_name
) {
816 STDMETHODIMP
NativeViewAccessibilityWin::role(LONG
* role
) {
823 ui::AXViewState state
;
824 view_
->GetAccessibleState(&state
);
825 *role
= MSAARole(state
.role
);
829 STDMETHODIMP
NativeViewAccessibilityWin::get_states(AccessibleStates
* states
) {
830 // This returns IAccessible2 states, which supplement MSAA states.
831 // See also the MSAA interface get_accState.
839 ui::AXViewState state
;
840 view_
->GetAccessibleState(&state
);
842 // There are only a couple of states we need to support
843 // in IAccessible2. If any more are added, we may want to
844 // add a helper function like MSAAState.
845 *states
= IA2_STATE_OPAQUE
;
846 if (state
.HasStateFlag(ui::AX_STATE_EDITABLE
))
847 *states
|= IA2_STATE_EDITABLE
;
852 STDMETHODIMP
NativeViewAccessibilityWin::get_uniqueID(LONG
* unique_id
) {
859 *unique_id
= unique_id_
;
863 STDMETHODIMP
NativeViewAccessibilityWin::get_windowHandle(HWND
* window_handle
) {
870 *window_handle
= HWNDForView(view_
);
871 return *window_handle
? S_OK
: S_FALSE
;
874 STDMETHODIMP
NativeViewAccessibilityWin::get_relationTargetsOfType(
882 if (!targets
|| !n_targets
)
888 // Only respond to requests for relations of type "alerts" on the
890 base::string16
type(type_bstr
);
891 if (type
!= L
"alerts" || view_
->parent())
894 // Collect all of the alert views that are still valid.
895 std::vector
<View
*> alert_views
;
896 ViewStorage
* view_storage
= ViewStorage::GetInstance();
897 for (size_t i
= 0; i
< alert_target_view_storage_ids_
.size(); ++i
) {
898 int view_storage_id
= alert_target_view_storage_ids_
[i
];
899 View
* view
= view_storage
->RetrieveView(view_storage_id
);
900 if (!view
|| !view_
->Contains(view
))
902 alert_views
.push_back(view
);
905 long count
= alert_views
.size();
909 // Don't return more targets than max_targets - but note that the caller
910 // is allowed to specify max_targets=0 to mean no limit.
911 if (max_targets
> 0 && count
> max_targets
)
914 // Return the number of targets.
917 // Allocate COM memory for the result array and populate it.
918 *targets
= static_cast<IUnknown
**>(
919 CoTaskMemAlloc(count
* sizeof(IUnknown
*)));
920 for (long i
= 0; i
< count
; ++i
) {
921 (*targets
)[i
] = alert_views
[i
]->GetNativeViewAccessible();
922 (*targets
)[i
]->AddRef();
931 STDMETHODIMP
NativeViewAccessibilityWin::get_nCharacters(LONG
* n_characters
) {
938 base::string16 text
= TextForIAccessibleText();
939 *n_characters
= static_cast<LONG
>(text
.size());
943 STDMETHODIMP
NativeViewAccessibilityWin::get_caretOffset(LONG
* offset
) {
950 ui::AXViewState state
;
951 view_
->GetAccessibleState(&state
);
952 *offset
= static_cast<LONG
>(state
.selection_end
);
956 STDMETHODIMP
NativeViewAccessibilityWin::get_nSelections(LONG
* n_selections
) {
963 ui::AXViewState state
;
964 view_
->GetAccessibleState(&state
);
965 if (state
.selection_start
!= state
.selection_end
)
972 STDMETHODIMP
NativeViewAccessibilityWin::get_selection(LONG selection_index
,
978 if (!start_offset
|| !end_offset
|| selection_index
!= 0)
981 ui::AXViewState state
;
982 view_
->GetAccessibleState(&state
);
983 *start_offset
= static_cast<LONG
>(state
.selection_start
);
984 *end_offset
= static_cast<LONG
>(state
.selection_end
);
988 STDMETHODIMP
NativeViewAccessibilityWin::get_text(LONG start_offset
,
994 ui::AXViewState state
;
995 view_
->GetAccessibleState(&state
);
996 base::string16 text_str
= TextForIAccessibleText();
997 LONG len
= static_cast<LONG
>(text_str
.size());
999 if (start_offset
== IA2_TEXT_OFFSET_LENGTH
) {
1001 } else if (start_offset
== IA2_TEXT_OFFSET_CARET
) {
1002 start_offset
= static_cast<LONG
>(state
.selection_end
);
1004 if (end_offset
== IA2_TEXT_OFFSET_LENGTH
) {
1005 end_offset
= static_cast<LONG
>(text_str
.size());
1006 } else if (end_offset
== IA2_TEXT_OFFSET_CARET
) {
1007 end_offset
= static_cast<LONG
>(state
.selection_end
);
1010 // The spec allows the arguments to be reversed.
1011 if (start_offset
> end_offset
) {
1012 LONG tmp
= start_offset
;
1013 start_offset
= end_offset
;
1017 // The spec does not allow the start or end offsets to be out or range;
1018 // we must return an error if so.
1019 if (start_offset
< 0)
1020 return E_INVALIDARG
;
1021 if (end_offset
> len
)
1022 return E_INVALIDARG
;
1024 base::string16 substr
=
1025 text_str
.substr(start_offset
, end_offset
- start_offset
);
1029 *text
= SysAllocString(substr
.c_str());
1034 STDMETHODIMP
NativeViewAccessibilityWin::get_textAtOffset(
1036 enum IA2TextBoundaryType boundary_type
,
1037 LONG
* start_offset
, LONG
* end_offset
,
1039 if (!start_offset
|| !end_offset
|| !text
)
1040 return E_INVALIDARG
;
1042 // The IAccessible2 spec says we don't have to implement the "sentence"
1043 // boundary type, we can just let the screenreader handle it.
1044 if (boundary_type
== IA2_TEXT_BOUNDARY_SENTENCE
) {
1051 const base::string16
& text_str
= TextForIAccessibleText();
1053 *start_offset
= FindBoundary(
1054 text_str
, boundary_type
, offset
, ui::BACKWARDS_DIRECTION
);
1055 *end_offset
= FindBoundary(
1056 text_str
, boundary_type
, offset
, ui::FORWARDS_DIRECTION
);
1057 return get_text(*start_offset
, *end_offset
, text
);
1060 STDMETHODIMP
NativeViewAccessibilityWin::get_textBeforeOffset(
1062 enum IA2TextBoundaryType boundary_type
,
1063 LONG
* start_offset
, LONG
* end_offset
,
1065 if (!start_offset
|| !end_offset
|| !text
)
1066 return E_INVALIDARG
;
1068 // The IAccessible2 spec says we don't have to implement the "sentence"
1069 // boundary type, we can just let the screenreader handle it.
1070 if (boundary_type
== IA2_TEXT_BOUNDARY_SENTENCE
) {
1077 const base::string16
& text_str
= TextForIAccessibleText();
1079 *start_offset
= FindBoundary(
1080 text_str
, boundary_type
, offset
, ui::BACKWARDS_DIRECTION
);
1081 *end_offset
= offset
;
1082 return get_text(*start_offset
, *end_offset
, text
);
1085 STDMETHODIMP
NativeViewAccessibilityWin::get_textAfterOffset(
1087 enum IA2TextBoundaryType boundary_type
,
1088 LONG
* start_offset
, LONG
* end_offset
,
1090 if (!start_offset
|| !end_offset
|| !text
)
1091 return E_INVALIDARG
;
1093 // The IAccessible2 spec says we don't have to implement the "sentence"
1094 // boundary type, we can just let the screenreader handle it.
1095 if (boundary_type
== IA2_TEXT_BOUNDARY_SENTENCE
) {
1102 const base::string16
& text_str
= TextForIAccessibleText();
1104 *start_offset
= offset
;
1105 *end_offset
= FindBoundary(
1106 text_str
, boundary_type
, offset
, ui::FORWARDS_DIRECTION
);
1107 return get_text(*start_offset
, *end_offset
, text
);
1110 STDMETHODIMP
NativeViewAccessibilityWin::get_offsetAtPoint(
1111 LONG x
, LONG y
, enum IA2CoordinateType coord_type
, LONG
* offset
) {
1116 return E_INVALIDARG
;
1118 // We don't support this method, but we have to return something
1119 // rather than E_NOTIMPL or screen readers will complain.
1125 // IServiceProvider methods.
1128 STDMETHODIMP
NativeViewAccessibilityWin::QueryService(
1129 REFGUID guidService
, REFIID riid
, void** object
) {
1133 if (riid
== IID_IAccessible2
|| riid
== IID_IAccessible2_2
)
1134 AccessibleWebViewRegistry::GetInstance()->EnableIAccessible2Support();
1136 if (guidService
== IID_IAccessible
||
1137 guidService
== IID_IAccessible2
||
1138 guidService
== IID_IAccessible2_2
||
1139 guidService
== IID_IAccessibleText
) {
1140 return QueryInterface(riid
, object
);
1143 // We only support the IAccessibleEx interface on Windows 8 and above. This
1144 // is needed for the On screen Keyboard to show up in metro mode, when the
1145 // user taps an editable region in the window.
1146 // All methods in the IAccessibleEx interface are unimplemented.
1147 if (riid
== IID_IAccessibleEx
&&
1148 base::win::GetVersion() >= base::win::VERSION_WIN8
) {
1149 return QueryInterface(riid
, object
);
1156 STDMETHODIMP
NativeViewAccessibilityWin::GetPatternProvider(
1157 PATTERNID id
, IUnknown
** provider
) {
1158 DVLOG(1) << "In Function: "
1160 << " for pattern id: "
1162 if (id
== UIA_ValuePatternId
|| id
== UIA_TextPatternId
) {
1163 ui::AXViewState state
;
1164 view_
->GetAccessibleState(&state
);
1165 long role
= MSAARole(state
.role
);
1167 if (role
== ROLE_SYSTEM_TEXT
) {
1168 DVLOG(1) << "Returning UIA text provider";
1169 base::win::UIATextProvider::CreateTextProvider(
1170 state
.value
, true, provider
);
1177 STDMETHODIMP
NativeViewAccessibilityWin::GetPropertyValue(PROPERTYID id
,
1179 DVLOG(1) << "In Function: "
1181 << " for property id: "
1183 if (id
== UIA_ControlTypePropertyId
) {
1184 ui::AXViewState state
;
1185 view_
->GetAccessibleState(&state
);
1186 long role
= MSAARole(state
.role
);
1187 if (role
== ROLE_SYSTEM_TEXT
) {
1189 ret
->lVal
= UIA_EditControlTypeId
;
1190 DVLOG(1) << "Returning Edit control type";
1192 DVLOG(1) << "Returning empty control type";
1193 V_VT(ret
) = VT_EMPTY
;
1196 V_VT(ret
) = VT_EMPTY
;
1205 void NativeViewAccessibility::RegisterWebView(View
* web_view
) {
1206 AccessibleWebViewRegistry::GetInstance()->RegisterWebView(web_view
);
1209 void NativeViewAccessibility::UnregisterWebView(View
* web_view
) {
1210 AccessibleWebViewRegistry::GetInstance()->UnregisterWebView(web_view
);
1213 int32
NativeViewAccessibilityWin::MSAAEvent(ui::AXEvent event
) {
1215 case ui::AX_EVENT_ALERT
:
1216 return EVENT_SYSTEM_ALERT
;
1217 case ui::AX_EVENT_FOCUS
:
1218 return EVENT_OBJECT_FOCUS
;
1219 case ui::AX_EVENT_MENU_START
:
1220 return EVENT_SYSTEM_MENUSTART
;
1221 case ui::AX_EVENT_MENU_END
:
1222 return EVENT_SYSTEM_MENUEND
;
1223 case ui::AX_EVENT_MENU_POPUP_START
:
1224 return EVENT_SYSTEM_MENUPOPUPSTART
;
1225 case ui::AX_EVENT_MENU_POPUP_END
:
1226 return EVENT_SYSTEM_MENUPOPUPEND
;
1227 case ui::AX_EVENT_TEXT_CHANGED
:
1228 return EVENT_OBJECT_NAMECHANGE
;
1229 case ui::AX_EVENT_SELECTION_CHANGED
:
1230 return IA2_EVENT_TEXT_CARET_MOVED
;
1231 case ui::AX_EVENT_VALUE_CHANGED
:
1232 return EVENT_OBJECT_VALUECHANGE
;
1234 // Not supported or invalid event.
1240 int32
NativeViewAccessibilityWin::MSAARole(ui::AXRole role
) {
1242 case ui::AX_ROLE_ALERT
:
1243 return ROLE_SYSTEM_ALERT
;
1244 case ui::AX_ROLE_APPLICATION
:
1245 return ROLE_SYSTEM_APPLICATION
;
1246 case ui::AX_ROLE_BUTTON_DROP_DOWN
:
1247 return ROLE_SYSTEM_BUTTONDROPDOWN
;
1248 case ui::AX_ROLE_POP_UP_BUTTON
:
1249 return ROLE_SYSTEM_BUTTONMENU
;
1250 case ui::AX_ROLE_CHECK_BOX
:
1251 return ROLE_SYSTEM_CHECKBUTTON
;
1252 case ui::AX_ROLE_COMBO_BOX
:
1253 return ROLE_SYSTEM_COMBOBOX
;
1254 case ui::AX_ROLE_DIALOG
:
1255 return ROLE_SYSTEM_DIALOG
;
1256 case ui::AX_ROLE_GROUP
:
1257 return ROLE_SYSTEM_GROUPING
;
1258 case ui::AX_ROLE_IMAGE
:
1259 return ROLE_SYSTEM_GRAPHIC
;
1260 case ui::AX_ROLE_LINK
:
1261 return ROLE_SYSTEM_LINK
;
1262 case ui::AX_ROLE_LOCATION_BAR
:
1263 return ROLE_SYSTEM_GROUPING
;
1264 case ui::AX_ROLE_MENU_BAR
:
1265 return ROLE_SYSTEM_MENUBAR
;
1266 case ui::AX_ROLE_MENU_ITEM
:
1267 return ROLE_SYSTEM_MENUITEM
;
1268 case ui::AX_ROLE_MENU_LIST_POPUP
:
1269 return ROLE_SYSTEM_MENUPOPUP
;
1270 case ui::AX_ROLE_TREE
:
1271 return ROLE_SYSTEM_OUTLINE
;
1272 case ui::AX_ROLE_TREE_ITEM
:
1273 return ROLE_SYSTEM_OUTLINEITEM
;
1274 case ui::AX_ROLE_TAB
:
1275 return ROLE_SYSTEM_PAGETAB
;
1276 case ui::AX_ROLE_TAB_LIST
:
1277 return ROLE_SYSTEM_PAGETABLIST
;
1278 case ui::AX_ROLE_PANE
:
1279 return ROLE_SYSTEM_PANE
;
1280 case ui::AX_ROLE_PROGRESS_INDICATOR
:
1281 return ROLE_SYSTEM_PROGRESSBAR
;
1282 case ui::AX_ROLE_BUTTON
:
1283 return ROLE_SYSTEM_PUSHBUTTON
;
1284 case ui::AX_ROLE_RADIO_BUTTON
:
1285 return ROLE_SYSTEM_RADIOBUTTON
;
1286 case ui::AX_ROLE_SCROLL_BAR
:
1287 return ROLE_SYSTEM_SCROLLBAR
;
1288 case ui::AX_ROLE_SPLITTER
:
1289 return ROLE_SYSTEM_SEPARATOR
;
1290 case ui::AX_ROLE_SLIDER
:
1291 return ROLE_SYSTEM_SLIDER
;
1292 case ui::AX_ROLE_STATIC_TEXT
:
1293 return ROLE_SYSTEM_STATICTEXT
;
1294 case ui::AX_ROLE_TEXT_FIELD
:
1295 return ROLE_SYSTEM_TEXT
;
1296 case ui::AX_ROLE_TITLE_BAR
:
1297 return ROLE_SYSTEM_TITLEBAR
;
1298 case ui::AX_ROLE_TOOLBAR
:
1299 return ROLE_SYSTEM_TOOLBAR
;
1300 case ui::AX_ROLE_WINDOW
:
1301 return ROLE_SYSTEM_WINDOW
;
1302 case ui::AX_ROLE_CLIENT
:
1304 // This is the default role for MSAA.
1305 return ROLE_SYSTEM_CLIENT
;
1309 int32
NativeViewAccessibilityWin::MSAAState(const ui::AXViewState
& state
) {
1310 // This maps MSAA states for get_accState(). See also the IAccessible2
1311 // interface get_states().
1313 int32 msaa_state
= 0;
1314 if (state
.HasStateFlag(ui::AX_STATE_CHECKED
))
1315 msaa_state
|= STATE_SYSTEM_CHECKED
;
1316 if (state
.HasStateFlag(ui::AX_STATE_COLLAPSED
))
1317 msaa_state
|= STATE_SYSTEM_COLLAPSED
;
1318 if (state
.HasStateFlag(ui::AX_STATE_DEFAULT
))
1319 msaa_state
|= STATE_SYSTEM_DEFAULT
;
1320 if (state
.HasStateFlag(ui::AX_STATE_EXPANDED
))
1321 msaa_state
|= STATE_SYSTEM_EXPANDED
;
1322 if (state
.HasStateFlag(ui::AX_STATE_HASPOPUP
))
1323 msaa_state
|= STATE_SYSTEM_HASPOPUP
;
1324 if (state
.HasStateFlag(ui::AX_STATE_HOVERED
))
1325 msaa_state
|= STATE_SYSTEM_HOTTRACKED
;
1326 if (state
.HasStateFlag(ui::AX_STATE_INVISIBLE
))
1327 msaa_state
|= STATE_SYSTEM_INVISIBLE
;
1328 if (state
.HasStateFlag(ui::AX_STATE_LINKED
))
1329 msaa_state
|= STATE_SYSTEM_LINKED
;
1330 if (state
.HasStateFlag(ui::AX_STATE_OFFSCREEN
))
1331 msaa_state
|= STATE_SYSTEM_OFFSCREEN
;
1332 if (state
.HasStateFlag(ui::AX_STATE_PRESSED
))
1333 msaa_state
|= STATE_SYSTEM_PRESSED
;
1334 if (state
.HasStateFlag(ui::AX_STATE_PROTECTED
))
1335 msaa_state
|= STATE_SYSTEM_PROTECTED
;
1336 if (state
.HasStateFlag(ui::AX_STATE_READ_ONLY
))
1337 msaa_state
|= STATE_SYSTEM_READONLY
;
1338 if (state
.HasStateFlag(ui::AX_STATE_SELECTED
))
1339 msaa_state
|= STATE_SYSTEM_SELECTED
;
1340 if (state
.HasStateFlag(ui::AX_STATE_FOCUSED
))
1341 msaa_state
|= STATE_SYSTEM_FOCUSED
;
1342 if (state
.HasStateFlag(ui::AX_STATE_DISABLED
))
1343 msaa_state
|= STATE_SYSTEM_UNAVAILABLE
;
1351 bool NativeViewAccessibilityWin::IsNavDirNext(int nav_dir
) const {
1352 return (nav_dir
== NAVDIR_RIGHT
||
1353 nav_dir
== NAVDIR_DOWN
||
1354 nav_dir
== NAVDIR_NEXT
);
1357 bool NativeViewAccessibilityWin::IsValidNav(
1358 int nav_dir
, int start_id
, int lower_bound
, int upper_bound
) const {
1359 if (IsNavDirNext(nav_dir
)) {
1360 if ((start_id
+ 1) > upper_bound
) {
1364 if ((start_id
- 1) <= lower_bound
) {
1371 bool NativeViewAccessibilityWin::IsValidId(const VARIANT
& child
) const {
1372 // View accessibility returns an IAccessible for each view so we only support
1373 // the CHILDID_SELF id.
1374 return (VT_I4
== child
.vt
) && (CHILDID_SELF
== child
.lVal
);
1377 void NativeViewAccessibilityWin::SetState(
1378 VARIANT
* msaa_state
, View
* view
) {
1379 // Ensure the output param is initialized to zero.
1380 msaa_state
->lVal
= 0;
1382 // Default state; all views can have accessibility focus.
1383 msaa_state
->lVal
|= STATE_SYSTEM_FOCUSABLE
;
1388 if (!view
->enabled())
1389 msaa_state
->lVal
|= STATE_SYSTEM_UNAVAILABLE
;
1390 if (!view
->visible())
1391 msaa_state
->lVal
|= STATE_SYSTEM_INVISIBLE
;
1392 if (!strcmp(view
->GetClassName(), CustomButton::kViewClassName
)) {
1393 CustomButton
* button
= static_cast<CustomButton
*>(view
);
1394 if (button
->IsHotTracked())
1395 msaa_state
->lVal
|= STATE_SYSTEM_HOTTRACKED
;
1397 if (view
->HasFocus())
1398 msaa_state
->lVal
|= STATE_SYSTEM_FOCUSED
;
1400 // Add on any view-specific states.
1401 ui::AXViewState view_state
;
1402 view
->GetAccessibleState(&view_state
);
1403 msaa_state
->lVal
|= MSAAState(view_state
);
1406 base::string16
NativeViewAccessibilityWin::TextForIAccessibleText() {
1407 ui::AXViewState state
;
1408 view_
->GetAccessibleState(&state
);
1409 if (state
.role
== ui::AX_ROLE_TEXT_FIELD
)
1415 void NativeViewAccessibilityWin::HandleSpecialTextOffset(
1416 const base::string16
& text
, LONG
* offset
) {
1417 if (*offset
== IA2_TEXT_OFFSET_LENGTH
) {
1418 *offset
= static_cast<LONG
>(text
.size());
1419 } else if (*offset
== IA2_TEXT_OFFSET_CARET
) {
1420 get_caretOffset(offset
);
1424 ui::TextBoundaryType
NativeViewAccessibilityWin::IA2TextBoundaryToTextBoundary(
1425 IA2TextBoundaryType ia2_boundary
) {
1426 switch(ia2_boundary
) {
1427 case IA2_TEXT_BOUNDARY_CHAR
: return ui::CHAR_BOUNDARY
;
1428 case IA2_TEXT_BOUNDARY_WORD
: return ui::WORD_BOUNDARY
;
1429 case IA2_TEXT_BOUNDARY_LINE
: return ui::LINE_BOUNDARY
;
1430 case IA2_TEXT_BOUNDARY_SENTENCE
: return ui::SENTENCE_BOUNDARY
;
1431 case IA2_TEXT_BOUNDARY_PARAGRAPH
: return ui::PARAGRAPH_BOUNDARY
;
1432 case IA2_TEXT_BOUNDARY_ALL
: return ui::ALL_BOUNDARY
;
1435 return ui::CHAR_BOUNDARY
;
1439 LONG
NativeViewAccessibilityWin::FindBoundary(
1440 const base::string16
& text
,
1441 IA2TextBoundaryType ia2_boundary
,
1443 ui::TextBoundaryDirection direction
) {
1444 HandleSpecialTextOffset(text
, &start_offset
);
1445 ui::TextBoundaryType boundary
= IA2TextBoundaryToTextBoundary(ia2_boundary
);
1446 std::vector
<int32
> line_breaks
;
1447 return ui::FindAccessibleTextBoundary(
1448 text
, line_breaks
, boundary
, start_offset
, direction
);
1451 void NativeViewAccessibilityWin::PopulateChildWidgetVector(
1452 std::vector
<Widget
*>* result_child_widgets
) {
1453 const Widget
* widget
= view()->GetWidget();
1457 std::set
<Widget
*> child_widgets
;
1458 Widget::GetAllChildWidgets(widget
->GetNativeView(), &child_widgets
);
1459 Widget::GetAllOwnedWidgets(widget
->GetNativeView(), &child_widgets
);
1460 for (std::set
<Widget
*>::const_iterator iter
= child_widgets
.begin();
1461 iter
!= child_widgets
.end(); ++iter
) {
1462 Widget
* child_widget
= *iter
;
1463 if (child_widget
== widget
)
1466 if (!child_widget
->IsVisible())
1469 if (widget
->GetNativeWindowProperty(kWidgetNativeViewHostKey
))
1472 result_child_widgets
->push_back(child_widget
);
1476 void NativeViewAccessibilityWin::AddAlertTarget() {
1477 ViewStorage
* view_storage
= ViewStorage::GetInstance();
1478 for (size_t i
= 0; i
< alert_target_view_storage_ids_
.size(); ++i
) {
1479 int view_storage_id
= alert_target_view_storage_ids_
[i
];
1480 View
* view
= view_storage
->RetrieveView(view_storage_id
);
1484 int view_storage_id
= view_storage
->CreateStorageID();
1485 view_storage
->StoreView(view_storage_id
, view_
);
1486 alert_target_view_storage_ids_
.push_back(view_storage_id
);
1489 void NativeViewAccessibilityWin::RemoveAlertTarget() {
1490 ViewStorage
* view_storage
= ViewStorage::GetInstance();
1492 while (i
< alert_target_view_storage_ids_
.size()) {
1493 int view_storage_id
= alert_target_view_storage_ids_
[i
];
1494 View
* view
= view_storage
->RetrieveView(view_storage_id
);
1495 if (view
== NULL
|| view
== view_
) {
1496 alert_target_view_storage_ids_
.erase(
1497 alert_target_view_storage_ids_
.begin() + i
);
1504 } // namespace views