diff options
author | dmazzoni@chromium.org <dmazzoni@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-05-10 19:35:42 +0000 |
---|---|---|
committer | dmazzoni@chromium.org <dmazzoni@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-05-10 19:35:42 +0000 |
commit | fae40a39f0130ab17cb0de4c355341ddd4eb7a00 (patch) | |
tree | 67b4660db445dd55e68ef8fe61d2b79a43190ebf /chrome/browser/browser_accessibility_manager.cc | |
parent | 611b66af1e3a7d747033fa1bcc75506994f16ac5 (diff) | |
download | chromium_src-fae40a39f0130ab17cb0de4c355341ddd4eb7a00.zip chromium_src-fae40a39f0130ab17cb0de4c355341ddd4eb7a00.tar.gz chromium_src-fae40a39f0130ab17cb0de4c355341ddd4eb7a00.tar.bz2 |
Reimplement accessibility of web content by caching the entire
accessibility tree in the browser process.
Adds new RPCs for a browser tab to request accessibility info from
a renderer; the renderer responds with a complete tree of
accessibility metadata for the entire DOM, which is then cached
in the RenderWidgetHostView. This part is cross-platform and will
help with accessibility on both Windows and Mac OS X.
For Windows, MSAA support for web content has been rewritten to
use this new cache. Tested in JAWS and NVDA screen readers.
Using Chrome with a screen reader is now fast and stable,
unlike the previous implementation. However, note that most
advanced functionality is still not supported, and much work remains
to make Chrome work well with a screen reader. This is a necessary
step to improve stability first.
BUG=25564
BUG=13291
TEST=See http://codereview.chromium.org/1806001
Review URL: http://codereview.chromium.org/1637018
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@46842 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/browser_accessibility_manager.cc')
-rw-r--r-- | chrome/browser/browser_accessibility_manager.cc | 231 |
1 files changed, 100 insertions, 131 deletions
diff --git a/chrome/browser/browser_accessibility_manager.cc b/chrome/browser/browser_accessibility_manager.cc index cd53e72..8e37719 100644 --- a/chrome/browser/browser_accessibility_manager.cc +++ b/chrome/browser/browser_accessibility_manager.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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. @@ -8,161 +8,130 @@ #include "chrome/browser/browser_accessibility.h" #include "chrome/browser/renderer_host/render_process_host.h" #include "chrome/browser/renderer_host/render_view_host.h" -#include "chrome/common/notification_service.h" #include "chrome/common/render_messages.h" using webkit_glue::WebAccessibility; -// The time in ms after which we give up and return an error when processing an -// accessibility message and no response has been received from the renderer. -static const int kAccessibilityMessageTimeOut = 10000; - -// static -BrowserAccessibilityManager* BrowserAccessibilityManager::GetInstance() { - return Singleton<BrowserAccessibilityManager>::get(); +// Factory method to create an instance of BrowserAccessibility +BrowserAccessibility* BrowserAccessibilityFactory::Create() { + CComObject<BrowserAccessibility>* instance; + HRESULT hr = CComObject<BrowserAccessibility>::CreateInstance(&instance); + DCHECK(SUCCEEDED(hr)); + return instance->NewReference(); } -BrowserAccessibilityManager::BrowserAccessibilityManager() { - registrar_.Add(this, NotificationType::RENDERER_PROCESS_TERMINATED, - NotificationService::AllSources()); +// static +// 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. +LONG BrowserAccessibilityManager::next_child_id_ = -1; + +BrowserAccessibilityManager::BrowserAccessibilityManager( + HWND parent_hwnd, + const webkit_glue::WebAccessibility& src, + BrowserAccessibilityFactory* factory) + : parent_hwnd_(parent_hwnd), + factory_(factory) { + HRESULT hr = ::CreateStdAccessibleObject( + parent_hwnd_, OBJID_WINDOW, IID_IAccessible, + reinterpret_cast<void **>(&window_iaccessible_)); + DCHECK(SUCCEEDED(hr)); + root_ = CreateAccessibilityTree(NULL, src, 0); + if (!focus_) + focus_ = root_; } BrowserAccessibilityManager::~BrowserAccessibilityManager() { - // Clear hashmap. - render_process_host_map_.clear(); + // Clients could still hold references to some nodes of the tree, so + // calling Inactivate 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 return E_FAIL; + root_->InactivateTree(); + root_->Release(); } -STDMETHODIMP BrowserAccessibilityManager::CreateAccessibilityInstance( - REFIID iid, int acc_obj_id, int routing_id, int process_id, - HWND parent_hwnd, void** interface_ptr) { - if (IID_IUnknown == iid || IID_IDispatch == iid || IID_IAccessible == iid) { - CComObject<BrowserAccessibility>* instance = NULL; - - HRESULT hr = CComObject<BrowserAccessibility>::CreateInstance(&instance); - DCHECK(SUCCEEDED(hr)); - - if (!instance) - return E_FAIL; - - ScopedComPtr<IAccessible> accessibility_instance(instance); - - // Set class member variables. - instance->Initialize(acc_obj_id, routing_id, process_id, parent_hwnd); - - // Retrieve the RenderViewHost connected to this request. - RenderViewHost* rvh = RenderViewHost::FromID(process_id, routing_id); - - // Update cache with RenderProcessHost/BrowserAccessibility pair. - if (rvh && rvh->process()) { - render_process_host_map_.insert(MapEntry(rvh->process()->id(), instance)); - } else { - // No RenderProcess active for this instance. - return E_FAIL; - } - - // All is well, assign the temp instance to the output pointer. - *interface_ptr = accessibility_instance.Detach(); - return S_OK; - } - // No supported interface found, return error. - *interface_ptr = NULL; - return E_NOINTERFACE; +BrowserAccessibility* BrowserAccessibilityManager::GetRoot() { + return root_; } -bool BrowserAccessibilityManager::RequestAccessibilityInfo( - WebAccessibility::InParams* in, int routing_id, int process_id) { - // Create and populate IPC message structure, for retrieval of accessibility - // information from the renderer. - WebAccessibility::InParams in_params; - in_params.object_id = in->object_id; - in_params.function_id = in->function_id; - in_params.child_id = in->child_id; - in_params.input_long1 = in->input_long1; - in_params.input_long2 = in->input_long2; - - // Retrieve the RenderViewHost connected to this request. - RenderViewHost* rvh = RenderViewHost::FromID(process_id, routing_id); - - // Send accessibility information retrieval message to the renderer. - bool success = false; - if (rvh && rvh->process() && rvh->process()->HasConnection()) { - IPC::SyncMessage* msg = - new ViewMsg_GetAccessibilityInfo(routing_id, in_params, &out_params_); - // Necessary for the send to keep the UI responsive. - msg->EnableMessagePumping(); - success = rvh->process()->SendWithTimeout(msg, - kAccessibilityMessageTimeOut); +BrowserAccessibility* BrowserAccessibilityManager::GetFromChildID( + LONG child_id) { + base::hash_map<LONG, BrowserAccessibility*>::iterator iter = + child_id_map_.find(child_id); + if (iter != child_id_map_.end()) { + return iter->second; + } else { + return NULL; } - return success; } -bool BrowserAccessibilityManager::ChangeAccessibilityFocus(int acc_obj_id, - int process_id, - int routing_id) { - BrowserAccessibility* browser_acc = - GetBrowserAccessibility(process_id, routing_id); - if (browser_acc) { - // Notify Access Technology that there was a change in keyboard focus. - ::NotifyWinEvent(EVENT_OBJECT_FOCUS, browser_acc->parent_hwnd(), - OBJID_CLIENT, static_cast<LONG>(acc_obj_id)); - return true; - } - return false; +IAccessible* BrowserAccessibilityManager::GetParentWindowIAccessible() { + return window_iaccessible_; } -bool BrowserAccessibilityManager::OnAccessibilityObjectStateChange( - int acc_obj_id, int process_id, int routing_id) { - BrowserAccessibility* browser_acc = - GetBrowserAccessibility(process_id, routing_id); - if (browser_acc) { - // Notify Access Technology that there was a change in state. - ::NotifyWinEvent(EVENT_OBJECT_STATECHANGE, browser_acc->parent_hwnd(), - OBJID_CLIENT, static_cast<LONG>(acc_obj_id)); - return true; - } - return false; +HWND BrowserAccessibilityManager::GetParentHWND() { + return parent_hwnd_; } -const WebAccessibility::OutParams& BrowserAccessibilityManager::response() { - return out_params_; +BrowserAccessibility* BrowserAccessibilityManager::GetFocus( + BrowserAccessibility* root) { + if (focus_ && (!root || focus_->IsDescendantOf(root))) + return focus_; + + return NULL; } -BrowserAccessibility* BrowserAccessibilityManager::GetBrowserAccessibility( - int process_id, int routing_id) { - // Retrieve the BrowserAccessibility connected to the requester's id. There - // could be multiple BrowserAccessibility connected to the given |process_id|, - // but they all have the same parent HWND, so using the first hit is fine. - RenderProcessHostMap::iterator it = - render_process_host_map_.lower_bound(process_id); +void BrowserAccessibilityManager::OnAccessibilityFocusChange(int renderer_id) { + base::hash_map<int, LONG>::iterator iter = + renderer_id_to_child_id_map_.find(renderer_id); + if (iter == renderer_id_to_child_id_map_.end()) + return; + + LONG child_id = iter->second; + base::hash_map<LONG, BrowserAccessibility*>::iterator uniq_iter = + child_id_map_.find(child_id); + if (uniq_iter != child_id_map_.end()) + focus_ = uniq_iter->second; + ::NotifyWinEvent(EVENT_OBJECT_FOCUS, parent_hwnd_, OBJID_CLIENT, child_id); +} - RenderProcessHostMap::iterator end_of_matching_objects = - render_process_host_map_.upper_bound(process_id); +void BrowserAccessibilityManager::OnAccessibilityObjectStateChange( + int renderer_id) { + base::hash_map<int, LONG>::iterator iter = + renderer_id_to_child_id_map_.find(renderer_id); + if (iter == renderer_id_to_child_id_map_.end()) + return; - for (; it != end_of_matching_objects; ++it) { - if (it->second && it->second->routing_id() == routing_id) - return it->second; - } - return NULL; + LONG child_id = iter->second; + ::NotifyWinEvent(EVENT_OBJECT_FOCUS, parent_hwnd_, OBJID_CLIENT, child_id); } -void BrowserAccessibilityManager::Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details) { - DCHECK(type == NotificationType::RENDERER_PROCESS_TERMINATED); - RenderProcessHost* rph = Source<RenderProcessHost>(source).ptr(); - DCHECK(rph); - - RenderProcessHostMap::iterator it = - render_process_host_map_.lower_bound(rph->id()); - - RenderProcessHostMap::iterator end_of_matching_objects = - render_process_host_map_.upper_bound(rph->id()); - - for (; it != end_of_matching_objects; ++it) { - if (it->second) { - // Set all matching BrowserAccessibility instances to inactive state. - // TODO(klink): Do more active memory cleanup as well. - it->second->set_instance_active(false); - } +BrowserAccessibility* BrowserAccessibilityManager::CreateAccessibilityTree( + BrowserAccessibility* parent, + const webkit_glue::WebAccessibility& src, + int index_in_parent) { + BrowserAccessibility* instance = factory_->Create(); + + // 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. + LONG child_id = next_child_id_; + next_child_id_--; + if (next_child_id_ == -2000000000) + next_child_id_ = -1; + + 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) + focus_ = instance; + 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; } |