summaryrefslogtreecommitdiffstats
path: root/chrome_frame/test
diff options
context:
space:
mode:
authorkkania@chromium.org <kkania@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-08-17 17:04:24 +0000
committerkkania@chromium.org <kkania@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-08-17 17:04:24 +0000
commit39f7f17f35ec7f78a2b5b413d4b0b02fc216da61 (patch)
tree08ce71d716ee7cf4106e035f78cf2a877933ea6f /chrome_frame/test
parent4aad7cc13daa4baa068a8e138ba2b5c3596cbeb4 (diff)
downloadchromium_src-39f7f17f35ec7f78a2b5b413d4b0b02fc216da61.zip
chromium_src-39f7f17f35ec7f78a2b5b413d4b0b02fc216da61.tar.gz
chromium_src-39f7f17f35ec7f78a2b5b413d4b0b02fc216da61.tar.bz2
[chrome_frame] Add utils for using MSAA for working with menus and for selecting/focusing elements. Change context menu tests to use this approach.
BUG=none TEST=none Review URL: http://codereview.chromium.org/3115002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@56373 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome_frame/test')
-rw-r--r--chrome_frame/test/chrome_frame_test_utils.cc280
-rw-r--r--chrome_frame/test/chrome_frame_test_utils.h57
-rw-r--r--chrome_frame/test/chrome_frame_ui_test_utils.cc425
-rw-r--r--chrome_frame/test/chrome_frame_ui_test_utils.h173
-rw-r--r--chrome_frame/test/ie_event_sink.cc5
-rw-r--r--chrome_frame/test/ie_event_sink.h7
-rw-r--r--chrome_frame/test/mock_ie_event_sink_actions.h96
-rw-r--r--chrome_frame/test/mock_ie_event_sink_test.h13
-rw-r--r--chrome_frame/test/navigation_test.cc17
-rw-r--r--chrome_frame/test/run_all_unittests.cc12
-rw-r--r--chrome_frame/test/ui_test.cc217
-rw-r--r--chrome_frame/test/win_event_receiver.cc32
-rw-r--r--chrome_frame/test/win_event_receiver.h25
13 files changed, 871 insertions, 488 deletions
diff --git a/chrome_frame/test/chrome_frame_test_utils.cc b/chrome_frame/test/chrome_frame_test_utils.cc
index 9c43cb2..b9ea02a 100644
--- a/chrome_frame/test/chrome_frame_test_utils.cc
+++ b/chrome_frame/test/chrome_frame_test_utils.cc
@@ -7,7 +7,6 @@
#include <atlbase.h>
#include <atlwin.h>
#include <iepmapi.h>
-#include <oleacc.h>
#include <oleauto.h>
#include <sddl.h>
@@ -34,7 +33,6 @@
#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 {
@@ -49,7 +47,6 @@ 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) {
@@ -955,283 +952,6 @@ 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 f05a487..3e63009 100644
--- a/chrome_frame/test/chrome_frame_test_utils.h
+++ b/chrome_frame/test/chrome_frame_test_utils.h
@@ -368,63 +368,6 @@ 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/chrome_frame_ui_test_utils.cc b/chrome_frame/test/chrome_frame_ui_test_utils.cc
new file mode 100644
index 0000000..63629f5
--- /dev/null
+++ b/chrome_frame/test/chrome_frame_ui_test_utils.cc
@@ -0,0 +1,425 @@
+// 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.
+
+#include "chrome_frame/test/chrome_frame_ui_test_utils.h"
+
+#include <windows.h>
+
+#include <sstream>
+#include <stack>
+
+#include "base/message_loop.h"
+#include "base/scoped_bstr_win.h"
+#include "base/scoped_ptr.h"
+#include "base/string_util.h"
+#include "gfx/rect.h"
+#include "chrome_frame/test/win_event_receiver.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chrome_frame_test {
+
+// Timeout for waiting on Chrome to create the accessibility tree for the DOM.
+const int kChromeDOMAccessibilityTreeTimeoutMs = 10 * 1000;
+
+// Timeout for waiting on a menu to popup.
+const int kMenuPopupTimeoutMs = 10 * 1000;
+
+// AccObject methods
+AccObject::AccObject(IAccessible* accessible, int child_id)
+ : accessible_(accessible), child_id_(child_id) {
+ DCHECK(accessible);
+ if (child_id != CHILDID_SELF) {
+ ScopedComPtr<IDispatch> dispatch;
+ // This class does not support referring to a full MSAA object using the
+ // parent object and the child id.
+ HRESULT result = accessible_->get_accChild(child_id_, dispatch.Receive());
+ if (result != S_FALSE && result != E_NOINTERFACE) {
+ LOG(ERROR) << "AccObject created which refers to full MSAA object using "
+ "parent object and child id. This should NOT be done.";
+ }
+ DCHECK(result == S_FALSE || result == E_NOINTERFACE);
+ }
+}
+
+// static
+AccObject* AccObject::CreateFromWindow(HWND hwnd) {
+ ScopedComPtr<IAccessible> accessible;
+ AccessibleObjectFromWindow(hwnd, OBJID_CLIENT,
+ IID_IAccessible, reinterpret_cast<void**>(accessible.Receive()));
+ if (accessible)
+ return new AccObject(accessible);
+ return NULL;
+}
+
+// static
+AccObject* AccObject::CreateFromDispatch(IDispatch* dispatch) {
+ if (dispatch) {
+ ScopedComPtr<IAccessible> accessible;
+ accessible.QueryFrom(dispatch);
+ if (accessible)
+ return new AccObject(accessible);
+ }
+ return NULL;
+}
+
+// static
+AccObject* AccObject::CreateFromPoint(int x, int y) {
+ ScopedComPtr<IAccessible> accessible;
+ ScopedVariant child_id;
+ POINT point = {x, y};
+ AccessibleObjectFromPoint(point, accessible.Receive(), child_id.Receive());
+ if (accessible && child_id.type() == VT_I4)
+ return new AccObject(accessible, V_I4(&child_id));
+ return NULL;
+}
+
+bool AccObject::DoDefaultAction() {
+ HRESULT result = accessible_->accDoDefaultAction(child_id_);
+ EXPECT_HRESULT_SUCCEEDED(result)
+ << "Could not do default action for AccObject: " << GetDescription();
+ return SUCCEEDED(result);
+}
+
+bool AccObject::Focus() {
+ EXPECT_HRESULT_SUCCEEDED(
+ accessible_->accSelect(SELFLAG_TAKEFOCUS, child_id_));
+
+ // Double check that the object actually received focus. In some cases
+ // the parent object must have the focus first.
+ bool did_focus = false;
+ ScopedVariant focused;
+ if (SUCCEEDED(accessible_->get_accFocus(focused.Receive()))) {
+ if (focused.type() != VT_EMPTY)
+ did_focus = true;
+ }
+ EXPECT_TRUE(did_focus) << "Could not focus AccObject: " << GetDescription();
+ return did_focus;
+}
+
+bool AccObject::Select() {
+ // SELFLAG_TAKESELECTION needs to be combined with the focus in order to
+ // take effect.
+ int selection_flag = SELFLAG_TAKEFOCUS | SELFLAG_TAKESELECTION;
+ EXPECT_HRESULT_SUCCEEDED(accessible_->accSelect(selection_flag, child_id_));
+
+ // Double check that the object actually received selection.
+ bool did_select = false;
+ ScopedVariant selected;
+ if (SUCCEEDED(accessible_->get_accSelection(selected.Receive()))) {
+ if (selected.type() != VT_EMPTY)
+ did_select = true;
+ }
+ EXPECT_TRUE(did_select) << "Could not select AccObject: " << GetDescription();
+ return did_select;
+}
+
+bool AccObject::GetName(std::wstring* name) {
+ DCHECK(name);
+ ScopedBstr name_bstr;
+ HRESULT result = accessible_->get_accName(child_id_, name_bstr.Receive());
+ if (SUCCEEDED(result))
+ name->assign(name_bstr, name_bstr.Length());
+ return SUCCEEDED(result);
+}
+
+bool AccObject::GetRoleText(std::wstring* role_text) {
+ DCHECK(role_text);
+ ScopedVariant role_variant;
+ if (SUCCEEDED(accessible_->get_accRole(child_id_, role_variant.Receive()))) {
+ if (role_variant.type() == VT_I4) {
+ wchar_t role_text_array[50];
+ UINT characters = ::GetRoleText(V_I4(&role_variant), role_text_array,
+ arraysize(role_text_array));
+ if (characters) {
+ *role_text = role_text_array;
+ return true;
+ } else {
+ DLOG(ERROR) << "GetRoleText failed for role: "
+ << V_I4(&role_variant);
+ }
+ } else if (role_variant.type() == VT_BSTR) {
+ *role_text = V_BSTR(&role_variant);
+ return true;
+ } else {
+ DLOG(ERROR) << "Role was unexpected variant type: "
+ << role_variant.type();
+ }
+ }
+ return false;
+}
+
+bool AccObject::GetValue(std::wstring* value) {
+ DCHECK(value);
+ ScopedBstr value_bstr;
+ HRESULT result = accessible_->get_accValue(child_id_, value_bstr.Receive());
+ if (SUCCEEDED(result))
+ value->assign(value_bstr, value_bstr.Length());
+ return SUCCEEDED(result);
+}
+
+bool AccObject::GetState(int* state) {
+ DCHECK(state);
+ ScopedVariant state_variant;
+ if (SUCCEEDED(accessible_->get_accState(child_id_,
+ state_variant.Receive()))) {
+ if (state_variant.type() == VT_I4) {
+ *state = V_I4(&state_variant);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool AccObject::GetLocation(gfx::Rect* location) {
+ DCHECK(location);
+ long left, top, width, height; // NOLINT
+ HRESULT result = accessible_->accLocation(&left, &top, &width, &height,
+ child_id_);
+ if (SUCCEEDED(result))
+ *location = gfx::Rect(left, top, width, height);
+ return SUCCEEDED(result);
+}
+
+AccObject* AccObject::GetParent() {
+ if (IsSimpleElement())
+ return new AccObject(accessible_);
+ ScopedComPtr<IDispatch> dispatch;
+ if (FAILED(accessible_->get_accParent(dispatch.Receive())))
+ return NULL;
+ return AccObject::CreateFromDispatch(dispatch.get());
+}
+
+bool AccObject::GetChildren(RefCountedAccObjectVector* client_objects) {
+ DCHECK(client_objects);
+ int child_count;
+ if (!GetChildCount(&child_count)) {
+ LOG(ERROR) << "Failed to get child count of AccObject";
+ return false;
+ }
+ if (child_count == 0)
+ return true;
+ scoped_array<VARIANT> unscoped_children(new VARIANT[child_count]);
+ long actual_child_count; // NOLINT
+ if (FAILED(AccessibleChildren(accessible_, 0L, child_count,
+ unscoped_children.get(),
+ &actual_child_count))) {
+ LOG(ERROR) << "Failed to get children of accessible object";
+ return false;
+ }
+ if (actual_child_count == 0)
+ return true;
+ // Convert the retrieved children array into an array of scoped children.
+ scoped_array<ScopedVariant> children(new ScopedVariant[child_count]);
+ for (int i = 0; i < child_count; ++i) {
+ children[i].Reset(unscoped_children[i]);
+ }
+
+ RefCountedAccObjectVector objects;
+ for (int i = 0; i < actual_child_count; i++) {
+ ScopedComPtr<IDispatch> dispatch;
+ if (children[i].type() == VT_I4) {
+ // According to MSDN, a server is allowed to return a full Accessibility
+ // object using the parent object and the child id. If get_accChild is
+ // called with the id, the server must return the actual IAccessible
+ // interface. Do that here to get an actual IAccessible interface if
+ // possible, since this class operates under the assumption that if the
+ // child id is not CHILDID_SELF, the object is a simple element. See the
+ // DCHECK in the constructor.
+ HRESULT result = accessible_->get_accChild(children[i],
+ dispatch.Receive());
+ if (result == S_FALSE || result == E_NOINTERFACE) {
+ // The object in question really is a simple element. Add it.
+ objects.push_back(new AccObject(accessible_, V_I4(&children[i])));
+ continue;
+ } else if (FAILED(result)) {
+ LOG(ERROR) << "Failed to determine if child id refers to a full "
+ << "object. Error: " << result;
+ return false;
+ }
+ // The object in question was actually a full object. It is saved in the
+ // |dispatch| arg and will be added down below.
+ } else if (children[i].type() == VT_DISPATCH) {
+ dispatch.Attach(V_DISPATCH(&children[i].Release()));
+ } else {
+ DLOG(WARNING) << "Unrecognizable child type, omitting from children";
+ continue;
+ }
+
+ scoped_refptr<AccObject> child = CreateFromDispatch(dispatch.get());
+ if (child) {
+ objects.push_back(child);
+ } else {
+ LOG(ERROR) << "Failed to create AccObject from IDispatch";
+ return false;
+ }
+ }
+
+ client_objects->insert(client_objects->end(), objects.begin(), objects.end());
+ return true;
+}
+
+bool AccObject::GetChildCount(int* child_count) {
+ DCHECK(child_count);
+ *child_count = 0;
+ if (!IsSimpleElement()) {
+ long long_child_count; // NOLINT
+ if (FAILED(accessible_->get_accChildCount(&long_child_count)))
+ return false;
+ *child_count = static_cast<int>(long_child_count);
+ }
+ return true;
+}
+
+bool AccObject::IsSimpleElement() {
+ return V_I4(&child_id_) != CHILDID_SELF;
+}
+
+bool AccObject::Equals(AccObject* other) {
+ if (other) {
+ DCHECK(child_id_.type() == VT_I4 && other->child_id_.type() == VT_I4);
+ return accessible_.get() == other->accessible_.get() &&
+ V_I4(&child_id_) == V_I4(&other->child_id_);
+ }
+ return false;
+}
+
+std::wstring AccObject::GetDescription() {
+ std::wstring name = L"-", role_text = L"-", value = L"-";
+ if (GetName(&name))
+ name = L"'" + name + L"'";
+ if (GetRoleText(&role_text))
+ role_text = L"'" + role_text + L"'";
+ if (GetValue(&value))
+ value = L"'" + value + L"'";
+ int state = 0;
+ GetState(&state);
+ return StringPrintf(L"[%ls, %ls, %ls, 0x%x]", name.c_str(), role_text.c_str(),
+ value.c_str(), state);
+}
+
+std::wstring AccObject::GetTree() {
+ std::wostringstream string_stream;
+ string_stream << L"Accessibility object tree:" << std::endl;
+ string_stream << L"[name, role_text, value, state]" << std::endl;
+
+ std::stack<std::pair<scoped_refptr<AccObject>, int> > pairs;
+ pairs.push(std::make_pair(this, 0));
+ while (!pairs.empty()) {
+ scoped_refptr<AccObject> object = pairs.top().first;
+ int depth = pairs.top().second;
+ pairs.pop();
+
+ for (int i = 0; i < depth; ++i)
+ string_stream << L" ";
+ string_stream << object->GetDescription() << std::endl;
+
+ RefCountedAccObjectVector children;
+ if (object->GetChildren(&children)) {
+ for (int i = static_cast<int>(children.size()) - 1; i >= 0; --i)
+ pairs.push(std::make_pair(children[i], depth + 1));
+ }
+ }
+ return string_stream.str();
+}
+
+// AccObjectMatcher methods
+AccObjectMatcher::AccObjectMatcher(const std::wstring& name,
+ const std::wstring& role_text,
+ const std::wstring& value)
+ : name_(name), role_text_(role_text), value_(value) {
+}
+
+bool AccObjectMatcher::FindHelper(AccObject* object,
+ scoped_refptr<AccObject>* match) const {
+ // Determine if |object| is a match.
+ bool does_match = true;
+ std::wstring name, role_text, value;
+ if (name_.length())
+ does_match = object->GetName(&name) && MatchPatternWide(name, name_);
+ if (does_match && role_text_.length()) {
+ does_match = object->GetRoleText(&role_text) &&
+ MatchPatternWide(role_text, role_text_);
+ }
+ if (does_match && value_.length())
+ does_match = object->GetValue(&value) && MatchPatternWide(value, value_);
+
+ if (does_match) {
+ *match = object;
+ } else {
+ // Try to match the children of |object|.
+ AccObject::RefCountedAccObjectVector children;
+ if (!object->GetChildren(&children)) {
+ LOG(ERROR) << "Could not get children of AccObject";
+ return false;
+ }
+ for (size_t i = 0; i < children.size(); ++i) {
+ if (!FindHelper(children[i], match)) {
+ return false;
+ }
+ if (*match)
+ break;
+ }
+ }
+ return true;
+}
+
+bool AccObjectMatcher::Find(AccObject* object,
+ scoped_refptr<AccObject>* match) const {
+ DCHECK(object);
+ DCHECK(match);
+ *match = NULL;
+ return FindHelper(object, match);
+}
+
+bool AccObjectMatcher::FindInWindow(HWND hwnd,
+ scoped_refptr<AccObject>* match) const {
+ scoped_refptr<AccObject> object(AccObject::CreateFromWindow(hwnd));
+ if (!object) {
+ LOG(INFO) << "Failed to get accessible object from window";
+ return false;
+ }
+ return Find(object.get(), match);
+}
+
+std::wstring AccObjectMatcher::GetDescription() const {
+ std::wostringstream ss;
+ ss << L"[";
+ if (name_.length())
+ ss << L"Name: '" << name_ << L"', ";
+ if (role_text_.length())
+ ss << L"Role: '" << role_text_ << L"', ";
+ if (value_.length())
+ ss << L"Value: '" << value_ << L"'";
+ ss << L"]";
+ return ss.str();
+}
+
+// Other methods
+bool FindAccObjectInWindow(HWND hwnd, const AccObjectMatcher& matcher,
+ scoped_refptr<AccObject>* object) {
+ DCHECK(object);
+ EXPECT_TRUE(matcher.FindInWindow(hwnd, object));
+ EXPECT_TRUE(object) << "Element not found for matcher: "
+ << matcher.GetDescription();
+ if (!object)
+ DumpAccessibilityTreeForWindow(hwnd);
+ return *object;
+}
+
+void DumpAccessibilityTreeForWindow(HWND hwnd) {
+ scoped_refptr<AccObject> object(AccObject::CreateFromWindow(hwnd));
+ if (object)
+ std::wcout << object->GetTree();
+ else
+ std::cout << "Could not get IAccessible for window" << std::endl;
+}
+
+bool IsDesktopUnlocked() {
+ HDESK desk = ::OpenInputDesktop(0, FALSE, DESKTOP_SWITCHDESKTOP);
+ if (desk)
+ ::CloseDesktop(desk);
+ return desk;
+}
+
+} // 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
new file mode 100644
index 0000000..4d254d5
--- /dev/null
+++ b/chrome_frame/test/chrome_frame_ui_test_utils.h
@@ -0,0 +1,173 @@
+// 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_CHROME_FRAME_UI_TEST_UTILS_H_
+#define CHROME_FRAME_TEST_CHROME_FRAME_UI_TEST_UTILS_H_
+
+#include <oleacc.h>
+
+#include <string>
+#include <vector>
+
+#include "base/ref_counted.h"
+#include "base/scoped_comptr_win.h"
+#include "base/scoped_variant_win.h"
+
+namespace gfx {
+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.
+class AccObject : public base::RefCounted<AccObject> {
+ public:
+ typedef std::vector<scoped_refptr<AccObject> > RefCountedAccObjectVector;
+
+ // Creates an AccObject with an IAccessible and child id. |accessible| must
+ // not be NULL. |child_id| must always be CHILDID_SELF unless this AccObject
+ // is a simple element.
+ AccObject(IAccessible* accessible, int child_id = CHILDID_SELF);
+
+ // Creates an AccObject corresponding to the given window. May return NULL
+ // if there is not accessible object for the window. The client owns the
+ // created AccObject.
+ static AccObject* CreateFromWindow(HWND hwnd);
+
+ // Creates an AccObject from querying the given IDispatch. May return NULL
+ // if the object does not implement IAccessible. The client owns the created
+ // AccObject.
+ // Note: This does not work in Chrome.
+ static AccObject* CreateFromDispatch(IDispatch* dispatch);
+
+ // Creates an AccObject corresponding to the accessible object at the screen
+ // coordinates given. Returns NULL on failure. The client owns the created
+ // AccObject.
+ static AccObject* CreateFromPoint(int x, int y);
+
+ // Performs the default action on this object. Returns whether the action
+ // performed successfully. Will cause test failure if unsuccessful.
+ bool DoDefaultAction();
+
+ // Focuses this object. Returns whether the object receives focus. Will cause
+ // test failure if the object is not focused.
+ bool Focus();
+
+ // Selects this object. Returns whether the object is now selected. Will cause
+ // test failure if the object is not selected.
+ bool Select();
+
+ // Gets the name of the object and returns true on success.
+ bool GetName(std::wstring* name);
+
+ // Gets the role text of the object and returns true on success.
+ bool GetRoleText(std::wstring* role_text);
+
+ // Gets the value of the object and returns true on success.
+ bool GetValue(std::wstring* value);
+
+ // Gets the state of the object and returns true on success.
+ bool GetState(int* state);
+
+ // Gets the location of the object in screen coordinates and returns true
+ // on success.
+ bool GetLocation(gfx::Rect* location);
+
+ // Gets the parent of the object. May return NULL.
+ AccObject* GetParent();
+
+ // Gets the children of this object and returns true on success. |objects|
+ // will not be modified unless if the operation is successful.
+ bool GetChildren(RefCountedAccObjectVector* objects);
+
+ // Gets the number of children of this object and returns true on success.
+ bool GetChildCount(int* child_count);
+
+ // Returns whether this object is a simple element.
+ bool IsSimpleElement();
+
+ // Returns whether the two AccObjects point to the same accessibility object.
+ // |other| can safely be NULL.
+ bool Equals(AccObject* other);
+
+ // Returns a description of this object.
+ std::wstring GetDescription();
+
+ // Returns a description of this object and it's accessibility tree. This
+ // description will be ended by a newline.
+ std::wstring GetTree();
+
+ private:
+ friend class base::RefCounted<AccObject>;
+ ~AccObject() {}
+
+ ScopedComPtr<IAccessible> accessible_;
+ ScopedVariant child_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(AccObject);
+};
+
+// Finds an accessibility object with properties that match the specified
+// matching patterns. These patterns can include the standard * and ? wildcards.
+class AccObjectMatcher {
+ 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 Text: 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.
+ AccObjectMatcher(const std::wstring& name = L"",
+ const std::wstring& role_text = 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(AccObject* object, scoped_refptr<AccObject>* 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, scoped_refptr<AccObject>* match) const;
+
+ // Return a description of the matcher, for debugging/logging purposes.
+ std::wstring GetDescription() const;
+
+ private:
+ bool FindHelper(AccObject* object, scoped_refptr<AccObject>* match) const;
+
+ std::wstring name_;
+ std::wstring role_text_;
+ std::wstring value_;
+};
+
+// Finds an AccObject from the given window that satisfied |matcher|.
+// Will cause test failure in case of error or if no match is found in the
+// accessibility tree of the specified window. Returns whether the object was
+// found.
+bool FindAccObjectInWindow(HWND hwnd, const AccObjectMatcher& matcher,
+ scoped_refptr<AccObject>* object);
+
+// Writes the accessibility tree for the given window to standard out. Used for
+// debugging/logging.
+void DumpAccessibilityTreeForWindow(HWND hwnd);
+
+// Returns whether the desktop is unlocked.
+bool IsDesktopUnlocked();
+
+} // namespace chrome_frame_test
+
+#endif // CHROME_FRAME_TEST_CHROME_FRAME_UI_TEST_UTILS_H_
diff --git a/chrome_frame/test/ie_event_sink.cc b/chrome_frame/test/ie_event_sink.cc
index 20eea5f..853ee59 100644
--- a/chrome_frame/test/ie_event_sink.cc
+++ b/chrome_frame/test/ie_event_sink.cc
@@ -324,11 +324,6 @@ void IEEventSink::Refresh() {
web_browser2_->Refresh2(refresh_level.AsInput());
}
-void IEEventSink::WaitForDOMAccessibilityTree() {
- if (IsCFRendering())
- WaitForChromeDOMAccessibilityTree(GetRendererWindow());
-}
-
// private methods
void IEEventSink::ConnectToChromeFrame() {
DCHECK(web_browser2_);
diff --git a/chrome_frame/test/ie_event_sink.h b/chrome_frame/test/ie_event_sink.h
index ac1bf93..feddcbf 100644
--- a/chrome_frame/test/ie_event_sink.h
+++ b/chrome_frame/test/ie_event_sink.h
@@ -111,6 +111,9 @@ class IEEventSink
// Get the HWND for the browser's renderer window.
HWND GetRendererWindow();
+ // Returns whether the browser has a renderer window.
+ bool HasRendererWindow();
+
// Launch IE, use the given listener, and navigate to the given url.
HRESULT LaunchIEAndNavigate(const std::wstring& navigate_url,
IEEventListener* listener);
@@ -127,10 +130,6 @@ 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 49d6d63..d0e46a5 100644
--- a/chrome_frame/test/mock_ie_event_sink_actions.h
+++ b/chrome_frame/test/mock_ie_event_sink_actions.h
@@ -5,10 +5,16 @@
#ifndef CHROME_FRAME_TEST_MOCK_IE_EVENT_SINK_ACTIONS_H_
#define CHROME_FRAME_TEST_MOCK_IE_EVENT_SINK_ACTIONS_H_
+#include <windows.h>
+
#include "base/scoped_bstr_win.h"
#include "chrome/common/chrome_switches.h"
#include "chrome_frame/test/chrome_frame_test_utils.h"
+#include "chrome_frame/test/chrome_frame_ui_test_utils.h"
+#include "chrome_frame/test/mock_ie_event_sink_test.h"
#include "chrome_frame/test/simulate_input.h"
+#include "gfx/point.h"
+#include "gfx/rect.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace chrome_frame_test {
@@ -103,8 +109,24 @@ ACTION_P5(SendExtendedKeysEnter, loop, delay, c, repeat, mod) {
chrome_frame_test::DelaySendExtendedKeysEnter(loop, delay, c, repeat, mod);
}
+namespace {
+
+void DoCloseWindowNow(HWND hwnd) {
+ ::PostMessage(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0);
+}
+
+} // namespace
+
ACTION(DoCloseWindow) {
- ::PostMessage(arg0, WM_SYSCOMMAND, SC_CLOSE, 0);
+ DoCloseWindowNow(arg0);
+}
+
+ACTION_P(DelayDoCloseWindow, delay) {
+ DCHECK(MessageLoop::current());
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ NewRunnableFunction(DoCloseWindowNow, arg0),
+ delay);
}
ACTION_P3(DelayGoBack, mock, loop, delay) {
@@ -125,6 +147,11 @@ ACTION_P(VerifyAddressBarUrl, mock) {
mock->event_sink()->ExpectAddressBarUrl(std::wstring(arg1));
}
+ACTION_P3(VerifyPageLoad, mock, in_cf, url) {
+ EXPECT_TRUE(static_cast<bool>(in_cf) == mock->event_sink()->IsCFRendering());
+ mock->event_sink()->ExpectAddressBarUrl(url);
+}
+
ACTION_P4(DelaySendScanCode, loop, delay, c, mod) {
loop->PostDelayedTask(FROM_HERE, NewRunnableFunction(
simulate_input::SendScanCode, c, mod), delay);
@@ -199,9 +226,70 @@ ACTION_P3(SelectItem, loop, delay, index) {
simulate_input::NONE);
}
-ACTION_P2(DoDefaultUIActionInDocument, mock, matcher) {
- mock->event_sink()->WaitForDOMAccessibilityTree();
- DoDefaultUIAction(mock->event_sink()->GetRendererWindow(), matcher);
+ACTION(FocusAccObject) {
+ scoped_refptr<AccObject> object;
+ if (FindAccObjectInWindow(arg0, AccObjectMatcher(), &object))
+ object->Focus();
+}
+
+ACTION_P(DoDefaultAction, matcher) {
+ scoped_refptr<AccObject> object;
+ if (FindAccObjectInWindow(arg0, matcher, &object))
+ object->DoDefaultAction();
+}
+
+ACTION_P(FocusAccObject, matcher) {
+ scoped_refptr<AccObject> object;
+ if (FindAccObjectInWindow(arg0, matcher, &object))
+ object->Focus();
+}
+
+ACTION_P(SelectAccObject, matcher) {
+ scoped_refptr<AccObject> object;
+ if (FindAccObjectInWindow(arg0, matcher, &object))
+ object->Select();
+}
+
+ACTION(OpenContextMenuAsync) {
+ // Special case this implementation because the top-left of the window is
+ // much more likely to be empty than the center.
+ HWND hwnd = arg0;
+ scoped_refptr<AccObject> object;
+ // TODO(kkania): Switch to using WM_CONTEXTMENU with coordinates -1, -1
+ // when Chrome supports this. See render_widget_host_view_win.h.
+ LPARAM coordinates = (1 << 16) | 1;
+ // IE needs both messages in order to work.
+ ::PostMessage(hwnd, WM_RBUTTONDOWN, (WPARAM)0, coordinates);
+ ::PostMessage(hwnd, WM_RBUTTONUP, (WPARAM)0, coordinates);
+}
+
+ACTION_P(OpenContextMenuAsync, matcher) {
+ HWND hwnd = arg0;
+ scoped_refptr<AccObject> object;
+ if (FindAccObjectInWindow(hwnd, matcher, &object)) {
+ // TODO(kkania): Switch to using WM_CONTEXTMENU with coordinates -1, -1
+ // when Chrome supports this. See render_widget_host_view_win.h.
+ gfx::Rect object_rect;
+ bool got_location = object->GetLocation(&object_rect);
+ EXPECT_TRUE(got_location) << "Could not get bounding rect for object: "
+ << object->GetDescription();
+ if (got_location) {
+ // WM_RBUTTON* messages expect a relative coordinate, while GetLocation
+ // returned a screen coordinate. Adjust.
+ RECT rect;
+ BOOL got_window_rect = ::GetWindowRect(hwnd, &rect);
+ EXPECT_TRUE(got_window_rect) << "Could not get window bounds";
+ if (got_window_rect) {
+ gfx::Rect window_rect(rect);
+ gfx::Point relative_origin =
+ object_rect.CenterPoint().Subtract(window_rect.origin());
+ LPARAM coordinates = (relative_origin.y() << 16) | relative_origin.x();
+ // IE needs both messages in order to work.
+ ::PostMessage(hwnd, WM_RBUTTONDOWN, (WPARAM)0, coordinates);
+ ::PostMessage(hwnd, WM_RBUTTONUP, (WPARAM)0, coordinates);
+ }
+ }
+ }
}
} // namespace chrome_frame_test
diff --git a/chrome_frame/test/mock_ie_event_sink_test.h b/chrome_frame/test/mock_ie_event_sink_test.h
index b6e772a..044d6a0 100644
--- a/chrome_frame/test/mock_ie_event_sink_test.h
+++ b/chrome_frame/test/mock_ie_event_sink_test.h
@@ -18,6 +18,7 @@
#include "chrome_frame/test/win_event_receiver.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/xulrunner-sdk/win/include/accessibility/AccessibleEventId.h"
namespace chrome_frame_test {
@@ -122,8 +123,12 @@ class MockIEEventSink : public IEEventListener {
// Override IE's OnDocumentComplete to call our OnLoad, iff it is IE actually
// rendering the page.
virtual void OnDocumentComplete(IDispatch* dispatch, VARIANT* url) {
- if (!event_sink_->IsCFRendering())
+ if (!event_sink_->IsCFRendering()) {
+ ::NotifyWinEvent(IA2_EVENT_DOCUMENT_LOAD_COMPLETE,
+ event_sink_->GetRendererWindow(),
+ OBJID_CLIENT, 0L);
OnLoad(IN_IE, V_BSTR(url));
+ }
}
// Override CF's OnLoad to call our OnLoad.
@@ -219,6 +224,12 @@ class MockWindowObserver : public WindowObserver {
WindowWatchdog window_watcher_;
};
+class MockAccessibilityEventObserver : public AccessibilityEventObserver {
+ public:
+ MOCK_METHOD1(OnAccDocLoad, void (HWND)); // NOLINT
+ MOCK_METHOD1(OnMenuPopup, void (HWND)); // NOLINT
+};
+
// This test fixture provides common methods needed for testing CF
// integration with IE. gMock is used to verify that IE is reporting correct
// navigational events and MockWebServer is used to verify that the correct
diff --git a/chrome_frame/test/navigation_test.cc b/chrome_frame/test/navigation_test.cc
index ebf60b0..76cad4e 100644
--- a/chrome_frame/test/navigation_test.cc
+++ b/chrome_frame/test/navigation_test.cc
@@ -6,6 +6,7 @@
#include "base/scoped_comptr_win.h"
#include "chrome_frame/test/chrome_frame_test_utils.h"
+#include "chrome_frame/test/chrome_frame_ui_test_utils.h"
#include "chrome_frame/test/mock_ie_event_sink_actions.h"
#include "chrome_frame/test/mock_ie_event_sink_test.h"
@@ -537,21 +538,25 @@ TEST_P(NavigationTransitionTest, DISABLED_JavascriptRedirection) {
// Test following a link.
TEST_P(NavigationTransitionTest, FollowLink) {
- if (page1_.invokes_cf() && page2_.invokes_cf() &&
- GetInstalledIEVersion() > IE_6) {
+ if (page1_.invokes_cf() && page2_.invokes_cf()) {
// For some reason IE 7 and 8 send two BeforeNavigate events for the second
- // page for this case.
+ // page for this case. All versions do not send the OnLoad event for the
+ // second page if both pages are renderered in CF.
LOG(ERROR) << "Test disabled for this configuration.";
return;
}
+ MockAccessibilityEventObserver acc_observer;
+ EXPECT_CALL(acc_observer, OnAccDocLoad(_)).Times(testing::AnyNumber());
+
ie_mock_.ExpectNavigation(page1_.invokes_cf(), 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(DoDefaultUIActionInDocument(&ie_mock_,
- UIObjectMatcher(L"", L"link")));
+ EXPECT_CALL(ie_mock_, OnLoad(page1_.invokes_cf(), StrEq(GetLinkPageUrl())));
+ EXPECT_CALL(acc_observer, OnAccDocLoad(_))
+ .WillOnce(DoDefaultAction(AccObjectMatcher(L"", L"link")))
+ .RetiresOnSaturation();
ie_mock_.ExpectNavigation(page2_.invokes_cf(), GetSimplePageUrl());
server_mock_.ExpectAndServeRequest(page2_, GetSimplePageUrl());
diff --git a/chrome_frame/test/run_all_unittests.cc b/chrome_frame/test/run_all_unittests.cc
index fdf559d8..a22d4f5 100644
--- a/chrome_frame/test/run_all_unittests.cc
+++ b/chrome_frame/test/run_all_unittests.cc
@@ -69,6 +69,18 @@ int main(int argc, char **argv) {
SetConfigBool(kChromeFrameHeadlessMode, true);
+ // Pretend that a screenreader is in use to cause Chrome to generate the
+ // accessibility tree during page load. Otherwise, Chrome will send back
+ // an unpopulated tree for the first request while it fetches the tree
+ // from the renderer.
+ BOOL is_screenreader_on = FALSE;
+ SystemParametersInfo(SPI_SETSCREENREADER, TRUE, NULL, 0);
+ SystemParametersInfo(SPI_GETSCREENREADER, 0, &is_screenreader_on, 0);
+ if (!is_screenreader_on) {
+ LOG(ERROR) << "Could not set screenreader property. Tests depending on "
+ << "MSAA in CF will likely fail...";
+ }
+
base::ProcessHandle crash_service = chrome_frame_test::StartCrashService();
int ret = -1;
// If mini_installer is used to register CF, we use the switch
diff --git a/chrome_frame/test/ui_test.cc b/chrome_frame/test/ui_test.cc
index 8eb4475..4f615b4 100644
--- a/chrome_frame/test/ui_test.cc
+++ b/chrome_frame/test/ui_test.cc
@@ -8,6 +8,7 @@
#include "base/scoped_variant_win.h"
#include "chrome/common/url_constants.h"
#include "chrome_frame/test/chrome_frame_test_utils.h"
+#include "chrome_frame/test/chrome_frame_ui_test_utils.h"
#include "chrome_frame/test/mock_ie_event_sink_actions.h"
#include "chrome_frame/test/mock_ie_event_sink_test.h"
@@ -223,7 +224,7 @@ TEST_P(FullTabUITest, FLAKY_ViewSource) {
ie_mock_.ExpectNewWindow(&view_source_mock);
// For some reason this happens occasionally at least on XP IE7.
- EXPECT_CALL(view_source_mock, OnLoad(false, StrEq(url_in_new_window)))
+ EXPECT_CALL(view_source_mock, OnLoad(IN_IE, StrEq(url_in_new_window)))
.Times(testing::AtMost(1));
EXPECT_CALL(view_source_mock, OnLoad(in_cf, StrEq(view_source_url)))
.WillOnce(testing::DoAll(
@@ -322,26 +323,31 @@ class ContextMenuTest : public MockIEEventSinkTest, public testing::Test {
ContextMenuTest() {}
virtual void SetUp() {
+ // These tests must run on an unlocked desktop in order to use MSAA to
+ // select menu items.
+ ASSERT_TRUE(IsDesktopUnlocked())
+ << "This test must run on an unlocked desktop";
+
// These are UI-related tests, so we do not care about the exact
// navigations that occur.
ie_mock_.ExpectAnyNavigations();
+ EXPECT_CALL(ie_mock_, OnLoad(_, _)).Times(testing::AnyNumber());
+ EXPECT_CALL(acc_observer_, OnAccDocLoad(_)).Times(testing::AnyNumber());
}
+
+ protected:
+ testing::NiceMock<MockAccessibilityEventObserver> acc_observer_;
};
-// Test Reload from context menu.
-// Marking this test FLAKY as it fails at times on the buildbot.
-// http://code.google.com/p/chromium/issues/detail?id=26549
-TEST_F(ContextMenuTest, FLAKY_CFReload) {
+// Test reloading from the context menu.
+TEST_F(ContextMenuTest, CFReload) {
server_mock_.ExpectAndServeAnyRequests(CFInvocation::MetaTag());
InSequence expect_in_sequence_for_scope;
- // Reload using Rt-Click + DOWN + DOWN + DOWN + ENTER
- EXPECT_CALL(ie_mock_, OnLoad(IN_CF, StrEq(GetSimplePageUrl())))
- .WillOnce(testing::DoAll(
- DelaySendMouseClick(&ie_mock_, &loop_, 0, 10, 10,
- simulate_input::RIGHT),
- SendExtendedKeysEnter(&loop_, 500, VK_DOWN, 3,
- simulate_input::NONE)));
+ EXPECT_CALL(acc_observer_, OnAccDocLoad(_))
+ .WillOnce(OpenContextMenuAsync());
+ EXPECT_CALL(acc_observer_, OnMenuPopup(_))
+ .WillOnce(DoDefaultAction(AccObjectMatcher(L"Reload")));
EXPECT_CALL(ie_mock_, OnLoad(IN_CF, StrEq(GetSimplePageUrl())))
.WillOnce(CloseBrowserMock(&ie_mock_));
@@ -349,21 +355,18 @@ TEST_F(ContextMenuTest, FLAKY_CFReload) {
LaunchIEAndNavigate(GetSimplePageUrl());
}
-// Test view source using context menu
-// Marking this test FLAKY as it fails at times on the buildbot.
-// http://code.google.com/p/chromium/issues/detail?id=26549
-TEST_F(ContextMenuTest, FLAKY_CFViewSource) {
+// Test view source from the context menu.
+TEST_F(ContextMenuTest, CFViewSource) {
server_mock_.ExpectAndServeAnyRequests(CFInvocation::MetaTag());
MockIEEventSink view_source_mock;
view_source_mock.ExpectAnyNavigations();
InSequence expect_in_sequence_for_scope;
- // View source using Rt-Click + UP + UP + UP + UP + ENTER
- EXPECT_CALL(ie_mock_, OnLoad(IN_CF, StrEq(GetSimplePageUrl())))
- .WillOnce(testing::DoAll(
- DelaySendMouseClick(&ie_mock_, &loop_, 0, 10, 10,
- simulate_input::RIGHT),
- SendExtendedKeysEnter(&loop_, 500, VK_UP, 4, simulate_input::NONE)));
+ // View the page source.
+ EXPECT_CALL(acc_observer_, OnAccDocLoad(_))
+ .WillOnce(OpenContextMenuAsync());
+ EXPECT_CALL(acc_observer_, OnMenuPopup(_))
+ .WillOnce(DoDefaultAction(AccObjectMatcher(L"View page source")));
// Expect notification for view-source window, handle new window event
// and attach a new ie_mock_ to the received web browser
@@ -375,6 +378,9 @@ TEST_F(ContextMenuTest, FLAKY_CFViewSource) {
url_in_new_window += view_source_url;
ie_mock_.ExpectNewWindow(&view_source_mock);
+ // For some reason this happens occasionally at least on XP IE7 and Win7 IE8.
+ EXPECT_CALL(view_source_mock, OnLoad(IN_IE, StrEq(url_in_new_window)))
+ .Times(testing::AtMost(1));
EXPECT_CALL(view_source_mock, OnLoad(IN_CF, StrEq(view_source_url)))
.WillOnce(testing::DoAll(
VerifyAddressBarUrlWithGcf(&view_source_mock),
@@ -386,43 +392,43 @@ TEST_F(ContextMenuTest, FLAKY_CFViewSource) {
LaunchIEAndNavigate(GetSimplePageUrl());
}
-TEST_F(ContextMenuTest, FLAKY_CFPageInfo) {
+TEST_F(ContextMenuTest, CFPageInfo) {
server_mock_.ExpectAndServeAnyRequests(CFInvocation::MetaTag());
MockWindowObserver win_observer_mock;
InSequence expect_in_sequence_for_scope;
- // View page information using Rt-Click + UP + UP + UP + ENTER
+ // View page information.
const wchar_t* kPageInfoWindowClass = L"Chrome_WidgetWin_0";
- EXPECT_CALL(ie_mock_, OnLoad(IN_CF, StrEq(GetSimplePageUrl())))
+ EXPECT_CALL(acc_observer_, OnAccDocLoad(_))
.WillOnce(testing::DoAll(
WatchWindow(&win_observer_mock, kPageInfoWindowClass),
- DelaySendMouseClick(&ie_mock_, &loop_, 0, 10, 10,
- simulate_input::RIGHT),
- SendExtendedKeysEnter(&loop_, 500, VK_UP, 3, simulate_input::NONE)));
+ OpenContextMenuAsync()));
+ EXPECT_CALL(acc_observer_, OnMenuPopup(_))
+ .WillOnce(DoDefaultAction(AccObjectMatcher(L"View page info")));
// Expect page info dialog to pop up. Dismiss the dialog with 'Esc' key
const char* kPageInfoCaption = "Security Information";
EXPECT_CALL(win_observer_mock, OnWindowDetected(_, StrEq(kPageInfoCaption)))
.WillOnce(testing::DoAll(
- DelaySendChar(&loop_, 100, VK_ESCAPE, simulate_input::NONE),
- DelayCloseBrowserMock(&loop_, 2000, &ie_mock_)));
+ DoCloseWindow(),
+ CloseBrowserMock(&ie_mock_)));
LaunchIEAndNavigate(GetSimplePageUrl());
}
-TEST_F(ContextMenuTest, FLAKY_CFInspector) {
+TEST_F(ContextMenuTest, CFInspector) {
server_mock_.ExpectAndServeAnyRequests(CFInvocation::MetaTag());
MockWindowObserver win_observer_mock;
InSequence expect_in_sequence_for_scope;
- // Open developer tools using Rt-Click + UP + UP + ENTER
+ // Open developer tools.
const wchar_t* kPageInfoWindowClass = L"Chrome_WidgetWin_0";
- EXPECT_CALL(ie_mock_, OnLoad(IN_CF, StrEq(GetSimplePageUrl())))
+ EXPECT_CALL(acc_observer_, OnAccDocLoad(_))
.WillOnce(testing::DoAll(
WatchWindow(&win_observer_mock, kPageInfoWindowClass),
- DelaySendMouseClick(&ie_mock_, &loop_, 0, 10, 10,
- simulate_input::RIGHT),
- SendExtendedKeysEnter(&loop_, 500, VK_UP, 2, simulate_input::NONE)));
+ OpenContextMenuAsync()));
+ EXPECT_CALL(acc_observer_, OnMenuPopup(_))
+ .WillOnce(DoDefaultAction(AccObjectMatcher(L"Inspect element")));
// Devtools begins life with "Untitled" caption and it changes
// later to the 'Developer Tools - <url> form.
@@ -430,10 +436,11 @@ TEST_F(ContextMenuTest, FLAKY_CFInspector) {
EXPECT_CALL(win_observer_mock,
OnWindowDetected(_, testing::StartsWith(kPageInfoCaption)))
.WillOnce(testing::DoAll(
- SetFocusToRenderer(&ie_mock_),
- DelayCloseBrowserMock(&loop_, 2000, &ie_mock_)));
+ DelayDoCloseWindow(5000), // wait to catch possible crash
+ DelayCloseBrowserMock(&loop_, 5500, &ie_mock_)));
- LaunchIEAndNavigate(GetSimplePageUrl());
+ LaunchIENavigateAndLoop(GetSimplePageUrl(),
+ kChromeFrameLongNavigationTimeoutInSeconds * 2);
}
TEST_F(ContextMenuTest, FLAKY_CFSaveAs) {
@@ -441,15 +448,14 @@ TEST_F(ContextMenuTest, FLAKY_CFSaveAs) {
MockWindowObserver win_observer_mock;
InSequence expect_in_sequence_for_scope;
- // Open'Save As' dialog using Rt-Click + DOWN + DOWN + DOWN + DOWN + ENTER
+ // Open 'Save As' dialog.
const wchar_t* kSaveDlgClass = L"#32770";
- EXPECT_CALL(ie_mock_, OnLoad(IN_CF, StrEq(GetSimplePageUrl())))
+ EXPECT_CALL(acc_observer_, OnAccDocLoad(_))
.WillOnce(testing::DoAll(
WatchWindow(&win_observer_mock, kSaveDlgClass),
- DelaySendMouseClick(&ie_mock_, &loop_, 0, 10, 10,
- simulate_input::RIGHT),
- SendExtendedKeysEnter(&loop_, 500, VK_DOWN, 4,
- simulate_input::NONE)));
+ OpenContextMenuAsync()));
+ EXPECT_CALL(acc_observer_, OnMenuPopup(_))
+ .WillOnce(DoDefaultAction(AccObjectMatcher(L"Save as...")));
FilePath temp_file_path;
EXPECT_TRUE(file_util::CreateTemporaryFile(&temp_file_path));
@@ -465,27 +471,30 @@ TEST_F(ContextMenuTest, FLAKY_CFSaveAs) {
DelaySendChar(&loop_, 200, VK_RETURN, simulate_input::NONE),
DelayCloseBrowserMock(&loop_, 4000, &ie_mock_)));
- LaunchIEAndNavigate(GetSimplePageUrl());
+ LaunchIENavigateAndLoop(GetSimplePageUrl(),
+ kChromeFrameLongNavigationTimeoutInSeconds * 2);
ASSERT_NE(INVALID_FILE_ATTRIBUTES, GetFileAttributes(kSaveFileName));
ASSERT_TRUE(DeleteFile(kSaveFileName));
}
// This tests that the about:version page can be opened via the CF context menu.
-TEST_F(ContextMenuTest, FLAKY_CFAboutVersionLoads) {
+TEST_F(ContextMenuTest, CFAboutVersionLoads) {
server_mock_.ExpectAndServeAnyRequests(CFInvocation::MetaTag());
const wchar_t* kAboutVersionUrl = L"gcf:about:version";
const wchar_t* kAboutVersionWithoutProtoUrl = L"about:version";
MockIEEventSink new_window_mock;
new_window_mock.ExpectAnyNavigations();
+ InSequence expect_in_sequence_for_scope;
- ie_mock_.ExpectNavigation(IN_CF, GetSimplePageUrl());
- EXPECT_CALL(ie_mock_, OnLoad(IN_CF, StrEq(GetSimplePageUrl())))
- .WillOnce(testing::DoAll(
- DelaySendMouseClick(&ie_mock_, &loop_, 0, 10, 10,
- simulate_input::RIGHT),
- SendExtendedKeysEnter(&loop_, 500, VK_UP, 1, simulate_input::NONE)));
+ EXPECT_CALL(acc_observer_, OnAccDocLoad(_))
+ .WillOnce(OpenContextMenuAsync());
+ EXPECT_CALL(acc_observer_, OnMenuPopup(_))
+ .WillOnce(DoDefaultAction(AccObjectMatcher(L"About*")));
ie_mock_.ExpectNewWindow(&new_window_mock);
+ // For some reason this happens occasionally at least on Win7 IE8.
+ EXPECT_CALL(new_window_mock, OnLoad(IN_IE, StrEq(kAboutVersionUrl)))
+ .Times(testing::AtMost(1));
EXPECT_CALL(new_window_mock,
OnLoad(IN_CF, StrEq(kAboutVersionWithoutProtoUrl)))
.WillOnce(testing::DoAll(
@@ -499,16 +508,15 @@ TEST_F(ContextMenuTest, FLAKY_CFAboutVersionLoads) {
LaunchIEAndNavigate(GetSimplePageUrl());
}
-TEST_F(ContextMenuTest, FLAKY_IEOpen) {
+TEST_F(ContextMenuTest, IEOpen) {
server_mock_.ExpectAndServeAnyRequests(CFInvocation::None());
- // Focus the renderer window by clicking and then tab once to highlight the
- // link.
- EXPECT_CALL(ie_mock_, OnLoad(IN_IE, StrEq(GetLinkPageUrl())))
- .WillOnce(testing::DoAll(
- DelaySendMouseClick(&ie_mock_, &loop_, 0, 1, 1, simulate_input::LEFT),
- DelaySendScanCode(&loop_, 1000, VK_TAB, simulate_input::NONE),
- OpenContextMenu(&loop_, 2000),
- SelectItem(&loop_, 3000, 0)));
+ InSequence expect_in_sequence_for_scope;
+
+ // Open the link throught the context menu.
+ EXPECT_CALL(acc_observer_, OnAccDocLoad(_))
+ .WillOnce(OpenContextMenuAsync(AccObjectMatcher(L"", L"link")));
+ EXPECT_CALL(acc_observer_, OnMenuPopup(_))
+ .WillOnce(DoDefaultAction(AccObjectMatcher(L"Open")));
EXPECT_CALL(ie_mock_, OnLoad(IN_IE, StrEq(GetSimplePageUrl())))
.WillOnce(testing::DoAll(
@@ -518,23 +526,17 @@ TEST_F(ContextMenuTest, FLAKY_IEOpen) {
LaunchIEAndNavigate(GetLinkPageUrl());
}
-TEST_F(ContextMenuTest, FLAKY_IEOpenInNewWindow) {
+TEST_F(ContextMenuTest, IEOpenInNewWindow) {
server_mock_.ExpectAndServeAnyRequests(CFInvocation::None());
MockIEEventSink new_window_mock;
new_window_mock.ExpectAnyNavigations();
+ InSequence expect_in_sequence_for_scope;
- int open_new_window_index = 2;
- if (chrome_frame_test::GetInstalledIEVersion() == IE_6)
- open_new_window_index = 1;
-
- // Focus the renderer window by clicking and then tab once to highlight the
- // link.
- EXPECT_CALL(ie_mock_, OnLoad(IN_IE, StrEq(GetLinkPageUrl())))
- .WillOnce(testing::DoAll(
- DelaySendMouseClick(&ie_mock_, &loop_, 0, 1, 1, simulate_input::LEFT),
- DelaySendScanCode(&loop_, 500, VK_TAB, simulate_input::NONE),
- OpenContextMenu(&loop_, 1000),
- SelectItem(&loop_, 1500, open_new_window_index)));
+ // Open the link in a new window.
+ EXPECT_CALL(acc_observer_, OnAccDocLoad(_))
+ .WillOnce(OpenContextMenuAsync(AccObjectMatcher(L"", L"link")));
+ EXPECT_CALL(acc_observer_, OnMenuPopup(_))
+ .WillOnce(DoDefaultAction(AccObjectMatcher(L"Open in New Window")));
ie_mock_.ExpectNewWindow(&new_window_mock);
EXPECT_CALL(new_window_mock, OnLoad(IN_IE, StrEq(GetSimplePageUrl())))
@@ -552,66 +554,31 @@ TEST_F(ContextMenuTest, FLAKY_IEOpenInNewWindow) {
// Test Back/Forward from context menu.
// Marking this test FLAKY as it fails at times on the buildbot.
// http://code.google.com/p/chromium/issues/detail?id=26549
-TEST_F(ContextMenuTest, FLAKY_IEBackForward) {
+TEST_F(ContextMenuTest, IEBackForward) {
server_mock_.ExpectAndServeAnyRequests(CFInvocation::None());
std::wstring page1 = GetLinkPageUrl();
std::wstring page2 = GetSimplePageUrl();
InSequence expect_in_sequence_for_scope;
- EXPECT_CALL(ie_mock_, OnLoad(IN_IE, StrEq(page1)))
- .WillOnce(Navigate(&ie_mock_, page2));
-
- // Go back using Rt-Click + DOWN + ENTER
- EXPECT_CALL(ie_mock_, OnLoad(IN_IE, StrEq(page2)))
- .WillOnce(testing::DoAll(
- DelaySendMouseClick(&ie_mock_, &loop_, 0, 10, 10,
- simulate_input::RIGHT),
- SendExtendedKeysEnter(&loop_, 500, VK_DOWN, 1,
- simulate_input::NONE)));
-
- // Go forward using Rt-Click + DOWN + DOWN + ENTER
- EXPECT_CALL(ie_mock_, OnLoad(IN_IE, StrEq(page1)))
- .WillOnce(testing::DoAll(
- DelaySendMouseClick(&ie_mock_, &loop_, 0, 10, 10,
- simulate_input::RIGHT),
- SendExtendedKeysEnter(&loop_, 500, VK_DOWN, 2,
- simulate_input::NONE)));
-
- EXPECT_CALL(ie_mock_, OnLoad(IN_IE, StrEq(page2)))
- .WillOnce(CloseBrowserMock(&ie_mock_));
-
- LaunchIEAndNavigate(page1);
-}
-
-// Test Back/Forward from context menu. Loads page 1 in chrome and page 2
-// in IE. Then it tests back and forward using context menu
-// Disabling this test as it won't work as per the current chrome external tab
-// design.
-// http://code.google.com/p/chromium/issues/detail?id=46615
-TEST_F(ContextMenuTest, DISABLED_BackForwardWithSwitch) {
- std::wstring page1 = GetLinkPageUrl();
- std::wstring page2 = GetSimplePageUrl();
- InSequence expect_in_sequence_for_scope;
-
- EXPECT_CALL(ie_mock_, OnLoad(IN_CF, StrEq(page1)))
+ // Navigate to second page.
+ EXPECT_CALL(acc_observer_, OnAccDocLoad(_))
.WillOnce(Navigate(&ie_mock_, page2));
- server_mock_.ExpectAndServeRequest(CFInvocation::None(), page2);
- // Go back using Rt-Click + DOWN + ENTER
- EXPECT_CALL(ie_mock_, OnLoad(IN_IE, StrEq(page2)))
+ // Go back.
+ EXPECT_CALL(acc_observer_, OnAccDocLoad(_))
.WillOnce(testing::DoAll(
- DelaySendMouseClick(&ie_mock_, &loop_, 0, 10, 10,
- simulate_input::RIGHT),
- SendExtendedKeysEnter(&loop_, 500, VK_DOWN, 1,
- simulate_input::NONE)));
+ VerifyPageLoad(&ie_mock_, IN_IE, page2),
+ OpenContextMenuAsync()));
+ EXPECT_CALL(acc_observer_, OnMenuPopup(_))
+ .WillOnce(DoDefaultAction(AccObjectMatcher(L"Back")));
- // Go forward using Rt-Click + DOWN + DOWN + ENTER
- EXPECT_CALL(ie_mock_, OnLoad(IN_CF, StrEq(page1)))
+ // Go forward.
+ EXPECT_CALL(acc_observer_, OnAccDocLoad(_))
.WillOnce(testing::DoAll(
- DelaySendMouseClick(&ie_mock_, &loop_, 0, 10, 10,
- simulate_input::RIGHT),
- SendExtendedKeysEnter(&loop_, 500, VK_DOWN, 2,
- simulate_input::NONE)));
+ VerifyPageLoad(&ie_mock_, IN_IE, page1),
+ OpenContextMenuAsync()));
+ EXPECT_CALL(acc_observer_, OnMenuPopup(_))
+ .WillOnce(DoDefaultAction(AccObjectMatcher(L"Forward")));
EXPECT_CALL(ie_mock_, OnLoad(IN_IE, StrEq(page2)))
.WillOnce(CloseBrowserMock(&ie_mock_));
diff --git a/chrome_frame/test/win_event_receiver.cc b/chrome_frame/test/win_event_receiver.cc
index 18debbf..b967d5c 100644
--- a/chrome_frame/test/win_event_receiver.cc
+++ b/chrome_frame/test/win_event_receiver.cc
@@ -9,6 +9,8 @@
#include "chrome_frame/function_stub.h"
+#include "third_party/xulrunner-sdk/win/include/accessibility/AccessibleEventId.h"
+
// WinEventReceiver methods
WinEventReceiver::WinEventReceiver()
: listener_(NULL),
@@ -49,9 +51,12 @@ bool WinEventReceiver::InitializeHook(DWORD event_min, DWORD event_max) {
DCHECK(hook_stub_ == NULL);
hook_stub_ = FunctionStub::Create(reinterpret_cast<uintptr_t>(this),
WinEventHook);
+ // Don't use WINEVENT_SKIPOWNPROCESS here because we fake generate an event
+ // in the mock IE event sink (IA2_EVENT_DOCUMENT_LOAD_COMPLETE) that we want
+ // to catch.
hook_ = SetWinEventHook(event_min, event_max, NULL,
reinterpret_cast<WINEVENTPROC>(hook_stub_->code()), 0,
- 0, WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);
+ 0, WINEVENT_OUTOFCONTEXT);
DLOG_IF(ERROR, hook_ == NULL) << "Unable to SetWinEvent hook";
return hook_ != NULL;
}
@@ -62,7 +67,7 @@ void WinEventReceiver::WinEventHook(WinEventReceiver* me, HWINEVENTHOOK hook,
LONG child_id, DWORD event_thread_id,
DWORD event_time) {
DCHECK(me->listener_ != NULL);
- me->listener_->OnEventReceived(event, hwnd);
+ me->listener_->OnEventReceived(event, hwnd, object_id, child_id);
}
// WindowWatchdog methods
@@ -84,7 +89,8 @@ void WindowWatchdog::RemoveObserver(WindowObserver* observer) {
win_event_receiver_.StopReceivingEvents();
}
-void WindowWatchdog::OnEventReceived(DWORD event, HWND hwnd) {
+void WindowWatchdog::OnEventReceived(DWORD event, HWND hwnd, LONG object_id,
+ LONG child_id) {
// 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
@@ -111,3 +117,23 @@ void WindowWatchdog::OnEventReceived(DWORD event, HWND hwnd) {
i->observer->OnWindowDetected(hwnd, caption);
}
}
+
+// AccessibilityEventListener methods
+AccessibilityEventObserver::AccessibilityEventObserver() {
+ event_receiver_.SetListenerForEvents(this, EVENT_SYSTEM_MENUPOPUPSTART,
+ IA2_EVENT_DOCUMENT_LOAD_COMPLETE);
+}
+
+void AccessibilityEventObserver::OnEventReceived(DWORD event,
+ HWND hwnd,
+ LONG object_id,
+ LONG child_id) {
+ switch (event) {
+ case EVENT_SYSTEM_MENUPOPUPSTART:
+ OnMenuPopup(hwnd);
+ break;
+ case IA2_EVENT_DOCUMENT_LOAD_COMPLETE:
+ OnAccDocLoad(hwnd);
+ break;
+ }
+}
diff --git a/chrome_frame/test/win_event_receiver.h b/chrome_frame/test/win_event_receiver.h
index 3e67f06..afa5924 100644
--- a/chrome_frame/test/win_event_receiver.h
+++ b/chrome_frame/test/win_event_receiver.h
@@ -18,7 +18,8 @@ class WinEventListener {
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;
+ virtual void OnEventReceived(DWORD event, HWND hwnd, LONG object_id,
+ LONG child_id) = 0;
};
// Receives WinEvents and forwards them to its listener. The event types the
@@ -81,10 +82,28 @@ class WindowWatchdog : public WinEventListener {
typedef std::vector<WindowObserverEntry> ObserverMap;
// Overriden from WinEventListener.
- virtual void OnEventReceived(DWORD event, HWND hwnd);
+ virtual void OnEventReceived(DWORD event, HWND hwnd, LONG object_id,
+ LONG child_id);
ObserverMap observers_;
WinEventReceiver win_event_receiver_;
};
-#endif // CHROME_FRAME_TEST_WIN_EVENT_RECEIVER_H_ \ No newline at end of file
+class AccessibilityEventObserver : public WinEventListener {
+ public:
+ AccessibilityEventObserver();
+
+ // Called when the DOM accessibility tree for the page is ready.
+ virtual void OnAccDocLoad(HWND hwnd) = 0;
+
+ // Called when a new menu is shown.
+ virtual void OnMenuPopup(HWND hwnd) = 0;
+
+ private:
+ virtual void OnEventReceived(DWORD event, HWND hwnd, LONG object_id,
+ LONG child_id);
+
+ WinEventReceiver event_receiver_;
+};
+
+#endif // CHROME_FRAME_TEST_WIN_EVENT_RECEIVER_H_