Use native radiobutton for Default Search Engine picker dialog.
[chromium-blink-merge.git] / content / browser / accessibility / browser_accessibility_win.cc
blob74994bd94aa4882f768d713dd2bfa72dc3f247d5
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/accessibility/browser_accessibility_win.h"
7 #include <UIAutomationClient.h>
8 #include <UIAutomationCoreApi.h>
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/string_split.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/win/enum_variant.h"
15 #include "base/win/scoped_comptr.h"
16 #include "base/win/windows_version.h"
17 #include "content/browser/accessibility/browser_accessibility_manager_win.h"
18 #include "content/browser/accessibility/browser_accessibility_state_impl.h"
19 #include "content/common/accessibility_messages.h"
20 #include "content/public/common/content_client.h"
21 #include "ui/accessibility/ax_text_utils.h"
22 #include "ui/base/win/accessibility_ids_win.h"
23 #include "ui/base/win/accessibility_misc_utils.h"
24 #include "ui/base/win/atl_module.h"
26 namespace content {
28 // These nonstandard GUIDs are taken directly from the Mozilla sources
29 // (accessible/src/msaa/nsAccessNodeWrap.cpp); some documentation is here:
30 // http://developer.mozilla.org/en/Accessibility/AT-APIs/ImplementationFeatures/MSAA
31 const GUID GUID_ISimpleDOM = {
32 0x0c539790, 0x12e4, 0x11cf,
33 0xb6, 0x61, 0x00, 0xaa, 0x00, 0x4c, 0xd6, 0xd8};
34 const GUID GUID_IAccessibleContentDocument = {
35 0xa5d8e1f3, 0x3571, 0x4d8f,
36 0x95, 0x21, 0x07, 0xed, 0x28, 0xfb, 0x07, 0x2e};
38 const base::char16 BrowserAccessibilityWin::kEmbeddedCharacter = L'\xfffc';
40 // static
41 LONG BrowserAccessibilityWin::next_unique_id_win_ =
42 base::win::kFirstBrowserAccessibilityManagerAccessibilityId;
45 // BrowserAccessibilityRelation
47 // A simple implementation of IAccessibleRelation, used to represent
48 // a relationship between two accessible nodes in the tree.
51 class BrowserAccessibilityRelation
52 : public CComObjectRootEx<CComMultiThreadModel>,
53 public IAccessibleRelation {
54 BEGIN_COM_MAP(BrowserAccessibilityRelation)
55 COM_INTERFACE_ENTRY(IAccessibleRelation)
56 END_COM_MAP()
58 CONTENT_EXPORT BrowserAccessibilityRelation() {}
59 CONTENT_EXPORT virtual ~BrowserAccessibilityRelation() {}
61 CONTENT_EXPORT void Initialize(BrowserAccessibilityWin* owner,
62 const base::string16& type);
63 CONTENT_EXPORT void AddTarget(int target_id);
65 // IAccessibleRelation methods.
66 CONTENT_EXPORT STDMETHODIMP get_relationType(BSTR* relation_type) override;
67 CONTENT_EXPORT STDMETHODIMP get_nTargets(long* n_targets) override;
68 CONTENT_EXPORT STDMETHODIMP
69 get_target(long target_index, IUnknown** target) override;
70 CONTENT_EXPORT STDMETHODIMP
71 get_targets(long max_targets, IUnknown** targets, long* n_targets) override;
73 // IAccessibleRelation methods not implemented.
74 CONTENT_EXPORT STDMETHODIMP
75 get_localizedRelationType(BSTR* relation_type) override {
76 return E_NOTIMPL;
79 private:
80 base::string16 type_;
81 base::win::ScopedComPtr<BrowserAccessibilityWin> owner_;
82 std::vector<int> target_ids_;
85 void BrowserAccessibilityRelation::Initialize(BrowserAccessibilityWin* owner,
86 const base::string16& type) {
87 owner_ = owner;
88 type_ = type;
91 void BrowserAccessibilityRelation::AddTarget(int target_id) {
92 target_ids_.push_back(target_id);
95 STDMETHODIMP BrowserAccessibilityRelation::get_relationType(
96 BSTR* relation_type) {
97 if (!relation_type)
98 return E_INVALIDARG;
100 if (!owner_->instance_active())
101 return E_FAIL;
103 *relation_type = SysAllocString(type_.c_str());
104 DCHECK(*relation_type);
105 return S_OK;
108 STDMETHODIMP BrowserAccessibilityRelation::get_nTargets(long* n_targets) {
109 if (!n_targets)
110 return E_INVALIDARG;
112 if (!owner_->instance_active())
113 return E_FAIL;
115 *n_targets = static_cast<long>(target_ids_.size());
117 BrowserAccessibilityManager* manager = owner_->manager();
118 for (long i = *n_targets - 1; i >= 0; --i) {
119 BrowserAccessibility* result = manager->GetFromID(target_ids_[i]);
120 if (!result || !result->instance_active()) {
121 *n_targets = 0;
122 break;
125 return S_OK;
128 STDMETHODIMP BrowserAccessibilityRelation::get_target(long target_index,
129 IUnknown** target) {
130 if (!target)
131 return E_INVALIDARG;
133 if (!owner_->instance_active())
134 return E_FAIL;
136 if (target_index < 0 ||
137 target_index >= static_cast<long>(target_ids_.size())) {
138 return E_INVALIDARG;
141 BrowserAccessibilityManager* manager = owner_->manager();
142 BrowserAccessibility* result =
143 manager->GetFromID(target_ids_[target_index]);
144 if (!result || !result->instance_active())
145 return E_FAIL;
147 *target = static_cast<IAccessible*>(
148 result->ToBrowserAccessibilityWin()->NewReference());
149 return S_OK;
152 STDMETHODIMP BrowserAccessibilityRelation::get_targets(long max_targets,
153 IUnknown** targets,
154 long* n_targets) {
155 if (!targets || !n_targets)
156 return E_INVALIDARG;
158 if (!owner_->instance_active())
159 return E_FAIL;
161 long count = static_cast<long>(target_ids_.size());
162 if (count > max_targets)
163 count = max_targets;
165 *n_targets = count;
166 if (count == 0)
167 return S_FALSE;
169 for (long i = 0; i < count; ++i) {
170 HRESULT result = get_target(i, &targets[i]);
171 if (result != S_OK)
172 return result;
175 return S_OK;
179 // BrowserAccessibilityWin::WinAttributes
182 BrowserAccessibilityWin::WinAttributes::WinAttributes()
183 : ia_role(0),
184 ia_state(0),
185 ia2_role(0),
186 ia2_state(0) {
189 BrowserAccessibilityWin::WinAttributes::~WinAttributes() {
193 // BrowserAccessibilityWin
196 // static
197 BrowserAccessibility* BrowserAccessibility::Create() {
198 ui::win::CreateATLModuleIfNeeded();
199 CComObject<BrowserAccessibilityWin>* instance;
200 HRESULT hr = CComObject<BrowserAccessibilityWin>::CreateInstance(&instance);
201 DCHECK(SUCCEEDED(hr));
202 return instance->NewReference();
205 BrowserAccessibilityWin* BrowserAccessibility::ToBrowserAccessibilityWin() {
206 return static_cast<BrowserAccessibilityWin*>(this);
209 BrowserAccessibilityWin::BrowserAccessibilityWin()
210 : win_attributes_(new WinAttributes()),
211 previous_scroll_x_(0),
212 previous_scroll_y_(0) {
213 // Start unique IDs at -1 and decrement each time, because get_accChild
214 // uses positive IDs to enumerate children, so we use negative IDs to
215 // clearly distinguish between indices and unique IDs.
216 unique_id_win_ = next_unique_id_win_;
217 if (next_unique_id_win_ ==
218 base::win::kLastBrowserAccessibilityManagerAccessibilityId) {
219 next_unique_id_win_ =
220 base::win::kFirstBrowserAccessibilityManagerAccessibilityId;
222 next_unique_id_win_--;
225 BrowserAccessibilityWin::~BrowserAccessibilityWin() {
226 for (size_t i = 0; i < relations_.size(); ++i)
227 relations_[i]->Release();
231 // IAccessible methods.
233 // Conventions:
234 // * Always test for instance_active() first and return E_FAIL if it's false.
235 // * Always check for invalid arguments first, even if they're unused.
236 // * Return S_FALSE if the only output is a string argument and it's empty.
239 HRESULT BrowserAccessibilityWin::accDoDefaultAction(VARIANT var_id) {
240 if (!instance_active())
241 return E_FAIL;
243 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
244 if (!target)
245 return E_INVALIDARG;
247 manager()->DoDefaultAction(*target);
248 return S_OK;
251 STDMETHODIMP BrowserAccessibilityWin::accHitTest(LONG x_left,
252 LONG y_top,
253 VARIANT* child) {
254 if (!instance_active())
255 return E_FAIL;
257 if (!child)
258 return E_INVALIDARG;
260 gfx::Point point(x_left, y_top);
261 if (!GetGlobalBoundsRect().Contains(point)) {
262 // Return S_FALSE and VT_EMPTY when the outside the object's boundaries.
263 child->vt = VT_EMPTY;
264 return S_FALSE;
267 BrowserAccessibility* result = BrowserAccessibilityForPoint(point);
268 if (result == this) {
269 // Point is within this object.
270 child->vt = VT_I4;
271 child->lVal = CHILDID_SELF;
272 } else {
273 child->vt = VT_DISPATCH;
274 child->pdispVal = result->ToBrowserAccessibilityWin()->NewReference();
276 return S_OK;
279 STDMETHODIMP BrowserAccessibilityWin::accLocation(LONG* x_left,
280 LONG* y_top,
281 LONG* width,
282 LONG* height,
283 VARIANT var_id) {
284 if (!instance_active())
285 return E_FAIL;
287 if (!x_left || !y_top || !width || !height)
288 return E_INVALIDARG;
290 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
291 if (!target)
292 return E_INVALIDARG;
294 gfx::Rect bounds = target->GetGlobalBoundsRect();
295 *x_left = bounds.x();
296 *y_top = bounds.y();
297 *width = bounds.width();
298 *height = bounds.height();
300 return S_OK;
303 STDMETHODIMP BrowserAccessibilityWin::accNavigate(LONG nav_dir,
304 VARIANT start,
305 VARIANT* end) {
306 BrowserAccessibilityWin* target = GetTargetFromChildID(start);
307 if (!target)
308 return E_INVALIDARG;
310 if ((nav_dir == NAVDIR_LASTCHILD || nav_dir == NAVDIR_FIRSTCHILD) &&
311 start.lVal != CHILDID_SELF) {
312 // MSAA states that navigating to first/last child can only be from self.
313 return E_INVALIDARG;
316 uint32 child_count = target->PlatformChildCount();
318 BrowserAccessibility* result = NULL;
319 switch (nav_dir) {
320 case NAVDIR_DOWN:
321 case NAVDIR_UP:
322 case NAVDIR_LEFT:
323 case NAVDIR_RIGHT:
324 // These directions are not implemented, matching Mozilla and IE.
325 return E_NOTIMPL;
326 case NAVDIR_FIRSTCHILD:
327 if (child_count > 0)
328 result = target->PlatformGetChild(0);
329 break;
330 case NAVDIR_LASTCHILD:
331 if (child_count > 0)
332 result = target->PlatformGetChild(child_count - 1);
333 break;
334 case NAVDIR_NEXT:
335 result = target->GetNextSibling();
336 break;
337 case NAVDIR_PREVIOUS:
338 result = target->GetPreviousSibling();
339 break;
342 if (!result) {
343 end->vt = VT_EMPTY;
344 return S_FALSE;
347 end->vt = VT_DISPATCH;
348 end->pdispVal = result->ToBrowserAccessibilityWin()->NewReference();
349 return S_OK;
352 STDMETHODIMP BrowserAccessibilityWin::get_accChild(VARIANT var_child,
353 IDispatch** disp_child) {
354 if (!instance_active())
355 return E_FAIL;
357 if (!disp_child)
358 return E_INVALIDARG;
360 *disp_child = NULL;
362 BrowserAccessibilityWin* target = GetTargetFromChildID(var_child);
363 if (!target)
364 return E_INVALIDARG;
366 (*disp_child) = target->NewReference();
367 return S_OK;
370 STDMETHODIMP BrowserAccessibilityWin::get_accChildCount(LONG* child_count) {
371 if (!instance_active())
372 return E_FAIL;
374 if (!child_count)
375 return E_INVALIDARG;
377 *child_count = PlatformChildCount();
379 return S_OK;
382 STDMETHODIMP BrowserAccessibilityWin::get_accDefaultAction(VARIANT var_id,
383 BSTR* def_action) {
384 if (!instance_active())
385 return E_FAIL;
387 if (!def_action)
388 return E_INVALIDARG;
390 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
391 if (!target)
392 return E_INVALIDARG;
394 return target->GetStringAttributeAsBstr(
395 ui::AX_ATTR_ACTION, def_action);
398 STDMETHODIMP BrowserAccessibilityWin::get_accDescription(VARIANT var_id,
399 BSTR* desc) {
400 if (!instance_active())
401 return E_FAIL;
403 if (!desc)
404 return E_INVALIDARG;
406 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
407 if (!target)
408 return E_INVALIDARG;
410 base::string16 description_str = target->description();
411 if (description_str.empty())
412 return S_FALSE;
414 *desc = SysAllocString(description_str.c_str());
416 DCHECK(*desc);
417 return S_OK;
420 STDMETHODIMP BrowserAccessibilityWin::get_accFocus(VARIANT* focus_child) {
421 if (!instance_active())
422 return E_FAIL;
424 if (!focus_child)
425 return E_INVALIDARG;
427 BrowserAccessibilityWin* focus = static_cast<BrowserAccessibilityWin*>(
428 manager()->GetFocus(this));
429 if (focus == this) {
430 focus_child->vt = VT_I4;
431 focus_child->lVal = CHILDID_SELF;
432 } else if (focus == NULL) {
433 focus_child->vt = VT_EMPTY;
434 } else {
435 focus_child->vt = VT_DISPATCH;
436 focus_child->pdispVal = focus->NewReference();
439 return S_OK;
442 STDMETHODIMP BrowserAccessibilityWin::get_accHelp(VARIANT var_id, BSTR* help) {
443 if (!instance_active())
444 return E_FAIL;
446 if (!help)
447 return E_INVALIDARG;
449 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
450 if (!target)
451 return E_INVALIDARG;
453 base::string16 help_str = target->help();
454 if (help_str.empty())
455 return S_FALSE;
457 *help = SysAllocString(help_str.c_str());
459 DCHECK(*help);
460 return S_OK;
463 STDMETHODIMP BrowserAccessibilityWin::get_accKeyboardShortcut(VARIANT var_id,
464 BSTR* acc_key) {
465 if (!instance_active())
466 return E_FAIL;
468 if (!acc_key)
469 return E_INVALIDARG;
471 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
472 if (!target)
473 return E_INVALIDARG;
475 return target->GetStringAttributeAsBstr(
476 ui::AX_ATTR_SHORTCUT, acc_key);
479 STDMETHODIMP BrowserAccessibilityWin::get_accName(VARIANT var_id, BSTR* name) {
480 if (!instance_active())
481 return E_FAIL;
483 if (!name)
484 return E_INVALIDARG;
486 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
487 if (!target)
488 return E_INVALIDARG;
490 base::string16 name_str = target->name();
492 // If the name is empty, see if it's labeled by another element.
493 if (name_str.empty()) {
494 int title_elem_id;
495 if (target->GetIntAttribute(ui::AX_ATTR_TITLE_UI_ELEMENT,
496 &title_elem_id)) {
497 BrowserAccessibilityWin* title_elem =
498 manager()->GetFromID(title_elem_id)->ToBrowserAccessibilityWin();
499 if (title_elem)
500 name_str = title_elem->GetNameRecursive();
504 if (name_str.empty())
505 return S_FALSE;
507 *name = SysAllocString(name_str.c_str());
509 DCHECK(*name);
510 return S_OK;
513 STDMETHODIMP BrowserAccessibilityWin::get_accParent(IDispatch** disp_parent) {
514 if (!instance_active())
515 return E_FAIL;
517 if (!disp_parent)
518 return E_INVALIDARG;
520 IAccessible* parent_obj = GetParent()->ToBrowserAccessibilityWin();
521 if (parent_obj == NULL) {
522 // This happens if we're the root of the tree;
523 // return the IAccessible for the window.
524 parent_obj =
525 manager()->ToBrowserAccessibilityManagerWin()->GetParentIAccessible();
526 // |parent| can only be NULL if the manager was created before the parent
527 // IAccessible was known and it wasn't subsequently set before a client
528 // requested it. This has been fixed. |parent| may also be NULL during
529 // destruction. Possible cases where this could occur include tabs being
530 // dragged to a new window, etc.
531 if (!parent_obj) {
532 DVLOG(1) << "In Function: "
533 << __FUNCTION__
534 << ". Parent IAccessible interface is NULL. Returning failure";
535 return E_FAIL;
538 parent_obj->AddRef();
539 *disp_parent = parent_obj;
540 return S_OK;
543 STDMETHODIMP BrowserAccessibilityWin::get_accRole(VARIANT var_id,
544 VARIANT* role) {
545 if (!instance_active())
546 return E_FAIL;
548 if (!role)
549 return E_INVALIDARG;
551 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
552 if (!target)
553 return E_INVALIDARG;
555 if (!target->role_name().empty()) {
556 role->vt = VT_BSTR;
557 role->bstrVal = SysAllocString(target->role_name().c_str());
558 } else {
559 role->vt = VT_I4;
560 role->lVal = target->ia_role();
562 return S_OK;
565 STDMETHODIMP BrowserAccessibilityWin::get_accState(VARIANT var_id,
566 VARIANT* state) {
567 if (!instance_active())
568 return E_FAIL;
570 if (!state)
571 return E_INVALIDARG;
573 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
574 if (!target)
575 return E_INVALIDARG;
577 state->vt = VT_I4;
578 state->lVal = target->ia_state();
579 if (manager()->GetFocus(NULL) == this)
580 state->lVal |= STATE_SYSTEM_FOCUSED;
582 return S_OK;
585 STDMETHODIMP BrowserAccessibilityWin::get_accValue(VARIANT var_id,
586 BSTR* value) {
587 if (!instance_active())
588 return E_FAIL;
590 if (!value)
591 return E_INVALIDARG;
593 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
594 if (!target)
595 return E_INVALIDARG;
597 if (target->ia_role() == ROLE_SYSTEM_PROGRESSBAR ||
598 target->ia_role() == ROLE_SYSTEM_SCROLLBAR ||
599 target->ia_role() == ROLE_SYSTEM_SLIDER) {
600 base::string16 value_text = target->GetValueText();
601 *value = SysAllocString(value_text.c_str());
602 DCHECK(*value);
603 return S_OK;
606 // Expose color well value.
607 if (target->ia2_role() == IA2_ROLE_COLOR_CHOOSER) {
608 int r = target->GetIntAttribute(
609 ui::AX_ATTR_COLOR_VALUE_RED);
610 int g = target->GetIntAttribute(
611 ui::AX_ATTR_COLOR_VALUE_GREEN);
612 int b = target->GetIntAttribute(
613 ui::AX_ATTR_COLOR_VALUE_BLUE);
614 base::string16 value_text;
615 value_text = base::IntToString16((r * 100) / 255) + L"% red " +
616 base::IntToString16((g * 100) / 255) + L"% green " +
617 base::IntToString16((b * 100) / 255) + L"% blue";
618 *value = SysAllocString(value_text.c_str());
619 DCHECK(*value);
620 return S_OK;
623 *value = SysAllocString(target->value().c_str());
624 DCHECK(*value);
625 return S_OK;
628 STDMETHODIMP BrowserAccessibilityWin::get_accHelpTopic(BSTR* help_file,
629 VARIANT var_id,
630 LONG* topic_id) {
631 return E_NOTIMPL;
634 STDMETHODIMP BrowserAccessibilityWin::get_accSelection(VARIANT* selected) {
635 if (!instance_active())
636 return E_FAIL;
638 if (GetRole() != ui::AX_ROLE_LIST_BOX)
639 return E_NOTIMPL;
641 unsigned long selected_count = 0;
642 for (size_t i = 0; i < InternalChildCount(); ++i) {
643 if (InternalGetChild(i)->HasState(ui::AX_STATE_SELECTED))
644 ++selected_count;
647 if (selected_count == 0) {
648 selected->vt = VT_EMPTY;
649 return S_OK;
652 if (selected_count == 1) {
653 for (size_t i = 0; i < InternalChildCount(); ++i) {
654 if (InternalGetChild(i)->HasState(ui::AX_STATE_SELECTED)) {
655 selected->vt = VT_DISPATCH;
656 selected->pdispVal =
657 InternalGetChild(i)->ToBrowserAccessibilityWin()->NewReference();
658 return S_OK;
663 // Multiple items are selected.
664 base::win::EnumVariant* enum_variant =
665 new base::win::EnumVariant(selected_count);
666 enum_variant->AddRef();
667 unsigned long index = 0;
668 for (size_t i = 0; i < InternalChildCount(); ++i) {
669 if (InternalGetChild(i)->HasState(ui::AX_STATE_SELECTED)) {
670 enum_variant->ItemAt(index)->vt = VT_DISPATCH;
671 enum_variant->ItemAt(index)->pdispVal =
672 InternalGetChild(i)->ToBrowserAccessibilityWin()->NewReference();
673 ++index;
676 selected->vt = VT_UNKNOWN;
677 selected->punkVal = static_cast<IUnknown*>(
678 static_cast<base::win::IUnknownImpl*>(enum_variant));
679 return S_OK;
682 STDMETHODIMP BrowserAccessibilityWin::accSelect(
683 LONG flags_sel, VARIANT var_id) {
684 if (!instance_active())
685 return E_FAIL;
687 if (flags_sel & SELFLAG_TAKEFOCUS) {
688 manager()->SetFocus(this, true);
689 return S_OK;
692 return S_FALSE;
695 STDMETHODIMP
696 BrowserAccessibilityWin::put_accName(VARIANT var_id, BSTR put_name) {
697 return E_NOTIMPL;
699 STDMETHODIMP
700 BrowserAccessibilityWin::put_accValue(VARIANT var_id, BSTR put_val) {
701 return E_NOTIMPL;
705 // IAccessible2 methods.
708 STDMETHODIMP BrowserAccessibilityWin::role(LONG* role) {
709 if (!instance_active())
710 return E_FAIL;
712 if (!role)
713 return E_INVALIDARG;
715 *role = ia2_role();
717 return S_OK;
720 STDMETHODIMP BrowserAccessibilityWin::get_attributes(BSTR* attributes) {
721 if (!instance_active())
722 return E_FAIL;
724 if (!attributes)
725 return E_INVALIDARG;
727 // The iaccessible2 attributes are a set of key-value pairs
728 // separated by semicolons, with a colon between the key and the value.
729 base::string16 str;
730 const std::vector<base::string16>& attributes_list = ia2_attributes();
731 for (unsigned int i = 0; i < attributes_list.size(); ++i) {
732 str += attributes_list[i] + L';';
735 if (str.empty())
736 return S_FALSE;
738 *attributes = SysAllocString(str.c_str());
739 DCHECK(*attributes);
740 return S_OK;
743 STDMETHODIMP BrowserAccessibilityWin::get_states(AccessibleStates* states) {
744 if (!instance_active())
745 return E_FAIL;
747 if (!states)
748 return E_INVALIDARG;
750 *states = ia2_state();
752 return S_OK;
755 STDMETHODIMP BrowserAccessibilityWin::get_uniqueID(LONG* unique_id) {
756 if (!instance_active())
757 return E_FAIL;
759 if (!unique_id)
760 return E_INVALIDARG;
762 *unique_id = unique_id_win_;
763 return S_OK;
766 STDMETHODIMP BrowserAccessibilityWin::get_windowHandle(HWND* window_handle) {
767 if (!instance_active())
768 return E_FAIL;
770 if (!window_handle)
771 return E_INVALIDARG;
773 *window_handle =
774 manager()->ToBrowserAccessibilityManagerWin()->GetParentHWND();
775 if (!*window_handle)
776 return E_FAIL;
778 return S_OK;
781 STDMETHODIMP BrowserAccessibilityWin::get_indexInParent(LONG* index_in_parent) {
782 if (!instance_active())
783 return E_FAIL;
785 if (!index_in_parent)
786 return E_INVALIDARG;
788 *index_in_parent = this->GetIndexInParent();
789 return S_OK;
792 STDMETHODIMP BrowserAccessibilityWin::get_nRelations(LONG* n_relations) {
793 if (!instance_active())
794 return E_FAIL;
796 if (!n_relations)
797 return E_INVALIDARG;
799 *n_relations = relations_.size();
800 return S_OK;
803 STDMETHODIMP BrowserAccessibilityWin::get_relation(
804 LONG relation_index,
805 IAccessibleRelation** relation) {
806 if (!instance_active())
807 return E_FAIL;
809 if (relation_index < 0 ||
810 relation_index >= static_cast<long>(relations_.size())) {
811 return E_INVALIDARG;
814 if (!relation)
815 return E_INVALIDARG;
817 relations_[relation_index]->AddRef();
818 *relation = relations_[relation_index];
819 return S_OK;
822 STDMETHODIMP BrowserAccessibilityWin::get_relations(
823 LONG max_relations,
824 IAccessibleRelation** relations,
825 LONG* n_relations) {
826 if (!instance_active())
827 return E_FAIL;
829 if (!relations || !n_relations)
830 return E_INVALIDARG;
832 long count = static_cast<long>(relations_.size());
833 *n_relations = count;
834 if (count == 0)
835 return S_FALSE;
837 for (long i = 0; i < count; ++i) {
838 relations_[i]->AddRef();
839 relations[i] = relations_[i];
842 return S_OK;
845 STDMETHODIMP BrowserAccessibilityWin::scrollTo(enum IA2ScrollType scroll_type) {
846 if (!instance_active())
847 return E_FAIL;
849 gfx::Rect r = GetLocation();
850 switch(scroll_type) {
851 case IA2_SCROLL_TYPE_TOP_LEFT:
852 manager()->ScrollToMakeVisible(*this, gfx::Rect(r.x(), r.y(), 0, 0));
853 break;
854 case IA2_SCROLL_TYPE_BOTTOM_RIGHT:
855 manager()->ScrollToMakeVisible(
856 *this, gfx::Rect(r.right(), r.bottom(), 0, 0));
857 break;
858 case IA2_SCROLL_TYPE_TOP_EDGE:
859 manager()->ScrollToMakeVisible(
860 *this, gfx::Rect(r.x(), r.y(), r.width(), 0));
861 break;
862 case IA2_SCROLL_TYPE_BOTTOM_EDGE:
863 manager()->ScrollToMakeVisible(
864 *this, gfx::Rect(r.x(), r.bottom(), r.width(), 0));
865 break;
866 case IA2_SCROLL_TYPE_LEFT_EDGE:
867 manager()->ScrollToMakeVisible(
868 *this, gfx::Rect(r.x(), r.y(), 0, r.height()));
869 break;
870 case IA2_SCROLL_TYPE_RIGHT_EDGE:
871 manager()->ScrollToMakeVisible(
872 *this, gfx::Rect(r.right(), r.y(), 0, r.height()));
873 break;
874 case IA2_SCROLL_TYPE_ANYWHERE:
875 default:
876 manager()->ScrollToMakeVisible(*this, r);
877 break;
880 manager()->ToBrowserAccessibilityManagerWin()->TrackScrollingObject(this);
882 return S_OK;
885 STDMETHODIMP BrowserAccessibilityWin::scrollToPoint(
886 enum IA2CoordinateType coordinate_type,
887 LONG x,
888 LONG y) {
889 if (!instance_active())
890 return E_FAIL;
892 gfx::Point scroll_to(x, y);
894 if (coordinate_type == IA2_COORDTYPE_SCREEN_RELATIVE) {
895 scroll_to -= manager()->GetViewBounds().OffsetFromOrigin();
896 } else if (coordinate_type == IA2_COORDTYPE_PARENT_RELATIVE) {
897 if (GetParent())
898 scroll_to += GetParent()->GetLocation().OffsetFromOrigin();
899 } else {
900 return E_INVALIDARG;
903 manager()->ScrollToPoint(*this, scroll_to);
904 manager()->ToBrowserAccessibilityManagerWin()->TrackScrollingObject(this);
906 return S_OK;
909 STDMETHODIMP BrowserAccessibilityWin::get_groupPosition(
910 LONG* group_level,
911 LONG* similar_items_in_group,
912 LONG* position_in_group) {
913 if (!instance_active())
914 return E_FAIL;
916 if (!group_level || !similar_items_in_group || !position_in_group)
917 return E_INVALIDARG;
919 *group_level = 0;
920 *similar_items_in_group = GetIntAttribute(ui::AX_ATTR_SET_SIZE);
921 *position_in_group = GetIntAttribute(ui::AX_ATTR_POS_IN_SET);
922 return S_OK;
926 // IAccessibleEx methods not implemented.
929 STDMETHODIMP BrowserAccessibilityWin::get_extendedRole(BSTR* extended_role) {
930 return E_NOTIMPL;
932 STDMETHODIMP
933 BrowserAccessibilityWin::get_localizedExtendedRole(
934 BSTR* localized_extended_role) {
935 return E_NOTIMPL;
937 STDMETHODIMP
938 BrowserAccessibilityWin::get_nExtendedStates(LONG* n_extended_states) {
939 return E_NOTIMPL;
941 STDMETHODIMP
942 BrowserAccessibilityWin::get_extendedStates(LONG max_extended_states,
943 BSTR** extended_states,
944 LONG* n_extended_states) {
945 return E_NOTIMPL;
947 STDMETHODIMP
948 BrowserAccessibilityWin::get_localizedExtendedStates(
949 LONG max_localized_extended_states,
950 BSTR** localized_extended_states,
951 LONG* n_localized_extended_states) {
952 return E_NOTIMPL;
954 STDMETHODIMP BrowserAccessibilityWin::get_locale(IA2Locale* locale) {
955 return E_NOTIMPL;
959 // IAccessibleApplication methods.
962 STDMETHODIMP BrowserAccessibilityWin::get_appName(BSTR* app_name) {
963 // No need to check |instance_active()| because this interface is
964 // global, and doesn't depend on any local state.
966 if (!app_name)
967 return E_INVALIDARG;
969 // GetProduct() returns a string like "Chrome/aa.bb.cc.dd", split out
970 // the part before the "/".
971 std::vector<std::string> product_components;
972 base::SplitString(GetContentClient()->GetProduct(), '/', &product_components);
973 DCHECK_EQ(2U, product_components.size());
974 if (product_components.size() != 2)
975 return E_FAIL;
976 *app_name = SysAllocString(base::UTF8ToUTF16(product_components[0]).c_str());
977 DCHECK(*app_name);
978 return *app_name ? S_OK : E_FAIL;
981 STDMETHODIMP BrowserAccessibilityWin::get_appVersion(BSTR* app_version) {
982 // No need to check |instance_active()| because this interface is
983 // global, and doesn't depend on any local state.
985 if (!app_version)
986 return E_INVALIDARG;
988 // GetProduct() returns a string like "Chrome/aa.bb.cc.dd", split out
989 // the part after the "/".
990 std::vector<std::string> product_components;
991 base::SplitString(GetContentClient()->GetProduct(), '/', &product_components);
992 DCHECK_EQ(2U, product_components.size());
993 if (product_components.size() != 2)
994 return E_FAIL;
995 *app_version =
996 SysAllocString(base::UTF8ToUTF16(product_components[1]).c_str());
997 DCHECK(*app_version);
998 return *app_version ? S_OK : E_FAIL;
1001 STDMETHODIMP BrowserAccessibilityWin::get_toolkitName(BSTR* toolkit_name) {
1002 // No need to check |instance_active()| because this interface is
1003 // global, and doesn't depend on any local state.
1005 if (!toolkit_name)
1006 return E_INVALIDARG;
1008 // This is hard-coded; all products based on the Chromium engine
1009 // will have the same toolkit name, so that assistive technology can
1010 // detect any Chrome-based product.
1011 *toolkit_name = SysAllocString(L"Chrome");
1012 DCHECK(*toolkit_name);
1013 return *toolkit_name ? S_OK : E_FAIL;
1016 STDMETHODIMP BrowserAccessibilityWin::get_toolkitVersion(
1017 BSTR* toolkit_version) {
1018 // No need to check |instance_active()| because this interface is
1019 // global, and doesn't depend on any local state.
1021 if (!toolkit_version)
1022 return E_INVALIDARG;
1024 std::string user_agent = GetContentClient()->GetUserAgent();
1025 *toolkit_version = SysAllocString(base::UTF8ToUTF16(user_agent).c_str());
1026 DCHECK(*toolkit_version);
1027 return *toolkit_version ? S_OK : E_FAIL;
1031 // IAccessibleImage methods.
1034 STDMETHODIMP BrowserAccessibilityWin::get_description(BSTR* desc) {
1035 if (!instance_active())
1036 return E_FAIL;
1038 if (!desc)
1039 return E_INVALIDARG;
1041 if (description().empty())
1042 return S_FALSE;
1044 *desc = SysAllocString(description().c_str());
1046 DCHECK(*desc);
1047 return S_OK;
1050 STDMETHODIMP BrowserAccessibilityWin::get_imagePosition(
1051 enum IA2CoordinateType coordinate_type,
1052 LONG* x,
1053 LONG* y) {
1054 if (!instance_active())
1055 return E_FAIL;
1057 if (!x || !y)
1058 return E_INVALIDARG;
1060 if (coordinate_type == IA2_COORDTYPE_SCREEN_RELATIVE) {
1061 HWND parent_hwnd =
1062 manager()->ToBrowserAccessibilityManagerWin()->GetParentHWND();
1063 if (!parent_hwnd)
1064 return E_FAIL;
1065 POINT top_left = {0, 0};
1066 ::ClientToScreen(parent_hwnd, &top_left);
1067 *x = GetLocation().x() + top_left.x;
1068 *y = GetLocation().y() + top_left.y;
1069 } else if (coordinate_type == IA2_COORDTYPE_PARENT_RELATIVE) {
1070 *x = GetLocation().x();
1071 *y = GetLocation().y();
1072 if (GetParent()) {
1073 *x -= GetParent()->GetLocation().x();
1074 *y -= GetParent()->GetLocation().y();
1076 } else {
1077 return E_INVALIDARG;
1080 return S_OK;
1083 STDMETHODIMP BrowserAccessibilityWin::get_imageSize(LONG* height, LONG* width) {
1084 if (!instance_active())
1085 return E_FAIL;
1087 if (!height || !width)
1088 return E_INVALIDARG;
1090 *height = GetLocation().height();
1091 *width = GetLocation().width();
1092 return S_OK;
1096 // IAccessibleTable methods.
1099 STDMETHODIMP BrowserAccessibilityWin::get_accessibleAt(
1100 long row,
1101 long column,
1102 IUnknown** accessible) {
1103 if (!instance_active())
1104 return E_FAIL;
1106 if (!accessible)
1107 return E_INVALIDARG;
1109 int columns;
1110 int rows;
1111 if (!GetIntAttribute(
1112 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
1113 !GetIntAttribute(
1114 ui::AX_ATTR_TABLE_ROW_COUNT, &rows) ||
1115 columns <= 0 ||
1116 rows <= 0) {
1117 return S_FALSE;
1120 if (row < 0 || row >= rows || column < 0 || column >= columns)
1121 return E_INVALIDARG;
1123 const std::vector<int32>& cell_ids = GetIntListAttribute(
1124 ui::AX_ATTR_CELL_IDS);
1125 DCHECK_EQ(columns * rows, static_cast<int>(cell_ids.size()));
1127 int cell_id = cell_ids[row * columns + column];
1128 BrowserAccessibilityWin* cell = GetFromID(cell_id);
1129 if (cell) {
1130 *accessible = static_cast<IAccessible*>(cell->NewReference());
1131 return S_OK;
1134 *accessible = NULL;
1135 return E_INVALIDARG;
1138 STDMETHODIMP BrowserAccessibilityWin::get_caption(IUnknown** accessible) {
1139 if (!instance_active())
1140 return E_FAIL;
1142 if (!accessible)
1143 return E_INVALIDARG;
1145 // TODO(dmazzoni): implement
1146 return S_FALSE;
1149 STDMETHODIMP BrowserAccessibilityWin::get_childIndex(long row,
1150 long column,
1151 long* cell_index) {
1152 if (!instance_active())
1153 return E_FAIL;
1155 if (!cell_index)
1156 return E_INVALIDARG;
1158 int columns;
1159 int rows;
1160 if (!GetIntAttribute(
1161 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
1162 !GetIntAttribute(
1163 ui::AX_ATTR_TABLE_ROW_COUNT, &rows) ||
1164 columns <= 0 ||
1165 rows <= 0) {
1166 return S_FALSE;
1169 if (row < 0 || row >= rows || column < 0 || column >= columns)
1170 return E_INVALIDARG;
1172 const std::vector<int32>& cell_ids = GetIntListAttribute(
1173 ui::AX_ATTR_CELL_IDS);
1174 const std::vector<int32>& unique_cell_ids = GetIntListAttribute(
1175 ui::AX_ATTR_UNIQUE_CELL_IDS);
1176 DCHECK_EQ(columns * rows, static_cast<int>(cell_ids.size()));
1177 int cell_id = cell_ids[row * columns + column];
1178 for (size_t i = 0; i < unique_cell_ids.size(); ++i) {
1179 if (unique_cell_ids[i] == cell_id) {
1180 *cell_index = (long)i;
1181 return S_OK;
1185 return S_FALSE;
1188 STDMETHODIMP BrowserAccessibilityWin::get_columnDescription(long column,
1189 BSTR* description) {
1190 if (!instance_active())
1191 return E_FAIL;
1193 if (!description)
1194 return E_INVALIDARG;
1196 int columns;
1197 int rows;
1198 if (!GetIntAttribute(
1199 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
1200 !GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT, &rows) ||
1201 columns <= 0 ||
1202 rows <= 0) {
1203 return S_FALSE;
1206 if (column < 0 || column >= columns)
1207 return E_INVALIDARG;
1209 const std::vector<int32>& cell_ids = GetIntListAttribute(
1210 ui::AX_ATTR_CELL_IDS);
1211 for (int i = 0; i < rows; ++i) {
1212 int cell_id = cell_ids[i * columns + column];
1213 BrowserAccessibilityWin* cell = static_cast<BrowserAccessibilityWin*>(
1214 manager()->GetFromID(cell_id));
1215 if (cell && cell->GetRole() == ui::AX_ROLE_COLUMN_HEADER) {
1216 base::string16 cell_name = cell->GetString16Attribute(
1217 ui::AX_ATTR_NAME);
1218 if (cell_name.size() > 0) {
1219 *description = SysAllocString(cell_name.c_str());
1220 return S_OK;
1223 if (cell->description().size() > 0) {
1224 *description = SysAllocString(cell->description().c_str());
1225 return S_OK;
1230 return S_FALSE;
1233 STDMETHODIMP BrowserAccessibilityWin::get_columnExtentAt(
1234 long row,
1235 long column,
1236 long* n_columns_spanned) {
1237 if (!instance_active())
1238 return E_FAIL;
1240 if (!n_columns_spanned)
1241 return E_INVALIDARG;
1243 int columns;
1244 int rows;
1245 if (!GetIntAttribute(
1246 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
1247 !GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT, &rows) ||
1248 columns <= 0 ||
1249 rows <= 0) {
1250 return S_FALSE;
1253 if (row < 0 || row >= rows || column < 0 || column >= columns)
1254 return E_INVALIDARG;
1256 const std::vector<int32>& cell_ids = GetIntListAttribute(
1257 ui::AX_ATTR_CELL_IDS);
1258 int cell_id = cell_ids[row * columns + column];
1259 BrowserAccessibilityWin* cell = static_cast<BrowserAccessibilityWin*>(
1260 manager()->GetFromID(cell_id));
1261 int colspan;
1262 if (cell &&
1263 cell->GetIntAttribute(
1264 ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN, &colspan) &&
1265 colspan >= 1) {
1266 *n_columns_spanned = colspan;
1267 return S_OK;
1270 return S_FALSE;
1273 STDMETHODIMP BrowserAccessibilityWin::get_columnHeader(
1274 IAccessibleTable** accessible_table,
1275 long* starting_row_index) {
1276 // TODO(dmazzoni): implement
1277 return E_NOTIMPL;
1280 STDMETHODIMP BrowserAccessibilityWin::get_columnIndex(long cell_index,
1281 long* column_index) {
1282 if (!instance_active())
1283 return E_FAIL;
1285 if (!column_index)
1286 return E_INVALIDARG;
1288 const std::vector<int32>& unique_cell_ids = GetIntListAttribute(
1289 ui::AX_ATTR_UNIQUE_CELL_IDS);
1290 int cell_id_count = static_cast<int>(unique_cell_ids.size());
1291 if (cell_index < 0)
1292 return E_INVALIDARG;
1293 if (cell_index >= cell_id_count)
1294 return S_FALSE;
1296 int cell_id = unique_cell_ids[cell_index];
1297 BrowserAccessibilityWin* cell =
1298 manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin();
1299 int col_index;
1300 if (cell &&
1301 cell->GetIntAttribute(
1302 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &col_index)) {
1303 *column_index = col_index;
1304 return S_OK;
1307 return S_FALSE;
1310 STDMETHODIMP BrowserAccessibilityWin::get_nColumns(long* column_count) {
1311 if (!instance_active())
1312 return E_FAIL;
1314 if (!column_count)
1315 return E_INVALIDARG;
1317 int columns;
1318 if (GetIntAttribute(
1319 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns)) {
1320 *column_count = columns;
1321 return S_OK;
1324 return S_FALSE;
1327 STDMETHODIMP BrowserAccessibilityWin::get_nRows(long* row_count) {
1328 if (!instance_active())
1329 return E_FAIL;
1331 if (!row_count)
1332 return E_INVALIDARG;
1334 int rows;
1335 if (GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT, &rows)) {
1336 *row_count = rows;
1337 return S_OK;
1340 return S_FALSE;
1343 STDMETHODIMP BrowserAccessibilityWin::get_nSelectedChildren(long* cell_count) {
1344 if (!instance_active())
1345 return E_FAIL;
1347 if (!cell_count)
1348 return E_INVALIDARG;
1350 // TODO(dmazzoni): add support for selected cells/rows/columns in tables.
1351 *cell_count = 0;
1352 return S_OK;
1355 STDMETHODIMP BrowserAccessibilityWin::get_nSelectedColumns(long* column_count) {
1356 if (!instance_active())
1357 return E_FAIL;
1359 if (!column_count)
1360 return E_INVALIDARG;
1362 *column_count = 0;
1363 return S_OK;
1366 STDMETHODIMP BrowserAccessibilityWin::get_nSelectedRows(long* row_count) {
1367 if (!instance_active())
1368 return E_FAIL;
1370 if (!row_count)
1371 return E_INVALIDARG;
1373 *row_count = 0;
1374 return S_OK;
1377 STDMETHODIMP BrowserAccessibilityWin::get_rowDescription(long row,
1378 BSTR* description) {
1379 if (!instance_active())
1380 return E_FAIL;
1382 if (!description)
1383 return E_INVALIDARG;
1385 int columns;
1386 int rows;
1387 if (!GetIntAttribute(
1388 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
1389 !GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT, &rows) ||
1390 columns <= 0 ||
1391 rows <= 0) {
1392 return S_FALSE;
1395 if (row < 0 || row >= rows)
1396 return E_INVALIDARG;
1398 const std::vector<int32>& cell_ids = GetIntListAttribute(
1399 ui::AX_ATTR_CELL_IDS);
1400 for (int i = 0; i < columns; ++i) {
1401 int cell_id = cell_ids[row * columns + i];
1402 BrowserAccessibilityWin* cell =
1403 manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin();
1404 if (cell && cell->GetRole() == ui::AX_ROLE_ROW_HEADER) {
1405 base::string16 cell_name = cell->GetString16Attribute(
1406 ui::AX_ATTR_NAME);
1407 if (cell_name.size() > 0) {
1408 *description = SysAllocString(cell_name.c_str());
1409 return S_OK;
1412 if (cell->description().size() > 0) {
1413 *description = SysAllocString(cell->description().c_str());
1414 return S_OK;
1419 return S_FALSE;
1422 STDMETHODIMP BrowserAccessibilityWin::get_rowExtentAt(long row,
1423 long column,
1424 long* n_rows_spanned) {
1425 if (!instance_active())
1426 return E_FAIL;
1428 if (!n_rows_spanned)
1429 return E_INVALIDARG;
1431 int columns;
1432 int rows;
1433 if (!GetIntAttribute(
1434 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
1435 !GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT, &rows) ||
1436 columns <= 0 ||
1437 rows <= 0) {
1438 return S_FALSE;
1441 if (row < 0 || row >= rows || column < 0 || column >= columns)
1442 return E_INVALIDARG;
1444 const std::vector<int32>& cell_ids = GetIntListAttribute(
1445 ui::AX_ATTR_CELL_IDS);
1446 int cell_id = cell_ids[row * columns + column];
1447 BrowserAccessibilityWin* cell =
1448 manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin();
1449 int rowspan;
1450 if (cell &&
1451 cell->GetIntAttribute(
1452 ui::AX_ATTR_TABLE_CELL_ROW_SPAN, &rowspan) &&
1453 rowspan >= 1) {
1454 *n_rows_spanned = rowspan;
1455 return S_OK;
1458 return S_FALSE;
1461 STDMETHODIMP BrowserAccessibilityWin::get_rowHeader(
1462 IAccessibleTable** accessible_table,
1463 long* starting_column_index) {
1464 // TODO(dmazzoni): implement
1465 return E_NOTIMPL;
1468 STDMETHODIMP BrowserAccessibilityWin::get_rowIndex(long cell_index,
1469 long* row_index) {
1470 if (!instance_active())
1471 return E_FAIL;
1473 if (!row_index)
1474 return E_INVALIDARG;
1476 const std::vector<int32>& unique_cell_ids = GetIntListAttribute(
1477 ui::AX_ATTR_UNIQUE_CELL_IDS);
1478 int cell_id_count = static_cast<int>(unique_cell_ids.size());
1479 if (cell_index < 0)
1480 return E_INVALIDARG;
1481 if (cell_index >= cell_id_count)
1482 return S_FALSE;
1484 int cell_id = unique_cell_ids[cell_index];
1485 BrowserAccessibilityWin* cell =
1486 manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin();
1487 int cell_row_index;
1488 if (cell &&
1489 cell->GetIntAttribute(
1490 ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &cell_row_index)) {
1491 *row_index = cell_row_index;
1492 return S_OK;
1495 return S_FALSE;
1498 STDMETHODIMP BrowserAccessibilityWin::get_selectedChildren(long max_children,
1499 long** children,
1500 long* n_children) {
1501 if (!instance_active())
1502 return E_FAIL;
1504 if (!children || !n_children)
1505 return E_INVALIDARG;
1507 // TODO(dmazzoni): Implement this.
1508 *n_children = 0;
1509 return S_OK;
1512 STDMETHODIMP BrowserAccessibilityWin::get_selectedColumns(long max_columns,
1513 long** columns,
1514 long* n_columns) {
1515 if (!instance_active())
1516 return E_FAIL;
1518 if (!columns || !n_columns)
1519 return E_INVALIDARG;
1521 // TODO(dmazzoni): Implement this.
1522 *n_columns = 0;
1523 return S_OK;
1526 STDMETHODIMP BrowserAccessibilityWin::get_selectedRows(long max_rows,
1527 long** rows,
1528 long* n_rows) {
1529 if (!instance_active())
1530 return E_FAIL;
1532 if (!rows || !n_rows)
1533 return E_INVALIDARG;
1535 // TODO(dmazzoni): Implement this.
1536 *n_rows = 0;
1537 return S_OK;
1540 STDMETHODIMP BrowserAccessibilityWin::get_summary(IUnknown** accessible) {
1541 if (!instance_active())
1542 return E_FAIL;
1544 if (!accessible)
1545 return E_INVALIDARG;
1547 // TODO(dmazzoni): implement
1548 return S_FALSE;
1551 STDMETHODIMP BrowserAccessibilityWin::get_isColumnSelected(
1552 long column,
1553 boolean* is_selected) {
1554 if (!instance_active())
1555 return E_FAIL;
1557 if (!is_selected)
1558 return E_INVALIDARG;
1560 // TODO(dmazzoni): Implement this.
1561 *is_selected = false;
1562 return S_OK;
1565 STDMETHODIMP BrowserAccessibilityWin::get_isRowSelected(long row,
1566 boolean* is_selected) {
1567 if (!instance_active())
1568 return E_FAIL;
1570 if (!is_selected)
1571 return E_INVALIDARG;
1573 // TODO(dmazzoni): Implement this.
1574 *is_selected = false;
1575 return S_OK;
1578 STDMETHODIMP BrowserAccessibilityWin::get_isSelected(long row,
1579 long column,
1580 boolean* is_selected) {
1581 if (!instance_active())
1582 return E_FAIL;
1584 if (!is_selected)
1585 return E_INVALIDARG;
1587 // TODO(dmazzoni): Implement this.
1588 *is_selected = false;
1589 return S_OK;
1592 STDMETHODIMP BrowserAccessibilityWin::get_rowColumnExtentsAtIndex(
1593 long index,
1594 long* row,
1595 long* column,
1596 long* row_extents,
1597 long* column_extents,
1598 boolean* is_selected) {
1599 if (!instance_active())
1600 return E_FAIL;
1602 if (!row || !column || !row_extents || !column_extents || !is_selected)
1603 return E_INVALIDARG;
1605 const std::vector<int32>& unique_cell_ids = GetIntListAttribute(
1606 ui::AX_ATTR_UNIQUE_CELL_IDS);
1607 int cell_id_count = static_cast<int>(unique_cell_ids.size());
1608 if (index < 0)
1609 return E_INVALIDARG;
1610 if (index >= cell_id_count)
1611 return S_FALSE;
1613 int cell_id = unique_cell_ids[index];
1614 BrowserAccessibilityWin* cell =
1615 manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin();
1616 int rowspan;
1617 int colspan;
1618 if (cell &&
1619 cell->GetIntAttribute(
1620 ui::AX_ATTR_TABLE_CELL_ROW_SPAN, &rowspan) &&
1621 cell->GetIntAttribute(
1622 ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN, &colspan) &&
1623 rowspan >= 1 &&
1624 colspan >= 1) {
1625 *row_extents = rowspan;
1626 *column_extents = colspan;
1627 return S_OK;
1630 return S_FALSE;
1633 STDMETHODIMP BrowserAccessibilityWin::selectRow(long row) {
1634 return E_NOTIMPL;
1637 STDMETHODIMP BrowserAccessibilityWin::selectColumn(long column) {
1638 return E_NOTIMPL;
1641 STDMETHODIMP BrowserAccessibilityWin::unselectRow(long row) {
1642 return E_NOTIMPL;
1645 STDMETHODIMP BrowserAccessibilityWin::unselectColumn(long column) {
1646 return E_NOTIMPL;
1649 STDMETHODIMP
1650 BrowserAccessibilityWin::get_modelChange(IA2TableModelChange* model_change) {
1651 return E_NOTIMPL;
1655 // IAccessibleTable2 methods.
1658 STDMETHODIMP BrowserAccessibilityWin::get_cellAt(long row,
1659 long column,
1660 IUnknown** cell) {
1661 return get_accessibleAt(row, column, cell);
1664 STDMETHODIMP BrowserAccessibilityWin::get_nSelectedCells(long* cell_count) {
1665 return get_nSelectedChildren(cell_count);
1668 STDMETHODIMP BrowserAccessibilityWin::get_selectedCells(
1669 IUnknown*** cells,
1670 long* n_selected_cells) {
1671 if (!instance_active())
1672 return E_FAIL;
1674 if (!cells || !n_selected_cells)
1675 return E_INVALIDARG;
1677 // TODO(dmazzoni): Implement this.
1678 *n_selected_cells = 0;
1679 return S_OK;
1682 STDMETHODIMP BrowserAccessibilityWin::get_selectedColumns(long** columns,
1683 long* n_columns) {
1684 if (!instance_active())
1685 return E_FAIL;
1687 if (!columns || !n_columns)
1688 return E_INVALIDARG;
1690 // TODO(dmazzoni): Implement this.
1691 *n_columns = 0;
1692 return S_OK;
1695 STDMETHODIMP BrowserAccessibilityWin::get_selectedRows(long** rows,
1696 long* n_rows) {
1697 if (!instance_active())
1698 return E_FAIL;
1700 if (!rows || !n_rows)
1701 return E_INVALIDARG;
1703 // TODO(dmazzoni): Implement this.
1704 *n_rows = 0;
1705 return S_OK;
1710 // IAccessibleTableCell methods.
1713 STDMETHODIMP BrowserAccessibilityWin::get_columnExtent(
1714 long* n_columns_spanned) {
1715 if (!instance_active())
1716 return E_FAIL;
1718 if (!n_columns_spanned)
1719 return E_INVALIDARG;
1721 int colspan;
1722 if (GetIntAttribute(
1723 ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN, &colspan) &&
1724 colspan >= 1) {
1725 *n_columns_spanned = colspan;
1726 return S_OK;
1729 return S_FALSE;
1732 STDMETHODIMP BrowserAccessibilityWin::get_columnHeaderCells(
1733 IUnknown*** cell_accessibles,
1734 long* n_column_header_cells) {
1735 if (!instance_active())
1736 return E_FAIL;
1738 if (!cell_accessibles || !n_column_header_cells)
1739 return E_INVALIDARG;
1741 *n_column_header_cells = 0;
1743 int column;
1744 if (!GetIntAttribute(
1745 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &column)) {
1746 return S_FALSE;
1749 BrowserAccessibility* table = GetParent();
1750 while (table && table->GetRole() != ui::AX_ROLE_TABLE)
1751 table = table->GetParent();
1752 if (!table) {
1753 NOTREACHED();
1754 return S_FALSE;
1757 int columns;
1758 int rows;
1759 if (!table->GetIntAttribute(
1760 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
1761 !table->GetIntAttribute(
1762 ui::AX_ATTR_TABLE_ROW_COUNT, &rows)) {
1763 return S_FALSE;
1765 if (columns <= 0 || rows <= 0 || column < 0 || column >= columns)
1766 return S_FALSE;
1768 const std::vector<int32>& cell_ids = table->GetIntListAttribute(
1769 ui::AX_ATTR_CELL_IDS);
1771 for (int i = 0; i < rows; ++i) {
1772 int cell_id = cell_ids[i * columns + column];
1773 BrowserAccessibilityWin* cell =
1774 manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin();
1775 if (cell && cell->GetRole() == ui::AX_ROLE_COLUMN_HEADER)
1776 (*n_column_header_cells)++;
1779 *cell_accessibles = static_cast<IUnknown**>(CoTaskMemAlloc(
1780 (*n_column_header_cells) * sizeof(cell_accessibles[0])));
1781 int index = 0;
1782 for (int i = 0; i < rows; ++i) {
1783 int cell_id = cell_ids[i * columns + column];
1784 BrowserAccessibility* cell = manager()->GetFromID(cell_id);
1785 if (cell && cell->GetRole() == ui::AX_ROLE_COLUMN_HEADER) {
1786 (*cell_accessibles)[index] = static_cast<IAccessible*>(
1787 cell->ToBrowserAccessibilityWin()->NewReference());
1788 ++index;
1792 return S_OK;
1795 STDMETHODIMP BrowserAccessibilityWin::get_columnIndex(long* column_index) {
1796 if (!instance_active())
1797 return E_FAIL;
1799 if (!column_index)
1800 return E_INVALIDARG;
1802 int column;
1803 if (GetIntAttribute(
1804 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &column)) {
1805 *column_index = column;
1806 return S_OK;
1809 return S_FALSE;
1812 STDMETHODIMP BrowserAccessibilityWin::get_rowExtent(long* n_rows_spanned) {
1813 if (!instance_active())
1814 return E_FAIL;
1816 if (!n_rows_spanned)
1817 return E_INVALIDARG;
1819 int rowspan;
1820 if (GetIntAttribute(
1821 ui::AX_ATTR_TABLE_CELL_ROW_SPAN, &rowspan) &&
1822 rowspan >= 1) {
1823 *n_rows_spanned = rowspan;
1824 return S_OK;
1827 return S_FALSE;
1830 STDMETHODIMP BrowserAccessibilityWin::get_rowHeaderCells(
1831 IUnknown*** cell_accessibles,
1832 long* n_row_header_cells) {
1833 if (!instance_active())
1834 return E_FAIL;
1836 if (!cell_accessibles || !n_row_header_cells)
1837 return E_INVALIDARG;
1839 *n_row_header_cells = 0;
1841 int row;
1842 if (!GetIntAttribute(
1843 ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &row)) {
1844 return S_FALSE;
1847 BrowserAccessibility* table = GetParent();
1848 while (table && table->GetRole() != ui::AX_ROLE_TABLE)
1849 table = table->GetParent();
1850 if (!table) {
1851 NOTREACHED();
1852 return S_FALSE;
1855 int columns;
1856 int rows;
1857 if (!table->GetIntAttribute(
1858 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
1859 !table->GetIntAttribute(
1860 ui::AX_ATTR_TABLE_ROW_COUNT, &rows)) {
1861 return S_FALSE;
1863 if (columns <= 0 || rows <= 0 || row < 0 || row >= rows)
1864 return S_FALSE;
1866 const std::vector<int32>& cell_ids = table->GetIntListAttribute(
1867 ui::AX_ATTR_CELL_IDS);
1869 for (int i = 0; i < columns; ++i) {
1870 int cell_id = cell_ids[row * columns + i];
1871 BrowserAccessibility* cell = manager()->GetFromID(cell_id);
1872 if (cell && cell->GetRole() == ui::AX_ROLE_ROW_HEADER)
1873 (*n_row_header_cells)++;
1876 *cell_accessibles = static_cast<IUnknown**>(CoTaskMemAlloc(
1877 (*n_row_header_cells) * sizeof(cell_accessibles[0])));
1878 int index = 0;
1879 for (int i = 0; i < columns; ++i) {
1880 int cell_id = cell_ids[row * columns + i];
1881 BrowserAccessibility* cell = manager()->GetFromID(cell_id);
1882 if (cell && cell->GetRole() == ui::AX_ROLE_ROW_HEADER) {
1883 (*cell_accessibles)[index] = static_cast<IAccessible*>(
1884 cell->ToBrowserAccessibilityWin()->NewReference());
1885 ++index;
1889 return S_OK;
1892 STDMETHODIMP BrowserAccessibilityWin::get_rowIndex(long* row_index) {
1893 if (!instance_active())
1894 return E_FAIL;
1896 if (!row_index)
1897 return E_INVALIDARG;
1899 int row;
1900 if (GetIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &row)) {
1901 *row_index = row;
1902 return S_OK;
1904 return S_FALSE;
1907 STDMETHODIMP BrowserAccessibilityWin::get_isSelected(boolean* is_selected) {
1908 if (!instance_active())
1909 return E_FAIL;
1911 if (!is_selected)
1912 return E_INVALIDARG;
1914 *is_selected = false;
1915 return S_OK;
1918 STDMETHODIMP BrowserAccessibilityWin::get_rowColumnExtents(
1919 long* row_index,
1920 long* column_index,
1921 long* row_extents,
1922 long* column_extents,
1923 boolean* is_selected) {
1924 if (!instance_active())
1925 return E_FAIL;
1927 if (!row_index ||
1928 !column_index ||
1929 !row_extents ||
1930 !column_extents ||
1931 !is_selected) {
1932 return E_INVALIDARG;
1935 int row;
1936 int column;
1937 int rowspan;
1938 int colspan;
1939 if (GetIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &row) &&
1940 GetIntAttribute(
1941 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &column) &&
1942 GetIntAttribute(
1943 ui::AX_ATTR_TABLE_CELL_ROW_SPAN, &rowspan) &&
1944 GetIntAttribute(
1945 ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN, &colspan)) {
1946 *row_index = row;
1947 *column_index = column;
1948 *row_extents = rowspan;
1949 *column_extents = colspan;
1950 *is_selected = false;
1951 return S_OK;
1954 return S_FALSE;
1957 STDMETHODIMP BrowserAccessibilityWin::get_table(IUnknown** table) {
1958 if (!instance_active())
1959 return E_FAIL;
1961 if (!table)
1962 return E_INVALIDARG;
1965 int row;
1966 int column;
1967 GetIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &row);
1968 GetIntAttribute(ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &column);
1970 BrowserAccessibility* find_table = GetParent();
1971 while (find_table && find_table->GetRole() != ui::AX_ROLE_TABLE)
1972 find_table = find_table->GetParent();
1973 if (!find_table) {
1974 NOTREACHED();
1975 return S_FALSE;
1978 *table = static_cast<IAccessibleTable*>(
1979 find_table->ToBrowserAccessibilityWin()->NewReference());
1981 return S_OK;
1985 // IAccessibleText methods.
1988 STDMETHODIMP BrowserAccessibilityWin::get_nCharacters(LONG* n_characters) {
1989 if (!instance_active())
1990 return E_FAIL;
1992 if (!n_characters)
1993 return E_INVALIDARG;
1995 *n_characters = TextForIAccessibleText().length();
1996 return S_OK;
1999 STDMETHODIMP BrowserAccessibilityWin::get_caretOffset(LONG* offset) {
2000 if (!instance_active())
2001 return E_FAIL;
2003 if (!offset)
2004 return E_INVALIDARG;
2006 // IA2 spec says that caret offset should be -1 if the object is not focused.
2007 if (manager()->GetFocus(this) != this) {
2008 *offset = -1;
2009 return S_FALSE;
2012 *offset = 0;
2013 if (IsEditableText()) {
2014 int sel_start = 0;
2015 if (GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START,
2016 &sel_start))
2017 *offset = sel_start;
2020 return S_OK;
2023 STDMETHODIMP BrowserAccessibilityWin::get_characterExtents(
2024 LONG offset,
2025 enum IA2CoordinateType coordinate_type,
2026 LONG* out_x,
2027 LONG* out_y,
2028 LONG* out_width,
2029 LONG* out_height) {
2030 if (!instance_active())
2031 return E_FAIL;
2033 if (!out_x || !out_y || !out_width || !out_height)
2034 return E_INVALIDARG;
2036 const base::string16& text_str = TextForIAccessibleText();
2037 HandleSpecialTextOffset(text_str, &offset);
2039 if (offset < 0 || offset > static_cast<LONG>(text_str.size()))
2040 return E_INVALIDARG;
2042 gfx::Rect character_bounds;
2043 if (coordinate_type == IA2_COORDTYPE_SCREEN_RELATIVE) {
2044 character_bounds = GetGlobalBoundsForRange(offset, 1);
2045 } else if (coordinate_type == IA2_COORDTYPE_PARENT_RELATIVE) {
2046 character_bounds = GetLocalBoundsForRange(offset, 1);
2047 character_bounds -= GetLocation().OffsetFromOrigin();
2048 } else {
2049 return E_INVALIDARG;
2052 *out_x = character_bounds.x();
2053 *out_y = character_bounds.y();
2054 *out_width = character_bounds.width();
2055 *out_height = character_bounds.height();
2057 return S_OK;
2060 STDMETHODIMP BrowserAccessibilityWin::get_nSelections(LONG* n_selections) {
2061 if (!instance_active())
2062 return E_FAIL;
2064 if (!n_selections)
2065 return E_INVALIDARG;
2067 *n_selections = 0;
2068 if (IsEditableText()) {
2069 int sel_start = 0;
2070 int sel_end = 0;
2071 if (GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START,
2072 &sel_start) &&
2073 GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, &sel_end) &&
2074 sel_start != sel_end)
2075 *n_selections = 1;
2078 return S_OK;
2081 STDMETHODIMP BrowserAccessibilityWin::get_selection(LONG selection_index,
2082 LONG* start_offset,
2083 LONG* end_offset) {
2084 if (!instance_active())
2085 return E_FAIL;
2087 if (!start_offset || !end_offset || selection_index != 0)
2088 return E_INVALIDARG;
2090 LONG n_selections = 0;
2091 if (FAILED(get_nSelections(&n_selections)) || n_selections < 1)
2092 return E_INVALIDARG;
2094 *start_offset = 0;
2095 *end_offset = 0;
2096 if (IsEditableText()) {
2097 int sel_start = 0;
2098 int sel_end = 0;
2099 if (GetIntAttribute(
2100 ui::AX_ATTR_TEXT_SEL_START, &sel_start) &&
2101 GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, &sel_end)) {
2102 *start_offset = sel_start;
2103 *end_offset = sel_end;
2107 return S_OK;
2110 STDMETHODIMP BrowserAccessibilityWin::get_text(LONG start_offset,
2111 LONG end_offset,
2112 BSTR* text) {
2113 if (!instance_active())
2114 return E_FAIL;
2116 if (!text)
2117 return E_INVALIDARG;
2119 const base::string16& text_str = TextForIAccessibleText();
2121 // Handle special text offsets.
2122 HandleSpecialTextOffset(text_str, &start_offset);
2123 HandleSpecialTextOffset(text_str, &end_offset);
2125 // The spec allows the arguments to be reversed.
2126 if (start_offset > end_offset) {
2127 LONG tmp = start_offset;
2128 start_offset = end_offset;
2129 end_offset = tmp;
2132 // The spec does not allow the start or end offsets to be out or range;
2133 // we must return an error if so.
2134 LONG len = text_str.length();
2135 if (start_offset < 0)
2136 return E_INVALIDARG;
2137 if (end_offset > len)
2138 return E_INVALIDARG;
2140 base::string16 substr = text_str.substr(start_offset,
2141 end_offset - start_offset);
2142 if (substr.empty())
2143 return S_FALSE;
2145 *text = SysAllocString(substr.c_str());
2146 DCHECK(*text);
2147 return S_OK;
2150 STDMETHODIMP BrowserAccessibilityWin::get_textAtOffset(
2151 LONG offset,
2152 enum IA2TextBoundaryType boundary_type,
2153 LONG* start_offset,
2154 LONG* end_offset,
2155 BSTR* text) {
2156 if (!instance_active())
2157 return E_FAIL;
2159 if (!start_offset || !end_offset || !text)
2160 return E_INVALIDARG;
2162 const base::string16& text_str = TextForIAccessibleText();
2163 HandleSpecialTextOffset(text_str, &offset);
2164 if (offset < 0)
2165 return E_INVALIDARG;
2167 LONG text_len = text_str.length();
2168 if (offset > text_len)
2169 return E_INVALIDARG;
2171 // The IAccessible2 spec says we don't have to implement the "sentence"
2172 // boundary type, we can just let the screenreader handle it.
2173 if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) {
2174 *start_offset = 0;
2175 *end_offset = 0;
2176 *text = NULL;
2177 return S_FALSE;
2180 // According to the IA2 Spec, only line boundaries should succeed when
2181 // the offset is one past the end of the text.
2182 if (offset == text_len && boundary_type != IA2_TEXT_BOUNDARY_LINE) {
2183 *start_offset = 0;
2184 *end_offset = 0;
2185 *text = nullptr;
2186 return S_FALSE;
2189 *start_offset = FindBoundary(
2190 text_str, boundary_type, offset, ui::BACKWARDS_DIRECTION);
2191 *end_offset = FindBoundary(
2192 text_str, boundary_type, offset, ui::FORWARDS_DIRECTION);
2193 return get_text(*start_offset, *end_offset, text);
2196 STDMETHODIMP BrowserAccessibilityWin::get_textBeforeOffset(
2197 LONG offset,
2198 enum IA2TextBoundaryType boundary_type,
2199 LONG* start_offset,
2200 LONG* end_offset,
2201 BSTR* text) {
2202 if (!instance_active())
2203 return E_FAIL;
2205 if (!start_offset || !end_offset || !text)
2206 return E_INVALIDARG;
2208 // The IAccessible2 spec says we don't have to implement the "sentence"
2209 // boundary type, we can just let the screenreader handle it.
2210 if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) {
2211 *start_offset = 0;
2212 *end_offset = 0;
2213 *text = NULL;
2214 return S_FALSE;
2217 const base::string16& text_str = TextForIAccessibleText();
2219 *start_offset = FindBoundary(
2220 text_str, boundary_type, offset, ui::BACKWARDS_DIRECTION);
2221 *end_offset = offset;
2222 return get_text(*start_offset, *end_offset, text);
2225 STDMETHODIMP BrowserAccessibilityWin::get_textAfterOffset(
2226 LONG offset,
2227 enum IA2TextBoundaryType boundary_type,
2228 LONG* start_offset,
2229 LONG* end_offset,
2230 BSTR* text) {
2231 if (!instance_active())
2232 return E_FAIL;
2234 if (!start_offset || !end_offset || !text)
2235 return E_INVALIDARG;
2237 // The IAccessible2 spec says we don't have to implement the "sentence"
2238 // boundary type, we can just let the screenreader handle it.
2239 if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) {
2240 *start_offset = 0;
2241 *end_offset = 0;
2242 *text = NULL;
2243 return S_FALSE;
2246 const base::string16& text_str = TextForIAccessibleText();
2248 *start_offset = offset;
2249 *end_offset = FindBoundary(
2250 text_str, boundary_type, offset, ui::FORWARDS_DIRECTION);
2251 return get_text(*start_offset, *end_offset, text);
2254 STDMETHODIMP BrowserAccessibilityWin::get_newText(IA2TextSegment* new_text) {
2255 if (!instance_active())
2256 return E_FAIL;
2258 if (!new_text)
2259 return E_INVALIDARG;
2261 if (!old_win_attributes_)
2262 return E_FAIL;
2264 int start, old_len, new_len;
2265 ComputeHypertextRemovedAndInserted(&start, &old_len, &new_len);
2266 if (new_len == 0)
2267 return E_FAIL;
2269 base::string16 substr = hypertext().substr(start, new_len);
2270 new_text->text = SysAllocString(substr.c_str());
2271 new_text->start = static_cast<long>(start);
2272 new_text->end = static_cast<long>(start + new_len);
2273 return S_OK;
2276 STDMETHODIMP BrowserAccessibilityWin::get_oldText(IA2TextSegment* old_text) {
2277 if (!instance_active())
2278 return E_FAIL;
2280 if (!old_text)
2281 return E_INVALIDARG;
2283 if (!old_win_attributes_)
2284 return E_FAIL;
2286 int start, old_len, new_len;
2287 ComputeHypertextRemovedAndInserted(&start, &old_len, &new_len);
2288 if (old_len == 0)
2289 return E_FAIL;
2291 base::string16 old_hypertext = old_win_attributes_->hypertext;
2292 base::string16 substr = old_hypertext.substr(start, old_len);
2293 old_text->text = SysAllocString(substr.c_str());
2294 old_text->start = static_cast<long>(start);
2295 old_text->end = static_cast<long>(start + old_len);
2296 return S_OK;
2299 STDMETHODIMP BrowserAccessibilityWin::get_offsetAtPoint(
2300 LONG x,
2301 LONG y,
2302 enum IA2CoordinateType coord_type,
2303 LONG* offset) {
2304 if (!instance_active())
2305 return E_FAIL;
2307 if (!offset)
2308 return E_INVALIDARG;
2310 // TODO(dmazzoni): implement this. We're returning S_OK for now so that
2311 // screen readers still return partially accurate results rather than
2312 // completely failing.
2313 *offset = 0;
2314 return S_OK;
2317 STDMETHODIMP BrowserAccessibilityWin::scrollSubstringTo(
2318 LONG start_index,
2319 LONG end_index,
2320 enum IA2ScrollType scroll_type) {
2321 // TODO(dmazzoni): adjust this for the start and end index, too.
2322 return scrollTo(scroll_type);
2325 STDMETHODIMP BrowserAccessibilityWin::scrollSubstringToPoint(
2326 LONG start_index,
2327 LONG end_index,
2328 enum IA2CoordinateType coordinate_type,
2329 LONG x, LONG y) {
2330 // TODO(dmazzoni): adjust this for the start and end index, too.
2331 return scrollToPoint(coordinate_type, x, y);
2334 STDMETHODIMP BrowserAccessibilityWin::addSelection(LONG start_offset,
2335 LONG end_offset) {
2336 if (!instance_active())
2337 return E_FAIL;
2339 const base::string16& text_str = TextForIAccessibleText();
2340 HandleSpecialTextOffset(text_str, &start_offset);
2341 HandleSpecialTextOffset(text_str, &end_offset);
2343 manager()->SetTextSelection(*this, start_offset, end_offset);
2344 return S_OK;
2347 STDMETHODIMP BrowserAccessibilityWin::removeSelection(LONG selection_index) {
2348 if (!instance_active())
2349 return E_FAIL;
2351 if (selection_index != 0)
2352 return E_INVALIDARG;
2354 manager()->SetTextSelection(*this, 0, 0);
2355 return S_OK;
2358 STDMETHODIMP BrowserAccessibilityWin::setCaretOffset(LONG offset) {
2359 if (!instance_active())
2360 return E_FAIL;
2362 const base::string16& text_str = TextForIAccessibleText();
2363 HandleSpecialTextOffset(text_str, &offset);
2364 manager()->SetTextSelection(*this, offset, offset);
2365 return S_OK;
2368 STDMETHODIMP BrowserAccessibilityWin::setSelection(LONG selection_index,
2369 LONG start_offset,
2370 LONG end_offset) {
2371 if (!instance_active())
2372 return E_FAIL;
2374 if (selection_index != 0)
2375 return E_INVALIDARG;
2377 const base::string16& text_str = TextForIAccessibleText();
2378 HandleSpecialTextOffset(text_str, &start_offset);
2379 HandleSpecialTextOffset(text_str, &end_offset);
2381 manager()->SetTextSelection(*this, start_offset, end_offset);
2382 return S_OK;
2386 // IAccessibleText methods not implemented.
2389 STDMETHODIMP BrowserAccessibilityWin::get_attributes(LONG offset,
2390 LONG* start_offset,
2391 LONG* end_offset,
2392 BSTR* text_attributes) {
2393 return E_NOTIMPL;
2397 // IAccessibleHypertext methods.
2400 STDMETHODIMP BrowserAccessibilityWin::get_nHyperlinks(long* hyperlink_count) {
2401 if (!instance_active())
2402 return E_FAIL;
2404 if (!hyperlink_count)
2405 return E_INVALIDARG;
2407 *hyperlink_count = hyperlink_offset_to_index().size();
2408 return S_OK;
2411 STDMETHODIMP BrowserAccessibilityWin::get_hyperlink(
2412 long index,
2413 IAccessibleHyperlink** hyperlink) {
2414 if (!instance_active())
2415 return E_FAIL;
2417 if (!hyperlink ||
2418 index < 0 ||
2419 index >= static_cast<long>(hyperlinks().size())) {
2420 return E_INVALIDARG;
2423 int32 id = hyperlinks()[index];
2424 BrowserAccessibilityWin* child =
2425 manager()->GetFromID(id)->ToBrowserAccessibilityWin();
2426 if (child) {
2427 *hyperlink = static_cast<IAccessibleHyperlink*>(child->NewReference());
2428 return S_OK;
2431 return E_FAIL;
2434 STDMETHODIMP BrowserAccessibilityWin::get_hyperlinkIndex(
2435 long char_index,
2436 long* hyperlink_index) {
2437 if (!instance_active())
2438 return E_FAIL;
2440 if (!hyperlink_index)
2441 return E_INVALIDARG;
2443 *hyperlink_index = -1;
2445 if (char_index < 0 ||
2446 char_index >= static_cast<long>(hypertext().size())) {
2447 return E_INVALIDARG;
2450 std::map<int32, int32>::iterator it =
2451 hyperlink_offset_to_index().find(char_index);
2452 if (it == hyperlink_offset_to_index().end())
2453 return E_FAIL;
2455 *hyperlink_index = it->second;
2456 return S_OK;
2460 // IAccessibleHyperlink not implemented.
2463 STDMETHODIMP BrowserAccessibilityWin::get_anchor(long index, VARIANT* anchor) {
2464 return E_NOTIMPL;
2466 STDMETHODIMP
2467 BrowserAccessibilityWin::get_anchorTarget(long index, VARIANT* anchor_target) {
2468 return E_NOTIMPL;
2470 STDMETHODIMP BrowserAccessibilityWin::get_startIndex(long* index) {
2471 return E_NOTIMPL;
2473 STDMETHODIMP BrowserAccessibilityWin::get_endIndex(long* index) {
2474 return E_NOTIMPL;
2476 STDMETHODIMP BrowserAccessibilityWin::get_valid(boolean* valid) {
2477 return E_NOTIMPL;
2481 // IAccessibleAction not implemented.
2484 STDMETHODIMP BrowserAccessibilityWin::nActions(long* n_actions) {
2485 return E_NOTIMPL;
2487 STDMETHODIMP BrowserAccessibilityWin::doAction(long action_index) {
2488 return E_NOTIMPL;
2490 STDMETHODIMP
2491 BrowserAccessibilityWin::get_description(long action_index, BSTR* description) {
2492 return E_NOTIMPL;
2494 STDMETHODIMP BrowserAccessibilityWin::get_keyBinding(long action_index,
2495 long n_max_bindings,
2496 BSTR** key_bindings,
2497 long* n_bindings) {
2498 return E_NOTIMPL;
2500 STDMETHODIMP BrowserAccessibilityWin::get_name(long action_index, BSTR* name) {
2501 return E_NOTIMPL;
2503 STDMETHODIMP
2504 BrowserAccessibilityWin::get_localizedName(long action_index,
2505 BSTR* localized_name) {
2506 return E_NOTIMPL;
2510 // IAccessibleValue methods.
2513 STDMETHODIMP BrowserAccessibilityWin::get_currentValue(VARIANT* value) {
2514 if (!instance_active())
2515 return E_FAIL;
2517 if (!value)
2518 return E_INVALIDARG;
2520 float float_val;
2521 if (GetFloatAttribute(
2522 ui::AX_ATTR_VALUE_FOR_RANGE, &float_val)) {
2523 value->vt = VT_R8;
2524 value->dblVal = float_val;
2525 return S_OK;
2528 value->vt = VT_EMPTY;
2529 return S_FALSE;
2532 STDMETHODIMP BrowserAccessibilityWin::get_minimumValue(VARIANT* value) {
2533 if (!instance_active())
2534 return E_FAIL;
2536 if (!value)
2537 return E_INVALIDARG;
2539 float float_val;
2540 if (GetFloatAttribute(ui::AX_ATTR_MIN_VALUE_FOR_RANGE,
2541 &float_val)) {
2542 value->vt = VT_R8;
2543 value->dblVal = float_val;
2544 return S_OK;
2547 value->vt = VT_EMPTY;
2548 return S_FALSE;
2551 STDMETHODIMP BrowserAccessibilityWin::get_maximumValue(VARIANT* value) {
2552 if (!instance_active())
2553 return E_FAIL;
2555 if (!value)
2556 return E_INVALIDARG;
2558 float float_val;
2559 if (GetFloatAttribute(ui::AX_ATTR_MAX_VALUE_FOR_RANGE,
2560 &float_val)) {
2561 value->vt = VT_R8;
2562 value->dblVal = float_val;
2563 return S_OK;
2566 value->vt = VT_EMPTY;
2567 return S_FALSE;
2570 STDMETHODIMP BrowserAccessibilityWin::setCurrentValue(VARIANT new_value) {
2571 // TODO(dmazzoni): Implement this.
2572 return E_NOTIMPL;
2576 // ISimpleDOMDocument methods.
2579 STDMETHODIMP BrowserAccessibilityWin::get_URL(BSTR* url) {
2580 if (!instance_active())
2581 return E_FAIL;
2583 if (!url)
2584 return E_INVALIDARG;
2586 return GetStringAttributeAsBstr(ui::AX_ATTR_DOC_URL, url);
2589 STDMETHODIMP BrowserAccessibilityWin::get_title(BSTR* title) {
2590 if (!instance_active())
2591 return E_FAIL;
2593 if (!title)
2594 return E_INVALIDARG;
2596 return GetStringAttributeAsBstr(ui::AX_ATTR_DOC_TITLE, title);
2599 STDMETHODIMP BrowserAccessibilityWin::get_mimeType(BSTR* mime_type) {
2600 if (!instance_active())
2601 return E_FAIL;
2603 if (!mime_type)
2604 return E_INVALIDARG;
2606 return GetStringAttributeAsBstr(
2607 ui::AX_ATTR_DOC_MIMETYPE, mime_type);
2610 STDMETHODIMP BrowserAccessibilityWin::get_docType(BSTR* doc_type) {
2611 if (!instance_active())
2612 return E_FAIL;
2614 if (!doc_type)
2615 return E_INVALIDARG;
2617 return GetStringAttributeAsBstr(
2618 ui::AX_ATTR_DOC_DOCTYPE, doc_type);
2621 STDMETHODIMP
2622 BrowserAccessibilityWin::get_nameSpaceURIForID(short name_space_id,
2623 BSTR* name_space_uri) {
2624 return E_NOTIMPL;
2626 STDMETHODIMP
2627 BrowserAccessibilityWin::put_alternateViewMediaTypes(
2628 BSTR* comma_separated_media_types) {
2629 return E_NOTIMPL;
2633 // ISimpleDOMNode methods.
2636 STDMETHODIMP BrowserAccessibilityWin::get_nodeInfo(
2637 BSTR* node_name,
2638 short* name_space_id,
2639 BSTR* node_value,
2640 unsigned int* num_children,
2641 unsigned int* unique_id,
2642 unsigned short* node_type) {
2643 if (!instance_active())
2644 return E_FAIL;
2646 if (!node_name || !name_space_id || !node_value || !num_children ||
2647 !unique_id || !node_type) {
2648 return E_INVALIDARG;
2651 base::string16 tag;
2652 if (GetString16Attribute(ui::AX_ATTR_HTML_TAG, &tag))
2653 *node_name = SysAllocString(tag.c_str());
2654 else
2655 *node_name = NULL;
2657 *name_space_id = 0;
2658 *node_value = SysAllocString(value().c_str());
2659 *num_children = PlatformChildCount();
2660 *unique_id = unique_id_win_;
2662 if (ia_role() == ROLE_SYSTEM_DOCUMENT) {
2663 *node_type = NODETYPE_DOCUMENT;
2664 } else if (ia_role() == ROLE_SYSTEM_TEXT &&
2665 ((ia2_state() & IA2_STATE_EDITABLE) == 0)) {
2666 *node_type = NODETYPE_TEXT;
2667 } else {
2668 *node_type = NODETYPE_ELEMENT;
2671 return S_OK;
2674 STDMETHODIMP BrowserAccessibilityWin::get_attributes(
2675 unsigned short max_attribs,
2676 BSTR* attrib_names,
2677 short* name_space_id,
2678 BSTR* attrib_values,
2679 unsigned short* num_attribs) {
2680 if (!instance_active())
2681 return E_FAIL;
2683 if (!attrib_names || !name_space_id || !attrib_values || !num_attribs)
2684 return E_INVALIDARG;
2686 *num_attribs = max_attribs;
2687 if (*num_attribs > GetHtmlAttributes().size())
2688 *num_attribs = GetHtmlAttributes().size();
2690 for (unsigned short i = 0; i < *num_attribs; ++i) {
2691 attrib_names[i] = SysAllocString(
2692 base::UTF8ToUTF16(GetHtmlAttributes()[i].first).c_str());
2693 name_space_id[i] = 0;
2694 attrib_values[i] = SysAllocString(
2695 base::UTF8ToUTF16(GetHtmlAttributes()[i].second).c_str());
2697 return S_OK;
2700 STDMETHODIMP BrowserAccessibilityWin::get_attributesForNames(
2701 unsigned short num_attribs,
2702 BSTR* attrib_names,
2703 short* name_space_id,
2704 BSTR* attrib_values) {
2705 if (!instance_active())
2706 return E_FAIL;
2708 if (!attrib_names || !name_space_id || !attrib_values)
2709 return E_INVALIDARG;
2711 for (unsigned short i = 0; i < num_attribs; ++i) {
2712 name_space_id[i] = 0;
2713 bool found = false;
2714 std::string name = base::UTF16ToUTF8((LPCWSTR)attrib_names[i]);
2715 for (unsigned int j = 0; j < GetHtmlAttributes().size(); ++j) {
2716 if (GetHtmlAttributes()[j].first == name) {
2717 attrib_values[i] = SysAllocString(
2718 base::UTF8ToUTF16(GetHtmlAttributes()[j].second).c_str());
2719 found = true;
2720 break;
2723 if (!found) {
2724 attrib_values[i] = NULL;
2727 return S_OK;
2730 STDMETHODIMP BrowserAccessibilityWin::get_computedStyle(
2731 unsigned short max_style_properties,
2732 boolean use_alternate_view,
2733 BSTR* style_properties,
2734 BSTR* style_values,
2735 unsigned short *num_style_properties) {
2736 if (!instance_active())
2737 return E_FAIL;
2739 if (!style_properties || !style_values)
2740 return E_INVALIDARG;
2742 // We only cache a single style property for now: DISPLAY
2744 base::string16 display;
2745 if (max_style_properties == 0 ||
2746 !GetString16Attribute(ui::AX_ATTR_DISPLAY, &display)) {
2747 *num_style_properties = 0;
2748 return S_OK;
2751 *num_style_properties = 1;
2752 style_properties[0] = SysAllocString(L"display");
2753 style_values[0] = SysAllocString(display.c_str());
2755 return S_OK;
2758 STDMETHODIMP BrowserAccessibilityWin::get_computedStyleForProperties(
2759 unsigned short num_style_properties,
2760 boolean use_alternate_view,
2761 BSTR* style_properties,
2762 BSTR* style_values) {
2763 if (!instance_active())
2764 return E_FAIL;
2766 if (!style_properties || !style_values)
2767 return E_INVALIDARG;
2769 // We only cache a single style property for now: DISPLAY
2771 for (unsigned short i = 0; i < num_style_properties; ++i) {
2772 base::string16 name = (LPCWSTR)style_properties[i];
2773 base::StringToLowerASCII(&name);
2774 if (name == L"display") {
2775 base::string16 display = GetString16Attribute(
2776 ui::AX_ATTR_DISPLAY);
2777 style_values[i] = SysAllocString(display.c_str());
2778 } else {
2779 style_values[i] = NULL;
2783 return S_OK;
2786 STDMETHODIMP BrowserAccessibilityWin::scrollTo(boolean placeTopLeft) {
2787 return scrollTo(placeTopLeft ?
2788 IA2_SCROLL_TYPE_TOP_LEFT : IA2_SCROLL_TYPE_ANYWHERE);
2791 STDMETHODIMP BrowserAccessibilityWin::get_parentNode(ISimpleDOMNode** node) {
2792 if (!instance_active())
2793 return E_FAIL;
2795 if (!node)
2796 return E_INVALIDARG;
2798 *node = GetParent()->ToBrowserAccessibilityWin()->NewReference();
2799 return S_OK;
2802 STDMETHODIMP BrowserAccessibilityWin::get_firstChild(ISimpleDOMNode** node) {
2803 if (!instance_active())
2804 return E_FAIL;
2806 if (!node)
2807 return E_INVALIDARG;
2809 if (PlatformChildCount() == 0) {
2810 *node = NULL;
2811 return S_FALSE;
2814 *node = PlatformGetChild(0)->ToBrowserAccessibilityWin()->NewReference();
2815 return S_OK;
2818 STDMETHODIMP BrowserAccessibilityWin::get_lastChild(ISimpleDOMNode** node) {
2819 if (!instance_active())
2820 return E_FAIL;
2822 if (!node)
2823 return E_INVALIDARG;
2825 if (PlatformChildCount() == 0) {
2826 *node = NULL;
2827 return S_FALSE;
2830 *node = PlatformGetChild(PlatformChildCount() - 1)
2831 ->ToBrowserAccessibilityWin()->NewReference();
2832 return S_OK;
2835 STDMETHODIMP BrowserAccessibilityWin::get_previousSibling(
2836 ISimpleDOMNode** node) {
2837 if (!instance_active())
2838 return E_FAIL;
2840 if (!node)
2841 return E_INVALIDARG;
2843 if (!GetParent() || GetIndexInParent() <= 0) {
2844 *node = NULL;
2845 return S_FALSE;
2848 *node = GetParent()->InternalGetChild(GetIndexInParent() - 1)->
2849 ToBrowserAccessibilityWin()->NewReference();
2850 return S_OK;
2853 STDMETHODIMP BrowserAccessibilityWin::get_nextSibling(ISimpleDOMNode** node) {
2854 if (!instance_active())
2855 return E_FAIL;
2857 if (!node)
2858 return E_INVALIDARG;
2860 if (!GetParent() ||
2861 GetIndexInParent() < 0 ||
2862 GetIndexInParent() >= static_cast<int>(
2863 GetParent()->InternalChildCount()) - 1) {
2864 *node = NULL;
2865 return S_FALSE;
2868 *node = GetParent()->InternalGetChild(GetIndexInParent() + 1)->
2869 ToBrowserAccessibilityWin()->NewReference();
2870 return S_OK;
2873 STDMETHODIMP BrowserAccessibilityWin::get_childAt(
2874 unsigned int child_index,
2875 ISimpleDOMNode** node) {
2876 if (!instance_active())
2877 return E_FAIL;
2879 if (!node)
2880 return E_INVALIDARG;
2882 if (child_index >= PlatformChildCount())
2883 return E_INVALIDARG;
2885 BrowserAccessibility* child = PlatformGetChild(child_index);
2886 if (!child) {
2887 *node = NULL;
2888 return S_FALSE;
2891 *node = child->ToBrowserAccessibilityWin()->NewReference();
2892 return S_OK;
2895 STDMETHODIMP BrowserAccessibilityWin::get_innerHTML(BSTR* innerHTML) {
2896 return E_NOTIMPL;
2899 STDMETHODIMP
2900 BrowserAccessibilityWin::get_localInterface(void** local_interface) {
2901 return E_NOTIMPL;
2904 STDMETHODIMP BrowserAccessibilityWin::get_language(BSTR* language) {
2905 return E_NOTIMPL;
2909 // ISimpleDOMText methods.
2912 STDMETHODIMP BrowserAccessibilityWin::get_domText(BSTR* dom_text) {
2913 if (!instance_active())
2914 return E_FAIL;
2916 if (!dom_text)
2917 return E_INVALIDARG;
2919 return GetStringAttributeAsBstr(
2920 ui::AX_ATTR_NAME, dom_text);
2923 STDMETHODIMP BrowserAccessibilityWin::get_clippedSubstringBounds(
2924 unsigned int start_index,
2925 unsigned int end_index,
2926 int* out_x,
2927 int* out_y,
2928 int* out_width,
2929 int* out_height) {
2930 // TODO(dmazzoni): fully support this API by intersecting the
2931 // rect with the container's rect.
2932 return get_unclippedSubstringBounds(
2933 start_index, end_index, out_x, out_y, out_width, out_height);
2936 STDMETHODIMP BrowserAccessibilityWin::get_unclippedSubstringBounds(
2937 unsigned int start_index,
2938 unsigned int end_index,
2939 int* out_x,
2940 int* out_y,
2941 int* out_width,
2942 int* out_height) {
2943 if (!instance_active())
2944 return E_FAIL;
2946 if (!out_x || !out_y || !out_width || !out_height)
2947 return E_INVALIDARG;
2949 const base::string16& text_str = TextForIAccessibleText();
2950 if (start_index > text_str.size() ||
2951 end_index > text_str.size() ||
2952 start_index > end_index) {
2953 return E_INVALIDARG;
2956 gfx::Rect bounds = GetGlobalBoundsForRange(
2957 start_index, end_index - start_index);
2958 *out_x = bounds.x();
2959 *out_y = bounds.y();
2960 *out_width = bounds.width();
2961 *out_height = bounds.height();
2962 return S_OK;
2965 STDMETHODIMP BrowserAccessibilityWin::scrollToSubstring(
2966 unsigned int start_index,
2967 unsigned int end_index) {
2968 if (!instance_active())
2969 return E_FAIL;
2971 const base::string16& text_str = TextForIAccessibleText();
2972 if (start_index > text_str.size() ||
2973 end_index > text_str.size() ||
2974 start_index > end_index) {
2975 return E_INVALIDARG;
2978 manager()->ScrollToMakeVisible(*this, GetLocalBoundsForRange(
2979 start_index, end_index - start_index));
2980 manager()->ToBrowserAccessibilityManagerWin()->TrackScrollingObject(this);
2982 return S_OK;
2985 STDMETHODIMP BrowserAccessibilityWin::get_fontFamily(BSTR* font_family) {
2986 return E_NOTIMPL;
2990 // IServiceProvider methods.
2993 STDMETHODIMP BrowserAccessibilityWin::QueryService(REFGUID guidService,
2994 REFIID riid,
2995 void** object) {
2996 if (!instance_active())
2997 return E_FAIL;
2999 // The system uses IAccessible APIs for many purposes, but only
3000 // assistive technology like screen readers uses IAccessible2.
3001 // Enable full accessibility support when IAccessible2 APIs are queried.
3002 if (riid == IID_IAccessible2)
3003 BrowserAccessibilityStateImpl::GetInstance()->EnableAccessibility();
3005 if (guidService == GUID_IAccessibleContentDocument) {
3006 // Special Mozilla extension: return the accessible for the root document.
3007 // Screen readers use this to distinguish between a document loaded event
3008 // on the root document vs on an iframe.
3009 return manager()->GetRoot()->ToBrowserAccessibilityWin()->QueryInterface(
3010 IID_IAccessible2, object);
3013 if (guidService == IID_IAccessible ||
3014 guidService == IID_IAccessible2 ||
3015 guidService == IID_IAccessibleAction ||
3016 guidService == IID_IAccessibleApplication ||
3017 guidService == IID_IAccessibleHyperlink ||
3018 guidService == IID_IAccessibleHypertext ||
3019 guidService == IID_IAccessibleImage ||
3020 guidService == IID_IAccessibleTable ||
3021 guidService == IID_IAccessibleTable2 ||
3022 guidService == IID_IAccessibleTableCell ||
3023 guidService == IID_IAccessibleText ||
3024 guidService == IID_IAccessibleValue ||
3025 guidService == IID_ISimpleDOMDocument ||
3026 guidService == IID_ISimpleDOMNode ||
3027 guidService == IID_ISimpleDOMText ||
3028 guidService == GUID_ISimpleDOM) {
3029 return QueryInterface(riid, object);
3032 // We only support the IAccessibleEx interface on Windows 8 and above. This
3033 // is needed for the on-screen Keyboard to show up in metro mode, when the
3034 // user taps an editable portion on the page.
3035 // All methods in the IAccessibleEx interface are unimplemented.
3036 if (riid == IID_IAccessibleEx &&
3037 base::win::GetVersion() >= base::win::VERSION_WIN8) {
3038 return QueryInterface(riid, object);
3041 *object = NULL;
3042 return E_FAIL;
3045 STDMETHODIMP
3046 BrowserAccessibilityWin::GetObjectForChild(long child_id, IAccessibleEx** ret) {
3047 return E_NOTIMPL;
3050 STDMETHODIMP
3051 BrowserAccessibilityWin::GetIAccessiblePair(IAccessible** acc, long* child_id) {
3052 return E_NOTIMPL;
3055 STDMETHODIMP BrowserAccessibilityWin::GetRuntimeId(SAFEARRAY** runtime_id) {
3056 return E_NOTIMPL;
3059 STDMETHODIMP
3060 BrowserAccessibilityWin::ConvertReturnedElement(
3061 IRawElementProviderSimple* element,
3062 IAccessibleEx** acc) {
3063 return E_NOTIMPL;
3066 STDMETHODIMP BrowserAccessibilityWin::GetPatternProvider(PATTERNID id,
3067 IUnknown** provider) {
3068 DVLOG(1) << "In Function: "
3069 << __FUNCTION__
3070 << " for pattern id: "
3071 << id;
3072 if (id == UIA_ValuePatternId || id == UIA_TextPatternId) {
3073 if (IsEditableText()) {
3074 DVLOG(1) << "Returning UIA text provider";
3075 base::win::UIATextProvider::CreateTextProvider(
3076 GetValueText(), true, provider);
3077 return S_OK;
3080 return E_NOTIMPL;
3083 STDMETHODIMP BrowserAccessibilityWin::GetPropertyValue(PROPERTYID id,
3084 VARIANT* ret) {
3085 DVLOG(1) << "In Function: "
3086 << __FUNCTION__
3087 << " for property id: "
3088 << id;
3089 V_VT(ret) = VT_EMPTY;
3090 if (id == UIA_ControlTypePropertyId) {
3091 if (IsEditableText()) {
3092 V_VT(ret) = VT_I4;
3093 ret->lVal = UIA_EditControlTypeId;
3094 DVLOG(1) << "Returning Edit control type";
3095 } else {
3096 DVLOG(1) << "Returning empty control type";
3099 return S_OK;
3102 STDMETHODIMP BrowserAccessibilityWin::get_ProviderOptions(
3103 enum ProviderOptions* ret) {
3104 return E_NOTIMPL;
3107 STDMETHODIMP BrowserAccessibilityWin::get_HostRawElementProvider(
3108 IRawElementProviderSimple** provider) {
3109 return E_NOTIMPL;
3113 // CComObjectRootEx methods.
3116 // static
3117 HRESULT WINAPI BrowserAccessibilityWin::InternalQueryInterface(
3118 void* this_ptr,
3119 const _ATL_INTMAP_ENTRY* entries,
3120 REFIID iid,
3121 void** object) {
3122 BrowserAccessibilityWin* accessibility =
3123 reinterpret_cast<BrowserAccessibilityWin*>(this_ptr);
3124 int32 ia_role = accessibility->ia_role();
3125 if (iid == IID_IAccessibleImage) {
3126 if (ia_role != ROLE_SYSTEM_GRAPHIC) {
3127 *object = NULL;
3128 return E_NOINTERFACE;
3130 } else if (iid == IID_IAccessibleTable || iid == IID_IAccessibleTable2) {
3131 if (ia_role != ROLE_SYSTEM_TABLE) {
3132 *object = NULL;
3133 return E_NOINTERFACE;
3135 } else if (iid == IID_IAccessibleTableCell) {
3136 if (!accessibility->IsCellOrTableHeaderRole()) {
3137 *object = NULL;
3138 return E_NOINTERFACE;
3140 } else if (iid == IID_IAccessibleValue) {
3141 if (ia_role != ROLE_SYSTEM_PROGRESSBAR &&
3142 ia_role != ROLE_SYSTEM_SCROLLBAR &&
3143 ia_role != ROLE_SYSTEM_SLIDER) {
3144 *object = NULL;
3145 return E_NOINTERFACE;
3147 } else if (iid == IID_ISimpleDOMDocument) {
3148 if (ia_role != ROLE_SYSTEM_DOCUMENT) {
3149 *object = NULL;
3150 return E_NOINTERFACE;
3154 return CComObjectRootBase::InternalQueryInterface(
3155 this_ptr, entries, iid, object);
3159 // Private methods.
3162 void BrowserAccessibilityWin::UpdateStep1ComputeWinAttributes() {
3163 // Swap win_attributes_ to old_win_attributes_, allowing us to see
3164 // exactly what changed and fire appropriate events. Note that
3165 // old_win_attributes_ is cleared at the end of UpdateStep3FireEvents.
3166 old_win_attributes_.swap(win_attributes_);
3167 win_attributes_.reset(new WinAttributes());
3169 InitRoleAndState();
3171 win_attributes_->ia2_attributes.clear();
3173 // Expose autocomplete attribute for combobox and textbox.
3174 StringAttributeToIA2(ui::AX_ATTR_AUTO_COMPLETE, "autocomplete");
3176 // Expose the "display" and "tag" attributes.
3177 StringAttributeToIA2(ui::AX_ATTR_DISPLAY, "display");
3178 StringAttributeToIA2(ui::AX_ATTR_DROPEFFECT, "dropeffect");
3179 StringAttributeToIA2(ui::AX_ATTR_TEXT_INPUT_TYPE, "text-input-type");
3180 StringAttributeToIA2(ui::AX_ATTR_HTML_TAG, "tag");
3181 StringAttributeToIA2(ui::AX_ATTR_ROLE, "xml-roles");
3183 // Expose "level" attribute for headings, trees, etc.
3184 IntAttributeToIA2(ui::AX_ATTR_HIERARCHICAL_LEVEL, "level");
3186 // Expose the set size and position in set.
3187 IntAttributeToIA2(ui::AX_ATTR_SET_SIZE, "setsize");
3188 IntAttributeToIA2(ui::AX_ATTR_POS_IN_SET, "posinset");
3190 if (ia_role() == ROLE_SYSTEM_CHECKBUTTON ||
3191 ia_role() == ROLE_SYSTEM_RADIOBUTTON ||
3192 ia2_role() == IA2_ROLE_CHECK_MENU_ITEM ||
3193 ia2_role() == IA2_ROLE_RADIO_MENU_ITEM ||
3194 ia2_role() == IA2_ROLE_TOGGLE_BUTTON) {
3195 win_attributes_->ia2_attributes.push_back(L"checkable:true");
3198 // Expose live region attributes.
3199 StringAttributeToIA2(ui::AX_ATTR_LIVE_STATUS, "live");
3200 StringAttributeToIA2(ui::AX_ATTR_LIVE_RELEVANT, "relevant");
3201 BoolAttributeToIA2(ui::AX_ATTR_LIVE_ATOMIC, "atomic");
3202 BoolAttributeToIA2(ui::AX_ATTR_LIVE_BUSY, "busy");
3204 // Expose aria-grabbed attributes.
3205 BoolAttributeToIA2(ui::AX_ATTR_GRABBED, "grabbed");
3207 // Expose container live region attributes.
3208 StringAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_STATUS,
3209 "container-live");
3210 StringAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_RELEVANT,
3211 "container-relevant");
3212 BoolAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_ATOMIC,
3213 "container-atomic");
3214 BoolAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_BUSY,
3215 "container-busy");
3217 // Expose table cell index.
3218 if (IsCellOrTableHeaderRole()) {
3219 BrowserAccessibility* table = GetParent();
3220 while (table && table->GetRole() != ui::AX_ROLE_TABLE)
3221 table = table->GetParent();
3222 if (table) {
3223 const std::vector<int32>& unique_cell_ids = table->GetIntListAttribute(
3224 ui::AX_ATTR_UNIQUE_CELL_IDS);
3225 for (size_t i = 0; i < unique_cell_ids.size(); ++i) {
3226 if (unique_cell_ids[i] == GetId()) {
3227 win_attributes_->ia2_attributes.push_back(
3228 base::string16(L"table-cell-index:") + base::IntToString16(i));
3234 // Expose invalid state for form controls and elements with aria-invalid.
3235 int invalid_state;
3236 if (GetIntAttribute(ui::AX_ATTR_INVALID_STATE, &invalid_state)) {
3237 // TODO(nektar): Handle the possibility of having multiple aria-invalid
3238 // attributes defined, e.g., "invalid:spelling,grammar".
3239 switch (invalid_state) {
3240 case ui::AX_INVALID_STATE_FALSE:
3241 win_attributes_->ia2_attributes.push_back(L"invalid:false");
3242 break;
3243 case ui::AX_INVALID_STATE_TRUE:
3244 win_attributes_->ia2_attributes.push_back(L"invalid:true");
3245 break;
3246 case ui::AX_INVALID_STATE_SPELLING:
3247 win_attributes_->ia2_attributes.push_back(L"invalid:spelling");
3248 break;
3249 case ui::AX_INVALID_STATE_GRAMMAR:
3250 win_attributes_->ia2_attributes.push_back(L"invalid:grammar");
3251 break;
3252 case ui::AX_INVALID_STATE_OTHER:
3254 base::string16 aria_invalid_value;
3255 if (GetString16Attribute(ui::AX_ATTR_ARIA_INVALID_VALUE,
3256 &aria_invalid_value)) {
3257 win_attributes_->ia2_attributes.push_back(
3258 L"invalid:" + aria_invalid_value);
3259 } else {
3260 // Set the attribute to L"true", since we cannot be more specific.
3261 win_attributes_->ia2_attributes.push_back(L"invalid:true");
3264 break;
3265 default:
3266 NOTREACHED();
3270 // Expose row or column header sort direction.
3271 int32 sort_direction;
3272 if ((ia_role() == ROLE_SYSTEM_COLUMNHEADER ||
3273 ia_role() == ROLE_SYSTEM_ROWHEADER) &&
3274 GetIntAttribute(ui::AX_ATTR_SORT_DIRECTION, &sort_direction)) {
3275 switch (sort_direction) {
3276 case ui::AX_SORT_DIRECTION_UNSORTED:
3277 win_attributes_->ia2_attributes.push_back(L"sort:none");
3278 break;
3279 case ui::AX_SORT_DIRECTION_ASCENDING:
3280 win_attributes_->ia2_attributes.push_back(L"sort:ascending");
3281 break;
3282 case ui::AX_SORT_DIRECTION_DESCENDING:
3283 win_attributes_->ia2_attributes.push_back(L"sort:descending");
3284 break;
3285 case ui::AX_SORT_DIRECTION_OTHER:
3286 win_attributes_->ia2_attributes.push_back(L"sort:other");
3287 break;
3288 default:
3289 NOTREACHED();
3293 // The calculation of the accessible name of an element has been
3294 // standardized in the HTML to Platform Accessibility APIs Implementation
3295 // Guide (http://www.w3.org/TR/html-aapi/). In order to return the
3296 // appropriate accessible name on Windows, we need to apply some logic
3297 // to the fields we get from WebKit.
3299 // TODO(dmazzoni): move most of this logic into WebKit.
3301 // WebKit gives us:
3303 // name: the default name, e.g. inner text
3304 // title ui element: a reference to a <label> element on the same
3305 // page that labels this node.
3306 // description: accessible labels that override the default name:
3307 // aria-label or aria-labelledby or aria-describedby
3308 // help: the value of the "title" attribute
3310 // On Windows, the logic we apply lets some fields take precedence and
3311 // always returns the primary name in "name" and the secondary name,
3312 // if any, in "description".
3314 int title_elem_id = GetIntAttribute(ui::AX_ATTR_TITLE_UI_ELEMENT);
3315 base::string16 name = GetString16Attribute(ui::AX_ATTR_NAME);
3316 base::string16 description = GetString16Attribute(ui::AX_ATTR_DESCRIPTION);
3317 base::string16 help = GetString16Attribute(ui::AX_ATTR_HELP);
3318 base::string16 value = GetString16Attribute(ui::AX_ATTR_VALUE);
3320 // WebKit annoyingly puts the title in the description if there's no other
3321 // description, which just confuses the rest of the logic. Put it back.
3322 // Now "help" is always the value of the "title" attribute, if present.
3323 base::string16 title_attr;
3324 if (GetHtmlAttribute("title", &title_attr) &&
3325 description == title_attr &&
3326 help.empty()) {
3327 help = description;
3328 description.clear();
3331 // Now implement the main logic: the descripion should become the name if
3332 // it's nonempty, and the help should become the description if
3333 // there's no description - or the name if there's no name or description.
3334 if (!description.empty()) {
3335 name = description;
3336 description.clear();
3338 if (!help.empty() && description.empty()) {
3339 description = help;
3340 help.clear();
3342 if (!description.empty() && name.empty() && !title_elem_id) {
3343 name = description;
3344 description.clear();
3347 // If it's a text field, also consider the placeholder.
3348 base::string16 placeholder;
3349 if (GetRole() == ui::AX_ROLE_TEXT_FIELD &&
3350 HasState(ui::AX_STATE_FOCUSABLE) &&
3351 GetHtmlAttribute("placeholder", &placeholder)) {
3352 if (name.empty() && !title_elem_id) {
3353 name = placeholder;
3354 } else if (description.empty()) {
3355 description = placeholder;
3359 // On Windows, the value of a document should be its url.
3360 if (GetRole() == ui::AX_ROLE_ROOT_WEB_AREA ||
3361 GetRole() == ui::AX_ROLE_WEB_AREA) {
3362 value = GetString16Attribute(ui::AX_ATTR_DOC_URL);
3365 // For certain roles (listbox option, static text, and list marker)
3366 // WebKit stores the main accessible text in the "value" - swap it so
3367 // that it's the "name".
3368 if (name.empty() &&
3369 (GetRole() == ui::AX_ROLE_STATIC_TEXT ||
3370 GetRole() == ui::AX_ROLE_LIST_MARKER ||
3371 IsListBoxOptionOrMenuListOption())) {
3372 base::string16 tmp = value;
3373 value = name;
3374 name = tmp;
3377 // If this doesn't have a value and is linked then set its value to the url
3378 // attribute. This allows screen readers to read an empty link's destination.
3379 if (value.empty() && (ia_state() & STATE_SYSTEM_LINKED))
3380 value = GetString16Attribute(ui::AX_ATTR_URL);
3382 win_attributes_->name = name;
3383 win_attributes_->description = description;
3384 win_attributes_->help = help;
3385 win_attributes_->value = value;
3387 // Clear any old relationships between this node and other nodes.
3388 for (size_t i = 0; i < relations_.size(); ++i)
3389 relations_[i]->Release();
3390 relations_.clear();
3392 // Handle title UI element.
3393 if (title_elem_id) {
3394 // Add a labelled by relationship.
3395 CComObject<BrowserAccessibilityRelation>* relation;
3396 HRESULT hr = CComObject<BrowserAccessibilityRelation>::CreateInstance(
3397 &relation);
3398 DCHECK(SUCCEEDED(hr));
3399 relation->AddRef();
3400 relation->Initialize(this, IA2_RELATION_LABELLED_BY);
3401 relation->AddTarget(title_elem_id);
3402 relations_.push_back(relation);
3405 // Expose slider value.
3406 if (ia_role() == ROLE_SYSTEM_PROGRESSBAR ||
3407 ia_role() == ROLE_SYSTEM_SCROLLBAR ||
3408 ia_role() == ROLE_SYSTEM_SLIDER) {
3409 win_attributes_->ia2_attributes.push_back(L"valuetext:" + GetValueText());
3412 // If this is a web area for a presentational iframe, give it a role of
3413 // something other than DOCUMENT so that the fact that it's a separate doc
3414 // is not exposed to AT.
3415 if (IsWebAreaForPresentationalIframe()) {
3416 win_attributes_->ia_role = ROLE_SYSTEM_GROUPING;
3417 win_attributes_->ia2_role = ROLE_SYSTEM_GROUPING;
3421 void BrowserAccessibilityWin::UpdateStep2ComputeHypertext() {
3422 // Construct the hypertext for this node, which contains the concatenation
3423 // of all of the static text of this node's children and an embedded object
3424 // character for all non-static-text children. Build up a map from the
3425 // character index of each embedded object character to the id of the
3426 // child object it points to.
3427 for (unsigned int i = 0; i < PlatformChildCount(); ++i) {
3428 BrowserAccessibilityWin* child =
3429 PlatformGetChild(i)->ToBrowserAccessibilityWin();
3430 if (child->GetRole() == ui::AX_ROLE_STATIC_TEXT) {
3431 win_attributes_->hypertext += child->name();
3432 } else {
3433 int32 char_offset = hypertext().size();
3434 int32 child_id = child->GetId();
3435 int32 index = hyperlinks().size();
3436 win_attributes_->hyperlink_offset_to_index[char_offset] = index;
3437 win_attributes_->hyperlinks.push_back(child_id);
3438 win_attributes_->hypertext += kEmbeddedCharacter;
3443 void BrowserAccessibilityWin::UpdateStep3FireEvents(bool is_subtree_creation) {
3444 BrowserAccessibilityManagerWin* manager =
3445 this->manager()->ToBrowserAccessibilityManagerWin();
3447 // Fire an event when an alert first appears.
3448 if (ia_role() == ROLE_SYSTEM_ALERT &&
3449 old_win_attributes_->ia_role != ROLE_SYSTEM_ALERT) {
3450 manager->NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, this);
3453 // Fire an event when a new subtree is created.
3454 if (is_subtree_creation)
3455 manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_SHOW, this);
3457 // The rest of the events only fire on changes, not on new objects.
3458 if (old_win_attributes_->ia_role != 0 ||
3459 !old_win_attributes_->role_name.empty()) {
3460 // Fire an event if the name, description, help, or value changes.
3461 if (name() != old_win_attributes_->name)
3462 manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_NAMECHANGE, this);
3463 if (description() != old_win_attributes_->description)
3464 manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_DESCRIPTIONCHANGE, this);
3465 if (help() != old_win_attributes_->help)
3466 manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_HELPCHANGE, this);
3467 if (value() != old_win_attributes_->value)
3468 manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_VALUECHANGE, this);
3469 if (ia_state() != old_win_attributes_->ia_state)
3470 manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_STATECHANGE, this);
3472 // Normally focus events are handled elsewhere, however
3473 // focus for managed descendants is platform-specific.
3474 // Fire a focus event if the focused descendant in a multi-select
3475 // list box changes.
3476 if (GetRole() == ui::AX_ROLE_LIST_BOX_OPTION &&
3477 (ia_state() & STATE_SYSTEM_FOCUSABLE) &&
3478 (ia_state() & STATE_SYSTEM_SELECTABLE) &&
3479 (ia_state() & STATE_SYSTEM_FOCUSED) &&
3480 !(old_win_attributes_->ia_state & STATE_SYSTEM_FOCUSED)) {
3481 manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_FOCUS, this);
3484 // Handle selection being added or removed.
3485 bool is_selected_now = (ia_state() & STATE_SYSTEM_SELECTED) != 0;
3486 bool was_selected_before =
3487 (old_win_attributes_->ia_state & STATE_SYSTEM_SELECTED) != 0;
3488 if (is_selected_now || was_selected_before) {
3489 bool multiselect = false;
3490 if (GetParent() && GetParent()->HasState(ui::AX_STATE_MULTISELECTABLE))
3491 multiselect = true;
3493 if (multiselect) {
3494 // In a multi-select box, fire SELECTIONADD and SELECTIONREMOVE events.
3495 if (is_selected_now && !was_selected_before) {
3496 manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_SELECTIONADD, this);
3497 } else if (!is_selected_now && was_selected_before) {
3498 manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_SELECTIONREMOVE, this);
3500 } else if (is_selected_now && !was_selected_before) {
3501 // In a single-select box, only fire SELECTION events.
3502 manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_SELECTION, this);
3506 // Fire an event if this container object has scrolled.
3507 int sx = 0;
3508 int sy = 0;
3509 if (GetIntAttribute(ui::AX_ATTR_SCROLL_X, &sx) &&
3510 GetIntAttribute(ui::AX_ATTR_SCROLL_Y, &sy)) {
3511 if (sx != previous_scroll_x_ || sy != previous_scroll_y_)
3512 manager->MaybeCallNotifyWinEvent(EVENT_SYSTEM_SCROLLINGEND, this);
3513 previous_scroll_x_ = sx;
3514 previous_scroll_y_ = sy;
3517 // Changing a static text node can affect the IAccessibleText hypertext
3518 // of the parent node, so force an update on the parent.
3519 BrowserAccessibilityWin* parent = GetParent()->ToBrowserAccessibilityWin();
3520 if (parent &&
3521 GetRole() == ui::AX_ROLE_STATIC_TEXT &&
3522 name() != old_win_attributes_->name) {
3523 parent->UpdateStep1ComputeWinAttributes();
3524 parent->UpdateStep2ComputeHypertext();
3525 parent->UpdateStep3FireEvents(false);
3528 // Fire hypertext-related events.
3529 int start, old_len, new_len;
3530 ComputeHypertextRemovedAndInserted(&start, &old_len, &new_len);
3531 if (old_len > 0) {
3532 // In-process screen readers may call IAccessibleText::get_oldText
3533 // in reaction to this event to retrieve the text that was removed.
3534 manager->MaybeCallNotifyWinEvent(IA2_EVENT_TEXT_REMOVED, this);
3536 if (new_len > 0) {
3537 // In-process screen readers may call IAccessibleText::get_newText
3538 // in reaction to this event to retrieve the text that was inserted.
3539 manager->MaybeCallNotifyWinEvent(IA2_EVENT_TEXT_INSERTED, this);
3543 old_win_attributes_.reset(nullptr);
3546 void BrowserAccessibilityWin::OnSubtreeWillBeDeleted() {
3547 manager()->ToBrowserAccessibilityManagerWin()->MaybeCallNotifyWinEvent(
3548 EVENT_OBJECT_HIDE, this);
3551 void BrowserAccessibilityWin::NativeAddReference() {
3552 AddRef();
3555 void BrowserAccessibilityWin::NativeReleaseReference() {
3556 Release();
3559 bool BrowserAccessibilityWin::IsNative() const {
3560 return true;
3563 void BrowserAccessibilityWin::OnLocationChanged() {
3564 manager()->ToBrowserAccessibilityManagerWin()->MaybeCallNotifyWinEvent(
3565 EVENT_OBJECT_LOCATIONCHANGE, this);
3568 BrowserAccessibilityWin* BrowserAccessibilityWin::NewReference() {
3569 AddRef();
3570 return this;
3573 BrowserAccessibilityWin* BrowserAccessibilityWin::GetTargetFromChildID(
3574 const VARIANT& var_id) {
3575 if (var_id.vt != VT_I4)
3576 return NULL;
3578 LONG child_id = var_id.lVal;
3579 if (child_id == CHILDID_SELF)
3580 return this;
3582 if (child_id >= 1 && child_id <= static_cast<LONG>(PlatformChildCount()))
3583 return PlatformGetChild(child_id - 1)->ToBrowserAccessibilityWin();
3585 return manager()->ToBrowserAccessibilityManagerWin()->
3586 GetFromUniqueIdWin(child_id);
3589 HRESULT BrowserAccessibilityWin::GetStringAttributeAsBstr(
3590 ui::AXStringAttribute attribute,
3591 BSTR* value_bstr) {
3592 base::string16 str;
3594 if (!GetString16Attribute(attribute, &str))
3595 return S_FALSE;
3597 if (str.empty())
3598 return S_FALSE;
3600 *value_bstr = SysAllocString(str.c_str());
3601 DCHECK(*value_bstr);
3603 return S_OK;
3606 void BrowserAccessibilityWin::StringAttributeToIA2(
3607 ui::AXStringAttribute attribute,
3608 const char* ia2_attr) {
3609 base::string16 value;
3610 if (GetString16Attribute(attribute, &value)) {
3611 win_attributes_->ia2_attributes.push_back(
3612 base::ASCIIToUTF16(ia2_attr) + L":" + value);
3616 void BrowserAccessibilityWin::BoolAttributeToIA2(
3617 ui::AXBoolAttribute attribute,
3618 const char* ia2_attr) {
3619 bool value;
3620 if (GetBoolAttribute(attribute, &value)) {
3621 win_attributes_->ia2_attributes.push_back(
3622 (base::ASCIIToUTF16(ia2_attr) + L":") +
3623 (value ? L"true" : L"false"));
3627 void BrowserAccessibilityWin::IntAttributeToIA2(
3628 ui::AXIntAttribute attribute,
3629 const char* ia2_attr) {
3630 int value;
3631 if (GetIntAttribute(attribute, &value)) {
3632 win_attributes_->ia2_attributes.push_back(
3633 base::ASCIIToUTF16(ia2_attr) + L":" +
3634 base::IntToString16(value));
3638 base::string16 BrowserAccessibilityWin::GetNameRecursive() const {
3639 if (!name().empty()) {
3640 return name();
3643 base::string16 result;
3644 for (uint32 i = 0; i < PlatformChildCount(); ++i) {
3645 result += PlatformGetChild(i)->ToBrowserAccessibilityWin()->
3646 GetNameRecursive();
3648 return result;
3651 base::string16 BrowserAccessibilityWin::GetValueText() {
3652 float fval;
3653 base::string16 value = this->value();
3655 if (value.empty() &&
3656 GetFloatAttribute(ui::AX_ATTR_VALUE_FOR_RANGE, &fval)) {
3657 value = base::UTF8ToUTF16(base::DoubleToString(fval));
3659 return value;
3662 base::string16 BrowserAccessibilityWin::TextForIAccessibleText() {
3663 if (IsEditableText() || GetRole() == ui::AX_ROLE_MENU_LIST_OPTION)
3664 return value();
3665 return (GetRole() == ui::AX_ROLE_STATIC_TEXT) ? name() : hypertext();
3668 bool BrowserAccessibilityWin::IsSameHypertextCharacter(size_t old_char_index,
3669 size_t new_char_index) {
3670 CHECK(old_win_attributes_);
3672 // For anything other than the "embedded character", we just compare the
3673 // characters directly.
3674 base::char16 old_ch = old_win_attributes_->hypertext[old_char_index];
3675 base::char16 new_ch = win_attributes_->hypertext[new_char_index];
3676 if (old_ch != new_ch)
3677 return false;
3678 if (old_ch == new_ch && new_ch != kEmbeddedCharacter)
3679 return true;
3681 // If it's an embedded character, they're only identical if the child id
3682 // the hyperlink points to is the same.
3683 std::map<int32, int32>& old_offset_to_index =
3684 old_win_attributes_->hyperlink_offset_to_index;
3685 std::vector<int32>& old_hyperlinks = old_win_attributes_->hyperlinks;
3686 int32 old_hyperlinks_count = static_cast<int32>(old_hyperlinks.size());
3687 std::map<int32, int32>::iterator iter;
3688 iter = old_offset_to_index.find(old_char_index);
3689 int old_index = (iter != old_offset_to_index.end()) ? iter->second : -1;
3690 int old_child_id = (old_index >= 0 && old_index < old_hyperlinks_count) ?
3691 old_hyperlinks[old_index] : -1;
3693 std::map<int32, int32>& new_offset_to_index =
3694 win_attributes_->hyperlink_offset_to_index;
3695 std::vector<int32>& new_hyperlinks = win_attributes_->hyperlinks;
3696 int32 new_hyperlinks_count = static_cast<int32>(new_hyperlinks.size());
3697 iter = new_offset_to_index.find(new_char_index);
3698 int new_index = (iter != new_offset_to_index.end()) ? iter->second : -1;
3699 int new_child_id = (new_index >= 0 && new_index < new_hyperlinks_count) ?
3700 new_hyperlinks[new_index] : -1;
3702 return old_child_id == new_child_id;
3705 void BrowserAccessibilityWin::ComputeHypertextRemovedAndInserted(
3706 int* start, int* old_len, int* new_len) {
3707 CHECK(old_win_attributes_);
3709 *start = 0;
3710 *old_len = 0;
3711 *new_len = 0;
3713 const base::string16& old_text = old_win_attributes_->hypertext;
3714 const base::string16& new_text = hypertext();
3716 size_t common_prefix = 0;
3717 while (common_prefix < old_text.size() &&
3718 common_prefix < new_text.size() &&
3719 IsSameHypertextCharacter(common_prefix, common_prefix)) {
3720 ++common_prefix;
3723 size_t common_suffix = 0;
3724 while (common_prefix + common_suffix < old_text.size() &&
3725 common_prefix + common_suffix < new_text.size() &&
3726 IsSameHypertextCharacter(
3727 old_text.size() - common_suffix - 1,
3728 new_text.size() - common_suffix - 1)) {
3729 ++common_suffix;
3732 *start = common_prefix;
3733 *old_len = old_text.size() - common_prefix - common_suffix;
3734 *new_len = new_text.size() - common_prefix - common_suffix;
3737 void BrowserAccessibilityWin::HandleSpecialTextOffset(
3738 const base::string16& text,
3739 LONG* offset) {
3740 if (*offset == IA2_TEXT_OFFSET_LENGTH)
3741 *offset = static_cast<LONG>(text.size());
3742 else if (*offset == IA2_TEXT_OFFSET_CARET)
3743 get_caretOffset(offset);
3746 ui::TextBoundaryType BrowserAccessibilityWin::IA2TextBoundaryToTextBoundary(
3747 IA2TextBoundaryType ia2_boundary) {
3748 switch(ia2_boundary) {
3749 case IA2_TEXT_BOUNDARY_CHAR:
3750 return ui::CHAR_BOUNDARY;
3751 case IA2_TEXT_BOUNDARY_WORD:
3752 return ui::WORD_BOUNDARY;
3753 case IA2_TEXT_BOUNDARY_LINE:
3754 return ui::LINE_BOUNDARY;
3755 case IA2_TEXT_BOUNDARY_SENTENCE:
3756 return ui::SENTENCE_BOUNDARY;
3757 case IA2_TEXT_BOUNDARY_PARAGRAPH:
3758 return ui::PARAGRAPH_BOUNDARY;
3759 case IA2_TEXT_BOUNDARY_ALL:
3760 return ui::ALL_BOUNDARY;
3761 default:
3762 NOTREACHED();
3764 return ui::CHAR_BOUNDARY;
3767 LONG BrowserAccessibilityWin::FindBoundary(
3768 const base::string16& text,
3769 IA2TextBoundaryType ia2_boundary,
3770 LONG start_offset,
3771 ui::TextBoundaryDirection direction) {
3772 HandleSpecialTextOffset(text, &start_offset);
3773 if (ia2_boundary == IA2_TEXT_BOUNDARY_WORD &&
3774 GetRole() == ui::AX_ROLE_TEXT_FIELD) {
3775 return GetWordStartBoundary(static_cast<int>(start_offset), direction);
3778 ui::TextBoundaryType boundary = IA2TextBoundaryToTextBoundary(ia2_boundary);
3779 const std::vector<int32>& line_breaks = GetIntListAttribute(
3780 ui::AX_ATTR_LINE_BREAKS);
3781 return ui::FindAccessibleTextBoundary(
3782 text, line_breaks, boundary, start_offset, direction);
3785 BrowserAccessibilityWin* BrowserAccessibilityWin::GetFromID(int32 id) {
3786 return manager()->GetFromID(id)->ToBrowserAccessibilityWin();
3789 bool BrowserAccessibilityWin::IsListBoxOptionOrMenuListOption() {
3790 if (!GetParent())
3791 return false;
3793 int32 role = GetRole();
3794 int32 parent_role = GetParent()->GetRole();
3796 if (role == ui::AX_ROLE_LIST_BOX_OPTION &&
3797 parent_role == ui::AX_ROLE_LIST_BOX) {
3798 return true;
3801 if (role == ui::AX_ROLE_MENU_LIST_OPTION &&
3802 parent_role == ui::AX_ROLE_MENU_LIST_POPUP) {
3803 return true;
3806 return false;
3809 void BrowserAccessibilityWin::InitRoleAndState() {
3810 int32 ia_role = 0;
3811 int32 ia_state = 0;
3812 base::string16 role_name;
3813 int32 ia2_role = 0;
3814 int32 ia2_state = IA2_STATE_OPAQUE;
3816 if (HasState(ui::AX_STATE_BUSY))
3817 ia_state |= STATE_SYSTEM_BUSY;
3818 if (HasState(ui::AX_STATE_CHECKED))
3819 ia_state |= STATE_SYSTEM_CHECKED;
3820 if (HasState(ui::AX_STATE_COLLAPSED))
3821 ia_state |= STATE_SYSTEM_COLLAPSED;
3822 if (HasState(ui::AX_STATE_EXPANDED))
3823 ia_state |= STATE_SYSTEM_EXPANDED;
3824 if (HasState(ui::AX_STATE_FOCUSABLE))
3825 ia_state |= STATE_SYSTEM_FOCUSABLE;
3826 if (HasState(ui::AX_STATE_HASPOPUP))
3827 ia_state |= STATE_SYSTEM_HASPOPUP;
3828 if (HasState(ui::AX_STATE_INDETERMINATE))
3829 ia_state |= STATE_SYSTEM_INDETERMINATE;
3830 if (HasIntAttribute(ui::AX_ATTR_INVALID_STATE) &&
3831 GetIntAttribute(ui::AX_ATTR_INVALID_STATE) != ui::AX_INVALID_STATE_FALSE)
3832 ia2_state |= IA2_STATE_INVALID_ENTRY;
3833 if (HasState(ui::AX_STATE_INVISIBLE))
3834 ia_state |= STATE_SYSTEM_INVISIBLE;
3835 if (HasState(ui::AX_STATE_LINKED))
3836 ia_state |= STATE_SYSTEM_LINKED;
3837 if (HasState(ui::AX_STATE_MULTISELECTABLE)) {
3838 ia_state |= STATE_SYSTEM_EXTSELECTABLE;
3839 ia_state |= STATE_SYSTEM_MULTISELECTABLE;
3841 // TODO(ctguil): Support STATE_SYSTEM_EXTSELECTABLE/accSelect.
3842 if (HasState(ui::AX_STATE_OFFSCREEN))
3843 ia_state |= STATE_SYSTEM_OFFSCREEN;
3844 if (HasState(ui::AX_STATE_PRESSED))
3845 ia_state |= STATE_SYSTEM_PRESSED;
3846 if (HasState(ui::AX_STATE_PROTECTED))
3847 ia_state |= STATE_SYSTEM_PROTECTED;
3848 if (HasState(ui::AX_STATE_REQUIRED))
3849 ia2_state |= IA2_STATE_REQUIRED;
3850 if (HasState(ui::AX_STATE_SELECTABLE))
3851 ia_state |= STATE_SYSTEM_SELECTABLE;
3852 if (HasState(ui::AX_STATE_SELECTED))
3853 ia_state |= STATE_SYSTEM_SELECTED;
3854 if (HasState(ui::AX_STATE_VISITED))
3855 ia_state |= STATE_SYSTEM_TRAVERSED;
3856 if (!HasState(ui::AX_STATE_ENABLED))
3857 ia_state |= STATE_SYSTEM_UNAVAILABLE;
3858 if (HasState(ui::AX_STATE_VERTICAL))
3859 ia2_state |= IA2_STATE_VERTICAL;
3860 if (HasState(ui::AX_STATE_HORIZONTAL))
3861 ia2_state |= IA2_STATE_HORIZONTAL;
3862 if (HasState(ui::AX_STATE_VISITED))
3863 ia_state |= STATE_SYSTEM_TRAVERSED;
3865 // Expose whether or not the mouse is over an element, but suppress
3866 // this for tests because it can make the test results flaky depending
3867 // on the position of the mouse.
3868 BrowserAccessibilityStateImpl* accessibility_state =
3869 BrowserAccessibilityStateImpl::GetInstance();
3870 if (!accessibility_state->disable_hot_tracking_for_testing()) {
3871 if (HasState(ui::AX_STATE_HOVERED))
3872 ia_state |= STATE_SYSTEM_HOTTRACKED;
3875 // WebKit marks everything as readonly unless it's editable text, so if it's
3876 // not readonly, mark it as editable now. The final computation of the
3877 // READONLY state for MSAA is below, after the switch.
3878 if (!HasState(ui::AX_STATE_READ_ONLY))
3879 ia2_state |= IA2_STATE_EDITABLE;
3881 if (GetBoolAttribute(ui::AX_ATTR_BUTTON_MIXED))
3882 ia_state |= STATE_SYSTEM_MIXED;
3884 if (GetBoolAttribute(ui::AX_ATTR_CAN_SET_VALUE))
3885 ia2_state |= IA2_STATE_EDITABLE;
3887 if (!GetStringAttribute(ui::AX_ATTR_AUTO_COMPLETE).empty())
3888 ia2_state |= IA2_STATE_SUPPORTS_AUTOCOMPLETION;
3890 base::string16 html_tag = GetString16Attribute(
3891 ui::AX_ATTR_HTML_TAG);
3892 switch (GetRole()) {
3893 case ui::AX_ROLE_ALERT:
3894 ia_role = ROLE_SYSTEM_ALERT;
3895 break;
3896 case ui::AX_ROLE_ALERT_DIALOG:
3897 ia_role = ROLE_SYSTEM_DIALOG;
3898 break;
3899 case ui::AX_ROLE_APPLICATION:
3900 ia_role = ROLE_SYSTEM_APPLICATION;
3901 break;
3902 case ui::AX_ROLE_ARTICLE:
3903 ia_role = ROLE_SYSTEM_DOCUMENT;
3904 ia_state |= STATE_SYSTEM_READONLY;
3905 break;
3906 case ui::AX_ROLE_BANNER:
3907 ia_role = ROLE_SYSTEM_GROUPING;
3908 ia2_role = IA2_ROLE_HEADER;
3909 break;
3910 case ui::AX_ROLE_BLOCKQUOTE:
3911 role_name = html_tag;
3912 ia2_role = IA2_ROLE_SECTION;
3913 break;
3914 case ui::AX_ROLE_BUSY_INDICATOR:
3915 ia_role = ROLE_SYSTEM_ANIMATION;
3916 ia_state |= STATE_SYSTEM_READONLY;
3917 break;
3918 case ui::AX_ROLE_BUTTON:
3919 ia_role = ROLE_SYSTEM_PUSHBUTTON;
3920 break;
3921 case ui::AX_ROLE_CANVAS:
3922 if (GetBoolAttribute(ui::AX_ATTR_CANVAS_HAS_FALLBACK)) {
3923 role_name = L"canvas";
3924 ia2_role = IA2_ROLE_CANVAS;
3925 } else {
3926 ia_role = ROLE_SYSTEM_GRAPHIC;
3928 break;
3929 case ui::AX_ROLE_CAPTION:
3930 ia_role = ROLE_SYSTEM_TEXT;
3931 ia2_role = IA2_ROLE_CAPTION;
3932 break;
3933 case ui::AX_ROLE_CELL:
3934 ia_role = ROLE_SYSTEM_CELL;
3935 break;
3936 case ui::AX_ROLE_CHECK_BOX:
3937 ia_role = ROLE_SYSTEM_CHECKBUTTON;
3938 ia2_state |= IA2_STATE_CHECKABLE;
3939 break;
3940 case ui::AX_ROLE_COLOR_WELL:
3941 ia_role = ROLE_SYSTEM_TEXT;
3942 ia2_role = IA2_ROLE_COLOR_CHOOSER;
3943 break;
3944 case ui::AX_ROLE_COLUMN:
3945 ia_role = ROLE_SYSTEM_COLUMN;
3946 break;
3947 case ui::AX_ROLE_COLUMN_HEADER:
3948 ia_role = ROLE_SYSTEM_COLUMNHEADER;
3949 break;
3950 case ui::AX_ROLE_COMBO_BOX:
3951 ia_role = ROLE_SYSTEM_COMBOBOX;
3952 break;
3953 case ui::AX_ROLE_COMPLEMENTARY:
3954 ia_role = ROLE_SYSTEM_GROUPING;
3955 ia2_role = IA2_ROLE_NOTE;
3956 break;
3957 case ui::AX_ROLE_CONTENT_INFO:
3958 ia_role = ROLE_SYSTEM_TEXT;
3959 ia2_role = IA2_ROLE_PARAGRAPH;
3960 break;
3961 case ui::AX_ROLE_DATE:
3962 case ui::AX_ROLE_DATE_TIME:
3963 ia_role = ROLE_SYSTEM_DROPLIST;
3964 ia2_role = IA2_ROLE_DATE_EDITOR;
3965 break;
3966 case ui::AX_ROLE_DIV:
3967 role_name = L"div";
3968 ia_role = ROLE_SYSTEM_GROUPING;
3969 ia2_role = IA2_ROLE_SECTION;
3970 break;
3971 case ui::AX_ROLE_DEFINITION:
3972 role_name = html_tag;
3973 ia2_role = IA2_ROLE_PARAGRAPH;
3974 ia_state |= STATE_SYSTEM_READONLY;
3975 break;
3976 case ui::AX_ROLE_DESCRIPTION_LIST_DETAIL:
3977 role_name = html_tag;
3978 ia_role = ROLE_SYSTEM_TEXT;
3979 ia2_role = IA2_ROLE_PARAGRAPH;
3980 break;
3981 case ui::AX_ROLE_DESCRIPTION_LIST:
3982 role_name = html_tag;
3983 ia_role = ROLE_SYSTEM_LIST;
3984 ia_state |= STATE_SYSTEM_READONLY;
3985 break;
3986 case ui::AX_ROLE_DESCRIPTION_LIST_TERM:
3987 ia_role = ROLE_SYSTEM_LISTITEM;
3988 ia_state |= STATE_SYSTEM_READONLY;
3989 break;
3990 case ui::AX_ROLE_DETAILS:
3991 role_name = html_tag;
3992 ia_role = ROLE_SYSTEM_GROUPING;
3993 break;
3994 case ui::AX_ROLE_DIALOG:
3995 ia_role = ROLE_SYSTEM_DIALOG;
3996 break;
3997 case ui::AX_ROLE_DISCLOSURE_TRIANGLE:
3998 ia_role = ROLE_SYSTEM_PUSHBUTTON;
3999 break;
4000 case ui::AX_ROLE_DOCUMENT:
4001 case ui::AX_ROLE_ROOT_WEB_AREA:
4002 case ui::AX_ROLE_WEB_AREA:
4003 ia_role = ROLE_SYSTEM_DOCUMENT;
4004 ia_state |= STATE_SYSTEM_READONLY;
4005 ia_state |= STATE_SYSTEM_FOCUSABLE;
4006 break;
4007 case ui::AX_ROLE_EMBEDDED_OBJECT:
4008 ia_role = ROLE_SYSTEM_CLIENT;
4009 ia2_role = IA2_ROLE_EMBEDDED_OBJECT;
4010 break;
4011 case ui::AX_ROLE_FIGCAPTION:
4012 role_name = html_tag;
4013 ia2_role = IA2_ROLE_CAPTION;
4014 break;
4015 case ui::AX_ROLE_FIGURE:
4016 ia_role = ROLE_SYSTEM_GROUPING;
4017 break;
4018 case ui::AX_ROLE_FORM:
4019 role_name = L"form";
4020 ia2_role = IA2_ROLE_FORM;
4021 break;
4022 case ui::AX_ROLE_FOOTER:
4023 ia_role = ROLE_SYSTEM_GROUPING;
4024 ia2_role = IA2_ROLE_FOOTER;
4025 break;
4026 case ui::AX_ROLE_GRID:
4027 ia_role = ROLE_SYSTEM_TABLE;
4028 ia_state |= STATE_SYSTEM_READONLY;
4029 break;
4030 case ui::AX_ROLE_GROUP: {
4031 base::string16 aria_role = GetString16Attribute(
4032 ui::AX_ATTR_ROLE);
4033 if (aria_role == L"group" || html_tag == L"fieldset") {
4034 ia_role = ROLE_SYSTEM_GROUPING;
4035 } else if (html_tag == L"li") {
4036 ia_role = ROLE_SYSTEM_LISTITEM;
4037 ia_state |= STATE_SYSTEM_READONLY;
4038 } else {
4039 if (html_tag.empty())
4040 role_name = L"div";
4041 else
4042 role_name = html_tag;
4043 ia2_role = IA2_ROLE_SECTION;
4045 break;
4047 case ui::AX_ROLE_HEADING:
4048 role_name = html_tag;
4049 ia2_role = IA2_ROLE_HEADING;
4050 break;
4051 case ui::AX_ROLE_IFRAME:
4052 ia_role = ROLE_SYSTEM_DOCUMENT;
4053 ia2_role = IA2_ROLE_INTERNAL_FRAME;
4054 ia_state = STATE_SYSTEM_READONLY;
4055 break;
4056 case ui::AX_ROLE_IFRAME_PRESENTATIONAL:
4057 ia_role = ROLE_SYSTEM_GROUPING;
4058 break;
4059 case ui::AX_ROLE_IMAGE:
4060 ia_role = ROLE_SYSTEM_GRAPHIC;
4061 ia_state |= STATE_SYSTEM_READONLY;
4062 break;
4063 case ui::AX_ROLE_IMAGE_MAP:
4064 role_name = html_tag;
4065 ia2_role = IA2_ROLE_IMAGE_MAP;
4066 ia_state |= STATE_SYSTEM_READONLY;
4067 break;
4068 case ui::AX_ROLE_IMAGE_MAP_LINK:
4069 ia_role = ROLE_SYSTEM_LINK;
4070 ia_state |= STATE_SYSTEM_LINKED;
4071 ia_state |= STATE_SYSTEM_READONLY;
4072 break;
4073 case ui::AX_ROLE_LABEL_TEXT:
4074 case ui::AX_ROLE_LEGEND:
4075 ia_role = ROLE_SYSTEM_TEXT;
4076 ia2_role = IA2_ROLE_LABEL;
4077 break;
4078 case ui::AX_ROLE_LINK:
4079 ia_role = ROLE_SYSTEM_LINK;
4080 ia_state |= STATE_SYSTEM_LINKED;
4081 break;
4082 case ui::AX_ROLE_LIST:
4083 ia_role = ROLE_SYSTEM_LIST;
4084 ia_state |= STATE_SYSTEM_READONLY;
4085 break;
4086 case ui::AX_ROLE_LIST_BOX:
4087 ia_role = ROLE_SYSTEM_LIST;
4088 break;
4089 case ui::AX_ROLE_LIST_BOX_OPTION:
4090 ia_role = ROLE_SYSTEM_LISTITEM;
4091 if (ia_state & STATE_SYSTEM_SELECTABLE) {
4092 ia_state |= STATE_SYSTEM_FOCUSABLE;
4093 if (HasState(ui::AX_STATE_FOCUSED))
4094 ia_state |= STATE_SYSTEM_FOCUSED;
4096 break;
4097 case ui::AX_ROLE_LIST_ITEM:
4098 ia_role = ROLE_SYSTEM_LISTITEM;
4099 ia_state |= STATE_SYSTEM_READONLY;
4100 break;
4101 case ui::AX_ROLE_MAIN:
4102 ia_role = ROLE_SYSTEM_GROUPING;
4103 ia2_role = IA2_ROLE_PARAGRAPH;
4104 break;
4105 case ui::AX_ROLE_MARQUEE:
4106 ia_role = ROLE_SYSTEM_ANIMATION;
4107 break;
4108 case ui::AX_ROLE_MATH:
4109 ia_role = ROLE_SYSTEM_EQUATION;
4110 break;
4111 case ui::AX_ROLE_MENU:
4112 case ui::AX_ROLE_MENU_BUTTON:
4113 ia_role = ROLE_SYSTEM_MENUPOPUP;
4114 break;
4115 case ui::AX_ROLE_MENU_BAR:
4116 ia_role = ROLE_SYSTEM_MENUBAR;
4117 break;
4118 case ui::AX_ROLE_MENU_ITEM:
4119 ia_role = ROLE_SYSTEM_MENUITEM;
4120 break;
4121 case ui::AX_ROLE_MENU_ITEM_CHECK_BOX:
4122 ia_role = ROLE_SYSTEM_MENUITEM;
4123 ia2_role = IA2_ROLE_CHECK_MENU_ITEM;
4124 ia2_state |= IA2_STATE_CHECKABLE;
4125 break;
4126 case ui::AX_ROLE_MENU_ITEM_RADIO:
4127 ia_role = ROLE_SYSTEM_MENUITEM;
4128 ia2_role = IA2_ROLE_RADIO_MENU_ITEM;
4129 break;
4130 case ui::AX_ROLE_MENU_LIST_POPUP:
4131 ia_role = ROLE_SYSTEM_LIST;
4132 ia2_state &= ~(IA2_STATE_EDITABLE);
4133 break;
4134 case ui::AX_ROLE_MENU_LIST_OPTION:
4135 ia_role = ROLE_SYSTEM_LISTITEM;
4136 ia2_state &= ~(IA2_STATE_EDITABLE);
4137 if (ia_state & STATE_SYSTEM_SELECTABLE) {
4138 ia_state |= STATE_SYSTEM_FOCUSABLE;
4139 if (HasState(ui::AX_STATE_FOCUSED))
4140 ia_state |= STATE_SYSTEM_FOCUSED;
4142 break;
4143 case ui::AX_ROLE_METER:
4144 role_name = html_tag;
4145 ia_role = ROLE_SYSTEM_PROGRESSBAR;
4146 break;
4147 case ui::AX_ROLE_NAVIGATION:
4148 ia_role = ROLE_SYSTEM_GROUPING;
4149 ia2_role = IA2_ROLE_SECTION;
4150 break;
4151 case ui::AX_ROLE_NOTE:
4152 ia_role = ROLE_SYSTEM_GROUPING;
4153 ia2_role = IA2_ROLE_NOTE;
4154 break;
4155 case ui::AX_ROLE_OUTLINE:
4156 ia_role = ROLE_SYSTEM_OUTLINE;
4157 break;
4158 case ui::AX_ROLE_PARAGRAPH:
4159 role_name = L"P";
4160 ia2_role = IA2_ROLE_PARAGRAPH;
4161 break;
4162 case ui::AX_ROLE_POP_UP_BUTTON:
4163 if (html_tag == L"select") {
4164 ia_role = ROLE_SYSTEM_COMBOBOX;
4165 } else {
4166 ia_role = ROLE_SYSTEM_BUTTONMENU;
4168 break;
4169 case ui::AX_ROLE_PRE:
4170 role_name = html_tag;
4171 ia_role = ROLE_SYSTEM_TEXT;
4172 ia2_role = IA2_ROLE_PARAGRAPH;
4173 break;
4174 case ui::AX_ROLE_PROGRESS_INDICATOR:
4175 ia_role = ROLE_SYSTEM_PROGRESSBAR;
4176 ia_state |= STATE_SYSTEM_READONLY;
4177 break;
4178 case ui::AX_ROLE_RADIO_BUTTON:
4179 ia_role = ROLE_SYSTEM_RADIOBUTTON;
4180 ia2_state = IA2_STATE_CHECKABLE;
4181 break;
4182 case ui::AX_ROLE_RADIO_GROUP:
4183 ia_role = ROLE_SYSTEM_GROUPING;
4184 break;
4185 case ui::AX_ROLE_REGION:
4186 if (html_tag == L"section") {
4187 ia_role = ROLE_SYSTEM_GROUPING;
4188 ia2_role = IA2_ROLE_SECTION;
4189 } else {
4190 ia_role = ROLE_SYSTEM_PANE;
4192 break;
4193 case ui::AX_ROLE_ROW:
4194 ia_role = ROLE_SYSTEM_ROW;
4195 break;
4196 case ui::AX_ROLE_ROW_HEADER:
4197 ia_role = ROLE_SYSTEM_ROWHEADER;
4198 break;
4199 case ui::AX_ROLE_RUBY:
4200 ia_role = ROLE_SYSTEM_TEXT;
4201 ia2_role = IA2_ROLE_TEXT_FRAME;
4202 break;
4203 case ui::AX_ROLE_RULER:
4204 ia_role = ROLE_SYSTEM_CLIENT;
4205 ia2_role = IA2_ROLE_RULER;
4206 ia_state |= STATE_SYSTEM_READONLY;
4207 break;
4208 case ui::AX_ROLE_SCROLL_AREA:
4209 ia_role = ROLE_SYSTEM_CLIENT;
4210 ia2_role = IA2_ROLE_SCROLL_PANE;
4211 ia_state |= STATE_SYSTEM_READONLY;
4212 ia2_state &= ~(IA2_STATE_EDITABLE);
4213 break;
4214 case ui::AX_ROLE_SCROLL_BAR:
4215 ia_role = ROLE_SYSTEM_SCROLLBAR;
4216 break;
4217 case ui::AX_ROLE_SEARCH:
4218 ia_role = ROLE_SYSTEM_GROUPING;
4219 ia2_role = IA2_ROLE_SECTION;
4220 break;
4221 case ui::AX_ROLE_SLIDER:
4222 ia_role = ROLE_SYSTEM_SLIDER;
4223 break;
4224 case ui::AX_ROLE_SPIN_BUTTON:
4225 ia_role = ROLE_SYSTEM_SPINBUTTON;
4226 break;
4227 case ui::AX_ROLE_SPIN_BUTTON_PART:
4228 ia_role = ROLE_SYSTEM_PUSHBUTTON;
4229 break;
4230 case ui::AX_ROLE_ANNOTATION:
4231 case ui::AX_ROLE_LIST_MARKER:
4232 case ui::AX_ROLE_STATIC_TEXT:
4233 ia_role = ROLE_SYSTEM_STATICTEXT;
4234 break;
4235 case ui::AX_ROLE_STATUS:
4236 ia_role = ROLE_SYSTEM_STATUSBAR;
4237 break;
4238 case ui::AX_ROLE_SPLITTER:
4239 ia_role = ROLE_SYSTEM_SEPARATOR;
4240 break;
4241 case ui::AX_ROLE_SVG_ROOT:
4242 ia_role = ROLE_SYSTEM_GRAPHIC;
4243 break;
4244 case ui::AX_ROLE_SWITCH:
4245 role_name = L"switch";
4246 ia2_role = IA2_ROLE_TOGGLE_BUTTON;
4247 break;
4248 case ui::AX_ROLE_TAB:
4249 ia_role = ROLE_SYSTEM_PAGETAB;
4250 break;
4251 case ui::AX_ROLE_TABLE: {
4252 base::string16 aria_role = GetString16Attribute(
4253 ui::AX_ATTR_ROLE);
4254 if (aria_role == L"treegrid") {
4255 ia_role = ROLE_SYSTEM_OUTLINE;
4256 } else {
4257 ia_role = ROLE_SYSTEM_TABLE;
4259 break;
4261 case ui::AX_ROLE_TABLE_HEADER_CONTAINER:
4262 ia_role = ROLE_SYSTEM_GROUPING;
4263 ia2_role = IA2_ROLE_SECTION;
4264 ia_state |= STATE_SYSTEM_READONLY;
4265 break;
4266 case ui::AX_ROLE_TAB_LIST:
4267 ia_role = ROLE_SYSTEM_PAGETABLIST;
4268 break;
4269 case ui::AX_ROLE_TAB_PANEL:
4270 ia_role = ROLE_SYSTEM_PROPERTYPAGE;
4271 break;
4272 case ui::AX_ROLE_TOGGLE_BUTTON:
4273 ia_role = ROLE_SYSTEM_PUSHBUTTON;
4274 ia2_role = IA2_ROLE_TOGGLE_BUTTON;
4275 break;
4276 case ui::AX_ROLE_TEXT_FIELD:
4277 case ui::AX_ROLE_SEARCH_BOX:
4278 ia_role = ROLE_SYSTEM_TEXT;
4279 if (HasState(ui::AX_STATE_MULTILINE))
4280 ia2_state |= IA2_STATE_MULTI_LINE;
4281 else
4282 ia2_state |= IA2_STATE_SINGLE_LINE;
4283 ia2_state |= IA2_STATE_EDITABLE;
4284 ia2_state |= IA2_STATE_SELECTABLE_TEXT;
4285 break;
4286 case ui::AX_ROLE_TIME:
4287 ia_role = ROLE_SYSTEM_SPINBUTTON;
4288 break;
4289 case ui::AX_ROLE_TIMER:
4290 ia_role = ROLE_SYSTEM_CLOCK;
4291 ia_state |= STATE_SYSTEM_READONLY;
4292 break;
4293 case ui::AX_ROLE_TOOLBAR:
4294 ia_role = ROLE_SYSTEM_TOOLBAR;
4295 ia_state |= STATE_SYSTEM_READONLY;
4296 break;
4297 case ui::AX_ROLE_TOOLTIP:
4298 ia_role = ROLE_SYSTEM_TOOLTIP;
4299 ia_state |= STATE_SYSTEM_READONLY;
4300 break;
4301 case ui::AX_ROLE_TREE:
4302 ia_role = ROLE_SYSTEM_OUTLINE;
4303 break;
4304 case ui::AX_ROLE_TREE_GRID:
4305 ia_role = ROLE_SYSTEM_OUTLINE;
4306 break;
4307 case ui::AX_ROLE_TREE_ITEM:
4308 ia_role = ROLE_SYSTEM_OUTLINEITEM;
4309 break;
4310 case ui::AX_ROLE_LINE_BREAK:
4311 ia_role = ROLE_SYSTEM_WHITESPACE;
4312 break;
4313 case ui::AX_ROLE_WINDOW:
4314 ia_role = ROLE_SYSTEM_WINDOW;
4315 break;
4317 // TODO(dmazzoni): figure out the proper MSAA role for all of these.
4318 case ui::AX_ROLE_DIRECTORY:
4319 case ui::AX_ROLE_IGNORED:
4320 case ui::AX_ROLE_LOG:
4321 case ui::AX_ROLE_NONE:
4322 case ui::AX_ROLE_PRESENTATIONAL:
4323 case ui::AX_ROLE_SLIDER_THUMB:
4324 default:
4325 ia_role = ROLE_SYSTEM_CLIENT;
4326 break;
4329 // Compute the final value of READONLY for MSAA.
4331 // We always set the READONLY state for elements that have the
4332 // aria-readonly attribute and for a few roles (in the switch above).
4333 // We clear the READONLY state on focusable controls and on a document.
4334 // Everything else, the majority of objects, do not have this state set.
4335 if (HasState(ui::AX_STATE_FOCUSABLE) &&
4336 ia_role != ROLE_SYSTEM_DOCUMENT) {
4337 ia_state &= ~(STATE_SYSTEM_READONLY);
4339 if (!HasState(ui::AX_STATE_READ_ONLY))
4340 ia_state &= ~(STATE_SYSTEM_READONLY);
4341 if (GetBoolAttribute(ui::AX_ATTR_ARIA_READONLY))
4342 ia_state |= STATE_SYSTEM_READONLY;
4344 // The role should always be set.
4345 DCHECK(!role_name.empty() || ia_role);
4347 // If we didn't explicitly set the IAccessible2 role, make it the same
4348 // as the MSAA role.
4349 if (!ia2_role)
4350 ia2_role = ia_role;
4352 win_attributes_->ia_role = ia_role;
4353 win_attributes_->ia_state = ia_state;
4354 win_attributes_->role_name = role_name;
4355 win_attributes_->ia2_role = ia2_role;
4356 win_attributes_->ia2_state = ia2_state;
4359 } // namespace content