Home | History | Annotate | Download | only in accessibility
      1 // Copyright (c) 2011 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 "chrome/browser/accessibility/browser_accessibility_manager.h"
      6 
      7 #include "base/logging.h"
      8 #include "chrome/browser/accessibility/browser_accessibility.h"
      9 #include "content/common/view_messages.h"
     10 
     11 using webkit_glue::WebAccessibility;
     12 
     13 BrowserAccessibility* BrowserAccessibilityFactory::Create() {
     14   return BrowserAccessibility::Create();
     15 }
     16 
     17 // Start child IDs at -1 and decrement each time, because clients use
     18 // child IDs of 1, 2, 3, ... to access the children of an object by
     19 // index, so we use negative IDs to clearly distinguish between indices
     20 // and unique IDs.
     21 // static
     22 int32 BrowserAccessibilityManager::next_child_id_ = -1;
     23 
     24 #if defined(OS_LINUX)
     25 // There's no OS-specific implementation of BrowserAccessibilityManager
     26 // on Linux, so just instantiate the base class.
     27 // static
     28 BrowserAccessibilityManager* BrowserAccessibilityManager::Create(
     29     gfx::NativeView parent_view,
     30     const WebAccessibility& src,
     31     BrowserAccessibilityDelegate* delegate,
     32     BrowserAccessibilityFactory* factory) {
     33   return new BrowserAccessibilityManager(
     34       parent_view, src, delegate, factory);
     35 }
     36 #endif
     37 
     38 BrowserAccessibilityManager::BrowserAccessibilityManager(
     39     gfx::NativeView parent_view,
     40     const WebAccessibility& src,
     41     BrowserAccessibilityDelegate* delegate,
     42     BrowserAccessibilityFactory* factory)
     43     : parent_view_(parent_view),
     44       delegate_(delegate),
     45       factory_(factory),
     46       focus_(NULL) {
     47   root_ = CreateAccessibilityTree(NULL, src, 0);
     48   if (!focus_)
     49     SetFocus(root_, false);
     50 }
     51 
     52 // static
     53 int32 BrowserAccessibilityManager::GetNextChildID() {
     54   // Get the next child ID, and wrap around when we get near the end
     55   // of a 32-bit integer range. It's okay to wrap around; we just want
     56   // to avoid it as long as possible because clients may cache the ID of
     57   // an object for a while to determine if they've seen it before.
     58   next_child_id_--;
     59   if (next_child_id_ == -2000000000)
     60     next_child_id_ = -1;
     61 
     62   return next_child_id_;
     63 }
     64 
     65 BrowserAccessibilityManager::~BrowserAccessibilityManager() {
     66   // Clients could still hold references to some nodes of the tree, so
     67   // calling InternalReleaseReference will make sure that as many nodes
     68   // as possible are released now, and remaining nodes are marked as
     69   // inactive so that calls to any methods on them will fail gracefully.
     70   focus_->InternalReleaseReference(false);
     71   root_->InternalReleaseReference(true);
     72 }
     73 
     74 BrowserAccessibility* BrowserAccessibilityManager::GetRoot() {
     75   return root_;
     76 }
     77 
     78 BrowserAccessibility* BrowserAccessibilityManager::GetFromChildID(
     79     int32 child_id) {
     80   base::hash_map<int32, BrowserAccessibility*>::iterator iter =
     81       child_id_map_.find(child_id);
     82   if (iter != child_id_map_.end()) {
     83     return iter->second;
     84   } else {
     85     return NULL;
     86   }
     87 }
     88 
     89 void BrowserAccessibilityManager::Remove(int32 child_id, int32 renderer_id) {
     90   child_id_map_.erase(child_id);
     91   renderer_id_to_child_id_map_.erase(renderer_id);
     92 }
     93 
     94 void BrowserAccessibilityManager::OnAccessibilityNotifications(
     95     const std::vector<ViewHostMsg_AccessibilityNotification_Params>& params) {
     96   for (uint32 index = 0; index < params.size(); index++) {
     97     const ViewHostMsg_AccessibilityNotification_Params& param = params[index];
     98 
     99     switch (param.notification_type) {
    100       case ViewHostMsg_AccessibilityNotification_Type::
    101           NOTIFICATION_TYPE_CHECK_STATE_CHANGED:
    102         OnAccessibilityObjectStateChange(param.acc_obj);
    103         break;
    104       case ViewHostMsg_AccessibilityNotification_Type::
    105           NOTIFICATION_TYPE_CHILDREN_CHANGED:
    106         OnAccessibilityObjectChildrenChange(param.acc_obj);
    107         break;
    108       case ViewHostMsg_AccessibilityNotification_Type::
    109           NOTIFICATION_TYPE_FOCUS_CHANGED:
    110         OnAccessibilityObjectFocusChange(param.acc_obj);
    111         break;
    112       case ViewHostMsg_AccessibilityNotification_Type::
    113           NOTIFICATION_TYPE_LOAD_COMPLETE:
    114         OnAccessibilityObjectLoadComplete(param.acc_obj);
    115         break;
    116       case ViewHostMsg_AccessibilityNotification_Type::
    117           NOTIFICATION_TYPE_VALUE_CHANGED:
    118         OnAccessibilityObjectValueChange(param.acc_obj);
    119         break;
    120       case ViewHostMsg_AccessibilityNotification_Type::
    121           NOTIFICATION_TYPE_SELECTED_TEXT_CHANGED:
    122         OnAccessibilityObjectTextChange(param.acc_obj);
    123         break;
    124       default:
    125         DCHECK(0);
    126         break;
    127     }
    128   }
    129 }
    130 
    131 void BrowserAccessibilityManager::OnAccessibilityObjectStateChange(
    132     const WebAccessibility& acc_obj) {
    133   BrowserAccessibility* new_browser_acc = UpdateNode(acc_obj, false);
    134   if (!new_browser_acc)
    135     return;
    136 
    137   NotifyAccessibilityEvent(
    138       ViewHostMsg_AccessibilityNotification_Type::
    139           NOTIFICATION_TYPE_CHECK_STATE_CHANGED,
    140       new_browser_acc);
    141 }
    142 
    143 void BrowserAccessibilityManager::OnAccessibilityObjectChildrenChange(
    144     const WebAccessibility& acc_obj) {
    145   BrowserAccessibility* new_browser_acc = UpdateNode(acc_obj, true);
    146   if (!new_browser_acc)
    147     return;
    148 
    149   NotifyAccessibilityEvent(
    150       ViewHostMsg_AccessibilityNotification_Type::
    151           NOTIFICATION_TYPE_CHILDREN_CHANGED,
    152       new_browser_acc);
    153 }
    154 
    155 void BrowserAccessibilityManager::OnAccessibilityObjectFocusChange(
    156   const WebAccessibility& acc_obj) {
    157   BrowserAccessibility* new_browser_acc = UpdateNode(acc_obj, false);
    158   if (!new_browser_acc)
    159     return;
    160 
    161   SetFocus(new_browser_acc, false);
    162   if (delegate_ && delegate_->HasFocus()) {
    163     GotFocus();
    164   } else if (!delegate_) {
    165     // Mac currently does not have a BrowserAccessibilityDelegate.
    166     NotifyAccessibilityEvent(
    167         ViewHostMsg_AccessibilityNotification_Type::
    168         NOTIFICATION_TYPE_FOCUS_CHANGED,
    169         focus_);
    170   }
    171 }
    172 
    173 void BrowserAccessibilityManager::OnAccessibilityObjectLoadComplete(
    174   const WebAccessibility& acc_obj) {
    175   SetFocus(NULL, false);
    176   root_->InternalReleaseReference(true);
    177 
    178   root_ = CreateAccessibilityTree(NULL, acc_obj, 0);
    179   if (!focus_)
    180     SetFocus(root_, false);
    181 
    182   NotifyAccessibilityEvent(
    183       ViewHostMsg_AccessibilityNotification_Type::
    184           NOTIFICATION_TYPE_LOAD_COMPLETE,
    185       root_);
    186   if (delegate_ && delegate_->HasFocus())
    187     GotFocus();
    188 }
    189 
    190 void BrowserAccessibilityManager::OnAccessibilityObjectValueChange(
    191     const WebAccessibility& acc_obj) {
    192   BrowserAccessibility* new_browser_acc = UpdateNode(acc_obj, false);
    193   if (!new_browser_acc)
    194     return;
    195 
    196   NotifyAccessibilityEvent(
    197       ViewHostMsg_AccessibilityNotification_Type::
    198           NOTIFICATION_TYPE_VALUE_CHANGED,
    199       new_browser_acc);
    200 }
    201 
    202 void BrowserAccessibilityManager::OnAccessibilityObjectTextChange(
    203     const WebAccessibility& acc_obj) {
    204   BrowserAccessibility* new_browser_acc = UpdateNode(acc_obj, false);
    205   if (!new_browser_acc)
    206     return;
    207 
    208   NotifyAccessibilityEvent(
    209       ViewHostMsg_AccessibilityNotification_Type::
    210           NOTIFICATION_TYPE_SELECTED_TEXT_CHANGED,
    211       new_browser_acc);
    212 }
    213 
    214 void BrowserAccessibilityManager::GotFocus() {
    215   // TODO(ctguil): Remove when tree update logic handles focus changes.
    216   if (!focus_)
    217     return;
    218 
    219   NotifyAccessibilityEvent(
    220       ViewHostMsg_AccessibilityNotification_Type::
    221           NOTIFICATION_TYPE_FOCUS_CHANGED,
    222       focus_);
    223 }
    224 
    225 gfx::NativeView BrowserAccessibilityManager::GetParentView() {
    226   return parent_view_;
    227 }
    228 
    229 BrowserAccessibility* BrowserAccessibilityManager::GetFocus(
    230     BrowserAccessibility* root) {
    231   if (focus_ && (!root || focus_->IsDescendantOf(root)))
    232     return focus_;
    233 
    234   return NULL;
    235 }
    236 
    237 void BrowserAccessibilityManager::SetFocus(
    238     BrowserAccessibility* node, bool notify) {
    239   if (focus_)
    240     focus_->InternalReleaseReference(false);
    241   focus_ = node;
    242   if (focus_)
    243     focus_->InternalAddReference();
    244 
    245   if (notify && node && delegate_)
    246     delegate_->SetAccessibilityFocus(node->renderer_id());
    247 }
    248 
    249 void BrowserAccessibilityManager::DoDefaultAction(
    250     const BrowserAccessibility& node) {
    251   if (delegate_)
    252     delegate_->AccessibilityDoDefaultAction(node.renderer_id());
    253 }
    254 
    255 gfx::Rect BrowserAccessibilityManager::GetViewBounds() {
    256   if (delegate_)
    257     return delegate_->GetViewBounds();
    258   return gfx::Rect();
    259 }
    260 
    261 BrowserAccessibility* BrowserAccessibilityManager::UpdateNode(
    262     const WebAccessibility& src,
    263     bool include_children) {
    264   base::hash_map<int32, int32>::iterator iter =
    265       renderer_id_to_child_id_map_.find(src.id);
    266   if (iter == renderer_id_to_child_id_map_.end())
    267     return NULL;
    268 
    269   int32 child_id = iter->second;
    270   BrowserAccessibility* current = GetFromChildID(child_id);
    271   if (!current)
    272     return NULL;
    273 
    274   if (!include_children) {
    275     DCHECK_EQ(0U, src.children.size());
    276     current->Initialize(
    277         this,
    278         current->parent(),
    279         current->child_id(),
    280         current->index_in_parent(),
    281         src);
    282     return current;
    283   }
    284 
    285   // Detach all of the nodes in the old tree and get a single flat vector
    286   // of all node pointers.
    287   std::vector<BrowserAccessibility*> old_tree_nodes;
    288   current->DetachTree(&old_tree_nodes);
    289 
    290   // Build a new tree, reusing old nodes if possible. Each node that's
    291   // reused will have its reference count incremented by one.
    292   current = CreateAccessibilityTree(NULL, src, -1);
    293 
    294   // Decrement the reference count of all nodes in the old tree, which will
    295   // delete any nodes no longer needed.
    296   for (int i = 0; i < static_cast<int>(old_tree_nodes.size()); i++)
    297     old_tree_nodes[i]->InternalReleaseReference(false);
    298 
    299   DCHECK(focus_);
    300   if (!focus_->instance_active())
    301     SetFocus(root_, false);
    302 
    303   return current;
    304 }
    305 
    306 BrowserAccessibility* BrowserAccessibilityManager::CreateAccessibilityTree(
    307     BrowserAccessibility* parent,
    308     const WebAccessibility& src,
    309     int index_in_parent) {
    310   BrowserAccessibility* instance = NULL;
    311   int32 child_id = 0;
    312   base::hash_map<int32, int32>::iterator iter =
    313       renderer_id_to_child_id_map_.find(src.id);
    314 
    315   // If a BrowserAccessibility instance for this ID already exists, add a
    316   // new reference to it and retrieve its children vector.
    317   if (iter != renderer_id_to_child_id_map_.end()) {
    318     child_id = iter->second;
    319     instance = GetFromChildID(child_id);
    320   }
    321 
    322   // If the node has changed roles, don't reuse a BrowserAccessibility
    323   // object, that could confuse a screen reader.
    324   if (instance && instance->role() != src.role)
    325     instance = NULL;
    326 
    327   if (instance) {
    328     // If we're reusing a node, it should already be detached from a parent
    329     // and any children. If not, that means we have a serious bug somewhere,
    330     // like the same child is reachable from two places in the same tree.
    331     DCHECK_EQ(static_cast<BrowserAccessibility*>(NULL), instance->parent());
    332     DCHECK_EQ(0U, instance->child_count());
    333 
    334     // If we're reusing a node, update its parent and increment its
    335     // reference count.
    336     instance->UpdateParent(parent, index_in_parent);
    337     instance->InternalAddReference();
    338   } else {
    339     // Otherwise, create a new instance.
    340     instance = factory_->Create();
    341     child_id = GetNextChildID();
    342   }
    343 
    344   instance->Initialize(this, parent, child_id, index_in_parent, src);
    345   child_id_map_[child_id] = instance;
    346   renderer_id_to_child_id_map_[src.id] = child_id;
    347   if ((src.state >> WebAccessibility::STATE_FOCUSED) & 1)
    348     SetFocus(instance, false);
    349   for (int i = 0; i < static_cast<int>(src.children.size()); ++i) {
    350     BrowserAccessibility* child = CreateAccessibilityTree(
    351         instance, src.children[i], i);
    352     instance->AddChild(child);
    353   }
    354 
    355   return instance;
    356 }
    357