summaryrefslogtreecommitdiffstats
path: root/chrome_frame
diff options
context:
space:
mode:
authorkkania@chromium.org <kkania@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-12-09 01:07:43 +0000
committerkkania@chromium.org <kkania@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-12-09 01:07:43 +0000
commit8ed2d73d12fe656de95cd707557ccdd87af1e08b (patch)
treef7532ed2f8fce81d1d412af35d2d26d00866eab1 /chrome_frame
parent9ff08dd2596afb91b423ff12c30208e2e2bb5587 (diff)
downloadchromium_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.gyp3
-rw-r--r--chrome_frame/test/chrome_frame_ui_test_utils.cc100
-rw-r--r--chrome_frame/test/chrome_frame_ui_test_utils.h42
-rw-r--r--chrome_frame/test/mock_ie_event_sink_actions.h13
-rw-r--r--chrome_frame/test/mock_ie_event_sink_test.h1
-rw-r--r--chrome_frame/test/run_all_unittests.cc6
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();
}