diff options
author | dmazzoni@chromium.org <dmazzoni@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-08 18:01:38 +0000 |
---|---|---|
committer | dmazzoni@chromium.org <dmazzoni@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-08 18:01:38 +0000 |
commit | d1fdc6f83a75e316aafef3ff0f84f30a132f08c3 (patch) | |
tree | 0bb8fa503fcdc9ff4e73cdcef887e1f3054edd6d /chrome/browser/accessibility | |
parent | e6025de05d4f747edb3f638ed99b95b1b08360b2 (diff) | |
download | chromium_src-d1fdc6f83a75e316aafef3ff0f84f30a132f08c3.zip chromium_src-d1fdc6f83a75e316aafef3ff0f84f30a132f08c3.tar.gz chromium_src-d1fdc6f83a75e316aafef3ff0f84f30a132f08c3.tar.bz2 |
Implement additional IAccessibleText methods to return the nearest
character, word, line, sentence, or paragraph within a text field.
These methods are needed by SAToGo.
BUG=36217
TEST=Added new unit test.
Review URL: http://codereview.chromium.org/3550017
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@61979 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/accessibility')
3 files changed, 304 insertions, 65 deletions
diff --git a/chrome/browser/accessibility/browser_accessibility_win.cc b/chrome/browser/accessibility/browser_accessibility_win.cc index 432eb1a..8817dfa 100644 --- a/chrome/browser/accessibility/browser_accessibility_win.cc +++ b/chrome/browser/accessibility/browser_accessibility_win.cc @@ -54,7 +54,7 @@ HRESULT BrowserAccessibilityWin::accDoDefaultAction(VARIANT var_id) { } STDMETHODIMP BrowserAccessibilityWin::accHitTest(LONG x_left, LONG y_top, - VARIANT* child) { + VARIANT* child) { if (!instance_active_) return E_FAIL; @@ -65,8 +65,8 @@ STDMETHODIMP BrowserAccessibilityWin::accHitTest(LONG x_left, LONG y_top, } STDMETHODIMP BrowserAccessibilityWin::accLocation(LONG* x_left, LONG* y_top, - LONG* width, LONG* height, - VARIANT var_id) { + LONG* width, LONG* height, + VARIANT var_id) { if (!instance_active_) return E_FAIL; @@ -534,45 +534,6 @@ STDMETHODIMP BrowserAccessibilityWin::get_nCharacters(LONG* n_characters) { return S_OK; } -STDMETHODIMP BrowserAccessibilityWin::get_text( - LONG start_offset, LONG end_offset, BSTR* text) { - if (!instance_active_) - return E_FAIL; - - if (!text) - return E_INVALIDARG; - - string16 text_str; - if (role_ == WebAccessibility::ROLE_TEXT_FIELD) { - text_str = value_; - } else { - text_str = name_; - } - - // 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. - LONG len = text_str.length(); - 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 BrowserAccessibilityWin::get_caretOffset(LONG* offset) { if (!instance_active_) return E_FAIL; @@ -585,7 +546,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_caretOffset(LONG* offset) { if (GetAttributeAsInt(WebAccessibility::ATTR_TEXT_SEL_START, &sel_start)) { *offset = sel_start; } else { - *offset = 0; + *offset = 0; } } else { *offset = 0; @@ -646,6 +607,121 @@ STDMETHODIMP BrowserAccessibilityWin::get_selection(LONG selection_index, return S_OK; } +STDMETHODIMP BrowserAccessibilityWin::get_text( + LONG start_offset, LONG end_offset, BSTR* text) { + if (!instance_active_) + return E_FAIL; + + if (!text) + return E_INVALIDARG; + + const string16& text_str = TextForIAccessibleText(); + + // 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. + LONG len = text_str.length(); + 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 BrowserAccessibilityWin::get_textAtOffset( + LONG offset, + enum IA2TextBoundaryType boundary_type, + LONG* start_offset, LONG* end_offset, + BSTR* text) { + if (!instance_active_) + return E_FAIL; + + 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, -1); + *end_offset = FindBoundary(text_str, boundary_type, offset, 1); + return get_text(*start_offset, *end_offset, text); +} + +STDMETHODIMP BrowserAccessibilityWin::get_textBeforeOffset( + LONG offset, + enum IA2TextBoundaryType boundary_type, + LONG* start_offset, LONG* end_offset, + BSTR* text) { + if (!instance_active_) + return E_FAIL; + + 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, -1); + *end_offset = offset; + return get_text(*start_offset, *end_offset, text); +} + +STDMETHODIMP BrowserAccessibilityWin::get_textAfterOffset( + LONG offset, + enum IA2TextBoundaryType boundary_type, + LONG* start_offset, LONG* end_offset, + BSTR* text) { + if (!instance_active_) + return E_FAIL; + + 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, 1); + return get_text(*start_offset, *end_offset, text); +} + // // ISimpleDOMDocument methods. // @@ -1129,6 +1205,76 @@ string16 BrowserAccessibilityWin::Escape(string16 str) { return EscapeQueryParamValueUTF8(str, false); } +const string16& BrowserAccessibilityWin::TextForIAccessibleText() { + if (role_ == WebAccessibility::ROLE_TEXT_FIELD) { + return value_; + } else { + return name_; + } +} + +LONG BrowserAccessibilityWin::FindBoundary( + const string16& text, + IA2TextBoundaryType boundary, + LONG start_offset, + LONG direction) { + LONG text_size = static_cast<LONG>(text.size()); + DCHECK(start_offset >= 0 && start_offset <= text_size); + DCHECK(direction == 1 || direction == -1); + + if (boundary == IA2_TEXT_BOUNDARY_CHAR) { + if (direction == 1 && start_offset < text_size) + return start_offset + 1; + else + return start_offset; + } + + LONG result = start_offset; + for (;;) { + LONG pos; + if (direction == 1) { + if (result >= text_size) + return text_size; + pos = result; + } else { + if (result <= 0) + return 0; + pos = result - 1; + } + + switch (boundary) { + case IA2_TEXT_BOUNDARY_WORD: + if (IsWhitespace(text[pos])) + return result; + break; + case IA2_TEXT_BOUNDARY_LINE: + case IA2_TEXT_BOUNDARY_PARAGRAPH: + if (text[pos] == '\n') + return result; + case IA2_TEXT_BOUNDARY_SENTENCE: + // Note that we don't actually have to implement sentence support; + // currently IAccessibleText functions return S_FALSE so that + // screenreaders will handle it on their own. + if ((text[pos] == '.' || text[pos] == '!' || text[pos] == '?') && + (pos == text_size - 1 || IsWhitespace(text[pos + 1]))) { + return result; + } + case IA2_TEXT_BOUNDARY_ALL: + default: + break; + } + + if (direction > 0) { + result++; + } else if (direction < 0) { + result--; + } else { + NOTREACHED(); + return result; + } + } +} + void BrowserAccessibilityWin::InitRoleAndState() { ia_state_ = 0; ia2_state_ = IA2_STATE_OPAQUE; diff --git a/chrome/browser/accessibility/browser_accessibility_win.h b/chrome/browser/accessibility/browser_accessibility_win.h index dbd014f..16ea136 100644 --- a/chrome/browser/accessibility/browser_accessibility_win.h +++ b/chrome/browser/accessibility/browser_accessibility_win.h @@ -230,8 +230,6 @@ class BrowserAccessibilityWin STDMETHODIMP get_nCharacters(LONG* n_characters); - STDMETHODIMP get_text(LONG start_offset, LONG end_offset, BSTR* text); - STDMETHODIMP get_caretOffset(LONG* offset); STDMETHODIMP get_nSelections(LONG* n_selections); @@ -240,6 +238,22 @@ class BrowserAccessibilityWin 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); // IAccessibleText methods not implemented. STDMETHODIMP addSelection(LONG start_offset, LONG end_offset) { @@ -256,26 +270,8 @@ class BrowserAccessibilityWin return E_NOTIMPL; } STDMETHODIMP get_offsetAtPoint(LONG x, LONG y, - enum IA2CoordinateType coord_type, - LONG* offset) { - return E_NOTIMPL; - } - STDMETHODIMP get_textBeforeOffset(LONG offset, - enum IA2TextBoundaryType boundary_type, - LONG* start_offset, LONG* end_offset, - BSTR* text) { - return E_NOTIMPL; - } - STDMETHODIMP get_textAfterOffset(LONG offset, - enum IA2TextBoundaryType boundary_type, - LONG* start_offset, LONG* end_offset, - BSTR* text) { - return E_NOTIMPL; - } - STDMETHODIMP get_textAtOffset(LONG offset, - enum IA2TextBoundaryType boundary_type, - LONG* start_offset, LONG* end_offset, - BSTR* text) { + enum IA2CoordinateType coord_type, + LONG* offset) { return E_NOTIMPL; } STDMETHODIMP removeSelection(LONG selection_index) { @@ -484,6 +480,18 @@ class BrowserAccessibilityWin // Escape a string like it would be escaped for a URL or HTML form. string16 Escape(string16 str); + // Get the text of this node for the purposes of IAccessibleText - it may + // be the name, it may be the value, etc. depending on the role. + const string16& TextForIAccessibleText(); + + // Search forwards (direction == 1) or backwards (direction == -1) from + // the given offset until the given IAccessible2 boundary (like word, + // sentence) is found, and return its offset. + LONG FindBoundary(const string16& text, + IA2TextBoundaryType boundary, + LONG start_offset, + LONG direction); + // COM objects are reference-counted. When we're done with this object // and it's removed from our accessibility tree, a client may still be // holding onto a pointer to this object, so we mark it as inactive diff --git a/chrome/browser/accessibility/browser_accessibility_win_unittest.cc b/chrome/browser/accessibility/browser_accessibility_win_unittest.cc index 549700a..dd264a1 100644 --- a/chrome/browser/accessibility/browser_accessibility_win_unittest.cc +++ b/chrome/browser/accessibility/browser_accessibility_win_unittest.cc @@ -274,3 +274,88 @@ TEST_F(BrowserAccessibilityTest, TestChildrenChangeNoLeaks) { delete manager; ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_); } + +TEST_F(BrowserAccessibilityTest, TestTextBoundaries) { + WebAccessibility text1; + text1.id = 11; + text1.role = WebAccessibility::ROLE_TEXT_FIELD; + text1.state = 0; + text1.value = L"One two three.\nFour five six."; + + WebAccessibility root; + root.id = 1; + root.role = WebAccessibility::ROLE_DOCUMENT; + root.state = 0; + root.children.push_back(text1); + + CountedBrowserAccessibility::global_obj_count_ = 0; + BrowserAccessibilityManager* manager = BrowserAccessibilityManager::Create( + GetDesktopWindow(), root, NULL, + new CountedBrowserAccessibilityFactory()); + ASSERT_EQ(2, CountedBrowserAccessibility::global_obj_count_); + + BrowserAccessibilityWin* root_obj = + manager->GetRoot()->toBrowserAccessibilityWin(); + BrowserAccessibilityWin* text1_obj = + root_obj->GetChild(0)->toBrowserAccessibilityWin(); + + BSTR text; + long start; + long end; + + long text1_len; + ASSERT_EQ(S_OK, text1_obj->get_nCharacters(&text1_len)); + + ASSERT_EQ(S_OK, text1_obj->get_text(0, text1_len, &text)); + ASSERT_EQ(text, text1.value); + SysFreeString(text); + + ASSERT_EQ(S_OK, text1_obj->get_text(0, 4, &text)); + ASSERT_EQ(text, std::wstring(L"One ")); + SysFreeString(text); + + ASSERT_EQ(S_OK, text1_obj->get_textAtOffset( + 1, IA2_TEXT_BOUNDARY_CHAR, &start, &end, &text)); + ASSERT_EQ(start, 1); + ASSERT_EQ(end, 2); + ASSERT_EQ(text, std::wstring(L"n")); + SysFreeString(text); + + ASSERT_EQ(S_FALSE, text1_obj->get_textAtOffset( + text1_len, IA2_TEXT_BOUNDARY_CHAR, &start, &end, &text)); + ASSERT_EQ(start, text1_len); + ASSERT_EQ(end, text1_len); + + ASSERT_EQ(S_OK, text1_obj->get_textAtOffset( + 1, IA2_TEXT_BOUNDARY_WORD, &start, &end, &text)); + ASSERT_EQ(start, 0); + ASSERT_EQ(end, 3); + ASSERT_EQ(text, std::wstring(L"One")); + SysFreeString(text); + + ASSERT_EQ(S_OK, text1_obj->get_textAtOffset( + 6, IA2_TEXT_BOUNDARY_WORD, &start, &end, &text)); + ASSERT_EQ(start, 4); + ASSERT_EQ(end, 7); + ASSERT_EQ(text, std::wstring(L"two")); + SysFreeString(text); + + ASSERT_EQ(S_OK, text1_obj->get_textAtOffset( + text1_len, IA2_TEXT_BOUNDARY_WORD, &start, &end, &text)); + ASSERT_EQ(start, 25); + ASSERT_EQ(end, 29); + ASSERT_EQ(text, std::wstring(L"six.")); + SysFreeString(text); + + ASSERT_EQ(S_OK, text1_obj->get_textAtOffset( + 1, IA2_TEXT_BOUNDARY_LINE, &start, &end, &text)); + ASSERT_EQ(start, 0); + ASSERT_EQ(end, 13); + ASSERT_EQ(text, std::wstring(L"One two three")); + SysFreeString(text); + + // Delete the manager and test that all BrowserAccessibility instances are + // deleted. + delete manager; + ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_); +} |