summaryrefslogtreecommitdiffstats
path: root/content
diff options
context:
space:
mode:
Diffstat (limited to 'content')
-rw-r--r--content/browser/accessibility/browser_accessibility.cc69
-rw-r--r--content/browser/accessibility/browser_accessibility.h55
-rw-r--r--content/browser/accessibility/browser_accessibility_mac_unittest.mm8
-rw-r--r--content/browser/accessibility/browser_accessibility_manager.cc295
-rw-r--r--content/browser/accessibility/browser_accessibility_manager.h41
-rw-r--r--content/browser/accessibility/browser_accessibility_manager_unittest.cc228
-rw-r--r--content/browser/accessibility/browser_accessibility_win_unittest.cc76
-rw-r--r--content/browser/accessibility/cross_platform_accessibility_browsertest.cc76
-rw-r--r--content/browser/renderer_host/render_view_host_impl.cc2
-rw-r--r--content/browser/renderer_host/render_view_host_impl.h4
-rw-r--r--content/browser/renderer_host/render_widget_host_impl.cc4
-rw-r--r--content/browser/renderer_host/render_widget_host_impl.h3
-rw-r--r--content/browser/renderer_host/render_widget_host_view_gtk.cc9
-rw-r--r--content/browser/renderer_host/render_widget_host_view_gtk.h1
-rw-r--r--content/browser/renderer_host/render_widget_host_view_win.cc5
-rw-r--r--content/browser/renderer_host/render_widget_host_view_win.h1
-rw-r--r--content/common/accessibility_messages.h17
-rw-r--r--content/common/accessibility_node_data.cc84
-rw-r--r--content/common/accessibility_node_data.h33
-rw-r--r--content/renderer/accessibility/accessibility_node_serializer.cc90
-rw-r--r--content/renderer/accessibility/accessibility_node_serializer.h7
-rw-r--r--content/renderer/accessibility/renderer_accessibility.h2
-rw-r--r--content/renderer/accessibility/renderer_accessibility_browsertest.cc248
-rw-r--r--content/renderer/accessibility/renderer_accessibility_complete.cc149
-rw-r--r--content/renderer/accessibility/renderer_accessibility_complete.h32
-rw-r--r--content/renderer/accessibility/renderer_accessibility_focus_only.cc25
26 files changed, 1016 insertions, 548 deletions
diff --git a/content/browser/accessibility/browser_accessibility.cc b/content/browser/accessibility/browser_accessibility.cc
index 7484ced..84913a6 100644
--- a/content/browser/accessibility/browser_accessibility.cc
+++ b/content/browser/accessibility/browser_accessibility.cc
@@ -34,7 +34,6 @@ BrowserAccessibility::BrowserAccessibility()
child_id_(0),
index_in_parent_(0),
renderer_id_(0),
- ref_count_(1),
role_(0),
state_(0),
instance_active_(false) {
@@ -52,23 +51,25 @@ void BrowserAccessibility::DetachTree(
parent_ = NULL;
}
-void BrowserAccessibility::PreInitialize(
+void BrowserAccessibility::InitializeTreeStructure(
BrowserAccessibilityManager* manager,
BrowserAccessibility* parent,
int32 child_id,
- int32 index_in_parent,
- const AccessibilityNodeData& src) {
+ int32 renderer_id,
+ int32 index_in_parent) {
manager_ = manager;
parent_ = parent;
child_id_ = child_id;
+ renderer_id_ = renderer_id;
index_in_parent_ = index_in_parent;
+}
- // Update all of the rest of the attributes.
+void BrowserAccessibility::InitializeData(const AccessibilityNodeData& src) {
+ DCHECK_EQ(renderer_id_, src.id);
name_ = src.name;
value_ = src.value;
role_ = src.role;
state_ = src.state;
- renderer_id_ = src.id;
string_attributes_ = src.string_attributes;
int_attributes_ = src.int_attributes;
float_attributes_ = src.float_attributes;
@@ -79,6 +80,7 @@ void BrowserAccessibility::PreInitialize(
line_breaks_ = src.line_breaks;
cell_ids_ = src.cell_ids;
unique_cell_ids_ = src.unique_cell_ids;
+ instance_active_ = true;
PreInitialize();
}
@@ -91,6 +93,11 @@ void BrowserAccessibility::AddChild(BrowserAccessibility* child) {
children_.push_back(child);
}
+void BrowserAccessibility::SwapChildren(
+ std::vector<BrowserAccessibility*>& children) {
+ children.swap(children_);
+}
+
void BrowserAccessibility::UpdateParent(BrowserAccessibility* parent,
int index_in_parent) {
parent_ = parent;
@@ -183,41 +190,25 @@ BrowserAccessibility* BrowserAccessibility::BrowserAccessibilityForPoint(
return this;
}
-void BrowserAccessibility::InternalAddReference() {
- ref_count_++;
-}
-
-void BrowserAccessibility::InternalReleaseReference(bool recursive) {
- DCHECK_GT(ref_count_, 0);
- // It is a bug for ref_count_ to be gt 1 when |recursive| is true.
- DCHECK(!recursive || ref_count_ == 1);
-
- if (recursive || ref_count_ == 1) {
- for (std::vector<BrowserAccessibility*>::iterator iter = children_.begin();
- iter != children_.end();
- ++iter) {
- (*iter)->InternalReleaseReference(true);
- }
- children_.clear();
- // Force this to be the last ref. As the DCHECK above indicates, this
- // should always be the case. Make it so defensively.
- ref_count_ = 1;
+void BrowserAccessibility::Destroy() {
+ for (std::vector<BrowserAccessibility*>::iterator iter = children_.begin();
+ iter != children_.end();
+ ++iter) {
+ (*iter)->Destroy();
}
+ children_.clear();
- ref_count_--;
- if (ref_count_ == 0) {
- // Allow the object to fire a TextRemoved notification.
- name_.clear();
- value_.clear();
- PostInitialize();
+ // Allow the object to fire a TextRemoved notification.
+ name_.clear();
+ value_.clear();
+ PostInitialize();
- manager_->NotifyAccessibilityEvent(
- AccessibilityNotificationObjectHide, this);
+ manager_->NotifyAccessibilityEvent(
+ AccessibilityNotificationObjectHide, this);
- instance_active_ = false;
- manager_->Remove(child_id_, renderer_id_);
- NativeReleaseReference();
- }
+ instance_active_ = false;
+ manager_->Remove(this);
+ NativeReleaseReference();
}
void BrowserAccessibility::NativeReleaseReference() {
@@ -332,8 +323,4 @@ string16 BrowserAccessibility::GetTextRecursive() const {
return result;
}
-void BrowserAccessibility::PreInitialize() {
- instance_active_ = true;
-}
-
} // namespace content
diff --git a/content/browser/accessibility/browser_accessibility.h b/content/browser/accessibility/browser_accessibility.h
index 07a79af..ee74077 100644
--- a/content/browser/accessibility/browser_accessibility.h
+++ b/content/browser/accessibility/browser_accessibility.h
@@ -56,7 +56,7 @@ class CONTENT_EXPORT BrowserAccessibility {
// including this node, onto the end of |nodes|.
virtual void DetachTree(std::vector<BrowserAccessibility*>* nodes);
- // Perform platform specific initialization. This can be called multiple times
+ // Perform platform-specific initialization. This can be called multiple times
// during the lifetime of this instance after the members of this base object
// have been reset with new values from the renderer process.
// Child dependent initialization can be done here.
@@ -66,17 +66,22 @@ class CONTENT_EXPORT BrowserAccessibility {
// cross-platform generic object.
virtual bool IsNative() const;
- // Initialize this object, reading attributes from |src|. Does not
- // recurse into children of |src| and build the whole subtree.
- void PreInitialize(BrowserAccessibilityManager* manager,
+ // Initialize the tree structure of this object.
+ void InitializeTreeStructure(
+ BrowserAccessibilityManager* manager,
BrowserAccessibility* parent,
int32 child_id,
- int32 index_in_parent,
- const AccessibilityNodeData& src);
+ int32 renderer_id,
+ int32 index_in_parent);
+
+ // Initialize this object's data.
+ void InitializeData(const AccessibilityNodeData& src);
// Add a child of this object.
void AddChild(BrowserAccessibility* child);
+ void SwapChildren(std::vector<BrowserAccessibility*>& children);
+
// Update the parent and index in parent if this node has been moved.
void UpdateParent(BrowserAccessibility* parent, int index_in_parent);
@@ -111,32 +116,16 @@ class CONTENT_EXPORT BrowserAccessibility {
// (in global screen coordinates).
BrowserAccessibility* BrowserAccessibilityForPoint(const gfx::Point& point);
+ // Marks this object for deletion, releases our reference to it, and
+ // recursively calls Destroy() on its children. May not delete
+ // immediately due to reference counting.
//
- // Reference counting
- //
- // Each object has an internal reference count and many platform
- // implementations may also use native reference counting.
- //
- // The internal reference counting is used because sometimes
- // multiple references to the same object exist temporarily during
- // an update. When the internal reference count reaches zero,
- // NativeReleaseReference is called.
- //
- // Native reference counting is used on some platforms because the
+ // Reference counting is used on some platforms because the
// operating system may hold onto a reference to a BrowserAccessibility
- // object even after we're through with it. On these platforms, when
- // the internal reference count reaches zero, instance_active is set
- // to zero, and all queries on this object should return failure.
- // The object isn't actually deleted until the operating system releases
- // all of its references.
- //
-
- // Increment this node's internal reference count.
- virtual void InternalAddReference();
-
- // Decrement this node's internal reference count. If the reference count
- // reaches zero, call NativeReleaseReference().
- virtual void InternalReleaseReference(bool recursive);
+ // object even after we're through with it. When a BrowserAccessibility
+ // has had Destroy() called but its reference count is not yet zero,
+ // queries on this object return failure
+ virtual void Destroy();
// Subclasses should override this to support platform reference counting.
virtual void NativeAddReference() { }
@@ -193,7 +182,6 @@ class CONTENT_EXPORT BrowserAccessibility {
int32 state() const { return state_; }
const string16& value() const { return value_; }
bool instance_active() const { return instance_active_; }
- int32 ref_count() const { return ref_count_; }
#if defined(OS_MACOSX) && __OBJC__
BrowserAccessibilityCocoa* ToBrowserAccessibilityCocoa();
@@ -257,7 +245,7 @@ class CONTENT_EXPORT BrowserAccessibility {
// during the lifetime of this instance after the members of this base object
// have been reset with new values from the renderer process.
// Perform child independent initialization in this method.
- virtual void PreInitialize();
+ virtual void PreInitialize() {}
BrowserAccessibility();
@@ -280,9 +268,6 @@ class CONTENT_EXPORT BrowserAccessibility {
// The children of this object.
std::vector<BrowserAccessibility*> children_;
- // The number of internal references to this object.
- int32 ref_count_;
-
// Accessibility metadata from the renderer
string16 name_;
string16 value_;
diff --git a/content/browser/accessibility/browser_accessibility_mac_unittest.mm b/content/browser/accessibility/browser_accessibility_mac_unittest.mm
index 37b0f9a..d047e33 100644
--- a/content/browser/accessibility/browser_accessibility_mac_unittest.mm
+++ b/content/browser/accessibility/browser_accessibility_mac_unittest.mm
@@ -61,9 +61,11 @@ class BrowserAccessibilityTest : public ui::CocoaTest {
root.id = 1000;
root.location.set_width(500);
root.location.set_height(100);
- root.role = AccessibilityNodeData::ROLE_WEB_AREA;
+ root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
root.string_attributes[AccessibilityNodeData::ATTR_HELP] =
ASCIIToUTF16("HelpText");
+ root.child_ids.push_back(1001);
+ root.child_ids.push_back(1002);
AccessibilityNodeData child1;
child1.id = 1001;
@@ -79,12 +81,10 @@ class BrowserAccessibilityTest : public ui::CocoaTest {
child2.location.set_height(100);
child2.role = AccessibilityNodeData::ROLE_HEADING;
- root.children.push_back(child1);
- root.children.push_back(child2);
-
delegate_.reset([[MockAccessibilityDelegate alloc] init]);
manager_.reset(
BrowserAccessibilityManager::Create(delegate_, root, NULL));
+ manager_->UpdateNodesForTesting(child1, child2);
accessibility_.reset([manager_->GetRoot()->ToBrowserAccessibilityCocoa()
retain]);
}
diff --git a/content/browser/accessibility/browser_accessibility_manager.cc b/content/browser/accessibility/browser_accessibility_manager.cc
index 7b3bf26..a60eaf3 100644
--- a/content/browser/accessibility/browser_accessibility_manager.cc
+++ b/content/browser/accessibility/browser_accessibility_manager.cc
@@ -61,9 +61,13 @@ BrowserAccessibilityManager::BrowserAccessibilityManager(
: parent_view_(parent_view),
delegate_(delegate),
factory_(factory),
+ root_(NULL),
focus_(NULL),
osk_state_(OSK_ALLOWED) {
- root_ = CreateAccessibilityTree(NULL, src, 0, false);
+ std::vector<AccessibilityNodeData> nodes;
+ nodes.push_back(src);
+ if (!UpdateNodes(nodes))
+ return;
if (!focus_)
SetFocus(root_, false);
}
@@ -82,12 +86,8 @@ int32 BrowserAccessibilityManager::GetNextChildID() {
}
BrowserAccessibilityManager::~BrowserAccessibilityManager() {
- // Clients could still hold references to some nodes of the tree, so
- // calling InternalReleaseReference will make sure that as many nodes
- // as possible are released now, and remaining nodes are marked as
- // inactive so that calls to any methods on them will fail gracefully.
- focus_->InternalReleaseReference(false);
- root_->InternalReleaseReference(true);
+ if (root_)
+ root_->Destroy();
}
BrowserAccessibility* BrowserAccessibilityManager::GetRoot() {
@@ -143,10 +143,12 @@ bool BrowserAccessibilityManager::IsOSKAllowed(const gfx::Rect& bounds) {
return bounds.Contains(touch_point);
}
-void BrowserAccessibilityManager::Remove(int32 child_id, int32 renderer_id) {
+void BrowserAccessibilityManager::Remove(BrowserAccessibility* node) {
+ if (node == focus_)
+ SetFocus(root_, false);
+ int child_id = node->child_id();
+ int renderer_id = node->renderer_id();
child_id_map_.erase(child_id);
-
- // TODO(ctguil): Investigate if hit. We should never have a newer entry.
DCHECK(renderer_id_to_child_id_map_[renderer_id] == child_id);
// Make sure we don't overwrite a newer entry (see UpdateNode for a possible
// corner case).
@@ -159,22 +161,15 @@ void BrowserAccessibilityManager::OnAccessibilityNotifications(
for (uint32 index = 0; index < params.size(); index++) {
const AccessibilityHostMsg_NotificationParams& param = params[index];
- // Update the tree.
- UpdateNode(param.acc_tree, param.includes_children);
+ // Update nodes that changed.
+ if (!UpdateNodes(param.nodes))
+ return;
// Find the node corresponding to the id that's the target of the
// notification (which may not be the root of the update tree).
- base::hash_map<int32, int32>::iterator iter =
- renderer_id_to_child_id_map_.find(param.id);
- if (iter == renderer_id_to_child_id_map_.end()) {
+ BrowserAccessibility* node = GetFromRendererID(param.id);
+ if (!node)
continue;
- }
- int32 child_id = iter->second;
- BrowserAccessibility* node = GetFromChildID(child_id);
- if (!node) {
- NOTREACHED();
- continue;
- }
int notification_type = param.notification_type;
if (notification_type == AccessibilityNotificationFocusChanged ||
@@ -218,13 +213,8 @@ BrowserAccessibility* BrowserAccessibilityManager::GetFocus(
void BrowserAccessibilityManager::SetFocus(
BrowserAccessibility* node, bool notify) {
- if (focus_ != node) {
- if (focus_)
- focus_->InternalReleaseReference(false);
+ if (focus_ != node)
focus_ = node;
- if (focus_)
- focus_->InternalAddReference();
- }
if (notify && node && delegate_)
delegate_->SetAccessibilityFocus(node->renderer_id());
@@ -264,142 +254,159 @@ gfx::Rect BrowserAccessibilityManager::GetViewBounds() {
return gfx::Rect();
}
-void BrowserAccessibilityManager::UpdateNode(
- const AccessibilityNodeData& src,
- bool include_children) {
- BrowserAccessibility* current = NULL;
-
- // Look for the node to replace. Either we're replacing the whole tree
- // (role is ROOT_WEB_AREA) or we look it up based on its renderer ID.
- if (src.role == AccessibilityNodeData::ROLE_ROOT_WEB_AREA) {
- current = root_;
- } else {
- base::hash_map<int32, int32>::iterator iter =
- renderer_id_to_child_id_map_.find(src.id);
- if (iter != renderer_id_to_child_id_map_.end()) {
- int32 child_id = iter->second;
- current = GetFromChildID(child_id);
- }
- }
+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);
+}
- // If we can't find the node to replace, we're out of sync with the
- // renderer (this would be a bug).
- DCHECK(current);
- if (!current)
- return;
+bool BrowserAccessibilityManager::UpdateNodes(
+ const std::vector<AccessibilityNodeData>& nodes) {
+ bool success = true;
- // If this update is just for a single node (|include_children| is false),
- // modify |current| directly and return - no tree changes are needed.
- if (!include_children) {
- DCHECK_EQ(0U, src.children.size());
- current->PreInitialize(
- this,
- current->parent(),
- current->child_id(),
- current->index_in_parent(),
- src);
- current->PostInitialize();
- return;
+ // 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;
}
- BrowserAccessibility* current_parent = current->parent();
- int current_index_in_parent = current->index_in_parent();
-
- // Detach all of the nodes in the old tree and get a single flat vector
- // of all node pointers.
- std::vector<BrowserAccessibility*> old_tree_nodes;
- current->DetachTree(&old_tree_nodes);
-
- // Build a new tree, reusing old nodes if possible. Each node that's
- // reused will have its reference count incremented by one.
- CreateAccessibilityTree(current_parent, src, current_index_in_parent, true);
-
- // Decrement the reference count of all nodes in the old tree, which will
- // delete any nodes no longer needed.
- for (int i = 0; i < static_cast<int>(old_tree_nodes.size()); i++)
- old_tree_nodes[i]->InternalReleaseReference(false);
-
- // If the only reference to the focused node is focus_ itself, then the
- // focused node is no longer in the tree, so set the focus to the root.
- if (focus_ && focus_->ref_count() == 1) {
- SetFocus(root_, 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++) {
+ BrowserAccessibility* instance = GetFromRendererID(nodes[i].id);
+ if (instance) {
+ instance->PostInitialize();
+ } else {
+ success = false;
+ }
+ }
- if (delegate_ && delegate_->HasFocus())
- NotifyAccessibilityEvent(AccessibilityNotificationBlur, focus_);
+ 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);
}
+
+ return success;
}
-BrowserAccessibility* BrowserAccessibilityManager::CreateAccessibilityTree(
+BrowserAccessibility* BrowserAccessibilityManager::CreateNode(
BrowserAccessibility* parent,
- const AccessibilityNodeData& src,
- int index_in_parent,
- bool send_show_events) {
- BrowserAccessibility* instance = NULL;
- int32 child_id = 0;
- bool children_can_send_show_events = send_show_events;
- base::hash_map<int32, int32>::iterator iter =
- renderer_id_to_child_id_map_.find(src.id);
+ int32 renderer_id,
+ int32 index_in_parent) {
+ BrowserAccessibility* instance = factory_->Create();
+ int32 child_id = GetNextChildID();
+ instance->InitializeTreeStructure(
+ this, parent, child_id, renderer_id, index_in_parent);
+ child_id_map_[child_id] = instance;
+ renderer_id_to_child_id_map_[renderer_id] = child_id;
+ return instance;
+}
- // If a BrowserAccessibility instance for this ID already exists, add a
- // new reference to it and retrieve its children vector.
- if (iter != renderer_id_to_child_id_map_.end()) {
- child_id = iter->second;
- instance = GetFromChildID(child_id);
+bool BrowserAccessibilityManager::UpdateNode(const AccessibilityNodeData& src) {
+ // This method updates one node in the tree based on serialized data
+ // received from the renderer. First, 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 != AccessibilityNodeData::ROLE_ROOT_WEB_AREA)
+ return false;
+ instance = CreateNode(NULL, src.id, 0);
}
- // If the node has changed roles, don't reuse a BrowserAccessibility
- // object, that could confuse a screen reader.
- // TODO(dtseng): Investigate when this gets hit; See crbug.com/93095.
- DCHECK(!instance || instance->role() == src.role);
-
- // If we're reusing a node, it should already be detached from a parent
- // and any children. If not, that means we have a serious bug somewhere,
- // like the same child is reachable from two places in the same tree.
- if (instance && (instance->parent() != NULL || instance->child_count() > 0)) {
- // TODO(dmazzoni): investigate this: http://crbug.com/161726
- LOG(WARNING) << "Reusing node that wasn't detached from parent";
- instance = NULL;
+ // 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.
+ 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 (instance) {
- // If we're reusing a node, update its parent and increment its
- // reference count.
- instance->UpdateParent(parent, index_in_parent);
- instance->InternalAddReference();
- send_show_events = false;
- } else {
- // Otherwise, create a new instance.
- instance = factory_->Create();
- child_id = GetNextChildID();
- children_can_send_show_events = false;
+ 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();
}
- instance->PreInitialize(this, parent, child_id, index_in_parent, src);
- child_id_map_[child_id] = instance;
- renderer_id_to_child_id_map_[src.id] = child_id;
-
- if ((src.state >> AccessibilityNodeData::STATE_FOCUSED) & 1)
- SetFocus(instance, false);
-
- for (int i = 0; i < static_cast<int>(src.children.size()); ++i) {
- BrowserAccessibility* child = CreateAccessibilityTree(
- instance, src.children[i], i, children_can_send_show_events);
- instance->AddChild(child);
+ // Now build a vector of new children, reusing objects that were already
+ // children of this node before.
+ std::vector<BrowserAccessibility*> new_children;
+ 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) {
+ instance->SwapChildren(new_children);
+ return false;
+ }
+ child->UpdateParent(instance, index_in_parent);
+ } else {
+ child = CreateNode(instance, child_renderer_id, index_in_parent);
+ }
+ new_children.push_back(child);
}
- if (src.role == AccessibilityNodeData::ROLE_ROOT_WEB_AREA)
- root_ = instance;
+ // Finally, swap in the new children vector for the old.
+ instance->SwapChildren(new_children);
- // Note: the purpose of send_show_events and children_can_send_show_events
- // is so that we send a single ObjectShow event for the root of a subtree
- // that just appeared for the first time, but not on any descendant of
- // that subtree.
- if (send_show_events)
- NotifyAccessibilityEvent(AccessibilityNotificationObjectShow, instance);
+ // Handle the case where this node is the new root of the tree.
+ if (src.role == AccessibilityNodeData::ROLE_ROOT_WEB_AREA &&
+ (!root_ || root_->renderer_id() != src.id)) {
+ if (root_)
+ root_->Destroy();
+ if (focus_ == root_)
+ focus_ = instance;
+ root_ = instance;
+ }
- instance->PostInitialize();
+ // Keep track of what node is focused.
+ if ((src.state >> AccessibilityNodeData::STATE_FOCUSED) & 1)
+ SetFocus(instance, false);
- return instance;
+ return true;
}
} // namespace content
diff --git a/content/browser/accessibility/browser_accessibility_manager.h b/content/browser/accessibility/browser_accessibility_manager.h
index 7a0cdb6..08010bf 100644
--- a/content/browser/accessibility/browser_accessibility_manager.h
+++ b/content/browser/accessibility/browser_accessibility_manager.h
@@ -37,6 +37,7 @@ class CONTENT_EXPORT BrowserAccessibilityDelegate {
virtual bool HasFocus() const = 0;
virtual gfx::Rect GetViewBounds() const = 0;
virtual gfx::Point GetLastTouchEventLocation() const = 0;
+ virtual void FatalAccessibilityTreeError() = 0;
};
class CONTENT_EXPORT BrowserAccessibilityFactory {
@@ -82,8 +83,8 @@ class CONTENT_EXPORT BrowserAccessibilityManager {
// Return a pointer to the root of the tree, does not make a new reference.
BrowserAccessibility* GetRoot();
- // Removes the BrowserAccessibility child_id and renderer_id from the manager.
- void Remove(int32 child_id, int32 renderer_id);
+ // Removes a node from the manager.
+ void Remove(BrowserAccessibility* node);
// Return a pointer to the object corresponding to the given child_id,
// does not make a new reference.
@@ -154,6 +155,19 @@ class CONTENT_EXPORT BrowserAccessibilityManager {
// focus event on a text box?
bool IsOSKAllowed(const gfx::Rect& bounds);
+ // For testing only: update the given nodes as if they were
+ // received from the renderer process in OnAccessibilityNotifications.
+ // Takes up to 7 nodes at once so tests don't need to create a vector
+ // each time.
+ void UpdateNodesForTesting(
+ const AccessibilityNodeData& node,
+ const AccessibilityNodeData& node2 = AccessibilityNodeData(),
+ const AccessibilityNodeData& node3 = AccessibilityNodeData(),
+ const AccessibilityNodeData& node4 = AccessibilityNodeData(),
+ const AccessibilityNodeData& node5 = AccessibilityNodeData(),
+ const AccessibilityNodeData& node6 = AccessibilityNodeData(),
+ const AccessibilityNodeData& node7 = AccessibilityNodeData());
+
protected:
BrowserAccessibilityManager(
gfx::NativeView parent_view,
@@ -184,19 +198,18 @@ class CONTENT_EXPORT BrowserAccessibilityManager {
OSK_ALLOWED
};
- // Update an accessibility node with an updated AccessibilityNodeData node
- // received from the renderer process. When |include_children| is true
- // the node's children will also be updated, otherwise only the node
- // itself is updated.
- void UpdateNode(const AccessibilityNodeData& src, bool include_children);
+ // Update a set of nodes using data received from the renderer
+ // process.
+ bool UpdateNodes(const std::vector<AccessibilityNodeData>& nodes);
+
+ // Update one node from the tree using data received from the renderer
+ // process. Returns true on success, false on fatal error.
+ bool UpdateNode(const AccessibilityNodeData& src);
- // Recursively build a tree of BrowserAccessibility objects from
- // the AccessibilityNodeData tree received from the renderer process.
- BrowserAccessibility* CreateAccessibilityTree(
+ BrowserAccessibility* CreateNode(
BrowserAccessibility* parent,
- const AccessibilityNodeData& src,
- int index_in_parent,
- bool send_show_events);
+ int32 renderer_id,
+ int32 index_in_parent);
protected:
// The next unique id for a BrowserAccessibility instance.
@@ -211,7 +224,7 @@ class CONTENT_EXPORT BrowserAccessibilityManager {
// Factory to create BrowserAccessibility objects (for dependency injection).
scoped_ptr<BrowserAccessibilityFactory> factory_;
- // The root of the tree of IAccessible objects and the element that
+ // The root of the tree of accessible objects and the element that
// currently has focus, if any.
BrowserAccessibility* root_;
BrowserAccessibility* focus_;
diff --git a/content/browser/accessibility/browser_accessibility_manager_unittest.cc b/content/browser/accessibility/browser_accessibility_manager_unittest.cc
index 4ddf741..dfcc738 100644
--- a/content/browser/accessibility/browser_accessibility_manager_unittest.cc
+++ b/content/browser/accessibility/browser_accessibility_manager_unittest.cc
@@ -50,6 +50,40 @@ class CountedBrowserAccessibilityFactory
}
};
+class TestBrowserAccessibilityDelegate
+ : public BrowserAccessibilityDelegate {
+ public:
+ TestBrowserAccessibilityDelegate()
+ : got_fatal_error_(false) {}
+
+ virtual void SetAccessibilityFocus(int acc_obj_id) OVERRIDE {}
+ virtual void AccessibilityDoDefaultAction(int acc_obj_id) OVERRIDE {}
+ virtual void AccessibilityScrollToMakeVisible(
+ int acc_obj_id, gfx::Rect subfocus) OVERRIDE {}
+ virtual void AccessibilityScrollToPoint(
+ int acc_obj_id, gfx::Point point) OVERRIDE {}
+ virtual void AccessibilitySetTextSelection(
+ int acc_obj_id, int start_offset, int end_offset) OVERRIDE {}
+ virtual bool HasFocus() const OVERRIDE {
+ return false;
+ }
+ virtual gfx::Rect GetViewBounds() const OVERRIDE {
+ return gfx::Rect();
+ }
+ virtual gfx::Point GetLastTouchEventLocation() const OVERRIDE {
+ return gfx::Point();
+ }
+ virtual void FatalAccessibilityTreeError() OVERRIDE {
+ got_fatal_error_ = true;
+ }
+
+ bool got_fatal_error() const { return got_fatal_error_; }
+ void reset_got_fatal_error() { got_fatal_error_ = false; }
+
+private:
+ bool got_fatal_error_;
+};
+
} // anonymous namespace
TEST(BrowserAccessibilityManagerTest, TestNoLeaks) {
@@ -71,10 +105,10 @@ TEST(BrowserAccessibilityManagerTest, TestNoLeaks) {
AccessibilityNodeData root;
root.id = 1;
root.name = UTF8ToUTF16("Document");
- root.role = AccessibilityNodeData::ROLE_DOCUMENT;
+ root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
root.state = 0;
- root.children.push_back(button);
- root.children.push_back(checkbox);
+ root.child_ids.push_back(2);
+ root.child_ids.push_back(3);
// Construct a BrowserAccessibilityManager with this
// AccessibilityNodeData tree and a factory for an instance-counting
@@ -87,6 +121,7 @@ TEST(BrowserAccessibilityManagerTest, TestNoLeaks) {
root,
NULL,
new CountedBrowserAccessibilityFactory());
+ manager->UpdateNodesForTesting(button, checkbox);
ASSERT_EQ(3, CountedBrowserAccessibility::global_obj_count_);
@@ -102,6 +137,7 @@ TEST(BrowserAccessibilityManagerTest, TestNoLeaks) {
root,
NULL,
new CountedBrowserAccessibilityFactory());
+ manager->UpdateNodesForTesting(button, checkbox);
ASSERT_EQ(3, CountedBrowserAccessibility::global_obj_count_);
CountedBrowserAccessibility* root_accessible =
@@ -155,11 +191,11 @@ TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects) {
AccessibilityNodeData tree1_root;
tree1_root.id = 1;
tree1_root.name = UTF8ToUTF16("Document");
- tree1_root.role = AccessibilityNodeData::ROLE_DOCUMENT;
+ tree1_root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
tree1_root.state = 0;
- tree1_root.children.push_back(tree1_child1);
- tree1_root.children.push_back(tree1_child2);
- tree1_root.children.push_back(tree1_child3);
+ tree1_root.child_ids.push_back(2);
+ tree1_root.child_ids.push_back(3);
+ tree1_root.child_ids.push_back(4);
// Tree 2:
//
@@ -175,26 +211,14 @@ TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects) {
tree2_child0.role = AccessibilityNodeData::ROLE_BUTTON;
tree2_child0.state = 0;
- AccessibilityNodeData tree2_child1;
- tree2_child1.id = 2;
- tree2_child1.name = UTF8ToUTF16("Child1");
- tree2_child1.role = AccessibilityNodeData::ROLE_BUTTON;
- tree2_child1.state = 0;
-
- AccessibilityNodeData tree2_child2;
- tree2_child2.id = 3;
- tree2_child2.name = UTF8ToUTF16("Child2");
- tree2_child2.role = AccessibilityNodeData::ROLE_BUTTON;
- tree2_child2.state = 0;
-
AccessibilityNodeData tree2_root;
tree2_root.id = 1;
tree2_root.name = UTF8ToUTF16("DocumentChanged");
- tree2_root.role = AccessibilityNodeData::ROLE_DOCUMENT;
+ tree2_root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
tree2_root.state = 0;
- tree2_root.children.push_back(tree2_child0);
- tree2_root.children.push_back(tree2_child1);
- tree2_root.children.push_back(tree2_child2);
+ tree2_root.child_ids.push_back(5);
+ tree2_root.child_ids.push_back(2);
+ tree2_root.child_ids.push_back(3);
// Construct a BrowserAccessibilityManager with tree1.
CountedBrowserAccessibility::global_obj_count_ = 0;
@@ -204,6 +228,7 @@ TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects) {
tree1_root,
NULL,
new CountedBrowserAccessibilityFactory());
+ manager->UpdateNodesForTesting(tree1_child1, tree1_child2, tree1_child3);
ASSERT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
// Save references to all of the objects.
@@ -230,8 +255,8 @@ TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects) {
params.push_back(AccessibilityHostMsg_NotificationParams());
AccessibilityHostMsg_NotificationParams* msg = &params[0];
msg->notification_type = AccessibilityNotificationChildrenChanged;
- msg->acc_tree = tree2_root;
- msg->includes_children = true;
+ msg->nodes.push_back(tree2_root);
+ msg->nodes.push_back(tree2_child0);
msg->id = tree2_root.id;
manager->OnAccessibilityNotifications(params);
@@ -289,7 +314,7 @@ TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects2) {
tree1_child1.name = UTF8ToUTF16("Child1");
tree1_child1.role = AccessibilityNodeData::ROLE_BUTTON;
tree1_child1.state = 0;
- tree1_child1.children.push_back(tree1_grandchild1);
+ tree1_child1.child_ids.push_back(4);
AccessibilityNodeData tree1_grandchild2;
tree1_grandchild2.id = 6;
@@ -302,7 +327,7 @@ TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects2) {
tree1_child2.name = UTF8ToUTF16("Child2");
tree1_child2.role = AccessibilityNodeData::ROLE_BUTTON;
tree1_child2.state = 0;
- tree1_child2.children.push_back(tree1_grandchild2);
+ tree1_child2.child_ids.push_back(6);
AccessibilityNodeData tree1_grandchild3;
tree1_grandchild3.id = 8;
@@ -315,23 +340,23 @@ TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects2) {
tree1_child3.name = UTF8ToUTF16("Child3");
tree1_child3.role = AccessibilityNodeData::ROLE_BUTTON;
tree1_child3.state = 0;
- tree1_child3.children.push_back(tree1_grandchild3);
+ tree1_child3.child_ids.push_back(8);
AccessibilityNodeData tree1_container;
tree1_container.id = 2;
tree1_container.name = UTF8ToUTF16("Container");
tree1_container.role = AccessibilityNodeData::ROLE_GROUP;
tree1_container.state = 0;
- tree1_container.children.push_back(tree1_child1);
- tree1_container.children.push_back(tree1_child2);
- tree1_container.children.push_back(tree1_child3);
+ tree1_container.child_ids.push_back(3);
+ tree1_container.child_ids.push_back(5);
+ tree1_container.child_ids.push_back(7);
AccessibilityNodeData tree1_root;
tree1_root.id = 1;
tree1_root.name = UTF8ToUTF16("Document");
- tree1_root.role = AccessibilityNodeData::ROLE_DOCUMENT;
+ tree1_root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
tree1_root.state = 0;
- tree1_root.children.push_back(tree1_container);
+ tree1_root.child_ids.push_back(2);
// Tree 2:
//
@@ -356,49 +381,16 @@ TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects2) {
tree2_child0.name = UTF8ToUTF16("Child0");
tree2_child0.role = AccessibilityNodeData::ROLE_BUTTON;
tree2_child0.state = 0;
- tree2_child0.children.push_back(tree2_grandchild0);
-
- AccessibilityNodeData tree2_grandchild1;
- tree2_grandchild1.id = 4;
- tree2_grandchild1.name = UTF8ToUTF16("GrandChild1");
- tree2_grandchild1.role = AccessibilityNodeData::ROLE_BUTTON;
- tree2_grandchild1.state = 0;
-
- AccessibilityNodeData tree2_child1;
- tree2_child1.id = 3;
- tree2_child1.name = UTF8ToUTF16("Child1");
- tree2_child1.role = AccessibilityNodeData::ROLE_BUTTON;
- tree2_child1.state = 0;
- tree2_child1.children.push_back(tree2_grandchild1);
-
- AccessibilityNodeData tree2_grandchild2;
- tree2_grandchild2.id = 6;
- tree2_grandchild2.name = UTF8ToUTF16("GrandChild1");
- tree2_grandchild2.role = AccessibilityNodeData::ROLE_BUTTON;
- tree2_grandchild2.state = 0;
-
- AccessibilityNodeData tree2_child2;
- tree2_child2.id = 5;
- tree2_child2.name = UTF8ToUTF16("Child2");
- tree2_child2.role = AccessibilityNodeData::ROLE_BUTTON;
- tree2_child2.state = 0;
- tree2_child2.children.push_back(tree2_grandchild2);
+ tree2_child0.child_ids.push_back(9);
AccessibilityNodeData tree2_container;
tree2_container.id = 2;
tree2_container.name = UTF8ToUTF16("Container");
tree2_container.role = AccessibilityNodeData::ROLE_GROUP;
tree2_container.state = 0;
- tree2_container.children.push_back(tree2_child0);
- tree2_container.children.push_back(tree2_child1);
- tree2_container.children.push_back(tree2_child2);
-
- AccessibilityNodeData tree2_root;
- tree2_root.id = 1;
- tree2_root.name = UTF8ToUTF16("Document");
- tree2_root.role = AccessibilityNodeData::ROLE_DOCUMENT;
- tree2_root.state = 0;
- tree2_root.children.push_back(tree2_container);
+ tree2_container.child_ids.push_back(10);
+ tree2_container.child_ids.push_back(3);
+ tree2_container.child_ids.push_back(5);
// Construct a BrowserAccessibilityManager with tree1.
CountedBrowserAccessibility::global_obj_count_ = 0;
@@ -408,6 +400,10 @@ TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects2) {
tree1_root,
NULL,
new CountedBrowserAccessibilityFactory());
+ manager->UpdateNodesForTesting(tree1_container,
+ tree1_child1, tree1_grandchild1,
+ tree1_child2, tree1_grandchild2,
+ tree1_child3, tree1_grandchild3);
ASSERT_EQ(8, CountedBrowserAccessibility::global_obj_count_);
// Save references to some objects.
@@ -436,8 +432,9 @@ TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects2) {
params.push_back(AccessibilityHostMsg_NotificationParams());
AccessibilityHostMsg_NotificationParams* msg = &params[0];
msg->notification_type = AccessibilityNotificationChildrenChanged;
- msg->acc_tree = tree2_container;
- msg->includes_children = true;
+ msg->nodes.push_back(tree2_container);
+ msg->nodes.push_back(tree2_child0);
+ msg->nodes.push_back(tree2_grandchild0);
msg->id = tree2_container.id;
manager->OnAccessibilityNotifications(params);
@@ -488,7 +485,7 @@ TEST(BrowserAccessibilityManagerTest, TestMoveChildUp) {
AccessibilityNodeData tree1_3;
tree1_3.id = 3;
tree1_3.state = 0;
- tree1_3.children.push_back(tree1_4);
+ tree1_3.child_ids.push_back(4);
AccessibilityNodeData tree1_2;
tree1_2.id = 2;
@@ -496,9 +493,10 @@ TEST(BrowserAccessibilityManagerTest, TestMoveChildUp) {
AccessibilityNodeData tree1_1;
tree1_1.id = 1;
+ tree1_1.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
tree1_1.state = 0;
- tree1_1.children.push_back(tree1_2);
- tree1_1.children.push_back(tree1_3);
+ tree1_1.child_ids.push_back(2);
+ tree1_1.child_ids.push_back(3);
// Tree 2:
//
@@ -518,13 +516,13 @@ TEST(BrowserAccessibilityManagerTest, TestMoveChildUp) {
AccessibilityNodeData tree2_4;
tree2_4.id = 4;
tree2_4.state = 0;
- tree2_4.children.push_back(tree2_6);
+ tree2_4.child_ids.push_back(6);
AccessibilityNodeData tree2_1;
tree2_1.id = 1;
tree2_1.state = 0;
- tree2_1.children.push_back(tree2_4);
- tree2_1.children.push_back(tree2_5);
+ tree2_1.child_ids.push_back(4);
+ tree2_1.child_ids.push_back(5);
// Construct a BrowserAccessibilityManager with tree1.
CountedBrowserAccessibility::global_obj_count_ = 0;
@@ -534,6 +532,7 @@ TEST(BrowserAccessibilityManagerTest, TestMoveChildUp) {
tree1_1,
NULL,
new CountedBrowserAccessibilityFactory());
+ manager->UpdateNodesForTesting(tree1_2, tree1_3, tree1_4);
ASSERT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
// Process a notification containing the changed subtree.
@@ -541,8 +540,10 @@ TEST(BrowserAccessibilityManagerTest, TestMoveChildUp) {
params.push_back(AccessibilityHostMsg_NotificationParams());
AccessibilityHostMsg_NotificationParams* msg = &params[0];
msg->notification_type = AccessibilityNotificationChildrenChanged;
- msg->acc_tree = tree2_1;
- msg->includes_children = true;
+ msg->nodes.push_back(tree2_1);
+ msg->nodes.push_back(tree2_4);
+ msg->nodes.push_back(tree2_5);
+ msg->nodes.push_back(tree2_6);
msg->id = tree2_1.id;
manager->OnAccessibilityNotifications(params);
@@ -576,20 +577,19 @@ TEST(BrowserAccessibilityManagerTest, TestCreateEmptyDocument) {
AccessibilityNodeData tree1_1;
tree1_1.id = 1;
tree1_1.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
+ tree1_1.child_ids.push_back(2);
AccessibilityNodeData tree1_2;
tree1_2.id = 2;
tree1_2.role = AccessibilityNodeData::ROLE_TEXT_FIELD;
- tree1_1.children.push_back(tree1_2);
-
// Process a load complete.
std::vector<AccessibilityHostMsg_NotificationParams> params;
params.push_back(AccessibilityHostMsg_NotificationParams());
AccessibilityHostMsg_NotificationParams* msg = &params[0];
msg->notification_type = AccessibilityNotificationLoadComplete;
- msg->acc_tree = tree1_1;
- msg->includes_children = true;
+ msg->nodes.push_back(tree1_1);
+ msg->nodes.push_back(tree1_2);
msg->id = tree1_1.id;
manager->OnAccessibilityNotifications(params);
@@ -607,15 +607,15 @@ TEST(BrowserAccessibilityManagerTest, TestCreateEmptyDocument) {
AccessibilityNodeData tree2_1;
tree2_1.id = 1;
tree2_1.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
+ tree2_1.child_ids.push_back(3);
AccessibilityNodeData tree2_2;
tree2_2.id = 3;
tree2_2.role = AccessibilityNodeData::ROLE_BUTTON;
- tree2_1.children.push_back(tree2_2);
-
- msg->acc_tree = tree2_1;
- msg->includes_children = true;
+ msg->nodes.clear();
+ msg->nodes.push_back(tree2_1);
+ msg->nodes.push_back(tree2_2);
msg->id = tree2_1.id;
// Fire another load complete.
@@ -635,4 +635,56 @@ TEST(BrowserAccessibilityManagerTest, TestCreateEmptyDocument) {
ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
}
+TEST(BrowserAccessibilityManagerTest, TestFatalError) {
+ // Test that BrowserAccessibilityManager raises a fatal error
+ // (which will crash the renderer) if the same id is used in
+ // two places in the tree.
+
+ AccessibilityNodeData root;
+ root.id = 1;
+ root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
+ root.child_ids.push_back(2);
+ root.child_ids.push_back(2);
+
+ CountedBrowserAccessibilityFactory* factory =
+ new CountedBrowserAccessibilityFactory();
+ scoped_ptr<TestBrowserAccessibilityDelegate> delegate(
+ new TestBrowserAccessibilityDelegate());
+ scoped_ptr<BrowserAccessibilityManager> manager;
+ ASSERT_FALSE(delegate->got_fatal_error());
+ manager.reset(BrowserAccessibilityManager::Create(
+ NULL,
+ root,
+ delegate.get(),
+ factory));
+ ASSERT_TRUE(delegate->got_fatal_error());
+
+ AccessibilityNodeData root2;
+ root2.id = 1;
+ root2.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
+ root2.child_ids.push_back(2);
+ root2.child_ids.push_back(3);
+
+ AccessibilityNodeData child1;
+ child1.id = 2;
+ child1.child_ids.push_back(4);
+ child1.child_ids.push_back(5);
+
+ AccessibilityNodeData child2;
+ child2.id = 3;
+ child2.child_ids.push_back(6);
+ child2.child_ids.push_back(5); // Duplicate
+
+ delegate->reset_got_fatal_error();
+ factory = new CountedBrowserAccessibilityFactory();
+ manager.reset(BrowserAccessibilityManager::Create(
+ NULL,
+ root2,
+ delegate.get(),
+ factory));
+ ASSERT_FALSE(delegate->got_fatal_error());
+ manager->UpdateNodesForTesting(child1, child2);
+ ASSERT_TRUE(delegate->got_fatal_error());
+}
+
} // namespace content
diff --git a/content/browser/accessibility/browser_accessibility_win_unittest.cc b/content/browser/accessibility/browser_accessibility_win_unittest.cc
index 55aff43..17b2cbd 100644
--- a/content/browser/accessibility/browser_accessibility_win_unittest.cc
+++ b/content/browser/accessibility/browser_accessibility_win_unittest.cc
@@ -82,10 +82,10 @@ TEST_F(BrowserAccessibilityTest, TestNoLeaks) {
AccessibilityNodeData root;
root.id = 1;
root.name = L"Document";
- root.role = AccessibilityNodeData::ROLE_DOCUMENT;
+ root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
root.state = 0;
- root.children.push_back(button);
- root.children.push_back(checkbox);
+ root.child_ids.push_back(2);
+ root.child_ids.push_back(3);
// Construct a BrowserAccessibilityManager with this
// AccessibilityNodeData tree and a factory for an instance-counting
@@ -98,6 +98,7 @@ TEST_F(BrowserAccessibilityTest, TestNoLeaks) {
root,
NULL,
new CountedBrowserAccessibilityFactory());
+ manager->UpdateNodesForTesting(button, checkbox);
ASSERT_EQ(3, CountedBrowserAccessibility::global_obj_count_);
// Delete the manager and test that all 3 instances are deleted.
@@ -112,6 +113,7 @@ TEST_F(BrowserAccessibilityTest, TestNoLeaks) {
root,
NULL,
new CountedBrowserAccessibilityFactory());
+ manager->UpdateNodesForTesting(button, checkbox);
ASSERT_EQ(3, CountedBrowserAccessibility::global_obj_count_);
IAccessible* root_accessible =
manager->GetRoot()->ToBrowserAccessibilityWin();
@@ -152,9 +154,9 @@ TEST_F(BrowserAccessibilityTest, TestChildrenChange) {
AccessibilityNodeData root;
root.id = 1;
root.name = L"Document";
- root.role = AccessibilityNodeData::ROLE_DOCUMENT;
+ root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
root.state = 0;
- root.children.push_back(text);
+ root.child_ids.push_back(2);
// Construct a BrowserAccessibilityManager with this
// AccessibilityNodeData tree and a factory for an instance-counting
@@ -166,6 +168,7 @@ TEST_F(BrowserAccessibilityTest, TestChildrenChange) {
root,
NULL,
new CountedBrowserAccessibilityFactory());
+ manager->UpdateNodesForTesting(text);
// Query for the text IAccessible and verify that it returns "old text" as its
// value.
@@ -190,8 +193,7 @@ TEST_F(BrowserAccessibilityTest, TestChildrenChange) {
text.name = L"new text";
AccessibilityHostMsg_NotificationParams param;
param.notification_type = AccessibilityNotificationChildrenChanged;
- param.acc_tree = text;
- param.includes_children = true;
+ param.nodes.push_back(text);
param.id = text.id;
std::vector<AccessibilityHostMsg_NotificationParams> notifications;
notifications.push_back(param);
@@ -224,25 +226,29 @@ TEST_F(BrowserAccessibilityTest, TestChildrenChangeNoLeaks) {
// Create AccessibilityNodeData objects for a simple document tree,
// representing the accessibility information used to initialize
// BrowserAccessibilityManager.
- AccessibilityNodeData text;
- text.id = 3;
- text.role = AccessibilityNodeData::ROLE_STATIC_TEXT;
- text.state = 0;
-
AccessibilityNodeData div;
div.id = 2;
div.role = AccessibilityNodeData::ROLE_GROUP;
div.state = 0;
- div.children.push_back(text);
- text.id = 4;
- div.children.push_back(text);
+ AccessibilityNodeData text3;
+ text3.id = 3;
+ text3.role = AccessibilityNodeData::ROLE_STATIC_TEXT;
+ text3.state = 0;
+
+ AccessibilityNodeData text4;
+ text4.id = 4;
+ text4.role = AccessibilityNodeData::ROLE_STATIC_TEXT;
+ text4.state = 0;
+
+ div.child_ids.push_back(3);
+ div.child_ids.push_back(4);
AccessibilityNodeData root;
root.id = 1;
- root.role = AccessibilityNodeData::ROLE_DOCUMENT;
+ root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
root.state = 0;
- root.children.push_back(div);
+ root.child_ids.push_back(2);
// Construct a BrowserAccessibilityManager with this
// AccessibilityNodeData tree and a factory for an instance-counting
@@ -255,15 +261,15 @@ TEST_F(BrowserAccessibilityTest, TestChildrenChangeNoLeaks) {
root,
NULL,
new CountedBrowserAccessibilityFactory());
+ manager->UpdateNodesForTesting(div, text3, text4);
ASSERT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
// Notify the BrowserAccessibilityManager that the div node and its children
// were removed and ensure that only one BrowserAccessibility instance exists.
- root.children.clear();
+ root.child_ids.clear();
AccessibilityHostMsg_NotificationParams param;
param.notification_type = AccessibilityNotificationChildrenChanged;
- param.acc_tree = root;
- param.includes_children = true;
+ param.nodes.push_back(root);
param.id = root.id;
std::vector<AccessibilityHostMsg_NotificationParams> notifications;
notifications.push_back(param);
@@ -286,14 +292,15 @@ TEST_F(BrowserAccessibilityTest, TestTextBoundaries) {
AccessibilityNodeData root;
root.id = 1;
- root.role = AccessibilityNodeData::ROLE_DOCUMENT;
+ root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
root.state = 0;
- root.children.push_back(text1);
+ root.child_ids.push_back(11);
CountedBrowserAccessibility::global_obj_count_ = 0;
BrowserAccessibilityManager* manager = BrowserAccessibilityManager::Create(
GetDesktopWindow(), root, NULL,
new CountedBrowserAccessibilityFactory());
+ manager->UpdateNodesForTesting(text1);
ASSERT_EQ(2, CountedBrowserAccessibility::global_obj_count_);
BrowserAccessibilityWin* root_obj =
@@ -381,15 +388,16 @@ TEST_F(BrowserAccessibilityTest, TestSimpleHypertext) {
AccessibilityNodeData root;
root.id = 1;
- root.role = AccessibilityNodeData::ROLE_DOCUMENT;
+ root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
root.state = 1 << AccessibilityNodeData::STATE_READONLY;
- root.children.push_back(text1);
- root.children.push_back(text2);
+ root.child_ids.push_back(11);
+ root.child_ids.push_back(12);
CountedBrowserAccessibility::global_obj_count_ = 0;
BrowserAccessibilityManager* manager = BrowserAccessibilityManager::Create(
GetDesktopWindow(), root, NULL,
new CountedBrowserAccessibilityFactory());
+ manager->UpdateNodesForTesting(root, text1, text2);
ASSERT_EQ(3, CountedBrowserAccessibility::global_obj_count_);
BrowserAccessibilityWin* root_obj =
@@ -451,7 +459,7 @@ TEST_F(BrowserAccessibilityTest, TestComplexHypertext) {
button1_text.role = AccessibilityNodeData::ROLE_STATIC_TEXT;
button1.state = 1 << AccessibilityNodeData::STATE_READONLY;
button1_text.state = 1 << AccessibilityNodeData::STATE_READONLY;
- button1.children.push_back(button1_text);
+ button1.child_ids.push_back(15);
AccessibilityNodeData link1, link1_text;
link1.id = 14;
@@ -461,21 +469,25 @@ TEST_F(BrowserAccessibilityTest, TestComplexHypertext) {
link1_text.role = AccessibilityNodeData::ROLE_STATIC_TEXT;
link1.state = 1 << AccessibilityNodeData::STATE_READONLY;
link1_text.state = 1 << AccessibilityNodeData::STATE_READONLY;
- link1.children.push_back(link1_text);
+ link1.child_ids.push_back(16);
AccessibilityNodeData root;
root.id = 1;
- root.role = AccessibilityNodeData::ROLE_DOCUMENT;
+ root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
root.state = 1 << AccessibilityNodeData::STATE_READONLY;
- root.children.push_back(text1);
- root.children.push_back(button1);
- root.children.push_back(text2);
- root.children.push_back(link1);
+ root.child_ids.push_back(11);
+ root.child_ids.push_back(13);
+ root.child_ids.push_back(12);
+ root.child_ids.push_back(14);
CountedBrowserAccessibility::global_obj_count_ = 0;
BrowserAccessibilityManager* manager = BrowserAccessibilityManager::Create(
GetDesktopWindow(), root, NULL,
new CountedBrowserAccessibilityFactory());
+ manager->UpdateNodesForTesting(root,
+ text1, button1, button1_text,
+ text2, link1, link1_text);
+
ASSERT_EQ(7, CountedBrowserAccessibility::global_obj_count_);
BrowserAccessibilityWin* root_obj =
diff --git a/content/browser/accessibility/cross_platform_accessibility_browsertest.cc b/content/browser/accessibility/cross_platform_accessibility_browsertest.cc
index 0ce2b50..9fe99c4 100644
--- a/content/browser/accessibility/cross_platform_accessibility_browsertest.cc
+++ b/content/browser/accessibility/cross_platform_accessibility_browsertest.cc
@@ -38,7 +38,7 @@ class CrossPlatformAccessibilityBrowserTest : public ContentBrowserTest {
// Tell the renderer to send an accessibility tree, then wait for the
// notification that it's been received.
- const AccessibilityNodeData& GetAccessibilityNodeDataTree(
+ const AccessibilityNodeDataTreeNode& GetAccessibilityNodeDataTree(
AccessibilityMode accessibility_mode = AccessibilityModeComplete) {
scoped_refptr<MessageLoopRunner> loop_runner(new MessageLoopRunner);
RenderWidgetHostView* host_view =
@@ -56,7 +56,7 @@ class CrossPlatformAccessibilityBrowserTest : public ContentBrowserTest {
// Make sure each node in the tree has an unique id.
void RecursiveAssertUniqueIds(
- const AccessibilityNodeData& node, base::hash_set<int>* ids) {
+ const AccessibilityNodeDataTreeNode& node, base::hash_set<int>* ids) {
ASSERT_TRUE(ids->find(node.id) == ids->end());
ids->insert(node.id);
for (size_t i = 0; i < node.children.size(); i++)
@@ -148,7 +148,7 @@ IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
"</body></html>";
GURL url(url_str);
NavigateToURL(shell(), url);
- const AccessibilityNodeData& tree = GetAccessibilityNodeDataTree();
+ const AccessibilityNodeDataTreeNode& tree = GetAccessibilityNodeDataTree();
// Check properties of the root element of the tree.
EXPECT_STREQ(url_str,
@@ -166,7 +166,7 @@ IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
// Check properites of the BODY element.
ASSERT_EQ(1U, tree.children.size());
- const AccessibilityNodeData& body = tree.children[0];
+ const AccessibilityNodeDataTreeNode& body = tree.children[0];
EXPECT_EQ(AccessibilityNodeData::ROLE_GROUP, body.role);
EXPECT_STREQ("body",
GetAttr(body, AccessibilityNodeData::ATTR_HTML_TAG).c_str());
@@ -176,7 +176,7 @@ IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
// Check properties of the two children of the BODY element.
ASSERT_EQ(2U, body.children.size());
- const AccessibilityNodeData& button = body.children[0];
+ const AccessibilityNodeDataTreeNode& button = body.children[0];
EXPECT_EQ(AccessibilityNodeData::ROLE_BUTTON, button.role);
EXPECT_STREQ(
"input", GetAttr(button, AccessibilityNodeData::ATTR_HTML_TAG).c_str());
@@ -190,7 +190,7 @@ IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
EXPECT_STREQ("value", UTF16ToUTF8(button.html_attributes[1].first).c_str());
EXPECT_STREQ("push", UTF16ToUTF8(button.html_attributes[1].second).c_str());
- const AccessibilityNodeData& checkbox = body.children[1];
+ const AccessibilityNodeDataTreeNode& checkbox = body.children[1];
EXPECT_EQ(AccessibilityNodeData::ROLE_CHECKBOX, checkbox.role);
EXPECT_STREQ(
"input", GetAttr(checkbox, AccessibilityNodeData::ATTR_HTML_TAG).c_str());
@@ -216,11 +216,11 @@ IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
GURL url(url_str);
NavigateToURL(shell(), url);
- const AccessibilityNodeData& tree = GetAccessibilityNodeDataTree();
+ const AccessibilityNodeDataTreeNode& tree = GetAccessibilityNodeDataTree();
ASSERT_EQ(1U, tree.children.size());
- const AccessibilityNodeData& body = tree.children[0];
+ const AccessibilityNodeDataTreeNode& body = tree.children[0];
ASSERT_EQ(1U, body.children.size());
- const AccessibilityNodeData& text = body.children[0];
+ const AccessibilityNodeDataTreeNode& text = body.children[0];
EXPECT_EQ(AccessibilityNodeData::ROLE_TEXT_FIELD, text.role);
EXPECT_STREQ(
"input", GetAttr(text, AccessibilityNodeData::ATTR_HTML_TAG).c_str());
@@ -245,11 +245,11 @@ IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
GURL url(url_str);
NavigateToURL(shell(), url);
- const AccessibilityNodeData& tree = GetAccessibilityNodeDataTree();
+ const AccessibilityNodeDataTreeNode& tree = GetAccessibilityNodeDataTree();
ASSERT_EQ(1U, tree.children.size());
- const AccessibilityNodeData& body = tree.children[0];
+ const AccessibilityNodeDataTreeNode& body = tree.children[0];
ASSERT_EQ(1U, body.children.size());
- const AccessibilityNodeData& text = body.children[0];
+ const AccessibilityNodeDataTreeNode& text = body.children[0];
EXPECT_EQ(AccessibilityNodeData::ROLE_TEXT_FIELD, text.role);
EXPECT_STREQ(
"input", GetAttr(text, AccessibilityNodeData::ATTR_HTML_TAG).c_str());
@@ -272,22 +272,22 @@ IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
GURL url(url_str);
NavigateToURL(shell(), url);
- const AccessibilityNodeData& tree = GetAccessibilityNodeDataTree();
+ const AccessibilityNodeDataTreeNode& tree = GetAccessibilityNodeDataTree();
ASSERT_EQ(1U, tree.children.size());
- const AccessibilityNodeData& table = tree.children[0];
+ const AccessibilityNodeDataTreeNode& table = tree.children[0];
EXPECT_EQ(AccessibilityNodeData::ROLE_TABLE, table.role);
- const AccessibilityNodeData& row = table.children[0];
+ const AccessibilityNodeDataTreeNode& row = table.children[0];
EXPECT_EQ(AccessibilityNodeData::ROLE_ROW, row.role);
- const AccessibilityNodeData& cell1 = row.children[0];
+ const AccessibilityNodeDataTreeNode& cell1 = row.children[0];
EXPECT_EQ(AccessibilityNodeData::ROLE_CELL, cell1.role);
- const AccessibilityNodeData& cell2 = row.children[1];
+ const AccessibilityNodeDataTreeNode& cell2 = row.children[1];
EXPECT_EQ(AccessibilityNodeData::ROLE_CELL, cell2.role);
- const AccessibilityNodeData& column1 = table.children[1];
+ const AccessibilityNodeDataTreeNode& column1 = table.children[1];
EXPECT_EQ(AccessibilityNodeData::ROLE_COLUMN, column1.role);
EXPECT_EQ(0U, column1.children.size());
EXPECT_EQ(1U, column1.indirect_child_ids.size());
EXPECT_EQ(cell1.id, column1.indirect_child_ids[0]);
- const AccessibilityNodeData& column2 = table.children[2];
+ const AccessibilityNodeDataTreeNode& column2 = table.children[2];
EXPECT_EQ(AccessibilityNodeData::ROLE_COLUMN, column2.role);
EXPECT_EQ(0U, column2.children.size());
EXPECT_EQ(1U, column2.indirect_child_ids.size());
@@ -311,7 +311,7 @@ IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
GURL url(url_str);
NavigateToURL(shell(), url);
- const AccessibilityNodeData& tree = GetAccessibilityNodeDataTree();
+ const AccessibilityNodeDataTreeNode& tree = GetAccessibilityNodeDataTree();
base::hash_set<int> ids;
RecursiveAssertUniqueIds(tree, &ids);
}
@@ -331,36 +331,36 @@ IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
GURL url(url_str);
NavigateToURL(shell(), url);
- const AccessibilityNodeData& tree = GetAccessibilityNodeDataTree();
+ const AccessibilityNodeDataTreeNode& tree = GetAccessibilityNodeDataTree();
ASSERT_EQ(1U, tree.children.size());
- const AccessibilityNodeData& body = tree.children[0];
+ const AccessibilityNodeDataTreeNode& body = tree.children[0];
ASSERT_EQ(3U, body.children.size());
- const AccessibilityNodeData& button1 = body.children[0];
+ const AccessibilityNodeDataTreeNode& button1 = body.children[0];
EXPECT_EQ(AccessibilityNodeData::ROLE_BUTTON, button1.role);
EXPECT_STREQ("Button 1", UTF16ToUTF8(button1.name).c_str());
- const AccessibilityNodeData& iframe = body.children[1];
+ const AccessibilityNodeDataTreeNode& iframe = body.children[1];
EXPECT_STREQ("iframe",
GetAttr(iframe, AccessibilityNodeData::ATTR_HTML_TAG).c_str());
ASSERT_EQ(1U, iframe.children.size());
- const AccessibilityNodeData& scroll_area = iframe.children[0];
+ const AccessibilityNodeDataTreeNode& scroll_area = iframe.children[0];
EXPECT_EQ(AccessibilityNodeData::ROLE_SCROLLAREA, scroll_area.role);
ASSERT_EQ(1U, scroll_area.children.size());
- const AccessibilityNodeData& sub_document = scroll_area.children[0];
+ const AccessibilityNodeDataTreeNode& sub_document = scroll_area.children[0];
EXPECT_EQ(AccessibilityNodeData::ROLE_WEB_AREA, sub_document.role);
ASSERT_EQ(1U, sub_document.children.size());
- const AccessibilityNodeData& sub_body = sub_document.children[0];
+ const AccessibilityNodeDataTreeNode& sub_body = sub_document.children[0];
ASSERT_EQ(1U, sub_body.children.size());
- const AccessibilityNodeData& button2 = sub_body.children[0];
+ const AccessibilityNodeDataTreeNode& button2 = sub_body.children[0];
EXPECT_EQ(AccessibilityNodeData::ROLE_BUTTON, button2.role);
EXPECT_STREQ("Button 2", UTF16ToUTF8(button2.name).c_str());
- const AccessibilityNodeData& button3 = body.children[2];
+ const AccessibilityNodeDataTreeNode& button3 = body.children[2];
EXPECT_EQ(AccessibilityNodeData::ROLE_BUTTON, button3.role);
EXPECT_STREQ("Button 3", UTF16ToUTF8(button3.name).c_str());
}
@@ -377,7 +377,7 @@ IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
GURL url(url_str);
NavigateToURL(shell(), url);
- const AccessibilityNodeData& tree = GetAccessibilityNodeDataTree();
+ const AccessibilityNodeDataTreeNode& tree = GetAccessibilityNodeDataTree();
base::hash_set<int> ids;
RecursiveAssertUniqueIds(tree, &ids);
}
@@ -404,8 +404,8 @@ IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
GURL url(url_str);
NavigateToURL(shell(), url);
- const AccessibilityNodeData& tree = GetAccessibilityNodeDataTree();
- const AccessibilityNodeData& table = tree.children[0];
+ const AccessibilityNodeDataTreeNode& tree = GetAccessibilityNodeDataTree();
+ const AccessibilityNodeDataTreeNode& table = tree.children[0];
EXPECT_EQ(AccessibilityNodeData::ROLE_TABLE, table.role);
ASSERT_GE(table.children.size(), 5U);
EXPECT_EQ(AccessibilityNodeData::ROLE_ROW, table.children[0].role);
@@ -417,10 +417,10 @@ IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
GetIntAttr(table, AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT));
EXPECT_EQ(2, GetIntAttr(table, AccessibilityNodeData::ATTR_TABLE_ROW_COUNT));
- const AccessibilityNodeData& cell1 = table.children[0].children[0];
- const AccessibilityNodeData& cell2 = table.children[0].children[1];
- const AccessibilityNodeData& cell3 = table.children[1].children[0];
- const AccessibilityNodeData& cell4 = table.children[1].children[1];
+ const AccessibilityNodeDataTreeNode& cell1 = table.children[0].children[0];
+ const AccessibilityNodeDataTreeNode& cell2 = table.children[0].children[1];
+ const AccessibilityNodeDataTreeNode& cell3 = table.children[1].children[0];
+ const AccessibilityNodeDataTreeNode& cell4 = table.children[1].children[1];
ASSERT_EQ(6U, table.cell_ids.size());
EXPECT_EQ(cell1.id, table.cell_ids[0]);
@@ -462,10 +462,10 @@ IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
"</div>";
GURL url(url_str);
NavigateToURL(shell(), url);
- const AccessibilityNodeData& tree = GetAccessibilityNodeDataTree();
+ const AccessibilityNodeDataTreeNode& tree = GetAccessibilityNodeDataTree();
ASSERT_EQ(1U, tree.children.size());
- const AccessibilityNodeData& textbox = tree.children[0];
+ const AccessibilityNodeDataTreeNode& textbox = tree.children[0];
EXPECT_EQ(
true, GetBoolAttr(textbox, AccessibilityNodeData::ATTR_CAN_SET_VALUE));
diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc
index 71c2275..40f36c0 100644
--- a/content/browser/renderer_host/render_view_host_impl.cc
+++ b/content/browser/renderer_host/render_view_host_impl.cc
@@ -1902,7 +1902,7 @@ void RenderViewHostImpl::OnAccessibilityNotifications(
if ((src_type == AccessibilityNotificationLayoutComplete ||
src_type == AccessibilityNotificationLoadComplete) &&
save_accessibility_tree_for_testing_) {
- accessibility_tree_ = param.acc_tree;
+ MakeAccessibilityNodeDataTree(param.nodes, &accessibility_tree_);
}
if (src_type == AccessibilityNotificationLayoutComplete) {
diff --git a/content/browser/renderer_host/render_view_host_impl.h b/content/browser/renderer_host/render_view_host_impl.h
index 1ee4483..2a0c8a2 100644
--- a/content/browser/renderer_host/render_view_host_impl.h
+++ b/content/browser/renderer_host/render_view_host_impl.h
@@ -418,7 +418,7 @@ class CONTENT_EXPORT RenderViewHostImpl
save_accessibility_tree_for_testing_ = save;
}
- const AccessibilityNodeData& accessibility_tree_for_testing() {
+ const AccessibilityNodeDataTreeNode& accessibility_tree_for_testing() {
return accessibility_tree_;
}
@@ -675,7 +675,7 @@ class CONTENT_EXPORT RenderViewHostImpl
std::string frame_tree_;
// The most recently received accessibility tree - for unit testing only.
- AccessibilityNodeData accessibility_tree_;
+ AccessibilityNodeDataTreeNode accessibility_tree_;
// The termination status of the last render view that terminated.
base::TerminationStatus render_view_termination_status_;
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index e0c3d28..176784c 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -2246,6 +2246,10 @@ void RenderWidgetHostImpl::AccessibilitySetTextSelection(
GetRoutingID(), object_id, start_offset, end_offset));
}
+void RenderWidgetHostImpl::FatalAccessibilityTreeError() {
+ Send(new AccessibilityMsg_FatalError(GetRoutingID()));
+}
+
void RenderWidgetHostImpl::ExecuteEditCommand(const std::string& command,
const std::string& value) {
Send(new ViewMsg_ExecuteEditCommand(GetRoutingID(), command, value));
diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h
index 201f258..61d8bfd 100644
--- a/content/browser/renderer_host/render_widget_host_impl.h
+++ b/content/browser/renderer_host/render_widget_host_impl.h
@@ -355,6 +355,9 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost,
void AccessibilitySetTextSelection(
int acc_obj_id, int start_offset, int end_offset);
+ // Kill the renderer because we got a fatal accessibility error.
+ void FatalAccessibilityTreeError();
+
// Executes the edit command on the RenderView.
void ExecuteEditCommand(const std::string& command,
const std::string& value);
diff --git a/content/browser/renderer_host/render_widget_host_view_gtk.cc b/content/browser/renderer_host/render_widget_host_view_gtk.cc
index bb3351e..6f525f3 100644
--- a/content/browser/renderer_host/render_widget_host_view_gtk.cc
+++ b/content/browser/renderer_host/render_widget_host_view_gtk.cc
@@ -1571,6 +1571,15 @@ gfx::Point RenderWidgetHostViewGtk::GetLastTouchEventLocation() const {
return gfx::Point();
}
+void RenderWidgetHostViewGtk::FatalAccessibilityTreeError() {
+ if (host_) {
+ host_->FatalAccessibilityTreeError();
+ SetBrowserAccessibilityManager(NULL);
+ } else {
+ CHECK(FALSE);
+ }
+}
+
void RenderWidgetHostViewGtk::OnAccessibilityNotifications(
const std::vector<AccessibilityHostMsg_NotificationParams>& params) {
if (!browser_accessibility_manager_.get()) {
diff --git a/content/browser/renderer_host/render_widget_host_view_gtk.h b/content/browser/renderer_host/render_widget_host_view_gtk.h
index b998cf4..46daaac 100644
--- a/content/browser/renderer_host/render_widget_host_view_gtk.h
+++ b/content/browser/renderer_host/render_widget_host_view_gtk.h
@@ -175,6 +175,7 @@ class CONTENT_EXPORT RenderWidgetHostViewGtk
virtual void AccessibilitySetTextSelection(
int acc_obj_id, int start_offset, int end_offset) OVERRIDE;
virtual gfx::Point GetLastTouchEventLocation() const OVERRIDE;
+ virtual void FatalAccessibilityTreeError() OVERRIDE;
// Get the root of the AtkObject* tree for accessibility.
AtkObject* GetAccessible();
diff --git a/content/browser/renderer_host/render_widget_host_view_win.cc b/content/browser/renderer_host/render_widget_host_view_win.cc
index 6500a8b..a5cb6cf 100644
--- a/content/browser/renderer_host/render_widget_host_view_win.cc
+++ b/content/browser/renderer_host/render_widget_host_view_win.cc
@@ -2592,6 +2592,11 @@ gfx::Point RenderWidgetHostViewWin::GetLastTouchEventLocation() const {
return last_touch_location_;
}
+void RenderWidgetHostViewWin::FatalAccessibilityTreeError() {
+ render_widget_host_->FatalAccessibilityTreeError();
+ SetBrowserAccessibilityManager(NULL);
+}
+
LRESULT RenderWidgetHostViewWin::OnGetObject(UINT message, WPARAM wparam,
LPARAM lparam, BOOL& handled) {
TRACE_EVENT0("browser", "RenderWidgetHostViewWin::OnGetObject");
diff --git a/content/browser/renderer_host/render_widget_host_view_win.h b/content/browser/renderer_host/render_widget_host_view_win.h
index 8c9a768..6f8c236 100644
--- a/content/browser/renderer_host/render_widget_host_view_win.h
+++ b/content/browser/renderer_host/render_widget_host_view_win.h
@@ -250,6 +250,7 @@ class RenderWidgetHostViewWin
virtual void AccessibilitySetTextSelection(
int acc_obj_id, int start_offset, int end_offset) OVERRIDE;
virtual gfx::Point GetLastTouchEventLocation() const OVERRIDE;
+ virtual void FatalAccessibilityTreeError() OVERRIDE;
// Overridden from ui::GestureEventHelper.
virtual bool DispatchLongPressGestureEvent(ui::GestureEvent* event) OVERRIDE;
diff --git a/content/common/accessibility_messages.h b/content/common/accessibility_messages.h
index c74907a..2cbc24d 100644
--- a/content/common/accessibility_messages.h
+++ b/content/common/accessibility_messages.h
@@ -128,7 +128,7 @@ IPC_STRUCT_TRAITS_BEGIN(content::AccessibilityNodeData)
IPC_STRUCT_TRAITS_MEMBER(int_attributes)
IPC_STRUCT_TRAITS_MEMBER(float_attributes)
IPC_STRUCT_TRAITS_MEMBER(bool_attributes)
- IPC_STRUCT_TRAITS_MEMBER(children)
+ IPC_STRUCT_TRAITS_MEMBER(child_ids)
IPC_STRUCT_TRAITS_MEMBER(indirect_child_ids)
IPC_STRUCT_TRAITS_MEMBER(html_attributes)
IPC_STRUCT_TRAITS_MEMBER(line_breaks)
@@ -137,18 +137,15 @@ IPC_STRUCT_TRAITS_BEGIN(content::AccessibilityNodeData)
IPC_STRUCT_TRAITS_END()
IPC_STRUCT_BEGIN(AccessibilityHostMsg_NotificationParams)
+ // Vector of nodes in the tree that need to be updated before
+ // sending the notification.
+ IPC_STRUCT_MEMBER(std::vector<content::AccessibilityNodeData>, nodes)
+
// Type of notification.
IPC_STRUCT_MEMBER(AccessibilityNotification, notification_type)
// ID of the node that the notification applies to.
IPC_STRUCT_MEMBER(int, id)
-
- // The accessibility node tree.
- IPC_STRUCT_MEMBER(content::AccessibilityNodeData, acc_tree)
-
- // Whether children are included in this tree, otherwise it's just an
- // update to this one node and existing children are left in place.
- IPC_STRUCT_MEMBER(bool, includes_children)
IPC_STRUCT_END()
// Messages sent from the browser to the renderer.
@@ -189,6 +186,10 @@ IPC_MESSAGE_ROUTED3(AccessibilityMsg_SetTextSelection,
// message was processed and it can send addition notifications.
IPC_MESSAGE_ROUTED0(AccessibilityMsg_Notifications_ACK)
+
+// Kill the renderer because we got a fatal error in the accessibility tree.
+IPC_MESSAGE_ROUTED0(AccessibilityMsg_FatalError)
+
// Messages sent from the renderer to the browser.
// Sent to notify the browser about renderer accessibility notifications.
diff --git a/content/common/accessibility_node_data.cc b/content/common/accessibility_node_data.cc
index 6c87d72..a34b376 100644
--- a/content/common/accessibility_node_data.cc
+++ b/content/common/accessibility_node_data.cc
@@ -6,6 +6,7 @@
#include <set>
+#include "base/hash_tables.h"
#include "base/string_number_conversions.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
@@ -40,13 +41,70 @@ AccessibilityNodeData::AccessibilityNodeData()
AccessibilityNodeData::~AccessibilityNodeData() {
}
+AccessibilityNodeDataTreeNode::AccessibilityNodeDataTreeNode()
+ : AccessibilityNodeData() {
+}
+
+AccessibilityNodeDataTreeNode::~AccessibilityNodeDataTreeNode() {
+}
+
+AccessibilityNodeDataTreeNode& AccessibilityNodeDataTreeNode::operator=(
+ const AccessibilityNodeData& src) {
+ AccessibilityNodeData::operator=(src);
+ return *this;
+}
+
+void MakeAccessibilityNodeDataTree(
+ const std::vector<AccessibilityNodeData>& src_vector,
+ AccessibilityNodeDataTreeNode* dst_root) {
+ // This method assumes |src_vector| contains all of the nodes of
+ // an accessibility tree, and that each parent comes before its
+ // children. Each node has an id, and the ids of its children.
+ // The output is a tree where each node contains its children.
+
+ // Initialize a hash map with all of the ids in |src_vector|.
+ base::hash_map<int32, AccessibilityNodeDataTreeNode*> id_map;
+ for (size_t i = 0; i < src_vector.size(); ++i)
+ id_map[src_vector[i].id] = NULL;
+
+ // Copy the nodes to the output tree one at a time.
+ for (size_t i = 0; i < src_vector.size(); ++i) {
+ const AccessibilityNodeData& src_node = src_vector[i];
+ AccessibilityNodeDataTreeNode* dst_node;
+
+ // If it's the first element in the vector, assume it's
+ // the root. For any other element, look for it in our
+ // hash map, and skip it if not there (meaning there was
+ // an extranous node, or the nodes were sent in the wrong
+ // order).
+ if (i == 0) {
+ dst_node = dst_root;
+ } else {
+ dst_node = id_map[src_node.id];
+ if (!dst_node)
+ continue;
+ }
+
+ // Copy the node data.
+ *dst_node = src_node;
+
+ // Add placeholders for all of the node's children in the tree,
+ // and add them to the hash map so we can find them when we
+ // encounter them in |src_vector|.
+ dst_node->children.reserve(src_node.child_ids.size());
+ for (size_t j = 0; j < src_node.child_ids.size(); ++j) {
+ int child_id = src_node.child_ids[j];
+ if (id_map.find(child_id) != id_map.end()) {
+ dst_node->children.push_back(AccessibilityNodeDataTreeNode());
+ id_map[child_id] = &dst_node->children.back();
+ }
+ }
+ }
+}
+
#ifndef NDEBUG
std::string AccessibilityNodeData::DebugString(bool recursive) const {
std::string result;
- static int indent = 0;
- result += "\n";
- for (int i = 0; i < indent; ++i)
- result += " ";
result += "id=" + IntToString(id);
@@ -407,8 +465,8 @@ std::string AccessibilityNodeData::DebugString(bool recursive) const {
}
}
- if (!children.empty())
- result += " children=" + IntToString(children.size());
+ if (!child_ids.empty())
+ result += " child_ids=" + IntVectorToString(child_ids);
if (!indirect_child_ids.empty())
result += " indirect_child_ids=" + IntVectorToString(indirect_child_ids);
@@ -419,6 +477,19 @@ std::string AccessibilityNodeData::DebugString(bool recursive) const {
if (!cell_ids.empty())
result += " cell_ids=" + IntVectorToString(cell_ids);
+ return result;
+}
+
+std::string AccessibilityNodeDataTreeNode::DebugString(bool recursive) const {
+ std::string result;
+
+ static int indent = 0;
+ result += "\n";
+ for (int i = 0; i < indent; ++i)
+ result += " ";
+
+ result += AccessibilityNodeData::DebugString(recursive);
+
if (recursive) {
result += "\n";
++indent;
@@ -429,6 +500,7 @@ std::string AccessibilityNodeData::DebugString(bool recursive) const {
return result;
}
+
#endif // ifndef NDEBUG
} // namespace content
diff --git a/content/common/accessibility_node_data.h b/content/common/accessibility_node_data.h
index 2fd9271..94eeb2e 100644
--- a/content/common/accessibility_node_data.h
+++ b/content/common/accessibility_node_data.h
@@ -264,10 +264,10 @@ struct CONTENT_EXPORT AccessibilityNodeData {
};
AccessibilityNodeData();
- ~AccessibilityNodeData();
+ virtual ~AccessibilityNodeData();
#ifndef NDEBUG
- std::string DebugString(bool recursive) const;
+ virtual std::string DebugString(bool recursive) const;
#endif
// This is a simple serializable struct. All member variables should be
@@ -282,7 +282,7 @@ struct CONTENT_EXPORT AccessibilityNodeData {
std::map<IntAttribute, int32> int_attributes;
std::map<FloatAttribute, float> float_attributes;
std::map<BoolAttribute, bool> bool_attributes;
- std::vector<AccessibilityNodeData> children;
+ std::vector<int32> child_ids;
std::vector<int32> indirect_child_ids;
std::vector<std::pair<string16, string16> > html_attributes;
std::vector<int32> line_breaks;
@@ -297,6 +297,33 @@ struct CONTENT_EXPORT AccessibilityNodeData {
std::vector<int32> unique_cell_ids;
};
+// For testing and debugging only: this subclass of AccessibilityNodeData
+// is used to represent a whole tree of accessibility nodes, where each
+// node owns its children. This makes it easy to print the tree structure
+// or search it recursively.
+struct CONTENT_EXPORT AccessibilityNodeDataTreeNode
+ : public AccessibilityNodeData {
+ AccessibilityNodeDataTreeNode();
+ virtual ~AccessibilityNodeDataTreeNode();
+
+ AccessibilityNodeDataTreeNode& operator=(const AccessibilityNodeData& src);
+
+ #ifndef NDEBUG
+ virtual std::string DebugString(bool recursive) const OVERRIDE;
+ #endif
+
+ std::vector<AccessibilityNodeDataTreeNode> children;
+};
+
+// Given a vector of accessibility nodes that represent a complete
+// accessibility tree, where each node appears before its children,
+// build a tree of AccessibilityNodeDataTreeNode objects for easier
+// testing and debugging, where each node contains its children.
+// The |dst| argument will become the root of the new tree.
+void MakeAccessibilityNodeDataTree(
+ const std::vector<AccessibilityNodeData>& src,
+ AccessibilityNodeDataTreeNode* dst);
+
} // namespace content
#endif // CONTENT_COMMON_ACCESSIBILITY_NODE_DATA_H_
diff --git a/content/renderer/accessibility/accessibility_node_serializer.cc b/content/renderer/accessibility/accessibility_node_serializer.cc
index 613a453..cb8f8f7f 100644
--- a/content/renderer/accessibility/accessibility_node_serializer.cc
+++ b/content/renderer/accessibility/accessibility_node_serializer.cc
@@ -350,8 +350,7 @@ uint32 ConvertState(const WebAccessibilityObject& o) {
void SerializeAccessibilityNode(
const WebAccessibilityObject& src,
- AccessibilityNodeData* dst,
- bool include_children) {
+ AccessibilityNodeData* dst) {
dst->name = src.title();
dst->role = ConvertRole(src.roleValue());
dst->state = ConvertState(src);
@@ -404,9 +403,6 @@ void SerializeAccessibilityNode(
dst->int_attributes[dst->ATTR_HIERARCHICAL_LEVEL] = src.hierarchicalLevel();
}
- if (dst->role == dst->ROLE_SLIDER)
- include_children = false;
-
// Treat the active list box item as focused.
if (dst->role == dst->ROLE_LISTBOX_OPTION && src.isSelectedOptionActive())
dst->state |= (1 << AccessibilityNodeData::STATE_FOCUSED);
@@ -440,9 +436,6 @@ void SerializeAccessibilityNode(
if (dst->role == dst->ROLE_EDITABLE_TEXT ||
dst->role == dst->ROLE_TEXTAREA ||
dst->role == dst->ROLE_TEXT_FIELD) {
- // Jaws gets confused by children of text fields, so we ignore them.
- include_children = false;
-
dst->int_attributes[dst->ATTR_TEXT_SEL_START] = src.selectionStart();
dst->int_attributes[dst->ATTR_TEXT_SEL_END] = src.selectionEnd();
@@ -590,46 +583,49 @@ void SerializeAccessibilityNode(
dst->int_attributes[dst->ATTR_TABLE_CELL_ROW_SPAN] = src.cellRowSpan();
}
- if (include_children) {
- // Recursively create children.
- int child_count = src.childCount();
- std::set<int32> child_ids;
- for (int i = 0; i < child_count; ++i) {
- WebAccessibilityObject child = src.childAt(i);
- int32 child_id = child.axID();
-
- // The child may be invalid due to issues in webkit accessibility code.
- // Don't add children that are invalid thus preventing a crash.
- // https://bugs.webkit.org/show_bug.cgi?id=44149
- // TODO(ctguil): We may want to remove this check as webkit stabilizes.
- if (child.isDetached())
- continue;
-
- // Children may duplicated in the webkit accessibility tree. Only add a
- // child once for the web accessibility tree.
- // https://bugs.webkit.org/show_bug.cgi?id=58930
- if (child_ids.find(child_id) != child_ids.end())
- continue;
- child_ids.insert(child_id);
-
- // Some nodes appear in the tree in more than one place: for example,
- // a cell in a table appears as a child of both a row and a column.
- // Only recursively add child nodes that have this node as its
- // unignored parent. For child nodes that are actually parented to
- // somethinng else, store only the ID.
- //
- // As an exception, also add children of an iframe element.
- // https://bugs.webkit.org/show_bug.cgi?id=57066
- if (is_iframe || IsParentUnignoredOf(src, child)) {
- dst->children.push_back(AccessibilityNodeData());
- SerializeAccessibilityNode(child,
- &dst->children.back(),
- include_children);
- } else {
- dst->indirect_child_ids.push_back(child_id);
- }
- }
+ // Add the ids of *indirect* children - those who are children of this node,
+ // but whose parent is *not* this node. One example is a table
+ // cell, which is a child of both a row and a column. Because the cell's
+ // parent is the row, the row adds it as a child, and the column adds it
+ // as an indirect child.
+ int child_count = src.childCount();
+ for (int i = 0; i < child_count; ++i) {
+ WebAccessibilityObject child = src.childAt(i);
+ if (!is_iframe && !child.isDetached() && !IsParentUnignoredOf(src, child))
+ dst->indirect_child_ids.push_back(child.axID());
}
}
+bool ShouldIncludeChildNode(
+ const WebAccessibilityObject& parent,
+ const WebAccessibilityObject& child) {
+ switch(parent.roleValue()) {
+ case WebKit::WebAccessibilityRoleSlider:
+ case WebKit::WebAccessibilityRoleEditableText:
+ case WebKit::WebAccessibilityRoleTextArea:
+ case WebKit::WebAccessibilityRoleTextField:
+ return false;
+ default:
+ break;
+ }
+
+ // The child may be invalid due to issues in webkit accessibility code.
+ // Don't add children that are invalid thus preventing a crash.
+ // https://bugs.webkit.org/show_bug.cgi?id=44149
+ // TODO(ctguil): We may want to remove this check as webkit stabilizes.
+ if (child.isDetached())
+ return false;
+
+ // Skip children whose parent isn't this - see indirect_child_ids, above.
+ // As an exception, include children of an iframe element.
+ bool is_iframe = false;
+ WebNode node = parent.node();
+ if (!node.isNull() && node.isElementNode()) {
+ WebElement element = node.to<WebElement>();
+ is_iframe = (element.tagName() == ASCIIToUTF16("IFRAME"));
+ }
+
+ return (is_iframe || IsParentUnignoredOf(parent, child));
+}
+
} // namespace content
diff --git a/content/renderer/accessibility/accessibility_node_serializer.h b/content/renderer/accessibility/accessibility_node_serializer.h
index d0fd74b..f0e9525 100644
--- a/content/renderer/accessibility/accessibility_node_serializer.h
+++ b/content/renderer/accessibility/accessibility_node_serializer.h
@@ -12,8 +12,11 @@ namespace content {
void SerializeAccessibilityNode(
const WebKit::WebAccessibilityObject& src,
- AccessibilityNodeData* dst,
- bool include_children);
+ AccessibilityNodeData* dst);
+
+bool ShouldIncludeChildNode(
+ const WebKit::WebAccessibilityObject& parent,
+ const WebKit::WebAccessibilityObject& child);
} // namespace content
diff --git a/content/renderer/accessibility/renderer_accessibility.h b/content/renderer/accessibility/renderer_accessibility.h
index 8f33d2b..b4b56dd 100644
--- a/content/renderer/accessibility/renderer_accessibility.h
+++ b/content/renderer/accessibility/renderer_accessibility.h
@@ -49,7 +49,7 @@ class RenderViewImpl;
//
// What both subclasses have in common is that they are responsible for
//
-class RendererAccessibility : public RenderViewObserver {
+class CONTENT_EXPORT RendererAccessibility : public RenderViewObserver {
public:
explicit RendererAccessibility(RenderViewImpl* render_view);
virtual ~RendererAccessibility();
diff --git a/content/renderer/accessibility/renderer_accessibility_browsertest.cc b/content/renderer/accessibility/renderer_accessibility_browsertest.cc
index 304822e..fbd83367 100644
--- a/content/renderer/accessibility/renderer_accessibility_browsertest.cc
+++ b/content/renderer/accessibility/renderer_accessibility_browsertest.cc
@@ -3,17 +3,57 @@
// found in the LICENSE file.
#include "base/utf_string_conversions.h"
-#include "content/common/accessibility_messages.h"
#include "content/common/accessibility_node_data.h"
#include "content/common/view_messages.h"
#include "content/public/test/render_view_test.h"
+#include "content/renderer/accessibility/renderer_accessibility_complete.h"
#include "content/renderer/render_view_impl.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/WebKit/Source/Platform/chromium/public/WebSize.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebAccessibilityObject.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
+using WebKit::WebAccessibilityObject;
+using WebKit::WebDocument;
+
namespace content {
+class TestRendererAccessibilityComplete : public RendererAccessibilityComplete {
+ public:
+ explicit TestRendererAccessibilityComplete(RenderViewImpl* render_view)
+ : RendererAccessibilityComplete(render_view),
+ browser_tree_node_count_(0) {
+ }
+
+ int browser_tree_node_count() { return browser_tree_node_count_; }
+
+ struct TestBrowserTreeNode : public BrowserTreeNode {
+ TestBrowserTreeNode(TestRendererAccessibilityComplete* owner)
+ : owner_(owner) {
+ owner_->browser_tree_node_count_++;
+ }
+
+ virtual ~TestBrowserTreeNode() {
+ owner_->browser_tree_node_count_--;
+ }
+
+ private:
+ TestRendererAccessibilityComplete* owner_;
+ };
+
+ virtual BrowserTreeNode* CreateBrowserTreeNode() {
+ return new TestBrowserTreeNode(this);
+ }
+
+ void SendPendingAccessibilityNotifications() {
+ RendererAccessibilityComplete::SendPendingAccessibilityNotifications();
+ }
+
+private:
+ int browser_tree_node_count_;
+};
+
class RendererAccessibilityTest : public RenderViewTest {
public:
RendererAccessibilityTest() {}
@@ -38,10 +78,16 @@ class RendererAccessibilityTest : public RenderViewTest {
ASSERT_TRUE(message);
Tuple1<std::vector<AccessibilityHostMsg_NotificationParams> > param;
AccessibilityHostMsg_Notifications::Read(message, &param);
- ASSERT_EQ(param.a.size(), 1U);
+ ASSERT_GE(param.a.size(), 1U);
*params = param.a[0];
}
+ int CountAccessibilityNodesSentToBrowser() {
+ AccessibilityHostMsg_NotificationParams notification;
+ GetLastAccNotification(&notification);
+ return notification.nodes.size();
+ }
+
protected:
IPC::TestSink* sink_;
@@ -49,6 +95,10 @@ class RendererAccessibilityTest : public RenderViewTest {
};
TEST_F(RendererAccessibilityTest, EditableTextModeFocusNotifications) {
+ // This is not a test of true web accessibility, it's a test of
+ // a mode used on Windows 8 in Metro mode where an extremely simplified
+ // accessibility tree containing only the current focused node is
+ // generated.
SetMode(AccessibilityModeEditableTextOnly);
// Set a minimum size and give focus so simulated events work.
@@ -76,16 +126,16 @@ TEST_F(RendererAccessibilityTest, EditableTextModeFocusNotifications) {
GetLastAccNotification(&notification);
EXPECT_EQ(notification.notification_type,
AccessibilityNotificationLayoutComplete);
- EXPECT_EQ(notification.includes_children, true);
EXPECT_EQ(notification.id, 1);
- EXPECT_EQ(notification.acc_tree.id, 1);
- EXPECT_EQ(notification.acc_tree.role,
+ EXPECT_EQ(notification.nodes.size(), 2U);
+ EXPECT_EQ(notification.nodes[0].id, 1);
+ EXPECT_EQ(notification.nodes[0].role,
AccessibilityNodeData::ROLE_ROOT_WEB_AREA);
- EXPECT_EQ(notification.acc_tree.state,
+ EXPECT_EQ(notification.nodes[0].state,
(1U << AccessibilityNodeData::STATE_READONLY) |
(1U << AccessibilityNodeData::STATE_FOCUSABLE) |
(1U << AccessibilityNodeData::STATE_FOCUSED));
- EXPECT_EQ(notification.acc_tree.children.size(), 1U);
+ EXPECT_EQ(notification.nodes[0].child_ids.size(), 1U);
}
// Now focus the input element, and check everything again.
@@ -97,19 +147,18 @@ TEST_F(RendererAccessibilityTest, EditableTextModeFocusNotifications) {
GetLastAccNotification(&notification);
EXPECT_EQ(notification.notification_type,
AccessibilityNotificationFocusChanged);
- EXPECT_EQ(notification.includes_children, true);
EXPECT_EQ(notification.id, 3);
- EXPECT_EQ(notification.acc_tree.id, 1);
- EXPECT_EQ(notification.acc_tree.role,
+ EXPECT_EQ(notification.nodes[0].id, 1);
+ EXPECT_EQ(notification.nodes[0].role,
AccessibilityNodeData::ROLE_ROOT_WEB_AREA);
- EXPECT_EQ(notification.acc_tree.state,
+ EXPECT_EQ(notification.nodes[0].state,
(1U << AccessibilityNodeData::STATE_READONLY) |
(1U << AccessibilityNodeData::STATE_FOCUSABLE));
- EXPECT_EQ(notification.acc_tree.children.size(), 1U);
- EXPECT_EQ(notification.acc_tree.children[0].id, 3);
- EXPECT_EQ(notification.acc_tree.children[0].role,
+ EXPECT_EQ(notification.nodes[0].child_ids.size(), 1U);
+ EXPECT_EQ(notification.nodes[1].id, 3);
+ EXPECT_EQ(notification.nodes[1].role,
AccessibilityNodeData::ROLE_GROUP);
- EXPECT_EQ(notification.acc_tree.children[0].state,
+ EXPECT_EQ(notification.nodes[1].state,
(1U << AccessibilityNodeData::STATE_FOCUSABLE) |
(1U << AccessibilityNodeData::STATE_FOCUSED));
}
@@ -122,7 +171,7 @@ TEST_F(RendererAccessibilityTest, EditableTextModeFocusNotifications) {
AccessibilityHostMsg_NotificationParams notification;
GetLastAccNotification(&notification);
EXPECT_EQ(notification.id, 4);
- EXPECT_EQ(notification.acc_tree.children[0].state,
+ EXPECT_EQ(notification.nodes[1].state,
(1U << AccessibilityNodeData::STATE_FOCUSABLE) |
(1U << AccessibilityNodeData::STATE_FOCUSED));
}
@@ -134,7 +183,7 @@ TEST_F(RendererAccessibilityTest, EditableTextModeFocusNotifications) {
AccessibilityHostMsg_NotificationParams notification;
GetLastAccNotification(&notification);
EXPECT_EQ(notification.id, 5);
- EXPECT_EQ(notification.acc_tree.children[0].state,
+ EXPECT_EQ(notification.nodes[1].state,
(1U << AccessibilityNodeData::STATE_FOCUSABLE) |
(1U << AccessibilityNodeData::STATE_FOCUSED));
}
@@ -146,7 +195,7 @@ TEST_F(RendererAccessibilityTest, EditableTextModeFocusNotifications) {
AccessibilityHostMsg_NotificationParams notification;
GetLastAccNotification(&notification);
EXPECT_EQ(notification.id, 6);
- EXPECT_EQ(notification.acc_tree.children[0].state,
+ EXPECT_EQ(notification.nodes[1].state,
(1U << AccessibilityNodeData::STATE_FOCUSABLE) |
(1U << AccessibilityNodeData::STATE_FOCUSED));
}
@@ -159,7 +208,7 @@ TEST_F(RendererAccessibilityTest, EditableTextModeFocusNotifications) {
AccessibilityHostMsg_NotificationParams notification;
GetLastAccNotification(&notification);
EXPECT_EQ(notification.id, 7);
- EXPECT_EQ(notification.acc_tree.children[0].state,
+ EXPECT_EQ(notification.nodes[1].state,
(1U << AccessibilityNodeData::STATE_FOCUSABLE) |
(1U << AccessibilityNodeData::STATE_FOCUSED) |
(1U << AccessibilityNodeData::STATE_READONLY));
@@ -172,7 +221,7 @@ TEST_F(RendererAccessibilityTest, EditableTextModeFocusNotifications) {
AccessibilityHostMsg_NotificationParams notification;
GetLastAccNotification(&notification);
EXPECT_EQ(notification.id, 8);
- EXPECT_EQ(notification.acc_tree.children[0].state,
+ EXPECT_EQ(notification.nodes[1].state,
(1U << AccessibilityNodeData::STATE_FOCUSABLE) |
(1U << AccessibilityNodeData::STATE_FOCUSED) |
(1U << AccessibilityNodeData::STATE_READONLY));
@@ -189,4 +238,163 @@ TEST_F(RendererAccessibilityTest, EditableTextModeFocusNotifications) {
}
}
+TEST_F(RendererAccessibilityTest, SendFullAccessibilityTreeOnReload) {
+ // The job of RendererAccessibilityComplete is to serialize the
+ // accessibility tree built by WebKit and send it to the browser.
+ // When the accessibility tree changes, it tries to send only
+ // the nodes that actually changed or were reparented. This test
+ // ensures that the messages sent are correct in cases when a page
+ // reloads, and that internal state is properly garbage-collected.
+ std::string html =
+ "<body>"
+ " <div role='group' id='A'>"
+ " <div role='group' id='A1'></div>"
+ " <div role='group' id='A2'></div>"
+ " </div>"
+ "</body>";
+ LoadHTML(html.c_str());
+
+ // Creating a RendererAccessibilityComplete should sent the tree
+ // to the browser.
+ scoped_ptr<TestRendererAccessibilityComplete> accessibility(
+ new TestRendererAccessibilityComplete(view()));
+ accessibility->SendPendingAccessibilityNotifications();
+ EXPECT_EQ(4, accessibility->browser_tree_node_count());
+ EXPECT_EQ(4, CountAccessibilityNodesSentToBrowser());
+
+ // If we post another notification but the tree doesn't change,
+ // we should only send 1 node to the browser.
+ sink_->ClearMessages();
+ WebDocument document = view()->GetWebView()->mainFrame()->document();
+ WebAccessibilityObject root_obj = document.accessibilityObject();
+ accessibility->HandleWebAccessibilityNotification(
+ root_obj,
+ WebKit::WebAccessibilityNotificationLayoutComplete);
+ accessibility->SendPendingAccessibilityNotifications();
+ EXPECT_EQ(4, accessibility->browser_tree_node_count());
+ EXPECT_EQ(1, CountAccessibilityNodesSentToBrowser());
+ {
+ // Make sure it's the root object that was updated.
+ AccessibilityHostMsg_NotificationParams notification;
+ GetLastAccNotification(&notification);
+ EXPECT_EQ(root_obj.axID(), notification.nodes[0].id);
+ }
+
+ // If we reload the page and send a notification, we should send
+ // all 4 nodes to the browser. Also double-check that we didn't
+ // leak any of the old BrowserTreeNodes.
+ LoadHTML(html.c_str());
+ document = view()->GetWebView()->mainFrame()->document();
+ root_obj = document.accessibilityObject();
+ sink_->ClearMessages();
+ accessibility->HandleWebAccessibilityNotification(
+ root_obj,
+ WebKit::WebAccessibilityNotificationLayoutComplete);
+ accessibility->SendPendingAccessibilityNotifications();
+ EXPECT_EQ(4, accessibility->browser_tree_node_count());
+ EXPECT_EQ(4, CountAccessibilityNodesSentToBrowser());
+
+ // Even if the first notification is sent on an element other than
+ // the root, the whole tree should be updated because we know
+ // the browser doesn't have the root element.
+ LoadHTML(html.c_str());
+ document = view()->GetWebView()->mainFrame()->document();
+ root_obj = document.accessibilityObject();
+ sink_->ClearMessages();
+ const WebAccessibilityObject& first_child = root_obj.firstChild();
+ accessibility->HandleWebAccessibilityNotification(
+ first_child,
+ WebKit::WebAccessibilityNotificationLiveRegionChanged);
+ accessibility->SendPendingAccessibilityNotifications();
+ EXPECT_EQ(4, accessibility->browser_tree_node_count());
+ EXPECT_EQ(4, CountAccessibilityNodesSentToBrowser());
+}
+
+TEST_F(RendererAccessibilityTest, HideAccessibilityObject) {
+ // Test RendererAccessibilityComplete and make sure it sends the
+ // proper notification to the browser when an object in the tree
+ // is hidden, but its children are not.
+ std::string html =
+ "<body>"
+ " <div role='group' id='A'>"
+ " <div role='group' id='B'>"
+ " <div role='group' id='C' style='visibility:visible'>"
+ " </div>"
+ " </div>"
+ " </div>"
+ "</body>";
+ LoadHTML(html.c_str());
+
+ scoped_ptr<TestRendererAccessibilityComplete> accessibility(
+ new TestRendererAccessibilityComplete(view()));
+ accessibility->SendPendingAccessibilityNotifications();
+ EXPECT_EQ(4, accessibility->browser_tree_node_count());
+ EXPECT_EQ(4, CountAccessibilityNodesSentToBrowser());
+
+ // Hide node 'B' ('C' stays visible).
+ ExecuteJavaScript(
+ "document.getElementById('B').style.visibility = 'hidden';");
+ // Force layout now.
+ ExecuteJavaScript("document.getElementById('B').offsetLeft;");
+
+ // Send a childrenChanged on 'A'.
+ sink_->ClearMessages();
+ WebDocument document = view()->GetWebView()->mainFrame()->document();
+ WebAccessibilityObject root_obj = document.accessibilityObject();
+ WebAccessibilityObject node_a = root_obj.childAt(0);
+ accessibility->HandleWebAccessibilityNotification(
+ node_a,
+ WebKit::WebAccessibilityNotificationChildrenChanged);
+
+ accessibility->SendPendingAccessibilityNotifications();
+ EXPECT_EQ(3, accessibility->browser_tree_node_count());
+ AccessibilityHostMsg_NotificationParams notification;
+ GetLastAccNotification(&notification);
+ ASSERT_EQ(2U, notification.nodes.size());
+ EXPECT_EQ(2, CountAccessibilityNodesSentToBrowser());
+}
+
+TEST_F(RendererAccessibilityTest, ShowAccessibilityObject) {
+ // Test RendererAccessibilityComplete and make sure it sends the
+ // proper notification to the browser when an object in the tree
+ // is shown, causing its own already-visible children to be
+ // reparented to it.
+ std::string html =
+ "<body>"
+ " <div role='group' id='A'>"
+ " <div role='group' id='B' style='visibility:hidden'>"
+ " <div role='group' id='C' style='visibility:visible'>"
+ " </div>"
+ " </div>"
+ " </div>"
+ "</body>";
+ LoadHTML(html.c_str());
+
+ scoped_ptr<TestRendererAccessibilityComplete> accessibility(
+ new TestRendererAccessibilityComplete(view()));
+ accessibility->SendPendingAccessibilityNotifications();
+ EXPECT_EQ(3, accessibility->browser_tree_node_count());
+ EXPECT_EQ(3, CountAccessibilityNodesSentToBrowser());
+
+ // Show node 'B', then send a childrenChanged on 'A'.
+ ExecuteJavaScript(
+ "document.getElementById('B').style.visibility = 'visible';");
+ ExecuteJavaScript("document.getElementById('B').offsetLeft;");
+
+ sink_->ClearMessages();
+ WebDocument document = view()->GetWebView()->mainFrame()->document();
+ WebAccessibilityObject root_obj = document.accessibilityObject();
+ WebAccessibilityObject node_a = root_obj.childAt(0);
+ accessibility->HandleWebAccessibilityNotification(
+ node_a,
+ WebKit::WebAccessibilityNotificationChildrenChanged);
+
+ accessibility->SendPendingAccessibilityNotifications();
+ EXPECT_EQ(4, accessibility->browser_tree_node_count());
+ AccessibilityHostMsg_NotificationParams notification;
+ GetLastAccNotification(&notification);
+ ASSERT_EQ(3U, notification.nodes.size());
+ EXPECT_EQ(3, CountAccessibilityNodesSentToBrowser());
+}
+
} // namespace content
diff --git a/content/renderer/accessibility/renderer_accessibility_complete.cc b/content/renderer/accessibility/renderer_accessibility_complete.cc
index 80923d1..6ece9dc 100644
--- a/content/renderer/accessibility/renderer_accessibility_complete.cc
+++ b/content/renderer/accessibility/renderer_accessibility_complete.cc
@@ -85,6 +85,9 @@ bool WebAccessibilityNotificationToAccessibilityNotification(
case WebKit::WebAccessibilityNotificationSelectedTextChanged:
*type = AccessibilityNotificationSelectedTextChanged;
break;
+ case WebKit::WebAccessibilityNotificationTextChanged:
+ *type = AccessibilityNotificationTextChanged;
+ break;
case WebKit::WebAccessibilityNotificationValueChanged:
*type = AccessibilityNotificationValueChanged;
break;
@@ -134,6 +137,7 @@ bool RendererAccessibilityComplete::OnMessageReceived(
OnScrollToPoint)
IPC_MESSAGE_HANDLER(AccessibilityMsg_SetTextSelection,
OnSetTextSelection)
+ IPC_MESSAGE_HANDLER(AccessibilityMsg_FatalError, OnFatalError)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
@@ -261,11 +265,6 @@ void RendererAccessibilityComplete::SendPendingAccessibilityNotifications() {
AccessibilityHostMsg_NotificationParams& notification =
src_notifications[i];
- // TODO(dtseng): Come up with a cleaner way of deciding to include children.
- WebAccessibilityObject rootObject = document.accessibilityObject();
- int root_id = rootObject.axID();
- bool includes_children = ShouldIncludeChildren(notification) ||
- root_id == notification.id;
WebAccessibilityObject obj = document.accessibilityObjectFromID(
notification.id);
if (!obj.updateBackingStoreAndCheckValidity())
@@ -276,11 +275,12 @@ void RendererAccessibilityComplete::SendPendingAccessibilityNotifications() {
// notification on a node before the page has loaded. Work our way
// up the parent chain until we find a node the browser has, or until
// we reach the root.
+ WebAccessibilityObject root_object = document.accessibilityObject();
+ int root_id = root_object.axID();
while (browser_id_map_.find(obj.axID()) == browser_id_map_.end() &&
!obj.isDetached() &&
obj.axID() != root_id) {
obj = obj.parentObject();
- includes_children = true;
if (notification.notification_type ==
AccessibilityNotificationChildrenChanged) {
notification.id = obj.axID();
@@ -323,39 +323,28 @@ void RendererAccessibilityComplete::SendPendingAccessibilityNotifications() {
if (!is_child_of_parent) {
obj = parent;
notification.id = obj.axID();
- includes_children = true;
}
}
// Allow WebKit to cache intermediate results since we're doing a bunch
// of read-only queries at once.
- rootObject.startCachingComputedObjectAttributesUntilTreeMutates();
+ root_object.startCachingComputedObjectAttributesUntilTreeMutates();
AccessibilityHostMsg_NotificationParams notification_msg;
notification_msg.notification_type = notification.notification_type;
notification_msg.id = notification.id;
- notification_msg.includes_children = includes_children;
- SerializeAccessibilityNode(obj,
- &notification_msg.acc_tree,
- includes_children);
- if (obj.axID() == root_id) {
- DCHECK_EQ(notification_msg.acc_tree.role,
- AccessibilityNodeData::ROLE_WEB_AREA);
- notification_msg.acc_tree.role =
- AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
- }
+ SerializeChangedNodes(obj, &notification_msg.nodes);
notification_msgs.push_back(notification_msg);
- if (includes_children)
- UpdateBrowserTree(notification_msg.acc_tree);
-
#ifndef NDEBUG
if (logging_) {
+ AccessibilityNodeDataTreeNode tree;
+ MakeAccessibilityNodeDataTree(notification_msg.nodes, &tree);
LOG(INFO) << "Accessibility update: \n"
<< "routing id=" << routing_id()
<< " notification="
<< AccessibilityNotificationToString(notification.notification_type)
- << "\n" << notification_msg.acc_tree.DebugString(true);
+ << "\n" << tree.DebugString(true);
}
#endif
}
@@ -363,34 +352,118 @@ void RendererAccessibilityComplete::SendPendingAccessibilityNotifications() {
Send(new AccessibilityHostMsg_Notifications(routing_id(), notification_msgs));
}
-void RendererAccessibilityComplete::UpdateBrowserTree(
- const AccessibilityNodeData& renderer_node) {
+RendererAccessibilityComplete::BrowserTreeNode*
+RendererAccessibilityComplete::CreateBrowserTreeNode() {
+ return new RendererAccessibilityComplete::BrowserTreeNode();
+}
+
+void RendererAccessibilityComplete::SerializeChangedNodes(
+ const WebKit::WebAccessibilityObject& obj,
+ std::vector<AccessibilityNodeData>* dst) {
+ // This method has three responsibilities:
+ // 1. Serialize |obj| into an AccessibilityNodeData, and append it to
+ // the end of the |dst| vector to be send to the browser process.
+ // 2. Determine if |obj| has any new children that the browser doesn't
+ // know about yet, and call SerializeChangedNodes recursively on those.
+ // 3. Update our internal data structure that keeps track of what nodes
+ // the browser knows about.
+
+ // First, find the BrowserTreeNode for this id in our data structure where
+ // we keep track of what accessibility objects the browser already knows
+ // about. If we don't find it, then this must be the new root of the
+ // accessibility tree.
BrowserTreeNode* browser_node = NULL;
base::hash_map<int32, BrowserTreeNode*>::iterator iter =
- browser_id_map_.find(renderer_node.id);
+ browser_id_map_.find(obj.axID());
if (iter != browser_id_map_.end()) {
browser_node = iter->second;
- ClearBrowserTreeNode(browser_node);
} else {
- DCHECK_EQ(renderer_node.role, AccessibilityNodeData::ROLE_ROOT_WEB_AREA);
if (browser_root_) {
ClearBrowserTreeNode(browser_root_);
browser_id_map_.erase(browser_root_->id);
delete browser_root_;
}
- browser_root_ = new BrowserTreeNode;
+ browser_root_ = CreateBrowserTreeNode();
browser_node = browser_root_;
- browser_node->id = renderer_node.id;
+ browser_node->id = obj.axID();
browser_id_map_[browser_node->id] = browser_node;
}
- browser_node->children.reserve(renderer_node.children.size());
- for (size_t i = 0; i < renderer_node.children.size(); ++i) {
- BrowserTreeNode* browser_child_node = new BrowserTreeNode;
- browser_child_node->id = renderer_node.children[i].id;
- browser_id_map_[browser_child_node->id] = browser_child_node;
- browser_node->children.push_back(browser_child_node);
- UpdateBrowserTree(renderer_node.children[i]);
+
+ // Serialize this node. This fills in all of the fields in
+ // AccessibilityNodeData except child_ids, which we handle below.
+ dst->push_back(AccessibilityNodeData());
+ AccessibilityNodeData* serialized_node = &dst->back();
+ SerializeAccessibilityNode(obj, serialized_node);
+ if (serialized_node->id == browser_root_->id)
+ serialized_node->role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
+
+ // Create set of the ids of the children of |obj| so we can quickly look
+ // up which children are new and which ones were there before.
+ base::hash_set<int32> new_child_ids;
+ for (unsigned i = 0; i < obj.childCount(); i++) {
+ WebAccessibilityObject child = obj.childAt(i);
+ if (ShouldIncludeChildNode(obj, child)) {
+ new_child_ids.insert(child.axID());
+ }
+ }
+
+ // Go through the old children and delete subtrees for child
+ // ids that are no longer present, and create a map from
+ // id to BrowserTreeNode for the rest. It's important to delete
+ // first in a separate pass so that nodes that are reparented
+ // don't end up children of two different parents in the middle
+ // of an update, which can lead to a double-free.
+ base::hash_map<int32, BrowserTreeNode*> browser_child_id_map;
+ std::vector<BrowserTreeNode*> old_children;
+ old_children.swap(browser_node->children);
+ for (size_t i = 0; i < old_children.size(); i++) {
+ BrowserTreeNode* old_child = old_children[i];
+ int old_child_id = old_child->id;
+ if (new_child_ids.find(old_child_id) == new_child_ids.end()) {
+ browser_id_map_.erase(old_child_id);
+ ClearBrowserTreeNode(old_child);
+ delete old_child;
+ } else {
+ browser_child_id_map[old_child_id] = old_child;
+ }
}
+
+ // Iterate over the children, make note of the ones that are new
+ // and need to be serialized, and update the BrowserTreeNode
+ // data structure to reflect the new tree.
+ std::vector<WebAccessibilityObject> children_to_serialize;
+ int child_count = obj.childCount();
+ browser_node->children.reserve(child_count);
+ for (int i = 0; i < child_count; i++) {
+ WebAccessibilityObject child = obj.childAt(i);
+ int child_id = child.axID();
+
+ // Checks to make sure the child is valid, attached to this node,
+ // and one we want to include in the tree.
+ if (!ShouldIncludeChildNode(obj, child))
+ continue;
+
+ // No need to do anything more with children that aren't new;
+ // the browser will reuse its existing object.
+ if (new_child_ids.find(child_id) == new_child_ids.end())
+ continue;
+
+ new_child_ids.erase(child_id);
+ serialized_node->child_ids.push_back(child_id);
+ if (browser_child_id_map.find(child_id) != browser_child_id_map.end()) {
+ browser_node->children.push_back(browser_child_id_map[child_id]);
+ } else {
+ BrowserTreeNode* new_child = CreateBrowserTreeNode();
+ new_child->id = child_id;
+ browser_node->children.push_back(new_child);
+ browser_id_map_[child_id] = new_child;
+ children_to_serialize.push_back(child);
+ }
+ }
+
+ // Serialize all of the new children, recursively.
+ for (size_t i = 0; i < children_to_serialize.size(); ++i)
+ SerializeChangedNodes(children_to_serialize[i], dst);
}
void RendererAccessibilityComplete::ClearBrowserTreeNode(
@@ -540,6 +613,10 @@ void RendererAccessibilityComplete::OnSetFocus(int acc_obj_id) {
obj.setFocused(true);
}
+void RendererAccessibilityComplete::OnFatalError() {
+ CHECK(false);
+}
+
bool RendererAccessibilityComplete::ShouldIncludeChildren(
const AccessibilityHostMsg_NotificationParams& notification) {
AccessibilityNotification type = notification.notification_type;
diff --git a/content/renderer/accessibility/renderer_accessibility_complete.h b/content/renderer/accessibility/renderer_accessibility_complete.h
index e03c0e9..b338a90 100644
--- a/content/renderer/accessibility/renderer_accessibility_complete.h
+++ b/content/renderer/accessibility/renderer_accessibility_complete.h
@@ -31,7 +31,8 @@ class RenderViewImpl;
// a serialized representation of that tree whenever it changes. It also
// handles requests from the browser to perform accessibility actions on
// nodes in the tree (e.g., change focus, or click on a button).
-class RendererAccessibilityComplete : public RendererAccessibility {
+class CONTENT_EXPORT RendererAccessibilityComplete
+ : public RendererAccessibility {
public:
explicit RendererAccessibilityComplete(RenderViewImpl* render_view);
virtual ~RendererAccessibilityComplete();
@@ -46,28 +47,33 @@ class RendererAccessibilityComplete : public RendererAccessibility {
const WebKit::WebAccessibilityObject& obj,
WebKit::WebAccessibilityNotification notification) OVERRIDE;
- private:
- // Handle an accessibility notification to be sent to the browser process.
- void HandleAccessibilityNotification(
- const WebKit::WebAccessibilityObject& obj,
- AccessibilityNotification notification);
-
// In order to keep track of what nodes the browser knows about, we keep a
// representation of the browser tree - just IDs and parent/child
// relationships.
- struct BrowserTreeNode {
+ struct CONTENT_EXPORT BrowserTreeNode {
BrowserTreeNode();
- ~BrowserTreeNode();
+ virtual ~BrowserTreeNode();
int32 id;
std::vector<BrowserTreeNode*> children;
};
+ virtual BrowserTreeNode* CreateBrowserTreeNode();
+
+ protected:
// Send queued notifications from the renderer to the browser.
void SendPendingAccessibilityNotifications();
- // Update our representation of what nodes the browser has, given a
- // tree of nodes.
- void UpdateBrowserTree(const AccessibilityNodeData& renderer_node);
+ private:
+ // Handle an accessibility notification to be sent to the browser process.
+ void HandleAccessibilityNotification(
+ const WebKit::WebAccessibilityObject& obj,
+ AccessibilityNotification notification);
+
+ // Serialize the given accessibility object |obj| and append it to
+ // |dst|, and then recursively also serialize any *new* children of
+ // |obj|, based on what object ids we know the browser already has.
+ void SerializeChangedNodes(const WebKit::WebAccessibilityObject& obj,
+ std::vector<AccessibilityNodeData>* dst);
// Clear the given node and recursively delete all of its descendants
// from the browser tree. (Does not delete |browser_node|).
@@ -80,8 +86,8 @@ class RendererAccessibilityComplete : public RendererAccessibility {
void OnScrollToMakeVisible(int acc_obj_id, gfx::Rect subfocus);
void OnScrollToPoint(int acc_obj_id, gfx::Point point);
void OnSetFocus(int acc_obj_id);
-
void OnSetTextSelection(int acc_obj_id, int start_offset, int end_offset);
+ void OnFatalError();
// Whether or not this notification typically needs to send
// updates to its children, too.
diff --git a/content/renderer/accessibility/renderer_accessibility_focus_only.cc b/content/renderer/accessibility/renderer_accessibility_focus_only.cc
index 601bc16..71da433 100644
--- a/content/renderer/accessibility/renderer_accessibility_focus_only.cc
+++ b/content/renderer/accessibility/renderer_accessibility_focus_only.cc
@@ -92,26 +92,25 @@ void RendererAccessibilityFocusOnly::HandleFocusedNodeChanged(
AccessibilityNotificationFocusChanged :
AccessibilityNotificationLayoutComplete;
- // This means that the new tree we send supercedes any previous tree,
- // not just a previous node.
- notification.includes_children = true;
-
// Set the id that the notification applies to: the root node if nothing
// has focus, otherwise the focused node.
notification.id = node_has_focus ? next_id_ : 1;
// Always include the root of the tree, the document. It always has id 1.
- notification.acc_tree.id = 1;
- notification.acc_tree.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
- notification.acc_tree.state =
+ notification.nodes.push_back(AccessibilityNodeData());
+ AccessibilityNodeData& root = notification.nodes[0];
+ root.id = 1;
+ root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
+ root.state =
(1 << AccessibilityNodeData::STATE_READONLY) |
(1 << AccessibilityNodeData::STATE_FOCUSABLE);
if (!node_has_focus)
- notification.acc_tree.state |= (1 << AccessibilityNodeData::STATE_FOCUSED);
- notification.acc_tree.location = gfx::Rect(render_view_->size());
+ root.state |= (1 << AccessibilityNodeData::STATE_FOCUSED);
+ root.location = gfx::Rect(render_view_->size());
+ root.child_ids.push_back(next_id_);
- notification.acc_tree.children.push_back(AccessibilityNodeData());
- AccessibilityNodeData& child = notification.acc_tree.children[0];
+ notification.nodes.push_back(AccessibilityNodeData());
+ AccessibilityNodeData& child = notification.nodes[1];
child.id = next_id_;
child.role = AccessibilityNodeData::ROLE_GROUP;
@@ -119,7 +118,7 @@ void RendererAccessibilityFocusOnly::HandleFocusedNodeChanged(
child.location = gfx::Rect(
const_cast<WebNode&>(node).to<WebElement>().boundsInViewportSpace());
} else if (render_view_->HasIMETextFocus()) {
- child.location = notification.acc_tree.location;
+ child.location = root.location;
} else {
child.location = gfx::Rect();
}
@@ -138,7 +137,7 @@ void RendererAccessibilityFocusOnly::HandleFocusedNodeChanged(
<< "routing id=" << routing_id()
<< " notification="
<< AccessibilityNotificationToString(notification.notification_type)
- << "\n" << notification.acc_tree.DebugString(true);
+ << "\n" << notification.nodes[0].DebugString(true);
}
#endif