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 = blink::WebAXRoleRootWebArea;
     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(blink::WebAXEventFocus, 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(blink::WebAXEventFocus, 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::OnAccessibilityEvents(
    128     const std::vector<AccessibilityHostMsg_EventParams>& params) {
    129   bool should_send_initial_focus = false;
    130 
    131   // Process all changes to the accessibility tree first.
    132   for (uint32 index = 0; index < params.size(); index++) {
    133     const AccessibilityHostMsg_EventParams& param = params[index];
    134     if (!UpdateNodes(param.nodes))
    135       return;
    136 
    137     // Set initial focus when a page is loaded.
    138     blink::WebAXEvent event_type = param.event_type;
    139     if (event_type == blink::WebAXEventLoadComplete) {
    140       if (!focus_) {
    141         SetFocus(root_, false);
    142         should_send_initial_focus = true;
    143       }
    144     }
    145   }
    146 
    147   if (should_send_initial_focus &&
    148       (!delegate_ || delegate_->HasFocus())) {
    149     NotifyAccessibilityEvent(blink::WebAXEventFocus, focus_);
    150   }
    151 
    152   // Now iterate over the events again and fire the events.
    153   for (uint32 index = 0; index < params.size(); index++) {
    154     const AccessibilityHostMsg_EventParams& param = params[index];
    155 
    156     // Find the node corresponding to the id that's the target of the
    157     // event (which may not be the root of the update tree).
    158     BrowserAccessibility* node = GetFromRendererID(param.id);
    159     if (!node)
    160       continue;
    161 
    162     blink::WebAXEvent event_type = param.event_type;
    163     if (event_type == blink::WebAXEventFocus ||
    164         event_type == blink::WebAXEventBlur) {
    165       SetFocus(node, false);
    166 
    167       if (osk_state_ != OSK_DISALLOWED_BECAUSE_TAB_HIDDEN &&
    168           osk_state_ != OSK_DISALLOWED_BECAUSE_TAB_JUST_APPEARED)
    169         osk_state_ = OSK_ALLOWED;
    170 
    171       // Don't send a native focus event if the window itself doesn't
    172       // have focus.
    173       if (delegate_ && !delegate_->HasFocus())
    174         continue;
    175     }
    176 
    177     // Send the event event to the operating system.
    178     NotifyAccessibilityEvent(event_type, node);
    179   }
    180 }
    181 
    182 BrowserAccessibility* BrowserAccessibilityManager::GetFocus(
    183     BrowserAccessibility* root) {
    184   if (focus_ && (!root || focus_->IsDescendantOf(root)))
    185     return focus_;
    186 
    187   return NULL;
    188 }
    189 
    190 void BrowserAccessibilityManager::SetFocus(
    191     BrowserAccessibility* node, bool notify) {
    192   if (focus_ != node)
    193     focus_ = node;
    194 
    195   if (notify && node && delegate_)
    196     delegate_->SetAccessibilityFocus(node->renderer_id());
    197 }
    198 
    199 void BrowserAccessibilityManager::SetRoot(BrowserAccessibility* node) {
    200   root_ = node;
    201   NotifyRootChanged();
    202 }
    203 
    204 void BrowserAccessibilityManager::DoDefaultAction(
    205     const BrowserAccessibility& node) {
    206   if (delegate_)
    207     delegate_->AccessibilityDoDefaultAction(node.renderer_id());
    208 }
    209 
    210 void BrowserAccessibilityManager::ScrollToMakeVisible(
    211     const BrowserAccessibility& node, gfx::Rect subfocus) {
    212   if (delegate_) {
    213     delegate_->AccessibilityScrollToMakeVisible(node.renderer_id(), subfocus);
    214   }
    215 }
    216 
    217 void BrowserAccessibilityManager::ScrollToPoint(
    218     const BrowserAccessibility& node, gfx::Point point) {
    219   if (delegate_) {
    220     delegate_->AccessibilityScrollToPoint(node.renderer_id(), point);
    221   }
    222 }
    223 
    224 void BrowserAccessibilityManager::SetTextSelection(
    225     const BrowserAccessibility& node, int start_offset, int end_offset) {
    226   if (delegate_) {
    227     delegate_->AccessibilitySetTextSelection(
    228         node.renderer_id(), start_offset, end_offset);
    229   }
    230 }
    231 
    232 gfx::Rect BrowserAccessibilityManager::GetViewBounds() {
    233   if (delegate_)
    234     return delegate_->GetViewBounds();
    235   return gfx::Rect();
    236 }
    237 
    238 void BrowserAccessibilityManager::UpdateNodesForTesting(
    239     const AccessibilityNodeData& node1,
    240     const AccessibilityNodeData& node2 /* = AccessibilityNodeData() */,
    241     const AccessibilityNodeData& node3 /* = AccessibilityNodeData() */,
    242     const AccessibilityNodeData& node4 /* = AccessibilityNodeData() */,
    243     const AccessibilityNodeData& node5 /* = AccessibilityNodeData() */,
    244     const AccessibilityNodeData& node6 /* = AccessibilityNodeData() */,
    245     const AccessibilityNodeData& node7 /* = AccessibilityNodeData() */) {
    246   std::vector<AccessibilityNodeData> nodes;
    247   nodes.push_back(node1);
    248   if (node2.id != AccessibilityNodeData().id)
    249     nodes.push_back(node2);
    250   if (node3.id != AccessibilityNodeData().id)
    251     nodes.push_back(node3);
    252   if (node4.id != AccessibilityNodeData().id)
    253     nodes.push_back(node4);
    254   if (node5.id != AccessibilityNodeData().id)
    255     nodes.push_back(node5);
    256   if (node6.id != AccessibilityNodeData().id)
    257     nodes.push_back(node6);
    258   if (node7.id != AccessibilityNodeData().id)
    259     nodes.push_back(node7);
    260   UpdateNodes(nodes);
    261 }
    262 
    263 bool BrowserAccessibilityManager::UpdateNodes(
    264     const std::vector<AccessibilityNodeData>& nodes) {
    265   bool success = true;
    266 
    267   // First, update all of the nodes in the tree.
    268   for (size_t i = 0; i < nodes.size() && success; i++) {
    269     if (!UpdateNode(nodes[i]))
    270       success = false;
    271   }
    272 
    273   // In a second pass, call PostInitialize on each one - this must
    274   // be called after all of each node's children are initialized too.
    275   for (size_t i = 0; i < nodes.size() && success; i++) {
    276     // Note: it's not a bug for nodes[i].id to not be found in the tree.
    277     // Consider this example:
    278     // Before:
    279     // A
    280     //   B
    281     //     C
    282     //   D
    283     //     E
    284     //       F
    285     // After:
    286     // A
    287     //   B
    288     //     C
    289     //       F
    290     //   D
    291     // In this example, F is being reparented. The renderer scans the tree
    292     // in order. If can't update "C" to add "F" as a child, when "F" is still
    293     // a child of "E". So it first updates "E", to remove "F" as a child.
    294     // Later, it ends up deleting "E". So when we get here, "E" was updated as
    295     // part of this sequence but it no longer exists in the final tree, so
    296     // there's nothing to postinitialize.
    297     BrowserAccessibility* instance = GetFromRendererID(nodes[i].id);
    298     if (instance)
    299       instance->PostInitialize();
    300   }
    301 
    302   if (!success) {
    303     // A bad accessibility tree could lead to memory corruption.
    304     // Ask the delegate to crash the renderer, or if not available,
    305     // crash the browser.
    306     if (delegate_)
    307       delegate_->FatalAccessibilityTreeError();
    308     else
    309       CHECK(false);
    310   }
    311 
    312   return success;
    313 }
    314 
    315 BrowserAccessibility* BrowserAccessibilityManager::CreateNode(
    316     BrowserAccessibility* parent,
    317     int32 renderer_id,
    318     int32 index_in_parent) {
    319   BrowserAccessibility* node = factory_->Create();
    320   node->InitializeTreeStructure(
    321       this, parent, renderer_id, index_in_parent);
    322   AddNodeToMap(node);
    323   return node;
    324 }
    325 
    326 void BrowserAccessibilityManager::AddNodeToMap(BrowserAccessibility* node) {
    327   renderer_id_map_[node->renderer_id()] = node;
    328 }
    329 
    330 bool BrowserAccessibilityManager::UpdateNode(const AccessibilityNodeData& src) {
    331   // This method updates one node in the tree based on serialized data
    332   // received from the renderer.
    333 
    334   // Create a set of child ids in |src| for fast lookup. If a duplicate id is
    335   // found, exit now with a fatal error before changing anything else.
    336   std::set<int32> new_child_ids;
    337   for (size_t i = 0; i < src.child_ids.size(); ++i) {
    338     if (new_child_ids.find(src.child_ids[i]) != new_child_ids.end())
    339       return false;
    340     new_child_ids.insert(src.child_ids[i]);
    341   }
    342 
    343   // Look up the node by id. If it's not found, then either the root
    344   // of the tree is being swapped, or we're out of sync with the renderer
    345   // and this is a serious error.
    346   BrowserAccessibility* instance = GetFromRendererID(src.id);
    347   if (!instance) {
    348     if (src.role != blink::WebAXRoleRootWebArea)
    349       return false;
    350     instance = CreateNode(NULL, src.id, 0);
    351   }
    352 
    353   // TODO(dmazzoni): avoid a linear scan here.
    354   for (size_t i = 0; i < src.bool_attributes.size(); i++) {
    355     if (src.bool_attributes[i].first ==
    356         AccessibilityNodeData::ATTR_UPDATE_LOCATION_ONLY) {
    357       instance->SetLocation(src.location);
    358       return true;
    359     }
    360   }
    361 
    362   // Update all of the node-specific data, like its role, state, name, etc.
    363   instance->InitializeData(src);
    364 
    365   //
    366   // Update the children in three steps:
    367   //
    368   // 1. Iterate over the old children and delete nodes that are no longer
    369   //    in the tree.
    370   // 2. Build up a vector of new children, reusing children that haven't
    371   //    changed (but may have been reordered) and adding new empty
    372   //    objects for new children.
    373   // 3. Swap in the new children vector for the old one.
    374 
    375   // Delete any previous children of this instance that are no longer
    376   // children first. We make a deletion-only pass first to prevent a
    377   // node that's being reparented from being the child of both its old
    378   // parent and new parent, which could lead to a double-free.
    379   // If a node is reparented, the renderer will always send us a fresh
    380   // copy of the node.
    381   const std::vector<BrowserAccessibility*>& old_children = instance->children();
    382   for (size_t i = 0; i < old_children.size(); ++i) {
    383     int old_id = old_children[i]->renderer_id();
    384     if (new_child_ids.find(old_id) == new_child_ids.end())
    385       old_children[i]->Destroy();
    386   }
    387 
    388   // Now build a vector of new children, reusing objects that were already
    389   // children of this node before.
    390   std::vector<BrowserAccessibility*> new_children;
    391   bool success = true;
    392   for (size_t i = 0; i < src.child_ids.size(); i++) {
    393     int32 child_renderer_id = src.child_ids[i];
    394     int32 index_in_parent = static_cast<int32>(i);
    395     BrowserAccessibility* child = GetFromRendererID(child_renderer_id);
    396     if (child) {
    397       if (child->parent() != instance) {
    398         // This is a serious error - nodes should never be reparented.
    399         // If this case occurs, continue so this node isn't left in an
    400         // inconsistent state, but return failure at the end.
    401         success = false;
    402         continue;
    403       }
    404       child->UpdateParent(instance, index_in_parent);
    405     } else {
    406       child = CreateNode(instance, child_renderer_id, index_in_parent);
    407     }
    408     new_children.push_back(child);
    409   }
    410 
    411   // Finally, swap in the new children vector for the old.
    412   instance->SwapChildren(new_children);
    413 
    414   // Handle the case where this node is the new root of the tree.
    415   if (src.role == blink::WebAXRoleRootWebArea &&
    416       (!root_ || root_->renderer_id() != src.id)) {
    417     if (root_)
    418       root_->Destroy();
    419     if (focus_ == root_)
    420       SetFocus(instance, false);
    421     SetRoot(instance);
    422   }
    423 
    424   // Keep track of what node is focused.
    425   if (src.role != blink::WebAXRoleRootWebArea &&
    426       src.role != blink::WebAXRoleWebArea &&
    427       (src.state >> blink::WebAXStateFocused & 1)) {
    428     SetFocus(instance, false);
    429   }
    430   return success;
    431 }
    432 
    433 }  // namespace content
    434