summaryrefslogtreecommitdiffstats
path: root/chrome_frame
diff options
context:
space:
mode:
authorkkania@chromium.org <kkania@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-08-02 21:26:34 +0000
committerkkania@chromium.org <kkania@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-08-02 21:26:34 +0000
commit586b3b0b16308c37fbf19f072dd82a448f456155 (patch)
tree4898b2914f167deabcddddc3f41e7bcacf1cd054 /chrome_frame
parent4a729ae7ff3f615214d2120a2218a5bef4742c58 (diff)
downloadchromium_src-586b3b0b16308c37fbf19f072dd82a448f456155.zip
chromium_src-586b3b0b16308c37fbf19f072dd82a448f456155.tar.gz
chromium_src-586b3b0b16308c37fbf19f072dd82a448f456155.tar.bz2
[chrome_frame] Add methods for finding and performing default action on Accessibility objects. Use this approach in one test.
BUG=none TEST=none Review URL: http://codereview.chromium.org/2895016 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@54603 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome_frame')
-rw-r--r--chrome_frame/chrome_frame.gyp42
-rw-r--r--chrome_frame/test/chrome_frame_test_utils.cc293
-rw-r--r--chrome_frame/test/chrome_frame_test_utils.h65
-rw-r--r--chrome_frame/test/ie_event_sink.cc27
-rw-r--r--chrome_frame/test/ie_event_sink.h4
-rw-r--r--chrome_frame/test/mock_ie_event_sink_actions.h6
-rw-r--r--chrome_frame/test/mock_ie_event_sink_test.h1
-rw-r--r--chrome_frame/test/navigation_test.cc17
-rw-r--r--chrome_frame/test/net/fake_external_tab.cc2
-rw-r--r--chrome_frame/test/win_event_receiver.cc (renamed from chrome_frame/test/window_watchdog.cc)85
-rw-r--r--chrome_frame/test/win_event_receiver.h90
-rw-r--r--chrome_frame/test/window_watchdog.h63
12 files changed, 549 insertions, 146 deletions
diff --git a/chrome_frame/chrome_frame.gyp b/chrome_frame/chrome_frame.gyp
index b517f51..7d36d33 100644
--- a/chrome_frame/chrome_frame.gyp
+++ b/chrome_frame/chrome_frame.gyp
@@ -152,8 +152,8 @@
'test/urlmon_moniker_tests.h',
'test/urlmon_moniker_unittest.cc',
'test/util_unittests.cc',
- 'test/window_watchdog.h',
- 'test/window_watchdog.cc',
+ 'test/win_event_receiver.h',
+ 'test/win_event_receiver.cc',
'unittest_precompile.h',
'unittest_precompile.cc',
'urlmon_upload_data_stream.cc',
@@ -172,7 +172,7 @@
['OS=="win"', {
'link_settings': {
'libraries': [
- '-lshdocvw.lib',
+ '-lshdocvw.lib', '-loleacc.lib',
],
},
'msvs_settings': {
@@ -246,8 +246,8 @@
'test/urlmon_moniker_tests.h',
'test/urlmon_moniker_integration_test.cc',
'test/url_request_test.cc',
- 'test/window_watchdog.cc',
- 'test/window_watchdog.h',
+ 'test/win_event_receiver.cc',
+ 'test/win_event_receiver.h',
'chrome_tab.h',
'chrome_tab.idl',
'test_utils.cc',
@@ -264,6 +264,11 @@
],
'conditions': [
['OS=="win"', {
+ 'link_settings': {
+ 'libraries': [
+ '-loleacc.lib',
+ ],
+ },
'msvs_settings': {
'VCLinkerTool': {
'DelayLoadDLLs': ['xpcom.dll', 'nspr4.dll'],
@@ -317,8 +322,8 @@
'test/simulate_input.h',
'test_utils.cc',
'test_utils.h',
- 'test/window_watchdog.cc',
- 'test/window_watchdog.h',
+ 'test/win_event_receiver.cc',
+ 'test/win_event_receiver.h',
],
'include_dirs': [
'<@(xul_include_directories)',
@@ -328,6 +333,11 @@
],
'conditions': [
['OS=="win"', {
+ 'link_settings': {
+ 'libraries': [
+ '-loleacc.lib',
+ ],
+ },
'dependencies': [
'../breakpad/breakpad.gyp:breakpad_handler',
'../chrome/chrome.gyp:automation',
@@ -370,8 +380,8 @@
'test/simulate_input.h',
'test/test_server.cc',
'test/test_server.h',
- 'test/window_watchdog.cc',
- 'test/window_watchdog.h',
+ 'test/win_event_receiver.cc',
+ 'test/win_event_receiver.h',
'test/net/fake_external_tab.cc',
'test/net/fake_external_tab.h',
'test/net/process_singleton_subclass.cc',
@@ -389,6 +399,11 @@
],
'conditions': [
['OS=="win"', {
+ 'link_settings': {
+ 'libraries': [
+ '-loleacc.lib',
+ ],
+ },
'msvs_settings': {
'VCLinkerTool': {
'DelayLoadDLLs': ['prntvpt.dll'],
@@ -438,8 +453,8 @@
'test_utils.h',
'test/simulate_input.cc',
'test/simulate_input.h',
- 'test/window_watchdog.cc',
- 'test/window_watchdog.h',
+ 'test/win_event_receiver.cc',
+ 'test/win_event_receiver.h',
'chrome_tab.h',
'chrome_tab.idl',
'../base/test/test_file_util_win.cc',
@@ -458,6 +473,11 @@
],
'conditions': [
['OS=="win"', {
+ 'link_settings': {
+ 'libraries': [
+ '-loleacc.lib',
+ ],
+ },
'dependencies': [
# TODO(slightlyoff): Get automation targets working on OS X
'../chrome/chrome.gyp:automation',
diff --git a/chrome_frame/test/chrome_frame_test_utils.cc b/chrome_frame/test/chrome_frame_test_utils.cc
index 8b041a6..78fccc1 100644
--- a/chrome_frame/test/chrome_frame_test_utils.cc
+++ b/chrome_frame/test/chrome_frame_test_utils.cc
@@ -7,8 +7,12 @@
#include <atlbase.h>
#include <atlwin.h>
#include <iepmapi.h>
+#include <oleacc.h>
+#include <oleauto.h>
#include <sddl.h>
+#include <sstream>
+
#include "base/command_line.h"
#include "base/file_version_info.h"
#include "base/file_util.h"
@@ -26,9 +30,11 @@
#include "chrome/common/chrome_switches.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_paths_internal.h"
+#include "chrome_frame/test/win_event_receiver.h"
#include "chrome_frame/utils.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/xulrunner-sdk/win/include/accessibility/AccessibleEventId.h"
namespace chrome_frame_test {
@@ -43,6 +49,7 @@ const char kChromeImageName[] = "chrome.exe";
const wchar_t kIEProfileName[] = L"iexplore";
const wchar_t kChromeLauncher[] = L"chrome_launcher.exe";
const int kChromeFrameLongNavigationTimeoutInSeconds = 10;
+const int kChromeDOMAccessibilityTreeTimeoutMs = 10 * 1000;
// Callback function for EnumThreadWindows.
BOOL CALLBACK CloseWindowsThreadCallback(HWND hwnd, LPARAM param) {
@@ -848,15 +855,6 @@ void WebBrowserEventSink::Exec(const GUID* cmd_group_guid, DWORD command_id,
command_id, cmd_exec_opt, in_args, out_args));
}
-void WebBrowserEventSink::WatchChromeWindow(const wchar_t* window_class) {
- DCHECK(window_class);
- window_watcher_.AddObserver(this, WideToUTF8(window_class));
-}
-
-void WebBrowserEventSink::StopWatching() {
- window_watcher_.RemoveObserver(this);
-}
-
std::wstring GetExeVersion(const std::wstring& exe_path) {
scoped_ptr<FileVersionInfo> ie_version_info(
FileVersionInfo::CreateFileVersionInfo(FilePath(exe_path)));
@@ -960,6 +958,283 @@ void DelaySendExtendedKeysEnter(TimedMsgLoop* loop, int delay, char c,
simulate_input::SendCharA, VK_RETURN, simulate_input::NONE), next_delay);
}
+UIObjectMatcher::UIObjectMatcher(const std::wstring& name,
+ const std::wstring& role,
+ const std::wstring& value)
+ : name_(name), role_(role), value_(value) {
+}
+
+bool UIObjectMatcher::Find(IAccessible* object, IAccessible** match) const {
+ DCHECK(object);
+ DCHECK(match);
+ ScopedVariant self_id(CHILDID_SELF);
+
+ // Determine if |object| is a match.
+ bool does_match = true;
+ if (name_.length()) {
+ ScopedBstr name;
+ object->get_accName(self_id, name.Receive());
+ std::wstring name_string;
+ name_string.assign(name, name.Length());
+ does_match = MatchPatternWide(name_string, name_);
+ }
+ if (does_match && role_.length()) {
+ ScopedVariant role;
+ object->get_accRole(self_id, role.Receive());
+ does_match = false;
+ if (role.type() == VT_I4) {
+ wchar_t role_text[50];
+ if (GetRoleText(V_I4(&role), role_text, arraysize(role_text))) {
+ does_match = MatchPatternWide(role_text, role_);
+ }
+ }
+ }
+ if (does_match && value_.length()) {
+ ScopedBstr value;
+ object->get_accValue(self_id, value.Receive());
+ std::wstring value_string;
+ value_string.assign(value, value.Length());
+ does_match = MatchPatternWide(value_string, value_);
+ }
+
+ if (does_match) {
+ *match = object;
+ object->AddRef();
+ } else {
+ // Get the accessible children of |object|.
+ long child_count, actual_child_count; // NOLINT
+ if (FAILED(object->get_accChildCount(&child_count))) {
+ LOG(ERROR) << "Failed to get child count of accessible object";
+ return false;
+ }
+ scoped_array<VARIANT> children(new VARIANT[child_count]);
+ if (FAILED(AccessibleChildren(object, 0L, child_count, children.get(),
+ &actual_child_count))) {
+ LOG(ERROR) << "Failed to get children of accessible object";
+ return false;
+ }
+
+ // Try each descendant.
+ for (int i = 0; i < actual_child_count; i++) {
+ if (children[i].vt == VT_DISPATCH) {
+ ScopedComPtr<IAccessible> accessible_child;
+ accessible_child.QueryFrom(V_DISPATCH(&children[i]));
+ if (accessible_child.get()) {
+ if (FAILED(Find(accessible_child.get(), match)))
+ return false;
+ if (*match)
+ break;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+bool UIObjectMatcher::FindInWindow(HWND hwnd, IAccessible** match) const {
+ ScopedComPtr<IAccessible> accessible;
+ HRESULT result = AccessibleObjectFromWindow(hwnd, OBJID_CLIENT,
+ IID_IAccessible, reinterpret_cast<void**>(accessible.Receive()));
+ if (FAILED(result) || !accessible.get()) {
+ LOG(INFO) << "Failed to get accessible object from window";
+ return false;
+ }
+ return Find(accessible.get(), match);
+}
+
+std::wstring UIObjectMatcher::GetDescription() const {
+ std::wostringstream ss;
+ ss << L"(";
+ if (name_.length())
+ ss << L"Name: \"" << name_ << L"\" ";
+ if (role_.length())
+ ss << L"Role: \"" << role_ << L"\" ";
+ if (value_.length())
+ ss << L"Value: \"" << value_ << L"\"";
+ ss << L")";
+ return ss.str();
+}
+
+void DoDefaultUIAction(HWND hwnd, const UIObjectMatcher& matcher) {
+ ScopedComPtr<IAccessible> object;
+ EXPECT_TRUE(matcher.FindInWindow(hwnd, object.Receive()));
+ EXPECT_TRUE(object.get());
+ if (object.get()) {
+ ScopedVariant self_id(CHILDID_SELF);
+ EXPECT_HRESULT_SUCCEEDED(object->accDoDefaultAction(self_id));
+ } else {
+ EXPECT_TRUE(object.get()) << "Element not found for matcher: "
+ << matcher.GetDescription();
+ DumpAccessibilityTreeForWindow(hwnd);
+ }
+}
+
+// Used to ensure a message loop is quit at most one time.
+class OneTimeLoopQuitter : public base::RefCounted<OneTimeLoopQuitter> {
+ public:
+ OneTimeLoopQuitter() : did_quit_(false), should_quit_(true) {}
+
+ void Quit() {
+ if (should_quit_ && !did_quit_)
+ MessageLoop::current()->Quit();
+ did_quit_ = true;
+ }
+
+ void Cancel() {
+ should_quit_ = false;
+ }
+
+ bool did_quit() const { return did_quit_; }
+
+ private:
+ bool did_quit_;
+ bool should_quit_;
+
+ DISALLOW_COPY_AND_ASSIGN(OneTimeLoopQuitter);
+};
+
+// Used to wait for an accessibility document load event. Starts listening
+// on creation.
+class AccessibilityDocumentLoadObserver : public WinEventListener {
+ public:
+ explicit AccessibilityDocumentLoadObserver(HWND hwnd)
+ : did_receive_load_(false),
+ hwnd_to_watch_(hwnd) {
+ event_receiver_.SetListenerForEvent(this, IA2_EVENT_DOCUMENT_LOAD_COMPLETE);
+ }
+
+ // Waits for the document load event to be received or the timeout to occur.
+ // This assumes there is a MessageLoop for the current thread.
+ bool WaitForDocumentLoad(int timeoutInMs) {
+ DCHECK(MessageLoop::current() != NULL);
+ if (!did_receive_load_) {
+ quitter = new OneTimeLoopQuitter();
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ NewRunnableMethod(quitter.get(), &OneTimeLoopQuitter::Quit),
+ timeoutInMs);
+ DLOG(INFO) << "Waiting for document load event";
+ MessageLoop::current()->Run();
+
+ DLOG_IF(WARNING, !quitter->did_quit())
+ << "Message loop was quit externally.";
+ quitter->Quit();
+ quitter.release();
+ }
+ return did_receive_load_;
+ }
+
+ private:
+ virtual void OnEventReceived(DWORD event, HWND hwnd) {
+ if (hwnd == hwnd_to_watch_) {
+ DLOG(INFO) << "Received document load event";
+ did_receive_load_ = true;
+ if (quitter.get())
+ quitter->Quit();
+ }
+ }
+
+ bool did_receive_load_;
+ HWND hwnd_to_watch_;
+ scoped_refptr<OneTimeLoopQuitter> quitter;
+ WinEventReceiver event_receiver_;
+
+ DISALLOW_COPY_AND_ASSIGN(AccessibilityDocumentLoadObserver);
+};
+
+bool WaitForChromeDOMAccessibilityTree(HWND hwnd) {
+ bool tree_is_ready = true;
+ // Create this here so it can watch for events that are processed when
+ // fetching IAccessible.
+ AccessibilityDocumentLoadObserver load_observer(hwnd);
+
+ // Get the first object in the current accessibility tree. The state of this
+ // object will be busy if Chrome is still processing the tree.
+ ScopedComPtr<IAccessible> first_object;
+ UIObjectMatcher first_object_matcher;
+ if (first_object_matcher.FindInWindow(hwnd, first_object.Receive()) &&
+ first_object.get()) {
+ ScopedVariant self_id(CHILDID_SELF);
+ ScopedVariant state;
+ if (SUCCEEDED(first_object->get_accState(self_id, state.Receive()))) {
+ if (state.type() == VT_I4 && V_I4(&state) & STATE_SYSTEM_BUSY) {
+ tree_is_ready = load_observer.WaitForDocumentLoad(
+ kChromeDOMAccessibilityTreeTimeoutMs);
+ }
+ }
+ }
+ DLOG_IF(ERROR, !tree_is_ready)
+ << "Gave up waiting for accessibility document load event";
+ return tree_is_ready;
+}
+
+namespace {
+
+void DumpAccessibilityTreeHelper(IAccessible* object, int depth) {
+ ScopedVariant self_id(CHILDID_SELF);
+ ScopedBstr name;
+ object->get_accName(self_id, name.Receive());
+ std::wstring name_string;
+ name_string.assign(name, name.Length());
+ for (int i = 0; i < depth; ++i) {
+ std::wcout << L"---";
+ }
+ std::wcout << L"\"" << name_string << L"\", ";
+
+ ScopedVariant role;
+ object->get_accRole(self_id, role.Receive());
+ if (role.type() == VT_I4) {
+ wchar_t role_text[50];
+ if (GetRoleText(V_I4(&role), role_text, arraysize(role_text)))
+ std::wcout << L"\"" << role_text << "\"";
+ }
+ std::wcout << ", ";
+
+ ScopedBstr value;
+ object->get_accValue(self_id, value.Receive());
+ std::wstring value_string;
+ value_string.assign(value, value.Length());
+ std::wcout << L"\"" << value_string << L"\"" << std::endl;
+
+ // Get the accessible children of |root|.
+ HRESULT result = S_OK;
+ long child_count, actual_child_count; // NOLINT
+ if (FAILED(object->get_accChildCount(&child_count)))
+ return;
+ scoped_array<VARIANT> children(new VARIANT[child_count]);
+ if (FAILED(AccessibleChildren(object, 0L, child_count, children.get(),
+ &actual_child_count)))
+ return;
+
+ for (int i = 0; i < actual_child_count; i++) {
+ if (children[i].vt == VT_DISPATCH) {
+ ScopedComPtr<IAccessible> accessible_child;
+ accessible_child.QueryFrom(V_DISPATCH(&children[i]));
+ if (accessible_child.get()) {
+ DumpAccessibilityTreeHelper(accessible_child.get(), depth + 1);
+ }
+ }
+ }
+}
+
+} // namespace
+
+void DumpAccessibilityTree(IAccessible* object) {
+ std::cout << "Accessibility object tree:" << std::endl;
+ std::cout << "NAME, ROLE, VALUE" << std::endl;
+ DumpAccessibilityTreeHelper(object, 0);
+}
+
+void DumpAccessibilityTreeForWindow(HWND hwnd) {
+ ScopedComPtr<IAccessible> accessible;
+ AccessibleObjectFromWindow(hwnd, OBJID_CLIENT,
+ IID_IAccessible, reinterpret_cast<void**>(accessible.Receive()));
+ if (accessible.get())
+ DumpAccessibilityTree(accessible);
+ else
+ std::cout << "Could not get IAccessible for window" << std::endl;
+}
+
CloseIeAtEndOfScope::~CloseIeAtEndOfScope() {
int closed = CloseAllIEWindows();
DLOG_IF(ERROR, closed != 0) << "Closed " << closed << " windows forcefully";
diff --git a/chrome_frame/test/chrome_frame_test_utils.h b/chrome_frame/test/chrome_frame_test_utils.h
index 4b74f1e..0df5285 100644
--- a/chrome_frame/test/chrome_frame_test_utils.h
+++ b/chrome_frame/test/chrome_frame_test_utils.h
@@ -23,7 +23,6 @@
#include "chrome_frame/test_utils.h"
#include "chrome_frame/test/simulate_input.h"
-#include "chrome_frame/test/window_watchdog.h"
#include "chrome_frame/utils.h"
// Include without path to make GYP build see it.
@@ -136,7 +135,6 @@ class WebBrowserEventSink
: public CComObjectRootEx<CComMultiThreadModel>,
public IDispEventSimpleImpl<0, WebBrowserEventSink,
&DIID_DWebBrowserEvents2>,
- public WindowObserver,
public IUnknown {
public:
typedef IDispEventSimpleImpl<0, WebBrowserEventSink,
@@ -209,10 +207,6 @@ END_SINK_MAP()
void Exec(const GUID* cmd_group_guid, DWORD command_id,
DWORD cmd_exec_opt, VARIANT* in_args, VARIANT* out_args);
- // Watch for new window created.
- void WatchChromeWindow(const wchar_t* window_class);
- void StopWatching();
-
// Overridable methods for the mock.
STDMETHOD_(void, OnNavigateError)(IDispatch* dispatch, VARIANT* url,
VARIANT* frame_name, VARIANT* status_code,
@@ -329,8 +323,6 @@ END_SINK_MAP()
static _ATL_FUNC_INFO kVoidMethodInfo;
static _ATL_FUNC_INFO kDocumentCompleteInfo;
static _ATL_FUNC_INFO kFileDownloadInfo;
-
- WindowWatchdog window_watcher_;
};
// Returns the path of the exe passed in.
@@ -377,6 +369,63 @@ class CloseIeAtEndOfScope {
~CloseIeAtEndOfScope();
};
+// Finds a UI/accessibility object with properties that match the specified
+// matching patterns. These patterns can include the standard * and ? wildcards.
+class UIObjectMatcher {
+ public:
+ // Create a matcher from the given string. |matcher| should include matching
+ // patterns for each property separated by colons. Matching patterns must
+ // be specified from left to right in the following order:
+ // 1) Name
+ // 2) Role: A string representation of a Windows object role, which can be
+ // found by using the win32 GetRoleText function. E.g.,
+ // ROLE_SYSTEM_ALERT should be represented as 'alert', and
+ // ROLE_SYSTEM_MENUPOPUP should be represented as 'popup menu'.
+ // 3) Value
+ // Matching patterns can be blank, essentially equal to *.
+ // Literal *, ?, and : characters can be escaped with a backslash.
+ UIObjectMatcher(const std::wstring& name = L"",
+ const std::wstring& role = L"",
+ const std::wstring& value = L"");
+
+ // Finds the first object which satisfies this matcher and sets as |match|.
+ // This searches the accessibility tree (including |object| itself) of
+ // |object| in a pre-order fasion. If no object is matched, |match| will be
+ // NULL. Returns true if no error occured while trying to find a match. It is
+ // possible to use this method to test for an object's non-existence.
+ bool Find(IAccessible* object, IAccessible** match) const;
+
+ // Same as above except that it searches within the accessibility tree of the
+ // given window, which must support the IAccessible interface.
+ bool FindInWindow(HWND hwnd, IAccessible** match) const;
+
+ // Return a description of the matcher, for debugging/logging purposes.
+ std::wstring GetDescription() const;
+
+ private:
+ std::wstring name_;
+ std::wstring role_;
+ std::wstring value_;
+};
+
+// Perform the default UI action for the object which satisfies |matcher|.
+// Will cause test failure in case of error or no match is found in the
+// accessibility tree of the specified window.
+void DoDefaultUIAction(HWND hwnd, const UIObjectMatcher& matcher);
+
+// Wait for Chrome to report that the DOM accessibility tree is ready. Chrome
+// prepares the tree asynchronously and will return a fake tree if it is not
+// ready. Returns whether the tree is ready. Returns false if the message loop
+// was quit while waiting for the tree.
+bool WaitForChromeDOMAccessibilityTree(HWND hwnd);
+
+// Writes the accessibility tree for |object| to standard out. Used for
+// debugging/logging. |object| must be non-null.
+void DumpAccessibilityTree(IAccessible* object);
+
+// Same as above except that it writes the tree for the given window.
+void DumpAccessibilityTreeForWindow(HWND hwnd);
+
// Starts the Chrome crash service which enables us to gather crash dumps
// during test runs.
base::ProcessHandle StartCrashService();
diff --git a/chrome_frame/test/ie_event_sink.cc b/chrome_frame/test/ie_event_sink.cc
index c04c3c8..fac3dc5 100644
--- a/chrome_frame/test/ie_event_sink.cc
+++ b/chrome_frame/test/ie_event_sink.cc
@@ -213,17 +213,6 @@ void IEEventSink::ExpectRendererWindowHasFocus() {
HWND renderer_window = GetRendererWindow();
EXPECT_TRUE(IsWindow(renderer_window));
- if (IsCFRendering()) {
- for (HWND first_child = renderer_window;
- IsWindow(first_child); first_child = GetWindow(first_child, GW_CHILD)) {
- renderer_window = first_child;
- }
-
- wchar_t class_name[MAX_PATH] = {0};
- GetClassName(renderer_window, class_name, arraysize(class_name));
- EXPECT_EQ(0, _wcsicmp(class_name, L"Chrome_RenderWidgetHostHWND"));
- }
-
DWORD renderer_thread = 0;
DWORD renderer_process = 0;
renderer_thread = GetWindowThreadProcessId(renderer_window,
@@ -270,9 +259,13 @@ HWND IEEventSink::GetRendererWindow() {
EXPECT_TRUE(IsWindow(activex_window));
// chrome tab window is the first (and the only) child of activex
- HWND chrome_tab_window = GetWindow(activex_window, GW_CHILD);
- EXPECT_TRUE(IsWindow(chrome_tab_window));
- renderer_window = GetWindow(chrome_tab_window, GW_CHILD);
+ for (HWND first_child = activex_window;
+ IsWindow(first_child); first_child = GetWindow(first_child, GW_CHILD)) {
+ renderer_window = first_child;
+ }
+ wchar_t class_name[MAX_PATH] = {0};
+ GetClassName(renderer_window, class_name, arraysize(class_name));
+ EXPECT_EQ(0, _wcsicmp(class_name, L"Chrome_RenderWidgetHostHWND"));
}
} else {
DCHECK(web_browser2_);
@@ -336,6 +329,12 @@ void IEEventSink::Refresh() {
web_browser2_->Refresh2(refresh_level.AsInput());
}
+void IEEventSink::WaitForDOMAccessibilityTree() {
+ if (IsCFRendering())
+ WaitForChromeDOMAccessibilityTree(GetRendererWindow());
+}
+
+// private methods
void IEEventSink::ConnectToChromeFrame() {
DCHECK(web_browser2_);
if (chrome_frame_.get())
diff --git a/chrome_frame/test/ie_event_sink.h b/chrome_frame/test/ie_event_sink.h
index 6694575..3268ccd 100644
--- a/chrome_frame/test/ie_event_sink.h
+++ b/chrome_frame/test/ie_event_sink.h
@@ -119,6 +119,10 @@ class IEEventSink
// Expect the address bar to have |url|.
void ExpectAddressBarUrl(const std::wstring& url);
+ // Waits for the Accessibility tree for the DOM to be ready. This is only
+ // necessary for Chrome, so this method is a no-op for IE.
+ void WaitForDOMAccessibilityTree();
+
// These methods are just simple wrappers of the IWebBrowser2 methods.
// They are needed because you cannot post tasks to IWebBrowser2.
void GoBack() {
diff --git a/chrome_frame/test/mock_ie_event_sink_actions.h b/chrome_frame/test/mock_ie_event_sink_actions.h
index ff7d84d..01ef90e 100644
--- a/chrome_frame/test/mock_ie_event_sink_actions.h
+++ b/chrome_frame/test/mock_ie_event_sink_actions.h
@@ -5,6 +5,7 @@
#ifndef CHROME_FRAME_TEST_MOCK_IE_EVENT_SINK_ACTIONS_H_
#define CHROME_FRAME_TEST_MOCK_IE_EVENT_SINK_ACTIONS_H_
+#include "chrome_frame/test/chrome_frame_test_utils.h"
#include "chrome_frame/test/simulate_input.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -167,6 +168,11 @@ ACTION_P3(SelectItem, loop, delay, index) {
simulate_input::NONE);
}
+ACTION_P2(DoDefaultUIActionInDocument, mock, matcher) {
+ mock->event_sink()->WaitForDOMAccessibilityTree();
+ DoDefaultUIAction(mock->event_sink()->GetRendererWindow(), matcher);
+}
+
} // namespace chrome_frame_test
#endif // CHROME_FRAME_TEST_MOCK_IE_EVENT_SINK_ACTIONS_H_ \ No newline at end of file
diff --git a/chrome_frame/test/mock_ie_event_sink_test.h b/chrome_frame/test/mock_ie_event_sink_test.h
index c2ad918..e751073 100644
--- a/chrome_frame/test/mock_ie_event_sink_test.h
+++ b/chrome_frame/test/mock_ie_event_sink_test.h
@@ -14,6 +14,7 @@
#include "chrome_frame/test/ie_event_sink.h"
#include "chrome_frame/test/test_server.h"
#include "chrome_frame/test/test_with_web_server.h"
+#include "chrome_frame/test/win_event_receiver.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome_frame/test/navigation_test.cc b/chrome_frame/test/navigation_test.cc
index 2324f86..52f0aac 100644
--- a/chrome_frame/test/navigation_test.cc
+++ b/chrome_frame/test/navigation_test.cc
@@ -536,8 +536,8 @@ TEST_P(NavigationTransitionTest, DISABLED_JavascriptRedirection) {
LaunchIEAndNavigate(redirect_url);
}
-// Test following a link by TAB + ENTER.
-TEST_P(NavigationTransitionTest, FLAKY_FollowLink) {
+// Test following a link.
+TEST_P(NavigationTransitionTest, FollowLink) {
if (page1_.invokes_cf() && page2_.invokes_cf() &&
GetInstalledIEVersion() > IE_6) {
// For some reason IE 7 and 8 send two BeforeNavigate events for the second
@@ -546,15 +546,16 @@ TEST_P(NavigationTransitionTest, FLAKY_FollowLink) {
return;
}
ie_mock_.ExpectNavigation(page1_.invokes_cf(), GetLinkPageUrl());
- server_mock_.ExpectAndServeRequestAllowCache(page1_, GetLinkPageUrl());
+ // Two requests are made when going from CF to IE, at least on Win7 IE8.
+ EXPECT_CALL(server_mock_, Get(_, UrlPathEq(GetLinkPageUrl()), _))
+ .Times(testing::Between(1, 2))
+ .WillRepeatedly(SendResponse(&server_mock_, page1_));
EXPECT_CALL(ie_mock_, OnLoad(page1_.invokes_cf(), StrEq(GetLinkPageUrl())))
- .WillOnce(testing::DoAll(
- SetFocusToRenderer(&ie_mock_),
- DelaySendChar(&loop_, 500, VK_TAB, simulate_input::NONE),
- DelaySendChar(&loop_, 1000, VK_RETURN, simulate_input::NONE)));
+ .WillOnce(DoDefaultUIActionInDocument(&ie_mock_,
+ UIObjectMatcher(L"", L"link")));
ie_mock_.ExpectNavigation(page2_.invokes_cf(), GetSimplePageUrl());
- server_mock_.ExpectAndServeRequestAllowCache(page2_, GetSimplePageUrl());
+ server_mock_.ExpectAndServeRequest(page2_, GetSimplePageUrl());
EXPECT_CALL(ie_mock_, OnLoad(page2_.invokes_cf(), StrEq(GetSimplePageUrl())))
.WillOnce(testing::DoAll(
VerifyAddressBarUrl(&ie_mock_),
diff --git a/chrome_frame/test/net/fake_external_tab.cc b/chrome_frame/test/net/fake_external_tab.cc
index 5d8e37f..ca0eece 100644
--- a/chrome_frame/test/net/fake_external_tab.cc
+++ b/chrome_frame/test/net/fake_external_tab.cc
@@ -36,7 +36,7 @@
#include "chrome_frame/utils.h"
#include "chrome_frame/test/chrome_frame_test_utils.h"
#include "chrome_frame/test/simulate_input.h"
-#include "chrome_frame/test/window_watchdog.h"
+#include "chrome_frame/test/win_event_receiver.h"
#include "chrome_frame/test/net/test_automation_resource_message_filter.h"
namespace {
diff --git a/chrome_frame/test/window_watchdog.cc b/chrome_frame/test/win_event_receiver.cc
index b388c0a..152fb0e 100644
--- a/chrome_frame/test/window_watchdog.cc
+++ b/chrome_frame/test/win_event_receiver.cc
@@ -2,65 +2,86 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "chrome_frame/test/window_watchdog.h"
+#include "chrome_frame/test/win_event_receiver.h"
#include "base/logging.h"
#include "base/string_util.h"
#include "chrome_frame/function_stub.h"
+// WinEventReceiver methods
+WinEventReceiver::WinEventReceiver() : hook_(NULL), hook_stub_(NULL) {
+}
-WindowWatchdog::WindowWatchdog() : hook_(NULL), hook_stub_(NULL) {
+WinEventReceiver::~WinEventReceiver() {
+ StopReceivingEvents();
}
-WindowWatchdog::~WindowWatchdog() {
- UninitializeHook();
+void WinEventReceiver::SetListenerForEvent(WinEventListener* listener,
+ DWORD event) {
+ SetListenerForEvents(listener, event, event);
}
-void WindowWatchdog::AddObserver(WindowObserver* observer,
- const std::string& window_class) {
- WindowObserverEntry new_entry = { observer, window_class };
- observers_.push_back(new_entry);
+void WinEventReceiver::SetListenerForEvents(WinEventListener* listener,
+ DWORD event_min, DWORD event_max) {
+ DCHECK(listener != NULL);
+ StopReceivingEvents();
- if (!hook_)
- InitializeHook();
+ listener_ = listener;
+
+ InitializeHook(event_min, event_max);
}
-void WindowWatchdog::RemoveObserver(WindowObserver* observer) {
- for (ObserverMap::iterator i = observers_.begin(); i != observers_.end();) {
- i = (observer = i->observer) ? observers_.erase(i) : ++i;
+void WinEventReceiver::StopReceivingEvents() {
+ if (hook_) {
+ ::UnhookWinEvent(hook_);
+ hook_ = NULL;
+ FunctionStub::Destroy(hook_stub_);
+ hook_stub_ = NULL;
}
-
- if (observers_.empty())
- UninitializeHook();
}
-bool WindowWatchdog::InitializeHook() {
+bool WinEventReceiver::InitializeHook(DWORD event_min, DWORD event_max) {
DCHECK(hook_ == NULL);
DCHECK(hook_stub_ == NULL);
hook_stub_ = FunctionStub::Create(reinterpret_cast<uintptr_t>(this),
WinEventHook);
- hook_ = SetWinEventHook(EVENT_OBJECT_SHOW, EVENT_OBJECT_SHOW, NULL,
+ hook_ = SetWinEventHook(event_min, event_max, NULL,
reinterpret_cast<WINEVENTPROC>(hook_stub_->code()), 0,
0, WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);
-
+ DLOG_IF(ERROR, hook_ == NULL) << "Unable to SetWinEvent hook";
return hook_ != NULL;
}
-void WindowWatchdog::UninitializeHook() {
- if (hook_) {
- ::UnhookWinEvent(hook_);
- hook_ = NULL;
- FunctionStub::Destroy(hook_stub_);
- hook_stub_ = NULL;
+// static
+void WinEventReceiver::WinEventHook(WinEventReceiver* me, HWINEVENTHOOK hook,
+ DWORD event, HWND hwnd, LONG object_id,
+ LONG child_id, DWORD event_thread_id,
+ DWORD event_time) {
+ DCHECK(me->listener_ != NULL);
+ me->listener_->OnEventReceived(event, hwnd);
+}
+
+// WindowWatchdog methods
+void WindowWatchdog::AddObserver(WindowObserver* observer,
+ const std::string& window_class) {
+ if (observers_.empty())
+ win_event_receiver_.SetListenerForEvent(this, EVENT_OBJECT_SHOW);
+
+ WindowObserverEntry new_entry = { observer, window_class };
+ observers_.push_back(new_entry);
+}
+
+void WindowWatchdog::RemoveObserver(WindowObserver* observer) {
+ for (ObserverMap::iterator i = observers_.begin(); i != observers_.end();) {
+ i = (observer == i->observer) ? observers_.erase(i) : ++i;
}
+
+ if (observers_.empty())
+ win_event_receiver_.StopReceivingEvents();
}
-// static
-void WindowWatchdog::WinEventHook(WindowWatchdog* me, HWINEVENTHOOK hook,
- DWORD event, HWND hwnd, LONG object_id,
- LONG child_id, DWORD event_thread_id,
- DWORD event_time) {
+void WindowWatchdog::OnEventReceived(DWORD event, HWND hwnd) {
// We need to look for top level windows and a natural check is for
// WS_CHILD. Instead, checking for WS_CAPTION allows us to filter
// out other stray popups
@@ -71,8 +92,8 @@ void WindowWatchdog::WinEventHook(WindowWatchdog* me, HWINEVENTHOOK hook,
::GetClassNameA(hwnd, class_name, arraysize(class_name));
ObserverMap interested_observers;
- for (ObserverMap::iterator i = me->observers_.begin();
- i != me->observers_.end(); i++) {
+ for (ObserverMap::iterator i = observers_.begin();
+ i != observers_.end(); i++) {
if (0 == lstrcmpA(i->window_class.c_str(), class_name)) {
interested_observers.push_back(*i);
}
diff --git a/chrome_frame/test/win_event_receiver.h b/chrome_frame/test/win_event_receiver.h
new file mode 100644
index 0000000..3e67f06
--- /dev/null
+++ b/chrome_frame/test/win_event_receiver.h
@@ -0,0 +1,90 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_FRAME_TEST_WIN_EVENT_RECEIVER_H_
+#define CHROME_FRAME_TEST_WIN_EVENT_RECEIVER_H_
+
+#include <windows.h>
+
+#include <string>
+#include <vector>
+
+struct FunctionStub;
+
+// Listens to WinEvents from the WinEventReceiver.
+class WinEventListener {
+ public:
+ virtual ~WinEventListener() {}
+ // Called when an event has been received. |hwnd| is the window that generated
+ // the event, or null if no window is associated with the event.
+ virtual void OnEventReceived(DWORD event, HWND hwnd) = 0;
+};
+
+// Receives WinEvents and forwards them to its listener. The event types the
+// listener wants to receive can be specified.
+class WinEventReceiver {
+ public:
+ WinEventReceiver();
+ ~WinEventReceiver();
+
+ // Sets the sole listener of this receiver. The listener will receive all
+ // WinEvents of the given event type. Any previous listener will be
+ // replaced. |listener| should not be NULL.
+ void SetListenerForEvent(WinEventListener* listener, DWORD event);
+
+ // Same as above, but sets a range of events to listen for.
+ void SetListenerForEvents(WinEventListener* listener, DWORD event_min,
+ DWORD event_max);
+
+ // Stops receiving events and forwarding them to the listener. It is
+ // permitted to call this even if the receiver has already been stopped.
+ void StopReceivingEvents();
+
+ private:
+ bool InitializeHook(DWORD event_min, DWORD event_max);
+
+ static void CALLBACK WinEventHook(WinEventReceiver* me, HWINEVENTHOOK hook,
+ DWORD event, HWND hwnd, LONG object_id, LONG child_id,
+ DWORD event_thread_id, DWORD event_time);
+
+ WinEventListener* listener_;
+ HWINEVENTHOOK hook_;
+ FunctionStub* hook_stub_;
+};
+
+// Observes window show events. Used with WindowWatchdog.
+class WindowObserver {
+ public:
+ virtual ~WindowObserver() {}
+ // Called when a window has been shown.
+ virtual void OnWindowDetected(HWND hwnd, const std::string& caption) = 0;
+};
+
+// Watch a for window to be shown with the given window class name.
+// If found, call the observer interested in it.
+class WindowWatchdog : public WinEventListener {
+ public:
+ // Register for notifications for |window_class|. An observer can register
+ // for multiple notifications.
+ void AddObserver(WindowObserver* observer, const std::string& window_class);
+
+ // Remove all entries for |observer|.
+ void RemoveObserver(WindowObserver* observer);
+
+ private:
+ struct WindowObserverEntry {
+ WindowObserver* observer;
+ std::string window_class;
+ };
+
+ typedef std::vector<WindowObserverEntry> ObserverMap;
+
+ // Overriden from WinEventListener.
+ virtual void OnEventReceived(DWORD event, HWND hwnd);
+
+ ObserverMap observers_;
+ WinEventReceiver win_event_receiver_;
+};
+
+#endif // CHROME_FRAME_TEST_WIN_EVENT_RECEIVER_H_ \ No newline at end of file
diff --git a/chrome_frame/test/window_watchdog.h b/chrome_frame/test/window_watchdog.h
deleted file mode 100644
index 60accda..0000000
--- a/chrome_frame/test/window_watchdog.h
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_FRAME_TEST_WINDOW_WATCHDOG_H_
-#define CHROME_FRAME_TEST_WINDOW_WATCHDOG_H_
-
-#include <windows.h>
-
-#include <string>
-#include <vector>
-
-struct FunctionStub;
-
-// Interface implemented by WindowWatchdog users. An observer can register
-// for notifications on multiple window classes.
-class WindowObserver { // NOLINT
- public:
- virtual void OnWindowDetected(HWND hwnd, const std::string& caption) = 0;
-
- protected:
- virtual ~WindowObserver() {}
-};
-
-// Watch a for window to be shown with the given window class name.
-// If found, call the observer interested in it.
-class WindowWatchdog {
- public:
- WindowWatchdog();
- ~WindowWatchdog();
-
- // Register for notifications for |window_class|. An observer can register
- // for multiple notifications
- void AddObserver(WindowObserver* observer, const std::string& window_class);
-
- // Remove all entries for |observer|
- void RemoveObserver(WindowObserver* observer);
-
- protected:
- bool InitializeHook();
- void UninitializeHook();
-
- static void CALLBACK WinEventHook(WindowWatchdog* me, HWINEVENTHOOK hook,
- DWORD event, HWND hwnd, LONG object_id, LONG child_id,
- DWORD event_thread_id, DWORD event_time);
-
- void OnDialogFound(HWND hwnd, const std::string& caption);
-
- protected:
- struct WindowObserverEntry {
- WindowObserver* observer;
- std::string window_class;
- };
-
- typedef std::vector<WindowObserverEntry> ObserverMap;
-
- HWINEVENTHOOK hook_;
- ObserverMap observers_;
- FunctionStub* hook_stub_;
-};
-
-
-#endif // CHROME_FRAME_TEST_WINDOW_WATCHDOG_H_