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());
575 STDMETHODIMP
AXPlatformNodeWin::get_indexInParent(LONG
* index_in_parent
) {
576 COM_OBJECT_VALIDATE_1_ARG(index_in_parent
);
577 *index_in_parent
= GetIndexInParent();
578 if (*index_in_parent
< 0)
585 // IAccessible2 methods not implemented.
588 STDMETHODIMP
AXPlatformNodeWin::get_attribute(BSTR name
, VARIANT
* attribute
) {
591 STDMETHODIMP
AXPlatformNodeWin::get_extendedRole(BSTR
* extended_role
) {
594 STDMETHODIMP
AXPlatformNodeWin::get_nRelations(LONG
* n_relations
) {
597 STDMETHODIMP
AXPlatformNodeWin::get_relation(LONG relation_index
,
598 IAccessibleRelation
** relation
) {
601 STDMETHODIMP
AXPlatformNodeWin::get_relations(LONG max_relations
,
602 IAccessibleRelation
** relations
,
606 STDMETHODIMP
AXPlatformNodeWin::scrollTo(enum IA2ScrollType scroll_type
) {
609 STDMETHODIMP
AXPlatformNodeWin::scrollToPoint(
610 enum IA2CoordinateType coordinate_type
,
615 STDMETHODIMP
AXPlatformNodeWin::get_groupPosition(LONG
* group_level
,
616 LONG
* similar_items_in_group
,
617 LONG
* position_in_group
) {
620 STDMETHODIMP
AXPlatformNodeWin::get_localizedExtendedRole(
621 BSTR
* localized_extended_role
) {
624 STDMETHODIMP
AXPlatformNodeWin::get_nExtendedStates(LONG
* n_extended_states
) {
627 STDMETHODIMP
AXPlatformNodeWin::get_extendedStates(LONG max_extended_states
,
628 BSTR
** extended_states
,
629 LONG
* n_extended_states
) {
632 STDMETHODIMP
AXPlatformNodeWin::get_localizedExtendedStates(
633 LONG max_localized_extended_states
,
634 BSTR
** localized_extended_states
,
635 LONG
* n_localized_extended_states
) {
638 STDMETHODIMP
AXPlatformNodeWin::get_locale(IA2Locale
* locale
) {
641 STDMETHODIMP
AXPlatformNodeWin::get_accessibleWithCaret(IUnknown
** accessible
,
642 long* caret_offset
) {
650 STDMETHODIMP
AXPlatformNodeWin::get_nCharacters(LONG
* n_characters
) {
651 COM_OBJECT_VALIDATE_1_ARG(n_characters
);
652 base::string16 text
= TextForIAccessibleText();
653 *n_characters
= static_cast<LONG
>(text
.size());
658 STDMETHODIMP
AXPlatformNodeWin::get_caretOffset(LONG
* offset
) {
659 COM_OBJECT_VALIDATE_1_ARG(offset
);
660 *offset
= static_cast<LONG
>(GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END
));
664 STDMETHODIMP
AXPlatformNodeWin::get_nSelections(LONG
* n_selections
) {
665 COM_OBJECT_VALIDATE_1_ARG(n_selections
);
666 int sel_start
= GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START
);
667 int sel_end
= GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END
);
668 if (sel_start
!= sel_end
)
675 STDMETHODIMP
AXPlatformNodeWin::get_selection(LONG selection_index
,
678 COM_OBJECT_VALIDATE_2_ARGS(start_offset
, end_offset
);
679 if (selection_index
!= 0)
682 *start_offset
= static_cast<LONG
>(
683 GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START
));
684 *end_offset
= static_cast<LONG
>(
685 GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END
));
689 STDMETHODIMP
AXPlatformNodeWin::get_text(LONG start_offset
,
692 COM_OBJECT_VALIDATE_1_ARG(text
);
693 int sel_end
= GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START
);
694 base::string16 text_str
= TextForIAccessibleText();
695 LONG len
= static_cast<LONG
>(text_str
.size());
697 if (start_offset
== IA2_TEXT_OFFSET_LENGTH
) {
699 } else if (start_offset
== IA2_TEXT_OFFSET_CARET
) {
700 start_offset
= static_cast<LONG
>(sel_end
);
702 if (end_offset
== IA2_TEXT_OFFSET_LENGTH
) {
703 end_offset
= static_cast<LONG
>(text_str
.size());
704 } else if (end_offset
== IA2_TEXT_OFFSET_CARET
) {
705 end_offset
= static_cast<LONG
>(sel_end
);
708 // The spec allows the arguments to be reversed.
709 if (start_offset
> end_offset
) {
710 LONG tmp
= start_offset
;
711 start_offset
= end_offset
;
715 // The spec does not allow the start or end offsets to be out or range;
716 // we must return an error if so.
717 if (start_offset
< 0)
719 if (end_offset
> len
)
722 base::string16 substr
=
723 text_str
.substr(start_offset
, end_offset
- start_offset
);
727 *text
= SysAllocString(substr
.c_str());
732 STDMETHODIMP
AXPlatformNodeWin::get_textAtOffset(
734 enum IA2TextBoundaryType boundary_type
,
735 LONG
* start_offset
, LONG
* end_offset
,
737 COM_OBJECT_VALIDATE_3_ARGS(start_offset
, end_offset
, text
);
738 // The IAccessible2 spec says we don't have to implement the "sentence"
739 // boundary type, we can just let the screen reader handle it.
740 if (boundary_type
== IA2_TEXT_BOUNDARY_SENTENCE
) {
747 const base::string16
& text_str
= TextForIAccessibleText();
749 *start_offset
= FindBoundary(
750 text_str
, boundary_type
, offset
, ui::BACKWARDS_DIRECTION
);
751 *end_offset
= FindBoundary(
752 text_str
, boundary_type
, offset
, ui::FORWARDS_DIRECTION
);
753 return get_text(*start_offset
, *end_offset
, text
);
756 STDMETHODIMP
AXPlatformNodeWin::get_textBeforeOffset(
758 enum IA2TextBoundaryType boundary_type
,
759 LONG
* start_offset
, LONG
* end_offset
,
761 if (!start_offset
|| !end_offset
|| !text
)
764 // The IAccessible2 spec says we don't have to implement the "sentence"
765 // boundary type, we can just let the screenreader handle it.
766 if (boundary_type
== IA2_TEXT_BOUNDARY_SENTENCE
) {
773 const base::string16
& text_str
= TextForIAccessibleText();
775 *start_offset
= FindBoundary(
776 text_str
, boundary_type
, offset
, ui::BACKWARDS_DIRECTION
);
777 *end_offset
= offset
;
778 return get_text(*start_offset
, *end_offset
, text
);
781 STDMETHODIMP
AXPlatformNodeWin::get_textAfterOffset(
783 enum IA2TextBoundaryType boundary_type
,
784 LONG
* start_offset
, LONG
* end_offset
,
786 if (!start_offset
|| !end_offset
|| !text
)
789 // The IAccessible2 spec says we don't have to implement the "sentence"
790 // boundary type, we can just let the screenreader handle it.
791 if (boundary_type
== IA2_TEXT_BOUNDARY_SENTENCE
) {
798 const base::string16
& text_str
= TextForIAccessibleText();
800 *start_offset
= offset
;
801 *end_offset
= FindBoundary(
802 text_str
, boundary_type
, offset
, ui::FORWARDS_DIRECTION
);
803 return get_text(*start_offset
, *end_offset
, text
);
806 STDMETHODIMP
AXPlatformNodeWin::get_offsetAtPoint(
807 LONG x
, LONG y
, enum IA2CoordinateType coord_type
, LONG
* offset
) {
808 COM_OBJECT_VALIDATE_1_ARG(offset
);
809 // We don't support this method, but we have to return something
810 // rather than E_NOTIMPL or screen readers will complain.
816 // IAccessibleText methods not implemented.
819 STDMETHODIMP
AXPlatformNodeWin::get_newText(IA2TextSegment
* new_text
) {
822 STDMETHODIMP
AXPlatformNodeWin::get_oldText(IA2TextSegment
* old_text
) {
825 STDMETHODIMP
AXPlatformNodeWin::addSelection(LONG start_offset
,
829 STDMETHODIMP
AXPlatformNodeWin::get_attributes(LONG offset
,
832 BSTR
* text_attributes
) {
835 STDMETHODIMP
AXPlatformNodeWin::get_characterExtents(
837 enum IA2CoordinateType coord_type
,
844 STDMETHODIMP
AXPlatformNodeWin::removeSelection(LONG selection_index
) {
847 STDMETHODIMP
AXPlatformNodeWin::setCaretOffset(LONG offset
) {
850 STDMETHODIMP
AXPlatformNodeWin::setSelection(LONG selection_index
,
855 STDMETHODIMP
AXPlatformNodeWin::scrollSubstringTo(
858 enum IA2ScrollType scroll_type
) {
861 STDMETHODIMP
AXPlatformNodeWin::scrollSubstringToPoint(
864 enum IA2CoordinateType coordinate_type
,
871 // IServiceProvider implementation.
874 STDMETHODIMP
AXPlatformNodeWin::QueryService(
875 REFGUID guidService
, REFIID riid
, void** object
) {
876 COM_OBJECT_VALIDATE_1_ARG(object
);
877 if (guidService
== IID_IAccessible
||
878 guidService
== IID_IAccessible2
||
879 guidService
== IID_IAccessible2_2
||
880 guidService
== IID_IAccessibleText
) {
881 return QueryInterface(riid
, object
);
889 // Private member functions.
892 bool AXPlatformNodeWin::IsValidId(const VARIANT
& child
) const {
893 // Since we have a separate IAccessible COM object for each node, we only
894 // support the CHILDID_SELF id.
895 return (VT_I4
== child
.vt
) && (CHILDID_SELF
== child
.lVal
);
898 int AXPlatformNodeWin::MSAARole() {
899 switch (GetData().role
) {
900 case ui::AX_ROLE_ALERT
:
901 return ROLE_SYSTEM_ALERT
;
902 case ui::AX_ROLE_APPLICATION
:
903 return ROLE_SYSTEM_APPLICATION
;
904 case ui::AX_ROLE_BUTTON_DROP_DOWN
:
905 return ROLE_SYSTEM_BUTTONDROPDOWN
;
906 case ui::AX_ROLE_POP_UP_BUTTON
:
907 return ROLE_SYSTEM_BUTTONMENU
;
908 case ui::AX_ROLE_CHECK_BOX
:
909 return ROLE_SYSTEM_CHECKBUTTON
;
910 case ui::AX_ROLE_COMBO_BOX
:
911 return ROLE_SYSTEM_COMBOBOX
;
912 case ui::AX_ROLE_DIALOG
:
913 return ROLE_SYSTEM_DIALOG
;
914 case ui::AX_ROLE_GROUP
:
915 return ROLE_SYSTEM_GROUPING
;
916 case ui::AX_ROLE_IMAGE
:
917 return ROLE_SYSTEM_GRAPHIC
;
918 case ui::AX_ROLE_LINK
:
919 return ROLE_SYSTEM_LINK
;
920 case ui::AX_ROLE_LOCATION_BAR
:
921 return ROLE_SYSTEM_GROUPING
;
922 case ui::AX_ROLE_MENU_BAR
:
923 return ROLE_SYSTEM_MENUBAR
;
924 case ui::AX_ROLE_MENU_ITEM
:
925 return ROLE_SYSTEM_MENUITEM
;
926 case ui::AX_ROLE_MENU_LIST_POPUP
:
927 return ROLE_SYSTEM_MENUPOPUP
;
928 case ui::AX_ROLE_TREE
:
929 return ROLE_SYSTEM_OUTLINE
;
930 case ui::AX_ROLE_TREE_ITEM
:
931 return ROLE_SYSTEM_OUTLINEITEM
;
932 case ui::AX_ROLE_TAB
:
933 return ROLE_SYSTEM_PAGETAB
;
934 case ui::AX_ROLE_TAB_LIST
:
935 return ROLE_SYSTEM_PAGETABLIST
;
936 case ui::AX_ROLE_PANE
:
937 return ROLE_SYSTEM_PANE
;
938 case ui::AX_ROLE_PROGRESS_INDICATOR
:
939 return ROLE_SYSTEM_PROGRESSBAR
;
940 case ui::AX_ROLE_BUTTON
:
941 return ROLE_SYSTEM_PUSHBUTTON
;
942 case ui::AX_ROLE_RADIO_BUTTON
:
943 return ROLE_SYSTEM_RADIOBUTTON
;
944 case ui::AX_ROLE_SCROLL_BAR
:
945 return ROLE_SYSTEM_SCROLLBAR
;
946 case ui::AX_ROLE_SPLITTER
:
947 return ROLE_SYSTEM_SEPARATOR
;
948 case ui::AX_ROLE_SLIDER
:
949 return ROLE_SYSTEM_SLIDER
;
950 case ui::AX_ROLE_STATIC_TEXT
:
951 return ROLE_SYSTEM_STATICTEXT
;
952 case ui::AX_ROLE_TEXT_FIELD
:
953 return ROLE_SYSTEM_TEXT
;
954 case ui::AX_ROLE_TITLE_BAR
:
955 return ROLE_SYSTEM_TITLEBAR
;
956 case ui::AX_ROLE_TOOLBAR
:
957 return ROLE_SYSTEM_TOOLBAR
;
958 case ui::AX_ROLE_WEB_VIEW
:
959 return ROLE_SYSTEM_GROUPING
;
960 case ui::AX_ROLE_WINDOW
:
961 return ROLE_SYSTEM_WINDOW
;
962 case ui::AX_ROLE_CLIENT
:
964 // This is the default role for MSAA.
965 return ROLE_SYSTEM_CLIENT
;
969 int AXPlatformNodeWin::MSAAState() {
970 uint32 state
= GetData().state
;
973 if (state
& (1 << ui::AX_STATE_CHECKED
))
974 msaa_state
|= STATE_SYSTEM_CHECKED
;
975 if (state
& (1 << ui::AX_STATE_COLLAPSED
))
976 msaa_state
|= STATE_SYSTEM_COLLAPSED
;
977 if (state
& (1 << ui::AX_STATE_DEFAULT
))
978 msaa_state
|= STATE_SYSTEM_DEFAULT
;
979 if (state
& (1 << ui::AX_STATE_EXPANDED
))
980 msaa_state
|= STATE_SYSTEM_EXPANDED
;
981 if (state
& (1 << ui::AX_STATE_FOCUSABLE
))
982 msaa_state
|= STATE_SYSTEM_FOCUSABLE
;
983 if (state
& (1 << ui::AX_STATE_FOCUSED
))
984 msaa_state
|= STATE_SYSTEM_FOCUSED
;
985 if (state
& (1 << ui::AX_STATE_HASPOPUP
))
986 msaa_state
|= STATE_SYSTEM_HASPOPUP
;
987 if (state
& (1 << ui::AX_STATE_HOVERED
))
988 msaa_state
|= STATE_SYSTEM_HOTTRACKED
;
989 if (state
& (1 << ui::AX_STATE_INVISIBLE
))
990 msaa_state
|= STATE_SYSTEM_INVISIBLE
;
991 if (state
& (1 << ui::AX_STATE_LINKED
))
992 msaa_state
|= STATE_SYSTEM_LINKED
;
993 if (state
& (1 << ui::AX_STATE_OFFSCREEN
))
994 msaa_state
|= STATE_SYSTEM_OFFSCREEN
;
995 if (state
& (1 << ui::AX_STATE_PRESSED
))
996 msaa_state
|= STATE_SYSTEM_PRESSED
;
997 if (state
& (1 << ui::AX_STATE_PROTECTED
))
998 msaa_state
|= STATE_SYSTEM_PROTECTED
;
999 if (state
& (1 << ui::AX_STATE_READ_ONLY
))
1000 msaa_state
|= STATE_SYSTEM_READONLY
;
1001 if (state
& (1 << ui::AX_STATE_SELECTABLE
))
1002 msaa_state
|= STATE_SYSTEM_SELECTABLE
;
1003 if (state
& (1 << ui::AX_STATE_SELECTED
))
1004 msaa_state
|= STATE_SYSTEM_SELECTED
;
1005 if (state
& (1 << ui::AX_STATE_DISABLED
))
1006 msaa_state
|= STATE_SYSTEM_UNAVAILABLE
;
1011 int AXPlatformNodeWin::MSAAEvent(ui::AXEvent event
) {
1013 case ui::AX_EVENT_ALERT
:
1014 return EVENT_SYSTEM_ALERT
;
1015 case ui::AX_EVENT_FOCUS
:
1016 return EVENT_OBJECT_FOCUS
;
1017 case ui::AX_EVENT_MENU_START
:
1018 return EVENT_SYSTEM_MENUSTART
;
1019 case ui::AX_EVENT_MENU_END
:
1020 return EVENT_SYSTEM_MENUEND
;
1021 case ui::AX_EVENT_MENU_POPUP_START
:
1022 return EVENT_SYSTEM_MENUPOPUPSTART
;
1023 case ui::AX_EVENT_MENU_POPUP_END
:
1024 return EVENT_SYSTEM_MENUPOPUPEND
;
1025 case ui::AX_EVENT_SELECTION
:
1026 return EVENT_OBJECT_SELECTION
;
1027 case ui::AX_EVENT_SELECTION_ADD
:
1028 return EVENT_OBJECT_SELECTIONADD
;
1029 case ui::AX_EVENT_SELECTION_REMOVE
:
1030 return EVENT_OBJECT_SELECTIONREMOVE
;
1031 case ui::AX_EVENT_TEXT_CHANGED
:
1032 return EVENT_OBJECT_NAMECHANGE
;
1033 case ui::AX_EVENT_TEXT_SELECTION_CHANGED
:
1034 return IA2_EVENT_TEXT_CARET_MOVED
;
1035 case ui::AX_EVENT_VALUE_CHANGED
:
1036 return EVENT_OBJECT_VALUECHANGE
;
1042 HRESULT
AXPlatformNodeWin::GetStringAttributeAsBstr(
1043 ui::AXStringAttribute attribute
,
1044 BSTR
* value_bstr
) const {
1047 if (!GetString16Attribute(attribute
, &str
))
1053 *value_bstr
= SysAllocString(str
.c_str());
1054 DCHECK(*value_bstr
);
1059 void AXPlatformNodeWin::AddAlertTarget() {
1060 g_alert_targets
.Get().insert(this);
1063 void AXPlatformNodeWin::RemoveAlertTarget() {
1064 if (g_alert_targets
.Get().find(this) != g_alert_targets
.Get().end())
1065 g_alert_targets
.Get().erase(this);
1068 base::string16
AXPlatformNodeWin::TextForIAccessibleText() {
1069 if (GetData().role
== ui::AX_ROLE_TEXT_FIELD
)
1070 return GetString16Attribute(ui::AX_ATTR_VALUE
);
1072 return GetString16Attribute(ui::AX_ATTR_NAME
);
1075 void AXPlatformNodeWin::HandleSpecialTextOffset(
1076 const base::string16
& text
, LONG
* offset
) {
1077 if (*offset
== IA2_TEXT_OFFSET_LENGTH
) {
1078 *offset
= static_cast<LONG
>(text
.size());
1079 } else if (*offset
== IA2_TEXT_OFFSET_CARET
) {
1080 get_caretOffset(offset
);
1084 ui::TextBoundaryType
AXPlatformNodeWin::IA2TextBoundaryToTextBoundary(
1085 IA2TextBoundaryType ia2_boundary
) {
1086 switch(ia2_boundary
) {
1087 case IA2_TEXT_BOUNDARY_CHAR
: return ui::CHAR_BOUNDARY
;
1088 case IA2_TEXT_BOUNDARY_WORD
: return ui::WORD_BOUNDARY
;
1089 case IA2_TEXT_BOUNDARY_LINE
: return ui::LINE_BOUNDARY
;
1090 case IA2_TEXT_BOUNDARY_SENTENCE
: return ui::SENTENCE_BOUNDARY
;
1091 case IA2_TEXT_BOUNDARY_PARAGRAPH
: return ui::PARAGRAPH_BOUNDARY
;
1092 case IA2_TEXT_BOUNDARY_ALL
: return ui::ALL_BOUNDARY
;
1095 return ui::CHAR_BOUNDARY
;
1099 LONG
AXPlatformNodeWin::FindBoundary(
1100 const base::string16
& text
,
1101 IA2TextBoundaryType ia2_boundary
,
1103 ui::TextBoundaryDirection direction
) {
1104 HandleSpecialTextOffset(text
, &start_offset
);
1105 ui::TextBoundaryType boundary
= IA2TextBoundaryToTextBoundary(ia2_boundary
);
1106 std::vector
<int32
> line_breaks
;
1107 return static_cast<LONG
>(ui::FindAccessibleTextBoundary(
1108 text
, line_breaks
, boundary
, start_offset
, direction
));