summaryrefslogtreecommitdiffstats
path: root/chrome/browser/accessibility/browser_accessibility_manager.cc
diff options
context:
space:
mode:
authordmazzoni@chromium.org <dmazzoni@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-06-23 22:19:39 +0000
committerdmazzoni@chromium.org <dmazzoni@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-06-23 22:19:39 +0000
commit219edb72922b55aeb817029660e44795ba1c74ae (patch)
treec6a76e3afc1d9ef6a53397dc66041848b770ff27 /chrome/browser/accessibility/browser_accessibility_manager.cc
parentcffc1ec53b392b9ace2cd246e9014ac0b9161ae0 (diff)
downloadchromium_src-219edb72922b55aeb817029660e44795ba1c74ae.zip
chromium_src-219edb72922b55aeb817029660e44795ba1c74ae.tar.gz
chromium_src-219edb72922b55aeb817029660e44795ba1c74ae.tar.bz2
Revert 90265 - Move browser accessibility code from chrome to content.
BUG=85932 TEST=none Review URL: http://codereview.chromium.org/7233022 TBR=dmazzoni@chromium.org Review URL: http://codereview.chromium.org/7247022 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@90279 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/accessibility/browser_accessibility_manager.cc')
-rw-r--r--chrome/browser/accessibility/browser_accessibility_manager.cc373
1 files changed, 373 insertions, 0 deletions
diff --git a/chrome/browser/accessibility/browser_accessibility_manager.cc b/chrome/browser/accessibility/browser_accessibility_manager.cc
new file mode 100644
index 0000000..4c58228
--- /dev/null
+++ b/chrome/browser/accessibility/browser_accessibility_manager.cc
@@ -0,0 +1,373 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/accessibility/browser_accessibility_manager.h"
+
+#include "base/logging.h"
+#include "chrome/browser/accessibility/browser_accessibility.h"
+#include "content/common/view_messages.h"
+
+using webkit_glue::WebAccessibility;
+
+BrowserAccessibility* BrowserAccessibilityFactory::Create() {
+ return BrowserAccessibility::Create();
+}
+
+// Start child IDs at -1 and decrement each time, because clients use
+// child IDs of 1, 2, 3, ... to access the children of an object by
+// index, so we use negative IDs to clearly distinguish between indices
+// and unique IDs.
+// static
+int32 BrowserAccessibilityManager::next_child_id_ = -1;
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
+// There's no OS-specific implementation of BrowserAccessibilityManager
+// on Unix, so just instantiate the base class.
+// static
+BrowserAccessibilityManager* BrowserAccessibilityManager::Create(
+ gfx::NativeView parent_view,
+ const WebAccessibility& src,
+ BrowserAccessibilityDelegate* delegate,
+ BrowserAccessibilityFactory* factory) {
+ return new BrowserAccessibilityManager(
+ parent_view, src, delegate, factory);
+}
+#endif
+
+BrowserAccessibilityManager::BrowserAccessibilityManager(
+ gfx::NativeView parent_view,
+ const WebAccessibility& src,
+ BrowserAccessibilityDelegate* delegate,
+ BrowserAccessibilityFactory* factory)
+ : parent_view_(parent_view),
+ delegate_(delegate),
+ factory_(factory),
+ focus_(NULL) {
+ root_ = CreateAccessibilityTree(NULL, src, 0);
+ if (!focus_)
+ SetFocus(root_, false);
+}
+
+// static
+int32 BrowserAccessibilityManager::GetNextChildID() {
+ // Get the next child ID, and wrap around when we get near the end
+ // of a 32-bit integer range. It's okay to wrap around; we just want
+ // to avoid it as long as possible because clients may cache the ID of
+ // an object for a while to determine if they've seen it before.
+ next_child_id_--;
+ if (next_child_id_ == -2000000000)
+ next_child_id_ = -1;
+
+ return next_child_id_;
+}
+
+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);
+}
+
+BrowserAccessibility* BrowserAccessibilityManager::GetRoot() {
+ return root_;
+}
+
+BrowserAccessibility* BrowserAccessibilityManager::GetFromChildID(
+ int32 child_id) {
+ base::hash_map<int32, BrowserAccessibility*>::iterator iter =
+ child_id_map_.find(child_id);
+ if (iter != child_id_map_.end()) {
+ return iter->second;
+ } else {
+ return NULL;
+ }
+}
+
+BrowserAccessibility* BrowserAccessibilityManager::GetFromRendererID(
+ int32 renderer_id) {
+ base::hash_map<int32, int32>::iterator iter =
+ renderer_id_to_child_id_map_.find(renderer_id);
+ if (iter == renderer_id_to_child_id_map_.end())
+ return NULL;
+
+ int32 child_id = iter->second;
+ return GetFromChildID(child_id);
+}
+
+void BrowserAccessibilityManager::Remove(int32 child_id, int32 renderer_id) {
+ child_id_map_.erase(child_id);
+ renderer_id_to_child_id_map_.erase(renderer_id);
+}
+
+void BrowserAccessibilityManager::OnAccessibilityNotifications(
+ const std::vector<ViewHostMsg_AccessibilityNotification_Params>& params) {
+ for (uint32 index = 0; index < params.size(); index++) {
+ const ViewHostMsg_AccessibilityNotification_Params& param = params[index];
+
+ switch (param.notification_type) {
+ case ViewHostMsg_AccessibilityNotification_Type::
+ NOTIFICATION_TYPE_CHECK_STATE_CHANGED:
+ OnAccessibilityObjectStateChange(param.acc_obj);
+ break;
+ case ViewHostMsg_AccessibilityNotification_Type::
+ NOTIFICATION_TYPE_CHILDREN_CHANGED:
+ OnAccessibilityObjectChildrenChange(param.acc_obj);
+ break;
+ case ViewHostMsg_AccessibilityNotification_Type::
+ NOTIFICATION_TYPE_FOCUS_CHANGED:
+ OnAccessibilityObjectFocusChange(param.acc_obj);
+ break;
+ case ViewHostMsg_AccessibilityNotification_Type::
+ NOTIFICATION_TYPE_LOAD_COMPLETE:
+ OnAccessibilityObjectLoadComplete(param.acc_obj);
+ break;
+ case ViewHostMsg_AccessibilityNotification_Type::
+ NOTIFICATION_TYPE_VALUE_CHANGED:
+ OnAccessibilityObjectValueChange(param.acc_obj);
+ break;
+ case ViewHostMsg_AccessibilityNotification_Type::
+ NOTIFICATION_TYPE_SELECTED_TEXT_CHANGED:
+ OnAccessibilityObjectTextChange(param.acc_obj);
+ break;
+ default:
+ DCHECK(0);
+ break;
+ }
+ }
+}
+
+void BrowserAccessibilityManager::OnAccessibilityObjectStateChange(
+ const WebAccessibility& acc_obj) {
+ BrowserAccessibility* new_browser_acc = UpdateNode(acc_obj, false);
+ if (!new_browser_acc)
+ return;
+
+ NotifyAccessibilityEvent(
+ ViewHostMsg_AccessibilityNotification_Type::
+ NOTIFICATION_TYPE_CHECK_STATE_CHANGED,
+ new_browser_acc);
+}
+
+void BrowserAccessibilityManager::OnAccessibilityObjectChildrenChange(
+ const WebAccessibility& acc_obj) {
+ BrowserAccessibility* new_browser_acc = UpdateNode(acc_obj, true);
+ if (!new_browser_acc)
+ return;
+
+ NotifyAccessibilityEvent(
+ ViewHostMsg_AccessibilityNotification_Type::
+ NOTIFICATION_TYPE_CHILDREN_CHANGED,
+ new_browser_acc);
+}
+
+void BrowserAccessibilityManager::OnAccessibilityObjectFocusChange(
+ const WebAccessibility& acc_obj) {
+ BrowserAccessibility* new_browser_acc = UpdateNode(acc_obj, false);
+ if (!new_browser_acc)
+ return;
+
+ SetFocus(new_browser_acc, false);
+ if (delegate_ && delegate_->HasFocus()) {
+ GotFocus();
+ } else if (!delegate_) {
+ // Mac currently does not have a BrowserAccessibilityDelegate.
+ NotifyAccessibilityEvent(
+ ViewHostMsg_AccessibilityNotification_Type::
+ NOTIFICATION_TYPE_FOCUS_CHANGED,
+ focus_);
+ }
+}
+
+void BrowserAccessibilityManager::OnAccessibilityObjectLoadComplete(
+ const WebAccessibility& acc_obj) {
+ SetFocus(NULL, false);
+ root_->InternalReleaseReference(true);
+
+ root_ = CreateAccessibilityTree(NULL, acc_obj, 0);
+ if (!focus_)
+ SetFocus(root_, false);
+
+ NotifyAccessibilityEvent(
+ ViewHostMsg_AccessibilityNotification_Type::
+ NOTIFICATION_TYPE_LOAD_COMPLETE,
+ root_);
+ if (delegate_ && delegate_->HasFocus())
+ GotFocus();
+}
+
+void BrowserAccessibilityManager::OnAccessibilityObjectValueChange(
+ const WebAccessibility& acc_obj) {
+ BrowserAccessibility* new_browser_acc = UpdateNode(acc_obj, false);
+ if (!new_browser_acc)
+ return;
+
+ NotifyAccessibilityEvent(
+ ViewHostMsg_AccessibilityNotification_Type::
+ NOTIFICATION_TYPE_VALUE_CHANGED,
+ new_browser_acc);
+}
+
+void BrowserAccessibilityManager::OnAccessibilityObjectTextChange(
+ const WebAccessibility& acc_obj) {
+ BrowserAccessibility* new_browser_acc = UpdateNode(acc_obj, false);
+ if (!new_browser_acc)
+ return;
+
+ NotifyAccessibilityEvent(
+ ViewHostMsg_AccessibilityNotification_Type::
+ NOTIFICATION_TYPE_SELECTED_TEXT_CHANGED,
+ new_browser_acc);
+}
+
+void BrowserAccessibilityManager::GotFocus() {
+ // TODO(ctguil): Remove when tree update logic handles focus changes.
+ if (!focus_)
+ return;
+
+ NotifyAccessibilityEvent(
+ ViewHostMsg_AccessibilityNotification_Type::
+ NOTIFICATION_TYPE_FOCUS_CHANGED,
+ focus_);
+}
+
+gfx::NativeView BrowserAccessibilityManager::GetParentView() {
+ return parent_view_;
+}
+
+BrowserAccessibility* BrowserAccessibilityManager::GetFocus(
+ BrowserAccessibility* root) {
+ if (focus_ && (!root || focus_->IsDescendantOf(root)))
+ return focus_;
+
+ return NULL;
+}
+
+void BrowserAccessibilityManager::SetFocus(
+ BrowserAccessibility* node, bool notify) {
+ if (focus_)
+ focus_->InternalReleaseReference(false);
+ focus_ = node;
+ if (focus_)
+ focus_->InternalAddReference();
+
+ if (notify && node && delegate_)
+ delegate_->SetAccessibilityFocus(node->renderer_id());
+}
+
+void BrowserAccessibilityManager::DoDefaultAction(
+ const BrowserAccessibility& node) {
+ if (delegate_)
+ delegate_->AccessibilityDoDefaultAction(node.renderer_id());
+}
+
+gfx::Rect BrowserAccessibilityManager::GetViewBounds() {
+ if (delegate_)
+ return delegate_->GetViewBounds();
+ return gfx::Rect();
+}
+
+BrowserAccessibility* BrowserAccessibilityManager::UpdateNode(
+ const WebAccessibility& src,
+ bool include_children) {
+ 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())
+ return NULL;
+
+ int32 child_id = iter->second;
+ BrowserAccessibility* current = GetFromChildID(child_id);
+ if (!current)
+ return NULL;
+
+ if (!include_children) {
+ DCHECK_EQ(0U, src.children.size());
+ current->Initialize(
+ this,
+ current->parent(),
+ current->child_id(),
+ current->index_in_parent(),
+ src);
+ return current;
+ }
+
+ 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.
+ current =
+ CreateAccessibilityTree(current_parent, src, current_index_in_parent);
+
+ // 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);
+
+ DCHECK(focus_);
+ if (!focus_->instance_active())
+ SetFocus(root_, false);
+
+ return current;
+}
+
+BrowserAccessibility* BrowserAccessibilityManager::CreateAccessibilityTree(
+ BrowserAccessibility* parent,
+ const WebAccessibility& src,
+ int index_in_parent) {
+ BrowserAccessibility* instance = NULL;
+ int32 child_id = 0;
+ base::hash_map<int32, int32>::iterator iter =
+ renderer_id_to_child_id_map_.find(src.id);
+
+ // 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);
+ }
+
+ // If the node has changed roles, don't reuse a BrowserAccessibility
+ // object, that could confuse a screen reader.
+ if (instance && instance->role() != src.role)
+ instance = NULL;
+
+ // 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)) {
+ NOTREACHED();
+ instance = NULL;
+ }
+
+ if (instance) {
+ // If we're reusing a node, update its parent and increment its
+ // reference count.
+ instance->UpdateParent(parent, index_in_parent);
+ instance->InternalAddReference();
+ } else {
+ // Otherwise, create a new instance.
+ instance = factory_->Create();
+ child_id = GetNextChildID();
+ }
+
+ instance->Initialize(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 >> WebAccessibility::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);
+ instance->AddChild(child);
+ }
+
+ return instance;
+}