diff options
author | tfarina@chromium.org <tfarina@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-14 16:33:48 +0000 |
---|---|---|
committer | tfarina@chromium.org <tfarina@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-14 16:33:48 +0000 |
commit | 64673f681549e7168f5fcb89de5935df08689138 (patch) | |
tree | 4ce2c0431f979d47bf91475b2ce08dcf8cbfb737 /ui | |
parent | 02f574ac804b054aad7243491a6434a70557d7f9 (diff) | |
download | chromium_src-64673f681549e7168f5fcb89de5935df08689138.zip chromium_src-64673f681549e7168f5fcb89de5935df08689138.tar.gz chromium_src-64673f681549e7168f5fcb89de5935df08689138.tar.bz2 |
views: Move accessibility/ directory to ui/views/.
BUG=104039
R=ben@chromium.org
Review URL: http://codereview.chromium.org/8533010
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@109894 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui')
-rw-r--r-- | ui/views/accessibility/OWNERS | 2 | ||||
-rw-r--r-- | ui/views/accessibility/native_view_accessibility_win.cc | 1030 | ||||
-rw-r--r-- | ui/views/accessibility/native_view_accessibility_win.h | 355 |
3 files changed, 1387 insertions, 0 deletions
diff --git a/ui/views/accessibility/OWNERS b/ui/views/accessibility/OWNERS new file mode 100644 index 0000000..11e8fd8 --- /dev/null +++ b/ui/views/accessibility/OWNERS @@ -0,0 +1,2 @@ +dmazzoni@chromium.org +dtseng@chromium.org diff --git a/ui/views/accessibility/native_view_accessibility_win.cc b/ui/views/accessibility/native_view_accessibility_win.cc new file mode 100644 index 0000000..8489a3e --- /dev/null +++ b/ui/views/accessibility/native_view_accessibility_win.cc @@ -0,0 +1,1030 @@ +// 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 "ui/views/accessibility/native_view_accessibility_win.h" + +#include <atlbase.h> +#include <atlcom.h> + +#include <vector> + +#include "third_party/iaccessible2/ia2_api_all.h" +#include "ui/base/accessibility/accessible_text_utils.h" +#include "ui/base/accessibility/accessible_view_state.h" +#include "ui/base/view_prop.h" +#include "views/widget/native_widget_win.h" +#include "views/widget/widget.h" + +using ui::AccessibilityTypes; + +// static +long NativeViewAccessibilityWin::next_unique_id_ = 1; + +// static +scoped_refptr<NativeViewAccessibilityWin> NativeViewAccessibilityWin::Create( + views::View* view) { + CComObject<NativeViewAccessibilityWin>* instance = NULL; + HRESULT hr = CComObject<NativeViewAccessibilityWin>::CreateInstance( + &instance); + DCHECK(SUCCEEDED(hr)); + instance->set_view(view); + return scoped_refptr<NativeViewAccessibilityWin>(instance); +} + +NativeViewAccessibilityWin::NativeViewAccessibilityWin() + : view_(NULL), + unique_id_(next_unique_id_++) { +} + +NativeViewAccessibilityWin::~NativeViewAccessibilityWin() { +} + +// TODO(ctguil): Handle case where child View is not contained by parent. +STDMETHODIMP NativeViewAccessibilityWin::accHitTest( + LONG x_left, LONG y_top, VARIANT* child) { + if (!child) + return E_INVALIDARG; + + if (!view_) + return E_FAIL; + + gfx::Point point(x_left, y_top); + views::View::ConvertPointToView(NULL, view_, &point); + + if (!view_->HitTest(point)) { + // If containing parent is not hit, return with failure. + child->vt = VT_EMPTY; + return S_FALSE; + } + + views::View* view = view_->GetEventHandlerForPoint(point); + if (view == view_) { + // No child hit, return parent id. + child->vt = VT_I4; + child->lVal = CHILDID_SELF; + } else { + child->vt = VT_DISPATCH; + child->pdispVal = view->GetNativeViewAccessible(); + child->pdispVal->AddRef(); + } + return S_OK; +} + +HRESULT NativeViewAccessibilityWin::accDoDefaultAction(VARIANT var_id) { + if (!IsValidId(var_id)) + return E_INVALIDARG; + + // The object does not support the method. This value is returned for + // controls that do not perform actions, such as edit fields. + return DISP_E_MEMBERNOTFOUND; +} + +STDMETHODIMP NativeViewAccessibilityWin::accLocation( + LONG* x_left, LONG* y_top, LONG* width, LONG* height, VARIANT var_id) { + if (!IsValidId(var_id) || !x_left || !y_top || !width || !height) + return E_INVALIDARG; + + if (!view_) + return E_FAIL; + + if (!view_->bounds().IsEmpty()) { + *width = view_->width(); + *height = view_->height(); + gfx::Point topleft(view_->bounds().origin()); + views::View::ConvertPointToScreen( + view_->parent() ? view_->parent() : view_, &topleft); + *x_left = topleft.x(); + *y_top = topleft.y(); + } else { + return E_FAIL; + } + return S_OK; +} + +STDMETHODIMP NativeViewAccessibilityWin::accNavigate( + LONG nav_dir, VARIANT start, VARIANT* end) { + if (start.vt != VT_I4 || !end) + return E_INVALIDARG; + + if (!view_) + return E_FAIL; + + switch (nav_dir) { + case NAVDIR_FIRSTCHILD: + case NAVDIR_LASTCHILD: { + if (start.lVal != CHILDID_SELF) { + // Start of navigation must be on the View itself. + return E_INVALIDARG; + } else if (!view_->has_children()) { + // No children found. + return S_FALSE; + } + + // Set child_id based on first or last child. + int child_id = 0; + if (nav_dir == NAVDIR_LASTCHILD) + child_id = view_->child_count() - 1; + + views::View* child = view_->child_at(child_id); + end->vt = VT_DISPATCH; + end->pdispVal = child->GetNativeViewAccessible(); + end->pdispVal->AddRef(); + return S_OK; + } + case NAVDIR_LEFT: + case NAVDIR_UP: + case NAVDIR_PREVIOUS: + case NAVDIR_RIGHT: + case NAVDIR_DOWN: + case NAVDIR_NEXT: { + // Retrieve parent to access view index and perform bounds checking. + views::View* parent = view_->parent(); + if (!parent) { + return E_FAIL; + } + + if (start.lVal == CHILDID_SELF) { + int view_index = parent->GetIndexOf(view_); + // Check navigation bounds, adjusting for View child indexing (MSAA + // child indexing starts with 1, whereas View indexing starts with 0). + if (!IsValidNav(nav_dir, view_index, -1, + parent->child_count() - 1)) { + // Navigation attempted to go out-of-bounds. + end->vt = VT_EMPTY; + return S_FALSE; + } else { + if (IsNavDirNext(nav_dir)) { + view_index += 1; + } else { + view_index -=1; + } + } + + views::View* child = parent->child_at(view_index); + end->pdispVal = child->GetNativeViewAccessible(); + end->vt = VT_DISPATCH; + end->pdispVal->AddRef(); + return S_OK; + } else { + // Check navigation bounds, adjusting for MSAA child indexing (MSAA + // child indexing starts with 1, whereas View indexing starts with 0). + if (!IsValidNav(nav_dir, start.lVal, 0, parent->child_count() + 1)) { + // Navigation attempted to go out-of-bounds. + end->vt = VT_EMPTY; + return S_FALSE; + } else { + if (IsNavDirNext(nav_dir)) { + start.lVal += 1; + } else { + start.lVal -= 1; + } + } + + HRESULT result = this->get_accChild(start, &end->pdispVal); + if (result == S_FALSE) { + // Child is a leaf. + end->vt = VT_I4; + end->lVal = start.lVal; + } else if (result == E_INVALIDARG) { + return E_INVALIDARG; + } else { + // Child is not a leaf. + end->vt = VT_DISPATCH; + } + } + break; + } + default: + return E_INVALIDARG; + } + // Navigation performed correctly. Global return for this function, if no + // error triggered an escape earlier. + return S_OK; +} + +STDMETHODIMP NativeViewAccessibilityWin::get_accChild(VARIANT var_child, + IDispatch** disp_child) { + if (var_child.vt != VT_I4 || !disp_child) + return E_INVALIDARG; + + if (!view_) + return E_FAIL; + + LONG child_id = V_I4(&var_child); + + if (child_id == CHILDID_SELF) { + // Remain with the same dispatch. + return S_OK; + } + + views::View* child_view = NULL; + if (child_id > 0) { + int child_id_as_index = child_id - 1; + if (child_id_as_index < view_->child_count()) { + // Note: child_id is a one based index when indexing children. + child_view = view_->child_at(child_id_as_index); + } else { + // Attempt to retrieve a child view with the specified id. + child_view = view_->GetViewByID(child_id); + } + } else { + // Negative values are used for events fired using the view's + // NativeWidgetWin. + views::NativeWidgetWin* widget = static_cast<views::NativeWidgetWin*>( + view_->GetWidget()->native_widget()); + child_view = widget->GetAccessibilityViewEventAt(child_id); + } + + if (!child_view) { + // No child found. + *disp_child = NULL; + return E_FAIL; + } + + *disp_child = child_view->GetNativeViewAccessible(); + (*disp_child)->AddRef(); + return S_OK; +} + +STDMETHODIMP NativeViewAccessibilityWin::get_accChildCount(LONG* child_count) { + if (!child_count || !view_) + return E_INVALIDARG; + + if (!view_) + return E_FAIL; + + *child_count = view_->child_count(); + return S_OK; +} + +STDMETHODIMP NativeViewAccessibilityWin::get_accDefaultAction( + VARIANT var_id, BSTR* def_action) { + if (!IsValidId(var_id) || !def_action) + return E_INVALIDARG; + + if (!view_) + return E_FAIL; + + ui::AccessibleViewState state; + view_->GetAccessibleState(&state); + string16 temp_action = state.default_action; + + if (!temp_action.empty()) { + *def_action = SysAllocString(temp_action.c_str()); + } else { + return S_FALSE; + } + + return S_OK; +} + +STDMETHODIMP NativeViewAccessibilityWin::get_accDescription( + VARIANT var_id, BSTR* desc) { + if (!IsValidId(var_id) || !desc) + return E_INVALIDARG; + + if (!view_) + return E_FAIL; + + string16 temp_desc; + + view_->GetTooltipText(gfx::Point(), &temp_desc); + if (!temp_desc.empty()) { + *desc = SysAllocString(temp_desc.c_str()); + } else { + return S_FALSE; + } + + return S_OK; +} + +STDMETHODIMP NativeViewAccessibilityWin::get_accFocus(VARIANT* focus_child) { + if (!focus_child) + return E_INVALIDARG; + + if (!view_) + return E_FAIL; + + views::FocusManager* focus_manager = view_->GetFocusManager(); + views::View* focus = focus_manager ? focus_manager->GetFocusedView() : NULL; + if (focus == view_) { + // This view has focus. + focus_child->vt = VT_I4; + focus_child->lVal = CHILDID_SELF; + } else if (focus && view_->Contains(focus)) { + // Return the child object that has the keyboard focus. + focus_child->pdispVal = focus->GetNativeViewAccessible(); + focus_child->pdispVal->AddRef(); + return S_OK; + } else { + // Neither this object nor any of its children has the keyboard focus. + focus_child->vt = VT_EMPTY; + } + return S_OK; +} + +STDMETHODIMP NativeViewAccessibilityWin::get_accKeyboardShortcut( + VARIANT var_id, BSTR* acc_key) { + if (!IsValidId(var_id) || !acc_key) + return E_INVALIDARG; + + if (!view_) + return E_FAIL; + + ui::AccessibleViewState state; + view_->GetAccessibleState(&state); + string16 temp_key = state.keyboard_shortcut; + + if (!temp_key.empty()) { + *acc_key = SysAllocString(temp_key.c_str()); + } else { + return S_FALSE; + } + + return S_OK; +} + +STDMETHODIMP NativeViewAccessibilityWin::get_accName( + VARIANT var_id, BSTR* name) { + if (!IsValidId(var_id) || !name) + return E_INVALIDARG; + + if (!view_) + return E_FAIL; + + // Retrieve the current view's name. + ui::AccessibleViewState state; + view_->GetAccessibleState(&state); + string16 temp_name = state.name; + if (!temp_name.empty()) { + // Return name retrieved. + *name = SysAllocString(temp_name.c_str()); + } else { + // If view has no name, return S_FALSE. + return S_FALSE; + } + + return S_OK; +} + +STDMETHODIMP NativeViewAccessibilityWin::get_accParent( + IDispatch** disp_parent) { + if (!disp_parent) + return E_INVALIDARG; + + if (!view_) + return E_FAIL; + + views::View* parent_view = view_->parent(); + + if (!parent_view) { + // This function can get called during teardown of WidetWin so we + // should bail out if we fail to get the HWND. + if (!view_->GetWidget() || !view_->GetWidget()->GetNativeView()) { + *disp_parent = NULL; + return S_FALSE; + } + + // For a View that has no parent (e.g. root), point the accessible parent + // to the default implementation, to interface with Windows' hierarchy + // and to support calls from e.g. WindowFromAccessibleObject. + HRESULT hr = + ::AccessibleObjectFromWindow(view_->GetWidget()->GetNativeView(), + OBJID_WINDOW, IID_IAccessible, + reinterpret_cast<void**>(disp_parent)); + + if (!SUCCEEDED(hr)) { + *disp_parent = NULL; + return S_FALSE; + } + + return S_OK; + } + + *disp_parent = parent_view->GetNativeViewAccessible(); + (*disp_parent)->AddRef(); + return S_OK; +} + +STDMETHODIMP NativeViewAccessibilityWin::get_accRole( + VARIANT var_id, VARIANT* role) { + if (!IsValidId(var_id) || !role) + return E_INVALIDARG; + + if (!view_) + return E_FAIL; + + ui::AccessibleViewState state; + view_->GetAccessibleState(&state); + role->vt = VT_I4; + role->lVal = MSAARole(state.role); + return S_OK; +} + +STDMETHODIMP NativeViewAccessibilityWin::get_accState( + VARIANT var_id, VARIANT* state) { + // This returns MSAA states. See also the IAccessible2 interface + // get_states(). + + if (!IsValidId(var_id) || !state) + return E_INVALIDARG; + + if (!view_) + return E_FAIL; + + state->vt = VT_I4; + + // Retrieve all currently applicable states of the parent. + SetState(state, view_); + + // Make sure that state is not empty, and has the proper type. + if (state->vt == VT_EMPTY) + return E_FAIL; + + return S_OK; +} + +STDMETHODIMP NativeViewAccessibilityWin::get_accValue( + VARIANT var_id, BSTR* value) { + if (!IsValidId(var_id) || !value) + return E_INVALIDARG; + + if (!view_) + return E_FAIL; + + // Retrieve the current view's value. + ui::AccessibleViewState state; + view_->GetAccessibleState(&state); + string16 temp_value = state.value; + + if (!temp_value.empty()) { + // Return value retrieved. + *value = SysAllocString(temp_value.c_str()); + } else { + // If view has no value, fall back into the default implementation. + *value = NULL; + return E_NOTIMPL; + } + + return S_OK; +} + +// IAccessible functions not supported. + +STDMETHODIMP NativeViewAccessibilityWin::get_accSelection(VARIANT* selected) { + if (selected) + selected->vt = VT_EMPTY; + return E_NOTIMPL; +} + +STDMETHODIMP NativeViewAccessibilityWin::accSelect( + LONG flagsSelect, VARIANT var_id) { + return E_NOTIMPL; +} + +STDMETHODIMP NativeViewAccessibilityWin::get_accHelp( + VARIANT var_id, BSTR* help) { + if (help) + *help = NULL; + return E_NOTIMPL; +} + +STDMETHODIMP NativeViewAccessibilityWin::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 E_NOTIMPL; +} + +STDMETHODIMP NativeViewAccessibilityWin::put_accName( + VARIANT var_id, BSTR put_name) { + // Deprecated. + return E_NOTIMPL; +} + +STDMETHODIMP NativeViewAccessibilityWin::put_accValue( + VARIANT var_id, BSTR put_val) { + // Deprecated. + return E_NOTIMPL; +} + +// +// IAccessible2 +// + +STDMETHODIMP NativeViewAccessibilityWin::role(LONG* role) { + if (!view_) + return E_FAIL; + + if (!role) + return E_INVALIDARG; + + ui::AccessibleViewState state; + view_->GetAccessibleState(&state); + *role = MSAARole(state.role); + return S_OK; +} + +STDMETHODIMP NativeViewAccessibilityWin::get_states(AccessibleStates* states) { + // This returns IAccessible2 states, which supplement MSAA states. + // See also the MSAA interface get_accState. + + if (!view_) + return E_FAIL; + + if (!states) + return E_INVALIDARG; + + ui::AccessibleViewState state; + view_->GetAccessibleState(&state); + + // There are only a couple of states we need to support + // in IAccessible2. If any more are added, we may want to + // add a helper function like MSAAState. + *states = IA2_STATE_OPAQUE; + if (state.state & AccessibilityTypes::STATE_EDITABLE) + *states |= IA2_STATE_EDITABLE; + + return S_OK; +} + +STDMETHODIMP NativeViewAccessibilityWin::get_uniqueID(LONG* unique_id) { + if (!view_) + return E_FAIL; + + if (!unique_id) + return E_INVALIDARG; + + *unique_id = unique_id_; + return S_OK; +} + +STDMETHODIMP NativeViewAccessibilityWin::get_windowHandle(HWND* window_handle) { + if (!view_) + return E_FAIL; + + if (!window_handle) + return E_INVALIDARG; + + *window_handle = view_->GetWidget()->GetNativeView(); + return S_OK; +} + +// +// IAccessibleText +// + +STDMETHODIMP NativeViewAccessibilityWin::get_nCharacters(LONG* n_characters) { + if (!view_) + return E_FAIL; + + if (!n_characters) + return E_INVALIDARG; + + string16 text = TextForIAccessibleText(); + *n_characters = static_cast<LONG>(text.size()); + return S_OK; +} + +STDMETHODIMP NativeViewAccessibilityWin::get_caretOffset(LONG* offset) { + if (!view_) + return E_FAIL; + + if (!offset) + return E_INVALIDARG; + + ui::AccessibleViewState state; + view_->GetAccessibleState(&state); + *offset = static_cast<LONG>(state.selection_end); + return S_OK; +} + +STDMETHODIMP NativeViewAccessibilityWin::get_nSelections(LONG* n_selections) { + if (!view_) + return E_FAIL; + + if (!n_selections) + return E_INVALIDARG; + + ui::AccessibleViewState state; + view_->GetAccessibleState(&state); + if (state.selection_start != state.selection_end) + *n_selections = 1; + else + *n_selections = 0; + return S_OK; +} + +STDMETHODIMP NativeViewAccessibilityWin::get_selection(LONG selection_index, + LONG* start_offset, + LONG* end_offset) { + if (!view_) + return E_FAIL; + + if (!start_offset || !end_offset || selection_index != 0) + return E_INVALIDARG; + + ui::AccessibleViewState state; + view_->GetAccessibleState(&state); + *start_offset = static_cast<LONG>(state.selection_start); + *end_offset = static_cast<LONG>(state.selection_end); + return S_OK; +} + +STDMETHODIMP NativeViewAccessibilityWin::get_text(LONG start_offset, + LONG end_offset, + BSTR* text) { + if (!view_) + return E_FAIL; + + ui::AccessibleViewState state; + view_->GetAccessibleState(&state); + string16 text_str = TextForIAccessibleText(); + LONG len = static_cast<LONG>(text_str.size()); + + if (start_offset == IA2_TEXT_OFFSET_LENGTH) { + start_offset = len; + } else if (start_offset == IA2_TEXT_OFFSET_CARET) { + start_offset = static_cast<LONG>(state.selection_end); + } + if (end_offset == IA2_TEXT_OFFSET_LENGTH) { + end_offset = static_cast<LONG>(text_str.size()); + } else if (end_offset == IA2_TEXT_OFFSET_CARET) { + end_offset = static_cast<LONG>(state.selection_end); + } + + // The spec allows the arguments to be reversed. + if (start_offset > end_offset) { + LONG tmp = start_offset; + start_offset = end_offset; + end_offset = tmp; + } + + // The spec does not allow the start or end offsets to be out or range; + // we must return an error if so. + if (start_offset < 0) + return E_INVALIDARG; + if (end_offset > len) + return E_INVALIDARG; + + string16 substr = text_str.substr(start_offset, end_offset - start_offset); + if (substr.empty()) + return S_FALSE; + + *text = SysAllocString(substr.c_str()); + DCHECK(*text); + return S_OK; +} + +STDMETHODIMP NativeViewAccessibilityWin::get_textAtOffset( + LONG offset, + enum IA2TextBoundaryType boundary_type, + LONG* start_offset, LONG* end_offset, + BSTR* text) { + if (!start_offset || !end_offset || !text) + return E_INVALIDARG; + + // The IAccessible2 spec says we don't have to implement the "sentence" + // boundary type, we can just let the screenreader handle it. + if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) { + *start_offset = 0; + *end_offset = 0; + *text = NULL; + return S_FALSE; + } + + const string16& text_str = TextForIAccessibleText(); + + *start_offset = FindBoundary( + text_str, boundary_type, offset, ui::BACKWARDS_DIRECTION); + *end_offset = FindBoundary( + text_str, boundary_type, offset, ui::FORWARDS_DIRECTION); + return get_text(*start_offset, *end_offset, text); +} + +STDMETHODIMP NativeViewAccessibilityWin::get_textBeforeOffset( + LONG offset, + enum IA2TextBoundaryType boundary_type, + LONG* start_offset, LONG* end_offset, + BSTR* text) { + if (!start_offset || !end_offset || !text) + return E_INVALIDARG; + + // The IAccessible2 spec says we don't have to implement the "sentence" + // boundary type, we can just let the screenreader handle it. + if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) { + *start_offset = 0; + *end_offset = 0; + *text = NULL; + return S_FALSE; + } + + const string16& text_str = TextForIAccessibleText(); + + *start_offset = FindBoundary( + text_str, boundary_type, offset, ui::BACKWARDS_DIRECTION); + *end_offset = offset; + return get_text(*start_offset, *end_offset, text); +} + +STDMETHODIMP NativeViewAccessibilityWin::get_textAfterOffset( + LONG offset, + enum IA2TextBoundaryType boundary_type, + LONG* start_offset, LONG* end_offset, + BSTR* text) { + if (!start_offset || !end_offset || !text) + return E_INVALIDARG; + + // The IAccessible2 spec says we don't have to implement the "sentence" + // boundary type, we can just let the screenreader handle it. + if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) { + *start_offset = 0; + *end_offset = 0; + *text = NULL; + return S_FALSE; + } + + const string16& text_str = TextForIAccessibleText(); + + *start_offset = offset; + *end_offset = FindBoundary( + text_str, boundary_type, offset, ui::FORWARDS_DIRECTION); + return get_text(*start_offset, *end_offset, text); +} + +STDMETHODIMP NativeViewAccessibilityWin::get_offsetAtPoint( + LONG x, LONG y, enum IA2CoordinateType coord_type, LONG* offset) { + if (!view_) + return E_FAIL; + + if (!offset) + return E_INVALIDARG; + + // We don't support this method, but we have to return something + // rather than E_NOTIMPL or screen readers will complain. + *offset = 0; + return S_OK; +} + +// +// IServiceProvider methods. +// + +STDMETHODIMP NativeViewAccessibilityWin::QueryService( + REFGUID guidService, REFIID riid, void** object) { + if (!view_) + return E_FAIL; + + if (guidService == IID_IAccessible || + guidService == IID_IAccessible2 || + guidService == IID_IAccessibleText) { + return QueryInterface(riid, object); + } + + *object = NULL; + return E_FAIL; +} + +// +// Static methods. +// + +int32 NativeViewAccessibilityWin::MSAAEvent(AccessibilityTypes::Event event) { + switch (event) { + case AccessibilityTypes::EVENT_ALERT: + return EVENT_SYSTEM_ALERT; + case AccessibilityTypes::EVENT_FOCUS: + return EVENT_OBJECT_FOCUS; + case AccessibilityTypes::EVENT_MENUSTART: + return EVENT_SYSTEM_MENUSTART; + case AccessibilityTypes::EVENT_MENUEND: + return EVENT_SYSTEM_MENUEND; + case AccessibilityTypes::EVENT_MENUPOPUPSTART: + return EVENT_SYSTEM_MENUPOPUPSTART; + case AccessibilityTypes::EVENT_MENUPOPUPEND: + return EVENT_SYSTEM_MENUPOPUPEND; + case AccessibilityTypes::EVENT_NAME_CHANGED: + return EVENT_OBJECT_NAMECHANGE; + case AccessibilityTypes::EVENT_TEXT_CHANGED: + return EVENT_OBJECT_VALUECHANGE; + case AccessibilityTypes::EVENT_SELECTION_CHANGED: + return IA2_EVENT_TEXT_CARET_MOVED; + case AccessibilityTypes::EVENT_VALUE_CHANGED: + return EVENT_OBJECT_VALUECHANGE; + default: + // Not supported or invalid event. + NOTREACHED(); + return -1; + } +} + +int32 NativeViewAccessibilityWin::MSAARole(AccessibilityTypes::Role role) { + switch (role) { + case AccessibilityTypes::ROLE_ALERT: +return ROLE_SYSTEM_ALERT; + case AccessibilityTypes::ROLE_APPLICATION: + return ROLE_SYSTEM_APPLICATION; + case AccessibilityTypes::ROLE_BUTTONDROPDOWN: + return ROLE_SYSTEM_BUTTONDROPDOWN; + case AccessibilityTypes::ROLE_BUTTONMENU: + return ROLE_SYSTEM_BUTTONMENU; + case AccessibilityTypes::ROLE_CHECKBUTTON: + return ROLE_SYSTEM_CHECKBUTTON; + case AccessibilityTypes::ROLE_COMBOBOX: + return ROLE_SYSTEM_COMBOBOX; + case AccessibilityTypes::ROLE_DIALOG: + return ROLE_SYSTEM_DIALOG; + case AccessibilityTypes::ROLE_GRAPHIC: + return ROLE_SYSTEM_GRAPHIC; + case AccessibilityTypes::ROLE_GROUPING: + return ROLE_SYSTEM_GROUPING; + case AccessibilityTypes::ROLE_LINK: + return ROLE_SYSTEM_LINK; + case AccessibilityTypes::ROLE_LOCATION_BAR: + return ROLE_SYSTEM_GROUPING; + case AccessibilityTypes::ROLE_MENUBAR: + return ROLE_SYSTEM_MENUBAR; + case AccessibilityTypes::ROLE_MENUITEM: + return ROLE_SYSTEM_MENUITEM; + case AccessibilityTypes::ROLE_MENUPOPUP: + return ROLE_SYSTEM_MENUPOPUP; + case AccessibilityTypes::ROLE_OUTLINE: + return ROLE_SYSTEM_OUTLINE; + case AccessibilityTypes::ROLE_OUTLINEITEM: + return ROLE_SYSTEM_OUTLINEITEM; + case AccessibilityTypes::ROLE_PAGETAB: + return ROLE_SYSTEM_PAGETAB; + case AccessibilityTypes::ROLE_PAGETABLIST: + return ROLE_SYSTEM_PAGETABLIST; + case AccessibilityTypes::ROLE_PANE: + return ROLE_SYSTEM_PANE; + case AccessibilityTypes::ROLE_PROGRESSBAR: + return ROLE_SYSTEM_PROGRESSBAR; + case AccessibilityTypes::ROLE_PUSHBUTTON: + return ROLE_SYSTEM_PUSHBUTTON; + case AccessibilityTypes::ROLE_RADIOBUTTON: + return ROLE_SYSTEM_RADIOBUTTON; + case AccessibilityTypes::ROLE_SCROLLBAR: + return ROLE_SYSTEM_SCROLLBAR; + case AccessibilityTypes::ROLE_SEPARATOR: + return ROLE_SYSTEM_SEPARATOR; + case AccessibilityTypes::ROLE_STATICTEXT: + return ROLE_SYSTEM_STATICTEXT; + case AccessibilityTypes::ROLE_TEXT: + return ROLE_SYSTEM_TEXT; + case AccessibilityTypes::ROLE_TITLEBAR: + return ROLE_SYSTEM_TITLEBAR; + case AccessibilityTypes::ROLE_TOOLBAR: + return ROLE_SYSTEM_TOOLBAR; + case AccessibilityTypes::ROLE_WINDOW: + return ROLE_SYSTEM_WINDOW; + case AccessibilityTypes::ROLE_CLIENT: + default: + // This is the default role for MSAA. + return ROLE_SYSTEM_CLIENT; + } +} + +int32 NativeViewAccessibilityWin::MSAAState(AccessibilityTypes::State state) { + // This maps MSAA states for get_accState(). See also the IAccessible2 + // interface get_states(). + + int32 msaa_state = 0; + if (state & AccessibilityTypes::STATE_CHECKED) + msaa_state |= STATE_SYSTEM_CHECKED; + if (state & AccessibilityTypes::STATE_COLLAPSED) + msaa_state |= STATE_SYSTEM_COLLAPSED; + if (state & AccessibilityTypes::STATE_DEFAULT) + msaa_state |= STATE_SYSTEM_DEFAULT; + if (state & AccessibilityTypes::STATE_EXPANDED) + msaa_state |= STATE_SYSTEM_EXPANDED; + if (state & AccessibilityTypes::STATE_HASPOPUP) + msaa_state |= STATE_SYSTEM_HASPOPUP; + if (state & AccessibilityTypes::STATE_HOTTRACKED) + msaa_state |= STATE_SYSTEM_HOTTRACKED; + if (state & AccessibilityTypes::STATE_INVISIBLE) + msaa_state |= STATE_SYSTEM_INVISIBLE; + if (state & AccessibilityTypes::STATE_LINKED) + msaa_state |= STATE_SYSTEM_LINKED; + if (state & AccessibilityTypes::STATE_OFFSCREEN) + msaa_state |= STATE_SYSTEM_OFFSCREEN; + if (state & AccessibilityTypes::STATE_PRESSED) + msaa_state |= STATE_SYSTEM_PRESSED; + if (state & AccessibilityTypes::STATE_PROTECTED) + msaa_state |= STATE_SYSTEM_PROTECTED; + if (state & AccessibilityTypes::STATE_READONLY) + msaa_state |= STATE_SYSTEM_READONLY; + if (state & AccessibilityTypes::STATE_SELECTED) + msaa_state |= STATE_SYSTEM_SELECTED; + if (state & AccessibilityTypes::STATE_FOCUSED) + msaa_state |= STATE_SYSTEM_FOCUSED; + if (state & AccessibilityTypes::STATE_UNAVAILABLE) + msaa_state |= STATE_SYSTEM_UNAVAILABLE; + return msaa_state; +} + +// +// Private methods. +// + +bool NativeViewAccessibilityWin::IsNavDirNext(int nav_dir) const { + return (nav_dir == NAVDIR_RIGHT || + nav_dir == NAVDIR_DOWN || + nav_dir == NAVDIR_NEXT); +} + +bool NativeViewAccessibilityWin::IsValidNav( + int nav_dir, int start_id, int lower_bound, int upper_bound) const { + if (IsNavDirNext(nav_dir)) { + if ((start_id + 1) > upper_bound) { + return false; + } + } else { + if ((start_id - 1) <= lower_bound) { + return false; + } + } + return true; +} + +bool NativeViewAccessibilityWin::IsValidId(const VARIANT& child) const { + // View accessibility returns an IAccessible for each view so we only support + // the CHILDID_SELF id. + return (VT_I4 == child.vt) && (CHILDID_SELF == child.lVal); +} + +void NativeViewAccessibilityWin::SetState( + VARIANT* msaa_state, views::View* view) { + // Ensure the output param is initialized to zero. + msaa_state->lVal = 0; + + // Default state; all views can have accessibility focus. + msaa_state->lVal |= STATE_SYSTEM_FOCUSABLE; + + if (!view) + return; + + if (!view->IsEnabled()) + msaa_state->lVal |= STATE_SYSTEM_UNAVAILABLE; + if (!view->IsVisible()) + msaa_state->lVal |= STATE_SYSTEM_INVISIBLE; + if (view->IsHotTracked()) + msaa_state->lVal |= STATE_SYSTEM_HOTTRACKED; + if (view->HasFocus()) + msaa_state->lVal |= STATE_SYSTEM_FOCUSED; + + // Add on any view-specific states. + ui::AccessibleViewState view_state; + view->GetAccessibleState(&view_state); + msaa_state->lVal |= MSAAState(view_state.state); +} + +string16 NativeViewAccessibilityWin::TextForIAccessibleText() { + ui::AccessibleViewState state; + view_->GetAccessibleState(&state); + if (state.role == AccessibilityTypes::ROLE_TEXT) + return state.value; + else + return state.name; +} + +void NativeViewAccessibilityWin::HandleSpecialTextOffset( + const string16& text, LONG* offset) { + if (*offset == IA2_TEXT_OFFSET_LENGTH) { + *offset = static_cast<LONG>(text.size()); + } else if (*offset == IA2_TEXT_OFFSET_CARET) { + get_caretOffset(offset); + } +} + +ui::TextBoundaryType NativeViewAccessibilityWin::IA2TextBoundaryToTextBoundary( + IA2TextBoundaryType ia2_boundary) { + switch(ia2_boundary) { + case IA2_TEXT_BOUNDARY_CHAR: return ui::CHAR_BOUNDARY; + case IA2_TEXT_BOUNDARY_WORD: return ui::WORD_BOUNDARY; + case IA2_TEXT_BOUNDARY_LINE: return ui::LINE_BOUNDARY; + case IA2_TEXT_BOUNDARY_SENTENCE: return ui::SENTENCE_BOUNDARY; + case IA2_TEXT_BOUNDARY_PARAGRAPH: return ui::PARAGRAPH_BOUNDARY; + case IA2_TEXT_BOUNDARY_ALL: return ui::ALL_BOUNDARY; + default: + NOTREACHED(); + return ui::CHAR_BOUNDARY; + } +} + +LONG NativeViewAccessibilityWin::FindBoundary( + const string16& text, + IA2TextBoundaryType ia2_boundary, + LONG start_offset, + ui::TextBoundaryDirection direction) { + HandleSpecialTextOffset(text, &start_offset); + ui::TextBoundaryType boundary = IA2TextBoundaryToTextBoundary(ia2_boundary); + std::vector<int32> line_breaks; + return ui::FindAccessibleTextBoundary( + text, line_breaks, boundary, start_offset, direction); +} diff --git a/ui/views/accessibility/native_view_accessibility_win.h b/ui/views/accessibility/native_view_accessibility_win.h new file mode 100644 index 0000000..854f023 --- /dev/null +++ b/ui/views/accessibility/native_view_accessibility_win.h @@ -0,0 +1,355 @@ +// 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. + +#ifndef VIEWS_ACCESSIBILITY_NATIVE_VIEW_ACCESSIBILITY_WIN_H_ +#define VIEWS_ACCESSIBILITY_NATIVE_VIEW_ACCESSIBILITY_WIN_H_ +#pragma once + +#include <atlbase.h> +#include <atlcom.h> + +#include <oleacc.h> + +#include "base/memory/scoped_ptr.h" +#include "third_party/iaccessible2/ia2_api_all.h" +#include "ui/base/accessibility/accessible_view_state.h" +#include "views/controls/native/native_view_host.h" +#include "views/view.h" + +namespace ui { +enum TextBoundaryDirection; +enum TextBoundaryType; +} + +// Note: do not put NativeViewAccessibilityWin in the namespace "views"; +// Visual Studio 2005 does not allow an ATL::CComObject symbol in a namespace. + +//////////////////////////////////////////////////////////////////////////////// +// +// NativeViewAccessibilityWin +// +// Class implementing the MSAA IAccessible COM interface for a generic View, +// providing accessibility to be used by screen readers and other assistive +// technology (AT). +// +//////////////////////////////////////////////////////////////////////////////// +class __declspec(uuid("26f5641a-246d-457b-a96d-07f3fae6acf2")) +NativeViewAccessibilityWin + : public CComObjectRootEx<CComMultiThreadModel>, + public IDispatchImpl<IAccessible2, &IID_IAccessible2, + &LIBID_IAccessible2Lib>, + public IAccessibleText, + public IServiceProvider { + public: + BEGIN_COM_MAP(NativeViewAccessibilityWin) + COM_INTERFACE_ENTRY2(IDispatch, IAccessible2) + COM_INTERFACE_ENTRY2(IAccessible, IAccessible2) + COM_INTERFACE_ENTRY(IAccessible2) + COM_INTERFACE_ENTRY(IAccessibleText) + COM_INTERFACE_ENTRY(IServiceProvider) + END_COM_MAP() + + // Create method for view accessibility. + static scoped_refptr<NativeViewAccessibilityWin> Create(views::View* view); + + virtual ~NativeViewAccessibilityWin(); + + void set_view(views::View* view) { view_ = view; } + + // Supported IAccessible methods. + + // Retrieves the child element or child object at a given point on the screen. + STDMETHODIMP accHitTest(LONG x_left, LONG y_top, VARIANT* child); + + // Performs the object's default action. + STDMETHODIMP accDoDefaultAction(VARIANT var_id); + + // 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 tooltip 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 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); + + // Retrieves the current value associated with the specified object. + STDMETHODIMP get_accValue(VARIANT var_id, BSTR* value); + + // Non-supported IAccessible methods. + + // Selections not applicable to views. + STDMETHODIMP get_accSelection(VARIANT* selected); + STDMETHODIMP accSelect(LONG flags_sel, VARIANT var_id); + + // Help functions not supported. + STDMETHODIMP get_accHelp(VARIANT var_id, BSTR* help); + STDMETHODIMP get_accHelpTopic(BSTR* help_file, + VARIANT var_id, + LONG* topic_id); + + // Deprecated functions, not implemented here. + STDMETHODIMP put_accName(VARIANT var_id, BSTR put_name); + STDMETHODIMP put_accValue(VARIANT var_id, BSTR put_val); + + // + // IAccessible2 + // + + STDMETHODIMP role(LONG* role); + + STDMETHODIMP get_states(AccessibleStates* states); + + STDMETHODIMP get_uniqueID(LONG* unique_id); + + STDMETHODIMP get_windowHandle(HWND* window_handle); + + // + // IAccessible2 methods not implemented. + // + + STDMETHODIMP get_attributes(BSTR* attributes) { + return E_NOTIMPL; + } + STDMETHODIMP get_indexInParent(LONG* index_in_parent) { + return E_NOTIMPL; + } + STDMETHODIMP get_extendedRole(BSTR* extended_role) { + return E_NOTIMPL; + } + STDMETHODIMP get_nRelations(LONG* n_relations) { + return E_NOTIMPL; + } + STDMETHODIMP get_relation(LONG relation_index, + IAccessibleRelation** relation) { + return E_NOTIMPL; + } + STDMETHODIMP get_relations(LONG max_relations, + IAccessibleRelation** relations, + LONG* n_relations) { + return E_NOTIMPL; + } + STDMETHODIMP scrollTo(enum IA2ScrollType scroll_type) { + return E_NOTIMPL; + } + STDMETHODIMP scrollToPoint( + enum IA2CoordinateType coordinate_type, + LONG x, + LONG y) { + return E_NOTIMPL; + } + STDMETHODIMP get_groupPosition(LONG* group_level, + LONG* similar_items_in_group, + LONG* position_in_group) { + return E_NOTIMPL; + } + STDMETHODIMP get_localizedExtendedRole( + BSTR* localized_extended_role) { + return E_NOTIMPL; + } + STDMETHODIMP get_nExtendedStates(LONG* n_extended_states) { + return E_NOTIMPL; + } + STDMETHODIMP get_extendedStates(LONG max_extended_states, + BSTR** extended_states, + LONG* n_extended_states) { + return E_NOTIMPL; + } + STDMETHODIMP get_localizedExtendedStates( + LONG max_localized_extended_states, + BSTR** localized_extended_states, + LONG* n_localized_extended_states) { + return E_NOTIMPL; + } + STDMETHODIMP get_locale(IA2Locale* locale) { + return E_NOTIMPL; + } + + // + // IAccessibleText methods. + // + + STDMETHODIMP get_nCharacters(LONG* n_characters); + + STDMETHODIMP get_caretOffset(LONG* offset); + + STDMETHODIMP get_nSelections(LONG* n_selections); + + STDMETHODIMP get_selection(LONG selection_index, + LONG* start_offset, + LONG* end_offset); + + STDMETHODIMP get_text(LONG start_offset, LONG end_offset, BSTR* text); + + STDMETHODIMP get_textAtOffset(LONG offset, + enum IA2TextBoundaryType boundary_type, + LONG* start_offset, LONG* end_offset, + BSTR* text); + + STDMETHODIMP get_textBeforeOffset(LONG offset, + enum IA2TextBoundaryType boundary_type, + LONG* start_offset, LONG* end_offset, + BSTR* text); + + STDMETHODIMP get_textAfterOffset(LONG offset, + enum IA2TextBoundaryType boundary_type, + LONG* start_offset, LONG* end_offset, + BSTR* text); + + STDMETHODIMP get_offsetAtPoint(LONG x, LONG y, + enum IA2CoordinateType coord_type, + LONG* offset); + + // + // IAccessibleText methods not implemented. + // + + STDMETHODIMP get_newText(IA2TextSegment* new_text) { + return E_NOTIMPL; + } + STDMETHODIMP get_oldText(IA2TextSegment* old_text) { + return E_NOTIMPL; + } + STDMETHODIMP addSelection(LONG start_offset, LONG end_offset) { + return E_NOTIMPL; + } + STDMETHODIMP get_attributes(LONG offset, + LONG* start_offset, + LONG* end_offset, + BSTR* text_attributes) { + return E_NOTIMPL; + } + STDMETHODIMP get_characterExtents(LONG offset, + enum IA2CoordinateType coord_type, + LONG* x, LONG* y, + LONG* width, LONG* height) { + return E_NOTIMPL; + } + STDMETHODIMP removeSelection(LONG selection_index) { + return E_NOTIMPL; + } + STDMETHODIMP setCaretOffset(LONG offset) { + return E_NOTIMPL; + } + STDMETHODIMP setSelection(LONG selection_index, + LONG start_offset, + LONG end_offset) { + return E_NOTIMPL; + } + STDMETHODIMP scrollSubstringTo(LONG start_index, + LONG end_index, + enum IA2ScrollType scroll_type) { + return E_NOTIMPL; + } + STDMETHODIMP scrollSubstringToPoint(LONG start_index, + LONG end_index, + enum IA2CoordinateType coordinate_type, + LONG x, LONG y) { + return E_NOTIMPL; + } + + // + // IServiceProvider methods. + // + + STDMETHODIMP QueryService(REFGUID guidService, REFIID riid, void** object); + + // Returns a conversion from the event (as defined in accessibility_types.h) + // to an MSAA event. + static int32 MSAAEvent(ui::AccessibilityTypes::Event event); + + // Returns a conversion from the Role (as defined in accessibility_types.h) + // to an MSAA role. + static int32 MSAARole(ui::AccessibilityTypes::Role role); + + // Returns a conversion from the State (as defined in accessibility_types.h) + // to MSAA states set. + static int32 MSAAState(ui::AccessibilityTypes::State state); + + private: + NativeViewAccessibilityWin(); + + // Determines navigation direction for accNavigate, based on left, up and + // previous being mapped all to previous and right, down, next being mapped + // to next. Returns true if navigation direction is next, false otherwise. + bool IsNavDirNext(int nav_dir) const; + + // Determines if the navigation target is within the allowed bounds. Returns + // true if it is, false otherwise. + bool IsValidNav(int nav_dir, + int start_id, + int lower_bound, + int upper_bound) const; + + // Determines if the child id variant is valid. + bool IsValidId(const VARIANT& child) const; + + // Helper function which sets applicable states of view. + void SetState(VARIANT* msaa_state, views::View* view); + + // Return the text to use for IAccessibleText. + string16 TextForIAccessibleText(); + + // If offset is a member of IA2TextSpecialOffsets this function updates the + // value of offset and returns, otherwise offset remains unchanged. + void HandleSpecialTextOffset(const string16& text, LONG* offset); + + // Convert from a IA2TextBoundaryType to a ui::TextBoundaryType. + ui::TextBoundaryType IA2TextBoundaryToTextBoundary(IA2TextBoundaryType type); + + // Search forwards (direction == 1) or backwards (direction == -1) + // from the given offset until the given boundary is found, and + // return the offset of that boundary. + LONG FindBoundary(const string16& text, + IA2TextBoundaryType ia2_boundary, + LONG start_offset, + ui::TextBoundaryDirection direction); + + // Give CComObject access to the class constructor. + template <class Base> friend class CComObject; + + // Member View needed for view-specific calls. + views::View* view_; + + // A unique id for each object, needed for IAccessible2. + long unique_id_; + + // Next unique id to assign. + static long next_unique_id_; + + DISALLOW_COPY_AND_ASSIGN(NativeViewAccessibilityWin); +}; + +#endif // VIEWS_ACCESSIBILITY_NATIVE_VIEW_ACCESSIBILITY_WIN_H_ |