OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "ui/views/accessibility/native_view_accessibility_win.h" | 5 #include "ui/views/accessibility/native_view_accessibility_win.h" |
6 | 6 |
7 #include <oleacc.h> | 7 #include <oleacc.h> |
8 #include <UIAutomationClient.h> | |
9 | 8 |
10 #include <set> | 9 #include <set> |
11 #include <vector> | 10 #include <vector> |
12 | 11 |
13 #include "base/memory/singleton.h" | 12 #include "base/memory/singleton.h" |
14 #include "base/strings/utf_string_conversions.h" | 13 #include "base/strings/utf_string_conversions.h" |
15 #include "base/win/scoped_comptr.h" | 14 #include "base/win/scoped_comptr.h" |
16 #include "base/win/windows_version.h" | 15 #include "base/win/windows_version.h" |
17 #include "third_party/iaccessible2/ia2_api_all.h" | 16 #include "third_party/iaccessible2/ia2_api_all.h" |
18 #include "ui/accessibility/ax_enums.h" | 17 #include "ui/accessibility/ax_enums.h" |
19 #include "ui/accessibility/ax_text_utils.h" | 18 #include "ui/accessibility/ax_text_utils.h" |
20 #include "ui/accessibility/ax_view_state.h" | 19 #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" | 20 #include "ui/base/win/accessibility_misc_utils.h" |
23 #include "ui/base/win/atl_module.h" | 21 #include "ui/base/win/atl_module.h" |
24 #include "ui/views/controls/button/custom_button.h" | 22 #include "ui/views/controls/button/custom_button.h" |
25 #include "ui/views/focus/focus_manager.h" | 23 #include "ui/views/focus/focus_manager.h" |
26 #include "ui/views/focus/view_storage.h" | 24 #include "ui/views/focus/view_storage.h" |
27 #include "ui/views/widget/widget.h" | 25 #include "ui/views/widget/widget.h" |
28 #include "ui/views/win/hwnd_util.h" | 26 #include "ui/views/win/hwnd_util.h" |
29 | 27 |
30 namespace views { | 28 namespace views { |
31 namespace { | |
32 | 29 |
33 class AccessibleWebViewRegistry { | 30 // static |
34 public: | 31 NativeViewAccessibility* NativeViewAccessibility::Create(View* view) { |
35 static AccessibleWebViewRegistry* GetInstance(); | 32 return new NativeViewAccessibilityWin(view); |
36 | |
37 void RegisterWebView(View* web_view); | |
38 | |
39 void UnregisterWebView(View* web_view); | |
40 | |
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); | |
46 | |
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(); | |
55 | |
56 private: | |
57 friend struct DefaultSingletonTraits<AccessibleWebViewRegistry>; | |
58 AccessibleWebViewRegistry(); | |
59 ~AccessibleWebViewRegistry() {} | |
60 | |
61 IAccessible* AccessibleObjectFromChildId(View* web_view, long child_id); | |
62 | |
63 void QueryIAccessible2Interface(View* web_view); | |
64 | |
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_; | |
68 | |
69 // The most recent top view used in a call to GetAccessibleFromWebView. | |
70 View* last_top_view_; | |
71 | |
72 // The most recent web view where an accessible object was found, | |
73 // corresponding to |last_top_view_|. | |
74 View* last_web_view_; | |
75 | |
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_; | |
80 | |
81 DISALLOW_COPY_AND_ASSIGN(AccessibleWebViewRegistry); | |
82 }; | |
83 | |
84 AccessibleWebViewRegistry::AccessibleWebViewRegistry() | |
85 : last_top_view_(NULL), | |
86 last_web_view_(NULL), | |
87 iaccessible2_support_enabled_(false) { | |
88 } | 33 } |
89 | 34 |
90 AccessibleWebViewRegistry* AccessibleWebViewRegistry::GetInstance() { | 35 NativeViewAccessibilityWin::NativeViewAccessibilityWin(View* view) |
91 return Singleton<AccessibleWebViewRegistry>::get(); | 36 : NativeViewAccessibility(view) { |
92 } | 37 } |
93 | 38 |
94 void AccessibleWebViewRegistry::RegisterWebView(View* web_view) { | 39 NativeViewAccessibilityWin::~NativeViewAccessibilityWin() { |
95 DCHECK(web_views_.find(web_view) == web_views_.end()); | |
96 web_views_.insert(web_view); | |
97 | |
98 if (iaccessible2_support_enabled_) | |
99 QueryIAccessible2Interface(web_view); | |
100 } | 40 } |
101 | 41 |
102 void AccessibleWebViewRegistry::UnregisterWebView(View* web_view) { | 42 gfx::NativeViewAccessible NativeViewAccessibilityWin::GetParent() { |
103 DCHECK(web_views_.find(web_view) != web_views_.end()); | 43 IAccessible* parent = NativeViewAccessibility::GetParent(); |
104 web_views_.erase(web_view); | 44 if (parent) |
105 if (last_web_view_ == web_view) { | 45 return parent; |
106 last_top_view_ = NULL; | |
107 last_web_view_ = NULL; | |
108 } | |
109 } | |
110 | 46 |
111 IAccessible* AccessibleWebViewRegistry::GetAccessibleFromWebView( | 47 HWND hwnd = HWNDForView(view_); |
112 View* top_view, long child_id) { | 48 if (!hwnd) |
113 // This function gets called frequently, so try to avoid searching all | 49 return NULL; |
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); | |
119 if (accessible) | |
120 return accessible; | |
121 } | |
122 | 50 |
123 // Search all web views. For each one, first ensure it's a descendant | 51 HRESULT hr = ::AccessibleObjectFromWindow( |
124 // of this view where the event was posted - and if so, see if it owns | 52 hwnd, OBJID_WINDOW, IID_IAccessible, |
125 // an accessible object with that child id. If so, save the view to speed | 53 reinterpret_cast<void**>(&parent)); |
126 // up the next notification. | 54 if (SUCCEEDED(hr)) |
127 for (std::set<View*>::iterator iter = web_views_.begin(); | 55 return parent; |
128 iter != web_views_.end(); ++iter) { | |
129 View* web_view = *iter; | |
130 if (top_view == web_view || !top_view->Contains(web_view)) | |
131 continue; | |
132 IAccessible* accessible = AccessibleObjectFromChildId(web_view, child_id); | |
133 if (accessible) { | |
134 last_top_view_ = top_view; | |
135 last_web_view_ = web_view; | |
136 return accessible; | |
137 } | |
138 } | |
139 | 56 |
140 return NULL; | 57 return NULL; |
141 } | 58 } |
142 | 59 |
143 void AccessibleWebViewRegistry::EnableIAccessible2Support() { | 60 gfx::AcceleratedWidget |
144 if (iaccessible2_support_enabled_) | 61 NativeViewAccessibilityWin::GetTargetForNativeAccessibilityEvent() { |
145 return; | 62 return HWNDForView(view_); |
146 iaccessible2_support_enabled_ = true; | |
147 for (std::set<View*>::iterator iter = web_views_.begin(); | |
148 iter != web_views_.end(); ++iter) { | |
149 QueryIAccessible2Interface(*iter); | |
150 } | |
151 } | |
152 | |
153 IAccessible* AccessibleWebViewRegistry::AccessibleObjectFromChildId( | |
154 View* web_view, | |
155 long child_id) { | |
156 IAccessible* web_view_accessible = web_view->GetNativeViewAccessible(); | |
157 if (web_view_accessible == NULL) | |
158 return NULL; | |
159 | |
160 VARIANT var_child; | |
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))) { | |
166 return result; | |
167 } | |
168 | |
169 return NULL; | |
170 } | |
171 | |
172 void AccessibleWebViewRegistry::QueryIAccessible2Interface(View* web_view) { | |
173 IAccessible* web_view_accessible = web_view->GetNativeViewAccessible(); | |
174 if (!web_view_accessible) | |
175 return; | |
176 | |
177 base::win::ScopedComPtr<IServiceProvider> service_provider; | |
178 if (S_OK != web_view_accessible->QueryInterface(service_provider.Receive())) | |
179 return; | |
180 base::win::ScopedComPtr<IAccessible2> iaccessible2; | |
181 service_provider->QueryService( | |
182 IID_IAccessible, IID_IAccessible2, | |
183 reinterpret_cast<void**>(iaccessible2.Receive())); | |
184 } | |
185 | |
186 } // anonymous namespace | |
187 | |
188 // static | |
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_; | |
193 | |
194 // static | |
195 NativeViewAccessibility* NativeViewAccessibility::Create(View* view) { | |
196 // Make sure ATL is initialized in this module. | |
197 ui::win::CreateATLModuleIfNeeded(); | |
198 | |
199 CComObject<NativeViewAccessibilityWin>* instance = NULL; | |
200 HRESULT hr = CComObject<NativeViewAccessibilityWin>::CreateInstance( | |
201 &instance); | |
202 DCHECK(SUCCEEDED(hr)); | |
203 instance->set_view(view); | |
204 instance->AddRef(); | |
205 return instance; | |
206 } | |
207 | |
208 NativeViewAccessibilityWin::NativeViewAccessibilityWin() | |
209 : unique_id_(next_unique_id_++) { | |
210 } | |
211 | |
212 NativeViewAccessibilityWin::~NativeViewAccessibilityWin() { | |
213 RemoveAlertTarget(); | |
214 } | |
215 | |
216 void NativeViewAccessibilityWin::NotifyAccessibilityEvent( | |
217 ui::AXEvent event_type) { | |
218 if (!view_) | |
219 return; | |
220 | |
221 ViewStorage* view_storage = ViewStorage::GetInstance(); | |
222 HWND hwnd = HWNDForView(view_); | |
223 int view_storage_id = view_storage_ids_[next_view_storage_id_index_]; | |
224 if (view_storage_id == 0) { | |
225 view_storage_id = view_storage->CreateStorageID(); | |
226 view_storage_ids_[next_view_storage_id_index_] = view_storage_id; | |
227 } else { | |
228 view_storage->RemoveView(view_storage_id); | |
229 } | |
230 view_storage->StoreView(view_storage_id, view_); | |
231 | |
232 // Positive child ids are used for enumerating direct children, | |
233 // negative child ids can be used as unique ids to refer to a specific | |
234 // descendants. Make index into view_storage_ids_ into a negative child id. | |
235 int child_id = | |
236 base::win::kFirstViewsAccessibilityId - next_view_storage_id_index_; | |
237 ::NotifyWinEvent(MSAAEvent(event_type), hwnd, OBJID_CLIENT, child_id); | |
238 next_view_storage_id_index_ = | |
239 (next_view_storage_id_index_ + 1) % kMaxViewStorageIds; | |
240 | |
241 // Keep track of views that are a target of an alert event. | |
242 if (event_type == ui::AX_EVENT_ALERT) | |
243 AddAlertTarget(); | |
244 } | |
245 | |
246 gfx::NativeViewAccessible NativeViewAccessibilityWin::GetNativeObject() { | |
247 return this; | |
248 } | |
249 | |
250 void NativeViewAccessibilityWin::Destroy() { | |
251 view_ = NULL; | |
252 Release(); | |
253 } | |
254 | |
255 STDMETHODIMP NativeViewAccessibilityWin::accHitTest( | |
256 LONG x_left, LONG y_top, VARIANT* child) { | |
257 if (!child) | |
258 return E_INVALIDARG; | |
259 | |
260 if (!view_ || !view_->GetWidget()) | |
261 return E_FAIL; | |
262 | |
263 // If this is a root view, our widget might have child widgets. | |
264 // Search child widgets first, since they're on top in the z-order. | |
265 if (view_->GetWidget()->GetRootView() == view_) { | |
266 std::vector<Widget*> child_widgets; | |
267 PopulateChildWidgetVector(&child_widgets); | |
268 for (size_t i = 0; i < child_widgets.size(); ++i) { | |
269 Widget* child_widget = child_widgets[i]; | |
270 IAccessible* child_accessible = | |
271 child_widget->GetRootView()->GetNativeViewAccessible(); | |
272 HRESULT result = child_accessible->accHitTest(x_left, y_top, child); | |
273 if (result == S_OK) | |
274 return result; | |
275 } | |
276 } | |
277 | |
278 gfx::Point point(x_left, y_top); | |
279 View::ConvertPointFromScreen(view_, &point); | |
280 | |
281 // If the point is not inside this view, return false. | |
282 if (!view_->HitTestPoint(point)) { | |
283 child->vt = VT_EMPTY; | |
284 return S_FALSE; | |
285 } | |
286 | |
287 // Check if the point is within any of the immediate children of this | |
288 // view. | |
289 View* hit_child_view = NULL; | |
290 for (int i = view_->child_count() - 1; i >= 0; --i) { | |
291 View* child_view = view_->child_at(i); | |
292 if (!child_view->visible()) | |
293 continue; | |
294 | |
295 gfx::Point point_in_child_coords(point); | |
296 view_->ConvertPointToTarget(view_, child_view, &point_in_child_coords); | |
297 if (child_view->HitTestPoint(point_in_child_coords)) { | |
298 hit_child_view = child_view; | |
299 break; | |
300 } | |
301 } | |
302 | |
303 // If the point was within one of this view's immediate children, | |
304 // call accHitTest recursively on that child's native view accessible - | |
305 // which may be a recursive call to this function or it may be overridden, | |
306 // for example in the case of a WebView. | |
307 if (hit_child_view) { | |
308 HRESULT result = hit_child_view->GetNativeViewAccessible()->accHitTest( | |
309 x_left, y_top, child); | |
310 | |
311 // If the recursive call returned CHILDID_SELF, we have to convert that | |
312 // into a VT_DISPATCH for the return value to this call. | |
313 if (S_OK == result && child->vt == VT_I4 && child->lVal == CHILDID_SELF) { | |
314 child->vt = VT_DISPATCH; | |
315 child->pdispVal = hit_child_view->GetNativeViewAccessible(); | |
316 // Always increment ref when returning a reference to a COM object. | |
317 child->pdispVal->AddRef(); | |
318 } | |
319 return result; | |
320 } | |
321 | |
322 // This object is the best match, so return CHILDID_SELF. It's tempting to | |
323 // simplify the logic and use VT_DISPATCH everywhere, but the Windows | |
324 // call AccessibleObjectFromPoint will keep calling accHitTest until some | |
325 // object returns CHILDID_SELF. | |
326 child->vt = VT_I4; | |
327 child->lVal = CHILDID_SELF; | |
328 return S_OK; | |
329 } | |
330 | |
331 HRESULT NativeViewAccessibilityWin::accDoDefaultAction(VARIANT var_id) { | |
332 if (!IsValidId(var_id)) | |
333 return E_INVALIDARG; | |
334 | |
335 // The object does not support the method. This value is returned for | |
336 // controls that do not perform actions, such as edit fields. | |
337 return DISP_E_MEMBERNOTFOUND; | |
338 } | |
339 | |
340 STDMETHODIMP NativeViewAccessibilityWin::accLocation( | |
341 LONG* x_left, LONG* y_top, LONG* width, LONG* height, VARIANT var_id) { | |
342 if (!IsValidId(var_id) || !x_left || !y_top || !width || !height) | |
343 return E_INVALIDARG; | |
344 | |
345 if (!view_) | |
346 return E_FAIL; | |
347 | |
348 if (!view_->bounds().IsEmpty()) { | |
349 *width = view_->width(); | |
350 *height = view_->height(); | |
351 gfx::Point topleft(view_->bounds().origin()); | |
352 View::ConvertPointToScreen( | |
353 view_->parent() ? view_->parent() : view_, &topleft); | |
354 *x_left = topleft.x(); | |
355 *y_top = topleft.y(); | |
356 } else { | |
357 return E_FAIL; | |
358 } | |
359 return S_OK; | |
360 } | |
361 | |
362 STDMETHODIMP NativeViewAccessibilityWin::accNavigate( | |
363 LONG nav_dir, VARIANT start, VARIANT* end) { | |
364 if (start.vt != VT_I4 || !end) | |
365 return E_INVALIDARG; | |
366 | |
367 if (!view_) | |
368 return E_FAIL; | |
369 | |
370 switch (nav_dir) { | |
371 case NAVDIR_FIRSTCHILD: | |
372 case NAVDIR_LASTCHILD: { | |
373 if (start.lVal != CHILDID_SELF) { | |
374 // Start of navigation must be on the View itself. | |
375 return E_INVALIDARG; | |
376 } else if (!view_->has_children()) { | |
377 // No children found. | |
378 return S_FALSE; | |
379 } | |
380 | |
381 // Set child_id based on first or last child. | |
382 int child_id = 0; | |
383 if (nav_dir == NAVDIR_LASTCHILD) | |
384 child_id = view_->child_count() - 1; | |
385 | |
386 View* child = view_->child_at(child_id); | |
387 end->vt = VT_DISPATCH; | |
388 end->pdispVal = child->GetNativeViewAccessible(); | |
389 end->pdispVal->AddRef(); | |
390 return S_OK; | |
391 } | |
392 case NAVDIR_LEFT: | |
393 case NAVDIR_UP: | |
394 case NAVDIR_PREVIOUS: | |
395 case NAVDIR_RIGHT: | |
396 case NAVDIR_DOWN: | |
397 case NAVDIR_NEXT: { | |
398 // Retrieve parent to access view index and perform bounds checking. | |
399 View* parent = view_->parent(); | |
400 if (!parent) { | |
401 return E_FAIL; | |
402 } | |
403 | |
404 if (start.lVal == CHILDID_SELF) { | |
405 int view_index = parent->GetIndexOf(view_); | |
406 // Check navigation bounds, adjusting for View child indexing (MSAA | |
407 // child indexing starts with 1, whereas View indexing starts with 0). | |
408 if (!IsValidNav(nav_dir, view_index, -1, | |
409 parent->child_count() - 1)) { | |
410 // Navigation attempted to go out-of-bounds. | |
411 end->vt = VT_EMPTY; | |
412 return S_FALSE; | |
413 } else { | |
414 if (IsNavDirNext(nav_dir)) { | |
415 view_index += 1; | |
416 } else { | |
417 view_index -=1; | |
418 } | |
419 } | |
420 | |
421 View* child = parent->child_at(view_index); | |
422 end->pdispVal = child->GetNativeViewAccessible(); | |
423 end->vt = VT_DISPATCH; | |
424 end->pdispVal->AddRef(); | |
425 return S_OK; | |
426 } else { | |
427 // Check navigation bounds, adjusting for MSAA child indexing (MSAA | |
428 // child indexing starts with 1, whereas View indexing starts with 0). | |
429 if (!IsValidNav(nav_dir, start.lVal, 0, parent->child_count() + 1)) { | |
430 // Navigation attempted to go out-of-bounds. | |
431 end->vt = VT_EMPTY; | |
432 return S_FALSE; | |
433 } else { | |
434 if (IsNavDirNext(nav_dir)) { | |
435 start.lVal += 1; | |
436 } else { | |
437 start.lVal -= 1; | |
438 } | |
439 } | |
440 | |
441 HRESULT result = this->get_accChild(start, &end->pdispVal); | |
442 if (result == S_FALSE) { | |
443 // Child is a leaf. | |
444 end->vt = VT_I4; | |
445 end->lVal = start.lVal; | |
446 } else if (result == E_INVALIDARG) { | |
447 return E_INVALIDARG; | |
448 } else { | |
449 // Child is not a leaf. | |
450 end->vt = VT_DISPATCH; | |
451 } | |
452 } | |
453 break; | |
454 } | |
455 default: | |
456 return E_INVALIDARG; | |
457 } | |
458 // Navigation performed correctly. Global return for this function, if no | |
459 // error triggered an escape earlier. | |
460 return S_OK; | |
461 } | |
462 | |
463 STDMETHODIMP NativeViewAccessibilityWin::get_accChild(VARIANT var_child, | |
464 IDispatch** disp_child) { | |
465 if (var_child.vt != VT_I4 || !disp_child) | |
466 return E_INVALIDARG; | |
467 | |
468 if (!view_ || !view_->GetWidget()) | |
469 return E_FAIL; | |
470 | |
471 LONG child_id = V_I4(&var_child); | |
472 | |
473 if (child_id == CHILDID_SELF) { | |
474 // Remain with the same dispatch. | |
475 return S_OK; | |
476 } | |
477 | |
478 // If this is a root view, our widget might have child widgets. Include | |
479 std::vector<Widget*> child_widgets; | |
480 if (view_->GetWidget()->GetRootView() == view_) | |
481 PopulateChildWidgetVector(&child_widgets); | |
482 int child_widget_count = static_cast<int>(child_widgets.size()); | |
483 | |
484 View* child_view = NULL; | |
485 if (child_id > 0) { | |
486 // Positive child ids are a 1-based child index, used by clients | |
487 // that want to enumerate all immediate children. | |
488 int child_id_as_index = child_id - 1; | |
489 if (child_id_as_index < view_->child_count()) { | |
490 child_view = view_->child_at(child_id_as_index); | |
491 } else if (child_id_as_index < view_->child_count() + child_widget_count) { | |
492 Widget* child_widget = | |
493 child_widgets[child_id_as_index - view_->child_count()]; | |
494 child_view = child_widget->GetRootView(); | |
495 } | |
496 } else { | |
497 // Negative child ids can be used to map to any descendant. | |
498 // Check child widget first. | |
499 for (int i = 0; i < child_widget_count; i++) { | |
500 Widget* child_widget = child_widgets[i]; | |
501 IAccessible* child_accessible = | |
502 child_widget->GetRootView()->GetNativeViewAccessible(); | |
503 HRESULT result = child_accessible->get_accChild(var_child, disp_child); | |
504 if (result == S_OK) | |
505 return result; | |
506 } | |
507 | |
508 // We map child ids to a view storage id that can refer to a | |
509 // specific view (if that view still exists). | |
510 int view_storage_id_index = | |
511 base::win::kFirstViewsAccessibilityId - child_id; | |
512 if (view_storage_id_index >= 0 && | |
513 view_storage_id_index < kMaxViewStorageIds) { | |
514 int view_storage_id = view_storage_ids_[view_storage_id_index]; | |
515 ViewStorage* view_storage = ViewStorage::GetInstance(); | |
516 child_view = view_storage->RetrieveView(view_storage_id); | |
517 } else { | |
518 *disp_child = AccessibleWebViewRegistry::GetInstance()-> | |
519 GetAccessibleFromWebView(view_, child_id); | |
520 if (*disp_child) | |
521 return S_OK; | |
522 } | |
523 } | |
524 | |
525 if (!child_view) { | |
526 // No child found. | |
527 *disp_child = NULL; | |
528 return E_FAIL; | |
529 } | |
530 | |
531 *disp_child = child_view->GetNativeViewAccessible(); | |
532 if (*disp_child) { | |
533 (*disp_child)->AddRef(); | |
534 return S_OK; | |
535 } | |
536 | |
537 return E_FAIL; | |
538 } | |
539 | |
540 STDMETHODIMP NativeViewAccessibilityWin::get_accChildCount(LONG* child_count) { | |
541 if (!child_count) | |
542 return E_INVALIDARG; | |
543 | |
544 if (!view_ || !view_->GetWidget()) | |
545 return E_FAIL; | |
546 | |
547 *child_count = view_->child_count(); | |
548 | |
549 // If this is a root view, our widget might have child widgets. Include | |
550 // them, too. | |
551 if (view_->GetWidget()->GetRootView() == view_) { | |
552 std::vector<Widget*> child_widgets; | |
553 PopulateChildWidgetVector(&child_widgets); | |
554 *child_count += child_widgets.size(); | |
555 } | |
556 | |
557 return S_OK; | |
558 } | |
559 | |
560 STDMETHODIMP NativeViewAccessibilityWin::get_accDefaultAction( | |
561 VARIANT var_id, BSTR* def_action) { | |
562 if (!IsValidId(var_id) || !def_action) | |
563 return E_INVALIDARG; | |
564 | |
565 if (!view_) | |
566 return E_FAIL; | |
567 | |
568 ui::AXViewState state; | |
569 view_->GetAccessibleState(&state); | |
570 base::string16 temp_action = state.default_action; | |
571 | |
572 if (!temp_action.empty()) { | |
573 *def_action = SysAllocString(temp_action.c_str()); | |
574 } else { | |
575 return S_FALSE; | |
576 } | |
577 | |
578 return S_OK; | |
579 } | |
580 | |
581 STDMETHODIMP NativeViewAccessibilityWin::get_accDescription( | |
582 VARIANT var_id, BSTR* desc) { | |
583 if (!IsValidId(var_id) || !desc) | |
584 return E_INVALIDARG; | |
585 | |
586 if (!view_) | |
587 return E_FAIL; | |
588 | |
589 base::string16 temp_desc; | |
590 | |
591 view_->GetTooltipText(gfx::Point(), &temp_desc); | |
592 if (!temp_desc.empty()) { | |
593 *desc = SysAllocString(temp_desc.c_str()); | |
594 } else { | |
595 return S_FALSE; | |
596 } | |
597 | |
598 return S_OK; | |
599 } | |
600 | |
601 STDMETHODIMP NativeViewAccessibilityWin::get_accFocus(VARIANT* focus_child) { | |
602 if (!focus_child) | |
603 return E_INVALIDARG; | |
604 | |
605 if (!view_) | |
606 return E_FAIL; | |
607 | |
608 FocusManager* focus_manager = view_->GetFocusManager(); | |
609 View* focus = focus_manager ? focus_manager->GetFocusedView() : NULL; | |
610 if (focus == view_) { | |
611 // This view has focus. | |
612 focus_child->vt = VT_I4; | |
613 focus_child->lVal = CHILDID_SELF; | |
614 } else if (focus && view_->Contains(focus)) { | |
615 // Return the child object that has the keyboard focus. | |
616 focus_child->vt = VT_DISPATCH; | |
617 focus_child->pdispVal = focus->GetNativeViewAccessible(); | |
618 focus_child->pdispVal->AddRef(); | |
619 return S_OK; | |
620 } else { | |
621 // Neither this object nor any of its children has the keyboard focus. | |
622 focus_child->vt = VT_EMPTY; | |
623 } | |
624 return S_OK; | |
625 } | |
626 | |
627 STDMETHODIMP NativeViewAccessibilityWin::get_accKeyboardShortcut( | |
628 VARIANT var_id, BSTR* acc_key) { | |
629 if (!IsValidId(var_id) || !acc_key) | |
630 return E_INVALIDARG; | |
631 | |
632 if (!view_) | |
633 return E_FAIL; | |
634 | |
635 ui::AXViewState state; | |
636 view_->GetAccessibleState(&state); | |
637 base::string16 temp_key = state.keyboard_shortcut; | |
638 | |
639 if (!temp_key.empty()) { | |
640 *acc_key = SysAllocString(temp_key.c_str()); | |
641 } else { | |
642 return S_FALSE; | |
643 } | |
644 | |
645 return S_OK; | |
646 } | |
647 | |
648 STDMETHODIMP NativeViewAccessibilityWin::get_accName( | |
649 VARIANT var_id, BSTR* name) { | |
650 if (!IsValidId(var_id) || !name) | |
651 return E_INVALIDARG; | |
652 | |
653 if (!view_) | |
654 return E_FAIL; | |
655 | |
656 // Retrieve the current view's name. | |
657 ui::AXViewState state; | |
658 view_->GetAccessibleState(&state); | |
659 base::string16 temp_name = state.name; | |
660 if (!temp_name.empty()) { | |
661 // Return name retrieved. | |
662 *name = SysAllocString(temp_name.c_str()); | |
663 } else { | |
664 // If view has no name, return S_FALSE. | |
665 return S_FALSE; | |
666 } | |
667 | |
668 return S_OK; | |
669 } | |
670 | |
671 STDMETHODIMP NativeViewAccessibilityWin::get_accParent( | |
672 IDispatch** disp_parent) { | |
673 if (!disp_parent) | |
674 return E_INVALIDARG; | |
675 | |
676 if (!view_) | |
677 return E_FAIL; | |
678 | |
679 *disp_parent = NULL; | |
680 View* parent_view = view_->parent(); | |
681 | |
682 if (!parent_view) { | |
683 HWND hwnd = HWNDForView(view_); | |
684 if (!hwnd) | |
685 return S_FALSE; | |
686 | |
687 return ::AccessibleObjectFromWindow( | |
688 hwnd, OBJID_WINDOW, IID_IAccessible, | |
689 reinterpret_cast<void**>(disp_parent)); | |
690 } | |
691 | |
692 *disp_parent = parent_view->GetNativeViewAccessible(); | |
693 (*disp_parent)->AddRef(); | |
694 return S_OK; | |
695 } | |
696 | |
697 STDMETHODIMP NativeViewAccessibilityWin::get_accRole( | |
698 VARIANT var_id, VARIANT* role) { | |
699 if (!IsValidId(var_id) || !role) | |
700 return E_INVALIDARG; | |
701 | |
702 if (!view_) | |
703 return E_FAIL; | |
704 | |
705 ui::AXViewState state; | |
706 view_->GetAccessibleState(&state); | |
707 role->vt = VT_I4; | |
708 role->lVal = MSAARole(state.role); | |
709 return S_OK; | |
710 } | |
711 | |
712 STDMETHODIMP NativeViewAccessibilityWin::get_accState( | |
713 VARIANT var_id, VARIANT* state) { | |
714 // This returns MSAA states. See also the IAccessible2 interface | |
715 // get_states(). | |
716 | |
717 if (!IsValidId(var_id) || !state) | |
718 return E_INVALIDARG; | |
719 | |
720 if (!view_) | |
721 return E_FAIL; | |
722 | |
723 state->vt = VT_I4; | |
724 | |
725 // Retrieve all currently applicable states of the parent. | |
726 SetState(state, view_); | |
727 | |
728 // Make sure that state is not empty, and has the proper type. | |
729 if (state->vt == VT_EMPTY) | |
730 return E_FAIL; | |
731 | |
732 return S_OK; | |
733 } | |
734 | |
735 STDMETHODIMP NativeViewAccessibilityWin::get_accValue(VARIANT var_id, | |
736 BSTR* value) { | |
737 if (!IsValidId(var_id) || !value) | |
738 return E_INVALIDARG; | |
739 | |
740 if (!view_) | |
741 return E_FAIL; | |
742 | |
743 // Retrieve the current view's value. | |
744 ui::AXViewState state; | |
745 view_->GetAccessibleState(&state); | |
746 base::string16 temp_value = state.value; | |
747 | |
748 if (!temp_value.empty()) { | |
749 // Return value retrieved. | |
750 *value = SysAllocString(temp_value.c_str()); | |
751 } else { | |
752 // If view has no value, fall back into the default implementation. | |
753 *value = NULL; | |
754 return E_NOTIMPL; | |
755 } | |
756 | |
757 return S_OK; | |
758 } | |
759 | |
760 STDMETHODIMP NativeViewAccessibilityWin::put_accValue(VARIANT var_id, | |
761 BSTR new_value) { | |
762 if (!IsValidId(var_id) || !new_value) | |
763 return E_INVALIDARG; | |
764 | |
765 if (!view_) | |
766 return E_FAIL; | |
767 | |
768 // Return an error if the view can't set the value. | |
769 ui::AXViewState state; | |
770 view_->GetAccessibleState(&state); | |
771 if (state.set_value_callback.is_null()) | |
772 return E_FAIL; | |
773 | |
774 state.set_value_callback.Run(new_value); | |
775 return S_OK; | |
776 } | |
777 | |
778 // IAccessible functions not supported. | |
779 | |
780 STDMETHODIMP NativeViewAccessibilityWin::get_accSelection(VARIANT* selected) { | |
781 if (selected) | |
782 selected->vt = VT_EMPTY; | |
783 return E_NOTIMPL; | |
784 } | |
785 | |
786 STDMETHODIMP NativeViewAccessibilityWin::accSelect( | |
787 LONG flagsSelect, VARIANT var_id) { | |
788 return E_NOTIMPL; | |
789 } | |
790 | |
791 STDMETHODIMP NativeViewAccessibilityWin::get_accHelp( | |
792 VARIANT var_id, BSTR* help) { | |
793 if (!IsValidId(var_id) || !help) | |
794 return E_INVALIDARG; | |
795 | |
796 if (!view_) | |
797 return E_FAIL; | |
798 | |
799 base::string16 temp = base::UTF8ToUTF16(view_->GetClassName()); | |
800 *help = SysAllocString(temp.c_str()); | |
801 return S_OK; | |
802 } | |
803 | |
804 STDMETHODIMP NativeViewAccessibilityWin::get_accHelpTopic( | |
805 BSTR* help_file, VARIANT var_id, LONG* topic_id) { | |
806 if (help_file) { | |
807 *help_file = NULL; | |
808 } | |
809 if (topic_id) { | |
810 *topic_id = static_cast<LONG>(-1); | |
811 } | |
812 return E_NOTIMPL; | |
813 } | |
814 | |
815 STDMETHODIMP NativeViewAccessibilityWin::put_accName( | |
816 VARIANT var_id, BSTR put_name) { | |
817 // Deprecated. | |
818 return E_NOTIMPL; | |
819 } | |
820 | |
821 // | |
822 // IAccessible2 | |
823 // | |
824 | |
825 STDMETHODIMP NativeViewAccessibilityWin::role(LONG* role) { | |
826 if (!view_) | |
827 return E_FAIL; | |
828 | |
829 if (!role) | |
830 return E_INVALIDARG; | |
831 | |
832 ui::AXViewState state; | |
833 view_->GetAccessibleState(&state); | |
834 *role = MSAARole(state.role); | |
835 return S_OK; | |
836 } | |
837 | |
838 STDMETHODIMP NativeViewAccessibilityWin::get_states(AccessibleStates* states) { | |
839 // This returns IAccessible2 states, which supplement MSAA states. | |
840 // See also the MSAA interface get_accState. | |
841 | |
842 if (!view_) | |
843 return E_FAIL; | |
844 | |
845 if (!states) | |
846 return E_INVALIDARG; | |
847 | |
848 ui::AXViewState state; | |
849 view_->GetAccessibleState(&state); | |
850 | |
851 // There are only a couple of states we need to support | |
852 // in IAccessible2. If any more are added, we may want to | |
853 // add a helper function like MSAAState. | |
854 *states = IA2_STATE_OPAQUE; | |
855 if (state.HasStateFlag(ui::AX_STATE_EDITABLE)) | |
856 *states |= IA2_STATE_EDITABLE; | |
857 | |
858 return S_OK; | |
859 } | |
860 | |
861 STDMETHODIMP NativeViewAccessibilityWin::get_uniqueID(LONG* unique_id) { | |
862 if (!view_) | |
863 return E_FAIL; | |
864 | |
865 if (!unique_id) | |
866 return E_INVALIDARG; | |
867 | |
868 *unique_id = unique_id_; | |
869 return S_OK; | |
870 } | |
871 | |
872 STDMETHODIMP NativeViewAccessibilityWin::get_windowHandle(HWND* window_handle) { | |
873 if (!view_) | |
874 return E_FAIL; | |
875 | |
876 if (!window_handle) | |
877 return E_INVALIDARG; | |
878 | |
879 *window_handle = HWNDForView(view_); | |
880 return *window_handle ? S_OK : S_FALSE; | |
881 } | |
882 | |
883 STDMETHODIMP NativeViewAccessibilityWin::get_relationTargetsOfType( | |
884 BSTR type_bstr, | |
885 long max_targets, | |
886 IUnknown ***targets, | |
887 long *n_targets) { | |
888 if (!view_) | |
889 return E_FAIL; | |
890 | |
891 if (!targets || !n_targets) | |
892 return E_INVALIDARG; | |
893 | |
894 *n_targets = 0; | |
895 *targets = NULL; | |
896 | |
897 // Only respond to requests for relations of type "alerts" on the | |
898 // root view. | |
899 base::string16 type(type_bstr); | |
900 if (type != L"alerts" || view_->parent()) | |
901 return S_FALSE; | |
902 | |
903 // Collect all of the alert views that are still valid. | |
904 std::vector<View*> alert_views; | |
905 ViewStorage* view_storage = ViewStorage::GetInstance(); | |
906 for (size_t i = 0; i < alert_target_view_storage_ids_.size(); ++i) { | |
907 int view_storage_id = alert_target_view_storage_ids_[i]; | |
908 View* view = view_storage->RetrieveView(view_storage_id); | |
909 if (!view || !view_->Contains(view)) | |
910 continue; | |
911 alert_views.push_back(view); | |
912 } | |
913 | |
914 long count = alert_views.size(); | |
915 if (count == 0) | |
916 return S_FALSE; | |
917 | |
918 // Don't return more targets than max_targets - but note that the caller | |
919 // is allowed to specify max_targets=0 to mean no limit. | |
920 if (max_targets > 0 && count > max_targets) | |
921 count = max_targets; | |
922 | |
923 // Return the number of targets. | |
924 *n_targets = count; | |
925 | |
926 // Allocate COM memory for the result array and populate it. | |
927 *targets = static_cast<IUnknown**>( | |
928 CoTaskMemAlloc(count * sizeof(IUnknown*))); | |
929 for (long i = 0; i < count; ++i) { | |
930 (*targets)[i] = alert_views[i]->GetNativeViewAccessible(); | |
931 (*targets)[i]->AddRef(); | |
932 } | |
933 return S_OK; | |
934 } | |
935 | |
936 STDMETHODIMP NativeViewAccessibilityWin::get_attributes(BSTR* attributes) { | |
937 if (!view_) | |
938 return E_FAIL; | |
939 | |
940 if (!attributes) | |
941 return E_INVALIDARG; | |
942 | |
943 base::string16 attributes_str; | |
944 | |
945 // Text fields need to report the attribute "text-model:a1" to instruct | |
946 // screen readers to use IAccessible2 APIs to handle text editing in this | |
947 // object (as opposed to treating it like a native Windows text box). | |
948 // The text-model:a1 attribute is documented here: | |
949 // http://www.linuxfoundation.org/collaborate/workgroups/accessibility/ia2/ia2
_implementation_guide | |
950 ui::AXViewState state; | |
951 view_->GetAccessibleState(&state); | |
952 if (state.role == ui::AX_ROLE_TEXT_FIELD) { | |
953 attributes_str = L"text-model:a1;"; | |
954 } | |
955 | |
956 *attributes = SysAllocString(attributes_str.c_str()); | |
957 DCHECK(*attributes); | |
958 return S_OK; | |
959 } | |
960 | |
961 // | |
962 // IAccessibleText | |
963 // | |
964 | |
965 STDMETHODIMP NativeViewAccessibilityWin::get_nCharacters(LONG* n_characters) { | |
966 if (!view_) | |
967 return E_FAIL; | |
968 | |
969 if (!n_characters) | |
970 return E_INVALIDARG; | |
971 | |
972 base::string16 text = TextForIAccessibleText(); | |
973 *n_characters = static_cast<LONG>(text.size()); | |
974 return S_OK; | |
975 } | |
976 | |
977 STDMETHODIMP NativeViewAccessibilityWin::get_caretOffset(LONG* offset) { | |
978 if (!view_) | |
979 return E_FAIL; | |
980 | |
981 if (!offset) | |
982 return E_INVALIDARG; | |
983 | |
984 ui::AXViewState state; | |
985 view_->GetAccessibleState(&state); | |
986 *offset = static_cast<LONG>(state.selection_end); | |
987 return S_OK; | |
988 } | |
989 | |
990 STDMETHODIMP NativeViewAccessibilityWin::get_nSelections(LONG* n_selections) { | |
991 if (!view_) | |
992 return E_FAIL; | |
993 | |
994 if (!n_selections) | |
995 return E_INVALIDARG; | |
996 | |
997 ui::AXViewState state; | |
998 view_->GetAccessibleState(&state); | |
999 if (state.selection_start != state.selection_end) | |
1000 *n_selections = 1; | |
1001 else | |
1002 *n_selections = 0; | |
1003 return S_OK; | |
1004 } | |
1005 | |
1006 STDMETHODIMP NativeViewAccessibilityWin::get_selection(LONG selection_index, | |
1007 LONG* start_offset, | |
1008 LONG* end_offset) { | |
1009 if (!view_) | |
1010 return E_FAIL; | |
1011 | |
1012 if (!start_offset || !end_offset || selection_index != 0) | |
1013 return E_INVALIDARG; | |
1014 | |
1015 ui::AXViewState state; | |
1016 view_->GetAccessibleState(&state); | |
1017 *start_offset = static_cast<LONG>(state.selection_start); | |
1018 *end_offset = static_cast<LONG>(state.selection_end); | |
1019 return S_OK; | |
1020 } | |
1021 | |
1022 STDMETHODIMP NativeViewAccessibilityWin::get_text(LONG start_offset, | |
1023 LONG end_offset, | |
1024 BSTR* text) { | |
1025 if (!view_) | |
1026 return E_FAIL; | |
1027 | |
1028 ui::AXViewState state; | |
1029 view_->GetAccessibleState(&state); | |
1030 base::string16 text_str = TextForIAccessibleText(); | |
1031 LONG len = static_cast<LONG>(text_str.size()); | |
1032 | |
1033 if (start_offset == IA2_TEXT_OFFSET_LENGTH) { | |
1034 start_offset = len; | |
1035 } else if (start_offset == IA2_TEXT_OFFSET_CARET) { | |
1036 start_offset = static_cast<LONG>(state.selection_end); | |
1037 } | |
1038 if (end_offset == IA2_TEXT_OFFSET_LENGTH) { | |
1039 end_offset = static_cast<LONG>(text_str.size()); | |
1040 } else if (end_offset == IA2_TEXT_OFFSET_CARET) { | |
1041 end_offset = static_cast<LONG>(state.selection_end); | |
1042 } | |
1043 | |
1044 // The spec allows the arguments to be reversed. | |
1045 if (start_offset > end_offset) { | |
1046 LONG tmp = start_offset; | |
1047 start_offset = end_offset; | |
1048 end_offset = tmp; | |
1049 } | |
1050 | |
1051 // The spec does not allow the start or end offsets to be out or range; | |
1052 // we must return an error if so. | |
1053 if (start_offset < 0) | |
1054 return E_INVALIDARG; | |
1055 if (end_offset > len) | |
1056 return E_INVALIDARG; | |
1057 | |
1058 base::string16 substr = | |
1059 text_str.substr(start_offset, end_offset - start_offset); | |
1060 if (substr.empty()) | |
1061 return S_FALSE; | |
1062 | |
1063 *text = SysAllocString(substr.c_str()); | |
1064 DCHECK(*text); | |
1065 return S_OK; | |
1066 } | |
1067 | |
1068 STDMETHODIMP NativeViewAccessibilityWin::get_textAtOffset( | |
1069 LONG offset, | |
1070 enum IA2TextBoundaryType boundary_type, | |
1071 LONG* start_offset, LONG* end_offset, | |
1072 BSTR* text) { | |
1073 if (!start_offset || !end_offset || !text) | |
1074 return E_INVALIDARG; | |
1075 | |
1076 // The IAccessible2 spec says we don't have to implement the "sentence" | |
1077 // boundary type, we can just let the screenreader handle it. | |
1078 if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) { | |
1079 *start_offset = 0; | |
1080 *end_offset = 0; | |
1081 *text = NULL; | |
1082 return S_FALSE; | |
1083 } | |
1084 | |
1085 const base::string16& text_str = TextForIAccessibleText(); | |
1086 | |
1087 *start_offset = FindBoundary( | |
1088 text_str, boundary_type, offset, ui::BACKWARDS_DIRECTION); | |
1089 *end_offset = FindBoundary( | |
1090 text_str, boundary_type, offset, ui::FORWARDS_DIRECTION); | |
1091 return get_text(*start_offset, *end_offset, text); | |
1092 } | |
1093 | |
1094 STDMETHODIMP NativeViewAccessibilityWin::get_textBeforeOffset( | |
1095 LONG offset, | |
1096 enum IA2TextBoundaryType boundary_type, | |
1097 LONG* start_offset, LONG* end_offset, | |
1098 BSTR* text) { | |
1099 if (!start_offset || !end_offset || !text) | |
1100 return E_INVALIDARG; | |
1101 | |
1102 // The IAccessible2 spec says we don't have to implement the "sentence" | |
1103 // boundary type, we can just let the screenreader handle it. | |
1104 if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) { | |
1105 *start_offset = 0; | |
1106 *end_offset = 0; | |
1107 *text = NULL; | |
1108 return S_FALSE; | |
1109 } | |
1110 | |
1111 const base::string16& text_str = TextForIAccessibleText(); | |
1112 | |
1113 *start_offset = FindBoundary( | |
1114 text_str, boundary_type, offset, ui::BACKWARDS_DIRECTION); | |
1115 *end_offset = offset; | |
1116 return get_text(*start_offset, *end_offset, text); | |
1117 } | |
1118 | |
1119 STDMETHODIMP NativeViewAccessibilityWin::get_textAfterOffset( | |
1120 LONG offset, | |
1121 enum IA2TextBoundaryType boundary_type, | |
1122 LONG* start_offset, LONG* end_offset, | |
1123 BSTR* text) { | |
1124 if (!start_offset || !end_offset || !text) | |
1125 return E_INVALIDARG; | |
1126 | |
1127 // The IAccessible2 spec says we don't have to implement the "sentence" | |
1128 // boundary type, we can just let the screenreader handle it. | |
1129 if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) { | |
1130 *start_offset = 0; | |
1131 *end_offset = 0; | |
1132 *text = NULL; | |
1133 return S_FALSE; | |
1134 } | |
1135 | |
1136 const base::string16& text_str = TextForIAccessibleText(); | |
1137 | |
1138 *start_offset = offset; | |
1139 *end_offset = FindBoundary( | |
1140 text_str, boundary_type, offset, ui::FORWARDS_DIRECTION); | |
1141 return get_text(*start_offset, *end_offset, text); | |
1142 } | |
1143 | |
1144 STDMETHODIMP NativeViewAccessibilityWin::get_offsetAtPoint( | |
1145 LONG x, LONG y, enum IA2CoordinateType coord_type, LONG* offset) { | |
1146 if (!view_) | |
1147 return E_FAIL; | |
1148 | |
1149 if (!offset) | |
1150 return E_INVALIDARG; | |
1151 | |
1152 // We don't support this method, but we have to return something | |
1153 // rather than E_NOTIMPL or screen readers will complain. | |
1154 *offset = 0; | |
1155 return S_OK; | |
1156 } | |
1157 | |
1158 // | |
1159 // IServiceProvider methods. | |
1160 // | |
1161 | |
1162 STDMETHODIMP NativeViewAccessibilityWin::QueryService( | |
1163 REFGUID guidService, REFIID riid, void** object) { | |
1164 if (!view_) | |
1165 return E_FAIL; | |
1166 | |
1167 if (riid == IID_IAccessible2 || riid == IID_IAccessible2_2) | |
1168 AccessibleWebViewRegistry::GetInstance()->EnableIAccessible2Support(); | |
1169 | |
1170 if (guidService == IID_IAccessible || | |
1171 guidService == IID_IAccessible2 || | |
1172 guidService == IID_IAccessible2_2 || | |
1173 guidService == IID_IAccessibleText) { | |
1174 return QueryInterface(riid, object); | |
1175 } | |
1176 | |
1177 // We only support the IAccessibleEx interface on Windows 8 and above. This | |
1178 // is needed for the On screen Keyboard to show up in metro mode, when the | |
1179 // user taps an editable region in the window. | |
1180 // All methods in the IAccessibleEx interface are unimplemented. | |
1181 if (riid == IID_IAccessibleEx && | |
1182 base::win::GetVersion() >= base::win::VERSION_WIN8) { | |
1183 return QueryInterface(riid, object); | |
1184 } | |
1185 | |
1186 *object = NULL; | |
1187 return E_FAIL; | |
1188 } | |
1189 | |
1190 STDMETHODIMP NativeViewAccessibilityWin::GetPatternProvider( | |
1191 PATTERNID id, IUnknown** provider) { | |
1192 if (!view_) | |
1193 return E_FAIL; | |
1194 | |
1195 if (!provider) | |
1196 return E_INVALIDARG; | |
1197 | |
1198 DVLOG(1) << "In Function: " | |
1199 << __FUNCTION__ | |
1200 << " for pattern id: " | |
1201 << id; | |
1202 if (id == UIA_ValuePatternId || id == UIA_TextPatternId) { | |
1203 ui::AXViewState state; | |
1204 view_->GetAccessibleState(&state); | |
1205 long role = MSAARole(state.role); | |
1206 | |
1207 if (role == ROLE_SYSTEM_TEXT) { | |
1208 DVLOG(1) << "Returning UIA text provider"; | |
1209 base::win::UIATextProvider::CreateTextProvider( | |
1210 state.value, true, provider); | |
1211 return S_OK; | |
1212 } | |
1213 } | |
1214 return E_NOTIMPL; | |
1215 } | |
1216 | |
1217 STDMETHODIMP NativeViewAccessibilityWin::GetPropertyValue(PROPERTYID id, | |
1218 VARIANT* ret) { | |
1219 if (!view_) | |
1220 return E_FAIL; | |
1221 | |
1222 if (!ret) | |
1223 return E_INVALIDARG; | |
1224 | |
1225 DVLOG(1) << "In Function: " | |
1226 << __FUNCTION__ | |
1227 << " for property id: " | |
1228 << id; | |
1229 if (id == UIA_ControlTypePropertyId) { | |
1230 ui::AXViewState state; | |
1231 view_->GetAccessibleState(&state); | |
1232 long role = MSAARole(state.role); | |
1233 if (role == ROLE_SYSTEM_TEXT) { | |
1234 V_VT(ret) = VT_I4; | |
1235 ret->lVal = UIA_EditControlTypeId; | |
1236 DVLOG(1) << "Returning Edit control type"; | |
1237 } else { | |
1238 DVLOG(1) << "Returning empty control type"; | |
1239 V_VT(ret) = VT_EMPTY; | |
1240 } | |
1241 } else { | |
1242 V_VT(ret) = VT_EMPTY; | |
1243 } | |
1244 return S_OK; | |
1245 } | |
1246 | |
1247 // | |
1248 // Static methods. | |
1249 // | |
1250 | |
1251 void NativeViewAccessibility::RegisterWebView(View* web_view) { | |
1252 AccessibleWebViewRegistry::GetInstance()->RegisterWebView(web_view); | |
1253 } | |
1254 | |
1255 void NativeViewAccessibility::UnregisterWebView(View* web_view) { | |
1256 AccessibleWebViewRegistry::GetInstance()->UnregisterWebView(web_view); | |
1257 } | |
1258 | |
1259 int32 NativeViewAccessibilityWin::MSAAEvent(ui::AXEvent event) { | |
1260 switch (event) { | |
1261 case ui::AX_EVENT_ALERT: | |
1262 return EVENT_SYSTEM_ALERT; | |
1263 case ui::AX_EVENT_FOCUS: | |
1264 return EVENT_OBJECT_FOCUS; | |
1265 case ui::AX_EVENT_MENU_START: | |
1266 return EVENT_SYSTEM_MENUSTART; | |
1267 case ui::AX_EVENT_MENU_END: | |
1268 return EVENT_SYSTEM_MENUEND; | |
1269 case ui::AX_EVENT_MENU_POPUP_START: | |
1270 return EVENT_SYSTEM_MENUPOPUPSTART; | |
1271 case ui::AX_EVENT_MENU_POPUP_END: | |
1272 return EVENT_SYSTEM_MENUPOPUPEND; | |
1273 case ui::AX_EVENT_SELECTION: | |
1274 return EVENT_OBJECT_SELECTION; | |
1275 case ui::AX_EVENT_SELECTION_ADD: | |
1276 return EVENT_OBJECT_SELECTIONADD; | |
1277 case ui::AX_EVENT_SELECTION_REMOVE: | |
1278 return EVENT_OBJECT_SELECTIONREMOVE; | |
1279 case ui::AX_EVENT_TEXT_CHANGED: | |
1280 return EVENT_OBJECT_NAMECHANGE; | |
1281 case ui::AX_EVENT_TEXT_SELECTION_CHANGED: | |
1282 return IA2_EVENT_TEXT_CARET_MOVED; | |
1283 case ui::AX_EVENT_VALUE_CHANGED: | |
1284 return EVENT_OBJECT_VALUECHANGE; | |
1285 default: | |
1286 // Not supported or invalid event. | |
1287 NOTREACHED(); | |
1288 return -1; | |
1289 } | |
1290 } | |
1291 | |
1292 int32 NativeViewAccessibilityWin::MSAARole(ui::AXRole role) { | |
1293 switch (role) { | |
1294 case ui::AX_ROLE_ALERT: | |
1295 return ROLE_SYSTEM_ALERT; | |
1296 case ui::AX_ROLE_APPLICATION: | |
1297 return ROLE_SYSTEM_APPLICATION; | |
1298 case ui::AX_ROLE_BUTTON_DROP_DOWN: | |
1299 return ROLE_SYSTEM_BUTTONDROPDOWN; | |
1300 case ui::AX_ROLE_POP_UP_BUTTON: | |
1301 return ROLE_SYSTEM_BUTTONMENU; | |
1302 case ui::AX_ROLE_CHECK_BOX: | |
1303 return ROLE_SYSTEM_CHECKBUTTON; | |
1304 case ui::AX_ROLE_COMBO_BOX: | |
1305 return ROLE_SYSTEM_COMBOBOX; | |
1306 case ui::AX_ROLE_DIALOG: | |
1307 return ROLE_SYSTEM_DIALOG; | |
1308 case ui::AX_ROLE_GROUP: | |
1309 return ROLE_SYSTEM_GROUPING; | |
1310 case ui::AX_ROLE_IMAGE: | |
1311 return ROLE_SYSTEM_GRAPHIC; | |
1312 case ui::AX_ROLE_LINK: | |
1313 return ROLE_SYSTEM_LINK; | |
1314 case ui::AX_ROLE_LOCATION_BAR: | |
1315 return ROLE_SYSTEM_GROUPING; | |
1316 case ui::AX_ROLE_MENU_BAR: | |
1317 return ROLE_SYSTEM_MENUBAR; | |
1318 case ui::AX_ROLE_MENU_ITEM: | |
1319 return ROLE_SYSTEM_MENUITEM; | |
1320 case ui::AX_ROLE_MENU_LIST_POPUP: | |
1321 return ROLE_SYSTEM_MENUPOPUP; | |
1322 case ui::AX_ROLE_TREE: | |
1323 return ROLE_SYSTEM_OUTLINE; | |
1324 case ui::AX_ROLE_TREE_ITEM: | |
1325 return ROLE_SYSTEM_OUTLINEITEM; | |
1326 case ui::AX_ROLE_TAB: | |
1327 return ROLE_SYSTEM_PAGETAB; | |
1328 case ui::AX_ROLE_TAB_LIST: | |
1329 return ROLE_SYSTEM_PAGETABLIST; | |
1330 case ui::AX_ROLE_PANE: | |
1331 return ROLE_SYSTEM_PANE; | |
1332 case ui::AX_ROLE_PROGRESS_INDICATOR: | |
1333 return ROLE_SYSTEM_PROGRESSBAR; | |
1334 case ui::AX_ROLE_BUTTON: | |
1335 return ROLE_SYSTEM_PUSHBUTTON; | |
1336 case ui::AX_ROLE_RADIO_BUTTON: | |
1337 return ROLE_SYSTEM_RADIOBUTTON; | |
1338 case ui::AX_ROLE_SCROLL_BAR: | |
1339 return ROLE_SYSTEM_SCROLLBAR; | |
1340 case ui::AX_ROLE_SPLITTER: | |
1341 return ROLE_SYSTEM_SEPARATOR; | |
1342 case ui::AX_ROLE_SLIDER: | |
1343 return ROLE_SYSTEM_SLIDER; | |
1344 case ui::AX_ROLE_STATIC_TEXT: | |
1345 return ROLE_SYSTEM_STATICTEXT; | |
1346 case ui::AX_ROLE_TEXT_FIELD: | |
1347 return ROLE_SYSTEM_TEXT; | |
1348 case ui::AX_ROLE_TITLE_BAR: | |
1349 return ROLE_SYSTEM_TITLEBAR; | |
1350 case ui::AX_ROLE_TOOLBAR: | |
1351 return ROLE_SYSTEM_TOOLBAR; | |
1352 case ui::AX_ROLE_WEB_VIEW: | |
1353 return ROLE_SYSTEM_GROUPING; | |
1354 case ui::AX_ROLE_WINDOW: | |
1355 return ROLE_SYSTEM_WINDOW; | |
1356 case ui::AX_ROLE_CLIENT: | |
1357 default: | |
1358 // This is the default role for MSAA. | |
1359 return ROLE_SYSTEM_CLIENT; | |
1360 } | |
1361 } | |
1362 | |
1363 int32 NativeViewAccessibilityWin::MSAAState(const ui::AXViewState& state) { | |
1364 // This maps MSAA states for get_accState(). See also the IAccessible2 | |
1365 // interface get_states(). | |
1366 | |
1367 int32 msaa_state = 0; | |
1368 if (state.HasStateFlag(ui::AX_STATE_CHECKED)) | |
1369 msaa_state |= STATE_SYSTEM_CHECKED; | |
1370 if (state.HasStateFlag(ui::AX_STATE_COLLAPSED)) | |
1371 msaa_state |= STATE_SYSTEM_COLLAPSED; | |
1372 if (state.HasStateFlag(ui::AX_STATE_DEFAULT)) | |
1373 msaa_state |= STATE_SYSTEM_DEFAULT; | |
1374 if (state.HasStateFlag(ui::AX_STATE_EXPANDED)) | |
1375 msaa_state |= STATE_SYSTEM_EXPANDED; | |
1376 if (state.HasStateFlag(ui::AX_STATE_HASPOPUP)) | |
1377 msaa_state |= STATE_SYSTEM_HASPOPUP; | |
1378 if (state.HasStateFlag(ui::AX_STATE_HOVERED)) | |
1379 msaa_state |= STATE_SYSTEM_HOTTRACKED; | |
1380 if (state.HasStateFlag(ui::AX_STATE_INVISIBLE)) | |
1381 msaa_state |= STATE_SYSTEM_INVISIBLE; | |
1382 if (state.HasStateFlag(ui::AX_STATE_LINKED)) | |
1383 msaa_state |= STATE_SYSTEM_LINKED; | |
1384 if (state.HasStateFlag(ui::AX_STATE_OFFSCREEN)) | |
1385 msaa_state |= STATE_SYSTEM_OFFSCREEN; | |
1386 if (state.HasStateFlag(ui::AX_STATE_PRESSED)) | |
1387 msaa_state |= STATE_SYSTEM_PRESSED; | |
1388 if (state.HasStateFlag(ui::AX_STATE_PROTECTED)) | |
1389 msaa_state |= STATE_SYSTEM_PROTECTED; | |
1390 if (state.HasStateFlag(ui::AX_STATE_READ_ONLY)) | |
1391 msaa_state |= STATE_SYSTEM_READONLY; | |
1392 if (state.HasStateFlag(ui::AX_STATE_SELECTABLE)) | |
1393 msaa_state |= STATE_SYSTEM_SELECTABLE; | |
1394 if (state.HasStateFlag(ui::AX_STATE_SELECTED)) | |
1395 msaa_state |= STATE_SYSTEM_SELECTED; | |
1396 if (state.HasStateFlag(ui::AX_STATE_FOCUSED)) | |
1397 msaa_state |= STATE_SYSTEM_FOCUSED; | |
1398 if (state.HasStateFlag(ui::AX_STATE_DISABLED)) | |
1399 msaa_state |= STATE_SYSTEM_UNAVAILABLE; | |
1400 return msaa_state; | |
1401 } | |
1402 | |
1403 // | |
1404 // Private methods. | |
1405 // | |
1406 | |
1407 bool NativeViewAccessibilityWin::IsNavDirNext(int nav_dir) const { | |
1408 return (nav_dir == NAVDIR_RIGHT || | |
1409 nav_dir == NAVDIR_DOWN || | |
1410 nav_dir == NAVDIR_NEXT); | |
1411 } | |
1412 | |
1413 bool NativeViewAccessibilityWin::IsValidNav( | |
1414 int nav_dir, int start_id, int lower_bound, int upper_bound) const { | |
1415 if (IsNavDirNext(nav_dir)) { | |
1416 if ((start_id + 1) > upper_bound) { | |
1417 return false; | |
1418 } | |
1419 } else { | |
1420 if ((start_id - 1) <= lower_bound) { | |
1421 return false; | |
1422 } | |
1423 } | |
1424 return true; | |
1425 } | |
1426 | |
1427 bool NativeViewAccessibilityWin::IsValidId(const VARIANT& child) const { | |
1428 // View accessibility returns an IAccessible for each view so we only support | |
1429 // the CHILDID_SELF id. | |
1430 return (VT_I4 == child.vt) && (CHILDID_SELF == child.lVal); | |
1431 } | |
1432 | |
1433 void NativeViewAccessibilityWin::SetState( | |
1434 VARIANT* msaa_state, View* view) { | |
1435 // Ensure the output param is initialized to zero. | |
1436 msaa_state->lVal = 0; | |
1437 | |
1438 // Default state; all views can have accessibility focus. | |
1439 msaa_state->lVal |= STATE_SYSTEM_FOCUSABLE; | |
1440 | |
1441 if (!view) | |
1442 return; | |
1443 | |
1444 if (!view->enabled()) | |
1445 msaa_state->lVal |= STATE_SYSTEM_UNAVAILABLE; | |
1446 if (!view->visible()) | |
1447 msaa_state->lVal |= STATE_SYSTEM_INVISIBLE; | |
1448 if (!strcmp(view->GetClassName(), CustomButton::kViewClassName)) { | |
1449 CustomButton* button = static_cast<CustomButton*>(view); | |
1450 if (button->IsHotTracked()) | |
1451 msaa_state->lVal |= STATE_SYSTEM_HOTTRACKED; | |
1452 } | |
1453 if (view->HasFocus()) | |
1454 msaa_state->lVal |= STATE_SYSTEM_FOCUSED; | |
1455 | |
1456 // Add on any view-specific states. | |
1457 ui::AXViewState view_state; | |
1458 view->GetAccessibleState(&view_state); | |
1459 msaa_state->lVal |= MSAAState(view_state); | |
1460 } | |
1461 | |
1462 base::string16 NativeViewAccessibilityWin::TextForIAccessibleText() { | |
1463 ui::AXViewState state; | |
1464 view_->GetAccessibleState(&state); | |
1465 if (state.role == ui::AX_ROLE_TEXT_FIELD) | |
1466 return state.value; | |
1467 else | |
1468 return state.name; | |
1469 } | |
1470 | |
1471 void NativeViewAccessibilityWin::HandleSpecialTextOffset( | |
1472 const base::string16& text, LONG* offset) { | |
1473 if (*offset == IA2_TEXT_OFFSET_LENGTH) { | |
1474 *offset = static_cast<LONG>(text.size()); | |
1475 } else if (*offset == IA2_TEXT_OFFSET_CARET) { | |
1476 get_caretOffset(offset); | |
1477 } | |
1478 } | |
1479 | |
1480 ui::TextBoundaryType NativeViewAccessibilityWin::IA2TextBoundaryToTextBoundary( | |
1481 IA2TextBoundaryType ia2_boundary) { | |
1482 switch(ia2_boundary) { | |
1483 case IA2_TEXT_BOUNDARY_CHAR: return ui::CHAR_BOUNDARY; | |
1484 case IA2_TEXT_BOUNDARY_WORD: return ui::WORD_BOUNDARY; | |
1485 case IA2_TEXT_BOUNDARY_LINE: return ui::LINE_BOUNDARY; | |
1486 case IA2_TEXT_BOUNDARY_SENTENCE: return ui::SENTENCE_BOUNDARY; | |
1487 case IA2_TEXT_BOUNDARY_PARAGRAPH: return ui::PARAGRAPH_BOUNDARY; | |
1488 case IA2_TEXT_BOUNDARY_ALL: return ui::ALL_BOUNDARY; | |
1489 default: | |
1490 NOTREACHED(); | |
1491 return ui::CHAR_BOUNDARY; | |
1492 } | |
1493 } | |
1494 | |
1495 LONG NativeViewAccessibilityWin::FindBoundary( | |
1496 const base::string16& text, | |
1497 IA2TextBoundaryType ia2_boundary, | |
1498 LONG start_offset, | |
1499 ui::TextBoundaryDirection direction) { | |
1500 HandleSpecialTextOffset(text, &start_offset); | |
1501 ui::TextBoundaryType boundary = IA2TextBoundaryToTextBoundary(ia2_boundary); | |
1502 std::vector<int32> line_breaks; | |
1503 return ui::FindAccessibleTextBoundary( | |
1504 text, line_breaks, boundary, start_offset, direction); | |
1505 } | |
1506 | |
1507 void NativeViewAccessibilityWin::PopulateChildWidgetVector( | |
1508 std::vector<Widget*>* result_child_widgets) { | |
1509 const Widget* widget = view()->GetWidget(); | |
1510 if (!widget) | |
1511 return; | |
1512 | |
1513 std::set<Widget*> child_widgets; | |
1514 Widget::GetAllOwnedWidgets(widget->GetNativeView(), &child_widgets); | |
1515 for (std::set<Widget*>::const_iterator iter = child_widgets.begin(); | |
1516 iter != child_widgets.end(); ++iter) { | |
1517 Widget* child_widget = *iter; | |
1518 DCHECK_NE(widget, child_widget); | |
1519 | |
1520 if (!child_widget->IsVisible()) | |
1521 continue; | |
1522 | |
1523 if (widget->GetNativeWindowProperty(kWidgetNativeViewHostKey)) | |
1524 continue; | |
1525 | |
1526 result_child_widgets->push_back(child_widget); | |
1527 } | |
1528 } | |
1529 | |
1530 void NativeViewAccessibilityWin::AddAlertTarget() { | |
1531 ViewStorage* view_storage = ViewStorage::GetInstance(); | |
1532 for (size_t i = 0; i < alert_target_view_storage_ids_.size(); ++i) { | |
1533 int view_storage_id = alert_target_view_storage_ids_[i]; | |
1534 View* view = view_storage->RetrieveView(view_storage_id); | |
1535 if (view == view_) | |
1536 return; | |
1537 } | |
1538 int view_storage_id = view_storage->CreateStorageID(); | |
1539 view_storage->StoreView(view_storage_id, view_); | |
1540 alert_target_view_storage_ids_.push_back(view_storage_id); | |
1541 } | |
1542 | |
1543 void NativeViewAccessibilityWin::RemoveAlertTarget() { | |
1544 ViewStorage* view_storage = ViewStorage::GetInstance(); | |
1545 size_t i = 0; | |
1546 while (i < alert_target_view_storage_ids_.size()) { | |
1547 int view_storage_id = alert_target_view_storage_ids_[i]; | |
1548 View* view = view_storage->RetrieveView(view_storage_id); | |
1549 if (view == NULL || view == view_) { | |
1550 alert_target_view_storage_ids_.erase( | |
1551 alert_target_view_storage_ids_.begin() + i); | |
1552 } else { | |
1553 ++i; | |
1554 } | |
1555 } | |
1556 } | 63 } |
1557 | 64 |
1558 } // namespace views | 65 } // namespace views |
OLD | NEW |