summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authorkkania@chromium.org <kkania@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-04-07 22:08:38 +0000
committerkkania@chromium.org <kkania@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-04-07 22:08:38 +0000
commiteb0bff94e83fb3d6829cc9fe01468fdce672e7c3 (patch)
treefd4c174cc8e8032ee74afd6fede881883dabf982 /chrome
parent4aa70db622005c8041be5228a628167a17e3b019 (diff)
downloadchromium_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')
-rw-r--r--chrome/browser/automation/automation_provider_observers.cc69
-rw-r--r--chrome/browser/automation/automation_provider_observers.h23
-rw-r--r--chrome/browser/automation/automation_tab_helper.cc147
-rw-r--r--chrome/browser/automation/automation_tab_helper.h119
-rw-r--r--chrome/browser/automation/automation_tab_helper_browsertest.cc209
-rw-r--r--chrome/browser/automation/automation_tab_helper_unittest.cc207
-rw-r--r--chrome/browser/automation/mock_tab_event_observer.cc21
-rw-r--r--chrome/browser/automation/mock_tab_event_observer.h30
-rw-r--r--chrome/browser/automation/testing_automation_provider.cc1
-rw-r--r--chrome/browser/ui/tab_contents/tab_contents_wrapper.cc2
-rw-r--r--chrome/browser/ui/tab_contents/tab_contents_wrapper.h7
-rw-r--r--chrome/chrome_browser.gypi2
-rw-r--r--chrome/chrome_renderer.gypi2
-rw-r--r--chrome/chrome_tests.gypi4
-rw-r--r--chrome/common/automation_messages_internal.h18
-rw-r--r--chrome/common/chrome_switches.cc6
-rw-r--r--chrome/renderer/automation/automation_renderer_helper.cc37
-rw-r--r--chrome/renderer/automation/automation_renderer_helper.h35
-rw-r--r--chrome/renderer/chrome_content_renderer_client.cc7
-rw-r--r--chrome/test/data/automation/client_redirects.html34
-rw-r--r--chrome/test/data/automation/meta_redirect.html11
-rw-r--r--chrome/test/data/automation/sends_message_on_load.html11
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>