Fix build break
[chromium-blink-merge.git] / ui / views / accessibility / native_view_accessibility_win.cc
bloba9031b3c7314ae60903a0ff63ccba2ab6adc8024
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>
8 #include <oleacc.h>
10 #include <set>
11 #include <vector>
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;
29 namespace views {
30 namespace {
32 class AccessibleWebViewRegistry {
33 public:
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);
46 private:
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.
56 View* last_top_view_;
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
92 // sent the last one.
93 if (last_top_view_ == top_view) {
94 IAccessible* accessible =
95 last_web_view_->AccessibleObjectFromChildId(child_id);
96 if (accessible)
97 return accessible;
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()))
108 continue;
109 IAccessible* accessible = web_view->AccessibleObjectFromChildId(child_id);
110 if (accessible) {
111 last_top_view_ = top_view;
112 last_web_view_ = web_view;
113 return accessible;
116 return NULL;
119 } // anonymous namespace
121 // static
122 long NativeViewAccessibilityWin::next_unique_id_ = 1;
123 int NativeViewAccessibilityWin::view_storage_ids_[kMaxViewStorageIds] = {0};
124 int NativeViewAccessibilityWin::next_view_storage_id_index_ = 0;
126 // static
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(
133 &instance);
134 DCHECK(SUCCEEDED(hr));
135 instance->set_view(view);
136 instance->AddRef();
137 return instance;
140 NativeViewAccessibilityWin::NativeViewAccessibilityWin()
141 : view_(NULL),
142 unique_id_(next_unique_id_++) {
145 NativeViewAccessibilityWin::~NativeViewAccessibilityWin() {
148 void NativeViewAccessibilityWin::NotifyAccessibilityEvent(
149 ui::AccessibilityTypes::Event event_type) {
150 if (!view_)
151 return;
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;
159 } else {
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.
167 int 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() {
175 return this;
178 void NativeViewAccessibilityWin::Destroy() {
179 view_ = NULL;
180 Release();
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) {
186 if (!child)
187 return E_INVALIDARG;
189 if (!view_)
190 return E_FAIL;
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;
198 return S_FALSE;
201 View* view = view_->GetEventHandlerForPoint(point);
202 if (view == view_) {
203 // No child hit, return parent id.
204 child->vt = VT_I4;
205 child->lVal = CHILDID_SELF;
206 } else {
207 child->vt = VT_DISPATCH;
208 child->pdispVal = view->GetNativeViewAccessible();
209 child->pdispVal->AddRef();
211 return S_OK;
214 HRESULT NativeViewAccessibilityWin::accDoDefaultAction(VARIANT var_id) {
215 if (!IsValidId(var_id))
216 return E_INVALIDARG;
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)
226 return E_INVALIDARG;
228 if (!view_)
229 return E_FAIL;
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();
239 } else {
240 return E_FAIL;
242 return S_OK;
245 STDMETHODIMP NativeViewAccessibilityWin::accNavigate(
246 LONG nav_dir, VARIANT start, VARIANT* end) {
247 if (start.vt != VT_I4 || !end)
248 return E_INVALIDARG;
250 if (!view_)
251 return E_FAIL;
253 switch (nav_dir) {
254 case NAVDIR_FIRSTCHILD:
255 case NAVDIR_LASTCHILD: {
256 if (start.lVal != CHILDID_SELF) {
257 // Start of navigation must be on the View itself.
258 return E_INVALIDARG;
259 } else if (!view_->has_children()) {
260 // No children found.
261 return S_FALSE;
264 // Set child_id based on first or last child.
265 int child_id = 0;
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();
273 return S_OK;
275 case NAVDIR_LEFT:
276 case NAVDIR_UP:
277 case NAVDIR_PREVIOUS:
278 case NAVDIR_RIGHT:
279 case NAVDIR_DOWN:
280 case NAVDIR_NEXT: {
281 // Retrieve parent to access view index and perform bounds checking.
282 View* parent = view_->parent();
283 if (!parent) {
284 return E_FAIL;
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.
294 end->vt = VT_EMPTY;
295 return S_FALSE;
296 } else {
297 if (IsNavDirNext(nav_dir)) {
298 view_index += 1;
299 } else {
300 view_index -=1;
304 View* child = parent->child_at(view_index);
305 end->pdispVal = child->GetNativeViewAccessible();
306 end->vt = VT_DISPATCH;
307 end->pdispVal->AddRef();
308 return S_OK;
309 } else {
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.
314 end->vt = VT_EMPTY;
315 return S_FALSE;
316 } else {
317 if (IsNavDirNext(nav_dir)) {
318 start.lVal += 1;
319 } else {
320 start.lVal -= 1;
324 HRESULT result = this->get_accChild(start, &end->pdispVal);
325 if (result == S_FALSE) {
326 // Child is a leaf.
327 end->vt = VT_I4;
328 end->lVal = start.lVal;
329 } else if (result == E_INVALIDARG) {
330 return E_INVALIDARG;
331 } else {
332 // Child is not a leaf.
333 end->vt = VT_DISPATCH;
336 break;
338 default:
339 return E_INVALIDARG;
341 // Navigation performed correctly. Global return for this function, if no
342 // error triggered an escape earlier.
343 return S_OK;
346 STDMETHODIMP NativeViewAccessibilityWin::get_accChild(VARIANT var_child,
347 IDispatch** disp_child) {
348 if (var_child.vt != VT_I4 || !disp_child)
349 return E_INVALIDARG;
351 if (!view_)
352 return E_FAIL;
354 LONG child_id = V_I4(&var_child);
356 if (child_id == CHILDID_SELF) {
357 // Remain with the same dispatch.
358 return S_OK;
361 View* child_view = NULL;
362 if (child_id > 0) {
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);
368 } else {
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);
383 if (*disp_child) {
384 (*disp_child)->AddRef();
385 return S_OK;
389 if (!child_view) {
390 // No child found.
391 *disp_child = NULL;
392 return E_FAIL;
395 *disp_child = child_view->GetNativeViewAccessible();
396 (*disp_child)->AddRef();
397 return S_OK;
400 STDMETHODIMP NativeViewAccessibilityWin::get_accChildCount(LONG* child_count) {
401 if (!child_count || !view_)
402 return E_INVALIDARG;
404 if (!view_)
405 return E_FAIL;
407 *child_count = view_->child_count();
408 return S_OK;
411 STDMETHODIMP NativeViewAccessibilityWin::get_accDefaultAction(
412 VARIANT var_id, BSTR* def_action) {
413 if (!IsValidId(var_id) || !def_action)
414 return E_INVALIDARG;
416 if (!view_)
417 return E_FAIL;
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());
425 } else {
426 return S_FALSE;
429 return S_OK;
432 STDMETHODIMP NativeViewAccessibilityWin::get_accDescription(
433 VARIANT var_id, BSTR* desc) {
434 if (!IsValidId(var_id) || !desc)
435 return E_INVALIDARG;
437 if (!view_)
438 return E_FAIL;
440 string16 temp_desc;
442 view_->GetTooltipText(gfx::Point(), &temp_desc);
443 if (!temp_desc.empty()) {
444 *desc = SysAllocString(temp_desc.c_str());
445 } else {
446 return S_FALSE;
449 return S_OK;
452 STDMETHODIMP NativeViewAccessibilityWin::get_accFocus(VARIANT* focus_child) {
453 if (!focus_child)
454 return E_INVALIDARG;
456 if (!view_)
457 return E_FAIL;
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();
470 return S_OK;
471 } else {
472 // Neither this object nor any of its children has the keyboard focus.
473 focus_child->vt = VT_EMPTY;
475 return S_OK;
478 STDMETHODIMP NativeViewAccessibilityWin::get_accKeyboardShortcut(
479 VARIANT var_id, BSTR* acc_key) {
480 if (!IsValidId(var_id) || !acc_key)
481 return E_INVALIDARG;
483 if (!view_)
484 return E_FAIL;
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());
492 } else {
493 return S_FALSE;
496 return S_OK;
499 STDMETHODIMP NativeViewAccessibilityWin::get_accName(
500 VARIANT var_id, BSTR* name) {
501 if (!IsValidId(var_id) || !name)
502 return E_INVALIDARG;
504 if (!view_)
505 return E_FAIL;
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());
514 } else {
515 // If view has no name, return S_FALSE.
516 return S_FALSE;
519 return S_OK;
522 STDMETHODIMP NativeViewAccessibilityWin::get_accParent(
523 IDispatch** disp_parent) {
524 if (!disp_parent)
525 return E_INVALIDARG;
527 if (!view_)
528 return E_FAIL;
530 *disp_parent = NULL;
531 View* parent_view = view_->parent();
533 if (!parent_view) {
534 HWND hwnd = HWNDForView(view_);
535 if (!hwnd)
536 return S_FALSE;
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();
545 return S_OK;
548 STDMETHODIMP NativeViewAccessibilityWin::get_accRole(
549 VARIANT var_id, VARIANT* role) {
550 if (!IsValidId(var_id) || !role)
551 return E_INVALIDARG;
553 if (!view_)
554 return E_FAIL;
556 ui::AccessibleViewState state;
557 view_->GetAccessibleState(&state);
558 role->vt = VT_I4;
559 role->lVal = MSAARole(state.role);
560 return S_OK;
563 STDMETHODIMP NativeViewAccessibilityWin::get_accState(
564 VARIANT var_id, VARIANT* state) {
565 // This returns MSAA states. See also the IAccessible2 interface
566 // get_states().
568 if (!IsValidId(var_id) || !state)
569 return E_INVALIDARG;
571 if (!view_)
572 return E_FAIL;
574 state->vt = VT_I4;
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)
581 return E_FAIL;
583 return S_OK;
586 STDMETHODIMP NativeViewAccessibilityWin::get_accValue(
587 VARIANT var_id, BSTR* value) {
588 if (!IsValidId(var_id) || !value)
589 return E_INVALIDARG;
591 if (!view_)
592 return E_FAIL;
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());
602 } else {
603 // If view has no value, fall back into the default implementation.
604 *value = NULL;
605 return E_NOTIMPL;
608 return S_OK;
611 // IAccessible functions not supported.
613 STDMETHODIMP NativeViewAccessibilityWin::get_accSelection(VARIANT* selected) {
614 if (selected)
615 selected->vt = VT_EMPTY;
616 return E_NOTIMPL;
619 STDMETHODIMP NativeViewAccessibilityWin::accSelect(
620 LONG flagsSelect, VARIANT var_id) {
621 return E_NOTIMPL;
624 STDMETHODIMP NativeViewAccessibilityWin::get_accHelp(
625 VARIANT var_id, BSTR* help) {
626 if (help)
627 *help = NULL;
628 return E_NOTIMPL;
631 STDMETHODIMP NativeViewAccessibilityWin::get_accHelpTopic(
632 BSTR* help_file, VARIANT var_id, LONG* topic_id) {
633 if (help_file) {
634 *help_file = NULL;
636 if (topic_id) {
637 *topic_id = static_cast<LONG>(-1);
639 return E_NOTIMPL;
642 STDMETHODIMP NativeViewAccessibilityWin::put_accName(
643 VARIANT var_id, BSTR put_name) {
644 // Deprecated.
645 return E_NOTIMPL;
648 STDMETHODIMP NativeViewAccessibilityWin::put_accValue(
649 VARIANT var_id, BSTR put_val) {
650 // Deprecated.
651 return E_NOTIMPL;
655 // IAccessible2
658 STDMETHODIMP NativeViewAccessibilityWin::role(LONG* role) {
659 if (!view_)
660 return E_FAIL;
662 if (!role)
663 return E_INVALIDARG;
665 ui::AccessibleViewState state;
666 view_->GetAccessibleState(&state);
667 *role = MSAARole(state.role);
668 return S_OK;
671 STDMETHODIMP NativeViewAccessibilityWin::get_states(AccessibleStates* states) {
672 // This returns IAccessible2 states, which supplement MSAA states.
673 // See also the MSAA interface get_accState.
675 if (!view_)
676 return E_FAIL;
678 if (!states)
679 return E_INVALIDARG;
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;
691 return S_OK;
694 STDMETHODIMP NativeViewAccessibilityWin::get_uniqueID(LONG* unique_id) {
695 if (!view_)
696 return E_FAIL;
698 if (!unique_id)
699 return E_INVALIDARG;
701 *unique_id = unique_id_;
702 return S_OK;
705 STDMETHODIMP NativeViewAccessibilityWin::get_windowHandle(HWND* window_handle) {
706 if (!view_)
707 return E_FAIL;
709 if (!window_handle)
710 return E_INVALIDARG;
712 *window_handle = HWNDForView(view_);
713 return *window_handle ? S_OK : S_FALSE;
717 // IAccessibleText
720 STDMETHODIMP NativeViewAccessibilityWin::get_nCharacters(LONG* n_characters) {
721 if (!view_)
722 return E_FAIL;
724 if (!n_characters)
725 return E_INVALIDARG;
727 string16 text = TextForIAccessibleText();
728 *n_characters = static_cast<LONG>(text.size());
729 return S_OK;
732 STDMETHODIMP NativeViewAccessibilityWin::get_caretOffset(LONG* offset) {
733 if (!view_)
734 return E_FAIL;
736 if (!offset)
737 return E_INVALIDARG;
739 ui::AccessibleViewState state;
740 view_->GetAccessibleState(&state);
741 *offset = static_cast<LONG>(state.selection_end);
742 return S_OK;
745 STDMETHODIMP NativeViewAccessibilityWin::get_nSelections(LONG* n_selections) {
746 if (!view_)
747 return E_FAIL;
749 if (!n_selections)
750 return E_INVALIDARG;
752 ui::AccessibleViewState state;
753 view_->GetAccessibleState(&state);
754 if (state.selection_start != state.selection_end)
755 *n_selections = 1;
756 else
757 *n_selections = 0;
758 return S_OK;
761 STDMETHODIMP NativeViewAccessibilityWin::get_selection(LONG selection_index,
762 LONG* start_offset,
763 LONG* end_offset) {
764 if (!view_)
765 return E_FAIL;
767 if (!start_offset || !end_offset || selection_index != 0)
768 return E_INVALIDARG;
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);
774 return S_OK;
777 STDMETHODIMP NativeViewAccessibilityWin::get_text(LONG start_offset,
778 LONG end_offset,
779 BSTR* text) {
780 if (!view_)
781 return E_FAIL;
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) {
789 start_offset = len;
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;
803 end_offset = tmp;
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)
809 return E_INVALIDARG;
810 if (end_offset > len)
811 return E_INVALIDARG;
813 string16 substr = text_str.substr(start_offset, end_offset - start_offset);
814 if (substr.empty())
815 return S_FALSE;
817 *text = SysAllocString(substr.c_str());
818 DCHECK(*text);
819 return S_OK;
822 STDMETHODIMP NativeViewAccessibilityWin::get_textAtOffset(
823 LONG offset,
824 enum IA2TextBoundaryType boundary_type,
825 LONG* start_offset, LONG* end_offset,
826 BSTR* text) {
827 if (!start_offset || !end_offset || !text)
828 return E_INVALIDARG;
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) {
833 *start_offset = 0;
834 *end_offset = 0;
835 *text = NULL;
836 return S_FALSE;
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(
849 LONG offset,
850 enum IA2TextBoundaryType boundary_type,
851 LONG* start_offset, LONG* end_offset,
852 BSTR* text) {
853 if (!start_offset || !end_offset || !text)
854 return E_INVALIDARG;
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) {
859 *start_offset = 0;
860 *end_offset = 0;
861 *text = NULL;
862 return S_FALSE;
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(
874 LONG offset,
875 enum IA2TextBoundaryType boundary_type,
876 LONG* start_offset, LONG* end_offset,
877 BSTR* text) {
878 if (!start_offset || !end_offset || !text)
879 return E_INVALIDARG;
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) {
884 *start_offset = 0;
885 *end_offset = 0;
886 *text = NULL;
887 return S_FALSE;
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) {
900 if (!view_)
901 return E_FAIL;
903 if (!offset)
904 return E_INVALIDARG;
906 // We don't support this method, but we have to return something
907 // rather than E_NOTIMPL or screen readers will complain.
908 *offset = 0;
909 return S_OK;
913 // IServiceProvider methods.
916 STDMETHODIMP NativeViewAccessibilityWin::QueryService(
917 REFGUID guidService, REFIID riid, void** object) {
918 if (!view_)
919 return E_FAIL;
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);
936 *object = NULL;
937 return E_FAIL;
940 STDMETHODIMP NativeViewAccessibilityWin::GetPatternProvider(
941 PATTERNID id, IUnknown** provider) {
942 DVLOG(1) << "In Function: "
943 << __FUNCTION__
944 << " for pattern id: "
945 << 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);
954 return S_OK;
957 return E_NOTIMPL;
960 STDMETHODIMP NativeViewAccessibilityWin::GetPropertyValue(PROPERTYID id,
961 VARIANT* ret) {
962 DVLOG(1) << "In Function: "
963 << __FUNCTION__
964 << " for property id: "
965 << 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) {
971 V_VT(ret) = VT_I4;
972 ret->lVal = UIA_EditControlTypeId;
973 DVLOG(1) << "Returning Edit control type";
974 } else {
975 DVLOG(1) << "Returning empty control type";
976 V_VT(ret) = VT_EMPTY;
978 } else {
979 V_VT(ret) = VT_EMPTY;
981 return S_OK;
985 // Static methods.
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) {
997 switch (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;
1018 default:
1019 // Not supported or invalid event.
1020 NOTREACHED();
1021 return -1;
1025 int32 NativeViewAccessibilityWin::MSAARole(AccessibilityTypes::Role role) {
1026 switch (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:
1088 default:
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;
1129 return msaa_state;
1133 // Private methods.
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) {
1146 return false;
1148 } else {
1149 if ((start_id - 1) <= lower_bound) {
1150 return false;
1153 return true;
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;
1170 if (!view)
1171 return;
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)
1195 return state.value;
1196 else
1197 return state.name;
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;
1218 default:
1219 NOTREACHED();
1220 return ui::CHAR_BOUNDARY;
1224 LONG NativeViewAccessibilityWin::FindBoundary(
1225 const string16& text,
1226 IA2TextBoundaryType ia2_boundary,
1227 LONG start_offset,
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