Chromium Code Reviews| Index: views/accessibility/native_view_accessibility_win.cc |
| =================================================================== |
| --- views/accessibility/native_view_accessibility_win.cc (revision 107097) |
| +++ views/accessibility/native_view_accessibility_win.cc (working copy) |
| @@ -2,8 +2,12 @@ |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| +#include <atlbase.h> |
| +#include <atlcom.h> |
| + |
| #include "views/accessibility/native_view_accessibility_win.h" |
| +#include "third_party/iaccessible2/ia2_api_all.h" |
| #include "ui/base/accessibility/accessible_view_state.h" |
| #include "ui/base/view_prop.h" |
| #include "views/widget/native_widget_win.h" |
| @@ -11,10 +15,8 @@ |
| using ui::AccessibilityTypes; |
| -namespace views { |
| -const char kViewsNativeHostPropForAccessibility[] = |
| - "Views_NativeViewHostHWNDForAccessibility"; |
| -} |
| +// static |
| +long NativeViewAccessibilityWin::next_unique_id_ = 1; |
| // static |
| scoped_refptr<NativeViewAccessibilityWin> NativeViewAccessibilityWin::Create( |
| @@ -27,7 +29,9 @@ |
| return scoped_refptr<NativeViewAccessibilityWin>(instance); |
| } |
| -NativeViewAccessibilityWin::NativeViewAccessibilityWin() : view_(NULL) { |
| +NativeViewAccessibilityWin::NativeViewAccessibilityWin() |
| + : view_(NULL), |
| + unique_id_(next_unique_id_++) { |
| } |
| NativeViewAccessibilityWin::~NativeViewAccessibilityWin() { |
| @@ -208,6 +212,8 @@ |
| if (child_id == CHILDID_SELF) { |
| // Remain with the same dispatch. |
|
sky
2011/10/25 20:08:45
Update this comment. Also document who does the re
dmazzoni
2011/10/26 16:46:13
I'll move this to a separate changelist, it's not
|
| + AddRef(); |
| + *disp_child = this; |
| return S_OK; |
| } |
| @@ -226,7 +232,7 @@ |
| // NativeWidgetWin. |
| views::NativeWidgetWin* widget = static_cast<views::NativeWidgetWin*>( |
| view_->GetWidget()->native_widget()); |
| - child_view = widget->GetAccessibilityViewEventAt(child_id); |
| + child_view = widget->GetAccessibilityViewFromChildId(child_id); |
| } |
| if (!child_view) { |
| @@ -419,6 +425,9 @@ |
| 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; |
| @@ -462,62 +471,6 @@ |
| return S_OK; |
| } |
| -// Helper functions. |
| - |
| -bool NativeViewAccessibilityWin::IsNavDirNext(int nav_dir) const { |
| - if (nav_dir == NAVDIR_RIGHT || nav_dir == NAVDIR_DOWN || |
| - nav_dir == NAVDIR_NEXT) { |
| - return true; |
| - } |
| - return false; |
| -} |
| - |
| -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); |
| -} |
| - |
| // IAccessible functions not supported. |
| STDMETHODIMP NativeViewAccessibilityWin::get_accSelection(VARIANT* selected) { |
| @@ -561,6 +514,211 @@ |
| 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_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: |
| @@ -580,7 +738,7 @@ |
| case AccessibilityTypes::EVENT_TEXT_CHANGED: |
| return EVENT_OBJECT_VALUECHANGE; |
| case AccessibilityTypes::EVENT_SELECTION_CHANGED: |
| - return EVENT_OBJECT_TEXTSELECTIONCHANGED; |
| + return IA2_EVENT_TEXT_CARET_MOVED; |
| case AccessibilityTypes::EVENT_VALUE_CHANGED: |
| return EVENT_OBJECT_VALUECHANGE; |
| default: |
| @@ -658,6 +816,9 @@ |
| } |
| 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; |
| @@ -691,3 +852,70 @@ |
| msaa_state |= STATE_SYSTEM_UNAVAILABLE; |
| return msaa_state; |
| } |
| + |
| +// |
| +// Private methods. |
| +// |
| + |
| +bool NativeViewAccessibilityWin::IsNavDirNext(int nav_dir) const { |
| + if (nav_dir == NAVDIR_RIGHT || nav_dir == NAVDIR_DOWN || |
|
sky
2011/10/25 20:08:45
nit: make this the return statement, eg:
return (
dmazzoni
2011/10/26 16:46:13
Done.
|
| + nav_dir == NAVDIR_NEXT) { |
| + return true; |
| + } |
| + return false; |
| +} |
| + |
| +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; |
| +} |