Home | History | Annotate | Download | only in accessibility
      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 "ui/views/accessibility/native_view_accessibility_win.h"
      6 
      7 #include <UIAutomationClient.h>
      8 #include <oleacc.h>
      9 
     10 #include <set>
     11 #include <vector>
     12 
     13 #include "base/memory/singleton.h"
     14 #include "base/win/windows_version.h"
     15 #include "third_party/iaccessible2/ia2_api_all.h"
     16 #include "ui/base/accessibility/accessible_text_utils.h"
     17 #include "ui/base/accessibility/accessible_view_state.h"
     18 #include "ui/base/win/accessibility_ids_win.h"
     19 #include "ui/base/win/accessibility_misc_utils.h"
     20 #include "ui/base/win/atl_module.h"
     21 #include "ui/views/controls/button/custom_button.h"
     22 #include "ui/views/focus/focus_manager.h"
     23 #include "ui/views/focus/view_storage.h"
     24 #include "ui/views/win/hwnd_util.h"
     25 
     26 using ui::AccessibilityTypes;
     27 
     28 namespace views {
     29 namespace {
     30 
     31 class AccessibleWebViewRegistry {
     32  public:
     33   static AccessibleWebViewRegistry* GetInstance();
     34 
     35   void RegisterWebView(AccessibleWebView* web_view);
     36 
     37   void UnregisterWebView(AccessibleWebView* web_view);
     38 
     39   // Given the view that received the request for the accessible
     40   // id in |top_view|, and the child id requested, return the native
     41   // accessible object with that child id from one of the WebViews in
     42   // |top_view|'s view hierarchy, if any.
     43   IAccessible* GetAccessibleFromWebView(View* top_view, long child_id);
     44 
     45  private:
     46   friend struct DefaultSingletonTraits<AccessibleWebViewRegistry>;
     47   AccessibleWebViewRegistry();
     48   ~AccessibleWebViewRegistry() {}
     49 
     50   // Set of all web views. We check whether each one is contained in a
     51   // top view dynamically rather than keeping track of a map.
     52   std::set<AccessibleWebView*> web_views_;
     53 
     54   // The most recent top view used in a call to GetAccessibleFromWebView.
     55   View* last_top_view_;
     56 
     57   // The most recent web view where an accessible object was found,
     58   // corresponding to |last_top_view_|.
     59   AccessibleWebView* last_web_view_;
     60 
     61   DISALLOW_COPY_AND_ASSIGN(AccessibleWebViewRegistry);
     62 };
     63 
     64 AccessibleWebViewRegistry::AccessibleWebViewRegistry()
     65     : last_top_view_(NULL),
     66       last_web_view_(NULL) {
     67 }
     68 
     69 AccessibleWebViewRegistry* AccessibleWebViewRegistry::GetInstance() {
     70   return Singleton<AccessibleWebViewRegistry>::get();
     71 }
     72 
     73 void AccessibleWebViewRegistry::RegisterWebView(AccessibleWebView* web_view) {
     74   DCHECK(web_views_.find(web_view) == web_views_.end());
     75   web_views_.insert(web_view);
     76 }
     77 
     78 void AccessibleWebViewRegistry::UnregisterWebView(AccessibleWebView* web_view) {
     79   DCHECK(web_views_.find(web_view) != web_views_.end());
     80   web_views_.erase(web_view);
     81   if (last_web_view_ == web_view) {
     82     last_top_view_ = NULL;
     83     last_web_view_ = NULL;
     84   }
     85 }
     86 
     87 IAccessible* AccessibleWebViewRegistry::GetAccessibleFromWebView(
     88     View* top_view, long child_id) {
     89   // This function gets called frequently, so try to avoid searching all
     90   // of the web views if the notification is on the same web view that
     91   // sent the last one.
     92   if (last_top_view_ == top_view) {
     93     IAccessible* accessible =
     94         last_web_view_->AccessibleObjectFromChildId(child_id);
     95     if (accessible)
     96       return accessible;
     97   }
     98 
     99   // Search all web views. For each one, first ensure it's a descendant
    100   // of this view where the event was posted - and if so, see if it owns
    101   // an accessible object with that child id. If so, save the view to speed
    102   // up the next notification.
    103   for (std::set<AccessibleWebView*>::iterator iter = web_views_.begin();
    104        iter != web_views_.end(); ++iter) {
    105     AccessibleWebView* web_view = *iter;
    106     if (!top_view->Contains(web_view->AsView()))
    107       continue;
    108     IAccessible* accessible = web_view->AccessibleObjectFromChildId(child_id);
    109     if (accessible) {
    110       last_top_view_ = top_view;
    111       last_web_view_ = web_view;
    112       return accessible;
    113     }
    114   }
    115   return NULL;
    116 }
    117 
    118 }  // anonymous namespace
    119 
    120 // static
    121 long NativeViewAccessibilityWin::next_unique_id_ = 1;
    122 int NativeViewAccessibilityWin::view_storage_ids_[kMaxViewStorageIds] = {0};
    123 int NativeViewAccessibilityWin::next_view_storage_id_index_ = 0;
    124 
    125 // static
    126 NativeViewAccessibility* NativeViewAccessibility::Create(View* view) {
    127   // Make sure ATL is initialized in this module.
    128   ui::win::CreateATLModuleIfNeeded();
    129 
    130   CComObject<NativeViewAccessibilityWin>* instance = NULL;
    131   HRESULT hr = CComObject<NativeViewAccessibilityWin>::CreateInstance(
    132       &instance);
    133   DCHECK(SUCCEEDED(hr));
    134   instance->set_view(view);
    135   instance->AddRef();
    136   return instance;
    137 }
    138 
    139 NativeViewAccessibilityWin::NativeViewAccessibilityWin()
    140     : view_(NULL),
    141       unique_id_(next_unique_id_++) {
    142 }
    143 
    144 NativeViewAccessibilityWin::~NativeViewAccessibilityWin() {
    145 }
    146 
    147 void NativeViewAccessibilityWin::NotifyAccessibilityEvent(
    148     ui::AccessibilityTypes::Event event_type) {
    149   if (!view_)
    150     return;
    151 
    152   ViewStorage* view_storage = ViewStorage::GetInstance();
    153   HWND hwnd = HWNDForView(view_);
    154   int view_storage_id = view_storage_ids_[next_view_storage_id_index_];
    155   if (view_storage_id == 0) {
    156     view_storage_id = view_storage->CreateStorageID();
    157     view_storage_ids_[next_view_storage_id_index_] = view_storage_id;
    158   } else {
    159     view_storage->RemoveView(view_storage_id);
    160   }
    161   view_storage->StoreView(view_storage_id, view_);
    162 
    163   // Positive child ids are used for enumerating direct children,
    164   // negative child ids can be used as unique ids to refer to a specific
    165   // descendants.  Make index into view_storage_ids_ into a negative child id.
    166   int child_id =
    167       base::win::kFirstViewsAccessibilityId - next_view_storage_id_index_;
    168   ::NotifyWinEvent(MSAAEvent(event_type), hwnd, OBJID_CLIENT, child_id);
    169   next_view_storage_id_index_ =
    170       (next_view_storage_id_index_ + 1) % kMaxViewStorageIds;
    171 }
    172 
    173 gfx::NativeViewAccessible NativeViewAccessibilityWin::GetNativeObject() {
    174   return this;
    175 }
    176 
    177 void NativeViewAccessibilityWin::Destroy() {
    178   view_ = NULL;
    179   Release();
    180 }
    181 
    182 // TODO(ctguil): Handle case where child View is not contained by parent.
    183 STDMETHODIMP NativeViewAccessibilityWin::accHitTest(
    184     LONG x_left, LONG y_top, VARIANT* child) {
    185   if (!child)
    186     return E_INVALIDARG;
    187 
    188   if (!view_)
    189     return E_FAIL;
    190 
    191   gfx::Point point(x_left, y_top);
    192   View::ConvertPointToTarget(NULL, view_, &point);
    193 
    194   if (!view_->HitTestPoint(point)) {
    195     // If containing parent is not hit, return with failure.
    196     child->vt = VT_EMPTY;
    197     return S_FALSE;
    198   }
    199 
    200   View* view = view_->GetEventHandlerForPoint(point);
    201   if (view == view_) {
    202     // No child hit, return parent id.
    203     child->vt = VT_I4;
    204     child->lVal = CHILDID_SELF;
    205   } else {
    206     child->vt = VT_DISPATCH;
    207     child->pdispVal = view->GetNativeViewAccessible();
    208     child->pdispVal->AddRef();
    209   }
    210   return S_OK;
    211 }
    212 
    213 HRESULT NativeViewAccessibilityWin::accDoDefaultAction(VARIANT var_id) {
    214   if (!IsValidId(var_id))
    215     return E_INVALIDARG;
    216 
    217   // The object does not support the method. This value is returned for
    218   // controls that do not perform actions, such as edit fields.
    219   return DISP_E_MEMBERNOTFOUND;
    220 }
    221 
    222 STDMETHODIMP NativeViewAccessibilityWin::accLocation(
    223     LONG* x_left, LONG* y_top, LONG* width, LONG* height, VARIANT var_id) {
    224   if (!IsValidId(var_id) || !x_left || !y_top || !width || !height)
    225     return E_INVALIDARG;
    226 
    227   if (!view_)
    228     return E_FAIL;
    229 
    230   if (!view_->bounds().IsEmpty()) {
    231     *width  = view_->width();
    232     *height = view_->height();
    233     gfx::Point topleft(view_->bounds().origin());
    234     View::ConvertPointToScreen(
    235         view_->parent() ? view_->parent() : view_, &topleft);
    236     *x_left = topleft.x();
    237     *y_top  = topleft.y();
    238   } else {
    239     return E_FAIL;
    240   }
    241   return S_OK;
    242 }
    243 
    244 STDMETHODIMP NativeViewAccessibilityWin::accNavigate(
    245     LONG nav_dir, VARIANT start, VARIANT* end) {
    246   if (start.vt != VT_I4 || !end)
    247     return E_INVALIDARG;
    248 
    249   if (!view_)
    250     return E_FAIL;
    251 
    252   switch (nav_dir) {
    253     case NAVDIR_FIRSTCHILD:
    254     case NAVDIR_LASTCHILD: {
    255       if (start.lVal != CHILDID_SELF) {
    256         // Start of navigation must be on the View itself.
    257         return E_INVALIDARG;
    258       } else if (!view_->has_children()) {
    259         // No children found.
    260         return S_FALSE;
    261       }
    262 
    263       // Set child_id based on first or last child.
    264       int child_id = 0;
    265       if (nav_dir == NAVDIR_LASTCHILD)
    266         child_id = view_->child_count() - 1;
    267 
    268       View* child = view_->child_at(child_id);
    269       end->vt = VT_DISPATCH;
    270       end->pdispVal = child->GetNativeViewAccessible();
    271       end->pdispVal->AddRef();
    272       return S_OK;
    273     }
    274     case NAVDIR_LEFT:
    275     case NAVDIR_UP:
    276     case NAVDIR_PREVIOUS:
    277     case NAVDIR_RIGHT:
    278     case NAVDIR_DOWN:
    279     case NAVDIR_NEXT: {
    280       // Retrieve parent to access view index and perform bounds checking.
    281       View* parent = view_->parent();
    282       if (!parent) {
    283         return E_FAIL;
    284       }
    285 
    286       if (start.lVal == CHILDID_SELF) {
    287         int view_index = parent->GetIndexOf(view_);
    288         // Check navigation bounds, adjusting for View child indexing (MSAA
    289         // child indexing starts with 1, whereas View indexing starts with 0).
    290         if (!IsValidNav(nav_dir, view_index, -1,
    291                         parent->child_count() - 1)) {
    292           // Navigation attempted to go out-of-bounds.
    293           end->vt = VT_EMPTY;
    294           return S_FALSE;
    295         } else {
    296           if (IsNavDirNext(nav_dir)) {
    297             view_index += 1;
    298           } else {
    299             view_index -=1;
    300           }
    301         }
    302 
    303         View* child = parent->child_at(view_index);
    304         end->pdispVal = child->GetNativeViewAccessible();
    305         end->vt = VT_DISPATCH;
    306         end->pdispVal->AddRef();
    307         return S_OK;
    308       } else {
    309         // Check navigation bounds, adjusting for MSAA child indexing (MSAA
    310         // child indexing starts with 1, whereas View indexing starts with 0).
    311         if (!IsValidNav(nav_dir, start.lVal, 0, parent->child_count() + 1)) {
    312             // Navigation attempted to go out-of-bounds.
    313             end->vt = VT_EMPTY;
    314             return S_FALSE;
    315           } else {
    316             if (IsNavDirNext(nav_dir)) {
    317               start.lVal += 1;
    318             } else {
    319               start.lVal -= 1;
    320             }
    321         }
    322 
    323         HRESULT result = this->get_accChild(start, &end->pdispVal);
    324         if (result == S_FALSE) {
    325           // Child is a leaf.
    326           end->vt = VT_I4;
    327           end->lVal = start.lVal;
    328         } else if (result == E_INVALIDARG) {
    329           return E_INVALIDARG;
    330         } else {
    331           // Child is not a leaf.
    332           end->vt = VT_DISPATCH;
    333         }
    334       }
    335       break;
    336     }
    337     default:
    338       return E_INVALIDARG;
    339   }
    340   // Navigation performed correctly. Global return for this function, if no
    341   // error triggered an escape earlier.
    342   return S_OK;
    343 }
    344 
    345 STDMETHODIMP NativeViewAccessibilityWin::get_accChild(VARIANT var_child,
    346                                                       IDispatch** disp_child) {
    347   if (var_child.vt != VT_I4 || !disp_child)
    348     return E_INVALIDARG;
    349 
    350   if (!view_)
    351     return E_FAIL;
    352 
    353   LONG child_id = V_I4(&var_child);
    354 
    355   if (child_id == CHILDID_SELF) {
    356     // Remain with the same dispatch.
    357     return S_OK;
    358   }
    359 
    360   View* child_view = NULL;
    361   if (child_id > 0) {
    362     // Positive child ids are a 1-based child index, used by clients
    363     // that want to enumerate all immediate children.
    364     int child_id_as_index = child_id - 1;
    365     if (child_id_as_index < view_->child_count())
    366       child_view = view_->child_at(child_id_as_index);
    367   } else {
    368     // Negative child ids can be used to map to any descendant;
    369     // we map child ids to a view storage id that can refer to a
    370     // specific view (if that view still exists).
    371     int view_storage_id_index =
    372         base::win::kFirstViewsAccessibilityId - child_id;
    373     if (view_storage_id_index >= 0 &&
    374         view_storage_id_index < kMaxViewStorageIds) {
    375       int view_storage_id = view_storage_ids_[view_storage_id_index];
    376       ViewStorage* view_storage = ViewStorage::GetInstance();
    377       child_view = view_storage->RetrieveView(view_storage_id);
    378     } else {
    379       *disp_child = AccessibleWebViewRegistry::GetInstance()->
    380           GetAccessibleFromWebView(view_, child_id);
    381       if (*disp_child) {
    382         (*disp_child)->AddRef();
    383         return S_OK;
    384       }
    385     }
    386   }
    387 
    388   if (!child_view) {
    389     // No child found.
    390     *disp_child = NULL;
    391     return E_FAIL;
    392   }
    393 
    394   *disp_child = child_view->GetNativeViewAccessible();
    395   (*disp_child)->AddRef();
    396   return S_OK;
    397 }
    398 
    399 STDMETHODIMP NativeViewAccessibilityWin::get_accChildCount(LONG* child_count) {
    400   if (!child_count || !view_)
    401     return E_INVALIDARG;
    402 
    403   if (!view_)
    404     return E_FAIL;
    405 
    406   *child_count = view_->child_count();
    407   return S_OK;
    408 }
    409 
    410 STDMETHODIMP NativeViewAccessibilityWin::get_accDefaultAction(
    411     VARIANT var_id, BSTR* def_action) {
    412   if (!IsValidId(var_id) || !def_action)
    413     return E_INVALIDARG;
    414 
    415   if (!view_)
    416     return E_FAIL;
    417 
    418   ui::AccessibleViewState state;
    419   view_->GetAccessibleState(&state);
    420   string16 temp_action = state.default_action;
    421 
    422   if (!temp_action.empty()) {
    423     *def_action = SysAllocString(temp_action.c_str());
    424   } else {
    425     return S_FALSE;
    426   }
    427 
    428   return S_OK;
    429 }
    430 
    431 STDMETHODIMP NativeViewAccessibilityWin::get_accDescription(
    432     VARIANT var_id, BSTR* desc) {
    433   if (!IsValidId(var_id) || !desc)
    434     return E_INVALIDARG;
    435 
    436   if (!view_)
    437     return E_FAIL;
    438 
    439   string16 temp_desc;
    440 
    441   view_->GetTooltipText(gfx::Point(), &temp_desc);
    442   if (!temp_desc.empty()) {
    443     *desc = SysAllocString(temp_desc.c_str());
    444   } else {
    445     return S_FALSE;
    446   }
    447 
    448   return S_OK;
    449 }
    450 
    451 STDMETHODIMP NativeViewAccessibilityWin::get_accFocus(VARIANT* focus_child) {
    452   if (!focus_child)
    453     return E_INVALIDARG;
    454 
    455   if (!view_)
    456     return E_FAIL;
    457 
    458   FocusManager* focus_manager = view_->GetFocusManager();
    459   View* focus = focus_manager ? focus_manager->GetFocusedView() : NULL;
    460   if (focus == view_) {
    461     // This view has focus.
    462     focus_child->vt = VT_I4;
    463     focus_child->lVal = CHILDID_SELF;
    464   } else if (focus && view_->Contains(focus)) {
    465     // Return the child object that has the keyboard focus.
    466     focus_child->vt = VT_DISPATCH;
    467     focus_child->pdispVal = focus->GetNativeViewAccessible();
    468     focus_child->pdispVal->AddRef();
    469     return S_OK;
    470   } else {
    471     // Neither this object nor any of its children has the keyboard focus.
    472     focus_child->vt = VT_EMPTY;
    473   }
    474   return S_OK;
    475 }
    476 
    477 STDMETHODIMP NativeViewAccessibilityWin::get_accKeyboardShortcut(
    478     VARIANT var_id, BSTR* acc_key) {
    479   if (!IsValidId(var_id) || !acc_key)
    480     return E_INVALIDARG;
    481 
    482   if (!view_)
    483     return E_FAIL;
    484 
    485   ui::AccessibleViewState state;
    486   view_->GetAccessibleState(&state);
    487   string16 temp_key = state.keyboard_shortcut;
    488 
    489   if (!temp_key.empty()) {
    490     *acc_key = SysAllocString(temp_key.c_str());
    491   } else {
    492     return S_FALSE;
    493   }
    494 
    495   return S_OK;
    496 }
    497 
    498 STDMETHODIMP NativeViewAccessibilityWin::get_accName(
    499     VARIANT var_id, BSTR* name) {
    500   if (!IsValidId(var_id) || !name)
    501     return E_INVALIDARG;
    502 
    503   if (!view_)
    504     return E_FAIL;
    505 
    506   // Retrieve the current view's name.
    507   ui::AccessibleViewState state;
    508   view_->GetAccessibleState(&state);
    509   string16 temp_name = state.name;
    510   if (!temp_name.empty()) {
    511     // Return name retrieved.
    512     *name = SysAllocString(temp_name.c_str());
    513   } else {
    514     // If view has no name, return S_FALSE.
    515     return S_FALSE;
    516   }
    517 
    518   return S_OK;
    519 }
    520 
    521 STDMETHODIMP NativeViewAccessibilityWin::get_accParent(
    522     IDispatch** disp_parent) {
    523   if (!disp_parent)
    524     return E_INVALIDARG;
    525 
    526   if (!view_)
    527     return E_FAIL;
    528 
    529   *disp_parent = NULL;
    530   View* parent_view = view_->parent();
    531 
    532   if (!parent_view) {
    533     HWND hwnd = HWNDForView(view_);
    534     if (!hwnd)
    535       return S_FALSE;
    536 
    537     return ::AccessibleObjectFromWindow(
    538         hwnd, OBJID_WINDOW, IID_IAccessible,
    539         reinterpret_cast<void**>(disp_parent));
    540   }
    541 
    542   *disp_parent = parent_view->GetNativeViewAccessible();
    543   (*disp_parent)->AddRef();
    544   return S_OK;
    545 }
    546 
    547 STDMETHODIMP NativeViewAccessibilityWin::get_accRole(
    548     VARIANT var_id, VARIANT* role) {
    549   if (!IsValidId(var_id) || !role)
    550     return E_INVALIDARG;
    551 
    552   if (!view_)
    553     return E_FAIL;
    554 
    555   ui::AccessibleViewState state;
    556   view_->GetAccessibleState(&state);
    557   role->vt = VT_I4;
    558   role->lVal = MSAARole(state.role);
    559   return S_OK;
    560 }
    561 
    562 STDMETHODIMP NativeViewAccessibilityWin::get_accState(
    563     VARIANT var_id, VARIANT* state) {
    564   // This returns MSAA states. See also the IAccessible2 interface
    565   // get_states().
    566 
    567   if (!IsValidId(var_id) || !state)
    568     return E_INVALIDARG;
    569 
    570   if (!view_)
    571     return E_FAIL;
    572 
    573   state->vt = VT_I4;
    574 
    575   // Retrieve all currently applicable states of the parent.
    576   SetState(state, view_);
    577 
    578   // Make sure that state is not empty, and has the proper type.
    579   if (state->vt == VT_EMPTY)
    580     return E_FAIL;
    581 
    582   return S_OK;
    583 }
    584 
    585 STDMETHODIMP NativeViewAccessibilityWin::get_accValue(VARIANT var_id,
    586                                                       BSTR* value) {
    587   if (!IsValidId(var_id) || !value)
    588     return E_INVALIDARG;
    589 
    590   if (!view_)
    591     return E_FAIL;
    592 
    593   // Retrieve the current view's value.
    594   ui::AccessibleViewState state;
    595   view_->GetAccessibleState(&state);
    596   string16 temp_value = state.value;
    597 
    598   if (!temp_value.empty()) {
    599     // Return value retrieved.
    600     *value = SysAllocString(temp_value.c_str());
    601   } else {
    602     // If view has no value, fall back into the default implementation.
    603     *value = NULL;
    604     return E_NOTIMPL;
    605   }
    606 
    607   return S_OK;
    608 }
    609 
    610 STDMETHODIMP NativeViewAccessibilityWin::put_accValue(VARIANT var_id,
    611                                                       BSTR new_value) {
    612   if (!IsValidId(var_id) || !new_value)
    613     return E_INVALIDARG;
    614 
    615   if (!view_)
    616     return E_FAIL;
    617 
    618   // Return an error if the view can't set the value.
    619   ui::AccessibleViewState state;
    620   view_->GetAccessibleState(&state);
    621   if (state.set_value_callback.is_null())
    622     return E_FAIL;
    623 
    624   state.set_value_callback.Run(new_value);
    625   return S_OK;
    626 }
    627 
    628 // IAccessible functions not supported.
    629 
    630 STDMETHODIMP NativeViewAccessibilityWin::get_accSelection(VARIANT* selected) {
    631   if (selected)
    632     selected->vt = VT_EMPTY;
    633   return E_NOTIMPL;
    634 }
    635 
    636 STDMETHODIMP NativeViewAccessibilityWin::accSelect(
    637     LONG flagsSelect, VARIANT var_id) {
    638   return E_NOTIMPL;
    639 }
    640 
    641 STDMETHODIMP NativeViewAccessibilityWin::get_accHelp(
    642     VARIANT var_id, BSTR* help) {
    643   if (help)
    644     *help = NULL;
    645   return E_NOTIMPL;
    646 }
    647 
    648 STDMETHODIMP NativeViewAccessibilityWin::get_accHelpTopic(
    649     BSTR* help_file, VARIANT var_id, LONG* topic_id) {
    650   if (help_file) {
    651     *help_file = NULL;
    652   }
    653   if (topic_id) {
    654     *topic_id = static_cast<LONG>(-1);
    655   }
    656   return E_NOTIMPL;
    657 }
    658 
    659 STDMETHODIMP NativeViewAccessibilityWin::put_accName(
    660     VARIANT var_id, BSTR put_name) {
    661   // Deprecated.
    662   return E_NOTIMPL;
    663 }
    664 
    665 //
    666 // IAccessible2
    667 //
    668 
    669 STDMETHODIMP NativeViewAccessibilityWin::role(LONG* role) {
    670   if (!view_)
    671     return E_FAIL;
    672 
    673   if (!role)
    674     return E_INVALIDARG;
    675 
    676   ui::AccessibleViewState state;
    677   view_->GetAccessibleState(&state);
    678   *role = MSAARole(state.role);
    679   return S_OK;
    680 }
    681 
    682 STDMETHODIMP NativeViewAccessibilityWin::get_states(AccessibleStates* states) {
    683   // This returns IAccessible2 states, which supplement MSAA states.
    684   // See also the MSAA interface get_accState.
    685 
    686   if (!view_)
    687     return E_FAIL;
    688 
    689   if (!states)
    690     return E_INVALIDARG;
    691 
    692   ui::AccessibleViewState state;
    693   view_->GetAccessibleState(&state);
    694 
    695   // There are only a couple of states we need to support
    696   // in IAccessible2. If any more are added, we may want to
    697   // add a helper function like MSAAState.
    698   *states = IA2_STATE_OPAQUE;
    699   if (state.state & AccessibilityTypes::STATE_EDITABLE)
    700     *states |= IA2_STATE_EDITABLE;
    701 
    702   return S_OK;
    703 }
    704 
    705 STDMETHODIMP NativeViewAccessibilityWin::get_uniqueID(LONG* unique_id) {
    706   if (!view_)
    707     return E_FAIL;
    708 
    709   if (!unique_id)
    710     return E_INVALIDARG;
    711 
    712   *unique_id = unique_id_;
    713   return S_OK;
    714 }
    715 
    716 STDMETHODIMP NativeViewAccessibilityWin::get_windowHandle(HWND* window_handle) {
    717   if (!view_)
    718     return E_FAIL;
    719 
    720   if (!window_handle)
    721     return E_INVALIDARG;
    722 
    723   *window_handle = HWNDForView(view_);
    724   return *window_handle ? S_OK : S_FALSE;
    725 }
    726 
    727 //
    728 // IAccessibleText
    729 //
    730 
    731 STDMETHODIMP NativeViewAccessibilityWin::get_nCharacters(LONG* n_characters) {
    732   if (!view_)
    733     return E_FAIL;
    734 
    735   if (!n_characters)
    736     return E_INVALIDARG;
    737 
    738   string16 text = TextForIAccessibleText();
    739   *n_characters = static_cast<LONG>(text.size());
    740   return S_OK;
    741 }
    742 
    743 STDMETHODIMP NativeViewAccessibilityWin::get_caretOffset(LONG* offset) {
    744   if (!view_)
    745     return E_FAIL;
    746 
    747   if (!offset)
    748     return E_INVALIDARG;
    749 
    750   ui::AccessibleViewState state;
    751   view_->GetAccessibleState(&state);
    752   *offset = static_cast<LONG>(state.selection_end);
    753   return S_OK;
    754 }
    755 
    756 STDMETHODIMP NativeViewAccessibilityWin::get_nSelections(LONG* n_selections) {
    757   if (!view_)
    758     return E_FAIL;
    759 
    760   if (!n_selections)
    761     return E_INVALIDARG;
    762 
    763   ui::AccessibleViewState state;
    764   view_->GetAccessibleState(&state);
    765   if (state.selection_start != state.selection_end)
    766     *n_selections = 1;
    767   else
    768     *n_selections = 0;
    769   return S_OK;
    770 }
    771 
    772 STDMETHODIMP NativeViewAccessibilityWin::get_selection(LONG selection_index,
    773                                                       LONG* start_offset,
    774                                                       LONG* end_offset) {
    775   if (!view_)
    776     return E_FAIL;
    777 
    778   if (!start_offset || !end_offset || selection_index != 0)
    779     return E_INVALIDARG;
    780 
    781   ui::AccessibleViewState state;
    782   view_->GetAccessibleState(&state);
    783   *start_offset = static_cast<LONG>(state.selection_start);
    784   *end_offset = static_cast<LONG>(state.selection_end);
    785   return S_OK;
    786 }
    787 
    788 STDMETHODIMP NativeViewAccessibilityWin::get_text(LONG start_offset,
    789                                                   LONG end_offset,
    790                                                   BSTR* text) {
    791   if (!view_)
    792     return E_FAIL;
    793 
    794   ui::AccessibleViewState state;
    795   view_->GetAccessibleState(&state);
    796   string16 text_str = TextForIAccessibleText();
    797   LONG len = static_cast<LONG>(text_str.size());
    798 
    799   if (start_offset == IA2_TEXT_OFFSET_LENGTH) {
    800     start_offset = len;
    801   } else if (start_offset == IA2_TEXT_OFFSET_CARET) {
    802     start_offset = static_cast<LONG>(state.selection_end);
    803   }
    804   if (end_offset == IA2_TEXT_OFFSET_LENGTH) {
    805     end_offset = static_cast<LONG>(text_str.size());
    806   } else if (end_offset == IA2_TEXT_OFFSET_CARET) {
    807     end_offset = static_cast<LONG>(state.selection_end);
    808   }
    809 
    810   // The spec allows the arguments to be reversed.
    811   if (start_offset > end_offset) {
    812     LONG tmp = start_offset;
    813     start_offset = end_offset;
    814     end_offset = tmp;
    815   }
    816 
    817   // The spec does not allow the start or end offsets to be out or range;
    818   // we must return an error if so.
    819   if (start_offset < 0)
    820     return E_INVALIDARG;
    821   if (end_offset > len)
    822     return E_INVALIDARG;
    823 
    824   string16 substr = text_str.substr(start_offset, end_offset - start_offset);
    825   if (substr.empty())
    826     return S_FALSE;
    827 
    828   *text = SysAllocString(substr.c_str());
    829   DCHECK(*text);
    830   return S_OK;
    831 }
    832 
    833 STDMETHODIMP NativeViewAccessibilityWin::get_textAtOffset(
    834     LONG offset,
    835     enum IA2TextBoundaryType boundary_type,
    836     LONG* start_offset, LONG* end_offset,
    837     BSTR* text) {
    838   if (!start_offset || !end_offset || !text)
    839     return E_INVALIDARG;
    840 
    841   // The IAccessible2 spec says we don't have to implement the "sentence"
    842   // boundary type, we can just let the screenreader handle it.
    843   if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) {
    844     *start_offset = 0;
    845     *end_offset = 0;
    846     *text = NULL;
    847     return S_FALSE;
    848   }
    849 
    850   const string16& text_str = TextForIAccessibleText();
    851 
    852   *start_offset = FindBoundary(
    853       text_str, boundary_type, offset, ui::BACKWARDS_DIRECTION);
    854   *end_offset = FindBoundary(
    855       text_str, boundary_type, offset, ui::FORWARDS_DIRECTION);
    856   return get_text(*start_offset, *end_offset, text);
    857 }
    858 
    859 STDMETHODIMP NativeViewAccessibilityWin::get_textBeforeOffset(
    860     LONG offset,
    861     enum IA2TextBoundaryType boundary_type,
    862     LONG* start_offset, LONG* end_offset,
    863     BSTR* text) {
    864   if (!start_offset || !end_offset || !text)
    865     return E_INVALIDARG;
    866 
    867   // The IAccessible2 spec says we don't have to implement the "sentence"
    868   // boundary type, we can just let the screenreader handle it.
    869   if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) {
    870     *start_offset = 0;
    871     *end_offset = 0;
    872     *text = NULL;
    873     return S_FALSE;
    874   }
    875 
    876   const string16& text_str = TextForIAccessibleText();
    877 
    878   *start_offset = FindBoundary(
    879       text_str, boundary_type, offset, ui::BACKWARDS_DIRECTION);
    880   *end_offset = offset;
    881   return get_text(*start_offset, *end_offset, text);
    882 }
    883 
    884 STDMETHODIMP NativeViewAccessibilityWin::get_textAfterOffset(
    885     LONG offset,
    886     enum IA2TextBoundaryType boundary_type,
    887     LONG* start_offset, LONG* end_offset,
    888     BSTR* text) {
    889   if (!start_offset || !end_offset || !text)
    890     return E_INVALIDARG;
    891 
    892   // The IAccessible2 spec says we don't have to implement the "sentence"
    893   // boundary type, we can just let the screenreader handle it.
    894   if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) {
    895     *start_offset = 0;
    896     *end_offset = 0;
    897     *text = NULL;
    898     return S_FALSE;
    899   }
    900 
    901   const string16& text_str = TextForIAccessibleText();
    902 
    903   *start_offset = offset;
    904   *end_offset = FindBoundary(
    905       text_str, boundary_type, offset, ui::FORWARDS_DIRECTION);
    906   return get_text(*start_offset, *end_offset, text);
    907 }
    908 
    909 STDMETHODIMP NativeViewAccessibilityWin::get_offsetAtPoint(
    910     LONG x, LONG y, enum IA2CoordinateType coord_type, LONG* offset) {
    911   if (!view_)
    912     return E_FAIL;
    913 
    914   if (!offset)
    915     return E_INVALIDARG;
    916 
    917   // We don't support this method, but we have to return something
    918   // rather than E_NOTIMPL or screen readers will complain.
    919   *offset = 0;
    920   return S_OK;
    921 }
    922 
    923 //
    924 // IServiceProvider methods.
    925 //
    926 
    927 STDMETHODIMP NativeViewAccessibilityWin::QueryService(
    928     REFGUID guidService, REFIID riid, void** object) {
    929   if (!view_)
    930     return E_FAIL;
    931 
    932   if (guidService == IID_IAccessible ||
    933       guidService == IID_IAccessible2 ||
    934       guidService == IID_IAccessibleText)  {
    935     return QueryInterface(riid, object);
    936   }
    937 
    938   // We only support the IAccessibleEx interface on Windows 8 and above. This
    939   // is needed for the On screen Keyboard to show up in metro mode, when the
    940   // user taps an editable region in the window.
    941   // All methods in the IAccessibleEx interface are unimplemented.
    942   if (riid == IID_IAccessibleEx &&
    943       base::win::GetVersion() >= base::win::VERSION_WIN8) {
    944     return QueryInterface(riid, object);
    945   }
    946 
    947   *object = NULL;
    948   return E_FAIL;
    949 }
    950 
    951 STDMETHODIMP NativeViewAccessibilityWin::GetPatternProvider(
    952     PATTERNID id, IUnknown** provider) {
    953   DVLOG(1) << "In Function: "
    954            << __FUNCTION__
    955            << " for pattern id: "
    956            << id;
    957   if (id == UIA_ValuePatternId || id == UIA_TextPatternId) {
    958     ui::AccessibleViewState state;
    959     view_->GetAccessibleState(&state);
    960     long role = MSAARole(state.role);
    961 
    962     if (role == ROLE_SYSTEM_TEXT) {
    963       DVLOG(1) << "Returning UIA text provider";
    964       base::win::UIATextProvider::CreateTextProvider(true, provider);
    965       return S_OK;
    966     }
    967   }
    968   return E_NOTIMPL;
    969 }
    970 
    971 STDMETHODIMP NativeViewAccessibilityWin::GetPropertyValue(PROPERTYID id,
    972                                                           VARIANT* ret) {
    973   DVLOG(1) << "In Function: "
    974            << __FUNCTION__
    975            << " for property id: "
    976            << id;
    977   if (id == UIA_ControlTypePropertyId) {
    978     ui::AccessibleViewState state;
    979     view_->GetAccessibleState(&state);
    980     long role = MSAARole(state.role);
    981     if (role == ROLE_SYSTEM_TEXT) {
    982       V_VT(ret) = VT_I4;
    983       ret->lVal = UIA_EditControlTypeId;
    984       DVLOG(1) << "Returning Edit control type";
    985     } else {
    986       DVLOG(1) << "Returning empty control type";
    987       V_VT(ret) = VT_EMPTY;
    988     }
    989   } else {
    990     V_VT(ret) = VT_EMPTY;
    991   }
    992   return S_OK;
    993 }
    994 
    995 //
    996 // Static methods.
    997 //
    998 
    999 void NativeViewAccessibility::RegisterWebView(AccessibleWebView* web_view) {
   1000   AccessibleWebViewRegistry::GetInstance()->RegisterWebView(web_view);
   1001 }
   1002 
   1003 void NativeViewAccessibility::UnregisterWebView(AccessibleWebView* web_view) {
   1004   AccessibleWebViewRegistry::GetInstance()->UnregisterWebView(web_view);
   1005 }
   1006 
   1007 int32 NativeViewAccessibilityWin::MSAAEvent(AccessibilityTypes::Event event) {
   1008   switch (event) {
   1009     case AccessibilityTypes::EVENT_ALERT:
   1010       return EVENT_SYSTEM_ALERT;
   1011     case AccessibilityTypes::EVENT_FOCUS:
   1012       return EVENT_OBJECT_FOCUS;
   1013     case AccessibilityTypes::EVENT_MENUSTART:
   1014       return EVENT_SYSTEM_MENUSTART;
   1015     case AccessibilityTypes::EVENT_MENUEND:
   1016       return EVENT_SYSTEM_MENUEND;
   1017     case AccessibilityTypes::EVENT_MENUPOPUPSTART:
   1018       return EVENT_SYSTEM_MENUPOPUPSTART;
   1019     case AccessibilityTypes::EVENT_MENUPOPUPEND:
   1020       return EVENT_SYSTEM_MENUPOPUPEND;
   1021     case AccessibilityTypes::EVENT_NAME_CHANGED:
   1022       return EVENT_OBJECT_NAMECHANGE;
   1023     case AccessibilityTypes::EVENT_TEXT_CHANGED:
   1024       return EVENT_OBJECT_VALUECHANGE;
   1025     case AccessibilityTypes::EVENT_SELECTION_CHANGED:
   1026       return IA2_EVENT_TEXT_CARET_MOVED;
   1027     case AccessibilityTypes::EVENT_VALUE_CHANGED:
   1028       return EVENT_OBJECT_VALUECHANGE;
   1029     default:
   1030       // Not supported or invalid event.
   1031       NOTREACHED();
   1032       return -1;
   1033   }
   1034 }
   1035 
   1036 int32 NativeViewAccessibilityWin::MSAARole(AccessibilityTypes::Role role) {
   1037   switch (role) {
   1038     case AccessibilityTypes::ROLE_ALERT:
   1039       return ROLE_SYSTEM_ALERT;
   1040     case AccessibilityTypes::ROLE_APPLICATION:
   1041       return ROLE_SYSTEM_APPLICATION;
   1042     case AccessibilityTypes::ROLE_BUTTONDROPDOWN:
   1043       return ROLE_SYSTEM_BUTTONDROPDOWN;
   1044     case AccessibilityTypes::ROLE_BUTTONMENU:
   1045       return ROLE_SYSTEM_BUTTONMENU;
   1046     case AccessibilityTypes::ROLE_CHECKBUTTON:
   1047       return ROLE_SYSTEM_CHECKBUTTON;
   1048     case AccessibilityTypes::ROLE_COMBOBOX:
   1049       return ROLE_SYSTEM_COMBOBOX;
   1050     case AccessibilityTypes::ROLE_DIALOG:
   1051       return ROLE_SYSTEM_DIALOG;
   1052     case AccessibilityTypes::ROLE_GRAPHIC:
   1053       return ROLE_SYSTEM_GRAPHIC;
   1054     case AccessibilityTypes::ROLE_GROUPING:
   1055       return ROLE_SYSTEM_GROUPING;
   1056     case AccessibilityTypes::ROLE_LINK:
   1057       return ROLE_SYSTEM_LINK;
   1058     case AccessibilityTypes::ROLE_LOCATION_BAR:
   1059       return ROLE_SYSTEM_GROUPING;
   1060     case AccessibilityTypes::ROLE_MENUBAR:
   1061       return ROLE_SYSTEM_MENUBAR;
   1062     case AccessibilityTypes::ROLE_MENUITEM:
   1063       return ROLE_SYSTEM_MENUITEM;
   1064     case AccessibilityTypes::ROLE_MENUPOPUP:
   1065       return ROLE_SYSTEM_MENUPOPUP;
   1066     case AccessibilityTypes::ROLE_OUTLINE:
   1067       return ROLE_SYSTEM_OUTLINE;
   1068     case AccessibilityTypes::ROLE_OUTLINEITEM:
   1069       return ROLE_SYSTEM_OUTLINEITEM;
   1070     case AccessibilityTypes::ROLE_PAGETAB:
   1071       return ROLE_SYSTEM_PAGETAB;
   1072     case AccessibilityTypes::ROLE_PAGETABLIST:
   1073       return ROLE_SYSTEM_PAGETABLIST;
   1074     case AccessibilityTypes::ROLE_PANE:
   1075       return ROLE_SYSTEM_PANE;
   1076     case AccessibilityTypes::ROLE_PROGRESSBAR:
   1077       return ROLE_SYSTEM_PROGRESSBAR;
   1078     case AccessibilityTypes::ROLE_PUSHBUTTON:
   1079       return ROLE_SYSTEM_PUSHBUTTON;
   1080     case AccessibilityTypes::ROLE_RADIOBUTTON:
   1081       return ROLE_SYSTEM_RADIOBUTTON;
   1082     case AccessibilityTypes::ROLE_SCROLLBAR:
   1083       return ROLE_SYSTEM_SCROLLBAR;
   1084     case AccessibilityTypes::ROLE_SEPARATOR:
   1085       return ROLE_SYSTEM_SEPARATOR;
   1086     case AccessibilityTypes::ROLE_SLIDER:
   1087       return ROLE_SYSTEM_SLIDER;
   1088     case AccessibilityTypes::ROLE_STATICTEXT:
   1089       return ROLE_SYSTEM_STATICTEXT;
   1090     case AccessibilityTypes::ROLE_TEXT:
   1091       return ROLE_SYSTEM_TEXT;
   1092     case AccessibilityTypes::ROLE_TITLEBAR:
   1093       return ROLE_SYSTEM_TITLEBAR;
   1094     case AccessibilityTypes::ROLE_TOOLBAR:
   1095       return ROLE_SYSTEM_TOOLBAR;
   1096     case AccessibilityTypes::ROLE_WINDOW:
   1097       return ROLE_SYSTEM_WINDOW;
   1098     case AccessibilityTypes::ROLE_CLIENT:
   1099     default:
   1100       // This is the default role for MSAA.
   1101       return ROLE_SYSTEM_CLIENT;
   1102   }
   1103 }
   1104 
   1105 int32 NativeViewAccessibilityWin::MSAAState(AccessibilityTypes::State state) {
   1106   // This maps MSAA states for get_accState(). See also the IAccessible2
   1107   // interface get_states().
   1108 
   1109   int32 msaa_state = 0;
   1110   if (state & AccessibilityTypes::STATE_CHECKED)
   1111     msaa_state |= STATE_SYSTEM_CHECKED;
   1112   if (state & AccessibilityTypes::STATE_COLLAPSED)
   1113     msaa_state |= STATE_SYSTEM_COLLAPSED;
   1114   if (state & AccessibilityTypes::STATE_DEFAULT)
   1115     msaa_state |= STATE_SYSTEM_DEFAULT;
   1116   if (state & AccessibilityTypes::STATE_EXPANDED)
   1117     msaa_state |= STATE_SYSTEM_EXPANDED;
   1118   if (state & AccessibilityTypes::STATE_HASPOPUP)
   1119     msaa_state |= STATE_SYSTEM_HASPOPUP;
   1120   if (state & AccessibilityTypes::STATE_HOTTRACKED)
   1121     msaa_state |= STATE_SYSTEM_HOTTRACKED;
   1122   if (state & AccessibilityTypes::STATE_INVISIBLE)
   1123     msaa_state |= STATE_SYSTEM_INVISIBLE;
   1124   if (state & AccessibilityTypes::STATE_LINKED)
   1125     msaa_state |= STATE_SYSTEM_LINKED;
   1126   if (state & AccessibilityTypes::STATE_OFFSCREEN)
   1127     msaa_state |= STATE_SYSTEM_OFFSCREEN;
   1128   if (state & AccessibilityTypes::STATE_PRESSED)
   1129     msaa_state |= STATE_SYSTEM_PRESSED;
   1130   if (state & AccessibilityTypes::STATE_PROTECTED)
   1131     msaa_state |= STATE_SYSTEM_PROTECTED;
   1132   if (state & AccessibilityTypes::STATE_READONLY)
   1133     msaa_state |= STATE_SYSTEM_READONLY;
   1134   if (state & AccessibilityTypes::STATE_SELECTED)
   1135     msaa_state |= STATE_SYSTEM_SELECTED;
   1136   if (state & AccessibilityTypes::STATE_FOCUSED)
   1137     msaa_state |= STATE_SYSTEM_FOCUSED;
   1138   if (state & AccessibilityTypes::STATE_UNAVAILABLE)
   1139     msaa_state |= STATE_SYSTEM_UNAVAILABLE;
   1140   return msaa_state;
   1141 }
   1142 
   1143 //
   1144 // Private methods.
   1145 //
   1146 
   1147 bool NativeViewAccessibilityWin::IsNavDirNext(int nav_dir) const {
   1148   return (nav_dir == NAVDIR_RIGHT ||
   1149           nav_dir == NAVDIR_DOWN ||
   1150           nav_dir == NAVDIR_NEXT);
   1151 }
   1152 
   1153 bool NativeViewAccessibilityWin::IsValidNav(
   1154     int nav_dir, int start_id, int lower_bound, int upper_bound) const {
   1155   if (IsNavDirNext(nav_dir)) {
   1156     if ((start_id + 1) > upper_bound) {
   1157       return false;
   1158     }
   1159   } else {
   1160     if ((start_id - 1) <= lower_bound) {
   1161       return false;
   1162     }
   1163   }
   1164   return true;
   1165 }
   1166 
   1167 bool NativeViewAccessibilityWin::IsValidId(const VARIANT& child) const {
   1168   // View accessibility returns an IAccessible for each view so we only support
   1169   // the CHILDID_SELF id.
   1170   return (VT_I4 == child.vt) && (CHILDID_SELF == child.lVal);
   1171 }
   1172 
   1173 void NativeViewAccessibilityWin::SetState(
   1174     VARIANT* msaa_state, View* view) {
   1175   // Ensure the output param is initialized to zero.
   1176   msaa_state->lVal = 0;
   1177 
   1178   // Default state; all views can have accessibility focus.
   1179   msaa_state->lVal |= STATE_SYSTEM_FOCUSABLE;
   1180 
   1181   if (!view)
   1182     return;
   1183 
   1184   if (!view->enabled())
   1185     msaa_state->lVal |= STATE_SYSTEM_UNAVAILABLE;
   1186   if (!view->visible())
   1187     msaa_state->lVal |= STATE_SYSTEM_INVISIBLE;
   1188   if (!strcmp(view->GetClassName(), CustomButton::kViewClassName)) {
   1189     CustomButton* button = static_cast<CustomButton*>(view);
   1190     if (button->IsHotTracked())
   1191       msaa_state->lVal |= STATE_SYSTEM_HOTTRACKED;
   1192   }
   1193   if (view->HasFocus())
   1194     msaa_state->lVal |= STATE_SYSTEM_FOCUSED;
   1195 
   1196   // Add on any view-specific states.
   1197   ui::AccessibleViewState view_state;
   1198   view->GetAccessibleState(&view_state);
   1199   msaa_state->lVal |= MSAAState(view_state.state);
   1200 }
   1201 
   1202 string16 NativeViewAccessibilityWin::TextForIAccessibleText() {
   1203   ui::AccessibleViewState state;
   1204   view_->GetAccessibleState(&state);
   1205   if (state.role == AccessibilityTypes::ROLE_TEXT)
   1206     return state.value;
   1207   else
   1208     return state.name;
   1209 }
   1210 
   1211 void NativeViewAccessibilityWin::HandleSpecialTextOffset(
   1212     const string16& text, LONG* offset) {
   1213   if (*offset == IA2_TEXT_OFFSET_LENGTH) {
   1214     *offset = static_cast<LONG>(text.size());
   1215   } else if (*offset == IA2_TEXT_OFFSET_CARET) {
   1216     get_caretOffset(offset);
   1217   }
   1218 }
   1219 
   1220 ui::TextBoundaryType NativeViewAccessibilityWin::IA2TextBoundaryToTextBoundary(
   1221     IA2TextBoundaryType ia2_boundary) {
   1222   switch(ia2_boundary) {
   1223     case IA2_TEXT_BOUNDARY_CHAR: return ui::CHAR_BOUNDARY;
   1224     case IA2_TEXT_BOUNDARY_WORD: return ui::WORD_BOUNDARY;
   1225     case IA2_TEXT_BOUNDARY_LINE: return ui::LINE_BOUNDARY;
   1226     case IA2_TEXT_BOUNDARY_SENTENCE: return ui::SENTENCE_BOUNDARY;
   1227     case IA2_TEXT_BOUNDARY_PARAGRAPH: return ui::PARAGRAPH_BOUNDARY;
   1228     case IA2_TEXT_BOUNDARY_ALL: return ui::ALL_BOUNDARY;
   1229     default:
   1230       NOTREACHED();
   1231       return ui::CHAR_BOUNDARY;
   1232   }
   1233 }
   1234 
   1235 LONG NativeViewAccessibilityWin::FindBoundary(
   1236     const string16& text,
   1237     IA2TextBoundaryType ia2_boundary,
   1238     LONG start_offset,
   1239     ui::TextBoundaryDirection direction) {
   1240   HandleSpecialTextOffset(text, &start_offset);
   1241   ui::TextBoundaryType boundary = IA2TextBoundaryToTextBoundary(ia2_boundary);
   1242   std::vector<int32> line_breaks;
   1243   return ui::FindAccessibleTextBoundary(
   1244       text, line_breaks, boundary, start_offset, direction);
   1245 }
   1246 
   1247 }  // namespace views
   1248