• 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/base/accessibility/accessible_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 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->GetFromRendererID(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->GetFromRendererID(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       AccessibilityNodeData::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       AccessibilityNodeData::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       AccessibilityNodeData::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       AccessibilityNodeData::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(AccessibilityNodeData::ATTR_TITLE_UI_ELEMENT,
472                                 &title_elem_id)) {
473       BrowserAccessibility* title_elem =
474           manager()->GetFromRendererID(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(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 = parent()->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 
503     // |parent| can only be NULL if the manager was created before the parent
504     // IAccessible was known and it wasn't subsequently set before a client
505     // requested it. Crash hard if this happens so that we get crash reports.
506     CHECK(parent_obj);
507   }
508 
509   parent_obj->AddRef();
510   *disp_parent = parent_obj;
511   return S_OK;
512 }
513 
get_accRole(VARIANT var_id,VARIANT * role)514 STDMETHODIMP BrowserAccessibilityWin::get_accRole(VARIANT var_id,
515                                                   VARIANT* role) {
516   if (!instance_active())
517     return E_FAIL;
518 
519   if (!role)
520     return E_INVALIDARG;
521 
522   BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
523   if (!target)
524     return E_INVALIDARG;
525 
526   if (!target->role_name_.empty()) {
527     role->vt = VT_BSTR;
528     role->bstrVal = SysAllocString(target->role_name_.c_str());
529   } else {
530     role->vt = VT_I4;
531     role->lVal = target->ia_role_;
532   }
533   return S_OK;
534 }
535 
get_accState(VARIANT var_id,VARIANT * state)536 STDMETHODIMP BrowserAccessibilityWin::get_accState(VARIANT var_id,
537                                                    VARIANT* state) {
538   if (!instance_active())
539     return E_FAIL;
540 
541   if (!state)
542     return E_INVALIDARG;
543 
544   BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
545   if (!target)
546     return E_INVALIDARG;
547 
548   state->vt = VT_I4;
549   state->lVal = target->ia_state_;
550   if (manager()->GetFocus(NULL) == this)
551     state->lVal |= STATE_SYSTEM_FOCUSED;
552 
553   return S_OK;
554 }
555 
get_accValue(VARIANT var_id,BSTR * value)556 STDMETHODIMP BrowserAccessibilityWin::get_accValue(VARIANT var_id,
557                                                    BSTR* value) {
558   if (!instance_active())
559     return E_FAIL;
560 
561   if (!value)
562     return E_INVALIDARG;
563 
564   BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
565   if (!target)
566     return E_INVALIDARG;
567 
568   if (target->ia_role() == ROLE_SYSTEM_PROGRESSBAR ||
569       target->ia_role() == ROLE_SYSTEM_SCROLLBAR ||
570       target->ia_role() == ROLE_SYSTEM_SLIDER) {
571     base::string16 value_text = target->GetValueText();
572     *value = SysAllocString(value_text.c_str());
573     DCHECK(*value);
574     return S_OK;
575   }
576 
577   // Expose color well value.
578   if (target->ia2_role() == IA2_ROLE_COLOR_CHOOSER) {
579     int r = target->GetIntAttribute(
580         AccessibilityNodeData::ATTR_COLOR_VALUE_RED);
581     int g = target->GetIntAttribute(
582         AccessibilityNodeData::ATTR_COLOR_VALUE_GREEN);
583     int b = target->GetIntAttribute(
584         AccessibilityNodeData::ATTR_COLOR_VALUE_BLUE);
585     base::string16 value_text;
586     value_text = base::IntToString16((r * 100) / 255) + L"% red " +
587                  base::IntToString16((g * 100) / 255) + L"% green " +
588                  base::IntToString16((b * 100) / 255) + L"% blue";
589     *value = SysAllocString(value_text.c_str());
590     DCHECK(*value);
591     return S_OK;
592   }
593 
594   *value = SysAllocString(UTF8ToUTF16(target->value()).c_str());
595   DCHECK(*value);
596   return S_OK;
597 }
598 
get_accHelpTopic(BSTR * help_file,VARIANT var_id,LONG * topic_id)599 STDMETHODIMP BrowserAccessibilityWin::get_accHelpTopic(BSTR* help_file,
600                                                        VARIANT var_id,
601                                                        LONG* topic_id) {
602   return E_NOTIMPL;
603 }
604 
get_accSelection(VARIANT * selected)605 STDMETHODIMP BrowserAccessibilityWin::get_accSelection(VARIANT* selected) {
606   if (!instance_active())
607     return E_FAIL;
608 
609   if (blink_role() != blink::WebAXRoleListBox)
610     return E_NOTIMPL;
611 
612   unsigned long selected_count = 0;
613   for (size_t i = 0; i < children().size(); ++i) {
614     if (children()[i]->HasState(blink::WebAXStateSelected))
615       ++selected_count;
616   }
617 
618   if (selected_count == 0) {
619     selected->vt = VT_EMPTY;
620     return S_OK;
621   }
622 
623   if (selected_count == 1) {
624     for (size_t i = 0; i < children().size(); ++i) {
625       if (children()[i]->HasState(blink::WebAXStateSelected)) {
626         selected->vt = VT_DISPATCH;
627         selected->pdispVal =
628             children()[i]->ToBrowserAccessibilityWin()->NewReference();
629         return S_OK;
630       }
631     }
632   }
633 
634   // Multiple items are selected.
635   base::win::EnumVariant* enum_variant =
636       new base::win::EnumVariant(selected_count);
637   enum_variant->AddRef();
638   unsigned long index = 0;
639   for (size_t i = 0; i < children().size(); ++i) {
640     if (children()[i]->HasState(blink::WebAXStateSelected)) {
641       enum_variant->ItemAt(index)->vt = VT_DISPATCH;
642       enum_variant->ItemAt(index)->pdispVal =
643         children()[i]->ToBrowserAccessibilityWin()->NewReference();
644       ++index;
645     }
646   }
647   selected->vt = VT_UNKNOWN;
648   selected->punkVal = static_cast<IUnknown*>(
649       static_cast<base::win::IUnknownImpl*>(enum_variant));
650   return S_OK;
651 }
652 
accSelect(LONG flags_sel,VARIANT var_id)653 STDMETHODIMP BrowserAccessibilityWin::accSelect(
654     LONG flags_sel, VARIANT var_id) {
655   if (!instance_active())
656     return E_FAIL;
657 
658   if (flags_sel & SELFLAG_TAKEFOCUS) {
659     manager()->SetFocus(this, true);
660     return S_OK;
661   }
662 
663   return S_FALSE;
664 }
665 
666 //
667 // IAccessible2 methods.
668 //
669 
role(LONG * role)670 STDMETHODIMP BrowserAccessibilityWin::role(LONG* role) {
671   if (!instance_active())
672     return E_FAIL;
673 
674   if (!role)
675     return E_INVALIDARG;
676 
677   *role = ia2_role_;
678 
679   return S_OK;
680 }
681 
get_attributes(BSTR * attributes)682 STDMETHODIMP BrowserAccessibilityWin::get_attributes(BSTR* attributes) {
683   if (!instance_active())
684     return E_FAIL;
685 
686   if (!attributes)
687     return E_INVALIDARG;
688 
689   // The iaccessible2 attributes are a set of key-value pairs
690   // separated by semicolons, with a colon between the key and the value.
691   base::string16 str;
692   for (unsigned int i = 0; i < ia2_attributes_.size(); ++i) {
693     if (i != 0)
694       str += L';';
695     str += ia2_attributes_[i];
696   }
697 
698   if (str.empty())
699     return S_FALSE;
700 
701   *attributes = SysAllocString(str.c_str());
702   DCHECK(*attributes);
703   return S_OK;
704 }
705 
get_states(AccessibleStates * states)706 STDMETHODIMP BrowserAccessibilityWin::get_states(AccessibleStates* states) {
707   if (!instance_active())
708     return E_FAIL;
709 
710   if (!states)
711     return E_INVALIDARG;
712 
713   *states = ia2_state_;
714 
715   return S_OK;
716 }
717 
get_uniqueID(LONG * unique_id)718 STDMETHODIMP BrowserAccessibilityWin::get_uniqueID(LONG* unique_id) {
719   if (!instance_active())
720     return E_FAIL;
721 
722   if (!unique_id)
723     return E_INVALIDARG;
724 
725   *unique_id = unique_id_win_;
726   return S_OK;
727 }
728 
get_windowHandle(HWND * window_handle)729 STDMETHODIMP BrowserAccessibilityWin::get_windowHandle(HWND* window_handle) {
730   if (!instance_active())
731     return E_FAIL;
732 
733   if (!window_handle)
734     return E_INVALIDARG;
735 
736   *window_handle = manager()->ToBrowserAccessibilityManagerWin()->parent_hwnd();
737   return S_OK;
738 }
739 
get_indexInParent(LONG * index_in_parent)740 STDMETHODIMP BrowserAccessibilityWin::get_indexInParent(LONG* index_in_parent) {
741   if (!instance_active())
742     return E_FAIL;
743 
744   if (!index_in_parent)
745     return E_INVALIDARG;
746 
747   *index_in_parent = this->index_in_parent();
748   return S_OK;
749 }
750 
get_nRelations(LONG * n_relations)751 STDMETHODIMP BrowserAccessibilityWin::get_nRelations(LONG* n_relations) {
752   if (!instance_active())
753     return E_FAIL;
754 
755   if (!n_relations)
756     return E_INVALIDARG;
757 
758   *n_relations = relations_.size();
759   return S_OK;
760 }
761 
get_relation(LONG relation_index,IAccessibleRelation ** relation)762 STDMETHODIMP BrowserAccessibilityWin::get_relation(
763     LONG relation_index,
764     IAccessibleRelation** relation) {
765   if (!instance_active())
766     return E_FAIL;
767 
768   if (relation_index < 0 ||
769       relation_index >= static_cast<long>(relations_.size())) {
770     return E_INVALIDARG;
771   }
772 
773   if (!relation)
774     return E_INVALIDARG;
775 
776   relations_[relation_index]->AddRef();
777   *relation = relations_[relation_index];
778   return S_OK;
779 }
780 
get_relations(LONG max_relations,IAccessibleRelation ** relations,LONG * n_relations)781 STDMETHODIMP BrowserAccessibilityWin::get_relations(
782     LONG max_relations,
783     IAccessibleRelation** relations,
784     LONG* n_relations) {
785   if (!instance_active())
786     return E_FAIL;
787 
788   if (!relations || !n_relations)
789     return E_INVALIDARG;
790 
791   long count = static_cast<long>(relations_.size());
792   *n_relations = count;
793   if (count == 0)
794     return S_FALSE;
795 
796   for (long i = 0; i < count; ++i) {
797     relations_[i]->AddRef();
798     relations[i] = relations_[i];
799   }
800 
801   return S_OK;
802 }
803 
scrollTo(enum IA2ScrollType scroll_type)804 STDMETHODIMP BrowserAccessibilityWin::scrollTo(enum IA2ScrollType scroll_type) {
805   if (!instance_active())
806     return E_FAIL;
807 
808   gfx::Rect r = location();
809   switch(scroll_type) {
810     case IA2_SCROLL_TYPE_TOP_LEFT:
811       manager()->ScrollToMakeVisible(*this, gfx::Rect(r.x(), r.y(), 0, 0));
812       break;
813     case IA2_SCROLL_TYPE_BOTTOM_RIGHT:
814       manager()->ScrollToMakeVisible(
815           *this, gfx::Rect(r.right(), r.bottom(), 0, 0));
816       break;
817     case IA2_SCROLL_TYPE_TOP_EDGE:
818       manager()->ScrollToMakeVisible(
819           *this, gfx::Rect(r.x(), r.y(), r.width(), 0));
820       break;
821     case IA2_SCROLL_TYPE_BOTTOM_EDGE:
822       manager()->ScrollToMakeVisible(
823           *this, gfx::Rect(r.x(), r.bottom(), r.width(), 0));
824     break;
825     case IA2_SCROLL_TYPE_LEFT_EDGE:
826       manager()->ScrollToMakeVisible(
827           *this, gfx::Rect(r.x(), r.y(), 0, r.height()));
828       break;
829     case IA2_SCROLL_TYPE_RIGHT_EDGE:
830       manager()->ScrollToMakeVisible(
831           *this, gfx::Rect(r.right(), r.y(), 0, r.height()));
832       break;
833     case IA2_SCROLL_TYPE_ANYWHERE:
834     default:
835       manager()->ScrollToMakeVisible(*this, r);
836       break;
837   }
838 
839   manager()->ToBrowserAccessibilityManagerWin()->TrackScrollingObject(this);
840 
841   return S_OK;
842 }
843 
scrollToPoint(enum IA2CoordinateType coordinate_type,LONG x,LONG y)844 STDMETHODIMP BrowserAccessibilityWin::scrollToPoint(
845     enum IA2CoordinateType coordinate_type,
846     LONG x,
847     LONG y) {
848   if (!instance_active())
849     return E_FAIL;
850 
851   gfx::Point scroll_to(x, y);
852 
853   if (coordinate_type == IA2_COORDTYPE_SCREEN_RELATIVE) {
854     scroll_to -= manager()->GetViewBounds().OffsetFromOrigin();
855   } else if (coordinate_type == IA2_COORDTYPE_PARENT_RELATIVE) {
856     if (parent())
857       scroll_to += parent()->location().OffsetFromOrigin();
858   } else {
859     return E_INVALIDARG;
860   }
861 
862   manager()->ScrollToPoint(*this, scroll_to);
863   manager()->ToBrowserAccessibilityManagerWin()->TrackScrollingObject(this);
864 
865   return S_OK;
866 }
867 
get_groupPosition(LONG * group_level,LONG * similar_items_in_group,LONG * position_in_group)868 STDMETHODIMP BrowserAccessibilityWin::get_groupPosition(
869     LONG* group_level,
870     LONG* similar_items_in_group,
871     LONG* position_in_group) {
872   if (!instance_active())
873     return E_FAIL;
874 
875   if (!group_level || !similar_items_in_group || !position_in_group)
876     return E_INVALIDARG;
877 
878   if (blink_role() == blink::WebAXRoleListBoxOption &&
879       parent() &&
880       parent()->role() == blink::WebAXRoleListBox) {
881     *group_level = 0;
882     *similar_items_in_group = parent()->PlatformChildCount();
883     *position_in_group = index_in_parent() + 1;
884     return S_OK;
885   }
886 
887   return E_NOTIMPL;
888 }
889 
890 //
891 // IAccessibleApplication methods.
892 //
893 
get_appName(BSTR * app_name)894 STDMETHODIMP BrowserAccessibilityWin::get_appName(BSTR* app_name) {
895   // No need to check |instance_active()| because this interface is
896   // global, and doesn't depend on any local state.
897 
898   if (!app_name)
899     return E_INVALIDARG;
900 
901   // GetProduct() returns a string like "Chrome/aa.bb.cc.dd", split out
902   // the part before the "/".
903   std::vector<std::string> product_components;
904   base::SplitString(GetContentClient()->GetProduct(), '/', &product_components);
905   DCHECK_EQ(2U, product_components.size());
906   if (product_components.size() != 2)
907     return E_FAIL;
908   *app_name = SysAllocString(UTF8ToUTF16(product_components[0]).c_str());
909   DCHECK(*app_name);
910   return *app_name ? S_OK : E_FAIL;
911 }
912 
get_appVersion(BSTR * app_version)913 STDMETHODIMP BrowserAccessibilityWin::get_appVersion(BSTR* app_version) {
914   // No need to check |instance_active()| because this interface is
915   // global, and doesn't depend on any local state.
916 
917   if (!app_version)
918     return E_INVALIDARG;
919 
920   // GetProduct() returns a string like "Chrome/aa.bb.cc.dd", split out
921   // the part after the "/".
922   std::vector<std::string> product_components;
923   base::SplitString(GetContentClient()->GetProduct(), '/', &product_components);
924   DCHECK_EQ(2U, product_components.size());
925   if (product_components.size() != 2)
926     return E_FAIL;
927   *app_version = SysAllocString(UTF8ToUTF16(product_components[1]).c_str());
928   DCHECK(*app_version);
929   return *app_version ? S_OK : E_FAIL;
930 }
931 
get_toolkitName(BSTR * toolkit_name)932 STDMETHODIMP BrowserAccessibilityWin::get_toolkitName(BSTR* toolkit_name) {
933   // No need to check |instance_active()| because this interface is
934   // global, and doesn't depend on any local state.
935 
936   if (!toolkit_name)
937     return E_INVALIDARG;
938 
939   // This is hard-coded; all products based on the Chromium engine
940   // will have the same toolkit name, so that assistive technology can
941   // detect any Chrome-based product.
942   *toolkit_name = SysAllocString(L"Chrome");
943   DCHECK(*toolkit_name);
944   return *toolkit_name ? S_OK : E_FAIL;
945 }
946 
get_toolkitVersion(BSTR * toolkit_version)947 STDMETHODIMP BrowserAccessibilityWin::get_toolkitVersion(
948     BSTR* toolkit_version) {
949   // No need to check |instance_active()| because this interface is
950   // global, and doesn't depend on any local state.
951 
952   if (!toolkit_version)
953     return E_INVALIDARG;
954 
955   std::string user_agent = GetContentClient()->GetUserAgent();
956   *toolkit_version = SysAllocString(UTF8ToUTF16(user_agent).c_str());
957   DCHECK(*toolkit_version);
958   return *toolkit_version ? S_OK : E_FAIL;
959 }
960 
961 //
962 // IAccessibleImage methods.
963 //
964 
get_description(BSTR * desc)965 STDMETHODIMP BrowserAccessibilityWin::get_description(BSTR* desc) {
966   if (!instance_active())
967     return E_FAIL;
968 
969   if (!desc)
970     return E_INVALIDARG;
971 
972   return GetStringAttributeAsBstr(
973       AccessibilityNodeData::ATTR_DESCRIPTION, desc);
974 }
975 
get_imagePosition(enum IA2CoordinateType coordinate_type,LONG * x,LONG * y)976 STDMETHODIMP BrowserAccessibilityWin::get_imagePosition(
977     enum IA2CoordinateType coordinate_type,
978     LONG* x,
979     LONG* y) {
980   if (!instance_active())
981     return E_FAIL;
982 
983   if (!x || !y)
984     return E_INVALIDARG;
985 
986   if (coordinate_type == IA2_COORDTYPE_SCREEN_RELATIVE) {
987     HWND parent_hwnd =
988         manager()->ToBrowserAccessibilityManagerWin()->parent_hwnd();
989     POINT top_left = {0, 0};
990     ::ClientToScreen(parent_hwnd, &top_left);
991     *x = location().x() + top_left.x;
992     *y = location().y() + top_left.y;
993   } else if (coordinate_type == IA2_COORDTYPE_PARENT_RELATIVE) {
994     *x = location().x();
995     *y = location().y();
996     if (parent()) {
997       *x -= parent()->location().x();
998       *y -= parent()->location().y();
999     }
1000   } else {
1001     return E_INVALIDARG;
1002   }
1003 
1004   return S_OK;
1005 }
1006 
get_imageSize(LONG * height,LONG * width)1007 STDMETHODIMP BrowserAccessibilityWin::get_imageSize(LONG* height, LONG* width) {
1008   if (!instance_active())
1009     return E_FAIL;
1010 
1011   if (!height || !width)
1012     return E_INVALIDARG;
1013 
1014   *height = location().height();
1015   *width = location().width();
1016   return S_OK;
1017 }
1018 
1019 //
1020 // IAccessibleTable methods.
1021 //
1022 
get_accessibleAt(long row,long column,IUnknown ** accessible)1023 STDMETHODIMP BrowserAccessibilityWin::get_accessibleAt(
1024     long row,
1025     long column,
1026     IUnknown** accessible) {
1027   if (!instance_active())
1028     return E_FAIL;
1029 
1030   if (!accessible)
1031     return E_INVALIDARG;
1032 
1033   int columns;
1034   int rows;
1035   if (!GetIntAttribute(
1036           AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT, &columns) ||
1037       !GetIntAttribute(
1038           AccessibilityNodeData::ATTR_TABLE_ROW_COUNT, &rows) ||
1039       columns <= 0 ||
1040       rows <= 0) {
1041     return S_FALSE;
1042   }
1043 
1044   if (row < 0 || row >= rows || column < 0 || column >= columns)
1045     return E_INVALIDARG;
1046 
1047   const std::vector<int32>& cell_ids = GetIntListAttribute(
1048       AccessibilityNodeData::ATTR_CELL_IDS);
1049   DCHECK_EQ(columns * rows, static_cast<int>(cell_ids.size()));
1050 
1051   int cell_id = cell_ids[row * columns + column];
1052   BrowserAccessibilityWin* cell = GetFromRendererID(cell_id);
1053   if (cell) {
1054     *accessible = static_cast<IAccessible*>(cell->NewReference());
1055     return S_OK;
1056   }
1057 
1058   *accessible = NULL;
1059   return E_INVALIDARG;
1060 }
1061 
get_caption(IUnknown ** accessible)1062 STDMETHODIMP BrowserAccessibilityWin::get_caption(IUnknown** accessible) {
1063   if (!instance_active())
1064     return E_FAIL;
1065 
1066   if (!accessible)
1067     return E_INVALIDARG;
1068 
1069   // TODO(dmazzoni): implement
1070   return S_FALSE;
1071 }
1072 
get_childIndex(long row,long column,long * cell_index)1073 STDMETHODIMP BrowserAccessibilityWin::get_childIndex(long row,
1074                                                      long column,
1075                                                      long* cell_index) {
1076   if (!instance_active())
1077     return E_FAIL;
1078 
1079   if (!cell_index)
1080     return E_INVALIDARG;
1081 
1082   int columns;
1083   int rows;
1084   if (!GetIntAttribute(
1085           AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT, &columns) ||
1086       !GetIntAttribute(
1087           AccessibilityNodeData::ATTR_TABLE_ROW_COUNT, &rows) ||
1088       columns <= 0 ||
1089       rows <= 0) {
1090     return S_FALSE;
1091   }
1092 
1093   if (row < 0 || row >= rows || column < 0 || column >= columns)
1094     return E_INVALIDARG;
1095 
1096   const std::vector<int32>& cell_ids = GetIntListAttribute(
1097       AccessibilityNodeData::ATTR_CELL_IDS);
1098   const std::vector<int32>& unique_cell_ids = GetIntListAttribute(
1099       AccessibilityNodeData::ATTR_UNIQUE_CELL_IDS);
1100   DCHECK_EQ(columns * rows, static_cast<int>(cell_ids.size()));
1101   int cell_id = cell_ids[row * columns + column];
1102   for (size_t i = 0; i < unique_cell_ids.size(); ++i) {
1103     if (unique_cell_ids[i] == cell_id) {
1104       *cell_index = (long)i;
1105       return S_OK;
1106     }
1107   }
1108 
1109   return S_FALSE;
1110 }
1111 
get_columnDescription(long column,BSTR * description)1112 STDMETHODIMP BrowserAccessibilityWin::get_columnDescription(long column,
1113                                                             BSTR* description) {
1114   if (!instance_active())
1115     return E_FAIL;
1116 
1117   if (!description)
1118     return E_INVALIDARG;
1119 
1120   int columns;
1121   int rows;
1122   if (!GetIntAttribute(
1123           AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT, &columns) ||
1124       !GetIntAttribute(AccessibilityNodeData::ATTR_TABLE_ROW_COUNT, &rows) ||
1125       columns <= 0 ||
1126       rows <= 0) {
1127     return S_FALSE;
1128   }
1129 
1130   if (column < 0 || column >= columns)
1131     return E_INVALIDARG;
1132 
1133   const std::vector<int32>& cell_ids = GetIntListAttribute(
1134       AccessibilityNodeData::ATTR_CELL_IDS);
1135   for (int i = 0; i < rows; ++i) {
1136     int cell_id = cell_ids[i * columns + column];
1137     BrowserAccessibilityWin* cell = static_cast<BrowserAccessibilityWin*>(
1138         manager()->GetFromRendererID(cell_id));
1139     if (cell && cell->blink_role() == blink::WebAXRoleColumnHeader) {
1140       base::string16 cell_name = cell->GetString16Attribute(
1141           AccessibilityNodeData::ATTR_NAME);
1142       if (cell_name.size() > 0) {
1143         *description = SysAllocString(cell_name.c_str());
1144         return S_OK;
1145       }
1146 
1147       return cell->GetStringAttributeAsBstr(
1148           AccessibilityNodeData::ATTR_DESCRIPTION, description);
1149     }
1150   }
1151 
1152   return S_FALSE;
1153 }
1154 
get_columnExtentAt(long row,long column,long * n_columns_spanned)1155 STDMETHODIMP BrowserAccessibilityWin::get_columnExtentAt(
1156     long row,
1157     long column,
1158     long* n_columns_spanned) {
1159   if (!instance_active())
1160     return E_FAIL;
1161 
1162   if (!n_columns_spanned)
1163     return E_INVALIDARG;
1164 
1165   int columns;
1166   int rows;
1167   if (!GetIntAttribute(
1168           AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT, &columns) ||
1169       !GetIntAttribute(AccessibilityNodeData::ATTR_TABLE_ROW_COUNT, &rows) ||
1170       columns <= 0 ||
1171       rows <= 0) {
1172     return S_FALSE;
1173   }
1174 
1175   if (row < 0 || row >= rows || column < 0 || column >= columns)
1176     return E_INVALIDARG;
1177 
1178   const std::vector<int32>& cell_ids = GetIntListAttribute(
1179       AccessibilityNodeData::ATTR_CELL_IDS);
1180   int cell_id = cell_ids[row * columns + column];
1181   BrowserAccessibilityWin* cell = static_cast<BrowserAccessibilityWin*>(
1182       manager()->GetFromRendererID(cell_id));
1183   int colspan;
1184   if (cell &&
1185       cell->GetIntAttribute(
1186           AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_SPAN, &colspan) &&
1187       colspan >= 1) {
1188     *n_columns_spanned = colspan;
1189     return S_OK;
1190   }
1191 
1192   return S_FALSE;
1193 }
1194 
get_columnHeader(IAccessibleTable ** accessible_table,long * starting_row_index)1195 STDMETHODIMP BrowserAccessibilityWin::get_columnHeader(
1196     IAccessibleTable** accessible_table,
1197     long* starting_row_index) {
1198   // TODO(dmazzoni): implement
1199   return E_NOTIMPL;
1200 }
1201 
get_columnIndex(long cell_index,long * column_index)1202 STDMETHODIMP BrowserAccessibilityWin::get_columnIndex(long cell_index,
1203                                                       long* column_index) {
1204   if (!instance_active())
1205     return E_FAIL;
1206 
1207   if (!column_index)
1208     return E_INVALIDARG;
1209 
1210   const std::vector<int32>& unique_cell_ids = GetIntListAttribute(
1211       AccessibilityNodeData::ATTR_UNIQUE_CELL_IDS);
1212   int cell_id_count = static_cast<int>(unique_cell_ids.size());
1213   if (cell_index < 0)
1214     return E_INVALIDARG;
1215   if (cell_index >= cell_id_count)
1216     return S_FALSE;
1217 
1218   int cell_id = unique_cell_ids[cell_index];
1219   BrowserAccessibilityWin* cell =
1220       manager()->GetFromRendererID(cell_id)->ToBrowserAccessibilityWin();
1221   int col_index;
1222   if (cell &&
1223       cell->GetIntAttribute(
1224           AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_INDEX, &col_index)) {
1225     *column_index = col_index;
1226     return S_OK;
1227   }
1228 
1229   return S_FALSE;
1230 }
1231 
get_nColumns(long * column_count)1232 STDMETHODIMP BrowserAccessibilityWin::get_nColumns(long* column_count) {
1233   if (!instance_active())
1234     return E_FAIL;
1235 
1236   if (!column_count)
1237     return E_INVALIDARG;
1238 
1239   int columns;
1240   if (GetIntAttribute(
1241           AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT, &columns)) {
1242     *column_count = columns;
1243     return S_OK;
1244   }
1245 
1246   return S_FALSE;
1247 }
1248 
get_nRows(long * row_count)1249 STDMETHODIMP BrowserAccessibilityWin::get_nRows(long* row_count) {
1250   if (!instance_active())
1251     return E_FAIL;
1252 
1253   if (!row_count)
1254     return E_INVALIDARG;
1255 
1256   int rows;
1257   if (GetIntAttribute(AccessibilityNodeData::ATTR_TABLE_ROW_COUNT, &rows)) {
1258     *row_count = rows;
1259     return S_OK;
1260   }
1261 
1262   return S_FALSE;
1263 }
1264 
get_nSelectedChildren(long * cell_count)1265 STDMETHODIMP BrowserAccessibilityWin::get_nSelectedChildren(long* cell_count) {
1266   if (!instance_active())
1267     return E_FAIL;
1268 
1269   if (!cell_count)
1270     return E_INVALIDARG;
1271 
1272   // TODO(dmazzoni): add support for selected cells/rows/columns in tables.
1273   *cell_count = 0;
1274   return S_OK;
1275 }
1276 
get_nSelectedColumns(long * column_count)1277 STDMETHODIMP BrowserAccessibilityWin::get_nSelectedColumns(long* column_count) {
1278   if (!instance_active())
1279     return E_FAIL;
1280 
1281   if (!column_count)
1282     return E_INVALIDARG;
1283 
1284   *column_count = 0;
1285   return S_OK;
1286 }
1287 
get_nSelectedRows(long * row_count)1288 STDMETHODIMP BrowserAccessibilityWin::get_nSelectedRows(long* row_count) {
1289   if (!instance_active())
1290     return E_FAIL;
1291 
1292   if (!row_count)
1293     return E_INVALIDARG;
1294 
1295   *row_count = 0;
1296   return S_OK;
1297 }
1298 
get_rowDescription(long row,BSTR * description)1299 STDMETHODIMP BrowserAccessibilityWin::get_rowDescription(long row,
1300                                                          BSTR* description) {
1301   if (!instance_active())
1302     return E_FAIL;
1303 
1304   if (!description)
1305     return E_INVALIDARG;
1306 
1307   int columns;
1308   int rows;
1309   if (!GetIntAttribute(
1310           AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT, &columns) ||
1311       !GetIntAttribute(AccessibilityNodeData::ATTR_TABLE_ROW_COUNT, &rows) ||
1312       columns <= 0 ||
1313       rows <= 0) {
1314     return S_FALSE;
1315   }
1316 
1317   if (row < 0 || row >= rows)
1318     return E_INVALIDARG;
1319 
1320   const std::vector<int32>& cell_ids = GetIntListAttribute(
1321       AccessibilityNodeData::ATTR_CELL_IDS);
1322   for (int i = 0; i < columns; ++i) {
1323     int cell_id = cell_ids[row * columns + i];
1324     BrowserAccessibilityWin* cell =
1325         manager()->GetFromRendererID(cell_id)->ToBrowserAccessibilityWin();
1326     if (cell && cell->blink_role() == blink::WebAXRoleRowHeader) {
1327       base::string16 cell_name = cell->GetString16Attribute(
1328           AccessibilityNodeData::ATTR_NAME);
1329       if (cell_name.size() > 0) {
1330         *description = SysAllocString(cell_name.c_str());
1331         return S_OK;
1332       }
1333 
1334       return cell->GetStringAttributeAsBstr(
1335           AccessibilityNodeData::ATTR_DESCRIPTION, description);
1336     }
1337   }
1338 
1339   return S_FALSE;
1340 }
1341 
get_rowExtentAt(long row,long column,long * n_rows_spanned)1342 STDMETHODIMP BrowserAccessibilityWin::get_rowExtentAt(long row,
1343                                                       long column,
1344                                                       long* n_rows_spanned) {
1345   if (!instance_active())
1346     return E_FAIL;
1347 
1348   if (!n_rows_spanned)
1349     return E_INVALIDARG;
1350 
1351   int columns;
1352   int rows;
1353   if (!GetIntAttribute(
1354           AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT, &columns) ||
1355       !GetIntAttribute(AccessibilityNodeData::ATTR_TABLE_ROW_COUNT, &rows) ||
1356       columns <= 0 ||
1357       rows <= 0) {
1358     return S_FALSE;
1359   }
1360 
1361   if (row < 0 || row >= rows || column < 0 || column >= columns)
1362     return E_INVALIDARG;
1363 
1364   const std::vector<int32>& cell_ids = GetIntListAttribute(
1365       AccessibilityNodeData::ATTR_CELL_IDS);
1366   int cell_id = cell_ids[row * columns + column];
1367   BrowserAccessibilityWin* cell =
1368       manager()->GetFromRendererID(cell_id)->ToBrowserAccessibilityWin();
1369   int rowspan;
1370   if (cell &&
1371       cell->GetIntAttribute(
1372           AccessibilityNodeData::ATTR_TABLE_CELL_ROW_SPAN, &rowspan) &&
1373       rowspan >= 1) {
1374     *n_rows_spanned = rowspan;
1375     return S_OK;
1376   }
1377 
1378   return S_FALSE;
1379 }
1380 
get_rowHeader(IAccessibleTable ** accessible_table,long * starting_column_index)1381 STDMETHODIMP BrowserAccessibilityWin::get_rowHeader(
1382     IAccessibleTable** accessible_table,
1383     long* starting_column_index) {
1384   // TODO(dmazzoni): implement
1385   return E_NOTIMPL;
1386 }
1387 
get_rowIndex(long cell_index,long * row_index)1388 STDMETHODIMP BrowserAccessibilityWin::get_rowIndex(long cell_index,
1389                                                    long* row_index) {
1390   if (!instance_active())
1391     return E_FAIL;
1392 
1393   if (!row_index)
1394     return E_INVALIDARG;
1395 
1396   const std::vector<int32>& unique_cell_ids = GetIntListAttribute(
1397       AccessibilityNodeData::ATTR_UNIQUE_CELL_IDS);
1398   int cell_id_count = static_cast<int>(unique_cell_ids.size());
1399   if (cell_index < 0)
1400     return E_INVALIDARG;
1401   if (cell_index >= cell_id_count)
1402     return S_FALSE;
1403 
1404   int cell_id = unique_cell_ids[cell_index];
1405   BrowserAccessibilityWin* cell =
1406       manager()->GetFromRendererID(cell_id)->ToBrowserAccessibilityWin();
1407   int cell_row_index;
1408   if (cell &&
1409       cell->GetIntAttribute(
1410           AccessibilityNodeData::ATTR_TABLE_CELL_ROW_INDEX, &cell_row_index)) {
1411     *row_index = cell_row_index;
1412     return S_OK;
1413   }
1414 
1415   return S_FALSE;
1416 }
1417 
get_selectedChildren(long max_children,long ** children,long * n_children)1418 STDMETHODIMP BrowserAccessibilityWin::get_selectedChildren(long max_children,
1419                                                            long** children,
1420                                                            long* n_children) {
1421   if (!instance_active())
1422     return E_FAIL;
1423 
1424   if (!children || !n_children)
1425     return E_INVALIDARG;
1426 
1427   // TODO(dmazzoni): Implement this.
1428   *n_children = 0;
1429   return S_OK;
1430 }
1431 
get_selectedColumns(long max_columns,long ** columns,long * n_columns)1432 STDMETHODIMP BrowserAccessibilityWin::get_selectedColumns(long max_columns,
1433                                                           long** columns,
1434                                                           long* n_columns) {
1435   if (!instance_active())
1436     return E_FAIL;
1437 
1438   if (!columns || !n_columns)
1439     return E_INVALIDARG;
1440 
1441   // TODO(dmazzoni): Implement this.
1442   *n_columns = 0;
1443   return S_OK;
1444 }
1445 
get_selectedRows(long max_rows,long ** rows,long * n_rows)1446 STDMETHODIMP BrowserAccessibilityWin::get_selectedRows(long max_rows,
1447                                                        long** rows,
1448                                                        long* n_rows) {
1449   if (!instance_active())
1450     return E_FAIL;
1451 
1452   if (!rows || !n_rows)
1453     return E_INVALIDARG;
1454 
1455   // TODO(dmazzoni): Implement this.
1456   *n_rows = 0;
1457   return S_OK;
1458 }
1459 
get_summary(IUnknown ** accessible)1460 STDMETHODIMP BrowserAccessibilityWin::get_summary(IUnknown** accessible) {
1461   if (!instance_active())
1462     return E_FAIL;
1463 
1464   if (!accessible)
1465     return E_INVALIDARG;
1466 
1467   // TODO(dmazzoni): implement
1468   return S_FALSE;
1469 }
1470 
get_isColumnSelected(long column,boolean * is_selected)1471 STDMETHODIMP BrowserAccessibilityWin::get_isColumnSelected(
1472     long column,
1473     boolean* is_selected) {
1474   if (!instance_active())
1475     return E_FAIL;
1476 
1477   if (!is_selected)
1478     return E_INVALIDARG;
1479 
1480   // TODO(dmazzoni): Implement this.
1481   *is_selected = false;
1482   return S_OK;
1483 }
1484 
get_isRowSelected(long row,boolean * is_selected)1485 STDMETHODIMP BrowserAccessibilityWin::get_isRowSelected(long row,
1486                                                         boolean* is_selected) {
1487   if (!instance_active())
1488     return E_FAIL;
1489 
1490   if (!is_selected)
1491     return E_INVALIDARG;
1492 
1493   // TODO(dmazzoni): Implement this.
1494   *is_selected = false;
1495   return S_OK;
1496 }
1497 
get_isSelected(long row,long column,boolean * is_selected)1498 STDMETHODIMP BrowserAccessibilityWin::get_isSelected(long row,
1499                                                      long column,
1500                                                      boolean* is_selected) {
1501   if (!instance_active())
1502     return E_FAIL;
1503 
1504   if (!is_selected)
1505     return E_INVALIDARG;
1506 
1507   // TODO(dmazzoni): Implement this.
1508   *is_selected = false;
1509   return S_OK;
1510 }
1511 
get_rowColumnExtentsAtIndex(long index,long * row,long * column,long * row_extents,long * column_extents,boolean * is_selected)1512 STDMETHODIMP BrowserAccessibilityWin::get_rowColumnExtentsAtIndex(
1513     long index,
1514     long* row,
1515     long* column,
1516     long* row_extents,
1517     long* column_extents,
1518     boolean* is_selected) {
1519   if (!instance_active())
1520     return E_FAIL;
1521 
1522   if (!row || !column || !row_extents || !column_extents || !is_selected)
1523     return E_INVALIDARG;
1524 
1525   const std::vector<int32>& unique_cell_ids = GetIntListAttribute(
1526       AccessibilityNodeData::ATTR_UNIQUE_CELL_IDS);
1527   int cell_id_count = static_cast<int>(unique_cell_ids.size());
1528   if (index < 0)
1529     return E_INVALIDARG;
1530   if (index >= cell_id_count)
1531     return S_FALSE;
1532 
1533   int cell_id = unique_cell_ids[index];
1534   BrowserAccessibilityWin* cell =
1535       manager()->GetFromRendererID(cell_id)->ToBrowserAccessibilityWin();
1536   int rowspan;
1537   int colspan;
1538   if (cell &&
1539       cell->GetIntAttribute(
1540           AccessibilityNodeData::ATTR_TABLE_CELL_ROW_SPAN, &rowspan) &&
1541       cell->GetIntAttribute(
1542           AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_SPAN, &colspan) &&
1543       rowspan >= 1 &&
1544       colspan >= 1) {
1545     *row_extents = rowspan;
1546     *column_extents = colspan;
1547     return S_OK;
1548   }
1549 
1550   return S_FALSE;
1551 }
1552 
1553 //
1554 // IAccessibleTable2 methods.
1555 //
1556 
get_cellAt(long row,long column,IUnknown ** cell)1557 STDMETHODIMP BrowserAccessibilityWin::get_cellAt(long row,
1558                                                  long column,
1559                                                  IUnknown** cell) {
1560   return get_accessibleAt(row, column, cell);
1561 }
1562 
get_nSelectedCells(long * cell_count)1563 STDMETHODIMP BrowserAccessibilityWin::get_nSelectedCells(long* cell_count) {
1564   return get_nSelectedChildren(cell_count);
1565 }
1566 
get_selectedCells(IUnknown *** cells,long * n_selected_cells)1567 STDMETHODIMP BrowserAccessibilityWin::get_selectedCells(
1568     IUnknown*** cells,
1569     long* n_selected_cells) {
1570   if (!instance_active())
1571     return E_FAIL;
1572 
1573   if (!cells || !n_selected_cells)
1574     return E_INVALIDARG;
1575 
1576   // TODO(dmazzoni): Implement this.
1577   *n_selected_cells = 0;
1578   return S_OK;
1579 }
1580 
get_selectedColumns(long ** columns,long * n_columns)1581 STDMETHODIMP BrowserAccessibilityWin::get_selectedColumns(long** columns,
1582                                                           long* n_columns) {
1583   if (!instance_active())
1584     return E_FAIL;
1585 
1586   if (!columns || !n_columns)
1587     return E_INVALIDARG;
1588 
1589   // TODO(dmazzoni): Implement this.
1590   *n_columns = 0;
1591   return S_OK;
1592 }
1593 
get_selectedRows(long ** rows,long * n_rows)1594 STDMETHODIMP BrowserAccessibilityWin::get_selectedRows(long** rows,
1595                                                        long* n_rows) {
1596   if (!instance_active())
1597     return E_FAIL;
1598 
1599   if (!rows || !n_rows)
1600     return E_INVALIDARG;
1601 
1602   // TODO(dmazzoni): Implement this.
1603   *n_rows = 0;
1604   return S_OK;
1605 }
1606 
1607 
1608 //
1609 // IAccessibleTableCell methods.
1610 //
1611 
get_columnExtent(long * n_columns_spanned)1612 STDMETHODIMP BrowserAccessibilityWin::get_columnExtent(
1613     long* n_columns_spanned) {
1614   if (!instance_active())
1615     return E_FAIL;
1616 
1617   if (!n_columns_spanned)
1618     return E_INVALIDARG;
1619 
1620   int colspan;
1621   if (GetIntAttribute(
1622           AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_SPAN, &colspan) &&
1623       colspan >= 1) {
1624     *n_columns_spanned = colspan;
1625     return S_OK;
1626   }
1627 
1628   return S_FALSE;
1629 }
1630 
get_columnHeaderCells(IUnknown *** cell_accessibles,long * n_column_header_cells)1631 STDMETHODIMP BrowserAccessibilityWin::get_columnHeaderCells(
1632     IUnknown*** cell_accessibles,
1633     long* n_column_header_cells) {
1634   if (!instance_active())
1635     return E_FAIL;
1636 
1637   if (!cell_accessibles || !n_column_header_cells)
1638     return E_INVALIDARG;
1639 
1640   *n_column_header_cells = 0;
1641 
1642   int column;
1643   if (!GetIntAttribute(
1644           AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_INDEX, &column)) {
1645     return S_FALSE;
1646   }
1647 
1648   BrowserAccessibility* table = parent();
1649   while (table && table->role() != blink::WebAXRoleTable)
1650     table = table->parent();
1651   if (!table) {
1652     NOTREACHED();
1653     return S_FALSE;
1654   }
1655 
1656   int columns;
1657   int rows;
1658   if (!table->GetIntAttribute(
1659           AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT, &columns) ||
1660       !table->GetIntAttribute(
1661           AccessibilityNodeData::ATTR_TABLE_ROW_COUNT, &rows)) {
1662     return S_FALSE;
1663   }
1664   if (columns <= 0 || rows <= 0 || column < 0 || column >= columns)
1665     return S_FALSE;
1666 
1667   const std::vector<int32>& cell_ids = table->GetIntListAttribute(
1668       AccessibilityNodeData::ATTR_CELL_IDS);
1669 
1670   for (int i = 0; i < rows; ++i) {
1671     int cell_id = cell_ids[i * columns + column];
1672     BrowserAccessibilityWin* cell =
1673         manager()->GetFromRendererID(cell_id)->ToBrowserAccessibilityWin();
1674     if (cell && cell->blink_role() == blink::WebAXRoleColumnHeader)
1675       (*n_column_header_cells)++;
1676   }
1677 
1678   *cell_accessibles = static_cast<IUnknown**>(CoTaskMemAlloc(
1679       (*n_column_header_cells) * sizeof(cell_accessibles[0])));
1680   int index = 0;
1681   for (int i = 0; i < rows; ++i) {
1682     int cell_id = cell_ids[i * columns + column];
1683     BrowserAccessibility* cell = manager()->GetFromRendererID(cell_id);
1684     if (cell && cell->role() == blink::WebAXRoleColumnHeader) {
1685       (*cell_accessibles)[index] = static_cast<IAccessible*>(
1686           cell->ToBrowserAccessibilityWin()->NewReference());
1687       ++index;
1688     }
1689   }
1690 
1691   return S_OK;
1692 }
1693 
get_columnIndex(long * column_index)1694 STDMETHODIMP BrowserAccessibilityWin::get_columnIndex(long* column_index) {
1695   if (!instance_active())
1696     return E_FAIL;
1697 
1698   if (!column_index)
1699     return E_INVALIDARG;
1700 
1701   int column;
1702   if (GetIntAttribute(
1703           AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_INDEX, &column)) {
1704     *column_index = column;
1705     return S_OK;
1706   }
1707 
1708   return S_FALSE;
1709 }
1710 
get_rowExtent(long * n_rows_spanned)1711 STDMETHODIMP BrowserAccessibilityWin::get_rowExtent(long* n_rows_spanned) {
1712   if (!instance_active())
1713     return E_FAIL;
1714 
1715   if (!n_rows_spanned)
1716     return E_INVALIDARG;
1717 
1718   int rowspan;
1719   if (GetIntAttribute(
1720           AccessibilityNodeData::ATTR_TABLE_CELL_ROW_SPAN, &rowspan) &&
1721       rowspan >= 1) {
1722     *n_rows_spanned = rowspan;
1723     return S_OK;
1724   }
1725 
1726   return S_FALSE;
1727 }
1728 
get_rowHeaderCells(IUnknown *** cell_accessibles,long * n_row_header_cells)1729 STDMETHODIMP BrowserAccessibilityWin::get_rowHeaderCells(
1730     IUnknown*** cell_accessibles,
1731     long* n_row_header_cells) {
1732   if (!instance_active())
1733     return E_FAIL;
1734 
1735   if (!cell_accessibles || !n_row_header_cells)
1736     return E_INVALIDARG;
1737 
1738   *n_row_header_cells = 0;
1739 
1740   int row;
1741   if (!GetIntAttribute(
1742           AccessibilityNodeData::ATTR_TABLE_CELL_ROW_INDEX, &row)) {
1743     return S_FALSE;
1744   }
1745 
1746   BrowserAccessibility* table = parent();
1747   while (table && table->role() != blink::WebAXRoleTable)
1748     table = table->parent();
1749   if (!table) {
1750     NOTREACHED();
1751     return S_FALSE;
1752   }
1753 
1754   int columns;
1755   int rows;
1756   if (!table->GetIntAttribute(
1757           AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT, &columns) ||
1758       !table->GetIntAttribute(
1759           AccessibilityNodeData::ATTR_TABLE_ROW_COUNT, &rows)) {
1760     return S_FALSE;
1761   }
1762   if (columns <= 0 || rows <= 0 || row < 0 || row >= rows)
1763     return S_FALSE;
1764 
1765   const std::vector<int32>& cell_ids = table->GetIntListAttribute(
1766       AccessibilityNodeData::ATTR_CELL_IDS);
1767 
1768   for (int i = 0; i < columns; ++i) {
1769     int cell_id = cell_ids[row * columns + i];
1770     BrowserAccessibility* cell = manager()->GetFromRendererID(cell_id);
1771     if (cell && cell->role() == blink::WebAXRoleRowHeader)
1772       (*n_row_header_cells)++;
1773   }
1774 
1775   *cell_accessibles = static_cast<IUnknown**>(CoTaskMemAlloc(
1776       (*n_row_header_cells) * sizeof(cell_accessibles[0])));
1777   int index = 0;
1778   for (int i = 0; i < columns; ++i) {
1779     int cell_id = cell_ids[row * columns + i];
1780     BrowserAccessibility* cell = manager()->GetFromRendererID(cell_id);
1781     if (cell && cell->role() == blink::WebAXRoleRowHeader) {
1782       (*cell_accessibles)[index] = static_cast<IAccessible*>(
1783           cell->ToBrowserAccessibilityWin()->NewReference());
1784       ++index;
1785     }
1786   }
1787 
1788   return S_OK;
1789 }
1790 
get_rowIndex(long * row_index)1791 STDMETHODIMP BrowserAccessibilityWin::get_rowIndex(long* row_index) {
1792   if (!instance_active())
1793     return E_FAIL;
1794 
1795   if (!row_index)
1796     return E_INVALIDARG;
1797 
1798   int row;
1799   if (GetIntAttribute(AccessibilityNodeData::ATTR_TABLE_CELL_ROW_INDEX, &row)) {
1800     *row_index = row;
1801     return S_OK;
1802   }
1803   return S_FALSE;
1804 }
1805 
get_isSelected(boolean * is_selected)1806 STDMETHODIMP BrowserAccessibilityWin::get_isSelected(boolean* is_selected) {
1807   if (!instance_active())
1808     return E_FAIL;
1809 
1810   if (!is_selected)
1811     return E_INVALIDARG;
1812 
1813   *is_selected = false;
1814   return S_OK;
1815 }
1816 
get_rowColumnExtents(long * row_index,long * column_index,long * row_extents,long * column_extents,boolean * is_selected)1817 STDMETHODIMP BrowserAccessibilityWin::get_rowColumnExtents(
1818     long* row_index,
1819     long* column_index,
1820     long* row_extents,
1821     long* column_extents,
1822     boolean* is_selected) {
1823   if (!instance_active())
1824     return E_FAIL;
1825 
1826   if (!row_index ||
1827       !column_index ||
1828       !row_extents ||
1829       !column_extents ||
1830       !is_selected) {
1831     return E_INVALIDARG;
1832   }
1833 
1834   int row;
1835   int column;
1836   int rowspan;
1837   int colspan;
1838   if (GetIntAttribute(AccessibilityNodeData::ATTR_TABLE_CELL_ROW_INDEX, &row) &&
1839       GetIntAttribute(
1840           AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_INDEX, &column) &&
1841       GetIntAttribute(
1842           AccessibilityNodeData::ATTR_TABLE_CELL_ROW_SPAN, &rowspan) &&
1843       GetIntAttribute(
1844           AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_SPAN, &colspan)) {
1845     *row_index = row;
1846     *column_index = column;
1847     *row_extents = rowspan;
1848     *column_extents = colspan;
1849     *is_selected = false;
1850     return S_OK;
1851   }
1852 
1853   return S_FALSE;
1854 }
1855 
get_table(IUnknown ** table)1856 STDMETHODIMP BrowserAccessibilityWin::get_table(IUnknown** table) {
1857   if (!instance_active())
1858     return E_FAIL;
1859 
1860   if (!table)
1861     return E_INVALIDARG;
1862 
1863 
1864   int row;
1865   int column;
1866   GetIntAttribute(AccessibilityNodeData::ATTR_TABLE_CELL_ROW_INDEX, &row);
1867   GetIntAttribute(AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_INDEX, &column);
1868 
1869   BrowserAccessibility* find_table = parent();
1870   while (find_table && find_table->role() != blink::WebAXRoleTable)
1871     find_table = find_table->parent();
1872   if (!find_table) {
1873     NOTREACHED();
1874     return S_FALSE;
1875   }
1876 
1877   *table = static_cast<IAccessibleTable*>(
1878       find_table->ToBrowserAccessibilityWin()->NewReference());
1879 
1880   return S_OK;
1881 }
1882 
1883 //
1884 // IAccessibleText methods.
1885 //
1886 
get_nCharacters(LONG * n_characters)1887 STDMETHODIMP BrowserAccessibilityWin::get_nCharacters(LONG* n_characters) {
1888   if (!instance_active())
1889     return E_FAIL;
1890 
1891   if (!n_characters)
1892     return E_INVALIDARG;
1893 
1894   *n_characters = TextForIAccessibleText().length();
1895   return S_OK;
1896 }
1897 
get_caretOffset(LONG * offset)1898 STDMETHODIMP BrowserAccessibilityWin::get_caretOffset(LONG* offset) {
1899   if (!instance_active())
1900     return E_FAIL;
1901 
1902   if (!offset)
1903     return E_INVALIDARG;
1904 
1905   *offset = 0;
1906   if (blink_role() == blink::WebAXRoleTextField ||
1907       blink_role() == blink::WebAXRoleTextArea) {
1908     int sel_start = 0;
1909     if (GetIntAttribute(AccessibilityNodeData::ATTR_TEXT_SEL_START,
1910                         &sel_start))
1911       *offset = sel_start;
1912   }
1913 
1914   return S_OK;
1915 }
1916 
get_characterExtents(LONG offset,enum IA2CoordinateType coordinate_type,LONG * out_x,LONG * out_y,LONG * out_width,LONG * out_height)1917 STDMETHODIMP BrowserAccessibilityWin::get_characterExtents(
1918     LONG offset,
1919     enum IA2CoordinateType coordinate_type,
1920     LONG* out_x,
1921     LONG* out_y,
1922     LONG* out_width,
1923     LONG* out_height) {
1924   if (!instance_active())
1925     return E_FAIL;
1926 
1927   if (!out_x || !out_y || !out_width || !out_height)
1928     return E_INVALIDARG;
1929 
1930   const base::string16& text_str = TextForIAccessibleText();
1931   HandleSpecialTextOffset(text_str, &offset);
1932 
1933   if (offset < 0 || offset > static_cast<LONG>(text_str.size()))
1934     return E_INVALIDARG;
1935 
1936   if (blink_role() != blink::WebAXRoleStaticText)
1937     return E_FAIL;
1938 
1939   gfx::Rect character_bounds;
1940   if (coordinate_type == IA2_COORDTYPE_SCREEN_RELATIVE) {
1941     character_bounds = GetGlobalBoundsForRange(offset, 1);
1942   } else if (coordinate_type == IA2_COORDTYPE_PARENT_RELATIVE) {
1943     character_bounds = GetLocalBoundsForRange(offset, 1);
1944     character_bounds -= location().OffsetFromOrigin();
1945   } else {
1946     return E_INVALIDARG;
1947   }
1948 
1949   *out_x = character_bounds.x();
1950   *out_y = character_bounds.y();
1951   *out_width = character_bounds.width();
1952   *out_height = character_bounds.height();
1953 
1954   return S_OK;
1955 }
1956 
get_nSelections(LONG * n_selections)1957 STDMETHODIMP BrowserAccessibilityWin::get_nSelections(LONG* n_selections) {
1958   if (!instance_active())
1959     return E_FAIL;
1960 
1961   if (!n_selections)
1962     return E_INVALIDARG;
1963 
1964   *n_selections = 0;
1965   if (blink_role() == blink::WebAXRoleTextField ||
1966       blink_role() == blink::WebAXRoleTextArea) {
1967     int sel_start = 0;
1968     int sel_end = 0;
1969     if (GetIntAttribute(AccessibilityNodeData::ATTR_TEXT_SEL_START,
1970                         &sel_start) &&
1971         GetIntAttribute(AccessibilityNodeData::ATTR_TEXT_SEL_END, &sel_end) &&
1972         sel_start != sel_end)
1973       *n_selections = 1;
1974   }
1975 
1976   return S_OK;
1977 }
1978 
get_selection(LONG selection_index,LONG * start_offset,LONG * end_offset)1979 STDMETHODIMP BrowserAccessibilityWin::get_selection(LONG selection_index,
1980                                                     LONG* start_offset,
1981                                                     LONG* end_offset) {
1982   if (!instance_active())
1983     return E_FAIL;
1984 
1985   if (!start_offset || !end_offset || selection_index != 0)
1986     return E_INVALIDARG;
1987 
1988   *start_offset = 0;
1989   *end_offset = 0;
1990   if (blink_role() == blink::WebAXRoleTextField ||
1991       blink_role() == blink::WebAXRoleTextArea) {
1992     int sel_start = 0;
1993     int sel_end = 0;
1994     if (GetIntAttribute(
1995             AccessibilityNodeData::ATTR_TEXT_SEL_START, &sel_start) &&
1996         GetIntAttribute(AccessibilityNodeData::ATTR_TEXT_SEL_END, &sel_end)) {
1997       *start_offset = sel_start;
1998       *end_offset = sel_end;
1999     }
2000   }
2001 
2002   return S_OK;
2003 }
2004 
get_text(LONG start_offset,LONG end_offset,BSTR * text)2005 STDMETHODIMP BrowserAccessibilityWin::get_text(LONG start_offset,
2006                                                LONG end_offset,
2007                                                BSTR* text) {
2008   if (!instance_active())
2009     return E_FAIL;
2010 
2011   if (!text)
2012     return E_INVALIDARG;
2013 
2014   const base::string16& text_str = TextForIAccessibleText();
2015 
2016   // Handle special text offsets.
2017   HandleSpecialTextOffset(text_str, &start_offset);
2018   HandleSpecialTextOffset(text_str, &end_offset);
2019 
2020   // The spec allows the arguments to be reversed.
2021   if (start_offset > end_offset) {
2022     LONG tmp = start_offset;
2023     start_offset = end_offset;
2024     end_offset = tmp;
2025   }
2026 
2027   // The spec does not allow the start or end offsets to be out or range;
2028   // we must return an error if so.
2029   LONG len = text_str.length();
2030   if (start_offset < 0)
2031     return E_INVALIDARG;
2032   if (end_offset > len)
2033     return E_INVALIDARG;
2034 
2035   base::string16 substr = text_str.substr(start_offset,
2036                                           end_offset - start_offset);
2037   if (substr.empty())
2038     return S_FALSE;
2039 
2040   *text = SysAllocString(substr.c_str());
2041   DCHECK(*text);
2042   return S_OK;
2043 }
2044 
get_textAtOffset(LONG offset,enum IA2TextBoundaryType boundary_type,LONG * start_offset,LONG * end_offset,BSTR * text)2045 STDMETHODIMP BrowserAccessibilityWin::get_textAtOffset(
2046     LONG offset,
2047     enum IA2TextBoundaryType boundary_type,
2048     LONG* start_offset,
2049     LONG* end_offset,
2050     BSTR* text) {
2051   if (!instance_active())
2052     return E_FAIL;
2053 
2054   if (!start_offset || !end_offset || !text)
2055     return E_INVALIDARG;
2056 
2057   // The IAccessible2 spec says we don't have to implement the "sentence"
2058   // boundary type, we can just let the screenreader handle it.
2059   if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) {
2060     *start_offset = 0;
2061     *end_offset = 0;
2062     *text = NULL;
2063     return S_FALSE;
2064   }
2065 
2066   const base::string16& text_str = TextForIAccessibleText();
2067 
2068   *start_offset = FindBoundary(
2069       text_str, boundary_type, offset, ui::BACKWARDS_DIRECTION);
2070   *end_offset = FindBoundary(
2071       text_str, boundary_type, offset, ui::FORWARDS_DIRECTION);
2072   return get_text(*start_offset, *end_offset, text);
2073 }
2074 
get_textBeforeOffset(LONG offset,enum IA2TextBoundaryType boundary_type,LONG * start_offset,LONG * end_offset,BSTR * text)2075 STDMETHODIMP BrowserAccessibilityWin::get_textBeforeOffset(
2076     LONG offset,
2077     enum IA2TextBoundaryType boundary_type,
2078     LONG* start_offset,
2079     LONG* end_offset,
2080     BSTR* text) {
2081   if (!instance_active())
2082     return E_FAIL;
2083 
2084   if (!start_offset || !end_offset || !text)
2085     return E_INVALIDARG;
2086 
2087   // The IAccessible2 spec says we don't have to implement the "sentence"
2088   // boundary type, we can just let the screenreader handle it.
2089   if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) {
2090     *start_offset = 0;
2091     *end_offset = 0;
2092     *text = NULL;
2093     return S_FALSE;
2094   }
2095 
2096   const base::string16& text_str = TextForIAccessibleText();
2097 
2098   *start_offset = FindBoundary(
2099       text_str, boundary_type, offset, ui::BACKWARDS_DIRECTION);
2100   *end_offset = offset;
2101   return get_text(*start_offset, *end_offset, text);
2102 }
2103 
get_textAfterOffset(LONG offset,enum IA2TextBoundaryType boundary_type,LONG * start_offset,LONG * end_offset,BSTR * text)2104 STDMETHODIMP BrowserAccessibilityWin::get_textAfterOffset(
2105     LONG offset,
2106     enum IA2TextBoundaryType boundary_type,
2107     LONG* start_offset,
2108     LONG* end_offset,
2109     BSTR* text) {
2110   if (!instance_active())
2111     return E_FAIL;
2112 
2113   if (!start_offset || !end_offset || !text)
2114     return E_INVALIDARG;
2115 
2116   // The IAccessible2 spec says we don't have to implement the "sentence"
2117   // boundary type, we can just let the screenreader handle it.
2118   if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) {
2119     *start_offset = 0;
2120     *end_offset = 0;
2121     *text = NULL;
2122     return S_FALSE;
2123   }
2124 
2125   const base::string16& text_str = TextForIAccessibleText();
2126 
2127   *start_offset = offset;
2128   *end_offset = FindBoundary(
2129       text_str, boundary_type, offset, ui::FORWARDS_DIRECTION);
2130   return get_text(*start_offset, *end_offset, text);
2131 }
2132 
get_newText(IA2TextSegment * new_text)2133 STDMETHODIMP BrowserAccessibilityWin::get_newText(IA2TextSegment* new_text) {
2134   if (!instance_active())
2135     return E_FAIL;
2136 
2137   if (!new_text)
2138     return E_INVALIDARG;
2139 
2140   base::string16 text = TextForIAccessibleText();
2141 
2142   new_text->text = SysAllocString(text.c_str());
2143   new_text->start = 0;
2144   new_text->end = static_cast<long>(text.size());
2145   return S_OK;
2146 }
2147 
get_oldText(IA2TextSegment * old_text)2148 STDMETHODIMP BrowserAccessibilityWin::get_oldText(IA2TextSegment* old_text) {
2149   if (!instance_active())
2150     return E_FAIL;
2151 
2152   if (!old_text)
2153     return E_INVALIDARG;
2154 
2155   old_text->text = SysAllocString(old_text_.c_str());
2156   old_text->start = 0;
2157   old_text->end = static_cast<long>(old_text_.size());
2158   return S_OK;
2159 }
2160 
get_offsetAtPoint(LONG x,LONG y,enum IA2CoordinateType coord_type,LONG * offset)2161 STDMETHODIMP BrowserAccessibilityWin::get_offsetAtPoint(
2162     LONG x,
2163     LONG y,
2164     enum IA2CoordinateType coord_type,
2165     LONG* offset) {
2166   if (!instance_active())
2167     return E_FAIL;
2168 
2169   if (!offset)
2170     return E_INVALIDARG;
2171 
2172   // TODO(dmazzoni): implement this. We're returning S_OK for now so that
2173   // screen readers still return partially accurate results rather than
2174   // completely failing.
2175   *offset = 0;
2176   return S_OK;
2177 }
2178 
scrollSubstringTo(LONG start_index,LONG end_index,enum IA2ScrollType scroll_type)2179 STDMETHODIMP BrowserAccessibilityWin::scrollSubstringTo(
2180     LONG start_index,
2181     LONG end_index,
2182     enum IA2ScrollType scroll_type) {
2183   // TODO(dmazzoni): adjust this for the start and end index, too.
2184   return scrollTo(scroll_type);
2185 }
2186 
scrollSubstringToPoint(LONG start_index,LONG end_index,enum IA2CoordinateType coordinate_type,LONG x,LONG y)2187 STDMETHODIMP BrowserAccessibilityWin::scrollSubstringToPoint(
2188     LONG start_index,
2189     LONG end_index,
2190     enum IA2CoordinateType coordinate_type,
2191     LONG x, LONG y) {
2192   // TODO(dmazzoni): adjust this for the start and end index, too.
2193   return scrollToPoint(coordinate_type, x, y);
2194 }
2195 
addSelection(LONG start_offset,LONG end_offset)2196 STDMETHODIMP BrowserAccessibilityWin::addSelection(LONG start_offset,
2197                                                    LONG end_offset) {
2198   if (!instance_active())
2199     return E_FAIL;
2200 
2201   const base::string16& text_str = TextForIAccessibleText();
2202   HandleSpecialTextOffset(text_str, &start_offset);
2203   HandleSpecialTextOffset(text_str, &end_offset);
2204 
2205   manager()->SetTextSelection(*this, start_offset, end_offset);
2206   return S_OK;
2207 }
2208 
removeSelection(LONG selection_index)2209 STDMETHODIMP BrowserAccessibilityWin::removeSelection(LONG selection_index) {
2210   if (!instance_active())
2211     return E_FAIL;
2212 
2213   if (selection_index != 0)
2214     return E_INVALIDARG;
2215 
2216   manager()->SetTextSelection(*this, 0, 0);
2217   return S_OK;
2218 }
2219 
setCaretOffset(LONG offset)2220 STDMETHODIMP BrowserAccessibilityWin::setCaretOffset(LONG offset) {
2221   if (!instance_active())
2222     return E_FAIL;
2223 
2224   const base::string16& text_str = TextForIAccessibleText();
2225   HandleSpecialTextOffset(text_str, &offset);
2226   manager()->SetTextSelection(*this, offset, offset);
2227   return S_OK;
2228 }
2229 
setSelection(LONG selection_index,LONG start_offset,LONG end_offset)2230 STDMETHODIMP BrowserAccessibilityWin::setSelection(LONG selection_index,
2231                                                    LONG start_offset,
2232                                                    LONG end_offset) {
2233   if (!instance_active())
2234     return E_FAIL;
2235 
2236   if (selection_index != 0)
2237     return E_INVALIDARG;
2238 
2239   const base::string16& text_str = TextForIAccessibleText();
2240   HandleSpecialTextOffset(text_str, &start_offset);
2241   HandleSpecialTextOffset(text_str, &end_offset);
2242 
2243   manager()->SetTextSelection(*this, start_offset, end_offset);
2244   return S_OK;
2245 }
2246 
2247 //
2248 // IAccessibleHypertext methods.
2249 //
2250 
get_nHyperlinks(long * hyperlink_count)2251 STDMETHODIMP BrowserAccessibilityWin::get_nHyperlinks(long* hyperlink_count) {
2252   if (!instance_active())
2253     return E_FAIL;
2254 
2255   if (!hyperlink_count)
2256     return E_INVALIDARG;
2257 
2258   *hyperlink_count = hyperlink_offset_to_index_.size();
2259   return S_OK;
2260 }
2261 
get_hyperlink(long index,IAccessibleHyperlink ** hyperlink)2262 STDMETHODIMP BrowserAccessibilityWin::get_hyperlink(
2263     long index,
2264     IAccessibleHyperlink** hyperlink) {
2265   if (!instance_active())
2266     return E_FAIL;
2267 
2268   if (!hyperlink ||
2269       index < 0 ||
2270       index >= static_cast<long>(hyperlinks_.size())) {
2271     return E_INVALIDARG;
2272   }
2273 
2274   BrowserAccessibilityWin* child =
2275       children()[hyperlinks_[index]]->ToBrowserAccessibilityWin();
2276   *hyperlink = static_cast<IAccessibleHyperlink*>(child->NewReference());
2277   return S_OK;
2278 }
2279 
get_hyperlinkIndex(long char_index,long * hyperlink_index)2280 STDMETHODIMP BrowserAccessibilityWin::get_hyperlinkIndex(
2281     long char_index,
2282     long* hyperlink_index) {
2283   if (!instance_active())
2284     return E_FAIL;
2285 
2286   if (!hyperlink_index)
2287     return E_INVALIDARG;
2288 
2289   *hyperlink_index = -1;
2290 
2291   if (char_index < 0 || char_index >= static_cast<long>(hypertext_.size()))
2292     return E_INVALIDARG;
2293 
2294   std::map<int32, int32>::iterator it =
2295       hyperlink_offset_to_index_.find(char_index);
2296   if (it == hyperlink_offset_to_index_.end())
2297     return E_FAIL;
2298 
2299   *hyperlink_index = it->second;
2300   return S_OK;
2301 }
2302 
2303 //
2304 // IAccessibleValue methods.
2305 //
2306 
get_currentValue(VARIANT * value)2307 STDMETHODIMP BrowserAccessibilityWin::get_currentValue(VARIANT* value) {
2308   if (!instance_active())
2309     return E_FAIL;
2310 
2311   if (!value)
2312     return E_INVALIDARG;
2313 
2314   float float_val;
2315   if (GetFloatAttribute(
2316           AccessibilityNodeData::ATTR_VALUE_FOR_RANGE, &float_val)) {
2317     value->vt = VT_R8;
2318     value->dblVal = float_val;
2319     return S_OK;
2320   }
2321 
2322   value->vt = VT_EMPTY;
2323   return S_FALSE;
2324 }
2325 
get_minimumValue(VARIANT * value)2326 STDMETHODIMP BrowserAccessibilityWin::get_minimumValue(VARIANT* value) {
2327   if (!instance_active())
2328     return E_FAIL;
2329 
2330   if (!value)
2331     return E_INVALIDARG;
2332 
2333   float float_val;
2334   if (GetFloatAttribute(AccessibilityNodeData::ATTR_MIN_VALUE_FOR_RANGE,
2335                         &float_val)) {
2336     value->vt = VT_R8;
2337     value->dblVal = float_val;
2338     return S_OK;
2339   }
2340 
2341   value->vt = VT_EMPTY;
2342   return S_FALSE;
2343 }
2344 
get_maximumValue(VARIANT * value)2345 STDMETHODIMP BrowserAccessibilityWin::get_maximumValue(VARIANT* value) {
2346   if (!instance_active())
2347     return E_FAIL;
2348 
2349   if (!value)
2350     return E_INVALIDARG;
2351 
2352   float float_val;
2353   if (GetFloatAttribute(AccessibilityNodeData::ATTR_MAX_VALUE_FOR_RANGE,
2354                         &float_val)) {
2355     value->vt = VT_R8;
2356     value->dblVal = float_val;
2357     return S_OK;
2358   }
2359 
2360   value->vt = VT_EMPTY;
2361   return S_FALSE;
2362 }
2363 
setCurrentValue(VARIANT new_value)2364 STDMETHODIMP BrowserAccessibilityWin::setCurrentValue(VARIANT new_value) {
2365   // TODO(dmazzoni): Implement this.
2366   return E_NOTIMPL;
2367 }
2368 
2369 //
2370 // ISimpleDOMDocument methods.
2371 //
2372 
get_URL(BSTR * url)2373 STDMETHODIMP BrowserAccessibilityWin::get_URL(BSTR* url) {
2374   if (!instance_active())
2375     return E_FAIL;
2376 
2377   if (!url)
2378     return E_INVALIDARG;
2379 
2380   return GetStringAttributeAsBstr(AccessibilityNodeData::ATTR_DOC_URL, url);
2381 }
2382 
get_title(BSTR * title)2383 STDMETHODIMP BrowserAccessibilityWin::get_title(BSTR* title) {
2384   if (!instance_active())
2385     return E_FAIL;
2386 
2387   if (!title)
2388     return E_INVALIDARG;
2389 
2390   return GetStringAttributeAsBstr(AccessibilityNodeData::ATTR_DOC_TITLE, title);
2391 }
2392 
get_mimeType(BSTR * mime_type)2393 STDMETHODIMP BrowserAccessibilityWin::get_mimeType(BSTR* mime_type) {
2394   if (!instance_active())
2395     return E_FAIL;
2396 
2397   if (!mime_type)
2398     return E_INVALIDARG;
2399 
2400   return GetStringAttributeAsBstr(
2401       AccessibilityNodeData::ATTR_DOC_MIMETYPE, mime_type);
2402 }
2403 
get_docType(BSTR * doc_type)2404 STDMETHODIMP BrowserAccessibilityWin::get_docType(BSTR* doc_type) {
2405   if (!instance_active())
2406     return E_FAIL;
2407 
2408   if (!doc_type)
2409     return E_INVALIDARG;
2410 
2411   return GetStringAttributeAsBstr(
2412       AccessibilityNodeData::ATTR_DOC_DOCTYPE, doc_type);
2413 }
2414 
2415 //
2416 // ISimpleDOMNode methods.
2417 //
2418 
get_nodeInfo(BSTR * node_name,short * name_space_id,BSTR * node_value,unsigned int * num_children,unsigned int * unique_id,unsigned short * node_type)2419 STDMETHODIMP BrowserAccessibilityWin::get_nodeInfo(
2420     BSTR* node_name,
2421     short* name_space_id,
2422     BSTR* node_value,
2423     unsigned int* num_children,
2424     unsigned int* unique_id,
2425     unsigned short* node_type) {
2426   if (!instance_active())
2427     return E_FAIL;
2428 
2429   if (!node_name || !name_space_id || !node_value || !num_children ||
2430       !unique_id || !node_type) {
2431     return E_INVALIDARG;
2432   }
2433 
2434   base::string16 tag;
2435   if (GetString16Attribute(AccessibilityNodeData::ATTR_HTML_TAG, &tag))
2436     *node_name = SysAllocString(tag.c_str());
2437   else
2438     *node_name = NULL;
2439 
2440   *name_space_id = 0;
2441   *node_value = SysAllocString(UTF8ToUTF16(value()).c_str());
2442   *num_children = PlatformChildCount();
2443   *unique_id = unique_id_win_;
2444 
2445   if (ia_role_ == ROLE_SYSTEM_DOCUMENT) {
2446     *node_type = NODETYPE_DOCUMENT;
2447   } else if (ia_role_ == ROLE_SYSTEM_TEXT &&
2448              ((ia2_state_ & IA2_STATE_EDITABLE) == 0)) {
2449     *node_type = NODETYPE_TEXT;
2450   } else {
2451     *node_type = NODETYPE_ELEMENT;
2452   }
2453 
2454   return S_OK;
2455 }
2456 
get_attributes(unsigned short max_attribs,BSTR * attrib_names,short * name_space_id,BSTR * attrib_values,unsigned short * num_attribs)2457 STDMETHODIMP BrowserAccessibilityWin::get_attributes(
2458     unsigned short max_attribs,
2459     BSTR* attrib_names,
2460     short* name_space_id,
2461     BSTR* attrib_values,
2462     unsigned short* num_attribs) {
2463   if (!instance_active())
2464     return E_FAIL;
2465 
2466   if (!attrib_names || !name_space_id || !attrib_values || !num_attribs)
2467     return E_INVALIDARG;
2468 
2469   *num_attribs = max_attribs;
2470   if (*num_attribs > html_attributes().size())
2471     *num_attribs = html_attributes().size();
2472 
2473   for (unsigned short i = 0; i < *num_attribs; ++i) {
2474     attrib_names[i] = SysAllocString(
2475         UTF8ToUTF16(html_attributes()[i].first).c_str());
2476     name_space_id[i] = 0;
2477     attrib_values[i] = SysAllocString(
2478         UTF8ToUTF16(html_attributes()[i].second).c_str());
2479   }
2480   return S_OK;
2481 }
2482 
get_attributesForNames(unsigned short num_attribs,BSTR * attrib_names,short * name_space_id,BSTR * attrib_values)2483 STDMETHODIMP BrowserAccessibilityWin::get_attributesForNames(
2484     unsigned short num_attribs,
2485     BSTR* attrib_names,
2486     short* name_space_id,
2487     BSTR* attrib_values) {
2488   if (!instance_active())
2489     return E_FAIL;
2490 
2491   if (!attrib_names || !name_space_id || !attrib_values)
2492     return E_INVALIDARG;
2493 
2494   for (unsigned short i = 0; i < num_attribs; ++i) {
2495     name_space_id[i] = 0;
2496     bool found = false;
2497     std::string name = UTF16ToUTF8((LPCWSTR)attrib_names[i]);
2498     for (unsigned int j = 0;  j < html_attributes().size(); ++j) {
2499       if (html_attributes()[j].first == name) {
2500         attrib_values[i] = SysAllocString(
2501             UTF8ToUTF16(html_attributes()[j].second).c_str());
2502         found = true;
2503         break;
2504       }
2505     }
2506     if (!found) {
2507       attrib_values[i] = NULL;
2508     }
2509   }
2510   return S_OK;
2511 }
2512 
get_computedStyle(unsigned short max_style_properties,boolean use_alternate_view,BSTR * style_properties,BSTR * style_values,unsigned short * num_style_properties)2513 STDMETHODIMP BrowserAccessibilityWin::get_computedStyle(
2514     unsigned short max_style_properties,
2515     boolean use_alternate_view,
2516     BSTR* style_properties,
2517     BSTR* style_values,
2518     unsigned short *num_style_properties)  {
2519   if (!instance_active())
2520     return E_FAIL;
2521 
2522   if (!style_properties || !style_values)
2523     return E_INVALIDARG;
2524 
2525   // We only cache a single style property for now: DISPLAY
2526 
2527   base::string16 display;
2528   if (max_style_properties == 0 ||
2529       !GetString16Attribute(AccessibilityNodeData::ATTR_DISPLAY, &display)) {
2530     *num_style_properties = 0;
2531     return S_OK;
2532   }
2533 
2534   *num_style_properties = 1;
2535   style_properties[0] = SysAllocString(L"display");
2536   style_values[0] = SysAllocString(display.c_str());
2537 
2538   return S_OK;
2539 }
2540 
get_computedStyleForProperties(unsigned short num_style_properties,boolean use_alternate_view,BSTR * style_properties,BSTR * style_values)2541 STDMETHODIMP BrowserAccessibilityWin::get_computedStyleForProperties(
2542     unsigned short num_style_properties,
2543     boolean use_alternate_view,
2544     BSTR* style_properties,
2545     BSTR* style_values) {
2546   if (!instance_active())
2547     return E_FAIL;
2548 
2549   if (!style_properties || !style_values)
2550     return E_INVALIDARG;
2551 
2552   // We only cache a single style property for now: DISPLAY
2553 
2554   for (unsigned short i = 0; i < num_style_properties; ++i) {
2555     base::string16 name = (LPCWSTR)style_properties[i];
2556     StringToLowerASCII(&name);
2557     if (name == L"display") {
2558       base::string16 display = GetString16Attribute(
2559           AccessibilityNodeData::ATTR_DISPLAY);
2560       style_values[i] = SysAllocString(display.c_str());
2561     } else {
2562       style_values[i] = NULL;
2563     }
2564   }
2565 
2566   return S_OK;
2567 }
2568 
scrollTo(boolean placeTopLeft)2569 STDMETHODIMP BrowserAccessibilityWin::scrollTo(boolean placeTopLeft) {
2570   return scrollTo(placeTopLeft ?
2571       IA2_SCROLL_TYPE_TOP_LEFT : IA2_SCROLL_TYPE_ANYWHERE);
2572 }
2573 
get_parentNode(ISimpleDOMNode ** node)2574 STDMETHODIMP BrowserAccessibilityWin::get_parentNode(ISimpleDOMNode** node) {
2575   if (!instance_active())
2576     return E_FAIL;
2577 
2578   if (!node)
2579     return E_INVALIDARG;
2580 
2581   *node = parent()->ToBrowserAccessibilityWin()->NewReference();
2582   return S_OK;
2583 }
2584 
get_firstChild(ISimpleDOMNode ** node)2585 STDMETHODIMP BrowserAccessibilityWin::get_firstChild(ISimpleDOMNode** node)  {
2586   if (!instance_active())
2587     return E_FAIL;
2588 
2589   if (!node)
2590     return E_INVALIDARG;
2591 
2592   if (PlatformChildCount() == 0) {
2593     *node = NULL;
2594     return S_FALSE;
2595   }
2596 
2597   *node = PlatformGetChild(0)->ToBrowserAccessibilityWin()->NewReference();
2598   return S_OK;
2599 }
2600 
get_lastChild(ISimpleDOMNode ** node)2601 STDMETHODIMP BrowserAccessibilityWin::get_lastChild(ISimpleDOMNode** node) {
2602   if (!instance_active())
2603     return E_FAIL;
2604 
2605   if (!node)
2606     return E_INVALIDARG;
2607 
2608   if (PlatformChildCount() == 0) {
2609     *node = NULL;
2610     return S_FALSE;
2611   }
2612 
2613   *node = PlatformGetChild(PlatformChildCount() - 1)
2614       ->ToBrowserAccessibilityWin()->NewReference();
2615   return S_OK;
2616 }
2617 
get_previousSibling(ISimpleDOMNode ** node)2618 STDMETHODIMP BrowserAccessibilityWin::get_previousSibling(
2619     ISimpleDOMNode** node) {
2620   if (!instance_active())
2621     return E_FAIL;
2622 
2623   if (!node)
2624     return E_INVALIDARG;
2625 
2626   if (!parent() || index_in_parent() <= 0) {
2627     *node = NULL;
2628     return S_FALSE;
2629   }
2630 
2631   *node = parent()->children()[index_in_parent() - 1]->
2632       ToBrowserAccessibilityWin()->NewReference();
2633   return S_OK;
2634 }
2635 
get_nextSibling(ISimpleDOMNode ** node)2636 STDMETHODIMP BrowserAccessibilityWin::get_nextSibling(ISimpleDOMNode** node) {
2637   if (!instance_active())
2638     return E_FAIL;
2639 
2640   if (!node)
2641     return E_INVALIDARG;
2642 
2643   if (!parent() ||
2644       index_in_parent() < 0 ||
2645       index_in_parent() >= static_cast<int>(parent()->children().size()) - 1) {
2646     *node = NULL;
2647     return S_FALSE;
2648   }
2649 
2650   *node = parent()->children()[index_in_parent() + 1]->
2651       ToBrowserAccessibilityWin()->NewReference();
2652   return S_OK;
2653 }
2654 
get_childAt(unsigned int child_index,ISimpleDOMNode ** node)2655 STDMETHODIMP BrowserAccessibilityWin::get_childAt(
2656     unsigned int child_index,
2657     ISimpleDOMNode** node) {
2658   if (!instance_active())
2659     return E_FAIL;
2660 
2661   if (!node)
2662     return E_INVALIDARG;
2663 
2664   if (child_index >= PlatformChildCount())
2665     return E_INVALIDARG;
2666 
2667   BrowserAccessibility* child = PlatformGetChild(child_index);
2668   if (!child) {
2669     *node = NULL;
2670     return S_FALSE;
2671   }
2672 
2673   *node = child->ToBrowserAccessibilityWin()->NewReference();
2674   return S_OK;
2675 }
2676 
2677 //
2678 // ISimpleDOMText methods.
2679 //
2680 
get_domText(BSTR * dom_text)2681 STDMETHODIMP BrowserAccessibilityWin::get_domText(BSTR* dom_text) {
2682   if (!instance_active())
2683     return E_FAIL;
2684 
2685   if (!dom_text)
2686     return E_INVALIDARG;
2687 
2688   return GetStringAttributeAsBstr(
2689       AccessibilityNodeData::ATTR_NAME, dom_text);
2690 }
2691 
get_clippedSubstringBounds(unsigned int start_index,unsigned int end_index,int * out_x,int * out_y,int * out_width,int * out_height)2692 STDMETHODIMP BrowserAccessibilityWin::get_clippedSubstringBounds(
2693     unsigned int start_index,
2694     unsigned int end_index,
2695     int* out_x,
2696     int* out_y,
2697     int* out_width,
2698     int* out_height) {
2699   // TODO(dmazzoni): fully support this API by intersecting the
2700   // rect with the container's rect.
2701   return get_unclippedSubstringBounds(
2702       start_index, end_index, out_x, out_y, out_width, out_height);
2703 }
2704 
get_unclippedSubstringBounds(unsigned int start_index,unsigned int end_index,int * out_x,int * out_y,int * out_width,int * out_height)2705 STDMETHODIMP BrowserAccessibilityWin::get_unclippedSubstringBounds(
2706     unsigned int start_index,
2707     unsigned int end_index,
2708     int* out_x,
2709     int* out_y,
2710     int* out_width,
2711     int* out_height) {
2712   if (!instance_active())
2713     return E_FAIL;
2714 
2715   if (!out_x || !out_y || !out_width || !out_height)
2716     return E_INVALIDARG;
2717 
2718   const base::string16& text_str = TextForIAccessibleText();
2719   if (start_index > text_str.size() ||
2720       end_index > text_str.size() ||
2721       start_index > end_index) {
2722     return E_INVALIDARG;
2723   }
2724 
2725   if (blink_role() != blink::WebAXRoleStaticText)
2726     return E_FAIL;
2727 
2728   gfx::Rect bounds = GetGlobalBoundsForRange(
2729       start_index, end_index - start_index);
2730   *out_x = bounds.x();
2731   *out_y = bounds.y();
2732   *out_width = bounds.width();
2733   *out_height = bounds.height();
2734   return S_OK;
2735 }
2736 
scrollToSubstring(unsigned int start_index,unsigned int end_index)2737 STDMETHODIMP BrowserAccessibilityWin::scrollToSubstring(
2738     unsigned int start_index,
2739     unsigned int end_index) {
2740   if (!instance_active())
2741     return E_FAIL;
2742 
2743   const base::string16& text_str = TextForIAccessibleText();
2744   if (start_index > text_str.size() ||
2745       end_index > text_str.size() ||
2746       start_index > end_index) {
2747     return E_INVALIDARG;
2748   }
2749 
2750   manager()->ScrollToMakeVisible(*this, GetLocalBoundsForRange(
2751       start_index, end_index - start_index));
2752   manager()->ToBrowserAccessibilityManagerWin()->TrackScrollingObject(this);
2753 
2754   return S_OK;
2755 }
2756 
2757 //
2758 // IServiceProvider methods.
2759 //
2760 
QueryService(REFGUID guidService,REFIID riid,void ** object)2761 STDMETHODIMP BrowserAccessibilityWin::QueryService(REFGUID guidService,
2762                                                    REFIID riid,
2763                                                    void** object) {
2764   if (!instance_active())
2765     return E_FAIL;
2766 
2767   // The system uses IAccessible APIs for many purposes, but only
2768   // assistive technology like screen readers uses IAccessible2.
2769   // Enable full accessibility support when IAccessible2 APIs are queried.
2770   if (riid == IID_IAccessible2)
2771     BrowserAccessibilityStateImpl::GetInstance()->EnableAccessibility();
2772 
2773   if (guidService == GUID_IAccessibleContentDocument) {
2774     // Special Mozilla extension: return the accessible for the root document.
2775     // Screen readers use this to distinguish between a document loaded event
2776     // on the root document vs on an iframe.
2777     return manager()->GetRoot()->ToBrowserAccessibilityWin()->QueryInterface(
2778         IID_IAccessible2, object);
2779   }
2780 
2781   if (guidService == IID_IAccessible ||
2782       guidService == IID_IAccessible2 ||
2783       guidService == IID_IAccessibleAction ||
2784       guidService == IID_IAccessibleApplication ||
2785       guidService == IID_IAccessibleHyperlink ||
2786       guidService == IID_IAccessibleHypertext ||
2787       guidService == IID_IAccessibleImage ||
2788       guidService == IID_IAccessibleTable ||
2789       guidService == IID_IAccessibleTable2 ||
2790       guidService == IID_IAccessibleTableCell ||
2791       guidService == IID_IAccessibleText ||
2792       guidService == IID_IAccessibleValue ||
2793       guidService == IID_ISimpleDOMDocument ||
2794       guidService == IID_ISimpleDOMNode ||
2795       guidService == IID_ISimpleDOMText ||
2796       guidService == GUID_ISimpleDOM) {
2797     return QueryInterface(riid, object);
2798   }
2799 
2800   // We only support the IAccessibleEx interface on Windows 8 and above. This
2801   // is needed for the on-screen Keyboard to show up in metro mode, when the
2802   // user taps an editable portion on the page.
2803   // All methods in the IAccessibleEx interface are unimplemented.
2804   if (riid == IID_IAccessibleEx &&
2805       base::win::GetVersion() >= base::win::VERSION_WIN8) {
2806     return QueryInterface(riid, object);
2807   }
2808 
2809   *object = NULL;
2810   return E_FAIL;
2811 }
2812 
GetPatternProvider(PATTERNID id,IUnknown ** provider)2813 STDMETHODIMP BrowserAccessibilityWin::GetPatternProvider(PATTERNID id,
2814                                                          IUnknown** provider) {
2815   DVLOG(1) << "In Function: "
2816            << __FUNCTION__
2817            << " for pattern id: "
2818            << id;
2819   if (id == UIA_ValuePatternId || id == UIA_TextPatternId) {
2820     if (IsEditableText()) {
2821       // The BrowserAccessibilityManager keeps track of instances when
2822       // we don't want to show the on-screen keyboard.
2823       if (!manager()->IsOSKAllowed(GetGlobalBoundsRect()))
2824         return E_NOTIMPL;
2825 
2826       DVLOG(1) << "Returning UIA text provider";
2827       base::win::UIATextProvider::CreateTextProvider(true, provider);
2828       return S_OK;
2829     }
2830   }
2831   return E_NOTIMPL;
2832 }
2833 
GetPropertyValue(PROPERTYID id,VARIANT * ret)2834 STDMETHODIMP BrowserAccessibilityWin::GetPropertyValue(PROPERTYID id,
2835                                                        VARIANT* ret) {
2836   DVLOG(1) << "In Function: "
2837            << __FUNCTION__
2838            << " for property id: "
2839            << id;
2840   V_VT(ret) = VT_EMPTY;
2841   if (id == UIA_ControlTypePropertyId) {
2842     if (IsEditableText()) {
2843       V_VT(ret) = VT_I4;
2844       ret->lVal = UIA_EditControlTypeId;
2845       DVLOG(1) << "Returning Edit control type";
2846     } else {
2847       DVLOG(1) << "Returning empty control type";
2848     }
2849   }
2850   return S_OK;
2851 }
2852 
2853 //
2854 // CComObjectRootEx methods.
2855 //
2856 
InternalQueryInterface(void * this_ptr,const _ATL_INTMAP_ENTRY * entries,REFIID iid,void ** object)2857 HRESULT WINAPI BrowserAccessibilityWin::InternalQueryInterface(
2858     void* this_ptr,
2859     const _ATL_INTMAP_ENTRY* entries,
2860     REFIID iid,
2861     void** object) {
2862   if (iid == IID_IAccessibleImage) {
2863     if (ia_role_ != ROLE_SYSTEM_GRAPHIC) {
2864       *object = NULL;
2865       return E_NOINTERFACE;
2866     }
2867   } else if (iid == IID_IAccessibleTable || iid == IID_IAccessibleTable2) {
2868     if (ia_role_ != ROLE_SYSTEM_TABLE) {
2869       *object = NULL;
2870       return E_NOINTERFACE;
2871     }
2872   } else if (iid == IID_IAccessibleTableCell) {
2873     if (ia_role_ != ROLE_SYSTEM_CELL) {
2874       *object = NULL;
2875       return E_NOINTERFACE;
2876     }
2877   } else if (iid == IID_IAccessibleValue) {
2878     if (ia_role_ != ROLE_SYSTEM_PROGRESSBAR &&
2879         ia_role_ != ROLE_SYSTEM_SCROLLBAR &&
2880         ia_role_ != ROLE_SYSTEM_SLIDER) {
2881       *object = NULL;
2882       return E_NOINTERFACE;
2883     }
2884   } else if (iid == IID_ISimpleDOMDocument) {
2885     if (ia_role_ != ROLE_SYSTEM_DOCUMENT) {
2886       *object = NULL;
2887       return E_NOINTERFACE;
2888     }
2889   }
2890 
2891   return CComObjectRootBase::InternalQueryInterface(
2892       this_ptr, entries, iid, object);
2893 }
2894 
2895 //
2896 // Private methods.
2897 //
2898 
2899 // Initialize this object and mark it as active.
PreInitialize()2900 void BrowserAccessibilityWin::PreInitialize() {
2901   BrowserAccessibility::PreInitialize();
2902 
2903   InitRoleAndState();
2904 
2905   // Expose the "display" and "tag" attributes.
2906   StringAttributeToIA2(AccessibilityNodeData::ATTR_DISPLAY, "display");
2907   StringAttributeToIA2(AccessibilityNodeData::ATTR_HTML_TAG, "tag");
2908   StringAttributeToIA2(AccessibilityNodeData::ATTR_ROLE, "xml-roles");
2909 
2910   // Expose "level" attribute for headings, trees, etc.
2911   IntAttributeToIA2(AccessibilityNodeData::ATTR_HIERARCHICAL_LEVEL, "level");
2912 
2913   // Expose the set size and position in set for listbox options.
2914   if (blink_role() == blink::WebAXRoleListBoxOption &&
2915       parent() &&
2916       parent()->role() == blink::WebAXRoleListBox) {
2917     ia2_attributes_.push_back(
2918         L"setsize:" + base::IntToString16(parent()->PlatformChildCount()));
2919     ia2_attributes_.push_back(
2920         L"setsize:" + base::IntToString16(index_in_parent() + 1));
2921   }
2922 
2923   if (ia_role_ == ROLE_SYSTEM_CHECKBUTTON ||
2924       ia_role_ == ROLE_SYSTEM_RADIOBUTTON ||
2925       ia2_role_ == IA2_ROLE_TOGGLE_BUTTON) {
2926     ia2_attributes_.push_back(L"checkable:true");
2927   }
2928 
2929   // Expose live region attributes.
2930   StringAttributeToIA2(AccessibilityNodeData::ATTR_LIVE_STATUS, "live");
2931   StringAttributeToIA2(AccessibilityNodeData::ATTR_LIVE_RELEVANT, "relevant");
2932   BoolAttributeToIA2(AccessibilityNodeData::ATTR_LIVE_ATOMIC, "atomic");
2933   BoolAttributeToIA2(AccessibilityNodeData::ATTR_LIVE_BUSY, "busy");
2934 
2935   // Expose container live region attributes.
2936   StringAttributeToIA2(AccessibilityNodeData::ATTR_CONTAINER_LIVE_STATUS,
2937                        "container-live");
2938   StringAttributeToIA2(AccessibilityNodeData::ATTR_CONTAINER_LIVE_RELEVANT,
2939                        "container-relevant");
2940   BoolAttributeToIA2(AccessibilityNodeData::ATTR_CONTAINER_LIVE_ATOMIC,
2941                      "container-atomic");
2942   BoolAttributeToIA2(AccessibilityNodeData::ATTR_CONTAINER_LIVE_BUSY,
2943                      "container-busy");
2944 
2945   // Expose slider value.
2946   if (ia_role_ == ROLE_SYSTEM_PROGRESSBAR ||
2947       ia_role_ == ROLE_SYSTEM_SCROLLBAR ||
2948       ia_role_ == ROLE_SYSTEM_SLIDER) {
2949     ia2_attributes_.push_back(L"valuetext:" + GetValueText());
2950   }
2951 
2952   // Expose table cell index.
2953   if (ia_role_ == ROLE_SYSTEM_CELL) {
2954     BrowserAccessibility* table = parent();
2955     while (table && table->role() != blink::WebAXRoleTable)
2956       table = table->parent();
2957     if (table) {
2958       const std::vector<int32>& unique_cell_ids = table->GetIntListAttribute(
2959           AccessibilityNodeData::ATTR_UNIQUE_CELL_IDS);
2960       for (size_t i = 0; i < unique_cell_ids.size(); ++i) {
2961         if (unique_cell_ids[i] == renderer_id()) {
2962           ia2_attributes_.push_back(
2963               base::string16(L"table-cell-index:") + base::IntToString16(i));
2964         }
2965       }
2966     }
2967   }
2968 
2969   // The calculation of the accessible name of an element has been
2970   // standardized in the HTML to Platform Accessibility APIs Implementation
2971   // Guide (http://www.w3.org/TR/html-aapi/). In order to return the
2972   // appropriate accessible name on Windows, we need to apply some logic
2973   // to the fields we get from WebKit.
2974   //
2975   // TODO(dmazzoni): move most of this logic into WebKit.
2976   //
2977   // WebKit gives us:
2978   //
2979   //   name: the default name, e.g. inner text
2980   //   title ui element: a reference to a <label> element on the same
2981   //       page that labels this node.
2982   //   description: accessible labels that override the default name:
2983   //       aria-label or aria-labelledby or aria-describedby
2984   //   help: the value of the "title" attribute
2985   //
2986   // On Windows, the logic we apply lets some fields take precedence and
2987   // always returns the primary name in "name" and the secondary name,
2988   // if any, in "description".
2989 
2990   int title_elem_id = GetIntAttribute(
2991       AccessibilityNodeData::ATTR_TITLE_UI_ELEMENT);
2992   std::string help = GetStringAttribute(AccessibilityNodeData::ATTR_HELP);
2993   std::string description = GetStringAttribute(
2994       AccessibilityNodeData::ATTR_DESCRIPTION);
2995 
2996   // WebKit annoyingly puts the title in the description if there's no other
2997   // description, which just confuses the rest of the logic. Put it back.
2998   // Now "help" is always the value of the "title" attribute, if present.
2999   std::string title_attr;
3000   if (GetHtmlAttribute("title", &title_attr) &&
3001       description == title_attr &&
3002       help.empty()) {
3003     help = description;
3004     description.clear();
3005   }
3006 
3007   // Now implement the main logic: the descripion should become the name if
3008   // it's nonempty, and the help should become the description if
3009   // there's no description - or the name if there's no name or description.
3010   if (!description.empty()) {
3011     set_name(description);
3012     description.clear();
3013   }
3014   if (!help.empty() && description.empty()) {
3015     description = help;
3016     help.clear();
3017   }
3018   if (!description.empty() && name().empty() && !title_elem_id) {
3019     set_name(description);
3020     description.clear();
3021   }
3022 
3023   // If it's a text field, also consider the placeholder.
3024   std::string placeholder;
3025   if (blink_role() == blink::WebAXRoleTextField &&
3026       HasState(blink::WebAXStateFocusable) &&
3027       GetHtmlAttribute("placeholder", &placeholder)) {
3028     if (name().empty() && !title_elem_id) {
3029       set_name(placeholder);
3030     } else if (description.empty()) {
3031       description = placeholder;
3032     }
3033   }
3034 
3035   SetStringAttribute(AccessibilityNodeData::ATTR_DESCRIPTION, description);
3036   SetStringAttribute(AccessibilityNodeData::ATTR_HELP, help);
3037 
3038   // On Windows, the value of a document should be its url.
3039   if (blink_role() == blink::WebAXRoleRootWebArea ||
3040       blink_role() == blink::WebAXRoleWebArea) {
3041     set_value(GetStringAttribute(AccessibilityNodeData::ATTR_DOC_URL));
3042   }
3043 
3044   // For certain roles (listbox option, static text, and list marker)
3045   // WebKit stores the main accessible text in the "value" - swap it so
3046   // that it's the "name".
3047   if (name().empty() &&
3048       (blink_role() == blink::WebAXRoleListBoxOption ||
3049        blink_role() == blink::WebAXRoleStaticText ||
3050        blink_role() == blink::WebAXRoleListMarker)) {
3051     std::string tmp = value();
3052     set_value(name());
3053     set_name(tmp);
3054   }
3055 
3056   // If this doesn't have a value and is linked then set its value to the url
3057   // attribute. This allows screen readers to read an empty link's destination.
3058   if (value().empty() && (ia_state_ & STATE_SYSTEM_LINKED))
3059     set_value(GetStringAttribute(AccessibilityNodeData::ATTR_URL));
3060 
3061   // Clear any old relationships between this node and other nodes.
3062   for (size_t i = 0; i < relations_.size(); ++i)
3063     relations_[i]->Release();
3064   relations_.clear();
3065 
3066   // Handle title UI element.
3067   if (title_elem_id) {
3068     // Add a labelled by relationship.
3069     CComObject<BrowserAccessibilityRelation>* relation;
3070     HRESULT hr = CComObject<BrowserAccessibilityRelation>::CreateInstance(
3071         &relation);
3072     DCHECK(SUCCEEDED(hr));
3073     relation->AddRef();
3074     relation->Initialize(this, IA2_RELATION_LABELLED_BY);
3075     relation->AddTarget(title_elem_id);
3076     relations_.push_back(relation);
3077   }
3078 }
3079 
PostInitialize()3080 void BrowserAccessibilityWin::PostInitialize() {
3081   BrowserAccessibility::PostInitialize();
3082 
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->role() == blink::WebAXRoleStaticText) {
3090       hypertext_ += 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 (blink_role() == blink::WebAXRoleAlert && first_time_)
3101     manager()->NotifyAccessibilityEvent(blink::WebAXEventAlert, 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           blink::WebAXEventShow, 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 (blink_role() == blink::WebAXRoleListBoxOption &&
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(AccessibilityNodeData::ATTR_SCROLL_X, &sx) &&
3151       GetIntAttribute(AccessibilityNodeData::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 
SetLocation(const gfx::Rect & new_location)3176 void BrowserAccessibilityWin::SetLocation(const gfx::Rect& new_location) {
3177   BrowserAccessibility::SetLocation(new_location);
3178   manager()->ToBrowserAccessibilityManagerWin()->MaybeCallNotifyWinEvent(
3179       EVENT_OBJECT_LOCATIONCHANGE, unique_id_win());
3180 }
3181 
NewReference()3182 BrowserAccessibilityWin* BrowserAccessibilityWin::NewReference() {
3183   AddRef();
3184   return this;
3185 }
3186 
GetTargetFromChildID(const VARIANT & var_id)3187 BrowserAccessibilityWin* BrowserAccessibilityWin::GetTargetFromChildID(
3188     const VARIANT& var_id) {
3189   if (var_id.vt != VT_I4)
3190     return NULL;
3191 
3192   LONG child_id = var_id.lVal;
3193   if (child_id == CHILDID_SELF)
3194     return this;
3195 
3196   if (child_id >= 1 && child_id <= static_cast<LONG>(PlatformChildCount()))
3197     return PlatformGetChild(child_id - 1)->ToBrowserAccessibilityWin();
3198 
3199   return manager()->ToBrowserAccessibilityManagerWin()->
3200       GetFromUniqueIdWin(child_id);
3201 }
3202 
GetStringAttributeAsBstr(AccessibilityNodeData::StringAttribute attribute,BSTR * value_bstr)3203 HRESULT BrowserAccessibilityWin::GetStringAttributeAsBstr(
3204     AccessibilityNodeData::StringAttribute attribute,
3205     BSTR* value_bstr) {
3206   base::string16 str;
3207 
3208   if (!GetString16Attribute(attribute, &str))
3209     return S_FALSE;
3210 
3211   if (str.empty())
3212     return S_FALSE;
3213 
3214   *value_bstr = SysAllocString(str.c_str());
3215   DCHECK(*value_bstr);
3216 
3217   return S_OK;
3218 }
3219 
StringAttributeToIA2(AccessibilityNodeData::StringAttribute attribute,const char * ia2_attr)3220 void BrowserAccessibilityWin::StringAttributeToIA2(
3221     AccessibilityNodeData::StringAttribute attribute,
3222     const char* ia2_attr) {
3223   base::string16 value;
3224   if (GetString16Attribute(attribute, &value))
3225     ia2_attributes_.push_back(ASCIIToUTF16(ia2_attr) + L":" + value);
3226 }
3227 
BoolAttributeToIA2(AccessibilityNodeData::BoolAttribute attribute,const char * ia2_attr)3228 void BrowserAccessibilityWin::BoolAttributeToIA2(
3229     AccessibilityNodeData::BoolAttribute attribute,
3230     const char* ia2_attr) {
3231   bool value;
3232   if (GetBoolAttribute(attribute, &value)) {
3233     ia2_attributes_.push_back((ASCIIToUTF16(ia2_attr) + L":") +
3234                               (value ? L"true" : L"false"));
3235   }
3236 }
3237 
IntAttributeToIA2(AccessibilityNodeData::IntAttribute attribute,const char * ia2_attr)3238 void BrowserAccessibilityWin::IntAttributeToIA2(
3239     AccessibilityNodeData::IntAttribute attribute,
3240     const char* ia2_attr) {
3241   int value;
3242   if (GetIntAttribute(attribute, &value)) {
3243     ia2_attributes_.push_back(ASCIIToUTF16(ia2_attr) + L":" +
3244                               base::IntToString16(value));
3245   }
3246 }
3247 
GetValueText()3248 base::string16 BrowserAccessibilityWin::GetValueText() {
3249   float fval;
3250   base::string16 value = UTF8ToUTF16(this->value());
3251 
3252   if (value.empty() &&
3253       GetFloatAttribute(AccessibilityNodeData::ATTR_VALUE_FOR_RANGE, &fval)) {
3254     value = UTF8ToUTF16(base::DoubleToString(fval));
3255   }
3256   return value;
3257 }
3258 
TextForIAccessibleText()3259 base::string16 BrowserAccessibilityWin::TextForIAccessibleText() {
3260   if (IsEditableText())
3261     return UTF8ToUTF16(value());
3262   return (blink_role() == blink::WebAXRoleStaticText) ?
3263       UTF8ToUTF16(name()) : hypertext_;
3264 }
3265 
HandleSpecialTextOffset(const base::string16 & text,LONG * offset)3266 void BrowserAccessibilityWin::HandleSpecialTextOffset(
3267     const base::string16& text,
3268     LONG* offset) {
3269   if (*offset == IA2_TEXT_OFFSET_LENGTH)
3270     *offset = static_cast<LONG>(text.size());
3271   else if (*offset == IA2_TEXT_OFFSET_CARET)
3272     get_caretOffset(offset);
3273 }
3274 
IA2TextBoundaryToTextBoundary(IA2TextBoundaryType ia2_boundary)3275 ui::TextBoundaryType BrowserAccessibilityWin::IA2TextBoundaryToTextBoundary(
3276     IA2TextBoundaryType ia2_boundary) {
3277   switch(ia2_boundary) {
3278     case IA2_TEXT_BOUNDARY_CHAR: return ui::CHAR_BOUNDARY;
3279     case IA2_TEXT_BOUNDARY_WORD: return ui::WORD_BOUNDARY;
3280     case IA2_TEXT_BOUNDARY_LINE: return ui::LINE_BOUNDARY;
3281     case IA2_TEXT_BOUNDARY_SENTENCE: return ui::SENTENCE_BOUNDARY;
3282     case IA2_TEXT_BOUNDARY_PARAGRAPH: return ui::PARAGRAPH_BOUNDARY;
3283     case IA2_TEXT_BOUNDARY_ALL: return ui::ALL_BOUNDARY;
3284     default:
3285       NOTREACHED();
3286       return ui::CHAR_BOUNDARY;
3287   }
3288 }
3289 
FindBoundary(const base::string16 & text,IA2TextBoundaryType ia2_boundary,LONG start_offset,ui::TextBoundaryDirection direction)3290 LONG BrowserAccessibilityWin::FindBoundary(
3291     const base::string16& text,
3292     IA2TextBoundaryType ia2_boundary,
3293     LONG start_offset,
3294     ui::TextBoundaryDirection direction) {
3295   HandleSpecialTextOffset(text, &start_offset);
3296   ui::TextBoundaryType boundary = IA2TextBoundaryToTextBoundary(ia2_boundary);
3297   const std::vector<int32>& line_breaks = GetIntListAttribute(
3298       AccessibilityNodeData::ATTR_LINE_BREAKS);
3299   return ui::FindAccessibleTextBoundary(
3300       text, line_breaks, boundary, start_offset, direction);
3301 }
3302 
GetFromRendererID(int32 renderer_id)3303 BrowserAccessibilityWin* BrowserAccessibilityWin::GetFromRendererID(
3304     int32 renderer_id) {
3305   return manager()->GetFromRendererID(renderer_id)->ToBrowserAccessibilityWin();
3306 }
3307 
InitRoleAndState()3308 void BrowserAccessibilityWin::InitRoleAndState() {
3309   ia_state_ = 0;
3310   ia2_state_ = IA2_STATE_OPAQUE;
3311   ia2_attributes_.clear();
3312 
3313   if (HasState(blink::WebAXStateBusy))
3314     ia_state_ |= STATE_SYSTEM_BUSY;
3315   if (HasState(blink::WebAXStateChecked))
3316     ia_state_ |= STATE_SYSTEM_CHECKED;
3317   if (HasState(blink::WebAXStateCollapsed))
3318     ia_state_ |= STATE_SYSTEM_COLLAPSED;
3319   if (HasState(blink::WebAXStateExpanded))
3320     ia_state_ |= STATE_SYSTEM_EXPANDED;
3321   if (HasState(blink::WebAXStateFocusable))
3322     ia_state_ |= STATE_SYSTEM_FOCUSABLE;
3323   if (HasState(blink::WebAXStateHaspopup))
3324     ia_state_ |= STATE_SYSTEM_HASPOPUP;
3325   if (HasState(blink::WebAXStateHovered))
3326     ia_state_ |= STATE_SYSTEM_HOTTRACKED;
3327   if (HasState(blink::WebAXStateIndeterminate))
3328     ia_state_ |= STATE_SYSTEM_INDETERMINATE;
3329   if (HasState(blink::WebAXStateInvisible))
3330     ia_state_ |= STATE_SYSTEM_INVISIBLE;
3331   if (HasState(blink::WebAXStateLinked))
3332     ia_state_ |= STATE_SYSTEM_LINKED;
3333   if (HasState(blink::WebAXStateMultiselectable)) {
3334     ia_state_ |= STATE_SYSTEM_EXTSELECTABLE;
3335     ia_state_ |= STATE_SYSTEM_MULTISELECTABLE;
3336   }
3337   // TODO(ctguil): Support STATE_SYSTEM_EXTSELECTABLE/accSelect.
3338   if (HasState(blink::WebAXStateOffscreen))
3339     ia_state_ |= STATE_SYSTEM_OFFSCREEN;
3340   if (HasState(blink::WebAXStatePressed))
3341     ia_state_ |= STATE_SYSTEM_PRESSED;
3342   if (HasState(blink::WebAXStateProtected))
3343     ia_state_ |= STATE_SYSTEM_PROTECTED;
3344   if (HasState(blink::WebAXStateRequired))
3345     ia2_state_ |= IA2_STATE_REQUIRED;
3346   if (HasState(blink::WebAXStateSelectable))
3347     ia_state_ |= STATE_SYSTEM_SELECTABLE;
3348   if (HasState(blink::WebAXStateSelected))
3349     ia_state_ |= STATE_SYSTEM_SELECTED;
3350   if (HasState(blink::WebAXStateVisited))
3351     ia_state_ |= STATE_SYSTEM_TRAVERSED;
3352   if (!HasState(blink::WebAXStateEnabled))
3353     ia_state_ |= STATE_SYSTEM_UNAVAILABLE;
3354   if (HasState(blink::WebAXStateVertical)) {
3355     ia2_state_ |= IA2_STATE_VERTICAL;
3356   } else {
3357     ia2_state_ |= IA2_STATE_HORIZONTAL;
3358   }
3359   if (HasState(blink::WebAXStateVisited))
3360     ia_state_ |= STATE_SYSTEM_TRAVERSED;
3361 
3362   // WebKit marks everything as readonly unless it's editable text, so if it's
3363   // not readonly, mark it as editable now. The final computation of the
3364   // READONLY state for MSAA is below, after the switch.
3365   if (!HasState(blink::WebAXStateReadonly))
3366     ia2_state_ |= IA2_STATE_EDITABLE;
3367 
3368   base::string16 invalid;
3369   if (GetHtmlAttribute("aria-invalid", &invalid))
3370     ia2_state_ |= IA2_STATE_INVALID_ENTRY;
3371 
3372   if (GetBoolAttribute(AccessibilityNodeData::ATTR_BUTTON_MIXED))
3373     ia_state_ |= STATE_SYSTEM_MIXED;
3374 
3375   if (GetBoolAttribute(AccessibilityNodeData::ATTR_CAN_SET_VALUE))
3376     ia2_state_ |= IA2_STATE_EDITABLE;
3377 
3378   base::string16 html_tag = GetString16Attribute(
3379       AccessibilityNodeData::ATTR_HTML_TAG);
3380   ia_role_ = 0;
3381   ia2_role_ = 0;
3382   switch (blink_role()) {
3383     case blink::WebAXRoleAlert:
3384       ia_role_ = ROLE_SYSTEM_ALERT;
3385       break;
3386     case blink::WebAXRoleAlertDialog:
3387       ia_role_ = ROLE_SYSTEM_DIALOG;
3388       break;
3389     case blink::WebAXRoleApplication:
3390       ia_role_ = ROLE_SYSTEM_APPLICATION;
3391       break;
3392     case blink::WebAXRoleArticle:
3393       ia_role_ = ROLE_SYSTEM_GROUPING;
3394       ia2_role_ = IA2_ROLE_SECTION;
3395       ia_state_ |= STATE_SYSTEM_READONLY;
3396       break;
3397     case blink::WebAXRoleBusyIndicator:
3398       ia_role_ = ROLE_SYSTEM_ANIMATION;
3399       ia_state_ |= STATE_SYSTEM_READONLY;
3400       break;
3401     case blink::WebAXRoleButton:
3402       ia_role_ = ROLE_SYSTEM_PUSHBUTTON;
3403       bool is_aria_pressed_defined;
3404       bool is_mixed;
3405       if (GetAriaTristate("aria-pressed", &is_aria_pressed_defined, &is_mixed))
3406         ia_state_ |= STATE_SYSTEM_PRESSED;
3407       if (is_aria_pressed_defined)
3408         ia2_role_ = IA2_ROLE_TOGGLE_BUTTON;
3409       if (is_mixed)
3410         ia_state_ |= STATE_SYSTEM_MIXED;
3411       break;
3412     case blink::WebAXRoleCanvas:
3413       if (GetBoolAttribute(AccessibilityNodeData::ATTR_CANVAS_HAS_FALLBACK)) {
3414         role_name_ = L"canvas";
3415         ia2_role_ = IA2_ROLE_CANVAS;
3416       } else {
3417         ia_role_ = ROLE_SYSTEM_GRAPHIC;
3418       }
3419       break;
3420     case blink::WebAXRoleCell:
3421       ia_role_ = ROLE_SYSTEM_CELL;
3422       break;
3423     case blink::WebAXRoleCheckBox:
3424       ia_role_ = ROLE_SYSTEM_CHECKBUTTON;
3425       break;
3426     case blink::WebAXRoleColorWell:
3427       ia_role_ = ROLE_SYSTEM_CLIENT;
3428       ia2_role_ = IA2_ROLE_COLOR_CHOOSER;
3429       break;
3430     case blink::WebAXRoleColumn:
3431       ia_role_ = ROLE_SYSTEM_COLUMN;
3432       ia_state_ |= STATE_SYSTEM_READONLY;
3433       break;
3434     case blink::WebAXRoleColumnHeader:
3435       ia_role_ = ROLE_SYSTEM_COLUMNHEADER;
3436       ia_state_ |= STATE_SYSTEM_READONLY;
3437       break;
3438     case blink::WebAXRoleComboBox:
3439       ia_role_ = ROLE_SYSTEM_COMBOBOX;
3440       break;
3441     case blink::WebAXRoleDiv:
3442       role_name_ = L"div";
3443       ia2_role_ = IA2_ROLE_SECTION;
3444       break;
3445     case blink::WebAXRoleDefinition:
3446       role_name_ = html_tag;
3447       ia2_role_ = IA2_ROLE_PARAGRAPH;
3448       ia_state_ |= STATE_SYSTEM_READONLY;
3449       break;
3450     case blink::WebAXRoleDescriptionListDetail:
3451       role_name_ = html_tag;
3452       ia2_role_ = IA2_ROLE_PARAGRAPH;
3453       ia_state_ |= STATE_SYSTEM_READONLY;
3454       break;
3455     case blink::WebAXRoleDescriptionListTerm:
3456       ia_role_ = ROLE_SYSTEM_LISTITEM;
3457       ia_state_ |= STATE_SYSTEM_READONLY;
3458       break;
3459     case blink::WebAXRoleDialog:
3460       ia_role_ = ROLE_SYSTEM_DIALOG;
3461       ia_state_ |= STATE_SYSTEM_READONLY;
3462       break;
3463     case blink::WebAXRoleDisclosureTriangle:
3464       ia_role_ = ROLE_SYSTEM_OUTLINEBUTTON;
3465       ia_state_ |= STATE_SYSTEM_READONLY;
3466       break;
3467     case blink::WebAXRoleDocument:
3468     case blink::WebAXRoleRootWebArea:
3469     case blink::WebAXRoleWebArea:
3470       ia_role_ = ROLE_SYSTEM_DOCUMENT;
3471       ia_state_ |= STATE_SYSTEM_READONLY;
3472       ia_state_ |= STATE_SYSTEM_FOCUSABLE;
3473       break;
3474     case blink::WebAXRoleEditableText:
3475       ia_role_ = ROLE_SYSTEM_TEXT;
3476       ia2_state_ |= IA2_STATE_SINGLE_LINE;
3477       ia2_state_ |= IA2_STATE_EDITABLE;
3478       break;
3479     case blink::WebAXRoleForm:
3480       role_name_ = L"form";
3481       ia2_role_ = IA2_ROLE_FORM;
3482       break;
3483     case blink::WebAXRoleFooter:
3484       ia_role_ = IA2_ROLE_FOOTER;
3485       ia_state_ |= STATE_SYSTEM_READONLY;
3486       break;
3487     case blink::WebAXRoleGrid:
3488       ia_role_ = ROLE_SYSTEM_TABLE;
3489       ia_state_ |= STATE_SYSTEM_READONLY;
3490       break;
3491     case blink::WebAXRoleGroup: {
3492       base::string16 aria_role = GetString16Attribute(
3493           AccessibilityNodeData::ATTR_ROLE);
3494       if (aria_role == L"group" || html_tag == L"fieldset") {
3495         ia_role_ = ROLE_SYSTEM_GROUPING;
3496       } else if (html_tag == L"li") {
3497         ia_role_ = ROLE_SYSTEM_LISTITEM;
3498       } else {
3499         if (html_tag.empty())
3500           role_name_ = L"div";
3501         else
3502           role_name_ = html_tag;
3503         ia2_role_ = IA2_ROLE_SECTION;
3504       }
3505       ia_state_ |= STATE_SYSTEM_READONLY;
3506       break;
3507     }
3508     case blink::WebAXRoleGrowArea:
3509       ia_role_ = ROLE_SYSTEM_GRIP;
3510       ia_state_ |= STATE_SYSTEM_READONLY;
3511       break;
3512     case blink::WebAXRoleHeading:
3513       role_name_ = html_tag;
3514       ia2_role_ = IA2_ROLE_HEADING;
3515       ia_state_ |= STATE_SYSTEM_READONLY;
3516       break;
3517     case blink::WebAXRoleHorizontalRule:
3518       ia_role_ = ROLE_SYSTEM_SEPARATOR;
3519       break;
3520     case blink::WebAXRoleImage:
3521       ia_role_ = ROLE_SYSTEM_GRAPHIC;
3522       ia_state_ |= STATE_SYSTEM_READONLY;
3523       break;
3524     case blink::WebAXRoleImageMap:
3525       role_name_ = html_tag;
3526       ia2_role_ = IA2_ROLE_IMAGE_MAP;
3527       ia_state_ |= STATE_SYSTEM_READONLY;
3528       break;
3529     case blink::WebAXRoleImageMapLink:
3530       ia_role_ = ROLE_SYSTEM_LINK;
3531       ia_state_ |= STATE_SYSTEM_LINKED;
3532       ia_state_ |= STATE_SYSTEM_READONLY;
3533       break;
3534     case blink::WebAXRoleLabel:
3535       ia_role_ = ROLE_SYSTEM_TEXT;
3536       ia2_role_ = IA2_ROLE_LABEL;
3537       break;
3538     case blink::WebAXRoleBanner:
3539     case blink::WebAXRoleComplementary:
3540     case blink::WebAXRoleContentInfo:
3541     case blink::WebAXRoleMain:
3542     case blink::WebAXRoleNavigation:
3543     case blink::WebAXRoleSearch:
3544       ia_role_ = ROLE_SYSTEM_GROUPING;
3545       ia2_role_ = IA2_ROLE_SECTION;
3546       ia_state_ |= STATE_SYSTEM_READONLY;
3547       break;
3548     case blink::WebAXRoleLink:
3549       ia_role_ = ROLE_SYSTEM_LINK;
3550       ia_state_ |= STATE_SYSTEM_LINKED;
3551       break;
3552     case blink::WebAXRoleList:
3553       ia_role_ = ROLE_SYSTEM_LIST;
3554       ia_state_ |= STATE_SYSTEM_READONLY;
3555       break;
3556     case blink::WebAXRoleListBox:
3557       ia_role_ = ROLE_SYSTEM_LIST;
3558       break;
3559     case blink::WebAXRoleListBoxOption:
3560       ia_role_ = ROLE_SYSTEM_LISTITEM;
3561       if (ia_state_ & STATE_SYSTEM_SELECTABLE) {
3562         ia_state_ |= STATE_SYSTEM_FOCUSABLE;
3563         if (HasState(blink::WebAXStateFocused))
3564           ia_state_ |= STATE_SYSTEM_FOCUSED;
3565       }
3566       break;
3567     case blink::WebAXRoleListItem:
3568       ia_role_ = ROLE_SYSTEM_LISTITEM;
3569       ia_state_ |= STATE_SYSTEM_READONLY;
3570       break;
3571     case blink::WebAXRoleListMarker:
3572       ia_role_ = ROLE_SYSTEM_TEXT;
3573       ia_state_ |= STATE_SYSTEM_READONLY;
3574       break;
3575     case blink::WebAXRoleMath:
3576       ia_role_ = ROLE_SYSTEM_EQUATION;
3577       ia_state_ |= STATE_SYSTEM_READONLY;
3578       break;
3579     case blink::WebAXRoleMenu:
3580     case blink::WebAXRoleMenuButton:
3581       ia_role_ = ROLE_SYSTEM_MENUPOPUP;
3582       break;
3583     case blink::WebAXRoleMenuBar:
3584       ia_role_ = ROLE_SYSTEM_MENUBAR;
3585       break;
3586     case blink::WebAXRoleMenuItem:
3587       ia_role_ = ROLE_SYSTEM_MENUITEM;
3588       break;
3589     case blink::WebAXRoleMenuListPopup:
3590       ia_role_ = ROLE_SYSTEM_CLIENT;
3591       break;
3592     case blink::WebAXRoleMenuListOption:
3593       ia_role_ = ROLE_SYSTEM_LISTITEM;
3594       if (ia_state_ & STATE_SYSTEM_SELECTABLE) {
3595         ia_state_ |= STATE_SYSTEM_FOCUSABLE;
3596         if (HasState(blink::WebAXStateFocused))
3597           ia_state_ |= STATE_SYSTEM_FOCUSED;
3598       }
3599       break;
3600     case blink::WebAXRoleNote:
3601       ia_role_ = ROLE_SYSTEM_GROUPING;
3602       ia2_role_ = IA2_ROLE_NOTE;
3603       ia_state_ |= STATE_SYSTEM_READONLY;
3604       break;
3605     case blink::WebAXRoleOutline:
3606       ia_role_ = ROLE_SYSTEM_OUTLINE;
3607       ia_state_ |= STATE_SYSTEM_READONLY;
3608       break;
3609     case blink::WebAXRoleParagraph:
3610       role_name_ = L"P";
3611       ia2_role_ = IA2_ROLE_PARAGRAPH;
3612       break;
3613     case blink::WebAXRolePopUpButton:
3614       if (html_tag == L"select") {
3615         ia_role_ = ROLE_SYSTEM_COMBOBOX;
3616       } else {
3617         ia_role_ = ROLE_SYSTEM_BUTTONMENU;
3618       }
3619       break;
3620     case blink::WebAXRoleProgressIndicator:
3621       ia_role_ = ROLE_SYSTEM_PROGRESSBAR;
3622       ia_state_ |= STATE_SYSTEM_READONLY;
3623       break;
3624     case blink::WebAXRoleRadioButton:
3625       ia_role_ = ROLE_SYSTEM_RADIOBUTTON;
3626       break;
3627     case blink::WebAXRoleRadioGroup:
3628       ia_role_ = ROLE_SYSTEM_GROUPING;
3629       ia2_role_ = IA2_ROLE_SECTION;
3630       break;
3631     case blink::WebAXRoleRegion:
3632       ia_role_ = ROLE_SYSTEM_GROUPING;
3633       ia2_role_ = IA2_ROLE_SECTION;
3634       ia_state_ |= STATE_SYSTEM_READONLY;
3635       break;
3636     case blink::WebAXRoleRow:
3637       ia_role_ = ROLE_SYSTEM_ROW;
3638       ia_state_ |= STATE_SYSTEM_READONLY;
3639       break;
3640     case blink::WebAXRoleRowHeader:
3641       ia_role_ = ROLE_SYSTEM_ROWHEADER;
3642       ia_state_ |= STATE_SYSTEM_READONLY;
3643       break;
3644     case blink::WebAXRoleRuler:
3645       ia_role_ = ROLE_SYSTEM_CLIENT;
3646       ia2_role_ = IA2_ROLE_RULER;
3647       ia_state_ |= STATE_SYSTEM_READONLY;
3648       break;
3649     case blink::WebAXRoleScrollArea:
3650       ia_role_ = ROLE_SYSTEM_CLIENT;
3651       ia2_role_ = IA2_ROLE_SCROLL_PANE;
3652       ia_state_ |= STATE_SYSTEM_READONLY;
3653       break;
3654     case blink::WebAXRoleScrollBar:
3655       ia_role_ = ROLE_SYSTEM_SCROLLBAR;
3656       break;
3657     case blink::WebAXRoleSlider:
3658       ia_role_ = ROLE_SYSTEM_SLIDER;
3659       break;
3660     case blink::WebAXRoleSpinButton:
3661       ia_role_ = ROLE_SYSTEM_SPINBUTTON;
3662       break;
3663     case blink::WebAXRoleSpinButtonPart:
3664       ia_role_ = ROLE_SYSTEM_PUSHBUTTON;
3665       break;
3666     case blink::WebAXRoleSplitGroup:
3667       ia_role_ = ROLE_SYSTEM_CLIENT;
3668       ia2_role_ = IA2_ROLE_SPLIT_PANE;
3669       ia_state_ |= STATE_SYSTEM_READONLY;
3670       break;
3671     case blink::WebAXRoleAnnotation:
3672     case blink::WebAXRoleStaticText:
3673       ia_role_ = ROLE_SYSTEM_TEXT;
3674       ia_state_ |= STATE_SYSTEM_READONLY;
3675       break;
3676     case blink::WebAXRoleStatus:
3677       ia_role_ = ROLE_SYSTEM_STATUSBAR;
3678       ia_state_ |= STATE_SYSTEM_READONLY;
3679       break;
3680     case blink::WebAXRoleSplitter:
3681       ia_role_ = ROLE_SYSTEM_SEPARATOR;
3682       break;
3683     case blink::WebAXRoleSVGRoot:
3684       ia_role_ = ROLE_SYSTEM_GRAPHIC;
3685       break;
3686     case blink::WebAXRoleTab:
3687       ia_role_ = ROLE_SYSTEM_PAGETAB;
3688       break;
3689     case blink::WebAXRoleTable: {
3690       base::string16 aria_role = GetString16Attribute(
3691           AccessibilityNodeData::ATTR_ROLE);
3692       if (aria_role == L"treegrid") {
3693         ia_role_ = ROLE_SYSTEM_OUTLINE;
3694       } else {
3695         ia_role_ = ROLE_SYSTEM_TABLE;
3696         ia_state_ |= STATE_SYSTEM_READONLY;
3697       }
3698       break;
3699     }
3700     case blink::WebAXRoleTableHeaderContainer:
3701       ia_role_ = ROLE_SYSTEM_GROUPING;
3702       ia2_role_ = IA2_ROLE_SECTION;
3703       ia_state_ |= STATE_SYSTEM_READONLY;
3704       break;
3705     case blink::WebAXRoleTabList:
3706       ia_role_ = ROLE_SYSTEM_PAGETABLIST;
3707       break;
3708     case blink::WebAXRoleTabPanel:
3709       ia_role_ = ROLE_SYSTEM_PROPERTYPAGE;
3710       break;
3711     case blink::WebAXRoleToggleButton:
3712       ia_role_ = ROLE_SYSTEM_PUSHBUTTON;
3713       ia2_role_ = IA2_ROLE_TOGGLE_BUTTON;
3714       break;
3715     case blink::WebAXRoleTextArea:
3716       ia_role_ = ROLE_SYSTEM_TEXT;
3717       ia2_state_ |= IA2_STATE_MULTI_LINE;
3718       ia2_state_ |= IA2_STATE_EDITABLE;
3719       ia2_state_ |= IA2_STATE_SELECTABLE_TEXT;
3720       break;
3721     case blink::WebAXRoleTextField:
3722       ia_role_ = ROLE_SYSTEM_TEXT;
3723       ia2_state_ |= IA2_STATE_SINGLE_LINE;
3724       ia2_state_ |= IA2_STATE_EDITABLE;
3725       ia2_state_ |= IA2_STATE_SELECTABLE_TEXT;
3726       break;
3727     case blink::WebAXRoleTimer:
3728       ia_role_ = ROLE_SYSTEM_CLOCK;
3729       ia_state_ |= STATE_SYSTEM_READONLY;
3730       break;
3731     case blink::WebAXRoleToolbar:
3732       ia_role_ = ROLE_SYSTEM_TOOLBAR;
3733       ia_state_ |= STATE_SYSTEM_READONLY;
3734       break;
3735     case blink::WebAXRoleUserInterfaceTooltip:
3736       ia_role_ = ROLE_SYSTEM_TOOLTIP;
3737       ia_state_ |= STATE_SYSTEM_READONLY;
3738       break;
3739     case blink::WebAXRoleTree:
3740       ia_role_ = ROLE_SYSTEM_OUTLINE;
3741       ia_state_ |= STATE_SYSTEM_READONLY;
3742       break;
3743     case blink::WebAXRoleTreeGrid:
3744       ia_role_ = ROLE_SYSTEM_OUTLINE;
3745       ia_state_ |= STATE_SYSTEM_READONLY;
3746       break;
3747     case blink::WebAXRoleTreeItem:
3748       ia_role_ = ROLE_SYSTEM_OUTLINEITEM;
3749       ia_state_ |= STATE_SYSTEM_READONLY;
3750       break;
3751     case blink::WebAXRoleWindow:
3752       ia_role_ = ROLE_SYSTEM_WINDOW;
3753       break;
3754 
3755     // TODO(dmazzoni): figure out the proper MSAA role for all of these.
3756     case blink::WebAXRoleBrowser:
3757     case blink::WebAXRoleDirectory:
3758     case blink::WebAXRoleDrawer:
3759     case blink::WebAXRoleHelpTag:
3760     case blink::WebAXRoleIgnored:
3761     case blink::WebAXRoleIncrementor:
3762     case blink::WebAXRoleLog:
3763     case blink::WebAXRoleMarquee:
3764     case blink::WebAXRoleMatte:
3765     case blink::WebAXRolePresentational:
3766     case blink::WebAXRoleRulerMarker:
3767     case blink::WebAXRoleSheet:
3768     case blink::WebAXRoleSliderThumb:
3769     case blink::WebAXRoleSystemWide:
3770     case blink::WebAXRoleValueIndicator:
3771     default:
3772       ia_role_ = ROLE_SYSTEM_CLIENT;
3773       break;
3774   }
3775 
3776   // Compute the final value of READONLY for MSAA.
3777   //
3778   // We always set the READONLY state for elements that have the
3779   // aria-readonly attribute and for a few roles (in the switch above).
3780   // We clear the READONLY state on focusable controls and on a document.
3781   // Everything else, the majority of objects, do not have this state set.
3782   if (HasState(blink::WebAXStateFocusable) &&
3783       ia_role_ != ROLE_SYSTEM_DOCUMENT) {
3784     ia_state_ &= ~(STATE_SYSTEM_READONLY);
3785   }
3786   if (!HasState(blink::WebAXStateReadonly))
3787     ia_state_ &= ~(STATE_SYSTEM_READONLY);
3788   if (GetBoolAttribute(AccessibilityNodeData::ATTR_ARIA_READONLY))
3789     ia_state_ |= STATE_SYSTEM_READONLY;
3790 
3791   // The role should always be set.
3792   DCHECK(!role_name_.empty() || ia_role_);
3793 
3794   // If we didn't explicitly set the IAccessible2 role, make it the same
3795   // as the MSAA role.
3796   if (!ia2_role_)
3797     ia2_role_ = ia_role_;
3798 }
3799 
3800 }  // namespace content
3801