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