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; |
+} |