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 "content/browser/accessibility/browser_accessibility_manager.h"
      6 
      7 #include "base/logging.h"
      8 #include "content/browser/accessibility/browser_accessibility.h"
      9 #include "content/common/accessibility_messages.h"
     10 
     11 namespace content {
     12 
     13 BrowserAccessibility* BrowserAccessibilityFactory::Create() {
     14   return BrowserAccessibility::Create();
     15 }
     16 
     17 #if !defined(OS_MACOSX) && \
     18     !defined(OS_WIN) && \
     19     !defined(TOOLKIT_GTK) && \
     20     !defined(OS_ANDROID) \
     21 // We have subclassess of BrowserAccessibilityManager on Mac, Linux/GTK,
     22 // and Win. For any other platform, instantiate the base class.
     23 // static
     24 BrowserAccessibilityManager* BrowserAccessibilityManager::Create(
     25     const AccessibilityNodeData& src,
     26     BrowserAccessibilityDelegate* delegate,
     27     BrowserAccessibilityFactory* factory) {
     28   return new BrowserAccessibilityManager(src, delegate, factory);
     29 }
     30 #endif
     31 
     32 BrowserAccessibilityManager::BrowserAccessibilityManager(
     33     BrowserAccessibilityDelegate* delegate,
     34     BrowserAccessibilityFactory* factory)
     35     : delegate_(delegate),
     36       factory_(factory),
     37       root_(NULL),
     38       focus_(NULL),
     39       osk_state_(OSK_ALLOWED) {
     40 }
     41 
     42 BrowserAccessibilityManager::BrowserAccessibilityManager(
     43     const AccessibilityNodeData& src,
     44     BrowserAccessibilityDelegate* delegate,
     45     BrowserAccessibilityFactory* factory)
     46     : delegate_(delegate),
     47       factory_(factory),
     48       root_(NULL),
     49       focus_(NULL),
     50       osk_state_(OSK_ALLOWED) {
     51   Initialize(src);
     52 }
     53 
     54 BrowserAccessibilityManager::~BrowserAccessibilityManager() {
     55   if (root_)
     56     root_->Destroy();
     57 }
     58 
     59 void BrowserAccessibilityManager::Initialize(const AccessibilityNodeData src) {
     60   std::vector<AccessibilityNodeData> nodes;
     61   nodes.push_back(src);
     62   if (!UpdateNodes(nodes))
     63     return;
     64   if (!focus_)
     65     SetFocus(root_, false);
     66 }
     67 
     68 // static
     69 AccessibilityNodeData BrowserAccessibilityManager::GetEmptyDocument() {
     70   AccessibilityNodeData empty_document;
     71   empty_document.id = 0;
     72   empty_document.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
     73   return empty_document;
     74 }
     75 
     76 BrowserAccessibility* BrowserAccessibilityManager::GetRoot() {
     77   return root_;
     78 }
     79 
     80 BrowserAccessibility* BrowserAccessibilityManager::GetFromRendererID(
     81     int32 renderer_id) {
     82   base::hash_map<int32, BrowserAccessibility*>::iterator iter =
     83       renderer_id_map_.find(renderer_id);
     84   if (iter != renderer_id_map_.end())
     85     return iter->second;
     86   return NULL;
     87 }
     88 
     89 void BrowserAccessibilityManager::GotFocus(bool touch_event_context) {
     90   if (!touch_event_context)
     91     osk_state_ = OSK_DISALLOWED_BECAUSE_TAB_JUST_APPEARED;
     92 
     93   if (!focus_)
     94     return;
     95 
     96   NotifyAccessibilityEvent(AccessibilityNotificationFocusChanged, focus_);
     97 }
     98 
     99 void BrowserAccessibilityManager::WasHidden() {
    100   osk_state_ = OSK_DISALLOWED_BECAUSE_TAB_HIDDEN;
    101 }
    102 
    103 void BrowserAccessibilityManager::GotMouseDown() {
    104   osk_state_ = OSK_ALLOWED_WITHIN_FOCUSED_OBJECT;
    105   NotifyAccessibilityEvent(AccessibilityNotificationFocusChanged, focus_);
    106 }
    107 
    108 bool BrowserAccessibilityManager::IsOSKAllowed(const gfx::Rect& bounds) {
    109   if (!delegate_ || !delegate_->HasFocus())
    110     return false;
    111 
    112   gfx::Point touch_point = delegate_->GetLastTouchEventLocation();
    113   return bounds.Contains(touch_point);
    114 }
    115 
    116 bool BrowserAccessibilityManager::UseRootScrollOffsetsWhenComputingBounds() {
    117   return true;
    118 }
    119 
    120 void BrowserAccessibilityManager::RemoveNode(BrowserAccessibility* node) {
    121   if (node == focus_)
    122     SetFocus(root_, false);
    123   int renderer_id = node->renderer_id();
    124   renderer_id_map_.erase(renderer_id);
    125 }
    126 
    127 void BrowserAccessibilityManager::OnAccessibilityNotifications(
    128     const std::vector<AccessibilityHostMsg_NotificationParams>& params) {
    129   for (uint32 index = 0; index < params.size(); index++) {
    130     const AccessibilityHostMsg_NotificationParams& param = params[index];
    131 
    132     // Update nodes that changed.
    133     if (!UpdateNodes(param.nodes))
    134       return;
    135 
    136     // Find the node corresponding to the id that's the target of the
    137     // notification (which may not be the root of the update tree).
    138     BrowserAccessibility* node = GetFromRendererID(param.id);
    139     if (!node)
    140       continue;
    141 
    142     int notification_type = param.notification_type;
    143     if (notification_type == AccessibilityNotificationFocusChanged ||
    144         notification_type == AccessibilityNotificationBlur) {
    145       SetFocus(node, false);
    146 
    147       if (osk_state_ != OSK_DISALLOWED_BECAUSE_TAB_HIDDEN &&
    148           osk_state_ != OSK_DISALLOWED_BECAUSE_TAB_JUST_APPEARED)
    149         osk_state_ = OSK_ALLOWED;
    150 
    151       // Don't send a native focus event if the window itself doesn't
    152       // have focus.
    153       if (delegate_ && !delegate_->HasFocus())
    154         continue;
    155     }
    156 
    157     // Send the notification event to the operating system.
    158     NotifyAccessibilityEvent(notification_type, node);
    159 
    160     // Set initial focus when a page is loaded.
    161     if (notification_type == AccessibilityNotificationLoadComplete) {
    162       if (!focus_)
    163         SetFocus(root_, false);
    164       if (!delegate_ || delegate_->HasFocus())
    165         NotifyAccessibilityEvent(AccessibilityNotificationFocusChanged, focus_);
    166     }
    167   }
    168 }
    169 
    170 BrowserAccessibility* BrowserAccessibilityManager::GetFocus(
    171     BrowserAccessibility* root) {
    172   if (focus_ && (!root || focus_->IsDescendantOf(root)))
    173     return focus_;
    174 
    175   return NULL;
    176 }
    177 
    178 void BrowserAccessibilityManager::SetFocus(
    179     BrowserAccessibility* node, bool notify) {
    180   if (focus_ != node)
    181     focus_ = node;
    182 
    183   if (notify && node && delegate_)
    184     delegate_->SetAccessibilityFocus(node->renderer_id());
    185 }
    186 
    187 void BrowserAccessibilityManager::SetRoot(BrowserAccessibility* node) {
    188   root_ = node;
    189   NotifyRootChanged();
    190 }
    191 
    192 void BrowserAccessibilityManager::DoDefaultAction(
    193     const BrowserAccessibility& node) {
    194   if (delegate_)
    195     delegate_->AccessibilityDoDefaultAction(node.renderer_id());
    196 }
    197 
    198 void BrowserAccessibilityManager::ScrollToMakeVisible(
    199     const BrowserAccessibility& node, gfx::Rect subfocus) {
    200   if (delegate_) {
    201     delegate_->AccessibilityScrollToMakeVisible(node.renderer_id(), subfocus);
    202   }
    203 }
    204 
    205 void BrowserAccessibilityManager::ScrollToPoint(
    206     const BrowserAccessibility& node, gfx::Point point) {
    207   if (delegate_) {
    208     delegate_->AccessibilityScrollToPoint(node.renderer_id(), point);
    209   }
    210 }
    211 
    212 void BrowserAccessibilityManager::SetTextSelection(
    213     const BrowserAccessibility& node, int start_offset, int end_offset) {
    214   if (delegate_) {
    215     delegate_->AccessibilitySetTextSelection(
    216         node.renderer_id(), start_offset, end_offset);
    217   }
    218 }
    219 
    220 gfx::Rect BrowserAccessibilityManager::GetViewBounds() {
    221   if (delegate_)
    222     return delegate_->GetViewBounds();
    223   return gfx::Rect();
    224 }
    225 
    226 void BrowserAccessibilityManager::UpdateNodesForTesting(
    227     const AccessibilityNodeData& node1,
    228     const AccessibilityNodeData& node2 /* = AccessibilityNodeData() */,
    229     const AccessibilityNodeData& node3 /* = AccessibilityNodeData() */,
    230     const AccessibilityNodeData& node4 /* = AccessibilityNodeData() */,
    231     const AccessibilityNodeData& node5 /* = AccessibilityNodeData() */,
    232     const AccessibilityNodeData& node6 /* = AccessibilityNodeData() */,
    233     const AccessibilityNodeData& node7 /* = AccessibilityNodeData() */) {
    234   std::vector<AccessibilityNodeData> nodes;
    235   nodes.push_back(node1);
    236   if (node2.id != AccessibilityNodeData().id)
    237     nodes.push_back(node2);
    238   if (node3.id != AccessibilityNodeData().id)
    239     nodes.push_back(node3);
    240   if (node4.id != AccessibilityNodeData().id)
    241     nodes.push_back(node4);
    242   if (node5.id != AccessibilityNodeData().id)
    243     nodes.push_back(node5);
    244   if (node6.id != AccessibilityNodeData().id)
    245     nodes.push_back(node6);
    246   if (node7.id != AccessibilityNodeData().id)
    247     nodes.push_back(node7);
    248   UpdateNodes(nodes);
    249 }
    250 
    251 bool BrowserAccessibilityManager::UpdateNodes(
    252     const std::vector<AccessibilityNodeData>& nodes) {
    253   bool success = true;
    254 
    255   // First, update all of the nodes in the tree.
    256   for (size_t i = 0; i < nodes.size() && success; i++) {
    257     if (!UpdateNode(nodes[i]))
    258       success = false;
    259   }
    260 
    261   // In a second pass, call PostInitialize on each one - this must
    262   // be called after all of each node's children are initialized too.
    263   for (size_t i = 0; i < nodes.size() && success; i++) {
    264     // Note: it's not a bug for nodes[i].id to not be found in the tree.
    265     // Consider this example:
    266     // Before:
    267     // A
    268     //   B
    269     //     C
    270     //   D
    271     //     E
    272     //       F
    273     // After:
    274     // A
    275     //   B
    276     //     C
    277     //       F
    278     //   D
    279     // In this example, F is being reparented. The renderer scans the tree
    280     // in order. If can't update "C" to add "F" as a child, when "F" is still
    281     // a child of "E". So it first updates "E", to remove "F" as a child.
    282     // Later, it ends up deleting "E". So when we get here, "E" was updated as
    283     // part of this sequence but it no longer exists in the final tree, so
    284     // there's nothing to postinitialize.
    285     BrowserAccessibility* instance = GetFromRendererID(nodes[i].id);
    286     if (instance)
    287       instance->PostInitialize();
    288   }
    289 
    290   if (!success) {
    291     // A bad accessibility tree could lead to memory corruption.
    292     // Ask the delegate to crash the renderer, or if not available,
    293     // crash the browser.
    294     if (delegate_)
    295       delegate_->FatalAccessibilityTreeError();
    296     else
    297       CHECK(false);
    298   }
    299 
    300   return success;
    301 }
    302 
    303 BrowserAccessibility* BrowserAccessibilityManager::CreateNode(
    304     BrowserAccessibility* parent,
    305     int32 renderer_id,
    306     int32 index_in_parent) {
    307   BrowserAccessibility* node = factory_->Create();
    308   node->InitializeTreeStructure(
    309       this, parent, renderer_id, index_in_parent);
    310   AddNodeToMap(node);
    311   return node;
    312 }
    313 
    314 void BrowserAccessibilityManager::AddNodeToMap(BrowserAccessibility* node) {
    315   renderer_id_map_[node->renderer_id()] = node;
    316 }
    317 
    318 bool BrowserAccessibilityManager::UpdateNode(const AccessibilityNodeData& src) {
    319   // This method updates one node in the tree based on serialized data
    320   // received from the renderer.
    321 
    322   // Create a set of child ids in |src| for fast lookup. If a duplicate id is
    323   // found, exit now with a fatal error before changing anything else.
    324   std::set<int32> new_child_ids;
    325   for (size_t i = 0; i < src.child_ids.size(); ++i) {
    326     if (new_child_ids.find(src.child_ids[i]) != new_child_ids.end())
    327       return false;
    328     new_child_ids.insert(src.child_ids[i]);
    329   }
    330 
    331   // Look up the node by id. If it's not found, then either the root
    332   // of the tree is being swapped, or we're out of sync with the renderer
    333   // and this is a serious error.
    334   BrowserAccessibility* instance = GetFromRendererID(src.id);
    335   if (!instance) {
    336     if (src.role != AccessibilityNodeData::ROLE_ROOT_WEB_AREA)
    337       return false;
    338     instance = CreateNode(NULL, src.id, 0);
    339   }
    340 
    341   if (src.bool_attributes.find(
    342           AccessibilityNodeData::ATTR_UPDATE_LOCATION_ONLY) !=
    343               src.bool_attributes.end()) {
    344     instance->SetLocation(src.location);
    345     return true;
    346   }
    347 
    348   // Update all of the node-specific data, like its role, state, name, etc.
    349   instance->InitializeData(src);
    350 
    351   //
    352   // Update the children in three steps:
    353   //
    354   // 1. Iterate over the old children and delete nodes that are no longer
    355   //    in the tree.
    356   // 2. Build up a vector of new children, reusing children that haven't
    357   //    changed (but may have been reordered) and adding new empty
    358   //    objects for new children.
    359   // 3. Swap in the new children vector for the old one.
    360 
    361   // Delete any previous children of this instance that are no longer
    362   // children first. We make a deletion-only pass first to prevent a
    363   // node that's being reparented from being the child of both its old
    364   // parent and new parent, which could lead to a double-free.
    365   // If a node is reparented, the renderer will always send us a fresh
    366   // copy of the node.
    367   const std::vector<BrowserAccessibility*>& old_children = instance->children();
    368   for (size_t i = 0; i < old_children.size(); ++i) {
    369     int old_id = old_children[i]->renderer_id();
    370     if (new_child_ids.find(old_id) == new_child_ids.end())
    371       old_children[i]->Destroy();
    372   }
    373 
    374   // Now build a vector of new children, reusing objects that were already
    375   // children of this node before.
    376   std::vector<BrowserAccessibility*> new_children;
    377   bool success = true;
    378   for (size_t i = 0; i < src.child_ids.size(); i++) {
    379     int32 child_renderer_id = src.child_ids[i];
    380     int32 index_in_parent = static_cast<int32>(i);
    381     BrowserAccessibility* child = GetFromRendererID(child_renderer_id);
    382     if (child) {
    383       if (child->parent() != instance) {
    384         // This is a serious error - nodes should never be reparented.
    385         // If this case occurs, continue so this node isn't left in an
    386         // inconsistent state, but return failure at the end.
    387         success = false;
    388         continue;
    389       }
    390       child->UpdateParent(instance, index_in_parent);
    391     } else {
    392       child = CreateNode(instance, child_renderer_id, index_in_parent);
    393     }
    394     new_children.push_back(child);
    395   }
    396 
    397   // Finally, swap in the new children vector for the old.
    398   instance->SwapChildren(new_children);
    399 
    400   // Handle the case where this node is the new root of the tree.
    401   if (src.role == AccessibilityNodeData::ROLE_ROOT_WEB_AREA &&
    402       (!root_ || root_->renderer_id() != src.id)) {
    403     if (root_)
    404       root_->Destroy();
    405     if (focus_ == root_)
    406       SetFocus(instance, false);
    407     SetRoot(instance);
    408   }
    409 
    410   // Keep track of what node is focused.
    411   if (src.role != AccessibilityNodeData::ROLE_ROOT_WEB_AREA &&
    412       src.role != AccessibilityNodeData::ROLE_WEB_AREA &&
    413       (src.state >> AccessibilityNodeData::STATE_FOCUSED & 1)) {
    414     SetFocus(instance, false);
    415   }
    416   return success;
    417 }
    418 
    419 }  // namespace content
    420