diff options
author | kkania@chromium.org <kkania@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-12-09 01:07:43 +0000 |
---|---|---|
committer | kkania@chromium.org <kkania@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-12-09 01:07:43 +0000 |
commit | 8ed2d73d12fe656de95cd707557ccdd87af1e08b (patch) | |
tree | f7532ed2f8fce81d1d412af35d2d26d00866eab1 /chrome_frame | |
parent | 9ff08dd2596afb91b423ff12c30208e2e2bb5587 (diff) | |
download | chromium_src-8ed2d73d12fe656de95cd707557ccdd87af1e08b.zip chromium_src-8ed2d73d12fe656de95cd707557ccdd87af1e08b.tar.gz chromium_src-8ed2d73d12fe656de95cd707557ccdd87af1e08b.tar.bz2 |
Add support for detecting text selection in chrome frame tests. This requires adding support for IAccessible2 interfaces.
BUG=none
TEST=none
Review URL: http://codereview.chromium.org/4406002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@68680 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome_frame')
-rw-r--r-- | chrome_frame/chrome_frame.gyp | 3 | ||||
-rw-r--r-- | chrome_frame/test/chrome_frame_ui_test_utils.cc | 100 | ||||
-rw-r--r-- | chrome_frame/test/chrome_frame_ui_test_utils.h | 42 | ||||
-rw-r--r-- | chrome_frame/test/mock_ie_event_sink_actions.h | 13 | ||||
-rw-r--r-- | chrome_frame/test/mock_ie_event_sink_test.h | 1 | ||||
-rw-r--r-- | chrome_frame/test/run_all_unittests.cc | 6 |
6 files changed, 144 insertions, 21 deletions
diff --git a/chrome_frame/chrome_frame.gyp b/chrome_frame/chrome_frame.gyp index afa7e80..579c0f3 100644 --- a/chrome_frame/chrome_frame.gyp +++ b/chrome_frame/chrome_frame.gyp @@ -263,6 +263,8 @@ '../net/net.gyp:net_test_support', '../testing/gmock.gyp:gmock', '../testing/gtest.gyp:gtest', + '../third_party/iaccessible2/iaccessible2.gyp:iaccessible2', + '../third_party/iaccessible2/iaccessible2.gyp:IAccessible2Proxy', '../third_party/libxslt/libxslt.gyp:libxslt', 'chrome_frame_ie', 'chrome_frame_npapi', @@ -598,6 +600,7 @@ '../net/net.gyp:net_test_support', '../testing/gmock.gyp:gmock', '../testing/gtest.gyp:gtest', + '../third_party/iaccessible2/iaccessible2.gyp:iaccessible2', 'chrome_frame_ie', 'chrome_frame_npapi', 'chrome_frame_strings', diff --git a/chrome_frame/test/chrome_frame_ui_test_utils.cc b/chrome_frame/test/chrome_frame_ui_test_utils.cc index 69e9bf2..d9013c0 100644 --- a/chrome_frame/test/chrome_frame_ui_test_utils.cc +++ b/chrome_frame/test/chrome_frame_ui_test_utils.cc @@ -10,16 +10,19 @@ #include <stack> #include "base/message_loop.h" +#include "base/path_service.h" #include "base/scoped_ptr.h" #include "base/string_util.h" #include "base/stringprintf.h" #include "base/utf_string_conversions.h" #include "base/win/scoped_bstr.h" +#include "chrome/common/chrome_paths.h" +#include "chrome_frame/test/win_event_receiver.h" +#include "chrome_frame/utils.h" #include "gfx/point.h" #include "gfx/rect.h" -#include "chrome_frame/test/win_event_receiver.h" +#include "ia2_api_all.h" // Generated NOLINT #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/xulrunner-sdk/win/include/accessibility/AccessibleEventId.h" namespace chrome_frame_test { @@ -105,11 +108,11 @@ bool AccObject::DoDefaultAction() { } bool AccObject::LeftClick() { - return PostMouseButtonMessages(WM_LBUTTONDOWN, WM_LBUTTONUP); + return PostMouseClickAtCenter(WM_LBUTTONDOWN, WM_LBUTTONUP); } bool AccObject::RightClick() { - return PostMouseButtonMessages(WM_RBUTTONDOWN, WM_RBUTTONUP); + return PostMouseClickAtCenter(WM_RBUTTONDOWN, WM_RBUTTONUP); } bool AccObject::Focus() { @@ -382,6 +385,56 @@ bool AccObject::GetWindowClassName(std::wstring* class_name) { return false; } +bool AccObject::GetSelectionRange(int* start_offset, int* end_offset) { + DCHECK(start_offset); + DCHECK(end_offset); + ScopedComPtr<IAccessibleText> accessible_text; + HRESULT hr = DoQueryService(IID_IAccessibleText, + accessible_, + accessible_text.Receive()); + if (FAILED(hr)) { + LOG(ERROR) << "Could not get IAccessibleText interface. Error: " << hr + << "\nIs IAccessible2Proxy.dll registered?"; + return false; + } + + LONG selection_count = 0; + accessible_text->get_nSelections(&selection_count); + LONG start = 0, end = 0; + if (selection_count > 0) { + if (FAILED(accessible_text->get_selection(0, &start, &end))) { + LOG(WARNING) << "Could not get first selection"; + return false; + } + } + *start_offset = start; + *end_offset = end; + return true; +} + +bool AccObject::GetSelectedText(std::wstring* text) { + DCHECK(text); + int start = 0, end = 0; + if (!GetSelectionRange(&start, &end)) + return false; + ScopedComPtr<IAccessibleText> accessible_text; + HRESULT hr = DoQueryService(IID_IAccessibleText, + accessible_, + accessible_text.Receive()); + if (FAILED(hr)) { + LOG(ERROR) << "Could not get IAccessibleText interface. Error: " << hr + << "\nIs IAccessible2Proxy.dll registered?"; + return false; + } + base::win::ScopedBstr text_bstr; + if (FAILED(accessible_text->get_text(start, end, text_bstr.Receive()))) { + LOG(WARNING) << "Could not get text from selection range"; + return false; + } + text->assign(text_bstr, text_bstr.Length()); + return true; +} + bool AccObject::IsSimpleElement() { return V_I4(&child_id_) != CHILDID_SELF; } @@ -456,10 +509,10 @@ AccObject* AccObject::CreateFromVariant(AccObject* object, // The object in question was actually a full object. return CreateFromDispatch(dispatch.get()); } - LOG(WARNING) << "Failed to determine if child id refers to a full " - << "object. Error: " << result << std::endl - << "Parent object: " << WideToUTF8(object->GetDescription()) - << std::endl << "Child ID: " << V_I4(&variant); + VLOG(1) << "Failed to determine if child id refers to a full " + << "object. Error: " << result << std::endl + << "Parent object: " << WideToUTF8(object->GetDescription()) + << std::endl << "Child ID: " << V_I4(&variant); return NULL; } else if (V_VT(&variant) == VT_DISPATCH) { return CreateFromDispatch(V_DISPATCH(&variant)); @@ -468,8 +521,8 @@ AccObject* AccObject::CreateFromVariant(AccObject* object, return NULL; } -bool AccObject::PostMouseButtonMessages(int button_up, int button_down) { - std::wstring class_name; +bool AccObject::PostMouseClickAtCenter(int button_down, int button_up) { + std::wstring class_name; if (!GetWindowClassName(&class_name)) { DLOG(ERROR) << "Could not get class name of window for accessibility " << "object: " << GetDescription(); @@ -484,14 +537,21 @@ bool AccObject::PostMouseButtonMessages(int button_up, int button_down) { if (!GetLocationInClient(&location)) return false; } + + gfx::Point center = location.CenterPoint(); + return PostMouseButtonMessages(button_down, button_up, + center.x(), center.y()); +} + +bool AccObject::PostMouseButtonMessages( + int button_down, int button_up, int x, int y) { HWND container_window; if (!GetWindow(&container_window)) return false; - gfx::Point center = location.CenterPoint(); - LPARAM coordinates = (center.y() << 16) | center.x(); - ::PostMessage(container_window, button_up, 0, coordinates); + LPARAM coordinates = MAKELPARAM(x, y); ::PostMessage(container_window, button_down, 0, coordinates); + ::PostMessage(container_window, button_up, 0, coordinates); return true; } @@ -614,6 +674,7 @@ void AccEventObserver::EventHandler::Handle(DWORD event, LONG child_id) { if (!observer_) return; + switch (event) { case EVENT_SYSTEM_MENUPOPUPSTART: observer_->OnMenuPopup(hwnd); @@ -621,6 +682,13 @@ void AccEventObserver::EventHandler::Handle(DWORD event, case IA2_EVENT_DOCUMENT_LOAD_COMPLETE: observer_->OnAccDocLoad(hwnd); break; + case IA2_EVENT_TEXT_CARET_MOVED: { + scoped_refptr<AccObject> object( + AccObject::CreateFromEvent(hwnd, object_id, child_id)); + if (object) + observer_->OnTextCaretMoved(hwnd, object.get()); + break; + } case EVENT_OBJECT_VALUECHANGE: if (observer_->is_watching_) { scoped_refptr<AccObject> object( @@ -670,4 +738,10 @@ bool IsDesktopUnlocked() { return desk; } +FilePath GetIAccessible2ProxyStubPath() { + FilePath path; + PathService::Get(chrome::DIR_APP, &path); + return path.AppendASCII("IAccessible2Proxy.dll"); +} + } // namespace chrome_frame_test diff --git a/chrome_frame/test/chrome_frame_ui_test_utils.h b/chrome_frame/test/chrome_frame_ui_test_utils.h index 1f49395..b0281b7 100644 --- a/chrome_frame/test/chrome_frame_ui_test_utils.h +++ b/chrome_frame/test/chrome_frame_ui_test_utils.h @@ -10,6 +10,7 @@ #include <string> #include <vector> +#include "base/file_path.h" #include "base/ref_counted.h" #include "base/scoped_comptr_win.h" #include "base/win/scoped_variant.h" @@ -21,12 +22,16 @@ class Rect; namespace chrome_frame_test { -// Wrapper for MSAA objects. In MSAA, there are two types of objects. The first, -// called an object or full object, has its own IAccessible interface. The -// second, called a simple element, does not have its own IAccessible interface -// and cannot have children. Simple elements must be referenced by combination -// of the parent object and the element's id in MSAA. This class handles this -// distinction transparently to the client. +// Wrapper for MSAA/IAccessible2 accessibility objects. IAccessible2 is an +// open standard which serves as a complement to MSAA and specifies additional +// interfaces and methods. Chrome currently supports a subset of this API. +// +// In MSAA, there are two types of objects. The first, called an object or full +// object, has its own IAccessible interface. The second, called a simple +// element, does not have its own IAccessible interface and cannot have +// children. Simple elements must be referenced by combination of the parent +// object and the element's id in MSAA. This class handles this distinction +// transparently to the client. class AccObject : public base::RefCounted<AccObject> { public: typedef std::vector<scoped_refptr<AccObject> > RefCountedAccObjectVector; @@ -126,6 +131,16 @@ class AccObject : public base::RefCounted<AccObject> { // on success. If this object does not have a window, this will return false. bool GetWindowClassName(std::wstring* class_name); + // Gets the range of text that is selected in this object. If no text is + // selected, |start_offset| and |end_offset| will be set to 0. Returns true + // on success. + // Requires IAccessible2 support. + bool GetSelectionRange(int* start_offset, int* end_offset); + + // Gets the text that is selected in this object. Returns true on success. + // Requires IAccessible2 support. + bool GetSelectedText(std::wstring* text); + // Returns whether this object is a simple element. bool IsSimpleElement(); @@ -151,8 +166,12 @@ class AccObject : public base::RefCounted<AccObject> { static AccObject* CreateFromVariant(AccObject* object, const VARIANT& variant); - // Helper method for posting mouse button messages at this object's location. - bool PostMouseButtonMessages(int button_up, int button_down); + // Posts the given mouse button down and up messages at this object's center. + // Returns true on success. + bool PostMouseClickAtCenter(int button_down, int button_up); + + // Helper method for posting mouse button messages. + bool PostMouseButtonMessages(int button_up, int button_down, int x, int y); ScopedComPtr<IAccessible> accessible_; base::win::ScopedVariant child_id_; @@ -223,6 +242,10 @@ class AccEventObserver : public WinEventListener { virtual void OnAccValueChange(HWND hwnd, AccObject* object, const std::wstring& new_value) = 0; + // Called when the text caret moves within the given object. This is + // triggered when the text selection changes also. + virtual void OnTextCaretMoved(HWND hwnd, AccObject* object) = 0; + // Called when a new menu is shown. virtual void OnMenuPopup(HWND hwnd) = 0; @@ -262,6 +285,9 @@ void DumpAccessibilityTreeForWindow(HWND hwnd); // Returns whether the desktop is unlocked. bool IsDesktopUnlocked(); +// Returns the location of the IAccessible2 COM proxy stub DLL. +FilePath GetIAccessible2ProxyStubPath(); + } // namespace chrome_frame_test #endif // CHROME_FRAME_TEST_CHROME_FRAME_UI_TEST_UTILS_H_ diff --git a/chrome_frame/test/mock_ie_event_sink_actions.h b/chrome_frame/test/mock_ie_event_sink_actions.h index b3f8fbd..9e9dc3b 100644 --- a/chrome_frame/test/mock_ie_event_sink_actions.h +++ b/chrome_frame/test/mock_ie_event_sink_actions.h @@ -27,6 +27,10 @@ MATCHER_P(UrlPathEq, url, "equals the path and query portion of the url") { return arg == chrome_frame_test::GetPathAndQueryFromUrl(url); } +MATCHER_P(AccSatisfies, matcher, "satisfies the given AccObjectMatcher") { + return matcher.DoesMatch(arg); +} + // IWebBrowser2 actions ACTION_P2(Navigate, mock, navigate_url) { @@ -323,6 +327,15 @@ ACTION_P2(ExpectDocumentReadystate, mock, ready_state) { mock->ExpectDocumentReadystate(ready_state); } +ACTION_P(VerifySelectedText, expected_text) { + std::wstring actual_text; + bool got_selection = arg1->GetSelectedText(&actual_text); + EXPECT_TRUE(got_selection); + if (got_selection) { + EXPECT_EQ(expected_text, actual_text); + } +} + // Polling actions ACTION_P3(CloseWhenFileSaved, mock, file, timeout_ms) { diff --git a/chrome_frame/test/mock_ie_event_sink_test.h b/chrome_frame/test/mock_ie_event_sink_test.h index 87cd2f2..7dc9450 100644 --- a/chrome_frame/test/mock_ie_event_sink_test.h +++ b/chrome_frame/test/mock_ie_event_sink_test.h @@ -266,6 +266,7 @@ class MockAccEventObserver : public AccEventObserver { MOCK_METHOD3(OnAccValueChange, void (HWND, AccObject*, // NOLINT const std::wstring&)); MOCK_METHOD1(OnMenuPopup, void (HWND)); // NOLINT + MOCK_METHOD2(OnTextCaretMoved, void (HWND, AccObject*)); // NOLINT }; // This test fixture provides common methods needed for testing CF diff --git a/chrome_frame/test/run_all_unittests.cc b/chrome_frame/test/run_all_unittests.cc index 1b2357e..92edb2f 100644 --- a/chrome_frame/test/run_all_unittests.cc +++ b/chrome_frame/test/run_all_unittests.cc @@ -9,6 +9,7 @@ #include "base/command_line.h" #include "chrome/common/chrome_paths.h" #include "chrome_frame/test/chrome_frame_test_utils.h" +#include "chrome_frame/test/chrome_frame_ui_test_utils.h" #include "chrome_frame/test_utils.h" #include "chrome_frame/utils.h" @@ -57,6 +58,11 @@ int main(int argc, char **argv) { // TODO(robertshield): Make these tests restore the original registration // once done. ScopedChromeFrameRegistrar registrar; + + // Register IAccessible2 proxy stub DLL, needed for some tests. + ScopedChromeFrameRegistrar ia2_registrar( + chrome_frame_test::GetIAccessible2ProxyStubPath().value()); + ret = test_suite.Run(); } |