Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / content / browser / accessibility / browser_accessibility_manager.cc
index 2161fb1..ce0564f 100644 (file)
 
 namespace content {
 
+ui::AXTreeUpdate MakeAXTreeUpdate(
+    const ui::AXNodeData& node1,
+    const ui::AXNodeData& node2 /* = ui::AXNodeData() */,
+    const ui::AXNodeData& node3 /* = ui::AXNodeData() */,
+    const ui::AXNodeData& node4 /* = ui::AXNodeData() */,
+    const ui::AXNodeData& node5 /* = ui::AXNodeData() */,
+    const ui::AXNodeData& node6 /* = ui::AXNodeData() */,
+    const ui::AXNodeData& node7 /* = ui::AXNodeData() */,
+    const ui::AXNodeData& node8 /* = ui::AXNodeData() */,
+    const ui::AXNodeData& node9 /* = ui::AXNodeData() */) {
+  CR_DEFINE_STATIC_LOCAL(ui::AXNodeData, empty_data, ());
+  int32 no_id = empty_data.id;
+
+  ui::AXTreeUpdate update;
+  update.nodes.push_back(node1);
+  if (node2.id != no_id)
+    update.nodes.push_back(node2);
+  if (node3.id != no_id)
+    update.nodes.push_back(node3);
+  if (node4.id != no_id)
+    update.nodes.push_back(node4);
+  if (node5.id != no_id)
+    update.nodes.push_back(node5);
+  if (node6.id != no_id)
+    update.nodes.push_back(node6);
+  if (node7.id != no_id)
+    update.nodes.push_back(node7);
+  if (node8.id != no_id)
+    update.nodes.push_back(node8);
+  if (node9.id != no_id)
+    update.nodes.push_back(node9);
+  return update;
+}
+
 BrowserAccessibility* BrowserAccessibilityFactory::Create() {
   return BrowserAccessibility::Create();
 }
 
 #if !defined(OS_MACOSX) && \
     !defined(OS_WIN) && \
-    !defined(TOOLKIT_GTK) && \
     !defined(OS_ANDROID) \
-// We have subclassess of BrowserAccessibilityManager on Mac, Linux/GTK,
-// and Win. For any other platform, instantiate the base class.
+// We have subclassess of BrowserAccessibilityManager on Mac, and Win. For any
+// other platform, instantiate the base class.
 // static
 BrowserAccessibilityManager* BrowserAccessibilityManager::Create(
-    const AccessibilityNodeData& src,
+    const ui::AXTreeUpdate& initial_tree,
     BrowserAccessibilityDelegate* delegate,
     BrowserAccessibilityFactory* factory) {
-  return new BrowserAccessibilityManager(src, delegate, factory);
+  return new BrowserAccessibilityManager(initial_tree, delegate, factory);
 }
 #endif
 
@@ -34,114 +67,132 @@ BrowserAccessibilityManager::BrowserAccessibilityManager(
     BrowserAccessibilityFactory* factory)
     : delegate_(delegate),
       factory_(factory),
-      root_(NULL),
+      tree_(new ui::AXTree()),
       focus_(NULL),
       osk_state_(OSK_ALLOWED) {
+  tree_->SetDelegate(this);
 }
 
 BrowserAccessibilityManager::BrowserAccessibilityManager(
-    const AccessibilityNodeData& src,
+    const ui::AXTreeUpdate& initial_tree,
     BrowserAccessibilityDelegate* delegate,
     BrowserAccessibilityFactory* factory)
     : delegate_(delegate),
       factory_(factory),
-      root_(NULL),
+      tree_(new ui::AXTree()),
       focus_(NULL),
       osk_state_(OSK_ALLOWED) {
-  Initialize(src);
+  tree_->SetDelegate(this);
+  Initialize(initial_tree);
 }
 
 BrowserAccessibilityManager::~BrowserAccessibilityManager() {
-  if (root_)
-    root_->Destroy();
+  tree_.reset(NULL);
 }
 
-void BrowserAccessibilityManager::Initialize(const AccessibilityNodeData src) {
-  std::vector<AccessibilityNodeData> nodes;
-  nodes.push_back(src);
-  if (!UpdateNodes(nodes))
-    return;
+void BrowserAccessibilityManager::Initialize(
+    const ui::AXTreeUpdate& initial_tree) {
+  if (!tree_->Unserialize(initial_tree)) {
+    if (delegate_) {
+      LOG(ERROR) << tree_->error();
+      delegate_->AccessibilityFatalError();
+    } else {
+      LOG(FATAL) << tree_->error();
+    }
+  }
+
   if (!focus_)
-    SetFocus(root_, false);
+    SetFocus(tree_->GetRoot(), false);
 }
 
 // static
-AccessibilityNodeData BrowserAccessibilityManager::GetEmptyDocument() {
-  AccessibilityNodeData empty_document;
+ui::AXTreeUpdate BrowserAccessibilityManager::GetEmptyDocument() {
+  ui::AXNodeData empty_document;
   empty_document.id = 0;
-  empty_document.role = WebKit::WebAXRoleRootWebArea;
-  return empty_document;
+  empty_document.role = ui::AX_ROLE_ROOT_WEB_AREA;
+  ui::AXTreeUpdate update;
+  update.nodes.push_back(empty_document);
+  return update;
 }
 
 BrowserAccessibility* BrowserAccessibilityManager::GetRoot() {
-  return root_;
+  return GetFromAXNode(tree_->GetRoot());
+}
+
+BrowserAccessibility* BrowserAccessibilityManager::GetFromAXNode(
+    ui::AXNode* node) {
+  return GetFromID(node->id());
 }
 
-BrowserAccessibility* BrowserAccessibilityManager::GetFromRendererID(
-    int32 renderer_id) {
+BrowserAccessibility* BrowserAccessibilityManager::GetFromID(int32 id) {
   base::hash_map<int32, BrowserAccessibility*>::iterator iter =
-      renderer_id_map_.find(renderer_id);
-  if (iter != renderer_id_map_.end())
+      id_wrapper_map_.find(id);
+  if (iter != id_wrapper_map_.end())
     return iter->second;
   return NULL;
 }
 
-void BrowserAccessibilityManager::GotFocus(bool touch_event_context) {
-  if (!touch_event_context)
-    osk_state_ = OSK_DISALLOWED_BECAUSE_TAB_JUST_APPEARED;
-
-  if (!focus_)
-    return;
-
-  NotifyAccessibilityEvent(WebKit::WebAXEventFocus, focus_);
+void BrowserAccessibilityManager::OnWindowFocused() {
+  if (focus_)
+    NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetFromAXNode(focus_));
 }
 
-void BrowserAccessibilityManager::WasHidden() {
-  osk_state_ = OSK_DISALLOWED_BECAUSE_TAB_HIDDEN;
+void BrowserAccessibilityManager::OnWindowBlurred() {
+  if (focus_)
+    NotifyAccessibilityEvent(ui::AX_EVENT_BLUR, GetFromAXNode(focus_));
 }
 
 void BrowserAccessibilityManager::GotMouseDown() {
   osk_state_ = OSK_ALLOWED_WITHIN_FOCUSED_OBJECT;
-  NotifyAccessibilityEvent(WebKit::WebAXEventFocus, focus_);
-}
-
-bool BrowserAccessibilityManager::IsOSKAllowed(const gfx::Rect& bounds) {
-  if (!delegate_ || !delegate_->HasFocus())
-    return false;
-
-  gfx::Point touch_point = delegate_->GetLastTouchEventLocation();
-  return bounds.Contains(touch_point);
+  NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetFromAXNode(focus_));
 }
 
 bool BrowserAccessibilityManager::UseRootScrollOffsetsWhenComputingBounds() {
   return true;
 }
 
-void BrowserAccessibilityManager::RemoveNode(BrowserAccessibility* node) {
-  if (node == focus_)
-    SetFocus(root_, false);
-  int renderer_id = node->renderer_id();
-  renderer_id_map_.erase(renderer_id);
-}
-
 void BrowserAccessibilityManager::OnAccessibilityEvents(
     const std::vector<AccessibilityHostMsg_EventParams>& params) {
+  bool should_send_initial_focus = false;
+
+  // Process all changes to the accessibility tree first.
   for (uint32 index = 0; index < params.size(); index++) {
     const AccessibilityHostMsg_EventParams& param = params[index];
-
-    // Update nodes that changed.
-    if (!UpdateNodes(param.nodes))
+    if (!tree_->Unserialize(param.update)) {
+      if (delegate_) {
+        LOG(ERROR) << tree_->error();
+        delegate_->AccessibilityFatalError();
+      } else {
+        CHECK(false) << tree_->error();
+      }
       return;
+    }
+
+    // Set focus to the root if it's not anywhere else.
+    if (!focus_) {
+      SetFocus(tree_->GetRoot(), false);
+      should_send_initial_focus = true;
+    }
+  }
+
+  if (should_send_initial_focus &&
+      (!delegate_ || delegate_->AccessibilityViewHasFocus())) {
+    NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetFromAXNode(focus_));
+  }
+
+  // Now iterate over the events again and fire the events.
+  for (uint32 index = 0; index < params.size(); index++) {
+    const AccessibilityHostMsg_EventParams& param = params[index];
 
     // Find the node corresponding to the id that's the target of the
     // event (which may not be the root of the update tree).
-    BrowserAccessibility* node = GetFromRendererID(param.id);
+    ui::AXNode* node = tree_->GetFromId(param.id);
     if (!node)
       continue;
 
-    WebKit::WebAXEvent event_type = param.event_type;
-    if (event_type == WebKit::WebAXEventFocus ||
-        event_type == WebKit::WebAXEventBlur) {
+    ui::AXEvent event_type = param.event_type;
+    if (event_type == ui::AX_EVENT_FOCUS ||
+        event_type == ui::AX_EVENT_BLUR) {
       SetFocus(node, false);
 
       if (osk_state_ != OSK_DISALLOWED_BECAUSE_TAB_HIDDEN &&
@@ -150,62 +201,83 @@ void BrowserAccessibilityManager::OnAccessibilityEvents(
 
       // Don't send a native focus event if the window itself doesn't
       // have focus.
-      if (delegate_ && !delegate_->HasFocus())
+      if (delegate_ && !delegate_->AccessibilityViewHasFocus())
         continue;
     }
 
     // Send the event event to the operating system.
-    NotifyAccessibilityEvent(event_type, node);
-
-    // Set initial focus when a page is loaded.
-    if (event_type == WebKit::WebAXEventLoadComplete) {
-      if (!focus_)
-        SetFocus(root_, false);
-      if (!delegate_ || delegate_->HasFocus())
-        NotifyAccessibilityEvent(WebKit::WebAXEventFocus, focus_);
-    }
+    NotifyAccessibilityEvent(event_type, GetFromAXNode(node));
   }
 }
 
+void BrowserAccessibilityManager::OnLocationChanges(
+    const std::vector<AccessibilityHostMsg_LocationChangeParams>& params) {
+  for (size_t i = 0; i < params.size(); ++i) {
+    BrowserAccessibility* obj = GetFromID(params[i].id);
+    if (!obj)
+      continue;
+    ui::AXNode* node = obj->node();
+    node->SetLocation(params[i].new_location);
+    obj->OnLocationChanged();
+  }
+}
+
+BrowserAccessibility* BrowserAccessibilityManager::GetActiveDescendantFocus(
+    BrowserAccessibility* root) {
+  BrowserAccessibility* node = BrowserAccessibilityManager::GetFocus(root);
+  if (!node)
+    return NULL;
+
+  int active_descendant_id;
+  if (node->GetIntAttribute(ui::AX_ATTR_ACTIVEDESCENDANT_ID,
+                            &active_descendant_id)) {
+    BrowserAccessibility* active_descendant =
+        node->manager()->GetFromID(active_descendant_id);
+    if (active_descendant)
+      return active_descendant;
+  }
+  return node;
+}
+
 BrowserAccessibility* BrowserAccessibilityManager::GetFocus(
     BrowserAccessibility* root) {
-  if (focus_ && (!root || focus_->IsDescendantOf(root)))
-    return focus_;
+  if (focus_ && (!root || focus_->IsDescendantOf(root->node())))
+    return GetFromAXNode(focus_);
 
   return NULL;
 }
 
-void BrowserAccessibilityManager::SetFocus(
-    BrowserAccessibility* node, bool notify) {
+void BrowserAccessibilityManager::SetFocus(ui::AXNode* node, bool notify) {
   if (focus_ != node)
     focus_ = node;
 
   if (notify && node && delegate_)
-    delegate_->SetAccessibilityFocus(node->renderer_id());
+    delegate_->AccessibilitySetFocus(node->id());
 }
 
-void BrowserAccessibilityManager::SetRoot(BrowserAccessibility* node) {
-  root_ = node;
-  NotifyRootChanged();
+void BrowserAccessibilityManager::SetFocus(
+    BrowserAccessibility* obj, bool notify) {
+  if (obj->node())
+    SetFocus(obj->node(), notify);
 }
 
 void BrowserAccessibilityManager::DoDefaultAction(
     const BrowserAccessibility& node) {
   if (delegate_)
-    delegate_->AccessibilityDoDefaultAction(node.renderer_id());
+    delegate_->AccessibilityDoDefaultAction(node.GetId());
 }
 
 void BrowserAccessibilityManager::ScrollToMakeVisible(
     const BrowserAccessibility& node, gfx::Rect subfocus) {
   if (delegate_) {
-    delegate_->AccessibilityScrollToMakeVisible(node.renderer_id(), subfocus);
+    delegate_->AccessibilityScrollToMakeVisible(node.GetId(), subfocus);
   }
 }
 
 void BrowserAccessibilityManager::ScrollToPoint(
     const BrowserAccessibility& node, gfx::Point point) {
   if (delegate_) {
-    delegate_->AccessibilityScrollToPoint(node.renderer_id(), point);
+    delegate_->AccessibilityScrollToPoint(node.GetId(), point);
   }
 }
 
@@ -213,209 +285,80 @@ void BrowserAccessibilityManager::SetTextSelection(
     const BrowserAccessibility& node, int start_offset, int end_offset) {
   if (delegate_) {
     delegate_->AccessibilitySetTextSelection(
-        node.renderer_id(), start_offset, end_offset);
+        node.GetId(), start_offset, end_offset);
   }
 }
 
 gfx::Rect BrowserAccessibilityManager::GetViewBounds() {
   if (delegate_)
-    return delegate_->GetViewBounds();
+    return delegate_->AccessibilityGetViewBounds();
   return gfx::Rect();
 }
 
-void BrowserAccessibilityManager::UpdateNodesForTesting(
-    const AccessibilityNodeData& node1,
-    const AccessibilityNodeData& node2 /* = AccessibilityNodeData() */,
-    const AccessibilityNodeData& node3 /* = AccessibilityNodeData() */,
-    const AccessibilityNodeData& node4 /* = AccessibilityNodeData() */,
-    const AccessibilityNodeData& node5 /* = AccessibilityNodeData() */,
-    const AccessibilityNodeData& node6 /* = AccessibilityNodeData() */,
-    const AccessibilityNodeData& node7 /* = AccessibilityNodeData() */) {
-  std::vector<AccessibilityNodeData> nodes;
-  nodes.push_back(node1);
-  if (node2.id != AccessibilityNodeData().id)
-    nodes.push_back(node2);
-  if (node3.id != AccessibilityNodeData().id)
-    nodes.push_back(node3);
-  if (node4.id != AccessibilityNodeData().id)
-    nodes.push_back(node4);
-  if (node5.id != AccessibilityNodeData().id)
-    nodes.push_back(node5);
-  if (node6.id != AccessibilityNodeData().id)
-    nodes.push_back(node6);
-  if (node7.id != AccessibilityNodeData().id)
-    nodes.push_back(node7);
-  UpdateNodes(nodes);
-}
-
-bool BrowserAccessibilityManager::UpdateNodes(
-    const std::vector<AccessibilityNodeData>& nodes) {
-  bool success = true;
-
-  // First, update all of the nodes in the tree.
-  for (size_t i = 0; i < nodes.size() && success; i++) {
-    if (!UpdateNode(nodes[i]))
-      success = false;
-  }
-
-  // In a second pass, call PostInitialize on each one - this must
-  // be called after all of each node's children are initialized too.
-  for (size_t i = 0; i < nodes.size() && success; i++) {
-    // Note: it's not a bug for nodes[i].id to not be found in the tree.
-    // Consider this example:
-    // Before:
-    // A
-    //   B
-    //     C
-    //   D
-    //     E
-    //       F
-    // After:
-    // A
-    //   B
-    //     C
-    //       F
-    //   D
-    // In this example, F is being reparented. The renderer scans the tree
-    // in order. If can't update "C" to add "F" as a child, when "F" is still
-    // a child of "E". So it first updates "E", to remove "F" as a child.
-    // Later, it ends up deleting "E". So when we get here, "E" was updated as
-    // part of this sequence but it no longer exists in the final tree, so
-    // there's nothing to postinitialize.
-    BrowserAccessibility* instance = GetFromRendererID(nodes[i].id);
-    if (instance)
-      instance->PostInitialize();
-  }
-
-  if (!success) {
-    // A bad accessibility tree could lead to memory corruption.
-    // Ask the delegate to crash the renderer, or if not available,
-    // crash the browser.
-    if (delegate_)
-      delegate_->FatalAccessibilityTreeError();
-    else
-      CHECK(false);
+BrowserAccessibility* BrowserAccessibilityManager::NextInTreeOrder(
+    BrowserAccessibility* node) {
+  if (!node)
+    return NULL;
+
+  if (node->PlatformChildCount() > 0)
+    return node->PlatformGetChild(0);
+  while (node) {
+    if (node->GetParent() &&
+        node->GetIndexInParent() <
+            static_cast<int>(node->GetParent()->PlatformChildCount()) - 1) {
+      return node->GetParent()->PlatformGetChild(node->GetIndexInParent() + 1);
+    }
+    node = node->GetParent();
   }
 
-  return success;
-}
-
-BrowserAccessibility* BrowserAccessibilityManager::CreateNode(
-    BrowserAccessibility* parent,
-    int32 renderer_id,
-    int32 index_in_parent) {
-  BrowserAccessibility* node = factory_->Create();
-  node->InitializeTreeStructure(
-      this, parent, renderer_id, index_in_parent);
-  AddNodeToMap(node);
-  return node;
-}
-
-void BrowserAccessibilityManager::AddNodeToMap(BrowserAccessibility* node) {
-  renderer_id_map_[node->renderer_id()] = node;
+  return NULL;
 }
 
-bool BrowserAccessibilityManager::UpdateNode(const AccessibilityNodeData& src) {
-  // This method updates one node in the tree based on serialized data
-  // received from the renderer.
+BrowserAccessibility* BrowserAccessibilityManager::PreviousInTreeOrder(
+    BrowserAccessibility* node) {
+  if (!node)
+    return NULL;
 
-  // Create a set of child ids in |src| for fast lookup. If a duplicate id is
-  // found, exit now with a fatal error before changing anything else.
-  std::set<int32> new_child_ids;
-  for (size_t i = 0; i < src.child_ids.size(); ++i) {
-    if (new_child_ids.find(src.child_ids[i]) != new_child_ids.end())
-      return false;
-    new_child_ids.insert(src.child_ids[i]);
+  if (node->GetParent() && node->GetIndexInParent() > 0) {
+    node = node->GetParent()->PlatformGetChild(node->GetIndexInParent() - 1);
+    while (node->PlatformChildCount() > 0)
+      node = node->PlatformGetChild(node->PlatformChildCount() - 1);
+    return node;
   }
 
-  // Look up the node by id. If it's not found, then either the root
-  // of the tree is being swapped, or we're out of sync with the renderer
-  // and this is a serious error.
-  BrowserAccessibility* instance = GetFromRendererID(src.id);
-  if (!instance) {
-    if (src.role != WebKit::WebAXRoleRootWebArea)
-      return false;
-    instance = CreateNode(NULL, src.id, 0);
-  }
+  return node->GetParent();
+}
 
-  // TODO(dmazzoni): avoid a linear scan here.
-  for (size_t i = 0; i < src.bool_attributes.size(); i++) {
-    if (src.bool_attributes[i].first ==
-        AccessibilityNodeData::ATTR_UPDATE_LOCATION_ONLY) {
-      instance->SetLocation(src.location);
-      return true;
-    }
+void BrowserAccessibilityManager::OnNodeWillBeDeleted(ui::AXNode* node) {
+  if (node == focus_ && tree_) {
+    if (node != tree_->GetRoot())
+      SetFocus(tree_->GetRoot(), false);
+    else
+      focus_ = NULL;
   }
+  if (id_wrapper_map_.find(node->id()) == id_wrapper_map_.end())
+    return;
+  GetFromAXNode(node)->Destroy();
+  id_wrapper_map_.erase(node->id());
+}
 
-  // Update all of the node-specific data, like its role, state, name, etc.
-  instance->InitializeData(src);
-
-  //
-  // Update the children in three steps:
-  //
-  // 1. Iterate over the old children and delete nodes that are no longer
-  //    in the tree.
-  // 2. Build up a vector of new children, reusing children that haven't
-  //    changed (but may have been reordered) and adding new empty
-  //    objects for new children.
-  // 3. Swap in the new children vector for the old one.
-
-  // Delete any previous children of this instance that are no longer
-  // children first. We make a deletion-only pass first to prevent a
-  // node that's being reparented from being the child of both its old
-  // parent and new parent, which could lead to a double-free.
-  // If a node is reparented, the renderer will always send us a fresh
-  // copy of the node.
-  const std::vector<BrowserAccessibility*>& old_children = instance->children();
-  for (size_t i = 0; i < old_children.size(); ++i) {
-    int old_id = old_children[i]->renderer_id();
-    if (new_child_ids.find(old_id) == new_child_ids.end())
-      old_children[i]->Destroy();
-  }
+void BrowserAccessibilityManager::OnNodeCreated(ui::AXNode* node) {
+  BrowserAccessibility* wrapper = factory_->Create();
+  wrapper->Init(this, node);
+  id_wrapper_map_[node->id()] = wrapper;
+  wrapper->OnDataChanged();
+}
 
-  // Now build a vector of new children, reusing objects that were already
-  // children of this node before.
-  std::vector<BrowserAccessibility*> new_children;
-  bool success = true;
-  for (size_t i = 0; i < src.child_ids.size(); i++) {
-    int32 child_renderer_id = src.child_ids[i];
-    int32 index_in_parent = static_cast<int32>(i);
-    BrowserAccessibility* child = GetFromRendererID(child_renderer_id);
-    if (child) {
-      if (child->parent() != instance) {
-        // This is a serious error - nodes should never be reparented.
-        // If this case occurs, continue so this node isn't left in an
-        // inconsistent state, but return failure at the end.
-        success = false;
-        continue;
-      }
-      child->UpdateParent(instance, index_in_parent);
-    } else {
-      child = CreateNode(instance, child_renderer_id, index_in_parent);
-    }
-    new_children.push_back(child);
-  }
+void BrowserAccessibilityManager::OnNodeChanged(ui::AXNode* node) {
+  GetFromAXNode(node)->OnDataChanged();
+}
 
-  // Finally, swap in the new children vector for the old.
-  instance->SwapChildren(new_children);
-
-  // Handle the case where this node is the new root of the tree.
-  if (src.role == WebKit::WebAXRoleRootWebArea &&
-      (!root_ || root_->renderer_id() != src.id)) {
-    if (root_)
-      root_->Destroy();
-    if (focus_ == root_)
-      SetFocus(instance, false);
-    SetRoot(instance);
-  }
+void BrowserAccessibilityManager::OnNodeCreationFinished(ui::AXNode* node) {
+  GetFromAXNode(node)->OnUpdateFinished();
+}
 
-  // Keep track of what node is focused.
-  if (src.role != WebKit::WebAXRoleRootWebArea &&
-      src.role != WebKit::WebAXRoleWebArea &&
-      (src.state >> WebKit::WebAXStateFocused & 1)) {
-    SetFocus(instance, false);
-  }
-  return success;
+void BrowserAccessibilityManager::OnNodeChangeFinished(ui::AXNode* node) {
+  GetFromAXNode(node)->OnUpdateFinished();
 }
 
 }  // namespace content