summaryrefslogtreecommitdiffstats
path: root/chrome/browser/automation
diff options
context:
space:
mode:
authorBen Murdoch <benm@google.com>2010-07-29 17:14:53 +0100
committerBen Murdoch <benm@google.com>2010-08-04 14:29:45 +0100
commitc407dc5cd9bdc5668497f21b26b09d988ab439de (patch)
tree7eaf8707c0309516bdb042ad976feedaf72b0bb1 /chrome/browser/automation
parent0998b1cdac5733f299c12d88bc31ef9c8035b8fa (diff)
downloadexternal_chromium-c407dc5cd9bdc5668497f21b26b09d988ab439de.zip
external_chromium-c407dc5cd9bdc5668497f21b26b09d988ab439de.tar.gz
external_chromium-c407dc5cd9bdc5668497f21b26b09d988ab439de.tar.bz2
Merge Chromium src@r53293
Change-Id: Ia79acf8670f385cee48c45b0a75371d8e950af34
Diffstat (limited to 'chrome/browser/automation')
-rw-r--r--chrome/browser/automation/automation_autocomplete_edit_tracker.h33
-rw-r--r--chrome/browser/automation/automation_browser_tracker.h32
-rw-r--r--chrome/browser/automation/automation_extension_function.cc165
-rw-r--r--chrome/browser/automation/automation_extension_function.h77
-rw-r--r--chrome/browser/automation/automation_extension_tracker.cc42
-rw-r--r--chrome/browser/automation/automation_extension_tracker.h39
-rw-r--r--chrome/browser/automation/automation_profile_impl.cc216
-rw-r--r--chrome/browser/automation/automation_profile_impl.h25
-rw-r--r--chrome/browser/automation/automation_provider.cc4133
-rw-r--r--chrome/browser/automation/automation_provider.h953
-rw-r--r--chrome/browser/automation/automation_provider_chromeos.cc27
-rw-r--r--chrome/browser/automation/automation_provider_gtk.cc221
-rw-r--r--chrome/browser/automation/automation_provider_json.cc55
-rw-r--r--chrome/browser/automation/automation_provider_json.h40
-rw-r--r--chrome/browser/automation/automation_provider_list.cc52
-rw-r--r--chrome/browser/automation/automation_provider_list.h48
-rw-r--r--chrome/browser/automation/automation_provider_list_generic.cc8
-rw-r--r--chrome/browser/automation/automation_provider_list_mac.mm13
-rw-r--r--chrome/browser/automation/automation_provider_mac.mm114
-rw-r--r--chrome/browser/automation/automation_provider_observers.cc1134
-rw-r--r--chrome/browser/automation/automation_provider_observers.h738
-rw-r--r--chrome/browser/automation/automation_provider_unittest.cc32
-rw-r--r--chrome/browser/automation/automation_provider_views.cc190
-rw-r--r--chrome/browser/automation/automation_provider_win.cc605
-rw-r--r--chrome/browser/automation/automation_resource_message_filter.cc389
-rw-r--r--chrome/browser/automation/automation_resource_message_filter.h202
-rw-r--r--chrome/browser/automation/automation_resource_routing_delegate.cc19
-rw-r--r--chrome/browser/automation/automation_resource_routing_delegate.h31
-rw-r--r--chrome/browser/automation/automation_resource_tracker.cc76
-rw-r--r--chrome/browser/automation/automation_resource_tracker.h158
-rw-r--r--chrome/browser/automation/automation_tab_tracker.h92
-rw-r--r--chrome/browser/automation/automation_window_tracker.h32
-rw-r--r--chrome/browser/automation/chrome_frame_automation_provider.cc79
-rw-r--r--chrome/browser/automation/chrome_frame_automation_provider.h41
-rw-r--r--chrome/browser/automation/extension_automation_constants.cc32
-rw-r--r--chrome/browser/automation/extension_automation_constants.h54
-rw-r--r--chrome/browser/automation/extension_port_container.cc266
-rw-r--r--chrome/browser/automation/extension_port_container.h87
-rw-r--r--chrome/browser/automation/ui_controls.h100
-rw-r--r--chrome/browser/automation/ui_controls_linux.cc289
-rw-r--r--chrome/browser/automation/ui_controls_mac.mm242
-rw-r--r--chrome/browser/automation/ui_controls_win.cc370
-rw-r--r--chrome/browser/automation/url_request_automation_job.cc460
-rw-r--r--chrome/browser/automation/url_request_automation_job.h127
44 files changed, 12138 insertions, 0 deletions
diff --git a/chrome/browser/automation/automation_autocomplete_edit_tracker.h b/chrome/browser/automation/automation_autocomplete_edit_tracker.h
new file mode 100644
index 0000000..3f741f3
--- /dev/null
+++ b/chrome/browser/automation/automation_autocomplete_edit_tracker.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2006-2008 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_AUTOCOMPLETE_EDIT_TRACKER_H_
+#define CHROME_BROWSER_AUTOMATION_AUTOMATION_AUTOCOMPLETE_EDIT_TRACKER_H_
+
+#include "chrome/browser/autocomplete/autocomplete_edit_view.h"
+#include "chrome/browser/automation/automation_resource_tracker.h"
+#include "chrome/common/notification_source.h"
+#include "chrome/common/notification_type.h"
+
+class AutomationAutocompleteEditTracker
+ : public AutomationResourceTracker<AutocompleteEditView*> {
+ public:
+ explicit AutomationAutocompleteEditTracker(IPC::Message::Sender* automation)
+ : AutomationResourceTracker<AutocompleteEditView*>(automation) { }
+
+ virtual ~AutomationAutocompleteEditTracker() {
+ }
+
+ virtual void AddObserver(AutocompleteEditView* resource) {
+ registrar_.Add(this, NotificationType::AUTOCOMPLETE_EDIT_DESTROYED,
+ Source<AutocompleteEditView>(resource));
+ }
+
+ virtual void RemoveObserver(AutocompleteEditView* resource) {
+ registrar_.Remove(this, NotificationType::AUTOCOMPLETE_EDIT_DESTROYED,
+ Source<AutocompleteEditView>(resource));
+ }
+};
+
+#endif // CHROME_BROWSER_AUTOMATION_AUTOMATION_AUTOCOMPLETE_EDIT_TRACKER_H_
diff --git a/chrome/browser/automation/automation_browser_tracker.h b/chrome/browser/automation/automation_browser_tracker.h
new file mode 100644
index 0000000..aa6d034
--- /dev/null
+++ b/chrome/browser/automation/automation_browser_tracker.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2006-2008 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_BROWSER_TRACKER_H__
+#define CHROME_BROWSER_AUTOMATION_AUTOMATION_BROWSER_TRACKER_H__
+
+#include "chrome/browser/automation/automation_resource_tracker.h"
+#include "chrome/browser/browser.h"
+#include "chrome/common/notification_source.h"
+
+// Tracks Browser objects.
+class AutomationBrowserTracker : public AutomationResourceTracker<Browser*> {
+ public:
+ explicit AutomationBrowserTracker(IPC::Message::Sender* automation)
+ : AutomationResourceTracker<Browser*>(automation) { }
+
+ virtual ~AutomationBrowserTracker() {
+ }
+
+ virtual void AddObserver(Browser* resource) {
+ registrar_.Add(this, NotificationType::BROWSER_CLOSED,
+ Source<Browser>(resource));
+ }
+
+ virtual void RemoveObserver(Browser* resource) {
+ registrar_.Remove(this, NotificationType::BROWSER_CLOSED,
+ Source<Browser>(resource));
+ }
+};
+
+#endif // CHROME_BROWSER_AUTOMATION_AUTOMATION_BROWSER_TRACKER_H__
diff --git a/chrome/browser/automation/automation_extension_function.cc b/chrome/browser/automation/automation_extension_function.cc
new file mode 100644
index 0000000..c58e45c
--- /dev/null
+++ b/chrome/browser/automation/automation_extension_function.cc
@@ -0,0 +1,165 @@
+// Copyright (c) 2009 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.
+
+// Implements AutomationExtensionFunction.
+
+#include "chrome/browser/automation/automation_extension_function.h"
+
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "chrome/browser/automation/extension_automation_constants.h"
+#include "chrome/browser/extensions/extension_function_dispatcher.h"
+#include "chrome/browser/renderer_host/render_view_host.h"
+#include "chrome/browser/renderer_host/render_view_host_delegate.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/browser/tab_contents/tab_contents_delegate.h"
+
+TabContents* AutomationExtensionFunction::api_handler_tab_ = NULL;
+AutomationExtensionFunction::PendingFunctionsMap
+ AutomationExtensionFunction::pending_functions_;
+
+void AutomationExtensionFunction::SetArgs(const ListValue* args) {
+ // Need to JSON-encode for sending over the wire to the automation user.
+ base::JSONWriter::Write(args, false, &args_);
+}
+
+const std::string AutomationExtensionFunction::GetResult() {
+ // Already JSON-encoded, so override the base class's implementation.
+ return json_result_;
+}
+
+bool AutomationExtensionFunction::RunImpl() {
+ namespace keys = extension_automation_constants;
+
+ DCHECK(api_handler_tab_) <<
+ "Why is this function still enabled if no target tab?";
+ if (!api_handler_tab_) {
+ error_ = "No longer automating functions.";
+ return false;
+ }
+
+ // We are being driven through automation, so we send the extension API
+ // request over to the automation host. We do this before decoding the
+ // 'args' JSON, otherwise we'd be decoding it only to encode it again.
+ DictionaryValue message_to_host;
+ message_to_host.SetString(keys::kAutomationNameKey, name_);
+ message_to_host.SetString(keys::kAutomationArgsKey, args_);
+ message_to_host.SetInteger(keys::kAutomationRequestIdKey, request_id_);
+ message_to_host.SetBoolean(keys::kAutomationHasCallbackKey, has_callback_);
+
+ std::string message;
+ base::JSONWriter::Write(&message_to_host, false, &message);
+ if (api_handler_tab_->delegate()) {
+ api_handler_tab_->delegate()->ForwardMessageToExternalHost(
+ message, keys::kAutomationOrigin, keys::kAutomationRequestTarget);
+ } else {
+ NOTREACHED() << "ExternalTabContainer is supposed to correctly manage "
+ "lifetime of api_handler_tab_.";
+ }
+
+ // Automation APIs are asynchronous so we need to stick around until
+ // our response comes back. Add ourselves to a static hash map keyed
+ // by request ID. The hash map keeps a reference count on us.
+ DCHECK(pending_functions_.find(request_id_) == pending_functions_.end());
+ pending_functions_[request_id_] = this;
+
+ return true;
+}
+
+ExtensionFunction* AutomationExtensionFunction::Factory() {
+ return new AutomationExtensionFunction();
+}
+
+void AutomationExtensionFunction::Enable(
+ TabContents* api_handler_tab,
+ const std::vector<std::string>& functions_enabled) {
+ DCHECK(api_handler_tab);
+ if (api_handler_tab_ && api_handler_tab != api_handler_tab_) {
+ NOTREACHED() << "Don't call with different API handler.";
+ return;
+ }
+ api_handler_tab_ = api_handler_tab;
+
+ std::vector<std::string> function_names;
+ if (functions_enabled.size() == 1 && functions_enabled[0] == "*") {
+ ExtensionFunctionDispatcher::GetAllFunctionNames(&function_names);
+ } else {
+ function_names = functions_enabled;
+ }
+
+ for (std::vector<std::string>::iterator it = function_names.begin();
+ it != function_names.end(); it++) {
+ // TODO(joi) Could make this a per-profile change rather than a global
+ // change. Could e.g. have the AutomationExtensionFunction store the
+ // profile pointer and dispatch to the original ExtensionFunction when the
+ // current profile is not that.
+ bool result = ExtensionFunctionDispatcher::OverrideFunction(
+ *it, AutomationExtensionFunction::Factory);
+ LOG_IF(WARNING, !result) << "Failed to override API function: " << *it;
+ }
+}
+
+void AutomationExtensionFunction::Disable() {
+ api_handler_tab_ = NULL;
+ ExtensionFunctionDispatcher::ResetFunctions();
+}
+
+bool AutomationExtensionFunction::InterceptMessageFromExternalHost(
+ RenderViewHost* view_host,
+ const std::string& message,
+ const std::string& origin,
+ const std::string& target) {
+ namespace keys = extension_automation_constants;
+
+ // We want only specially-tagged messages passed via the conduit tab.
+ if (api_handler_tab_ &&
+ view_host == api_handler_tab_->render_view_host() &&
+ origin == keys::kAutomationOrigin &&
+ target == keys::kAutomationResponseTarget) {
+ // This is an extension API response being sent back via postMessage,
+ // so redirect it.
+ scoped_ptr<Value> message_value(base::JSONReader::Read(message, false));
+ DCHECK(message_value->IsType(Value::TYPE_DICTIONARY));
+ if (message_value->IsType(Value::TYPE_DICTIONARY)) {
+ DictionaryValue* message_dict =
+ reinterpret_cast<DictionaryValue*>(message_value.get());
+
+ int request_id = -1;
+ bool got_value = message_dict->GetInteger(keys::kAutomationRequestIdKey,
+ &request_id);
+ DCHECK(got_value);
+ if (got_value) {
+ std::string error;
+ bool success = !message_dict->GetString(keys::kAutomationErrorKey,
+ &error);
+
+ std::string response;
+ got_value = message_dict->GetString(keys::kAutomationResponseKey,
+ &response);
+ DCHECK(!success || got_value);
+
+ PendingFunctionsMap::iterator it = pending_functions_.find(request_id);
+ DCHECK(it != pending_functions_.end());
+
+ if (it != pending_functions_.end()) {
+ scoped_refptr<AutomationExtensionFunction> func = it->second;
+ pending_functions_.erase(it);
+
+ // Our local ref should be the last remaining.
+ DCHECK(func && func->HasOneRef());
+
+ if (func) {
+ func->json_result_ = response;
+ func->error_ = error;
+
+ func->SendResponse(success);
+ }
+ }
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
diff --git a/chrome/browser/automation/automation_extension_function.h b/chrome/browser/automation/automation_extension_function.h
new file mode 100644
index 0000000..5e15e9e
--- /dev/null
+++ b/chrome/browser/automation/automation_extension_function.h
@@ -0,0 +1,77 @@
+// Copyright (c) 2009 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.
+
+// Defines AutomationExtensionFunction.
+
+#ifndef CHROME_BROWSER_AUTOMATION_AUTOMATION_EXTENSION_FUNCTION_H_
+#define CHROME_BROWSER_AUTOMATION_AUTOMATION_EXTENSION_FUNCTION_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "chrome/browser/extensions/extension_function.h"
+
+class RenderViewHost;
+class TabContents;
+
+// An extension function that pipes the extension API call through the
+// automation interface, so that extensions can be tested using UITests.
+class AutomationExtensionFunction : public AsyncExtensionFunction {
+ public:
+ AutomationExtensionFunction() { }
+
+ // ExtensionFunction implementation.
+ virtual void SetArgs(const ListValue* args);
+ virtual const std::string GetResult();
+ virtual bool RunImpl();
+
+ static ExtensionFunction* Factory();
+
+ // Enable API automation of selected APIs. Overridden extension API messages
+ // will be routed to the automation client attached to |api_handler_tab|.
+ //
+ // If the list of enabled functions is non-empty, we enable according to the
+ // list ("*" means enable all, otherwise we enable individual named
+ // functions). An empty list makes this function a no-op.
+ //
+ // Note that all calls to this function are additive. Functions previously
+ // enabled will remain enabled until you call Disable().
+ //
+ // Calling this function after enabling one or more functions with a
+ // tab other than the one previously used is an error.
+ static void Enable(TabContents* api_handler_tab,
+ const std::vector<std::string>& functions_enabled);
+
+ // Restore the default API function implementations and reset the stored
+ // API handler.
+ static void Disable();
+
+ // Intercepts messages sent from the external host to check if they
+ // are actually responses to extension API calls. If they are, redirects
+ // the message to respond to the pending asynchronous API call and returns
+ // true, otherwise returns false to indicate the message was not intercepted.
+ static bool InterceptMessageFromExternalHost(RenderViewHost* view_host,
+ const std::string& message,
+ const std::string& origin,
+ const std::string& target);
+
+ private:
+ ~AutomationExtensionFunction() {}
+
+ // Weak reference, lifetime managed by the ExternalTabContainer instance
+ // owning the TabContents in question.
+ static TabContents* api_handler_tab_;
+
+ typedef std::map<int, scoped_refptr<AutomationExtensionFunction> >
+ PendingFunctionsMap;
+ static PendingFunctionsMap pending_functions_;
+
+ std::string args_;
+ std::string json_result_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutomationExtensionFunction);
+};
+
+#endif // CHROME_BROWSER_AUTOMATION_AUTOMATION_EXTENSION_FUNCTION_H_
diff --git a/chrome/browser/automation/automation_extension_tracker.cc b/chrome/browser/automation/automation_extension_tracker.cc
new file mode 100644
index 0000000..8f09e1f
--- /dev/null
+++ b/chrome/browser/automation/automation_extension_tracker.cc
@@ -0,0 +1,42 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/automation/automation_extension_tracker.h"
+#include "chrome/browser/extensions/extensions_service.h"
+#include "chrome/browser/profile.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/common/notification_service.h"
+
+AutomationExtensionTracker::AutomationExtensionTracker(
+ IPC::Message::Sender* automation)
+ : AutomationResourceTracker<Extension*>(automation) {
+ registrar_.Add(this, NotificationType::EXTENSION_UNLOADED,
+ NotificationService::AllSources());
+ registrar_.Add(this, NotificationType::EXTENSION_UNLOADED_DISABLED,
+ NotificationService::AllSources());
+}
+
+AutomationExtensionTracker::~AutomationExtensionTracker() {
+}
+
+void AutomationExtensionTracker::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ if (type != NotificationType::EXTENSION_UNLOADED &&
+ type != NotificationType::EXTENSION_UNLOADED_DISABLED)
+ return;
+
+ Extension* extension = Details<Extension>(details).ptr();
+ Profile* profile = Source<Profile>(source).ptr();
+ if (profile) {
+ ExtensionsService* service = profile->GetExtensionsService();
+ if (service) {
+ // Remove this extension only if it is uninstalled, not just disabled.
+ // If it is being uninstalled, the extension will not be in the regular
+ // or disabled list.
+ if (!service->GetExtensionById(extension->id(), true))
+ CloseResource(extension);
+ }
+ }
+}
diff --git a/chrome/browser/automation/automation_extension_tracker.h b/chrome/browser/automation/automation_extension_tracker.h
new file mode 100644
index 0000000..e55a2eb
--- /dev/null
+++ b/chrome/browser/automation/automation_extension_tracker.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_AUTOMATION_AUTOMATION_EXTENSION_TRACKER_H_
+#define CHROME_BROWSER_AUTOMATION_AUTOMATION_EXTENSION_TRACKER_H_
+
+#include "chrome/browser/automation/automation_resource_tracker.h"
+
+class Extension;
+
+// Tracks an Extension. An Extension is removed on uninstall, not on disable.
+class AutomationExtensionTracker
+ : public AutomationResourceTracker<Extension*> {
+ public:
+ AutomationExtensionTracker(IPC::Message::Sender* automation);
+
+ virtual ~AutomationExtensionTracker();
+
+ // This is empty because we do not want to add an observer for every
+ // extension added to the tracker. This is because the profile, not the
+ // extension, is the one who sends the notification about extension
+ // uninstalls. Instead of using this method, one observer is added for all
+ // extensions in the constructor.
+ virtual void AddObserver(Extension* resource) {}
+
+ // See related comment above as to why this method is empty.
+ virtual void RemoveObserver(Extension* resource) {}
+
+ // Overriding AutomationResourceTracker Observe. AutomationResourceTracker's
+ // Observe expects the NotificationSource to be the object that is closing.
+ // This is not true for the relevant extension notifications, so we have to
+ // the observation ourselves.
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+};
+
+#endif // CHROME_BROWSER_AUTOMATION_AUTOMATION_EXTENSION_TRACKER_H_
diff --git a/chrome/browser/automation/automation_profile_impl.cc b/chrome/browser/automation/automation_profile_impl.cc
new file mode 100644
index 0000000..751019a
--- /dev/null
+++ b/chrome/browser/automation/automation_profile_impl.cc
@@ -0,0 +1,216 @@
+// Copyright (c) 2006-2009 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_profile_impl.h"
+
+#include <map>
+
+#include "chrome/browser/automation/automation_resource_message_filter.h"
+#include "chrome/browser/chrome_thread.h"
+#include "chrome/browser/net/chrome_url_request_context.h"
+#include "chrome/browser/profile.h"
+#include "net/base/net_errors.h"
+#include "net/url_request/url_request_context.h"
+#include "chrome/test/automation/automation_messages.h"
+
+namespace AutomationRequestContext {
+
+// A special Request context for automation. Substitute a few things
+// like cookie store, proxy settings etc to handle them differently
+// for automation.
+class AutomationURLRequestContext : public ChromeURLRequestContext {
+ public:
+ AutomationURLRequestContext(ChromeURLRequestContext* original_context,
+ net::CookieStore* automation_cookie_store,
+ net::CookiePolicy* automation_cookie_policy)
+ : ChromeURLRequestContext(original_context),
+ // We must hold a reference to |original_context|, since many
+ // of the dependencies that ChromeURLRequestContext(original_context)
+ // copied are scoped to |original_context|.
+ original_context_(original_context) {
+ cookie_policy_ = automation_cookie_policy;
+ cookie_store_ = automation_cookie_store;
+ }
+
+ virtual bool IsExternal() const {
+ return true;
+ }
+
+ private:
+ virtual ~AutomationURLRequestContext() {
+ // Clear out members before calling base class dtor since we don't
+ // own any of them.
+
+ // Clear URLRequestContext members.
+ host_resolver_ = NULL;
+ proxy_service_ = NULL;
+ http_transaction_factory_ = NULL;
+ ftp_transaction_factory_ = NULL;
+ cookie_store_ = NULL;
+ transport_security_state_ = NULL;
+ }
+
+ scoped_refptr<ChromeURLRequestContext> original_context_;
+ DISALLOW_COPY_AND_ASSIGN(AutomationURLRequestContext);
+};
+
+// CookieStore specialization to have automation specific
+// behavior for cookies.
+class AutomationCookieStore : public net::CookieStore {
+ public:
+ AutomationCookieStore(net::CookieStore* original_cookie_store,
+ AutomationResourceMessageFilter* automation_client,
+ int tab_handle)
+ : original_cookie_store_(original_cookie_store),
+ automation_client_(automation_client),
+ tab_handle_(tab_handle) {
+ }
+
+ virtual ~AutomationCookieStore() {
+ DLOG(INFO) << "In " << __FUNCTION__;
+ }
+
+ // CookieStore implementation.
+ virtual bool SetCookieWithOptions(const GURL& url,
+ const std::string& cookie_line,
+ const net::CookieOptions& options) {
+ // The cookie_string_ is available only once, i.e. once it is read by
+ // it is invalidated.
+ cookie_string_ = cookie_line;
+ return true;
+ }
+
+ virtual std::string GetCookiesWithOptions(const GURL& url,
+ const net::CookieOptions& options) {
+ return cookie_string_;
+ }
+
+ virtual void DeleteCookie(const GURL& url,
+ const std::string& cookie_name) {
+ NOTREACHED() << "Should not get called for an automation profile";
+ }
+
+ virtual net::CookieMonster* GetCookieMonster() {
+ NOTREACHED() << "Should not get called for an automation profile";
+ return NULL;
+ }
+
+ protected:
+ void SendIPCMessageOnIOThread(IPC::Message* m) {
+ if (ChromeThread::CurrentlyOn(ChromeThread::IO)) {
+ automation_client_->Send(m);
+ } else {
+ Task* task = NewRunnableMethod(this,
+ &AutomationCookieStore::SendIPCMessageOnIOThread, m);
+ ChromeThread::PostTask(ChromeThread::IO, FROM_HERE, task);
+ }
+ }
+
+ net::CookieStore* original_cookie_store_;
+ scoped_refptr<AutomationResourceMessageFilter> automation_client_;
+ int tab_handle_;
+ std::string cookie_string_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AutomationCookieStore);
+};
+
+// CookiePolicy specialization for automation specific cookie policies.
+class AutomationCookiePolicy : public net::CookiePolicy {
+ public:
+ AutomationCookiePolicy(AutomationResourceMessageFilter* automation_client,
+ int tab_handle, net::CookieStore* cookie_store)
+ : automation_client_(automation_client),
+ tab_handle_(tab_handle),
+ cookie_store_(cookie_store) {}
+
+ virtual int CanGetCookies(const GURL& url,
+ const GURL& first_party_for_cookies,
+ net::CompletionCallback* callback) {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
+
+ if (automation_client_.get()) {
+ automation_client_->GetCookiesForUrl(tab_handle_, url, callback,
+ cookie_store_.get());
+ return net::ERR_IO_PENDING;
+ }
+ return net::ERR_ACCESS_DENIED;
+ }
+
+ virtual int CanSetCookie(const GURL& url,
+ const GURL& first_party_for_cookies,
+ const std::string& cookie_line,
+ net::CompletionCallback* callback) {
+ if (automation_client_.get()) {
+ automation_client_->Send(new AutomationMsg_SetCookieAsync(0,
+ tab_handle_, url, cookie_line));
+ }
+ return net::ERR_ACCESS_DENIED;
+ }
+
+ private:
+ scoped_refptr<AutomationResourceMessageFilter> automation_client_;
+ int tab_handle_;
+ scoped_refptr<net::CookieStore> cookie_store_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutomationCookiePolicy);
+};
+
+class Factory : public ChromeURLRequestContextFactory {
+ public:
+ Factory(ChromeURLRequestContextGetter* original_context_getter,
+ Profile* profile,
+ AutomationResourceMessageFilter* automation_client,
+ int tab_handle)
+ : ChromeURLRequestContextFactory(profile),
+ original_context_getter_(original_context_getter),
+ automation_client_(automation_client),
+ tab_handle_(tab_handle) {
+ }
+
+ virtual ChromeURLRequestContext* Create() {
+ ChromeURLRequestContext* original_context =
+ original_context_getter_->GetIOContext();
+
+ // Create an automation cookie store.
+ scoped_refptr<net::CookieStore> automation_cookie_store =
+ new AutomationCookieStore(original_context->cookie_store(),
+ automation_client_,
+ tab_handle_);
+
+ // Create an automation cookie policy.
+ AutomationCookiePolicy* automation_cookie_policy =
+ new AutomationCookiePolicy(automation_client_,
+ tab_handle_,
+ automation_cookie_store);
+
+ return new AutomationURLRequestContext(original_context,
+ automation_cookie_store,
+ automation_cookie_policy);
+ }
+
+ private:
+ scoped_refptr<ChromeURLRequestContextGetter> original_context_getter_;
+ scoped_refptr<AutomationResourceMessageFilter> automation_client_;
+ int tab_handle_;
+};
+
+ChromeURLRequestContextGetter* CreateAutomationURLRequestContextForTab(
+ int tab_handle,
+ Profile* profile,
+ AutomationResourceMessageFilter* automation_client) {
+ ChromeURLRequestContextGetter* original_context =
+ static_cast<ChromeURLRequestContextGetter*>(
+ profile->GetRequestContext());
+
+ ChromeURLRequestContextGetter* request_context =
+ new ChromeURLRequestContextGetter(
+ NULL, // Don't register an observer on PrefService.
+ new Factory(original_context, profile, automation_client,
+ tab_handle));
+ return request_context;
+}
+
+} // namespace AutomationRequestContext
+
diff --git a/chrome/browser/automation/automation_profile_impl.h b/chrome/browser/automation/automation_profile_impl.h
new file mode 100644
index 0000000..5bfba8e
--- /dev/null
+++ b/chrome/browser/automation/automation_profile_impl.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2009 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_PROFILE_IMPL_H_
+#define CHROME_BROWSER_AUTOMATION_AUTOMATION_PROFILE_IMPL_H_
+
+#include "ipc/ipc_message.h"
+
+class Profile;
+class ChromeURLRequestContextGetter;
+class AutomationResourceMessageFilter;
+
+namespace AutomationRequestContext {
+
+// Returns the URL request context to be used by HTTP requests handled over
+// the automation channel.
+ChromeURLRequestContextGetter* CreateAutomationURLRequestContextForTab(
+ int tab_handle,
+ Profile* profile,
+ AutomationResourceMessageFilter* automation_client);
+
+}
+
+#endif // CHROME_BROWSER_AUTOMATION_AUTOMATION_PROFILE_IMPL_H_
diff --git a/chrome/browser/automation/automation_provider.cc b/chrome/browser/automation/automation_provider.cc
new file mode 100644
index 0000000..6c66931
--- /dev/null
+++ b/chrome/browser/automation/automation_provider.cc
@@ -0,0 +1,4133 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/automation/automation_provider.h"
+
+#include <set>
+
+#include "app/l10n_util.h"
+#include "app/message_box_flags.h"
+#include "base/callback.h"
+#include "base/file_path.h"
+#include "base/file_version_info.h"
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/json/string_escape.h"
+#include "base/keyboard_codes.h"
+#include "base/message_loop.h"
+#include "base/path_service.h"
+#include "base/process_util.h"
+#include "base/stl_util-inl.h"
+#include "base/string_util.h"
+#include "base/task.h"
+#include "base/thread.h"
+#include "base/utf_string_conversions.h"
+#include "base/values.h"
+#include "base/waitable_event.h"
+#include "chrome/app/chrome_dll_resource.h"
+#include "chrome/browser/app_modal_dialog.h"
+#include "chrome/browser/app_modal_dialog_queue.h"
+#include "chrome/browser/autofill/autofill_manager.h"
+#include "chrome/browser/automation/automation_autocomplete_edit_tracker.h"
+#include "chrome/browser/automation/automation_browser_tracker.h"
+#include "chrome/browser/automation/automation_extension_tracker.h"
+#include "chrome/browser/automation/automation_provider_json.h"
+#include "chrome/browser/automation/automation_provider_list.h"
+#include "chrome/browser/automation/automation_provider_observers.h"
+#include "chrome/browser/automation/automation_resource_message_filter.h"
+#include "chrome/browser/automation/automation_tab_tracker.h"
+#include "chrome/browser/automation/automation_window_tracker.h"
+#include "chrome/browser/automation/extension_port_container.h"
+#include "chrome/browser/autocomplete/autocomplete_edit.h"
+#include "chrome/browser/blocked_popup_container.h"
+#include "chrome/browser/bookmarks/bookmark_model.h"
+#include "chrome/browser/bookmarks/bookmark_storage.h"
+#include "chrome/browser/browser_list.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/browser_window.h"
+#include "chrome/browser/browsing_data_remover.h"
+#include "chrome/browser/character_encoding.h"
+#include "chrome/browser/chrome_thread.h"
+#include "chrome/browser/dom_operation_notification_details.h"
+#include "chrome/browser/debugger/devtools_manager.h"
+#include "chrome/browser/download/download_item.h"
+#include "chrome/browser/download/download_shelf.h"
+#include "chrome/browser/download/save_package.h"
+#include "chrome/browser/extensions/crx_installer.h"
+#include "chrome/browser/extensions/extension_browser_event_router.h"
+#include "chrome/browser/extensions/extension_host.h"
+#include "chrome/browser/extensions/extension_install_ui.h"
+#include "chrome/browser/extensions/extension_message_service.h"
+#include "chrome/browser/extensions/extension_tabs_module.h"
+#include "chrome/browser/extensions/extension_toolbar_model.h"
+#include "chrome/browser/extensions/extensions_service.h"
+#include "chrome/browser/extensions/user_script_master.h"
+#include "chrome/browser/find_bar.h"
+#include "chrome/browser/find_bar_controller.h"
+#include "chrome/browser/find_notification_details.h"
+#include "chrome/browser/host_content_settings_map.h"
+#include "chrome/browser/importer/importer.h"
+#include "chrome/browser/importer/importer_data_types.h"
+#include "chrome/browser/io_thread.h"
+#include "chrome/browser/location_bar.h"
+#include "chrome/browser/login_prompt.h"
+#include "chrome/browser/net/url_request_mock_util.h"
+#include "chrome/browser/platform_util.h"
+#include "chrome/browser/pref_service.h"
+#include "chrome/browser/printing/print_job.h"
+#include "chrome/browser/profile_manager.h"
+#include "chrome/browser/renderer_host/render_process_host.h"
+#include "chrome/browser/renderer_host/render_view_host.h"
+#include "chrome/browser/ssl/ssl_manager.h"
+#include "chrome/browser/ssl/ssl_blocking_page.h"
+#include "chrome/browser/tab_contents/infobar_delegate.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/browser/tab_contents/tab_contents_view.h"
+#include "chrome/browser/translate/translate_infobar_delegate.h"
+#include "chrome/common/automation_constants.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/chrome_version_info.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/common/json_value_serializer.h"
+#include "chrome/common/net/url_request_context_getter.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/test/automation/automation_messages.h"
+#include "chrome/test/automation/tab_proxy.h"
+#include "net/proxy/proxy_service.h"
+#include "net/proxy/proxy_config_service_fixed.h"
+#include "net/url_request/url_request_context.h"
+#include "chrome/browser/automation/ui_controls.h"
+#include "views/event.h"
+#include "webkit/glue/password_form.h"
+#include "webkit/glue/plugins/plugin_list.h"
+
+#if defined(OS_WIN)
+#include "chrome/browser/external_tab_container_win.h"
+#endif // defined(OS_WIN)
+
+using base::Time;
+
+class AutomationInterstitialPage : public InterstitialPage {
+ public:
+ AutomationInterstitialPage(TabContents* tab,
+ const GURL& url,
+ const std::string& contents)
+ : InterstitialPage(tab, true, url),
+ contents_(contents) {
+ }
+
+ virtual std::string GetHTMLContents() { return contents_; }
+
+ private:
+ std::string contents_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutomationInterstitialPage);
+};
+
+class ClickTask : public Task {
+ public:
+ explicit ClickTask(int flags) : flags_(flags) {}
+ virtual ~ClickTask() {}
+
+ virtual void Run() {
+ ui_controls::MouseButton button = ui_controls::LEFT;
+ if ((flags_ & views::Event::EF_LEFT_BUTTON_DOWN) ==
+ views::Event::EF_LEFT_BUTTON_DOWN) {
+ button = ui_controls::LEFT;
+ } else if ((flags_ & views::Event::EF_RIGHT_BUTTON_DOWN) ==
+ views::Event::EF_RIGHT_BUTTON_DOWN) {
+ button = ui_controls::RIGHT;
+ } else if ((flags_ & views::Event::EF_MIDDLE_BUTTON_DOWN) ==
+ views::Event::EF_MIDDLE_BUTTON_DOWN) {
+ button = ui_controls::MIDDLE;
+ } else {
+ NOTREACHED();
+ }
+
+ ui_controls::SendMouseClick(button);
+ }
+
+ private:
+ int flags_;
+
+ DISALLOW_COPY_AND_ASSIGN(ClickTask);
+};
+
+AutomationProvider::AutomationProvider(Profile* profile)
+ : redirect_query_(0),
+ profile_(profile),
+ reply_message_(NULL),
+ popup_menu_waiter_(NULL) {
+ browser_tracker_.reset(new AutomationBrowserTracker(this));
+ extension_tracker_.reset(new AutomationExtensionTracker(this));
+ tab_tracker_.reset(new AutomationTabTracker(this));
+ window_tracker_.reset(new AutomationWindowTracker(this));
+ autocomplete_edit_tracker_.reset(
+ new AutomationAutocompleteEditTracker(this));
+ new_tab_ui_load_observer_.reset(new NewTabUILoadObserver(this));
+ dom_operation_observer_.reset(new DomOperationNotificationObserver(this));
+ metric_event_duration_observer_.reset(new MetricEventDurationObserver());
+ extension_test_result_observer_.reset(
+ new ExtensionTestResultNotificationObserver(this));
+ g_browser_process->AddRefModule();
+}
+
+AutomationProvider::~AutomationProvider() {
+ STLDeleteContainerPairSecondPointers(port_containers_.begin(),
+ port_containers_.end());
+ port_containers_.clear();
+
+ // Make sure that any outstanding NotificationObservers also get destroyed.
+ ObserverList<NotificationObserver>::Iterator it(notification_observer_list_);
+ NotificationObserver* observer;
+ while ((observer = it.GetNext()) != NULL)
+ delete observer;
+
+ if (channel_.get()) {
+ channel_->Close();
+ }
+ g_browser_process->ReleaseModule();
+}
+
+void AutomationProvider::ConnectToChannel(const std::string& channel_id) {
+ automation_resource_message_filter_ = new AutomationResourceMessageFilter;
+ channel_.reset(
+ new IPC::SyncChannel(channel_id, IPC::Channel::MODE_CLIENT, this,
+ automation_resource_message_filter_,
+ g_browser_process->io_thread()->message_loop(),
+ true, g_browser_process->shutdown_event()));
+ scoped_ptr<FileVersionInfo> version_info(chrome::GetChromeVersionInfo());
+ std::string version_string;
+ if (version_info != NULL) {
+ version_string = WideToASCII(version_info->file_version());
+ }
+
+ // Send a hello message with our current automation protocol version.
+ channel_->Send(new AutomationMsg_Hello(0, version_string.c_str()));
+}
+
+void AutomationProvider::SetExpectedTabCount(size_t expected_tabs) {
+ if (expected_tabs == 0) {
+ Send(new AutomationMsg_InitialLoadsComplete(0));
+ } else {
+ initial_load_observer_.reset(new InitialLoadObserver(expected_tabs, this));
+ }
+}
+
+NotificationObserver* AutomationProvider::AddNavigationStatusListener(
+ NavigationController* tab, IPC::Message* reply_message,
+ int number_of_navigations, bool include_current_navigation) {
+ NotificationObserver* observer =
+ new NavigationNotificationObserver(tab, this, reply_message,
+ number_of_navigations,
+ include_current_navigation);
+
+ notification_observer_list_.AddObserver(observer);
+ return observer;
+}
+
+void AutomationProvider::RemoveNavigationStatusListener(
+ NotificationObserver* obs) {
+ notification_observer_list_.RemoveObserver(obs);
+}
+
+NotificationObserver* AutomationProvider::AddTabStripObserver(
+ Browser* parent,
+ IPC::Message* reply_message) {
+ NotificationObserver* observer =
+ new TabAppendedNotificationObserver(parent, this, reply_message);
+ notification_observer_list_.AddObserver(observer);
+
+ return observer;
+}
+
+void AutomationProvider::RemoveTabStripObserver(NotificationObserver* obs) {
+ notification_observer_list_.RemoveObserver(obs);
+}
+
+void AutomationProvider::AddLoginHandler(NavigationController* tab,
+ LoginHandler* handler) {
+ login_handler_map_[tab] = handler;
+}
+
+void AutomationProvider::RemoveLoginHandler(NavigationController* tab) {
+ DCHECK(login_handler_map_[tab]);
+ login_handler_map_.erase(tab);
+}
+
+void AutomationProvider::AddPortContainer(ExtensionPortContainer* port) {
+ int port_id = port->port_id();
+ DCHECK_NE(-1, port_id);
+ DCHECK(port_containers_.find(port_id) == port_containers_.end());
+
+ port_containers_[port_id] = port;
+}
+
+void AutomationProvider::RemovePortContainer(ExtensionPortContainer* port) {
+ int port_id = port->port_id();
+ DCHECK_NE(-1, port_id);
+
+ PortContainerMap::iterator it = port_containers_.find(port_id);
+ DCHECK(it != port_containers_.end());
+
+ if (it != port_containers_.end()) {
+ delete it->second;
+ port_containers_.erase(it);
+ }
+}
+
+ExtensionPortContainer* AutomationProvider::GetPortContainer(
+ int port_id) const {
+ PortContainerMap::const_iterator it = port_containers_.find(port_id);
+ if (it == port_containers_.end())
+ return NULL;
+
+ return it->second;
+}
+
+int AutomationProvider::GetIndexForNavigationController(
+ const NavigationController* controller, const Browser* parent) const {
+ DCHECK(parent);
+ return parent->GetIndexOfController(controller);
+}
+
+int AutomationProvider::AddExtension(Extension* extension) {
+ DCHECK(extension);
+ return extension_tracker_->Add(extension);
+}
+
+Extension* AutomationProvider::GetExtension(int extension_handle) {
+ return extension_tracker_->GetResource(extension_handle);
+}
+
+Extension* AutomationProvider::GetEnabledExtension(int extension_handle) {
+ Extension* extension = extension_tracker_->GetResource(extension_handle);
+ ExtensionsService* service = profile_->GetExtensionsService();
+ if (extension && service &&
+ service->GetExtensionById(extension->id(), false))
+ return extension;
+ return NULL;
+}
+
+Extension* AutomationProvider::GetDisabledExtension(int extension_handle) {
+ Extension* extension = extension_tracker_->GetResource(extension_handle);
+ ExtensionsService* service = profile_->GetExtensionsService();
+ if (extension && service &&
+ service->GetExtensionById(extension->id(), true) &&
+ !service->GetExtensionById(extension->id(), false))
+ return extension;
+ return NULL;
+}
+
+void AutomationProvider::OnMessageReceived(const IPC::Message& message) {
+ IPC_BEGIN_MESSAGE_MAP(AutomationProvider, message)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_CloseBrowser, CloseBrowser)
+ IPC_MESSAGE_HANDLER(AutomationMsg_CloseBrowserRequestAsync,
+ CloseBrowserAsync)
+ IPC_MESSAGE_HANDLER(AutomationMsg_ActivateTab, ActivateTab)
+ IPC_MESSAGE_HANDLER(AutomationMsg_ActiveTabIndex, GetActiveTabIndex)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_AppendTab, AppendTab)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_CloseTab, CloseTab)
+ IPC_MESSAGE_HANDLER(AutomationMsg_GetCookies, GetCookies)
+ IPC_MESSAGE_HANDLER(AutomationMsg_SetCookie, SetCookie)
+ IPC_MESSAGE_HANDLER(AutomationMsg_DeleteCookie, DeleteCookie)
+ IPC_MESSAGE_HANDLER(AutomationMsg_ShowCollectedCookiesDialog,
+ ShowCollectedCookiesDialog)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_NavigateToURL, NavigateToURL)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(
+ AutomationMsg_NavigateToURLBlockUntilNavigationsComplete,
+ NavigateToURLBlockUntilNavigationsComplete)
+ IPC_MESSAGE_HANDLER(AutomationMsg_NavigationAsync, NavigationAsync)
+ IPC_MESSAGE_HANDLER(AutomationMsg_NavigationAsyncWithDisposition,
+ NavigationAsyncWithDisposition)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_GoBack, GoBack)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_GoForward, GoForward)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_Reload, Reload)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_SetAuth, SetAuth)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_CancelAuth, CancelAuth)
+ IPC_MESSAGE_HANDLER(AutomationMsg_NeedsAuth, NeedsAuth)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_RedirectsFrom,
+ GetRedirectsFrom)
+ IPC_MESSAGE_HANDLER(AutomationMsg_BrowserWindowCount, GetBrowserWindowCount)
+ IPC_MESSAGE_HANDLER(AutomationMsg_NormalBrowserWindowCount,
+ GetNormalBrowserWindowCount)
+ IPC_MESSAGE_HANDLER(AutomationMsg_BrowserWindow, GetBrowserWindow)
+ IPC_MESSAGE_HANDLER(AutomationMsg_GetBrowserLocale, GetBrowserLocale)
+ IPC_MESSAGE_HANDLER(AutomationMsg_LastActiveBrowserWindow,
+ GetLastActiveBrowserWindow)
+ IPC_MESSAGE_HANDLER(AutomationMsg_ActiveWindow, GetActiveWindow)
+ IPC_MESSAGE_HANDLER(AutomationMsg_FindNormalBrowserWindow,
+ FindNormalBrowserWindow)
+ IPC_MESSAGE_HANDLER(AutomationMsg_IsWindowActive, IsWindowActive)
+ IPC_MESSAGE_HANDLER(AutomationMsg_ActivateWindow, ActivateWindow)
+ IPC_MESSAGE_HANDLER(AutomationMsg_IsWindowMaximized, IsWindowMaximized)
+ IPC_MESSAGE_HANDLER(AutomationMsg_WindowExecuteCommandAsync,
+ ExecuteBrowserCommandAsync)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_WindowExecuteCommand,
+ ExecuteBrowserCommand)
+ IPC_MESSAGE_HANDLER(AutomationMsg_TerminateSession, TerminateSession)
+ IPC_MESSAGE_HANDLER(AutomationMsg_WindowViewBounds, WindowGetViewBounds)
+ IPC_MESSAGE_HANDLER(AutomationMsg_GetWindowBounds, GetWindowBounds)
+ IPC_MESSAGE_HANDLER(AutomationMsg_SetWindowBounds, SetWindowBounds)
+ IPC_MESSAGE_HANDLER(AutomationMsg_SetWindowVisible, SetWindowVisible)
+ IPC_MESSAGE_HANDLER(AutomationMsg_WindowClick, WindowSimulateClick)
+ IPC_MESSAGE_HANDLER(AutomationMsg_WindowMouseMove, WindowSimulateMouseMove)
+ IPC_MESSAGE_HANDLER(AutomationMsg_WindowKeyPress, WindowSimulateKeyPress)
+#if !defined(OS_MACOSX)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_WindowDrag,
+ WindowSimulateDrag)
+#endif // !defined(OS_MACOSX)
+ IPC_MESSAGE_HANDLER(AutomationMsg_TabCount, GetTabCount)
+ IPC_MESSAGE_HANDLER(AutomationMsg_Type, GetType)
+ IPC_MESSAGE_HANDLER(AutomationMsg_Tab, GetTab)
+#if defined(OS_WIN)
+ IPC_MESSAGE_HANDLER(AutomationMsg_TabHWND, GetTabHWND)
+#endif // defined(OS_WIN)
+ IPC_MESSAGE_HANDLER(AutomationMsg_TabProcessID, GetTabProcessID)
+ IPC_MESSAGE_HANDLER(AutomationMsg_TabTitle, GetTabTitle)
+ IPC_MESSAGE_HANDLER(AutomationMsg_TabIndex, GetTabIndex)
+ IPC_MESSAGE_HANDLER(AutomationMsg_TabURL, GetTabURL)
+ IPC_MESSAGE_HANDLER(AutomationMsg_ShelfVisibility, GetShelfVisibility)
+ IPC_MESSAGE_HANDLER(AutomationMsg_IsFullscreen, IsFullscreen)
+ IPC_MESSAGE_HANDLER(AutomationMsg_IsFullscreenBubbleVisible,
+ GetFullscreenBubbleVisibility)
+ IPC_MESSAGE_HANDLER(AutomationMsg_HandleUnused, HandleUnused)
+ IPC_MESSAGE_HANDLER(AutomationMsg_ApplyAccelerator, ApplyAccelerator)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_DomOperation,
+ ExecuteJavascript)
+ IPC_MESSAGE_HANDLER(AutomationMsg_ConstrainedWindowCount,
+ GetConstrainedWindowCount)
+ IPC_MESSAGE_HANDLER(AutomationMsg_FindInPage, HandleFindInPageRequest)
+ IPC_MESSAGE_HANDLER(AutomationMsg_GetFocusedViewID, GetFocusedViewID)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_InspectElement,
+ HandleInspectElementRequest)
+ IPC_MESSAGE_HANDLER(AutomationMsg_DownloadDirectory, GetDownloadDirectory)
+ IPC_MESSAGE_HANDLER(AutomationMsg_SetProxyConfig, SetProxyConfig);
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_OpenNewBrowserWindow,
+ OpenNewBrowserWindow)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_OpenNewBrowserWindowOfType,
+ OpenNewBrowserWindowOfType)
+ IPC_MESSAGE_HANDLER(AutomationMsg_WindowForBrowser, GetWindowForBrowser)
+ IPC_MESSAGE_HANDLER(AutomationMsg_AutocompleteEditForBrowser,
+ GetAutocompleteEditForBrowser)
+ IPC_MESSAGE_HANDLER(AutomationMsg_BrowserForWindow, GetBrowserForWindow)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_ShowInterstitialPage,
+ ShowInterstitialPage)
+ IPC_MESSAGE_HANDLER(AutomationMsg_HideInterstitialPage,
+ HideInterstitialPage)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_WaitForTabToBeRestored,
+ WaitForTabToBeRestored)
+ IPC_MESSAGE_HANDLER(AutomationMsg_GetSecurityState, GetSecurityState)
+ IPC_MESSAGE_HANDLER(AutomationMsg_GetPageType, GetPageType)
+ IPC_MESSAGE_HANDLER(AutomationMsg_GetMetricEventDuration,
+ GetMetricEventDuration)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_ActionOnSSLBlockingPage,
+ ActionOnSSLBlockingPage)
+ IPC_MESSAGE_HANDLER(AutomationMsg_BringBrowserToFront, BringBrowserToFront)
+ IPC_MESSAGE_HANDLER(AutomationMsg_IsMenuCommandEnabled,
+ IsMenuCommandEnabled)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_PrintNow, PrintNow)
+ IPC_MESSAGE_HANDLER(AutomationMsg_PrintAsync, PrintAsync)
+ IPC_MESSAGE_HANDLER(AutomationMsg_SavePage, SavePage)
+ IPC_MESSAGE_HANDLER(AutomationMsg_AutocompleteEditGetText,
+ GetAutocompleteEditText)
+ IPC_MESSAGE_HANDLER(AutomationMsg_AutocompleteEditSetText,
+ SetAutocompleteEditText)
+ IPC_MESSAGE_HANDLER(AutomationMsg_AutocompleteEditIsQueryInProgress,
+ AutocompleteEditIsQueryInProgress)
+ IPC_MESSAGE_HANDLER(AutomationMsg_AutocompleteEditGetMatches,
+ AutocompleteEditGetMatches)
+ IPC_MESSAGE_HANDLER(AutomationMsg_OpenFindInPage,
+ HandleOpenFindInPageRequest)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_Find, HandleFindRequest)
+ IPC_MESSAGE_HANDLER(AutomationMsg_FindWindowVisibility,
+ GetFindWindowVisibility)
+ IPC_MESSAGE_HANDLER(AutomationMsg_FindWindowLocation,
+ HandleFindWindowLocationRequest)
+ IPC_MESSAGE_HANDLER(AutomationMsg_BookmarkBarVisibility,
+ GetBookmarkBarVisibility)
+ IPC_MESSAGE_HANDLER(AutomationMsg_GetBookmarksAsJSON,
+ GetBookmarksAsJSON)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_WaitForBookmarkModelToLoad,
+ WaitForBookmarkModelToLoad)
+ IPC_MESSAGE_HANDLER(AutomationMsg_AddBookmarkGroup,
+ AddBookmarkGroup)
+ IPC_MESSAGE_HANDLER(AutomationMsg_AddBookmarkURL,
+ AddBookmarkURL)
+ IPC_MESSAGE_HANDLER(AutomationMsg_ReparentBookmark,
+ ReparentBookmark)
+ IPC_MESSAGE_HANDLER(AutomationMsg_SetBookmarkTitle,
+ SetBookmarkTitle)
+ IPC_MESSAGE_HANDLER(AutomationMsg_SetBookmarkURL,
+ SetBookmarkURL)
+ IPC_MESSAGE_HANDLER(AutomationMsg_RemoveBookmark,
+ RemoveBookmark)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_SendJSONRequest,
+ SendJSONRequest)
+ IPC_MESSAGE_HANDLER(AutomationMsg_GetInfoBarCount, GetInfoBarCount)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_ClickInfoBarAccept,
+ ClickInfoBarAccept)
+ IPC_MESSAGE_HANDLER(AutomationMsg_GetLastNavigationTime,
+ GetLastNavigationTime)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_WaitForNavigation,
+ WaitForNavigation)
+ IPC_MESSAGE_HANDLER(AutomationMsg_SetIntPreference, SetIntPreference)
+ IPC_MESSAGE_HANDLER(AutomationMsg_ShowingAppModalDialog,
+ GetShowingAppModalDialog)
+ IPC_MESSAGE_HANDLER(AutomationMsg_ClickAppModalDialogButton,
+ ClickAppModalDialogButton)
+ IPC_MESSAGE_HANDLER(AutomationMsg_SetStringPreference, SetStringPreference)
+ IPC_MESSAGE_HANDLER(AutomationMsg_GetBooleanPreference,
+ GetBooleanPreference)
+ IPC_MESSAGE_HANDLER(AutomationMsg_SetBooleanPreference,
+ SetBooleanPreference)
+ IPC_MESSAGE_HANDLER(AutomationMsg_GetPageCurrentEncoding,
+ GetPageCurrentEncoding)
+ IPC_MESSAGE_HANDLER(AutomationMsg_OverrideEncoding, OverrideEncoding)
+ IPC_MESSAGE_HANDLER(AutomationMsg_SavePackageShouldPromptUser,
+ SavePackageShouldPromptUser)
+ IPC_MESSAGE_HANDLER(AutomationMsg_WindowTitle, GetWindowTitle)
+ IPC_MESSAGE_HANDLER(AutomationMsg_SetShelfVisibility, SetShelfVisibility)
+ IPC_MESSAGE_HANDLER(AutomationMsg_BlockedPopupCount, GetBlockedPopupCount)
+ IPC_MESSAGE_HANDLER(AutomationMsg_SelectAll, SelectAll)
+ IPC_MESSAGE_HANDLER(AutomationMsg_Cut, Cut)
+ IPC_MESSAGE_HANDLER(AutomationMsg_Copy, Copy)
+ IPC_MESSAGE_HANDLER(AutomationMsg_Paste, Paste)
+ IPC_MESSAGE_HANDLER(AutomationMsg_ReloadAsync, ReloadAsync)
+ IPC_MESSAGE_HANDLER(AutomationMsg_StopAsync, StopAsync)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(
+ AutomationMsg_WaitForBrowserWindowCountToBecome,
+ WaitForBrowserWindowCountToBecome)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(
+ AutomationMsg_WaitForAppModalDialogToBeShown,
+ WaitForAppModalDialogToBeShown)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(
+ AutomationMsg_GoBackBlockUntilNavigationsComplete,
+ GoBackBlockUntilNavigationsComplete)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(
+ AutomationMsg_GoForwardBlockUntilNavigationsComplete,
+ GoForwardBlockUntilNavigationsComplete)
+ IPC_MESSAGE_HANDLER(AutomationMsg_SetPageFontSize, OnSetPageFontSize)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_InstallExtension,
+ InstallExtension)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_LoadExpandedExtension,
+ LoadExpandedExtension)
+ IPC_MESSAGE_HANDLER(AutomationMsg_GetEnabledExtensions,
+ GetEnabledExtensions)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_WaitForExtensionTestResult,
+ WaitForExtensionTestResult)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(
+ AutomationMsg_InstallExtensionAndGetHandle,
+ InstallExtensionAndGetHandle)
+ IPC_MESSAGE_HANDLER(AutomationMsg_UninstallExtension,
+ UninstallExtension)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_EnableExtension,
+ EnableExtension)
+ IPC_MESSAGE_HANDLER(AutomationMsg_DisableExtension,
+ DisableExtension)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(
+ AutomationMsg_ExecuteExtensionActionInActiveTabAsync,
+ ExecuteExtensionActionInActiveTabAsync)
+ IPC_MESSAGE_HANDLER(AutomationMsg_MoveExtensionBrowserAction,
+ MoveExtensionBrowserAction)
+ IPC_MESSAGE_HANDLER(AutomationMsg_GetExtensionProperty,
+ GetExtensionProperty)
+ IPC_MESSAGE_HANDLER(AutomationMsg_ShutdownSessionService,
+ ShutdownSessionService)
+ IPC_MESSAGE_HANDLER(AutomationMsg_SaveAsAsync, SaveAsAsync)
+ IPC_MESSAGE_HANDLER(AutomationMsg_SetContentSetting, SetContentSetting)
+ IPC_MESSAGE_HANDLER(AutomationMsg_RemoveBrowsingData, RemoveBrowsingData)
+ IPC_MESSAGE_HANDLER(AutomationMsg_ResetToDefaultTheme, ResetToDefaultTheme)
+#if defined(TOOLKIT_VIEWS)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_WaitForFocusedViewIDToChange,
+ WaitForFocusedViewIDToChange)
+ IPC_MESSAGE_HANDLER(AutomationMsg_StartTrackingPopupMenus,
+ StartTrackingPopupMenus)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_WaitForPopupMenuToOpen,
+ WaitForPopupMenuToOpen)
+#endif // defined(TOOLKIT_VIEWS)
+#if defined(OS_WIN)
+ // These are for use with external tabs.
+ IPC_MESSAGE_HANDLER(AutomationMsg_CreateExternalTab, CreateExternalTab)
+ IPC_MESSAGE_HANDLER(AutomationMsg_ProcessUnhandledAccelerator,
+ ProcessUnhandledAccelerator)
+ IPC_MESSAGE_HANDLER(AutomationMsg_SetInitialFocus, SetInitialFocus)
+ IPC_MESSAGE_HANDLER(AutomationMsg_TabReposition, OnTabReposition)
+ IPC_MESSAGE_HANDLER(AutomationMsg_ForwardContextMenuCommandToChrome,
+ OnForwardContextMenuCommandToChrome)
+ IPC_MESSAGE_HANDLER(AutomationMsg_NavigateInExternalTab,
+ NavigateInExternalTab)
+ IPC_MESSAGE_HANDLER(AutomationMsg_NavigateExternalTabAtIndex,
+ NavigateExternalTabAtIndex)
+ IPC_MESSAGE_HANDLER(AutomationMsg_ConnectExternalTab, ConnectExternalTab)
+ IPC_MESSAGE_HANDLER(AutomationMsg_SetEnableExtensionAutomation,
+ SetEnableExtensionAutomation)
+ IPC_MESSAGE_HANDLER(AutomationMsg_HandleMessageFromExternalHost,
+ OnMessageFromExternalHost)
+ IPC_MESSAGE_HANDLER(AutomationMsg_BrowserMove, OnBrowserMoved)
+ IPC_MESSAGE_HANDLER(AutomationMsg_RunUnloadHandlers, OnRunUnloadHandlers)
+#endif // defined(OS_WIN)
+#if defined(OS_CHROMEOS)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_LoginWithUserAndPass,
+ LoginWithUserAndPass)
+#endif // defined(OS_CHROMEOS)
+ IPC_END_MESSAGE_MAP()
+}
+
+void AutomationProvider::ActivateTab(int handle, int at_index, int* status) {
+ *status = -1;
+ if (browser_tracker_->ContainsHandle(handle) && at_index > -1) {
+ Browser* browser = browser_tracker_->GetResource(handle);
+ if (at_index >= 0 && at_index < browser->tab_count()) {
+ browser->SelectTabContentsAt(at_index, true);
+ *status = 0;
+ }
+ }
+}
+
+void AutomationProvider::AppendTab(int handle, const GURL& url,
+ IPC::Message* reply_message) {
+ int append_tab_response = -1; // -1 is the error code
+ NotificationObserver* observer = NULL;
+
+ if (browser_tracker_->ContainsHandle(handle)) {
+ Browser* browser = browser_tracker_->GetResource(handle);
+ observer = AddTabStripObserver(browser, reply_message);
+ TabContents* tab_contents = browser->AddTabWithURL(
+ url, GURL(), PageTransition::TYPED, -1, TabStripModel::ADD_SELECTED,
+ NULL, std::string());
+ if (tab_contents) {
+ append_tab_response =
+ GetIndexForNavigationController(&tab_contents->controller(), browser);
+ }
+ }
+
+ if (append_tab_response < 0) {
+ // The append tab failed. Remove the TabStripObserver
+ if (observer) {
+ RemoveTabStripObserver(observer);
+ delete observer;
+ }
+
+ AutomationMsg_AppendTab::WriteReplyParams(reply_message,
+ append_tab_response);
+ Send(reply_message);
+ }
+}
+
+void AutomationProvider::NavigateToURL(int handle, const GURL& url,
+ IPC::Message* reply_message) {
+ NavigateToURLBlockUntilNavigationsComplete(handle, url, 1, reply_message);
+}
+
+void AutomationProvider::NavigateToURLBlockUntilNavigationsComplete(
+ int handle, const GURL& url, int number_of_navigations,
+ IPC::Message* reply_message) {
+ if (tab_tracker_->ContainsHandle(handle)) {
+ NavigationController* tab = tab_tracker_->GetResource(handle);
+
+ // Simulate what a user would do. Activate the tab and then navigate.
+ // We could allow navigating in a background tab in future.
+ Browser* browser = FindAndActivateTab(tab);
+
+ if (browser) {
+ AddNavigationStatusListener(tab, reply_message, number_of_navigations,
+ false);
+
+ // TODO(darin): avoid conversion to GURL
+ browser->OpenURL(url, GURL(), CURRENT_TAB, PageTransition::TYPED);
+ return;
+ }
+ }
+
+ AutomationMsg_NavigateToURL::WriteReplyParams(
+ reply_message, AUTOMATION_MSG_NAVIGATION_ERROR);
+ Send(reply_message);
+}
+
+void AutomationProvider::NavigationAsync(int handle,
+ const GURL& url,
+ bool* status) {
+ NavigationAsyncWithDisposition(handle, url, CURRENT_TAB, status);
+}
+
+void AutomationProvider::NavigationAsyncWithDisposition(
+ int handle,
+ const GURL& url,
+ WindowOpenDisposition disposition,
+ bool* status) {
+ *status = false;
+
+ if (tab_tracker_->ContainsHandle(handle)) {
+ NavigationController* tab = tab_tracker_->GetResource(handle);
+
+ // Simulate what a user would do. Activate the tab and then navigate.
+ // We could allow navigating in a background tab in future.
+ Browser* browser = FindAndActivateTab(tab);
+
+ if (browser) {
+ // Don't add any listener unless a callback mechanism is desired.
+ // TODO(vibhor): Do this if such a requirement arises in future.
+ browser->OpenURL(url, GURL(), disposition, PageTransition::TYPED);
+ *status = true;
+ }
+ }
+}
+
+void AutomationProvider::GoBack(int handle, IPC::Message* reply_message) {
+ if (tab_tracker_->ContainsHandle(handle)) {
+ NavigationController* tab = tab_tracker_->GetResource(handle);
+ Browser* browser = FindAndActivateTab(tab);
+ if (browser && browser->command_updater()->IsCommandEnabled(IDC_BACK)) {
+ AddNavigationStatusListener(tab, reply_message, 1, false);
+ browser->GoBack(CURRENT_TAB);
+ return;
+ }
+ }
+
+ AutomationMsg_GoBack::WriteReplyParams(
+ reply_message, AUTOMATION_MSG_NAVIGATION_ERROR);
+ Send(reply_message);
+}
+
+void AutomationProvider::GoForward(int handle, IPC::Message* reply_message) {
+ if (tab_tracker_->ContainsHandle(handle)) {
+ NavigationController* tab = tab_tracker_->GetResource(handle);
+ Browser* browser = FindAndActivateTab(tab);
+ if (browser && browser->command_updater()->IsCommandEnabled(IDC_FORWARD)) {
+ AddNavigationStatusListener(tab, reply_message, 1, false);
+ browser->GoForward(CURRENT_TAB);
+ return;
+ }
+ }
+
+ AutomationMsg_GoForward::WriteReplyParams(
+ reply_message, AUTOMATION_MSG_NAVIGATION_ERROR);
+ Send(reply_message);
+}
+
+void AutomationProvider::Reload(int handle, IPC::Message* reply_message) {
+ if (tab_tracker_->ContainsHandle(handle)) {
+ NavigationController* tab = tab_tracker_->GetResource(handle);
+ Browser* browser = FindAndActivateTab(tab);
+ if (browser && browser->command_updater()->IsCommandEnabled(IDC_RELOAD)) {
+ AddNavigationStatusListener(tab, reply_message, 1, false);
+ browser->Reload(CURRENT_TAB);
+ return;
+ }
+ }
+
+ AutomationMsg_Reload::WriteReplyParams(
+ reply_message, AUTOMATION_MSG_NAVIGATION_ERROR);
+ Send(reply_message);
+}
+
+void AutomationProvider::SetAuth(int tab_handle,
+ const std::wstring& username,
+ const std::wstring& password,
+ IPC::Message* reply_message) {
+ if (tab_tracker_->ContainsHandle(tab_handle)) {
+ NavigationController* tab = tab_tracker_->GetResource(tab_handle);
+ LoginHandlerMap::iterator iter = login_handler_map_.find(tab);
+
+ if (iter != login_handler_map_.end()) {
+ // If auth is needed again after this, assume login has failed. This is
+ // not strictly correct, because a navigation can require both proxy and
+ // server auth, but it should be OK for now.
+ LoginHandler* handler = iter->second;
+ AddNavigationStatusListener(tab, reply_message, 1, false);
+ handler->SetAuth(username, password);
+ return;
+ }
+ }
+
+ AutomationMsg_SetAuth::WriteReplyParams(
+ reply_message, AUTOMATION_MSG_NAVIGATION_AUTH_NEEDED);
+ Send(reply_message);
+}
+
+void AutomationProvider::CancelAuth(int tab_handle,
+ IPC::Message* reply_message) {
+ if (tab_tracker_->ContainsHandle(tab_handle)) {
+ NavigationController* tab = tab_tracker_->GetResource(tab_handle);
+ LoginHandlerMap::iterator iter = login_handler_map_.find(tab);
+
+ if (iter != login_handler_map_.end()) {
+ // If auth is needed again after this, something is screwy.
+ LoginHandler* handler = iter->second;
+ AddNavigationStatusListener(tab, reply_message, 1, false);
+ handler->CancelAuth();
+ return;
+ }
+ }
+
+ AutomationMsg_CancelAuth::WriteReplyParams(
+ reply_message, AUTOMATION_MSG_NAVIGATION_AUTH_NEEDED);
+ Send(reply_message);
+}
+
+void AutomationProvider::NeedsAuth(int tab_handle, bool* needs_auth) {
+ *needs_auth = false;
+
+ if (tab_tracker_->ContainsHandle(tab_handle)) {
+ NavigationController* tab = tab_tracker_->GetResource(tab_handle);
+ LoginHandlerMap::iterator iter = login_handler_map_.find(tab);
+
+ if (iter != login_handler_map_.end()) {
+ // The LoginHandler will be in our map IFF the tab needs auth.
+ *needs_auth = true;
+ }
+ }
+}
+
+void AutomationProvider::GetRedirectsFrom(int tab_handle,
+ const GURL& source_url,
+ IPC::Message* reply_message) {
+ DCHECK(!redirect_query_) << "Can only handle one redirect query at once.";
+ if (tab_tracker_->ContainsHandle(tab_handle)) {
+ NavigationController* tab = tab_tracker_->GetResource(tab_handle);
+ HistoryService* history_service =
+ tab->profile()->GetHistoryService(Profile::EXPLICIT_ACCESS);
+
+ DCHECK(history_service) << "Tab " << tab_handle << "'s profile " <<
+ "has no history service";
+ if (history_service) {
+ DCHECK(reply_message_ == NULL);
+ reply_message_ = reply_message;
+ // Schedule a history query for redirects. The response will be sent
+ // asynchronously from the callback the history system uses to notify us
+ // that it's done: OnRedirectQueryComplete.
+ redirect_query_ = history_service->QueryRedirectsFrom(
+ source_url, &consumer_,
+ NewCallback(this, &AutomationProvider::OnRedirectQueryComplete));
+ return; // Response will be sent when query completes.
+ }
+ }
+
+ // Send failure response.
+ std::vector<GURL> empty;
+ AutomationMsg_RedirectsFrom::WriteReplyParams(reply_message, false, empty);
+ Send(reply_message);
+}
+
+void AutomationProvider::GetActiveTabIndex(int handle, int* active_tab_index) {
+ *active_tab_index = -1; // -1 is the error code
+ if (browser_tracker_->ContainsHandle(handle)) {
+ Browser* browser = browser_tracker_->GetResource(handle);
+ *active_tab_index = browser->selected_index();
+ }
+}
+
+void AutomationProvider::GetBrowserLocale(string16* locale) {
+ DCHECK(g_browser_process);
+ *locale = ASCIIToUTF16(g_browser_process->GetApplicationLocale());
+}
+
+void AutomationProvider::GetBrowserWindowCount(int* window_count) {
+ *window_count = static_cast<int>(BrowserList::size());
+}
+
+void AutomationProvider::GetNormalBrowserWindowCount(int* window_count) {
+ *window_count = static_cast<int>(
+ BrowserList::GetBrowserCountForType(profile_, Browser::TYPE_NORMAL));
+}
+
+void AutomationProvider::GetShowingAppModalDialog(bool* showing_dialog,
+ int* dialog_button) {
+ AppModalDialog* dialog_delegate =
+ Singleton<AppModalDialogQueue>()->active_dialog();
+ *showing_dialog = (dialog_delegate != NULL);
+ if (*showing_dialog)
+ *dialog_button = dialog_delegate->GetDialogButtons();
+ else
+ *dialog_button = MessageBoxFlags::DIALOGBUTTON_NONE;
+}
+
+void AutomationProvider::ClickAppModalDialogButton(int button, bool* success) {
+ *success = false;
+
+ AppModalDialog* dialog_delegate =
+ Singleton<AppModalDialogQueue>()->active_dialog();
+ if (dialog_delegate &&
+ (dialog_delegate->GetDialogButtons() & button) == button) {
+ if ((button & MessageBoxFlags::DIALOGBUTTON_OK) ==
+ MessageBoxFlags::DIALOGBUTTON_OK) {
+ dialog_delegate->AcceptWindow();
+ *success = true;
+ }
+ if ((button & MessageBoxFlags::DIALOGBUTTON_CANCEL) ==
+ MessageBoxFlags::DIALOGBUTTON_CANCEL) {
+ DCHECK(!*success) << "invalid param, OK and CANCEL specified";
+ dialog_delegate->CancelWindow();
+ *success = true;
+ }
+ }
+}
+
+void AutomationProvider::ShutdownSessionService(int handle, bool* result) {
+ if (browser_tracker_->ContainsHandle(handle)) {
+ Browser* browser = browser_tracker_->GetResource(handle);
+ browser->profile()->ShutdownSessionService();
+ *result = true;
+ } else {
+ *result = false;
+ }
+}
+
+void AutomationProvider::GetBrowserWindow(int index, int* handle) {
+ *handle = 0;
+ if (index >= 0) {
+ BrowserList::const_iterator iter = BrowserList::begin();
+ for (; (iter != BrowserList::end()) && (index > 0); ++iter, --index) {}
+ if (iter != BrowserList::end()) {
+ *handle = browser_tracker_->Add(*iter);
+ }
+ }
+}
+
+void AutomationProvider::FindNormalBrowserWindow(int* handle) {
+ *handle = 0;
+ Browser* browser = BrowserList::FindBrowserWithType(profile_,
+ Browser::TYPE_NORMAL,
+ false);
+ if (browser)
+ *handle = browser_tracker_->Add(browser);
+}
+
+void AutomationProvider::GetLastActiveBrowserWindow(int* handle) {
+ *handle = 0;
+ Browser* browser = BrowserList::GetLastActive();
+ if (browser)
+ *handle = browser_tracker_->Add(browser);
+}
+
+#if defined(OS_POSIX)
+// TODO(estade): use this implementation for all platforms?
+void AutomationProvider::GetActiveWindow(int* handle) {
+ gfx::NativeWindow window =
+ BrowserList::GetLastActive()->window()->GetNativeHandle();
+ *handle = window_tracker_->Add(window);
+}
+#endif
+
+void AutomationProvider::ExecuteBrowserCommandAsync(int handle, int command,
+ bool* success) {
+ *success = false;
+ if (browser_tracker_->ContainsHandle(handle)) {
+ Browser* browser = browser_tracker_->GetResource(handle);
+ if (browser->command_updater()->SupportsCommand(command) &&
+ browser->command_updater()->IsCommandEnabled(command)) {
+ browser->ExecuteCommand(command);
+ *success = true;
+ }
+ }
+}
+
+void AutomationProvider::ExecuteBrowserCommand(
+ int handle, int command, IPC::Message* reply_message) {
+ // List of commands which just finish synchronously and don't require
+ // setting up an observer.
+ static const int kSynchronousCommands[] = {
+ IDC_HOME,
+ IDC_SELECT_NEXT_TAB,
+ IDC_SELECT_PREVIOUS_TAB,
+ IDC_SHOW_BOOKMARK_MANAGER,
+ };
+ if (browser_tracker_->ContainsHandle(handle)) {
+ Browser* browser = browser_tracker_->GetResource(handle);
+ if (browser->command_updater()->SupportsCommand(command) &&
+ browser->command_updater()->IsCommandEnabled(command)) {
+ // First check if we can handle the command without using an observer.
+ for (size_t i = 0; i < arraysize(kSynchronousCommands); i++) {
+ if (command == kSynchronousCommands[i]) {
+ browser->ExecuteCommand(command);
+ AutomationMsg_WindowExecuteCommand::WriteReplyParams(reply_message,
+ true);
+ Send(reply_message);
+ return;
+ }
+ }
+
+ // Use an observer if we have one, otherwise fail.
+ if (ExecuteBrowserCommandObserver::CreateAndRegisterObserver(
+ this, browser, command, reply_message)) {
+ browser->ExecuteCommand(command);
+ return;
+ }
+ }
+ }
+ AutomationMsg_WindowExecuteCommand::WriteReplyParams(reply_message, false);
+ Send(reply_message);
+}
+
+// This task just adds another task to the event queue. This is useful if
+// you want to ensure that any tasks added to the event queue after this one
+// have already been processed by the time |task| is run.
+class InvokeTaskLaterTask : public Task {
+ public:
+ explicit InvokeTaskLaterTask(Task* task) : task_(task) {}
+ virtual ~InvokeTaskLaterTask() {}
+
+ virtual void Run() {
+ MessageLoop::current()->PostTask(FROM_HERE, task_);
+ }
+
+ private:
+ Task* task_;
+
+ DISALLOW_COPY_AND_ASSIGN(InvokeTaskLaterTask);
+};
+
+void AutomationProvider::WindowSimulateClick(const IPC::Message& message,
+ int handle,
+ const gfx::Point& click,
+ int flags) {
+ if (window_tracker_->ContainsHandle(handle)) {
+ ui_controls::SendMouseMoveNotifyWhenDone(click.x(), click.y(),
+ new ClickTask(flags));
+ }
+}
+
+void AutomationProvider::WindowSimulateMouseMove(const IPC::Message& message,
+ int handle,
+ const gfx::Point& location) {
+ if (window_tracker_->ContainsHandle(handle))
+ ui_controls::SendMouseMove(location.x(), location.y());
+}
+
+void AutomationProvider::WindowSimulateKeyPress(const IPC::Message& message,
+ int handle,
+ int key,
+ int flags) {
+ if (!window_tracker_->ContainsHandle(handle))
+ return;
+
+ gfx::NativeWindow window = window_tracker_->GetResource(handle);
+ // The key event is sent to whatever window is active.
+ ui_controls::SendKeyPress(window, static_cast<base::KeyboardCode>(key),
+ ((flags & views::Event::EF_CONTROL_DOWN) ==
+ views::Event::EF_CONTROL_DOWN),
+ ((flags & views::Event::EF_SHIFT_DOWN) ==
+ views::Event::EF_SHIFT_DOWN),
+ ((flags & views::Event::EF_ALT_DOWN) ==
+ views::Event::EF_ALT_DOWN),
+ ((flags & views::Event::EF_COMMAND_DOWN) ==
+ views::Event::EF_COMMAND_DOWN));
+}
+
+void AutomationProvider::IsWindowActive(int handle, bool* success,
+ bool* is_active) {
+ if (window_tracker_->ContainsHandle(handle)) {
+ *is_active =
+ platform_util::IsWindowActive(window_tracker_->GetResource(handle));
+ *success = true;
+ } else {
+ *success = false;
+ *is_active = false;
+ }
+}
+
+void AutomationProvider::GetTabCount(int handle, int* tab_count) {
+ *tab_count = -1; // -1 is the error code
+
+ if (browser_tracker_->ContainsHandle(handle)) {
+ Browser* browser = browser_tracker_->GetResource(handle);
+ *tab_count = browser->tab_count();
+ }
+}
+
+void AutomationProvider::GetType(int handle, int* type_as_int) {
+ *type_as_int = -1; // -1 is the error code
+
+ if (browser_tracker_->ContainsHandle(handle)) {
+ Browser* browser = browser_tracker_->GetResource(handle);
+ *type_as_int = static_cast<int>(browser->type());
+ }
+}
+
+void AutomationProvider::GetTab(int win_handle, int tab_index,
+ int* tab_handle) {
+ *tab_handle = 0;
+ if (browser_tracker_->ContainsHandle(win_handle) && (tab_index >= 0)) {
+ Browser* browser = browser_tracker_->GetResource(win_handle);
+ if (tab_index < browser->tab_count()) {
+ TabContents* tab_contents =
+ browser->GetTabContentsAt(tab_index);
+ *tab_handle = tab_tracker_->Add(&tab_contents->controller());
+ }
+ }
+}
+
+void AutomationProvider::GetTabTitle(int handle, int* title_string_size,
+ std::wstring* title) {
+ *title_string_size = -1; // -1 is the error code
+ if (tab_tracker_->ContainsHandle(handle)) {
+ NavigationController* tab = tab_tracker_->GetResource(handle);
+ NavigationEntry* entry = tab->GetActiveEntry();
+ if (entry != NULL) {
+ *title = UTF16ToWideHack(entry->title());
+ } else {
+ *title = std::wstring();
+ }
+ *title_string_size = static_cast<int>(title->size());
+ }
+}
+
+void AutomationProvider::GetTabIndex(int handle, int* tabstrip_index) {
+ *tabstrip_index = -1; // -1 is the error code
+
+ if (tab_tracker_->ContainsHandle(handle)) {
+ NavigationController* tab = tab_tracker_->GetResource(handle);
+ Browser* browser = Browser::GetBrowserForController(tab, NULL);
+ *tabstrip_index = browser->tabstrip_model()->GetIndexOfController(tab);
+ }
+}
+
+void AutomationProvider::HandleUnused(const IPC::Message& message, int handle) {
+ if (window_tracker_->ContainsHandle(handle)) {
+ window_tracker_->Remove(window_tracker_->GetResource(handle));
+ }
+}
+
+void AutomationProvider::OnChannelError() {
+ LOG(INFO) << "AutomationProxy went away, shutting down app.";
+ AutomationProviderList::GetInstance()->RemoveProvider(this);
+}
+
+// TODO(brettw) change this to accept GURLs when history supports it
+void AutomationProvider::OnRedirectQueryComplete(
+ HistoryService::Handle request_handle,
+ GURL from_url,
+ bool success,
+ history::RedirectList* redirects) {
+ DCHECK(request_handle == redirect_query_);
+ DCHECK(reply_message_ != NULL);
+
+ std::vector<GURL> redirects_gurl;
+ reply_message_->WriteBool(success);
+ if (success) {
+ for (size_t i = 0; i < redirects->size(); i++)
+ redirects_gurl.push_back(redirects->at(i));
+ }
+
+ IPC::ParamTraits<std::vector<GURL> >::Write(reply_message_, redirects_gurl);
+
+ Send(reply_message_);
+ redirect_query_ = 0;
+ reply_message_ = NULL;
+}
+
+bool AutomationProvider::Send(IPC::Message* msg) {
+ DCHECK(channel_.get());
+ return channel_->Send(msg);
+}
+
+Browser* AutomationProvider::FindAndActivateTab(
+ NavigationController* controller) {
+ int tab_index;
+ Browser* browser = Browser::GetBrowserForController(controller, &tab_index);
+ if (browser)
+ browser->SelectTabContentsAt(tab_index, true);
+
+ return browser;
+}
+
+namespace {
+
+class GetCookiesTask : public Task {
+ public:
+ GetCookiesTask(const GURL& url,
+ URLRequestContextGetter* context_getter,
+ base::WaitableEvent* event,
+ std::string* cookies)
+ : url_(url),
+ context_getter_(context_getter),
+ event_(event),
+ cookies_(cookies) {}
+
+ virtual void Run() {
+ *cookies_ = context_getter_->GetCookieStore()->GetCookies(url_);
+ event_->Signal();
+ }
+
+ private:
+ const GURL& url_;
+ URLRequestContextGetter* const context_getter_;
+ base::WaitableEvent* const event_;
+ std::string* const cookies_;
+
+ DISALLOW_COPY_AND_ASSIGN(GetCookiesTask);
+};
+
+std::string GetCookiesForURL(
+ const GURL& url,
+ URLRequestContextGetter* context_getter) {
+ std::string cookies;
+ base::WaitableEvent event(true /* manual reset */,
+ false /* not initially signaled */);
+ CHECK(ChromeThread::PostTask(
+ ChromeThread::IO, FROM_HERE,
+ new GetCookiesTask(url, context_getter, &event, &cookies)));
+ event.Wait();
+ return cookies;
+}
+
+class SetCookieTask : public Task {
+ public:
+ SetCookieTask(const GURL& url,
+ const std::string& value,
+ URLRequestContextGetter* context_getter,
+ base::WaitableEvent* event,
+ bool* rv)
+ : url_(url),
+ value_(value),
+ context_getter_(context_getter),
+ event_(event),
+ rv_(rv) {}
+
+ virtual void Run() {
+ *rv_ = context_getter_->GetCookieStore()->SetCookie(url_, value_);
+ event_->Signal();
+ }
+
+ private:
+ const GURL& url_;
+ const std::string& value_;
+ URLRequestContextGetter* const context_getter_;
+ base::WaitableEvent* const event_;
+ bool* const rv_;
+
+ DISALLOW_COPY_AND_ASSIGN(SetCookieTask);
+};
+
+bool SetCookieForURL(
+ const GURL& url,
+ const std::string& value,
+ URLRequestContextGetter* context_getter) {
+ base::WaitableEvent event(true /* manual reset */,
+ false /* not initially signaled */);
+ bool rv = false;
+ CHECK(ChromeThread::PostTask(
+ ChromeThread::IO, FROM_HERE,
+ new SetCookieTask(url, value, context_getter, &event, &rv)));
+ event.Wait();
+ return rv;
+}
+
+class DeleteCookieTask : public Task {
+ public:
+ DeleteCookieTask(const GURL& url,
+ const std::string& name,
+ const scoped_refptr<URLRequestContextGetter>& context_getter)
+ : url_(url),
+ name_(name),
+ context_getter_(context_getter) {}
+
+ virtual void Run() {
+ net::CookieStore* cookie_store = context_getter_->GetCookieStore();
+ cookie_store->DeleteCookie(url_, name_);
+ }
+
+ private:
+ const GURL url_;
+ const std::string name_;
+ const scoped_refptr<URLRequestContextGetter> context_getter_;
+
+ DISALLOW_COPY_AND_ASSIGN(DeleteCookieTask);
+};
+
+} // namespace
+
+void AutomationProvider::GetCookies(const GURL& url, int handle,
+ int* value_size,
+ std::string* value) {
+ *value_size = -1;
+ if (url.is_valid() && tab_tracker_->ContainsHandle(handle)) {
+ NavigationController* tab = tab_tracker_->GetResource(handle);
+
+ // Since we are running on the UI thread don't call GetURLRequestContext().
+ scoped_refptr<URLRequestContextGetter> request_context =
+ tab->tab_contents()->request_context();
+ if (!request_context.get())
+ request_context = tab->profile()->GetRequestContext();
+
+ *value = GetCookiesForURL(url, request_context.get());
+ *value_size = static_cast<int>(value->size());
+ }
+}
+
+void AutomationProvider::SetCookie(const GURL& url,
+ const std::string value,
+ int handle,
+ int* response_value) {
+ *response_value = -1;
+
+ if (url.is_valid() && tab_tracker_->ContainsHandle(handle)) {
+ NavigationController* tab = tab_tracker_->GetResource(handle);
+
+ scoped_refptr<URLRequestContextGetter> request_context =
+ tab->tab_contents()->request_context();
+ if (!request_context.get())
+ request_context = tab->profile()->GetRequestContext();
+
+ if (SetCookieForURL(url, value, request_context.get()))
+ *response_value = 1;
+ }
+}
+
+void AutomationProvider::DeleteCookie(const GURL& url,
+ const std::string& cookie_name,
+ int handle, bool* success) {
+ *success = false;
+ if (url.is_valid() && tab_tracker_->ContainsHandle(handle)) {
+ NavigationController* tab = tab_tracker_->GetResource(handle);
+ ChromeThread::PostTask(
+ ChromeThread::IO, FROM_HERE,
+ new DeleteCookieTask(url, cookie_name,
+ tab->profile()->GetRequestContext()));
+ *success = true;
+ }
+}
+
+void AutomationProvider::ShowCollectedCookiesDialog(
+ int handle, bool* success) {
+ *success = false;
+ if (tab_tracker_->ContainsHandle(handle)) {
+ TabContents* tab_contents =
+ tab_tracker_->GetResource(handle)->tab_contents();
+ tab_contents->delegate()->ShowCollectedCookiesDialog(tab_contents);
+ *success = true;
+ }
+}
+
+void AutomationProvider::GetTabURL(int handle, bool* success, GURL* url) {
+ *success = false;
+ if (tab_tracker_->ContainsHandle(handle)) {
+ NavigationController* tab = tab_tracker_->GetResource(handle);
+ // Return what the user would see in the location bar.
+ *url = tab->GetActiveEntry()->virtual_url();
+ *success = true;
+ }
+}
+
+void AutomationProvider::GetTabProcessID(int handle, int* process_id) {
+ *process_id = -1;
+
+ if (tab_tracker_->ContainsHandle(handle)) {
+ *process_id = 0;
+ TabContents* tab_contents =
+ tab_tracker_->GetResource(handle)->tab_contents();
+ RenderProcessHost* rph = tab_contents->GetRenderProcessHost();
+ if (rph)
+ *process_id = base::GetProcId(rph->GetHandle());
+ }
+}
+
+void AutomationProvider::ApplyAccelerator(int handle, int id) {
+ NOTREACHED() << "This function has been deprecated. "
+ << "Please use ExecuteBrowserCommandAsync instead.";
+}
+
+void AutomationProvider::ExecuteJavascript(int handle,
+ const std::wstring& frame_xpath,
+ const std::wstring& script,
+ IPC::Message* reply_message) {
+ bool succeeded = false;
+ TabContents* tab_contents = GetTabContentsForHandle(handle, NULL);
+ if (tab_contents) {
+ // Set the routing id of this message with the controller.
+ // This routing id needs to be remembered for the reverse
+ // communication while sending back the response of
+ // this javascript execution.
+ std::wstring set_automation_id;
+ SStringPrintf(&set_automation_id,
+ L"window.domAutomationController.setAutomationId(%d);",
+ reply_message->routing_id());
+
+ DCHECK(reply_message_ == NULL);
+ reply_message_ = reply_message;
+
+ tab_contents->render_view_host()->ExecuteJavascriptInWebFrame(
+ frame_xpath, set_automation_id);
+ tab_contents->render_view_host()->ExecuteJavascriptInWebFrame(
+ frame_xpath, script);
+ succeeded = true;
+ }
+
+ if (!succeeded) {
+ AutomationMsg_DomOperation::WriteReplyParams(reply_message, std::string());
+ Send(reply_message);
+ }
+}
+
+void AutomationProvider::GetShelfVisibility(int handle, bool* visible) {
+ *visible = false;
+
+ if (browser_tracker_->ContainsHandle(handle)) {
+#if defined(OS_CHROMEOS)
+ // Chromium OS shows FileBrowse ui rather than download shelf. So we
+ // enumerate all browsers and look for a chrome://filebrowse... pop up.
+ for (BrowserList::const_iterator it = BrowserList::begin();
+ it != BrowserList::end(); ++it) {
+ if ((*it)->type() == Browser::TYPE_POPUP) {
+ const GURL& url =
+ (*it)->GetTabContentsAt((*it)->selected_index())->GetURL();
+
+ if (url.SchemeIs(chrome::kChromeUIScheme) &&
+ url.host() == chrome::kChromeUIFileBrowseHost) {
+ *visible = true;
+ break;
+ }
+ }
+ }
+#else
+ Browser* browser = browser_tracker_->GetResource(handle);
+ if (browser) {
+ *visible = browser->window()->IsDownloadShelfVisible();
+ }
+#endif
+ }
+}
+
+void AutomationProvider::SetShelfVisibility(int handle, bool visible) {
+ if (browser_tracker_->ContainsHandle(handle)) {
+ Browser* browser = browser_tracker_->GetResource(handle);
+ if (browser) {
+ if (visible)
+ browser->window()->GetDownloadShelf()->Show();
+ else
+ browser->window()->GetDownloadShelf()->Close();
+ }
+ }
+}
+
+void AutomationProvider::IsFullscreen(int handle, bool* visible) {
+ *visible = false;
+
+ if (browser_tracker_->ContainsHandle(handle)) {
+ Browser* browser = browser_tracker_->GetResource(handle);
+ if (browser)
+ *visible = browser->window()->IsFullscreen();
+ }
+}
+
+void AutomationProvider::GetFullscreenBubbleVisibility(int handle,
+ bool* visible) {
+ *visible = false;
+
+ if (browser_tracker_->ContainsHandle(handle)) {
+ Browser* browser = browser_tracker_->GetResource(handle);
+ if (browser)
+ *visible = browser->window()->IsFullscreenBubbleVisible();
+ }
+}
+
+void AutomationProvider::GetConstrainedWindowCount(int handle, int* count) {
+ *count = -1; // -1 is the error code
+ if (tab_tracker_->ContainsHandle(handle)) {
+ NavigationController* nav_controller = tab_tracker_->GetResource(handle);
+ TabContents* tab_contents = nav_controller->tab_contents();
+ if (tab_contents) {
+ *count = static_cast<int>(tab_contents->child_windows_.size());
+ }
+ }
+}
+
+void AutomationProvider::HandleFindInPageRequest(
+ int handle, const std::wstring& find_request,
+ int forward, int match_case, int* active_ordinal, int* matches_found) {
+ NOTREACHED() << "This function has been deprecated."
+ << "Please use HandleFindRequest instead.";
+ *matches_found = -1;
+ return;
+}
+
+void AutomationProvider::HandleFindRequest(
+ int handle,
+ const AutomationMsg_Find_Params& params,
+ IPC::Message* reply_message) {
+ if (!tab_tracker_->ContainsHandle(handle)) {
+ AutomationMsg_FindInPage::WriteReplyParams(reply_message, -1, -1);
+ Send(reply_message);
+ return;
+ }
+
+ NavigationController* nav = tab_tracker_->GetResource(handle);
+ TabContents* tab_contents = nav->tab_contents();
+
+ find_in_page_observer_.reset(new
+ FindInPageNotificationObserver(this, tab_contents, reply_message));
+
+ tab_contents->set_current_find_request_id(
+ FindInPageNotificationObserver::kFindInPageRequestId);
+ tab_contents->render_view_host()->StartFinding(
+ FindInPageNotificationObserver::kFindInPageRequestId,
+ params.search_string, params.forward, params.match_case,
+ params.find_next);
+}
+
+void AutomationProvider::HandleOpenFindInPageRequest(
+ const IPC::Message& message, int handle) {
+ if (browser_tracker_->ContainsHandle(handle)) {
+ Browser* browser = browser_tracker_->GetResource(handle);
+ browser->FindInPage(false, false);
+ }
+}
+
+void AutomationProvider::GetFindWindowVisibility(int handle, bool* visible) {
+ *visible = false;
+ Browser* browser = browser_tracker_->GetResource(handle);
+ if (browser) {
+ FindBarTesting* find_bar =
+ browser->GetFindBarController()->find_bar()->GetFindBarTesting();
+ find_bar->GetFindBarWindowInfo(NULL, visible);
+ }
+}
+
+void AutomationProvider::HandleFindWindowLocationRequest(int handle, int* x,
+ int* y) {
+ gfx::Point position(0, 0);
+ bool visible = false;
+ if (browser_tracker_->ContainsHandle(handle)) {
+ Browser* browser = browser_tracker_->GetResource(handle);
+ FindBarTesting* find_bar =
+ browser->GetFindBarController()->find_bar()->GetFindBarTesting();
+ find_bar->GetFindBarWindowInfo(&position, &visible);
+ }
+
+ *x = position.x();
+ *y = position.y();
+}
+
+// Bookmark bar visibility is based on the pref (e.g. is it in the toolbar).
+// Presence in the NTP is NOT considered visible by this call.
+void AutomationProvider::GetBookmarkBarVisibility(int handle,
+ bool* visible,
+ bool* animating) {
+ *visible = false;
+ *animating = false;
+
+ if (browser_tracker_->ContainsHandle(handle)) {
+ Browser* browser = browser_tracker_->GetResource(handle);
+ if (browser) {
+#if 0 // defined(TOOLKIT_VIEWS) && defined(OS_LINUX)
+ // TODO(jrg): Was removed in rev43789 for perf. Need to investigate.
+
+ // IsBookmarkBarVisible() line looks correct but is not
+ // consistent across platforms. Specifically, on Mac/Linux, it
+ // returns false if the bar is hidden in a pref (even if visible
+ // on the NTP). On ChromeOS, it returned true if on NTP
+ // independent of the pref. Making the code more consistent
+ // caused a perf bot regression on Windows (which shares views).
+ // See http://crbug.com/40225
+ *visible = browser->profile()->GetPrefs()->GetBoolean(
+ prefs::kShowBookmarkBar);
+#else
+ *visible = browser->window()->IsBookmarkBarVisible();
+#endif
+ *animating = browser->window()->IsBookmarkBarAnimating();
+ }
+ }
+}
+
+void AutomationProvider::GetBookmarksAsJSON(int handle,
+ std::string* bookmarks_as_json,
+ bool *success) {
+ *success = false;
+ if (browser_tracker_->ContainsHandle(handle)) {
+ Browser* browser = browser_tracker_->GetResource(handle);
+ if (browser) {
+ if (!browser->profile()->GetBookmarkModel()->IsLoaded()) {
+ return;
+ }
+ scoped_refptr<BookmarkStorage> storage = new BookmarkStorage(
+ browser->profile(),
+ browser->profile()->GetBookmarkModel());
+ *success = storage->SerializeData(bookmarks_as_json);
+ }
+ }
+}
+
+void AutomationProvider::WaitForBookmarkModelToLoad(
+ int handle,
+ IPC::Message* reply_message) {
+ if (browser_tracker_->ContainsHandle(handle)) {
+ Browser* browser = browser_tracker_->GetResource(handle);
+ BookmarkModel* model = browser->profile()->GetBookmarkModel();
+ if (model->IsLoaded()) {
+ AutomationMsg_WaitForBookmarkModelToLoad::WriteReplyParams(
+ reply_message, true);
+ Send(reply_message);
+ } else {
+ // The observer will delete itself when done.
+ new AutomationProviderBookmarkModelObserver(this, reply_message,
+ model);
+ }
+ }
+}
+
+void AutomationProvider::AddBookmarkGroup(int handle,
+ int64 parent_id, int index,
+ std::wstring title,
+ bool* success) {
+ if (browser_tracker_->ContainsHandle(handle)) {
+ Browser* browser = browser_tracker_->GetResource(handle);
+ if (browser) {
+ BookmarkModel* model = browser->profile()->GetBookmarkModel();
+ if (!model->IsLoaded()) {
+ *success = false;
+ return;
+ }
+ const BookmarkNode* parent = model->GetNodeByID(parent_id);
+ DCHECK(parent);
+ if (parent) {
+ const BookmarkNode* child = model->AddGroup(parent, index,
+ WideToUTF16(title));
+ DCHECK(child);
+ if (child)
+ *success = true;
+ }
+ }
+ }
+ *success = false;
+}
+
+void AutomationProvider::AddBookmarkURL(int handle,
+ int64 parent_id, int index,
+ std::wstring title, const GURL& url,
+ bool* success) {
+ if (browser_tracker_->ContainsHandle(handle)) {
+ Browser* browser = browser_tracker_->GetResource(handle);
+ if (browser) {
+ BookmarkModel* model = browser->profile()->GetBookmarkModel();
+ if (!model->IsLoaded()) {
+ *success = false;
+ return;
+ }
+ const BookmarkNode* parent = model->GetNodeByID(parent_id);
+ DCHECK(parent);
+ if (parent) {
+ const BookmarkNode* child = model->AddURL(parent, index,
+ WideToUTF16(title), url);
+ DCHECK(child);
+ if (child)
+ *success = true;
+ }
+ }
+ }
+ *success = false;
+}
+
+void AutomationProvider::ReparentBookmark(int handle,
+ int64 id, int64 new_parent_id,
+ int index,
+ bool* success) {
+ if (browser_tracker_->ContainsHandle(handle)) {
+ Browser* browser = browser_tracker_->GetResource(handle);
+ if (browser) {
+ BookmarkModel* model = browser->profile()->GetBookmarkModel();
+ if (!model->IsLoaded()) {
+ *success = false;
+ return;
+ }
+ const BookmarkNode* node = model->GetNodeByID(id);
+ DCHECK(node);
+ const BookmarkNode* new_parent = model->GetNodeByID(new_parent_id);
+ DCHECK(new_parent);
+ if (node && new_parent) {
+ model->Move(node, new_parent, index);
+ *success = true;
+ }
+ }
+ }
+ *success = false;
+}
+
+void AutomationProvider::SetBookmarkTitle(int handle,
+ int64 id, std::wstring title,
+ bool* success) {
+ if (browser_tracker_->ContainsHandle(handle)) {
+ Browser* browser = browser_tracker_->GetResource(handle);
+ if (browser) {
+ BookmarkModel* model = browser->profile()->GetBookmarkModel();
+ if (!model->IsLoaded()) {
+ *success = false;
+ return;
+ }
+ const BookmarkNode* node = model->GetNodeByID(id);
+ DCHECK(node);
+ if (node) {
+ model->SetTitle(node, WideToUTF16(title));
+ *success = true;
+ }
+ }
+ }
+ *success = false;
+}
+
+void AutomationProvider::SetBookmarkURL(int handle,
+ int64 id, const GURL& url,
+ bool* success) {
+ if (browser_tracker_->ContainsHandle(handle)) {
+ Browser* browser = browser_tracker_->GetResource(handle);
+ if (browser) {
+ BookmarkModel* model = browser->profile()->GetBookmarkModel();
+ if (!model->IsLoaded()) {
+ *success = false;
+ return;
+ }
+ const BookmarkNode* node = model->GetNodeByID(id);
+ DCHECK(node);
+ if (node) {
+ model->SetURL(node, url);
+ *success = true;
+ }
+ }
+ }
+ *success = false;
+}
+
+void AutomationProvider::RemoveBookmark(int handle,
+ int64 id,
+ bool* success) {
+ if (browser_tracker_->ContainsHandle(handle)) {
+ Browser* browser = browser_tracker_->GetResource(handle);
+ if (browser) {
+ BookmarkModel* model = browser->profile()->GetBookmarkModel();
+ if (!model->IsLoaded()) {
+ *success = false;
+ return;
+ }
+ const BookmarkNode* node = model->GetNodeByID(id);
+ DCHECK(node);
+ if (node) {
+ const BookmarkNode* parent = node->GetParent();
+ DCHECK(parent);
+ model->Remove(parent, parent->IndexOfChild(node));
+ *success = true;
+ }
+ }
+ }
+ *success = false;
+}
+
+// Sample json input: { "command": "SetWindowDimensions",
+// "x": 20, # optional
+// "y": 20, # optional
+// "width": 800, # optional
+// "height": 600 } # optional
+void AutomationProvider::SetWindowDimensions(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message) {
+ gfx::Rect rect = browser->window()->GetRestoredBounds();
+ int x, y, width, height;
+ if (args->GetInteger(L"x", &x))
+ rect.set_x(x);
+ if (args->GetInteger(L"y", &y))
+ rect.set_y(y);
+ if (args->GetInteger(L"width", &width))
+ rect.set_width(width);
+ if (args->GetInteger(L"height", &height))
+ rect.set_height(height);
+ browser->window()->SetBounds(rect);
+ AutomationJSONReply(this, reply_message).SendSuccess(NULL);
+}
+
+ListValue* AutomationProvider::GetInfobarsInfo(TabContents* tc) {
+ // Each infobar may have different properties depending on the type.
+ ListValue* infobars = new ListValue;
+ for (int infobar_index = 0;
+ infobar_index < tc->infobar_delegate_count();
+ ++infobar_index) {
+ DictionaryValue* infobar_item = new DictionaryValue;
+ InfoBarDelegate* infobar = tc->GetInfoBarDelegateAt(infobar_index);
+ if (infobar->AsConfirmInfoBarDelegate()) {
+ // Also covers ThemeInstalledInfoBarDelegate and
+ // CrashedExtensionInfoBarDelegate.
+ infobar_item->SetString(L"type", "confirm_infobar");
+ ConfirmInfoBarDelegate* confirm_infobar =
+ infobar->AsConfirmInfoBarDelegate();
+ infobar_item->SetString(L"text", confirm_infobar->GetMessageText());
+ infobar_item->SetString(L"link_text", confirm_infobar->GetLinkText());
+ ListValue* buttons_list = new ListValue;
+ int buttons = confirm_infobar->GetButtons();
+ if (ConfirmInfoBarDelegate::BUTTON_OK & buttons) {
+ StringValue* button_label = new StringValue(
+ confirm_infobar->GetButtonLabel(
+ ConfirmInfoBarDelegate::BUTTON_OK));
+ buttons_list->Append(button_label);
+ }
+ if (ConfirmInfoBarDelegate::BUTTON_CANCEL & buttons) {
+ StringValue* button_label = new StringValue(
+ confirm_infobar->GetButtonLabel(
+ ConfirmInfoBarDelegate::BUTTON_CANCEL));
+ buttons_list->Append(button_label);
+ }
+ infobar_item->Set(L"buttons", buttons_list);
+ } else if (infobar->AsAlertInfoBarDelegate()) {
+ infobar_item->SetString(L"type", "alert_infobar");
+ AlertInfoBarDelegate* alert_infobar =
+ infobar->AsAlertInfoBarDelegate();
+ infobar_item->SetString(L"text", alert_infobar->GetMessageText());
+ } else if (infobar->AsLinkInfoBarDelegate()) {
+ infobar_item->SetString(L"type", "link_infobar");
+ LinkInfoBarDelegate* link_infobar = infobar->AsLinkInfoBarDelegate();
+ infobar_item->SetString(L"link_text", link_infobar->GetLinkText());
+ } else if (infobar->AsTranslateInfoBarDelegate()) {
+ infobar_item->SetString(L"type", "translate_infobar");
+ TranslateInfoBarDelegate* translate_infobar =
+ infobar->AsTranslateInfoBarDelegate();
+ infobar_item->SetString(L"original_lang_code",
+ translate_infobar->GetOriginalLanguageCode());
+ infobar_item->SetString(L"target_lang_code",
+ translate_infobar->GetTargetLanguageCode());
+ } else if (infobar->AsExtensionInfoBarDelegate()) {
+ infobar_item->SetString(L"type", "extension_infobar");
+ } else {
+ infobar_item->SetString(L"type", "unknown_infobar");
+ }
+ infobars->Append(infobar_item);
+ }
+ return infobars;
+}
+
+// Sample json input: { "command": "WaitForInfobarCount",
+// "count": COUNT,
+// "tab_index": INDEX }
+// Sample output: {}
+void AutomationProvider::WaitForInfobarCount(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message) {
+ int tab_index;
+ int count;
+ if (!args->GetInteger(L"count", &count) || count < 0 ||
+ !args->GetInteger(L"tab_index", &tab_index) || tab_index < 0) {
+ AutomationJSONReply(this, reply_message).SendError(
+ "Missing or invalid args: 'count', 'tab_index'.");
+ return;
+ }
+
+ TabContents* tab_contents = browser->GetTabContentsAt(tab_index);
+ // Observer deletes itself.
+ new WaitForInfobarCountObserver(this, reply_message, tab_contents, count);
+}
+
+namespace {
+
+// Task to get info about BrowserChildProcessHost. Must run on IO thread to
+// honor the semantics of BrowserChildProcessHost.
+// Used by AutomationProvider::GetBrowserInfo().
+class GetChildProcessHostInfoTask : public Task {
+ public:
+ GetChildProcessHostInfoTask(base::WaitableEvent* event,
+ ListValue* child_processes)
+ : event_(event),
+ child_processes_(child_processes) {}
+
+ virtual void Run() {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
+ for (BrowserChildProcessHost::Iterator iter; !iter.Done(); ++iter) {
+ // Only add processes which are already started,
+ // since we need their handle.
+ if ((*iter)->handle() == base::kNullProcessHandle) {
+ continue;
+ }
+ ChildProcessInfo* info = *iter;
+ DictionaryValue* item = new DictionaryValue;
+ item->SetString(L"name", info->name());
+ item->SetString(L"type",
+ ChildProcessInfo::GetTypeNameInEnglish(info->type()));
+ item->SetInteger(L"pid", base::GetProcId(info->handle()));
+ child_processes_->Append(item);
+ }
+ event_->Signal();
+ }
+
+ private:
+ base::WaitableEvent* const event_; // weak
+ ListValue* child_processes_;
+
+ DISALLOW_COPY_AND_ASSIGN(GetChildProcessHostInfoTask);
+};
+
+} // namespace
+
+// Sample json input: { "command": "GetBrowserInfo" }
+// Refer to GetBrowserInfo() in chrome/test/pyautolib/pyauto.py for
+// sample json output.
+void AutomationProvider::GetBrowserInfo(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message) {
+ DictionaryValue* properties = new DictionaryValue;
+ properties->SetString(L"ChromeVersion", chrome::kChromeVersion);
+ properties->SetString(L"BrowserProcessExecutableName",
+ chrome::kBrowserProcessExecutableName);
+ properties->SetString(L"HelperProcessExecutableName",
+ chrome::kHelperProcessExecutableName);
+ properties->SetString(L"BrowserProcessExecutablePath",
+ chrome::kBrowserProcessExecutablePath);
+ properties->SetString(L"HelperProcessExecutablePath",
+ chrome::kHelperProcessExecutablePath);
+ properties->SetString(L"command_line_string",
+ CommandLine::ForCurrentProcess()->command_line_string());
+
+ std::string branding;
+#if defined(GOOGLE_CHROME_BUILD)
+ branding = "Google Chrome";
+#elif defined(CHROMIUM_BUILD)
+ branding = "Chromium";
+#else
+ branding = "Unknown Branding";
+#endif
+ properties->SetString(L"branding", branding);
+
+ scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
+ return_value->Set(L"properties", properties);
+
+ return_value->SetInteger(L"browser_pid", base::GetCurrentProcId());
+ // Add info about all windows in a list of dictionaries, one dictionary
+ // item per window.
+ ListValue* windows = new ListValue;
+ int windex = 0;
+ for (BrowserList::const_iterator it = BrowserList::begin();
+ it != BrowserList::end();
+ ++it, ++windex) {
+ DictionaryValue* browser_item = new DictionaryValue;
+ browser = *it;
+ browser_item->SetInteger(L"index", windex);
+ // Window properties
+ gfx::Rect rect = browser->window()->GetRestoredBounds();
+ browser_item->SetInteger(L"x", rect.x());
+ browser_item->SetInteger(L"y", rect.y());
+ browser_item->SetInteger(L"width", rect.width());
+ browser_item->SetInteger(L"height", rect.height());
+ browser_item->SetBoolean(L"fullscreen",
+ browser->window()->IsFullscreen());
+ browser_item->SetInteger(L"selected_tab", browser->selected_index());
+ browser_item->SetBoolean(L"incognito",
+ browser->profile()->IsOffTheRecord());
+ // For each window, add info about all tabs in a list of dictionaries,
+ // one dictionary item per tab.
+ ListValue* tabs = new ListValue;
+ for (int i = 0; i < browser->tab_count(); ++i) {
+ TabContents* tc = browser->GetTabContentsAt(i);
+ DictionaryValue* tab = new DictionaryValue;
+ tab->SetInteger(L"index", i);
+ tab->SetString(L"url", tc->GetURL().spec());
+ tab->SetInteger(L"renderer_pid",
+ base::GetProcId(tc->GetRenderProcessHost()->GetHandle()));
+ tab->Set(L"infobars", GetInfobarsInfo(tc));
+ tabs->Append(tab);
+ }
+ browser_item->Set(L"tabs", tabs);
+
+ windows->Append(browser_item);
+ }
+ return_value->Set(L"windows", windows);
+
+ return_value->SetString(L"child_process_path",
+ ChildProcessHost::GetChildPath(true).value());
+ // Child processes are the processes for plugins and other workers.
+ // Add all child processes in a list of dictionaries, one dictionary item
+ // per child process.
+ ListValue* child_processes = new ListValue;
+ base::WaitableEvent event(true /* manual reset */,
+ false /* not initially signaled */);
+ CHECK(ChromeThread::PostTask(
+ ChromeThread::IO, FROM_HERE,
+ new GetChildProcessHostInfoTask(&event, child_processes)));
+ event.Wait();
+ return_value->Set(L"child_processes", child_processes);
+
+ // Add all extension processes in a list of dictionaries, one dictionary
+ // item per extension process.
+ ListValue* extension_processes = new ListValue;
+ ProfileManager* profile_manager = g_browser_process->profile_manager();
+ for (ProfileManager::const_iterator it = profile_manager->begin();
+ it != profile_manager->end(); ++it) {
+ ExtensionProcessManager* process_manager =
+ (*it)->GetExtensionProcessManager();
+ ExtensionProcessManager::const_iterator jt;
+ for (jt = process_manager->begin(); jt != process_manager->end(); ++jt) {
+ ExtensionHost* ex_host = *jt;
+ // Don't add dead extension processes.
+ if (!ex_host->IsRenderViewLive())
+ continue;
+ DictionaryValue* item = new DictionaryValue;
+ item->SetString(L"name", ex_host->extension()->name());
+ item->SetInteger(
+ L"pid",
+ base::GetProcId(ex_host->render_process_host()->GetHandle()));
+ extension_processes->Append(item);
+ }
+ }
+ return_value->Set(L"extension_processes", extension_processes);
+ AutomationJSONReply(this, reply_message).SendSuccess(return_value.get());
+}
+
+// Sample json input: { "command": "GetHistoryInfo",
+// "search_text": "some text" }
+// Refer chrome/test/pyautolib/history_info.py for sample json output.
+void AutomationProvider::GetHistoryInfo(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message) {
+ consumer_.CancelAllRequests();
+
+ string16 search_text;
+ args->GetString("search_text", &search_text);
+
+ // Fetch history.
+ HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
+ history::QueryOptions options;
+ // The observer owns itself. It deletes itself after it fetches history.
+ AutomationProviderHistoryObserver* history_observer =
+ new AutomationProviderHistoryObserver(this, reply_message);
+ hs->QueryHistory(
+ search_text,
+ options,
+ &consumer_,
+ NewCallback(history_observer,
+ &AutomationProviderHistoryObserver::HistoryQueryComplete));
+}
+
+// Sample json input: { "command": "AddHistoryItem",
+// "item": { "URL": "http://www.google.com",
+// "title": "Google", # optional
+// "time": 12345 # optional (time_t)
+// } }
+// Refer chrome/test/pyautolib/pyauto.py for details on input.
+void AutomationProvider::AddHistoryItem(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message) {
+ DictionaryValue* item = NULL;
+ args->GetDictionary(L"item", &item);
+ string16 url_text;
+ string16 title;
+ base::Time time = base::Time::Now();
+ AutomationJSONReply reply(this, reply_message);
+
+ if (!item->GetString("url", &url_text)) {
+ reply.SendError("bad args (no URL in dict?)");
+ return;
+ }
+ GURL gurl(url_text);
+ item->GetString("title", &title); // Don't care if it fails.
+ int it;
+ double dt;
+ if (item->GetInteger(L"time", &it))
+ time = base::Time::FromTimeT(it);
+ else if (item->GetReal(L"time", &dt))
+ time = base::Time::FromDoubleT(dt);
+
+ // Ideas for "dummy" values (e.g. id_scope) came from
+ // chrome/browser/autocomplete/history_contents_provider_unittest.cc
+ HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
+ const void* id_scope = reinterpret_cast<void*>(1);
+ hs->AddPage(gurl, time,
+ id_scope,
+ 0,
+ GURL(),
+ PageTransition::LINK,
+ history::RedirectList(),
+ false);
+ if (title.length())
+ hs->SetPageTitle(gurl, title);
+ reply.SendSuccess(NULL);
+}
+
+// Sample json input: { "command": "GetDownloadsInfo" }
+// Refer chrome/test/pyautolib/download_info.py for sample json output.
+void AutomationProvider::GetDownloadsInfo(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message) {
+ AutomationProviderDownloadManagerObserver observer;
+ std::vector<DownloadItem*> downloads;
+ scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
+ AutomationJSONReply reply(this, reply_message);
+
+ if (!profile_->HasCreatedDownloadManager()) {
+ reply.SendError("no download manager");
+ return;
+ }
+ // Use DownloadManager's GetDownloads() method and not GetCurrentDownloads()
+ // since that would be transient; a download might enter and empty out
+ // the current download queue too soon to be noticed.
+ profile_->GetDownloadManager()->GetDownloads(&observer, L"");
+ downloads = observer.Downloads();
+
+ std::map<DownloadItem::DownloadState, std::string> state_to_string;
+ state_to_string[DownloadItem::IN_PROGRESS] = std::string("IN_PROGRESS");
+ state_to_string[DownloadItem::CANCELLED] = std::string("CANCELLED");
+ state_to_string[DownloadItem::REMOVING] = std::string("REMOVING");
+ state_to_string[DownloadItem::COMPLETE] = std::string("COMPLETE");
+
+ std::map<DownloadItem::SafetyState, std::string> safety_state_to_string;
+ safety_state_to_string[DownloadItem::SAFE] = std::string("SAFE");
+ safety_state_to_string[DownloadItem::DANGEROUS] = std::string("DANGEROUS");
+ safety_state_to_string[DownloadItem::DANGEROUS_BUT_VALIDATED] =
+ std::string("DANGEROUS_BUT_VALIDATED");
+
+ ListValue* list_of_downloads = new ListValue;
+ for (std::vector<DownloadItem*>::iterator it = downloads.begin();
+ it != downloads.end();
+ it++) { // Fill info about each download item.
+ DictionaryValue* dl_item_value = new DictionaryValue;
+ dl_item_value->SetInteger(L"id", static_cast<int>((*it)->id()));
+ dl_item_value->SetString(L"url", (*it)->url().spec());
+ dl_item_value->SetString(L"referrer_url", (*it)->referrer_url().spec());
+ dl_item_value->SetString(L"file_name", (*it)->file_name().value());
+ dl_item_value->SetString(L"full_path", (*it)->full_path().value());
+ dl_item_value->SetBoolean(L"is_paused", (*it)->is_paused());
+ dl_item_value->SetBoolean(L"open_when_complete",
+ (*it)->open_when_complete());
+ dl_item_value->SetBoolean(L"is_extension_install",
+ (*it)->is_extension_install());
+ dl_item_value->SetBoolean(L"is_temporary", (*it)->is_temporary());
+ dl_item_value->SetBoolean(L"is_otr", (*it)->is_otr()); // off-the-record
+ dl_item_value->SetString(L"state", state_to_string[(*it)->state()]);
+ dl_item_value->SetString(L"safety_state",
+ safety_state_to_string[(*it)->safety_state()]);
+ dl_item_value->SetInteger(L"PercentComplete", (*it)->PercentComplete());
+ list_of_downloads->Append(dl_item_value);
+ }
+ return_value->Set(L"downloads", list_of_downloads);
+
+ reply.SendSuccess(return_value.get());
+ // All value objects allocated above are owned by |return_value|
+ // and get freed by it.
+}
+
+void AutomationProvider::WaitForDownloadsToComplete(
+ Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message) {
+ AutomationProviderDownloadManagerObserver observer;
+ std::vector<DownloadItem*> downloads;
+ AutomationJSONReply reply(this, reply_message);
+
+ // Look for a quick return.
+ if (!profile_->HasCreatedDownloadManager()) {
+ reply.SendSuccess(NULL); // No download manager.
+ return;
+ }
+ profile_->GetDownloadManager()->GetCurrentDownloads(&observer, FilePath());
+ downloads = observer.Downloads();
+ if (downloads.size() == 0) {
+ reply.SendSuccess(NULL);
+ return;
+ }
+
+ // The observer owns itself. When the last observed item pings, it
+ // deletes itself.
+ AutomationProviderDownloadItemObserver* item_observer =
+ new AutomationProviderDownloadItemObserver(
+ this, reply_message, downloads.size());
+ for (std::vector<DownloadItem*>::iterator i = downloads.begin();
+ i != downloads.end();
+ i++) {
+ (*i)->AddObserver(item_observer);
+ }
+}
+
+// Sample json input: { "command": "GetPrefsInfo" }
+// Refer chrome/test/pyautolib/prefs_info.py for sample json output.
+void AutomationProvider::GetPrefsInfo(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message) {
+ const PrefService::PreferenceSet& prefs =
+ profile_->GetPrefs()->preference_set();
+ DictionaryValue* items = new DictionaryValue;
+ for (PrefService::PreferenceSet::const_iterator it = prefs.begin();
+ it != prefs.end(); ++it) {
+ items->Set((*it)->name(), (*it)->GetValue()->DeepCopy());
+ }
+ scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
+ return_value->Set(L"prefs", items); // return_value owns items.
+ AutomationJSONReply(this, reply_message).SendSuccess(return_value.get());
+}
+
+// Sample json input: { "command": "SetPrefs", "path": path, "value": value }
+void AutomationProvider::SetPrefs(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message) {
+ std::wstring path;
+ Value* val;
+ AutomationJSONReply reply(this, reply_message);
+ if (args->GetString(L"path", &path) && args->Get(L"value", &val)) {
+ PrefService* pref_service = profile_->GetPrefs();
+ const PrefService::Preference* pref =
+ pref_service->FindPreference(path.c_str());
+ if (!pref) { // Not a registered pref.
+ reply.SendError("pref not registered.");
+ return;
+ } else if (pref->IsManaged()) { // Do not attempt to change a managed pref.
+ reply.SendError("pref is managed. cannot be changed.");
+ return;
+ } else { // Set the pref.
+ pref_service->Set(path.c_str(), *val);
+ }
+ } else {
+ reply.SendError("no pref path or value given.");
+ return;
+ }
+
+ reply.SendSuccess(NULL);
+}
+
+// Sample json input: { "command": "GetOmniboxInfo" }
+// Refer chrome/test/pyautolib/omnibox_info.py for sample json output.
+void AutomationProvider::GetOmniboxInfo(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message) {
+ scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
+
+ LocationBar* loc_bar = browser->window()->GetLocationBar();
+ AutocompleteEditView* edit_view = loc_bar->location_entry();
+ AutocompleteEditModel* model = edit_view->model();
+
+ // Fill up matches.
+ ListValue* matches = new ListValue;
+ const AutocompleteResult& result = model->result();
+ for (AutocompleteResult::const_iterator i = result.begin();
+ i != result.end(); ++i) {
+ const AutocompleteMatch& match = *i;
+ DictionaryValue* item = new DictionaryValue; // owned by return_value
+ item->SetString(L"type", AutocompleteMatch::TypeToString(match.type));
+ item->SetBoolean(L"starred", match.starred);
+ item->SetString(L"destination_url", match.destination_url.spec());
+ item->SetString(L"contents", match.contents);
+ item->SetString(L"description", match.description);
+ matches->Append(item);
+ }
+ return_value->Set(L"matches", matches);
+
+ // Fill up other properties.
+ DictionaryValue* properties = new DictionaryValue; // owned by return_value
+ properties->SetBoolean(L"has_focus", model->has_focus());
+ properties->SetBoolean(L"query_in_progress", model->query_in_progress());
+ properties->SetString(L"keyword", model->keyword());
+ properties->SetString(L"text", edit_view->GetText());
+ return_value->Set(L"properties", properties);
+
+ AutomationJSONReply(this, reply_message).SendSuccess(return_value.get());
+}
+
+// Sample json input: { "command": "SetOmniboxText",
+// "text": "goog" }
+void AutomationProvider::SetOmniboxText(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message) {
+ std::wstring text;
+ AutomationJSONReply reply(this, reply_message);
+ if (!args->GetString(L"text", &text)) {
+ reply.SendError("text missing");
+ return;
+ }
+ browser->FocusLocationBar();
+ LocationBar* loc_bar = browser->window()->GetLocationBar();
+ AutocompleteEditView* edit_view = loc_bar->location_entry();
+ edit_view->model()->OnSetFocus(false);
+ edit_view->SetUserText(text);
+ reply.SendSuccess(NULL);
+}
+
+// Sample json input: { "command": "OmniboxMovePopupSelection",
+// "count": 1 }
+// Negative count implies up, positive implies down. Count values will be
+// capped by the size of the popup list.
+void AutomationProvider::OmniboxMovePopupSelection(
+ Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message) {
+ int count;
+ AutomationJSONReply reply(this, reply_message);
+ if (!args->GetInteger(L"count", &count)) {
+ reply.SendError("count missing");
+ return;
+ }
+ LocationBar* loc_bar = browser->window()->GetLocationBar();
+ AutocompleteEditModel* model = loc_bar->location_entry()->model();
+ model->OnUpOrDownKeyPressed(count);
+ reply.SendSuccess(NULL);
+}
+
+// Sample json input: { "command": "OmniboxAcceptInput" }
+void AutomationProvider::OmniboxAcceptInput(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message) {
+ NavigationController& controller =
+ browser->GetSelectedTabContents()->controller();
+ // Setup observer to wait until the selected item loads.
+ NotificationObserver* observer =
+ new OmniboxAcceptNotificationObserver(&controller, this, reply_message);
+ notification_observer_list_.AddObserver(observer);
+
+ browser->window()->GetLocationBar()->AcceptInput();
+}
+
+// Sample json input: { "command": "GetInitialLoadTimes" }
+// Refer to InitialLoadObserver::GetTimingInformation() for sample output.
+void AutomationProvider::GetInitialLoadTimes(
+ Browser*,
+ DictionaryValue*,
+ IPC::Message* reply_message) {
+ scoped_ptr<DictionaryValue> return_value(
+ initial_load_observer_->GetTimingInformation());
+
+ std::string json_return;
+ base::JSONWriter::Write(return_value.get(), false, &json_return);
+ AutomationMsg_SendJSONRequest::WriteReplyParams(
+ reply_message, json_return, true);
+ Send(reply_message);
+}
+
+// Sample json input: { "command": "GetPluginsInfo" }
+// Refer chrome/test/pyautolib/plugins_info.py for sample json output.
+void AutomationProvider::GetPluginsInfo(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message) {
+ std::vector<WebPluginInfo> plugins;
+ NPAPI::PluginList::Singleton()->GetPlugins(false, &plugins);
+ ListValue* items = new ListValue;
+ for (std::vector<WebPluginInfo>::const_iterator it = plugins.begin();
+ it != plugins.end();
+ ++it) {
+ DictionaryValue* item = new DictionaryValue;
+ item->SetStringFromUTF16(L"name", it->name);
+ item->SetString(L"path", it->path.value());
+ item->SetStringFromUTF16(L"version", it->version);
+ item->SetStringFromUTF16(L"desc", it->desc);
+ item->SetBoolean(L"enabled", it->enabled);
+ // Add info about mime types.
+ ListValue* mime_types = new ListValue();
+ for (std::vector<WebPluginMimeType>::const_iterator type_it =
+ it->mime_types.begin();
+ type_it != it->mime_types.end();
+ ++type_it) {
+ DictionaryValue* mime_type = new DictionaryValue();
+ mime_type->SetString(L"mimeType", type_it->mime_type);
+ mime_type->SetStringFromUTF16(L"description", type_it->description);
+
+ ListValue* file_extensions = new ListValue();
+ for (std::vector<std::string>::const_iterator ext_it =
+ type_it->file_extensions.begin();
+ ext_it != type_it->file_extensions.end();
+ ++ext_it) {
+ file_extensions->Append(new StringValue(*ext_it));
+ }
+ mime_type->Set(L"fileExtensions", file_extensions);
+
+ mime_types->Append(mime_type);
+ }
+ item->Set(L"mimeTypes", mime_types);
+ items->Append(item);
+ }
+ scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
+ return_value->Set(L"plugins", items); // return_value owns items.
+
+ AutomationJSONReply(this, reply_message).SendSuccess(return_value.get());
+}
+
+// Sample json input:
+// { "command": "EnablePlugin",
+// "path": "/Library/Internet Plug-Ins/Flash Player.plugin" }
+void AutomationProvider::EnablePlugin(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message) {
+ FilePath::StringType path;
+ AutomationJSONReply reply(this, reply_message);
+ if (!args->GetString(L"path", &path)) {
+ reply.SendError("path not specified.");
+ return;
+ } else if (!NPAPI::PluginList::Singleton()->EnablePlugin(FilePath(path))) {
+ reply.SendError(StringPrintf("Could not enable plugin for path %s.",
+ path.c_str()));
+ return;
+ }
+ reply.SendSuccess(NULL);
+}
+
+// Sample json input:
+// { "command": "DisablePlugin",
+// "path": "/Library/Internet Plug-Ins/Flash Player.plugin" }
+void AutomationProvider::DisablePlugin(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message) {
+ FilePath::StringType path;
+ AutomationJSONReply reply(this, reply_message);
+ if (!args->GetString(L"path", &path)) {
+ reply.SendError("path not specified.");
+ return;
+ } else if (!NPAPI::PluginList::Singleton()->DisablePlugin(FilePath(path))) {
+ reply.SendError(StringPrintf("Could not disable plugin for path %s.",
+ path.c_str()));
+ return;
+ }
+ reply.SendSuccess(NULL);
+}
+
+// Sample json input:
+// { "command": "SaveTabContents",
+// "tab_index": 0,
+// "filename": <a full pathname> }
+// Sample json output:
+// {}
+void AutomationProvider::SaveTabContents(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message) {
+ int tab_index = 0;
+ FilePath::StringType filename;
+ FilePath::StringType parent_directory;
+ TabContents* tab_contents = NULL;
+
+ if (!args->GetInteger(L"tab_index", &tab_index) ||
+ !args->GetString(L"filename", &filename)) {
+ AutomationJSONReply(this, reply_message).SendError(
+ "tab_index or filename param missing");
+ return;
+ } else {
+ tab_contents = browser->GetTabContentsAt(tab_index);
+ if (!tab_contents) {
+ AutomationJSONReply(this, reply_message).SendError(
+ "no tab at tab_index");
+ return;
+ }
+ }
+ // We're doing a SAVE_AS_ONLY_HTML so the the directory path isn't
+ // used. Nevertheless, SavePackage requires it be valid. Sigh.
+ parent_directory = FilePath(filename).DirName().value();
+ if (!tab_contents->SavePage(FilePath(filename), FilePath(parent_directory),
+ SavePackage::SAVE_AS_ONLY_HTML)) {
+ AutomationJSONReply(this, reply_message).SendError(
+ "Could not initiate SavePage");
+ return;
+ }
+ // The observer will delete itself when done.
+ new SavePackageNotificationObserver(tab_contents->save_package(),
+ this, reply_message);
+}
+
+// Refer to ImportSettings() in chrome/test/pyautolib/pyauto.py for sample
+// json input.
+// Sample json output: "{}"
+void AutomationProvider::ImportSettings(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message) {
+ // Map from the json string passed over to the import item masks.
+ std::map<std::string, ImportItem> string_to_import_item;
+ string_to_import_item["HISTORY"] = importer::HISTORY;
+ string_to_import_item["FAVORITES"] = importer::FAVORITES;
+ string_to_import_item["COOKIES"] = importer::COOKIES;
+ string_to_import_item["PASSWORDS"] = importer::PASSWORDS;
+ string_to_import_item["SEARCH_ENGINES"] = importer::SEARCH_ENGINES;
+ string_to_import_item["HOME_PAGE"] = importer::HOME_PAGE;
+ string_to_import_item["ALL"] = importer::ALL;
+
+ std::wstring browser_name;
+ int import_items = 0;
+ ListValue* import_items_list = NULL;
+ bool first_run;
+
+ if (!args->GetString(L"import_from", &browser_name) ||
+ !args->GetBoolean(L"first_run", &first_run) ||
+ !args->GetList(L"import_items", &import_items_list)) {
+ AutomationJSONReply(this, reply_message).SendError(
+ "Incorrect type for one or more of the arguments.");
+ return;
+ }
+
+ int num_items = import_items_list->GetSize();
+ for (int i = 0; i < num_items; i++) {
+ std::string item;
+ import_items_list->GetString(i, &item);
+ // If the provided string is not part of the map, error out.
+ if (!ContainsKey(string_to_import_item, item)) {
+ AutomationJSONReply(this, reply_message).SendError(
+ "Invalid item string found in import_items.");
+ return;
+ }
+ import_items |= string_to_import_item[item];
+ }
+
+ ImporterHost* importer_host = new ImporterHost();
+ // Get the correct ProfileInfo based on the browser they user provided.
+ importer::ProfileInfo profile_info;
+ int num_browsers = importer_host->GetAvailableProfileCount();
+ int i = 0;
+ for ( ; i < num_browsers; i++) {
+ std::wstring name = importer_host->GetSourceProfileNameAt(i);
+ if (name == browser_name) {
+ profile_info = importer_host->GetSourceProfileInfoAt(i);
+ break;
+ }
+ }
+ // If we made it to the end of the loop, then the input was bad.
+ if (i == num_browsers) {
+ AutomationJSONReply(this, reply_message).SendError(
+ "Invalid browser name string found.");
+ return;
+ }
+
+ Profile* profile = browser->profile();
+
+ importer_host->SetObserver(
+ new AutomationProviderImportSettingsObserver(this, reply_message));
+ importer_host->StartImportSettings(profile_info, profile, import_items,
+ new ProfileWriter(profile), first_run);
+}
+
+// See AddSavedPassword() in chrome/test/functional/pyauto.py for sample json
+// input.
+// Sample json output: { "password_added": true }
+void AutomationProvider::AddSavedPassword(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message) {
+ string16 username;
+ string16 password;
+ base::Time time = base::Time::Now();
+ AutomationJSONReply reply(this, reply_message);
+
+ if (!args->GetStringAsUTF16(L"password", &password) ||
+ !args->GetStringAsUTF16(L"username", &username)) {
+ reply.SendError("Username and password must be strings.");
+ return;
+ }
+
+ // If the time is specified, change time to the specified time.
+ int it;
+ double dt;
+ if (args->GetInteger(L"time", &it))
+ time = base::Time::FromTimeT(it);
+ else if (args->GetReal(L"time", &dt))
+ time = base::Time::FromDoubleT(dt);
+
+ webkit_glue::PasswordForm new_password;
+ new_password.username_value = username;
+ new_password.password_value = password;
+ new_password.date_created = time;
+
+ Profile* profile = browser->profile();
+ // Use IMPLICIT_ACCESS since new passwords aren't added off the record.
+ PasswordStore* password_store =
+ profile->GetPasswordStore(Profile::IMPLICIT_ACCESS);
+
+ // Set the return based on whether setting the password succeeded.
+ scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
+
+ // It will be null if it's accessed in an incognito window.
+ if (password_store != NULL) {
+ password_store->AddLogin(new_password);
+ return_value->SetBoolean(L"password_added", true);
+ } else {
+ return_value->SetBoolean(L"password_added", false);
+ }
+
+ reply.SendSuccess(return_value.get());
+}
+
+// Sample json input: { "command": "GetSavedPasswords" }
+// Refer to GetSavedPasswords() in chrome/test/pyautolib/pyauto.py for sample
+// json output.
+void AutomationProvider::GetSavedPasswords(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message) {
+ Profile* profile = browser->profile();
+ // Use EXPLICIT_ACCESS since saved passwords can be retreived off the record.
+ PasswordStore* password_store =
+ profile->GetPasswordStore(Profile::EXPLICIT_ACCESS);
+ password_store->GetAutofillableLogins(
+ new AutomationProviderGetPasswordsObserver(this, reply_message));
+ // Observer deletes itself after returning.
+}
+
+// Refer to ClearBrowsingData() in chrome/test/pyautolib/pyauto.py for sample
+// json input.
+// Sample json output: {}
+void AutomationProvider::ClearBrowsingData(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message) {
+ std::map<std::string, BrowsingDataRemover::TimePeriod> string_to_time_period;
+ string_to_time_period["LAST_HOUR"] = BrowsingDataRemover::LAST_HOUR;
+ string_to_time_period["LAST_DAY"] = BrowsingDataRemover::LAST_DAY;
+ string_to_time_period["LAST_WEEK"] = BrowsingDataRemover::LAST_WEEK;
+ string_to_time_period["FOUR_WEEKS"] = BrowsingDataRemover::FOUR_WEEKS;
+ string_to_time_period["EVERYTHING"] = BrowsingDataRemover::EVERYTHING;
+
+ std::map<std::string, int> string_to_mask_value;
+ string_to_mask_value["HISTORY"] = BrowsingDataRemover::REMOVE_HISTORY;
+ string_to_mask_value["DOWNLOADS"] = BrowsingDataRemover::REMOVE_DOWNLOADS;
+ string_to_mask_value["COOKIES"] = BrowsingDataRemover::REMOVE_COOKIES;
+ string_to_mask_value["PASSWORDS"] = BrowsingDataRemover::REMOVE_PASSWORDS;
+ string_to_mask_value["FORM_DATA"] = BrowsingDataRemover::REMOVE_FORM_DATA;
+ string_to_mask_value["CACHE"] = BrowsingDataRemover::REMOVE_CACHE;
+
+ std::string time_period;
+ ListValue* to_remove;
+ if (!args->GetString(L"time_period", &time_period) ||
+ !args->GetList(L"to_remove", &to_remove)) {
+ AutomationJSONReply(this, reply_message).SendError(
+ "time_period must be a string and to_remove a list.");
+ return;
+ }
+
+ int remove_mask = 0;
+ int num_removals = to_remove->GetSize();
+ for (int i = 0; i < num_removals; i++) {
+ std::string removal;
+ to_remove->GetString(i, &removal);
+ // If the provided string is not part of the map, then error out.
+ if (!ContainsKey(string_to_mask_value, removal)) {
+ AutomationJSONReply(this, reply_message).SendError(
+ "Invalid browsing data string found in to_remove.");
+ return;
+ }
+ remove_mask |= string_to_mask_value[removal];
+ }
+
+ if (!ContainsKey(string_to_time_period, time_period)) {
+ AutomationJSONReply(this, reply_message).SendError(
+ "Invalid string for time_period.");
+ return;
+ }
+
+ BrowsingDataRemover* remover = new BrowsingDataRemover(
+ profile(), string_to_time_period[time_period], base::Time());
+
+ remover->AddObserver(
+ new AutomationProviderBrowsingDataObserver(this, reply_message));
+ remover->Remove(remove_mask);
+ // BrowsingDataRemover deletes itself using DeleteTask.
+ // The observer also deletes itself after sending the reply.
+}
+
+// Sample json input: { "command": "GetThemeInfo" }
+// Refer GetThemeInfo() in chrome/test/pyautolib/pyauto.py for sample output.
+void AutomationProvider::GetThemeInfo(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message) {
+ scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
+ Extension* theme = browser->profile()->GetTheme();
+ if (theme) {
+ return_value->SetString(L"name", theme->name());
+ return_value->Set(L"images", theme->GetThemeImages()->DeepCopy());
+ return_value->Set(L"colors", theme->GetThemeColors()->DeepCopy());
+ return_value->Set(L"tints", theme->GetThemeTints()->DeepCopy());
+ }
+ AutomationJSONReply(this, reply_message).SendSuccess(return_value.get());
+}
+
+// Sample json input:
+// { "command": "GetAutoFillProfile" }
+// Refer to GetAutoFillProfile() in chrome/test/pyautolib/pyauto.py for sample
+// json output.
+void AutomationProvider::GetAutoFillProfile(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message) {
+ // Get the AutoFillProfiles currently in the database.
+ int tab_index = 0;
+ args->GetInteger(L"tab_index", &tab_index);
+ TabContents* tab_contents = browser->GetTabContentsAt(tab_index);
+ AutomationJSONReply reply(this, reply_message);
+
+ if (tab_contents) {
+ PersonalDataManager* pdm = tab_contents->profile()->GetOriginalProfile()
+ ->GetPersonalDataManager();
+ if (pdm) {
+ std::vector<AutoFillProfile*> autofill_profiles = pdm->profiles();
+ std::vector<CreditCard*> credit_cards = pdm->credit_cards();
+
+ ListValue* profiles = GetListFromAutoFillProfiles(autofill_profiles);
+ ListValue* cards = GetListFromCreditCards(credit_cards);
+
+ scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
+
+ return_value->Set(L"profiles", profiles);
+ return_value->Set(L"credit_cards", cards);
+ reply.SendSuccess(return_value.get());
+ } else {
+ reply.SendError("No PersonalDataManager.");
+ return;
+ }
+ } else {
+ reply.SendError("No tab at that index.");
+ return;
+ }
+}
+
+// Refer to FillAutoFillProfile() in chrome/test/pyautolib/pyauto.py for sample
+// json input.
+// Sample json output: {}
+void AutomationProvider::FillAutoFillProfile(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message) {
+ AutomationJSONReply reply(this, reply_message);
+ ListValue* profiles = NULL;
+ ListValue* cards = NULL;
+ args->GetList(L"profiles", &profiles);
+ args->GetList(L"credit_cards", &cards);
+ std::string error_mesg;
+
+ std::vector<AutoFillProfile> autofill_profiles;
+ std::vector<CreditCard> credit_cards;
+ // Create an AutoFillProfile for each of the dictionary profiles.
+ if (profiles) {
+ autofill_profiles = GetAutoFillProfilesFromList(*profiles, &error_mesg);
+ }
+ // Create a CreditCard for each of the dictionary values.
+ if (cards) {
+ credit_cards = GetCreditCardsFromList(*cards, &error_mesg);
+ }
+ if (!error_mesg.empty()) {
+ reply.SendError(error_mesg);
+ return;
+ }
+
+ // Save the AutoFillProfiles.
+ int tab_index = 0;
+ args->GetInteger(L"tab_index", &tab_index);
+ TabContents* tab_contents = browser->GetTabContentsAt(tab_index);
+
+ if (tab_contents) {
+ PersonalDataManager* pdm = tab_contents->profile()
+ ->GetPersonalDataManager();
+ if (pdm) {
+ pdm->OnAutoFillDialogApply(profiles? &autofill_profiles : NULL,
+ cards? &credit_cards : NULL);
+ } else {
+ reply.SendError("No PersonalDataManager.");
+ return;
+ }
+ } else {
+ reply.SendError("No tab at that index.");
+ return;
+ }
+ reply.SendSuccess(NULL);
+}
+
+/* static */
+ListValue* AutomationProvider::GetListFromAutoFillProfiles(
+ std::vector<AutoFillProfile*> autofill_profiles) {
+ ListValue* profiles = new ListValue;
+
+ std::map<AutoFillFieldType, std::wstring> autofill_type_to_string
+ = GetAutoFillFieldToStringMap();
+
+ // For each AutoFillProfile, transform it to a dictionary object to return.
+ for (std::vector<AutoFillProfile*>::iterator it = autofill_profiles.begin();
+ it != autofill_profiles.end(); ++it) {
+ AutoFillProfile* profile = *it;
+ DictionaryValue* profile_info = new DictionaryValue;
+ profile_info->SetStringFromUTF16(L"label", profile->Label());
+ // For each of the types, if it has a value, add it to the dictionary.
+ for (std::map<AutoFillFieldType, std::wstring>::iterator
+ type_it = autofill_type_to_string.begin();
+ type_it != autofill_type_to_string.end(); ++type_it) {
+ string16 value = profile->GetFieldText(AutoFillType(type_it->first));
+ if (value.length()) { // If there was something stored for that value.
+ profile_info->SetStringFromUTF16(type_it->second, value);
+ }
+ }
+ profiles->Append(profile_info);
+ }
+ return profiles;
+}
+
+/* static */
+ListValue* AutomationProvider::GetListFromCreditCards(
+ std::vector<CreditCard*> credit_cards) {
+ ListValue* cards = new ListValue;
+
+ std::map<AutoFillFieldType, std::wstring> credit_card_type_to_string =
+ GetCreditCardFieldToStringMap();
+
+ // For each AutoFillProfile, transform it to a dictionary object to return.
+ for (std::vector<CreditCard*>::iterator it = credit_cards.begin();
+ it != credit_cards.end(); ++it) {
+ CreditCard* card = *it;
+ DictionaryValue* card_info = new DictionaryValue;
+ card_info->SetStringFromUTF16(L"label", card->Label());
+ // For each of the types, if it has a value, add it to the dictionary.
+ for (std::map<AutoFillFieldType, std::wstring>::iterator type_it =
+ credit_card_type_to_string.begin();
+ type_it != credit_card_type_to_string.end(); ++type_it) {
+ string16 value = card->GetFieldText(AutoFillType(type_it->first));
+ // If there was something stored for that value.
+ if (value.length()) {
+ card_info->SetStringFromUTF16(type_it->second, value);
+ }
+ }
+ cards->Append(card_info);
+ }
+ return cards;
+}
+
+/* static */
+std::vector<AutoFillProfile> AutomationProvider::GetAutoFillProfilesFromList(
+ const ListValue& profiles, std::string* error_message) {
+ std::vector<AutoFillProfile> autofill_profiles;
+ DictionaryValue* profile_info = NULL;
+ string16 profile_label;
+ string16 current_value;
+
+ std::map<AutoFillFieldType, std::wstring> autofill_type_to_string =
+ GetAutoFillFieldToStringMap();
+
+ int num_profiles = profiles.GetSize();
+ for (int i = 0; i < num_profiles; i++) {
+ profiles.GetDictionary(i, &profile_info);
+ profile_info->GetString("label", &profile_label);
+ // Choose an id of 0 so that a unique id will be created.
+ AutoFillProfile profile(profile_label, 0);
+ // Loop through the possible profile types and add those provided.
+ for (std::map<AutoFillFieldType, std::wstring>::iterator type_it =
+ autofill_type_to_string.begin();
+ type_it != autofill_type_to_string.end(); ++type_it) {
+ if (profile_info->HasKey(type_it->second)) {
+ if (profile_info->GetStringAsUTF16(type_it->second, &current_value)) {
+ profile.SetInfo(AutoFillType(type_it->first), current_value);
+ } else {
+ *error_message= "All values must be strings";
+ break;
+ }
+ }
+ }
+ autofill_profiles.push_back(profile);
+ }
+ return autofill_profiles;
+}
+
+/* static */
+std::vector<CreditCard> AutomationProvider::GetCreditCardsFromList(
+ const ListValue& cards, std::string* error_message) {
+ std::vector<CreditCard> credit_cards;
+ DictionaryValue* card_info = NULL;
+ string16 card_label;
+ string16 current_value;
+
+ std::map<AutoFillFieldType, std::wstring> credit_card_type_to_string =
+ GetCreditCardFieldToStringMap();
+
+ int num_credit_cards = cards.GetSize();
+ for (int i = 0; i < num_credit_cards; i++) {
+ cards.GetDictionary(i, &card_info);
+ card_info->GetString("label", &card_label);
+ CreditCard card(card_label, 0);
+ // Loop through the possible credit card fields and add those provided.
+ for (std::map<AutoFillFieldType, std::wstring>::iterator type_it =
+ credit_card_type_to_string.begin();
+ type_it != credit_card_type_to_string.end(); ++type_it) {
+ if (card_info->HasKey(type_it->second)) {
+ if (card_info->GetStringAsUTF16(type_it->second, &current_value)) {
+ card.SetInfo(AutoFillType(type_it->first), current_value);
+ } else {
+ *error_message= "All values must be strings";
+ break;
+ }
+ }
+ }
+ credit_cards.push_back(card);
+ }
+ return credit_cards;
+}
+
+/* static */
+std::map<AutoFillFieldType, std::wstring>
+ AutomationProvider::GetAutoFillFieldToStringMap() {
+ std::map<AutoFillFieldType, std::wstring> autofill_type_to_string;
+ autofill_type_to_string[NAME_FIRST] = L"NAME_FIRST";
+ autofill_type_to_string[NAME_MIDDLE] = L"NAME_MIDDLE";
+ autofill_type_to_string[NAME_LAST] = L"NAME_LAST";
+ autofill_type_to_string[COMPANY_NAME] = L"COMPANY_NAME";
+ autofill_type_to_string[EMAIL_ADDRESS] = L"EMAIL_ADDRESS";
+ autofill_type_to_string[ADDRESS_HOME_LINE1] = L"ADDRESS_HOME_LINE1";
+ autofill_type_to_string[ADDRESS_HOME_LINE2] = L"ADDRESS_HOME_LINE2";
+ autofill_type_to_string[ADDRESS_HOME_CITY] = L"ADDRESS_HOME_CITY";
+ autofill_type_to_string[ADDRESS_HOME_STATE] = L"ADDRESS_HOME_STATE";
+ autofill_type_to_string[ADDRESS_HOME_ZIP] = L"ADDRESS_HOME_ZIP";
+ autofill_type_to_string[ADDRESS_HOME_COUNTRY] = L"ADDRESS_HOME_COUNTRY";
+ autofill_type_to_string[PHONE_HOME_NUMBER] = L"PHONE_HOME_NUMBER";
+ autofill_type_to_string[PHONE_FAX_NUMBER] = L"PHONE_FAX_NUMBER";
+ autofill_type_to_string[NAME_FIRST] = L"NAME_FIRST";
+ return autofill_type_to_string;
+}
+
+/* static */
+std::map<AutoFillFieldType, std::wstring>
+ AutomationProvider::GetCreditCardFieldToStringMap() {
+ std::map<AutoFillFieldType, std::wstring> credit_card_type_to_string;
+ credit_card_type_to_string[CREDIT_CARD_NAME] = L"CREDIT_CARD_NAME";
+ credit_card_type_to_string[CREDIT_CARD_NUMBER] = L"CREDIT_CARD_NUMBER";
+ credit_card_type_to_string[CREDIT_CARD_TYPE] = L"CREDIT_CARD_TYPE";
+ credit_card_type_to_string[CREDIT_CARD_EXP_MONTH] = L"CREDIT_CARD_EXP_MONTH";
+ credit_card_type_to_string[CREDIT_CARD_EXP_4_DIGIT_YEAR] =
+ L"CREDIT_CARD_EXP_4_DIGIT_YEAR";
+ return credit_card_type_to_string;
+}
+
+void AutomationProvider::SendJSONRequest(int handle,
+ std::string json_request,
+ IPC::Message* reply_message) {
+ Browser* browser = NULL;
+ scoped_ptr<Value> values;
+
+ // Basic error checking.
+ if (browser_tracker_->ContainsHandle(handle)) {
+ browser = browser_tracker_->GetResource(handle);
+ }
+ if (!browser) {
+ AutomationJSONReply(this, reply_message).SendError("no browser object");
+ return;
+ }
+ base::JSONReader reader;
+ std::string error;
+ values.reset(reader.ReadAndReturnError(json_request, true, NULL, &error));
+ if (!error.empty()) {
+ AutomationJSONReply(this, reply_message).SendError(error);
+ return;
+ }
+
+ // Make sure input is a dict with a string command.
+ std::string command;
+ DictionaryValue* dict_value = NULL;
+ if (values->GetType() != Value::TYPE_DICTIONARY) {
+ AutomationJSONReply(this, reply_message).SendError("not a dict");
+ return;
+ }
+ // Ownership remains with "values" variable.
+ dict_value = static_cast<DictionaryValue*>(values.get());
+ if (!dict_value->GetStringASCII(std::string("command"), &command)) {
+ AutomationJSONReply(this, reply_message).SendError(
+ "no command key in dict or not a string command");
+ return;
+ }
+
+ // Map json commands to their handlers.
+ std::map<std::string, JsonHandler> handler_map;
+ handler_map["DisablePlugin"] = &AutomationProvider::DisablePlugin;
+ handler_map["EnablePlugin"] = &AutomationProvider::EnablePlugin;
+ handler_map["GetPluginsInfo"] = &AutomationProvider::GetPluginsInfo;
+
+ handler_map["GetBrowserInfo"] = &AutomationProvider::GetBrowserInfo;
+
+ handler_map["WaitForInfobarCount"] = &AutomationProvider::WaitForInfobarCount;
+
+ handler_map["GetHistoryInfo"] = &AutomationProvider::GetHistoryInfo;
+ handler_map["AddHistoryItem"] = &AutomationProvider::AddHistoryItem;
+
+ handler_map["GetOmniboxInfo"] = &AutomationProvider::GetOmniboxInfo;
+ handler_map["SetOmniboxText"] = &AutomationProvider::SetOmniboxText;
+ handler_map["OmniboxAcceptInput"] = &AutomationProvider::OmniboxAcceptInput;
+ handler_map["OmniboxMovePopupSelection"] =
+ &AutomationProvider::OmniboxMovePopupSelection;
+
+ handler_map["GetPrefsInfo"] = &AutomationProvider::GetPrefsInfo;
+ handler_map["SetPrefs"] = &AutomationProvider::SetPrefs;
+
+ handler_map["SetWindowDimensions"] = &AutomationProvider::SetWindowDimensions;
+
+ handler_map["GetDownloadsInfo"] = &AutomationProvider::GetDownloadsInfo;
+ handler_map["WaitForAllDownloadsToComplete"] =
+ &AutomationProvider::WaitForDownloadsToComplete;
+
+ handler_map["GetInitialLoadTimes"] = &AutomationProvider::GetInitialLoadTimes;
+
+ handler_map["SaveTabContents"] = &AutomationProvider::SaveTabContents;
+
+ handler_map["ImportSettings"] = &AutomationProvider::ImportSettings;
+
+ handler_map["AddSavedPassword"] = &AutomationProvider::AddSavedPassword;
+ handler_map["GetSavedPasswords"] = &AutomationProvider::GetSavedPasswords;
+
+ handler_map["ClearBrowsingData"] = &AutomationProvider::ClearBrowsingData;
+
+ // SetTheme() implemented using InstallExtension().
+ handler_map["GetThemeInfo"] = &AutomationProvider::GetThemeInfo;
+
+ handler_map["GetAutoFillProfile"] = &AutomationProvider::GetAutoFillProfile;
+ handler_map["FillAutoFillProfile"] = &AutomationProvider::FillAutoFillProfile;
+
+ if (handler_map.find(std::string(command)) != handler_map.end()) {
+ (this->*handler_map[command])(browser, dict_value, reply_message);
+ } else {
+ std::string error_string = "Unknown command. Options: ";
+ for (std::map<std::string, JsonHandler>::const_iterator it =
+ handler_map.begin(); it != handler_map.end(); ++it) {
+ error_string += it->first + ", ";
+ }
+ AutomationJSONReply(this, reply_message).SendError(error_string);
+ }
+}
+
+void AutomationProvider::HandleInspectElementRequest(
+ int handle, int x, int y, IPC::Message* reply_message) {
+ TabContents* tab_contents = GetTabContentsForHandle(handle, NULL);
+ if (tab_contents) {
+ DCHECK(reply_message_ == NULL);
+ reply_message_ = reply_message;
+
+ DevToolsManager::GetInstance()->InspectElement(
+ tab_contents->render_view_host(), x, y);
+ } else {
+ AutomationMsg_InspectElement::WriteReplyParams(reply_message, -1);
+ Send(reply_message);
+ }
+}
+
+void AutomationProvider::ReceivedInspectElementResponse(int num_resources) {
+ if (reply_message_) {
+ AutomationMsg_InspectElement::WriteReplyParams(reply_message_,
+ num_resources);
+ Send(reply_message_);
+ reply_message_ = NULL;
+ }
+}
+
+class SetProxyConfigTask : public Task {
+ public:
+ SetProxyConfigTask(URLRequestContextGetter* request_context_getter,
+ const std::string& new_proxy_config)
+ : request_context_getter_(request_context_getter),
+ proxy_config_(new_proxy_config) {}
+ virtual void Run() {
+ // First, deserialize the JSON string. If this fails, log and bail.
+ JSONStringValueSerializer deserializer(proxy_config_);
+ std::string error_msg;
+ scoped_ptr<Value> root(deserializer.Deserialize(NULL, &error_msg));
+ if (!root.get() || root->GetType() != Value::TYPE_DICTIONARY) {
+ DLOG(WARNING) << "Received bad JSON string for ProxyConfig: "
+ << error_msg;
+ return;
+ }
+
+ scoped_ptr<DictionaryValue> dict(
+ static_cast<DictionaryValue*>(root.release()));
+ // Now put together a proxy configuration from the deserialized string.
+ net::ProxyConfig pc;
+ PopulateProxyConfig(*dict.get(), &pc);
+
+ net::ProxyService* proxy_service =
+ request_context_getter_->GetURLRequestContext()->proxy_service();
+ DCHECK(proxy_service);
+ scoped_ptr<net::ProxyConfigService> proxy_config_service(
+ new net::ProxyConfigServiceFixed(pc));
+ proxy_service->ResetConfigService(proxy_config_service.release());
+ }
+
+ void PopulateProxyConfig(const DictionaryValue& dict, net::ProxyConfig* pc) {
+ DCHECK(pc);
+ bool no_proxy = false;
+ if (dict.GetBoolean(automation::kJSONProxyNoProxy, &no_proxy)) {
+ // Make no changes to the ProxyConfig.
+ return;
+ }
+ bool auto_config;
+ if (dict.GetBoolean(automation::kJSONProxyAutoconfig, &auto_config)) {
+ pc->set_auto_detect(true);
+ }
+ std::string pac_url;
+ if (dict.GetString(automation::kJSONProxyPacUrl, &pac_url)) {
+ pc->set_pac_url(GURL(pac_url));
+ }
+ std::string proxy_bypass_list;
+ if (dict.GetString(automation::kJSONProxyBypassList, &proxy_bypass_list)) {
+ pc->proxy_rules().bypass_rules.ParseFromString(proxy_bypass_list);
+ }
+ std::string proxy_server;
+ if (dict.GetString(automation::kJSONProxyServer, &proxy_server)) {
+ pc->proxy_rules().ParseFromString(proxy_server);
+ }
+ }
+
+ private:
+ scoped_refptr<URLRequestContextGetter> request_context_getter_;
+ std::string proxy_config_;
+};
+
+
+void AutomationProvider::SetProxyConfig(const std::string& new_proxy_config) {
+ URLRequestContextGetter* context_getter = Profile::GetDefaultRequestContext();
+ if (!context_getter) {
+ FilePath user_data_dir;
+ PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
+ ProfileManager* profile_manager = g_browser_process->profile_manager();
+ DCHECK(profile_manager);
+ Profile* profile = profile_manager->GetDefaultProfile(user_data_dir);
+ DCHECK(profile);
+ context_getter = profile->GetRequestContext();
+ }
+ DCHECK(context_getter);
+
+ ChromeThread::PostTask(
+ ChromeThread::IO, FROM_HERE,
+ new SetProxyConfigTask(context_getter, new_proxy_config));
+}
+
+void AutomationProvider::GetDownloadDirectory(
+ int handle, FilePath* download_directory) {
+ DLOG(INFO) << "Handling download directory request";
+ if (tab_tracker_->ContainsHandle(handle)) {
+ NavigationController* tab = tab_tracker_->GetResource(handle);
+ DownloadManager* dlm = tab->profile()->GetDownloadManager();
+ DCHECK(dlm);
+ *download_directory = dlm->download_path();
+ }
+}
+
+void AutomationProvider::OpenNewBrowserWindow(bool show,
+ IPC::Message* reply_message) {
+ OpenNewBrowserWindowOfType(static_cast<int>(Browser::TYPE_NORMAL), show,
+ reply_message);
+}
+
+void AutomationProvider::OpenNewBrowserWindowOfType(
+ int type, bool show, IPC::Message* reply_message) {
+ new BrowserOpenedNotificationObserver(this, reply_message);
+ // We may have no current browser windows open so don't rely on
+ // asking an existing browser to execute the IDC_NEWWINDOW command
+ Browser* browser = new Browser(static_cast<Browser::Type>(type), profile_);
+ browser->CreateBrowserWindow();
+ browser->AddBlankTab(true);
+ if (show)
+ browser->window()->Show();
+}
+
+void AutomationProvider::GetWindowForBrowser(int browser_handle,
+ bool* success,
+ int* handle) {
+ *success = false;
+ *handle = 0;
+
+ if (browser_tracker_->ContainsHandle(browser_handle)) {
+ Browser* browser = browser_tracker_->GetResource(browser_handle);
+ gfx::NativeWindow win = browser->window()->GetNativeHandle();
+ // Add() returns the existing handle for the resource if any.
+ *handle = window_tracker_->Add(win);
+ *success = true;
+ }
+}
+
+void AutomationProvider::GetAutocompleteEditForBrowser(
+ int browser_handle,
+ bool* success,
+ int* autocomplete_edit_handle) {
+ *success = false;
+ *autocomplete_edit_handle = 0;
+
+ if (browser_tracker_->ContainsHandle(browser_handle)) {
+ Browser* browser = browser_tracker_->GetResource(browser_handle);
+ LocationBar* loc_bar = browser->window()->GetLocationBar();
+ AutocompleteEditView* edit_view = loc_bar->location_entry();
+ // Add() returns the existing handle for the resource if any.
+ *autocomplete_edit_handle = autocomplete_edit_tracker_->Add(edit_view);
+ *success = true;
+ }
+}
+
+void AutomationProvider::ShowInterstitialPage(int tab_handle,
+ const std::string& html_text,
+ IPC::Message* reply_message) {
+ if (tab_tracker_->ContainsHandle(tab_handle)) {
+ NavigationController* controller = tab_tracker_->GetResource(tab_handle);
+ TabContents* tab_contents = controller->tab_contents();
+
+ AddNavigationStatusListener(controller, reply_message, 1, false);
+ AutomationInterstitialPage* interstitial =
+ new AutomationInterstitialPage(tab_contents,
+ GURL("about:interstitial"),
+ html_text);
+ interstitial->Show();
+ return;
+ }
+
+ AutomationMsg_ShowInterstitialPage::WriteReplyParams(
+ reply_message, AUTOMATION_MSG_NAVIGATION_ERROR);
+ Send(reply_message);
+}
+
+void AutomationProvider::HideInterstitialPage(int tab_handle,
+ bool* success) {
+ *success = false;
+ TabContents* tab_contents = GetTabContentsForHandle(tab_handle, NULL);
+ if (tab_contents && tab_contents->interstitial_page()) {
+ tab_contents->interstitial_page()->DontProceed();
+ *success = true;
+ }
+}
+
+void AutomationProvider::CloseTab(int tab_handle,
+ bool wait_until_closed,
+ IPC::Message* reply_message) {
+ if (tab_tracker_->ContainsHandle(tab_handle)) {
+ NavigationController* controller = tab_tracker_->GetResource(tab_handle);
+ int index;
+ Browser* browser = Browser::GetBrowserForController(controller, &index);
+ DCHECK(browser);
+ new TabClosedNotificationObserver(this, wait_until_closed, reply_message);
+ browser->CloseContents(controller->tab_contents());
+ return;
+ }
+
+ AutomationMsg_CloseTab::WriteReplyParams(reply_message, false);
+ Send(reply_message);
+}
+
+void AutomationProvider::CloseBrowser(int browser_handle,
+ IPC::Message* reply_message) {
+ if (browser_tracker_->ContainsHandle(browser_handle)) {
+ Browser* browser = browser_tracker_->GetResource(browser_handle);
+ new BrowserClosedNotificationObserver(browser, this,
+ reply_message);
+ browser->window()->Close();
+ } else {
+ NOTREACHED();
+ }
+}
+
+void AutomationProvider::CloseBrowserAsync(int browser_handle) {
+ if (browser_tracker_->ContainsHandle(browser_handle)) {
+ Browser* browser = browser_tracker_->GetResource(browser_handle);
+ browser->window()->Close();
+ } else {
+ NOTREACHED();
+ }
+}
+
+void AutomationProvider::WaitForTabToBeRestored(int tab_handle,
+ IPC::Message* reply_message) {
+ if (tab_tracker_->ContainsHandle(tab_handle)) {
+ NavigationController* tab = tab_tracker_->GetResource(tab_handle);
+ restore_tracker_.reset(
+ new NavigationControllerRestoredObserver(this, tab, reply_message));
+ }
+}
+
+void AutomationProvider::GetSecurityState(int handle, bool* success,
+ SecurityStyle* security_style,
+ int* ssl_cert_status,
+ int* insecure_content_status) {
+ if (tab_tracker_->ContainsHandle(handle)) {
+ NavigationController* tab = tab_tracker_->GetResource(handle);
+ NavigationEntry* entry = tab->GetActiveEntry();
+ *success = true;
+ *security_style = entry->ssl().security_style();
+ *ssl_cert_status = entry->ssl().cert_status();
+ *insecure_content_status = entry->ssl().content_status();
+ } else {
+ *success = false;
+ *security_style = SECURITY_STYLE_UNKNOWN;
+ *ssl_cert_status = 0;
+ *insecure_content_status = 0;
+ }
+}
+
+void AutomationProvider::GetPageType(int handle, bool* success,
+ NavigationEntry::PageType* page_type) {
+ if (tab_tracker_->ContainsHandle(handle)) {
+ NavigationController* tab = tab_tracker_->GetResource(handle);
+ NavigationEntry* entry = tab->GetActiveEntry();
+ *page_type = entry->page_type();
+ *success = true;
+ // In order to return the proper result when an interstitial is shown and
+ // no navigation entry were created for it we need to ask the TabContents.
+ if (*page_type == NavigationEntry::NORMAL_PAGE &&
+ tab->tab_contents()->showing_interstitial_page())
+ *page_type = NavigationEntry::INTERSTITIAL_PAGE;
+ } else {
+ *success = false;
+ *page_type = NavigationEntry::NORMAL_PAGE;
+ }
+}
+
+void AutomationProvider::GetMetricEventDuration(const std::string& event_name,
+ int* duration_ms) {
+ *duration_ms = metric_event_duration_observer_->GetEventDurationMs(
+ event_name);
+}
+
+void AutomationProvider::ActionOnSSLBlockingPage(int handle, bool proceed,
+ IPC::Message* reply_message) {
+ if (tab_tracker_->ContainsHandle(handle)) {
+ NavigationController* tab = tab_tracker_->GetResource(handle);
+ NavigationEntry* entry = tab->GetActiveEntry();
+ if (entry->page_type() == NavigationEntry::INTERSTITIAL_PAGE) {
+ TabContents* tab_contents = tab->tab_contents();
+ InterstitialPage* ssl_blocking_page =
+ InterstitialPage::GetInterstitialPage(tab_contents);
+ if (ssl_blocking_page) {
+ if (proceed) {
+ AddNavigationStatusListener(tab, reply_message, 1, false);
+ ssl_blocking_page->Proceed();
+ return;
+ }
+ ssl_blocking_page->DontProceed();
+ AutomationMsg_ActionOnSSLBlockingPage::WriteReplyParams(
+ reply_message, AUTOMATION_MSG_NAVIGATION_SUCCESS);
+ Send(reply_message);
+ return;
+ }
+ }
+ }
+ // We failed.
+ AutomationMsg_ActionOnSSLBlockingPage::WriteReplyParams(
+ reply_message, AUTOMATION_MSG_NAVIGATION_ERROR);
+ Send(reply_message);
+}
+
+void AutomationProvider::BringBrowserToFront(int browser_handle,
+ bool* success) {
+ if (browser_tracker_->ContainsHandle(browser_handle)) {
+ Browser* browser = browser_tracker_->GetResource(browser_handle);
+ browser->window()->Activate();
+ *success = true;
+ } else {
+ *success = false;
+ }
+}
+
+void AutomationProvider::IsMenuCommandEnabled(int browser_handle,
+ int message_num,
+ bool* menu_item_enabled) {
+ if (browser_tracker_->ContainsHandle(browser_handle)) {
+ Browser* browser = browser_tracker_->GetResource(browser_handle);
+ *menu_item_enabled =
+ browser->command_updater()->IsCommandEnabled(message_num);
+ } else {
+ *menu_item_enabled = false;
+ }
+}
+
+void AutomationProvider::PrintNow(int tab_handle,
+ IPC::Message* reply_message) {
+ NavigationController* tab = NULL;
+ TabContents* tab_contents = GetTabContentsForHandle(tab_handle, &tab);
+ if (tab_contents) {
+ FindAndActivateTab(tab);
+ notification_observer_list_.AddObserver(
+ new DocumentPrintedNotificationObserver(this, reply_message));
+ if (tab_contents->PrintNow())
+ return;
+ }
+ AutomationMsg_PrintNow::WriteReplyParams(reply_message, false);
+ Send(reply_message);
+}
+
+void AutomationProvider::SavePage(int tab_handle,
+ const FilePath& file_name,
+ const FilePath& dir_path,
+ int type,
+ bool* success) {
+ if (!tab_tracker_->ContainsHandle(tab_handle)) {
+ *success = false;
+ return;
+ }
+
+ NavigationController* nav = tab_tracker_->GetResource(tab_handle);
+ Browser* browser = FindAndActivateTab(nav);
+ DCHECK(browser);
+ if (!browser->command_updater()->IsCommandEnabled(IDC_SAVE_PAGE)) {
+ *success = false;
+ return;
+ }
+
+ SavePackage::SavePackageType save_type =
+ static_cast<SavePackage::SavePackageType>(type);
+ DCHECK(save_type >= SavePackage::SAVE_AS_ONLY_HTML &&
+ save_type <= SavePackage::SAVE_AS_COMPLETE_HTML);
+ nav->tab_contents()->SavePage(file_name, dir_path, save_type);
+
+ *success = true;
+}
+
+void AutomationProvider::GetAutocompleteEditText(int autocomplete_edit_handle,
+ bool* success,
+ std::wstring* text) {
+ *success = false;
+ if (autocomplete_edit_tracker_->ContainsHandle(autocomplete_edit_handle)) {
+ *text = autocomplete_edit_tracker_->GetResource(autocomplete_edit_handle)->
+ GetText();
+ *success = true;
+ }
+}
+
+void AutomationProvider::SetAutocompleteEditText(int autocomplete_edit_handle,
+ const std::wstring& text,
+ bool* success) {
+ *success = false;
+ if (autocomplete_edit_tracker_->ContainsHandle(autocomplete_edit_handle)) {
+ autocomplete_edit_tracker_->GetResource(autocomplete_edit_handle)->
+ SetUserText(text);
+ *success = true;
+ }
+}
+
+void AutomationProvider::AutocompleteEditGetMatches(
+ int autocomplete_edit_handle,
+ bool* success,
+ std::vector<AutocompleteMatchData>* matches) {
+ *success = false;
+ if (autocomplete_edit_tracker_->ContainsHandle(autocomplete_edit_handle)) {
+ const AutocompleteResult& result = autocomplete_edit_tracker_->
+ GetResource(autocomplete_edit_handle)->model()->result();
+ for (AutocompleteResult::const_iterator i = result.begin();
+ i != result.end(); ++i)
+ matches->push_back(AutocompleteMatchData(*i));
+ *success = true;
+ }
+}
+
+void AutomationProvider::AutocompleteEditIsQueryInProgress(
+ int autocomplete_edit_handle,
+ bool* success,
+ bool* query_in_progress) {
+ *success = false;
+ *query_in_progress = false;
+ if (autocomplete_edit_tracker_->ContainsHandle(autocomplete_edit_handle)) {
+ *query_in_progress = autocomplete_edit_tracker_->
+ GetResource(autocomplete_edit_handle)->model()->query_in_progress();
+ *success = true;
+ }
+}
+
+#if !defined(OS_MACOSX)
+
+#endif // !defined(OS_MACOSX)
+
+TabContents* AutomationProvider::GetTabContentsForHandle(
+ int handle, NavigationController** tab) {
+ if (tab_tracker_->ContainsHandle(handle)) {
+ NavigationController* nav_controller = tab_tracker_->GetResource(handle);
+ if (tab)
+ *tab = nav_controller;
+ return nav_controller->tab_contents();
+ }
+ return NULL;
+}
+
+TestingAutomationProvider::TestingAutomationProvider(Profile* profile)
+ : AutomationProvider(profile) {
+ BrowserList::AddObserver(this);
+ registrar_.Add(this, NotificationType::SESSION_END,
+ NotificationService::AllSources());
+}
+
+TestingAutomationProvider::~TestingAutomationProvider() {
+ BrowserList::RemoveObserver(this);
+}
+
+void TestingAutomationProvider::OnChannelError() {
+ BrowserList::CloseAllBrowsersAndExit();
+ AutomationProvider::OnChannelError();
+}
+
+void TestingAutomationProvider::OnBrowserAdded(const Browser* browser) {
+}
+
+void TestingAutomationProvider::OnBrowserRemoving(const Browser* browser) {
+ // For backwards compatibility with the testing automation interface, we
+ // want the automation provider (and hence the process) to go away when the
+ // last browser goes away.
+ if (BrowserList::size() == 1 && !CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kKeepAliveForTest)) {
+ // If you change this, update Observer for NotificationType::SESSION_END
+ // below.
+ MessageLoop::current()->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &TestingAutomationProvider::OnRemoveProvider));
+ }
+}
+
+void TestingAutomationProvider::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ DCHECK(type == NotificationType::SESSION_END);
+ // OnBrowserRemoving does a ReleaseLater. When session end is received we exit
+ // before the task runs resulting in this object not being deleted. This
+ // Release balance out the Release scheduled by OnBrowserRemoving.
+ Release();
+}
+
+void TestingAutomationProvider::OnRemoveProvider() {
+ AutomationProviderList::GetInstance()->RemoveProvider(this);
+}
+
+void AutomationProvider::GetInfoBarCount(int handle, int* count) {
+ *count = -1; // -1 means error.
+ if (tab_tracker_->ContainsHandle(handle)) {
+ NavigationController* nav_controller = tab_tracker_->GetResource(handle);
+ if (nav_controller)
+ *count = nav_controller->tab_contents()->infobar_delegate_count();
+ }
+}
+
+void AutomationProvider::ClickInfoBarAccept(int handle,
+ int info_bar_index,
+ bool wait_for_navigation,
+ IPC::Message* reply_message) {
+ bool success = false;
+ if (tab_tracker_->ContainsHandle(handle)) {
+ NavigationController* nav_controller = tab_tracker_->GetResource(handle);
+ if (nav_controller) {
+ int count = nav_controller->tab_contents()->infobar_delegate_count();
+ if (info_bar_index >= 0 && info_bar_index < count) {
+ if (wait_for_navigation) {
+ AddNavigationStatusListener(nav_controller, reply_message, 1, false);
+ }
+ InfoBarDelegate* delegate =
+ nav_controller->tab_contents()->GetInfoBarDelegateAt(
+ info_bar_index);
+ if (delegate->AsConfirmInfoBarDelegate())
+ delegate->AsConfirmInfoBarDelegate()->Accept();
+ success = true;
+ }
+ }
+ }
+
+ // This "!wait_for_navigation || !success condition" logic looks suspicious.
+ // It will send a failure message when success is true but
+ // |wait_for_navigation| is false.
+ // TODO(phajdan.jr): investgate whether the reply param (currently
+ // AUTOMATION_MSG_NAVIGATION_ERROR) should depend on success.
+ if (!wait_for_navigation || !success)
+ AutomationMsg_ClickInfoBarAccept::WriteReplyParams(
+ reply_message, AUTOMATION_MSG_NAVIGATION_ERROR);
+}
+
+void AutomationProvider::GetLastNavigationTime(int handle,
+ int64* last_navigation_time) {
+ Time time = tab_tracker_->GetLastNavigationTime(handle);
+ *last_navigation_time = time.ToInternalValue();
+}
+
+void AutomationProvider::WaitForNavigation(int handle,
+ int64 last_navigation_time,
+ IPC::Message* reply_message) {
+ NavigationController* controller = tab_tracker_->GetResource(handle);
+ Time time = tab_tracker_->GetLastNavigationTime(handle);
+
+ if (time.ToInternalValue() > last_navigation_time || !controller) {
+ AutomationMsg_WaitForNavigation::WriteReplyParams(reply_message,
+ controller == NULL ? AUTOMATION_MSG_NAVIGATION_ERROR :
+ AUTOMATION_MSG_NAVIGATION_SUCCESS);
+ Send(reply_message);
+ return;
+ }
+
+ AddNavigationStatusListener(controller, reply_message, 1, true);
+}
+
+void AutomationProvider::SetIntPreference(int handle,
+ const std::wstring& name,
+ int value,
+ bool* success) {
+ *success = false;
+ if (browser_tracker_->ContainsHandle(handle)) {
+ Browser* browser = browser_tracker_->GetResource(handle);
+ browser->profile()->GetPrefs()->SetInteger(name.c_str(), value);
+ *success = true;
+ }
+}
+
+void AutomationProvider::SetStringPreference(int handle,
+ const std::wstring& name,
+ const std::string& value,
+ bool* success) {
+ *success = false;
+ if (browser_tracker_->ContainsHandle(handle)) {
+ Browser* browser = browser_tracker_->GetResource(handle);
+ browser->profile()->GetPrefs()->SetString(name.c_str(), value);
+ *success = true;
+ }
+}
+
+void AutomationProvider::GetBooleanPreference(int handle,
+ const std::wstring& name,
+ bool* success,
+ bool* value) {
+ *success = false;
+ *value = false;
+ if (browser_tracker_->ContainsHandle(handle)) {
+ Browser* browser = browser_tracker_->GetResource(handle);
+ *value = browser->profile()->GetPrefs()->GetBoolean(name.c_str());
+ *success = true;
+ }
+}
+
+void AutomationProvider::SetBooleanPreference(int handle,
+ const std::wstring& name,
+ bool value,
+ bool* success) {
+ *success = false;
+ if (browser_tracker_->ContainsHandle(handle)) {
+ Browser* browser = browser_tracker_->GetResource(handle);
+ browser->profile()->GetPrefs()->SetBoolean(name.c_str(), value);
+ *success = true;
+ }
+}
+
+// Gets the current used encoding name of the page in the specified tab.
+void AutomationProvider::GetPageCurrentEncoding(
+ int tab_handle, std::string* current_encoding) {
+ if (tab_tracker_->ContainsHandle(tab_handle)) {
+ NavigationController* nav = tab_tracker_->GetResource(tab_handle);
+ Browser* browser = FindAndActivateTab(nav);
+ DCHECK(browser);
+
+ if (browser->command_updater()->IsCommandEnabled(IDC_ENCODING_MENU))
+ *current_encoding = nav->tab_contents()->encoding();
+ }
+}
+
+// Gets the current used encoding name of the page in the specified tab.
+void AutomationProvider::OverrideEncoding(int tab_handle,
+ const std::string& encoding_name,
+ bool* success) {
+ *success = false;
+ if (tab_tracker_->ContainsHandle(tab_handle)) {
+ NavigationController* nav = tab_tracker_->GetResource(tab_handle);
+ if (!nav)
+ return;
+ Browser* browser = FindAndActivateTab(nav);
+
+ // If the browser has UI, simulate what a user would do.
+ // Activate the tab and then click the encoding menu.
+ if (browser &&
+ browser->command_updater()->IsCommandEnabled(IDC_ENCODING_MENU)) {
+ int selected_encoding_id =
+ CharacterEncoding::GetCommandIdByCanonicalEncodingName(encoding_name);
+ if (selected_encoding_id) {
+ browser->OverrideEncoding(selected_encoding_id);
+ *success = true;
+ }
+ } else {
+ // There is no UI, Chrome probably runs as Chrome-Frame mode.
+ // Try to get TabContents and call its override_encoding method.
+ TabContents* contents = nav->tab_contents();
+ if (!contents)
+ return;
+ const std::string selected_encoding =
+ CharacterEncoding::GetCanonicalEncodingNameByAliasName(encoding_name);
+ if (selected_encoding.empty())
+ return;
+ contents->SetOverrideEncoding(selected_encoding);
+ }
+ }
+}
+
+void AutomationProvider::SavePackageShouldPromptUser(bool should_prompt) {
+ SavePackage::SetShouldPromptUser(should_prompt);
+}
+
+void AutomationProvider::GetBlockedPopupCount(int handle, int* count) {
+ *count = -1; // -1 is the error code
+ if (tab_tracker_->ContainsHandle(handle)) {
+ NavigationController* nav_controller = tab_tracker_->GetResource(handle);
+ TabContents* tab_contents = nav_controller->tab_contents();
+ if (tab_contents) {
+ BlockedPopupContainer* container =
+ tab_contents->blocked_popup_container();
+ if (container) {
+ *count = static_cast<int>(container->GetBlockedPopupCount());
+ } else {
+ // If we don't have a container, we don't have any blocked popups to
+ // contain!
+ *count = 0;
+ }
+ }
+ }
+}
+
+void AutomationProvider::SelectAll(int tab_handle) {
+ RenderViewHost* view = GetViewForTab(tab_handle);
+ if (!view) {
+ NOTREACHED();
+ return;
+ }
+
+ view->SelectAll();
+}
+
+void AutomationProvider::Cut(int tab_handle) {
+ RenderViewHost* view = GetViewForTab(tab_handle);
+ if (!view) {
+ NOTREACHED();
+ return;
+ }
+
+ view->Cut();
+}
+
+void AutomationProvider::Copy(int tab_handle) {
+ RenderViewHost* view = GetViewForTab(tab_handle);
+ if (!view) {
+ NOTREACHED();
+ return;
+ }
+
+ view->Copy();
+}
+
+void AutomationProvider::Paste(int tab_handle) {
+ RenderViewHost* view = GetViewForTab(tab_handle);
+ if (!view) {
+ NOTREACHED();
+ return;
+ }
+
+ view->Paste();
+}
+
+void AutomationProvider::ReloadAsync(int tab_handle) {
+ if (tab_tracker_->ContainsHandle(tab_handle)) {
+ NavigationController* tab = tab_tracker_->GetResource(tab_handle);
+ if (!tab) {
+ NOTREACHED();
+ return;
+ }
+
+ const bool check_for_repost = true;
+ tab->Reload(check_for_repost);
+ }
+}
+
+void AutomationProvider::StopAsync(int tab_handle) {
+ RenderViewHost* view = GetViewForTab(tab_handle);
+ if (!view) {
+ // We tolerate StopAsync being called even before a view has been created.
+ // So just log a warning instead of a NOTREACHED().
+ DLOG(WARNING) << "StopAsync: no view for handle " << tab_handle;
+ return;
+ }
+
+ view->Stop();
+}
+
+void AutomationProvider::OnSetPageFontSize(int tab_handle,
+ int font_size) {
+ AutomationPageFontSize automation_font_size =
+ static_cast<AutomationPageFontSize>(font_size);
+
+ if (automation_font_size < SMALLEST_FONT ||
+ automation_font_size > LARGEST_FONT) {
+ DLOG(ERROR) << "Invalid font size specified : "
+ << font_size;
+ return;
+ }
+
+ if (tab_tracker_->ContainsHandle(tab_handle)) {
+ NavigationController* tab = tab_tracker_->GetResource(tab_handle);
+ DCHECK(tab != NULL);
+ if (tab && tab->tab_contents()) {
+ DCHECK(tab->tab_contents()->profile() != NULL);
+ tab->tab_contents()->profile()->GetPrefs()->SetInteger(
+ prefs::kWebKitDefaultFontSize, font_size);
+ }
+ }
+}
+
+void AutomationProvider::RemoveBrowsingData(int remove_mask) {
+ BrowsingDataRemover* remover;
+ remover = new BrowsingDataRemover(profile(),
+ BrowsingDataRemover::EVERYTHING, // All time periods.
+ base::Time());
+ remover->Remove(remove_mask);
+ // BrowsingDataRemover deletes itself.
+}
+
+void AutomationProvider::WaitForBrowserWindowCountToBecome(
+ int target_count, IPC::Message* reply_message) {
+ if (static_cast<int>(BrowserList::size()) == target_count) {
+ AutomationMsg_WaitForBrowserWindowCountToBecome::WriteReplyParams(
+ reply_message, true);
+ Send(reply_message);
+ return;
+ }
+
+ // Set up an observer (it will delete itself).
+ new BrowserCountChangeNotificationObserver(target_count, this, reply_message);
+}
+
+void AutomationProvider::WaitForAppModalDialogToBeShown(
+ IPC::Message* reply_message) {
+ if (Singleton<AppModalDialogQueue>()->HasActiveDialog()) {
+ AutomationMsg_WaitForAppModalDialogToBeShown::WriteReplyParams(
+ reply_message, true);
+ Send(reply_message);
+ return;
+ }
+
+ // Set up an observer (it will delete itself).
+ new AppModalDialogShownObserver(this, reply_message);
+}
+
+void AutomationProvider::GoBackBlockUntilNavigationsComplete(
+ int handle, int number_of_navigations, IPC::Message* reply_message) {
+ if (tab_tracker_->ContainsHandle(handle)) {
+ NavigationController* tab = tab_tracker_->GetResource(handle);
+ Browser* browser = FindAndActivateTab(tab);
+ if (browser && browser->command_updater()->IsCommandEnabled(IDC_BACK)) {
+ AddNavigationStatusListener(tab, reply_message, number_of_navigations,
+ false);
+ browser->GoBack(CURRENT_TAB);
+ return;
+ }
+ }
+
+ AutomationMsg_GoBackBlockUntilNavigationsComplete::WriteReplyParams(
+ reply_message, AUTOMATION_MSG_NAVIGATION_ERROR);
+ Send(reply_message);
+}
+
+void AutomationProvider::GoForwardBlockUntilNavigationsComplete(
+ int handle, int number_of_navigations, IPC::Message* reply_message) {
+ if (tab_tracker_->ContainsHandle(handle)) {
+ NavigationController* tab = tab_tracker_->GetResource(handle);
+ Browser* browser = FindAndActivateTab(tab);
+ if (browser && browser->command_updater()->IsCommandEnabled(IDC_FORWARD)) {
+ AddNavigationStatusListener(tab, reply_message, number_of_navigations,
+ false);
+ browser->GoForward(CURRENT_TAB);
+ return;
+ }
+ }
+
+ AutomationMsg_GoForwardBlockUntilNavigationsComplete::WriteReplyParams(
+ reply_message, AUTOMATION_MSG_NAVIGATION_ERROR);
+ Send(reply_message);
+}
+
+RenderViewHost* AutomationProvider::GetViewForTab(int tab_handle) {
+ if (tab_tracker_->ContainsHandle(tab_handle)) {
+ NavigationController* tab = tab_tracker_->GetResource(tab_handle);
+ if (!tab) {
+ NOTREACHED();
+ return NULL;
+ }
+
+ TabContents* tab_contents = tab->tab_contents();
+ if (!tab_contents) {
+ NOTREACHED();
+ return NULL;
+ }
+
+ RenderViewHost* view_host = tab_contents->render_view_host();
+ return view_host;
+ }
+
+ return NULL;
+}
+
+void AutomationProvider::GetBrowserForWindow(int window_handle,
+ bool* success,
+ int* browser_handle) {
+ *success = false;
+ *browser_handle = 0;
+
+ gfx::NativeWindow window = window_tracker_->GetResource(window_handle);
+ if (!window)
+ return;
+
+ BrowserList::const_iterator iter = BrowserList::begin();
+ for (;iter != BrowserList::end(); ++iter) {
+ gfx::NativeWindow this_window = (*iter)->window()->GetNativeHandle();
+ if (window == this_window) {
+ // Add() returns the existing handle for the resource if any.
+ *browser_handle = browser_tracker_->Add(*iter);
+ *success = true;
+ return;
+ }
+ }
+}
+
+void AutomationProvider::InstallExtension(const FilePath& crx_path,
+ IPC::Message* reply_message) {
+ ExtensionsService* service = profile_->GetExtensionsService();
+ if (service) {
+ // The observer will delete itself when done.
+ new ExtensionInstallNotificationObserver(this,
+ AutomationMsg_InstallExtension::ID,
+ reply_message);
+
+ const FilePath& install_dir = service->install_directory();
+ scoped_refptr<CrxInstaller> installer(
+ new CrxInstaller(install_dir,
+ service,
+ NULL)); // silent install, no UI
+ installer->set_allow_privilege_increase(true);
+ installer->InstallCrx(crx_path);
+ } else {
+ AutomationMsg_InstallExtension::WriteReplyParams(
+ reply_message, AUTOMATION_MSG_EXTENSION_INSTALL_FAILED);
+ Send(reply_message);
+ }
+}
+
+void AutomationProvider::LoadExpandedExtension(
+ const FilePath& extension_dir,
+ IPC::Message* reply_message) {
+ if (profile_->GetExtensionsService()) {
+ // The observer will delete itself when done.
+ new ExtensionInstallNotificationObserver(
+ this,
+ AutomationMsg_LoadExpandedExtension::ID,
+ reply_message);
+
+ profile_->GetExtensionsService()->LoadExtension(extension_dir);
+ } else {
+ AutomationMsg_LoadExpandedExtension::WriteReplyParams(
+ reply_message, AUTOMATION_MSG_EXTENSION_INSTALL_FAILED);
+ Send(reply_message);
+ }
+}
+
+void AutomationProvider::GetEnabledExtensions(
+ std::vector<FilePath>* result) {
+ ExtensionsService* service = profile_->GetExtensionsService();
+ DCHECK(service);
+ if (service->extensions_enabled()) {
+ const ExtensionList* extensions = service->extensions();
+ DCHECK(extensions);
+ for (size_t i = 0; i < extensions->size(); ++i) {
+ Extension* extension = (*extensions)[i];
+ DCHECK(extension);
+ if (extension->location() == Extension::INTERNAL ||
+ extension->location() == Extension::LOAD) {
+ result->push_back(extension->path());
+ }
+ }
+ }
+}
+
+void AutomationProvider::WaitForExtensionTestResult(
+ IPC::Message* reply_message) {
+ DCHECK(reply_message_ == NULL);
+ reply_message_ = reply_message;
+ // Call MaybeSendResult, because the result might have come in before
+ // we were waiting on it.
+ extension_test_result_observer_->MaybeSendResult();
+}
+
+void AutomationProvider::InstallExtensionAndGetHandle(
+ const FilePath& crx_path, bool with_ui, IPC::Message* reply_message) {
+ ExtensionsService* service = profile_->GetExtensionsService();
+ ExtensionProcessManager* manager = profile_->GetExtensionProcessManager();
+ if (service && manager) {
+ // The observer will delete itself when done.
+ new ExtensionReadyNotificationObserver(
+ manager,
+ this,
+ AutomationMsg_InstallExtensionAndGetHandle::ID,
+ reply_message);
+
+ ExtensionInstallUI* client =
+ (with_ui ? new ExtensionInstallUI(profile_) : NULL);
+ scoped_refptr<CrxInstaller> installer(
+ new CrxInstaller(service->install_directory(),
+ service,
+ client));
+ installer->set_allow_privilege_increase(true);
+ installer->InstallCrx(crx_path);
+ } else {
+ AutomationMsg_InstallExtensionAndGetHandle::WriteReplyParams(
+ reply_message, 0);
+ Send(reply_message);
+ }
+}
+
+void AutomationProvider::UninstallExtension(int extension_handle,
+ bool* success) {
+ *success = false;
+ Extension* extension = GetExtension(extension_handle);
+ ExtensionsService* service = profile_->GetExtensionsService();
+ if (extension && service) {
+ ExtensionUnloadNotificationObserver observer;
+ service->UninstallExtension(extension->id(), false);
+ // The extension unload notification should have been sent synchronously
+ // with the uninstall. Just to be safe, check that it was received.
+ *success = observer.did_receive_unload_notification();
+ }
+}
+
+void AutomationProvider::EnableExtension(int extension_handle,
+ IPC::Message* reply_message) {
+ Extension* extension = GetDisabledExtension(extension_handle);
+ ExtensionsService* service = profile_->GetExtensionsService();
+ ExtensionProcessManager* manager = profile_->GetExtensionProcessManager();
+ // Only enable if this extension is disabled.
+ if (extension && service && manager) {
+ // The observer will delete itself when done.
+ new ExtensionReadyNotificationObserver(
+ manager,
+ this,
+ AutomationMsg_EnableExtension::ID,
+ reply_message);
+ service->EnableExtension(extension->id());
+ } else {
+ AutomationMsg_EnableExtension::WriteReplyParams(reply_message, false);
+ Send(reply_message);
+ }
+}
+
+void AutomationProvider::DisableExtension(int extension_handle,
+ bool* success) {
+ *success = false;
+ Extension* extension = GetEnabledExtension(extension_handle);
+ ExtensionsService* service = profile_->GetExtensionsService();
+ if (extension && service) {
+ ExtensionUnloadNotificationObserver observer;
+ service->DisableExtension(extension->id());
+ // The extension unload notification should have been sent synchronously
+ // with the disable. Just to be safe, check that it was received.
+ *success = observer.did_receive_unload_notification();
+ }
+}
+
+void AutomationProvider::ExecuteExtensionActionInActiveTabAsync(
+ int extension_handle, int browser_handle,
+ IPC::Message* reply_message) {
+ bool success = false;
+ Extension* extension = GetEnabledExtension(extension_handle);
+ ExtensionsService* service = profile_->GetExtensionsService();
+ ExtensionMessageService* message_service =
+ profile_->GetExtensionMessageService();
+ Browser* browser = browser_tracker_->GetResource(browser_handle);
+ if (extension && service && message_service && browser) {
+ int tab_id = ExtensionTabUtil::GetTabId(browser->GetSelectedTabContents());
+ if (extension->page_action()) {
+ ExtensionBrowserEventRouter::GetInstance()->PageActionExecuted(
+ browser->profile(), extension->id(), "action", tab_id, "", 1);
+ success = true;
+ } else if (extension->browser_action()) {
+ ExtensionBrowserEventRouter::GetInstance()->BrowserActionExecuted(
+ browser->profile(), extension->id(), browser);
+ success = true;
+ }
+ }
+ AutomationMsg_ExecuteExtensionActionInActiveTabAsync::WriteReplyParams(
+ reply_message, success);
+ Send(reply_message);
+}
+
+void AutomationProvider::MoveExtensionBrowserAction(
+ int extension_handle, int index, bool* success) {
+ *success = false;
+ Extension* extension = GetEnabledExtension(extension_handle);
+ ExtensionsService* service = profile_->GetExtensionsService();
+ if (extension && service) {
+ ExtensionToolbarModel* toolbar = service->toolbar_model();
+ if (toolbar) {
+ if (index >= 0 && index < static_cast<int>(toolbar->size())) {
+ toolbar->MoveBrowserAction(extension, index);
+ *success = true;
+ } else {
+ DLOG(WARNING) << "Attempted to move browser action to invalid index.";
+ }
+ }
+ }
+}
+
+void AutomationProvider::GetExtensionProperty(
+ int extension_handle,
+ AutomationMsg_ExtensionProperty type,
+ bool* success,
+ std::string* value) {
+ *success = false;
+ Extension* extension = GetExtension(extension_handle);
+ ExtensionsService* service = profile_->GetExtensionsService();
+ if (extension && service) {
+ ExtensionToolbarModel* toolbar = service->toolbar_model();
+ int found_index = -1;
+ int index = 0;
+ switch (type) {
+ case AUTOMATION_MSG_EXTENSION_ID:
+ *value = extension->id();
+ *success = true;
+ break;
+ case AUTOMATION_MSG_EXTENSION_NAME:
+ *value = extension->name();
+ *success = true;
+ break;
+ case AUTOMATION_MSG_EXTENSION_VERSION:
+ *value = extension->VersionString();
+ *success = true;
+ break;
+ case AUTOMATION_MSG_EXTENSION_BROWSER_ACTION_INDEX:
+ if (toolbar) {
+ for (ExtensionList::const_iterator iter = toolbar->begin();
+ iter != toolbar->end(); iter++) {
+ // Skip this extension if we are in incognito mode
+ // and it is not incognito-enabled.
+ if (profile_->IsOffTheRecord() &&
+ !service->IsIncognitoEnabled(*iter))
+ continue;
+ if (*iter == extension) {
+ found_index = index;
+ break;
+ }
+ index++;
+ }
+ *value = IntToString(found_index);
+ *success = true;
+ }
+ break;
+ default:
+ LOG(WARNING) << "Trying to get undefined extension property";
+ break;
+ }
+ }
+}
+
+void AutomationProvider::SaveAsAsync(int tab_handle) {
+ NavigationController* tab = NULL;
+ TabContents* tab_contents = GetTabContentsForHandle(tab_handle, &tab);
+ if (tab_contents)
+ tab_contents->OnSavePage();
+}
+
+void AutomationProvider::SetContentSetting(
+ int handle,
+ const std::string& host,
+ ContentSettingsType content_type,
+ ContentSetting setting,
+ bool* success) {
+ *success = false;
+ if (browser_tracker_->ContainsHandle(handle)) {
+ Browser* browser = browser_tracker_->GetResource(handle);
+ HostContentSettingsMap* map =
+ browser->profile()->GetHostContentSettingsMap();
+ if (host.empty()) {
+ map->SetDefaultContentSetting(content_type, setting);
+ } else {
+ map->SetContentSetting(HostContentSettingsMap::Pattern(host),
+ content_type, setting);
+ }
+ *success = true;
+ }
+}
+
+#if !defined(TOOLKIT_VIEWS)
+void AutomationProvider::GetFocusedViewID(int handle, int* view_id) {
+ NOTIMPLEMENTED();
+};
+
+void AutomationProvider::WaitForFocusedViewIDToChange(
+ int handle, int previous_view_id, IPC::Message* reply_message) {
+ NOTIMPLEMENTED();
+}
+
+void AutomationProvider::StartTrackingPopupMenus(
+ int browser_handle, bool* success) {
+ NOTIMPLEMENTED();
+}
+
+void AutomationProvider::WaitForPopupMenuToOpen(IPC::Message* reply_message) {
+ NOTIMPLEMENTED();
+}
+#endif // !defined(TOOLKIT_VIEWS)
+
+void AutomationProvider::ResetToDefaultTheme() {
+ profile_->ClearTheme();
+}
diff --git a/chrome/browser/automation/automation_provider.h b/chrome/browser/automation/automation_provider.h
new file mode 100644
index 0000000..f166771
--- /dev/null
+++ b/chrome/browser/automation/automation_provider.h
@@ -0,0 +1,953 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This implements a browser-side endpoint for UI automation activity.
+// The client-side endpoint is implemented by AutomationProxy.
+// The entire lifetime of this object should be contained within that of
+// the BrowserProcess, and in particular the NotificationService that's
+// hung off of it.
+
+#ifndef CHROME_BROWSER_AUTOMATION_AUTOMATION_PROVIDER_H_
+#define CHROME_BROWSER_AUTOMATION_AUTOMATION_PROVIDER_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "chrome/browser/autofill/field_types.h"
+#include "chrome/browser/browser_list.h"
+#include "chrome/browser/history/history.h"
+#include "chrome/browser/tab_contents/navigation_entry.h"
+#include "chrome/common/content_settings.h"
+#include "chrome/common/notification_registrar.h"
+#include "chrome/test/automation/automation_constants.h"
+#include "ipc/ipc_message.h"
+#include "ipc/ipc_channel.h"
+#if defined(OS_WIN)
+#include "views/event.h"
+#endif // defined(OS_WIN)
+
+struct AutomationMsg_Find_Params;
+class PopupMenuWaiter;
+
+namespace IPC {
+struct Reposition_Params;
+struct ExternalTabSettings;
+class ChannelProxy;
+}
+
+class AutoFillProfile;
+class AutomationAutocompleteEditTracker;
+class AutomationBrowserTracker;
+class AutomationExtensionTracker;
+class AutomationResourceMessageFilter;
+class AutomationTabTracker;
+class AutomationWindowTracker;
+class CreditCard;
+class DictionaryValue;
+class Extension;
+class ExtensionPortContainer;
+class ExtensionTestResultNotificationObserver;
+class ExternalTabContainer;
+class LoginHandler;
+class MetricEventDurationObserver;
+class InitialLoadObserver;
+class NavigationControllerRestoredObserver;
+struct AutocompleteMatchData;
+
+namespace gfx {
+class Point;
+}
+
+class AutomationProvider : public base::RefCounted<AutomationProvider>,
+ public IPC::Channel::Listener,
+ public IPC::Message::Sender {
+ public:
+ explicit AutomationProvider(Profile* profile);
+
+ Profile* profile() const { return profile_; }
+
+ // Establishes a connection to an automation client, if present.
+ // An AutomationProxy should be established (probably in a different process)
+ // before calling this.
+ void ConnectToChannel(const std::string& channel_id);
+
+ // Sets the number of tabs that we expect; when this number of tabs has
+ // loaded, an AutomationMsg_InitialLoadsComplete message is sent.
+ void SetExpectedTabCount(size_t expected_tabs);
+
+ // Add a listener for navigation status notification. Currently only
+ // navigation completion is observed; when the |number_of_navigations|
+ // complete, the completed_response object is sent; if the server requires
+ // authentication, we instead send the auth_needed_response object. A pointer
+ // to the added navigation observer is returned. This object should NOT be
+ // deleted and should be released by calling the corresponding
+ // RemoveNavigationStatusListener method.
+ NotificationObserver* AddNavigationStatusListener(
+ NavigationController* tab, IPC::Message* reply_message,
+ int number_of_navigations, bool include_current_navigation);
+
+ void RemoveNavigationStatusListener(NotificationObserver* obs);
+
+ // Add an observer for the TabStrip. Currently only Tab append is observed. A
+ // navigation listener is created on successful notification of tab append. A
+ // pointer to the added navigation observer is returned. This object should
+ // NOT be deleted and should be released by calling the corresponding
+ // RemoveTabStripObserver method.
+ NotificationObserver* AddTabStripObserver(Browser* parent,
+ IPC::Message* reply_message);
+ void RemoveTabStripObserver(NotificationObserver* obs);
+
+ // Get the index of a particular NavigationController object
+ // in the given parent window. This method uses
+ // TabStrip::GetIndexForNavigationController to get the index.
+ int GetIndexForNavigationController(const NavigationController* controller,
+ const Browser* parent) const;
+
+ // Add or remove a non-owning reference to a tab's LoginHandler. This is for
+ // when a login prompt is shown for HTTP/FTP authentication.
+ // TODO(mpcomplete): The login handling is a fairly special purpose feature.
+ // Eventually we'll probably want ways to interact with the ChromeView of the
+ // login window in a generic manner, such that it can be used for anything,
+ // not just logins.
+ void AddLoginHandler(NavigationController* tab, LoginHandler* handler);
+ void RemoveLoginHandler(NavigationController* tab);
+
+ // Add an extension port container.
+ // Takes ownership of the container.
+ void AddPortContainer(ExtensionPortContainer* port);
+ // Remove and delete the port container.
+ void RemovePortContainer(ExtensionPortContainer* port);
+ // Get the port container for the given port id.
+ ExtensionPortContainer* GetPortContainer(int port_id) const;
+
+ // IPC implementations
+ virtual bool Send(IPC::Message* msg);
+ virtual void OnMessageReceived(const IPC::Message& msg);
+ virtual void OnChannelError();
+
+ // Received response from inspector controller
+ void ReceivedInspectElementResponse(int num_resources);
+
+ IPC::Message* reply_message_release() {
+ IPC::Message* reply_message = reply_message_;
+ reply_message_ = NULL;
+ return reply_message;
+ }
+
+ // Adds the extension passed in to the extension tracker, and returns
+ // the associated handle. If the tracker already contains the extension,
+ // the handle is simply returned.
+ int AddExtension(Extension* extension);
+
+#if defined(OS_WIN)
+ // Adds the external tab passed in to the tab tracker.
+ bool AddExternalTab(ExternalTabContainer* external_tab);
+#endif
+
+ protected:
+ friend class base::RefCounted<AutomationProvider>;
+ friend class PopupMenuWaiter;
+ virtual ~AutomationProvider();
+
+ private:
+ // IPC Message callbacks.
+ void CloseBrowser(int handle, IPC::Message* reply_message);
+ void CloseBrowserAsync(int browser_handle);
+ void ActivateTab(int handle, int at_index, int* status);
+ void AppendTab(int handle, const GURL& url, IPC::Message* reply_message);
+ void CloseTab(int tab_handle, bool wait_until_closed,
+ IPC::Message* reply_message);
+
+ void GetActiveTabIndex(int handle, int* active_tab_index);
+ void GetCookies(const GURL& url, int handle, int* value_size,
+ std::string* value);
+ void SetCookie(const GURL& url,
+ const std::string value,
+ int handle,
+ int* response_value);
+ void DeleteCookie(const GURL& url, const std::string& cookie_name,
+ int handle, bool* success);
+ void ShowCollectedCookiesDialog(int handle, bool* success);
+ void GetBrowserWindowCount(int* window_count);
+ void GetBrowserLocale(string16* locale);
+ void GetNormalBrowserWindowCount(int* window_count);
+ void GetShowingAppModalDialog(bool* showing_dialog, int* dialog_button);
+ void ClickAppModalDialogButton(int button, bool* success);
+ void ShutdownSessionService(int handle, bool* result);
+ // Be aware that the browser window returned might be of non TYPE_NORMAL
+ // or in incognito mode.
+ void GetBrowserWindow(int index, int* handle);
+ void FindNormalBrowserWindow(int* handle);
+ void GetLastActiveBrowserWindow(int* handle);
+ void GetActiveWindow(int* handle);
+ void ExecuteBrowserCommandAsync(int handle, int command, bool* success);
+ void ExecuteBrowserCommand(int handle, int command,
+ IPC::Message* reply_message);
+ void TerminateSession(int handle, bool* success);
+ void WindowGetViewBounds(int handle, int view_id, bool screen_coordinates,
+ bool* success, gfx::Rect* bounds);
+ void WindowSimulateDrag(int handle,
+ std::vector<gfx::Point> drag_path,
+ int flags,
+ bool press_escape_en_route,
+ IPC::Message* reply_message);
+ void WindowSimulateClick(const IPC::Message& message,
+ int handle,
+ const gfx::Point& click,
+ int flags);
+ void WindowSimulateMouseMove(const IPC::Message& message,
+ int handle,
+ const gfx::Point& location);
+ void WindowSimulateKeyPress(const IPC::Message& message,
+ int handle,
+ int key,
+ int flags);
+ void GetWindowBounds(int handle, gfx::Rect* bounds, bool* result);
+ void SetWindowBounds(int handle, const gfx::Rect& bounds, bool* result);
+ void SetWindowVisible(int handle, bool visible, bool* result);
+ void IsWindowActive(int handle, bool* success, bool* is_active);
+ void ActivateWindow(int handle);
+ void IsWindowMaximized(int handle, bool* is_maximized, bool* success);
+
+ void GetTabCount(int handle, int* tab_count);
+ void GetType(int handle, int* type_as_int);
+ void GetTab(int win_handle, int tab_index, int* tab_handle);
+#if defined(OS_WIN)
+ // TODO(port): Replace HWND.
+ void GetTabHWND(int handle, HWND* tab_hwnd);
+#endif // defined(OS_WIN)
+ void GetTabProcessID(int handle, int* process_id);
+ void GetTabTitle(int handle, int* title_string_size, std::wstring* title);
+ void GetTabIndex(int handle, int* tabstrip_index);
+ void GetTabURL(int handle, bool* success, GURL* url);
+ void HandleUnused(const IPC::Message& message, int handle);
+ void NavigateToURL(int handle, const GURL& url, IPC::Message* reply_message);
+ void NavigateToURLBlockUntilNavigationsComplete(int handle, const GURL& url,
+ int number_of_navigations,
+ IPC::Message* reply_message);
+ void NavigationAsync(int handle, const GURL& url, bool* status);
+ void NavigationAsyncWithDisposition(int handle,
+ const GURL& url,
+ WindowOpenDisposition disposition,
+ bool* status);
+ void GoBack(int handle, IPC::Message* reply_message);
+ void GoForward(int handle, IPC::Message* reply_message);
+ void Reload(int handle, IPC::Message* reply_message);
+ void SetAuth(int tab_handle, const std::wstring& username,
+ const std::wstring& password, IPC::Message* reply_message);
+ void CancelAuth(int tab_handle, IPC::Message* reply_message);
+ void NeedsAuth(int tab_handle, bool* needs_auth);
+ void GetRedirectsFrom(int tab_handle,
+ const GURL& source_url,
+ IPC::Message* reply_message);
+ void ExecuteJavascript(int handle,
+ const std::wstring& frame_xpath,
+ const std::wstring& script,
+ IPC::Message* reply_message);
+ void GetShelfVisibility(int handle, bool* visible);
+ void SetShelfVisibility(int handle, bool visible);
+ void SetFilteredInet(const IPC::Message& message, bool enabled);
+ void GetFilteredInetHitCount(int* hit_count);
+ void SetProxyConfig(const std::string& new_proxy_config);
+ void IsFullscreen(int handle, bool* is_fullscreen);
+ void GetFullscreenBubbleVisibility(int handle, bool* is_visible);
+ void SetContentSetting(int handle,
+ const std::string& host,
+ ContentSettingsType content_type,
+ ContentSetting setting,
+ bool* success);
+
+ void GetFocusedViewID(int handle, int* view_id);
+
+ // Helper function to find the browser window that contains a given
+ // NavigationController and activate that tab.
+ // Returns the Browser if found.
+ Browser* FindAndActivateTab(NavigationController* contents);
+
+ // Deprecated.
+ void ApplyAccelerator(int handle, int id);
+
+ void GetConstrainedWindowCount(int handle, int* count);
+
+ // This function has been deprecated, please use HandleFindRequest.
+ void HandleFindInPageRequest(int handle,
+ const std::wstring& find_request,
+ int forward,
+ int match_case,
+ int* active_ordinal,
+ int* matches_found);
+
+ // Responds to the FindInPage request, retrieves the search query parameters,
+ // launches an observer to listen for results and issues a StartFind request.
+ void HandleFindRequest(int handle,
+ const AutomationMsg_Find_Params& params,
+ IPC::Message* reply_message);
+
+ // Responds to requests to open the FindInPage window.
+ void HandleOpenFindInPageRequest(const IPC::Message& message,
+ int handle);
+
+ // Get the visibility state of the Find window.
+ void GetFindWindowVisibility(int handle, bool* visible);
+
+ // Responds to requests to find the location of the Find window.
+ void HandleFindWindowLocationRequest(int handle, int* x, int* y);
+
+ // Get the visibility state of the Bookmark bar.
+ void GetBookmarkBarVisibility(int handle, bool* visible, bool* animating);
+
+ // Get the bookmarks as a JSON string.
+ void GetBookmarksAsJSON(int handle, std::string* bookmarks_as_json,
+ bool *success);
+
+ // Wait for the bookmark model to load.
+ void WaitForBookmarkModelToLoad(int handle, IPC::Message* reply_message);
+
+ // Set |loaded| to true if the bookmark model has loaded, else false.
+ void BookmarkModelHasLoaded(int handle, bool* loaded);
+
+ // Editing, modification, and removal of bookmarks.
+ // Bookmarks are referenced by id.
+ void AddBookmarkGroup(int handle,
+ int64 parent_id, int index, std::wstring title,
+ bool* success);
+ void AddBookmarkURL(int handle,
+ int64 parent_id, int index,
+ std::wstring title, const GURL& url,
+ bool* success);
+ void ReparentBookmark(int handle,
+ int64 id, int64 new_parent_id, int index,
+ bool* success);
+ void SetBookmarkTitle(int handle,
+ int64 id, std::wstring title,
+ bool* success);
+ void SetBookmarkURL(int handle,
+ int64 id, const GURL& url,
+ bool* success);
+ void RemoveBookmark(int handle,
+ int64 id,
+ bool* success);
+
+ // Set window dimensions.
+ // Uses the JSON interface for input/output.
+ void SetWindowDimensions(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message);
+
+ // Get info about infobars in the given TabContents object.
+ // This includes info about the type of infobars, the message text,
+ // buttons, etc.
+ // Caller owns the returned object.
+ ListValue* GetInfobarsInfo(TabContents* tc);
+
+ // Wait for infobar count in a given tab to become a certain value.
+ // Uses the JSON interface for input/output.
+ void WaitForInfobarCount(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message);
+
+ // Get info about the chromium/chrome in use.
+ // This includes things like version, executable name, executable path.
+ // Uses the JSON interface for input/output.
+ void GetBrowserInfo(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message);
+
+ // Get info about downloads. This includes only ones that have been
+ // registered by the history system.
+ // Uses the JSON interface for input/output.
+ void GetDownloadsInfo(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message);
+
+ // Wait for all downloads to complete.
+ // Uses the JSON interface for input/output.
+ void WaitForDownloadsToComplete(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message);
+
+ // Get info about history.
+ // Uses the JSON interface for input/output.
+ void GetHistoryInfo(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message);
+
+ // Add an item to the history service.
+ // Uses the JSON interface for input/output.
+ void AddHistoryItem(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message);
+
+ // Get info about preferences.
+ // Uses the JSON interface for input/output.
+ void GetPrefsInfo(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message);
+
+ // Set prefs.
+ // Uses the JSON interface for input/output.
+ void SetPrefs(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message);
+
+ // Return load times of initial tabs.
+ // Uses the JSON interface for input/output.
+ // Only includes tabs from command line arguments or session restore.
+ // See declaration of InitialLoadObserver in automation_provider_observers.h
+ // for example response.
+ void GetInitialLoadTimes(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message);
+
+ // Get info about plugins.
+ // Uses the JSON interface for input/output.
+ void GetPluginsInfo(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message);
+
+ // Enable a plugin.
+ // Uses the JSON interface for input/output.
+ void EnablePlugin(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message);
+
+ // Disable a plugin.
+ // Uses the JSON interface for input/output.
+ void DisablePlugin(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message);
+
+ // Get info about omnibox.
+ // Contains data about the matches (url, content, description)
+ // in the omnibox popup, the text in the omnibox.
+ // Uses the JSON interface for input/output.
+ void GetOmniboxInfo(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message);
+
+ // Set text in the omnibox. This sets focus to the omnibox.
+ // Uses the JSON interface for input/output.
+ void SetOmniboxText(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message);
+
+ // Move omnibox popup selection up or down.
+ // Uses the JSON interface for input/output.
+ void OmniboxMovePopupSelection(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message);
+
+ // Accept the current string of text in the omnibox.
+ // This is equivalent to clicking or hiting enter on a popup selection.
+ // Blocks until the page loads.
+ // Uses the JSON interface for input/output.
+ void OmniboxAcceptInput(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message);
+
+ // Save the contents of a tab into a file.
+ // Uses the JSON interface for input/output.
+ void SaveTabContents(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message);
+
+ // Import the given settings from the given browser.
+ // Uses the JSON interface for input/output.
+ void ImportSettings(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message);
+
+ // Add a new username-password combination to the saved passwords.
+ // Uses the JSON interface for input/output.
+ void AddSavedPassword(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message);
+
+ // Return the saved username/password combinations.
+ // Uses the JSON interface for input/output.
+ void GetSavedPasswords(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message);
+
+ // Clear the specified browsing data. This call provides similar
+ // functionality to RemoveBrowsingData but is synchronous.
+ // Uses the JSON interface for input/output.
+ void ClearBrowsingData(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message);
+
+ // Get info about theme.
+ // Uses the JSON interface for input/output.
+ void GetThemeInfo(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message);
+
+ // Get the profiles that are currently saved to the DB.
+ // Uses the JSON interface for input/output.
+ void GetAutoFillProfile(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message);
+
+ // Fill in an AutoFillProfile with the given profile information.
+ // Uses the JSON interface for input/output.
+ void FillAutoFillProfile(Browser* browser,
+ DictionaryValue* args,
+ IPC::Message* reply_message);
+
+ // Translate DictionaryValues of autofill profiles and credit cards to the
+ // data structure used in the browser.
+ // Args:
+ // profiles/cards: the ListValue of profiles/credit cards to translate.
+ // error_message: a pointer to the return string in case of error.
+ static std::vector<AutoFillProfile> GetAutoFillProfilesFromList(
+ const ListValue& profiles, std::string* error_message);
+ static std::vector<CreditCard> GetCreditCardsFromList(
+ const ListValue& cards, std::string* error_message);
+
+ // The opposite of the above: translates from the internal data structure
+ // for profiles and credit cards to a ListValue of DictionaryValues. The
+ // caller owns the returned object.
+ static ListValue* GetListFromAutoFillProfiles(
+ std::vector<AutoFillProfile*> autofill_profiles);
+ static ListValue* GetListFromCreditCards(
+ std::vector<CreditCard*> credit_cards);
+
+ // Return the map from the internal data representation to the string value
+ // of auto fill fields and credit card fields.
+ static std::map<AutoFillFieldType, std::wstring>
+ GetAutoFillFieldToStringMap();
+ static std::map<AutoFillFieldType, std::wstring>
+ GetCreditCardFieldToStringMap();
+
+ // Generic pattern for pyautolib
+ // Uses the JSON interface for input/output.
+ void SendJSONRequest(int handle,
+ std::string json_request,
+ IPC::Message* reply_message);
+
+ // Method ptr for json handlers.
+ // Uses the JSON interface for input/output.
+ typedef void (AutomationProvider::*JsonHandler)(Browser* browser,
+ DictionaryValue*,
+ IPC::Message*);
+
+ // Responds to InspectElement request
+ void HandleInspectElementRequest(int handle,
+ int x,
+ int y,
+ IPC::Message* reply_message);
+
+ void GetDownloadDirectory(int handle, FilePath* download_directory);
+
+ // Retrieves a Browser from a Window and vice-versa.
+ void GetWindowForBrowser(int window_handle, bool* success, int* handle);
+ void GetBrowserForWindow(int window_handle, bool* success,
+ int* browser_handle);
+
+ void GetAutocompleteEditForBrowser(int browser_handle, bool* success,
+ int* autocomplete_edit_handle);
+
+ // If |show| is true, call Show() on the new window after creating it.
+ void OpenNewBrowserWindow(bool show, IPC::Message* reply_message);
+ void OpenNewBrowserWindowOfType(int type,
+ bool show,
+ IPC::Message* reply_message);
+
+ void ShowInterstitialPage(int tab_handle,
+ const std::string& html_text,
+ IPC::Message* reply_message);
+ void HideInterstitialPage(int tab_handle, bool* success);
+
+ void OnSetPageFontSize(int tab_handle, int font_size);
+
+ // See browsing_data_remover.h for explanation of bitmap fields.
+ void RemoveBrowsingData(int remove_mask);
+
+ void InstallExtension(const FilePath& crx_path,
+ IPC::Message* reply_message);
+
+ void LoadExpandedExtension(const FilePath& extension_dir,
+ IPC::Message* reply_message);
+
+ void GetEnabledExtensions(std::vector<FilePath>* result);
+
+ void WaitForExtensionTestResult(IPC::Message* reply_message);
+
+ void InstallExtensionAndGetHandle(const FilePath& crx_path,
+ bool with_ui,
+ IPC::Message* reply_message);
+
+ void UninstallExtension(int extension_handle,
+ bool* success);
+
+ void ReloadExtension(int extension_handle,
+ IPC::Message* reply_message);
+
+ void EnableExtension(int extension_handle,
+ IPC::Message* reply_message);
+
+ void DisableExtension(int extension_handle,
+ bool* success);
+
+ void ExecuteExtensionActionInActiveTabAsync(int extension_handle,
+ int browser_handle,
+ IPC::Message* reply_message);
+
+ void MoveExtensionBrowserAction(int extension_handle, int index,
+ bool* success);
+
+ void GetExtensionProperty(int extension_handle,
+ AutomationMsg_ExtensionProperty type,
+ bool* success,
+ std::string* value);
+
+
+ // See comment in AutomationMsg_WaitForTabToBeRestored.
+ void WaitForTabToBeRestored(int tab_handle, IPC::Message* reply_message);
+
+ // Gets the security state for the tab associated to the specified |handle|.
+ void GetSecurityState(int handle, bool* success,
+ SecurityStyle* security_style, int* ssl_cert_status,
+ int* insecure_content_status);
+
+ // Gets the page type for the tab associated to the specified |handle|.
+ void GetPageType(int handle, bool* success,
+ NavigationEntry::PageType* page_type);
+
+ // Gets the duration in ms of the last event matching |event_name|.
+ // |duration_ms| is -1 if the event hasn't occurred yet.
+ void GetMetricEventDuration(const std::string& event_name, int* duration_ms);
+
+ // Simulates an action on the SSL blocking page at the tab specified by
+ // |handle|. If |proceed| is true, it is equivalent to the user pressing the
+ // 'Proceed' button, if false the 'Get me out of there button'.
+ // Not that this fails if the tab is not displaying a SSL blocking page.
+ void ActionOnSSLBlockingPage(int handle,
+ bool proceed,
+ IPC::Message* reply_message);
+
+ // Brings the browser window to the front and activates it.
+ void BringBrowserToFront(int browser_handle, bool* success);
+
+ // Checks to see if a command on the browser's CommandController is enabled.
+ void IsMenuCommandEnabled(int browser_handle,
+ int message_num,
+ bool* menu_item_enabled);
+
+ // Prints the current tab immediately.
+ void PrintNow(int tab_handle, IPC::Message* reply_message);
+
+ // Asynchronous request for printing the current tab.
+ void PrintAsync(int tab_handle);
+
+ // Save the current web page.
+ void SavePage(int tab_handle,
+ const FilePath& file_name,
+ const FilePath& dir_path,
+ int type,
+ bool* success);
+
+ // Retrieves the visible text from the autocomplete edit.
+ void GetAutocompleteEditText(int autocomplete_edit_handle,
+ bool* success, std::wstring* text);
+
+ // Sets the visible text from the autocomplete edit.
+ void SetAutocompleteEditText(int autocomplete_edit_handle,
+ const std::wstring& text,
+ bool* success);
+
+ // Retrieves if a query to an autocomplete provider is in progress.
+ void AutocompleteEditIsQueryInProgress(int autocomplete_edit_handle,
+ bool* success,
+ bool* query_in_progress);
+
+ // Retrieves the individual autocomplete matches displayed by the popup.
+ void AutocompleteEditGetMatches(int autocomplete_edit_handle,
+ bool* success,
+ std::vector<AutocompleteMatchData>* matches);
+
+
+ // Retrieves the number of info-bars currently showing in |count|.
+ void GetInfoBarCount(int handle, int* count);
+
+ // Causes a click on the "accept" button of the info-bar at |info_bar_index|.
+ // If |wait_for_navigation| is true, it sends the reply after a navigation has
+ // occurred.
+ void ClickInfoBarAccept(int handle, int info_bar_index,
+ bool wait_for_navigation,
+ IPC::Message* reply_message);
+
+ // Retrieves the last time a navigation occurred for the tab.
+ void GetLastNavigationTime(int handle, int64* last_navigation_time);
+
+ // Waits for a new navigation in the tab if none has happened since
+ // |last_navigation_time|.
+ void WaitForNavigation(int handle,
+ int64 last_navigation_time,
+ IPC::Message* reply_message);
+
+ // Sets the int value for preference with name |name|.
+ void SetIntPreference(int handle,
+ const std::wstring& name,
+ int value,
+ bool* success);
+
+ // Sets the string value for preference with name |name|.
+ void SetStringPreference(int handle,
+ const std::wstring& name,
+ const std::string& value,
+ bool* success);
+
+ // Gets the bool value for preference with name |name|.
+ void GetBooleanPreference(int handle,
+ const std::wstring& name,
+ bool* success,
+ bool* value);
+
+ // Sets the bool value for preference with name |name|.
+ void SetBooleanPreference(int handle,
+ const std::wstring& name,
+ bool value,
+ bool* success);
+
+ // Resets to the default theme.
+ void ResetToDefaultTheme();
+
+ // Gets the current used encoding name of the page in the specified tab.
+ void GetPageCurrentEncoding(int tab_handle, std::string* current_encoding);
+
+ // Uses the specified encoding to override the encoding of the page in the
+ // specified tab.
+ void OverrideEncoding(int tab_handle,
+ const std::string& encoding_name,
+ bool* success);
+
+ void SavePackageShouldPromptUser(bool should_prompt);
+
+ // Enables extension automation (for e.g. UITests).
+ void SetEnableExtensionAutomation(
+ int tab_handle,
+ const std::vector<std::string>& functions_enabled);
+
+ void GetWindowTitle(int handle, string16* text);
+
+ // Returns the number of blocked popups in the tab |handle|.
+ void GetBlockedPopupCount(int handle, int* count);
+
+ // Selects all contents on the page.
+ void SelectAll(int tab_handle);
+
+ // Edit operations on the page.
+ void Cut(int tab_handle);
+ void Copy(int tab_handle);
+ void Paste(int tab_handle);
+
+ void ReloadAsync(int tab_handle);
+ void StopAsync(int tab_handle);
+ void SaveAsAsync(int tab_handle);
+
+ void WaitForBrowserWindowCountToBecome(int target_count,
+ IPC::Message* reply_message);
+
+ void WaitForAppModalDialogToBeShown(IPC::Message* reply_message);
+
+ void GoBackBlockUntilNavigationsComplete(int handle,
+ int number_of_navigations,
+ IPC::Message* reply_message);
+
+ void GoForwardBlockUntilNavigationsComplete(int handle,
+ int number_of_navigations,
+ IPC::Message* reply_message);
+
+ // Convert a tab handle into a TabContents. If |tab| is non-NULL a pointer
+ // to the tab is also returned. Returns NULL in case of failure or if the tab
+ // is not of the TabContents type.
+ TabContents* GetTabContentsForHandle(int handle, NavigationController** tab);
+
+#if defined(OS_CHROMEOS)
+ // Logs in through the Chrome OS Login Wizard with given |username| and
+ // password. Returns true via |reply_message| on success.
+ void LoginWithUserAndPass(const std::string& username,
+ const std::string& password,
+ IPC::Message* reply_message);
+#endif
+
+ // Callback for history redirect queries.
+ virtual void OnRedirectQueryComplete(
+ HistoryService::Handle request_handle,
+ GURL from_url,
+ bool success,
+ history::RedirectList* redirects);
+
+ // Returns the associated view for the tab handle passed in.
+ // Returns NULL on failure.
+ RenderViewHost* GetViewForTab(int tab_handle);
+
+ // Returns the extension for the given handle. Returns NULL if there is
+ // no extension for the handle.
+ Extension* GetExtension(int extension_handle);
+
+ // Returns the extension for the given handle, if the handle is valid and
+ // the associated extension is enabled. Returns NULL otherwise.
+ Extension* GetEnabledExtension(int extension_handle);
+
+ // Returns the extension for the given handle, if the handle is valid and
+ // the associated extension is disabled. Returns NULL otherwise.
+ Extension* GetDisabledExtension(int extension_handle);
+
+ // Block until the focused view ID changes to something other than
+ // previous_view_id.
+ void WaitForFocusedViewIDToChange(int handle,
+ int previous_view_id,
+ IPC::Message* reply_message);
+
+ // Start tracking popup menus. Must be called before executing the
+ // command that might open the popup menu; then call WaitForPopupMenuToOpen.
+ void StartTrackingPopupMenus(int browser_handle, bool* success);
+
+ // Wait until a popup menu has opened.
+ void WaitForPopupMenuToOpen(IPC::Message* reply_message);
+
+ // Method called by the popup menu tracker when a popup menu is opened.
+ void NotifyPopupMenuOpened();
+
+#if defined(OS_WIN)
+ // The functions in this block are for use with external tabs, so they are
+ // Windows only.
+
+ // The container of an externally hosted tab calls this to reflect any
+ // accelerator keys that it did not process. This gives the tab a chance
+ // to handle the keys
+ void ProcessUnhandledAccelerator(const IPC::Message& message, int handle,
+ const MSG& msg);
+
+ void SetInitialFocus(const IPC::Message& message, int handle, bool reverse,
+ bool restore_focus_to_view);
+
+ void OnTabReposition(int tab_handle,
+ const IPC::Reposition_Params& params);
+
+ void OnForwardContextMenuCommandToChrome(int tab_handle, int command);
+
+ void CreateExternalTab(const IPC::ExternalTabSettings& settings,
+ gfx::NativeWindow* tab_container_window,
+ gfx::NativeWindow* tab_window,
+ int* tab_handle);
+
+ void ConnectExternalTab(uint64 cookie,
+ bool allow,
+ gfx::NativeWindow parent_window,
+ gfx::NativeWindow* tab_container_window,
+ gfx::NativeWindow* tab_window,
+ int* tab_handle);
+
+ void NavigateInExternalTab(
+ int handle, const GURL& url, const GURL& referrer,
+ AutomationMsg_NavigationResponseValues* status);
+ void NavigateExternalTabAtIndex(
+ int handle, int index, AutomationMsg_NavigationResponseValues* status);
+
+ // Handler for a message sent by the automation client.
+ void OnMessageFromExternalHost(int handle, const std::string& message,
+ const std::string& origin,
+ const std::string& target);
+
+ // Determine if the message from the external host represents a browser
+ // event, and if so dispatch it.
+ bool InterceptBrowserEventMessageFromExternalHost(const std::string& message,
+ const std::string& origin,
+ const std::string& target);
+
+ void OnBrowserMoved(int handle);
+
+ void OnRunUnloadHandlers(int handle, gfx::NativeWindow notification_window,
+ int notification_message);
+
+ ExternalTabContainer* GetExternalTabForHandle(int handle);
+#endif // defined(OS_WIN)
+
+ typedef ObserverList<NotificationObserver> NotificationObserverList;
+ typedef std::map<NavigationController*, LoginHandler*> LoginHandlerMap;
+ typedef std::map<int, ExtensionPortContainer*> PortContainerMap;
+
+ scoped_ptr<IPC::ChannelProxy> channel_;
+ scoped_ptr<InitialLoadObserver> initial_load_observer_;
+ scoped_ptr<NotificationObserver> new_tab_ui_load_observer_;
+ scoped_ptr<NotificationObserver> find_in_page_observer_;
+ scoped_ptr<NotificationObserver> dom_operation_observer_;
+ scoped_ptr<NotificationObserver> dom_inspector_observer_;
+ scoped_ptr<ExtensionTestResultNotificationObserver>
+ extension_test_result_observer_;
+ scoped_ptr<MetricEventDurationObserver> metric_event_duration_observer_;
+ scoped_ptr<AutomationBrowserTracker> browser_tracker_;
+ scoped_ptr<AutomationExtensionTracker> extension_tracker_;
+ scoped_ptr<AutomationTabTracker> tab_tracker_;
+ scoped_ptr<AutomationWindowTracker> window_tracker_;
+ scoped_ptr<AutomationAutocompleteEditTracker> autocomplete_edit_tracker_;
+ scoped_ptr<NavigationControllerRestoredObserver> restore_tracker_;
+ LoginHandlerMap login_handler_map_;
+ PortContainerMap port_containers_;
+ NotificationObserverList notification_observer_list_;
+ scoped_refptr<AutomationResourceMessageFilter>
+ automation_resource_message_filter_;
+
+ // Handle for an in-process redirect query. We expect only one redirect query
+ // at a time (we should have only one caller, and it will block while waiting
+ // for the results) so there is only one handle. When non-0, indicates a
+ // query in progress.
+ HistoryService::Handle redirect_query_;
+
+ // Consumer for asynchronous history queries.
+ CancelableRequestConsumer consumer_;
+
+ Profile* profile_;
+
+ IPC::Message* reply_message_;
+
+ // Keep track of whether a popup menu has been opened since the last time
+ // that StartTrackingPopupMenus has been called.
+ bool popup_menu_opened_;
+
+ // A temporary object that receives a notification when a popup menu opens.
+ PopupMenuWaiter* popup_menu_waiter_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutomationProvider);
+};
+
+// When life started, the AutomationProvider class was a singleton and was meant
+// only for UI tests. It had specific behavior (like for example, when the
+// channel was shut down. it closed all open Browsers). The new
+// AutomationProvider serves other purposes than just UI testing. This class is
+// meant to provide the OLD functionality for backward compatibility
+class TestingAutomationProvider : public AutomationProvider,
+ public BrowserList::Observer,
+ public NotificationObserver {
+ public:
+ explicit TestingAutomationProvider(Profile* profile);
+
+ // BrowserList::Observer implementation
+ // Called immediately after a browser is added to the list
+ virtual void OnBrowserAdded(const Browser* browser);
+ // Called immediately before a browser is removed from the list
+ virtual void OnBrowserRemoving(const Browser* browser);
+
+ // IPC implementations
+ virtual void OnChannelError();
+
+ private:
+ virtual ~TestingAutomationProvider();
+
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ void OnRemoveProvider(); // Called via PostTask
+
+ NotificationRegistrar registrar_;
+};
+
+#endif // CHROME_BROWSER_AUTOMATION_AUTOMATION_PROVIDER_H_
diff --git a/chrome/browser/automation/automation_provider_chromeos.cc b/chrome/browser/automation/automation_provider_chromeos.cc
new file mode 100644
index 0000000..6e1cc2b
--- /dev/null
+++ b/chrome/browser/automation/automation_provider_chromeos.cc
@@ -0,0 +1,27 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/automation/automation_provider.h"
+
+#include "chrome/browser/automation/automation_provider_observers.h"
+#include "chrome/browser/chromeos/login/login_screen.h"
+#include "chrome/browser/chromeos/login/user_manager.h"
+#include "chrome/browser/chromeos/login/wizard_controller.h"
+#include "views/window/window_gtk.h"
+
+void AutomationProvider::LoginWithUserAndPass(const std::string& username,
+ const std::string& password,
+ IPC::Message* reply_message) {
+ WizardController* controller = WizardController::default_controller();
+ chromeos::NewUserView* new_user_view =
+ controller->GetLoginScreen()->view();
+
+ new_user_view->SetUsername(username);
+ new_user_view->SetPassword(password);
+
+ // Set up an observer (it will delete itself).
+ new LoginManagerObserver(this, reply_message);
+
+ new_user_view->Login();
+}
diff --git a/chrome/browser/automation/automation_provider_gtk.cc b/chrome/browser/automation/automation_provider_gtk.cc
new file mode 100644
index 0000000..8b391ee
--- /dev/null
+++ b/chrome/browser/automation/automation_provider_gtk.cc
@@ -0,0 +1,221 @@
+// Copyright (c) 2009 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_provider.h"
+
+#include <gtk/gtk.h>
+
+#include "chrome/browser/automation/ui_controls.h"
+#include "chrome/browser/automation/automation_browser_tracker.h"
+#include "chrome/browser/automation/automation_window_tracker.h"
+#include "chrome/browser/gtk/browser_window_gtk.h"
+#include "chrome/browser/gtk/gtk_util.h"
+#include "chrome/browser/gtk/view_id_util.h"
+#include "chrome/test/automation/automation_messages.h"
+#include "gfx/point.h"
+#include "gfx/rect.h"
+
+void AutomationProvider::SetWindowBounds(int handle, const gfx::Rect& bounds,
+ bool* success) {
+ *success = false;
+ GtkWindow* window = window_tracker_->GetResource(handle);
+ if (window) {
+ gtk_window_move(window, bounds.x(), bounds.height());
+ gtk_window_resize(window, bounds.width(), bounds.height());
+ *success = true;
+ }
+}
+
+void AutomationProvider::SetWindowVisible(int handle, bool visible,
+ bool* result) {
+ *result = false;
+ GtkWindow* window = window_tracker_->GetResource(handle);
+ if (window) {
+ if (visible) {
+ gtk_window_present(window);
+ } else {
+ gtk_widget_hide(GTK_WIDGET(window));
+ }
+ *result = true;
+ }
+}
+
+#if !defined(TOOLKIT_VIEWS)
+void AutomationProvider::WindowGetViewBounds(int handle, int view_id,
+ bool screen_coordinates,
+ bool* success,
+ gfx::Rect* bounds) {
+ *success = false;
+
+ GtkWindow* window = window_tracker_->GetResource(handle);
+ if (window) {
+ GtkWidget* widget = ViewIDUtil::GetWidget(GTK_WIDGET(window),
+ static_cast<ViewID>(view_id));
+ if (!widget)
+ return;
+ *success = true;
+ *bounds = gfx::Rect(widget->allocation.width, widget->allocation.height);
+ gint x, y;
+ if (screen_coordinates) {
+ gfx::Point point = gtk_util::GetWidgetScreenPosition(widget);
+ x = point.x();
+ y = point.y();
+ } else {
+ gtk_widget_translate_coordinates(widget, GTK_WIDGET(window),
+ 0, 0, &x, &y);
+ }
+ bounds->set_origin(gfx::Point(x, y));
+ }
+}
+#endif
+
+void AutomationProvider::ActivateWindow(int handle) {
+ NOTIMPLEMENTED();
+}
+
+void AutomationProvider::IsWindowMaximized(int handle, bool* is_maximized,
+ bool* success) {
+ *success = false;
+ NOTIMPLEMENTED();
+}
+
+void AutomationProvider::PrintAsync(int tab_handle) {
+ NOTIMPLEMENTED();
+}
+
+// This task sends a WindowDragResponse message with the appropriate
+// routing ID to the automation proxy. This is implemented as a task so that
+// we know that the mouse events (and any tasks that they spawn on the message
+// loop) have been processed by the time this is sent.
+class WindowDragResponseTask : public Task {
+ public:
+ WindowDragResponseTask(AutomationProvider* provider,
+ IPC::Message* reply_message)
+ : provider_(provider),
+ reply_message_(reply_message) {
+ DCHECK(provider_);
+ DCHECK(reply_message_);
+ }
+
+ virtual ~WindowDragResponseTask() {
+ }
+
+ virtual void Run() {
+ AutomationMsg_WindowDrag::WriteReplyParams(reply_message_, true);
+ provider_->Send(reply_message_);
+ }
+
+ private:
+ AutomationProvider* provider_;
+ IPC::Message* reply_message_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowDragResponseTask);
+};
+
+// A task that just runs a SendMouseEvent and performs another task when done.
+class MouseEventTask : public Task {
+ public:
+ MouseEventTask(Task* next_task, ui_controls::MouseButtonState state)
+ : next_task_(next_task),
+ state_(state) {}
+
+ virtual ~MouseEventTask() {
+ }
+
+ virtual void Run() {
+ ui_controls::SendMouseEventsNotifyWhenDone(ui_controls::LEFT, state_,
+ next_task_);
+ }
+
+ private:
+ // The task to execute when we are done.
+ Task* next_task_;
+
+ // Mouse press or mouse release.
+ ui_controls::MouseButtonState state_;
+
+ DISALLOW_COPY_AND_ASSIGN(MouseEventTask);
+};
+
+// A task that just runs a SendMouseMove and performs another task when done.
+class MouseMoveTask : public Task {
+ public:
+ MouseMoveTask(Task* next_task, int absolute_x, int absolute_y)
+ : next_task_(next_task),
+ x_(absolute_x),
+ y_(absolute_y) {
+ }
+
+ virtual ~MouseMoveTask() {
+ }
+
+ virtual void Run() {
+ ui_controls::SendMouseMoveNotifyWhenDone(x_, y_, next_task_);
+ }
+
+ private:
+ // The task to execute when we are done.
+ Task* next_task_;
+
+ // Coordinates of the press.
+ int x_;
+ int y_;
+
+ DISALLOW_COPY_AND_ASSIGN(MouseMoveTask);
+};
+
+void AutomationProvider::WindowSimulateDrag(int handle,
+ std::vector<gfx::Point> drag_path,
+ int flags,
+ bool press_escape_en_route,
+ IPC::Message* reply_message) {
+ // TODO(estade): don't ignore |flags| or |escape_en_route|.
+ gfx::NativeWindow window =
+ browser_tracker_->GetResource(handle)->window()->GetNativeHandle();
+ if (window && (drag_path.size() > 1)) {
+ int x, y;
+ gdk_window_get_position(GTK_WIDGET(window)->window, &x, &y);
+
+ // Create a nested stack of tasks to run.
+ Task* next_task = new WindowDragResponseTask(this, reply_message);
+ next_task = new MouseEventTask(next_task, ui_controls::UP);
+ next_task = new MouseEventTask(next_task, ui_controls::UP);
+ for (size_t i = drag_path.size() - 1; i > 0; --i) {
+ // Smooth out the mouse movements by adding intermediate points. This
+ // better simulates a real user drag.
+ int dest_x = drag_path[i].x() + x;
+ int dest_y = drag_path[i].y() + y;
+ int half_step_x = (dest_x + drag_path[i - 1].x() + x) / 2;
+ int half_step_y = (dest_y + drag_path[i - 1].y() + y) / 2;
+
+ next_task = new MouseMoveTask(next_task, dest_x, dest_y);
+ next_task = new MouseMoveTask(next_task, half_step_x, half_step_y);
+ }
+ next_task = new MouseEventTask(next_task, ui_controls::DOWN);
+
+ ui_controls::SendMouseMoveNotifyWhenDone(x + drag_path[0].x(),
+ y + drag_path[0].y(),
+ next_task);
+ } else {
+ AutomationMsg_WindowDrag::WriteReplyParams(reply_message, false);
+ Send(reply_message);
+ }
+}
+
+void AutomationProvider::TerminateSession(int handle, bool* success) {
+ *success = false;
+ NOTIMPLEMENTED();
+}
+
+void AutomationProvider::GetWindowBounds(int handle, gfx::Rect* bounds,
+ bool* result) {
+ *result = false;
+ NOTIMPLEMENTED();
+}
+
+void AutomationProvider::GetWindowTitle(int handle, string16* text) {
+ gfx::NativeWindow window = window_tracker_->GetResource(handle);
+ const gchar* title = gtk_window_get_title(window);
+ text->assign(UTF8ToUTF16(title));
+}
diff --git a/chrome/browser/automation/automation_provider_json.cc b/chrome/browser/automation/automation_provider_json.cc
new file mode 100644
index 0000000..05f3a1b
--- /dev/null
+++ b/chrome/browser/automation/automation_provider_json.cc
@@ -0,0 +1,55 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/automation/automation_provider_json.h"
+
+#include "base/json/json_writer.h"
+#include "base/json/string_escape.h"
+#include "chrome/test/automation/automation_messages.h"
+
+namespace {
+
+// Util for creating a JSON error return string (dict with key
+// 'error' and error string value). No need to quote input.
+std::string JSONErrorString(const std::string& err) {
+ std::string prefix = "{\"error\": \"";
+ std::string no_quote_err;
+ std::string suffix = "\"}";
+
+ base::JsonDoubleQuote(err, false, &no_quote_err);
+ return prefix + no_quote_err + suffix;
+}
+
+} // namespace
+
+AutomationJSONReply::AutomationJSONReply(AutomationProvider* provider,
+ IPC::Message* reply_message)
+ : provider_(provider),
+ message_(reply_message) {
+}
+
+AutomationJSONReply::~AutomationJSONReply() {
+ DCHECK(!message_) << "JSON automation request not replied!";
+}
+
+void AutomationJSONReply::SendSuccess(const Value* value) {
+ DCHECK(message_) << "Resending reply for JSON automation request";
+ std::string json_string = "{}";
+ if (value)
+ base::JSONWriter::Write(value, false, &json_string);
+ AutomationMsg_SendJSONRequest::WriteReplyParams(
+ message_, json_string, true);
+ provider_->Send(message_);
+ message_ = NULL;
+}
+
+void AutomationJSONReply::SendError(const std::string& error_message) {
+ DCHECK(message_) << "Resending reply for JSON automation request";
+ std::string json_string = JSONErrorString(error_message);
+ AutomationMsg_SendJSONRequest::WriteReplyParams(
+ message_, json_string, false);
+ provider_->Send(message_);
+ message_ = NULL;
+}
+
diff --git a/chrome/browser/automation/automation_provider_json.h b/chrome/browser/automation/automation_provider_json.h
new file mode 100644
index 0000000..a203c58
--- /dev/null
+++ b/chrome/browser/automation/automation_provider_json.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Support utilities for the JSON automation interface used by PyAuto.
+
+#ifndef CHROME_BROWSER_AUTOMATION_AUTOMATION_PROVIDER_JSON_H_
+#define CHROME_BROWSER_AUTOMATION_AUTOMATION_PROVIDER_JSON_H_
+
+#include <string>
+
+#include "base/values.h"
+#include "ipc/ipc_message.h"
+#include "chrome/browser/automation/automation_provider.h"
+
+// Helper to ensure we always send a reply message for JSON automation requests.
+class AutomationJSONReply {
+ public:
+ // Creates a new reply object for the IPC message |reply_message| for
+ // |provider|. The caller is expected to call SendSuccess() or SendError()
+ // before destroying this object.
+ AutomationJSONReply(AutomationProvider* provider,
+ IPC::Message* reply_message);
+
+ ~AutomationJSONReply();
+
+ // Send a success reply along with data contained in |value|.
+ // An empty message will be sent if |value| is NULL.
+ void SendSuccess(const Value* value);
+
+ // Send an error reply along with error message |error_message|.
+ void SendError(const std::string& error_message);
+
+ private:
+ AutomationProvider* provider_;
+ IPC::Message* message_;
+};
+
+#endif // CHROME_BROWSER_AUTOMATION_AUTOMATION_PROVIDER_JSON_H_
+
diff --git a/chrome/browser/automation/automation_provider_list.cc b/chrome/browser/automation/automation_provider_list.cc
new file mode 100644
index 0000000..6676626
--- /dev/null
+++ b/chrome/browser/automation/automation_provider_list.cc
@@ -0,0 +1,52 @@
+// Copyright (c) 2006-2008 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_provider_list.h"
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "chrome/browser/automation/automation_provider.h"
+#include "chrome/browser/browser_process.h"
+
+AutomationProviderList* AutomationProviderList::instance_ = NULL;
+
+AutomationProviderList::AutomationProviderList() {
+}
+
+AutomationProviderList::~AutomationProviderList() {
+ iterator iter = automation_providers_.begin();
+ while (iter != automation_providers_.end()) {
+ (*iter)->Release();
+ iter = automation_providers_.erase(iter);
+ }
+ instance_ = NULL;
+}
+
+bool AutomationProviderList::AddProvider(AutomationProvider* provider) {
+ provider->AddRef();
+ automation_providers_.push_back(provider);
+ return true;
+}
+
+bool AutomationProviderList::RemoveProvider(AutomationProvider* provider) {
+ const iterator remove_provider =
+ find(automation_providers_.begin(), automation_providers_.end(), provider);
+ if (remove_provider != automation_providers_.end()) {
+ (*remove_provider)->Release();
+ automation_providers_.erase(remove_provider);
+ if (automation_providers_.empty())
+ OnLastProviderRemoved();
+ return true;
+ }
+ return false;
+}
+
+AutomationProviderList* AutomationProviderList::GetInstance() {
+ if (!instance_) {
+ instance_ = new AutomationProviderList;
+ }
+ DCHECK(NULL != instance_);
+ return instance_;
+}
diff --git a/chrome/browser/automation/automation_provider_list.h b/chrome/browser/automation/automation_provider_list.h
new file mode 100644
index 0000000..058dc34
--- /dev/null
+++ b/chrome/browser/automation/automation_provider_list.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2006-2008 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_PROVIDER_LIST_H_
+#define CHROME_BROWSER_AUTOMATION_AUTOMATION_PROVIDER_LIST_H_
+
+#include <vector>
+#include "base/basictypes.h"
+
+class AutomationProvider;
+
+// Stores a list of all AutomationProvider objects.
+class AutomationProviderList {
+ public:
+ ~AutomationProviderList();
+ typedef std::vector<AutomationProvider*> list_type;
+ typedef list_type::iterator iterator;
+ typedef list_type::const_iterator const_iterator;
+
+ // Adds and removes automation providers from the global list.
+ bool AddProvider(AutomationProvider* provider);
+ bool RemoveProvider(AutomationProvider* provider);
+
+ const_iterator begin() {
+ return automation_providers_.begin();
+ }
+
+ const_iterator end() {
+ return automation_providers_.end();
+ }
+
+ size_t size() {
+ return automation_providers_.size();
+ }
+
+ static AutomationProviderList* GetInstance();
+
+ private:
+ AutomationProviderList();
+ void OnLastProviderRemoved();
+ list_type automation_providers_;
+ static AutomationProviderList* instance_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutomationProviderList);
+};
+
+#endif // CHROME_BROWSER_AUTOMATION_AUTOMATION_PROVIDER_LIST_H_
diff --git a/chrome/browser/automation/automation_provider_list_generic.cc b/chrome/browser/automation/automation_provider_list_generic.cc
new file mode 100644
index 0000000..868d509
--- /dev/null
+++ b/chrome/browser/automation/automation_provider_list_generic.cc
@@ -0,0 +1,8 @@
+// Copyright (c) 2009 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_provider_list.h"
+
+void AutomationProviderList::OnLastProviderRemoved() {
+}
diff --git a/chrome/browser/automation/automation_provider_list_mac.mm b/chrome/browser/automation/automation_provider_list_mac.mm
new file mode 100644
index 0000000..2cfdb3f
--- /dev/null
+++ b/chrome/browser/automation/automation_provider_list_mac.mm
@@ -0,0 +1,13 @@
+// Copyright (c) 2009 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_provider_list.h"
+
+#import <AppKit/AppKit.h>
+
+void AutomationProviderList::OnLastProviderRemoved() {
+ // We need to explicitly quit the application here because on Mac
+ // the controller holds an additional reference to g_browser_process.
+ [NSApp terminate:nil];
+}
diff --git a/chrome/browser/automation/automation_provider_mac.mm b/chrome/browser/automation/automation_provider_mac.mm
new file mode 100644
index 0000000..ed55d2d
--- /dev/null
+++ b/chrome/browser/automation/automation_provider_mac.mm
@@ -0,0 +1,114 @@
+// Copyright (c) 2009 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_provider.h"
+
+#import <Cocoa/Cocoa.h>
+
+#include "app/l10n_util.h"
+#include "app/l10n_util_mac.h"
+#include "base/sys_string_conversions.h"
+#include "chrome/browser/automation/automation_browser_tracker.h"
+#include "chrome/browser/automation/automation_window_tracker.h"
+#include "chrome/browser/cocoa/tab_window_controller.h"
+#include "chrome/test/automation/automation_messages.h"
+#include "gfx/point.h"
+#include "gfx/rect.h"
+#include "grit/generated_resources.h"
+
+void AutomationProvider::SetWindowBounds(int handle, const gfx::Rect& bounds,
+ bool* success) {
+ *success = false;
+ NSWindow* window = window_tracker_->GetResource(handle);
+ if (window) {
+ NSRect new_bounds = NSRectFromCGRect(bounds.ToCGRect());
+
+ if ([[NSScreen screens] count] > 0) {
+ new_bounds.origin.y =
+ [[[NSScreen screens] objectAtIndex:0] frame].size.height -
+ new_bounds.origin.y - new_bounds.size.height;
+ }
+
+ [window setFrame:new_bounds display:NO];
+ *success = true;
+ }
+}
+
+void AutomationProvider::SetWindowVisible(int handle, bool visible,
+ bool* result) {
+ *result = false;
+ NSWindow* window = window_tracker_->GetResource(handle);
+ if (window) {
+ if (visible) {
+ [window orderFront:nil];
+ } else {
+ [window orderOut:nil];
+ }
+ *result = true;
+ }
+}
+
+void AutomationProvider::WindowGetViewBounds(int handle, int view_id,
+ bool screen_coordinates,
+ bool* success,
+ gfx::Rect* bounds) {
+ // AutomationProxyVisibleTest claims that this is used only by Chrome Views
+ // which we don't use on the Mac. Is this true?
+
+ *success = false;
+ NOTIMPLEMENTED();
+}
+
+void AutomationProvider::ActivateWindow(int handle) { NOTIMPLEMENTED(); }
+
+void AutomationProvider::IsWindowMaximized(int handle, bool* is_maximized,
+ bool* success) {
+ *success = false;
+ NOTIMPLEMENTED();
+}
+
+void AutomationProvider::PrintAsync(int tab_handle) {
+ NOTIMPLEMENTED();
+}
+
+void AutomationProvider::WindowSimulateDrag(int handle,
+ std::vector<gfx::Point> drag_path,
+ int flags,
+ bool press_escape_en_route,
+ IPC::Message* reply_message) {
+ NOTIMPLEMENTED();
+ AutomationMsg_WindowDrag::WriteReplyParams(reply_message, false);
+ Send(reply_message);
+}
+
+void AutomationProvider::TerminateSession(int handle, bool* success) {
+ *success = false;
+ NOTIMPLEMENTED();
+}
+
+void AutomationProvider::GetWindowBounds(int handle, gfx::Rect* bounds,
+ bool* result) {
+ *result = false;
+ NOTIMPLEMENTED();
+}
+
+void AutomationProvider::GetWindowTitle(int handle, string16* text) {
+ gfx::NativeWindow window = window_tracker_->GetResource(handle);
+ NSString* title = nil;
+ if ([[window delegate] isKindOfClass:[TabWindowController class]]) {
+ TabWindowController* delegate =
+ reinterpret_cast<TabWindowController*>([window delegate]);
+ title = [delegate selectedTabTitle];
+ } else {
+ title = [window title];
+ }
+ // If we don't yet have a title, use "Untitled".
+ if (![title length]) {
+ text->assign(WideToUTF16(l10n_util::GetString(
+ IDS_BROWSER_WINDOW_MAC_TAB_UNTITLED)));
+ return;
+ }
+
+ text->assign(base::SysNSStringToUTF16(title));
+}
diff --git a/chrome/browser/automation/automation_provider_observers.cc b/chrome/browser/automation/automation_provider_observers.cc
new file mode 100644
index 0000000..f731309
--- /dev/null
+++ b/chrome/browser/automation/automation_provider_observers.cc
@@ -0,0 +1,1134 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/automation/automation_provider_observers.h"
+
+#include "base/basictypes.h"
+#include "base/json/json_writer.h"
+#include "base/string_util.h"
+#include "chrome/app/chrome_dll_resource.h"
+#include "chrome/browser/automation/automation_provider.h"
+#include "chrome/browser/automation/automation_provider_json.h"
+#include "chrome/browser/bookmarks/bookmark_model.h"
+#include "chrome/browser/dom_operation_notification_details.h"
+#include "chrome/browser/download/download_item.h"
+#include "chrome/browser/download/save_package.h"
+#include "chrome/browser/extensions/extension_host.h"
+#include "chrome/browser/extensions/extension_process_manager.h"
+#include "chrome/browser/extensions/extension_updater.h"
+#include "chrome/browser/login_prompt.h"
+#include "chrome/browser/metrics/metric_event_duration_details.h"
+#include "chrome/browser/printing/print_job.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/tab_contents/navigation_controller.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/test/automation/automation_constants.h"
+
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/login/authentication_notification_details.h"
+#endif
+
+// Holds onto start and stop timestamps for a particular tab
+class InitialLoadObserver::TabTime {
+ public:
+ explicit TabTime(base::TimeTicks started)
+ : load_start_time_(started) {
+ }
+ void set_stop_time(base::TimeTicks stopped) {
+ load_stop_time_ = stopped;
+ }
+ base::TimeTicks stop_time() const {
+ return load_stop_time_;
+ }
+ base::TimeTicks start_time() const {
+ return load_start_time_;
+ }
+ private:
+ base::TimeTicks load_start_time_;
+ base::TimeTicks load_stop_time_;
+};
+
+InitialLoadObserver::InitialLoadObserver(size_t tab_count,
+ AutomationProvider* automation)
+ : automation_(automation),
+ outstanding_tab_count_(tab_count),
+ init_time_(base::TimeTicks::Now()) {
+ if (outstanding_tab_count_ > 0) {
+ registrar_.Add(this, NotificationType::LOAD_START,
+ NotificationService::AllSources());
+ registrar_.Add(this, NotificationType::LOAD_STOP,
+ NotificationService::AllSources());
+ }
+}
+
+InitialLoadObserver::~InitialLoadObserver() {
+}
+
+void InitialLoadObserver::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ if (type == NotificationType::LOAD_START) {
+ if (outstanding_tab_count_ > loading_tabs_.size())
+ loading_tabs_.insert(TabTimeMap::value_type(
+ source.map_key(),
+ TabTime(base::TimeTicks::Now())));
+ } else if (type == NotificationType::LOAD_STOP) {
+ if (outstanding_tab_count_ > finished_tabs_.size()) {
+ TabTimeMap::iterator iter = loading_tabs_.find(source.map_key());
+ if (iter != loading_tabs_.end()) {
+ finished_tabs_.insert(source.map_key());
+ iter->second.set_stop_time(base::TimeTicks::Now());
+ }
+ if (outstanding_tab_count_ == finished_tabs_.size())
+ ConditionMet();
+ }
+ } else {
+ NOTREACHED();
+ }
+}
+
+DictionaryValue* InitialLoadObserver::GetTimingInformation() const {
+ ListValue* items = new ListValue;
+ for (TabTimeMap::const_iterator it = loading_tabs_.begin();
+ it != loading_tabs_.end();
+ ++it) {
+ DictionaryValue* item = new DictionaryValue;
+ base::TimeDelta delta_start = it->second.start_time() - init_time_;
+
+ item->SetReal(L"load_start_ms", delta_start.InMillisecondsF());
+ if (it->second.stop_time().is_null()) {
+ item->Set(L"load_stop_ms", Value::CreateNullValue());
+ } else {
+ base::TimeDelta delta_stop = it->second.stop_time() - init_time_;
+ item->SetReal(L"load_stop_ms", delta_stop.InMillisecondsF());
+ }
+ items->Append(item);
+ }
+ DictionaryValue* return_value = new DictionaryValue;
+ return_value->Set(L"tabs", items);
+ return return_value;
+}
+
+void InitialLoadObserver::ConditionMet() {
+ registrar_.RemoveAll();
+ automation_->Send(new AutomationMsg_InitialLoadsComplete(0));
+}
+
+NewTabUILoadObserver::NewTabUILoadObserver(AutomationProvider* automation)
+ : automation_(automation) {
+ registrar_.Add(this, NotificationType::INITIAL_NEW_TAB_UI_LOAD,
+ NotificationService::AllSources());
+}
+
+NewTabUILoadObserver::~NewTabUILoadObserver() {
+}
+
+void NewTabUILoadObserver::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ if (type == NotificationType::INITIAL_NEW_TAB_UI_LOAD) {
+ Details<int> load_time(details);
+ automation_->Send(
+ new AutomationMsg_InitialNewTabUILoadComplete(0, *load_time.ptr()));
+ } else {
+ NOTREACHED();
+ }
+}
+
+NavigationControllerRestoredObserver::NavigationControllerRestoredObserver(
+ AutomationProvider* automation,
+ NavigationController* controller,
+ IPC::Message* reply_message)
+ : automation_(automation),
+ controller_(controller),
+ reply_message_(reply_message) {
+ if (FinishedRestoring()) {
+ SendDone();
+ } else {
+ registrar_.Add(this, NotificationType::LOAD_STOP,
+ NotificationService::AllSources());
+ }
+}
+
+NavigationControllerRestoredObserver::~NavigationControllerRestoredObserver() {
+}
+
+void NavigationControllerRestoredObserver::Observe(
+ NotificationType type, const NotificationSource& source,
+ const NotificationDetails& details) {
+ if (FinishedRestoring()) {
+ SendDone();
+ registrar_.RemoveAll();
+ }
+}
+
+bool NavigationControllerRestoredObserver::FinishedRestoring() {
+ return (!controller_->needs_reload() && !controller_->pending_entry() &&
+ !controller_->tab_contents()->is_loading());
+}
+
+void NavigationControllerRestoredObserver::SendDone() {
+ DCHECK(reply_message_ != NULL);
+ automation_->Send(reply_message_);
+}
+
+NavigationNotificationObserver::NavigationNotificationObserver(
+ NavigationController* controller,
+ AutomationProvider* automation,
+ IPC::Message* reply_message,
+ int number_of_navigations,
+ bool include_current_navigation)
+ : automation_(automation),
+ reply_message_(reply_message),
+ controller_(controller),
+ navigations_remaining_(number_of_navigations),
+ navigation_started_(false) {
+ DCHECK_LT(0, navigations_remaining_);
+ Source<NavigationController> source(controller_);
+ registrar_.Add(this, NotificationType::NAV_ENTRY_COMMITTED, source);
+ registrar_.Add(this, NotificationType::LOAD_START, source);
+ registrar_.Add(this, NotificationType::LOAD_STOP, source);
+ registrar_.Add(this, NotificationType::AUTH_NEEDED, source);
+ registrar_.Add(this, NotificationType::AUTH_SUPPLIED, source);
+ registrar_.Add(this, NotificationType::AUTH_CANCELLED, source);
+
+ if (include_current_navigation && controller->tab_contents()->is_loading())
+ navigation_started_ = true;
+}
+
+NavigationNotificationObserver::~NavigationNotificationObserver() {
+ if (reply_message_) {
+ // This means we did not receive a notification for this navigation.
+ // Send over a failed navigation status back to the caller to ensure that
+ // the caller does not hang waiting for the response.
+ IPC::ParamTraits<AutomationMsg_NavigationResponseValues>::Write(
+ reply_message_, AUTOMATION_MSG_NAVIGATION_ERROR);
+ automation_->Send(reply_message_);
+ reply_message_ = NULL;
+ }
+
+ automation_->RemoveNavigationStatusListener(this);
+}
+
+void NavigationNotificationObserver::Observe(
+ NotificationType type, const NotificationSource& source,
+ const NotificationDetails& details) {
+ // We listen for 2 events to determine when the navigation started because:
+ // - when this is used by the WaitForNavigation method, we might be invoked
+ // afer the load has started (but not after the entry was committed, as
+ // WaitForNavigation compares times of the last navigation).
+ // - when this is used with a page requiring authentication, we will not get
+ // a NotificationType::NAV_ENTRY_COMMITTED until after we authenticate, so
+ // we need the NotificationType::LOAD_START.
+ if (type == NotificationType::NAV_ENTRY_COMMITTED ||
+ type == NotificationType::LOAD_START) {
+ navigation_started_ = true;
+ } else if (type == NotificationType::LOAD_STOP) {
+ if (navigation_started_) {
+ navigation_started_ = false;
+ if (--navigations_remaining_ == 0)
+ ConditionMet(AUTOMATION_MSG_NAVIGATION_SUCCESS);
+ }
+ } else if (type == NotificationType::AUTH_SUPPLIED ||
+ type == NotificationType::AUTH_CANCELLED) {
+ // The LoginHandler for this tab is no longer valid.
+ automation_->RemoveLoginHandler(controller_);
+
+ // Treat this as if navigation started again, since load start/stop don't
+ // occur while authentication is ongoing.
+ navigation_started_ = true;
+ } else if (type == NotificationType::AUTH_NEEDED) {
+ // Remember the login handler that wants authentication.
+ // We do this in all cases (not just when navigation_started_ == true) so
+ // tests can still wait for auth dialogs outside of navigation.
+ LoginHandler* handler =
+ Details<LoginNotificationDetails>(details)->handler();
+ automation_->AddLoginHandler(controller_, handler);
+
+ // Respond that authentication is needed.
+ navigation_started_ = false;
+ ConditionMet(AUTOMATION_MSG_NAVIGATION_AUTH_NEEDED);
+ } else {
+ NOTREACHED();
+ }
+}
+
+void NavigationNotificationObserver::ConditionMet(
+ AutomationMsg_NavigationResponseValues navigation_result) {
+ DCHECK(reply_message_ != NULL);
+
+ IPC::ParamTraits<AutomationMsg_NavigationResponseValues>::Write(
+ reply_message_, navigation_result);
+ automation_->Send(reply_message_);
+ reply_message_ = NULL;
+
+ delete this;
+}
+
+TabStripNotificationObserver::TabStripNotificationObserver(
+ NotificationType notification, AutomationProvider* automation)
+ : automation_(automation),
+ notification_(notification) {
+ registrar_.Add(this, notification_, NotificationService::AllSources());
+}
+
+TabStripNotificationObserver::~TabStripNotificationObserver() {
+}
+
+void TabStripNotificationObserver::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ if (type == notification_) {
+ ObserveTab(Source<NavigationController>(source).ptr());
+
+ // If verified, no need to observe anymore
+ automation_->RemoveTabStripObserver(this);
+ delete this;
+ } else {
+ NOTREACHED();
+ }
+}
+
+TabAppendedNotificationObserver::TabAppendedNotificationObserver(
+ Browser* parent, AutomationProvider* automation,
+ IPC::Message* reply_message)
+ : TabStripNotificationObserver(NotificationType::TAB_PARENTED, automation),
+ parent_(parent),
+ reply_message_(reply_message) {
+}
+
+void TabAppendedNotificationObserver::ObserveTab(
+ NavigationController* controller) {
+ if (automation_->GetIndexForNavigationController(controller, parent_) ==
+ TabStripModel::kNoTab) {
+ // This tab notification doesn't belong to the parent_.
+ return;
+ }
+
+ automation_->AddNavigationStatusListener(controller, reply_message_, 1,
+ false);
+}
+
+TabClosedNotificationObserver::TabClosedNotificationObserver(
+ AutomationProvider* automation, bool wait_until_closed,
+ IPC::Message* reply_message)
+ : TabStripNotificationObserver(wait_until_closed ?
+ NotificationType::TAB_CLOSED : NotificationType::TAB_CLOSING,
+ automation),
+ reply_message_(reply_message),
+ for_browser_command_(false) {
+}
+
+void TabClosedNotificationObserver::ObserveTab(
+ NavigationController* controller) {
+ if (for_browser_command_) {
+ AutomationMsg_WindowExecuteCommand::WriteReplyParams(reply_message_,
+ true);
+ } else {
+ AutomationMsg_CloseTab::WriteReplyParams(reply_message_, true);
+ }
+ automation_->Send(reply_message_);
+}
+
+void TabClosedNotificationObserver::set_for_browser_command(
+ bool for_browser_command) {
+ for_browser_command_ = for_browser_command;
+}
+
+bool DidExtensionHostsStopLoading(ExtensionProcessManager* manager) {
+ for (ExtensionProcessManager::const_iterator iter = manager->begin();
+ iter != manager->end(); ++iter) {
+ if (!(*iter)->did_stop_loading())
+ return false;
+ }
+ return true;
+}
+
+ExtensionInstallNotificationObserver::ExtensionInstallNotificationObserver(
+ AutomationProvider* automation, int id, IPC::Message* reply_message)
+ : automation_(automation),
+ id_(id),
+ reply_message_(reply_message) {
+ registrar_.Add(this, NotificationType::EXTENSION_LOADED,
+ NotificationService::AllSources());
+ registrar_.Add(this, NotificationType::EXTENSION_INSTALL_ERROR,
+ NotificationService::AllSources());
+ registrar_.Add(this, NotificationType::EXTENSION_UPDATE_DISABLED,
+ NotificationService::AllSources());
+}
+
+ExtensionInstallNotificationObserver::~ExtensionInstallNotificationObserver() {
+}
+
+void ExtensionInstallNotificationObserver::Observe(
+ NotificationType type, const NotificationSource& source,
+ const NotificationDetails& details) {
+ switch (type.value) {
+ case NotificationType::EXTENSION_LOADED:
+ SendResponse(AUTOMATION_MSG_EXTENSION_INSTALL_SUCCEEDED);
+ break;
+ case NotificationType::EXTENSION_INSTALL_ERROR:
+ case NotificationType::EXTENSION_UPDATE_DISABLED:
+ SendResponse(AUTOMATION_MSG_EXTENSION_INSTALL_FAILED);
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+
+ delete this;
+}
+
+void ExtensionInstallNotificationObserver::SendResponse(
+ AutomationMsg_ExtensionResponseValues response) {
+ if (reply_message_ != NULL) {
+ switch (id_) {
+ case AutomationMsg_InstallExtension::ID:
+ AutomationMsg_InstallExtension::WriteReplyParams(reply_message_,
+ response);
+ break;
+ case AutomationMsg_LoadExpandedExtension::ID:
+ AutomationMsg_LoadExpandedExtension::WriteReplyParams(reply_message_,
+ response);
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+
+ automation_->Send(reply_message_);
+ reply_message_ = NULL;
+ }
+}
+
+ExtensionReadyNotificationObserver::ExtensionReadyNotificationObserver(
+ ExtensionProcessManager* manager, AutomationProvider* automation, int id,
+ IPC::Message* reply_message)
+ : manager_(manager),
+ automation_(automation),
+ id_(id),
+ reply_message_(reply_message),
+ extension_(NULL) {
+ registrar_.Add(this, NotificationType::EXTENSION_HOST_DID_STOP_LOADING,
+ NotificationService::AllSources());
+ registrar_.Add(this, NotificationType::EXTENSION_LOADED,
+ NotificationService::AllSources());
+ registrar_.Add(this, NotificationType::EXTENSION_INSTALL_ERROR,
+ NotificationService::AllSources());
+ registrar_.Add(this, NotificationType::EXTENSION_UPDATE_DISABLED,
+ NotificationService::AllSources());
+}
+
+ExtensionReadyNotificationObserver::~ExtensionReadyNotificationObserver() {
+}
+
+void ExtensionReadyNotificationObserver::Observe(
+ NotificationType type, const NotificationSource& source,
+ const NotificationDetails& details) {
+ bool success = false;
+ switch (type.value) {
+ case NotificationType::EXTENSION_HOST_DID_STOP_LOADING:
+ // Only continue on with this method if our extension has been loaded
+ // and all the extension hosts have stopped loading.
+ if (!extension_ || !DidExtensionHostsStopLoading(manager_))
+ return;
+ success = true;
+ break;
+ case NotificationType::EXTENSION_LOADED:
+ extension_ = Details<Extension>(details).ptr();
+ if (!DidExtensionHostsStopLoading(manager_))
+ return;
+ success = true;
+ break;
+ case NotificationType::EXTENSION_INSTALL_ERROR:
+ case NotificationType::EXTENSION_UPDATE_DISABLED:
+ success = false;
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+
+ if (id_ == AutomationMsg_InstallExtensionAndGetHandle::ID) {
+ // A handle of zero indicates an error.
+ int extension_handle = 0;
+ if (extension_)
+ extension_handle = automation_->AddExtension(extension_);
+ AutomationMsg_InstallExtensionAndGetHandle::WriteReplyParams(
+ reply_message_, extension_handle);
+ } else if (id_ == AutomationMsg_EnableExtension::ID) {
+ AutomationMsg_EnableExtension::WriteReplyParams(reply_message_, true);
+ } else {
+ NOTREACHED();
+ LOG(ERROR) << "Cannot write reply params for unknown message id.";
+ }
+
+ automation_->Send(reply_message_);
+ delete this;
+}
+
+ExtensionUnloadNotificationObserver::ExtensionUnloadNotificationObserver()
+ : did_receive_unload_notification_(false) {
+ registrar_.Add(this, NotificationType::EXTENSION_UNLOADED,
+ NotificationService::AllSources());
+ registrar_.Add(this, NotificationType::EXTENSION_UNLOADED_DISABLED,
+ NotificationService::AllSources());
+}
+
+ExtensionUnloadNotificationObserver::~ExtensionUnloadNotificationObserver() {
+}
+
+void ExtensionUnloadNotificationObserver::Observe(
+ NotificationType type, const NotificationSource& source,
+ const NotificationDetails& details) {
+ if (type.value == NotificationType::EXTENSION_UNLOADED ||
+ type.value == NotificationType::EXTENSION_UNLOADED_DISABLED) {
+ did_receive_unload_notification_ = true;
+ } else {
+ NOTREACHED();
+ }
+}
+
+ExtensionTestResultNotificationObserver::
+ ExtensionTestResultNotificationObserver(AutomationProvider* automation)
+ : automation_(automation) {
+ registrar_.Add(this, NotificationType::EXTENSION_TEST_PASSED,
+ NotificationService::AllSources());
+ registrar_.Add(this, NotificationType::EXTENSION_TEST_FAILED,
+ NotificationService::AllSources());
+}
+
+ExtensionTestResultNotificationObserver::
+ ~ExtensionTestResultNotificationObserver() {
+}
+
+void ExtensionTestResultNotificationObserver::Observe(
+ NotificationType type, const NotificationSource& source,
+ const NotificationDetails& details) {
+ switch (type.value) {
+ case NotificationType::EXTENSION_TEST_PASSED:
+ results_.push_back(true);
+ messages_.push_back("");
+ break;
+
+ case NotificationType::EXTENSION_TEST_FAILED:
+ results_.push_back(false);
+ messages_.push_back(*(Details<std::string>(details).ptr()));
+ break;
+
+ default:
+ NOTREACHED();
+ }
+ // There may be a reply message waiting for this event, so check.
+ MaybeSendResult();
+}
+
+void ExtensionTestResultNotificationObserver::MaybeSendResult() {
+ if (results_.size() > 0) {
+ // This release method should return the automation's current
+ // reply message, or NULL if there is no current one. If it is not
+ // NULL, we are stating that we will handle this reply message.
+ IPC::Message* reply_message = automation_->reply_message_release();
+ // Send the result back if we have a reply message.
+ if (reply_message) {
+ AutomationMsg_WaitForExtensionTestResult::WriteReplyParams(
+ reply_message, results_.front(), messages_.front());
+ results_.pop_front();
+ messages_.pop_front();
+ automation_->Send(reply_message);
+ }
+ }
+}
+
+BrowserOpenedNotificationObserver::BrowserOpenedNotificationObserver(
+ AutomationProvider* automation, IPC::Message* reply_message)
+ : automation_(automation),
+ reply_message_(reply_message),
+ for_browser_command_(false) {
+ registrar_.Add(this, NotificationType::BROWSER_OPENED,
+ NotificationService::AllSources());
+}
+
+BrowserOpenedNotificationObserver::~BrowserOpenedNotificationObserver() {
+}
+
+void BrowserOpenedNotificationObserver::Observe(
+ NotificationType type, const NotificationSource& source,
+ const NotificationDetails& details) {
+ if (type == NotificationType::BROWSER_OPENED) {
+ if (for_browser_command_) {
+ AutomationMsg_WindowExecuteCommand::WriteReplyParams(reply_message_,
+ true);
+ }
+ automation_->Send(reply_message_);
+ delete this;
+ } else {
+ NOTREACHED();
+ }
+}
+
+void BrowserOpenedNotificationObserver::set_for_browser_command(
+ bool for_browser_command) {
+ for_browser_command_ = for_browser_command;
+}
+
+BrowserClosedNotificationObserver::BrowserClosedNotificationObserver(
+ Browser* browser,
+ AutomationProvider* automation,
+ IPC::Message* reply_message)
+ : automation_(automation),
+ reply_message_(reply_message),
+ for_browser_command_(false) {
+ registrar_.Add(this, NotificationType::BROWSER_CLOSED,
+ Source<Browser>(browser));
+}
+
+void BrowserClosedNotificationObserver::Observe(
+ NotificationType type, const NotificationSource& source,
+ const NotificationDetails& details) {
+ DCHECK(type == NotificationType::BROWSER_CLOSED);
+ Details<bool> close_app(details);
+ DCHECK(reply_message_ != NULL);
+ if (for_browser_command_) {
+ AutomationMsg_WindowExecuteCommand::WriteReplyParams(reply_message_,
+ true);
+ } else {
+ AutomationMsg_CloseBrowser::WriteReplyParams(reply_message_, true,
+ *(close_app.ptr()));
+ }
+ automation_->Send(reply_message_);
+ reply_message_ = NULL;
+ delete this;
+}
+
+void BrowserClosedNotificationObserver::set_for_browser_command(
+ bool for_browser_command) {
+ for_browser_command_ = for_browser_command;
+}
+
+BrowserCountChangeNotificationObserver::BrowserCountChangeNotificationObserver(
+ int target_count,
+ AutomationProvider* automation,
+ IPC::Message* reply_message)
+ : target_count_(target_count),
+ automation_(automation),
+ reply_message_(reply_message) {
+ registrar_.Add(this, NotificationType::BROWSER_OPENED,
+ NotificationService::AllSources());
+ registrar_.Add(this, NotificationType::BROWSER_CLOSED,
+ NotificationService::AllSources());
+}
+
+void BrowserCountChangeNotificationObserver::Observe(
+ NotificationType type, const NotificationSource& source,
+ const NotificationDetails& details) {
+ DCHECK(type == NotificationType::BROWSER_OPENED ||
+ type == NotificationType::BROWSER_CLOSED);
+ int current_count = static_cast<int>(BrowserList::size());
+ if (type == NotificationType::BROWSER_CLOSED) {
+ // At the time of the notification the browser being closed is not removed
+ // from the list. The real count is one less than the reported count.
+ DCHECK_LT(0, current_count);
+ current_count--;
+ }
+ if (current_count == target_count_) {
+ AutomationMsg_WaitForBrowserWindowCountToBecome::WriteReplyParams(
+ reply_message_, true);
+ automation_->Send(reply_message_);
+ reply_message_ = NULL;
+ delete this;
+ }
+}
+
+AppModalDialogShownObserver::AppModalDialogShownObserver(
+ AutomationProvider* automation, IPC::Message* reply_message)
+ : automation_(automation),
+ reply_message_(reply_message) {
+ registrar_.Add(this, NotificationType::APP_MODAL_DIALOG_SHOWN,
+ NotificationService::AllSources());
+}
+
+AppModalDialogShownObserver::~AppModalDialogShownObserver() {
+}
+
+void AppModalDialogShownObserver::Observe(
+ NotificationType type, const NotificationSource& source,
+ const NotificationDetails& details) {
+ DCHECK(type == NotificationType::APP_MODAL_DIALOG_SHOWN);
+
+ AutomationMsg_WaitForAppModalDialogToBeShown::WriteReplyParams(
+ reply_message_, true);
+ automation_->Send(reply_message_);
+ reply_message_ = NULL;
+ delete this;
+}
+
+namespace {
+
+// Define mapping from command to notification
+struct CommandNotification {
+ int command;
+ NotificationType::Type notification_type;
+};
+
+const struct CommandNotification command_notifications[] = {
+ {IDC_DUPLICATE_TAB, NotificationType::TAB_PARENTED},
+ {IDC_NEW_TAB, NotificationType::INITIAL_NEW_TAB_UI_LOAD},
+
+ // Returns as soon as the restored tab is created. To further wait until
+ // the content page is loaded, use WaitForTabToBeRestored.
+ {IDC_RESTORE_TAB, NotificationType::TAB_PARENTED},
+
+ // For the following commands, we need to wait for a new tab to be created,
+ // load to finish, and title to change.
+ {IDC_MANAGE_EXTENSIONS, NotificationType::TAB_CONTENTS_TITLE_UPDATED},
+ {IDC_OPTIONS, NotificationType::TAB_CONTENTS_TITLE_UPDATED},
+ {IDC_SHOW_DOWNLOADS, NotificationType::TAB_CONTENTS_TITLE_UPDATED},
+ {IDC_SHOW_HISTORY, NotificationType::TAB_CONTENTS_TITLE_UPDATED},
+};
+
+} // namespace
+
+ExecuteBrowserCommandObserver::~ExecuteBrowserCommandObserver() {
+}
+
+// static
+bool ExecuteBrowserCommandObserver::CreateAndRegisterObserver(
+ AutomationProvider* automation, Browser* browser, int command,
+ IPC::Message* reply_message) {
+ bool result = true;
+ switch (command) {
+ case IDC_NEW_WINDOW:
+ case IDC_NEW_INCOGNITO_WINDOW: {
+ BrowserOpenedNotificationObserver* observer =
+ new BrowserOpenedNotificationObserver(automation, reply_message);
+ observer->set_for_browser_command(true);
+ break;
+ }
+ case IDC_CLOSE_WINDOW: {
+ BrowserClosedNotificationObserver* observer =
+ new BrowserClosedNotificationObserver(browser, automation,
+ reply_message);
+ observer->set_for_browser_command(true);
+ break;
+ }
+ case IDC_CLOSE_TAB: {
+ TabClosedNotificationObserver* observer =
+ new TabClosedNotificationObserver(automation, true, reply_message);
+ observer->set_for_browser_command(true);
+ break;
+ }
+ case IDC_BACK:
+ case IDC_FORWARD:
+ case IDC_RELOAD: {
+ automation->AddNavigationStatusListener(
+ &browser->GetSelectedTabContents()->controller(),
+ reply_message, 1, false);
+ break;
+ }
+ default: {
+ ExecuteBrowserCommandObserver* observer =
+ new ExecuteBrowserCommandObserver(automation, reply_message);
+ if (!observer->Register(command)) {
+ delete observer;
+ result = false;
+ }
+ break;
+ }
+ }
+ return result;
+}
+
+void ExecuteBrowserCommandObserver::Observe(
+ NotificationType type, const NotificationSource& source,
+ const NotificationDetails& details) {
+ if (type == notification_type_) {
+ AutomationMsg_WindowExecuteCommand::WriteReplyParams(reply_message_,
+ true);
+ automation_->Send(reply_message_);
+ delete this;
+ } else {
+ NOTREACHED();
+ }
+}
+
+ExecuteBrowserCommandObserver::ExecuteBrowserCommandObserver(
+ AutomationProvider* automation, IPC::Message* reply_message)
+ : automation_(automation),
+ reply_message_(reply_message) {
+}
+
+bool ExecuteBrowserCommandObserver::Register(int command) {
+ if (!GetNotificationType(command, &notification_type_))
+ return false;
+ registrar_.Add(this, notification_type_, NotificationService::AllSources());
+ return true;
+}
+
+bool ExecuteBrowserCommandObserver::GetNotificationType(
+ int command, NotificationType::Type* type) {
+ if (!type)
+ return false;
+ bool found = false;
+ for (unsigned int i = 0; i < arraysize(command_notifications); i++) {
+ if (command_notifications[i].command == command) {
+ *type = command_notifications[i].notification_type;
+ found = true;
+ break;
+ }
+ }
+ return found;
+}
+
+FindInPageNotificationObserver::FindInPageNotificationObserver(
+ AutomationProvider* automation, TabContents* parent_tab,
+ IPC::Message* reply_message)
+ : automation_(automation),
+ active_match_ordinal_(-1),
+ reply_message_(reply_message) {
+ registrar_.Add(this, NotificationType::FIND_RESULT_AVAILABLE,
+ Source<TabContents>(parent_tab));
+}
+
+FindInPageNotificationObserver::~FindInPageNotificationObserver() {
+}
+
+void FindInPageNotificationObserver::Observe(
+ NotificationType type, const NotificationSource& source,
+ const NotificationDetails& details) {
+ if (type == NotificationType::FIND_RESULT_AVAILABLE) {
+ Details<FindNotificationDetails> find_details(details);
+ if (find_details->request_id() == kFindInPageRequestId) {
+ // We get multiple responses and one of those will contain the ordinal.
+ // This message comes to us before the final update is sent.
+ if (find_details->active_match_ordinal() > -1)
+ active_match_ordinal_ = find_details->active_match_ordinal();
+ if (find_details->final_update()) {
+ if (reply_message_ != NULL) {
+ AutomationMsg_FindInPage::WriteReplyParams(reply_message_,
+ active_match_ordinal_, find_details->number_of_matches());
+ automation_->Send(reply_message_);
+ reply_message_ = NULL;
+ } else {
+ DLOG(WARNING) << "Multiple final Find messages observed.";
+ }
+ } else {
+ DLOG(INFO) << "Ignoring, since we only care about the final message";
+ }
+ }
+ } else {
+ NOTREACHED();
+ }
+}
+
+// static
+const int FindInPageNotificationObserver::kFindInPageRequestId = -1;
+
+DomOperationNotificationObserver::DomOperationNotificationObserver(
+ AutomationProvider* automation)
+ : automation_(automation) {
+ registrar_.Add(this, NotificationType::DOM_OPERATION_RESPONSE,
+ NotificationService::AllSources());
+}
+
+DomOperationNotificationObserver::~DomOperationNotificationObserver() {
+}
+
+void DomOperationNotificationObserver::Observe(
+ NotificationType type, const NotificationSource& source,
+ const NotificationDetails& details) {
+ if (NotificationType::DOM_OPERATION_RESPONSE == type) {
+ Details<DomOperationNotificationDetails> dom_op_details(details);
+
+ IPC::Message* reply_message = automation_->reply_message_release();
+ if (reply_message) {
+ AutomationMsg_DomOperation::WriteReplyParams(reply_message,
+ dom_op_details->json());
+ automation_->Send(reply_message);
+ }
+ }
+}
+
+DocumentPrintedNotificationObserver::DocumentPrintedNotificationObserver(
+ AutomationProvider* automation, IPC::Message* reply_message)
+ : automation_(automation),
+ success_(false),
+ reply_message_(reply_message) {
+ registrar_.Add(this, NotificationType::PRINT_JOB_EVENT,
+ NotificationService::AllSources());
+}
+
+DocumentPrintedNotificationObserver::~DocumentPrintedNotificationObserver() {
+ DCHECK(reply_message_ != NULL);
+ AutomationMsg_PrintNow::WriteReplyParams(reply_message_, success_);
+ automation_->Send(reply_message_);
+ automation_->RemoveNavigationStatusListener(this);
+}
+
+void DocumentPrintedNotificationObserver::Observe(
+ NotificationType type, const NotificationSource& source,
+ const NotificationDetails& details) {
+ using namespace printing;
+ DCHECK(type == NotificationType::PRINT_JOB_EVENT);
+ switch (Details<JobEventDetails>(details)->type()) {
+ case JobEventDetails::JOB_DONE: {
+ // Succeeded.
+ success_ = true;
+ delete this;
+ break;
+ }
+ case JobEventDetails::USER_INIT_CANCELED:
+ case JobEventDetails::FAILED: {
+ // Failed.
+ delete this;
+ break;
+ }
+ case JobEventDetails::NEW_DOC:
+ case JobEventDetails::USER_INIT_DONE:
+ case JobEventDetails::DEFAULT_INIT_DONE:
+ case JobEventDetails::NEW_PAGE:
+ case JobEventDetails::PAGE_DONE:
+ case JobEventDetails::DOC_DONE:
+ case JobEventDetails::ALL_PAGES_REQUESTED: {
+ // Don't care.
+ break;
+ }
+ default: {
+ NOTREACHED();
+ break;
+ }
+ }
+}
+
+MetricEventDurationObserver::MetricEventDurationObserver() {
+ registrar_.Add(this, NotificationType::METRIC_EVENT_DURATION,
+ NotificationService::AllSources());
+}
+
+int MetricEventDurationObserver::GetEventDurationMs(
+ const std::string& event_name) {
+ EventDurationMap::const_iterator it = durations_.find(event_name);
+ if (it == durations_.end())
+ return -1;
+ return it->second;
+}
+
+void MetricEventDurationObserver::Observe(NotificationType type,
+ const NotificationSource& source, const NotificationDetails& details) {
+ if (type != NotificationType::METRIC_EVENT_DURATION) {
+ NOTREACHED();
+ return;
+ }
+ MetricEventDurationDetails* metric_event_duration =
+ Details<MetricEventDurationDetails>(details).ptr();
+ durations_[metric_event_duration->event_name] =
+ metric_event_duration->duration_ms;
+}
+
+#if defined(OS_CHROMEOS)
+LoginManagerObserver::LoginManagerObserver(
+ AutomationProvider* automation,
+ IPC::Message* reply_message)
+ : automation_(automation),
+ reply_message_(reply_message) {
+
+ registrar_.Add(this, NotificationType::LOGIN_AUTHENTICATION,
+ NotificationService::AllSources());
+}
+
+void LoginManagerObserver::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ DCHECK(type == NotificationType::LOGIN_AUTHENTICATION);
+ Details<AuthenticationNotificationDetails> auth_details(details);
+ AutomationMsg_LoginWithUserAndPass::WriteReplyParams(reply_message_,
+ auth_details->success());
+ automation_->Send(reply_message_);
+ delete this;
+}
+#endif
+
+AutomationProviderBookmarkModelObserver::AutomationProviderBookmarkModelObserver(
+ AutomationProvider* provider,
+ IPC::Message* reply_message,
+ BookmarkModel* model) {
+ automation_provider_ = provider;
+ reply_message_ = reply_message;
+ model_ = model;
+ model_->AddObserver(this);
+}
+
+AutomationProviderBookmarkModelObserver::~AutomationProviderBookmarkModelObserver() {
+ model_->RemoveObserver(this);
+}
+
+void AutomationProviderBookmarkModelObserver::ReplyAndDelete(bool success) {
+ AutomationMsg_WaitForBookmarkModelToLoad::WriteReplyParams(
+ reply_message_, success);
+ automation_provider_->Send(reply_message_);
+ delete this;
+}
+
+void AutomationProviderDownloadItemObserver::OnDownloadFileCompleted(
+ DownloadItem* download) {
+ download->RemoveObserver(this);
+ if (--downloads_ == 0) {
+ AutomationJSONReply(provider_, reply_message_).SendSuccess(NULL);
+ delete this;
+ }
+}
+
+void AutomationProviderHistoryObserver::HistoryQueryComplete(
+ HistoryService::Handle request_handle,
+ history::QueryResults* results) {
+ scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
+
+ ListValue* history_list = new ListValue;
+ for (size_t i = 0; i < results->size(); ++i) {
+ DictionaryValue* page_value = new DictionaryValue;
+ history::URLResult const &page = (*results)[i];
+ page_value->SetStringFromUTF16(L"title", page.title());
+ page_value->SetString(L"url", page.url().spec());
+ page_value->SetReal(L"time",
+ static_cast<double>(page.visit_time().ToDoubleT()));
+ page_value->SetStringFromUTF16(L"snippet", page.snippet().text());
+ page_value->SetBoolean(
+ L"starred",
+ provider_->profile()->GetBookmarkModel()->IsBookmarked(page.url()));
+ history_list->Append(page_value);
+ }
+
+ return_value->Set(L"history", history_list);
+ // Return history info.
+ AutomationJSONReply reply(provider_, reply_message_);
+ reply.SendSuccess(return_value.get());
+ delete this;
+}
+
+void AutomationProviderImportSettingsObserver::ImportEnded() {
+ // Send back an empty success message.
+ AutomationJSONReply(provider_, reply_message_).SendSuccess(NULL);
+ delete this;
+}
+
+void AutomationProviderGetPasswordsObserver::OnPasswordStoreRequestDone(
+ int handle, const std::vector<webkit_glue::PasswordForm*>& result) {
+ scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
+
+ ListValue* passwords = new ListValue;
+ for (std::vector<webkit_glue::PasswordForm*>::const_iterator it =
+ result.begin(); it != result.end(); ++it) {
+ DictionaryValue* password_val = new DictionaryValue;
+ webkit_glue::PasswordForm* password_form = *it;
+ password_val->SetStringFromUTF16(L"username",
+ password_form->username_value);
+ password_val->SetStringFromUTF16(L"password",
+ password_form->password_value);
+ password_val->SetReal(
+ L"time", static_cast<double>(
+ password_form->date_created.ToDoubleT()));
+ passwords->Append(password_val);
+ }
+
+ return_value->Set(L"passwords", passwords);
+ AutomationJSONReply(provider_, reply_message_).SendSuccess(
+ return_value.get());
+ delete this;
+}
+
+void AutomationProviderBrowsingDataObserver::OnBrowsingDataRemoverDone() {
+ // Send back an empty success message
+ AutomationJSONReply(provider_, reply_message_).SendSuccess(NULL);
+ delete this;
+}
+
+OmniboxAcceptNotificationObserver::OmniboxAcceptNotificationObserver(
+ NavigationController* controller,
+ AutomationProvider* automation,
+ IPC::Message* reply_message)
+ : automation_(automation),
+ reply_message_(reply_message),
+ controller_(controller) {
+ Source<NavigationController> source(controller_);
+ registrar_.Add(this, NotificationType::LOAD_STOP, source);
+ // Pages requiring auth don't send LOAD_STOP.
+ registrar_.Add(this, NotificationType::AUTH_NEEDED, source);
+}
+
+OmniboxAcceptNotificationObserver::~OmniboxAcceptNotificationObserver() {
+ automation_->RemoveNavigationStatusListener(this);
+}
+
+void OmniboxAcceptNotificationObserver::Observe(
+ NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ if (type == NotificationType::LOAD_STOP ||
+ type == NotificationType::AUTH_NEEDED) {
+ AutomationJSONReply(automation_, reply_message_).SendSuccess(NULL);
+ delete this;
+ } else {
+ NOTREACHED();
+ }
+}
+
+SavePackageNotificationObserver::SavePackageNotificationObserver(
+ SavePackage* save_package,
+ AutomationProvider* automation,
+ IPC::Message* reply_message) : automation_(automation),
+ reply_message_(reply_message) {
+ Source<SavePackage> source(save_package);
+ registrar_.Add(this, NotificationType::SAVE_PACKAGE_SUCCESSFULLY_FINISHED,
+ source);
+}
+
+void SavePackageNotificationObserver::Observe(
+ NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ if (type == NotificationType::SAVE_PACKAGE_SUCCESSFULLY_FINISHED) {
+ AutomationJSONReply(automation_, reply_message_).SendSuccess(NULL);
+ delete this;
+ } else {
+ NOTREACHED();
+ }
+}
+
+WaitForInfobarCountObserver::WaitForInfobarCountObserver(
+ AutomationProvider* automation,
+ IPC::Message* reply_message,
+ TabContents* tab_contents,
+ int count)
+ : automation_(automation),
+ reply_message_(reply_message),
+ tab_contents_(tab_contents),
+ count_(count) {
+ if (tab_contents->infobar_delegate_count() == count) {
+ ConditionMet();
+ return;
+ }
+ registrar_.Add(this, NotificationType::TAB_CONTENTS_INFOBAR_ADDED,
+ Source<TabContents>(tab_contents_));
+ registrar_.Add(this, NotificationType::TAB_CONTENTS_INFOBAR_REMOVED,
+ Source<TabContents>(tab_contents_));
+}
+
+void WaitForInfobarCountObserver::Observe(
+ NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ DCHECK(type == NotificationType::TAB_CONTENTS_INFOBAR_ADDED ||
+ type == NotificationType::TAB_CONTENTS_INFOBAR_REMOVED);
+ if (tab_contents_->infobar_delegate_count() == count_) {
+ ConditionMet();
+ }
+}
+
+void WaitForInfobarCountObserver::ConditionMet() {
+ registrar_.RemoveAll();
+ AutomationJSONReply(automation_, reply_message_).SendSuccess(NULL);
+ delete this;
+}
diff --git a/chrome/browser/automation/automation_provider_observers.h b/chrome/browser/automation/automation_provider_observers.h
new file mode 100644
index 0000000..732d1fd
--- /dev/null
+++ b/chrome/browser/automation/automation_provider_observers.h
@@ -0,0 +1,738 @@
+// Copyright (c) 2009 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_PROVIDER_OBSERVERS_H_
+#define CHROME_BROWSER_AUTOMATION_AUTOMATION_PROVIDER_OBSERVERS_H_
+
+#include <deque>
+#include <map>
+#include <set>
+
+#include "chrome/browser/bookmarks/bookmark_model_observer.h"
+#include "chrome/browser/browsing_data_remover.h"
+#include "chrome/browser/download/download_item.h"
+#include "chrome/browser/download/download_manager.h"
+#include "chrome/browser/importer/importer.h"
+#include "chrome/browser/importer/importer_data_types.h"
+#include "chrome/browser/password_manager/password_store.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/common/notification_observer.h"
+#include "chrome/common/notification_registrar.h"
+#include "chrome/common/notification_type.h"
+#include "chrome/test/automation/automation_messages.h"
+
+class AutomationProvider;
+class Browser;
+class Extension;
+class ExtensionProcessManager;
+class NavigationController;
+class SavePackage;
+class TabContents;
+
+namespace IPC {
+class Message;
+}
+
+class InitialLoadObserver : public NotificationObserver {
+ public:
+ InitialLoadObserver(size_t tab_count, AutomationProvider* automation);
+ ~InitialLoadObserver();
+
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ // Caller owns the return value and is responsible for deleting it.
+ // Example return value:
+ // {'tabs': [{'start_time_ms': 1, 'stop_time_ms': 2.5},
+ // {'start_time_ms': 0.5, 'stop_time_ms': 3}]}
+ // stop_time_ms values may be null if WaitForInitialLoads has not finished.
+ // Only includes entries for the |tab_count| tabs we are monitoring.
+ // There is no defined ordering of the return value.
+ DictionaryValue* GetTimingInformation() const;
+
+ private:
+ class TabTime;
+ typedef std::map<uintptr_t, TabTime> TabTimeMap;
+ typedef std::set<uintptr_t> TabSet;
+
+ void ConditionMet();
+
+ NotificationRegistrar registrar_;
+
+ AutomationProvider* automation_;
+ size_t outstanding_tab_count_;
+ base::TimeTicks init_time_;
+ TabTimeMap loading_tabs_;
+ TabSet finished_tabs_;
+
+ DISALLOW_COPY_AND_ASSIGN(InitialLoadObserver);
+};
+
+// Watches for NewTabUI page loads for performance timing purposes.
+class NewTabUILoadObserver : public NotificationObserver {
+ public:
+ explicit NewTabUILoadObserver(AutomationProvider* automation);
+ ~NewTabUILoadObserver();
+
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ private:
+ NotificationRegistrar registrar_;
+ AutomationProvider* automation_;
+
+ DISALLOW_COPY_AND_ASSIGN(NewTabUILoadObserver);
+};
+
+class NavigationControllerRestoredObserver : public NotificationObserver {
+ public:
+ NavigationControllerRestoredObserver(AutomationProvider* automation,
+ NavigationController* controller,
+ IPC::Message* reply_message);
+ ~NavigationControllerRestoredObserver();
+
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ private:
+ bool FinishedRestoring();
+ void SendDone();
+
+ NotificationRegistrar registrar_;
+ AutomationProvider* automation_;
+ NavigationController* controller_;
+ IPC::Message* reply_message_;
+
+ DISALLOW_COPY_AND_ASSIGN(NavigationControllerRestoredObserver);
+};
+
+class NavigationNotificationObserver : public NotificationObserver {
+ public:
+ NavigationNotificationObserver(NavigationController* controller,
+ AutomationProvider* automation,
+ IPC::Message* reply_message,
+ int number_of_navigations,
+ bool include_current_navigation);
+ ~NavigationNotificationObserver();
+
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ private:
+ void ConditionMet(AutomationMsg_NavigationResponseValues navigation_result);
+
+ NotificationRegistrar registrar_;
+ AutomationProvider* automation_;
+ IPC::Message* reply_message_;
+ NavigationController* controller_;
+ int navigations_remaining_;
+ bool navigation_started_;
+
+ DISALLOW_COPY_AND_ASSIGN(NavigationNotificationObserver);
+};
+
+class TabStripNotificationObserver : public NotificationObserver {
+ public:
+ TabStripNotificationObserver(NotificationType notification,
+ AutomationProvider* automation);
+ virtual ~TabStripNotificationObserver();
+
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ virtual void ObserveTab(NavigationController* controller) = 0;
+
+ protected:
+ NotificationRegistrar registrar_;
+ AutomationProvider* automation_;
+ NotificationType notification_;
+};
+
+class TabAppendedNotificationObserver : public TabStripNotificationObserver {
+ public:
+ TabAppendedNotificationObserver(Browser* parent,
+ AutomationProvider* automation,
+ IPC::Message* reply_message);
+
+ virtual void ObserveTab(NavigationController* controller);
+
+ protected:
+ Browser* parent_;
+ IPC::Message* reply_message_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TabAppendedNotificationObserver);
+};
+
+class TabClosedNotificationObserver : public TabStripNotificationObserver {
+ public:
+ TabClosedNotificationObserver(AutomationProvider* automation,
+ bool wait_until_closed,
+ IPC::Message* reply_message);
+
+ virtual void ObserveTab(NavigationController* controller);
+
+ void set_for_browser_command(bool for_browser_command);
+
+ protected:
+ IPC::Message* reply_message_;
+ bool for_browser_command_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TabClosedNotificationObserver);
+};
+
+// Observes when an extension has finished installing or possible install
+// errors. This does not guarantee that the extension is ready for use.
+class ExtensionInstallNotificationObserver : public NotificationObserver {
+ public:
+ ExtensionInstallNotificationObserver(AutomationProvider* automation,
+ int id,
+ IPC::Message* reply_message);
+ ~ExtensionInstallNotificationObserver();
+
+ // Implementation of NotificationObserver.
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ private:
+ // Send |response| back to the provider's client.
+ void SendResponse(AutomationMsg_ExtensionResponseValues response);
+
+ NotificationRegistrar registrar_;
+ scoped_refptr<AutomationProvider> automation_;
+ int id_;
+ IPC::Message* reply_message_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExtensionInstallNotificationObserver);
+};
+
+// Observes when an extension has finished loading and is ready for use. Also
+// checks for possible install errors.
+class ExtensionReadyNotificationObserver : public NotificationObserver {
+ public:
+ ExtensionReadyNotificationObserver(ExtensionProcessManager* manager,
+ AutomationProvider* automation,
+ int id,
+ IPC::Message* reply_message);
+ ~ExtensionReadyNotificationObserver();
+
+ // Implementation of NotificationObserver.
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ private:
+ NotificationRegistrar registrar_;
+ ExtensionProcessManager* manager_;
+ scoped_refptr<AutomationProvider> automation_;
+ int id_;
+ IPC::Message* reply_message_;
+ Extension* extension_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExtensionReadyNotificationObserver);
+};
+
+class ExtensionUnloadNotificationObserver : public NotificationObserver {
+ public:
+ ExtensionUnloadNotificationObserver();
+ ~ExtensionUnloadNotificationObserver();
+
+ // Implementation of NotificationObserver.
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ bool did_receive_unload_notification() {
+ return did_receive_unload_notification_;
+ }
+
+ private:
+ NotificationRegistrar registrar_;
+ bool did_receive_unload_notification_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExtensionUnloadNotificationObserver);
+};
+
+class ExtensionTestResultNotificationObserver : public NotificationObserver {
+ public:
+ explicit ExtensionTestResultNotificationObserver(
+ AutomationProvider* automation);
+ ~ExtensionTestResultNotificationObserver();
+
+ // Implementation of NotificationObserver.
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ // Sends a test result back to the provider's client, if there is a pending
+ // provider message and there is a result in the queue.
+ void MaybeSendResult();
+
+ private:
+ NotificationRegistrar registrar_;
+ AutomationProvider* automation_;
+ // Two queues containing the test results. Although typically only
+ // one result will be in each queue, there are cases where a queue is
+ // needed.
+ // For example, perhaps two events occur asynchronously and their
+ // order of completion is not guaranteed. If the test wants to make sure
+ // both finish before continuing, a queue is needed. The test would then
+ // need to wait twice.
+ std::deque<bool> results_;
+ std::deque<std::string> messages_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExtensionTestResultNotificationObserver);
+};
+
+class BrowserOpenedNotificationObserver : public NotificationObserver {
+ public:
+ BrowserOpenedNotificationObserver(AutomationProvider* automation,
+ IPC::Message* reply_message);
+ ~BrowserOpenedNotificationObserver();
+
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ void set_for_browser_command(bool for_browser_command);
+
+ private:
+ NotificationRegistrar registrar_;
+ AutomationProvider* automation_;
+ IPC::Message* reply_message_;
+ bool for_browser_command_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowserOpenedNotificationObserver);
+};
+
+class BrowserClosedNotificationObserver : public NotificationObserver {
+ public:
+ BrowserClosedNotificationObserver(Browser* browser,
+ AutomationProvider* automation,
+ IPC::Message* reply_message);
+
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ void set_for_browser_command(bool for_browser_command);
+
+ private:
+ NotificationRegistrar registrar_;
+ AutomationProvider* automation_;
+ IPC::Message* reply_message_;
+ bool for_browser_command_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowserClosedNotificationObserver);
+};
+
+class BrowserCountChangeNotificationObserver : public NotificationObserver {
+ public:
+ BrowserCountChangeNotificationObserver(int target_count,
+ AutomationProvider* automation,
+ IPC::Message* reply_message);
+
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ private:
+ int target_count_;
+ NotificationRegistrar registrar_;
+ AutomationProvider* automation_;
+ IPC::Message* reply_message_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowserCountChangeNotificationObserver);
+};
+
+class AppModalDialogShownObserver : public NotificationObserver {
+ public:
+ AppModalDialogShownObserver(AutomationProvider* automation,
+ IPC::Message* reply_message);
+ ~AppModalDialogShownObserver();
+
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ private:
+ NotificationRegistrar registrar_;
+ AutomationProvider* automation_;
+ IPC::Message* reply_message_;
+
+ DISALLOW_COPY_AND_ASSIGN(AppModalDialogShownObserver);
+};
+
+class ExecuteBrowserCommandObserver : public NotificationObserver {
+ public:
+ ~ExecuteBrowserCommandObserver();
+
+ static bool CreateAndRegisterObserver(AutomationProvider* automation,
+ Browser* browser,
+ int command,
+ IPC::Message* reply_message);
+
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ private:
+ ExecuteBrowserCommandObserver(AutomationProvider* automation,
+ IPC::Message* reply_message);
+
+ bool Register(int command);
+
+ bool GetNotificationType(int command, NotificationType::Type* type);
+
+ NotificationRegistrar registrar_;
+ AutomationProvider* automation_;
+ NotificationType::Type notification_type_;
+ IPC::Message* reply_message_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExecuteBrowserCommandObserver);
+};
+
+class FindInPageNotificationObserver : public NotificationObserver {
+ public:
+ FindInPageNotificationObserver(AutomationProvider* automation,
+ TabContents* parent_tab,
+ IPC::Message* reply_message);
+ ~FindInPageNotificationObserver();
+
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ // The Find mechanism is over asynchronous IPC, so a search is kicked off and
+ // we wait for notification to find out what the results are. As the user is
+ // typing, new search requests can be issued and the Request ID helps us make
+ // sense of whether this is the current request or an old one. The unit tests,
+ // however, which uses this constant issues only one search at a time, so we
+ // don't need a rolling id to identify each search. But, we still need to
+ // specify one, so we just use a fixed one - its value does not matter.
+ static const int kFindInPageRequestId;
+
+ private:
+ NotificationRegistrar registrar_;
+ AutomationProvider* automation_;
+ // We will at some point (before final update) be notified of the ordinal and
+ // we need to preserve it so we can send it later.
+ int active_match_ordinal_;
+ IPC::Message* reply_message_;
+
+ DISALLOW_COPY_AND_ASSIGN(FindInPageNotificationObserver);
+};
+
+class DomOperationNotificationObserver : public NotificationObserver {
+ public:
+ explicit DomOperationNotificationObserver(AutomationProvider* automation);
+ ~DomOperationNotificationObserver();
+
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ private:
+ NotificationRegistrar registrar_;
+ AutomationProvider* automation_;
+
+ DISALLOW_COPY_AND_ASSIGN(DomOperationNotificationObserver);
+};
+
+class DocumentPrintedNotificationObserver : public NotificationObserver {
+ public:
+ DocumentPrintedNotificationObserver(AutomationProvider* automation,
+ IPC::Message* reply_message);
+ ~DocumentPrintedNotificationObserver();
+
+ virtual void Observe(NotificationType type, const NotificationSource& source,
+ const NotificationDetails& details);
+
+ private:
+ NotificationRegistrar registrar_;
+ scoped_refptr<AutomationProvider> automation_;
+ bool success_;
+ IPC::Message* reply_message_;
+
+ DISALLOW_COPY_AND_ASSIGN(DocumentPrintedNotificationObserver);
+};
+
+// Collects METRIC_EVENT_DURATION notifications and keep track of the times.
+class MetricEventDurationObserver : public NotificationObserver {
+ public:
+ MetricEventDurationObserver();
+
+ // Get the duration of an event. Returns -1 if we haven't seen the event.
+ int GetEventDurationMs(const std::string& event_name);
+
+ // NotificationObserver interface.
+ virtual void Observe(NotificationType type, const NotificationSource& source,
+ const NotificationDetails& details);
+
+ private:
+ NotificationRegistrar registrar_;
+
+ typedef std::map<std::string, int> EventDurationMap;
+ EventDurationMap durations_;
+
+ DISALLOW_COPY_AND_ASSIGN(MetricEventDurationObserver);
+};
+
+#if defined(OS_CHROMEOS)
+// Collects LOGIN_AUTHENTICATION notifications and returns
+// whether authentication succeeded to the automation provider.
+class LoginManagerObserver : public NotificationObserver {
+ public:
+ LoginManagerObserver(AutomationProvider* automation,
+ IPC::Message* reply_message);
+
+ // NotificationObserver interface.
+ virtual void Observe(NotificationType type, const NotificationSource& source,
+ const NotificationDetails& details);
+
+ private:
+ NotificationRegistrar registrar_;
+ AutomationProvider* automation_;
+ IPC::Message* reply_message_;
+
+ DISALLOW_COPY_AND_ASSIGN(LoginManagerObserver);
+};
+#endif
+
+// Waits for the bookmark model to load.
+class AutomationProviderBookmarkModelObserver : BookmarkModelObserver {
+ public:
+ AutomationProviderBookmarkModelObserver(AutomationProvider* provider,
+ IPC::Message* reply_message,
+ BookmarkModel* model);
+ virtual ~AutomationProviderBookmarkModelObserver();
+
+ virtual void Loaded(BookmarkModel* model) {
+ ReplyAndDelete(true);
+ }
+ virtual void BookmarkModelBeingDeleted(BookmarkModel* model) {
+ ReplyAndDelete(false);
+ }
+ virtual void BookmarkNodeMoved(BookmarkModel* model,
+ const BookmarkNode* old_parent,
+ int old_index,
+ const BookmarkNode* new_parent,
+ int new_index) {}
+ virtual void BookmarkNodeAdded(BookmarkModel* model,
+ const BookmarkNode* parent,
+ int index) {}
+ virtual void BookmarkNodeRemoved(BookmarkModel* model,
+ const BookmarkNode* parent,
+ int old_index,
+ const BookmarkNode* node) {}
+ virtual void BookmarkNodeChanged(BookmarkModel* model,
+ const BookmarkNode* node) {}
+ virtual void BookmarkNodeFavIconLoaded(BookmarkModel* model,
+ const BookmarkNode* node) {}
+ virtual void BookmarkNodeChildrenReordered(BookmarkModel* model,
+ const BookmarkNode* node) {}
+
+ private:
+ // Reply to the automation message with the given success value,
+ // then delete myself (which removes myself from the bookmark model
+ // observer list).
+ void ReplyAndDelete(bool success);
+
+ scoped_refptr<AutomationProvider> automation_provider_;
+ IPC::Message* reply_message_;
+ BookmarkModel* model_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutomationProviderBookmarkModelObserver);
+};
+
+// When asked for pending downloads, the DownloadManager places
+// results in a DownloadManager::Observer.
+class AutomationProviderDownloadManagerObserver
+ : public DownloadManager::Observer {
+ public:
+ AutomationProviderDownloadManagerObserver() : DownloadManager::Observer() {}
+ virtual ~AutomationProviderDownloadManagerObserver() {}
+ virtual void ModelChanged() {}
+ virtual void SetDownloads(std::vector<DownloadItem*>& downloads) {
+ downloads_ = downloads;
+ }
+ std::vector<DownloadItem*> Downloads() {
+ return downloads_;
+ }
+ private:
+ std::vector<DownloadItem*> downloads_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutomationProviderDownloadManagerObserver);
+};
+
+
+// Allows the automation provider to wait for all downloads to finish.
+class AutomationProviderDownloadItemObserver : public DownloadItem::Observer {
+ public:
+ AutomationProviderDownloadItemObserver(
+ AutomationProvider* provider,
+ IPC::Message* reply_message,
+ int downloads) {
+ provider_ = provider;
+ reply_message_ = reply_message;
+ downloads_ = downloads;
+ }
+ virtual ~AutomationProviderDownloadItemObserver() {}
+
+ virtual void OnDownloadUpdated(DownloadItem* download) { }
+ virtual void OnDownloadFileCompleted(DownloadItem* download);
+ virtual void OnDownloadOpened(DownloadItem* download) { }
+
+ private:
+ AutomationProvider* provider_;
+ IPC::Message* reply_message_;
+ int downloads_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutomationProviderDownloadItemObserver);
+};
+
+// Allows the automation provider to wait for history queries to finish.
+class AutomationProviderHistoryObserver {
+ public:
+ AutomationProviderHistoryObserver(
+ AutomationProvider* provider,
+ IPC::Message* reply_message) {
+ provider_ = provider;
+ reply_message_ = reply_message;
+ }
+ ~AutomationProviderHistoryObserver() {}
+ void HistoryQueryComplete(HistoryService::Handle request_handle,
+ history::QueryResults* results);
+
+ private:
+ AutomationProvider* provider_;
+ IPC::Message* reply_message_;
+};
+
+// Allows the automation provider to wait for import queries to finish.
+class AutomationProviderImportSettingsObserver
+ : public ImporterHost::Observer {
+ public:
+ AutomationProviderImportSettingsObserver(
+ AutomationProvider* provider,
+ IPC::Message* reply_message)
+ : provider_(provider),
+ reply_message_(reply_message) {}
+ void ImportStarted() {}
+ void ImportItemStarted(importer::ImportItem item) {}
+ void ImportItemEnded(importer::ImportItem item) {}
+ void ImportEnded();
+ private:
+ AutomationProvider* provider_;
+ IPC::Message* reply_message_;
+};
+
+// Allows automation provider to wait for getting passwords to finish.
+class AutomationProviderGetPasswordsObserver :
+ public PasswordStoreConsumer {
+ public:
+ AutomationProviderGetPasswordsObserver(
+ AutomationProvider* provider,
+ IPC::Message* reply_message)
+ : provider_(provider),
+ reply_message_(reply_message) {}
+
+ void OnPasswordStoreRequestDone(
+ int handle, const std::vector<webkit_glue::PasswordForm*>& result);
+
+ private:
+ AutomationProvider* provider_;
+ IPC::Message* reply_message_;
+};
+
+// Allows the automation provider to wait for clearing browser data to finish.
+class AutomationProviderBrowsingDataObserver
+ : public BrowsingDataRemover::Observer {
+ public:
+ AutomationProviderBrowsingDataObserver(
+ AutomationProvider* provider,
+ IPC::Message* reply_message)
+ : provider_(provider),
+ reply_message_(reply_message) {}
+ void OnBrowsingDataRemoverDone();
+
+ private:
+ AutomationProvider* provider_;
+ IPC::Message* reply_message_;
+};
+
+// Allows automation provider to wait until page load after selecting an item
+// in the omnibox popup.
+class OmniboxAcceptNotificationObserver : public NotificationObserver {
+ public:
+ OmniboxAcceptNotificationObserver(NavigationController* controller,
+ AutomationProvider* automation,
+ IPC::Message* reply_message);
+ ~OmniboxAcceptNotificationObserver();
+
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ private:
+ NotificationRegistrar registrar_;
+ AutomationProvider* automation_;
+ IPC::Message* reply_message_;
+ NavigationController* controller_;
+
+ DISALLOW_COPY_AND_ASSIGN(OmniboxAcceptNotificationObserver);
+};
+
+// Allows the automation provider to wait for a save package notification.
+class SavePackageNotificationObserver : public NotificationObserver {
+ public:
+ SavePackageNotificationObserver(SavePackage* save_package,
+ AutomationProvider* automation,
+ IPC::Message* reply_message);
+ virtual ~SavePackageNotificationObserver() {}
+
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ private:
+ NotificationRegistrar registrar_;
+ AutomationProvider* automation_;
+ IPC::Message* reply_message_;
+
+ DISALLOW_COPY_AND_ASSIGN(SavePackageNotificationObserver);
+};
+
+// Allows the automation provider to wait for a given number of infobars.
+class WaitForInfobarCountObserver : public NotificationObserver {
+ public:
+ WaitForInfobarCountObserver(AutomationProvider* automation,
+ IPC::Message* reply_message,
+ TabContents* tab_contents,
+ int count);
+
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ private:
+ void ConditionMet();
+
+ NotificationRegistrar registrar_;
+ AutomationProvider* automation_;
+ IPC::Message* reply_message_;
+ TabContents* tab_contents_;
+ int count_;
+
+ DISALLOW_COPY_AND_ASSIGN(WaitForInfobarCountObserver);
+};
+
+#endif // CHROME_BROWSER_AUTOMATION_AUTOMATION_PROVIDER_OBSERVERS_H_
diff --git a/chrome/browser/automation/automation_provider_unittest.cc b/chrome/browser/automation/automation_provider_unittest.cc
new file mode 100644
index 0000000..356d0f4
--- /dev/null
+++ b/chrome/browser/automation/automation_provider_unittest.cc
@@ -0,0 +1,32 @@
+// Copyright (c) 2009 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/chrome_frame_automation_provider.h"
+#include "ipc/ipc_message.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class MockChromeFrameAutomationProvider
+ : public ChromeFrameAutomationProvider {
+ public:
+ explicit MockChromeFrameAutomationProvider(Profile* profile)
+ : ChromeFrameAutomationProvider(profile) {}
+
+ virtual ~MockChromeFrameAutomationProvider() {}
+
+ MOCK_METHOD1(OnUnhandledMessage,
+ void (const IPC::Message& message)); // NOLINT
+};
+
+TEST(AutomationProviderTest, TestInvalidChromeFrameMessage) {
+ IPC::Message bad_msg(1, -1, IPC::Message::PRIORITY_NORMAL);
+
+ scoped_refptr<MockChromeFrameAutomationProvider>
+ mock(new MockChromeFrameAutomationProvider(NULL));
+
+ EXPECT_CALL(*mock, OnUnhandledMessage(testing::Property(&IPC::Message::type,
+ -1))).Times(1);
+ mock->OnMessageReceived(bad_msg);
+}
+
diff --git a/chrome/browser/automation/automation_provider_views.cc b/chrome/browser/automation/automation_provider_views.cc
new file mode 100644
index 0000000..f130079
--- /dev/null
+++ b/chrome/browser/automation/automation_provider_views.cc
@@ -0,0 +1,190 @@
+// Copyright (c) 2009 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_provider.h"
+
+#include "chrome/browser/automation/automation_browser_tracker.h"
+#include "chrome/browser/automation/automation_window_tracker.h"
+#include "chrome/browser/views/frame/browser_view.h"
+#include "chrome/browser/views/toolbar_view.h"
+#include "chrome/test/automation/automation_messages.h"
+#include "gfx/point.h"
+#include "views/controls/menu/menu_wrapper.h"
+#include "views/focus/focus_manager.h"
+#include "views/view.h"
+#include "views/widget/root_view.h"
+#include "views/widget/widget.h"
+
+void AutomationProvider::WindowGetViewBounds(int handle, int view_id,
+ bool screen_coordinates,
+ bool* success,
+ gfx::Rect* bounds) {
+ *success = false;
+
+ if (window_tracker_->ContainsHandle(handle)) {
+ gfx::NativeWindow window = window_tracker_->GetResource(handle);
+ views::RootView* root_view = views::Widget::FindRootView(window);
+ if (root_view) {
+ views::View* view = root_view->GetViewByID(view_id);
+ if (view) {
+ *success = true;
+ gfx::Point point;
+ if (screen_coordinates)
+ views::View::ConvertPointToScreen(view, &point);
+ else
+ views::View::ConvertPointToView(view, root_view, &point);
+ *bounds = view->GetLocalBounds(false);
+ bounds->set_origin(point);
+ }
+ }
+ }
+}
+
+void AutomationProvider::GetFocusedViewID(int handle, int* view_id) {
+ *view_id = -1;
+ if (window_tracker_->ContainsHandle(handle)) {
+ gfx::NativeWindow window = window_tracker_->GetResource(handle);
+ views::FocusManager* focus_manager =
+ views::FocusManager::GetFocusManagerForNativeWindow(window);
+ DCHECK(focus_manager);
+ views::View* focused_view = focus_manager->GetFocusedView();
+ if (focused_view)
+ *view_id = focused_view->GetID();
+ }
+}
+
+// Helper class that waits until the focus has changed to a view other
+// than the one with the provided view id.
+class ViewFocusChangeWaiter : public views::FocusChangeListener {
+ public:
+ ViewFocusChangeWaiter(views::FocusManager* focus_manager,
+ int previous_view_id,
+ AutomationProvider* automation,
+ IPC::Message* reply_message)
+ : focus_manager_(focus_manager),
+ previous_view_id_(previous_view_id),
+ automation_(automation),
+ reply_message_(reply_message),
+ ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {
+ focus_manager_->AddFocusChangeListener(this);
+ // Call the focus change notification once in case the focus has
+ // already changed.
+ FocusWillChange(NULL, focus_manager_->GetFocusedView());
+ }
+
+ ~ViewFocusChangeWaiter() {
+ focus_manager_->RemoveFocusChangeListener(this);
+ }
+
+ // Inherited from FocusChangeListener
+ virtual void FocusWillChange(views::View* focused_before,
+ views::View* focused_now) {
+ // This listener is called before focus actually changes. Post a task
+ // that will get run after focus changes.
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ method_factory_.NewRunnableMethod(
+ &ViewFocusChangeWaiter::FocusChanged,
+ focused_before,
+ focused_now));
+ }
+
+ private:
+ void FocusChanged(views::View* focused_before,
+ views::View* focused_now) {
+ if (focused_now && focused_now->GetID() != previous_view_id_) {
+ AutomationMsg_WaitForFocusedViewIDToChange::WriteReplyParams(
+ reply_message_, true, focused_now->GetID());
+
+ automation_->Send(reply_message_);
+ delete this;
+ }
+ }
+
+ views::FocusManager* focus_manager_;
+ int previous_view_id_;
+ AutomationProvider* automation_;
+ IPC::Message* reply_message_;
+ ScopedRunnableMethodFactory<ViewFocusChangeWaiter> method_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ViewFocusChangeWaiter);
+};
+
+void AutomationProvider::WaitForFocusedViewIDToChange(
+ int handle, int previous_view_id, IPC::Message* reply_message) {
+ if (!window_tracker_->ContainsHandle(handle))
+ return;
+ gfx::NativeWindow window = window_tracker_->GetResource(handle);
+ views::FocusManager* focus_manager =
+ views::FocusManager::GetFocusManagerForNativeWindow(window);
+
+ // The waiter will respond to the IPC and delete itself when done.
+ new ViewFocusChangeWaiter(focus_manager,
+ previous_view_id,
+ this,
+ reply_message);
+}
+
+class PopupMenuWaiter : public views::MenuListener {
+ public:
+ PopupMenuWaiter(ToolbarView* toolbar_view,
+ AutomationProvider* automation)
+ : toolbar_view_(toolbar_view),
+ automation_(automation),
+ reply_message_(NULL) {
+ toolbar_view_->AddMenuListener(this);
+ }
+
+ // Implementation of views::MenuListener
+ virtual void OnMenuOpened() {
+ toolbar_view_->RemoveMenuListener(this);
+ automation_->popup_menu_opened_ = true;
+ automation_->popup_menu_waiter_ = NULL;
+ if (reply_message_) {
+ AutomationMsg_WaitForPopupMenuToOpen::WriteReplyParams(
+ reply_message_, true);
+ automation_->Send(reply_message_);
+ }
+ delete this;
+ }
+
+ void set_reply_message(IPC::Message* reply_message) {
+ reply_message_ = reply_message;
+ }
+
+ private:
+ ToolbarView* toolbar_view_;
+ AutomationProvider* automation_;
+ IPC::Message* reply_message_;
+
+ DISALLOW_COPY_AND_ASSIGN(PopupMenuWaiter);
+};
+
+void AutomationProvider::StartTrackingPopupMenus(
+ int browser_handle, bool* success) {
+ if (browser_tracker_->ContainsHandle(browser_handle)) {
+ Browser* browser = browser_tracker_->GetResource(browser_handle);
+ BrowserView* browser_view = reinterpret_cast<BrowserView*>(
+ browser->window());
+ ToolbarView* toolbar_view = browser_view->GetToolbarView();
+ popup_menu_opened_ = false;
+ popup_menu_waiter_ = new PopupMenuWaiter(toolbar_view, this);
+ *success = true;
+ }
+}
+
+void AutomationProvider::WaitForPopupMenuToOpen(IPC::Message* reply_message) {
+ // See if the menu already opened and return true if so.
+ if (popup_menu_opened_) {
+ AutomationMsg_WaitForPopupMenuToOpen::WriteReplyParams(
+ reply_message, true);
+ Send(reply_message);
+ return;
+ }
+
+ // Otherwise, register this reply message with the waiter,
+ // which will handle responding to this IPC when the popup
+ // menu opens.
+ popup_menu_waiter_->set_reply_message(reply_message);
+}
diff --git a/chrome/browser/automation/automation_provider_win.cc b/chrome/browser/automation/automation_provider_win.cc
new file mode 100644
index 0000000..51959bd
--- /dev/null
+++ b/chrome/browser/automation/automation_provider_win.cc
@@ -0,0 +1,605 @@
+// Copyright (c) 2009 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_provider.h"
+
+#include "base/json/json_reader.h"
+#include "base/keyboard_codes.h"
+#include "chrome/browser/automation/automation_browser_tracker.h"
+#include "chrome/browser/automation/automation_extension_function.h"
+#include "chrome/browser/automation/automation_tab_tracker.h"
+#include "chrome/browser/automation/automation_window_tracker.h"
+#include "chrome/browser/automation/extension_automation_constants.h"
+#include "chrome/browser/automation/extension_port_container.h"
+#include "chrome/browser/automation/ui_controls.h"
+#include "chrome/browser/browser_window.h"
+#include "chrome/browser/extensions/extension_message_service.h"
+#include "chrome/browser/external_tab_container_win.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/renderer_host/render_view_host.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/browser/views/bookmark_bar_view.h"
+#include "chrome/test/automation/automation_messages.h"
+#include "views/widget/root_view.h"
+#include "views/widget/widget_win.h"
+#include "views/window/window.h"
+
+// This task just adds another task to the event queue. This is useful if
+// you want to ensure that any tasks added to the event queue after this one
+// have already been processed by the time |task| is run.
+class InvokeTaskLaterTask : public Task {
+ public:
+ explicit InvokeTaskLaterTask(Task* task) : task_(task) {}
+ virtual ~InvokeTaskLaterTask() {}
+
+ virtual void Run() {
+ MessageLoop::current()->PostTask(FROM_HERE, task_);
+ }
+
+ private:
+ Task* task_;
+
+ DISALLOW_COPY_AND_ASSIGN(InvokeTaskLaterTask);
+};
+
+static void MoveMouse(const POINT& point) {
+ SetCursorPos(point.x, point.y);
+
+ // Now, make sure that GetMessagePos returns the values we just set by
+ // simulating a mouse move. The value returned by GetMessagePos is updated
+ // when a mouse move event is removed from the event queue.
+ PostMessage(NULL, WM_MOUSEMOVE, 0, MAKELPARAM(point.x, point.y));
+ MSG msg;
+ while (PeekMessage(&msg, NULL, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE)) {
+ }
+
+ // Verify
+#ifndef NDEBUG
+ DWORD pos = GetMessagePos();
+ gfx::Point cursor_point(pos);
+ DCHECK_EQ(point.x, cursor_point.x());
+ DCHECK_EQ(point.y, cursor_point.y());
+#endif
+}
+
+BOOL CALLBACK EnumThreadWndProc(HWND hwnd, LPARAM l_param) {
+ if (hwnd == reinterpret_cast<HWND>(l_param)) {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+void AutomationProvider::GetActiveWindow(int* handle) {
+ HWND window = GetForegroundWindow();
+
+ // Let's make sure this window belongs to our process.
+ if (EnumThreadWindows(::GetCurrentThreadId(),
+ EnumThreadWndProc,
+ reinterpret_cast<LPARAM>(window))) {
+ // We enumerated all the windows and did not find the foreground window,
+ // it is not our window, ignore it.
+ *handle = 0;
+ return;
+ }
+
+ *handle = window_tracker_->Add(window);
+}
+
+// This task enqueues a mouse event on the event loop, so that the view
+// that it's being sent to can do the requisite post-processing.
+class MouseEventTask : public Task {
+ public:
+ MouseEventTask(views::View* view,
+ views::Event::EventType type,
+ const gfx::Point& point,
+ int flags)
+ : view_(view), type_(type), point_(point), flags_(flags) {}
+ virtual ~MouseEventTask() {}
+
+ virtual void Run() {
+ views::MouseEvent event(type_, point_.x(), point_.y(), flags_);
+ // We need to set the cursor position before we process the event because
+ // some code (tab dragging, for instance) queries the actual cursor location
+ // rather than the location of the mouse event. Note that the reason why
+ // the drag code moved away from using mouse event locations was because
+ // our conversion to screen location doesn't work well with multiple
+ // monitors, so this only works reliably in a single monitor setup.
+ gfx::Point screen_location(point_.x(), point_.y());
+ view_->ConvertPointToScreen(view_, &screen_location);
+ MoveMouse(screen_location.ToPOINT());
+ switch (type_) {
+ case views::Event::ET_MOUSE_PRESSED:
+ view_->OnMousePressed(event);
+ break;
+
+ case views::Event::ET_MOUSE_DRAGGED:
+ view_->OnMouseDragged(event);
+ break;
+
+ case views::Event::ET_MOUSE_RELEASED:
+ view_->OnMouseReleased(event, false);
+ break;
+
+ default:
+ NOTREACHED();
+ }
+ }
+
+ private:
+ views::View* view_;
+ views::Event::EventType type_;
+ gfx::Point point_;
+ int flags_;
+
+ DISALLOW_COPY_AND_ASSIGN(MouseEventTask);
+};
+
+// This task sends a WindowDragResponse message with the appropriate
+// routing ID to the automation proxy. This is implemented as a task so that
+// we know that the mouse events (and any tasks that they spawn on the message
+// loop) have been processed by the time this is sent.
+class WindowDragResponseTask : public Task {
+ public:
+ WindowDragResponseTask(AutomationProvider* provider,
+ IPC::Message* reply_message)
+ : provider_(provider), reply_message_(reply_message) {}
+ virtual ~WindowDragResponseTask() {}
+
+ virtual void Run() {
+ DCHECK(reply_message_ != NULL);
+ AutomationMsg_WindowDrag::WriteReplyParams(reply_message_, true);
+ provider_->Send(reply_message_);
+ }
+
+ private:
+ AutomationProvider* provider_;
+ IPC::Message* reply_message_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowDragResponseTask);
+};
+
+void AutomationProvider::WindowSimulateDrag(int handle,
+ std::vector<gfx::Point> drag_path,
+ int flags,
+ bool press_escape_en_route,
+ IPC::Message* reply_message) {
+ if (browser_tracker_->ContainsHandle(handle) && (drag_path.size() > 1)) {
+ gfx::NativeWindow window =
+ browser_tracker_->GetResource(handle)->window()->GetNativeHandle();
+
+ UINT down_message = 0;
+ UINT up_message = 0;
+ WPARAM wparam_flags = 0;
+ if (flags & views::Event::EF_SHIFT_DOWN)
+ wparam_flags |= MK_SHIFT;
+ if (flags & views::Event::EF_CONTROL_DOWN)
+ wparam_flags |= MK_CONTROL;
+ if (flags & views::Event::EF_LEFT_BUTTON_DOWN) {
+ wparam_flags |= MK_LBUTTON;
+ down_message = WM_LBUTTONDOWN;
+ up_message = WM_LBUTTONUP;
+ }
+ if (flags & views::Event::EF_MIDDLE_BUTTON_DOWN) {
+ wparam_flags |= MK_MBUTTON;
+ down_message = WM_MBUTTONDOWN;
+ up_message = WM_MBUTTONUP;
+ }
+ if (flags & views::Event::EF_RIGHT_BUTTON_DOWN) {
+ wparam_flags |= MK_RBUTTON;
+ down_message = WM_LBUTTONDOWN;
+ up_message = WM_LBUTTONUP;
+ }
+
+ Browser* browser = browser_tracker_->GetResource(handle);
+ DCHECK(browser);
+ HWND top_level_hwnd =
+ reinterpret_cast<HWND>(browser->window()->GetNativeHandle());
+ POINT temp = drag_path[0].ToPOINT();
+ MapWindowPoints(top_level_hwnd, HWND_DESKTOP, &temp, 1);
+ MoveMouse(temp);
+ SendMessage(top_level_hwnd, down_message, wparam_flags,
+ MAKELPARAM(drag_path[0].x(), drag_path[0].y()));
+ for (int i = 1; i < static_cast<int>(drag_path.size()); ++i) {
+ temp = drag_path[i].ToPOINT();
+ MapWindowPoints(top_level_hwnd, HWND_DESKTOP, &temp, 1);
+ MoveMouse(temp);
+ SendMessage(top_level_hwnd, WM_MOUSEMOVE, wparam_flags,
+ MAKELPARAM(drag_path[i].x(), drag_path[i].y()));
+ }
+ POINT end = drag_path[drag_path.size() - 1].ToPOINT();
+ MapWindowPoints(top_level_hwnd, HWND_DESKTOP, &end, 1);
+ MoveMouse(end);
+
+ if (press_escape_en_route) {
+ // Press Escape.
+ ui_controls::SendKeyPress(window, base::VKEY_ESCAPE,
+ ((flags & views::Event::EF_CONTROL_DOWN)
+ == views::Event::EF_CONTROL_DOWN),
+ ((flags & views::Event::EF_SHIFT_DOWN) ==
+ views::Event::EF_SHIFT_DOWN),
+ ((flags & views::Event::EF_ALT_DOWN) ==
+ views::Event::EF_ALT_DOWN),
+ false);
+ }
+ SendMessage(top_level_hwnd, up_message, wparam_flags,
+ MAKELPARAM(end.x, end.y));
+
+ MessageLoop::current()->PostTask(FROM_HERE, new InvokeTaskLaterTask(
+ new WindowDragResponseTask(this, reply_message)));
+ } else {
+ AutomationMsg_WindowDrag::WriteReplyParams(reply_message, false);
+ Send(reply_message);
+ }
+}
+
+void AutomationProvider::GetWindowBounds(int handle, gfx::Rect* bounds,
+ bool* success) {
+ *success = false;
+ HWND hwnd = window_tracker_->GetResource(handle);
+ if (hwnd) {
+ *success = true;
+ WINDOWPLACEMENT window_placement;
+ GetWindowPlacement(hwnd, &window_placement);
+ *bounds = window_placement.rcNormalPosition;
+ }
+}
+
+void AutomationProvider::SetWindowBounds(int handle, const gfx::Rect& bounds,
+ bool* success) {
+ *success = false;
+ if (window_tracker_->ContainsHandle(handle)) {
+ HWND hwnd = window_tracker_->GetResource(handle);
+ if (::MoveWindow(hwnd, bounds.x(), bounds.y(), bounds.width(),
+ bounds.height(), true)) {
+ *success = true;
+ }
+ }
+}
+
+void AutomationProvider::SetWindowVisible(int handle, bool visible,
+ bool* result) {
+ if (window_tracker_->ContainsHandle(handle)) {
+ HWND hwnd = window_tracker_->GetResource(handle);
+ ::ShowWindow(hwnd, visible ? SW_SHOW : SW_HIDE);
+ *result = true;
+ } else {
+ *result = false;
+ }
+}
+
+void AutomationProvider::ActivateWindow(int handle) {
+ if (window_tracker_->ContainsHandle(handle)) {
+ ::SetActiveWindow(window_tracker_->GetResource(handle));
+ }
+}
+
+void AutomationProvider::IsWindowMaximized(int handle, bool* is_maximized,
+ bool* success) {
+ *success = false;
+
+ HWND hwnd = window_tracker_->GetResource(handle);
+ if (hwnd) {
+ *success = true;
+ WINDOWPLACEMENT window_placement;
+ GetWindowPlacement(hwnd, &window_placement);
+ *is_maximized = (window_placement.showCmd == SW_MAXIMIZE);
+ }
+}
+
+void AutomationProvider::GetTabHWND(int handle, HWND* tab_hwnd) {
+ *tab_hwnd = NULL;
+
+ if (tab_tracker_->ContainsHandle(handle)) {
+ NavigationController* tab = tab_tracker_->GetResource(handle);
+ *tab_hwnd = tab->tab_contents()->GetNativeView();
+ }
+}
+
+void AutomationProvider::CreateExternalTab(
+ const IPC::ExternalTabSettings& settings,
+ gfx::NativeWindow* tab_container_window, gfx::NativeWindow* tab_window,
+ int* tab_handle) {
+ *tab_handle = 0;
+ *tab_container_window = NULL;
+ *tab_window = NULL;
+ scoped_refptr<ExternalTabContainer> external_tab_container =
+ new ExternalTabContainer(this, automation_resource_message_filter_);
+
+ Profile* profile = settings.is_off_the_record ?
+ profile_->GetOffTheRecordProfile() : profile_;
+
+ // When the ExternalTabContainer window is created we grab a reference on it
+ // which is released when the window is destroyed.
+ external_tab_container->Init(profile, settings.parent, settings.dimensions,
+ settings.style, settings.load_requests_via_automation,
+ settings.handle_top_level_requests, NULL, settings.initial_url,
+ settings.referrer, settings.infobars_enabled);
+
+ if (AddExternalTab(external_tab_container)) {
+ TabContents* tab_contents = external_tab_container->tab_contents();
+ *tab_handle = external_tab_container->tab_handle();
+ *tab_container_window = external_tab_container->GetNativeView();
+ *tab_window = tab_contents->GetNativeView();
+ } else {
+ external_tab_container->Uninitialize();
+ }
+}
+
+bool AutomationProvider::AddExternalTab(ExternalTabContainer* external_tab) {
+ DCHECK(external_tab != NULL);
+
+ TabContents* tab_contents = external_tab->tab_contents();
+ if (tab_contents) {
+ int tab_handle = tab_tracker_->Add(&tab_contents->controller());
+ external_tab->SetTabHandle(tab_handle);
+ return true;
+ }
+
+ return false;
+}
+
+void AutomationProvider::ProcessUnhandledAccelerator(
+ const IPC::Message& message, int handle, const MSG& msg) {
+ ExternalTabContainer* external_tab = GetExternalTabForHandle(handle);
+ if (external_tab) {
+ external_tab->ProcessUnhandledAccelerator(msg);
+ }
+ // This message expects no response.
+}
+
+void AutomationProvider::SetInitialFocus(const IPC::Message& message,
+ int handle, bool reverse,
+ bool restore_focus_to_view) {
+ ExternalTabContainer* external_tab = GetExternalTabForHandle(handle);
+ if (external_tab) {
+ external_tab->FocusThroughTabTraversal(reverse, restore_focus_to_view);
+ }
+ // This message expects no response.
+}
+
+void AutomationProvider::PrintAsync(int tab_handle) {
+ NavigationController* tab = NULL;
+ TabContents* tab_contents = GetTabContentsForHandle(tab_handle, &tab);
+ if (tab_contents) {
+ if (tab_contents->PrintNow())
+ return;
+ }
+}
+
+ExternalTabContainer* AutomationProvider::GetExternalTabForHandle(int handle) {
+ if (tab_tracker_->ContainsHandle(handle)) {
+ NavigationController* tab = tab_tracker_->GetResource(handle);
+ return ExternalTabContainer::GetContainerForTab(
+ tab->tab_contents()->GetNativeView());
+ }
+
+ return NULL;
+}
+
+void AutomationProvider::OnTabReposition(
+ int tab_handle, const IPC::Reposition_Params& params) {
+ if (!tab_tracker_->ContainsHandle(tab_handle))
+ return;
+
+ if (!IsWindow(params.window))
+ return;
+
+ unsigned long process_id = 0;
+ unsigned long thread_id = 0;
+
+ thread_id = GetWindowThreadProcessId(params.window, &process_id);
+
+ if (thread_id != GetCurrentThreadId()) {
+ DCHECK_EQ(thread_id, GetCurrentThreadId());
+ return;
+ }
+
+ SetWindowPos(params.window, params.window_insert_after, params.left,
+ params.top, params.width, params.height, params.flags);
+
+ if (params.set_parent) {
+ if (IsWindow(params.parent_window)) {
+ if (!SetParent(params.window, params.parent_window))
+ DLOG(WARNING) << "SetParent failed. Error 0x%x" << GetLastError();
+ }
+ }
+}
+
+void AutomationProvider::OnForwardContextMenuCommandToChrome(int tab_handle,
+ int command) {
+ if (tab_tracker_->ContainsHandle(tab_handle)) {
+ NavigationController* tab = tab_tracker_->GetResource(tab_handle);
+ if (!tab) {
+ NOTREACHED();
+ return;
+ }
+
+ TabContents* tab_contents = tab->tab_contents();
+ if (!tab_contents || !tab_contents->delegate()) {
+ NOTREACHED();
+ return;
+ }
+
+ tab_contents->delegate()->ExecuteContextMenuCommand(command);
+ }
+}
+
+void AutomationProvider::ConnectExternalTab(
+ uint64 cookie,
+ bool allow,
+ gfx::NativeWindow parent_window,
+ gfx::NativeWindow* tab_container_window,
+ gfx::NativeWindow* tab_window,
+ int* tab_handle) {
+ *tab_handle = 0;
+ *tab_container_window = NULL;
+ *tab_window = NULL;
+
+ scoped_refptr<ExternalTabContainer> external_tab_container =
+ ExternalTabContainer::RemovePendingTab(static_cast<uintptr_t>(cookie));
+ if (!external_tab_container.get()) {
+ NOTREACHED();
+ return;
+ }
+
+ if (allow && AddExternalTab(external_tab_container)) {
+ external_tab_container->Reinitialize(this,
+ automation_resource_message_filter_,
+ parent_window);
+ TabContents* tab_contents = external_tab_container->tab_contents();
+ *tab_handle = external_tab_container->tab_handle();
+ *tab_container_window = external_tab_container->GetNativeView();
+ *tab_window = tab_contents->GetNativeView();
+ } else {
+ external_tab_container->Uninitialize();
+ }
+}
+
+void AutomationProvider::TerminateSession(int handle, bool* success) {
+ *success = false;
+
+ if (browser_tracker_->ContainsHandle(handle)) {
+ Browser* browser = browser_tracker_->GetResource(handle);
+ HWND window = browser->window()->GetNativeHandle();
+ *success = (::PostMessageW(window, WM_ENDSESSION, 0, 0) == TRUE);
+ }
+}
+
+void AutomationProvider::SetEnableExtensionAutomation(
+ int tab_handle,
+ const std::vector<std::string>& functions_enabled) {
+ ExternalTabContainer* external_tab = GetExternalTabForHandle(tab_handle);
+ if (external_tab) {
+ external_tab->SetEnableExtensionAutomation(functions_enabled);
+ } else {
+ // Tab must exist, and must be an external tab so that its
+ // delegate has an on-empty
+ // implementation of ForwardMessageToExternalHost.
+ DLOG(WARNING) <<
+ "SetEnableExtensionAutomation called with invalid tab handle.";
+ }
+}
+
+void AutomationProvider::OnBrowserMoved(int tab_handle) {
+ ExternalTabContainer* external_tab = GetExternalTabForHandle(tab_handle);
+ if (external_tab) {
+ external_tab->WindowMoved();
+ } else {
+ DLOG(WARNING) <<
+ "AutomationProvider::OnBrowserMoved called with invalid tab handle.";
+ }
+}
+
+void AutomationProvider::GetWindowTitle(int handle, string16* text) {
+ gfx::NativeWindow window = window_tracker_->GetResource(handle);
+ std::wstring result;
+ int length = ::GetWindowTextLength(window) + 1;
+ ::GetWindowText(window, WriteInto(&result, length), length);
+ text->assign(WideToUTF16(result));
+}
+
+void AutomationProvider::OnMessageFromExternalHost(int handle,
+ const std::string& message,
+ const std::string& origin,
+ const std::string& target) {
+ RenderViewHost* view_host = GetViewForTab(handle);
+ if (!view_host)
+ return;
+
+ if (AutomationExtensionFunction::InterceptMessageFromExternalHost(
+ view_host, message, origin, target)) {
+ // Message was diverted.
+ return;
+ }
+
+ if (ExtensionPortContainer::InterceptMessageFromExternalHost(
+ message, origin, target, this, view_host, handle)) {
+ // Message was diverted.
+ return;
+ }
+
+ if (InterceptBrowserEventMessageFromExternalHost(message, origin, target)) {
+ // Message was diverted.
+ return;
+ }
+
+ view_host->ForwardMessageFromExternalHost(message, origin, target);
+}
+
+bool AutomationProvider::InterceptBrowserEventMessageFromExternalHost(
+ const std::string& message, const std::string& origin,
+ const std::string& target) {
+ if (target !=
+ extension_automation_constants::kAutomationBrowserEventRequestTarget)
+ return false;
+
+ if (origin != extension_automation_constants::kAutomationOrigin) {
+ LOG(WARNING) << "Wrong origin on automation browser event " << origin;
+ return false;
+ }
+
+ // The message is a JSON-encoded array with two elements, both strings. The
+ // first is the name of the event to dispatch. The second is a JSON-encoding
+ // of the arguments specific to that event.
+ scoped_ptr<Value> message_value(base::JSONReader::Read(message, false));
+ if (!message_value.get() || !message_value->IsType(Value::TYPE_LIST)) {
+ LOG(WARNING) << "Invalid browser event specified through automation";
+ return false;
+ }
+
+ const ListValue* args = static_cast<const ListValue*>(message_value.get());
+
+ std::string event_name;
+ if (!args->GetString(0, &event_name)) {
+ LOG(WARNING) << "No browser event name specified through automation";
+ return false;
+ }
+
+ std::string json_args;
+ if (!args->GetString(1, &json_args)) {
+ LOG(WARNING) << "No browser event args specified through automation";
+ return false;
+ }
+
+ if (profile()->GetExtensionMessageService()) {
+ profile()->GetExtensionMessageService()->DispatchEventToRenderers(
+ event_name, json_args, profile()->IsOffTheRecord(), GURL());
+ }
+
+ return true;
+}
+
+void AutomationProvider::NavigateInExternalTab(
+ int handle, const GURL& url, const GURL& referrer,
+ AutomationMsg_NavigationResponseValues* status) {
+ *status = AUTOMATION_MSG_NAVIGATION_ERROR;
+
+ if (tab_tracker_->ContainsHandle(handle)) {
+ NavigationController* tab = tab_tracker_->GetResource(handle);
+ tab->LoadURL(url, referrer, PageTransition::TYPED);
+ *status = AUTOMATION_MSG_NAVIGATION_SUCCESS;
+ }
+}
+
+void AutomationProvider::NavigateExternalTabAtIndex(
+ int handle, int navigation_index,
+ AutomationMsg_NavigationResponseValues* status) {
+ *status = AUTOMATION_MSG_NAVIGATION_ERROR;
+
+ if (tab_tracker_->ContainsHandle(handle)) {
+ NavigationController* tab = tab_tracker_->GetResource(handle);
+ tab->GoToIndex(navigation_index);
+ *status = AUTOMATION_MSG_NAVIGATION_SUCCESS;
+ }
+}
+
+void AutomationProvider::OnRunUnloadHandlers(
+ int handle, gfx::NativeWindow notification_window,
+ int notification_message) {
+ ExternalTabContainer* external_tab = GetExternalTabForHandle(handle);
+ if (external_tab) {
+ external_tab->RunUnloadHandlers(notification_window, notification_message);
+ }
+}
+
diff --git a/chrome/browser/automation/automation_resource_message_filter.cc b/chrome/browser/automation/automation_resource_message_filter.cc
new file mode 100644
index 0000000..68fc346
--- /dev/null
+++ b/chrome/browser/automation/automation_resource_message_filter.cc
@@ -0,0 +1,389 @@
+// Copyright (c) 2006-2009 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_resource_message_filter.h"
+
+#include "base/histogram.h"
+#include "base/path_service.h"
+#include "base/stl_util-inl.h"
+#include "chrome/browser/automation/url_request_automation_job.h"
+#include "chrome/browser/net/url_request_failed_dns_job.h"
+#include "chrome/browser/net/url_request_mock_http_job.h"
+#include "chrome/browser/net/url_request_mock_util.h"
+#include "chrome/browser/net/url_request_slow_download_job.h"
+#include "chrome/browser/net/url_request_slow_http_job.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/browser/chrome_thread.h"
+#include "chrome/test/automation/automation_messages.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/cookie_store.h"
+#include "net/base/net_errors.h"
+#include "net/url_request/url_request_filter.h"
+
+AutomationResourceMessageFilter::RenderViewMap
+ AutomationResourceMessageFilter::filtered_render_views_;
+
+int AutomationResourceMessageFilter::unique_request_id_ = 1;
+
+AutomationResourceMessageFilter::AutomationResourceMessageFilter()
+ : channel_(NULL) {
+ ChromeThread::PostTask(
+ ChromeThread::IO, FROM_HERE,
+ NewRunnableFunction(
+ URLRequestAutomationJob::EnsureProtocolFactoryRegistered));
+}
+
+AutomationResourceMessageFilter::~AutomationResourceMessageFilter() {
+}
+
+// Called on the IPC thread:
+void AutomationResourceMessageFilter::OnFilterAdded(IPC::Channel* channel) {
+ DCHECK(channel_ == NULL);
+ channel_ = channel;
+}
+
+void AutomationResourceMessageFilter::OnFilterRemoved() {
+ channel_ = NULL;
+}
+
+// Called on the IPC thread:
+void AutomationResourceMessageFilter::OnChannelConnected(int32 peer_pid) {
+}
+
+// Called on the IPC thread:
+void AutomationResourceMessageFilter::OnChannelClosing() {
+ channel_ = NULL;
+ request_map_.clear();
+
+ // Only erase RenderViews which are associated with this
+ // AutomationResourceMessageFilter instance.
+ RenderViewMap::iterator index = filtered_render_views_.begin();
+ while (index != filtered_render_views_.end()) {
+ const AutomationDetails& details = (*index).second;
+ if (details.filter.get() == this) {
+ filtered_render_views_.erase(index++);
+ } else {
+ index++;
+ }
+ }
+}
+
+// Called on the IPC thread:
+bool AutomationResourceMessageFilter::OnMessageReceived(
+ const IPC::Message& message) {
+ int request_id;
+ if (URLRequestAutomationJob::MayFilterMessage(message, &request_id)) {
+ RequestMap::iterator it = request_map_.find(request_id);
+ if (it != request_map_.end()) {
+ URLRequestAutomationJob* job = it->second;
+ DCHECK(job);
+ if (job) {
+ job->OnMessage(message);
+ return true;
+ }
+ }
+ }
+
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(AutomationResourceMessageFilter, message)
+ IPC_MESSAGE_HANDLER(AutomationMsg_SetFilteredInet,
+ OnSetFilteredInet)
+ IPC_MESSAGE_HANDLER(AutomationMsg_GetFilteredInetHitCount,
+ OnGetFilteredInetHitCount)
+ IPC_MESSAGE_HANDLER(AutomationMsg_RecordHistograms,
+ OnRecordHistograms)
+ IPC_MESSAGE_HANDLER(AutomationMsg_GetCookiesHostResponse,
+ OnGetCookiesHostResponse)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+
+ return handled;
+}
+
+// Called on the IPC thread:
+bool AutomationResourceMessageFilter::Send(IPC::Message* message) {
+ // This has to be called on the IO thread.
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
+ if (!channel_) {
+ delete message;
+ return false;
+ }
+
+ return channel_->Send(message);
+}
+
+bool AutomationResourceMessageFilter::RegisterRequest(
+ URLRequestAutomationJob* job) {
+ if (!job) {
+ NOTREACHED();
+ return false;
+ }
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
+
+ // Register pending jobs in the pending request map for servicing later.
+ if (job->is_pending()) {
+ DCHECK(!ContainsKey(pending_request_map_, job->id()));
+ DCHECK(!ContainsKey(request_map_, job->id()));
+ pending_request_map_[job->id()] = job;
+ } else {
+ DCHECK(!ContainsKey(request_map_, job->id()));
+ DCHECK(!ContainsKey(pending_request_map_, job->id()));
+ request_map_[job->id()] = job;
+ }
+
+ return true;
+}
+
+void AutomationResourceMessageFilter::UnRegisterRequest(
+ URLRequestAutomationJob* job) {
+ if (!job) {
+ NOTREACHED();
+ return;
+ }
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
+
+ if (job->is_pending()) {
+ DCHECK(ContainsKey(pending_request_map_, job->id()));
+ pending_request_map_.erase(job->id());
+ } else {
+ DCHECK(ContainsKey(request_map_, job->id()));
+ request_map_.erase(job->id());
+ }
+}
+
+bool AutomationResourceMessageFilter::RegisterRenderView(
+ int renderer_pid, int renderer_id, int tab_handle,
+ AutomationResourceMessageFilter* filter,
+ bool pending_view) {
+ if (!renderer_pid || !renderer_id || !tab_handle) {
+ NOTREACHED();
+ return false;
+ }
+
+ ChromeThread::PostTask(
+ ChromeThread::IO, FROM_HERE,
+ NewRunnableFunction(
+ AutomationResourceMessageFilter::RegisterRenderViewInIOThread,
+ renderer_pid, renderer_id, tab_handle, filter, pending_view));
+ return true;
+}
+
+void AutomationResourceMessageFilter::UnRegisterRenderView(
+ int renderer_pid, int renderer_id) {
+ ChromeThread::PostTask(
+ ChromeThread::IO, FROM_HERE,
+ NewRunnableFunction(
+ AutomationResourceMessageFilter::UnRegisterRenderViewInIOThread,
+ renderer_pid, renderer_id));
+}
+
+bool AutomationResourceMessageFilter::ResumePendingRenderView(
+ int renderer_pid, int renderer_id, int tab_handle,
+ AutomationResourceMessageFilter* filter) {
+ if (!renderer_pid || !renderer_id || !tab_handle) {
+ NOTREACHED();
+ return false;
+ }
+
+ ChromeThread::PostTask(
+ ChromeThread::IO, FROM_HERE,
+ NewRunnableFunction(
+ AutomationResourceMessageFilter::ResumePendingRenderViewInIOThread,
+ renderer_pid, renderer_id, tab_handle, filter));
+ return true;
+}
+
+void AutomationResourceMessageFilter::RegisterRenderViewInIOThread(
+ int renderer_pid, int renderer_id,
+ int tab_handle, AutomationResourceMessageFilter* filter,
+ bool pending_view) {
+ RenderViewMap::iterator automation_details_iter(
+ filtered_render_views_.find(RendererId(renderer_pid, renderer_id)));
+ if (automation_details_iter != filtered_render_views_.end()) {
+ DCHECK(automation_details_iter->second.ref_count > 0);
+ automation_details_iter->second.ref_count++;
+ } else {
+ filtered_render_views_[RendererId(renderer_pid, renderer_id)] =
+ AutomationDetails(tab_handle, filter, pending_view);
+ }
+}
+
+// static
+void AutomationResourceMessageFilter::UnRegisterRenderViewInIOThread(
+ int renderer_pid, int renderer_id) {
+ RenderViewMap::iterator automation_details_iter(
+ filtered_render_views_.find(RendererId(renderer_pid, renderer_id)));
+
+ if (automation_details_iter == filtered_render_views_.end()) {
+ LOG(INFO) << "UnRegisterRenderViewInIOThread: already unregistered";
+ return;
+ }
+
+ automation_details_iter->second.ref_count--;
+
+ if (automation_details_iter->second.ref_count <= 0) {
+ filtered_render_views_.erase(RendererId(renderer_pid, renderer_id));
+ }
+}
+
+// static
+bool AutomationResourceMessageFilter::ResumePendingRenderViewInIOThread(
+ int renderer_pid, int renderer_id, int tab_handle,
+ AutomationResourceMessageFilter* filter) {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
+
+ RenderViewMap::iterator automation_details_iter(
+ filtered_render_views_.find(RendererId(renderer_pid, renderer_id)));
+
+ if (automation_details_iter == filtered_render_views_.end()) {
+ NOTREACHED() << "Failed to find pending view for renderer pid:"
+ << renderer_pid
+ << ", render view id:"
+ << renderer_id;
+ return false;
+ }
+
+ DCHECK(automation_details_iter->second.is_pending_render_view);
+
+ AutomationResourceMessageFilter* old_filter =
+ automation_details_iter->second.filter;
+ DCHECK(old_filter != NULL);
+
+ filtered_render_views_[RendererId(renderer_pid, renderer_id)] =
+ AutomationDetails(tab_handle, filter, false);
+
+ ResumeJobsForPendingView(tab_handle, old_filter, filter);
+ return true;
+}
+
+bool AutomationResourceMessageFilter::LookupRegisteredRenderView(
+ int renderer_pid, int renderer_id, AutomationDetails* details) {
+ bool found = false;
+ RenderViewMap::iterator it = filtered_render_views_.find(RendererId(
+ renderer_pid, renderer_id));
+ if (it != filtered_render_views_.end()) {
+ found = true;
+ if (details)
+ *details = it->second;
+ }
+
+ return found;
+}
+
+bool AutomationResourceMessageFilter::GetAutomationRequestId(
+ int request_id, int* automation_request_id) {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
+
+ RequestMap::iterator it = request_map_.begin();
+ while (it != request_map_.end()) {
+ URLRequestAutomationJob* job = it->second;
+ DCHECK(job);
+ if (job && job->request_id() == request_id) {
+ *automation_request_id = job->id();
+ return true;
+ }
+ it++;
+ }
+
+ return false;
+}
+
+bool AutomationResourceMessageFilter::SendDownloadRequestToHost(
+ int routing_id, int tab_handle, int request_id) {
+ int automation_request_id = 0;
+ bool valid_id = GetAutomationRequestId(request_id, &automation_request_id);
+ if (!valid_id) {
+ NOTREACHED() << "Invalid request id: " << request_id;
+ return false;
+ }
+
+ return Send(new AutomationMsg_DownloadRequestInHost(0, tab_handle,
+ automation_request_id));
+}
+
+void AutomationResourceMessageFilter::OnSetFilteredInet(bool enable) {
+ chrome_browser_net::SetUrlRequestMocksEnabled(enable);
+}
+
+void AutomationResourceMessageFilter::OnGetFilteredInetHitCount(
+ int* hit_count) {
+ *hit_count = URLRequestFilter::GetInstance()->hit_count();
+}
+
+void AutomationResourceMessageFilter::OnRecordHistograms(
+ const std::vector<std::string>& histogram_list) {
+ for (size_t index = 0; index < histogram_list.size(); ++index) {
+ Histogram::DeserializeHistogramInfo(histogram_list[index]);
+ }
+}
+
+void AutomationResourceMessageFilter::GetCookiesForUrl(
+ int tab_handle, const GURL& url, net::CompletionCallback* callback,
+ net::CookieStore* cookie_store) {
+ DCHECK(callback != NULL);
+ DCHECK(cookie_store != NULL);
+
+ int completion_callback_id = GetNextCompletionCallbackId();
+ DCHECK(!ContainsKey(completion_callback_map_, completion_callback_id));
+
+ CookieCompletionInfo cookie_info;
+ cookie_info.completion_callback = callback;
+ cookie_info.cookie_store = cookie_store;
+
+ completion_callback_map_[completion_callback_id] = cookie_info;
+
+ Send(new AutomationMsg_GetCookiesFromHost(0, tab_handle, url,
+ completion_callback_id));
+}
+
+void AutomationResourceMessageFilter::OnGetCookiesHostResponse(
+ int tab_handle, bool success, const GURL& url, const std::string& cookies,
+ int cookie_id) {
+ CompletionCallbackMap::iterator index =
+ completion_callback_map_.find(cookie_id);
+ if (index != completion_callback_map_.end()) {
+ net::CompletionCallback* callback = index->second.completion_callback;
+ scoped_refptr<net::CookieStore> cookie_store = index->second.cookie_store;
+
+ DCHECK(callback != NULL);
+ DCHECK(cookie_store.get() != NULL);
+
+ completion_callback_map_.erase(index);
+
+ // Set the cookie in the cookie store so that the callback can read it.
+ cookie_store->SetCookieWithOptions(url, cookies, net::CookieOptions());
+
+ Tuple1<int> params;
+ params.a = success ? net::OK : net::ERR_ACCESS_DENIED;
+ callback->RunWithParams(params);
+
+ // The cookie for this URL is only valid until it is read by the callback.
+ cookie_store->SetCookieWithOptions(url, "", net::CookieOptions());
+ } else {
+ NOTREACHED() << "Received invalid completion callback id:"
+ << cookie_id;
+ }
+}
+
+// static
+void AutomationResourceMessageFilter::ResumeJobsForPendingView(
+ int tab_handle,
+ AutomationResourceMessageFilter* old_filter,
+ AutomationResourceMessageFilter* new_filter) {
+ DCHECK(old_filter != NULL);
+ DCHECK(new_filter != NULL);
+
+ RequestMap pending_requests = old_filter->pending_request_map_;
+
+ for (RequestMap::iterator index = old_filter->pending_request_map_.begin();
+ index != old_filter->pending_request_map_.end(); index++) {
+ scoped_refptr<URLRequestAutomationJob> job = (*index).second;
+ DCHECK_EQ(job->message_filter(), old_filter);
+ DCHECK(job->is_pending());
+ // StartPendingJob will register the job with the new filter.
+ job->StartPendingJob(tab_handle, new_filter);
+ }
+
+ old_filter->pending_request_map_.clear();
+}
diff --git a/chrome/browser/automation/automation_resource_message_filter.h b/chrome/browser/automation/automation_resource_message_filter.h
new file mode 100644
index 0000000..55b9bf2
--- /dev/null
+++ b/chrome/browser/automation/automation_resource_message_filter.h
@@ -0,0 +1,202 @@
+// Copyright (c) 2006-2009 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_RESOURCE_MESSAGE_FILTER_H_
+#define CHROME_BROWSER_AUTOMATION_AUTOMATION_RESOURCE_MESSAGE_FILTER_H_
+
+#include <map>
+
+#include "base/atomicops.h"
+#include "base/lock.h"
+#include "base/platform_thread.h"
+#include "ipc/ipc_channel_proxy.h"
+#include "net/base/completion_callback.h"
+
+class URLRequestAutomationJob;
+class GURL;
+
+namespace net {
+class CookieStore;
+} // namespace net
+
+// This class filters out incoming automation IPC messages for network
+// requests and processes them on the IPC thread. As a result, network
+// requests are not delayed by costly UI processing that may be occurring
+// on the main thread of the browser. It also means that any hangs in
+// starting a network request will not interfere with browser UI.
+class AutomationResourceMessageFilter
+ : public IPC::ChannelProxy::MessageFilter,
+ public IPC::Message::Sender {
+ public:
+ // Information needed to send IPCs through automation.
+ struct AutomationDetails {
+ AutomationDetails() : tab_handle(0), ref_count(1),
+ is_pending_render_view(false) {}
+ AutomationDetails(int tab, AutomationResourceMessageFilter* flt,
+ bool pending_view)
+ : tab_handle(tab), ref_count(1), filter(flt),
+ is_pending_render_view(pending_view) {
+ }
+
+ int tab_handle;
+ int ref_count;
+ scoped_refptr<AutomationResourceMessageFilter> filter;
+ // Indicates whether network requests issued by this render view need to
+ // be executed later.
+ bool is_pending_render_view;
+ };
+
+ // Create the filter.
+ AutomationResourceMessageFilter();
+ virtual ~AutomationResourceMessageFilter();
+
+ // Returns a new automation request id. This is unique across all instances
+ // of AutomationResourceMessageFilter.
+ int NewAutomationRequestId() {
+ return base::subtle::Barrier_AtomicIncrement(&unique_request_id_, 1);
+ }
+
+ // IPC::ChannelProxy::MessageFilter methods:
+ virtual void OnFilterAdded(IPC::Channel* channel);
+ virtual void OnFilterRemoved();
+
+ virtual void OnChannelConnected(int32 peer_pid);
+ virtual void OnChannelClosing();
+ virtual bool OnMessageReceived(const IPC::Message& message);
+
+ // ResourceDispatcherHost::Receiver methods:
+ virtual bool Send(IPC::Message* message);
+
+ // Add request to the list of outstanding requests.
+ virtual bool RegisterRequest(URLRequestAutomationJob* job);
+
+ // Remove request from the list of outstanding requests.
+ virtual void UnRegisterRequest(URLRequestAutomationJob* job);
+
+ // Can be called from the UI thread.
+ // The pending_view parameter should be true if network requests initiated by
+ // this render view need to be paused waiting for an acknowledgement from
+ // the external host.
+ static bool RegisterRenderView(int renderer_pid, int renderer_id,
+ int tab_handle, AutomationResourceMessageFilter* filter,
+ bool pending_view);
+ static void UnRegisterRenderView(int renderer_pid, int renderer_id);
+
+ // Can be called from the UI thread.
+ // Resumes pending render views, i.e. network requests issued by this view
+ // can now be serviced.
+ static bool ResumePendingRenderView(int renderer_pid, int renderer_id,
+ int tab_handle, AutomationResourceMessageFilter* filter);
+
+ // Called only on the IO thread.
+ static bool LookupRegisteredRenderView(
+ int renderer_pid, int renderer_id, AutomationDetails* details);
+
+ // Sends the download request to the automation host.
+ bool SendDownloadRequestToHost(int routing_id, int tab_handle,
+ int request_id);
+
+ // Retrieves cookies for the url passed in from the external host. The
+ // callback passed in is notified on success or failure asynchronously.
+ void GetCookiesForUrl(int tab_handle, const GURL& url,
+ net::CompletionCallback* callback,
+ net::CookieStore* cookie_store);
+
+ // This function gets invoked when we receive a response from the external
+ // host for the cookie request sent in GetCookiesForUrl above. It sets the
+ // cookie temporarily on the cookie store and executes the completion
+ // callback which reads the cookie from the store. The cookie value is reset
+ // after the callback finishes executing.
+ void OnGetCookiesHostResponse(int tab_handle, bool success, const GURL& url,
+ const std::string& cookies, int cookie_id);
+
+ protected:
+ // Retrieves the automation request id for the passed in chrome request
+ // id and returns it in the automation_request_id parameter.
+ // Returns true on success.
+ bool GetAutomationRequestId(int request_id, int* automation_request_id);
+
+ static void RegisterRenderViewInIOThread(int renderer_pid, int renderer_id,
+ int tab_handle, AutomationResourceMessageFilter* filter,
+ bool pending_view);
+ static void UnRegisterRenderViewInIOThread(int renderer_pid, int renderer_id);
+
+ static bool ResumePendingRenderViewInIOThread(
+ int renderer_pid, int renderer_id, int tab_handle,
+ AutomationResourceMessageFilter* filter);
+
+ private:
+ void OnSetFilteredInet(bool enable);
+ void OnGetFilteredInetHitCount(int* hit_count);
+ void OnRecordHistograms(const std::vector<std::string>& histogram_list);
+
+ // Resumes pending jobs from the old AutomationResourceMessageFilter instance
+ // passed in.
+ static void ResumeJobsForPendingView(
+ int tab_handle,
+ AutomationResourceMessageFilter* old_filter,
+ AutomationResourceMessageFilter* new_filter);
+
+ int GetNextCompletionCallbackId() {
+ return ++next_completion_callback_id_;
+ }
+
+ // A unique renderer id is a combination of renderer process id and
+ // it's routing id.
+ struct RendererId {
+ int pid_;
+ int id_;
+
+ RendererId() : pid_(0), id_(0) {}
+ RendererId(int pid, int id) : pid_(pid), id_(id) {}
+
+ bool operator < (const RendererId& rhs) const {
+ return ((pid_ == rhs.pid_) ? (id_ < rhs.id_) : (pid_ < rhs.pid_));
+ }
+ };
+
+ typedef std::map<RendererId, AutomationDetails> RenderViewMap;
+ typedef std::map<int, scoped_refptr<URLRequestAutomationJob> > RequestMap;
+
+ // The channel associated with the automation connection. This pointer is not
+ // owned by this class.
+ IPC::Channel* channel_;
+
+ // A unique request id per process.
+ static int unique_request_id_;
+
+ // Map of outstanding requests.
+ RequestMap request_map_;
+
+ // Map of pending requests, i.e. requests which were waiting for the external
+ // host to connect back.
+ RequestMap pending_request_map_;
+
+ // Map of render views interested in diverting url requests over automation.
+ static RenderViewMap filtered_render_views_;
+
+ // Contains information used for completing the request to read cookies from
+ // the host coming in from the renderer.
+ struct CookieCompletionInfo {
+ net::CompletionCallback* completion_callback;
+ scoped_refptr<net::CookieStore> cookie_store;
+ };
+
+ // Map of completion callback id to CookieCompletionInfo, which contains the
+ // actual callback which is invoked on successful retrieval of cookies from
+ // host. The mapping is setup when GetCookiesForUrl is invoked to retrieve
+ // cookies from the host and is removed when we receive a response from the
+ // host. Please see the OnGetCookiesHostResponse function.
+ typedef std::map<int, CookieCompletionInfo> CompletionCallbackMap;
+ CompletionCallbackMap completion_callback_map_;
+
+ // Contains the id of the next completion callback. This is passed to the the
+ // external host as a cookie referring to the completion callback.
+ int next_completion_callback_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutomationResourceMessageFilter);
+};
+
+#endif // CHROME_BROWSER_AUTOMATION_AUTOMATION_RESOURCE_MESSAGE_FILTER_H_
+
diff --git a/chrome/browser/automation/automation_resource_routing_delegate.cc b/chrome/browser/automation/automation_resource_routing_delegate.cc
new file mode 100644
index 0000000..954b05e
--- /dev/null
+++ b/chrome/browser/automation/automation_resource_routing_delegate.cc
@@ -0,0 +1,19 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/automation/automation_resource_routing_delegate.h"
+
+void AutomationResourceRoutingDelegate::RegisterRenderViewHost(
+ RenderViewHost* render_view_host) {
+}
+
+void AutomationResourceRoutingDelegate::UnregisterRenderViewHost(
+ RenderViewHost* render_view_host) {
+}
+
+AutomationResourceRoutingDelegate::AutomationResourceRoutingDelegate() {
+}
+
+AutomationResourceRoutingDelegate::~AutomationResourceRoutingDelegate() {
+}
diff --git a/chrome/browser/automation/automation_resource_routing_delegate.h b/chrome/browser/automation/automation_resource_routing_delegate.h
new file mode 100644
index 0000000..2438e4d
--- /dev/null
+++ b/chrome/browser/automation/automation_resource_routing_delegate.h
@@ -0,0 +1,31 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_AUTOMATION_AUTOMATION_RESOURCE_ROUTING_DELEGATE_H_
+#define CHROME_BROWSER_AUTOMATION_AUTOMATION_RESOURCE_ROUTING_DELEGATE_H_
+
+#include "base/basictypes.h"
+
+class RenderViewHost;
+
+// Interface for registering RenderViewHost instances for resource routing
+// automation.
+class AutomationResourceRoutingDelegate {
+ public:
+ // Call to register |render_view_host| for resource routing automation
+ // by the delegate.
+ virtual void RegisterRenderViewHost(RenderViewHost* render_view_host);
+
+ // Call to unregister |render_view_host| from resource routing automation.
+ virtual void UnregisterRenderViewHost(RenderViewHost* render_view_host);
+
+ protected:
+ AutomationResourceRoutingDelegate();
+ virtual ~AutomationResourceRoutingDelegate();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AutomationResourceRoutingDelegate);
+};
+
+#endif // CHROME_BROWSER_AUTOMATION_AUTOMATION_RESOURCE_ROUTING_DELEGATE_H_
diff --git a/chrome/browser/automation/automation_resource_tracker.cc b/chrome/browser/automation/automation_resource_tracker.cc
new file mode 100644
index 0000000..de20ba1
--- /dev/null
+++ b/chrome/browser/automation/automation_resource_tracker.cc
@@ -0,0 +1,76 @@
+// Copyright (c) 2006-2008 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_resource_tracker.h"
+
+#include "chrome/common/notification_service.h"
+#include "chrome/test/automation/automation_messages.h"
+
+int AutomationResourceTrackerImpl::AddImpl(void* resource) {
+ if (ContainsResourceImpl(resource))
+ return resource_to_handle_[resource];
+
+ int handle = GenerateHandle();
+ DCHECK(!ContainsHandleImpl(handle));
+
+ resource_to_handle_[resource] = handle;
+ handle_to_resource_[handle] = resource;
+
+ AddObserverTypeProxy(resource);
+
+ return handle;
+}
+
+void AutomationResourceTrackerImpl::RemoveImpl(void* resource) {
+ if (!ContainsResourceImpl(resource))
+ return;
+
+ int handle = resource_to_handle_[resource];
+ DCHECK(handle_to_resource_[handle] == resource);
+
+ RemoveObserverTypeProxy(resource);
+
+ resource_to_handle_.erase(resource);
+ handle_to_resource_.erase(handle);
+}
+
+int AutomationResourceTrackerImpl::GenerateHandle() {
+ static int handle = 0;
+ return ++handle;
+}
+
+bool AutomationResourceTrackerImpl::ContainsResourceImpl(void* resource) {
+ return resource_to_handle_.find(resource) != resource_to_handle_.end();
+}
+
+bool AutomationResourceTrackerImpl::ContainsHandleImpl(int handle) {
+ return handle_to_resource_.find(handle) != handle_to_resource_.end();
+}
+
+void* AutomationResourceTrackerImpl::GetResourceImpl(int handle) {
+ HandleToResourceMap::const_iterator iter = handle_to_resource_.find(handle);
+ if (iter == handle_to_resource_.end())
+ return NULL;
+
+ return iter->second;
+}
+
+int AutomationResourceTrackerImpl::GetHandleImpl(void* resource) {
+ ResourceToHandleMap::const_iterator iter =
+ resource_to_handle_.find(resource);
+ if (iter == resource_to_handle_.end())
+ return 0;
+
+ return iter->second;
+}
+
+void AutomationResourceTrackerImpl::HandleCloseNotification(void* resource) {
+ if (!ContainsResourceImpl(resource))
+ return;
+
+ sender_->Send(
+ new AutomationMsg_InvalidateHandle(0, resource_to_handle_[resource]));
+
+ RemoveImpl(resource);
+}
diff --git a/chrome/browser/automation/automation_resource_tracker.h b/chrome/browser/automation/automation_resource_tracker.h
new file mode 100644
index 0000000..58703e7
--- /dev/null
+++ b/chrome/browser/automation/automation_resource_tracker.h
@@ -0,0 +1,158 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_AUTOMATION_AUTOMATION_RESOURCE_TRACKER_H__
+#define CHROME_BROWSER_AUTOMATION_AUTOMATION_RESOURCE_TRACKER_H__
+
+#include <map>
+
+#include "base/basictypes.h"
+#include "chrome/common/notification_observer.h"
+#include "chrome/common/notification_registrar.h"
+#include "chrome/common/notification_type.h"
+#include "ipc/ipc_message.h"
+
+template <class T> class Source;
+
+// Template trick so that AutomationResourceTracker can be used with non-pointer
+// types.
+template <class T>
+struct AutomationResourceTraits {
+ typedef T ValueType;
+};
+
+template <class T>
+struct AutomationResourceTraits<T*> {
+ typedef T ValueType;
+};
+
+// This class exists for the sole purpose of allowing some of the implementation
+// of AutomationResourceTracker to live in a .cc file.
+class AutomationResourceTrackerImpl {
+ public:
+ explicit AutomationResourceTrackerImpl(IPC::Message::Sender* sender)
+ : sender_(sender) {}
+
+ virtual ~AutomationResourceTrackerImpl() {}
+
+ // These need to be implemented in AutomationResourceTracker,
+ // since it needs to call the subclass's type-specific notification
+ // registration functions.
+ virtual void AddObserverTypeProxy(void* resource) = 0;
+ virtual void RemoveObserverTypeProxy(void* resource) = 0;
+
+ int AddImpl(void* resource);
+ void RemoveImpl(void* resource);
+ int GenerateHandle();
+ bool ContainsResourceImpl(void* resource);
+ bool ContainsHandleImpl(int handle);
+ void* GetResourceImpl(int handle);
+ int GetHandleImpl(void* resource);
+ void HandleCloseNotification(void* resource);
+
+ protected:
+ typedef std::map<void*, int> ResourceToHandleMap;
+ typedef std::map<int, void*> HandleToResourceMap;
+ ResourceToHandleMap resource_to_handle_;
+ HandleToResourceMap handle_to_resource_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AutomationResourceTrackerImpl);
+
+ IPC::Message::Sender* sender_;
+};
+
+// This template defines a superclass for an object that wants to track
+// a particular kind of application resource (like windows or tabs) for
+// automation purposes. The only things that a subclass should need to
+// define are AddObserver and RemoveObserver for the given resource's
+// close notifications.
+template <class T>
+class AutomationResourceTracker : public NotificationObserver,
+ private AutomationResourceTrackerImpl {
+ public:
+ explicit AutomationResourceTracker(IPC::Message::Sender* automation)
+ : AutomationResourceTrackerImpl(automation) {}
+
+ virtual ~AutomationResourceTracker() {
+ }
+
+ // The implementations for these should call the NotificationService
+ // to add and remove this object as an observer for the appropriate
+ // resource closing notification.
+ virtual void AddObserver(T resource) = 0;
+ virtual void RemoveObserver(T resource) = 0;
+
+ // Adds the given resource to this tracker, and returns a handle that
+ // can be used to refer to that resource. If the resource is already
+ // being tracked, the handle may be the same as one returned previously.
+ int Add(T resource) {
+ return AddImpl(resource);
+ }
+
+ // Removes the given resource from this tracker. If the resource is not
+ // currently present in the tracker, this is a no-op.
+ void Remove(T resource) {
+ RemoveImpl(resource);
+ }
+
+ // Returns true if this tracker currently tracks the resource pointed to
+ // by the parameter.
+ bool ContainsResource(T resource) {
+ return ContainsResourceImpl(resource);
+ }
+
+ // Returns true if this tracker currently tracks the given handle.
+ bool ContainsHandle(int handle) {
+ return ContainsHandleImpl(handle);
+ }
+
+ // Returns the resource pointer associated with a given handle, or NULL
+ // if that handle is not present in the mapping.
+ T GetResource(int handle) {
+ return static_cast<T>(GetResourceImpl(handle));
+ }
+
+ // Returns the handle associated with a given resource pointer, or 0 if
+ // the resource is not currently in the mapping.
+ int GetHandle(T resource) {
+ return GetHandleImpl(resource);
+ }
+
+ // NotificationObserver implementation--the only thing that this tracker
+ // does in response to notifications is to tell the AutomationProxy
+ // that the associated handle is now invalid.
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ T resource =
+ Source<typename AutomationResourceTraits<T>::ValueType>(source).ptr();
+
+ CloseResource(resource);
+ }
+
+ protected:
+ // Removes |resource| from the tracker, and handles sending the close
+ // notification back to the client. This typically should not be called
+ // directly, unless there is no appropriate notification available
+ // for the resource type.
+ void CloseResource(T resource) {
+ HandleCloseNotification(resource);
+ }
+
+ NotificationRegistrar registrar_;
+
+ private:
+ // These proxy calls from the base Impl class to the template's subclss.
+ virtual void AddObserverTypeProxy(void* resource) {
+ AddObserver(static_cast<T>(resource));
+ }
+ virtual void RemoveObserverTypeProxy(void* resource) {
+ RemoveObserver(static_cast<T>(resource));
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(AutomationResourceTracker);
+};
+
+#endif // CHROME_BROWSER_AUTOMATION_AUTOMATION_RESOURCE_TRACKER_H__
diff --git a/chrome/browser/automation/automation_tab_tracker.h b/chrome/browser/automation/automation_tab_tracker.h
new file mode 100644
index 0000000..02b40be
--- /dev/null
+++ b/chrome/browser/automation/automation_tab_tracker.h
@@ -0,0 +1,92 @@
+// Copyright (c) 2006-2008 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_TRACKER_H_
+#define CHROME_BROWSER_AUTOMATION_AUTOMATION_TAB_TRACKER_H_
+
+#include <map>
+
+#include "base/time.h"
+#include "chrome/browser/automation/automation_resource_tracker.h"
+#include "chrome/browser/tab_contents/navigation_controller.h"
+#include "chrome/common/notification_registrar.h"
+#include "chrome/common/notification_type.h"
+
+class AutomationTabTracker
+ : public AutomationResourceTracker<NavigationController*> {
+ public:
+ explicit AutomationTabTracker(IPC::Message::Sender* automation)
+ : AutomationResourceTracker<NavigationController*>(automation) {}
+
+ virtual ~AutomationTabTracker() {
+ }
+
+ virtual void AddObserver(NavigationController* resource) {
+ // This tab could either be a regular tab or an external tab
+ // Register for both notifications.
+ registrar_.Add(this, NotificationType::TAB_CLOSING,
+ Source<NavigationController>(resource));
+ registrar_.Add(this, NotificationType::EXTERNAL_TAB_CLOSED,
+ Source<NavigationController>(resource));
+ // We also want to know about navigations so we can keep track of the last
+ // navigation time.
+ registrar_.Add(this, NotificationType::LOAD_STOP,
+ Source<NavigationController>(resource));
+ }
+
+ virtual void RemoveObserver(NavigationController* resource) {
+ registrar_.Remove(this, NotificationType::TAB_CLOSING,
+ Source<NavigationController>(resource));
+ registrar_.Remove(this, NotificationType::EXTERNAL_TAB_CLOSED,
+ Source<NavigationController>(resource));
+ registrar_.Remove(this, NotificationType::LOAD_STOP,
+ Source<NavigationController>(resource));
+ }
+
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ switch (type.value) {
+ case NotificationType::LOAD_STOP:
+ last_navigation_times_[Source<NavigationController>(source).ptr()] =
+ base::Time::Now();
+ return;
+ case NotificationType::EXTERNAL_TAB_CLOSED:
+ case NotificationType::TAB_CLOSING:
+ {
+ std::map<NavigationController*, base::Time>::iterator iter =
+ last_navigation_times_.find(
+ Source<NavigationController>(source).ptr());
+ if (iter != last_navigation_times_.end())
+ last_navigation_times_.erase(iter);
+ }
+ break;
+ default:
+ NOTREACHED();
+ }
+ AutomationResourceTracker<NavigationController*>::Observe(type, source,
+ details);
+ }
+
+ base::Time GetLastNavigationTime(int handle) {
+ if (ContainsHandle(handle)) {
+ NavigationController* controller = GetResource(handle);
+ if (controller) {
+ std::map<NavigationController*, base::Time>::const_iterator iter =
+ last_navigation_times_.find(controller);
+ if (iter != last_navigation_times_.end())
+ return iter->second;
+ }
+ }
+ return base::Time();
+ }
+
+ private:
+ // Last time a navigation occurred.
+ std::map<NavigationController*, base::Time> last_navigation_times_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutomationTabTracker);
+};
+
+#endif // CHROME_BROWSER_AUTOMATION_AUTOMATION_TAB_TRACKER_H_
diff --git a/chrome/browser/automation/automation_window_tracker.h b/chrome/browser/automation/automation_window_tracker.h
new file mode 100644
index 0000000..02cfeea
--- /dev/null
+++ b/chrome/browser/automation/automation_window_tracker.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2006-2008 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_WINDOW_TRACKER_H_
+#define CHROME_BROWSER_AUTOMATION_AUTOMATION_WINDOW_TRACKER_H_
+
+#include "build/build_config.h"
+#include "chrome/browser/automation/automation_resource_tracker.h"
+#include "chrome/common/native_window_notification_source.h"
+#include "gfx/native_widget_types.h"
+
+class AutomationWindowTracker
+ : public AutomationResourceTracker<gfx::NativeWindow> {
+ public:
+ explicit AutomationWindowTracker(IPC::Message::Sender* automation)
+ : AutomationResourceTracker<gfx::NativeWindow>(automation) { }
+ virtual ~AutomationWindowTracker() {
+ }
+
+ virtual void AddObserver(gfx::NativeWindow resource) {
+ registrar_.Add(this, NotificationType::WINDOW_CLOSED,
+ Source<gfx::NativeWindow>(resource));
+ }
+
+ virtual void RemoveObserver(gfx::NativeWindow resource) {
+ registrar_.Remove(this, NotificationType::WINDOW_CLOSED,
+ Source<gfx::NativeWindow>(resource));
+ }
+};
+
+#endif // CHROME_BROWSER_AUTOMATION_AUTOMATION_WINDOW_TRACKER_H_
diff --git a/chrome/browser/automation/chrome_frame_automation_provider.cc b/chrome/browser/automation/chrome_frame_automation_provider.cc
new file mode 100644
index 0000000..52f8ded
--- /dev/null
+++ b/chrome/browser/automation/chrome_frame_automation_provider.cc
@@ -0,0 +1,79 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/automation/chrome_frame_automation_provider.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/profile_manager.h"
+#include "chrome/test/automation/automation_messages.h"
+#include "ipc/ipc_message.h"
+#include "ipc/ipc_channel.h"
+
+ChromeFrameAutomationProvider::ChromeFrameAutomationProvider(Profile* profile)
+ : AutomationProvider(profile) {}
+
+void ChromeFrameAutomationProvider::OnMessageReceived(
+ const IPC::Message& message) {
+ if (IsValidMessage(message.type())) {
+ AutomationProvider::OnMessageReceived(message);
+ } else {
+ OnUnhandledMessage(message);
+ }
+}
+
+void ChromeFrameAutomationProvider::OnUnhandledMessage(
+ const IPC::Message& message) {
+ NOTREACHED() << __FUNCTION__
+ << " Unhandled message type: "
+ << message.type();
+}
+
+bool ChromeFrameAutomationProvider::IsValidMessage(uint32 type) {
+ bool is_valid_message = false;
+
+ switch (type) {
+ case AutomationMsg_CreateExternalTab::ID:
+ case AutomationMsg_ConnectExternalTab::ID:
+#if defined(OS_WIN)
+ case AutomationMsg_BrowserMove::ID:
+ case AutomationMsg_ProcessUnhandledAccelerator::ID:
+ case AutomationMsg_TabReposition::ID:
+ case AutomationMsg_ForwardContextMenuCommandToChrome::ID:
+#endif // defined(OS_WIN)
+ case AutomationMsg_NavigateInExternalTab::ID:
+ case AutomationMsg_NavigateExternalTabAtIndex::ID:
+ case AutomationMsg_Find::ID:
+ case AutomationMsg_InstallExtension::ID:
+ case AutomationMsg_LoadExpandedExtension::ID:
+ case AutomationMsg_GetEnabledExtensions::ID:
+ case AutomationMsg_SetEnableExtensionAutomation::ID:
+ case AutomationMsg_SetInitialFocus::ID:
+ case AutomationMsg_SetPageFontSize::ID:
+ case AutomationMsg_SetProxyConfig::ID:
+ case AutomationMsg_Cut::ID:
+ case AutomationMsg_Copy::ID:
+ case AutomationMsg_Paste::ID:
+ case AutomationMsg_SelectAll::ID:
+ case AutomationMsg_ReloadAsync::ID:
+ case AutomationMsg_StopAsync::ID:
+ case AutomationMsg_PrintAsync::ID:
+ case AutomationMsg_HandleUnused::ID:
+ case AutomationMsg_HandleMessageFromExternalHost::ID:
+ case AutomationMsg_RequestStarted::ID:
+ case AutomationMsg_RequestData::ID:
+ case AutomationMsg_RequestEnd::ID:
+ case AutomationMsg_SaveAsAsync::ID:
+ case AutomationMsg_RemoveBrowsingData::ID:
+ case AutomationMsg_OverrideEncoding::ID:
+ case AutomationMsg_RunUnloadHandlers::ID: {
+ is_valid_message = true;
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ return is_valid_message;
+}
+
diff --git a/chrome/browser/automation/chrome_frame_automation_provider.h b/chrome/browser/automation/chrome_frame_automation_provider.h
new file mode 100644
index 0000000..0ab6a8f
--- /dev/null
+++ b/chrome/browser/automation/chrome_frame_automation_provider.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2006-2009 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.
+
+// This implements a browser-side endpoint for ChromeFrame UI automation
+// activity. The client-side endpoint is implemented by
+// ChromeFrameAutomationClient.
+// The entire lifetime of this object should be contained within that of
+// the BrowserProcess
+
+#ifndef CHROME_BROWSER_AUTOMATION_CHROME_FRAME_AUTOMATION_PROVIDER_H_
+#define CHROME_BROWSER_AUTOMATION_CHROME_FRAME_AUTOMATION_PROVIDER_H_
+
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "chrome/browser/automation/automation_provider.h"
+
+class Profile;
+
+// This class services automation IPC requests coming in from ChromeFrame
+// instances.
+class ChromeFrameAutomationProvider : public AutomationProvider {
+ public:
+ explicit ChromeFrameAutomationProvider(Profile* profile);
+
+ // IPC::Channel::Listener overrides.
+ virtual void OnMessageReceived(const IPC::Message& message);
+
+ protected:
+ // This function is called when we receive an invalid message type.
+ virtual void OnUnhandledMessage(const IPC::Message& message);
+
+ // Returns true if the message received is a valid chrome frame message.
+ bool IsValidMessage(uint32 type);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ChromeFrameAutomationProvider);
+};
+
+#endif // CHROME_BROWSER_AUTOMATION_CHROME_FRAME_AUTOMATION_PROVIDER_H_
+
diff --git a/chrome/browser/automation/extension_automation_constants.cc b/chrome/browser/automation/extension_automation_constants.cc
new file mode 100644
index 0000000..c513945
--- /dev/null
+++ b/chrome/browser/automation/extension_automation_constants.cc
@@ -0,0 +1,32 @@
+// Copyright (c) 2009 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/extension_automation_constants.h"
+
+namespace extension_automation_constants {
+
+const char kAutomationOrigin[] = "__priv_xtapi";
+const wchar_t kAutomationRequestIdKey[] = L"rqid";
+
+const wchar_t kAutomationHasCallbackKey[] = L"hascb";
+const wchar_t kAutomationErrorKey[] = L"err";
+const wchar_t kAutomationNameKey[] = L"name";
+const wchar_t kAutomationArgsKey[] = L"args";
+const wchar_t kAutomationResponseKey[] = L"res";
+const char kAutomationRequestTarget[] = "__priv_xtreq";
+const char kAutomationResponseTarget[] = "__priv_xtres";
+
+const wchar_t kAutomationConnectionIdKey[] = L"connid";
+const wchar_t kAutomationMessageDataKey[] = L"data";
+const wchar_t kAutomationExtensionIdKey[] = L"extid";
+const wchar_t kAutomationPortIdKey[] = L"portid";
+const wchar_t kAutomationChannelNameKey[] = L"chname";
+const wchar_t kAutomationTabJsonKey[] = L"tab";
+
+const char kAutomationPortRequestTarget[] = "__priv_prtreq";
+const char kAutomationPortResponseTarget[] = "__priv_prtres";
+
+const char kAutomationBrowserEventRequestTarget[] = "__priv_evtreq";
+
+} // namespace extension_automation_constants
diff --git a/chrome/browser/automation/extension_automation_constants.h b/chrome/browser/automation/extension_automation_constants.h
new file mode 100644
index 0000000..8c29293
--- /dev/null
+++ b/chrome/browser/automation/extension_automation_constants.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2009 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.
+
+// Constants used to encode requests and responses for automation.
+
+#ifndef CHROME_BROWSER_AUTOMATION_EXTENSION_AUTOMATION_CONSTANTS_H_
+#define CHROME_BROWSER_AUTOMATION_EXTENSION_AUTOMATION_CONSTANTS_H_
+
+namespace extension_automation_constants {
+
+// All extension automation related messages will have this origin.
+extern const char kAutomationOrigin[];
+// Key used for all extension automation request types.
+extern const wchar_t kAutomationRequestIdKey[];
+
+// Keys used for API communications
+extern const wchar_t kAutomationHasCallbackKey[];
+extern const wchar_t kAutomationErrorKey[]; // not present implies success
+extern const wchar_t kAutomationNameKey[];
+extern const wchar_t kAutomationArgsKey[];
+extern const wchar_t kAutomationResponseKey[];
+// All external API requests have this target.
+extern const char kAutomationRequestTarget[];
+// All API responses should have this target.
+extern const char kAutomationResponseTarget[];
+
+// Keys used for port communications
+extern const wchar_t kAutomationConnectionIdKey[];
+extern const wchar_t kAutomationMessageDataKey[];
+extern const wchar_t kAutomationExtensionIdKey[];
+extern const wchar_t kAutomationPortIdKey[];
+extern const wchar_t kAutomationChannelNameKey[];
+extern const wchar_t kAutomationTabJsonKey[];
+
+// All external port message requests should have this target.
+extern const char kAutomationPortRequestTarget[];
+// All external port message responses have this target.
+extern const char kAutomationPortResponseTarget[];
+
+// All external browser events have this target.
+extern const char kAutomationBrowserEventRequestTarget[];
+
+// The command codes for our private port protocol.
+enum PrivatePortCommand {
+ OPEN_CHANNEL = 0,
+ CHANNEL_OPENED = 1,
+ POST_MESSAGE = 2,
+ CHANNEL_CLOSED = 3,
+};
+
+}; // namespace automation_extension_constants
+
+#endif // CHROME_BROWSER_AUTOMATION_EXTENSION_AUTOMATION_CONSTANTS_H_
diff --git a/chrome/browser/automation/extension_port_container.cc b/chrome/browser/automation/extension_port_container.cc
new file mode 100644
index 0000000..e93f42c
--- /dev/null
+++ b/chrome/browser/automation/extension_port_container.cc
@@ -0,0 +1,266 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/automation/extension_port_container.h"
+
+#include "base/logging.h"
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/values.h"
+#include "chrome/browser/automation/automation_provider.h"
+#include "chrome/browser/automation/extension_automation_constants.h"
+#include "chrome/browser/chrome_thread.h"
+#include "chrome/browser/extensions/extension_message_service.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/renderer_host/render_process_host.h"
+#include "chrome/browser/renderer_host/render_view_host.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/common/render_messages.h"
+#include "chrome/test/automation/automation_messages.h"
+
+// TODO(siggi): Find a more structured way to read and write JSON messages.
+
+namespace ext = extension_automation_constants;
+
+ExtensionPortContainer::ExtensionPortContainer(AutomationProvider* automation,
+ int tab_handle) :
+ automation_(automation), service_(NULL), port_id_(-1),
+ tab_handle_(tab_handle) {
+ service_ = automation_->profile()->GetExtensionMessageService();
+ DCHECK(service_);
+}
+
+ExtensionPortContainer::~ExtensionPortContainer() {
+ DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_UI);
+
+ if (port_id_ != -1)
+ service_->CloseChannel(port_id_);
+
+ NotificationService::current()->Notify(
+ NotificationType::EXTENSION_PORT_DELETED_DEBUG,
+ Source<IPC::Message::Sender>(this),
+ NotificationService::NoDetails());
+}
+
+bool ExtensionPortContainer::PostResponseToExternalPort(
+ const std::string& message) {
+ return automation_->Send(
+ new AutomationMsg_ForwardMessageToExternalHost(
+ 0, tab_handle_, message, ext::kAutomationOrigin,
+ ext::kAutomationPortResponseTarget));
+}
+
+bool ExtensionPortContainer::PostMessageToExternalPort(
+ const std::string& message) {
+ return automation_->Send(
+ new AutomationMsg_ForwardMessageToExternalHost(
+ 0, tab_handle_, message,
+ ext::kAutomationOrigin,
+ ext::kAutomationPortRequestTarget));
+}
+
+void ExtensionPortContainer::PostMessageFromExternalPort(
+ const std::string &message) {
+ service_->PostMessageFromRenderer(port_id_, message);
+}
+
+bool ExtensionPortContainer::Connect(const std::string &extension_id,
+ int process_id,
+ int routing_id,
+ int connection_id,
+ const std::string& channel_name,
+ const std::string& tab_json) {
+ DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_UI);
+
+ port_id_ = service_->OpenSpecialChannelToExtension(
+ extension_id, channel_name, tab_json, this);
+ if (port_id_ == -1) {
+ // In this case a disconnect message has been dispatched.
+ return false;
+ }
+
+ SendConnectionResponse(connection_id, port_id_);
+ return true;
+}
+
+void ExtensionPortContainer::SendConnectionResponse(int connection_id,
+ int port_id) {
+ // Compose the reply message.
+ scoped_ptr<DictionaryValue> msg_dict(new DictionaryValue());
+ msg_dict->SetInteger(ext::kAutomationRequestIdKey, ext::CHANNEL_OPENED);
+ msg_dict->SetInteger(ext::kAutomationConnectionIdKey, connection_id);
+ msg_dict->SetInteger(ext::kAutomationPortIdKey, port_id);
+
+ std::string msg_json;
+ base::JSONWriter::Write(msg_dict.get(), false, &msg_json);
+
+ PostResponseToExternalPort(msg_json);
+}
+
+bool ExtensionPortContainer::Send(IPC::Message *message) {
+ DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_UI);
+
+ IPC_BEGIN_MESSAGE_MAP(ExtensionPortContainer, *message)
+ IPC_MESSAGE_HANDLER(ViewMsg_ExtensionMessageInvoke,
+ OnExtensionMessageInvoke)
+ IPC_MESSAGE_UNHANDLED_ERROR()
+ IPC_END_MESSAGE_MAP()
+
+ delete message;
+ return true;
+}
+
+void ExtensionPortContainer::OnExtensionMessageInvoke(
+ const std::string& function_name,
+ const ListValue& args,
+ bool requires_incognito_access,
+ const GURL& event_url) {
+ if (function_name == ExtensionMessageService::kDispatchOnMessage) {
+ DCHECK_EQ(args.GetSize(), 2u);
+
+ std::string message;
+ int source_port_id;
+ if (args.GetString(0, &message) && args.GetInteger(1, &source_port_id))
+ OnExtensionHandleMessage(message, source_port_id);
+ } else if (function_name == ExtensionMessageService::kDispatchOnDisconnect) {
+ DCHECK_EQ(args.GetSize(), 1u);
+ int port_id;
+ if (args.GetInteger(0, &port_id))
+ OnExtensionPortDisconnected(port_id);
+ } else if (function_name == ExtensionMessageService::kDispatchOnConnect) {
+ // Do nothing.
+ // TODO(siggi): implement
+ } else {
+ NOTREACHED() << function_name << " shouldn't be called.";
+ }
+}
+
+void ExtensionPortContainer::OnExtensionHandleMessage(
+ const std::string& message, int source_port_id) {
+ // Compose the reply message and fire it away.
+ DictionaryValue msg_dict;
+ msg_dict.SetInteger(ext::kAutomationRequestIdKey, ext::POST_MESSAGE);
+ msg_dict.SetInteger(ext::kAutomationPortIdKey, port_id_);
+ msg_dict.SetString(ext::kAutomationMessageDataKey, message);
+
+ std::string msg_json;
+ base::JSONWriter::Write(&msg_dict, false, &msg_json);
+
+ PostMessageToExternalPort(msg_json);
+}
+
+void ExtensionPortContainer::OnExtensionPortDisconnected(int source_port_id) {
+ // Compose the disconnect message and fire it away.
+ DictionaryValue msg_dict;
+ msg_dict.SetInteger(ext::kAutomationRequestIdKey, ext::CHANNEL_CLOSED);
+ msg_dict.SetInteger(ext::kAutomationPortIdKey, port_id_);
+
+ std::string msg_json;
+ base::JSONWriter::Write(&msg_dict, false, &msg_json);
+
+ PostMessageToExternalPort(msg_json);
+}
+
+bool ExtensionPortContainer::InterceptMessageFromExternalHost(
+ const std::string& message, const std::string& origin,
+ const std::string& target, AutomationProvider* automation,
+ RenderViewHost *view_host, int tab_handle) {
+ if (target != ext::kAutomationPortRequestTarget)
+ return false;
+
+ if (origin != ext::kAutomationOrigin) {
+ // TODO(siggi): Should we block the message on wrong origin?
+ LOG(WARNING) << "Wrong origin on automation port message " << origin;
+ }
+
+ scoped_ptr<Value> message_value(base::JSONReader::Read(message, false));
+ DCHECK(message_value->IsType(Value::TYPE_DICTIONARY));
+ if (!message_value->IsType(Value::TYPE_DICTIONARY))
+ return true;
+
+ DictionaryValue* message_dict =
+ reinterpret_cast<DictionaryValue*>(message_value.get());
+
+ int command = -1;
+ bool got_value = message_dict->GetInteger(ext::kAutomationRequestIdKey,
+ &command);
+ DCHECK(got_value);
+ if (!got_value)
+ return true;
+
+ if (command == ext::OPEN_CHANNEL) {
+ // Extract the "extension_id" and "connection_id" parameters.
+ std::string extension_id;
+ got_value = message_dict->GetString(ext::kAutomationExtensionIdKey,
+ &extension_id);
+ DCHECK(got_value);
+ if (!got_value)
+ return true;
+
+ int connection_id;
+ got_value = message_dict->GetInteger(ext::kAutomationConnectionIdKey,
+ &connection_id);
+ DCHECK(got_value);
+ if (!got_value)
+ return true;
+
+ std::string channel_name;
+ // Channel name is optional.
+ message_dict->GetString(ext::kAutomationChannelNameKey, &channel_name);
+
+ // Tab information is optional, try to retrieve it
+ // and re-flatten it to a string.
+ std::string tab_json("null");
+ DictionaryValue* tab = NULL;
+ if (message_dict->GetDictionary(ext::kAutomationTabJsonKey, &tab))
+ base::JSONWriter::Write(tab, false, &tab_json);
+
+ int routing_id = view_host->routing_id();
+ // Create the extension port and connect it.
+ scoped_ptr<ExtensionPortContainer> port(
+ new ExtensionPortContainer(automation, tab_handle));
+
+ int process_id = view_host->process()->id();
+ if (port->Connect(extension_id, process_id, routing_id, connection_id,
+ channel_name, tab_json)) {
+ // We have a successful connection.
+ automation->AddPortContainer(port.release());
+ }
+ } else if (command == ext::POST_MESSAGE) {
+ int port_id = -1;
+ got_value = message_dict->GetInteger(ext::kAutomationPortIdKey, &port_id);
+ DCHECK(got_value);
+ if (!got_value)
+ return true;
+
+ std::string data;
+ got_value = message_dict->GetString(ext::kAutomationMessageDataKey, &data);
+ DCHECK(got_value);
+ if (!got_value)
+ return true;
+
+ ExtensionPortContainer* port = automation->GetPortContainer(port_id);
+ DCHECK(port);
+ if (port)
+ port->PostMessageFromExternalPort(data);
+ } else if (command == ext::CHANNEL_CLOSED) {
+ int port_id = -1;
+ got_value = message_dict->GetInteger(ext::kAutomationPortIdKey, &port_id);
+ DCHECK(got_value);
+ if (!got_value)
+ return true;
+
+ ExtensionPortContainer* port = automation->GetPortContainer(port_id);
+ DCHECK(port);
+ if (port) {
+ // This will delete the port and notify the other end of the disconnect.
+ automation->RemovePortContainer(port);
+ }
+ } else {
+ // We don't expect other messages here.
+ NOTREACHED();
+ }
+
+ return true;
+}
diff --git a/chrome/browser/automation/extension_port_container.h b/chrome/browser/automation/extension_port_container.h
new file mode 100644
index 0000000..8fd8299
--- /dev/null
+++ b/chrome/browser/automation/extension_port_container.h
@@ -0,0 +1,87 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_AUTOMATION_EXTENSION_PORT_CONTAINER_H_
+#define CHROME_BROWSER_AUTOMATION_EXTENSION_PORT_CONTAINER_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/ref_counted.h"
+#include "ipc/ipc_message.h"
+
+class AutomationProvider;
+class ExtensionMessageService;
+class GURL;
+class ListValue;
+class MessageLoop;
+class RenderViewHost;
+
+// This class represents an external port to an extension, opened
+// through the automation interface.
+class ExtensionPortContainer : public IPC::Message::Sender {
+ public:
+
+ // Intercepts and processes a message posted through the automation interface.
+ // Returns true if the message was intercepted.
+ static bool InterceptMessageFromExternalHost(const std::string& message,
+ const std::string& origin,
+ const std::string& target,
+ AutomationProvider* automation,
+ RenderViewHost *view_host,
+ int tab_handle);
+
+ ExtensionPortContainer(AutomationProvider* automation, int tab_handle);
+ ~ExtensionPortContainer();
+
+ int port_id() const { return port_id_; }
+ void set_port_id(int port_id) { port_id_ = port_id; }
+
+ // IPC implementation.
+ virtual bool Send(IPC::Message* msg);
+
+ private:
+ // Posts a message to the external host.
+ bool PostMessageToExternalPort(const std::string& message);
+ // Posts a request response message to the external host.
+ bool PostResponseToExternalPort(const std::string& message);
+
+ // Forwards a message from the external port.
+ void PostMessageFromExternalPort(const std::string& message);
+
+ // Attempts to connect this instance to the extension id, sends
+ // a response to the connecting party.
+ // Returns true if the connection was successful.
+ bool Connect(const std::string &extension_id,
+ int process_id,
+ int routing_id,
+ int connection_id,
+ const std::string& channel_name,
+ const std::string& tab_json);
+
+ // Sends a connect response to the external port.
+ void SendConnectionResponse(int connection_id, int port_id);
+
+ void OnExtensionMessageInvoke(const std::string& function_name,
+ const ListValue& args,
+ bool requires_incognito_access,
+ const GURL& event_url);
+ void OnExtensionHandleMessage(const std::string& message, int source_port_id);
+ void OnExtensionPortDisconnected(int source_port_id);
+
+ // Our automation provider.
+ AutomationProvider* automation_;
+
+ // The extension message service.
+ scoped_refptr<ExtensionMessageService> service_;
+
+ // Our assigned port id.
+ int port_id_;
+ // Handle to our associated tab.
+ int tab_handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExtensionPortContainer);
+};
+
+#endif // CHROME_BROWSER_AUTOMATION_EXTENSION_PORT_CONTAINER_H_
diff --git a/chrome/browser/automation/ui_controls.h b/chrome/browser/automation/ui_controls.h
new file mode 100644
index 0000000..b0a2675
--- /dev/null
+++ b/chrome/browser/automation/ui_controls.h
@@ -0,0 +1,100 @@
+// Copyright (c) 2009 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_UI_CONTROLS_H_
+#define CHROME_BROWSER_AUTOMATION_UI_CONTROLS_H_
+
+#include "build/build_config.h"
+
+#include <string>
+
+#if defined(OS_WIN)
+#include <wtypes.h>
+#endif
+
+#include "gfx/native_widget_types.h"
+#include "gfx/point.h"
+#include "base/keyboard_codes.h"
+
+#if defined(TOOLKIT_VIEWS)
+namespace views {
+class View;
+}
+#endif
+
+class Task;
+
+namespace ui_controls {
+
+// Many of the functions in this class include a variant that takes a Task.
+// The version that takes a Task waits until the generated event is processed.
+// Once the generated event is processed the Task is Run (and deleted). Note
+// that this is a somewhat fragile process in that any event of the correct
+// type (key down, mouse click, etc.) will trigger the Task to be run. Hence
+// a usage such as
+//
+// SendKeyPress(...);
+// SendKeyPressNotifyWhenDone(..., task);
+//
+// might trigger |task| early.
+//
+// Note: Windows does not currently do anything with the |window| argument for
+// these functions, so passing NULL is ok.
+
+// Send a key press with/without modifier keys.
+bool SendKeyPress(gfx::NativeWindow window,
+ base::KeyboardCode key,
+ bool control,
+ bool shift,
+ bool alt,
+ bool command);
+bool SendKeyPressNotifyWhenDone(gfx::NativeWindow window,
+ base::KeyboardCode key,
+ bool control,
+ bool shift,
+ bool alt,
+ bool command,
+ Task* task);
+
+// Simulate a mouse move. (x,y) are absolute screen coordinates.
+bool SendMouseMove(long x, long y);
+bool SendMouseMoveNotifyWhenDone(long x, long y, Task* task);
+
+enum MouseButton {
+ LEFT = 0,
+ MIDDLE,
+ RIGHT,
+};
+
+// Used to indicate the state of the button when generating events.
+enum MouseButtonState {
+ UP = 1,
+ DOWN = 2
+};
+
+// Sends a mouse down and/or up message. The click will be sent to wherever
+// the cursor currently is, so be sure to move the cursor before calling this
+// (and be sure the cursor has arrived!).
+bool SendMouseEvents(MouseButton type, int state);
+bool SendMouseEventsNotifyWhenDone(MouseButton type, int state, Task* task);
+// Same as SendMouseEvents with UP | DOWN.
+bool SendMouseClick(MouseButton type);
+
+// A combination of SendMouseMove to the middle of the view followed by
+// SendMouseEvents.
+void MoveMouseToCenterAndPress(
+#if defined(TOOLKIT_VIEWS)
+ views::View* view,
+#elif defined(TOOLKIT_GTK)
+ GtkWidget* widget,
+#elif defined(OS_MACOSX)
+ NSWindow* window,
+#endif
+ MouseButton button,
+ int state,
+ Task* task);
+
+} // ui_controls
+
+#endif // CHROME_BROWSER_AUTOMATION_UI_CONTROLS_H_
diff --git a/chrome/browser/automation/ui_controls_linux.cc b/chrome/browser/automation/ui_controls_linux.cc
new file mode 100644
index 0000000..c9dca78
--- /dev/null
+++ b/chrome/browser/automation/ui_controls_linux.cc
@@ -0,0 +1,289 @@
+// Copyright (c) 2009 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/ui_controls.h"
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+
+#include "gfx/rect.h"
+#include "base/event_synthesis_gtk.h"
+#include "base/keyboard_code_conversion_gtk.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "chrome/browser/gtk/gtk_util.h"
+#include "chrome/test/automation/automation_constants.h"
+
+#if defined(TOOLKIT_VIEWS)
+#include "views/view.h"
+#include "views/widget/widget.h"
+#endif
+
+namespace {
+
+class EventWaiter : public MessageLoopForUI::Observer {
+ public:
+ EventWaiter(Task* task, GdkEventType type, int count)
+ : task_(task),
+ type_(type),
+ count_(count) {
+ MessageLoopForUI::current()->AddObserver(this);
+ }
+
+ virtual ~EventWaiter() {
+ MessageLoopForUI::current()->RemoveObserver(this);
+ }
+
+ // MessageLoop::Observer implementation:
+ virtual void WillProcessEvent(GdkEvent* event) {
+ if ((event->type == type_) && (--count_ == 0)) {
+ // At the time we're invoked the event has not actually been processed.
+ // Use PostTask to make sure the event has been processed before
+ // notifying.
+ // NOTE: if processing a message results in running a nested message
+ // loop, then DidProcessEvent isn't immediately sent. As such, we do
+ // the processing in WillProcessEvent rather than DidProcessEvent.
+ MessageLoop::current()->PostTask(FROM_HERE, task_);
+ delete this;
+ }
+ }
+
+ virtual void DidProcessEvent(GdkEvent* event) {
+ // No-op.
+ }
+
+ private:
+ // We pass ownership of task_ to MessageLoop when the current event is
+ // received.
+ Task* task_;
+ GdkEventType type_;
+ // The number of events of this type to wait for.
+ int count_;
+};
+
+class ClickTask : public Task {
+ public:
+ ClickTask(ui_controls::MouseButton button, int state, Task* followup)
+ : button_(button), state_(state), followup_(followup) {
+ }
+
+ virtual ~ClickTask() {}
+
+ virtual void Run() {
+ if (followup_)
+ ui_controls::SendMouseEventsNotifyWhenDone(button_, state_, followup_);
+ else
+ ui_controls::SendMouseEvents(button_, state_);
+ }
+
+ private:
+ ui_controls::MouseButton button_;
+ int state_;
+ Task* followup_;
+};
+
+void FakeAMouseMotionEvent(gint x, gint y) {
+ GdkEvent* event = gdk_event_new(GDK_MOTION_NOTIFY);
+
+ event->motion.send_event = false;
+ event->motion.time = gtk_util::XTimeNow();
+
+ GtkWidget* grab_widget = gtk_grab_get_current();
+ if (grab_widget) {
+ // If there is a grab, we need to target all events at it regardless of
+ // what widget the mouse is over.
+ event->motion.window = grab_widget->window;
+ } else {
+ event->motion.window = gdk_window_at_pointer(&x, &y);
+ }
+ g_object_ref(event->motion.window);
+ event->motion.x = x;
+ event->motion.y = y;
+ gint origin_x, origin_y;
+ gdk_window_get_origin(event->motion.window, &origin_x, &origin_y);
+ event->motion.x_root = x + origin_x;
+ event->motion.y_root = y + origin_y;
+
+ event->motion.device = gdk_device_get_core_pointer();
+ event->type = GDK_MOTION_NOTIFY;
+
+ gdk_event_put(event);
+ gdk_event_free(event);
+}
+
+} // namespace
+
+namespace ui_controls {
+
+bool SendKeyPress(gfx::NativeWindow window,
+ base::KeyboardCode key,
+ bool control, bool shift, bool alt, bool command) {
+ DCHECK(command == false); // No command key on Linux
+ GdkWindow* event_window = NULL;
+ GtkWidget* grab_widget = gtk_grab_get_current();
+ if (grab_widget) {
+ // If there is a grab, send all events to the grabbed widget.
+ event_window = grab_widget->window;
+ } else if (window) {
+ event_window = GTK_WIDGET(window)->window;
+ } else {
+ // No target was specified. Send the events to the active toplevel.
+ GList* windows = gtk_window_list_toplevels();
+ for (GList* element = windows; element; element = g_list_next(element)) {
+ GtkWindow* this_window = GTK_WINDOW(element->data);
+ if (gtk_window_is_active(this_window)) {
+ event_window = GTK_WIDGET(this_window)->window;
+ break;
+ }
+ }
+ g_list_free(windows);
+ }
+ if (!event_window) {
+ NOTREACHED() << "Window not specified and none is active";
+ return false;
+ }
+
+ std::vector<GdkEvent*> events;
+ base::SynthesizeKeyPressEvents(event_window, key, control, shift, alt,
+ &events);
+ for (std::vector<GdkEvent*>::iterator iter = events.begin();
+ iter != events.end(); ++iter) {
+ gdk_event_put(*iter);
+ // gdk_event_put appends a copy of the event.
+ gdk_event_free(*iter);
+ }
+
+ return true;
+}
+
+bool SendKeyPressNotifyWhenDone(gfx::NativeWindow window,
+ base::KeyboardCode key,
+ bool control, bool shift,
+ bool alt, bool command,
+ Task* task) {
+ DCHECK(command == false); // No command key on Linux
+ int release_count = 1;
+ if (control)
+ release_count++;
+ if (shift)
+ release_count++;
+ if (alt)
+ release_count++;
+ // This object will delete itself after running |task|.
+ new EventWaiter(task, GDK_KEY_RELEASE, release_count);
+ return SendKeyPress(window, key, control, shift, alt, command);
+}
+
+bool SendMouseMove(long x, long y) {
+ gdk_display_warp_pointer(gdk_display_get_default(), gdk_screen_get_default(),
+ x, y);
+ // Sometimes gdk_display_warp_pointer fails to send back any indication of
+ // the move, even though it succesfully moves the server cursor. We fake it in
+ // order to get drags to work.
+ FakeAMouseMotionEvent(x, y);
+
+ return true;
+}
+
+bool SendMouseMoveNotifyWhenDone(long x, long y, Task* task) {
+ bool rv = SendMouseMove(x, y);
+ // We can't rely on any particular event signalling the completion of the
+ // mouse move. Posting the task to the message loop hopefully guarantees
+ // the pointer has moved before task is run (although it may not run it as
+ // soon as it could).
+ MessageLoop::current()->PostTask(FROM_HERE, task);
+ return rv;
+}
+
+bool SendMouseEvents(MouseButton type, int state) {
+ GdkEvent* event = gdk_event_new(GDK_BUTTON_PRESS);
+
+ event->button.send_event = false;
+ event->button.time = gtk_util::XTimeNow();
+
+ gint x, y;
+ GtkWidget* grab_widget = gtk_grab_get_current();
+ if (grab_widget) {
+ // If there is a grab, we need to target all events at it regardless of
+ // what widget the mouse is over.
+ event->button.window = grab_widget->window;
+ gdk_window_get_pointer(event->button.window, &x, &y, NULL);
+ } else {
+ event->button.window = gdk_window_at_pointer(&x, &y);
+ }
+
+ g_object_ref(event->button.window);
+ event->button.x = x;
+ event->button.y = y;
+ gint origin_x, origin_y;
+ gdk_window_get_origin(event->button.window, &origin_x, &origin_y);
+ event->button.x_root = x + origin_x;
+ event->button.y_root = y + origin_y;
+
+ event->button.axes = NULL;
+ GdkModifierType modifier;
+ gdk_window_get_pointer(event->button.window, NULL, NULL, &modifier);
+ event->button.state = modifier;
+ event->button.button = type == LEFT ? 1 : (type == MIDDLE ? 2 : 3);
+ event->button.device = gdk_device_get_core_pointer();
+
+ event->button.type = GDK_BUTTON_PRESS;
+ if (state & DOWN)
+ gdk_event_put(event);
+
+ // Also send a release event.
+ GdkEvent* release_event = gdk_event_copy(event);
+ release_event->button.type = GDK_BUTTON_RELEASE;
+ release_event->button.time++;
+ if (state & UP)
+ gdk_event_put(release_event);
+
+ gdk_event_free(event);
+ gdk_event_free(release_event);
+
+ return false;
+}
+
+bool SendMouseEventsNotifyWhenDone(MouseButton type, int state, Task* task) {
+ bool rv = SendMouseEvents(type, state);
+ GdkEventType wait_type;
+ if (state & UP) {
+ wait_type = GDK_BUTTON_RELEASE;
+ } else {
+ if (type == LEFT)
+ wait_type = GDK_BUTTON_PRESS;
+ else if (type == MIDDLE)
+ wait_type = GDK_2BUTTON_PRESS;
+ else
+ wait_type = GDK_3BUTTON_PRESS;
+ }
+ new EventWaiter(task, wait_type, 1);
+ return rv;
+}
+
+bool SendMouseClick(MouseButton type) {
+ return SendMouseEvents(type, UP | DOWN);
+}
+
+#if defined(TOOLKIT_VIEWS)
+void MoveMouseToCenterAndPress(views::View* view, MouseButton button,
+ int state, Task* task) {
+ gfx::Point view_center(view->width() / 2, view->height() / 2);
+ views::View::ConvertPointToScreen(view, &view_center);
+ SendMouseMoveNotifyWhenDone(view_center.x(), view_center.y(),
+ new ClickTask(button, state, task));
+}
+#else
+void MoveMouseToCenterAndPress(GtkWidget* widget,
+ MouseButton button,
+ int state,
+ Task* task) {
+ gfx::Rect bounds = gtk_util::GetWidgetScreenBounds(widget);
+ SendMouseMoveNotifyWhenDone(bounds.x() + bounds.width() / 2,
+ bounds.y() + bounds.height() / 2,
+ new ClickTask(button, state, task));
+}
+#endif
+
+} // namespace ui_controls
diff --git a/chrome/browser/automation/ui_controls_mac.mm b/chrome/browser/automation/ui_controls_mac.mm
new file mode 100644
index 0000000..715135c
--- /dev/null
+++ b/chrome/browser/automation/ui_controls_mac.mm
@@ -0,0 +1,242 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/automation/ui_controls.h"
+
+#import <Cocoa/Cocoa.h>
+#include <mach/mach_time.h>
+
+#include "base/message_loop.h"
+#include "chrome/browser/chrome_thread.h"
+
+// Implementation details: We use [NSApplication sendEvent:] instead
+// of [NSApplication postEvent:atStart:] so that the event gets sent
+// immediately. This lets us run the post-event task right
+// immediately as well. Unfortunately I cannot subclass NSEvent (it's
+// probably a class cluster) to allow other easy answers. For
+// example, if I could subclass NSEvent, I could run the Task in it's
+// dealloc routine (which necessarily happens after the event is
+// dispatched). Unlike Linux, Mac does not have message loop
+// observer/notification. Unlike windows, I cannot post non-events
+// into the event queue. (I can post other kinds of tasks but can't
+// guarantee their order with regards to events).
+
+namespace {
+
+// From
+// http://stackoverflow.com/questions/1597383/cgeventtimestamp-to-nsdate
+// Which credits Apple sample code for this routine.
+uint64_t UpTimeInNanoseconds(void) {
+ uint64_t time;
+ uint64_t timeNano;
+ static mach_timebase_info_data_t sTimebaseInfo;
+
+ time = mach_absolute_time();
+
+ // Convert to nanoseconds.
+
+ // If this is the first time we've run, get the timebase.
+ // We can use denom == 0 to indicate that sTimebaseInfo is
+ // uninitialised because it makes no sense to have a zero
+ // denominator is a fraction.
+ if (sTimebaseInfo.denom == 0) {
+ (void) mach_timebase_info(&sTimebaseInfo);
+ }
+
+ // This could overflow; for testing needs we probably don't care.
+ timeNano = time * sTimebaseInfo.numer / sTimebaseInfo.denom;
+ return timeNano;
+}
+
+NSTimeInterval TimeIntervalSinceSystemStartup() {
+ return UpTimeInNanoseconds() / 1000000000.0;
+}
+
+} // anonymous namespace
+
+
+namespace ui_controls {
+
+bool SendKeyPress(gfx::NativeWindow window,
+ base::KeyboardCode key,
+ bool control,
+ bool shift,
+ bool alt,
+ bool command) {
+ return SendKeyPressNotifyWhenDone(window, key,
+ control, shift, alt, command,
+ NULL);
+}
+
+// Win and Linux implement a SendKeyPress() this as a
+// SendKeyPressAndRelease(), so we should as well (despite the name).
+//
+// TODO(jrg): handle "characters" better (e.g. apply shift?)
+bool SendKeyPressNotifyWhenDone(gfx::NativeWindow window,
+ base::KeyboardCode key,
+ bool control,
+ bool shift,
+ bool alt,
+ bool command,
+ Task* task) {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
+ NSUInteger flags = 0;
+ if (control)
+ flags |= NSControlKeyMask;
+ if (shift)
+ flags |= NSShiftKeyMask;
+ if (alt)
+ flags |= NSAlternateKeyMask;
+ if (command)
+ flags |= NSCommandKeyMask;
+ unsigned char keycode = key;
+ NSString* charactersIgnoringModifiers = [[[NSString alloc]
+ initWithBytes:&keycode
+ length:1
+ encoding:NSUTF8StringEncoding]
+ autorelease];
+ NSString* characters = charactersIgnoringModifiers;
+
+ // For events other than mouse moved, [event locationInWindow] is
+ // UNDEFINED if the event is not NSMouseMoved. Thus, the (0,0)
+ // locaiton should be fine.
+ // First a key down...
+ NSEvent* event =
+ [NSEvent keyEventWithType:NSKeyDown
+ location:NSMakePoint(0,0)
+ modifierFlags:flags
+ timestamp:TimeIntervalSinceSystemStartup()
+ windowNumber:[window windowNumber]
+ context:nil
+ characters:characters
+ charactersIgnoringModifiers:charactersIgnoringModifiers
+ isARepeat:NO
+ keyCode:key];
+ [[NSApplication sharedApplication] sendEvent:event];
+ // Then a key up.
+ event =
+ [NSEvent keyEventWithType:NSKeyUp
+ location:NSMakePoint(0,0)
+ modifierFlags:flags
+ timestamp:TimeIntervalSinceSystemStartup()
+ windowNumber:[window windowNumber]
+ context:nil
+ characters:characters
+ charactersIgnoringModifiers:charactersIgnoringModifiers
+ isARepeat:NO
+ keyCode:key];
+ [[NSApplication sharedApplication] sendEvent:event];
+
+ if (task)
+ MessageLoop::current()->PostTask(FROM_HERE, task);
+ return true;
+}
+
+bool SendMouseMove(long x, long y) {
+ return SendMouseMoveNotifyWhenDone(x, y, NULL);
+}
+
+// Input position is in screen coordinates. However, NSMouseMoved
+// events require them window-relative, so we adjust. We *DO* flip
+// the coordinate space, so input events can be the same for all
+// platforms. E.g. (0,0) is upper-left.
+bool SendMouseMoveNotifyWhenDone(long x, long y, Task* task) {
+ NSWindow* window = [[NSApplication sharedApplication] keyWindow];
+ CGFloat screenHeight = [[NSScreen mainScreen] frame].size.height;
+ NSPoint pointInWindow = NSMakePoint(x, screenHeight - y); // flip!
+ if (window)
+ pointInWindow = [window convertScreenToBase:pointInWindow];
+ NSTimeInterval timestamp = TimeIntervalSinceSystemStartup();
+
+ NSEvent* event =
+ [NSEvent mouseEventWithType:NSMouseMoved
+ location:pointInWindow
+ modifierFlags:0
+ timestamp:timestamp
+ windowNumber:[window windowNumber]
+ context:nil
+ eventNumber:0
+ clickCount:0
+ pressure:0.0];
+ [[NSApplication sharedApplication] postEvent:event atStart:NO];
+ if (task)
+ MessageLoop::current()->PostTask(FROM_HERE, task);
+ return true;
+}
+
+bool SendMouseEvents(MouseButton type, int state) {
+ return SendMouseEventsNotifyWhenDone(type, state, NULL);
+}
+
+bool SendMouseEventsNotifyWhenDone(MouseButton type, int state, Task* task) {
+ // On windows it appears state can be (UP|DOWN). It is unclear if
+ // that'll happen here but prepare for it just in case.
+ if (state == (UP|DOWN)) {
+ return (SendMouseEventsNotifyWhenDone(type, DOWN, NULL) &&
+ SendMouseEventsNotifyWhenDone(type, UP, task));
+ }
+
+ NSEventType etype = 0;
+ if (type == LEFT) {
+ if (state == UP) {
+ etype = NSLeftMouseUp;
+ } else {
+ etype = NSLeftMouseDown;
+ }
+ } else if (type == MIDDLE) {
+ if (state == UP) {
+ etype = NSOtherMouseUp;
+ } else {
+ etype = NSOtherMouseDown;
+ }
+ } else if (type == RIGHT) {
+ if (state == UP) {
+ etype = NSRightMouseUp;
+ } else {
+ etype = NSRightMouseDown;
+ }
+ } else {
+ return false;
+ }
+ NSWindow* window = [[NSApplication sharedApplication] keyWindow];
+ NSPoint location = [NSEvent mouseLocation];
+ NSPoint pointInWindow = location;
+ if (window)
+ pointInWindow = [window convertScreenToBase:pointInWindow];
+
+ NSEvent* event =
+ [NSEvent mouseEventWithType:etype
+ location:pointInWindow
+ modifierFlags:0
+ timestamp:TimeIntervalSinceSystemStartup()
+ windowNumber:[window windowNumber]
+ context:nil
+ eventNumber:0
+ clickCount:0
+ pressure:0.0];
+ [[NSApplication sharedApplication] sendEvent:event];
+ if (task)
+ MessageLoop::current()->PostTask(FROM_HERE, task);
+ return true;
+}
+
+bool SendMouseClick(MouseButton type) {
+ return SendMouseEventsNotifyWhenDone(type, UP|DOWN, NULL);
+}
+
+// This appears to only be used by a function in test/ui_test_utils.h:
+// ui_test_utils::ClickOnView(). That is not implemented on Mac, so
+// we don't need to implement MoveMouseToCenterAndPress(). I've
+// suggested an implementation of ClickOnView() which would call Cocoa
+// directly and not need this indirection, so this may not be needed,
+// ever.
+void MoveMouseToCenterAndPress(
+ NSWindow* window,
+ MouseButton button,
+ int state,
+ Task* task) {
+ NOTIMPLEMENTED();
+}
+
+} // ui_controls
diff --git a/chrome/browser/automation/ui_controls_win.cc b/chrome/browser/automation/ui_controls_win.cc
new file mode 100644
index 0000000..50bb88f
--- /dev/null
+++ b/chrome/browser/automation/ui_controls_win.cc
@@ -0,0 +1,370 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/automation/ui_controls.h"
+
+#include "base/keyboard_codes.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/win_util.h"
+#include "base/ref_counted.h"
+#include "base/task.h"
+#include "views/view.h"
+
+namespace ui_controls {
+
+namespace {
+
+// InputDispatcher ------------------------------------------------------------
+
+// InputDispatcher is used to listen for a mouse/keyboard event. When the
+// appropriate event is received the task is notified.
+class InputDispatcher : public base::RefCounted<InputDispatcher> {
+ public:
+ InputDispatcher(Task* task, WPARAM message_waiting_for);
+
+ // Invoked from the hook. If mouse_message matches message_waiting_for_
+ // MatchingMessageFound is invoked.
+ void DispatchedMessage(WPARAM mouse_message);
+
+ // Invoked when a matching event is found. Uninstalls the hook and schedules
+ // an event that notifies the task.
+ void MatchingMessageFound();
+
+ private:
+ friend class base::RefCounted<InputDispatcher>;
+
+ ~InputDispatcher();
+
+ // Notifies the task and release this (which should delete it).
+ void NotifyTask();
+
+ // The task we notify.
+ scoped_ptr<Task> task_;
+
+ // Message we're waiting for. Not used for keyboard events.
+ const WPARAM message_waiting_for_;
+
+ DISALLOW_COPY_AND_ASSIGN(InputDispatcher);
+};
+
+// Have we installed the hook?
+bool installed_hook_ = false;
+
+// Return value from SetWindowsHookEx.
+HHOOK next_hook_ = NULL;
+
+// If a hook is installed, this is the dispatcher.
+InputDispatcher* current_dispatcher_ = NULL;
+
+// Callback from hook when a mouse message is received.
+LRESULT CALLBACK MouseHook(int n_code, WPARAM w_param, LPARAM l_param) {
+ HHOOK next_hook = next_hook_;
+ if (n_code == HC_ACTION) {
+ DCHECK(current_dispatcher_);
+ current_dispatcher_->DispatchedMessage(w_param);
+ }
+ return CallNextHookEx(next_hook, n_code, w_param, l_param);
+}
+
+// Callback from hook when a key message is received.
+LRESULT CALLBACK KeyHook(int n_code, WPARAM w_param, LPARAM l_param) {
+ HHOOK next_hook = next_hook_;
+ if (n_code == HC_ACTION) {
+ DCHECK(current_dispatcher_);
+ if (l_param & (1 << 30)) // Only send on key up.
+ current_dispatcher_->MatchingMessageFound();
+ }
+ return CallNextHookEx(next_hook, n_code, w_param, l_param);
+}
+
+// Installs dispatcher as the current hook.
+void InstallHook(InputDispatcher* dispatcher, bool key_hook) {
+ DCHECK(!installed_hook_);
+ current_dispatcher_ = dispatcher;
+ installed_hook_ = true;
+ if (key_hook) {
+ next_hook_ = SetWindowsHookEx(WH_KEYBOARD, &KeyHook, NULL,
+ GetCurrentThreadId());
+ } else {
+ // NOTE: I originally tried WH_CALLWNDPROCRET, but for some reason I
+ // didn't get a mouse message like I do with MouseHook.
+ next_hook_ = SetWindowsHookEx(WH_MOUSE, &MouseHook, NULL,
+ GetCurrentThreadId());
+ }
+ DCHECK(next_hook_);
+}
+
+// Uninstalls the hook set in InstallHook.
+void UninstallHook(InputDispatcher* dispatcher) {
+ if (current_dispatcher_ == dispatcher) {
+ installed_hook_ = false;
+ current_dispatcher_ = NULL;
+ UnhookWindowsHookEx(next_hook_);
+ }
+}
+
+InputDispatcher::InputDispatcher(Task* task, UINT message_waiting_for)
+ : task_(task), message_waiting_for_(message_waiting_for) {
+ InstallHook(this, message_waiting_for == WM_KEYUP);
+}
+
+InputDispatcher::~InputDispatcher() {
+ // Make sure the hook isn't installed.
+ UninstallHook(this);
+}
+
+void InputDispatcher::DispatchedMessage(WPARAM message) {
+ if (message == message_waiting_for_)
+ MatchingMessageFound();
+}
+
+void InputDispatcher::MatchingMessageFound() {
+ UninstallHook(this);
+ // At the time we're invoked the event has not actually been processed.
+ // Use PostTask to make sure the event has been processed before notifying.
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE, NewRunnableMethod(this, &InputDispatcher::NotifyTask), 0);
+}
+
+void InputDispatcher::NotifyTask() {
+ task_->Run();
+ Release();
+}
+
+// Private functions ----------------------------------------------------------
+
+// Populate the INPUT structure with the appropriate keyboard event
+// parameters required by SendInput
+bool FillKeyboardInput(base::KeyboardCode key, INPUT* input, bool key_up) {
+ memset(input, 0, sizeof(INPUT));
+ input->type = INPUT_KEYBOARD;
+ input->ki.wVk = win_util::KeyboardCodeToWin(key);
+ input->ki.dwFlags = key_up ? KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP :
+ KEYEVENTF_EXTENDEDKEY;
+
+ return true;
+}
+
+// Send a key event (up/down)
+bool SendKeyEvent(base::KeyboardCode key, bool up) {
+ INPUT input = { 0 };
+
+ if (!FillKeyboardInput(key, &input, up))
+ return false;
+
+ if (!::SendInput(1, &input, sizeof(INPUT)))
+ return false;
+
+ return true;
+}
+
+bool SendKeyPressImpl(base::KeyboardCode key,
+ bool control, bool shift, bool alt,
+ Task* task) {
+ scoped_refptr<InputDispatcher> dispatcher(
+ task ? new InputDispatcher(task, WM_KEYUP) : NULL);
+
+ // If a pop-up menu is open, it won't receive events sent using SendInput.
+ // Check for a pop-up menu using its window class (#32768) and if one
+ // exists, send the key event directly there.
+ HWND popup_menu = ::FindWindow(L"#32768", 0);
+ if (popup_menu != NULL && popup_menu == ::GetTopWindow(NULL)) {
+ WPARAM w_param = win_util::KeyboardCodeToWin(key);
+ LPARAM l_param = 0;
+ ::SendMessage(popup_menu, WM_KEYDOWN, w_param, l_param);
+ ::SendMessage(popup_menu, WM_KEYUP, w_param, l_param);
+
+ if (dispatcher.get())
+ dispatcher->AddRef();
+ return true;
+ }
+
+ INPUT input[8] = { 0 }; // 8, assuming all the modifiers are activated
+
+ UINT i = 0;
+ if (control) {
+ if (!FillKeyboardInput(base::VKEY_CONTROL, &input[i], false))
+ return false;
+ i++;
+ }
+
+ if (shift) {
+ if (!FillKeyboardInput(base::VKEY_SHIFT, &input[i], false))
+ return false;
+ i++;
+ }
+
+ if (alt) {
+ if (!FillKeyboardInput(base::VKEY_MENU, &input[i], false))
+ return false;
+ i++;
+ }
+
+ if (!FillKeyboardInput(key, &input[i], false))
+ return false;
+ i++;
+
+ if (!FillKeyboardInput(key, &input[i], true))
+ return false;
+ i++;
+
+ if (alt) {
+ if (!FillKeyboardInput(base::VKEY_MENU, &input[i], true))
+ return false;
+ i++;
+ }
+
+ if (shift) {
+ if (!FillKeyboardInput(base::VKEY_SHIFT, &input[i], true))
+ return false;
+ i++;
+ }
+
+ if (control) {
+ if (!FillKeyboardInput(base::VKEY_CONTROL, &input[i], true))
+ return false;
+ i++;
+ }
+
+ if (::SendInput(i, input, sizeof(INPUT)) != i)
+ return false;
+
+ if (dispatcher.get())
+ dispatcher->AddRef();
+ return true;
+}
+
+bool SendMouseMoveImpl(long x, long y, Task* task) {
+ // First check if the mouse is already there.
+ POINT current_pos;
+ ::GetCursorPos(&current_pos);
+ if (x == current_pos.x && y == current_pos.y) {
+ if (task)
+ MessageLoop::current()->PostTask(FROM_HERE, task);
+ return true;
+ }
+
+ INPUT input = { 0 };
+
+ int screen_width = ::GetSystemMetrics(SM_CXSCREEN) - 1;
+ int screen_height = ::GetSystemMetrics(SM_CYSCREEN) - 1;
+ LONG pixel_x = static_cast<LONG>(x * (65535.0f / screen_width));
+ LONG pixel_y = static_cast<LONG>(y * (65535.0f / screen_height));
+
+ input.type = INPUT_MOUSE;
+ input.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
+ input.mi.dx = pixel_x;
+ input.mi.dy = pixel_y;
+
+ scoped_refptr<InputDispatcher> dispatcher(
+ task ? new InputDispatcher(task, WM_MOUSEMOVE) : NULL);
+
+ if (!::SendInput(1, &input, sizeof(INPUT)))
+ return false;
+
+ if (dispatcher.get())
+ dispatcher->AddRef();
+
+ return true;
+}
+
+bool SendMouseEventsImpl(MouseButton type, int state, Task* task) {
+ DWORD down_flags = MOUSEEVENTF_ABSOLUTE;
+ DWORD up_flags = MOUSEEVENTF_ABSOLUTE;
+ UINT last_event;
+
+ switch (type) {
+ case LEFT:
+ down_flags |= MOUSEEVENTF_LEFTDOWN;
+ up_flags |= MOUSEEVENTF_LEFTUP;
+ last_event = (state & UP) ? WM_LBUTTONUP : WM_LBUTTONDOWN;
+ break;
+
+ case MIDDLE:
+ down_flags |= MOUSEEVENTF_MIDDLEDOWN;
+ up_flags |= MOUSEEVENTF_MIDDLEUP;
+ last_event = (state & UP) ? WM_MBUTTONUP : WM_MBUTTONDOWN;
+ break;
+
+ case RIGHT:
+ down_flags |= MOUSEEVENTF_RIGHTDOWN;
+ up_flags |= MOUSEEVENTF_RIGHTUP;
+ last_event = (state & UP) ? WM_RBUTTONUP : WM_RBUTTONDOWN;
+ break;
+
+ default:
+ NOTREACHED();
+ return false;
+ }
+
+ scoped_refptr<InputDispatcher> dispatcher(
+ task ? new InputDispatcher(task, last_event) : NULL);
+
+ INPUT input = { 0 };
+ input.type = INPUT_MOUSE;
+ input.mi.dwFlags = down_flags;
+ if ((state & DOWN) && !::SendInput(1, &input, sizeof(INPUT)))
+ return false;
+
+ input.mi.dwFlags = up_flags;
+ if ((state & UP) && !::SendInput(1, &input, sizeof(INPUT)))
+ return false;
+
+ if (dispatcher.get())
+ dispatcher->AddRef();
+
+ return true;
+}
+
+} // namespace
+
+// public functions -----------------------------------------------------------
+
+bool SendKeyPress(gfx::NativeWindow window, base::KeyboardCode key,
+ bool control, bool shift, bool alt, bool command) {
+ DCHECK(command == false); // No command key on Windows
+ return SendKeyPressImpl(key, control, shift, alt, NULL);
+}
+
+bool SendKeyPressNotifyWhenDone(gfx::NativeWindow window,
+ base::KeyboardCode key,
+ bool control, bool shift, bool alt,
+ bool command,
+ Task* task) {
+ DCHECK(command == false); // No command key on Windows
+ return SendKeyPressImpl(key, control, shift, alt, task);
+}
+
+bool SendMouseMove(long x, long y) {
+ return SendMouseMoveImpl(x, y, NULL);
+}
+
+bool SendMouseMoveNotifyWhenDone(long x, long y, Task* task) {
+ return SendMouseMoveImpl(x, y, task);
+}
+
+bool SendMouseEvents(MouseButton type, int state) {
+ return SendMouseEventsImpl(type, state, NULL);
+}
+
+bool SendMouseEventsNotifyWhenDone(MouseButton type, int state, Task* task) {
+ return SendMouseEventsImpl(type, state, task);
+}
+
+bool SendMouseClick(MouseButton type) {
+ return SendMouseEventsImpl(type, UP | DOWN, NULL);
+}
+
+void MoveMouseToCenterAndPress(views::View* view, MouseButton button,
+ int state, Task* task) {
+ DCHECK(view);
+ DCHECK(view->GetWidget());
+ gfx::Point view_center(view->width() / 2, view->height() / 2);
+ views::View::ConvertPointToScreen(view, &view_center);
+ SendMouseMove(view_center.x(), view_center.y());
+ SendMouseEventsNotifyWhenDone(button, state, task);
+}
+
+} // ui_controls
diff --git a/chrome/browser/automation/url_request_automation_job.cc b/chrome/browser/automation/url_request_automation_job.cc
new file mode 100644
index 0000000..6cdfdbf
--- /dev/null
+++ b/chrome/browser/automation/url_request_automation_job.cc
@@ -0,0 +1,460 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/automation/url_request_automation_job.h"
+
+#include "base/message_loop.h"
+#include "base/time.h"
+#include "chrome/browser/automation/automation_resource_message_filter.h"
+#include "chrome/browser/chrome_thread.h"
+#include "chrome/browser/renderer_host/render_view_host.h"
+#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
+#include "chrome/browser/renderer_host/resource_dispatcher_host_request_info.h"
+#include "chrome/test/automation/automation_messages.h"
+#include "net/base/cookie_monster.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_request_headers.h"
+#include "net/http/http_util.h"
+#include "net/url_request/url_request_context.h"
+
+using base::Time;
+using base::TimeDelta;
+
+// The list of filtered headers that are removed from requests sent via
+// StartAsync(). These must be lower case.
+static const char* const kFilteredHeaderStrings[] = {
+ "accept",
+ "cache-control",
+ "connection",
+ "cookie",
+ "expect",
+ "max-forwards",
+ "proxy-authorization",
+ "te",
+ "upgrade",
+ "via"
+};
+
+int URLRequestAutomationJob::instance_count_ = 0;
+bool URLRequestAutomationJob::is_protocol_factory_registered_ = false;
+
+URLRequest::ProtocolFactory* URLRequestAutomationJob::old_http_factory_
+ = NULL;
+URLRequest::ProtocolFactory* URLRequestAutomationJob::old_https_factory_
+ = NULL;
+
+URLRequestAutomationJob::URLRequestAutomationJob(URLRequest* request, int tab,
+ int request_id, AutomationResourceMessageFilter* filter, bool is_pending)
+ : URLRequestJob(request),
+ tab_(tab),
+ message_filter_(filter),
+ pending_buf_size_(0),
+ redirect_status_(0),
+ request_id_(request_id),
+ is_pending_(is_pending) {
+ DLOG(INFO) << "URLRequestAutomationJob create. Count: " << ++instance_count_;
+ DCHECK(message_filter_ != NULL);
+
+ if (message_filter_) {
+ id_ = message_filter_->NewAutomationRequestId();
+ DCHECK_NE(id_, 0);
+ }
+}
+
+URLRequestAutomationJob::~URLRequestAutomationJob() {
+ DLOG(INFO) << "URLRequestAutomationJob delete. Count: " << --instance_count_;
+ Cleanup();
+}
+
+bool URLRequestAutomationJob::EnsureProtocolFactoryRegistered() {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
+
+ if (!is_protocol_factory_registered_) {
+ old_http_factory_ =
+ URLRequest::RegisterProtocolFactory("http",
+ &URLRequestAutomationJob::Factory);
+ old_https_factory_ =
+ URLRequest::RegisterProtocolFactory("https",
+ &URLRequestAutomationJob::Factory);
+ is_protocol_factory_registered_ = true;
+ }
+
+ return true;
+}
+
+URLRequestJob* URLRequestAutomationJob::Factory(URLRequest* request,
+ const std::string& scheme) {
+ bool scheme_is_http = request->url().SchemeIs("http");
+ bool scheme_is_https = request->url().SchemeIs("https");
+
+ // Returning null here just means that the built-in handler will be used.
+ if (scheme_is_http || scheme_is_https) {
+ ResourceDispatcherHostRequestInfo* request_info =
+ ResourceDispatcherHost::InfoForRequest(request);
+ if (request_info) {
+ int child_id = request_info->child_id();
+ int route_id = request_info->route_id();
+
+ if (request_info->process_type() == ChildProcessInfo::PLUGIN_PROCESS) {
+ child_id = request_info->host_renderer_id();
+ route_id = request_info->host_render_view_id();
+ }
+
+ AutomationResourceMessageFilter::AutomationDetails details;
+ if (AutomationResourceMessageFilter::LookupRegisteredRenderView(
+ child_id, route_id, &details)) {
+ URLRequestAutomationJob* job = new URLRequestAutomationJob(request,
+ details.tab_handle, request_info->request_id(), details.filter,
+ details.is_pending_render_view);
+ return job;
+ }
+ }
+
+ if (scheme_is_http && old_http_factory_)
+ return old_http_factory_(request, scheme);
+ else if (scheme_is_https && old_https_factory_)
+ return old_https_factory_(request, scheme);
+ }
+ return NULL;
+}
+
+// URLRequestJob Implementation.
+void URLRequestAutomationJob::Start() {
+ if (!is_pending()) {
+ // Start reading asynchronously so that all error reporting and data
+ // callbacks happen as they would for network requests.
+ MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &URLRequestAutomationJob::StartAsync));
+ } else {
+ // If this is a pending job, then register it immediately with the message
+ // filter so it can be serviced later when we receive a request from the
+ // external host to connect to the corresponding external tab.
+ message_filter_->RegisterRequest(this);
+ }
+}
+
+void URLRequestAutomationJob::Kill() {
+ if (message_filter_.get()) {
+ if (!is_pending()) {
+ message_filter_->Send(new AutomationMsg_RequestEnd(0, tab_, id_,
+ URLRequestStatus(URLRequestStatus::CANCELED, net::ERR_ABORTED)));
+ }
+ }
+ DisconnectFromMessageFilter();
+ URLRequestJob::Kill();
+}
+
+bool URLRequestAutomationJob::ReadRawData(
+ net::IOBuffer* buf, int buf_size, int* bytes_read) {
+ DLOG(INFO) << "URLRequestAutomationJob: " <<
+ request_->url().spec() << " - read pending: " << buf_size;
+
+ // We should not receive a read request for a pending job.
+ DCHECK(!is_pending());
+
+ pending_buf_ = buf;
+ pending_buf_size_ = buf_size;
+
+ if (message_filter_) {
+ message_filter_->Send(new AutomationMsg_RequestRead(0, tab_, id_,
+ buf_size));
+ SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
+ } else {
+ ChromeThread::PostTask(ChromeThread::IO, FROM_HERE,
+ NewRunnableMethod(this,
+ &URLRequestAutomationJob::NotifyJobCompletionTask));
+ }
+ return false;
+}
+
+bool URLRequestAutomationJob::GetMimeType(std::string* mime_type) const {
+ if (!mime_type_.empty()) {
+ *mime_type = mime_type_;
+ } else if (headers_) {
+ headers_->GetMimeType(mime_type);
+ }
+
+ return (!mime_type->empty());
+}
+
+bool URLRequestAutomationJob::GetCharset(std::string* charset) {
+ if (headers_)
+ return headers_->GetCharset(charset);
+ return false;
+}
+
+void URLRequestAutomationJob::GetResponseInfo(net::HttpResponseInfo* info) {
+ if (headers_)
+ info->headers = headers_;
+ if (request_->url().SchemeIsSecure()) {
+ // Make up a fake certificate for this response since we don't have
+ // access to the real SSL info.
+ const char* kCertIssuer = "Chrome Internal";
+ const int kLifetimeDays = 100;
+
+ info->ssl_info.cert =
+ new net::X509Certificate(request_->url().GetWithEmptyPath().spec(),
+ kCertIssuer,
+ Time::Now(),
+ Time::Now() +
+ TimeDelta::FromDays(kLifetimeDays));
+ info->ssl_info.cert_status = 0;
+ info->ssl_info.security_bits = 0;
+ }
+}
+
+int URLRequestAutomationJob::GetResponseCode() const {
+ if (headers_)
+ return headers_->response_code();
+
+ static const int kDefaultResponseCode = 200;
+ return kDefaultResponseCode;
+}
+
+bool URLRequestAutomationJob::IsRedirectResponse(
+ GURL* location, int* http_status_code) {
+ if (!net::HttpResponseHeaders::IsRedirectResponseCode(redirect_status_))
+ return false;
+
+ *http_status_code = redirect_status_;
+ *location = GURL(redirect_url_);
+ return true;
+}
+
+bool URLRequestAutomationJob::MayFilterMessage(const IPC::Message& message,
+ int* request_id) {
+ switch (message.type()) {
+ case AutomationMsg_RequestStarted::ID:
+ case AutomationMsg_RequestData::ID:
+ case AutomationMsg_RequestEnd::ID: {
+ void* iter = NULL;
+ int tab = 0;
+ if (message.ReadInt(&iter, &tab) &&
+ message.ReadInt(&iter, request_id)) {
+ return true;
+ }
+ break;
+ }
+ }
+
+ return false;
+}
+
+void URLRequestAutomationJob::OnMessage(const IPC::Message& message) {
+ if (!request_) {
+ NOTREACHED() << __FUNCTION__
+ << ": Unexpected request received for job:"
+ << id();
+ return;
+ }
+
+ IPC_BEGIN_MESSAGE_MAP(URLRequestAutomationJob, message)
+ IPC_MESSAGE_HANDLER(AutomationMsg_RequestStarted, OnRequestStarted)
+ IPC_MESSAGE_HANDLER(AutomationMsg_RequestData, OnDataAvailable)
+ IPC_MESSAGE_HANDLER(AutomationMsg_RequestEnd, OnRequestEnd)
+ IPC_END_MESSAGE_MAP()
+}
+
+void URLRequestAutomationJob::OnRequestStarted(int tab, int id,
+ const IPC::AutomationURLResponse& response) {
+ DLOG(INFO) << "URLRequestAutomationJob: " <<
+ request_->url().spec() << " - response started.";
+ set_expected_content_size(response.content_length);
+ mime_type_ = response.mime_type;
+
+ redirect_url_ = response.redirect_url;
+ redirect_status_ = response.redirect_status;
+ DCHECK(redirect_status_ == 0 || redirect_status_ == 200 ||
+ (redirect_status_ >= 300 && redirect_status_ < 400));
+
+ if (!response.headers.empty()) {
+ headers_ = new net::HttpResponseHeaders(
+ net::HttpUtil::AssembleRawHeaders(response.headers.data(),
+ response.headers.size()));
+ }
+ NotifyHeadersComplete();
+}
+
+void URLRequestAutomationJob::OnDataAvailable(
+ int tab, int id, const std::string& bytes) {
+ DLOG(INFO) << "URLRequestAutomationJob: " <<
+ request_->url().spec() << " - data available, Size: " << bytes.size();
+ DCHECK(!bytes.empty());
+
+ // The request completed, and we have all the data.
+ // Clear any IO pending status.
+ SetStatus(URLRequestStatus());
+
+ if (pending_buf_ && pending_buf_->data()) {
+ DCHECK_GE(pending_buf_size_, bytes.size());
+ const int bytes_to_copy = std::min(bytes.size(), pending_buf_size_);
+ memcpy(pending_buf_->data(), &bytes[0], bytes_to_copy);
+
+ pending_buf_ = NULL;
+ pending_buf_size_ = 0;
+
+ NotifyReadComplete(bytes_to_copy);
+ } else {
+ NOTREACHED() << "Received unexpected data of length:" << bytes.size();
+ }
+}
+
+void URLRequestAutomationJob::OnRequestEnd(
+ int tab, int id, const URLRequestStatus& status) {
+#ifndef NDEBUG
+ std::string url;
+ if (request_)
+ url = request_->url().spec();
+ DLOG(INFO) << "URLRequestAutomationJob: "
+ << url << " - request end. Status: " << status.status();
+#endif
+
+ // TODO(tommi): When we hit certificate errors, notify the delegate via
+ // OnSSLCertificateError(). Right now we don't have the certificate
+ // so we don't. We could possibly call OnSSLCertificateError with a NULL
+ // certificate, but I'm not sure if all implementations expect it.
+ // if (status.status() == URLRequestStatus::FAILED &&
+ // net::IsCertificateError(status.os_error()) && request_->delegate()) {
+ // request_->delegate()->OnSSLCertificateError(request_, status.os_error());
+ // }
+
+ DisconnectFromMessageFilter();
+ // NotifyDone may have been called on the job if the original request was
+ // redirected.
+ if (!is_done()) {
+ // We can complete the job if we have a valid response or a pending read.
+ // An end request can be received in the following cases
+ // 1. We failed to connect to the server, in which case we did not receive
+ // a valid response.
+ // 2. In response to a read request.
+ if (!has_response_started() || pending_buf_) {
+ NotifyDone(status);
+ } else {
+ // Wait for the http stack to issue a Read request where we will notify
+ // that the job has completed.
+ request_status_ = status;
+ return;
+ }
+ }
+
+ // Reset any pending reads.
+ if (pending_buf_) {
+ pending_buf_ = NULL;
+ pending_buf_size_ = 0;
+ NotifyReadComplete(0);
+ }
+}
+
+void URLRequestAutomationJob::Cleanup() {
+ headers_ = NULL;
+ mime_type_.erase();
+
+ id_ = 0;
+ tab_ = 0;
+
+ DCHECK(message_filter_ == NULL);
+ DisconnectFromMessageFilter();
+
+ pending_buf_ = NULL;
+ pending_buf_size_ = 0;
+}
+
+void URLRequestAutomationJob::StartAsync() {
+ DLOG(INFO) << "URLRequestAutomationJob: start request: " <<
+ (request_ ? request_->url().spec() : "NULL request");
+
+ // If the job is cancelled before we got a chance to start it
+ // we have nothing much to do here.
+ if (is_done())
+ return;
+
+ // We should not receive a Start request for a pending job.
+ DCHECK(!is_pending());
+
+ if (!request_) {
+ NotifyStartError(URLRequestStatus(URLRequestStatus::FAILED,
+ net::ERR_FAILED));
+ return;
+ }
+
+ // Register this request with automation message filter.
+ message_filter_->RegisterRequest(this);
+
+ // Strip unwanted headers.
+ net::HttpRequestHeaders new_request_headers;
+ new_request_headers.MergeFrom(request_->extra_request_headers());
+ for (size_t i = 0; i < arraysize(kFilteredHeaderStrings); ++i)
+ new_request_headers.RemoveHeader(kFilteredHeaderStrings[i]);
+
+ if (request_->context()) {
+ // Only add default Accept-Language and Accept-Charset if the request
+ // didn't have them specified.
+ if (!new_request_headers.HasHeader(
+ net::HttpRequestHeaders::kAcceptLanguage) &&
+ !request_->context()->accept_language().empty()) {
+ new_request_headers.SetHeader(net::HttpRequestHeaders::kAcceptLanguage,
+ request_->context()->accept_language());
+ }
+ if (!new_request_headers.HasHeader(
+ net::HttpRequestHeaders::kAcceptCharset) &&
+ !request_->context()->accept_charset().empty()) {
+ new_request_headers.SetHeader(net::HttpRequestHeaders::kAcceptCharset,
+ request_->context()->accept_charset());
+ }
+ }
+
+ // Ensure that we do not send username and password fields in the referrer.
+ GURL referrer(request_->GetSanitizedReferrer());
+
+ // The referrer header must be suppressed if the preceding URL was
+ // a secure one and the new one is not.
+ if (referrer.SchemeIsSecure() && !request_->url().SchemeIsSecure()) {
+ DLOG(INFO) <<
+ "Suppressing referrer header since going from secure to non-secure";
+ referrer = GURL();
+ }
+
+ // Ask automation to start this request.
+ IPC::AutomationURLRequest automation_request = {
+ request_->url().spec(),
+ request_->method(),
+ referrer.spec(),
+ new_request_headers.ToString(),
+ request_->get_upload()
+ };
+
+ DCHECK(message_filter_);
+ message_filter_->Send(new AutomationMsg_RequestStart(0, tab_, id_,
+ automation_request));
+}
+
+void URLRequestAutomationJob::DisconnectFromMessageFilter() {
+ if (message_filter_) {
+ message_filter_->UnRegisterRequest(this);
+ message_filter_ = NULL;
+ }
+}
+
+void URLRequestAutomationJob::StartPendingJob(
+ int new_tab_handle,
+ AutomationResourceMessageFilter* new_filter) {
+ DCHECK(new_filter != NULL);
+ tab_ = new_tab_handle;
+ message_filter_ = new_filter;
+ is_pending_ = false;
+ Start();
+}
+
+void URLRequestAutomationJob::NotifyJobCompletionTask() {
+ if (!is_done()) {
+ NotifyDone(request_status_);
+ }
+ // Reset any pending reads.
+ if (pending_buf_) {
+ pending_buf_ = NULL;
+ pending_buf_size_ = 0;
+ NotifyReadComplete(0);
+ }
+}
diff --git a/chrome/browser/automation/url_request_automation_job.h b/chrome/browser/automation/url_request_automation_job.h
new file mode 100644
index 0000000..b8c7442
--- /dev/null
+++ b/chrome/browser/automation/url_request_automation_job.h
@@ -0,0 +1,127 @@
+// Copyright (c) 2006-2009 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.
+// This class simulates what wininet does when a dns lookup fails.
+
+#ifndef CHROME_BROWSER_AUTOMATION_URL_REQUEST_AUTOMATION_JOB_H_
+#define CHROME_BROWSER_AUTOMATION_URL_REQUEST_AUTOMATION_JOB_H_
+
+#include <vector>
+
+#include "chrome/browser/automation/automation_resource_message_filter.h"
+#include "chrome/common/ref_counted_util.h"
+#include "net/http/http_response_headers.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_job.h"
+
+class AutomationResourceMessageFilter;
+
+namespace IPC {
+class Message;
+struct AutomationURLResponse;
+};
+
+// URLRequestJob implementation that loads the resources using
+// automation.
+class URLRequestAutomationJob : public URLRequestJob {
+ public:
+ URLRequestAutomationJob(URLRequest* request, int tab, int request_id,
+ AutomationResourceMessageFilter* filter,
+ bool is_pending);
+
+ // Register our factory for HTTP/HTTPs requests.
+ static bool EnsureProtocolFactoryRegistered();
+
+ static URLRequest::ProtocolFactory Factory;
+
+ // URLRequestJob methods.
+ virtual void Start();
+ virtual void Kill();
+ virtual bool GetMimeType(std::string* mime_type) const;
+ virtual bool GetCharset(std::string* charset);
+ virtual void GetResponseInfo(net::HttpResponseInfo* info);
+ virtual int GetResponseCode() const;
+ virtual bool IsRedirectResponse(GURL* location, int* http_status_code);
+
+ // Peek and process automation messages for URL requests.
+ static bool MayFilterMessage(const IPC::Message& message, int* request_id);
+ void OnMessage(const IPC::Message& message);
+
+ int id() const {
+ return id_;
+ }
+
+ int request_id() const {
+ return request_id_;
+ }
+
+ bool is_pending() const {
+ return is_pending_;
+ }
+
+ AutomationResourceMessageFilter* message_filter() const {
+ return message_filter_;
+ }
+
+ // Resumes a job, which was waiting for the external host to connect to the
+ // automation channel. This is to ensure that this request gets routed to the
+ // external host.
+ void StartPendingJob(int new_tab_handle,
+ AutomationResourceMessageFilter* new_filter);
+
+ protected:
+ // Protected URLRequestJob override.
+ virtual bool ReadRawData(net::IOBuffer* buf, int buf_size, int* bytes_read);
+
+ void StartAsync();
+ void Cleanup();
+ void DisconnectFromMessageFilter();
+
+ // IPC message handlers.
+ void OnRequestStarted(int tab, int id,
+ const IPC::AutomationURLResponse& response);
+ void OnDataAvailable(int tab, int id, const std::string& bytes);
+ void OnRequestEnd(int tab, int id, const URLRequestStatus& status);
+
+ private:
+ virtual ~URLRequestAutomationJob();
+
+ // Task which is scheduled in the URLRequestAutomationJob::ReadRawData
+ // function, which completes the job.
+ void NotifyJobCompletionTask();
+
+ int id_;
+ int tab_;
+ scoped_refptr<AutomationResourceMessageFilter> message_filter_;
+
+ scoped_refptr<net::IOBuffer> pending_buf_;
+ size_t pending_buf_size_;
+
+ std::string mime_type_;
+ scoped_refptr<net::HttpResponseHeaders> headers_;
+ std::string redirect_url_;
+ int redirect_status_;
+ int request_id_;
+
+ static int instance_count_;
+
+ static bool is_protocol_factory_registered_;
+ // The previous HTTP/HTTPs protocol factories. We pass unhandled
+ // requests off to these factories
+ static URLRequest::ProtocolFactory* old_http_factory_;
+ static URLRequest::ProtocolFactory* old_https_factory_;
+
+ // Set to true if the job is waiting for the external host to connect to the
+ // automation channel, which will be used for routing the network requests to
+ // the host.
+ bool is_pending_;
+
+ // Contains the request status code, which is eventually passed to the http
+ // stack when we receive a Read request for a completed job.
+ URLRequestStatus request_status_;
+
+ DISALLOW_COPY_AND_ASSIGN(URLRequestAutomationJob);
+};
+
+#endif // CHROME_BROWSER_AUTOMATION_URL_REQUEST_AUTOMATION_JOB_H_
+