diff options
author | klink@chromium.org <klink@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-09-30 23:56:50 +0000 |
---|---|---|
committer | klink@chromium.org <klink@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-09-30 23:56:50 +0000 |
commit | 266eb6ff422cf97eef1e56e78d0a9e73e9aef34a (patch) | |
tree | e2534c18efb1e38683a0b835956d2c067df7f6be | |
parent | eed46c091b85924b097a5d506b3da6371b93007f (diff) | |
download | chromium_src-266eb6ff422cf97eef1e56e78d0a9e73e9aef34a.zip chromium_src-266eb6ff422cf97eef1e56e78d0a9e73e9aef34a.tar.gz chromium_src-266eb6ff422cf97eef1e56e78d0a9e73e9aef34a.tar.bz2 |
Adds MSAA/IAccessible exposure of web content.
Review URL: http://codereview.chromium.org/4057
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@2738 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/browser.vcproj | 24 | ||||
-rw-r--r-- | chrome/browser/browser_accessibility.cc | 555 | ||||
-rw-r--r-- | chrome/browser/browser_accessibility.h | 169 | ||||
-rw-r--r-- | chrome/browser/browser_accessibility_manager.cc | 167 | ||||
-rw-r--r-- | chrome/browser/browser_accessibility_manager.h | 108 | ||||
-rw-r--r-- | chrome/browser/iaccessible_function_ids.h | 38 | ||||
-rw-r--r-- | chrome/browser/render_view_host.cc | 6 | ||||
-rw-r--r-- | chrome/browser/render_widget_host_hwnd.cc | 51 | ||||
-rw-r--r-- | chrome/browser/render_widget_host_hwnd.h | 8 | ||||
-rw-r--r-- | chrome/common/render_messages.h | 129 | ||||
-rw-r--r-- | chrome/common/render_messages_internal.h | 14 | ||||
-rw-r--r-- | chrome/renderer/render_view.cc | 39 | ||||
-rw-r--r-- | chrome/renderer/render_view.h | 8 | ||||
-rw-r--r-- | webkit/build/glue/glue.vcproj | 8 | ||||
-rw-r--r-- | webkit/glue/glue_accessibility.cc | 281 | ||||
-rw-r--r-- | webkit/glue/glue_accessibility.h | 68 |
16 files changed, 1666 insertions, 7 deletions
diff --git a/chrome/browser/browser.vcproj b/chrome/browser/browser.vcproj index e87a3e0..70830bc 100644 --- a/chrome/browser/browser.vcproj +++ b/chrome/browser/browser.vcproj @@ -1959,6 +1959,30 @@ </File> </Filter> <Filter + Name="Accessibility" + > + <File + RelativePath=".\browser_accessibility.cc" + > + </File> + <File + RelativePath=".\browser_accessibility.h" + > + </File> + <File + RelativePath=".\browser_accessibility_manager.cc" + > + </File> + <File + RelativePath=".\browser_accessibility_manager.h" + > + </File> + <File + RelativePath=".\iaccessible_function_ids.h" + > + </File> + </Filter> + <Filter Name="Download" > <File diff --git a/chrome/browser/browser_accessibility.cc b/chrome/browser/browser_accessibility.cc new file mode 100644 index 0000000..bb6b8d2 --- /dev/null +++ b/chrome/browser/browser_accessibility.cc @@ -0,0 +1,555 @@ +// Copyright (c) 2006-2008 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/browser_accessibility.h" + +#include "chrome/browser/browser_accessibility_manager.h" +#include "chrome/browser/iaccessible_function_ids.h" + +BrowserAccessibility::BrowserAccessibility() + : iaccessible_id_(-1), + instance_active_(true) { +} + +HRESULT BrowserAccessibility::accDoDefaultAction(VARIANT var_id) { + if (!instance_active()) { + // Instance no longer active, fail gracefully. + // TODO(klink): Once we have MSAA events, change these fails for having + // BrowserAccessibilityManager firing the right event. + return E_FAIL; + } + + if (var_id.vt != VT_I4) + return E_INVALIDARG; + + if (!RequestAccessibilityInfo(IACCESSIBLE_FUNC_ACCDODEFAULTACTION, var_id, + NULL, NULL)) { + return E_FAIL; + } + + if (!response().return_code) + return S_FALSE; + + return S_OK; +} + +STDMETHODIMP BrowserAccessibility::accHitTest(LONG x_left, LONG y_top, + VARIANT* child) { + if (!instance_active()) { + // Instance no longer active, fail gracefully. + return E_FAIL; + } + + if (!child) + return E_INVALIDARG; + + if (!parent_hwnd()) { + // Parent HWND needed for coordinate conversion. + return E_FAIL; + } + + // Convert coordinates to test from screen into client window coordinates, to + // maintain sandbox functionality on renderer side. + POINT p = {x_left, y_top}; + ::ScreenToClient(parent_hwnd(), &p); + + if (!RequestAccessibilityInfo(IACCESSIBLE_FUNC_ACCHITTEST, EmptyVariant(), + p.x, p.y)) { + return E_FAIL; + } + + if (!response().return_code) { + // The point is outside of the object's boundaries. + child->vt = VT_EMPTY; + return S_FALSE; + } + + if (response().output_long1 == -1) { + if (CreateInstance(IID_IAccessible, response().iaccessible_id, + reinterpret_cast<void**>(&child->pdispVal)) == S_OK) { + child->vt = VT_DISPATCH; + // Increment the reference count for the retrieved interface. + child->pdispVal->AddRef(); + } else { + return E_NOINTERFACE; + } + } else { + child->vt = VT_I4; + child->lVal = response().output_long1; + } + + return S_OK; +} + +STDMETHODIMP BrowserAccessibility::accLocation(LONG* x_left, LONG* y_top, + LONG* width, LONG* height, + VARIANT var_id) { + if (!instance_active()) { + // Instance no longer active, fail gracefully. + return E_FAIL; + } + + if (var_id.vt != VT_I4 || !x_left || !y_top || !width || !height || + !parent_hwnd()) { + return E_INVALIDARG; + } + + if (!RequestAccessibilityInfo(IACCESSIBLE_FUNC_ACCLOCATION, var_id, NULL, + NULL)) { + return E_FAIL; + } + + POINT top_left = {0, 0}; + + // Find the top left corner of the containing window in screen coords, and + // adjust the output position by this amount. + ::ClientToScreen(parent_hwnd(), &top_left); + + *x_left = response().output_long1 + top_left.x; + *y_top = response().output_long2 + top_left.y; + + *width = response().output_long3; + *height = response().output_long4; + + return S_OK; +} + +STDMETHODIMP BrowserAccessibility::accNavigate(LONG nav_dir, VARIANT start, + VARIANT* end) { + if (!instance_active()) { + // Instance no longer active, fail gracefully. + return E_FAIL; + } + + if (start.vt != VT_I4 || !end) + return E_INVALIDARG; + + if ((nav_dir == NAVDIR_LASTCHILD || nav_dir == NAVDIR_FIRSTCHILD) && + start.lVal != CHILDID_SELF) { + // MSAA states that navigating to first/last child can only be from self. + return E_INVALIDARG; + } + + if (nav_dir == NAVDIR_DOWN || nav_dir == NAVDIR_UP || + nav_dir == NAVDIR_LEFT || nav_dir == NAVDIR_RIGHT) { + // Directions not implemented, matching Mozilla and IE. + return E_INVALIDARG; + } + + if (!RequestAccessibilityInfo(IACCESSIBLE_FUNC_ACCNAVIGATE, start, nav_dir, + NULL)) { + return E_FAIL; + } + + if (!response().return_code) { + // No screen element was found in the specified direction. + end->vt = VT_EMPTY; + return S_FALSE; + } + + if (response().output_long1 == -1) { + if (CreateInstance(IID_IAccessible, response().iaccessible_id, + reinterpret_cast<void**>(&end->pdispVal)) == S_OK) { + end->vt = VT_DISPATCH; + // Increment the reference count for the retrieved interface. + end->pdispVal->AddRef(); + } else { + return E_NOINTERFACE; + } + } else { + end->vt = VT_I4; + end->lVal = response().output_long1; + } + + return S_OK; +} + +STDMETHODIMP BrowserAccessibility::get_accChild(VARIANT var_child, + IDispatch** disp_child) { + if (!instance_active()) { + // Instance no longer active, fail gracefully. + return E_FAIL; + } + + if (var_child.vt != VT_I4 || !disp_child) + return E_INVALIDARG; + + // If var_child is the parent, remain with the same IDispatch. + if (var_child.lVal == CHILDID_SELF && iaccessible_id_ != 0) + return S_OK; + + if (!RequestAccessibilityInfo(IACCESSIBLE_FUNC_GET_ACCCHILD, var_child, NULL, + NULL)) { + return E_FAIL; + } + + if (!response().return_code) { + // When at a leaf, children are handled by the parent object. + *disp_child = NULL; + return S_FALSE; + } + + // Retrieve the IUnknown interface for the parent view, and assign the + // IDispatch returned. + if (CreateInstance(IID_IAccessible, response().iaccessible_id, + reinterpret_cast<void**>(disp_child)) == S_OK) { + // Increment the reference count for the retrieved interface. + (*disp_child)->AddRef(); + return S_OK; + } else { + return E_NOINTERFACE; + } +} + +STDMETHODIMP BrowserAccessibility::get_accChildCount(LONG* child_count) { + if (!instance_active()) { + // Instance no longer active, fail gracefully. + return E_FAIL; + } + + if (!child_count) + return E_INVALIDARG; + + if (!RequestAccessibilityInfo(IACCESSIBLE_FUNC_GET_ACCCHILDCOUNT, + EmptyVariant(), NULL, NULL)) { + return E_FAIL; + } + + *child_count = response().output_long1; + return S_OK; +} + +STDMETHODIMP BrowserAccessibility::get_accDefaultAction(VARIANT var_id, + BSTR* def_action) { + if (!instance_active()) { + // Instance no longer active, fail gracefully. + return E_FAIL; + } + + if (var_id.vt != VT_I4 || !def_action) + return E_INVALIDARG; + + if (!RequestAccessibilityInfo(IACCESSIBLE_FUNC_GET_ACCDEFAULTACTION, var_id, + NULL, NULL)) { + return E_FAIL; + } + + if (!response().return_code) { + // No string found. + return S_FALSE; + } + + *def_action = CComBSTR(response().output_string.c_str()).Detach(); + + DCHECK(*def_action); + return S_OK; +} + +STDMETHODIMP BrowserAccessibility::get_accDescription(VARIANT var_id, + BSTR* desc) { + if (!instance_active()) { + // Instance no longer active, fail gracefully. + return E_FAIL; + } + + if (var_id.vt != VT_I4 || !desc) + return E_INVALIDARG; + + if (!RequestAccessibilityInfo(IACCESSIBLE_FUNC_GET_ACCDESCRIPTION, var_id, + NULL, NULL)) { + return E_FAIL; + } + + if (!response().return_code) { + // No string found. + return S_FALSE; + } + + *desc = CComBSTR(response().output_string.c_str()).Detach(); + + DCHECK(*desc); + return S_OK; +} + +STDMETHODIMP BrowserAccessibility::get_accFocus(VARIANT* focus_child) { + if (!instance_active()) { + // Instance no longer active, fail gracefully. + return E_FAIL; + } + + if (!focus_child) + return E_INVALIDARG; + + if (!RequestAccessibilityInfo(IACCESSIBLE_FUNC_GET_ACCFOCUS, EmptyVariant(), + NULL, NULL)) { + return E_FAIL; + } + + if (!response().return_code) { + // The window that contains this object is not the active window. + focus_child->vt = VT_EMPTY; + return S_FALSE; + } + + if (response().output_long1 == -1) { + if (CreateInstance(IID_IAccessible, response().iaccessible_id, + reinterpret_cast<void**>(&focus_child->pdispVal)) == S_OK) { + focus_child->vt = VT_DISPATCH; + // Increment the reference count for the retrieved interface. + focus_child->pdispVal->AddRef(); + } else { + return E_NOINTERFACE; + } + } else { + focus_child->vt = VT_I4; + focus_child->lVal = response().output_long1; + } + + return S_OK; +} + +STDMETHODIMP BrowserAccessibility::get_accHelp(VARIANT var_id, BSTR* help) { + if (!instance_active()) { + // Instance no longer active, fail gracefully. + return E_FAIL; + } + + if (var_id.vt != VT_I4 || !help) + return E_INVALIDARG; + + if (!RequestAccessibilityInfo(IACCESSIBLE_FUNC_GET_ACCHELP, var_id, NULL, + NULL)) { + return E_FAIL; + } + + if (!response().return_code) { + // No string found. + return S_FALSE; + } + + *help = CComBSTR(response().output_string.c_str()).Detach(); + + DCHECK(*help); + return S_OK; +} + +STDMETHODIMP BrowserAccessibility::get_accKeyboardShortcut(VARIANT var_id, + BSTR* acc_key) { + if (!instance_active()) { + // Instance no longer active, fail gracefully. + return E_FAIL; + } + + if (var_id.vt != VT_I4 || !acc_key) + return E_INVALIDARG; + + if (!RequestAccessibilityInfo(IACCESSIBLE_FUNC_GET_ACCKEYBOARDSHORTCUT, + var_id, NULL, NULL)) { + return E_FAIL; + } + + if (!response().return_code) { + // No string found. + return S_FALSE; + } + + *acc_key = CComBSTR(response().output_string.c_str()).Detach(); + + DCHECK(*acc_key); + return S_OK; +} + +STDMETHODIMP BrowserAccessibility::get_accName(VARIANT var_id, BSTR* name) { + if (!instance_active()) { + // Instance no longer active, fail gracefully. + return E_FAIL; + } + + if (var_id.vt != VT_I4 || !name) + return E_INVALIDARG; + + if (!RequestAccessibilityInfo(IACCESSIBLE_FUNC_GET_ACCNAME, var_id, NULL, + NULL)) { + return E_FAIL; + } + + if (!response().return_code) { + // No string found. + return S_FALSE; + } + + *name = CComBSTR(response().output_string.c_str()).Detach(); + + DCHECK(*name); + return S_OK; +} + +STDMETHODIMP BrowserAccessibility::get_accParent(IDispatch** disp_parent) { + if (!instance_active()) { + // Instance no longer active, fail gracefully. + return E_FAIL; + } + + if (!disp_parent || !parent_hwnd()) + return E_INVALIDARG; + + // Root node's parent is the containing HWND's IAccessible. + if (iaccessible_id() == 0) { + // For an object that has no parent (e.g. root), point the accessible parent + // to the default implementation. + HRESULT hr = + ::CreateStdAccessibleObject(parent_hwnd(), OBJID_WINDOW, + IID_IAccessible, + reinterpret_cast<void**>(disp_parent)); + + if (!SUCCEEDED(hr)) { + *disp_parent = NULL; + return S_FALSE; + } + return S_OK; + } + + if (!RequestAccessibilityInfo(IACCESSIBLE_FUNC_GET_ACCPARENT, EmptyVariant(), + NULL, NULL)) { + return E_FAIL; + } + + if (!response().return_code) { + // No parent exists for this object. + return S_FALSE; + } + + // Retrieve the IUnknown interface for the parent view, and assign the + // IDispatch returned. + if (CreateInstance(IID_IAccessible, response().iaccessible_id, + reinterpret_cast<void**>(disp_parent)) == S_OK) { + // Increment the reference count for the retrieved interface. + (*disp_parent)->AddRef(); + return S_OK; + } else { + return E_NOINTERFACE; + } +} + +STDMETHODIMP BrowserAccessibility::get_accRole(VARIANT var_id, VARIANT* role) { + if (!instance_active()) { + // Instance no longer active, fail gracefully. + return E_FAIL; + } + + if (var_id.vt != VT_I4 || !role) + return E_INVALIDARG; + + if (!RequestAccessibilityInfo(IACCESSIBLE_FUNC_GET_ACCROLE, var_id, NULL, + NULL)) { + return E_FAIL; + } + + role->vt = VT_I4; + role->lVal = response().output_long1; + + return S_OK; +} + +STDMETHODIMP BrowserAccessibility::get_accState(VARIANT var_id, + VARIANT* state) { + if (!instance_active()) { + // Instance no longer active, fail gracefully. + return E_FAIL; + } + + if (var_id.vt != VT_I4 || !state) + return E_INVALIDARG; + + if (!RequestAccessibilityInfo(IACCESSIBLE_FUNC_GET_ACCSTATE, var_id, NULL, + NULL)) { + return E_FAIL; + } + + state->vt = VT_I4; + state->lVal = response().output_long1; + + return S_OK; +} + +STDMETHODIMP BrowserAccessibility::get_accValue(VARIANT var_id, BSTR* value) { + if (!instance_active()) { + // Instance no longer active, fail gracefully. + return E_FAIL; + } + + if (var_id.vt != VT_I4 || !value) + return E_INVALIDARG; + + if (!RequestAccessibilityInfo(IACCESSIBLE_FUNC_GET_ACCVALUE, var_id, NULL, + NULL)) { + return E_FAIL; + } + + if (!response().return_code) { + // No string found. + return S_FALSE; + } + + *value = CComBSTR(response().output_string.c_str()).Detach(); + + DCHECK(*value); + return S_OK; +} + +STDMETHODIMP BrowserAccessibility::accSelect(LONG flags_select, + VARIANT var_id) { + return DISP_E_MEMBERNOTFOUND; +} + +STDMETHODIMP BrowserAccessibility::get_accHelpTopic(BSTR* help_file, + VARIANT var_id, + LONG* topic_id) { + if (help_file) { + *help_file = NULL; + } + if (topic_id) { + *topic_id = static_cast<LONG>(-1); + } + return DISP_E_MEMBERNOTFOUND; +} + +STDMETHODIMP BrowserAccessibility::get_accSelection(VARIANT* selected) { + if (selected) + selected->vt = VT_EMPTY; + + return DISP_E_MEMBERNOTFOUND; +} + +STDMETHODIMP BrowserAccessibility::put_accName(VARIANT var_id, BSTR put_name) { + return DISP_E_MEMBERNOTFOUND; +} + +STDMETHODIMP BrowserAccessibility::put_accValue(VARIANT var_id, BSTR put_val) { + return DISP_E_MEMBERNOTFOUND; +} + +STDMETHODIMP BrowserAccessibility::CreateInstance(REFIID iid, + int iaccessible_id, + void** interface_ptr) { + return BrowserAccessibilityManager::Instance()->CreateAccessibilityInstance( + iid, iaccessible_id, instance_id(), interface_ptr); +} + +bool BrowserAccessibility::RequestAccessibilityInfo(int iaccessible_func_id, + VARIANT var_id, LONG input1, + LONG input2) { + return BrowserAccessibilityManager::Instance()->RequestAccessibilityInfo( + iaccessible_id(), instance_id(), iaccessible_func_id, var_id, input1, + input2); +} + +ViewHostMsg_Accessibility_Out_Params BrowserAccessibility::response() { + return BrowserAccessibilityManager::Instance()->response(); +} + +HWND BrowserAccessibility::parent_hwnd() { + return BrowserAccessibilityManager::Instance()->parent_hwnd(instance_id()); +} + diff --git a/chrome/browser/browser_accessibility.h b/chrome/browser/browser_accessibility.h new file mode 100644 index 0000000..5032183 --- /dev/null +++ b/chrome/browser/browser_accessibility.h @@ -0,0 +1,169 @@ +// Copyright (c) 2006-2008 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. + +#ifndef CHROME_BROWSER_BROWSER_ACCESSIBILITY_H_ +#define CHROME_BROWSER_BROWSER_ACCESSIBILITY_H_ + +#include <atlbase.h> +#include <atlcom.h> + +#include <oleacc.h> + +#include "chrome/common/render_messages.h" + +//////////////////////////////////////////////////////////////////////////////// +// +// BrowserAccessibility +// +// Class implementing the MSAA IAccessible COM interface for the +// Browser-Renderer communication of MSAA information, providing accessibility +// to be used by screen readers and other assistive technology (AT). +// +//////////////////////////////////////////////////////////////////////////////// +class ATL_NO_VTABLE BrowserAccessibility + : public CComObjectRootEx<CComMultiThreadModel>, + public IDispatchImpl<IAccessible, &IID_IAccessible, &LIBID_Accessibility> { + public: + BEGIN_COM_MAP(BrowserAccessibility) + COM_INTERFACE_ENTRY2(IDispatch, IAccessible) + COM_INTERFACE_ENTRY(IAccessible) + END_COM_MAP() + + BrowserAccessibility(); + ~BrowserAccessibility() {} + + // Supported IAccessible methods. + + // Performs the default action on a given object. + STDMETHODIMP accDoDefaultAction(VARIANT var_id); + + // Retrieves the child element or child object at a given point on the screen. + STDMETHODIMP accHitTest(LONG x_left, LONG y_top, VARIANT* child); + + // Retrieves the specified object's current screen location. + STDMETHODIMP accLocation(LONG* x_left, + LONG* y_top, + LONG* width, + LONG* height, + VARIANT var_id); + + // Traverses to another UI element and retrieves the object. + STDMETHODIMP accNavigate(LONG nav_dir, VARIANT start, VARIANT* end); + + // Retrieves an IDispatch interface pointer for the specified child. + STDMETHODIMP get_accChild(VARIANT var_child, IDispatch** disp_child); + + // Retrieves the number of accessible children. + STDMETHODIMP get_accChildCount(LONG* child_count); + + // Retrieves a string that describes the object's default action. + STDMETHODIMP get_accDefaultAction(VARIANT var_id, BSTR* default_action); + + // Retrieves the object's description. + STDMETHODIMP get_accDescription(VARIANT var_id, BSTR* desc); + + // Retrieves the object that has the keyboard focus. + STDMETHODIMP get_accFocus(VARIANT* focus_child); + + // Retrieves the help information associated with the object. + STDMETHODIMP get_accHelp(VARIANT var_id, BSTR* help); + + // Retrieves the specified object's shortcut. + STDMETHODIMP get_accKeyboardShortcut(VARIANT var_id, BSTR* access_key); + + // Retrieves the name of the specified object. + STDMETHODIMP get_accName(VARIANT var_id, BSTR* name); + + // Retrieves the IDispatch interface of the object's parent. + STDMETHODIMP get_accParent(IDispatch** disp_parent); + + // Retrieves information describing the role of the specified object. + STDMETHODIMP get_accRole(VARIANT var_id, VARIANT* role); + + // Retrieves the current state of the specified object. + STDMETHODIMP get_accState(VARIANT var_id, VARIANT* state); + + // Returns the value associated with the object. + STDMETHODIMP get_accValue(VARIANT var_id, BSTR* value); + + // Non-supported (by WebKit) IAccessible methods. + STDMETHODIMP accSelect(LONG flags_sel, VARIANT var_id); + + STDMETHODIMP get_accHelpTopic(BSTR* help_file, + VARIANT var_id, + LONG* topic_id); + + STDMETHODIMP get_accSelection(VARIANT* selected); + + // Deprecated functions, not implemented here. + STDMETHODIMP put_accName(VARIANT var_id, BSTR put_name); + STDMETHODIMP put_accValue(VARIANT var_id, BSTR put_val); + + // Modify/retrieve the unique id of this IAccessible instance. + void set_iaccessible_id(int iaccessible_id) { + iaccessible_id_ = iaccessible_id; + } + int iaccessible_id() const { return iaccessible_id_; } + + // Modify/retrieve the unique id of this IAccessible's routing variables. + void set_instance_id(int instance_id) { + instance_id_ = instance_id; + } + int instance_id() const { return instance_id_; } + + // Modify/retrieve the state (active/inactive) of this instance. + void set_instance_active(bool instance_active) { + instance_active_ = instance_active; + } + int instance_active() const { return instance_active_; } + + private: + // Creates an empty VARIANT. Used as the equivalent of a NULL (unused) input + // parameter. + VARIANT EmptyVariant() const { + VARIANT empty; + empty.vt = VT_EMPTY; + return empty; + } + + // Wrapper functions, calling through to singleton instance of + // BrowserAccessibilityManager. + + // Creates an instance of BrowserAccessibility, initializes it and sets the + // |iaccessible_id| and |parent_id|. + STDMETHODIMP CreateInstance(REFIID iid, + int iaccessible_id, + void** interface_ptr); + + // Composes and sends a message for requesting needed accessibility + // information. Unused LONG input parameters should be NULL, and the VARIANT + // an empty, valid instance. + bool RequestAccessibilityInfo(int iaccessible_func_id, + VARIANT var_id, + LONG input1, LONG input2); + + // Accessors. + ViewHostMsg_Accessibility_Out_Params response(); + HWND parent_hwnd(); + + // Id to uniquely distinguish this instance in the render-side caching, + // mapping it to the correct IAccessible on that side. Initialized to -1. + int iaccessible_id_; + + // The unique id of this IAccessible instance. Used to help + // BrowserAccessibilityManager instance retrieve the correct member + // variables for this process. + int instance_id_; + + // The instance should only be active if there is a non-terminated + // RenderProcessHost associated with it. The BrowserAccessibilityManager keeps + // track of this state, and sets it to false to disable all calls into the + // renderer from this instance of BroweserAccessibility, and have all + // IAccessible functions return E_FAIL. + bool instance_active_; + + DISALLOW_COPY_AND_ASSIGN(BrowserAccessibility); +}; +#endif // CHROME_BROWSER_BROWSER_ACCESSIBILITY_H_ + diff --git a/chrome/browser/browser_accessibility_manager.cc b/chrome/browser/browser_accessibility_manager.cc new file mode 100644 index 0000000..c670cf8 --- /dev/null +++ b/chrome/browser/browser_accessibility_manager.cc @@ -0,0 +1,167 @@ +// Copyright (c) 2006-2008 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/browser_accessibility_manager.h" + +#include "chrome/browser/browser_accessibility.h" +#include "chrome/browser/render_process_host.h" +#include "chrome/browser/render_widget_host.h" + +// 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 = 500; + +// static +BrowserAccessibilityManager* BrowserAccessibilityManager::Instance() { + return Singleton<BrowserAccessibilityManager>::get(); +} + +BrowserAccessibilityManager::BrowserAccessibilityManager() + : instance_id_(0) { + NotificationService::current()->AddObserver(this, + NOTIFY_RENDERER_PROCESS_TERMINATED, NotificationService::AllSources()); +} + +BrowserAccessibilityManager::~BrowserAccessibilityManager() { + // Clear hashmaps. + instance_map_.clear(); + render_process_host_map_.clear(); + + NotificationService::current()->RemoveObserver(this, + NOTIFY_RENDERER_PROCESS_TERMINATED, NotificationService::AllSources()); +} + +STDMETHODIMP BrowserAccessibilityManager::CreateAccessibilityInstance( + REFIID iid, int iaccessible_id, int instance_id, 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; + + CComPtr<IAccessible> accessibility_instance(instance); + + // Set unique ids. + instance->set_iaccessible_id(iaccessible_id); + instance->set_instance_id(instance_id); + + // Retrieve the RenderWidgetHost connected to this request. + InstanceMap::iterator it = instance_map_.find(instance_id); + + if (it != instance_map_.end()) { + UniqueMembers* members = it->second; + + if (!members || !members->render_widget_host_) + return E_FAIL; + + render_process_host_map_[members->render_widget_host_->process()] = + 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; +} + +bool BrowserAccessibilityManager::RequestAccessibilityInfo( + int iaccessible_id, int instance_id, int iaccessible_func_id, + VARIANT var_id, LONG input1, LONG input2) { + // Create and populate input message structure. + ViewMsg_Accessibility_In_Params in_params; + + in_params.iaccessible_id = iaccessible_id; + in_params.iaccessible_function_id = iaccessible_func_id; + in_params.input_variant_lval = var_id.lVal; + in_params.input_long1 = input1; + in_params.input_long2 = input2; + + // Retrieve the RenderWidgetHost connected to this request. + InstanceMap::iterator it = instance_map_.find(instance_id); + + if (it == instance_map_.end()) { + // Id not found. + return false; + } + + UniqueMembers* members = it->second; + + if (!members || !members->render_widget_host_) + return false; + + IPC::SyncMessage* msg = + new ViewMsg_GetAccessibilityInfo( + members->render_widget_host_->routing_id(), in_params, &out_params_); + + // Necessary for the send to keep the UI responsive. + msg->EnableMessagePumping(); + bool success = members->render_widget_host_->process()->channel()-> + SendWithTimeout(msg, kAccessibilityMessageTimeOut); + + return success; +} + +ViewHostMsg_Accessibility_Out_Params BrowserAccessibilityManager::response() { + return out_params_; +} + +HWND BrowserAccessibilityManager::parent_hwnd(int id) { + // Retrieve the parent HWND connected to the requester's id. + InstanceMap::iterator it = instance_map_.find(id); + + if (it == instance_map_.end()) { + // Id not found. + return NULL; + } + + UniqueMembers* members = it->second; + + if (!members || !members->parent_hwnd_) + return NULL; + + return members->parent_hwnd_; +} + +int BrowserAccessibilityManager::SetMembers(BrowserAccessibility* browser_acc, + HWND parent_hwnd, RenderWidgetHost* render_widget_host) { + // Set HWND and RenderWidgetHost connected to |browser_acc|. + instance_map_[instance_id_] = + new UniqueMembers(parent_hwnd, render_widget_host); + + render_process_host_map_[render_widget_host->process()] = browser_acc; + return instance_id_++; +} + +void BrowserAccessibilityManager::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + DCHECK(type == NOTIFY_RENDERER_PROCESS_TERMINATED); + RenderProcessHost* rph = Source<RenderProcessHost>(source).ptr(); + DCHECK(rph); + RenderProcessHostMap::iterator it = render_process_host_map_.find(rph); + + if (it == render_process_host_map_.end() || !it->second) { + // RenderProcessHost not associated with any BrowserAccessibility instance. + return; + } + + // Set BrowserAccessibility instance to inactive state. + it->second->set_instance_active(false); + render_process_host_map_.erase(it); + + // Delete entry also from InstanceMap. + InstanceMap::iterator it2 = instance_map_.find(it->second->instance_id()); + + if (it2 != instance_map_.end()) + instance_map_.erase(it2); +} diff --git a/chrome/browser/browser_accessibility_manager.h b/chrome/browser/browser_accessibility_manager.h new file mode 100644 index 0000000..1ac25e4 --- /dev/null +++ b/chrome/browser/browser_accessibility_manager.h @@ -0,0 +1,108 @@ +// Copyright (c) 2006-2008 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. + +#ifndef CHROME_BROWSER_BROWSER_ACCESSIBILITY_MANAGER_H_ +#define CHROME_BROWSER_BROWSER_ACCESSIBILITY_MANAGER_H_ + +#include <oaidl.h> // Needed for VARIANT structure. +#include <hash_map> + +#include "base/singleton.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/render_messages.h" + +class BrowserAccessibility; +class RenderProcessHost; +class RenderWidgetHost; + +// Member variable structure, used in instance hashmap. +struct UniqueMembers { + RenderWidgetHost* render_widget_host_; + HWND parent_hwnd_; + + UniqueMembers(HWND parent_hwnd, RenderWidgetHost* render_widget_host) + : parent_hwnd_(parent_hwnd), + render_widget_host_(render_widget_host) { + } +}; + +typedef stdext::hash_map<int, UniqueMembers*> InstanceMap; +typedef stdext::hash_map<RenderProcessHost*, BrowserAccessibility*> + RenderProcessHostMap; + +//////////////////////////////////////////////////////////////////////////////// +// +// BrowserAccessibilityManager +// +// Used to manage instance creation and memory handling for browser side +// accessibility. A singleton class. It implements NotificationObserver to +// ensure that a termination of a renderer process gets propagated to the +// active BrowserAccessibility instances calling into it. Each such instance +// will upon such an event be set to an inactive state, failing calls from the +// assistive technology gracefully. +//////////////////////////////////////////////////////////////////////////////// +class BrowserAccessibilityManager : public NotificationObserver { + public: + // Gets the singleton BrowserAccessibilityManager object. The first time this + // method is called, a CacheManagerHost object is constructed and returned. + // Subsequent calls will return the same object. + static BrowserAccessibilityManager* Instance(); + + // Creates an instance of BrowserAccessibility, initializes it and sets the + // iaccessible_id and parent_id. + STDMETHODIMP CreateAccessibilityInstance(REFIID iid, + int iaccessible_id, + int instance_id, + void** interface_ptr); + + // Composes and sends a message for requesting needed accessibility + // information. Unused LONG input parameters should be NULL, and the VARIANT + // an empty, valid instance. + bool RequestAccessibilityInfo(int iaccessible_id, + int instance_id, + int iaccessible_func_id, + VARIANT var_id, + LONG input1, + LONG input2); + + // Wrapper function, for cleaner code. + ViewHostMsg_Accessibility_Out_Params response(); + + // Retrieves the parent HWND connected to the provided id. + HWND parent_hwnd(int id); + + // Mutator, needed since constructor does not take any arguments, and to keep + // instance accessor clean. + int SetMembers(BrowserAccessibility* browser_acc, + HWND parent_hwnd, + RenderWidgetHost* render_widget_host); + + // NotificationObserver implementation. + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + protected: + // This class is a singleton. Do not instantiate directly. + BrowserAccessibilityManager(); + friend DefaultSingletonTraits<BrowserAccessibilityManager>; + + ~BrowserAccessibilityManager(); + + private: + // Caching of the unique member variables used to handle browser accessibility + // requests from multiple processes. + InstanceMap instance_map_; + int instance_id_; + + // Reverse mapping to track which RenderProcessHosts are active. If a + // RenderProcessHost is found to be terminated, it should be removed from this + // mapping, and the connected BrowserAccessibility ids/instances invalidated. + RenderProcessHostMap render_process_host_map_; + + ViewHostMsg_Accessibility_Out_Params out_params_; + + DISALLOW_COPY_AND_ASSIGN(BrowserAccessibilityManager); +}; +#endif // CHROME_BROWSER_BROWSER_ACCESSIBILITY_MANAGER_H_ diff --git a/chrome/browser/iaccessible_function_ids.h b/chrome/browser/iaccessible_function_ids.h new file mode 100644 index 0000000..4f97681 --- /dev/null +++ b/chrome/browser/iaccessible_function_ids.h @@ -0,0 +1,38 @@ +// Copyright (c) 2006-2008 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. + +// This defines an enumeration of IDs that can uniquely identify a call to a +// specific IAccessible function. Should match the support implemented in WebKit +// (functions marked with return value E_NOTIMPL are also excluded). + +#ifndef CHROME_BROWSER_IACCESSIBLE_FUNCTION_IDS_H_ +#define CHROME_BROWSER_IACCESSIBLE_FUNCTION_IDS_H_ + +enum IAccessibleID { + IACCESSIBLE_FUNC_NONE = 0, + + // Supported IAccessible interface functions. + IACCESSIBLE_FUNC_ACCDODEFAULTACTION, + IACCESSIBLE_FUNC_ACCHITTEST, + IACCESSIBLE_FUNC_ACCLOCATION, + IACCESSIBLE_FUNC_ACCNAVIGATE, + IACCESSIBLE_FUNC_GET_ACCCHILD, + IACCESSIBLE_FUNC_GET_ACCCHILDCOUNT, + IACCESSIBLE_FUNC_GET_ACCDEFAULTACTION, + IACCESSIBLE_FUNC_GET_ACCDESCRIPTION, + IACCESSIBLE_FUNC_GET_ACCFOCUS, + IACCESSIBLE_FUNC_GET_ACCHELP, + IACCESSIBLE_FUNC_GET_ACCKEYBOARDSHORTCUT, + IACCESSIBLE_FUNC_GET_ACCNAME, + IACCESSIBLE_FUNC_GET_ACCPARENT, + IACCESSIBLE_FUNC_GET_ACCROLE, + IACCESSIBLE_FUNC_GET_ACCSTATE, + IACCESSIBLE_FUNC_GET_ACCVALUE + + // The deprecated put_accName and put_accValue are not supported here, nor is + // accSelect, get_accHelpTopic and get_accSelection (matching WebKit's + // support). +}; + +#endif // CHROME_BROWSER_IACCESSIBLE_FUNCTION_IDS_H_ diff --git a/chrome/browser/render_view_host.cc b/chrome/browser/render_view_host.cc index 9128c87..d269017 100644 --- a/chrome/browser/render_view_host.cc +++ b/chrome/browser/render_view_host.cc @@ -246,7 +246,7 @@ void RenderViewHost::FirePageUnload() { // Start the hang monitor in case the renderer hangs in the unload handler. is_waiting_for_unload_ack_ = true; StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kUnloadTimeoutMS)); - ClosePage(site_instance()->process_host_id(), + ClosePage(site_instance()->process_host_id(), routing_id()); } @@ -638,7 +638,7 @@ void RenderViewHost::OnMessageReceived(const IPC::Message& msg) { #ifdef CHROME_PERSONALIZATION IPC_MESSAGE_HANDLER(ViewHostMsg_PersonalizationEvent, OnPersonalizationEvent) -#endif +#endif IPC_MESSAGE_HANDLER(ViewHostMsg_GoToEntryAtOffset, OnMsgGoToEntryAtOffset) IPC_MESSAGE_HANDLER(ViewHostMsg_SetTooltipText, OnMsgSetTooltipText) @@ -1193,7 +1193,7 @@ void RenderViewHost::OnDebugDisconnect() { } #ifdef CHROME_PERSONALIZATION -void RenderViewHost::RaisePersonalizationEvent(std::string event_name, +void RenderViewHost::RaisePersonalizationEvent(std::string event_name, std::string event_arg) { Send(new ViewMsg_PersonalizationEvent(routing_id_, event_name, diff --git a/chrome/browser/render_widget_host_hwnd.cc b/chrome/browser/render_widget_host_hwnd.cc index 8ca727c..bae1cfa 100644 --- a/chrome/browser/render_widget_host_hwnd.cc +++ b/chrome/browser/render_widget_host_hwnd.cc @@ -9,6 +9,8 @@ #include "base/gfx/rect.h" #include "base/histogram.h" #include "base/win_util.h" +#include "chrome/browser/browser_accessibility.h" +#include "chrome/browser/browser_accessibility_manager.h" #include "chrome/browser/render_process_host.h" // TODO(beng): (Cleanup) we should not need to include this file... see comment // in |DidBecomeSelected|. @@ -747,6 +749,55 @@ LRESULT RenderWidgetHostHWND::OnMouseActivate(UINT, WPARAM, LPARAM, } } +LRESULT RenderWidgetHostHWND::OnGetObject(UINT message, WPARAM wparam, + LPARAM lparam, BOOL& handled) { + LRESULT reference_result = static_cast<LRESULT>(0L); + + // Accessibility readers will send an OBJID_CLIENT message. + if (OBJID_CLIENT == lparam) { + // If our MSAA DOM root is already created, reuse that pointer. Otherwise, + // create a new one. + if (!browser_accessibility_root_) { + CComObject<BrowserAccessibility>* accessibility = NULL; + + if (!SUCCEEDED(CComObject<BrowserAccessibility>::CreateInstance( + &accessibility)) || !accessibility) { + // Return with failure. + return static_cast<LRESULT>(0L); + } + + CComPtr<IAccessible> accessibility_comptr(accessibility); + + // Root id is always 0, to distinguish this particular instance when + // mapping to the render-side IAccessible. + accessibility->set_iaccessible_id(0); + + // Set the unique member variables of this particular process. + accessibility->set_instance_id(BrowserAccessibilityManager::Instance()-> + SetMembers(accessibility, m_hWnd, render_widget_host_)); + + // All is well, assign the temp instance to the class smart pointer. + browser_accessibility_root_.Attach(accessibility_comptr.Detach()); + + if (!browser_accessibility_root_) { + // Paranoia check. Return with failure. + NOTREACHED(); + return static_cast<LRESULT>(0L); + } + + // Notify that an instance of IAccessible was allocated for m_hWnd. + ::NotifyWinEvent(EVENT_OBJECT_CREATE, m_hWnd, OBJID_CLIENT, + CHILDID_SELF); + } + + // Create a reference to ViewAccessibility that MSAA will marshall + // to the client. + reference_result = LresultFromObject(IID_IAccessible, wparam, + static_cast<IAccessible*>(browser_accessibility_root_)); + } + return reference_result; +} + void RenderWidgetHostHWND::OnFinalMessage(HWND window) { render_widget_host_->ViewDestroyed(); delete this; diff --git a/chrome/browser/render_widget_host_hwnd.h b/chrome/browser/render_widget_host_hwnd.h index bc16c6d..c9a392b 100644 --- a/chrome/browser/render_widget_host_hwnd.h +++ b/chrome/browser/render_widget_host_hwnd.h @@ -107,6 +107,7 @@ class RenderWidgetHostHWND : MESSAGE_HANDLER(WM_SYSCHAR, OnKeyEvent) MESSAGE_HANDLER(WM_IME_CHAR, OnKeyEvent) MESSAGE_HANDLER(WM_MOUSEACTIVATE, OnMouseActivate) + MESSAGE_HANDLER(WM_GETOBJECT, OnGetObject) END_MSG_MAP() // Overridden from RenderWidgetHostView: @@ -164,6 +165,9 @@ class RenderWidgetHostHWND : LRESULT OnWheelEvent( UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled); LRESULT OnMouseActivate(UINT, WPARAM, LPARAM, BOOL& handled); + // Handle MSAA requests for accessibility information. + LRESULT OnGetObject(UINT message, WPARAM wparam, LPARAM lparam, + BOOL& handled); // Handle vertical scrolling LRESULT OnVScroll(int code, short position, HWND scrollbar_control); // Handle horizontal scrolling @@ -246,6 +250,10 @@ class RenderWidgetHostHWND : // when shown again. HWND parent_hwnd_; + // Instance of accessibility information for the root of the MSAA + // tree representation of the WebKit render tree. + CComPtr<IAccessible> browser_accessibility_root_; + // The time at which this view started displaying white pixels as a result of // not having anything to paint (empty backing store from renderer). This // value returns true for is_null() if we are not recording whiteout times. diff --git a/chrome/common/render_messages.h b/chrome/common/render_messages.h index 3c2ee58..c4038a2 100644 --- a/chrome/common/render_messages.h +++ b/chrome/common/render_messages.h @@ -375,6 +375,55 @@ struct ViewHostMsg_DidPrintPage_Params { double actual_shrink; }; +// Parameters structure to hold a union of the possible IAccessible function +// INPUT variables, with the unused fields always set to default value. Used in +// ViewMsg_GetAccessibilityInfo, as only parameter. +struct ViewMsg_Accessibility_In_Params { + // Identifier to uniquely distinguish which instance of IAccessible is being + // called upon on the renderer side. + int iaccessible_id; + + // Identifier to resolve which IAccessible interface function is being called. + int iaccessible_function_id; + + // Function input parameters. + // Input VARIANT structure's LONG field to specify requested object. + long input_variant_lval; + + // LONG input parameters, used differently depending on the function called. + long input_long1; + long input_long2; +}; + +// Parameters structure to hold a union of the possible IAccessible function +// OUTPUT variables, with the unused fields always set to default value. Used in +// ViewHostMsg_GetAccessibilityInfoResponse, as only parameter. +struct ViewHostMsg_Accessibility_Out_Params { + // Identifier to uniquely distinguish which instance of IAccessible is being + // called upon on the renderer side. + int iaccessible_id; + + // Function output parameters. + // Output VARIANT structure's LONG field to specify requested object. + long output_variant_lval; + + // LONG output parameters, used differently depending on the function called. + // output_long1 can in some cases be set to -1 to indicate that the child + // object found by the called IAccessible function is not a simple object. + long output_long1; + long output_long2; + long output_long3; + long output_long4; + + // String output parameter. + std::wstring output_string; + + // Return code, either S_OK (true) or S_FALSE (false). WebKit MSAA error + // return codes (E_POINTER, E_INVALIDARG, E_FAIL, E_NOTIMPL) must be handled + // on the browser side by input validation. + bool return_code; +}; + // The first parameter for the ViewHostMsg_ImeUpdateStatus message. enum ViewHostMsg_ImeControl { IME_DISABLE = 0, @@ -579,6 +628,86 @@ struct ParamTraits<WebInputEvent::Type> { } }; +// Traits for ViewMsg_Accessibility_In_Params structure to pack/unpack. +template <> +struct ParamTraits<ViewMsg_Accessibility_In_Params> { + typedef ViewMsg_Accessibility_In_Params param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.iaccessible_id); + WriteParam(m, p.iaccessible_function_id); + WriteParam(m, p.input_variant_lval); + WriteParam(m, p.input_long1); + WriteParam(m, p.input_long2); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return + ReadParam(m, iter, &p->iaccessible_id) && + ReadParam(m, iter, &p->iaccessible_function_id) && + ReadParam(m, iter, &p->input_variant_lval) && + ReadParam(m, iter, &p->input_long1) && + ReadParam(m, iter, &p->input_long2); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"("); + LogParam(p.iaccessible_id, l); + l->append(L", "); + LogParam(p.iaccessible_function_id, l); + l->append(L", "); + LogParam(p.input_variant_lval, l); + l->append(L", "); + LogParam(p.input_long1, l); + l->append(L", "); + LogParam(p.input_long2, l); + l->append(L")"); + } +}; + +// Traits for ViewHostMsg_Accessibility_Out_Params structure to pack/unpack. +template <> +struct ParamTraits<ViewHostMsg_Accessibility_Out_Params> { + typedef ViewHostMsg_Accessibility_Out_Params param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.iaccessible_id); + WriteParam(m, p.output_variant_lval); + WriteParam(m, p.output_long1); + WriteParam(m, p.output_long2); + WriteParam(m, p.output_long3); + WriteParam(m, p.output_long4); + WriteParam(m, p.output_string); + WriteParam(m, p.return_code); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return + ReadParam(m, iter, &p->iaccessible_id) && + ReadParam(m, iter, &p->output_variant_lval) && + ReadParam(m, iter, &p->output_long1) && + ReadParam(m, iter, &p->output_long2) && + ReadParam(m, iter, &p->output_long3) && + ReadParam(m, iter, &p->output_long4) && + ReadParam(m, iter, &p->output_string) && + ReadParam(m, iter, &p->return_code); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"("); + LogParam(p.iaccessible_id, l); + l->append(L", "); + LogParam(p.output_variant_lval, l); + l->append(L", "); + LogParam(p.output_long1, l); + l->append(L", "); + LogParam(p.output_long2, l); + l->append(L", "); + LogParam(p.output_long3, l); + l->append(L", "); + LogParam(p.output_long4, l); + l->append(L", "); + LogParam(p.output_string, l); + l->append(L", "); + LogParam(p.return_code, l); + l->append(L")"); + } +}; + template <> struct ParamTraits<ViewHostMsg_ImeControl> { typedef ViewHostMsg_ImeControl param_type; diff --git a/chrome/common/render_messages_internal.h b/chrome/common/render_messages_internal.h index faf8beb..9480f06 100644 --- a/chrome/common/render_messages_internal.h +++ b/chrome/common/render_messages_internal.h @@ -372,6 +372,20 @@ IPC_BEGIN_MESSAGES(View, 1) int /* back_list_count */, int /* forward_list_count */) + // Retreive information from the MSAA DOM subtree, for accessibility purposes. + IPC_SYNC_MESSAGE_ROUTED1_1(ViewMsg_GetAccessibilityInfo, + ViewMsg_Accessibility_In_Params + /* input parameters */, + ViewHostMsg_Accessibility_Out_Params + /* output parameters */) + + // Requests the renderer to clear cashed accessibility information. Takes an + // id to clear a specific hashmap entry, and a bool; true clears all, false + // does not. + IPC_MESSAGE_ROUTED2(ViewMsg_ClearAccessibilityInfo, + int /* iaccessible_id */, + bool /* clear_all */) + // Get all savable resource links from current webpage, include main // frame and sub-frame. IPC_MESSAGE_ROUTED1(ViewMsg_GetAllSavableResourceLinksForCurrentPage, diff --git a/chrome/renderer/render_view.cc b/chrome/renderer/render_view.cc index 636fc54..af907c1 100644 --- a/chrome/renderer/render_view.cc +++ b/chrome/renderer/render_view.cc @@ -310,7 +310,8 @@ void RenderView::OnMessageReceived(const IPC::Message& message) { OnAllowDomAutomationBindings) IPC_MESSAGE_HANDLER(ViewMsg_AllowBindings, OnAllowBindings) IPC_MESSAGE_HANDLER(ViewMsg_SetDOMUIProperty, OnSetDOMUIProperty) - IPC_MESSAGE_HANDLER(ViewMsg_DragSourceEndedOrMoved, OnDragSourceEndedOrMoved) + IPC_MESSAGE_HANDLER(ViewMsg_DragSourceEndedOrMoved, + OnDragSourceEndedOrMoved) IPC_MESSAGE_HANDLER(ViewMsg_DragSourceSystemDragEnded, OnDragSourceSystemDragEnded) IPC_MESSAGE_HANDLER(ViewMsg_SetInitialFocus, OnSetInitialFocus) @@ -328,6 +329,9 @@ void RenderView::OnMessageReceived(const IPC::Message& message) { IPC_MESSAGE_HANDLER(ViewMsg_GetSerializedHtmlDataForCurrentPageWithLocalLinks, OnGetSerializedHtmlDataForCurrentPageWithLocalLinks) IPC_MESSAGE_HANDLER(ViewMsg_GetApplicationInfo, OnGetApplicationInfo) + IPC_MESSAGE_HANDLER(ViewMsg_GetAccessibilityInfo, OnGetAccessibilityInfo) + IPC_MESSAGE_HANDLER(ViewMsg_ClearAccessibilityInfo, + OnClearAccessibilityInfo) IPC_MESSAGE_HANDLER(ViewMsg_ShouldClose, OnMsgShouldClose) IPC_MESSAGE_HANDLER(ViewMsg_ClosePage, OnClosePage) IPC_MESSAGE_HANDLER(ViewMsg_ThemeChanged, OnThemeChanged) @@ -1051,6 +1055,11 @@ void RenderView::UpdateURL(WebFrame* frame) { // we don't want the transition type to persist. if (extra_data) extra_data->transition_type = PageTransition::LINK; // Just clear it. + + if (glue_accessibility_.get()) { + // Clear accessibility info cache. + glue_accessibility_->ClearIAccessibleMap(-1, true); + } } // Tell the embedding application that the title of the active page has changed @@ -1146,7 +1155,7 @@ void RenderView::DidStartProvisionalLoadForFrame( NavigationGesture gesture) { if (webview->GetMainFrame() == frame) { navigation_gesture_ = gesture; - + // Make sure redirect tracking state is clear for the new load. completed_client_redirect_src_ = GURL(); } @@ -2260,8 +2269,7 @@ void RenderView::OnAllowDomAutomationBindings(bool allow_bindings) { } void RenderView::OnAllowBindings(bool enable_dom_ui_bindings, - bool enable_external_host_bindings) -{ + bool enable_external_host_bindings) { enable_dom_ui_bindings_ = enable_dom_ui_bindings; enable_external_host_bindings_ = enable_external_host_bindings; } @@ -2406,6 +2414,29 @@ void RenderView::OnUpdateBackForwardListCount(int back_list_count, history_forward_list_count_ = forward_list_count; } +void RenderView::OnGetAccessibilityInfo( + const ViewMsg_Accessibility_In_Params& in_params, + ViewHostMsg_Accessibility_Out_Params* out_params) { + + if (!glue_accessibility_.get()) + glue_accessibility_.reset(new GlueAccessibility()); + + if (!glue_accessibility_-> + GetAccessibilityInfo(webview(), in_params, out_params)) { + return; + } +} + +void RenderView::OnClearAccessibilityInfo(int iaccessible_id, bool clear_all) { + if (!glue_accessibility_.get()) { + // If accessibility is not activated, ignore clearing message. + return; + } + + if (!glue_accessibility_->ClearIAccessibleMap(iaccessible_id, clear_all)) + return; +} + void RenderView::OnGetAllSavableResourceLinksForCurrentPage( const GURL& page_url) { // Prepare list to storage all savable resource links. diff --git a/chrome/renderer/render_view.h b/chrome/renderer/render_view.h index 6f6b9c3..71faf63 100644 --- a/chrome/renderer/render_view.h +++ b/chrome/renderer/render_view.h @@ -26,6 +26,7 @@ #include "chrome/renderer/render_widget.h" #include "webkit/glue/console_message_level.h" #include "webkit/glue/dom_serializer_delegate.h" +#include "webkit/glue/glue_accessibility.h" #include "webkit/glue/webview_delegate.h" #include "webkit/glue/webview.h" @@ -418,6 +419,9 @@ class RenderView : public RenderWidget, public WebViewDelegate, void OnEnableViewSourceMode(); void OnUpdateBackForwardListCount(int back_list_count, int forward_list_count); + void OnGetAccessibilityInfo(const ViewMsg_Accessibility_In_Params& in_params, + ViewHostMsg_Accessibility_Out_Params* out_params); + void OnClearAccessibilityInfo(int iaccessible_id, bool clear_all); // Checks if the RenderView should close, runs the beforeunload handler and // sends ViewMsg_ShouldClose to the browser. @@ -612,6 +616,10 @@ class RenderView : public RenderWidget, public WebViewDelegate, // True if the page has any frame-level unload or beforeunload listeners. bool has_unload_listener_; + // Handles accessibility requests into the renderer side, as well as + // maintains the cache and other features of the accessibility tree. + scoped_ptr<GlueAccessibility> glue_accessibility_; + DISALLOW_EVIL_CONSTRUCTORS(RenderView); }; diff --git a/webkit/build/glue/glue.vcproj b/webkit/build/glue/glue.vcproj index 080bd78..bd7c70c 100644 --- a/webkit/build/glue/glue.vcproj +++ b/webkit/build/glue/glue.vcproj @@ -369,6 +369,14 @@ > </File> <File + RelativePath="..\..\glue\glue_accessibility.cc" + > + </File> + <File + RelativePath="..\..\glue\glue_accessibility.h" + > + </File> + <File RelativePath="..\..\glue\glue_serialize.cc" > </File> diff --git a/webkit/glue/glue_accessibility.cc b/webkit/glue/glue_accessibility.cc new file mode 100644 index 0000000..60c1cf9 --- /dev/null +++ b/webkit/glue/glue_accessibility.cc @@ -0,0 +1,281 @@ +// Copyright (c) 2006-2008 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 <comdef.h> + +#include "config.h" + +#pragma warning(push, 0) +#include "AccessibleDocument.h" +#include "AXObjectCache.h" +#include "Document.h" +#include "Frame.h" +#pragma warning(pop) +#undef LOG + +#include "webkit/glue/glue_accessibility.h" + +#include "chrome/browser/iaccessible_function_ids.h" +#include "webkit/glue/webframe_impl.h" +#include "webkit/glue/webview_impl.h" + +// struct GlueAccessibility::GlueAccessibilityRoot +struct GlueAccessibility::GlueAccessibilityRoot { + GlueAccessibilityRoot() {} + + // Root of the WebKit IAccessible tree. + COMPtr<AccessibleDocument> accessibility_root_; +}; + +// class GlueAccessibility +GlueAccessibility::GlueAccessibility() + : root_(new GlueAccessibilityRoot) { +} + +GlueAccessibility::~GlueAccessibility() { + delete root_; +} + +bool GlueAccessibility::GetAccessibilityInfo(WebView* view, + const ViewMsg_Accessibility_In_Params& in_params, + ViewHostMsg_Accessibility_Out_Params* out_params) { + if (!root_->accessibility_root_ && !InitAccessibilityRoot(view)) { + // Failure in retrieving the root. + return false; + } + + // Temporary storing for the currently active IAccessible. + COMPtr<IAccessible> active_iaccessible; + IntToIAccessibleMap::iterator it = + int_to_iaccessible_map_.find(in_params.iaccessible_id); + + if (it == int_to_iaccessible_map_.end()) { + // Map did not contain the data requested. + return false; + } + + active_iaccessible = it->second; + + if (!active_iaccessible) { + // Requested IAccessible not found. Paranoia check. + NOTREACHED(); + return false; + } + + // Input VARIANT, determined by the browser side to be of type VT_I4. + VARIANT input_variant; + input_variant.vt = VT_I4; + input_variant.lVal = in_params.input_variant_lval; + + // Output variables, used locally to retrieve data. + VARIANT output_variant; + ::VariantInit(&output_variant); + BSTR output_bstr; + bool string_output = false; + HRESULT hr = S_FALSE; + + switch (in_params.iaccessible_function_id) { + case IACCESSIBLE_FUNC_ACCDODEFAULTACTION : + hr = active_iaccessible->accDoDefaultAction(input_variant); + break; + case IACCESSIBLE_FUNC_ACCHITTEST : + hr = active_iaccessible->accHitTest(in_params.input_long1, + in_params.input_long2, + &output_variant); + break; + case IACCESSIBLE_FUNC_ACCLOCATION : + hr = active_iaccessible->accLocation(&out_params->output_long1, + &out_params->output_long2, + &out_params->output_long3, + &out_params->output_long4, + input_variant); + break; + case IACCESSIBLE_FUNC_ACCNAVIGATE : + hr = active_iaccessible->accNavigate(in_params.input_long1, input_variant, + &output_variant); + break; + case IACCESSIBLE_FUNC_GET_ACCCHILD : + if (input_variant.lVal == CHILDID_SELF) { + // If child requested is CHILDID_SELF, stay with the same IAccessible. + out_params->iaccessible_id = in_params.iaccessible_id; + hr = S_OK; + break; + } + hr = active_iaccessible->get_accChild(input_variant, + reinterpret_cast<IDispatch **>(&output_variant.pdispVal)); + output_variant.vt = VT_DISPATCH; + break; + case IACCESSIBLE_FUNC_GET_ACCCHILDCOUNT : + hr = active_iaccessible->get_accChildCount(&out_params->output_long1); + break; + case IACCESSIBLE_FUNC_GET_ACCDEFAULTACTION : + hr = active_iaccessible->get_accDefaultAction(input_variant, + &output_bstr); + string_output = true; + break; + case IACCESSIBLE_FUNC_GET_ACCDESCRIPTION : + hr = active_iaccessible->get_accDescription(input_variant, &output_bstr); + string_output = true; + break; + case IACCESSIBLE_FUNC_GET_ACCFOCUS : + hr = active_iaccessible->get_accFocus(&output_variant); + break; + case IACCESSIBLE_FUNC_GET_ACCHELP : + hr = active_iaccessible->get_accHelp(input_variant, &output_bstr); + string_output = true; + break; + case IACCESSIBLE_FUNC_GET_ACCKEYBOARDSHORTCUT : + hr = active_iaccessible->get_accKeyboardShortcut(input_variant, + &output_bstr); + string_output = true; + break; + case IACCESSIBLE_FUNC_GET_ACCNAME : + hr = active_iaccessible->get_accName(input_variant, &output_bstr); + string_output = true; + break; + case IACCESSIBLE_FUNC_GET_ACCPARENT : + hr = active_iaccessible->get_accParent( + reinterpret_cast<IDispatch **>(&output_variant.pdispVal)); + output_variant.vt = VT_DISPATCH; + break; + case IACCESSIBLE_FUNC_GET_ACCROLE : + hr = active_iaccessible->get_accRole(input_variant, &output_variant); + break; + case IACCESSIBLE_FUNC_GET_ACCSTATE : + hr = active_iaccessible->get_accState(input_variant, &output_variant); + break; + case IACCESSIBLE_FUNC_GET_ACCVALUE : + hr = active_iaccessible->get_accValue(input_variant, &output_bstr); + string_output = true; + break; + default: + // Memory cleanup. + ::VariantClear(&input_variant); + ::VariantClear(&output_variant); + + // Non-supported function id. + return false; + } + + // Return code handling. + if (hr == S_OK) { + out_params->return_code = true; + + // All is ok, assign output string if needed. + if (string_output) { + out_params->output_string = _bstr_t(output_bstr); + ::SysFreeString(output_bstr); + } + + } else if (hr == S_FALSE) { + out_params->return_code = false; + } else { + // Memory cleanup. + ::VariantClear(&input_variant); + ::VariantClear(&output_variant); + + // Generate a generic failure on the browser side. Input validation is the + // responsibility of the browser side, as is correctly handling calls to + // non-supported functions appropriately. + return false; + } + + // Output and hashmap assignments, as appropriate. + if (output_variant.vt == VT_DISPATCH && output_variant.pdispVal) { + IAccessibleToIntMap::iterator it = + iaccessible_to_int_map_.find( + reinterpret_cast<IAccessible *>(output_variant.pdispVal)); + + if (it != iaccessible_to_int_map_.end()) { + // Data already present in map, return previously assigned id. + out_params->iaccessible_id = it->second; + out_params->output_long1 = -1; + } else { + // Insert new IAccessible in hashmaps. + int_to_iaccessible_map_[iaccessible_id_] = + reinterpret_cast<IAccessible *>(output_variant.pdispVal); + iaccessible_to_int_map_[ + reinterpret_cast<IAccessible *>(output_variant.pdispVal)] = + iaccessible_id_; + out_params->iaccessible_id = iaccessible_id_++; + out_params->output_long1 = -1; + } + } else if (output_variant.vt == VT_I4) { + out_params->output_long1 = output_variant.lVal; + } + + // Memory cleanup. + ::VariantClear(&input_variant); + ::VariantClear(&output_variant); + + return true; +} + +bool GlueAccessibility::InitAccessibilityRoot(WebView* view) { + WebCore::AXObjectCache::enableAccessibility(); + iaccessible_id_ = 0; + + WebFrame* main_frame = view->GetMainFrame(); + + if (!main_frame) + return false; + + WebFrameImpl* main_frame_impl = static_cast<WebFrameImpl*>(main_frame); + WebCore::Frame* frame = main_frame_impl->frame(); + + WebCore::Document* currentDocument = frame->document(); + if (!currentDocument) { + root_->accessibility_root_ = 0; + return false; + } else if (!root_->accessibility_root_ || + root_->accessibility_root_->document() != currentDocument) { + // Either we've never had a wrapper for this frame's top-level Document, + // the Document renderer was destroyed and its wrapper was detached, or + // the previous Document is in the page cache, and the current document + // needs to be wrapped. + root_->accessibility_root_ = new AccessibleDocument(currentDocument); + } + // Insert root in hashmaps. + int_to_iaccessible_map_[iaccessible_id_] = root_->accessibility_root_.get(); + iaccessible_to_int_map_[root_->accessibility_root_.get()] = iaccessible_id_++; + + return true; +} + +bool GlueAccessibility::ClearIAccessibleMap(int iaccessible_id, + bool clear_all) { + if (clear_all) { + // Clear maps and invalidate root. + int_to_iaccessible_map_.clear(); + iaccessible_to_int_map_.clear(); + root_->accessibility_root_ = 0; + return true; + } + + IntToIAccessibleMap::iterator it = + int_to_iaccessible_map_.find(iaccessible_id); + + if (it == int_to_iaccessible_map_.end()) { + // Element not found. + return false; + } else { + if (it->second) { + // Erase element from reverse hashmap. + IAccessibleToIntMap::iterator it2 = + iaccessible_to_int_map_.find(it->second); + + DCHECK(it2 != iaccessible_to_int_map_.end()); + iaccessible_to_int_map_.erase(it2); + } + + int_to_iaccessible_map_.erase(it); + + if (iaccessible_id == 0) { + // Invalidate root. + root_->accessibility_root_ = 0; + } + } + + return true; +} diff --git a/webkit/glue/glue_accessibility.h b/webkit/glue/glue_accessibility.h new file mode 100644 index 0000000..576ccc4 --- /dev/null +++ b/webkit/glue/glue_accessibility.h @@ -0,0 +1,68 @@ +// Copyright (c) 2006-2008 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. + +#ifndef WEBKIT_GLUE_GLUE_ACCESSIBILITY_H_ +#define WEBKIT_GLUE_GLUE_ACCESSIBILITY_H_ + +#include <oleacc.h> +#include <hash_map> + +#include "chrome/common/render_messages.h" + +class WebView; + +typedef stdext::hash_map<int, IAccessible*> IntToIAccessibleMap; +typedef stdext::hash_map<IAccessible*, int> IAccessibleToIntMap; + +//////////////////////////////////////////////////////////////////////////////// +// +// GlueAccessibility +// +// Operations that access the underlying WebKit DOM directly, exposing +// accessibility information. +//////////////////////////////////////////////////////////////////////////////// +class GlueAccessibility { + public: + GlueAccessibility(); + ~GlueAccessibility(); + + // Retrieves the IAccessible information as requested in in_params, by calling + // into WebKit's implementation of IAccessible. Maintains a hashmap of the + // currently active (browser ref count not zero) IAccessibles. Returns true if + // successful, false otherwise. + bool GetAccessibilityInfo(WebView* view, + const ViewMsg_Accessibility_In_Params& in_params, + ViewHostMsg_Accessibility_Out_Params* out_params); + + // Retrieves the RenderObject associated with this WebView, and uses it to + // initialize the root of the render-side MSAA tree with the associated + // accessibility information. Returns true if successful, false otherwise. + bool InitAccessibilityRoot(WebView* view); + + // Erases the entry identified by the |iaccessible_id| from the hash map. If + // |clear_all| is true, all entries are erased. Returns true if successful, + // false otherwise. + bool ClearIAccessibleMap(int iaccessible_id, bool clear_all); + + private: + // Wrapper around the COM pointer that holds the root of the MSAA tree, to + // ensure that we are not requiring WebKit includes outside of glue. + struct GlueAccessibilityRoot; + GlueAccessibilityRoot* root_; + + // Hashmap for cashing of elements in use by the AT, mapping id (int) to an + // IAccessible pointer. + IntToIAccessibleMap int_to_iaccessible_map_; + // Hashmap for cashing of elements in use by the AT, mapping an IAccessible + // pointer to its id (int). Needed for reverse lookup, to ensure unnecessary + // duplicate entries are not created in the IntToIAccessibleMap (above). + IAccessibleToIntMap iaccessible_to_int_map_; + + // Unique identifier for retrieving an IAccessible from the page's hashmap. + int iaccessible_id_; + + DISALLOW_COPY_AND_ASSIGN(GlueAccessibility); +}; + +#endif // WEBKIT_GLUE_GLUE_ACCESSIBILITY_H_ |