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"
7 #include <UIAutomationClient.h>
13 #include "base/memory/singleton.h"
14 #include "base/win/windows_version.h"
15 #include "third_party/iaccessible2/ia2_api_all.h"
16 #include "ui/base/accessibility/accessible_text_utils.h"
17 #include "ui/base/accessibility/accessible_view_state.h"
18 #include "ui/base/win/accessibility_ids_win.h"
19 #include "ui/base/win/accessibility_misc_utils.h"
20 #include "ui/base/win/atl_module.h"
21 #include "ui/views/controls/button/custom_button.h"
22 #include "ui/views/controls/webview/webview.h"
23 #include "ui/views/focus/focus_manager.h"
24 #include "ui/views/focus/view_storage.h"
25 #include "ui/views/win/hwnd_util.h"
27 using ui::AccessibilityTypes
;
32 class AccessibleWebViewRegistry
{
34 static AccessibleWebViewRegistry
* GetInstance();
36 void RegisterWebView(AccessibleWebView
* web_view
);
38 void UnregisterWebView(AccessibleWebView
* web_view
);
40 // Given the view that received the request for the accessible
41 // id in |top_view|, and the child id requested, return the native
42 // accessible object with that child id from one of the WebViews in
43 // |top_view|'s view hierarchy, if any.
44 IAccessible
* GetAccessibleFromWebView(View
* top_view
, long child_id
);
47 friend struct DefaultSingletonTraits
<AccessibleWebViewRegistry
>;
48 AccessibleWebViewRegistry();
49 ~AccessibleWebViewRegistry() {}
51 // Set of all web views. We check whether each one is contained in a
52 // top view dynamically rather than keeping track of a map.
53 std::set
<AccessibleWebView
*> web_views_
;
55 // The most recent top view used in a call to GetAccessibleFromWebView.
58 // The most recent web view where an accessible object was found,
59 // corresponding to |last_top_view_|.
60 AccessibleWebView
* last_web_view_
;
62 DISALLOW_COPY_AND_ASSIGN(AccessibleWebViewRegistry
);
65 AccessibleWebViewRegistry::AccessibleWebViewRegistry()
66 : last_top_view_(NULL
),
67 last_web_view_(NULL
) {
70 AccessibleWebViewRegistry
* AccessibleWebViewRegistry::GetInstance() {
71 return Singleton
<AccessibleWebViewRegistry
>::get();
74 void AccessibleWebViewRegistry::RegisterWebView(AccessibleWebView
* web_view
) {
75 DCHECK(web_views_
.find(web_view
) == web_views_
.end());
76 web_views_
.insert(web_view
);
79 void AccessibleWebViewRegistry::UnregisterWebView(AccessibleWebView
* web_view
) {
80 DCHECK(web_views_
.find(web_view
) != web_views_
.end());
81 web_views_
.erase(web_view
);
82 if (last_web_view_
== web_view
) {
83 last_top_view_
= NULL
;
84 last_web_view_
= NULL
;
88 IAccessible
* AccessibleWebViewRegistry::GetAccessibleFromWebView(
89 View
* top_view
, long child_id
) {
90 // This function gets called frequently, so try to avoid searching all
91 // of the web views if the notification is on the same web view that
93 if (last_top_view_
== top_view
) {
94 IAccessible
* accessible
=
95 last_web_view_
->AccessibleObjectFromChildId(child_id
);
100 // Search all web views. For each one, first ensure it's a descendant
101 // of this view where the event was posted - and if so, see if it owns
102 // an accessible object with that child id. If so, save the view to speed
103 // up the next notification.
104 for (std::set
<AccessibleWebView
*>::iterator iter
= web_views_
.begin();
105 iter
!= web_views_
.end(); ++iter
) {
106 AccessibleWebView
* web_view
= *iter
;
107 if (!top_view
->Contains(web_view
->AsView()))
109 IAccessible
* accessible
= web_view
->AccessibleObjectFromChildId(child_id
);
111 last_top_view_
= top_view
;
112 last_web_view_
= web_view
;
119 } // anonymous namespace
122 long NativeViewAccessibilityWin::next_unique_id_
= 1;
123 int NativeViewAccessibilityWin::view_storage_ids_
[kMaxViewStorageIds
] = {0};
124 int NativeViewAccessibilityWin::next_view_storage_id_index_
= 0;
127 NativeViewAccessibility
* NativeViewAccessibility::Create(View
* view
) {
128 // Make sure ATL is initialized in this module.
129 ui::win::CreateATLModuleIfNeeded();
131 CComObject
<NativeViewAccessibilityWin
>* instance
= NULL
;
132 HRESULT hr
= CComObject
<NativeViewAccessibilityWin
>::CreateInstance(
134 DCHECK(SUCCEEDED(hr
));
135 instance
->set_view(view
);
140 NativeViewAccessibilityWin::NativeViewAccessibilityWin()
142 unique_id_(next_unique_id_
++) {
145 NativeViewAccessibilityWin::~NativeViewAccessibilityWin() {
148 void NativeViewAccessibilityWin::NotifyAccessibilityEvent(
149 ui::AccessibilityTypes::Event event_type
) {
153 ViewStorage
* view_storage
= ViewStorage::GetInstance();
154 HWND hwnd
= HWNDForView(view_
);
155 int view_storage_id
= view_storage_ids_
[next_view_storage_id_index_
];
156 if (view_storage_id
== 0) {
157 view_storage_id
= view_storage
->CreateStorageID();
158 view_storage_ids_
[next_view_storage_id_index_
] = view_storage_id
;
160 view_storage
->RemoveView(view_storage_id
);
162 view_storage
->StoreView(view_storage_id
, view_
);
164 // Positive child ids are used for enumerating direct children,
165 // negative child ids can be used as unique ids to refer to a specific
166 // descendants. Make index into view_storage_ids_ into a negative child id.
168 base::win::kFirstViewsAccessibilityId
- next_view_storage_id_index_
;
169 ::NotifyWinEvent(MSAAEvent(event_type
), hwnd
, OBJID_CLIENT
, child_id
);
170 next_view_storage_id_index_
=
171 (next_view_storage_id_index_
+ 1) % kMaxViewStorageIds
;
174 gfx::NativeViewAccessible
NativeViewAccessibilityWin::GetNativeObject() {
178 void NativeViewAccessibilityWin::Destroy() {
183 // TODO(ctguil): Handle case where child View is not contained by parent.
184 STDMETHODIMP
NativeViewAccessibilityWin::accHitTest(
185 LONG x_left
, LONG y_top
, VARIANT
* child
) {
192 gfx::Point
point(x_left
, y_top
);
193 View::ConvertPointToTarget(NULL
, view_
, &point
);
195 if (!view_
->HitTestPoint(point
)) {
196 // If containing parent is not hit, return with failure.
197 child
->vt
= VT_EMPTY
;
201 View
* view
= view_
->GetEventHandlerForPoint(point
);
203 // No child hit, return parent id.
205 child
->lVal
= CHILDID_SELF
;
207 child
->vt
= VT_DISPATCH
;
208 child
->pdispVal
= view
->GetNativeViewAccessible();
209 child
->pdispVal
->AddRef();
214 HRESULT
NativeViewAccessibilityWin::accDoDefaultAction(VARIANT var_id
) {
215 if (!IsValidId(var_id
))
218 // The object does not support the method. This value is returned for
219 // controls that do not perform actions, such as edit fields.
220 return DISP_E_MEMBERNOTFOUND
;
223 STDMETHODIMP
NativeViewAccessibilityWin::accLocation(
224 LONG
* x_left
, LONG
* y_top
, LONG
* width
, LONG
* height
, VARIANT var_id
) {
225 if (!IsValidId(var_id
) || !x_left
|| !y_top
|| !width
|| !height
)
231 if (!view_
->bounds().IsEmpty()) {
232 *width
= view_
->width();
233 *height
= view_
->height();
234 gfx::Point
topleft(view_
->bounds().origin());
235 View::ConvertPointToScreen(
236 view_
->parent() ? view_
->parent() : view_
, &topleft
);
237 *x_left
= topleft
.x();
238 *y_top
= topleft
.y();
245 STDMETHODIMP
NativeViewAccessibilityWin::accNavigate(
246 LONG nav_dir
, VARIANT start
, VARIANT
* end
) {
247 if (start
.vt
!= VT_I4
|| !end
)
254 case NAVDIR_FIRSTCHILD
:
255 case NAVDIR_LASTCHILD
: {
256 if (start
.lVal
!= CHILDID_SELF
) {
257 // Start of navigation must be on the View itself.
259 } else if (!view_
->has_children()) {
260 // No children found.
264 // Set child_id based on first or last child.
266 if (nav_dir
== NAVDIR_LASTCHILD
)
267 child_id
= view_
->child_count() - 1;
269 View
* child
= view_
->child_at(child_id
);
270 end
->vt
= VT_DISPATCH
;
271 end
->pdispVal
= child
->GetNativeViewAccessible();
272 end
->pdispVal
->AddRef();
277 case NAVDIR_PREVIOUS
:
281 // Retrieve parent to access view index and perform bounds checking.
282 View
* parent
= view_
->parent();
287 if (start
.lVal
== CHILDID_SELF
) {
288 int view_index
= parent
->GetIndexOf(view_
);
289 // Check navigation bounds, adjusting for View child indexing (MSAA
290 // child indexing starts with 1, whereas View indexing starts with 0).
291 if (!IsValidNav(nav_dir
, view_index
, -1,
292 parent
->child_count() - 1)) {
293 // Navigation attempted to go out-of-bounds.
297 if (IsNavDirNext(nav_dir
)) {
304 View
* child
= parent
->child_at(view_index
);
305 end
->pdispVal
= child
->GetNativeViewAccessible();
306 end
->vt
= VT_DISPATCH
;
307 end
->pdispVal
->AddRef();
310 // Check navigation bounds, adjusting for MSAA child indexing (MSAA
311 // child indexing starts with 1, whereas View indexing starts with 0).
312 if (!IsValidNav(nav_dir
, start
.lVal
, 0, parent
->child_count() + 1)) {
313 // Navigation attempted to go out-of-bounds.
317 if (IsNavDirNext(nav_dir
)) {
324 HRESULT result
= this->get_accChild(start
, &end
->pdispVal
);
325 if (result
== S_FALSE
) {
328 end
->lVal
= start
.lVal
;
329 } else if (result
== E_INVALIDARG
) {
332 // Child is not a leaf.
333 end
->vt
= VT_DISPATCH
;
341 // Navigation performed correctly. Global return for this function, if no
342 // error triggered an escape earlier.
346 STDMETHODIMP
NativeViewAccessibilityWin::get_accChild(VARIANT var_child
,
347 IDispatch
** disp_child
) {
348 if (var_child
.vt
!= VT_I4
|| !disp_child
)
354 LONG child_id
= V_I4(&var_child
);
356 if (child_id
== CHILDID_SELF
) {
357 // Remain with the same dispatch.
361 View
* child_view
= NULL
;
363 // Positive child ids are a 1-based child index, used by clients
364 // that want to enumerate all immediate children.
365 int child_id_as_index
= child_id
- 1;
366 if (child_id_as_index
< view_
->child_count())
367 child_view
= view_
->child_at(child_id_as_index
);
369 // Negative child ids can be used to map to any descendant;
370 // we map child ids to a view storage id that can refer to a
371 // specific view (if that view still exists).
372 int view_storage_id_index
=
373 base::win::kFirstViewsAccessibilityId
- child_id
;
374 if (view_storage_id_index
>= 0 &&
375 view_storage_id_index
< kMaxViewStorageIds
) {
376 int view_storage_id
= view_storage_ids_
[view_storage_id_index
];
377 ViewStorage
* view_storage
= ViewStorage::GetInstance();
378 child_view
= view_storage
->RetrieveView(view_storage_id
);
381 *disp_child
= AccessibleWebViewRegistry::GetInstance()->
382 GetAccessibleFromWebView(view_
, child_id
);
384 (*disp_child
)->AddRef();
395 *disp_child
= child_view
->GetNativeViewAccessible();
396 (*disp_child
)->AddRef();
400 STDMETHODIMP
NativeViewAccessibilityWin::get_accChildCount(LONG
* child_count
) {
401 if (!child_count
|| !view_
)
407 *child_count
= view_
->child_count();
411 STDMETHODIMP
NativeViewAccessibilityWin::get_accDefaultAction(
412 VARIANT var_id
, BSTR
* def_action
) {
413 if (!IsValidId(var_id
) || !def_action
)
419 ui::AccessibleViewState state
;
420 view_
->GetAccessibleState(&state
);
421 string16 temp_action
= state
.default_action
;
423 if (!temp_action
.empty()) {
424 *def_action
= SysAllocString(temp_action
.c_str());
432 STDMETHODIMP
NativeViewAccessibilityWin::get_accDescription(
433 VARIANT var_id
, BSTR
* desc
) {
434 if (!IsValidId(var_id
) || !desc
)
442 view_
->GetTooltipText(gfx::Point(), &temp_desc
);
443 if (!temp_desc
.empty()) {
444 *desc
= SysAllocString(temp_desc
.c_str());
452 STDMETHODIMP
NativeViewAccessibilityWin::get_accFocus(VARIANT
* focus_child
) {
459 FocusManager
* focus_manager
= view_
->GetFocusManager();
460 View
* focus
= focus_manager
? focus_manager
->GetFocusedView() : NULL
;
461 if (focus
== view_
) {
462 // This view has focus.
463 focus_child
->vt
= VT_I4
;
464 focus_child
->lVal
= CHILDID_SELF
;
465 } else if (focus
&& view_
->Contains(focus
)) {
466 // Return the child object that has the keyboard focus.
467 focus_child
->vt
= VT_DISPATCH
;
468 focus_child
->pdispVal
= focus
->GetNativeViewAccessible();
469 focus_child
->pdispVal
->AddRef();
472 // Neither this object nor any of its children has the keyboard focus.
473 focus_child
->vt
= VT_EMPTY
;
478 STDMETHODIMP
NativeViewAccessibilityWin::get_accKeyboardShortcut(
479 VARIANT var_id
, BSTR
* acc_key
) {
480 if (!IsValidId(var_id
) || !acc_key
)
486 ui::AccessibleViewState state
;
487 view_
->GetAccessibleState(&state
);
488 string16 temp_key
= state
.keyboard_shortcut
;
490 if (!temp_key
.empty()) {
491 *acc_key
= SysAllocString(temp_key
.c_str());
499 STDMETHODIMP
NativeViewAccessibilityWin::get_accName(
500 VARIANT var_id
, BSTR
* name
) {
501 if (!IsValidId(var_id
) || !name
)
507 // Retrieve the current view's name.
508 ui::AccessibleViewState state
;
509 view_
->GetAccessibleState(&state
);
510 string16 temp_name
= state
.name
;
511 if (!temp_name
.empty()) {
512 // Return name retrieved.
513 *name
= SysAllocString(temp_name
.c_str());
515 // If view has no name, return S_FALSE.
522 STDMETHODIMP
NativeViewAccessibilityWin::get_accParent(
523 IDispatch
** disp_parent
) {
531 View
* parent_view
= view_
->parent();
534 HWND hwnd
= HWNDForView(view_
);
538 return ::AccessibleObjectFromWindow(
539 hwnd
, OBJID_WINDOW
, IID_IAccessible
,
540 reinterpret_cast<void**>(disp_parent
));
543 *disp_parent
= parent_view
->GetNativeViewAccessible();
544 (*disp_parent
)->AddRef();
548 STDMETHODIMP
NativeViewAccessibilityWin::get_accRole(
549 VARIANT var_id
, VARIANT
* role
) {
550 if (!IsValidId(var_id
) || !role
)
556 ui::AccessibleViewState state
;
557 view_
->GetAccessibleState(&state
);
559 role
->lVal
= MSAARole(state
.role
);
563 STDMETHODIMP
NativeViewAccessibilityWin::get_accState(
564 VARIANT var_id
, VARIANT
* state
) {
565 // This returns MSAA states. See also the IAccessible2 interface
568 if (!IsValidId(var_id
) || !state
)
576 // Retrieve all currently applicable states of the parent.
577 SetState(state
, view_
);
579 // Make sure that state is not empty, and has the proper type.
580 if (state
->vt
== VT_EMPTY
)
586 STDMETHODIMP
NativeViewAccessibilityWin::get_accValue(
587 VARIANT var_id
, BSTR
* value
) {
588 if (!IsValidId(var_id
) || !value
)
594 // Retrieve the current view's value.
595 ui::AccessibleViewState state
;
596 view_
->GetAccessibleState(&state
);
597 string16 temp_value
= state
.value
;
599 if (!temp_value
.empty()) {
600 // Return value retrieved.
601 *value
= SysAllocString(temp_value
.c_str());
603 // If view has no value, fall back into the default implementation.
611 // IAccessible functions not supported.
613 STDMETHODIMP
NativeViewAccessibilityWin::get_accSelection(VARIANT
* selected
) {
615 selected
->vt
= VT_EMPTY
;
619 STDMETHODIMP
NativeViewAccessibilityWin::accSelect(
620 LONG flagsSelect
, VARIANT var_id
) {
624 STDMETHODIMP
NativeViewAccessibilityWin::get_accHelp(
625 VARIANT var_id
, BSTR
* help
) {
631 STDMETHODIMP
NativeViewAccessibilityWin::get_accHelpTopic(
632 BSTR
* help_file
, VARIANT var_id
, LONG
* topic_id
) {
637 *topic_id
= static_cast<LONG
>(-1);
642 STDMETHODIMP
NativeViewAccessibilityWin::put_accName(
643 VARIANT var_id
, BSTR put_name
) {
648 STDMETHODIMP
NativeViewAccessibilityWin::put_accValue(
649 VARIANT var_id
, BSTR put_val
) {
658 STDMETHODIMP
NativeViewAccessibilityWin::role(LONG
* role
) {
665 ui::AccessibleViewState state
;
666 view_
->GetAccessibleState(&state
);
667 *role
= MSAARole(state
.role
);
671 STDMETHODIMP
NativeViewAccessibilityWin::get_states(AccessibleStates
* states
) {
672 // This returns IAccessible2 states, which supplement MSAA states.
673 // See also the MSAA interface get_accState.
681 ui::AccessibleViewState state
;
682 view_
->GetAccessibleState(&state
);
684 // There are only a couple of states we need to support
685 // in IAccessible2. If any more are added, we may want to
686 // add a helper function like MSAAState.
687 *states
= IA2_STATE_OPAQUE
;
688 if (state
.state
& AccessibilityTypes::STATE_EDITABLE
)
689 *states
|= IA2_STATE_EDITABLE
;
694 STDMETHODIMP
NativeViewAccessibilityWin::get_uniqueID(LONG
* unique_id
) {
701 *unique_id
= unique_id_
;
705 STDMETHODIMP
NativeViewAccessibilityWin::get_windowHandle(HWND
* window_handle
) {
712 *window_handle
= HWNDForView(view_
);
713 return *window_handle
? S_OK
: S_FALSE
;
720 STDMETHODIMP
NativeViewAccessibilityWin::get_nCharacters(LONG
* n_characters
) {
727 string16 text
= TextForIAccessibleText();
728 *n_characters
= static_cast<LONG
>(text
.size());
732 STDMETHODIMP
NativeViewAccessibilityWin::get_caretOffset(LONG
* offset
) {
739 ui::AccessibleViewState state
;
740 view_
->GetAccessibleState(&state
);
741 *offset
= static_cast<LONG
>(state
.selection_end
);
745 STDMETHODIMP
NativeViewAccessibilityWin::get_nSelections(LONG
* n_selections
) {
752 ui::AccessibleViewState state
;
753 view_
->GetAccessibleState(&state
);
754 if (state
.selection_start
!= state
.selection_end
)
761 STDMETHODIMP
NativeViewAccessibilityWin::get_selection(LONG selection_index
,
767 if (!start_offset
|| !end_offset
|| selection_index
!= 0)
770 ui::AccessibleViewState state
;
771 view_
->GetAccessibleState(&state
);
772 *start_offset
= static_cast<LONG
>(state
.selection_start
);
773 *end_offset
= static_cast<LONG
>(state
.selection_end
);
777 STDMETHODIMP
NativeViewAccessibilityWin::get_text(LONG start_offset
,
783 ui::AccessibleViewState state
;
784 view_
->GetAccessibleState(&state
);
785 string16 text_str
= TextForIAccessibleText();
786 LONG len
= static_cast<LONG
>(text_str
.size());
788 if (start_offset
== IA2_TEXT_OFFSET_LENGTH
) {
790 } else if (start_offset
== IA2_TEXT_OFFSET_CARET
) {
791 start_offset
= static_cast<LONG
>(state
.selection_end
);
793 if (end_offset
== IA2_TEXT_OFFSET_LENGTH
) {
794 end_offset
= static_cast<LONG
>(text_str
.size());
795 } else if (end_offset
== IA2_TEXT_OFFSET_CARET
) {
796 end_offset
= static_cast<LONG
>(state
.selection_end
);
799 // The spec allows the arguments to be reversed.
800 if (start_offset
> end_offset
) {
801 LONG tmp
= start_offset
;
802 start_offset
= end_offset
;
806 // The spec does not allow the start or end offsets to be out or range;
807 // we must return an error if so.
808 if (start_offset
< 0)
810 if (end_offset
> len
)
813 string16 substr
= text_str
.substr(start_offset
, end_offset
- start_offset
);
817 *text
= SysAllocString(substr
.c_str());
822 STDMETHODIMP
NativeViewAccessibilityWin::get_textAtOffset(
824 enum IA2TextBoundaryType boundary_type
,
825 LONG
* start_offset
, LONG
* end_offset
,
827 if (!start_offset
|| !end_offset
|| !text
)
830 // The IAccessible2 spec says we don't have to implement the "sentence"
831 // boundary type, we can just let the screenreader handle it.
832 if (boundary_type
== IA2_TEXT_BOUNDARY_SENTENCE
) {
839 const string16
& text_str
= TextForIAccessibleText();
841 *start_offset
= FindBoundary(
842 text_str
, boundary_type
, offset
, ui::BACKWARDS_DIRECTION
);
843 *end_offset
= FindBoundary(
844 text_str
, boundary_type
, offset
, ui::FORWARDS_DIRECTION
);
845 return get_text(*start_offset
, *end_offset
, text
);
848 STDMETHODIMP
NativeViewAccessibilityWin::get_textBeforeOffset(
850 enum IA2TextBoundaryType boundary_type
,
851 LONG
* start_offset
, LONG
* end_offset
,
853 if (!start_offset
|| !end_offset
|| !text
)
856 // The IAccessible2 spec says we don't have to implement the "sentence"
857 // boundary type, we can just let the screenreader handle it.
858 if (boundary_type
== IA2_TEXT_BOUNDARY_SENTENCE
) {
865 const string16
& text_str
= TextForIAccessibleText();
867 *start_offset
= FindBoundary(
868 text_str
, boundary_type
, offset
, ui::BACKWARDS_DIRECTION
);
869 *end_offset
= offset
;
870 return get_text(*start_offset
, *end_offset
, text
);
873 STDMETHODIMP
NativeViewAccessibilityWin::get_textAfterOffset(
875 enum IA2TextBoundaryType boundary_type
,
876 LONG
* start_offset
, LONG
* end_offset
,
878 if (!start_offset
|| !end_offset
|| !text
)
881 // The IAccessible2 spec says we don't have to implement the "sentence"
882 // boundary type, we can just let the screenreader handle it.
883 if (boundary_type
== IA2_TEXT_BOUNDARY_SENTENCE
) {
890 const string16
& text_str
= TextForIAccessibleText();
892 *start_offset
= offset
;
893 *end_offset
= FindBoundary(
894 text_str
, boundary_type
, offset
, ui::FORWARDS_DIRECTION
);
895 return get_text(*start_offset
, *end_offset
, text
);
898 STDMETHODIMP
NativeViewAccessibilityWin::get_offsetAtPoint(
899 LONG x
, LONG y
, enum IA2CoordinateType coord_type
, LONG
* offset
) {
906 // We don't support this method, but we have to return something
907 // rather than E_NOTIMPL or screen readers will complain.
913 // IServiceProvider methods.
916 STDMETHODIMP
NativeViewAccessibilityWin::QueryService(
917 REFGUID guidService
, REFIID riid
, void** object
) {
921 if (guidService
== IID_IAccessible
||
922 guidService
== IID_IAccessible2
||
923 guidService
== IID_IAccessibleText
) {
924 return QueryInterface(riid
, object
);
927 // We only support the IAccessibleEx interface on Windows 8 and above. This
928 // is needed for the On screen Keyboard to show up in metro mode, when the
929 // user taps an editable region in the window.
930 // All methods in the IAccessibleEx interface are unimplemented.
931 if (riid
== IID_IAccessibleEx
&&
932 base::win::GetVersion() >= base::win::VERSION_WIN8
) {
933 return QueryInterface(riid
, object
);
940 STDMETHODIMP
NativeViewAccessibilityWin::GetPatternProvider(
941 PATTERNID id
, IUnknown
** provider
) {
942 DVLOG(1) << "In Function: "
944 << " for pattern id: "
946 if (id
== UIA_ValuePatternId
|| id
== UIA_TextPatternId
) {
947 ui::AccessibleViewState state
;
948 view_
->GetAccessibleState(&state
);
949 long role
= MSAARole(state
.role
);
951 if (role
== ROLE_SYSTEM_TEXT
) {
952 DVLOG(1) << "Returning UIA text provider";
953 base::win::UIATextProvider::CreateTextProvider(true, provider
);
960 STDMETHODIMP
NativeViewAccessibilityWin::GetPropertyValue(PROPERTYID id
,
962 DVLOG(1) << "In Function: "
964 << " for property id: "
966 if (id
== UIA_ControlTypePropertyId
) {
967 ui::AccessibleViewState state
;
968 view_
->GetAccessibleState(&state
);
969 long role
= MSAARole(state
.role
);
970 if (role
== ROLE_SYSTEM_TEXT
) {
972 ret
->lVal
= UIA_EditControlTypeId
;
973 DVLOG(1) << "Returning Edit control type";
975 DVLOG(1) << "Returning empty control type";
976 V_VT(ret
) = VT_EMPTY
;
979 V_VT(ret
) = VT_EMPTY
;
988 void NativeViewAccessibility::RegisterWebView(AccessibleWebView
* web_view
) {
989 AccessibleWebViewRegistry::GetInstance()->RegisterWebView(web_view
);
992 void NativeViewAccessibility::UnregisterWebView(AccessibleWebView
* web_view
) {
993 AccessibleWebViewRegistry::GetInstance()->UnregisterWebView(web_view
);
996 int32
NativeViewAccessibilityWin::MSAAEvent(AccessibilityTypes::Event event
) {
998 case AccessibilityTypes::EVENT_ALERT
:
999 return EVENT_SYSTEM_ALERT
;
1000 case AccessibilityTypes::EVENT_FOCUS
:
1001 return EVENT_OBJECT_FOCUS
;
1002 case AccessibilityTypes::EVENT_MENUSTART
:
1003 return EVENT_SYSTEM_MENUSTART
;
1004 case AccessibilityTypes::EVENT_MENUEND
:
1005 return EVENT_SYSTEM_MENUEND
;
1006 case AccessibilityTypes::EVENT_MENUPOPUPSTART
:
1007 return EVENT_SYSTEM_MENUPOPUPSTART
;
1008 case AccessibilityTypes::EVENT_MENUPOPUPEND
:
1009 return EVENT_SYSTEM_MENUPOPUPEND
;
1010 case AccessibilityTypes::EVENT_NAME_CHANGED
:
1011 return EVENT_OBJECT_NAMECHANGE
;
1012 case AccessibilityTypes::EVENT_TEXT_CHANGED
:
1013 return EVENT_OBJECT_VALUECHANGE
;
1014 case AccessibilityTypes::EVENT_SELECTION_CHANGED
:
1015 return IA2_EVENT_TEXT_CARET_MOVED
;
1016 case AccessibilityTypes::EVENT_VALUE_CHANGED
:
1017 return EVENT_OBJECT_VALUECHANGE
;
1019 // Not supported or invalid event.
1025 int32
NativeViewAccessibilityWin::MSAARole(AccessibilityTypes::Role role
) {
1027 case AccessibilityTypes::ROLE_ALERT
:
1028 return ROLE_SYSTEM_ALERT
;
1029 case AccessibilityTypes::ROLE_APPLICATION
:
1030 return ROLE_SYSTEM_APPLICATION
;
1031 case AccessibilityTypes::ROLE_BUTTONDROPDOWN
:
1032 return ROLE_SYSTEM_BUTTONDROPDOWN
;
1033 case AccessibilityTypes::ROLE_BUTTONMENU
:
1034 return ROLE_SYSTEM_BUTTONMENU
;
1035 case AccessibilityTypes::ROLE_CHECKBUTTON
:
1036 return ROLE_SYSTEM_CHECKBUTTON
;
1037 case AccessibilityTypes::ROLE_COMBOBOX
:
1038 return ROLE_SYSTEM_COMBOBOX
;
1039 case AccessibilityTypes::ROLE_DIALOG
:
1040 return ROLE_SYSTEM_DIALOG
;
1041 case AccessibilityTypes::ROLE_GRAPHIC
:
1042 return ROLE_SYSTEM_GRAPHIC
;
1043 case AccessibilityTypes::ROLE_GROUPING
:
1044 return ROLE_SYSTEM_GROUPING
;
1045 case AccessibilityTypes::ROLE_LINK
:
1046 return ROLE_SYSTEM_LINK
;
1047 case AccessibilityTypes::ROLE_LOCATION_BAR
:
1048 return ROLE_SYSTEM_GROUPING
;
1049 case AccessibilityTypes::ROLE_MENUBAR
:
1050 return ROLE_SYSTEM_MENUBAR
;
1051 case AccessibilityTypes::ROLE_MENUITEM
:
1052 return ROLE_SYSTEM_MENUITEM
;
1053 case AccessibilityTypes::ROLE_MENUPOPUP
:
1054 return ROLE_SYSTEM_MENUPOPUP
;
1055 case AccessibilityTypes::ROLE_OUTLINE
:
1056 return ROLE_SYSTEM_OUTLINE
;
1057 case AccessibilityTypes::ROLE_OUTLINEITEM
:
1058 return ROLE_SYSTEM_OUTLINEITEM
;
1059 case AccessibilityTypes::ROLE_PAGETAB
:
1060 return ROLE_SYSTEM_PAGETAB
;
1061 case AccessibilityTypes::ROLE_PAGETABLIST
:
1062 return ROLE_SYSTEM_PAGETABLIST
;
1063 case AccessibilityTypes::ROLE_PANE
:
1064 return ROLE_SYSTEM_PANE
;
1065 case AccessibilityTypes::ROLE_PROGRESSBAR
:
1066 return ROLE_SYSTEM_PROGRESSBAR
;
1067 case AccessibilityTypes::ROLE_PUSHBUTTON
:
1068 return ROLE_SYSTEM_PUSHBUTTON
;
1069 case AccessibilityTypes::ROLE_RADIOBUTTON
:
1070 return ROLE_SYSTEM_RADIOBUTTON
;
1071 case AccessibilityTypes::ROLE_SCROLLBAR
:
1072 return ROLE_SYSTEM_SCROLLBAR
;
1073 case AccessibilityTypes::ROLE_SEPARATOR
:
1074 return ROLE_SYSTEM_SEPARATOR
;
1075 case AccessibilityTypes::ROLE_SLIDER
:
1076 return ROLE_SYSTEM_SLIDER
;
1077 case AccessibilityTypes::ROLE_STATICTEXT
:
1078 return ROLE_SYSTEM_STATICTEXT
;
1079 case AccessibilityTypes::ROLE_TEXT
:
1080 return ROLE_SYSTEM_TEXT
;
1081 case AccessibilityTypes::ROLE_TITLEBAR
:
1082 return ROLE_SYSTEM_TITLEBAR
;
1083 case AccessibilityTypes::ROLE_TOOLBAR
:
1084 return ROLE_SYSTEM_TOOLBAR
;
1085 case AccessibilityTypes::ROLE_WINDOW
:
1086 return ROLE_SYSTEM_WINDOW
;
1087 case AccessibilityTypes::ROLE_CLIENT
:
1089 // This is the default role for MSAA.
1090 return ROLE_SYSTEM_CLIENT
;
1094 int32
NativeViewAccessibilityWin::MSAAState(AccessibilityTypes::State state
) {
1095 // This maps MSAA states for get_accState(). See also the IAccessible2
1096 // interface get_states().
1098 int32 msaa_state
= 0;
1099 if (state
& AccessibilityTypes::STATE_CHECKED
)
1100 msaa_state
|= STATE_SYSTEM_CHECKED
;
1101 if (state
& AccessibilityTypes::STATE_COLLAPSED
)
1102 msaa_state
|= STATE_SYSTEM_COLLAPSED
;
1103 if (state
& AccessibilityTypes::STATE_DEFAULT
)
1104 msaa_state
|= STATE_SYSTEM_DEFAULT
;
1105 if (state
& AccessibilityTypes::STATE_EXPANDED
)
1106 msaa_state
|= STATE_SYSTEM_EXPANDED
;
1107 if (state
& AccessibilityTypes::STATE_HASPOPUP
)
1108 msaa_state
|= STATE_SYSTEM_HASPOPUP
;
1109 if (state
& AccessibilityTypes::STATE_HOTTRACKED
)
1110 msaa_state
|= STATE_SYSTEM_HOTTRACKED
;
1111 if (state
& AccessibilityTypes::STATE_INVISIBLE
)
1112 msaa_state
|= STATE_SYSTEM_INVISIBLE
;
1113 if (state
& AccessibilityTypes::STATE_LINKED
)
1114 msaa_state
|= STATE_SYSTEM_LINKED
;
1115 if (state
& AccessibilityTypes::STATE_OFFSCREEN
)
1116 msaa_state
|= STATE_SYSTEM_OFFSCREEN
;
1117 if (state
& AccessibilityTypes::STATE_PRESSED
)
1118 msaa_state
|= STATE_SYSTEM_PRESSED
;
1119 if (state
& AccessibilityTypes::STATE_PROTECTED
)
1120 msaa_state
|= STATE_SYSTEM_PROTECTED
;
1121 if (state
& AccessibilityTypes::STATE_READONLY
)
1122 msaa_state
|= STATE_SYSTEM_READONLY
;
1123 if (state
& AccessibilityTypes::STATE_SELECTED
)
1124 msaa_state
|= STATE_SYSTEM_SELECTED
;
1125 if (state
& AccessibilityTypes::STATE_FOCUSED
)
1126 msaa_state
|= STATE_SYSTEM_FOCUSED
;
1127 if (state
& AccessibilityTypes::STATE_UNAVAILABLE
)
1128 msaa_state
|= STATE_SYSTEM_UNAVAILABLE
;
1136 bool NativeViewAccessibilityWin::IsNavDirNext(int nav_dir
) const {
1137 return (nav_dir
== NAVDIR_RIGHT
||
1138 nav_dir
== NAVDIR_DOWN
||
1139 nav_dir
== NAVDIR_NEXT
);
1142 bool NativeViewAccessibilityWin::IsValidNav(
1143 int nav_dir
, int start_id
, int lower_bound
, int upper_bound
) const {
1144 if (IsNavDirNext(nav_dir
)) {
1145 if ((start_id
+ 1) > upper_bound
) {
1149 if ((start_id
- 1) <= lower_bound
) {
1156 bool NativeViewAccessibilityWin::IsValidId(const VARIANT
& child
) const {
1157 // View accessibility returns an IAccessible for each view so we only support
1158 // the CHILDID_SELF id.
1159 return (VT_I4
== child
.vt
) && (CHILDID_SELF
== child
.lVal
);
1162 void NativeViewAccessibilityWin::SetState(
1163 VARIANT
* msaa_state
, View
* view
) {
1164 // Ensure the output param is initialized to zero.
1165 msaa_state
->lVal
= 0;
1167 // Default state; all views can have accessibility focus.
1168 msaa_state
->lVal
|= STATE_SYSTEM_FOCUSABLE
;
1173 if (!view
->enabled())
1174 msaa_state
->lVal
|= STATE_SYSTEM_UNAVAILABLE
;
1175 if (!view
->visible())
1176 msaa_state
->lVal
|= STATE_SYSTEM_INVISIBLE
;
1177 if (view
->GetClassName() == CustomButton::kViewClassName
) {
1178 CustomButton
* button
= static_cast<CustomButton
*>(view
);
1179 if (button
->IsHotTracked())
1180 msaa_state
->lVal
|= STATE_SYSTEM_HOTTRACKED
;
1182 if (view
->HasFocus())
1183 msaa_state
->lVal
|= STATE_SYSTEM_FOCUSED
;
1185 // Add on any view-specific states.
1186 ui::AccessibleViewState view_state
;
1187 view
->GetAccessibleState(&view_state
);
1188 msaa_state
->lVal
|= MSAAState(view_state
.state
);
1191 string16
NativeViewAccessibilityWin::TextForIAccessibleText() {
1192 ui::AccessibleViewState state
;
1193 view_
->GetAccessibleState(&state
);
1194 if (state
.role
== AccessibilityTypes::ROLE_TEXT
)
1200 void NativeViewAccessibilityWin::HandleSpecialTextOffset(
1201 const string16
& text
, LONG
* offset
) {
1202 if (*offset
== IA2_TEXT_OFFSET_LENGTH
) {
1203 *offset
= static_cast<LONG
>(text
.size());
1204 } else if (*offset
== IA2_TEXT_OFFSET_CARET
) {
1205 get_caretOffset(offset
);
1209 ui::TextBoundaryType
NativeViewAccessibilityWin::IA2TextBoundaryToTextBoundary(
1210 IA2TextBoundaryType ia2_boundary
) {
1211 switch(ia2_boundary
) {
1212 case IA2_TEXT_BOUNDARY_CHAR
: return ui::CHAR_BOUNDARY
;
1213 case IA2_TEXT_BOUNDARY_WORD
: return ui::WORD_BOUNDARY
;
1214 case IA2_TEXT_BOUNDARY_LINE
: return ui::LINE_BOUNDARY
;
1215 case IA2_TEXT_BOUNDARY_SENTENCE
: return ui::SENTENCE_BOUNDARY
;
1216 case IA2_TEXT_BOUNDARY_PARAGRAPH
: return ui::PARAGRAPH_BOUNDARY
;
1217 case IA2_TEXT_BOUNDARY_ALL
: return ui::ALL_BOUNDARY
;
1220 return ui::CHAR_BOUNDARY
;
1224 LONG
NativeViewAccessibilityWin::FindBoundary(
1225 const string16
& text
,
1226 IA2TextBoundaryType ia2_boundary
,
1228 ui::TextBoundaryDirection direction
) {
1229 HandleSpecialTextOffset(text
, &start_offset
);
1230 ui::TextBoundaryType boundary
= IA2TextBoundaryToTextBoundary(ia2_boundary
);
1231 std::vector
<int32
> line_breaks
;
1232 return ui::FindAccessibleTextBoundary(
1233 text
, line_breaks
, boundary
, start_offset
, direction
);
1236 } // namespace views