diff options
author | kkania@chromium.org <kkania@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-07 22:08:38 +0000 |
---|---|---|
committer | kkania@chromium.org <kkania@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-07 22:08:38 +0000 |
commit | eb0bff94e83fb3d6829cc9fe01468fdce672e7c3 (patch) | |
tree | fd4c174cc8e8032ee74afd6fede881883dabf982 /chrome | |
parent | 4aa70db622005c8041be5228a628167a17e3b019 (diff) | |
download | chromium_src-eb0bff94e83fb3d6829cc9fe01468fdce672e7c3.zip chromium_src-eb0bff94e83fb3d6829cc9fe01468fdce672e7c3.tar.gz chromium_src-eb0bff94e83fb3d6829cc9fe01468fdce672e7c3.tar.bz2 |
Adds TabContents/RenderView observers for automation/testing messages.
The RenderViewObserver currently sends two messages to the browser related
to tracking client redirects, which is needed for determining whether to
wait for a future navigation.
This is particularly needed to wait correctly in chromedriver for form
submissions and javascript redirects. However, timed redirects still need
to be considered.
BUG=none
TEST=none
Review URL: http://codereview.chromium.org/6676136
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@80848 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
22 files changed, 962 insertions, 40 deletions
diff --git a/chrome/browser/automation/automation_provider_observers.cc b/chrome/browser/automation/automation_provider_observers.cc index 3e8d840..07be91d 100644 --- a/chrome/browser/automation/automation_provider_observers.cc +++ b/chrome/browser/automation/automation_provider_observers.cc @@ -2055,48 +2055,53 @@ AllTabsStoppedLoadingObserver::AllTabsStoppedLoadingObserver( IPC::Message* reply_message) : automation_(automation->AsWeakPtr()), reply_message_(reply_message) { - registrar_.Add(this, NotificationType::LOAD_STOP, - NotificationService::AllSources()); - registrar_.Add(this, NotificationType::TAB_CONTENTS_DISCONNECTED, - NotificationService::AllSources()); - registrar_.Add(this, NotificationType::TAB_CONTENTS_DESTROYED, - NotificationService::AllSources()); - registrar_.Add(this, NotificationType::TAB_CONTENTS_SWAPPED, - NotificationService::AllSources()); - registrar_.Add(this, NotificationType::TAB_CLOSED, - NotificationService::AllSources()); - CheckIfStopped(); + for (BrowserList::const_iterator iter = BrowserList::begin(); + iter != BrowserList::end(); + ++iter) { + Browser* browser = *iter; + for (int i = 0; i < browser->tab_count(); ++i) { + TabContentsWrapper* contents_wrapper = + browser->GetTabContentsWrapperAt(i); + StartObserving(contents_wrapper->automation_tab_helper()); + if (contents_wrapper->automation_tab_helper()->has_pending_loads()) + pending_tabs_.insert(contents_wrapper->tab_contents()); + } + } + CheckIfNoMorePendingLoads(); } -AllTabsStoppedLoadingObserver::~AllTabsStoppedLoadingObserver() {} +AllTabsStoppedLoadingObserver::~AllTabsStoppedLoadingObserver() { +} -void AllTabsStoppedLoadingObserver::Observe( - NotificationType type, - const NotificationSource& source, - const NotificationDetails& details) { - CheckIfStopped(); +void AllTabsStoppedLoadingObserver::OnFirstPendingLoad( + TabContents* tab_contents) { + pending_tabs_.insert(tab_contents); } -void AllTabsStoppedLoadingObserver::CheckIfStopped() { +void AllTabsStoppedLoadingObserver::OnNoMorePendingLoads( + TabContents* tab_contents) { if (!automation_) { delete this; return; } - bool done_loading = true; - BrowserList::const_iterator iter = BrowserList::begin(); - for (; iter != BrowserList::end(); ++iter) { - Browser* browser = *iter; - for (int i = 0; i < browser->tab_count(); ++i) { - TabContents* tab = browser->GetTabContentsAt(i); - if (tab->is_loading()) { - done_loading = false; - break; - } - } - if (!done_loading) - break; + + TabSet::iterator iter = pending_tabs_.find(tab_contents); + if (iter == pending_tabs_.end()) { + LOG(ERROR) << "Received OnNoMorePendingLoads for tab without " + << "OnFirstPendingLoad."; + return; } - if (done_loading) { + pending_tabs_.erase(iter); + CheckIfNoMorePendingLoads(); +} + +void AllTabsStoppedLoadingObserver::CheckIfNoMorePendingLoads() { + if (!automation_) { + delete this; + return; + } + + if (pending_tabs_.empty()) { AutomationJSONReply(automation_, reply_message_.release()).SendSuccess(NULL); delete this; diff --git a/chrome/browser/automation/automation_provider_observers.h b/chrome/browser/automation/automation_provider_observers.h index ad40525..4fa7679 100644 --- a/chrome/browser/automation/automation_provider_observers.h +++ b/chrome/browser/automation/automation_provider_observers.h @@ -16,6 +16,7 @@ #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "chrome/browser/automation/automation_provider_json.h" +#include "chrome/browser/automation/automation_tab_helper.h" #include "chrome/browser/bookmarks/bookmark_model_observer.h" #include "chrome/browser/browsing_data_remover.h" #if defined(OS_CHROMEOS) @@ -1176,20 +1177,28 @@ class InputEventAckNotificationObserver : public NotificationObserver { DISALLOW_COPY_AND_ASSIGN(InputEventAckNotificationObserver); }; -// Allows the automation provider to wait for all tabs to stop loading. -class AllTabsStoppedLoadingObserver : public NotificationObserver { +// Allows the automation provider to wait for all the tabs to finish any +// pending loads. This only waits for tabs that exist at the observer's +// creation. Will send a message on construction if no tabs are loading +// currently. +class AllTabsStoppedLoadingObserver : public TabEventObserver { public: - // Registers for notifications and checks to see if all tabs have stopped. AllTabsStoppedLoadingObserver(AutomationProvider* automation, IPC::Message* reply_message); virtual ~AllTabsStoppedLoadingObserver(); - virtual void Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details); + // TabEventObserver implementation. + virtual void OnFirstPendingLoad(TabContents* tab_contents); + virtual void OnNoMorePendingLoads(TabContents* tab_contents); private: - void CheckIfStopped(); + typedef std::set<TabContents*> TabSet; + + // Checks if there are no pending loads. If none, it will send an automation + // relpy and delete itself. + void CheckIfNoMorePendingLoads(); + + TabSet pending_tabs_; NotificationRegistrar registrar_; base::WeakPtr<AutomationProvider> automation_; scoped_ptr<IPC::Message> reply_message_; diff --git a/chrome/browser/automation/automation_tab_helper.cc b/chrome/browser/automation/automation_tab_helper.cc new file mode 100644 index 0000000..0151ee4 --- /dev/null +++ b/chrome/browser/automation/automation_tab_helper.cc @@ -0,0 +1,147 @@ +// Copyright (c) 2011 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/browser/automation/automation_tab_helper.h" + +#include <algorithm> + +#include "content/browser/tab_contents/navigation_controller.h" +#include "content/browser/tab_contents/tab_contents.h" +#include "chrome/common/automation_messages.h" +#include "ipc/ipc_message.h" +#include "ipc/ipc_message_macros.h" + +TabEventObserver::TabEventObserver() { } + +TabEventObserver::~TabEventObserver() { + for (size_t i = 0; i < event_sources_.size(); ++i) { + if (event_sources_[i]) + event_sources_[i]->RemoveObserver(this); + } +} + +void TabEventObserver::StartObserving(AutomationTabHelper* tab_helper) { + tab_helper->AddObserver(this); + event_sources_.push_back(tab_helper->AsWeakPtr()); +} + +void TabEventObserver::StopObserving(AutomationTabHelper* tab_helper) { + tab_helper->RemoveObserver(this); + EventSourceVector::iterator iter = + std::find(event_sources_.begin(), event_sources_.end(), tab_helper); + if (iter != event_sources_.end()) + event_sources_.erase(iter); +} + +AutomationTabHelper::AutomationTabHelper(TabContents* tab_contents) + : TabContentsObserver(tab_contents), + is_loading_(false) { +} + +AutomationTabHelper::~AutomationTabHelper() { } + +void AutomationTabHelper::AddObserver(TabEventObserver* observer) { + observers_.AddObserver(observer); +} + +void AutomationTabHelper::RemoveObserver(TabEventObserver* observer) { + observers_.RemoveObserver(observer); +} + +bool AutomationTabHelper::has_pending_loads() const { + return is_loading_ || !pending_client_redirects_.empty(); +} + +void AutomationTabHelper::DidStartLoading() { + if (is_loading_) { + // DidStartLoading is often called twice. Once when the renderer sends a + // load start message, and once when the browser calls it directly as a + // result of some user-initiated navigation. + VLOG(1) << "Received DidStartLoading while loading already started."; + return; + } + bool had_pending_loads = has_pending_loads(); + is_loading_ = true; + if (!had_pending_loads) { + FOR_EACH_OBSERVER(TabEventObserver, observers_, + OnFirstPendingLoad(tab_contents())); + } +} + +void AutomationTabHelper::DidStopLoading() { + if (!is_loading_) { + LOG(WARNING) << "Received DidStopLoading while loading already stopped."; + return; + } + is_loading_ = false; + if (!has_pending_loads()) { + FOR_EACH_OBSERVER(TabEventObserver, observers_, + OnNoMorePendingLoads(tab_contents())); + } +} + +void AutomationTabHelper::RenderViewGone() { + OnTabOrRenderViewDestroyed(); +} + +void AutomationTabHelper::OnTabContentsDestroyed() { + OnTabOrRenderViewDestroyed(); +} + +void AutomationTabHelper::OnTabOrRenderViewDestroyed() { + if (has_pending_loads()) { + is_loading_ = false; + pending_client_redirects_.clear(); + FOR_EACH_OBSERVER(TabEventObserver, observers_, + OnNoMorePendingLoads(tab_contents())); + } +} + +bool AutomationTabHelper::OnMessageReceived(const IPC::Message& message) { + bool handled = true; + bool msg_is_good = true; + IPC_BEGIN_MESSAGE_MAP_EX(AutomationTabHelper, message, msg_is_good) + IPC_MESSAGE_HANDLER(AutomationMsg_WillPerformClientRedirect, + OnWillPerformClientRedirect) + IPC_MESSAGE_HANDLER(AutomationMsg_DidCompleteOrCancelClientRedirect, + OnDidCompleteOrCancelClientRedirect) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP_EX() + if (!msg_is_good) { + LOG(ERROR) << "Failed to deserialize an IPC message"; + } + return handled; +} + +void AutomationTabHelper::OnWillPerformClientRedirect( + int64 frame_id, double delay_seconds) { + // Ignore all non-zero delays. + // TODO(kkania): Handle timed redirects. + if (delay_seconds > 0) { + LOG(WARNING) << "Ignoring timed redirect scheduled for " << delay_seconds + << " seconds later. Will not wait for the redirect to occur"; + return; + } + + bool first_pending_load = !has_pending_loads(); + pending_client_redirects_.insert(frame_id); + if (first_pending_load) { + FOR_EACH_OBSERVER(TabEventObserver, observers_, + OnFirstPendingLoad(tab_contents())); + } +} + +void AutomationTabHelper::OnDidCompleteOrCancelClientRedirect(int64 frame_id) { + std::set<int64>::const_iterator iter = + pending_client_redirects_.find(frame_id); + // It is possible that we did not track the redirect becasue it had a non-zero + // delay. See the comment in |OnWillPerformClientRedirect|. + if (iter != pending_client_redirects_.end()) { + pending_client_redirects_.erase(iter); + if (!has_pending_loads()) { + FOR_EACH_OBSERVER(TabEventObserver, observers_, + OnNoMorePendingLoads(tab_contents())); + } + } +} diff --git a/chrome/browser/automation/automation_tab_helper.h b/chrome/browser/automation/automation_tab_helper.h new file mode 100644 index 0000000..0b9bccb --- /dev/null +++ b/chrome/browser/automation/automation_tab_helper.h @@ -0,0 +1,119 @@ +// Copyright (c) 2011 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_BROWSER_AUTOMATION_AUTOMATION_TAB_HELPER_H_ +#define CHROME_BROWSER_AUTOMATION_AUTOMATION_TAB_HELPER_H_ +#pragma once + +#include <set> +#include <vector> + +#include "base/basictypes.h" +#include "base/observer_list.h" +#include "base/memory/weak_ptr.h" +#include "content/browser/tab_contents/tab_contents_observer.h" + +class TabContents; +class AutomationTabHelper; + +namespace IPC { +class Message; +} + +// An observer API implemented by classes which are interested in various +// tab events from AutomationTabHelper(s). +class TabEventObserver { + public: + // |LOAD_START| and |LOAD_STOP| notifications may occur several times for a + // sequence of loads that may appear as one complete navigation to a user. + // For instance, navigating to a non-existent page will cause a load start + // and stop for the non-existent page; following that, Chrome will schedule + // a navigation to an error page which causes another load start and stop. + // + // A pending load is a load that is currently in progress or one that is + // scheduled to occur immediately. The only scheduled loads that are + // tracked are client redirects, such as javascript redirects. + // TODO(kkania): Handle redirects that are scheduled to occur later, and + // change this definition of a pending load. + // TODO(kkania): Track other types of scheduled navigations. + + // Called when the tab that had no pending loads now has a new pending + // load. |tab_contents| will always be valid. + virtual void OnFirstPendingLoad(TabContents* tab_contents) { } + + // Called when the tab that had one or more pending loads now has no + // pending loads. |tab_contents| will always be valid. + // + // This method will always be called if |OnFirstPendingLoad| was called. + virtual void OnNoMorePendingLoads(TabContents* tab_contents) { } + + protected: + TabEventObserver(); + virtual ~TabEventObserver(); + + // On construction, this class does not observe any events. This method + // sets us up to observe events from the given |AutomationTabHelper|. + void StartObserving(AutomationTabHelper* tab_helper); + + // Stop observing events from the given |AutomationTabHelper|. This does not + // need to be called before the helper dies, and it is ok if this object is + // destructed while it is still observing an |AutomationTabHelper|. + void StopObserving(AutomationTabHelper* tab_helper); + + private: + friend class AutomationTabHelperTest; + typedef std::vector<base::WeakPtr<AutomationTabHelper> > EventSourceVector; + + // Vector of all the event sources we are observing. Tracked so that this + // class can remove itself from each source at its destruction. + EventSourceVector event_sources_; + + DISALLOW_COPY_AND_ASSIGN(TabEventObserver); +}; + +// Per-tab automation support class. Receives automation/testing messages +// from the renderer. Broadcasts tab events to |TabEventObserver|s. +class AutomationTabHelper + : public TabContentsObserver, + public base::SupportsWeakPtr<AutomationTabHelper> { + public: + explicit AutomationTabHelper(TabContents* tab_contents); + virtual ~AutomationTabHelper(); + + void AddObserver(TabEventObserver* observer); + void RemoveObserver(TabEventObserver* observer); + + // Returns true if the tab is loading or the tab is scheduled to load + // immediately. Note that scheduled loads may be canceled. + bool has_pending_loads() const; + + private: + friend class AutomationTabHelperTest; + + // TabContentsObserver implementation. + virtual void DidStartLoading(); + virtual void DidStopLoading(); + virtual void RenderViewGone(); + virtual void OnTabContentsDestroyed(); + virtual bool OnMessageReceived(const IPC::Message& message); + + void OnWillPerformClientRedirect(int64 frame_id, double delay_seconds); + void OnDidCompleteOrCancelClientRedirect(int64 frame_id); + void OnTabOrRenderViewDestroyed(); + + // True if the tab is currently loading. If a navigation is scheduled but not + // yet loading, this will be false. + bool is_loading_; + + // Set of all the frames (by frame ID) that are scheduled to perform a client + // redirect. + std::set<int64> pending_client_redirects_; + + // List of all the |TabEventObserver|s, which we broadcast events to. + ObserverList<TabEventObserver> observers_; + + DISALLOW_COPY_AND_ASSIGN(AutomationTabHelper); +}; + +#endif // CHROME_BROWSER_AUTOMATION_AUTOMATION_TAB_HELPER_H_ diff --git a/chrome/browser/automation/automation_tab_helper_browsertest.cc b/chrome/browser/automation/automation_tab_helper_browsertest.cc new file mode 100644 index 0000000..75cc259 --- /dev/null +++ b/chrome/browser/automation/automation_tab_helper_browsertest.cc @@ -0,0 +1,209 @@ +// Copyright (c) 2011 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 <string> + +#include "base/file_path.h" +#include "base/path_service.h" +#include "base/stringprintf.h" +#include "chrome/browser/automation/automation_tab_helper.h" +#include "chrome/browser/automation/mock_tab_event_observer.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/url_constants.h" +#include "chrome/test/in_process_browser_test.h" +#include "chrome/test/ui_test_utils.h" +#include "content/browser/renderer_host/render_view_host.h" +#include "content/browser/tab_contents/tab_contents.h" +#include "content/common/notification_details.h" +#include "content/common/notification_observer.h" +#include "content/common/notification_registrar.h" +#include "content/common/notification_service.h" +#include "content/common/notification_source.h" +#include "content/common/notification_type.h" +#include "net/base/net_util.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::_; + +class MockNotificationObserver : public NotificationObserver { + public: + MockNotificationObserver() { } + virtual ~MockNotificationObserver() { } + + MOCK_METHOD3(Observe, void(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details)); + + void Register(NotificationType type, const NotificationSource& source) { + registrar_.Add(this, type, source); + } + + private: + NotificationRegistrar registrar_; + + DISALLOW_COPY_AND_ASSIGN(MockNotificationObserver); +}; + +class AutomationTabHelperBrowserTest : public InProcessBrowserTest { + public: + AutomationTabHelperBrowserTest() { + EnableDOMAutomation(); + } + virtual ~AutomationTabHelperBrowserTest() { } + + void SetUpInProcessBrowserTestFixture() { + EXPECT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir_)); + test_data_dir_ = test_data_dir_.AppendASCII("automation"); + } + + // Add default expectations for a client redirection initiated by script, + // and quit the message loop when the redirect has completed. This expects + // that the tab receives news of the redirect before the script returns. + void ExpectClientRedirectAndBreak( + MockTabEventObserver* mock_tab_observer, + MockNotificationObserver* mock_notification_observer) { + mock_notification_observer->Register( + NotificationType::DOM_OPERATION_RESPONSE, + NotificationService::AllSources()); + + testing::InSequence expect_in_sequence; + EXPECT_CALL(*mock_tab_observer, OnFirstPendingLoad(_)); + EXPECT_CALL(*mock_notification_observer, Observe( + testing::Eq(NotificationType::DOM_OPERATION_RESPONSE), _, _)); + EXPECT_CALL(*mock_tab_observer, OnNoMorePendingLoads(_)) + .WillOnce(testing::InvokeWithoutArgs( + MessageLoopForUI::current(), &MessageLoop::Quit)); + } + + // Executes javascript to execute a given test case found in the current + // page's script. If |wait_for_response| is true, this method will not + // return until the javascript has been executed. + // Use with [ASSERT|EXPECT]_NO_FATAL_FAILURE. + void RunTestCaseInJavaScript(int test_case_number, bool wait_for_response) { + std::string script = base::StringPrintf("runTestCase(%d);", + test_case_number); + RenderViewHost* host = + browser()->GetSelectedTabContents()->render_view_host(); + if (wait_for_response) { + ASSERT_TRUE(ui_test_utils::ExecuteJavaScript( + host, L"", ASCIIToWide(script))); + } else { + script += "window.domAutomationController.setAutomationId(0);" + "window.domAutomationController.send(0);"; + host->ExecuteJavascriptInWebFrame(ASCIIToUTF16(""), ASCIIToUTF16(script)); + } + } + + // Returns the |AutomationTabHelper| for the first browser's first tab. + AutomationTabHelper* tab_helper() { + return browser()->GetTabContentsWrapperAt(0)->automation_tab_helper(); + } + + protected: + FilePath test_data_dir_; +}; + +IN_PROC_BROWSER_TEST_F(AutomationTabHelperBrowserTest, FormSubmission) { + ui_test_utils::NavigateToURL(browser(), net::FilePathToFileURL( + test_data_dir_.AppendASCII("client_redirects.html"))); + + MockNotificationObserver mock_notification_observer; + MockTabEventObserver mock_observer(tab_helper()); + + ExpectClientRedirectAndBreak(&mock_observer, &mock_notification_observer); + + ASSERT_NO_FATAL_FAILURE(RunTestCaseInJavaScript(1, false)); + ui_test_utils::RunMessageLoop(); +} + +IN_PROC_BROWSER_TEST_F(AutomationTabHelperBrowserTest, + CancelFormSubmission) { + ui_test_utils::NavigateToURL(browser(), net::FilePathToFileURL( + test_data_dir_.AppendASCII("client_redirects.html"))); + + MockNotificationObserver mock_notification_observer; + MockTabEventObserver mock_observer(tab_helper()); + + testing::InSequence expect_in_sequence; + EXPECT_CALL(mock_observer, OnFirstPendingLoad(_)).Times(0); + + ASSERT_NO_FATAL_FAILURE(RunTestCaseInJavaScript(2, true)); +} + +IN_PROC_BROWSER_TEST_F(AutomationTabHelperBrowserTest, + JsRedirectToSite) { + ui_test_utils::NavigateToURL(browser(), net::FilePathToFileURL( + test_data_dir_.AppendASCII("client_redirects.html"))); + + MockNotificationObserver mock_notification_observer; + MockTabEventObserver mock_observer(tab_helper()); + + ExpectClientRedirectAndBreak(&mock_observer, &mock_notification_observer); + + ASSERT_NO_FATAL_FAILURE(RunTestCaseInJavaScript(3, false)); + ui_test_utils::RunMessageLoop(); +} + +IN_PROC_BROWSER_TEST_F(AutomationTabHelperBrowserTest, + JsRedirectToUnknownSite) { + ui_test_utils::NavigateToURL(browser(), net::FilePathToFileURL( + test_data_dir_.AppendASCII("client_redirects.html"))); + + MockNotificationObserver mock_notification_observer; + MockTabEventObserver mock_observer(tab_helper()); + + ExpectClientRedirectAndBreak(&mock_observer, &mock_notification_observer); + + ASSERT_NO_FATAL_FAILURE(RunTestCaseInJavaScript(4, false)); + ui_test_utils::RunMessageLoop(); +} + +IN_PROC_BROWSER_TEST_F(AutomationTabHelperBrowserTest, + MetaTagRedirect) { + MockTabEventObserver mock_observer(tab_helper()); + + testing::InSequence expect_in_sequence; + EXPECT_CALL(mock_observer, OnFirstPendingLoad(_)); + EXPECT_CALL(mock_observer, OnNoMorePendingLoads(_)); + + ui_test_utils::NavigateToURLBlockUntilNavigationsComplete( + browser(), + net::FilePathToFileURL(test_data_dir_.AppendASCII("meta_redirect.html")), + 2); +} + +// Tests that the load stop event occurs after the window onload handler. +IN_PROC_BROWSER_TEST_F(AutomationTabHelperBrowserTest, + LoadStopComesAfterOnLoad) { + MockNotificationObserver mock_notification_observer; + mock_notification_observer.Register(NotificationType::DOM_OPERATION_RESPONSE, + NotificationService::AllSources()); + MockTabEventObserver mock_tab_observer(tab_helper()); + + EXPECT_CALL(mock_tab_observer, OnFirstPendingLoad(_)); + { + testing::InSequence expect_in_sequence; + EXPECT_CALL(mock_notification_observer, Observe( + testing::Eq(NotificationType::DOM_OPERATION_RESPONSE), _, _)); + EXPECT_CALL(mock_tab_observer, OnNoMorePendingLoads(_)); + } + + ui_test_utils::NavigateToURL(browser(), net::FilePathToFileURL( + test_data_dir_.AppendASCII("sends_message_on_load.html"))); +} + +// Tests that a crashed tab reports that it has stopped loading. +IN_PROC_BROWSER_TEST_F(AutomationTabHelperBrowserTest, + CrashedTabStopsLoading) { + MockTabEventObserver mock_tab_observer(tab_helper()); + + testing::InSequence expect_in_sequence; + EXPECT_CALL(mock_tab_observer, OnFirstPendingLoad(_)); + EXPECT_CALL(mock_tab_observer, OnNoMorePendingLoads(_)); + + ui_test_utils::NavigateToURL(browser(), GURL(chrome::kAboutCrashURL)); +} diff --git a/chrome/browser/automation/automation_tab_helper_unittest.cc b/chrome/browser/automation/automation_tab_helper_unittest.cc new file mode 100644 index 0000000..8e3453f --- /dev/null +++ b/chrome/browser/automation/automation_tab_helper_unittest.cc @@ -0,0 +1,207 @@ +// Copyright (c) 2011 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 <map> + +#include "base/basictypes.h" +#include "chrome/browser/automation/automation_tab_helper.h" +#include "chrome/browser/automation/mock_tab_event_observer.h" +#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" +#include "chrome/browser/ui/tab_contents/test_tab_contents_wrapper.h" +#include "content/browser/tab_contents/tab_contents.h" +#include "content/browser/tab_contents/test_tab_contents.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::_; + +class AutomationTabHelperTest : public TabContentsWrapperTestHarness { + public: + virtual void SetUp() { + TabContentsWrapperTestHarness::SetUp(); + mock_observer_.StartObserving(tab_helper()); + } + + protected: + // These are here so that we don't have to add each test as a + // |AutomationTabHelper| friend. + void StartLoading() { + tab_helper()->DidStartLoading(); + } + + void StopLoading() { + tab_helper()->DidStopLoading(); + } + + void TabContentsDestroyed() { + tab_helper()->OnTabContentsDestroyed(); + } + + void WillPerformClientRedirect(int64 frame_id) { + tab_helper()->OnWillPerformClientRedirect(frame_id, 0); + } + + void CompleteOrCancelClientRedirect(int64 frame_id) { + tab_helper()->OnDidCompleteOrCancelClientRedirect(frame_id); + } + + AutomationTabHelper* tab_helper() { + return contents_wrapper()->automation_tab_helper(); + } + + MockTabEventObserver mock_observer_; +}; + +ACTION_P2(CheckHasPendingLoads, tab_helper, has_pending_loads) { + EXPECT_EQ(has_pending_loads, tab_helper->has_pending_loads()); +} + +TEST_F(AutomationTabHelperTest, AddAndRemoveObserver) { + testing::MockFunction<void()> check; + + { + testing::InSequence expect_in_sequence; + EXPECT_CALL(mock_observer_, OnFirstPendingLoad(_)); + EXPECT_CALL(check, Call()); + } + + StartLoading(); + check.Call(); + tab_helper()->RemoveObserver(&mock_observer_); + StartLoading(); +} + +TEST_F(AutomationTabHelperTest, StartStopLoading) { + testing::MockFunction<void()> check; + + testing::InSequence expect_in_sequence; + EXPECT_CALL(mock_observer_, OnFirstPendingLoad(contents())) + .WillOnce(CheckHasPendingLoads(tab_helper(), true)); + EXPECT_CALL(check, Call()); + EXPECT_CALL(mock_observer_, OnNoMorePendingLoads(contents())) + .WillOnce(CheckHasPendingLoads(tab_helper(), false)); + + EXPECT_FALSE(tab_helper()->has_pending_loads()); + StartLoading(); + EXPECT_TRUE(tab_helper()->has_pending_loads()); + check.Call(); + StopLoading(); + EXPECT_FALSE(tab_helper()->has_pending_loads()); +} + +TEST_F(AutomationTabHelperTest, DuplicateLoads) { + testing::InSequence expect_in_sequence; + EXPECT_CALL(mock_observer_, OnFirstPendingLoad(contents())); + EXPECT_CALL(mock_observer_, OnNoMorePendingLoads(contents())); + + StartLoading(); + StartLoading(); + StopLoading(); + StopLoading(); +} + +TEST_F(AutomationTabHelperTest, ClientRedirect) { + testing::MockFunction<void()> check; + + testing::InSequence expect_in_sequence; + EXPECT_CALL(mock_observer_, OnFirstPendingLoad(contents())) + .WillOnce(CheckHasPendingLoads(tab_helper(), true)); + EXPECT_CALL(check, Call()); + EXPECT_CALL(mock_observer_, OnNoMorePendingLoads(contents())) + .WillOnce(CheckHasPendingLoads(tab_helper(), false)); + + WillPerformClientRedirect(1); + EXPECT_TRUE(tab_helper()->has_pending_loads()); + check.Call(); + CompleteOrCancelClientRedirect(1); + EXPECT_FALSE(tab_helper()->has_pending_loads()); +} + +TEST_F(AutomationTabHelperTest, DiscardExtraClientRedirects) { + EXPECT_CALL(mock_observer_, OnFirstPendingLoad(contents())); + EXPECT_CALL(mock_observer_, OnNoMorePendingLoads(contents())); + + WillPerformClientRedirect(1); + WillPerformClientRedirect(1); + CompleteOrCancelClientRedirect(1); + EXPECT_FALSE(tab_helper()->has_pending_loads()); + CompleteOrCancelClientRedirect(1); + CompleteOrCancelClientRedirect(2); +} + +TEST_F(AutomationTabHelperTest, StartStopLoadingWithClientRedirect) { + testing::MockFunction<void()> check; + + testing::InSequence expect_in_sequence; + EXPECT_CALL(mock_observer_, OnFirstPendingLoad(contents())); + EXPECT_CALL(mock_observer_, OnNoMorePendingLoads(contents())); + + StartLoading(); + WillPerformClientRedirect(1); + CompleteOrCancelClientRedirect(1); + StopLoading(); +} + +TEST_F(AutomationTabHelperTest, ClientRedirectBeforeLoad) { + testing::MockFunction<void()> check; + + testing::InSequence expect_in_sequence; + EXPECT_CALL(mock_observer_, OnFirstPendingLoad(contents())); + EXPECT_CALL(check, Call()); + EXPECT_CALL(mock_observer_, OnNoMorePendingLoads(contents())); + + StartLoading(); + WillPerformClientRedirect(1); + CompleteOrCancelClientRedirect(1); + EXPECT_TRUE(tab_helper()->has_pending_loads()); + check.Call(); + StopLoading(); +} + +TEST_F(AutomationTabHelperTest, ClientRedirectAfterLoad) { + testing::MockFunction<void()> check; + + testing::InSequence expect_in_sequence; + EXPECT_CALL(mock_observer_, OnFirstPendingLoad(contents())); + EXPECT_CALL(check, Call()); + EXPECT_CALL(mock_observer_, OnNoMorePendingLoads(contents())); + + StartLoading(); + WillPerformClientRedirect(1); + StopLoading(); + EXPECT_TRUE(tab_helper()->has_pending_loads()); + check.Call(); + CompleteOrCancelClientRedirect(1); + EXPECT_FALSE(tab_helper()->has_pending_loads()); +} + +TEST_F(AutomationTabHelperTest, AllFramesMustFinishRedirects) { + testing::MockFunction<void()> check; + + testing::InSequence expect_in_sequence; + EXPECT_CALL(mock_observer_, OnFirstPendingLoad(contents())); + EXPECT_CALL(check, Call()); + EXPECT_CALL(mock_observer_, OnNoMorePendingLoads(contents())); + + WillPerformClientRedirect(1); + WillPerformClientRedirect(2); + CompleteOrCancelClientRedirect(1); + check.Call(); + EXPECT_TRUE(tab_helper()->has_pending_loads()); + CompleteOrCancelClientRedirect(2); + EXPECT_FALSE(tab_helper()->has_pending_loads()); +} + +TEST_F(AutomationTabHelperTest, DestroyedTabStopsLoading) { + testing::MockFunction<void()> check; + + testing::InSequence expect_in_sequence; + EXPECT_CALL(mock_observer_, OnFirstPendingLoad(contents())); + EXPECT_CALL(mock_observer_, OnNoMorePendingLoads(contents())); + + StartLoading(); + WillPerformClientRedirect(1); + TabContentsDestroyed(); + EXPECT_FALSE(tab_helper()->has_pending_loads()); +} diff --git a/chrome/browser/automation/mock_tab_event_observer.cc b/chrome/browser/automation/mock_tab_event_observer.cc new file mode 100644 index 0000000..762caa9 --- /dev/null +++ b/chrome/browser/automation/mock_tab_event_observer.cc @@ -0,0 +1,21 @@ +// Copyright (c) 2011 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/browser/automation/mock_tab_event_observer.h" + +MockTabEventObserver::MockTabEventObserver() { } + +MockTabEventObserver::MockTabEventObserver(AutomationTabHelper* tab_helper) { + TabEventObserver::StartObserving(tab_helper); +} + +MockTabEventObserver::~MockTabEventObserver() { } + +void MockTabEventObserver::StartObserving(AutomationTabHelper* tab_helper) { + TabEventObserver::StartObserving(tab_helper); +} + +void MockTabEventObserver::StopObserving(AutomationTabHelper* tab_helper) { + TabEventObserver::StopObserving(tab_helper); +} diff --git a/chrome/browser/automation/mock_tab_event_observer.h b/chrome/browser/automation/mock_tab_event_observer.h new file mode 100644 index 0000000..d0400e6 --- /dev/null +++ b/chrome/browser/automation/mock_tab_event_observer.h @@ -0,0 +1,30 @@ +// Copyright (c) 2011 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_BROWSER_AUTOMATION_MOCK_TAB_EVENT_OBSERVER_H_ +#define CHROME_BROWSER_AUTOMATION_MOCK_TAB_EVENT_OBSERVER_H_ +#pragma once + +#include "chrome/browser/automation/automation_tab_helper.h" +#include "testing/gmock/include/gmock/gmock.h" + +class MockTabEventObserver : public TabEventObserver { + public: + MockTabEventObserver(); + // Convenience constructor for observing the |tab_helper| on creation. + explicit MockTabEventObserver(AutomationTabHelper* tab_helper); + virtual ~MockTabEventObserver(); + + // Promote these to public for testing purposes. + void StartObserving(AutomationTabHelper* tab_helper); + void StopObserving(AutomationTabHelper* tab_helper); + + MOCK_METHOD1(OnFirstPendingLoad, void(TabContents* tab_contents)); + MOCK_METHOD1(OnNoMorePendingLoads, void(TabContents* tab_contents)); + + private: + DISALLOW_COPY_AND_ASSIGN(MockTabEventObserver); +}; + +#endif // CHROME_BROWSER_AUTOMATION_MOCK_TAB_EVENT_OBSERVER_H_ diff --git a/chrome/browser/automation/testing_automation_provider.cc b/chrome/browser/automation/testing_automation_provider.cc index 67ffa9e..c258cdd 100644 --- a/chrome/browser/automation/testing_automation_provider.cc +++ b/chrome/browser/automation/testing_automation_provider.cc @@ -4869,6 +4869,7 @@ void TestingAutomationProvider::SetNTPMenuMode( void TestingAutomationProvider::WaitForAllTabsToStopLoading( DictionaryValue* args, IPC::Message* reply_message) { + // This class will send the message immediately if no tab is loading. new AllTabsStoppedLoadingObserver(this, reply_message); } diff --git a/chrome/browser/ui/tab_contents/tab_contents_wrapper.cc b/chrome/browser/ui/tab_contents/tab_contents_wrapper.cc index 010fa07..7d309f1 100644 --- a/chrome/browser/ui/tab_contents/tab_contents_wrapper.cc +++ b/chrome/browser/ui/tab_contents/tab_contents_wrapper.cc @@ -7,6 +7,7 @@ #include "base/lazy_instance.h" #include "chrome/browser/autocomplete_history_manager.h" #include "chrome/browser/autofill/autofill_manager.h" +#include "chrome/browser/automation/automation_tab_helper.h" #include "chrome/browser/bookmarks/bookmark_model.h" #include "chrome/browser/custom_handlers/protocol_handler_registry.h" #include "chrome/browser/custom_handlers/register_protocol_handler_infobar_delegate.h" @@ -55,6 +56,7 @@ TabContentsWrapper::TabContentsWrapper(TabContents* contents) // Create the tab helpers. autocomplete_history_manager_.reset(new AutocompleteHistoryManager(contents)); autofill_manager_.reset(new AutofillManager(contents)); + automation_tab_helper_.reset(new AutomationTabHelper(contents)); extension_tab_helper_.reset(new ExtensionTabHelper(this)); favicon_tab_helper_.reset(new FaviconTabHelper(contents)); find_tab_helper_.reset(new FindTabHelper(contents)); diff --git a/chrome/browser/ui/tab_contents/tab_contents_wrapper.h b/chrome/browser/ui/tab_contents/tab_contents_wrapper.h index 7b39bd6..b26e6a9 100644 --- a/chrome/browser/ui/tab_contents/tab_contents_wrapper.h +++ b/chrome/browser/ui/tab_contents/tab_contents_wrapper.h @@ -26,6 +26,7 @@ class PrintPreviewMessageHandler; class AutocompleteHistoryManager; class AutofillManager; +class AutomationTabHelper; class DevToolsObserver; class Extension; class ExtensionMessageObserver; @@ -100,6 +101,11 @@ class TabContentsWrapper : public NotificationObserver, AutofillManager* autofill_manager() { return autofill_manager_.get(); } + // Used only for testing/automation. + AutomationTabHelper* automation_tab_helper() { + return automation_tab_helper_.get(); + } + ExtensionTabHelper* extension_tab_helper() { return extension_tab_helper_.get(); } @@ -166,6 +172,7 @@ class TabContentsWrapper : public NotificationObserver, scoped_ptr<AutocompleteHistoryManager> autocomplete_history_manager_; scoped_ptr<AutofillManager> autofill_manager_; + scoped_ptr<AutomationTabHelper> automation_tab_helper_; scoped_ptr<ExtensionTabHelper> extension_tab_helper_; scoped_ptr<FaviconTabHelper> favicon_tab_helper_; scoped_ptr<FindTabHelper> find_tab_helper_; diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 824faeb..3d8fae5 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -225,6 +225,8 @@ 'browser/automation/automation_resource_routing_delegate.h', 'browser/automation/automation_resource_tracker.cc', 'browser/automation/automation_resource_tracker.h', + 'browser/automation/automation_tab_helper.cc', + 'browser/automation/automation_tab_helper.h', 'browser/automation/automation_tab_tracker.cc', 'browser/automation/automation_tab_tracker.h', 'browser/automation/automation_window_tracker.cc', diff --git a/chrome/chrome_renderer.gypi b/chrome/chrome_renderer.gypi index a0d50e9..39b7c89 100644 --- a/chrome/chrome_renderer.gypi +++ b/chrome/chrome_renderer.gypi @@ -49,6 +49,8 @@ 'renderer/autofill/form_manager.h', 'renderer/autofill/password_autofill_manager.cc', 'renderer/autofill/password_autofill_manager.h', + 'renderer/automation/automation_renderer_helper.cc', + 'renderer/automation/automation_renderer_helper.h', 'renderer/automation/dom_automation_controller.cc', 'renderer/automation/dom_automation_controller.h', 'renderer/automation/dom_automation_v8_extension.cc', diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index 6ec1390..57b70c0 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -54,6 +54,8 @@ 'browser/autofill/autofill_common_test.h', 'browser/autofill/data_driven_test.cc', 'browser/autofill/data_driven_test.h', + 'browser/automation/mock_tab_event_observer.cc', + 'browser/automation/mock_tab_event_observer.h', # The only thing used from browser is Browser::Type. 'browser/extensions/test_extension_prefs.cc', 'browser/extensions/test_extension_prefs.h', @@ -1230,6 +1232,7 @@ 'browser/autofill/phone_number_unittest.cc', 'browser/autofill/select_control_handler_unittest.cc', 'browser/automation/automation_provider_unittest.cc', + 'browser/automation/automation_tab_helper_unittest.cc', 'browser/background_application_list_model_unittest.cc', 'browser/background_contents_service_unittest.cc', 'browser/background_mode_manager_unittest.cc', @@ -2178,6 +2181,7 @@ 'browser/accessibility/renderer_accessibility_browsertest.cc', 'browser/autocomplete/autocomplete_browsertest.cc', 'browser/autofill/form_structure_browsertest.cc', + 'browser/automation/automation_tab_helper_browsertest.cc', 'browser/browser_browsertest.cc', 'browser/browsing_data_database_helper_browsertest.cc', 'browser/browsing_data_helper_browsertest.h', diff --git a/chrome/common/automation_messages_internal.h b/chrome/common/automation_messages_internal.h index 58d9830..694314d 100644 --- a/chrome/common/automation_messages_internal.h +++ b/chrome/common/automation_messages_internal.h @@ -1469,3 +1469,21 @@ IPC_SYNC_MESSAGE_CONTROL1_2(AutomationMsg_GetParentBrowserOfTab, // None expected IPC_MESSAGE_ROUTED1(AutomationMsg_MoveWindow, gfx::Rect /* window position and dimentions */) + +// Renderer -> browser messages. + +// Sent when the renderer has scheduled a client redirect to occur. +IPC_MESSAGE_ROUTED2(AutomationMsg_WillPerformClientRedirect, + int64 /* frame_id */, + double /* # of seconds till redirect will be performed */) + +// Sent when the renderer has completed or canceled a client redirect for a +// particular frame. This message may be sent multiple times for the same +// redirect. +IPC_MESSAGE_ROUTED1(AutomationMsg_DidCompleteOrCancelClientRedirect, + int64 /* frame_id */) + + +// YOUR NEW MESSAGE MIGHT NOT BELONG HERE. +// This is the section for renderer -> browser automation messages. If it is +// an automation <-> browser message, put it above this section. diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc index 9334281..6a08e18 100644 --- a/chrome/common/chrome_switches.cc +++ b/chrome/common/chrome_switches.cc @@ -372,10 +372,14 @@ const char kDnsPrefetchDisable[] = "dns-prefetch-disable"; // Use the specified DNS server for raw DNS resolution. const char kDnsServer[] = "dns-server"; -// Specifies if the dom_automation_controller_ needs to be bound in the +// Specifies if the |DOMAutomationController| needs to be bound in the // renderer. This binding happens on per-frame basis and hence can potentially // be a performance bottleneck. One should only enable it when automating // dom based tests. +// Also enables sending/receiving renderer automation messages through the +// |AutomationRenderViewHelper|. +// TODO(kkania): Rename this to enable-renderer-automation after moving the +// |DOMAutomationController| to the |AutomationRenderViewHelper|. const char kDomAutomationController[] = "dom-automation"; // Dump any accumualted histograms to the log when browser terminates (requires diff --git a/chrome/renderer/automation/automation_renderer_helper.cc b/chrome/renderer/automation/automation_renderer_helper.cc new file mode 100644 index 0000000..cba167e --- /dev/null +++ b/chrome/renderer/automation/automation_renderer_helper.cc @@ -0,0 +1,37 @@ +// Copyright (c) 2011 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/renderer/automation/automation_renderer_helper.h" + +#include "base/basictypes.h" +#include "chrome/common/automation_messages.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebURL.h" + +using WebKit::WebFrame; +using WebKit::WebURL; + +AutomationRendererHelper::AutomationRendererHelper(RenderView* render_view) + : RenderViewObserver(render_view) { +} + +AutomationRendererHelper::~AutomationRendererHelper() { } + +void AutomationRendererHelper::WillPerformClientRedirect( + WebFrame* frame, const WebURL& from, const WebURL& to, double interval, + double fire_time) { + Send(new AutomationMsg_WillPerformClientRedirect( + routing_id(), frame->identifier(), interval)); +} + +void AutomationRendererHelper::DidCancelClientRedirect(WebFrame* frame) { + Send(new AutomationMsg_DidCompleteOrCancelClientRedirect( + routing_id(), frame->identifier())); +} + +void AutomationRendererHelper::DidCompleteClientRedirect( + WebFrame* frame, const WebURL& from) { + Send(new AutomationMsg_DidCompleteOrCancelClientRedirect( + routing_id(), frame->identifier())); +} diff --git a/chrome/renderer/automation/automation_renderer_helper.h b/chrome/renderer/automation/automation_renderer_helper.h new file mode 100644 index 0000000..f601f04 --- /dev/null +++ b/chrome/renderer/automation/automation_renderer_helper.h @@ -0,0 +1,35 @@ +// Copyright (c) 2011 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_RENDERER_AUTOMATION_AUTOMATION_RENDERER_HELPER_H_ +#define CHROME_RENDERER_AUTOMATION_AUTOMATION_RENDERER_HELPER_H_ +#pragma once + +#include "content/renderer/render_view_observer.h" + +namespace WebKit { +class WebFrame; +class WebURL; +} + +// Filters automation/testing messages sent to a |RenderView| and sends +// automation/testing messages to the browser. +class AutomationRendererHelper : public RenderViewObserver { + public: + explicit AutomationRendererHelper(RenderView* render_view); + virtual ~AutomationRendererHelper(); + + private: + // RenderViewObserver implementation. + virtual void WillPerformClientRedirect( + WebKit::WebFrame* frame, const WebKit::WebURL& from, + const WebKit::WebURL& to, double interval, double fire_time); + virtual void DidCancelClientRedirect(WebKit::WebFrame* frame); + virtual void DidCompleteClientRedirect(WebKit::WebFrame* frame, + const WebKit::WebURL& from); + + DISALLOW_COPY_AND_ASSIGN(AutomationRendererHelper); +}; + +#endif // CHROME_RENDERER_AUTOMATION_AUTOMATION_RENDERER_HELPER_H_ diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc index 40ea77a..0703b7b 100644 --- a/chrome/renderer/chrome_content_renderer_client.cc +++ b/chrome/renderer/chrome_content_renderer_client.cc @@ -20,6 +20,7 @@ #include "chrome/renderer/autofill/autofill_agent.h" #include "chrome/renderer/autofill/form_manager.h" #include "chrome/renderer/autofill/password_autofill_manager.h" +#include "chrome/renderer/automation/automation_renderer_helper.h" #include "chrome/renderer/blocked_plugin.h" #include "chrome/renderer/devtools_agent.h" #include "chrome/renderer/extensions/bindings_utils.h" @@ -122,6 +123,12 @@ void ChromeContentRendererClient::RenderViewCreated(RenderView* render_view) { new PrintWebViewHelper(render_view); new SearchBox(render_view); + + // Used only for testing/automation. + if (CommandLine::ForCurrentProcess()->HasSwitch( + switches::kDomAutomationController)) { + new AutomationRendererHelper(render_view); + } } SkBitmap* ChromeContentRendererClient::GetSadPluginBitmap() { diff --git a/chrome/test/data/automation/client_redirects.html b/chrome/test/data/automation/client_redirects.html new file mode 100644 index 0000000..b3a221a --- /dev/null +++ b/chrome/test/data/automation/client_redirects.html @@ -0,0 +1,34 @@ +<!-- Used for testing that the automation code handles client redirects +---- correctly. Tests can call |runTestCase| to run various redirect +---- cases, such as javascript redirects and form submissions. +!--> +<html> + <script> + var g_shouldSubmit = true; + + function runTestCase(case_number) { + if (case_number == 1) { + document.querySelector("form").submit(); + } else if (case_number == 2) { + g_shouldSubmit = false; + // Submitting with javascript does not call the onsubmit handler, + // so fire a synthetic event instead. + var event = document.createEvent('MouseEvents'); + event.initMouseEvent('click', true, true, window, + 0, 0, 0, 0, 0, false, false, + false, false, 0, null); + document.querySelector("#submitButton").dispatchEvent(event); + } else if (case_number == 3) { + window.location.href = "client_redirects.html"; + } else if (case_number == 4) { + window.location.href = "nosuchpageexistshopefully"; + } + } + </script> + <body> + <form name="form" method="get" onsubmit="return g_shouldSubmit"> + <input name="text" type="text" value="1" /> + <input id="submitButton" type="submit" value="Submit" /> + </form> + </body> +</html> diff --git a/chrome/test/data/automation/meta_redirect.html b/chrome/test/data/automation/meta_redirect.html new file mode 100644 index 0000000..a1b83fe --- /dev/null +++ b/chrome/test/data/automation/meta_redirect.html @@ -0,0 +1,11 @@ +<!-- Used for testing that the automation code handles a meta tag redirect +---- correctly. +!--> +<html> + <head> + <meta http-equiv="refresh" content="0;url=client_redirects.html"> + </head> + <body> + Redirecting... + </body> +</html> diff --git a/chrome/test/data/automation/sends_message_on_load.html b/chrome/test/data/automation/sends_message_on_load.html new file mode 100644 index 0000000..794ada5 --- /dev/null +++ b/chrome/test/data/automation/sends_message_on_load.html @@ -0,0 +1,11 @@ +<!-- Used for testing that the automation code waits until the onload handler +---- has finished. +!--> +<html> + <script> + window.onload = function() { + window.domAutomationController.setAutomationId(0); + window.domAutomationController.send(1); + } + </script> +</html> |