• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "content/browser/accessibility/browser_accessibility_win.h"
6 
7 #include <UIAutomationClient.h>
8 #include <UIAutomationCoreApi.h>
9 
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 
25 namespace content {
26 
27 // These nonstandard GUIDs are taken directly from the Mozilla sources
28 // (accessible/src/msaa/nsAccessNodeWrap.cpp); some documentation is here:
29 // http://developer.mozilla.org/en/Accessibility/AT-APIs/ImplementationFeatures/MSAA
30 const GUID GUID_ISimpleDOM = {
31     0x0c539790, 0x12e4, 0x11cf,
32     0xb6, 0x61, 0x00, 0xaa, 0x00, 0x4c, 0xd6, 0xd8};
33 const GUID GUID_IAccessibleContentDocument = {
34     0xa5d8e1f3, 0x3571, 0x4d8f,
35     0x95, 0x21, 0x07, 0xed, 0x28, 0xfb, 0x07, 0x2e};
36 
37 const base::char16 BrowserAccessibilityWin::kEmbeddedCharacter[] = L"\xfffc";
38 
39 // static
40 LONG BrowserAccessibilityWin::next_unique_id_win_ =
41     base::win::kFirstBrowserAccessibilityManagerAccessibilityId;
42 
43 //
44 // BrowserAccessibilityRelation
45 //
46 // A simple implementation of IAccessibleRelation, used to represent
47 // a relationship between two accessible nodes in the tree.
48 //
49 
50 class BrowserAccessibilityRelation
51     : public CComObjectRootEx<CComMultiThreadModel>,
52       public IAccessibleRelation {
53   BEGIN_COM_MAP(BrowserAccessibilityRelation)
COM_INTERFACE_ENTRY(IAccessibleRelation)54     COM_INTERFACE_ENTRY(IAccessibleRelation)
55   END_COM_MAP()
56 
57   CONTENT_EXPORT BrowserAccessibilityRelation() {}
~BrowserAccessibilityRelation()58   CONTENT_EXPORT virtual ~BrowserAccessibilityRelation() {}
59 
60   CONTENT_EXPORT void Initialize(BrowserAccessibilityWin* owner,
61                                  const base::string16& type);
62   CONTENT_EXPORT void AddTarget(int target_id);
63 
64   // IAccessibleRelation methods.
65   CONTENT_EXPORT STDMETHODIMP get_relationType(BSTR* relation_type);
66   CONTENT_EXPORT STDMETHODIMP get_nTargets(long* n_targets);
67   CONTENT_EXPORT STDMETHODIMP get_target(long target_index, IUnknown** target);
68   CONTENT_EXPORT STDMETHODIMP get_targets(long max_targets,
69                                           IUnknown** targets,
70                                           long* n_targets);
71 
72   // IAccessibleRelation methods not implemented.
get_localizedRelationType(BSTR * relation_type)73   CONTENT_EXPORT STDMETHODIMP get_localizedRelationType(BSTR* relation_type) {
74     return E_NOTIMPL;
75   }
76 
77  private:
78   base::string16 type_;
79   base::win::ScopedComPtr<BrowserAccessibilityWin> owner_;
80   std::vector<int> target_ids_;
81 };
82 
Initialize(BrowserAccessibilityWin * owner,const base::string16 & type)83 void BrowserAccessibilityRelation::Initialize(BrowserAccessibilityWin* owner,
84                                               const base::string16& type) {
85   owner_ = owner;
86   type_ = type;
87 }
88 
AddTarget(int target_id)89 void BrowserAccessibilityRelation::AddTarget(int target_id) {
90   target_ids_.push_back(target_id);
91 }
92 
get_relationType(BSTR * relation_type)93 STDMETHODIMP BrowserAccessibilityRelation::get_relationType(
94     BSTR* relation_type) {
95   if (!relation_type)
96     return E_INVALIDARG;
97 
98   if (!owner_->instance_active())
99     return E_FAIL;
100 
101   *relation_type = SysAllocString(type_.c_str());
102   DCHECK(*relation_type);
103   return S_OK;
104 }
105 
get_nTargets(long * n_targets)106 STDMETHODIMP BrowserAccessibilityRelation::get_nTargets(long* n_targets) {
107   if (!n_targets)
108     return E_INVALIDARG;
109 
110   if (!owner_->instance_active())
111     return E_FAIL;
112 
113   *n_targets = static_cast<long>(target_ids_.size());
114 
115   BrowserAccessibilityManager* manager = owner_->manager();
116   for (long i = *n_targets - 1; i >= 0; --i) {
117     BrowserAccessibility* result = manager->GetFromID(target_ids_[i]);
118     if (!result || !result->instance_active()) {
119       *n_targets = 0;
120       break;
121     }
122   }
123   return S_OK;
124 }
125 
get_target(long target_index,IUnknown ** target)126 STDMETHODIMP BrowserAccessibilityRelation::get_target(long target_index,
127                                                       IUnknown** target) {
128   if (!target)
129     return E_INVALIDARG;
130 
131   if (!owner_->instance_active())
132     return E_FAIL;
133 
134   if (target_index < 0 ||
135       target_index >= static_cast<long>(target_ids_.size())) {
136     return E_INVALIDARG;
137   }
138 
139   BrowserAccessibilityManager* manager = owner_->manager();
140   BrowserAccessibility* result =
141       manager->GetFromID(target_ids_[target_index]);
142   if (!result || !result->instance_active())
143     return E_FAIL;
144 
145   *target = static_cast<IAccessible*>(
146       result->ToBrowserAccessibilityWin()->NewReference());
147   return S_OK;
148 }
149 
get_targets(long max_targets,IUnknown ** targets,long * n_targets)150 STDMETHODIMP BrowserAccessibilityRelation::get_targets(long max_targets,
151                                                        IUnknown** targets,
152                                                        long* n_targets) {
153   if (!targets || !n_targets)
154     return E_INVALIDARG;
155 
156   if (!owner_->instance_active())
157     return E_FAIL;
158 
159   long count = static_cast<long>(target_ids_.size());
160   if (count > max_targets)
161     count = max_targets;
162 
163   *n_targets = count;
164   if (count == 0)
165     return S_FALSE;
166 
167   for (long i = 0; i < count; ++i) {
168     HRESULT result = get_target(i, &targets[i]);
169     if (result != S_OK)
170       return result;
171   }
172 
173   return S_OK;
174 }
175 
176 //
177 // BrowserAccessibilityWin
178 //
179 
180 // static
Create()181 BrowserAccessibility* BrowserAccessibility::Create() {
182   CComObject<BrowserAccessibilityWin>* instance;
183   HRESULT hr = CComObject<BrowserAccessibilityWin>::CreateInstance(&instance);
184   DCHECK(SUCCEEDED(hr));
185   return instance->NewReference();
186 }
187 
ToBrowserAccessibilityWin()188 BrowserAccessibilityWin* BrowserAccessibility::ToBrowserAccessibilityWin() {
189   return static_cast<BrowserAccessibilityWin*>(this);
190 }
191 
BrowserAccessibilityWin()192 BrowserAccessibilityWin::BrowserAccessibilityWin()
193     : ia_role_(0),
194       ia_state_(0),
195       ia2_role_(0),
196       ia2_state_(0),
197       first_time_(true),
198       old_ia_state_(0),
199       previous_scroll_x_(0),
200       previous_scroll_y_(0) {
201   // Start unique IDs at -1 and decrement each time, because get_accChild
202   // uses positive IDs to enumerate children, so we use negative IDs to
203   // clearly distinguish between indices and unique IDs.
204   unique_id_win_ = next_unique_id_win_;
205   if (next_unique_id_win_ ==
206           base::win::kLastBrowserAccessibilityManagerAccessibilityId) {
207     next_unique_id_win_ =
208         base::win::kFirstBrowserAccessibilityManagerAccessibilityId;
209   }
210   next_unique_id_win_--;
211 }
212 
~BrowserAccessibilityWin()213 BrowserAccessibilityWin::~BrowserAccessibilityWin() {
214   for (size_t i = 0; i < relations_.size(); ++i)
215     relations_[i]->Release();
216 }
217 
218 //
219 // IAccessible methods.
220 //
221 // Conventions:
222 // * Always test for instance_active() first and return E_FAIL if it's false.
223 // * Always check for invalid arguments first, even if they're unused.
224 // * Return S_FALSE if the only output is a string argument and it's empty.
225 //
226 
accDoDefaultAction(VARIANT var_id)227 HRESULT BrowserAccessibilityWin::accDoDefaultAction(VARIANT var_id) {
228   if (!instance_active())
229     return E_FAIL;
230 
231   BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
232   if (!target)
233     return E_INVALIDARG;
234 
235   manager()->DoDefaultAction(*target);
236   return S_OK;
237 }
238 
accHitTest(LONG x_left,LONG y_top,VARIANT * child)239 STDMETHODIMP BrowserAccessibilityWin::accHitTest(LONG x_left,
240                                                  LONG y_top,
241                                                  VARIANT* child) {
242   if (!instance_active())
243     return E_FAIL;
244 
245   if (!child)
246     return E_INVALIDARG;
247 
248   gfx::Point point(x_left, y_top);
249   if (!GetGlobalBoundsRect().Contains(point)) {
250     // Return S_FALSE and VT_EMPTY when the outside the object's boundaries.
251     child->vt = VT_EMPTY;
252     return S_FALSE;
253   }
254 
255   BrowserAccessibility* result = BrowserAccessibilityForPoint(point);
256   if (result == this) {
257     // Point is within this object.
258     child->vt = VT_I4;
259     child->lVal = CHILDID_SELF;
260   } else {
261     child->vt = VT_DISPATCH;
262     child->pdispVal = result->ToBrowserAccessibilityWin()->NewReference();
263   }
264   return S_OK;
265 }
266 
accLocation(LONG * x_left,LONG * y_top,LONG * width,LONG * height,VARIANT var_id)267 STDMETHODIMP BrowserAccessibilityWin::accLocation(LONG* x_left,
268                                                   LONG* y_top,
269                                                   LONG* width,
270                                                   LONG* height,
271                                                   VARIANT var_id) {
272   if (!instance_active())
273     return E_FAIL;
274 
275   if (!x_left || !y_top || !width || !height)
276     return E_INVALIDARG;
277 
278   BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
279   if (!target)
280     return E_INVALIDARG;
281 
282   gfx::Rect bounds = target->GetGlobalBoundsRect();
283   *x_left = bounds.x();
284   *y_top  = bounds.y();
285   *width  = bounds.width();
286   *height = bounds.height();
287 
288   return S_OK;
289 }
290 
accNavigate(LONG nav_dir,VARIANT start,VARIANT * end)291 STDMETHODIMP BrowserAccessibilityWin::accNavigate(LONG nav_dir,
292                                                   VARIANT start,
293                                                   VARIANT* end) {
294   BrowserAccessibilityWin* target = GetTargetFromChildID(start);
295   if (!target)
296     return E_INVALIDARG;
297 
298   if ((nav_dir == NAVDIR_LASTCHILD || nav_dir == NAVDIR_FIRSTCHILD) &&
299       start.lVal != CHILDID_SELF) {
300     // MSAA states that navigating to first/last child can only be from self.
301     return E_INVALIDARG;
302   }
303 
304   uint32 child_count = target->PlatformChildCount();
305 
306   BrowserAccessibility* result = NULL;
307   switch (nav_dir) {
308     case NAVDIR_DOWN:
309     case NAVDIR_UP:
310     case NAVDIR_LEFT:
311     case NAVDIR_RIGHT:
312       // These directions are not implemented, matching Mozilla and IE.
313       return E_NOTIMPL;
314     case NAVDIR_FIRSTCHILD:
315       if (child_count > 0)
316         result = target->PlatformGetChild(0);
317       break;
318     case NAVDIR_LASTCHILD:
319       if (child_count > 0)
320         result = target->PlatformGetChild(child_count - 1);
321       break;
322     case NAVDIR_NEXT:
323       result = target->GetNextSibling();
324       break;
325     case NAVDIR_PREVIOUS:
326       result = target->GetPreviousSibling();
327       break;
328   }
329 
330   if (!result) {
331     end->vt = VT_EMPTY;
332     return S_FALSE;
333   }
334 
335   end->vt = VT_DISPATCH;
336   end->pdispVal = result->ToBrowserAccessibilityWin()->NewReference();
337   return S_OK;
338 }
339 
get_accChild(VARIANT var_child,IDispatch ** disp_child)340 STDMETHODIMP BrowserAccessibilityWin::get_accChild(VARIANT var_child,
341                                                    IDispatch** disp_child) {
342   if (!instance_active())
343     return E_FAIL;
344 
345   if (!disp_child)
346     return E_INVALIDARG;
347 
348   *disp_child = NULL;
349 
350   BrowserAccessibilityWin* target = GetTargetFromChildID(var_child);
351   if (!target)
352     return E_INVALIDARG;
353 
354   (*disp_child) = target->NewReference();
355   return S_OK;
356 }
357 
get_accChildCount(LONG * child_count)358 STDMETHODIMP BrowserAccessibilityWin::get_accChildCount(LONG* child_count) {
359   if (!instance_active())
360     return E_FAIL;
361 
362   if (!child_count)
363     return E_INVALIDARG;
364 
365   *child_count = PlatformChildCount();
366 
367   return S_OK;
368 }
369 
get_accDefaultAction(VARIANT var_id,BSTR * def_action)370 STDMETHODIMP BrowserAccessibilityWin::get_accDefaultAction(VARIANT var_id,
371                                                            BSTR* def_action) {
372   if (!instance_active())
373     return E_FAIL;
374 
375   if (!def_action)
376     return E_INVALIDARG;
377 
378   BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
379   if (!target)
380     return E_INVALIDARG;
381 
382   return target->GetStringAttributeAsBstr(
383       ui::AX_ATTR_SHORTCUT, def_action);
384 }
385 
get_accDescription(VARIANT var_id,BSTR * desc)386 STDMETHODIMP BrowserAccessibilityWin::get_accDescription(VARIANT var_id,
387                                                          BSTR* desc) {
388   if (!instance_active())
389     return E_FAIL;
390 
391   if (!desc)
392     return E_INVALIDARG;
393 
394   BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
395   if (!target)
396     return E_INVALIDARG;
397 
398   return target->GetStringAttributeAsBstr(
399       ui::AX_ATTR_DESCRIPTION, desc);
400 }
401 
get_accFocus(VARIANT * focus_child)402 STDMETHODIMP BrowserAccessibilityWin::get_accFocus(VARIANT* focus_child) {
403   if (!instance_active())
404     return E_FAIL;
405 
406   if (!focus_child)
407     return E_INVALIDARG;
408 
409   BrowserAccessibilityWin* focus = static_cast<BrowserAccessibilityWin*>(
410       manager()->GetFocus(this));
411   if (focus == this) {
412     focus_child->vt = VT_I4;
413     focus_child->lVal = CHILDID_SELF;
414   } else if (focus == NULL) {
415     focus_child->vt = VT_EMPTY;
416   } else {
417     focus_child->vt = VT_DISPATCH;
418     focus_child->pdispVal = focus->NewReference();
419   }
420 
421   return S_OK;
422 }
423 
get_accHelp(VARIANT var_id,BSTR * help)424 STDMETHODIMP BrowserAccessibilityWin::get_accHelp(VARIANT var_id, BSTR* help) {
425   if (!instance_active())
426     return E_FAIL;
427 
428   if (!help)
429     return E_INVALIDARG;
430 
431   BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
432   if (!target)
433     return E_INVALIDARG;
434 
435   return target->GetStringAttributeAsBstr(
436       ui::AX_ATTR_HELP, help);
437 }
438 
get_accKeyboardShortcut(VARIANT var_id,BSTR * acc_key)439 STDMETHODIMP BrowserAccessibilityWin::get_accKeyboardShortcut(VARIANT var_id,
440                                                               BSTR* acc_key) {
441   if (!instance_active())
442     return E_FAIL;
443 
444   if (!acc_key)
445     return E_INVALIDARG;
446 
447   BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
448   if (!target)
449     return E_INVALIDARG;
450 
451   return target->GetStringAttributeAsBstr(
452       ui::AX_ATTR_SHORTCUT, acc_key);
453 }
454 
get_accName(VARIANT var_id,BSTR * name)455 STDMETHODIMP BrowserAccessibilityWin::get_accName(VARIANT var_id, BSTR* name) {
456   if (!instance_active())
457     return E_FAIL;
458 
459   if (!name)
460     return E_INVALIDARG;
461 
462   BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
463   if (!target)
464     return E_INVALIDARG;
465 
466   std::string name_str = target->name();
467 
468   // If the name is empty, see if it's labeled by another element.
469   if (name_str.empty()) {
470     int title_elem_id;
471     if (target->GetIntAttribute(ui::AX_ATTR_TITLE_UI_ELEMENT,
472                                 &title_elem_id)) {
473       BrowserAccessibility* title_elem =
474           manager()->GetFromID(title_elem_id);
475       if (title_elem)
476         name_str = title_elem->GetTextRecursive();
477     }
478   }
479 
480   if (name_str.empty())
481     return S_FALSE;
482 
483   *name = SysAllocString(base::UTF8ToUTF16(name_str).c_str());
484 
485   DCHECK(*name);
486   return S_OK;
487 }
488 
get_accParent(IDispatch ** disp_parent)489 STDMETHODIMP BrowserAccessibilityWin::get_accParent(IDispatch** disp_parent) {
490   if (!instance_active())
491     return E_FAIL;
492 
493   if (!disp_parent)
494     return E_INVALIDARG;
495 
496   IAccessible* parent_obj = GetParent()->ToBrowserAccessibilityWin();
497   if (parent_obj == NULL) {
498     // This happens if we're the root of the tree;
499     // return the IAccessible for the window.
500     parent_obj =
501          manager()->ToBrowserAccessibilityManagerWin()->parent_iaccessible();
502     // |parent| can only be NULL if the manager was created before the parent
503     // IAccessible was known and it wasn't subsequently set before a client
504     // requested it. This has been fixed. |parent| may also be NULL during
505     // destruction. Possible cases where this could occur include tabs being
506     // dragged to a new window, etc.
507     if (!parent_obj) {
508       DVLOG(1) <<  "In Function: "
509                << __FUNCTION__
510                << ". Parent IAccessible interface is NULL. Returning failure";
511       return E_FAIL;
512     }
513   }
514   parent_obj->AddRef();
515   *disp_parent = parent_obj;
516   return S_OK;
517 }
518 
get_accRole(VARIANT var_id,VARIANT * role)519 STDMETHODIMP BrowserAccessibilityWin::get_accRole(VARIANT var_id,
520                                                   VARIANT* role) {
521   if (!instance_active())
522     return E_FAIL;
523 
524   if (!role)
525     return E_INVALIDARG;
526 
527   BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
528   if (!target)
529     return E_INVALIDARG;
530 
531   if (!target->role_name_.empty()) {
532     role->vt = VT_BSTR;
533     role->bstrVal = SysAllocString(target->role_name_.c_str());
534   } else {
535     role->vt = VT_I4;
536     role->lVal = target->ia_role_;
537   }
538   return S_OK;
539 }
540 
get_accState(VARIANT var_id,VARIANT * state)541 STDMETHODIMP BrowserAccessibilityWin::get_accState(VARIANT var_id,
542                                                    VARIANT* state) {
543   if (!instance_active())
544     return E_FAIL;
545 
546   if (!state)
547     return E_INVALIDARG;
548 
549   BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
550   if (!target)
551     return E_INVALIDARG;
552 
553   state->vt = VT_I4;
554   state->lVal = target->ia_state_;
555   if (manager()->GetFocus(NULL) == this)
556     state->lVal |= STATE_SYSTEM_FOCUSED;
557 
558   return S_OK;
559 }
560 
get_accValue(VARIANT var_id,BSTR * value)561 STDMETHODIMP BrowserAccessibilityWin::get_accValue(VARIANT var_id,
562                                                    BSTR* value) {
563   if (!instance_active())
564     return E_FAIL;
565 
566   if (!value)
567     return E_INVALIDARG;
568 
569   BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
570   if (!target)
571     return E_INVALIDARG;
572 
573   if (target->ia_role() == ROLE_SYSTEM_PROGRESSBAR ||
574       target->ia_role() == ROLE_SYSTEM_SCROLLBAR ||
575       target->ia_role() == ROLE_SYSTEM_SLIDER) {
576     base::string16 value_text = target->GetValueText();
577     *value = SysAllocString(value_text.c_str());
578     DCHECK(*value);
579     return S_OK;
580   }
581 
582   // Expose color well value.
583   if (target->ia2_role() == IA2_ROLE_COLOR_CHOOSER) {
584     int r = target->GetIntAttribute(
585         ui::AX_ATTR_COLOR_VALUE_RED);
586     int g = target->GetIntAttribute(
587         ui::AX_ATTR_COLOR_VALUE_GREEN);
588     int b = target->GetIntAttribute(
589         ui::AX_ATTR_COLOR_VALUE_BLUE);
590     base::string16 value_text;
591     value_text = base::IntToString16((r * 100) / 255) + L"% red " +
592                  base::IntToString16((g * 100) / 255) + L"% green " +
593                  base::IntToString16((b * 100) / 255) + L"% blue";
594     *value = SysAllocString(value_text.c_str());
595     DCHECK(*value);
596     return S_OK;
597   }
598 
599   *value = SysAllocString(base::UTF8ToUTF16(target->value()).c_str());
600   DCHECK(*value);
601   return S_OK;
602 }
603 
get_accHelpTopic(BSTR * help_file,VARIANT var_id,LONG * topic_id)604 STDMETHODIMP BrowserAccessibilityWin::get_accHelpTopic(BSTR* help_file,
605                                                        VARIANT var_id,
606                                                        LONG* topic_id) {
607   return E_NOTIMPL;
608 }
609 
get_accSelection(VARIANT * selected)610 STDMETHODIMP BrowserAccessibilityWin::get_accSelection(VARIANT* selected) {
611   if (!instance_active())
612     return E_FAIL;
613 
614   if (GetRole() != ui::AX_ROLE_LIST_BOX)
615     return E_NOTIMPL;
616 
617   unsigned long selected_count = 0;
618   for (size_t i = 0; i < InternalChildCount(); ++i) {
619     if (InternalGetChild(i)->HasState(ui::AX_STATE_SELECTED))
620       ++selected_count;
621   }
622 
623   if (selected_count == 0) {
624     selected->vt = VT_EMPTY;
625     return S_OK;
626   }
627 
628   if (selected_count == 1) {
629     for (size_t i = 0; i < InternalChildCount(); ++i) {
630       if (InternalGetChild(i)->HasState(ui::AX_STATE_SELECTED)) {
631         selected->vt = VT_DISPATCH;
632         selected->pdispVal =
633             InternalGetChild(i)->ToBrowserAccessibilityWin()->NewReference();
634         return S_OK;
635       }
636     }
637   }
638 
639   // Multiple items are selected.
640   base::win::EnumVariant* enum_variant =
641       new base::win::EnumVariant(selected_count);
642   enum_variant->AddRef();
643   unsigned long index = 0;
644   for (size_t i = 0; i < InternalChildCount(); ++i) {
645     if (InternalGetChild(i)->HasState(ui::AX_STATE_SELECTED)) {
646       enum_variant->ItemAt(index)->vt = VT_DISPATCH;
647       enum_variant->ItemAt(index)->pdispVal =
648         InternalGetChild(i)->ToBrowserAccessibilityWin()->NewReference();
649       ++index;
650     }
651   }
652   selected->vt = VT_UNKNOWN;
653   selected->punkVal = static_cast<IUnknown*>(
654       static_cast<base::win::IUnknownImpl*>(enum_variant));
655   return S_OK;
656 }
657 
accSelect(LONG flags_sel,VARIANT var_id)658 STDMETHODIMP BrowserAccessibilityWin::accSelect(
659     LONG flags_sel, VARIANT var_id) {
660   if (!instance_active())
661     return E_FAIL;
662 
663   if (flags_sel & SELFLAG_TAKEFOCUS) {
664     manager()->SetFocus(this, true);
665     return S_OK;
666   }
667 
668   return S_FALSE;
669 }
670 
671 //
672 // IAccessible2 methods.
673 //
674 
role(LONG * role)675 STDMETHODIMP BrowserAccessibilityWin::role(LONG* role) {
676   if (!instance_active())
677     return E_FAIL;
678 
679   if (!role)
680     return E_INVALIDARG;
681 
682   *role = ia2_role_;
683 
684   return S_OK;
685 }
686 
get_attributes(BSTR * attributes)687 STDMETHODIMP BrowserAccessibilityWin::get_attributes(BSTR* attributes) {
688   if (!instance_active())
689     return E_FAIL;
690 
691   if (!attributes)
692     return E_INVALIDARG;
693 
694   // The iaccessible2 attributes are a set of key-value pairs
695   // separated by semicolons, with a colon between the key and the value.
696   base::string16 str;
697   for (unsigned int i = 0; i < ia2_attributes_.size(); ++i) {
698     if (i != 0)
699       str += L';';
700     str += ia2_attributes_[i];
701   }
702 
703   if (str.empty())
704     return S_FALSE;
705 
706   *attributes = SysAllocString(str.c_str());
707   DCHECK(*attributes);
708   return S_OK;
709 }
710 
get_states(AccessibleStates * states)711 STDMETHODIMP BrowserAccessibilityWin::get_states(AccessibleStates* states) {
712   if (!instance_active())
713     return E_FAIL;
714 
715   if (!states)
716     return E_INVALIDARG;
717 
718   *states = ia2_state_;
719 
720   return S_OK;
721 }
722 
get_uniqueID(LONG * unique_id)723 STDMETHODIMP BrowserAccessibilityWin::get_uniqueID(LONG* unique_id) {
724   if (!instance_active())
725     return E_FAIL;
726 
727   if (!unique_id)
728     return E_INVALIDARG;
729 
730   *unique_id = unique_id_win_;
731   return S_OK;
732 }
733 
get_windowHandle(HWND * window_handle)734 STDMETHODIMP BrowserAccessibilityWin::get_windowHandle(HWND* window_handle) {
735   if (!instance_active())
736     return E_FAIL;
737 
738   if (!window_handle)
739     return E_INVALIDARG;
740 
741   *window_handle = manager()->ToBrowserAccessibilityManagerWin()->parent_hwnd();
742   if (!*window_handle)
743     return E_FAIL;
744 
745   return S_OK;
746 }
747 
get_indexInParent(LONG * index_in_parent)748 STDMETHODIMP BrowserAccessibilityWin::get_indexInParent(LONG* index_in_parent) {
749   if (!instance_active())
750     return E_FAIL;
751 
752   if (!index_in_parent)
753     return E_INVALIDARG;
754 
755   *index_in_parent = this->GetIndexInParent();
756   return S_OK;
757 }
758 
get_nRelations(LONG * n_relations)759 STDMETHODIMP BrowserAccessibilityWin::get_nRelations(LONG* n_relations) {
760   if (!instance_active())
761     return E_FAIL;
762 
763   if (!n_relations)
764     return E_INVALIDARG;
765 
766   *n_relations = relations_.size();
767   return S_OK;
768 }
769 
get_relation(LONG relation_index,IAccessibleRelation ** relation)770 STDMETHODIMP BrowserAccessibilityWin::get_relation(
771     LONG relation_index,
772     IAccessibleRelation** relation) {
773   if (!instance_active())
774     return E_FAIL;
775 
776   if (relation_index < 0 ||
777       relation_index >= static_cast<long>(relations_.size())) {
778     return E_INVALIDARG;
779   }
780 
781   if (!relation)
782     return E_INVALIDARG;
783 
784   relations_[relation_index]->AddRef();
785   *relation = relations_[relation_index];
786   return S_OK;
787 }
788 
get_relations(LONG max_relations,IAccessibleRelation ** relations,LONG * n_relations)789 STDMETHODIMP BrowserAccessibilityWin::get_relations(
790     LONG max_relations,
791     IAccessibleRelation** relations,
792     LONG* n_relations) {
793   if (!instance_active())
794     return E_FAIL;
795 
796   if (!relations || !n_relations)
797     return E_INVALIDARG;
798 
799   long count = static_cast<long>(relations_.size());
800   *n_relations = count;
801   if (count == 0)
802     return S_FALSE;
803 
804   for (long i = 0; i < count; ++i) {
805     relations_[i]->AddRef();
806     relations[i] = relations_[i];
807   }
808 
809   return S_OK;
810 }
811 
scrollTo(enum IA2ScrollType scroll_type)812 STDMETHODIMP BrowserAccessibilityWin::scrollTo(enum IA2ScrollType scroll_type) {
813   if (!instance_active())
814     return E_FAIL;
815 
816   gfx::Rect r = GetLocation();
817   switch(scroll_type) {
818     case IA2_SCROLL_TYPE_TOP_LEFT:
819       manager()->ScrollToMakeVisible(*this, gfx::Rect(r.x(), r.y(), 0, 0));
820       break;
821     case IA2_SCROLL_TYPE_BOTTOM_RIGHT:
822       manager()->ScrollToMakeVisible(
823           *this, gfx::Rect(r.right(), r.bottom(), 0, 0));
824       break;
825     case IA2_SCROLL_TYPE_TOP_EDGE:
826       manager()->ScrollToMakeVisible(
827           *this, gfx::Rect(r.x(), r.y(), r.width(), 0));
828       break;
829     case IA2_SCROLL_TYPE_BOTTOM_EDGE:
830       manager()->ScrollToMakeVisible(
831           *this, gfx::Rect(r.x(), r.bottom(), r.width(), 0));
832     break;
833     case IA2_SCROLL_TYPE_LEFT_EDGE:
834       manager()->ScrollToMakeVisible(
835           *this, gfx::Rect(r.x(), r.y(), 0, r.height()));
836       break;
837     case IA2_SCROLL_TYPE_RIGHT_EDGE:
838       manager()->ScrollToMakeVisible(
839           *this, gfx::Rect(r.right(), r.y(), 0, r.height()));
840       break;
841     case IA2_SCROLL_TYPE_ANYWHERE:
842     default:
843       manager()->ScrollToMakeVisible(*this, r);
844       break;
845   }
846 
847   manager()->ToBrowserAccessibilityManagerWin()->TrackScrollingObject(this);
848 
849   return S_OK;
850 }
851 
scrollToPoint(enum IA2CoordinateType coordinate_type,LONG x,LONG y)852 STDMETHODIMP BrowserAccessibilityWin::scrollToPoint(
853     enum IA2CoordinateType coordinate_type,
854     LONG x,
855     LONG y) {
856   if (!instance_active())
857     return E_FAIL;
858 
859   gfx::Point scroll_to(x, y);
860 
861   if (coordinate_type == IA2_COORDTYPE_SCREEN_RELATIVE) {
862     scroll_to -= manager()->GetViewBounds().OffsetFromOrigin();
863   } else if (coordinate_type == IA2_COORDTYPE_PARENT_RELATIVE) {
864     if (GetParent())
865       scroll_to += GetParent()->GetLocation().OffsetFromOrigin();
866   } else {
867     return E_INVALIDARG;
868   }
869 
870   manager()->ScrollToPoint(*this, scroll_to);
871   manager()->ToBrowserAccessibilityManagerWin()->TrackScrollingObject(this);
872 
873   return S_OK;
874 }
875 
get_groupPosition(LONG * group_level,LONG * similar_items_in_group,LONG * position_in_group)876 STDMETHODIMP BrowserAccessibilityWin::get_groupPosition(
877     LONG* group_level,
878     LONG* similar_items_in_group,
879     LONG* position_in_group) {
880   if (!instance_active())
881     return E_FAIL;
882 
883   if (!group_level || !similar_items_in_group || !position_in_group)
884     return E_INVALIDARG;
885 
886   if (GetRole() == ui::AX_ROLE_LIST_BOX_OPTION &&
887       GetParent() &&
888       GetParent()->GetRole() == ui::AX_ROLE_LIST_BOX) {
889     *group_level = 0;
890     *similar_items_in_group = GetParent()->PlatformChildCount();
891     *position_in_group = GetIndexInParent() + 1;
892     return S_OK;
893   }
894 
895   return E_NOTIMPL;
896 }
897 
898 //
899 // IAccessibleApplication methods.
900 //
901 
get_appName(BSTR * app_name)902 STDMETHODIMP BrowserAccessibilityWin::get_appName(BSTR* app_name) {
903   // No need to check |instance_active()| because this interface is
904   // global, and doesn't depend on any local state.
905 
906   if (!app_name)
907     return E_INVALIDARG;
908 
909   // GetProduct() returns a string like "Chrome/aa.bb.cc.dd", split out
910   // the part before the "/".
911   std::vector<std::string> product_components;
912   base::SplitString(GetContentClient()->GetProduct(), '/', &product_components);
913   DCHECK_EQ(2U, product_components.size());
914   if (product_components.size() != 2)
915     return E_FAIL;
916   *app_name = SysAllocString(base::UTF8ToUTF16(product_components[0]).c_str());
917   DCHECK(*app_name);
918   return *app_name ? S_OK : E_FAIL;
919 }
920 
get_appVersion(BSTR * app_version)921 STDMETHODIMP BrowserAccessibilityWin::get_appVersion(BSTR* app_version) {
922   // No need to check |instance_active()| because this interface is
923   // global, and doesn't depend on any local state.
924 
925   if (!app_version)
926     return E_INVALIDARG;
927 
928   // GetProduct() returns a string like "Chrome/aa.bb.cc.dd", split out
929   // the part after the "/".
930   std::vector<std::string> product_components;
931   base::SplitString(GetContentClient()->GetProduct(), '/', &product_components);
932   DCHECK_EQ(2U, product_components.size());
933   if (product_components.size() != 2)
934     return E_FAIL;
935   *app_version =
936       SysAllocString(base::UTF8ToUTF16(product_components[1]).c_str());
937   DCHECK(*app_version);
938   return *app_version ? S_OK : E_FAIL;
939 }
940 
get_toolkitName(BSTR * toolkit_name)941 STDMETHODIMP BrowserAccessibilityWin::get_toolkitName(BSTR* toolkit_name) {
942   // No need to check |instance_active()| because this interface is
943   // global, and doesn't depend on any local state.
944 
945   if (!toolkit_name)
946     return E_INVALIDARG;
947 
948   // This is hard-coded; all products based on the Chromium engine
949   // will have the same toolkit name, so that assistive technology can
950   // detect any Chrome-based product.
951   *toolkit_name = SysAllocString(L"Chrome");
952   DCHECK(*toolkit_name);
953   return *toolkit_name ? S_OK : E_FAIL;
954 }
955 
get_toolkitVersion(BSTR * toolkit_version)956 STDMETHODIMP BrowserAccessibilityWin::get_toolkitVersion(
957     BSTR* toolkit_version) {
958   // No need to check |instance_active()| because this interface is
959   // global, and doesn't depend on any local state.
960 
961   if (!toolkit_version)
962     return E_INVALIDARG;
963 
964   std::string user_agent = GetContentClient()->GetUserAgent();
965   *toolkit_version = SysAllocString(base::UTF8ToUTF16(user_agent).c_str());
966   DCHECK(*toolkit_version);
967   return *toolkit_version ? S_OK : E_FAIL;
968 }
969 
970 //
971 // IAccessibleImage methods.
972 //
973 
get_description(BSTR * desc)974 STDMETHODIMP BrowserAccessibilityWin::get_description(BSTR* desc) {
975   if (!instance_active())
976     return E_FAIL;
977 
978   if (!desc)
979     return E_INVALIDARG;
980 
981   return GetStringAttributeAsBstr(
982       ui::AX_ATTR_DESCRIPTION, desc);
983 }
984 
get_imagePosition(enum IA2CoordinateType coordinate_type,LONG * x,LONG * y)985 STDMETHODIMP BrowserAccessibilityWin::get_imagePosition(
986     enum IA2CoordinateType coordinate_type,
987     LONG* x,
988     LONG* y) {
989   if (!instance_active())
990     return E_FAIL;
991 
992   if (!x || !y)
993     return E_INVALIDARG;
994 
995   if (coordinate_type == IA2_COORDTYPE_SCREEN_RELATIVE) {
996     HWND parent_hwnd =
997         manager()->ToBrowserAccessibilityManagerWin()->parent_hwnd();
998     if (!parent_hwnd)
999       return E_FAIL;
1000     POINT top_left = {0, 0};
1001     ::ClientToScreen(parent_hwnd, &top_left);
1002     *x = GetLocation().x() + top_left.x;
1003     *y = GetLocation().y() + top_left.y;
1004   } else if (coordinate_type == IA2_COORDTYPE_PARENT_RELATIVE) {
1005     *x = GetLocation().x();
1006     *y = GetLocation().y();
1007     if (GetParent()) {
1008       *x -= GetParent()->GetLocation().x();
1009       *y -= GetParent()->GetLocation().y();
1010     }
1011   } else {
1012     return E_INVALIDARG;
1013   }
1014 
1015   return S_OK;
1016 }
1017 
get_imageSize(LONG * height,LONG * width)1018 STDMETHODIMP BrowserAccessibilityWin::get_imageSize(LONG* height, LONG* width) {
1019   if (!instance_active())
1020     return E_FAIL;
1021 
1022   if (!height || !width)
1023     return E_INVALIDARG;
1024 
1025   *height = GetLocation().height();
1026   *width = GetLocation().width();
1027   return S_OK;
1028 }
1029 
1030 //
1031 // IAccessibleTable methods.
1032 //
1033 
get_accessibleAt(long row,long column,IUnknown ** accessible)1034 STDMETHODIMP BrowserAccessibilityWin::get_accessibleAt(
1035     long row,
1036     long column,
1037     IUnknown** accessible) {
1038   if (!instance_active())
1039     return E_FAIL;
1040 
1041   if (!accessible)
1042     return E_INVALIDARG;
1043 
1044   int columns;
1045   int rows;
1046   if (!GetIntAttribute(
1047           ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
1048       !GetIntAttribute(
1049           ui::AX_ATTR_TABLE_ROW_COUNT, &rows) ||
1050       columns <= 0 ||
1051       rows <= 0) {
1052     return S_FALSE;
1053   }
1054 
1055   if (row < 0 || row >= rows || column < 0 || column >= columns)
1056     return E_INVALIDARG;
1057 
1058   const std::vector<int32>& cell_ids = GetIntListAttribute(
1059       ui::AX_ATTR_CELL_IDS);
1060   DCHECK_EQ(columns * rows, static_cast<int>(cell_ids.size()));
1061 
1062   int cell_id = cell_ids[row * columns + column];
1063   BrowserAccessibilityWin* cell = GetFromID(cell_id);
1064   if (cell) {
1065     *accessible = static_cast<IAccessible*>(cell->NewReference());
1066     return S_OK;
1067   }
1068 
1069   *accessible = NULL;
1070   return E_INVALIDARG;
1071 }
1072 
get_caption(IUnknown ** accessible)1073 STDMETHODIMP BrowserAccessibilityWin::get_caption(IUnknown** accessible) {
1074   if (!instance_active())
1075     return E_FAIL;
1076 
1077   if (!accessible)
1078     return E_INVALIDARG;
1079 
1080   // TODO(dmazzoni): implement
1081   return S_FALSE;
1082 }
1083 
get_childIndex(long row,long column,long * cell_index)1084 STDMETHODIMP BrowserAccessibilityWin::get_childIndex(long row,
1085                                                      long column,
1086                                                      long* cell_index) {
1087   if (!instance_active())
1088     return E_FAIL;
1089 
1090   if (!cell_index)
1091     return E_INVALIDARG;
1092 
1093   int columns;
1094   int rows;
1095   if (!GetIntAttribute(
1096           ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
1097       !GetIntAttribute(
1098           ui::AX_ATTR_TABLE_ROW_COUNT, &rows) ||
1099       columns <= 0 ||
1100       rows <= 0) {
1101     return S_FALSE;
1102   }
1103 
1104   if (row < 0 || row >= rows || column < 0 || column >= columns)
1105     return E_INVALIDARG;
1106 
1107   const std::vector<int32>& cell_ids = GetIntListAttribute(
1108       ui::AX_ATTR_CELL_IDS);
1109   const std::vector<int32>& unique_cell_ids = GetIntListAttribute(
1110       ui::AX_ATTR_UNIQUE_CELL_IDS);
1111   DCHECK_EQ(columns * rows, static_cast<int>(cell_ids.size()));
1112   int cell_id = cell_ids[row * columns + column];
1113   for (size_t i = 0; i < unique_cell_ids.size(); ++i) {
1114     if (unique_cell_ids[i] == cell_id) {
1115       *cell_index = (long)i;
1116       return S_OK;
1117     }
1118   }
1119 
1120   return S_FALSE;
1121 }
1122 
get_columnDescription(long column,BSTR * description)1123 STDMETHODIMP BrowserAccessibilityWin::get_columnDescription(long column,
1124                                                             BSTR* description) {
1125   if (!instance_active())
1126     return E_FAIL;
1127 
1128   if (!description)
1129     return E_INVALIDARG;
1130 
1131   int columns;
1132   int rows;
1133   if (!GetIntAttribute(
1134           ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
1135       !GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT, &rows) ||
1136       columns <= 0 ||
1137       rows <= 0) {
1138     return S_FALSE;
1139   }
1140 
1141   if (column < 0 || column >= columns)
1142     return E_INVALIDARG;
1143 
1144   const std::vector<int32>& cell_ids = GetIntListAttribute(
1145       ui::AX_ATTR_CELL_IDS);
1146   for (int i = 0; i < rows; ++i) {
1147     int cell_id = cell_ids[i * columns + column];
1148     BrowserAccessibilityWin* cell = static_cast<BrowserAccessibilityWin*>(
1149         manager()->GetFromID(cell_id));
1150     if (cell && cell->GetRole() == ui::AX_ROLE_COLUMN_HEADER) {
1151       base::string16 cell_name = cell->GetString16Attribute(
1152           ui::AX_ATTR_NAME);
1153       if (cell_name.size() > 0) {
1154         *description = SysAllocString(cell_name.c_str());
1155         return S_OK;
1156       }
1157 
1158       return cell->GetStringAttributeAsBstr(
1159           ui::AX_ATTR_DESCRIPTION, description);
1160     }
1161   }
1162 
1163   return S_FALSE;
1164 }
1165 
get_columnExtentAt(long row,long column,long * n_columns_spanned)1166 STDMETHODIMP BrowserAccessibilityWin::get_columnExtentAt(
1167     long row,
1168     long column,
1169     long* n_columns_spanned) {
1170   if (!instance_active())
1171     return E_FAIL;
1172 
1173   if (!n_columns_spanned)
1174     return E_INVALIDARG;
1175 
1176   int columns;
1177   int rows;
1178   if (!GetIntAttribute(
1179           ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
1180       !GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT, &rows) ||
1181       columns <= 0 ||
1182       rows <= 0) {
1183     return S_FALSE;
1184   }
1185 
1186   if (row < 0 || row >= rows || column < 0 || column >= columns)
1187     return E_INVALIDARG;
1188 
1189   const std::vector<int32>& cell_ids = GetIntListAttribute(
1190       ui::AX_ATTR_CELL_IDS);
1191   int cell_id = cell_ids[row * columns + column];
1192   BrowserAccessibilityWin* cell = static_cast<BrowserAccessibilityWin*>(
1193       manager()->GetFromID(cell_id));
1194   int colspan;
1195   if (cell &&
1196       cell->GetIntAttribute(
1197           ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN, &colspan) &&
1198       colspan >= 1) {
1199     *n_columns_spanned = colspan;
1200     return S_OK;
1201   }
1202 
1203   return S_FALSE;
1204 }
1205 
get_columnHeader(IAccessibleTable ** accessible_table,long * starting_row_index)1206 STDMETHODIMP BrowserAccessibilityWin::get_columnHeader(
1207     IAccessibleTable** accessible_table,
1208     long* starting_row_index) {
1209   // TODO(dmazzoni): implement
1210   return E_NOTIMPL;
1211 }
1212 
get_columnIndex(long cell_index,long * column_index)1213 STDMETHODIMP BrowserAccessibilityWin::get_columnIndex(long cell_index,
1214                                                       long* column_index) {
1215   if (!instance_active())
1216     return E_FAIL;
1217 
1218   if (!column_index)
1219     return E_INVALIDARG;
1220 
1221   const std::vector<int32>& unique_cell_ids = GetIntListAttribute(
1222       ui::AX_ATTR_UNIQUE_CELL_IDS);
1223   int cell_id_count = static_cast<int>(unique_cell_ids.size());
1224   if (cell_index < 0)
1225     return E_INVALIDARG;
1226   if (cell_index >= cell_id_count)
1227     return S_FALSE;
1228 
1229   int cell_id = unique_cell_ids[cell_index];
1230   BrowserAccessibilityWin* cell =
1231       manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin();
1232   int col_index;
1233   if (cell &&
1234       cell->GetIntAttribute(
1235           ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &col_index)) {
1236     *column_index = col_index;
1237     return S_OK;
1238   }
1239 
1240   return S_FALSE;
1241 }
1242 
get_nColumns(long * column_count)1243 STDMETHODIMP BrowserAccessibilityWin::get_nColumns(long* column_count) {
1244   if (!instance_active())
1245     return E_FAIL;
1246 
1247   if (!column_count)
1248     return E_INVALIDARG;
1249 
1250   int columns;
1251   if (GetIntAttribute(
1252           ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns)) {
1253     *column_count = columns;
1254     return S_OK;
1255   }
1256 
1257   return S_FALSE;
1258 }
1259 
get_nRows(long * row_count)1260 STDMETHODIMP BrowserAccessibilityWin::get_nRows(long* row_count) {
1261   if (!instance_active())
1262     return E_FAIL;
1263 
1264   if (!row_count)
1265     return E_INVALIDARG;
1266 
1267   int rows;
1268   if (GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT, &rows)) {
1269     *row_count = rows;
1270     return S_OK;
1271   }
1272 
1273   return S_FALSE;
1274 }
1275 
get_nSelectedChildren(long * cell_count)1276 STDMETHODIMP BrowserAccessibilityWin::get_nSelectedChildren(long* cell_count) {
1277   if (!instance_active())
1278     return E_FAIL;
1279 
1280   if (!cell_count)
1281     return E_INVALIDARG;
1282 
1283   // TODO(dmazzoni): add support for selected cells/rows/columns in tables.
1284   *cell_count = 0;
1285   return S_OK;
1286 }
1287 
get_nSelectedColumns(long * column_count)1288 STDMETHODIMP BrowserAccessibilityWin::get_nSelectedColumns(long* column_count) {
1289   if (!instance_active())
1290     return E_FAIL;
1291 
1292   if (!column_count)
1293     return E_INVALIDARG;
1294 
1295   *column_count = 0;
1296   return S_OK;
1297 }
1298 
get_nSelectedRows(long * row_count)1299 STDMETHODIMP BrowserAccessibilityWin::get_nSelectedRows(long* row_count) {
1300   if (!instance_active())
1301     return E_FAIL;
1302 
1303   if (!row_count)
1304     return E_INVALIDARG;
1305 
1306   *row_count = 0;
1307   return S_OK;
1308 }
1309 
get_rowDescription(long row,BSTR * description)1310 STDMETHODIMP BrowserAccessibilityWin::get_rowDescription(long row,
1311                                                          BSTR* description) {
1312   if (!instance_active())
1313     return E_FAIL;
1314 
1315   if (!description)
1316     return E_INVALIDARG;
1317 
1318   int columns;
1319   int rows;
1320   if (!GetIntAttribute(
1321           ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
1322       !GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT, &rows) ||
1323       columns <= 0 ||
1324       rows <= 0) {
1325     return S_FALSE;
1326   }
1327 
1328   if (row < 0 || row >= rows)
1329     return E_INVALIDARG;
1330 
1331   const std::vector<int32>& cell_ids = GetIntListAttribute(
1332       ui::AX_ATTR_CELL_IDS);
1333   for (int i = 0; i < columns; ++i) {
1334     int cell_id = cell_ids[row * columns + i];
1335     BrowserAccessibilityWin* cell =
1336         manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin();
1337     if (cell && cell->GetRole() == ui::AX_ROLE_ROW_HEADER) {
1338       base::string16 cell_name = cell->GetString16Attribute(
1339           ui::AX_ATTR_NAME);
1340       if (cell_name.size() > 0) {
1341         *description = SysAllocString(cell_name.c_str());
1342         return S_OK;
1343       }
1344 
1345       return cell->GetStringAttributeAsBstr(
1346           ui::AX_ATTR_DESCRIPTION, description);
1347     }
1348   }
1349 
1350   return S_FALSE;
1351 }
1352 
get_rowExtentAt(long row,long column,long * n_rows_spanned)1353 STDMETHODIMP BrowserAccessibilityWin::get_rowExtentAt(long row,
1354                                                       long column,
1355                                                       long* n_rows_spanned) {
1356   if (!instance_active())
1357     return E_FAIL;
1358 
1359   if (!n_rows_spanned)
1360     return E_INVALIDARG;
1361 
1362   int columns;
1363   int rows;
1364   if (!GetIntAttribute(
1365           ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
1366       !GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT, &rows) ||
1367       columns <= 0 ||
1368       rows <= 0) {
1369     return S_FALSE;
1370   }
1371 
1372   if (row < 0 || row >= rows || column < 0 || column >= columns)
1373     return E_INVALIDARG;
1374 
1375   const std::vector<int32>& cell_ids = GetIntListAttribute(
1376       ui::AX_ATTR_CELL_IDS);
1377   int cell_id = cell_ids[row * columns + column];
1378   BrowserAccessibilityWin* cell =
1379       manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin();
1380   int rowspan;
1381   if (cell &&
1382       cell->GetIntAttribute(
1383           ui::AX_ATTR_TABLE_CELL_ROW_SPAN, &rowspan) &&
1384       rowspan >= 1) {
1385     *n_rows_spanned = rowspan;
1386     return S_OK;
1387   }
1388 
1389   return S_FALSE;
1390 }
1391 
get_rowHeader(IAccessibleTable ** accessible_table,long * starting_column_index)1392 STDMETHODIMP BrowserAccessibilityWin::get_rowHeader(
1393     IAccessibleTable** accessible_table,
1394     long* starting_column_index) {
1395   // TODO(dmazzoni): implement
1396   return E_NOTIMPL;
1397 }
1398 
get_rowIndex(long cell_index,long * row_index)1399 STDMETHODIMP BrowserAccessibilityWin::get_rowIndex(long cell_index,
1400                                                    long* row_index) {
1401   if (!instance_active())
1402     return E_FAIL;
1403 
1404   if (!row_index)
1405     return E_INVALIDARG;
1406 
1407   const std::vector<int32>& unique_cell_ids = GetIntListAttribute(
1408       ui::AX_ATTR_UNIQUE_CELL_IDS);
1409   int cell_id_count = static_cast<int>(unique_cell_ids.size());
1410   if (cell_index < 0)
1411     return E_INVALIDARG;
1412   if (cell_index >= cell_id_count)
1413     return S_FALSE;
1414 
1415   int cell_id = unique_cell_ids[cell_index];
1416   BrowserAccessibilityWin* cell =
1417       manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin();
1418   int cell_row_index;
1419   if (cell &&
1420       cell->GetIntAttribute(
1421           ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &cell_row_index)) {
1422     *row_index = cell_row_index;
1423     return S_OK;
1424   }
1425 
1426   return S_FALSE;
1427 }
1428 
get_selectedChildren(long max_children,long ** children,long * n_children)1429 STDMETHODIMP BrowserAccessibilityWin::get_selectedChildren(long max_children,
1430                                                            long** children,
1431                                                            long* n_children) {
1432   if (!instance_active())
1433     return E_FAIL;
1434 
1435   if (!children || !n_children)
1436     return E_INVALIDARG;
1437 
1438   // TODO(dmazzoni): Implement this.
1439   *n_children = 0;
1440   return S_OK;
1441 }
1442 
get_selectedColumns(long max_columns,long ** columns,long * n_columns)1443 STDMETHODIMP BrowserAccessibilityWin::get_selectedColumns(long max_columns,
1444                                                           long** columns,
1445                                                           long* n_columns) {
1446   if (!instance_active())
1447     return E_FAIL;
1448 
1449   if (!columns || !n_columns)
1450     return E_INVALIDARG;
1451 
1452   // TODO(dmazzoni): Implement this.
1453   *n_columns = 0;
1454   return S_OK;
1455 }
1456 
get_selectedRows(long max_rows,long ** rows,long * n_rows)1457 STDMETHODIMP BrowserAccessibilityWin::get_selectedRows(long max_rows,
1458                                                        long** rows,
1459                                                        long* n_rows) {
1460   if (!instance_active())
1461     return E_FAIL;
1462 
1463   if (!rows || !n_rows)
1464     return E_INVALIDARG;
1465 
1466   // TODO(dmazzoni): Implement this.
1467   *n_rows = 0;
1468   return S_OK;
1469 }
1470 
get_summary(IUnknown ** accessible)1471 STDMETHODIMP BrowserAccessibilityWin::get_summary(IUnknown** accessible) {
1472   if (!instance_active())
1473     return E_FAIL;
1474 
1475   if (!accessible)
1476     return E_INVALIDARG;
1477 
1478   // TODO(dmazzoni): implement
1479   return S_FALSE;
1480 }
1481 
get_isColumnSelected(long column,boolean * is_selected)1482 STDMETHODIMP BrowserAccessibilityWin::get_isColumnSelected(
1483     long column,
1484     boolean* is_selected) {
1485   if (!instance_active())
1486     return E_FAIL;
1487 
1488   if (!is_selected)
1489     return E_INVALIDARG;
1490 
1491   // TODO(dmazzoni): Implement this.
1492   *is_selected = false;
1493   return S_OK;
1494 }
1495 
get_isRowSelected(long row,boolean * is_selected)1496 STDMETHODIMP BrowserAccessibilityWin::get_isRowSelected(long row,
1497                                                         boolean* is_selected) {
1498   if (!instance_active())
1499     return E_FAIL;
1500 
1501   if (!is_selected)
1502     return E_INVALIDARG;
1503 
1504   // TODO(dmazzoni): Implement this.
1505   *is_selected = false;
1506   return S_OK;
1507 }
1508 
get_isSelected(long row,long column,boolean * is_selected)1509 STDMETHODIMP BrowserAccessibilityWin::get_isSelected(long row,
1510                                                      long column,
1511                                                      boolean* is_selected) {
1512   if (!instance_active())
1513     return E_FAIL;
1514 
1515   if (!is_selected)
1516     return E_INVALIDARG;
1517 
1518   // TODO(dmazzoni): Implement this.
1519   *is_selected = false;
1520   return S_OK;
1521 }
1522 
get_rowColumnExtentsAtIndex(long index,long * row,long * column,long * row_extents,long * column_extents,boolean * is_selected)1523 STDMETHODIMP BrowserAccessibilityWin::get_rowColumnExtentsAtIndex(
1524     long index,
1525     long* row,
1526     long* column,
1527     long* row_extents,
1528     long* column_extents,
1529     boolean* is_selected) {
1530   if (!instance_active())
1531     return E_FAIL;
1532 
1533   if (!row || !column || !row_extents || !column_extents || !is_selected)
1534     return E_INVALIDARG;
1535 
1536   const std::vector<int32>& unique_cell_ids = GetIntListAttribute(
1537       ui::AX_ATTR_UNIQUE_CELL_IDS);
1538   int cell_id_count = static_cast<int>(unique_cell_ids.size());
1539   if (index < 0)
1540     return E_INVALIDARG;
1541   if (index >= cell_id_count)
1542     return S_FALSE;
1543 
1544   int cell_id = unique_cell_ids[index];
1545   BrowserAccessibilityWin* cell =
1546       manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin();
1547   int rowspan;
1548   int colspan;
1549   if (cell &&
1550       cell->GetIntAttribute(
1551           ui::AX_ATTR_TABLE_CELL_ROW_SPAN, &rowspan) &&
1552       cell->GetIntAttribute(
1553           ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN, &colspan) &&
1554       rowspan >= 1 &&
1555       colspan >= 1) {
1556     *row_extents = rowspan;
1557     *column_extents = colspan;
1558     return S_OK;
1559   }
1560 
1561   return S_FALSE;
1562 }
1563 
1564 //
1565 // IAccessibleTable2 methods.
1566 //
1567 
get_cellAt(long row,long column,IUnknown ** cell)1568 STDMETHODIMP BrowserAccessibilityWin::get_cellAt(long row,
1569                                                  long column,
1570                                                  IUnknown** cell) {
1571   return get_accessibleAt(row, column, cell);
1572 }
1573 
get_nSelectedCells(long * cell_count)1574 STDMETHODIMP BrowserAccessibilityWin::get_nSelectedCells(long* cell_count) {
1575   return get_nSelectedChildren(cell_count);
1576 }
1577 
get_selectedCells(IUnknown *** cells,long * n_selected_cells)1578 STDMETHODIMP BrowserAccessibilityWin::get_selectedCells(
1579     IUnknown*** cells,
1580     long* n_selected_cells) {
1581   if (!instance_active())
1582     return E_FAIL;
1583 
1584   if (!cells || !n_selected_cells)
1585     return E_INVALIDARG;
1586 
1587   // TODO(dmazzoni): Implement this.
1588   *n_selected_cells = 0;
1589   return S_OK;
1590 }
1591 
get_selectedColumns(long ** columns,long * n_columns)1592 STDMETHODIMP BrowserAccessibilityWin::get_selectedColumns(long** columns,
1593                                                           long* n_columns) {
1594   if (!instance_active())
1595     return E_FAIL;
1596 
1597   if (!columns || !n_columns)
1598     return E_INVALIDARG;
1599 
1600   // TODO(dmazzoni): Implement this.
1601   *n_columns = 0;
1602   return S_OK;
1603 }
1604 
get_selectedRows(long ** rows,long * n_rows)1605 STDMETHODIMP BrowserAccessibilityWin::get_selectedRows(long** rows,
1606                                                        long* n_rows) {
1607   if (!instance_active())
1608     return E_FAIL;
1609 
1610   if (!rows || !n_rows)
1611     return E_INVALIDARG;
1612 
1613   // TODO(dmazzoni): Implement this.
1614   *n_rows = 0;
1615   return S_OK;
1616 }
1617 
1618 
1619 //
1620 // IAccessibleTableCell methods.
1621 //
1622 
get_columnExtent(long * n_columns_spanned)1623 STDMETHODIMP BrowserAccessibilityWin::get_columnExtent(
1624     long* n_columns_spanned) {
1625   if (!instance_active())
1626     return E_FAIL;
1627 
1628   if (!n_columns_spanned)
1629     return E_INVALIDARG;
1630 
1631   int colspan;
1632   if (GetIntAttribute(
1633           ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN, &colspan) &&
1634       colspan >= 1) {
1635     *n_columns_spanned = colspan;
1636     return S_OK;
1637   }
1638 
1639   return S_FALSE;
1640 }
1641 
get_columnHeaderCells(IUnknown *** cell_accessibles,long * n_column_header_cells)1642 STDMETHODIMP BrowserAccessibilityWin::get_columnHeaderCells(
1643     IUnknown*** cell_accessibles,
1644     long* n_column_header_cells) {
1645   if (!instance_active())
1646     return E_FAIL;
1647 
1648   if (!cell_accessibles || !n_column_header_cells)
1649     return E_INVALIDARG;
1650 
1651   *n_column_header_cells = 0;
1652 
1653   int column;
1654   if (!GetIntAttribute(
1655           ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &column)) {
1656     return S_FALSE;
1657   }
1658 
1659   BrowserAccessibility* table = GetParent();
1660   while (table && table->GetRole() != ui::AX_ROLE_TABLE)
1661     table = table->GetParent();
1662   if (!table) {
1663     NOTREACHED();
1664     return S_FALSE;
1665   }
1666 
1667   int columns;
1668   int rows;
1669   if (!table->GetIntAttribute(
1670           ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
1671       !table->GetIntAttribute(
1672           ui::AX_ATTR_TABLE_ROW_COUNT, &rows)) {
1673     return S_FALSE;
1674   }
1675   if (columns <= 0 || rows <= 0 || column < 0 || column >= columns)
1676     return S_FALSE;
1677 
1678   const std::vector<int32>& cell_ids = table->GetIntListAttribute(
1679       ui::AX_ATTR_CELL_IDS);
1680 
1681   for (int i = 0; i < rows; ++i) {
1682     int cell_id = cell_ids[i * columns + column];
1683     BrowserAccessibilityWin* cell =
1684         manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin();
1685     if (cell && cell->GetRole() == ui::AX_ROLE_COLUMN_HEADER)
1686       (*n_column_header_cells)++;
1687   }
1688 
1689   *cell_accessibles = static_cast<IUnknown**>(CoTaskMemAlloc(
1690       (*n_column_header_cells) * sizeof(cell_accessibles[0])));
1691   int index = 0;
1692   for (int i = 0; i < rows; ++i) {
1693     int cell_id = cell_ids[i * columns + column];
1694     BrowserAccessibility* cell = manager()->GetFromID(cell_id);
1695     if (cell && cell->GetRole() == ui::AX_ROLE_COLUMN_HEADER) {
1696       (*cell_accessibles)[index] = static_cast<IAccessible*>(
1697           cell->ToBrowserAccessibilityWin()->NewReference());
1698       ++index;
1699     }
1700   }
1701 
1702   return S_OK;
1703 }
1704 
get_columnIndex(long * column_index)1705 STDMETHODIMP BrowserAccessibilityWin::get_columnIndex(long* column_index) {
1706   if (!instance_active())
1707     return E_FAIL;
1708 
1709   if (!column_index)
1710     return E_INVALIDARG;
1711 
1712   int column;
1713   if (GetIntAttribute(
1714           ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &column)) {
1715     *column_index = column;
1716     return S_OK;
1717   }
1718 
1719   return S_FALSE;
1720 }
1721 
get_rowExtent(long * n_rows_spanned)1722 STDMETHODIMP BrowserAccessibilityWin::get_rowExtent(long* n_rows_spanned) {
1723   if (!instance_active())
1724     return E_FAIL;
1725 
1726   if (!n_rows_spanned)
1727     return E_INVALIDARG;
1728 
1729   int rowspan;
1730   if (GetIntAttribute(
1731           ui::AX_ATTR_TABLE_CELL_ROW_SPAN, &rowspan) &&
1732       rowspan >= 1) {
1733     *n_rows_spanned = rowspan;
1734     return S_OK;
1735   }
1736 
1737   return S_FALSE;
1738 }
1739 
get_rowHeaderCells(IUnknown *** cell_accessibles,long * n_row_header_cells)1740 STDMETHODIMP BrowserAccessibilityWin::get_rowHeaderCells(
1741     IUnknown*** cell_accessibles,
1742     long* n_row_header_cells) {
1743   if (!instance_active())
1744     return E_FAIL;
1745 
1746   if (!cell_accessibles || !n_row_header_cells)
1747     return E_INVALIDARG;
1748 
1749   *n_row_header_cells = 0;
1750 
1751   int row;
1752   if (!GetIntAttribute(
1753           ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &row)) {
1754     return S_FALSE;
1755   }
1756 
1757   BrowserAccessibility* table = GetParent();
1758   while (table && table->GetRole() != ui::AX_ROLE_TABLE)
1759     table = table->GetParent();
1760   if (!table) {
1761     NOTREACHED();
1762     return S_FALSE;
1763   }
1764 
1765   int columns;
1766   int rows;
1767   if (!table->GetIntAttribute(
1768           ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
1769       !table->GetIntAttribute(
1770           ui::AX_ATTR_TABLE_ROW_COUNT, &rows)) {
1771     return S_FALSE;
1772   }
1773   if (columns <= 0 || rows <= 0 || row < 0 || row >= rows)
1774     return S_FALSE;
1775 
1776   const std::vector<int32>& cell_ids = table->GetIntListAttribute(
1777       ui::AX_ATTR_CELL_IDS);
1778 
1779   for (int i = 0; i < columns; ++i) {
1780     int cell_id = cell_ids[row * columns + i];
1781     BrowserAccessibility* cell = manager()->GetFromID(cell_id);
1782     if (cell && cell->GetRole() == ui::AX_ROLE_ROW_HEADER)
1783       (*n_row_header_cells)++;
1784   }
1785 
1786   *cell_accessibles = static_cast<IUnknown**>(CoTaskMemAlloc(
1787       (*n_row_header_cells) * sizeof(cell_accessibles[0])));
1788   int index = 0;
1789   for (int i = 0; i < columns; ++i) {
1790     int cell_id = cell_ids[row * columns + i];
1791     BrowserAccessibility* cell = manager()->GetFromID(cell_id);
1792     if (cell && cell->GetRole() == ui::AX_ROLE_ROW_HEADER) {
1793       (*cell_accessibles)[index] = static_cast<IAccessible*>(
1794           cell->ToBrowserAccessibilityWin()->NewReference());
1795       ++index;
1796     }
1797   }
1798 
1799   return S_OK;
1800 }
1801 
get_rowIndex(long * row_index)1802 STDMETHODIMP BrowserAccessibilityWin::get_rowIndex(long* row_index) {
1803   if (!instance_active())
1804     return E_FAIL;
1805 
1806   if (!row_index)
1807     return E_INVALIDARG;
1808 
1809   int row;
1810   if (GetIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &row)) {
1811     *row_index = row;
1812     return S_OK;
1813   }
1814   return S_FALSE;
1815 }
1816 
get_isSelected(boolean * is_selected)1817 STDMETHODIMP BrowserAccessibilityWin::get_isSelected(boolean* is_selected) {
1818   if (!instance_active())
1819     return E_FAIL;
1820 
1821   if (!is_selected)
1822     return E_INVALIDARG;
1823 
1824   *is_selected = false;
1825   return S_OK;
1826 }
1827 
get_rowColumnExtents(long * row_index,long * column_index,long * row_extents,long * column_extents,boolean * is_selected)1828 STDMETHODIMP BrowserAccessibilityWin::get_rowColumnExtents(
1829     long* row_index,
1830     long* column_index,
1831     long* row_extents,
1832     long* column_extents,
1833     boolean* is_selected) {
1834   if (!instance_active())
1835     return E_FAIL;
1836 
1837   if (!row_index ||
1838       !column_index ||
1839       !row_extents ||
1840       !column_extents ||
1841       !is_selected) {
1842     return E_INVALIDARG;
1843   }
1844 
1845   int row;
1846   int column;
1847   int rowspan;
1848   int colspan;
1849   if (GetIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &row) &&
1850       GetIntAttribute(
1851           ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &column) &&
1852       GetIntAttribute(
1853           ui::AX_ATTR_TABLE_CELL_ROW_SPAN, &rowspan) &&
1854       GetIntAttribute(
1855           ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN, &colspan)) {
1856     *row_index = row;
1857     *column_index = column;
1858     *row_extents = rowspan;
1859     *column_extents = colspan;
1860     *is_selected = false;
1861     return S_OK;
1862   }
1863 
1864   return S_FALSE;
1865 }
1866 
get_table(IUnknown ** table)1867 STDMETHODIMP BrowserAccessibilityWin::get_table(IUnknown** table) {
1868   if (!instance_active())
1869     return E_FAIL;
1870 
1871   if (!table)
1872     return E_INVALIDARG;
1873 
1874 
1875   int row;
1876   int column;
1877   GetIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &row);
1878   GetIntAttribute(ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &column);
1879 
1880   BrowserAccessibility* find_table = GetParent();
1881   while (find_table && find_table->GetRole() != ui::AX_ROLE_TABLE)
1882     find_table = find_table->GetParent();
1883   if (!find_table) {
1884     NOTREACHED();
1885     return S_FALSE;
1886   }
1887 
1888   *table = static_cast<IAccessibleTable*>(
1889       find_table->ToBrowserAccessibilityWin()->NewReference());
1890 
1891   return S_OK;
1892 }
1893 
1894 //
1895 // IAccessibleText methods.
1896 //
1897 
get_nCharacters(LONG * n_characters)1898 STDMETHODIMP BrowserAccessibilityWin::get_nCharacters(LONG* n_characters) {
1899   if (!instance_active())
1900     return E_FAIL;
1901 
1902   if (!n_characters)
1903     return E_INVALIDARG;
1904 
1905   *n_characters = TextForIAccessibleText().length();
1906   return S_OK;
1907 }
1908 
get_caretOffset(LONG * offset)1909 STDMETHODIMP BrowserAccessibilityWin::get_caretOffset(LONG* offset) {
1910   if (!instance_active())
1911     return E_FAIL;
1912 
1913   if (!offset)
1914     return E_INVALIDARG;
1915 
1916   *offset = 0;
1917   if (GetRole() == ui::AX_ROLE_TEXT_FIELD ||
1918       GetRole() == ui::AX_ROLE_TEXT_AREA) {
1919     int sel_start = 0;
1920     if (GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START,
1921                         &sel_start))
1922       *offset = sel_start;
1923   }
1924 
1925   return S_OK;
1926 }
1927 
get_characterExtents(LONG offset,enum IA2CoordinateType coordinate_type,LONG * out_x,LONG * out_y,LONG * out_width,LONG * out_height)1928 STDMETHODIMP BrowserAccessibilityWin::get_characterExtents(
1929     LONG offset,
1930     enum IA2CoordinateType coordinate_type,
1931     LONG* out_x,
1932     LONG* out_y,
1933     LONG* out_width,
1934     LONG* out_height) {
1935   if (!instance_active())
1936     return E_FAIL;
1937 
1938   if (!out_x || !out_y || !out_width || !out_height)
1939     return E_INVALIDARG;
1940 
1941   const base::string16& text_str = TextForIAccessibleText();
1942   HandleSpecialTextOffset(text_str, &offset);
1943 
1944   if (offset < 0 || offset > static_cast<LONG>(text_str.size()))
1945     return E_INVALIDARG;
1946 
1947   gfx::Rect character_bounds;
1948   if (coordinate_type == IA2_COORDTYPE_SCREEN_RELATIVE) {
1949     character_bounds = GetGlobalBoundsForRange(offset, 1);
1950   } else if (coordinate_type == IA2_COORDTYPE_PARENT_RELATIVE) {
1951     character_bounds = GetLocalBoundsForRange(offset, 1);
1952     character_bounds -= GetLocation().OffsetFromOrigin();
1953   } else {
1954     return E_INVALIDARG;
1955   }
1956 
1957   *out_x = character_bounds.x();
1958   *out_y = character_bounds.y();
1959   *out_width = character_bounds.width();
1960   *out_height = character_bounds.height();
1961 
1962   return S_OK;
1963 }
1964 
get_nSelections(LONG * n_selections)1965 STDMETHODIMP BrowserAccessibilityWin::get_nSelections(LONG* n_selections) {
1966   if (!instance_active())
1967     return E_FAIL;
1968 
1969   if (!n_selections)
1970     return E_INVALIDARG;
1971 
1972   *n_selections = 0;
1973   if (GetRole() == ui::AX_ROLE_TEXT_FIELD ||
1974       GetRole() == ui::AX_ROLE_TEXT_AREA) {
1975     int sel_start = 0;
1976     int sel_end = 0;
1977     if (GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START,
1978                         &sel_start) &&
1979         GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, &sel_end) &&
1980         sel_start != sel_end)
1981       *n_selections = 1;
1982   }
1983 
1984   return S_OK;
1985 }
1986 
get_selection(LONG selection_index,LONG * start_offset,LONG * end_offset)1987 STDMETHODIMP BrowserAccessibilityWin::get_selection(LONG selection_index,
1988                                                     LONG* start_offset,
1989                                                     LONG* end_offset) {
1990   if (!instance_active())
1991     return E_FAIL;
1992 
1993   if (!start_offset || !end_offset || selection_index != 0)
1994     return E_INVALIDARG;
1995 
1996   *start_offset = 0;
1997   *end_offset = 0;
1998   if (GetRole() == ui::AX_ROLE_TEXT_FIELD ||
1999       GetRole() == ui::AX_ROLE_TEXT_AREA) {
2000     int sel_start = 0;
2001     int sel_end = 0;
2002     if (GetIntAttribute(
2003             ui::AX_ATTR_TEXT_SEL_START, &sel_start) &&
2004         GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, &sel_end)) {
2005       *start_offset = sel_start;
2006       *end_offset = sel_end;
2007     }
2008   }
2009 
2010   return S_OK;
2011 }
2012 
get_text(LONG start_offset,LONG end_offset,BSTR * text)2013 STDMETHODIMP BrowserAccessibilityWin::get_text(LONG start_offset,
2014                                                LONG end_offset,
2015                                                BSTR* text) {
2016   if (!instance_active())
2017     return E_FAIL;
2018 
2019   if (!text)
2020     return E_INVALIDARG;
2021 
2022   const base::string16& text_str = TextForIAccessibleText();
2023 
2024   // Handle special text offsets.
2025   HandleSpecialTextOffset(text_str, &start_offset);
2026   HandleSpecialTextOffset(text_str, &end_offset);
2027 
2028   // The spec allows the arguments to be reversed.
2029   if (start_offset > end_offset) {
2030     LONG tmp = start_offset;
2031     start_offset = end_offset;
2032     end_offset = tmp;
2033   }
2034 
2035   // The spec does not allow the start or end offsets to be out or range;
2036   // we must return an error if so.
2037   LONG len = text_str.length();
2038   if (start_offset < 0)
2039     return E_INVALIDARG;
2040   if (end_offset > len)
2041     return E_INVALIDARG;
2042 
2043   base::string16 substr = text_str.substr(start_offset,
2044                                           end_offset - start_offset);
2045   if (substr.empty())
2046     return S_FALSE;
2047 
2048   *text = SysAllocString(substr.c_str());
2049   DCHECK(*text);
2050   return S_OK;
2051 }
2052 
get_textAtOffset(LONG offset,enum IA2TextBoundaryType boundary_type,LONG * start_offset,LONG * end_offset,BSTR * text)2053 STDMETHODIMP BrowserAccessibilityWin::get_textAtOffset(
2054     LONG offset,
2055     enum IA2TextBoundaryType boundary_type,
2056     LONG* start_offset,
2057     LONG* end_offset,
2058     BSTR* text) {
2059   if (!instance_active())
2060     return E_FAIL;
2061 
2062   if (!start_offset || !end_offset || !text)
2063     return E_INVALIDARG;
2064 
2065   // The IAccessible2 spec says we don't have to implement the "sentence"
2066   // boundary type, we can just let the screenreader handle it.
2067   if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) {
2068     *start_offset = 0;
2069     *end_offset = 0;
2070     *text = NULL;
2071     return S_FALSE;
2072   }
2073 
2074   const base::string16& text_str = TextForIAccessibleText();
2075 
2076   *start_offset = FindBoundary(
2077       text_str, boundary_type, offset, ui::BACKWARDS_DIRECTION);
2078   *end_offset = FindBoundary(
2079       text_str, boundary_type, offset, ui::FORWARDS_DIRECTION);
2080   return get_text(*start_offset, *end_offset, text);
2081 }
2082 
get_textBeforeOffset(LONG offset,enum IA2TextBoundaryType boundary_type,LONG * start_offset,LONG * end_offset,BSTR * text)2083 STDMETHODIMP BrowserAccessibilityWin::get_textBeforeOffset(
2084     LONG offset,
2085     enum IA2TextBoundaryType boundary_type,
2086     LONG* start_offset,
2087     LONG* end_offset,
2088     BSTR* text) {
2089   if (!instance_active())
2090     return E_FAIL;
2091 
2092   if (!start_offset || !end_offset || !text)
2093     return E_INVALIDARG;
2094 
2095   // The IAccessible2 spec says we don't have to implement the "sentence"
2096   // boundary type, we can just let the screenreader handle it.
2097   if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) {
2098     *start_offset = 0;
2099     *end_offset = 0;
2100     *text = NULL;
2101     return S_FALSE;
2102   }
2103 
2104   const base::string16& text_str = TextForIAccessibleText();
2105 
2106   *start_offset = FindBoundary(
2107       text_str, boundary_type, offset, ui::BACKWARDS_DIRECTION);
2108   *end_offset = offset;
2109   return get_text(*start_offset, *end_offset, text);
2110 }
2111 
get_textAfterOffset(LONG offset,enum IA2TextBoundaryType boundary_type,LONG * start_offset,LONG * end_offset,BSTR * text)2112 STDMETHODIMP BrowserAccessibilityWin::get_textAfterOffset(
2113     LONG offset,
2114     enum IA2TextBoundaryType boundary_type,
2115     LONG* start_offset,
2116     LONG* end_offset,
2117     BSTR* text) {
2118   if (!instance_active())
2119     return E_FAIL;
2120 
2121   if (!start_offset || !end_offset || !text)
2122     return E_INVALIDARG;
2123 
2124   // The IAccessible2 spec says we don't have to implement the "sentence"
2125   // boundary type, we can just let the screenreader handle it.
2126   if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) {
2127     *start_offset = 0;
2128     *end_offset = 0;
2129     *text = NULL;
2130     return S_FALSE;
2131   }
2132 
2133   const base::string16& text_str = TextForIAccessibleText();
2134 
2135   *start_offset = offset;
2136   *end_offset = FindBoundary(
2137       text_str, boundary_type, offset, ui::FORWARDS_DIRECTION);
2138   return get_text(*start_offset, *end_offset, text);
2139 }
2140 
get_newText(IA2TextSegment * new_text)2141 STDMETHODIMP BrowserAccessibilityWin::get_newText(IA2TextSegment* new_text) {
2142   if (!instance_active())
2143     return E_FAIL;
2144 
2145   if (!new_text)
2146     return E_INVALIDARG;
2147 
2148   base::string16 text = TextForIAccessibleText();
2149 
2150   new_text->text = SysAllocString(text.c_str());
2151   new_text->start = 0;
2152   new_text->end = static_cast<long>(text.size());
2153   return S_OK;
2154 }
2155 
get_oldText(IA2TextSegment * old_text)2156 STDMETHODIMP BrowserAccessibilityWin::get_oldText(IA2TextSegment* old_text) {
2157   if (!instance_active())
2158     return E_FAIL;
2159 
2160   if (!old_text)
2161     return E_INVALIDARG;
2162 
2163   old_text->text = SysAllocString(old_text_.c_str());
2164   old_text->start = 0;
2165   old_text->end = static_cast<long>(old_text_.size());
2166   return S_OK;
2167 }
2168 
get_offsetAtPoint(LONG x,LONG y,enum IA2CoordinateType coord_type,LONG * offset)2169 STDMETHODIMP BrowserAccessibilityWin::get_offsetAtPoint(
2170     LONG x,
2171     LONG y,
2172     enum IA2CoordinateType coord_type,
2173     LONG* offset) {
2174   if (!instance_active())
2175     return E_FAIL;
2176 
2177   if (!offset)
2178     return E_INVALIDARG;
2179 
2180   // TODO(dmazzoni): implement this. We're returning S_OK for now so that
2181   // screen readers still return partially accurate results rather than
2182   // completely failing.
2183   *offset = 0;
2184   return S_OK;
2185 }
2186 
scrollSubstringTo(LONG start_index,LONG end_index,enum IA2ScrollType scroll_type)2187 STDMETHODIMP BrowserAccessibilityWin::scrollSubstringTo(
2188     LONG start_index,
2189     LONG end_index,
2190     enum IA2ScrollType scroll_type) {
2191   // TODO(dmazzoni): adjust this for the start and end index, too.
2192   return scrollTo(scroll_type);
2193 }
2194 
scrollSubstringToPoint(LONG start_index,LONG end_index,enum IA2CoordinateType coordinate_type,LONG x,LONG y)2195 STDMETHODIMP BrowserAccessibilityWin::scrollSubstringToPoint(
2196     LONG start_index,
2197     LONG end_index,
2198     enum IA2CoordinateType coordinate_type,
2199     LONG x, LONG y) {
2200   // TODO(dmazzoni): adjust this for the start and end index, too.
2201   return scrollToPoint(coordinate_type, x, y);
2202 }
2203 
addSelection(LONG start_offset,LONG end_offset)2204 STDMETHODIMP BrowserAccessibilityWin::addSelection(LONG start_offset,
2205                                                    LONG end_offset) {
2206   if (!instance_active())
2207     return E_FAIL;
2208 
2209   const base::string16& text_str = TextForIAccessibleText();
2210   HandleSpecialTextOffset(text_str, &start_offset);
2211   HandleSpecialTextOffset(text_str, &end_offset);
2212 
2213   manager()->SetTextSelection(*this, start_offset, end_offset);
2214   return S_OK;
2215 }
2216 
removeSelection(LONG selection_index)2217 STDMETHODIMP BrowserAccessibilityWin::removeSelection(LONG selection_index) {
2218   if (!instance_active())
2219     return E_FAIL;
2220 
2221   if (selection_index != 0)
2222     return E_INVALIDARG;
2223 
2224   manager()->SetTextSelection(*this, 0, 0);
2225   return S_OK;
2226 }
2227 
setCaretOffset(LONG offset)2228 STDMETHODIMP BrowserAccessibilityWin::setCaretOffset(LONG offset) {
2229   if (!instance_active())
2230     return E_FAIL;
2231 
2232   const base::string16& text_str = TextForIAccessibleText();
2233   HandleSpecialTextOffset(text_str, &offset);
2234   manager()->SetTextSelection(*this, offset, offset);
2235   return S_OK;
2236 }
2237 
setSelection(LONG selection_index,LONG start_offset,LONG end_offset)2238 STDMETHODIMP BrowserAccessibilityWin::setSelection(LONG selection_index,
2239                                                    LONG start_offset,
2240                                                    LONG end_offset) {
2241   if (!instance_active())
2242     return E_FAIL;
2243 
2244   if (selection_index != 0)
2245     return E_INVALIDARG;
2246 
2247   const base::string16& text_str = TextForIAccessibleText();
2248   HandleSpecialTextOffset(text_str, &start_offset);
2249   HandleSpecialTextOffset(text_str, &end_offset);
2250 
2251   manager()->SetTextSelection(*this, start_offset, end_offset);
2252   return S_OK;
2253 }
2254 
2255 //
2256 // IAccessibleHypertext methods.
2257 //
2258 
get_nHyperlinks(long * hyperlink_count)2259 STDMETHODIMP BrowserAccessibilityWin::get_nHyperlinks(long* hyperlink_count) {
2260   if (!instance_active())
2261     return E_FAIL;
2262 
2263   if (!hyperlink_count)
2264     return E_INVALIDARG;
2265 
2266   *hyperlink_count = hyperlink_offset_to_index_.size();
2267   return S_OK;
2268 }
2269 
get_hyperlink(long index,IAccessibleHyperlink ** hyperlink)2270 STDMETHODIMP BrowserAccessibilityWin::get_hyperlink(
2271     long index,
2272     IAccessibleHyperlink** hyperlink) {
2273   if (!instance_active())
2274     return E_FAIL;
2275 
2276   if (!hyperlink ||
2277       index < 0 ||
2278       index >= static_cast<long>(hyperlinks_.size())) {
2279     return E_INVALIDARG;
2280   }
2281 
2282   BrowserAccessibilityWin* child =
2283       InternalGetChild(hyperlinks_[index])->ToBrowserAccessibilityWin();
2284   *hyperlink = static_cast<IAccessibleHyperlink*>(child->NewReference());
2285   return S_OK;
2286 }
2287 
get_hyperlinkIndex(long char_index,long * hyperlink_index)2288 STDMETHODIMP BrowserAccessibilityWin::get_hyperlinkIndex(
2289     long char_index,
2290     long* hyperlink_index) {
2291   if (!instance_active())
2292     return E_FAIL;
2293 
2294   if (!hyperlink_index)
2295     return E_INVALIDARG;
2296 
2297   *hyperlink_index = -1;
2298 
2299   if (char_index < 0 || char_index >= static_cast<long>(hypertext_.size()))
2300     return E_INVALIDARG;
2301 
2302   std::map<int32, int32>::iterator it =
2303       hyperlink_offset_to_index_.find(char_index);
2304   if (it == hyperlink_offset_to_index_.end())
2305     return E_FAIL;
2306 
2307   *hyperlink_index = it->second;
2308   return S_OK;
2309 }
2310 
2311 //
2312 // IAccessibleValue methods.
2313 //
2314 
get_currentValue(VARIANT * value)2315 STDMETHODIMP BrowserAccessibilityWin::get_currentValue(VARIANT* value) {
2316   if (!instance_active())
2317     return E_FAIL;
2318 
2319   if (!value)
2320     return E_INVALIDARG;
2321 
2322   float float_val;
2323   if (GetFloatAttribute(
2324           ui::AX_ATTR_VALUE_FOR_RANGE, &float_val)) {
2325     value->vt = VT_R8;
2326     value->dblVal = float_val;
2327     return S_OK;
2328   }
2329 
2330   value->vt = VT_EMPTY;
2331   return S_FALSE;
2332 }
2333 
get_minimumValue(VARIANT * value)2334 STDMETHODIMP BrowserAccessibilityWin::get_minimumValue(VARIANT* value) {
2335   if (!instance_active())
2336     return E_FAIL;
2337 
2338   if (!value)
2339     return E_INVALIDARG;
2340 
2341   float float_val;
2342   if (GetFloatAttribute(ui::AX_ATTR_MIN_VALUE_FOR_RANGE,
2343                         &float_val)) {
2344     value->vt = VT_R8;
2345     value->dblVal = float_val;
2346     return S_OK;
2347   }
2348 
2349   value->vt = VT_EMPTY;
2350   return S_FALSE;
2351 }
2352 
get_maximumValue(VARIANT * value)2353 STDMETHODIMP BrowserAccessibilityWin::get_maximumValue(VARIANT* value) {
2354   if (!instance_active())
2355     return E_FAIL;
2356 
2357   if (!value)
2358     return E_INVALIDARG;
2359 
2360   float float_val;
2361   if (GetFloatAttribute(ui::AX_ATTR_MAX_VALUE_FOR_RANGE,
2362                         &float_val)) {
2363     value->vt = VT_R8;
2364     value->dblVal = float_val;
2365     return S_OK;
2366   }
2367 
2368   value->vt = VT_EMPTY;
2369   return S_FALSE;
2370 }
2371 
setCurrentValue(VARIANT new_value)2372 STDMETHODIMP BrowserAccessibilityWin::setCurrentValue(VARIANT new_value) {
2373   // TODO(dmazzoni): Implement this.
2374   return E_NOTIMPL;
2375 }
2376 
2377 //
2378 // ISimpleDOMDocument methods.
2379 //
2380 
get_URL(BSTR * url)2381 STDMETHODIMP BrowserAccessibilityWin::get_URL(BSTR* url) {
2382   if (!instance_active())
2383     return E_FAIL;
2384 
2385   if (!url)
2386     return E_INVALIDARG;
2387 
2388   return GetStringAttributeAsBstr(ui::AX_ATTR_DOC_URL, url);
2389 }
2390 
get_title(BSTR * title)2391 STDMETHODIMP BrowserAccessibilityWin::get_title(BSTR* title) {
2392   if (!instance_active())
2393     return E_FAIL;
2394 
2395   if (!title)
2396     return E_INVALIDARG;
2397 
2398   return GetStringAttributeAsBstr(ui::AX_ATTR_DOC_TITLE, title);
2399 }
2400 
get_mimeType(BSTR * mime_type)2401 STDMETHODIMP BrowserAccessibilityWin::get_mimeType(BSTR* mime_type) {
2402   if (!instance_active())
2403     return E_FAIL;
2404 
2405   if (!mime_type)
2406     return E_INVALIDARG;
2407 
2408   return GetStringAttributeAsBstr(
2409       ui::AX_ATTR_DOC_MIMETYPE, mime_type);
2410 }
2411 
get_docType(BSTR * doc_type)2412 STDMETHODIMP BrowserAccessibilityWin::get_docType(BSTR* doc_type) {
2413   if (!instance_active())
2414     return E_FAIL;
2415 
2416   if (!doc_type)
2417     return E_INVALIDARG;
2418 
2419   return GetStringAttributeAsBstr(
2420       ui::AX_ATTR_DOC_DOCTYPE, doc_type);
2421 }
2422 
2423 //
2424 // ISimpleDOMNode methods.
2425 //
2426 
get_nodeInfo(BSTR * node_name,short * name_space_id,BSTR * node_value,unsigned int * num_children,unsigned int * unique_id,unsigned short * node_type)2427 STDMETHODIMP BrowserAccessibilityWin::get_nodeInfo(
2428     BSTR* node_name,
2429     short* name_space_id,
2430     BSTR* node_value,
2431     unsigned int* num_children,
2432     unsigned int* unique_id,
2433     unsigned short* node_type) {
2434   if (!instance_active())
2435     return E_FAIL;
2436 
2437   if (!node_name || !name_space_id || !node_value || !num_children ||
2438       !unique_id || !node_type) {
2439     return E_INVALIDARG;
2440   }
2441 
2442   base::string16 tag;
2443   if (GetString16Attribute(ui::AX_ATTR_HTML_TAG, &tag))
2444     *node_name = SysAllocString(tag.c_str());
2445   else
2446     *node_name = NULL;
2447 
2448   *name_space_id = 0;
2449   *node_value = SysAllocString(base::UTF8ToUTF16(value()).c_str());
2450   *num_children = PlatformChildCount();
2451   *unique_id = unique_id_win_;
2452 
2453   if (ia_role_ == ROLE_SYSTEM_DOCUMENT) {
2454     *node_type = NODETYPE_DOCUMENT;
2455   } else if (ia_role_ == ROLE_SYSTEM_TEXT &&
2456              ((ia2_state_ & IA2_STATE_EDITABLE) == 0)) {
2457     *node_type = NODETYPE_TEXT;
2458   } else {
2459     *node_type = NODETYPE_ELEMENT;
2460   }
2461 
2462   return S_OK;
2463 }
2464 
get_attributes(unsigned short max_attribs,BSTR * attrib_names,short * name_space_id,BSTR * attrib_values,unsigned short * num_attribs)2465 STDMETHODIMP BrowserAccessibilityWin::get_attributes(
2466     unsigned short max_attribs,
2467     BSTR* attrib_names,
2468     short* name_space_id,
2469     BSTR* attrib_values,
2470     unsigned short* num_attribs) {
2471   if (!instance_active())
2472     return E_FAIL;
2473 
2474   if (!attrib_names || !name_space_id || !attrib_values || !num_attribs)
2475     return E_INVALIDARG;
2476 
2477   *num_attribs = max_attribs;
2478   if (*num_attribs > GetHtmlAttributes().size())
2479     *num_attribs = GetHtmlAttributes().size();
2480 
2481   for (unsigned short i = 0; i < *num_attribs; ++i) {
2482     attrib_names[i] = SysAllocString(
2483         base::UTF8ToUTF16(GetHtmlAttributes()[i].first).c_str());
2484     name_space_id[i] = 0;
2485     attrib_values[i] = SysAllocString(
2486         base::UTF8ToUTF16(GetHtmlAttributes()[i].second).c_str());
2487   }
2488   return S_OK;
2489 }
2490 
get_attributesForNames(unsigned short num_attribs,BSTR * attrib_names,short * name_space_id,BSTR * attrib_values)2491 STDMETHODIMP BrowserAccessibilityWin::get_attributesForNames(
2492     unsigned short num_attribs,
2493     BSTR* attrib_names,
2494     short* name_space_id,
2495     BSTR* attrib_values) {
2496   if (!instance_active())
2497     return E_FAIL;
2498 
2499   if (!attrib_names || !name_space_id || !attrib_values)
2500     return E_INVALIDARG;
2501 
2502   for (unsigned short i = 0; i < num_attribs; ++i) {
2503     name_space_id[i] = 0;
2504     bool found = false;
2505     std::string name = base::UTF16ToUTF8((LPCWSTR)attrib_names[i]);
2506     for (unsigned int j = 0;  j < GetHtmlAttributes().size(); ++j) {
2507       if (GetHtmlAttributes()[j].first == name) {
2508         attrib_values[i] = SysAllocString(
2509             base::UTF8ToUTF16(GetHtmlAttributes()[j].second).c_str());
2510         found = true;
2511         break;
2512       }
2513     }
2514     if (!found) {
2515       attrib_values[i] = NULL;
2516     }
2517   }
2518   return S_OK;
2519 }
2520 
get_computedStyle(unsigned short max_style_properties,boolean use_alternate_view,BSTR * style_properties,BSTR * style_values,unsigned short * num_style_properties)2521 STDMETHODIMP BrowserAccessibilityWin::get_computedStyle(
2522     unsigned short max_style_properties,
2523     boolean use_alternate_view,
2524     BSTR* style_properties,
2525     BSTR* style_values,
2526     unsigned short *num_style_properties)  {
2527   if (!instance_active())
2528     return E_FAIL;
2529 
2530   if (!style_properties || !style_values)
2531     return E_INVALIDARG;
2532 
2533   // We only cache a single style property for now: DISPLAY
2534 
2535   base::string16 display;
2536   if (max_style_properties == 0 ||
2537       !GetString16Attribute(ui::AX_ATTR_DISPLAY, &display)) {
2538     *num_style_properties = 0;
2539     return S_OK;
2540   }
2541 
2542   *num_style_properties = 1;
2543   style_properties[0] = SysAllocString(L"display");
2544   style_values[0] = SysAllocString(display.c_str());
2545 
2546   return S_OK;
2547 }
2548 
get_computedStyleForProperties(unsigned short num_style_properties,boolean use_alternate_view,BSTR * style_properties,BSTR * style_values)2549 STDMETHODIMP BrowserAccessibilityWin::get_computedStyleForProperties(
2550     unsigned short num_style_properties,
2551     boolean use_alternate_view,
2552     BSTR* style_properties,
2553     BSTR* style_values) {
2554   if (!instance_active())
2555     return E_FAIL;
2556 
2557   if (!style_properties || !style_values)
2558     return E_INVALIDARG;
2559 
2560   // We only cache a single style property for now: DISPLAY
2561 
2562   for (unsigned short i = 0; i < num_style_properties; ++i) {
2563     base::string16 name = (LPCWSTR)style_properties[i];
2564     StringToLowerASCII(&name);
2565     if (name == L"display") {
2566       base::string16 display = GetString16Attribute(
2567           ui::AX_ATTR_DISPLAY);
2568       style_values[i] = SysAllocString(display.c_str());
2569     } else {
2570       style_values[i] = NULL;
2571     }
2572   }
2573 
2574   return S_OK;
2575 }
2576 
scrollTo(boolean placeTopLeft)2577 STDMETHODIMP BrowserAccessibilityWin::scrollTo(boolean placeTopLeft) {
2578   return scrollTo(placeTopLeft ?
2579       IA2_SCROLL_TYPE_TOP_LEFT : IA2_SCROLL_TYPE_ANYWHERE);
2580 }
2581 
get_parentNode(ISimpleDOMNode ** node)2582 STDMETHODIMP BrowserAccessibilityWin::get_parentNode(ISimpleDOMNode** node) {
2583   if (!instance_active())
2584     return E_FAIL;
2585 
2586   if (!node)
2587     return E_INVALIDARG;
2588 
2589   *node = GetParent()->ToBrowserAccessibilityWin()->NewReference();
2590   return S_OK;
2591 }
2592 
get_firstChild(ISimpleDOMNode ** node)2593 STDMETHODIMP BrowserAccessibilityWin::get_firstChild(ISimpleDOMNode** node)  {
2594   if (!instance_active())
2595     return E_FAIL;
2596 
2597   if (!node)
2598     return E_INVALIDARG;
2599 
2600   if (PlatformChildCount() == 0) {
2601     *node = NULL;
2602     return S_FALSE;
2603   }
2604 
2605   *node = PlatformGetChild(0)->ToBrowserAccessibilityWin()->NewReference();
2606   return S_OK;
2607 }
2608 
get_lastChild(ISimpleDOMNode ** node)2609 STDMETHODIMP BrowserAccessibilityWin::get_lastChild(ISimpleDOMNode** node) {
2610   if (!instance_active())
2611     return E_FAIL;
2612 
2613   if (!node)
2614     return E_INVALIDARG;
2615 
2616   if (PlatformChildCount() == 0) {
2617     *node = NULL;
2618     return S_FALSE;
2619   }
2620 
2621   *node = PlatformGetChild(PlatformChildCount() - 1)
2622       ->ToBrowserAccessibilityWin()->NewReference();
2623   return S_OK;
2624 }
2625 
get_previousSibling(ISimpleDOMNode ** node)2626 STDMETHODIMP BrowserAccessibilityWin::get_previousSibling(
2627     ISimpleDOMNode** node) {
2628   if (!instance_active())
2629     return E_FAIL;
2630 
2631   if (!node)
2632     return E_INVALIDARG;
2633 
2634   if (!GetParent() || GetIndexInParent() <= 0) {
2635     *node = NULL;
2636     return S_FALSE;
2637   }
2638 
2639   *node = GetParent()->InternalGetChild(GetIndexInParent() - 1)->
2640       ToBrowserAccessibilityWin()->NewReference();
2641   return S_OK;
2642 }
2643 
get_nextSibling(ISimpleDOMNode ** node)2644 STDMETHODIMP BrowserAccessibilityWin::get_nextSibling(ISimpleDOMNode** node) {
2645   if (!instance_active())
2646     return E_FAIL;
2647 
2648   if (!node)
2649     return E_INVALIDARG;
2650 
2651   if (!GetParent() ||
2652       GetIndexInParent() < 0 ||
2653       GetIndexInParent() >= static_cast<int>(
2654           GetParent()->InternalChildCount()) - 1) {
2655     *node = NULL;
2656     return S_FALSE;
2657   }
2658 
2659   *node = GetParent()->InternalGetChild(GetIndexInParent() + 1)->
2660       ToBrowserAccessibilityWin()->NewReference();
2661   return S_OK;
2662 }
2663 
get_childAt(unsigned int child_index,ISimpleDOMNode ** node)2664 STDMETHODIMP BrowserAccessibilityWin::get_childAt(
2665     unsigned int child_index,
2666     ISimpleDOMNode** node) {
2667   if (!instance_active())
2668     return E_FAIL;
2669 
2670   if (!node)
2671     return E_INVALIDARG;
2672 
2673   if (child_index >= PlatformChildCount())
2674     return E_INVALIDARG;
2675 
2676   BrowserAccessibility* child = PlatformGetChild(child_index);
2677   if (!child) {
2678     *node = NULL;
2679     return S_FALSE;
2680   }
2681 
2682   *node = child->ToBrowserAccessibilityWin()->NewReference();
2683   return S_OK;
2684 }
2685 
2686 //
2687 // ISimpleDOMText methods.
2688 //
2689 
get_domText(BSTR * dom_text)2690 STDMETHODIMP BrowserAccessibilityWin::get_domText(BSTR* dom_text) {
2691   if (!instance_active())
2692     return E_FAIL;
2693 
2694   if (!dom_text)
2695     return E_INVALIDARG;
2696 
2697   return GetStringAttributeAsBstr(
2698       ui::AX_ATTR_NAME, dom_text);
2699 }
2700 
get_clippedSubstringBounds(unsigned int start_index,unsigned int end_index,int * out_x,int * out_y,int * out_width,int * out_height)2701 STDMETHODIMP BrowserAccessibilityWin::get_clippedSubstringBounds(
2702     unsigned int start_index,
2703     unsigned int end_index,
2704     int* out_x,
2705     int* out_y,
2706     int* out_width,
2707     int* out_height) {
2708   // TODO(dmazzoni): fully support this API by intersecting the
2709   // rect with the container's rect.
2710   return get_unclippedSubstringBounds(
2711       start_index, end_index, out_x, out_y, out_width, out_height);
2712 }
2713 
get_unclippedSubstringBounds(unsigned int start_index,unsigned int end_index,int * out_x,int * out_y,int * out_width,int * out_height)2714 STDMETHODIMP BrowserAccessibilityWin::get_unclippedSubstringBounds(
2715     unsigned int start_index,
2716     unsigned int end_index,
2717     int* out_x,
2718     int* out_y,
2719     int* out_width,
2720     int* out_height) {
2721   if (!instance_active())
2722     return E_FAIL;
2723 
2724   if (!out_x || !out_y || !out_width || !out_height)
2725     return E_INVALIDARG;
2726 
2727   const base::string16& text_str = TextForIAccessibleText();
2728   if (start_index > text_str.size() ||
2729       end_index > text_str.size() ||
2730       start_index > end_index) {
2731     return E_INVALIDARG;
2732   }
2733 
2734   gfx::Rect bounds = GetGlobalBoundsForRange(
2735       start_index, end_index - start_index);
2736   *out_x = bounds.x();
2737   *out_y = bounds.y();
2738   *out_width = bounds.width();
2739   *out_height = bounds.height();
2740   return S_OK;
2741 }
2742 
scrollToSubstring(unsigned int start_index,unsigned int end_index)2743 STDMETHODIMP BrowserAccessibilityWin::scrollToSubstring(
2744     unsigned int start_index,
2745     unsigned int end_index) {
2746   if (!instance_active())
2747     return E_FAIL;
2748 
2749   const base::string16& text_str = TextForIAccessibleText();
2750   if (start_index > text_str.size() ||
2751       end_index > text_str.size() ||
2752       start_index > end_index) {
2753     return E_INVALIDARG;
2754   }
2755 
2756   manager()->ScrollToMakeVisible(*this, GetLocalBoundsForRange(
2757       start_index, end_index - start_index));
2758   manager()->ToBrowserAccessibilityManagerWin()->TrackScrollingObject(this);
2759 
2760   return S_OK;
2761 }
2762 
2763 //
2764 // IServiceProvider methods.
2765 //
2766 
QueryService(REFGUID guidService,REFIID riid,void ** object)2767 STDMETHODIMP BrowserAccessibilityWin::QueryService(REFGUID guidService,
2768                                                    REFIID riid,
2769                                                    void** object) {
2770   if (!instance_active())
2771     return E_FAIL;
2772 
2773   // The system uses IAccessible APIs for many purposes, but only
2774   // assistive technology like screen readers uses IAccessible2.
2775   // Enable full accessibility support when IAccessible2 APIs are queried.
2776   if (riid == IID_IAccessible2)
2777     BrowserAccessibilityStateImpl::GetInstance()->EnableAccessibility();
2778 
2779   if (guidService == GUID_IAccessibleContentDocument) {
2780     // Special Mozilla extension: return the accessible for the root document.
2781     // Screen readers use this to distinguish between a document loaded event
2782     // on the root document vs on an iframe.
2783     return manager()->GetRoot()->ToBrowserAccessibilityWin()->QueryInterface(
2784         IID_IAccessible2, object);
2785   }
2786 
2787   if (guidService == IID_IAccessible ||
2788       guidService == IID_IAccessible2 ||
2789       guidService == IID_IAccessibleAction ||
2790       guidService == IID_IAccessibleApplication ||
2791       guidService == IID_IAccessibleHyperlink ||
2792       guidService == IID_IAccessibleHypertext ||
2793       guidService == IID_IAccessibleImage ||
2794       guidService == IID_IAccessibleTable ||
2795       guidService == IID_IAccessibleTable2 ||
2796       guidService == IID_IAccessibleTableCell ||
2797       guidService == IID_IAccessibleText ||
2798       guidService == IID_IAccessibleValue ||
2799       guidService == IID_ISimpleDOMDocument ||
2800       guidService == IID_ISimpleDOMNode ||
2801       guidService == IID_ISimpleDOMText ||
2802       guidService == GUID_ISimpleDOM) {
2803     return QueryInterface(riid, object);
2804   }
2805 
2806   // We only support the IAccessibleEx interface on Windows 8 and above. This
2807   // is needed for the on-screen Keyboard to show up in metro mode, when the
2808   // user taps an editable portion on the page.
2809   // All methods in the IAccessibleEx interface are unimplemented.
2810   if (riid == IID_IAccessibleEx &&
2811       base::win::GetVersion() >= base::win::VERSION_WIN8) {
2812     return QueryInterface(riid, object);
2813   }
2814 
2815   *object = NULL;
2816   return E_FAIL;
2817 }
2818 
GetPatternProvider(PATTERNID id,IUnknown ** provider)2819 STDMETHODIMP BrowserAccessibilityWin::GetPatternProvider(PATTERNID id,
2820                                                          IUnknown** provider) {
2821   DVLOG(1) << "In Function: "
2822            << __FUNCTION__
2823            << " for pattern id: "
2824            << id;
2825   if (id == UIA_ValuePatternId || id == UIA_TextPatternId) {
2826     if (IsEditableText()) {
2827       DVLOG(1) << "Returning UIA text provider";
2828       base::win::UIATextProvider::CreateTextProvider(
2829           GetValueText(), true, provider);
2830       return S_OK;
2831     }
2832   }
2833   return E_NOTIMPL;
2834 }
2835 
GetPropertyValue(PROPERTYID id,VARIANT * ret)2836 STDMETHODIMP BrowserAccessibilityWin::GetPropertyValue(PROPERTYID id,
2837                                                        VARIANT* ret) {
2838   DVLOG(1) << "In Function: "
2839            << __FUNCTION__
2840            << " for property id: "
2841            << id;
2842   V_VT(ret) = VT_EMPTY;
2843   if (id == UIA_ControlTypePropertyId) {
2844     if (IsEditableText()) {
2845       V_VT(ret) = VT_I4;
2846       ret->lVal = UIA_EditControlTypeId;
2847       DVLOG(1) << "Returning Edit control type";
2848     } else {
2849       DVLOG(1) << "Returning empty control type";
2850     }
2851   }
2852   return S_OK;
2853 }
2854 
2855 //
2856 // CComObjectRootEx methods.
2857 //
2858 
InternalQueryInterface(void * this_ptr,const _ATL_INTMAP_ENTRY * entries,REFIID iid,void ** object)2859 HRESULT WINAPI BrowserAccessibilityWin::InternalQueryInterface(
2860     void* this_ptr,
2861     const _ATL_INTMAP_ENTRY* entries,
2862     REFIID iid,
2863     void** object) {
2864   if (iid == IID_IAccessibleImage) {
2865     if (ia_role_ != ROLE_SYSTEM_GRAPHIC) {
2866       *object = NULL;
2867       return E_NOINTERFACE;
2868     }
2869   } else if (iid == IID_IAccessibleTable || iid == IID_IAccessibleTable2) {
2870     if (ia_role_ != ROLE_SYSTEM_TABLE) {
2871       *object = NULL;
2872       return E_NOINTERFACE;
2873     }
2874   } else if (iid == IID_IAccessibleTableCell) {
2875     if (ia_role_ != ROLE_SYSTEM_CELL) {
2876       *object = NULL;
2877       return E_NOINTERFACE;
2878     }
2879   } else if (iid == IID_IAccessibleValue) {
2880     if (ia_role_ != ROLE_SYSTEM_PROGRESSBAR &&
2881         ia_role_ != ROLE_SYSTEM_SCROLLBAR &&
2882         ia_role_ != ROLE_SYSTEM_SLIDER) {
2883       *object = NULL;
2884       return E_NOINTERFACE;
2885     }
2886   } else if (iid == IID_ISimpleDOMDocument) {
2887     if (ia_role_ != ROLE_SYSTEM_DOCUMENT) {
2888       *object = NULL;
2889       return E_NOINTERFACE;
2890     }
2891   }
2892 
2893   return CComObjectRootBase::InternalQueryInterface(
2894       this_ptr, entries, iid, object);
2895 }
2896 
2897 //
2898 // Private methods.
2899 //
2900 
2901 // Called every time this node's data changes.
OnDataChanged()2902 void BrowserAccessibilityWin::OnDataChanged() {
2903   BrowserAccessibility::OnDataChanged();
2904 
2905   InitRoleAndState();
2906 
2907   // Expose the "display" and "tag" attributes.
2908   StringAttributeToIA2(ui::AX_ATTR_DISPLAY, "display");
2909   StringAttributeToIA2(ui::AX_ATTR_HTML_TAG, "tag");
2910   StringAttributeToIA2(ui::AX_ATTR_ROLE, "xml-roles");
2911 
2912   // Expose "level" attribute for headings, trees, etc.
2913   IntAttributeToIA2(ui::AX_ATTR_HIERARCHICAL_LEVEL, "level");
2914 
2915   // Expose the set size and position in set for listbox options.
2916   if (GetRole() == ui::AX_ROLE_LIST_BOX_OPTION &&
2917       GetParent() &&
2918       GetParent()->GetRole() == ui::AX_ROLE_LIST_BOX) {
2919     ia2_attributes_.push_back(
2920         L"setsize:" + base::IntToString16(GetParent()->PlatformChildCount()));
2921     ia2_attributes_.push_back(
2922         L"setsize:" + base::IntToString16(GetIndexInParent() + 1));
2923   }
2924 
2925   if (ia_role_ == ROLE_SYSTEM_CHECKBUTTON ||
2926       ia_role_ == ROLE_SYSTEM_RADIOBUTTON ||
2927       ia2_role_ == IA2_ROLE_TOGGLE_BUTTON) {
2928     ia2_attributes_.push_back(L"checkable:true");
2929   }
2930 
2931   // Expose live region attributes.
2932   StringAttributeToIA2(ui::AX_ATTR_LIVE_STATUS, "live");
2933   StringAttributeToIA2(ui::AX_ATTR_LIVE_RELEVANT, "relevant");
2934   BoolAttributeToIA2(ui::AX_ATTR_LIVE_ATOMIC, "atomic");
2935   BoolAttributeToIA2(ui::AX_ATTR_LIVE_BUSY, "busy");
2936 
2937   // Expose container live region attributes.
2938   StringAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_STATUS,
2939                        "container-live");
2940   StringAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_RELEVANT,
2941                        "container-relevant");
2942   BoolAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_ATOMIC,
2943                      "container-atomic");
2944   BoolAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_BUSY,
2945                      "container-busy");
2946 
2947   // Expose slider value.
2948   if (ia_role_ == ROLE_SYSTEM_PROGRESSBAR ||
2949       ia_role_ == ROLE_SYSTEM_SCROLLBAR ||
2950       ia_role_ == ROLE_SYSTEM_SLIDER) {
2951     ia2_attributes_.push_back(L"valuetext:" + GetValueText());
2952   }
2953 
2954   // Expose table cell index.
2955   if (ia_role_ == ROLE_SYSTEM_CELL) {
2956     BrowserAccessibility* table = GetParent();
2957     while (table && table->GetRole() != ui::AX_ROLE_TABLE)
2958       table = table->GetParent();
2959     if (table) {
2960       const std::vector<int32>& unique_cell_ids = table->GetIntListAttribute(
2961           ui::AX_ATTR_UNIQUE_CELL_IDS);
2962       for (size_t i = 0; i < unique_cell_ids.size(); ++i) {
2963         if (unique_cell_ids[i] == GetId()) {
2964           ia2_attributes_.push_back(
2965               base::string16(L"table-cell-index:") + base::IntToString16(i));
2966         }
2967       }
2968     }
2969   }
2970 
2971   // The calculation of the accessible name of an element has been
2972   // standardized in the HTML to Platform Accessibility APIs Implementation
2973   // Guide (http://www.w3.org/TR/html-aapi/). In order to return the
2974   // appropriate accessible name on Windows, we need to apply some logic
2975   // to the fields we get from WebKit.
2976   //
2977   // TODO(dmazzoni): move most of this logic into WebKit.
2978   //
2979   // WebKit gives us:
2980   //
2981   //   name: the default name, e.g. inner text
2982   //   title ui element: a reference to a <label> element on the same
2983   //       page that labels this node.
2984   //   description: accessible labels that override the default name:
2985   //       aria-label or aria-labelledby or aria-describedby
2986   //   help: the value of the "title" attribute
2987   //
2988   // On Windows, the logic we apply lets some fields take precedence and
2989   // always returns the primary name in "name" and the secondary name,
2990   // if any, in "description".
2991 
2992   int title_elem_id = GetIntAttribute(
2993       ui::AX_ATTR_TITLE_UI_ELEMENT);
2994   std::string help = GetStringAttribute(ui::AX_ATTR_HELP);
2995   std::string description = GetStringAttribute(
2996       ui::AX_ATTR_DESCRIPTION);
2997 
2998   // WebKit annoyingly puts the title in the description if there's no other
2999   // description, which just confuses the rest of the logic. Put it back.
3000   // Now "help" is always the value of the "title" attribute, if present.
3001   std::string title_attr;
3002   if (GetHtmlAttribute("title", &title_attr) &&
3003       description == title_attr &&
3004       help.empty()) {
3005     help = description;
3006     description.clear();
3007   }
3008 
3009   // Now implement the main logic: the descripion should become the name if
3010   // it's nonempty, and the help should become the description if
3011   // there's no description - or the name if there's no name or description.
3012   if (!description.empty()) {
3013     set_name(description);
3014     description.clear();
3015   }
3016   if (!help.empty() && description.empty()) {
3017     description = help;
3018     help.clear();
3019   }
3020   if (!description.empty() && name().empty() && !title_elem_id) {
3021     set_name(description);
3022     description.clear();
3023   }
3024 
3025   // If it's a text field, also consider the placeholder.
3026   std::string placeholder;
3027   if (GetRole() == ui::AX_ROLE_TEXT_FIELD &&
3028       HasState(ui::AX_STATE_FOCUSABLE) &&
3029       GetHtmlAttribute("placeholder", &placeholder)) {
3030     if (name().empty() && !title_elem_id) {
3031       set_name(placeholder);
3032     } else if (description.empty()) {
3033       description = placeholder;
3034     }
3035   }
3036 
3037   SetStringAttribute(ui::AX_ATTR_DESCRIPTION, description);
3038   SetStringAttribute(ui::AX_ATTR_HELP, help);
3039 
3040   // On Windows, the value of a document should be its url.
3041   if (GetRole() == ui::AX_ROLE_ROOT_WEB_AREA ||
3042       GetRole() == ui::AX_ROLE_WEB_AREA) {
3043     set_value(GetStringAttribute(ui::AX_ATTR_DOC_URL));
3044   }
3045 
3046   // For certain roles (listbox option, static text, and list marker)
3047   // WebKit stores the main accessible text in the "value" - swap it so
3048   // that it's the "name".
3049   if (name().empty() &&
3050       (GetRole() == ui::AX_ROLE_LIST_BOX_OPTION ||
3051        GetRole() == ui::AX_ROLE_STATIC_TEXT ||
3052        GetRole() == ui::AX_ROLE_LIST_MARKER)) {
3053     std::string tmp = value();
3054     set_value(name());
3055     set_name(tmp);
3056   }
3057 
3058   // If this doesn't have a value and is linked then set its value to the url
3059   // attribute. This allows screen readers to read an empty link's destination.
3060   if (value().empty() && (ia_state_ & STATE_SYSTEM_LINKED))
3061     set_value(GetStringAttribute(ui::AX_ATTR_URL));
3062 
3063   // Clear any old relationships between this node and other nodes.
3064   for (size_t i = 0; i < relations_.size(); ++i)
3065     relations_[i]->Release();
3066   relations_.clear();
3067 
3068   // Handle title UI element.
3069   if (title_elem_id) {
3070     // Add a labelled by relationship.
3071     CComObject<BrowserAccessibilityRelation>* relation;
3072     HRESULT hr = CComObject<BrowserAccessibilityRelation>::CreateInstance(
3073         &relation);
3074     DCHECK(SUCCEEDED(hr));
3075     relation->AddRef();
3076     relation->Initialize(this, IA2_RELATION_LABELLED_BY);
3077     relation->AddTarget(title_elem_id);
3078     relations_.push_back(relation);
3079   }
3080 }
3081 
OnUpdateFinished()3082 void BrowserAccessibilityWin::OnUpdateFinished() {
3083   // Construct the hypertext for this node.
3084   hyperlink_offset_to_index_.clear();
3085   hyperlinks_.clear();
3086   hypertext_.clear();
3087   for (unsigned int i = 0; i < PlatformChildCount(); ++i) {
3088     BrowserAccessibility* child = PlatformGetChild(i);
3089     if (child->GetRole() == ui::AX_ROLE_STATIC_TEXT) {
3090       hypertext_ += base::UTF8ToUTF16(child->name());
3091     } else {
3092       hyperlink_offset_to_index_[hypertext_.size()] = hyperlinks_.size();
3093       hypertext_ += kEmbeddedCharacter;
3094       hyperlinks_.push_back(i);
3095     }
3096   }
3097   DCHECK_EQ(hyperlink_offset_to_index_.size(), hyperlinks_.size());
3098 
3099   // Fire an event when an alert first appears.
3100   if (GetRole() == ui::AX_ROLE_ALERT && first_time_)
3101     manager()->NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, this);
3102 
3103   // Fire events if text has changed.
3104   base::string16 text = TextForIAccessibleText();
3105   if (previous_text_ != text) {
3106     if (!previous_text_.empty() && !text.empty()) {
3107       manager()->NotifyAccessibilityEvent(
3108           ui::AX_EVENT_SHOW, this);
3109     }
3110 
3111     // TODO(dmazzoni): Look into HIDE events, too.
3112 
3113     old_text_ = previous_text_;
3114     previous_text_ = text;
3115   }
3116 
3117   BrowserAccessibilityManagerWin* manager =
3118       this->manager()->ToBrowserAccessibilityManagerWin();
3119 
3120   // Fire events if the state has changed.
3121   if (!first_time_ && ia_state_ != old_ia_state_) {
3122     // Normally focus events are handled elsewhere, however
3123     // focus for managed descendants is platform-specific.
3124     // Fire a focus event if the focused descendant in a multi-select
3125     // list box changes.
3126     if (GetRole() == ui::AX_ROLE_LIST_BOX_OPTION &&
3127         (ia_state_ & STATE_SYSTEM_FOCUSABLE) &&
3128         (ia_state_ & STATE_SYSTEM_SELECTABLE) &&
3129         (ia_state_ & STATE_SYSTEM_FOCUSED) &&
3130         !(old_ia_state_ & STATE_SYSTEM_FOCUSED)) {
3131       manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_FOCUS, unique_id_win());
3132     }
3133 
3134     if ((ia_state_ & STATE_SYSTEM_SELECTED) &&
3135         !(old_ia_state_ & STATE_SYSTEM_SELECTED)) {
3136       manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_SELECTIONADD,
3137                                        unique_id_win());
3138     } else if (!(ia_state_ & STATE_SYSTEM_SELECTED) &&
3139                (old_ia_state_ & STATE_SYSTEM_SELECTED)) {
3140       manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_SELECTIONREMOVE,
3141                                        unique_id_win());
3142     }
3143 
3144     old_ia_state_ = ia_state_;
3145   }
3146 
3147   // Fire an event if this container object has scrolled.
3148   int sx = 0;
3149   int sy = 0;
3150   if (GetIntAttribute(ui::AX_ATTR_SCROLL_X, &sx) &&
3151       GetIntAttribute(ui::AX_ATTR_SCROLL_Y, &sy)) {
3152     if (!first_time_ &&
3153         (sx != previous_scroll_x_ || sy != previous_scroll_y_)) {
3154       manager->MaybeCallNotifyWinEvent(EVENT_SYSTEM_SCROLLINGEND,
3155                                        unique_id_win());
3156     }
3157     previous_scroll_x_ = sx;
3158     previous_scroll_y_ = sy;
3159   }
3160 
3161   first_time_ = false;
3162 }
3163 
NativeAddReference()3164 void BrowserAccessibilityWin::NativeAddReference() {
3165   AddRef();
3166 }
3167 
NativeReleaseReference()3168 void BrowserAccessibilityWin::NativeReleaseReference() {
3169   Release();
3170 }
3171 
IsNative() const3172 bool BrowserAccessibilityWin::IsNative() const {
3173   return true;
3174 }
3175 
OnLocationChanged() const3176 void BrowserAccessibilityWin::OnLocationChanged() const {
3177   manager()->ToBrowserAccessibilityManagerWin()->MaybeCallNotifyWinEvent(
3178       EVENT_OBJECT_LOCATIONCHANGE, unique_id_win());
3179 }
3180 
NewReference()3181 BrowserAccessibilityWin* BrowserAccessibilityWin::NewReference() {
3182   AddRef();
3183   return this;
3184 }
3185 
GetTargetFromChildID(const VARIANT & var_id)3186 BrowserAccessibilityWin* BrowserAccessibilityWin::GetTargetFromChildID(
3187     const VARIANT& var_id) {
3188   if (var_id.vt != VT_I4)
3189     return NULL;
3190 
3191   LONG child_id = var_id.lVal;
3192   if (child_id == CHILDID_SELF)
3193     return this;
3194 
3195   if (child_id >= 1 && child_id <= static_cast<LONG>(PlatformChildCount()))
3196     return PlatformGetChild(child_id - 1)->ToBrowserAccessibilityWin();
3197 
3198   return manager()->ToBrowserAccessibilityManagerWin()->
3199       GetFromUniqueIdWin(child_id);
3200 }
3201 
GetStringAttributeAsBstr(ui::AXStringAttribute attribute,BSTR * value_bstr)3202 HRESULT BrowserAccessibilityWin::GetStringAttributeAsBstr(
3203     ui::AXStringAttribute attribute,
3204     BSTR* value_bstr) {
3205   base::string16 str;
3206 
3207   if (!GetString16Attribute(attribute, &str))
3208     return S_FALSE;
3209 
3210   if (str.empty())
3211     return S_FALSE;
3212 
3213   *value_bstr = SysAllocString(str.c_str());
3214   DCHECK(*value_bstr);
3215 
3216   return S_OK;
3217 }
3218 
StringAttributeToIA2(ui::AXStringAttribute attribute,const char * ia2_attr)3219 void BrowserAccessibilityWin::StringAttributeToIA2(
3220     ui::AXStringAttribute attribute,
3221     const char* ia2_attr) {
3222   base::string16 value;
3223   if (GetString16Attribute(attribute, &value))
3224     ia2_attributes_.push_back(base::ASCIIToUTF16(ia2_attr) + L":" + value);
3225 }
3226 
BoolAttributeToIA2(ui::AXBoolAttribute attribute,const char * ia2_attr)3227 void BrowserAccessibilityWin::BoolAttributeToIA2(
3228     ui::AXBoolAttribute attribute,
3229     const char* ia2_attr) {
3230   bool value;
3231   if (GetBoolAttribute(attribute, &value)) {
3232     ia2_attributes_.push_back((base::ASCIIToUTF16(ia2_attr) + L":") +
3233                               (value ? L"true" : L"false"));
3234   }
3235 }
3236 
IntAttributeToIA2(ui::AXIntAttribute attribute,const char * ia2_attr)3237 void BrowserAccessibilityWin::IntAttributeToIA2(
3238     ui::AXIntAttribute attribute,
3239     const char* ia2_attr) {
3240   int value;
3241   if (GetIntAttribute(attribute, &value)) {
3242     ia2_attributes_.push_back(base::ASCIIToUTF16(ia2_attr) + L":" +
3243                               base::IntToString16(value));
3244   }
3245 }
3246 
GetValueText()3247 base::string16 BrowserAccessibilityWin::GetValueText() {
3248   float fval;
3249   base::string16 value = base::UTF8ToUTF16(this->value());
3250 
3251   if (value.empty() &&
3252       GetFloatAttribute(ui::AX_ATTR_VALUE_FOR_RANGE, &fval)) {
3253     value = base::UTF8ToUTF16(base::DoubleToString(fval));
3254   }
3255   return value;
3256 }
3257 
TextForIAccessibleText()3258 base::string16 BrowserAccessibilityWin::TextForIAccessibleText() {
3259   if (IsEditableText())
3260     return base::UTF8ToUTF16(value());
3261   return (GetRole() == ui::AX_ROLE_STATIC_TEXT) ?
3262       base::UTF8ToUTF16(name()) : hypertext_;
3263 }
3264 
HandleSpecialTextOffset(const base::string16 & text,LONG * offset)3265 void BrowserAccessibilityWin::HandleSpecialTextOffset(
3266     const base::string16& text,
3267     LONG* offset) {
3268   if (*offset == IA2_TEXT_OFFSET_LENGTH)
3269     *offset = static_cast<LONG>(text.size());
3270   else if (*offset == IA2_TEXT_OFFSET_CARET)
3271     get_caretOffset(offset);
3272 }
3273 
IA2TextBoundaryToTextBoundary(IA2TextBoundaryType ia2_boundary)3274 ui::TextBoundaryType BrowserAccessibilityWin::IA2TextBoundaryToTextBoundary(
3275     IA2TextBoundaryType ia2_boundary) {
3276   switch(ia2_boundary) {
3277     case IA2_TEXT_BOUNDARY_CHAR: return ui::CHAR_BOUNDARY;
3278     case IA2_TEXT_BOUNDARY_WORD: return ui::WORD_BOUNDARY;
3279     case IA2_TEXT_BOUNDARY_LINE: return ui::LINE_BOUNDARY;
3280     case IA2_TEXT_BOUNDARY_SENTENCE: return ui::SENTENCE_BOUNDARY;
3281     case IA2_TEXT_BOUNDARY_PARAGRAPH: return ui::PARAGRAPH_BOUNDARY;
3282     case IA2_TEXT_BOUNDARY_ALL: return ui::ALL_BOUNDARY;
3283     default:
3284       NOTREACHED();
3285       return ui::CHAR_BOUNDARY;
3286   }
3287 }
3288 
FindBoundary(const base::string16 & text,IA2TextBoundaryType ia2_boundary,LONG start_offset,ui::TextBoundaryDirection direction)3289 LONG BrowserAccessibilityWin::FindBoundary(
3290     const base::string16& text,
3291     IA2TextBoundaryType ia2_boundary,
3292     LONG start_offset,
3293     ui::TextBoundaryDirection direction) {
3294   HandleSpecialTextOffset(text, &start_offset);
3295   ui::TextBoundaryType boundary = IA2TextBoundaryToTextBoundary(ia2_boundary);
3296   const std::vector<int32>& line_breaks = GetIntListAttribute(
3297       ui::AX_ATTR_LINE_BREAKS);
3298   return ui::FindAccessibleTextBoundary(
3299       text, line_breaks, boundary, start_offset, direction);
3300 }
3301 
GetFromID(int32 id)3302 BrowserAccessibilityWin* BrowserAccessibilityWin::GetFromID(int32 id) {
3303   return manager()->GetFromID(id)->ToBrowserAccessibilityWin();
3304 }
3305 
InitRoleAndState()3306 void BrowserAccessibilityWin::InitRoleAndState() {
3307   ia_state_ = 0;
3308   ia2_state_ = IA2_STATE_OPAQUE;
3309   ia2_attributes_.clear();
3310 
3311   if (HasState(ui::AX_STATE_BUSY))
3312     ia_state_ |= STATE_SYSTEM_BUSY;
3313   if (HasState(ui::AX_STATE_CHECKED))
3314     ia_state_ |= STATE_SYSTEM_CHECKED;
3315   if (HasState(ui::AX_STATE_COLLAPSED))
3316     ia_state_ |= STATE_SYSTEM_COLLAPSED;
3317   if (HasState(ui::AX_STATE_EXPANDED))
3318     ia_state_ |= STATE_SYSTEM_EXPANDED;
3319   if (HasState(ui::AX_STATE_FOCUSABLE))
3320     ia_state_ |= STATE_SYSTEM_FOCUSABLE;
3321   if (HasState(ui::AX_STATE_HASPOPUP))
3322     ia_state_ |= STATE_SYSTEM_HASPOPUP;
3323   if (HasState(ui::AX_STATE_HOVERED))
3324     ia_state_ |= STATE_SYSTEM_HOTTRACKED;
3325   if (HasState(ui::AX_STATE_INDETERMINATE))
3326     ia_state_ |= STATE_SYSTEM_INDETERMINATE;
3327   if (HasState(ui::AX_STATE_INVISIBLE))
3328     ia_state_ |= STATE_SYSTEM_INVISIBLE;
3329   if (HasState(ui::AX_STATE_LINKED))
3330     ia_state_ |= STATE_SYSTEM_LINKED;
3331   if (HasState(ui::AX_STATE_MULTISELECTABLE)) {
3332     ia_state_ |= STATE_SYSTEM_EXTSELECTABLE;
3333     ia_state_ |= STATE_SYSTEM_MULTISELECTABLE;
3334   }
3335   // TODO(ctguil): Support STATE_SYSTEM_EXTSELECTABLE/accSelect.
3336   if (HasState(ui::AX_STATE_OFFSCREEN))
3337     ia_state_ |= STATE_SYSTEM_OFFSCREEN;
3338   if (HasState(ui::AX_STATE_PRESSED))
3339     ia_state_ |= STATE_SYSTEM_PRESSED;
3340   if (HasState(ui::AX_STATE_PROTECTED))
3341     ia_state_ |= STATE_SYSTEM_PROTECTED;
3342   if (HasState(ui::AX_STATE_REQUIRED))
3343     ia2_state_ |= IA2_STATE_REQUIRED;
3344   if (HasState(ui::AX_STATE_SELECTABLE))
3345     ia_state_ |= STATE_SYSTEM_SELECTABLE;
3346   if (HasState(ui::AX_STATE_SELECTED))
3347     ia_state_ |= STATE_SYSTEM_SELECTED;
3348   if (HasState(ui::AX_STATE_VISITED))
3349     ia_state_ |= STATE_SYSTEM_TRAVERSED;
3350   if (!HasState(ui::AX_STATE_ENABLED))
3351     ia_state_ |= STATE_SYSTEM_UNAVAILABLE;
3352   if (HasState(ui::AX_STATE_VERTICAL)) {
3353     ia2_state_ |= IA2_STATE_VERTICAL;
3354   } else {
3355     ia2_state_ |= IA2_STATE_HORIZONTAL;
3356   }
3357   if (HasState(ui::AX_STATE_VISITED))
3358     ia_state_ |= STATE_SYSTEM_TRAVERSED;
3359 
3360   // WebKit marks everything as readonly unless it's editable text, so if it's
3361   // not readonly, mark it as editable now. The final computation of the
3362   // READONLY state for MSAA is below, after the switch.
3363   if (!HasState(ui::AX_STATE_READ_ONLY))
3364     ia2_state_ |= IA2_STATE_EDITABLE;
3365 
3366   base::string16 invalid;
3367   if (GetHtmlAttribute("aria-invalid", &invalid))
3368     ia2_state_ |= IA2_STATE_INVALID_ENTRY;
3369 
3370   if (GetBoolAttribute(ui::AX_ATTR_BUTTON_MIXED))
3371     ia_state_ |= STATE_SYSTEM_MIXED;
3372 
3373   if (GetBoolAttribute(ui::AX_ATTR_CAN_SET_VALUE))
3374     ia2_state_ |= IA2_STATE_EDITABLE;
3375 
3376   base::string16 html_tag = GetString16Attribute(
3377       ui::AX_ATTR_HTML_TAG);
3378   ia_role_ = 0;
3379   ia2_role_ = 0;
3380   switch (GetRole()) {
3381     case ui::AX_ROLE_ALERT:
3382       ia_role_ = ROLE_SYSTEM_ALERT;
3383       break;
3384     case ui::AX_ROLE_ALERT_DIALOG:
3385       ia_role_ = ROLE_SYSTEM_DIALOG;
3386       break;
3387     case ui::AX_ROLE_APPLICATION:
3388       ia_role_ = ROLE_SYSTEM_APPLICATION;
3389       break;
3390     case ui::AX_ROLE_ARTICLE:
3391       ia_role_ = ROLE_SYSTEM_GROUPING;
3392       ia2_role_ = IA2_ROLE_SECTION;
3393       ia_state_ |= STATE_SYSTEM_READONLY;
3394       break;
3395     case ui::AX_ROLE_BUSY_INDICATOR:
3396       ia_role_ = ROLE_SYSTEM_ANIMATION;
3397       ia_state_ |= STATE_SYSTEM_READONLY;
3398       break;
3399     case ui::AX_ROLE_BUTTON:
3400       ia_role_ = ROLE_SYSTEM_PUSHBUTTON;
3401       bool is_aria_pressed_defined;
3402       bool is_mixed;
3403       if (GetAriaTristate("aria-pressed", &is_aria_pressed_defined, &is_mixed))
3404         ia_state_ |= STATE_SYSTEM_PRESSED;
3405       if (is_aria_pressed_defined)
3406         ia2_role_ = IA2_ROLE_TOGGLE_BUTTON;
3407       if (is_mixed)
3408         ia_state_ |= STATE_SYSTEM_MIXED;
3409       break;
3410     case ui::AX_ROLE_CANVAS:
3411       if (GetBoolAttribute(ui::AX_ATTR_CANVAS_HAS_FALLBACK)) {
3412         role_name_ = L"canvas";
3413         ia2_role_ = IA2_ROLE_CANVAS;
3414       } else {
3415         ia_role_ = ROLE_SYSTEM_GRAPHIC;
3416       }
3417       break;
3418     case ui::AX_ROLE_CELL:
3419       ia_role_ = ROLE_SYSTEM_CELL;
3420       break;
3421     case ui::AX_ROLE_CHECK_BOX:
3422       ia_role_ = ROLE_SYSTEM_CHECKBUTTON;
3423       break;
3424     case ui::AX_ROLE_COLOR_WELL:
3425       ia_role_ = ROLE_SYSTEM_CLIENT;
3426       ia2_role_ = IA2_ROLE_COLOR_CHOOSER;
3427       break;
3428     case ui::AX_ROLE_COLUMN:
3429       ia_role_ = ROLE_SYSTEM_COLUMN;
3430       ia_state_ |= STATE_SYSTEM_READONLY;
3431       break;
3432     case ui::AX_ROLE_COLUMN_HEADER:
3433       ia_role_ = ROLE_SYSTEM_COLUMNHEADER;
3434       ia_state_ |= STATE_SYSTEM_READONLY;
3435       break;
3436     case ui::AX_ROLE_COMBO_BOX:
3437       ia_role_ = ROLE_SYSTEM_COMBOBOX;
3438       break;
3439     case ui::AX_ROLE_DIV:
3440       role_name_ = L"div";
3441       ia2_role_ = IA2_ROLE_SECTION;
3442       break;
3443     case ui::AX_ROLE_DEFINITION:
3444       role_name_ = html_tag;
3445       ia2_role_ = IA2_ROLE_PARAGRAPH;
3446       ia_state_ |= STATE_SYSTEM_READONLY;
3447       break;
3448     case ui::AX_ROLE_DESCRIPTION_LIST_DETAIL:
3449       role_name_ = html_tag;
3450       ia2_role_ = IA2_ROLE_PARAGRAPH;
3451       ia_state_ |= STATE_SYSTEM_READONLY;
3452       break;
3453     case ui::AX_ROLE_DESCRIPTION_LIST_TERM:
3454       ia_role_ = ROLE_SYSTEM_LISTITEM;
3455       ia_state_ |= STATE_SYSTEM_READONLY;
3456       break;
3457     case ui::AX_ROLE_DIALOG:
3458       ia_role_ = ROLE_SYSTEM_DIALOG;
3459       ia_state_ |= STATE_SYSTEM_READONLY;
3460       break;
3461     case ui::AX_ROLE_DISCLOSURE_TRIANGLE:
3462       ia_role_ = ROLE_SYSTEM_OUTLINEBUTTON;
3463       ia_state_ |= STATE_SYSTEM_READONLY;
3464       break;
3465     case ui::AX_ROLE_DOCUMENT:
3466     case ui::AX_ROLE_ROOT_WEB_AREA:
3467     case ui::AX_ROLE_WEB_AREA:
3468       ia_role_ = ROLE_SYSTEM_DOCUMENT;
3469       ia_state_ |= STATE_SYSTEM_READONLY;
3470       ia_state_ |= STATE_SYSTEM_FOCUSABLE;
3471       break;
3472     case ui::AX_ROLE_EDITABLE_TEXT:
3473       ia_role_ = ROLE_SYSTEM_TEXT;
3474       ia2_state_ |= IA2_STATE_SINGLE_LINE;
3475       ia2_state_ |= IA2_STATE_EDITABLE;
3476       break;
3477     case ui::AX_ROLE_FORM:
3478       role_name_ = L"form";
3479       ia2_role_ = IA2_ROLE_FORM;
3480       break;
3481     case ui::AX_ROLE_FOOTER:
3482       ia_role_ = IA2_ROLE_FOOTER;
3483       ia_state_ |= STATE_SYSTEM_READONLY;
3484       break;
3485     case ui::AX_ROLE_GRID:
3486       ia_role_ = ROLE_SYSTEM_TABLE;
3487       ia_state_ |= STATE_SYSTEM_READONLY;
3488       break;
3489     case ui::AX_ROLE_GROUP: {
3490       base::string16 aria_role = GetString16Attribute(
3491           ui::AX_ATTR_ROLE);
3492       if (aria_role == L"group" || html_tag == L"fieldset") {
3493         ia_role_ = ROLE_SYSTEM_GROUPING;
3494       } else if (html_tag == L"li") {
3495         ia_role_ = ROLE_SYSTEM_LISTITEM;
3496       } else {
3497         if (html_tag.empty())
3498           role_name_ = L"div";
3499         else
3500           role_name_ = html_tag;
3501         ia2_role_ = IA2_ROLE_SECTION;
3502       }
3503       ia_state_ |= STATE_SYSTEM_READONLY;
3504       break;
3505     }
3506     case ui::AX_ROLE_GROW_AREA:
3507       ia_role_ = ROLE_SYSTEM_GRIP;
3508       ia_state_ |= STATE_SYSTEM_READONLY;
3509       break;
3510     case ui::AX_ROLE_HEADING:
3511       role_name_ = html_tag;
3512       ia2_role_ = IA2_ROLE_HEADING;
3513       ia_state_ |= STATE_SYSTEM_READONLY;
3514       break;
3515     case ui::AX_ROLE_HORIZONTAL_RULE:
3516       ia_role_ = ROLE_SYSTEM_SEPARATOR;
3517       break;
3518     case ui::AX_ROLE_IFRAME:
3519       ia_role_ = ROLE_SYSTEM_CLIENT;
3520       ia2_role_ = IA2_ROLE_INTERNAL_FRAME;
3521       break;
3522     case ui::AX_ROLE_IMAGE:
3523       ia_role_ = ROLE_SYSTEM_GRAPHIC;
3524       ia_state_ |= STATE_SYSTEM_READONLY;
3525       break;
3526     case ui::AX_ROLE_IMAGE_MAP:
3527       role_name_ = html_tag;
3528       ia2_role_ = IA2_ROLE_IMAGE_MAP;
3529       ia_state_ |= STATE_SYSTEM_READONLY;
3530       break;
3531     case ui::AX_ROLE_IMAGE_MAP_LINK:
3532       ia_role_ = ROLE_SYSTEM_LINK;
3533       ia_state_ |= STATE_SYSTEM_LINKED;
3534       ia_state_ |= STATE_SYSTEM_READONLY;
3535       break;
3536     case ui::AX_ROLE_LABEL_TEXT:
3537       ia_role_ = ROLE_SYSTEM_TEXT;
3538       ia2_role_ = IA2_ROLE_LABEL;
3539       break;
3540     case ui::AX_ROLE_BANNER:
3541     case ui::AX_ROLE_COMPLEMENTARY:
3542     case ui::AX_ROLE_CONTENT_INFO:
3543     case ui::AX_ROLE_MAIN:
3544     case ui::AX_ROLE_NAVIGATION:
3545     case ui::AX_ROLE_SEARCH:
3546       ia_role_ = ROLE_SYSTEM_GROUPING;
3547       ia2_role_ = IA2_ROLE_SECTION;
3548       ia_state_ |= STATE_SYSTEM_READONLY;
3549       break;
3550     case ui::AX_ROLE_LINK:
3551       ia_role_ = ROLE_SYSTEM_LINK;
3552       ia_state_ |= STATE_SYSTEM_LINKED;
3553       break;
3554     case ui::AX_ROLE_LIST:
3555       ia_role_ = ROLE_SYSTEM_LIST;
3556       ia_state_ |= STATE_SYSTEM_READONLY;
3557       break;
3558     case ui::AX_ROLE_LIST_BOX:
3559       ia_role_ = ROLE_SYSTEM_LIST;
3560       break;
3561     case ui::AX_ROLE_LIST_BOX_OPTION:
3562       ia_role_ = ROLE_SYSTEM_LISTITEM;
3563       if (ia_state_ & STATE_SYSTEM_SELECTABLE) {
3564         ia_state_ |= STATE_SYSTEM_FOCUSABLE;
3565         if (HasState(ui::AX_STATE_FOCUSED))
3566           ia_state_ |= STATE_SYSTEM_FOCUSED;
3567       }
3568       break;
3569     case ui::AX_ROLE_LIST_ITEM:
3570       ia_role_ = ROLE_SYSTEM_LISTITEM;
3571       ia_state_ |= STATE_SYSTEM_READONLY;
3572       break;
3573     case ui::AX_ROLE_MATH_ELEMENT:
3574       ia_role_ = ROLE_SYSTEM_EQUATION;
3575       ia_state_ |= STATE_SYSTEM_READONLY;
3576       break;
3577     case ui::AX_ROLE_MENU:
3578     case ui::AX_ROLE_MENU_BUTTON:
3579       ia_role_ = ROLE_SYSTEM_MENUPOPUP;
3580       break;
3581     case ui::AX_ROLE_MENU_BAR:
3582       ia_role_ = ROLE_SYSTEM_MENUBAR;
3583       break;
3584     case ui::AX_ROLE_MENU_ITEM:
3585       ia_role_ = ROLE_SYSTEM_MENUITEM;
3586       break;
3587     case ui::AX_ROLE_MENU_LIST_POPUP:
3588       ia_role_ = ROLE_SYSTEM_CLIENT;
3589       break;
3590     case ui::AX_ROLE_MENU_LIST_OPTION:
3591       ia_role_ = ROLE_SYSTEM_LISTITEM;
3592       if (ia_state_ & STATE_SYSTEM_SELECTABLE) {
3593         ia_state_ |= STATE_SYSTEM_FOCUSABLE;
3594         if (HasState(ui::AX_STATE_FOCUSED))
3595           ia_state_ |= STATE_SYSTEM_FOCUSED;
3596       }
3597       break;
3598     case ui::AX_ROLE_NOTE:
3599       ia_role_ = ROLE_SYSTEM_GROUPING;
3600       ia2_role_ = IA2_ROLE_NOTE;
3601       ia_state_ |= STATE_SYSTEM_READONLY;
3602       break;
3603     case ui::AX_ROLE_OUTLINE:
3604       ia_role_ = ROLE_SYSTEM_OUTLINE;
3605       ia_state_ |= STATE_SYSTEM_READONLY;
3606       break;
3607     case ui::AX_ROLE_PARAGRAPH:
3608       role_name_ = L"P";
3609       ia2_role_ = IA2_ROLE_PARAGRAPH;
3610       break;
3611     case ui::AX_ROLE_POP_UP_BUTTON:
3612       if (html_tag == L"select") {
3613         ia_role_ = ROLE_SYSTEM_COMBOBOX;
3614       } else {
3615         ia_role_ = ROLE_SYSTEM_BUTTONMENU;
3616       }
3617       break;
3618     case ui::AX_ROLE_PROGRESS_INDICATOR:
3619       ia_role_ = ROLE_SYSTEM_PROGRESSBAR;
3620       ia_state_ |= STATE_SYSTEM_READONLY;
3621       break;
3622     case ui::AX_ROLE_RADIO_BUTTON:
3623       ia_role_ = ROLE_SYSTEM_RADIOBUTTON;
3624       break;
3625     case ui::AX_ROLE_RADIO_GROUP:
3626       ia_role_ = ROLE_SYSTEM_GROUPING;
3627       ia2_role_ = IA2_ROLE_SECTION;
3628       break;
3629     case ui::AX_ROLE_REGION:
3630       ia_role_ = ROLE_SYSTEM_GROUPING;
3631       ia2_role_ = IA2_ROLE_SECTION;
3632       ia_state_ |= STATE_SYSTEM_READONLY;
3633       break;
3634     case ui::AX_ROLE_ROW:
3635       ia_role_ = ROLE_SYSTEM_ROW;
3636       ia_state_ |= STATE_SYSTEM_READONLY;
3637       break;
3638     case ui::AX_ROLE_ROW_HEADER:
3639       ia_role_ = ROLE_SYSTEM_ROWHEADER;
3640       ia_state_ |= STATE_SYSTEM_READONLY;
3641       break;
3642     case ui::AX_ROLE_RULER:
3643       ia_role_ = ROLE_SYSTEM_CLIENT;
3644       ia2_role_ = IA2_ROLE_RULER;
3645       ia_state_ |= STATE_SYSTEM_READONLY;
3646       break;
3647     case ui::AX_ROLE_SCROLL_AREA:
3648       ia_role_ = ROLE_SYSTEM_CLIENT;
3649       ia2_role_ = IA2_ROLE_SCROLL_PANE;
3650       ia_state_ |= STATE_SYSTEM_READONLY;
3651       ia2_state_ &= ~(IA2_STATE_EDITABLE);
3652       break;
3653     case ui::AX_ROLE_SCROLL_BAR:
3654       ia_role_ = ROLE_SYSTEM_SCROLLBAR;
3655       break;
3656     case ui::AX_ROLE_SLIDER:
3657       ia_role_ = ROLE_SYSTEM_SLIDER;
3658       break;
3659     case ui::AX_ROLE_SPIN_BUTTON:
3660       ia_role_ = ROLE_SYSTEM_SPINBUTTON;
3661       break;
3662     case ui::AX_ROLE_SPIN_BUTTON_PART:
3663       ia_role_ = ROLE_SYSTEM_PUSHBUTTON;
3664       break;
3665     case ui::AX_ROLE_SPLIT_GROUP:
3666       ia_role_ = ROLE_SYSTEM_CLIENT;
3667       ia2_role_ = IA2_ROLE_SPLIT_PANE;
3668       ia_state_ |= STATE_SYSTEM_READONLY;
3669       break;
3670     case ui::AX_ROLE_ANNOTATION:
3671     case ui::AX_ROLE_LIST_MARKER:
3672     case ui::AX_ROLE_STATIC_TEXT:
3673       ia_role_ = ROLE_SYSTEM_STATICTEXT;
3674       break;
3675     case ui::AX_ROLE_STATUS:
3676       ia_role_ = ROLE_SYSTEM_STATUSBAR;
3677       ia_state_ |= STATE_SYSTEM_READONLY;
3678       break;
3679     case ui::AX_ROLE_SPLITTER:
3680       ia_role_ = ROLE_SYSTEM_SEPARATOR;
3681       break;
3682     case ui::AX_ROLE_SVG_ROOT:
3683       ia_role_ = ROLE_SYSTEM_GRAPHIC;
3684       break;
3685     case ui::AX_ROLE_TAB:
3686       ia_role_ = ROLE_SYSTEM_PAGETAB;
3687       break;
3688     case ui::AX_ROLE_TABLE: {
3689       base::string16 aria_role = GetString16Attribute(
3690           ui::AX_ATTR_ROLE);
3691       if (aria_role == L"treegrid") {
3692         ia_role_ = ROLE_SYSTEM_OUTLINE;
3693       } else {
3694         ia_role_ = ROLE_SYSTEM_TABLE;
3695         ia_state_ |= STATE_SYSTEM_READONLY;
3696       }
3697       break;
3698     }
3699     case ui::AX_ROLE_TABLE_HEADER_CONTAINER:
3700       ia_role_ = ROLE_SYSTEM_GROUPING;
3701       ia2_role_ = IA2_ROLE_SECTION;
3702       ia_state_ |= STATE_SYSTEM_READONLY;
3703       break;
3704     case ui::AX_ROLE_TAB_LIST:
3705       ia_role_ = ROLE_SYSTEM_PAGETABLIST;
3706       break;
3707     case ui::AX_ROLE_TAB_PANEL:
3708       ia_role_ = ROLE_SYSTEM_PROPERTYPAGE;
3709       break;
3710     case ui::AX_ROLE_TOGGLE_BUTTON:
3711       ia_role_ = ROLE_SYSTEM_PUSHBUTTON;
3712       ia2_role_ = IA2_ROLE_TOGGLE_BUTTON;
3713       break;
3714     case ui::AX_ROLE_TEXT_AREA:
3715       ia_role_ = ROLE_SYSTEM_TEXT;
3716       ia2_state_ |= IA2_STATE_MULTI_LINE;
3717       ia2_state_ |= IA2_STATE_EDITABLE;
3718       ia2_state_ |= IA2_STATE_SELECTABLE_TEXT;
3719       break;
3720     case ui::AX_ROLE_TEXT_FIELD:
3721       ia_role_ = ROLE_SYSTEM_TEXT;
3722       ia2_state_ |= IA2_STATE_SINGLE_LINE;
3723       ia2_state_ |= IA2_STATE_EDITABLE;
3724       ia2_state_ |= IA2_STATE_SELECTABLE_TEXT;
3725       break;
3726     case ui::AX_ROLE_TIMER:
3727       ia_role_ = ROLE_SYSTEM_CLOCK;
3728       ia_state_ |= STATE_SYSTEM_READONLY;
3729       break;
3730     case ui::AX_ROLE_TOOLBAR:
3731       ia_role_ = ROLE_SYSTEM_TOOLBAR;
3732       ia_state_ |= STATE_SYSTEM_READONLY;
3733       break;
3734     case ui::AX_ROLE_TOOLTIP:
3735       ia_role_ = ROLE_SYSTEM_TOOLTIP;
3736       ia_state_ |= STATE_SYSTEM_READONLY;
3737       break;
3738     case ui::AX_ROLE_TREE:
3739       ia_role_ = ROLE_SYSTEM_OUTLINE;
3740       ia_state_ |= STATE_SYSTEM_READONLY;
3741       break;
3742     case ui::AX_ROLE_TREE_GRID:
3743       ia_role_ = ROLE_SYSTEM_OUTLINE;
3744       ia_state_ |= STATE_SYSTEM_READONLY;
3745       break;
3746     case ui::AX_ROLE_TREE_ITEM:
3747       ia_role_ = ROLE_SYSTEM_OUTLINEITEM;
3748       ia_state_ |= STATE_SYSTEM_READONLY;
3749       break;
3750     case ui::AX_ROLE_WINDOW:
3751       ia_role_ = ROLE_SYSTEM_WINDOW;
3752       break;
3753 
3754     // TODO(dmazzoni): figure out the proper MSAA role for all of these.
3755     case ui::AX_ROLE_BROWSER:
3756     case ui::AX_ROLE_DIRECTORY:
3757     case ui::AX_ROLE_DRAWER:
3758     case ui::AX_ROLE_HELP_TAG:
3759     case ui::AX_ROLE_IGNORED:
3760     case ui::AX_ROLE_INCREMENTOR:
3761     case ui::AX_ROLE_LOG:
3762     case ui::AX_ROLE_MARQUEE:
3763     case ui::AX_ROLE_MATTE:
3764     case ui::AX_ROLE_PRESENTATIONAL:
3765     case ui::AX_ROLE_RULER_MARKER:
3766     case ui::AX_ROLE_SHEET:
3767     case ui::AX_ROLE_SLIDER_THUMB:
3768     case ui::AX_ROLE_SYSTEM_WIDE:
3769     case ui::AX_ROLE_VALUE_INDICATOR:
3770     default:
3771       ia_role_ = ROLE_SYSTEM_CLIENT;
3772       break;
3773   }
3774 
3775   // Compute the final value of READONLY for MSAA.
3776   //
3777   // We always set the READONLY state for elements that have the
3778   // aria-readonly attribute and for a few roles (in the switch above).
3779   // We clear the READONLY state on focusable controls and on a document.
3780   // Everything else, the majority of objects, do not have this state set.
3781   if (HasState(ui::AX_STATE_FOCUSABLE) &&
3782       ia_role_ != ROLE_SYSTEM_DOCUMENT) {
3783     ia_state_ &= ~(STATE_SYSTEM_READONLY);
3784   }
3785   if (!HasState(ui::AX_STATE_READ_ONLY))
3786     ia_state_ &= ~(STATE_SYSTEM_READONLY);
3787   if (GetBoolAttribute(ui::AX_ATTR_ARIA_READONLY))
3788     ia_state_ |= STATE_SYSTEM_READONLY;
3789 
3790   // The role should always be set.
3791   DCHECK(!role_name_.empty() || ia_role_);
3792 
3793   // If we didn't explicitly set the IAccessible2 role, make it the same
3794   // as the MSAA role.
3795   if (!ia2_role_)
3796     ia2_role_ = ia_role_;
3797 }
3798 
3799 }  // namespace content
3800