1 // Copyright 2015 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.
9 #include "base/containers/hash_tables.h"
10 #include "base/lazy_instance.h"
11 #include "base/win/scoped_comptr.h"
12 #include "base/win/scoped_variant.h"
13 #include "third_party/iaccessible2/ia2_api_all.h"
14 #include "ui/accessibility/ax_node_data.h"
15 #include "ui/accessibility/ax_text_utils.h"
16 #include "ui/accessibility/platform/ax_platform_node_delegate.h"
17 #include "ui/accessibility/platform/ax_platform_node_win.h"
18 #include "ui/base/win/atl_module.h"
21 // Macros to use at the top of any AXPlatformNodeWin function that implements
22 // a COM interface. Because COM objects are reference counted and clients
23 // are completely untrusted, it's important to always first check that our
24 // object is still valid, and then check that all pointer arguments are
28 #define COM_OBJECT_VALIDATE() \
31 #define COM_OBJECT_VALIDATE_1_ARG(arg) \
32 if (!delegate_) return E_FAIL; \
33 if (!arg) return E_INVALIDARG
34 #define COM_OBJECT_VALIDATE_2_ARGS(arg1, arg2) \
35 if (!delegate_) return E_FAIL; \
36 if (!arg1) return E_INVALIDARG; \
37 if (!arg2) return E_INVALIDARG
38 #define COM_OBJECT_VALIDATE_3_ARGS(arg1, arg2, arg3) \
39 if (!delegate_) return E_FAIL; \
40 if (!arg1) return E_INVALIDARG; \
41 if (!arg2) return E_INVALIDARG; \
42 if (!arg3) return E_INVALIDARG
43 #define COM_OBJECT_VALIDATE_4_ARGS(arg1, arg2, arg3, arg4) \
44 if (!delegate_) return E_FAIL; \
45 if (!arg1) return E_INVALIDARG; \
46 if (!arg2) return E_INVALIDARG; \
47 if (!arg3) return E_INVALIDARG; \
48 if (!arg4) return E_INVALIDARG
49 #define COM_OBJECT_VALIDATE_VAR_ID(var_id) \
50 if (!delegate_) return E_FAIL; \
51 if (!IsValidId(var_id)) return E_INVALIDARG
52 #define COM_OBJECT_VALIDATE_VAR_ID_1_ARG(var_id, arg) \
53 if (!delegate_) return E_FAIL; \
54 if (!IsValidId(var_id)) return E_INVALIDARG; \
55 if (!arg) return E_INVALIDARG
56 #define COM_OBJECT_VALIDATE_VAR_ID_2_ARGS(var_id, arg1, arg2) \
57 if (!delegate_) return E_FAIL; \
58 if (!IsValidId(var_id)) return E_INVALIDARG; \
59 if (!arg1) return E_INVALIDARG; \
60 if (!arg2) return E_INVALIDARG
61 #define COM_OBJECT_VALIDATE_VAR_ID_3_ARGS(var_id, arg1, arg2, arg3) \
62 if (!delegate_) return E_FAIL; \
63 if (!IsValidId(var_id)) return E_INVALIDARG; \
64 if (!arg1) return E_INVALIDARG; \
65 if (!arg2) return E_INVALIDARG; \
66 if (!arg3) return E_INVALIDARG
67 #define COM_OBJECT_VALIDATE_VAR_ID_4_ARGS(var_id, arg1, arg2, arg3, arg4) \
68 if (!delegate_) return E_FAIL; \
69 if (!IsValidId(var_id)) return E_INVALIDARG; \
70 if (!arg1) return E_INVALIDARG; \
71 if (!arg2) return E_INVALIDARG; \
72 if (!arg3) return E_INVALIDARG; \
73 if (!arg4) return E_INVALIDARG
79 typedef base::hash_map
<LONG
, AXPlatformNodeWin
*> UniqueIdWinMap
;
80 // Map from each AXPlatformNodeWin's unique id to its instance.
81 base::LazyInstance
<UniqueIdWinMap
> g_unique_id_win_map
=
82 LAZY_INSTANCE_INITIALIZER
;
84 typedef base::hash_set
<AXPlatformNodeWin
*> AXPlatformNodeWinSet
;
85 // Set of all AXPlatformNodeWin objects that were the target of an
87 base::LazyInstance
<AXPlatformNodeWinSet
> g_alert_targets
=
88 LAZY_INSTANCE_INITIALIZER
;
90 LONG
GetNextNegativeUniqueIdForWinAccessibility(AXPlatformNodeWin
* obj
) {
91 static LONG next_unique_id
= -1;
92 LONG unique_id
= next_unique_id
;
93 if (next_unique_id
== LONG_MIN
)
98 g_unique_id_win_map
.Get().insert(std::make_pair(unique_id
, obj
));
103 void UnregisterNegativeUniqueId(LONG unique_id
) {
104 g_unique_id_win_map
.Get().erase(unique_id
);
110 AXPlatformNode
* AXPlatformNode::Create(AXPlatformNodeDelegate
* delegate
) {
111 // Make sure ATL is initialized in this module.
112 ui::win::CreateATLModuleIfNeeded();
114 CComObject
<AXPlatformNodeWin
>* instance
= nullptr;
115 HRESULT hr
= CComObject
<AXPlatformNodeWin
>::CreateInstance(&instance
);
116 DCHECK(SUCCEEDED(hr
));
117 instance
->Init(delegate
);
123 AXPlatformNode
* AXPlatformNode::FromNativeViewAccessible(
124 gfx::NativeViewAccessible accessible
) {
125 base::win::ScopedComPtr
<AXPlatformNodeWin
> ax_platform_node
;
126 accessible
->QueryInterface(ax_platform_node
.Receive());
127 return ax_platform_node
.get();
130 AXPlatformNodeWin::AXPlatformNodeWin()
131 : unique_id_win_(GetNextNegativeUniqueIdForWinAccessibility(this)) {
134 AXPlatformNodeWin::~AXPlatformNodeWin() {
139 // AXPlatformNode implementation.
142 void AXPlatformNodeWin::Destroy() {
144 UnregisterNegativeUniqueId(unique_id_win_
);
149 gfx::NativeViewAccessible
AXPlatformNodeWin::GetNativeViewAccessible() {
153 void AXPlatformNodeWin::NotifyAccessibilityEvent(ui::AXEvent event_type
) {
154 HWND hwnd
= delegate_
->GetTargetForNativeAccessibilityEvent();
158 int native_event
= MSAAEvent(event_type
);
159 if (native_event
< EVENT_MIN
)
162 ::NotifyWinEvent(native_event
, hwnd
, OBJID_CLIENT
, unique_id_win_
);
164 // Keep track of objects that are a target of an alert event.
165 if (event_type
== ui::AX_EVENT_ALERT
)
169 int AXPlatformNodeWin::GetIndexInParent() {
170 base::win::ScopedComPtr
<IDispatch
> parent_dispatch
;
171 base::win::ScopedComPtr
<IAccessible
> parent_accessible
;
172 if (S_OK
!= get_accParent(parent_dispatch
.Receive()))
174 if (S_OK
!= parent_dispatch
.QueryInterface(parent_accessible
.Receive()))
177 LONG child_count
= 0;
178 if (S_OK
!= parent_accessible
->get_accChildCount(&child_count
))
180 for (LONG index
= 1; index
<= child_count
; ++index
) {
181 base::win::ScopedVariant
childid_index(index
);
182 base::win::ScopedComPtr
<IDispatch
> child_dispatch
;
183 base::win::ScopedComPtr
<IAccessible
> child_accessible
;
184 if (S_OK
== parent_accessible
->get_accChild(childid_index
,
185 child_dispatch
.Receive()) &&
186 S_OK
== child_dispatch
.QueryInterface(child_accessible
.Receive())) {
187 if (child_accessible
.get() == this)
196 // IAccessible implementation.
199 STDMETHODIMP
AXPlatformNodeWin::accHitTest(
200 LONG x_left
, LONG y_top
, VARIANT
* child
) {
201 COM_OBJECT_VALIDATE_1_ARG(child
);
202 gfx::NativeViewAccessible hit_child
= delegate_
->HitTestSync(x_left
, y_top
);
204 child
->vt
= VT_EMPTY
;
208 if (hit_child
== this) {
209 // This object is the best match, so return CHILDID_SELF. It's tempting to
210 // simplify the logic and use VT_DISPATCH everywhere, but the Windows
211 // call AccessibleObjectFromPoint will keep calling accHitTest until some
212 // object returns CHILDID_SELF.
214 child
->lVal
= CHILDID_SELF
;
218 // Call accHitTest recursively on the result, which may be a recursive call
219 // to this function or it may be overridden, for example in the case of a
221 HRESULT result
= hit_child
->accHitTest(x_left
, y_top
, child
);
223 // If the recursive call returned CHILDID_SELF, we have to convert that
224 // into a VT_DISPATCH for the return value to this call.
225 if (S_OK
== result
&& child
->vt
== VT_I4
&& child
->lVal
== CHILDID_SELF
) {
226 child
->vt
= VT_DISPATCH
;
227 child
->pdispVal
= hit_child
;
228 // Always increment ref when returning a reference to a COM object.
229 child
->pdispVal
->AddRef();
234 HRESULT
AXPlatformNodeWin::accDoDefaultAction(VARIANT var_id
) {
235 COM_OBJECT_VALIDATE_VAR_ID(var_id
);
236 delegate_
->DoDefaultAction();
240 STDMETHODIMP
AXPlatformNodeWin::accLocation(
241 LONG
* x_left
, LONG
* y_top
, LONG
* width
, LONG
* height
, VARIANT var_id
) {
242 COM_OBJECT_VALIDATE_VAR_ID_4_ARGS(var_id
, x_left
, y_top
, width
, height
);
243 gfx::Rect bounds
= GetData().location
;
244 bounds
+= delegate_
->GetGlobalCoordinateOffset();
245 *x_left
= bounds
.x();
247 *width
= bounds
.width();
248 *height
= bounds
.height();
250 if (bounds
.IsEmpty())
256 STDMETHODIMP
AXPlatformNodeWin::accNavigate(
257 LONG nav_dir
, VARIANT start
, VARIANT
* end
) {
258 COM_OBJECT_VALIDATE_VAR_ID_1_ARG(start
, end
);
259 IAccessible
* result
= nullptr;
266 // These directions are not implemented, matching Mozilla and IE.
269 case NAVDIR_FIRSTCHILD
:
270 if (delegate_
->GetChildCount() > 0)
271 result
= delegate_
->ChildAtIndex(0);
274 case NAVDIR_LASTCHILD
:
275 if (delegate_
->GetChildCount() > 0)
276 result
= delegate_
->ChildAtIndex(delegate_
->GetChildCount() - 1);
280 AXPlatformNodeBase
* next
= GetNextSibling();
282 result
= next
->GetNativeViewAccessible();
286 case NAVDIR_PREVIOUS
: {
287 AXPlatformNodeBase
* previous
= GetPreviousSibling();
289 result
= previous
->GetNativeViewAccessible();
302 end
->vt
= VT_DISPATCH
;
303 end
->pdispVal
= result
;
304 // Always increment ref when returning a reference to a COM object.
305 end
->pdispVal
->AddRef();
310 STDMETHODIMP
AXPlatformNodeWin::get_accChild(VARIANT var_child
,
311 IDispatch
** disp_child
) {
312 COM_OBJECT_VALIDATE_1_ARG(disp_child
);
313 LONG child_id
= V_I4(&var_child
);
314 if (child_id
== CHILDID_SELF
) {
316 (*disp_child
)->AddRef();
320 if (child_id
>= 1 && child_id
<= delegate_
->GetChildCount()) {
321 // Positive child ids are a 1-based child index, used by clients
322 // that want to enumerate all immediate children.
323 *disp_child
= delegate_
->ChildAtIndex(child_id
- 1);
326 (*disp_child
)->AddRef();
333 // Negative child ids can be used to map to any descendant.
334 UniqueIdWinMap
* unique_ids
= g_unique_id_win_map
.Pointer();
335 auto iter
= unique_ids
->find(child_id
);
336 if (iter
!= unique_ids
->end()) {
337 *disp_child
= iter
->second
;
338 (*disp_child
)->AddRef();
342 *disp_child
= nullptr;
346 STDMETHODIMP
AXPlatformNodeWin::get_accChildCount(LONG
* child_count
) {
347 COM_OBJECT_VALIDATE_1_ARG(child_count
);
348 *child_count
= delegate_
->GetChildCount();
352 STDMETHODIMP
AXPlatformNodeWin::get_accDefaultAction(
353 VARIANT var_id
, BSTR
* def_action
) {
354 COM_OBJECT_VALIDATE_VAR_ID_1_ARG(var_id
, def_action
);
355 return GetStringAttributeAsBstr(ui::AX_ATTR_ACTION
, def_action
);
358 STDMETHODIMP
AXPlatformNodeWin::get_accDescription(
359 VARIANT var_id
, BSTR
* desc
) {
360 COM_OBJECT_VALIDATE_VAR_ID_1_ARG(var_id
, desc
);
361 return GetStringAttributeAsBstr(ui::AX_ATTR_DESCRIPTION
, desc
);
364 STDMETHODIMP
AXPlatformNodeWin::get_accFocus(VARIANT
* focus_child
) {
365 COM_OBJECT_VALIDATE_1_ARG(focus_child
);
366 gfx::NativeViewAccessible focus_accessible
= delegate_
->GetFocus();
367 if (focus_accessible
== this) {
368 focus_child
->vt
= VT_I4
;
369 focus_child
->lVal
= CHILDID_SELF
;
370 } else if (focus_accessible
) {
371 focus_child
->vt
= VT_DISPATCH
;
372 focus_child
->pdispVal
= focus_accessible
;
373 focus_child
->pdispVal
->AddRef();
376 focus_child
->vt
= VT_EMPTY
;
382 STDMETHODIMP
AXPlatformNodeWin::get_accKeyboardShortcut(
383 VARIANT var_id
, BSTR
* acc_key
) {
384 COM_OBJECT_VALIDATE_VAR_ID_1_ARG(var_id
, acc_key
);
385 return GetStringAttributeAsBstr(ui::AX_ATTR_SHORTCUT
, acc_key
);
388 STDMETHODIMP
AXPlatformNodeWin::get_accName(
389 VARIANT var_id
, BSTR
* name
) {
390 COM_OBJECT_VALIDATE_VAR_ID_1_ARG(var_id
, name
);
391 return GetStringAttributeAsBstr(ui::AX_ATTR_NAME
, name
);
394 STDMETHODIMP
AXPlatformNodeWin::get_accParent(
395 IDispatch
** disp_parent
) {
396 COM_OBJECT_VALIDATE_1_ARG(disp_parent
);
397 *disp_parent
= GetParent();
399 (*disp_parent
)->AddRef();
406 STDMETHODIMP
AXPlatformNodeWin::get_accRole(
407 VARIANT var_id
, VARIANT
* role
) {
408 COM_OBJECT_VALIDATE_VAR_ID_1_ARG(var_id
, role
);
410 role
->lVal
= MSAARole();
414 STDMETHODIMP
AXPlatformNodeWin::get_accState(
415 VARIANT var_id
, VARIANT
* state
) {
416 COM_OBJECT_VALIDATE_VAR_ID_1_ARG(var_id
, state
);
418 state
->lVal
= MSAAState();
422 STDMETHODIMP
AXPlatformNodeWin::get_accHelp(
423 VARIANT var_id
, BSTR
* help
) {
424 COM_OBJECT_VALIDATE_VAR_ID_1_ARG(var_id
, help
);
425 return GetStringAttributeAsBstr(ui::AX_ATTR_HELP
, help
);
428 STDMETHODIMP
AXPlatformNodeWin::get_accValue(VARIANT var_id
, BSTR
* value
) {
429 COM_OBJECT_VALIDATE_VAR_ID_1_ARG(var_id
, value
);
430 return GetStringAttributeAsBstr(ui::AX_ATTR_VALUE
, value
);
433 STDMETHODIMP
AXPlatformNodeWin::put_accValue(VARIANT var_id
,
435 COM_OBJECT_VALIDATE_VAR_ID(var_id
);
436 if (delegate_
->SetStringValue(new_value
))
441 // IAccessible functions not supported.
443 STDMETHODIMP
AXPlatformNodeWin::get_accSelection(VARIANT
* selected
) {
444 COM_OBJECT_VALIDATE_1_ARG(selected
);
446 selected
->vt
= VT_EMPTY
;
450 STDMETHODIMP
AXPlatformNodeWin::accSelect(
451 LONG flagsSelect
, VARIANT var_id
) {
452 COM_OBJECT_VALIDATE_VAR_ID(var_id
);
456 STDMETHODIMP
AXPlatformNodeWin::get_accHelpTopic(
457 BSTR
* help_file
, VARIANT var_id
, LONG
* topic_id
) {
458 COM_OBJECT_VALIDATE_VAR_ID_2_ARGS(var_id
, help_file
, topic_id
);
460 *help_file
= nullptr;
463 *topic_id
= static_cast<LONG
>(-1);
468 STDMETHODIMP
AXPlatformNodeWin::put_accName(
469 VARIANT var_id
, BSTR put_name
) {
470 COM_OBJECT_VALIDATE_VAR_ID(var_id
);
476 // IAccessible2 implementation.
479 STDMETHODIMP
AXPlatformNodeWin::role(LONG
* role
) {
480 COM_OBJECT_VALIDATE_1_ARG(role
);
485 STDMETHODIMP
AXPlatformNodeWin::get_states(AccessibleStates
* states
) {
486 COM_OBJECT_VALIDATE_1_ARG(states
);
487 // There are only a couple of states we need to support
488 // in IAccessible2. If any more are added, we may want to
489 // add a helper function like MSAAState.
490 *states
= IA2_STATE_OPAQUE
;
491 if (GetData().state
& (1 << ui::AX_STATE_EDITABLE
))
492 *states
|= IA2_STATE_EDITABLE
;
497 STDMETHODIMP
AXPlatformNodeWin::get_uniqueID(LONG
* unique_id
) {
498 COM_OBJECT_VALIDATE_1_ARG(unique_id
);
499 *unique_id
= unique_id_win_
;
503 STDMETHODIMP
AXPlatformNodeWin::get_windowHandle(HWND
* window_handle
) {
504 COM_OBJECT_VALIDATE_1_ARG(window_handle
);
505 *window_handle
= delegate_
->GetTargetForNativeAccessibilityEvent();
506 return *window_handle
? S_OK
: S_FALSE
;
509 STDMETHODIMP
AXPlatformNodeWin::get_relationTargetsOfType(
514 COM_OBJECT_VALIDATE_2_ARGS(targets
, n_targets
);
519 // Only respond to requests for relations of type "alerts".
520 base::string16
type(type_bstr
);
521 if (type
!= L
"alerts")
524 // Collect all of the objects that have had an alert fired on them that
525 // are a descendant of this object.
526 std::vector
<AXPlatformNodeWin
*> alert_targets
;
527 for (auto iter
= g_alert_targets
.Get().begin();
528 iter
!= g_alert_targets
.Get().end();
530 AXPlatformNodeWin
* target
= *iter
;
531 if (IsDescendant(target
))
532 alert_targets
.push_back(target
);
535 long count
= static_cast<long>(alert_targets
.size());
539 // Don't return more targets than max_targets - but note that the caller
540 // is allowed to specify max_targets=0 to mean no limit.
541 if (max_targets
> 0 && count
> max_targets
)
544 // Return the number of targets.
547 // Allocate COM memory for the result array and populate it.
548 *targets
= static_cast<IUnknown
**>(
549 CoTaskMemAlloc(count
* sizeof(IUnknown
*)));
550 for (long i
= 0; i
< count
; ++i
) {
551 (*targets
)[i
] = static_cast<IAccessible
*>(alert_targets
[i
]);
552 (*targets
)[i
]->AddRef();
557 STDMETHODIMP
AXPlatformNodeWin::get_attributes(BSTR
* attributes
) {
558 COM_OBJECT_VALIDATE_1_ARG(attributes
);
559 base::string16 attributes_str
;
561 // Text fields need to report the attribute "text-model:a1" to instruct
562 // screen readers to use IAccessible2 APIs to handle text editing in this
563 // object (as opposed to treating it like a native Windows text box).
564 // The text-model:a1 attribute is documented here:
565 // http://www.linuxfoundation.org/collaborate/workgroups/accessibility/ia2/ia2_implementation_guide
566 if (GetData().role
== ui::AX_ROLE_TEXT_FIELD
) {
567 attributes_str
= L
"text-model:a1;";
570 *attributes
= SysAllocString(attributes_str
.c_str());
579 STDMETHODIMP
AXPlatformNodeWin::get_nCharacters(LONG
* n_characters
) {
580 COM_OBJECT_VALIDATE_1_ARG(n_characters
);
581 base::string16 text
= TextForIAccessibleText();
582 *n_characters
= static_cast<LONG
>(text
.size());
587 STDMETHODIMP
AXPlatformNodeWin::get_caretOffset(LONG
* offset
) {
588 COM_OBJECT_VALIDATE_1_ARG(offset
);
589 *offset
= static_cast<LONG
>(GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END
));
593 STDMETHODIMP
AXPlatformNodeWin::get_nSelections(LONG
* n_selections
) {
594 COM_OBJECT_VALIDATE_1_ARG(n_selections
);
595 int sel_start
= GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START
);
596 int sel_end
= GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END
);
597 if (sel_start
!= sel_end
)
604 STDMETHODIMP
AXPlatformNodeWin::get_selection(LONG selection_index
,
607 COM_OBJECT_VALIDATE_2_ARGS(start_offset
, end_offset
);
608 if (selection_index
!= 0)
611 *start_offset
= static_cast<LONG
>(
612 GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START
));
613 *end_offset
= static_cast<LONG
>(
614 GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END
));
618 STDMETHODIMP
AXPlatformNodeWin::get_text(LONG start_offset
,
621 COM_OBJECT_VALIDATE_1_ARG(text
);
622 int sel_end
= GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START
);
623 base::string16 text_str
= TextForIAccessibleText();
624 LONG len
= static_cast<LONG
>(text_str
.size());
626 if (start_offset
== IA2_TEXT_OFFSET_LENGTH
) {
628 } else if (start_offset
== IA2_TEXT_OFFSET_CARET
) {
629 start_offset
= static_cast<LONG
>(sel_end
);
631 if (end_offset
== IA2_TEXT_OFFSET_LENGTH
) {
632 end_offset
= static_cast<LONG
>(text_str
.size());
633 } else if (end_offset
== IA2_TEXT_OFFSET_CARET
) {
634 end_offset
= static_cast<LONG
>(sel_end
);
637 // The spec allows the arguments to be reversed.
638 if (start_offset
> end_offset
) {
639 LONG tmp
= start_offset
;
640 start_offset
= end_offset
;
644 // The spec does not allow the start or end offsets to be out or range;
645 // we must return an error if so.
646 if (start_offset
< 0)
648 if (end_offset
> len
)
651 base::string16 substr
=
652 text_str
.substr(start_offset
, end_offset
- start_offset
);
656 *text
= SysAllocString(substr
.c_str());
661 STDMETHODIMP
AXPlatformNodeWin::get_textAtOffset(
663 enum IA2TextBoundaryType boundary_type
,
664 LONG
* start_offset
, LONG
* end_offset
,
666 COM_OBJECT_VALIDATE_3_ARGS(start_offset
, end_offset
, text
);
667 // The IAccessible2 spec says we don't have to implement the "sentence"
668 // boundary type, we can just let the screen reader handle it.
669 if (boundary_type
== IA2_TEXT_BOUNDARY_SENTENCE
) {
676 const base::string16
& text_str
= TextForIAccessibleText();
678 *start_offset
= FindBoundary(
679 text_str
, boundary_type
, offset
, ui::BACKWARDS_DIRECTION
);
680 *end_offset
= FindBoundary(
681 text_str
, boundary_type
, offset
, ui::FORWARDS_DIRECTION
);
682 return get_text(*start_offset
, *end_offset
, text
);
685 STDMETHODIMP
AXPlatformNodeWin::get_textBeforeOffset(
687 enum IA2TextBoundaryType boundary_type
,
688 LONG
* start_offset
, LONG
* end_offset
,
690 if (!start_offset
|| !end_offset
|| !text
)
693 // The IAccessible2 spec says we don't have to implement the "sentence"
694 // boundary type, we can just let the screenreader handle it.
695 if (boundary_type
== IA2_TEXT_BOUNDARY_SENTENCE
) {
702 const base::string16
& text_str
= TextForIAccessibleText();
704 *start_offset
= FindBoundary(
705 text_str
, boundary_type
, offset
, ui::BACKWARDS_DIRECTION
);
706 *end_offset
= offset
;
707 return get_text(*start_offset
, *end_offset
, text
);
710 STDMETHODIMP
AXPlatformNodeWin::get_textAfterOffset(
712 enum IA2TextBoundaryType boundary_type
,
713 LONG
* start_offset
, LONG
* end_offset
,
715 if (!start_offset
|| !end_offset
|| !text
)
718 // The IAccessible2 spec says we don't have to implement the "sentence"
719 // boundary type, we can just let the screenreader handle it.
720 if (boundary_type
== IA2_TEXT_BOUNDARY_SENTENCE
) {
727 const base::string16
& text_str
= TextForIAccessibleText();
729 *start_offset
= offset
;
730 *end_offset
= FindBoundary(
731 text_str
, boundary_type
, offset
, ui::FORWARDS_DIRECTION
);
732 return get_text(*start_offset
, *end_offset
, text
);
735 STDMETHODIMP
AXPlatformNodeWin::get_offsetAtPoint(
736 LONG x
, LONG y
, enum IA2CoordinateType coord_type
, LONG
* offset
) {
737 COM_OBJECT_VALIDATE_1_ARG(offset
);
738 // We don't support this method, but we have to return something
739 // rather than E_NOTIMPL or screen readers will complain.
745 // IServiceProvider implementation.
748 STDMETHODIMP
AXPlatformNodeWin::QueryService(
749 REFGUID guidService
, REFIID riid
, void** object
) {
750 COM_OBJECT_VALIDATE_1_ARG(object
);
751 if (guidService
== IID_IAccessible
||
752 guidService
== IID_IAccessible2
||
753 guidService
== IID_IAccessible2_2
||
754 guidService
== IID_IAccessibleText
) {
755 return QueryInterface(riid
, object
);
763 // Private member functions.
766 bool AXPlatformNodeWin::IsValidId(const VARIANT
& child
) const {
767 // Since we have a separate IAccessible COM object for each node, we only
768 // support the CHILDID_SELF id.
769 return (VT_I4
== child
.vt
) && (CHILDID_SELF
== child
.lVal
);
772 int AXPlatformNodeWin::MSAARole() {
773 switch (GetData().role
) {
774 case ui::AX_ROLE_ALERT
:
775 return ROLE_SYSTEM_ALERT
;
776 case ui::AX_ROLE_APPLICATION
:
777 return ROLE_SYSTEM_APPLICATION
;
778 case ui::AX_ROLE_BUTTON_DROP_DOWN
:
779 return ROLE_SYSTEM_BUTTONDROPDOWN
;
780 case ui::AX_ROLE_POP_UP_BUTTON
:
781 return ROLE_SYSTEM_BUTTONMENU
;
782 case ui::AX_ROLE_CHECK_BOX
:
783 return ROLE_SYSTEM_CHECKBUTTON
;
784 case ui::AX_ROLE_COMBO_BOX
:
785 return ROLE_SYSTEM_COMBOBOX
;
786 case ui::AX_ROLE_DIALOG
:
787 return ROLE_SYSTEM_DIALOG
;
788 case ui::AX_ROLE_GROUP
:
789 return ROLE_SYSTEM_GROUPING
;
790 case ui::AX_ROLE_IMAGE
:
791 return ROLE_SYSTEM_GRAPHIC
;
792 case ui::AX_ROLE_LINK
:
793 return ROLE_SYSTEM_LINK
;
794 case ui::AX_ROLE_LOCATION_BAR
:
795 return ROLE_SYSTEM_GROUPING
;
796 case ui::AX_ROLE_MENU_BAR
:
797 return ROLE_SYSTEM_MENUBAR
;
798 case ui::AX_ROLE_MENU_ITEM
:
799 return ROLE_SYSTEM_MENUITEM
;
800 case ui::AX_ROLE_MENU_LIST_POPUP
:
801 return ROLE_SYSTEM_MENUPOPUP
;
802 case ui::AX_ROLE_TREE
:
803 return ROLE_SYSTEM_OUTLINE
;
804 case ui::AX_ROLE_TREE_ITEM
:
805 return ROLE_SYSTEM_OUTLINEITEM
;
806 case ui::AX_ROLE_TAB
:
807 return ROLE_SYSTEM_PAGETAB
;
808 case ui::AX_ROLE_TAB_LIST
:
809 return ROLE_SYSTEM_PAGETABLIST
;
810 case ui::AX_ROLE_PANE
:
811 return ROLE_SYSTEM_PANE
;
812 case ui::AX_ROLE_PROGRESS_INDICATOR
:
813 return ROLE_SYSTEM_PROGRESSBAR
;
814 case ui::AX_ROLE_BUTTON
:
815 return ROLE_SYSTEM_PUSHBUTTON
;
816 case ui::AX_ROLE_RADIO_BUTTON
:
817 return ROLE_SYSTEM_RADIOBUTTON
;
818 case ui::AX_ROLE_SCROLL_BAR
:
819 return ROLE_SYSTEM_SCROLLBAR
;
820 case ui::AX_ROLE_SPLITTER
:
821 return ROLE_SYSTEM_SEPARATOR
;
822 case ui::AX_ROLE_SLIDER
:
823 return ROLE_SYSTEM_SLIDER
;
824 case ui::AX_ROLE_STATIC_TEXT
:
825 return ROLE_SYSTEM_STATICTEXT
;
826 case ui::AX_ROLE_TEXT_FIELD
:
827 return ROLE_SYSTEM_TEXT
;
828 case ui::AX_ROLE_TITLE_BAR
:
829 return ROLE_SYSTEM_TITLEBAR
;
830 case ui::AX_ROLE_TOOLBAR
:
831 return ROLE_SYSTEM_TOOLBAR
;
832 case ui::AX_ROLE_WEB_VIEW
:
833 return ROLE_SYSTEM_GROUPING
;
834 case ui::AX_ROLE_WINDOW
:
835 return ROLE_SYSTEM_WINDOW
;
836 case ui::AX_ROLE_CLIENT
:
838 // This is the default role for MSAA.
839 return ROLE_SYSTEM_CLIENT
;
843 int AXPlatformNodeWin::MSAAState() {
844 uint32 state
= GetData().state
;
847 if (state
& (1 << ui::AX_STATE_CHECKED
))
848 msaa_state
|= STATE_SYSTEM_CHECKED
;
849 if (state
& (1 << ui::AX_STATE_COLLAPSED
))
850 msaa_state
|= STATE_SYSTEM_COLLAPSED
;
851 if (state
& (1 << ui::AX_STATE_DEFAULT
))
852 msaa_state
|= STATE_SYSTEM_DEFAULT
;
853 if (state
& (1 << ui::AX_STATE_EXPANDED
))
854 msaa_state
|= STATE_SYSTEM_EXPANDED
;
855 if (state
& (1 << ui::AX_STATE_FOCUSABLE
))
856 msaa_state
|= STATE_SYSTEM_FOCUSABLE
;
857 if (state
& (1 << ui::AX_STATE_FOCUSED
))
858 msaa_state
|= STATE_SYSTEM_FOCUSED
;
859 if (state
& (1 << ui::AX_STATE_HASPOPUP
))
860 msaa_state
|= STATE_SYSTEM_HASPOPUP
;
861 if (state
& (1 << ui::AX_STATE_HOVERED
))
862 msaa_state
|= STATE_SYSTEM_HOTTRACKED
;
863 if (state
& (1 << ui::AX_STATE_INVISIBLE
))
864 msaa_state
|= STATE_SYSTEM_INVISIBLE
;
865 if (state
& (1 << ui::AX_STATE_LINKED
))
866 msaa_state
|= STATE_SYSTEM_LINKED
;
867 if (state
& (1 << ui::AX_STATE_OFFSCREEN
))
868 msaa_state
|= STATE_SYSTEM_OFFSCREEN
;
869 if (state
& (1 << ui::AX_STATE_PRESSED
))
870 msaa_state
|= STATE_SYSTEM_PRESSED
;
871 if (state
& (1 << ui::AX_STATE_PROTECTED
))
872 msaa_state
|= STATE_SYSTEM_PROTECTED
;
873 if (state
& (1 << ui::AX_STATE_READ_ONLY
))
874 msaa_state
|= STATE_SYSTEM_READONLY
;
875 if (state
& (1 << ui::AX_STATE_SELECTABLE
))
876 msaa_state
|= STATE_SYSTEM_SELECTABLE
;
877 if (state
& (1 << ui::AX_STATE_SELECTED
))
878 msaa_state
|= STATE_SYSTEM_SELECTED
;
879 if (state
& (1 << ui::AX_STATE_DISABLED
))
880 msaa_state
|= STATE_SYSTEM_UNAVAILABLE
;
885 int AXPlatformNodeWin::MSAAEvent(ui::AXEvent event
) {
887 case ui::AX_EVENT_ALERT
:
888 return EVENT_SYSTEM_ALERT
;
889 case ui::AX_EVENT_FOCUS
:
890 return EVENT_OBJECT_FOCUS
;
891 case ui::AX_EVENT_MENU_START
:
892 return EVENT_SYSTEM_MENUSTART
;
893 case ui::AX_EVENT_MENU_END
:
894 return EVENT_SYSTEM_MENUEND
;
895 case ui::AX_EVENT_MENU_POPUP_START
:
896 return EVENT_SYSTEM_MENUPOPUPSTART
;
897 case ui::AX_EVENT_MENU_POPUP_END
:
898 return EVENT_SYSTEM_MENUPOPUPEND
;
899 case ui::AX_EVENT_SELECTION
:
900 return EVENT_OBJECT_SELECTION
;
901 case ui::AX_EVENT_SELECTION_ADD
:
902 return EVENT_OBJECT_SELECTIONADD
;
903 case ui::AX_EVENT_SELECTION_REMOVE
:
904 return EVENT_OBJECT_SELECTIONREMOVE
;
905 case ui::AX_EVENT_TEXT_CHANGED
:
906 return EVENT_OBJECT_NAMECHANGE
;
907 case ui::AX_EVENT_TEXT_SELECTION_CHANGED
:
908 return IA2_EVENT_TEXT_CARET_MOVED
;
909 case ui::AX_EVENT_VALUE_CHANGED
:
910 return EVENT_OBJECT_VALUECHANGE
;
916 HRESULT
AXPlatformNodeWin::GetStringAttributeAsBstr(
917 ui::AXStringAttribute attribute
,
918 BSTR
* value_bstr
) const {
921 if (!GetString16Attribute(attribute
, &str
))
927 *value_bstr
= SysAllocString(str
.c_str());
933 void AXPlatformNodeWin::AddAlertTarget() {
934 g_alert_targets
.Get().insert(this);
937 void AXPlatformNodeWin::RemoveAlertTarget() {
938 if (g_alert_targets
.Get().find(this) != g_alert_targets
.Get().end())
939 g_alert_targets
.Get().erase(this);
942 base::string16
AXPlatformNodeWin::TextForIAccessibleText() {
943 if (GetData().role
== ui::AX_ROLE_TEXT_FIELD
)
944 return GetString16Attribute(ui::AX_ATTR_VALUE
);
946 return GetString16Attribute(ui::AX_ATTR_NAME
);
949 void AXPlatformNodeWin::HandleSpecialTextOffset(
950 const base::string16
& text
, LONG
* offset
) {
951 if (*offset
== IA2_TEXT_OFFSET_LENGTH
) {
952 *offset
= static_cast<LONG
>(text
.size());
953 } else if (*offset
== IA2_TEXT_OFFSET_CARET
) {
954 get_caretOffset(offset
);
958 ui::TextBoundaryType
AXPlatformNodeWin::IA2TextBoundaryToTextBoundary(
959 IA2TextBoundaryType ia2_boundary
) {
960 switch(ia2_boundary
) {
961 case IA2_TEXT_BOUNDARY_CHAR
: return ui::CHAR_BOUNDARY
;
962 case IA2_TEXT_BOUNDARY_WORD
: return ui::WORD_BOUNDARY
;
963 case IA2_TEXT_BOUNDARY_LINE
: return ui::LINE_BOUNDARY
;
964 case IA2_TEXT_BOUNDARY_SENTENCE
: return ui::SENTENCE_BOUNDARY
;
965 case IA2_TEXT_BOUNDARY_PARAGRAPH
: return ui::PARAGRAPH_BOUNDARY
;
966 case IA2_TEXT_BOUNDARY_ALL
: return ui::ALL_BOUNDARY
;
969 return ui::CHAR_BOUNDARY
;
973 LONG
AXPlatformNodeWin::FindBoundary(
974 const base::string16
& text
,
975 IA2TextBoundaryType ia2_boundary
,
977 ui::TextBoundaryDirection direction
) {
978 HandleSpecialTextOffset(text
, &start_offset
);
979 ui::TextBoundaryType boundary
= IA2TextBoundaryToTextBoundary(ia2_boundary
);
980 std::vector
<int32
> line_breaks
;
981 return static_cast<LONG
>(ui::FindAccessibleTextBoundary(
982 text
, line_breaks
, boundary
, start_offset
, direction
));