summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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_provider.cc245
-rw-r--r--chrome/browser/automation/automation_provider.h52
-rw-r--r--chrome/browser/automation/automation_provider_observers.cc165
-rw-r--r--chrome/browser/automation/automation_provider_observers.h96
-rw-r--r--chrome/browser/automation/automation_resource_tracker.h10
-rw-r--r--chrome/browser/gtk/browser_actions_toolbar_gtk.cc33
-rw-r--r--chrome/browser/gtk/browser_actions_toolbar_gtk.h2
-rw-r--r--chrome/chrome.gyp3
-rw-r--r--chrome/chrome_browser.gypi2
-rw-r--r--chrome/chrome_tests.gypi3
-rw-r--r--chrome/test/automation/automation_constants.h10
-rw-r--r--chrome/test/automation/automation_messages.h37
-rw-r--r--chrome/test/automation/automation_messages_internal.h53
-rw-r--r--chrome/test/automation/automation_proxy.cc30
-rw-r--r--chrome/test/automation/automation_proxy.h13
-rw-r--r--chrome/test/automation/extension_proxy.cc141
-rw-r--r--chrome/test/automation/extension_proxy.h88
-rw-r--r--chrome/test/automation/extension_proxy_uitest.cc178
-rw-r--r--chrome/test/pyautolib/pyautolib.cc7
21 files changed, 1201 insertions, 48 deletions
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_provider.cc b/chrome/browser/automation/automation_provider.cc
index 67e73f0..addc2fd 100644
--- a/chrome/browser/automation/automation_provider.cc
+++ b/chrome/browser/automation/automation_provider.cc
@@ -25,6 +25,7 @@
#include "chrome/browser/app_modal_dialog.h"
#include "chrome/browser/app_modal_dialog_queue.h"
#include "chrome/browser/automation/automation_extension_function.h"
+#include "chrome/browser/automation/automation_extension_tracker.h"
#include "chrome/browser/automation/automation_provider_list.h"
#include "chrome/browser/automation/automation_provider_observers.h"
#include "chrome/browser/automation/extension_automation_constants.h"
@@ -41,8 +42,12 @@
#include "chrome/browser/download/download_manager.h"
#include "chrome/browser/download/download_shelf.h"
#include "chrome/browser/extensions/crx_installer.h"
+#include "chrome/browser/extensions/extension_browser_event_router.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"
@@ -63,6 +68,7 @@
#include "chrome/browser/tab_contents/tab_contents_view.h"
#include "chrome/common/automation_constants.h"
#include "chrome/common/chrome_paths.h"
+#include "chrome/common/extensions/extension.h"
#include "chrome/common/json_value_serializer.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/platform_util.h"
@@ -144,6 +150,7 @@ AutomationProvider::AutomationProvider(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(
@@ -151,6 +158,8 @@ AutomationProvider::AutomationProvider(Profile* profile)
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();
}
@@ -274,6 +283,34 @@ int AutomationProvider::GetIndexForNavigationController(
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)
@@ -491,6 +528,24 @@ void AutomationProvider::OnMessageReceived(const IPC::Message& message) {
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)
@@ -2607,9 +2662,9 @@ void AutomationProvider::InstallExtension(const FilePath& crx_path,
ExtensionsService* service = profile_->GetExtensionsService();
if (service) {
// The observer will delete itself when done.
- new ExtensionNotificationObserver(this,
- AutomationMsg_InstallExtension::ID,
- reply_message);
+ new ExtensionInstallNotificationObserver(this,
+ AutomationMsg_InstallExtension::ID,
+ reply_message);
const FilePath& install_dir = service->install_directory();
scoped_refptr<CrxInstaller> installer(
@@ -2630,9 +2685,10 @@ void AutomationProvider::LoadExpandedExtension(
IPC::Message* reply_message) {
if (profile_->GetExtensionsService()) {
// The observer will delete itself when done.
- new ExtensionNotificationObserver(this,
- AutomationMsg_LoadExpandedExtension::ID,
- reply_message);
+ new ExtensionInstallNotificationObserver(
+ this,
+ AutomationMsg_LoadExpandedExtension::ID,
+ reply_message);
profile_->GetExtensionsService()->LoadExtension(extension_dir);
} else {
@@ -2659,6 +2715,183 @@ void AutomationProvider::GetEnabledExtensions(
}
}
+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, 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);
+
+ scoped_refptr<CrxInstaller> installer(
+ new CrxInstaller(service->install_directory(),
+ service,
+ NULL)); // silent install, no UI
+ 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);
diff --git a/chrome/browser/automation/automation_provider.h b/chrome/browser/automation/automation_provider.h
index 929bc20..f3d03cb6 100644
--- a/chrome/browser/automation/automation_provider.h
+++ b/chrome/browser/automation/automation_provider.h
@@ -43,7 +43,10 @@ struct Reposition_Params;
struct ExternalTabSettings;
}
+class AutomationExtensionTracker;
+class Extension;
class ExtensionPortContainer;
+class ExtensionTestResultNotificationObserver;
class ExternalTabContainer;
class LoginHandler;
class MetricEventDurationObserver;
@@ -130,6 +133,11 @@ class AutomationProvider : public base::RefCounted<AutomationProvider>,
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);
+
// Adds the external tab passed in to the tab tracker.
bool AddExternalTab(ExternalTabContainer* external_tab);
@@ -381,6 +389,35 @@ class AutomationProvider : public base::RefCounted<AutomationProvider>,
void GetEnabledExtensions(std::vector<FilePath>* result);
+ void WaitForExtensionTestResult(IPC::Message* reply_message);
+
+ void InstallExtensionAndGetHandle(const FilePath& crx_path,
+ 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);
+
void NavigateInExternalTab(
int handle, const GURL& url, const GURL& referrer,
AutomationMsg_NavigationResponseValues* status);
@@ -595,6 +632,18 @@ class AutomationProvider : public base::RefCounted<AutomationProvider>,
// 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,
@@ -621,8 +670,11 @@ class AutomationProvider : public base::RefCounted<AutomationProvider>,
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_;
diff --git a/chrome/browser/automation/automation_provider_observers.cc b/chrome/browser/automation/automation_provider_observers.cc
index da7537d..24b770a 100644
--- a/chrome/browser/automation/automation_provider_observers.cc
+++ b/chrome/browser/automation/automation_provider_observers.cc
@@ -5,15 +5,21 @@
#include "chrome/browser/automation/automation_provider_observers.h"
#include "base/basictypes.h"
+#include "base/string_util.h"
#include "chrome/app/chrome_dll_resource.h"
#include "chrome/browser/automation/automation_provider.h"
#include "chrome/browser/bookmarks/bookmark_model.h"
#include "chrome/browser/dom_operation_notification_details.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/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_WIN)
#include "chrome/browser/printing/print_job.h"
@@ -282,7 +288,16 @@ void TabClosedNotificationObserver::set_for_browser_command(
for_browser_command_ = for_browser_command;
}
-ExtensionNotificationObserver::ExtensionNotificationObserver(
+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),
@@ -297,10 +312,10 @@ ExtensionNotificationObserver::ExtensionNotificationObserver(
NotificationService::AllSources());
}
-ExtensionNotificationObserver::~ExtensionNotificationObserver() {
+ExtensionInstallNotificationObserver::~ExtensionInstallNotificationObserver() {
}
-void ExtensionNotificationObserver::Observe(
+void ExtensionInstallNotificationObserver::Observe(
NotificationType type, const NotificationSource& source,
const NotificationDetails& details) {
switch (type.value) {
@@ -322,7 +337,7 @@ void ExtensionNotificationObserver::Observe(
delete this;
}
-void ExtensionNotificationObserver::SendResponse(
+void ExtensionInstallNotificationObserver::SendResponse(
AutomationMsg_ExtensionResponseValues response) {
if (reply_message_ != NULL) {
switch (id_) {
@@ -344,6 +359,148 @@ void ExtensionNotificationObserver::SendResponse(
}
}
+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_OVERINSTALL_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:
+ case NotificationType::EXTENSION_OVERINSTALL_ERROR:
+ 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),
diff --git a/chrome/browser/automation/automation_provider_observers.h b/chrome/browser/automation/automation_provider_observers.h
index 892be51..6bed0f6 100644
--- a/chrome/browser/automation/automation_provider_observers.h
+++ b/chrome/browser/automation/automation_provider_observers.h
@@ -5,6 +5,7 @@
#ifndef CHROME_BROWSER_AUTOMATION_AUTOMATION_PROVIDER_OBSERVERS_H_
#define CHROME_BROWSER_AUTOMATION_AUTOMATION_PROVIDER_OBSERVERS_H_
+#include <deque>
#include <map>
#include <set>
@@ -17,6 +18,8 @@
class AutomationProvider;
class Browser;
+class Extension;
+class ExtensionProcessManager;
class NavigationController;
class TabContents;
@@ -166,12 +169,14 @@ class TabClosedNotificationObserver : public TabStripNotificationObserver {
DISALLOW_COPY_AND_ASSIGN(TabClosedNotificationObserver);
};
-class ExtensionNotificationObserver : public NotificationObserver {
+// 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:
- ExtensionNotificationObserver(AutomationProvider* automation,
- int id,
- IPC::Message* reply_message);
- virtual ~ExtensionNotificationObserver();
+ ExtensionInstallNotificationObserver(AutomationProvider* automation,
+ int id,
+ IPC::Message* reply_message);
+ ~ExtensionInstallNotificationObserver();
// Implementation of NotificationObserver.
virtual void Observe(NotificationType type,
@@ -179,6 +184,7 @@ class ExtensionNotificationObserver : public NotificationObserver {
const NotificationDetails& details);
private:
+ // Send |response| back to the provider's client.
void SendResponse(AutomationMsg_ExtensionResponseValues response);
NotificationRegistrar registrar_;
@@ -186,7 +192,85 @@ class ExtensionNotificationObserver : public NotificationObserver {
int id_;
IPC::Message* reply_message_;
- DISALLOW_COPY_AND_ASSIGN(ExtensionNotificationObserver);
+ 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 {
diff --git a/chrome/browser/automation/automation_resource_tracker.h b/chrome/browser/automation/automation_resource_tracker.h
index 8500aea..9f6df8b 100644
--- a/chrome/browser/automation/automation_resource_tracker.h
+++ b/chrome/browser/automation/automation_resource_tracker.h
@@ -129,10 +129,18 @@ class AutomationResourceTracker : public NotificationObserver,
T resource =
Source<typename AutomationResourceTraits<T>::ValueType>(source).ptr();
- HandleCloseNotification(resource);
+ 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:
diff --git a/chrome/browser/gtk/browser_actions_toolbar_gtk.cc b/chrome/browser/gtk/browser_actions_toolbar_gtk.cc
index 7f3ec625..1d25c2a 100644
--- a/chrome/browser/gtk/browser_actions_toolbar_gtk.cc
+++ b/chrome/browser/gtk/browser_actions_toolbar_gtk.cc
@@ -359,7 +359,7 @@ BrowserActionsToolbarGtk::BrowserActionsToolbarGtk(Browser* browser)
G_CALLBACK(OnOverflowButtonPressThunk), this);
gtk_box_pack_start(GTK_BOX(hbox_.get()), gripper, FALSE, FALSE, 0);
- gtk_box_pack_start(GTK_BOX(hbox_.get()), button_hbox_, TRUE, TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(hbox_.get()), button_hbox_.get(), TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(hbox_.get()), overflow_button_.widget(),
FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(hbox_.get()), separator_, FALSE, FALSE, 0);
@@ -378,7 +378,7 @@ BrowserActionsToolbarGtk::BrowserActionsToolbarGtk(Browser* browser)
g_signal_connect(widget(), "hierarchy-changed",
G_CALLBACK(OnHierarchyChangedThunk), this);
- ViewIDUtil::SetID(button_hbox_, VIEW_ID_BROWSER_ACTION_TOOLBAR);
+ ViewIDUtil::SetID(button_hbox_.get(), VIEW_ID_BROWSER_ACTION_TOOLBAR);
}
BrowserActionsToolbarGtk::~BrowserActionsToolbarGtk() {
@@ -390,6 +390,7 @@ BrowserActionsToolbarGtk::~BrowserActionsToolbarGtk() {
if (model_)
model_->RemoveObserver(this);
+ button_hbox_.Destroy();
hbox_.Destroy();
}
@@ -410,10 +411,10 @@ void BrowserActionsToolbarGtk::Update() {
void BrowserActionsToolbarGtk::SetupDrags() {
GtkTargetEntry drag_target = GetDragTargetEntry();
- gtk_drag_dest_set(button_hbox_, GTK_DEST_DEFAULT_DROP, &drag_target, 1,
+ gtk_drag_dest_set(button_hbox_.get(), GTK_DEST_DEFAULT_DROP, &drag_target, 1,
GDK_ACTION_MOVE);
- g_signal_connect(button_hbox_, "drag-motion",
+ g_signal_connect(button_hbox_.get(), "drag-motion",
G_CALLBACK(OnDragMotionThunk), this);
}
@@ -448,8 +449,8 @@ void BrowserActionsToolbarGtk::CreateButtonForExtension(Extension* extension,
linked_ptr<BrowserActionButton> button(
new BrowserActionButton(this, extension));
gtk_chrome_shrinkable_hbox_pack_start(
- GTK_CHROME_SHRINKABLE_HBOX(button_hbox_), button->widget(), 0);
- gtk_box_reorder_child(GTK_BOX(button_hbox_), button->widget(), index);
+ GTK_CHROME_SHRINKABLE_HBOX(button_hbox_.get()), button->widget(), 0);
+ gtk_box_reorder_child(GTK_BOX(button_hbox_.get()), button->widget(), index);
gtk_widget_show(button->widget());
extension_button_map_[extension->id()] = button;
@@ -529,7 +530,7 @@ void BrowserActionsToolbarGtk::BrowserActionRemoved(Extension* extension) {
if (drag_button_ != NULL) {
// Break the current drag.
- gtk_grab_remove(button_hbox_);
+ gtk_grab_remove(button_hbox_.get());
}
RemoveButtonForExtension(extension);
@@ -558,7 +559,7 @@ void BrowserActionsToolbarGtk::BrowserActionMoved(Extension* extension,
if (profile_->IsOffTheRecord())
index = model_->OriginalIndexToIncognito(index);
- gtk_box_reorder_child(GTK_BOX(button_hbox_), button->widget(), index);
+ gtk_box_reorder_child(GTK_BOX(button_hbox_.get()), button->widget(), index);
}
void BrowserActionsToolbarGtk::ModelLoaded() {
@@ -568,14 +569,14 @@ void BrowserActionsToolbarGtk::ModelLoaded() {
void BrowserActionsToolbarGtk::AnimationProgressed(const Animation* animation) {
int width = start_width_ + (desired_width_ - start_width_) *
animation->GetCurrentValue();
- gtk_widget_set_size_request(button_hbox_, width, -1);
+ gtk_widget_set_size_request(button_hbox_.get(), width, -1);
if (width == desired_width_)
resize_animation_.Reset();
}
void BrowserActionsToolbarGtk::AnimationEnded(const Animation* animation) {
- gtk_widget_set_size_request(button_hbox_, desired_width_, -1);
+ gtk_widget_set_size_request(button_hbox_.get(), desired_width_, -1);
}
void BrowserActionsToolbarGtk::ExecuteCommandById(int command_id) {
@@ -619,11 +620,11 @@ void BrowserActionsToolbarGtk::SetButtonHBoxWidth(int new_width) {
gint max_width = WidthForIconCount(model_->size());
new_width = std::min(max_width, new_width);
new_width = std::max(new_width, 0);
- gtk_widget_set_size_request(button_hbox_, new_width, -1);
+ gtk_widget_set_size_request(button_hbox_.get(), new_width, -1);
int showing_icon_count =
gtk_chrome_shrinkable_hbox_get_visible_child_count(
- GTK_CHROME_SHRINKABLE_HBOX(button_hbox_));
+ GTK_CHROME_SHRINKABLE_HBOX(button_hbox_.get()));
if (model_->size() > static_cast<size_t>(showing_icon_count)) {
if (!GTK_WIDGET_VISIBLE(overflow_button_.widget())) {
@@ -633,7 +634,7 @@ void BrowserActionsToolbarGtk::SetButtonHBoxWidth(int new_width) {
gtk_widget_size_request(overflow_button_.widget(), &req);
new_width -= req.width;
new_width = std::max(new_width, 0);
- gtk_widget_set_size_request(button_hbox_, new_width, -1);
+ gtk_widget_set_size_request(button_hbox_.get(), new_width, -1);
gtk_widget_show(overflow_button_.widget());
}
@@ -654,7 +655,7 @@ gboolean BrowserActionsToolbarGtk::OnDragMotion(GtkWidget* widget,
// We will go ahead and reorder the child in order to provide visual feedback
// to the user. We don't inform the model that it has moved until the drag
// ends.
- gtk_box_reorder_child(GTK_BOX(button_hbox_), drag_button_->widget(),
+ gtk_box_reorder_child(GTK_BOX(button_hbox_.get()), drag_button_->widget(),
drop_index_);
gdk_drag_status(drag_context, GDK_ACTION_MOVE, time);
@@ -765,7 +766,7 @@ gboolean BrowserActionsToolbarGtk::OnGripperButtonRelease(
// the perfect size to fit the buttons.
int visible_icon_count =
gtk_chrome_shrinkable_hbox_get_visible_child_count(
- GTK_CHROME_SHRINKABLE_HBOX(button_hbox_));
+ GTK_CHROME_SHRINKABLE_HBOX(button_hbox_.get()));
AnimateToShowNIcons(visible_icon_count);
model_->SetVisibleIconCount(visible_icon_count);
@@ -785,7 +786,7 @@ gboolean BrowserActionsToolbarGtk::OnOverflowButtonPress(
int visible_icon_count =
gtk_chrome_shrinkable_hbox_get_visible_child_count(
- GTK_CHROME_SHRINKABLE_HBOX(button_hbox_));
+ GTK_CHROME_SHRINKABLE_HBOX(button_hbox_.get()));
for (int i = visible_icon_count; i < static_cast<int>(model_->size()); ++i) {
Extension* extension = model_->GetExtensionByIndex(i);
BrowserActionButton* button = extension_button_map_[extension->id()].get();
diff --git a/chrome/browser/gtk/browser_actions_toolbar_gtk.h b/chrome/browser/gtk/browser_actions_toolbar_gtk.h
index 955f55e..0f8376b 100644
--- a/chrome/browser/gtk/browser_actions_toolbar_gtk.h
+++ b/chrome/browser/gtk/browser_actions_toolbar_gtk.h
@@ -152,7 +152,7 @@ class BrowserActionsToolbarGtk : public ExtensionToolbarModel::Observer,
OwnedWidgetGtk hbox_;
// Contains the browser action buttons.
- GtkWidget* button_hbox_;
+ OwnedWidgetGtk button_hbox_;
OverflowButton overflow_button_;
scoped_ptr<MenuGtk> overflow_menu_;
diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp
index 2fb4b38..fef2da9 100644
--- a/chrome/chrome.gyp
+++ b/chrome/chrome.gyp
@@ -1454,6 +1454,7 @@
'dependencies': [
'theme_resources',
'../skia/skia.gyp:skia',
+ '../testing/gtest.gyp:gtest',
],
'include_dirs': [
'..',
@@ -1470,6 +1471,8 @@
'test/automation/automation_proxy.h',
'test/automation/browser_proxy.cc',
'test/automation/browser_proxy.h',
+ 'test/automation/extension_proxy.cc',
+ 'test/automation/extension_proxy.h',
'test/automation/tab_proxy.cc',
'test/automation/tab_proxy.h',
'test/automation/window_proxy.cc',
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 31a0109..70c3868 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -175,6 +175,8 @@
'browser/automation/extension_automation_constants.cc',
'browser/automation/automation_extension_function.h',
'browser/automation/automation_extension_function.cc',
+ 'browser/automation/automation_extension_tracker.cc',
+ 'browser/automation/automation_extension_tracker.h',
'browser/automation/automation_profile_impl.cc',
'browser/automation/automation_profile_impl.h',
'browser/automation/automation_provider_chromeos.cc',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index cb73e8e..cc21528 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -102,6 +102,8 @@
'test/automation/browser_proxy.h',
'test/automation/dom_element_proxy.cc',
'test/automation/dom_element_proxy.h',
+ 'test/automation/extension_proxy.cc',
+ 'test/automation/extension_proxy.h',
'test/automation/javascript_execution_controller.cc',
'test/automation/javascript_execution_controller.h',
'test/automation/tab_proxy.cc',
@@ -305,6 +307,7 @@
'browser/views/find_bar_host_uitest.cc',
'common/logging_chrome_uitest.cc',
'test/automation/automation_proxy_uitest.cc',
+ 'test/automation/extension_proxy_uitest.cc',
'test/automated_ui_tests/automated_ui_test_test.cc',
'test/chrome_process_util_uitest.cc',
'test/ui/dom_checker_uitest.cc',
diff --git a/chrome/test/automation/automation_constants.h b/chrome/test/automation/automation_constants.h
index cf54a39..f5e3b6f 100644
--- a/chrome/test/automation/automation_constants.h
+++ b/chrome/test/automation/automation_constants.h
@@ -31,4 +31,14 @@ enum AutomationMsg_ExtensionResponseValues {
AUTOMATION_MSG_EXTENSION_ALREADY_INSTALLED,
};
+// Used in the AutomationMsg_GetExtensionProperty to identify which extension
+// property should be retrieved, instead of having separate messages for each
+// property.
+enum AutomationMsg_ExtensionProperty {
+ AUTOMATION_MSG_EXTENSION_ID = 0,
+ AUTOMATION_MSG_EXTENSION_NAME,
+ AUTOMATION_MSG_EXTENSION_VERSION,
+ AUTOMATION_MSG_EXTENSION_BROWSER_ACTION_INDEX,
+};
+
#endif // CHROME_TEST_AUTOMATION_AUTOMATION_CONSTANTS_H_
diff --git a/chrome/test/automation/automation_messages.h b/chrome/test/automation/automation_messages.h
index 22ec4d6..ddd1c83 100644
--- a/chrome/test/automation/automation_messages.h
+++ b/chrome/test/automation/automation_messages.h
@@ -127,6 +127,43 @@ struct ParamTraits<AutomationMsg_ExtensionResponseValues> {
};
template <>
+struct ParamTraits<AutomationMsg_ExtensionProperty> {
+ typedef AutomationMsg_ExtensionProperty param_type;
+ static void Write(Message* m, const param_type& p) {
+ m->WriteInt(p);
+ }
+ static bool Read(const Message* m, void** iter, param_type* p) {
+ int type;
+ if (!m->ReadInt(iter, &type))
+ return false;
+ *p = static_cast<AutomationMsg_ExtensionProperty>(type);
+ return true;
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ std::wstring control;
+ switch (p) {
+ case AUTOMATION_MSG_EXTENSION_ID:
+ control = L"AUTOMATION_MSG_EXTENSION_ID";
+ break;
+ case AUTOMATION_MSG_EXTENSION_NAME:
+ control = L"AUTOMATION_MSG_EXTENSION_NAME";
+ break;
+ case AUTOMATION_MSG_EXTENSION_VERSION:
+ control = L"AUTOMATION_MSG_EXTENSION_VERSION";
+ break;
+ case AUTOMATION_MSG_EXTENSION_BROWSER_ACTION_INDEX:
+ control = L"AUTOMATION_MSG_EXTENSION_BROWSER_ACTION_INDEX";
+ break;
+ default:
+ control = L"UNKNOWN";
+ break;
+ }
+
+ LogParam(control, l);
+ }
+};
+
+template <>
struct ParamTraits<SecurityStyle> {
typedef SecurityStyle param_type;
static void Write(Message* m, const param_type& p) {
diff --git a/chrome/test/automation/automation_messages_internal.h b/chrome/test/automation/automation_messages_internal.h
index 74dabad..8430172 100644
--- a/chrome/test/automation/automation_messages_internal.h
+++ b/chrome/test/automation/automation_messages_internal.h
@@ -13,6 +13,7 @@
#include "base/basictypes.h"
#include "base/string16.h"
+#include "base/values.h"
#include "chrome/common/content_settings.h"
#include "chrome/common/navigation_types.h"
#include "chrome/test/automation/autocomplete_edit_proxy.h"
@@ -1321,4 +1322,56 @@ IPC_BEGIN_MESSAGES(Automation)
std::string /* JSON response */,
bool /* success */)
+ // Installs an extension from the crx file and returns its id.
+ // On error, |extension handle| will be 0.
+ IPC_SYNC_MESSAGE_ROUTED1_1(AutomationMsg_InstallExtensionAndGetHandle,
+ FilePath /* full path to crx file */,
+ int /* extension handle */)
+
+ // Waits for the next extension test result. Sets |test result| as the
+ // received result and |message| as any accompanying message with the
+ // result, which could be the empty string.
+ IPC_SYNC_MESSAGE_ROUTED0_2(AutomationMsg_WaitForExtensionTestResult,
+ bool /* test result */,
+ std::string /* message */)
+
+ // Uninstalls an extension. On success |success| is true.
+ IPC_SYNC_MESSAGE_ROUTED1_1(AutomationMsg_UninstallExtension,
+ int /* extension handle */,
+ bool /* success */)
+
+ // Enables an extension. On success |success| is true.
+ IPC_SYNC_MESSAGE_ROUTED1_1(AutomationMsg_EnableExtension,
+ int /* extension handle */,
+ bool /* success */)
+
+ // Disables an extension. On success |success| is true.
+ IPC_SYNC_MESSAGE_ROUTED1_1(AutomationMsg_DisableExtension,
+ int /* extension handle */,
+ bool /* success */)
+
+ // Executes the action associated with the given extension. This executes
+ // the extension's page or browser action in the given browser, but does
+ // not open popups. On success |success| is true.
+ IPC_SYNC_MESSAGE_ROUTED2_1(
+ AutomationMsg_ExecuteExtensionActionInActiveTabAsync,
+ int /* extension handle */,
+ int /* browser handle */,
+ bool /* success */)
+
+ // Moves the browser action to the given index in the browser action toolbar.
+ // On success |success| is true.
+ IPC_SYNC_MESSAGE_ROUTED2_1(AutomationMsg_MoveExtensionBrowserAction,
+ int /* extension handle */,
+ int /* index */,
+ bool /* success */)
+
+ // Gets an extension property |property type|. On success |success| is true,
+ // and |property value| is set.
+ IPC_SYNC_MESSAGE_ROUTED2_2(AutomationMsg_GetExtensionProperty,
+ int /* extension handle */,
+ AutomationMsg_ExtensionProperty /* property type */,
+ bool /* success */,
+ std::string /* property value */)
+
IPC_END_MESSAGES(Automation)
diff --git a/chrome/test/automation/automation_proxy.cc b/chrome/test/automation/automation_proxy.cc
index 46fcc3b..04bc7ea 100644
--- a/chrome/test/automation/automation_proxy.cc
+++ b/chrome/test/automation/automation_proxy.cc
@@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include <sstream>
-
#include "chrome/test/automation/automation_proxy.h"
+#include <sstream>
+
#include "base/basictypes.h"
#include "base/file_version_info.h"
#include "base/logging.h"
@@ -16,8 +16,10 @@
#include "chrome/test/automation/automation_constants.h"
#include "chrome/test/automation/automation_messages.h"
#include "chrome/test/automation/browser_proxy.h"
+#include "chrome/test/automation/extension_proxy.h"
#include "chrome/test/automation/tab_proxy.h"
#include "chrome/test/automation/window_proxy.h"
+#include <gtest/gtest.h>
#include "ipc/ipc_descriptors.h"
#if defined(OS_WIN)
// TODO(port): Enable when dialog_delegate is ported.
@@ -230,11 +232,25 @@ bool AutomationProxy::SavePackageShouldPromptUser(bool should_prompt) {
return Send(new AutomationMsg_SavePackageShouldPromptUser(0, should_prompt));
}
-bool AutomationProxy::InstallExtension(const FilePath& crx_file) {
- AutomationMsg_ExtensionResponseValues response;
- if (!Send(new AutomationMsg_InstallExtension(0, crx_file, &response)))
- return false;
- return response == AUTOMATION_MSG_EXTENSION_INSTALL_SUCCEEDED;
+scoped_refptr<ExtensionProxy> AutomationProxy::InstallExtension(
+ const FilePath& crx_file) {
+ int handle = 0;
+ if (!Send(new AutomationMsg_InstallExtensionAndGetHandle(0, crx_file,
+ &handle)))
+ return NULL;
+
+ return ProxyObjectFromHandle<ExtensionProxy>(handle);
+}
+
+void AutomationProxy::EnsureExtensionTestResult() {
+ bool result;
+ std::string message;
+ if (!Send(new AutomationMsg_WaitForExtensionTestResult(0, &result,
+ &message))) {
+ FAIL() << "Could not send WaitForExtensionTestResult message";
+ return;
+ }
+ ASSERT_TRUE(result) << "Extension test message: " << message;
}
bool AutomationProxy::GetEnabledExtensions(
diff --git a/chrome/test/automation/automation_proxy.h b/chrome/test/automation/automation_proxy.h
index 7809ec1..f0a6cd8 100644
--- a/chrome/test/automation/automation_proxy.h
+++ b/chrome/test/automation/automation_proxy.h
@@ -26,6 +26,7 @@
#include "ipc/ipc_sync_channel.h"
class BrowserProxy;
+class ExtensionProxy;
class TabProxy;
class WindowProxy;
@@ -181,10 +182,13 @@ class AutomationProxy : public IPC::Channel::Listener,
// sent.
bool SavePackageShouldPromptUser(bool should_prompt) WARN_UNUSED_RESULT;
- // Installs the extension crx. Returns true only if extension was installed
- // and loaded successfully.
- // Note: Overinstalls will fail.
- bool InstallExtension(const FilePath& crx_file) WARN_UNUSED_RESULT;
+ // Installs the extension crx. Returns the ExtensionProxy for the
+ // installed extension, or NULL on failure.
+ // Note: Overinstalls and downgrades will return NULL.
+ scoped_refptr<ExtensionProxy> InstallExtension(const FilePath& crx_file);
+
+ // Asserts that the next extension test result is true.
+ void EnsureExtensionTestResult();
// Gets a list of all enabled extensions' base directories.
// Returns true on success.
@@ -196,6 +200,7 @@ class AutomationProxy : public IPC::Channel::Listener,
bool LoginWithUserAndPass(const std::string& username,
const std::string& password) WARN_UNUSED_RESULT;
#endif
+
// Returns the ID of the automation IPC channel, so that it can be
// passed to the app as a launch parameter.
const std::string& channel_id() const { return channel_id_; }
diff --git a/chrome/test/automation/extension_proxy.cc b/chrome/test/automation/extension_proxy.cc
new file mode 100644
index 0000000..66aa8e8
--- /dev/null
+++ b/chrome/test/automation/extension_proxy.cc
@@ -0,0 +1,141 @@
+// 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/test/automation/extension_proxy.h"
+
+#include "chrome/test/automation/automation_messages.h"
+#include "chrome/test/automation/automation_proxy.h"
+#include "chrome/test/automation/browser_proxy.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+ExtensionProxy::ExtensionProxy(AutomationMessageSender* sender,
+ AutomationHandleTracker* tracker,
+ int handle)
+ : AutomationResourceProxy(tracker, sender, handle) {
+}
+
+bool ExtensionProxy::Uninstall() {
+ if (!is_valid())
+ return false;
+
+ bool success = false;
+ if (!sender_->Send(new AutomationMsg_UninstallExtension(0, handle_,
+ &success)))
+ return false;
+ return success;
+}
+
+bool ExtensionProxy::Enable() {
+ if (!is_valid())
+ return false;
+
+ bool success = false;
+ if (!sender_->Send(new AutomationMsg_EnableExtension(0, handle_,
+ &success)))
+ return false;
+ return success;
+}
+
+bool ExtensionProxy::Disable() {
+ if (!is_valid())
+ return false;
+
+ bool success = false;
+ if (!sender_->Send(new AutomationMsg_DisableExtension(0, handle_,
+ &success)))
+ return false;
+ return success;
+}
+
+bool ExtensionProxy::ExecuteActionInActiveTabAsync(BrowserProxy* browser) {
+ if (!is_valid())
+ return false;
+
+ bool success = false;
+ if (!sender_->Send(new AutomationMsg_ExecuteExtensionActionInActiveTabAsync(
+ 0, handle_, browser->handle(), &success)))
+ return false;
+ return success;
+}
+
+bool ExtensionProxy::MoveBrowserAction(int index) {
+ if (!is_valid())
+ return false;
+ bool success = false;
+ if (!sender_->Send(
+ new AutomationMsg_MoveExtensionBrowserAction(0, handle_, index,
+ &success)))
+ return false;
+ return success;
+}
+
+bool ExtensionProxy::GetId(std::string* id) {
+ DCHECK(id);
+ return GetProperty(AUTOMATION_MSG_EXTENSION_ID, id);
+}
+
+bool ExtensionProxy::GetName(std::string* name) {
+ DCHECK(name);
+ return GetProperty(AUTOMATION_MSG_EXTENSION_NAME, name);
+}
+
+bool ExtensionProxy::GetVersion(std::string* version) {
+ DCHECK(version);
+ return GetProperty(AUTOMATION_MSG_EXTENSION_VERSION, version);
+}
+
+bool ExtensionProxy::GetBrowserActionIndex(int* index) {
+ DCHECK(index);
+ std::string index_string;
+ if (!GetProperty(AUTOMATION_MSG_EXTENSION_BROWSER_ACTION_INDEX,
+ &index_string))
+ return false;
+ // Do not modify |index| until we are sure we can get the value, just to be
+ // nice to the caller.
+ int converted_index;
+ if (!StringToInt(index_string, &converted_index)) {
+ LOG(ERROR) << "Received index string could not be converted to int: "
+ << index_string;
+ return false;
+ }
+ *index = converted_index;
+ return true;
+}
+
+void ExtensionProxy::EnsureIdMatches(const std::string& expected_id) {
+ std::string id;
+ ASSERT_TRUE(GetId(&id));
+ ASSERT_EQ(expected_id, id);
+}
+
+void ExtensionProxy::EnsureNameMatches(const std::string& expected_name) {
+ std::string name;
+ ASSERT_TRUE(GetName(&name));
+ ASSERT_EQ(expected_name, name);
+}
+
+void ExtensionProxy::EnsureVersionMatches(const std::string& expected_version) {
+ std::string version;
+ ASSERT_TRUE(GetVersion(&version));
+ ASSERT_EQ(expected_version, version);
+}
+
+void ExtensionProxy::EnsureBrowserActionIndexMatches(int expected_index) {
+ int index;
+ ASSERT_TRUE(GetBrowserActionIndex(&index));
+ ASSERT_EQ(expected_index, index);
+}
+
+bool ExtensionProxy::GetProperty(AutomationMsg_ExtensionProperty type,
+ std::string* value) {
+ DCHECK(value);
+ if (!is_valid())
+ return false;
+
+ bool success = false;
+ if (!sender_->Send(new AutomationMsg_GetExtensionProperty(0, handle_, type,
+ &success, value)))
+ return false;
+ return success;
+}
diff --git a/chrome/test/automation/extension_proxy.h b/chrome/test/automation/extension_proxy.h
new file mode 100644
index 0000000..c6ae6dda
--- /dev/null
+++ b/chrome/test/automation/extension_proxy.h
@@ -0,0 +1,88 @@
+// 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_TEST_AUTOMATION_EXTENSION_PROXY_H_
+#define CHROME_TEST_AUTOMATION_EXTENSION_PROXY_H_
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/weak_ptr.h"
+#include "chrome/test/automation/automation_constants.h"
+#include "chrome/test/automation/automation_handle_tracker.h"
+
+class AutomationMessageSender;
+class BrowserProxy;
+
+// This class presents the interface to actions that can be performed on
+// a given extension. This refers to a particular version of that extension.
+// For example, it refers to Google Translate 1.0. If the extension is
+// updated to a newer version, this proxy is invalidated.
+class ExtensionProxy : public AutomationResourceProxy {
+ public:
+ // Creates an extension proxy referring to an extension id.
+ ExtensionProxy(AutomationMessageSender* sender,
+ AutomationHandleTracker* tracker,
+ int handle);
+
+ // Uninstalls this extension. Returns true on success.
+ bool Uninstall() WARN_UNUSED_RESULT;
+
+ // Enables this extension. Returns true on success. The extension
+ // should be disabled when this is called.
+ bool Enable() WARN_UNUSED_RESULT;
+
+ // Disables this extension. Returns true on success. The extension
+ // should be enabled when this is called.
+ bool Disable() WARN_UNUSED_RESULT;
+
+ // Executes the action associated with this extension. This may be a page
+ // action or a browser action. This is similar to clicking, but does not
+ // work with popups. Also, for page actions, this will execute the action
+ // even if the page action is not shown for the active tab. Returns true on
+ // success.
+ // TODO(kkania): Add support for popups.
+ bool ExecuteActionInActiveTabAsync(BrowserProxy* browser)
+ WARN_UNUSED_RESULT;
+
+ // Moves the browser action from its current location in the browser action
+ // toolbar to a new |index|. Index should be less than the number of browser
+ // actions in the toolbar. Returns true on success.
+ bool MoveBrowserAction(int index) WARN_UNUSED_RESULT;
+
+ // Gets the id of this extension. Returns true on success.
+ bool GetId(std::string* id) WARN_UNUSED_RESULT;
+
+ // Gets the name of this extension. Returns true on success.
+ bool GetName(std::string* name) WARN_UNUSED_RESULT;
+
+ // Gets the version string of this extension. Returns true on success.
+ bool GetVersion(std::string* version) WARN_UNUSED_RESULT;
+
+ // Gets the index (zero-based) of this extension's browser action in
+ // the browser action toolbar. |index| will be set to -1 if the extension
+ // does not have a browser action in the toolbar. Returns true on success.
+ bool GetBrowserActionIndex(int* index) WARN_UNUSED_RESULT;
+
+ // Asserts that |expected_id| matches this extension's id.
+ void EnsureIdMatches(const std::string& expected_id);
+
+ // Asserts that |expected_name| matches this extension's name.
+ void EnsureNameMatches(const std::string& expected_name);
+
+ // Asserts that |expected_version| matches this extension's name.
+ void EnsureVersionMatches(const std::string& expected_version);
+
+ // Asserts that |expected_index| matches the index (zero-based) of this
+ // extension's browser action in the browser action toolbar.
+ void EnsureBrowserActionIndexMatches(int expected_index);
+
+ private:
+ // Gets the string value of the property of type |type|. Returns true on
+ // success.
+ bool GetProperty(AutomationMsg_ExtensionProperty type, std::string* value)
+ WARN_UNUSED_RESULT;
+};
+
+#endif // CHROME_TEST_AUTOMATION_EXTENSION_PROXY_H_
diff --git a/chrome/test/automation/extension_proxy_uitest.cc b/chrome/test/automation/extension_proxy_uitest.cc
new file mode 100644
index 0000000..18d385c
--- /dev/null
+++ b/chrome/test/automation/extension_proxy_uitest.cc
@@ -0,0 +1,178 @@
+// 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 "base/path_service.h"
+#include "base/ref_counted.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/test/automation/automation_proxy.h"
+#include "chrome/test/automation/browser_proxy.h"
+#include "chrome/test/automation/extension_proxy.h"
+#include "chrome/test/automation/tab_proxy.h"
+#include "chrome/test/ui/ui_test.h"
+
+namespace {
+
+// These tests are not meant to test the extension system itself, but to verify
+// the correctness of ExtensionProxy and the AutomationProvider implementation
+// behind it.
+class ExtensionProxyUITest : public UITest {
+ public:
+ ExtensionProxyUITest() {}
+
+ virtual void SetUp() {
+ UITest::SetUp();
+ simple_extension_= InstallSimpleBrowserActionExtension();
+ ASSERT_TRUE(simple_extension_.get());
+ }
+
+ protected:
+ // Installs a simple browser action extension from the sample_extensions
+ // folder. Returns an ExtensionProxy, which could be NULL.
+ scoped_refptr<ExtensionProxy> InstallSimpleBrowserActionExtension() {
+ return automation()->InstallExtension(
+ test_data_directory_.AppendASCII("extensions").AppendASCII("uitest").
+ AppendASCII("simple_browser_action.crx"));
+ }
+
+ // Installs a extension which, when clicking the browser action, renames
+ // the current tab to the tab's index. Returns an ExtensionProxy,
+ // which could be NULL.
+ scoped_refptr<ExtensionProxy> InstallRenameTabExtension() {
+ return automation()->InstallExtension(
+ test_data_directory_.AppendASCII("extensions").AppendASCII("uitest").
+ AppendASCII("rename_tab.crx"));
+ }
+
+ // The google translate extension, which is installed on test setup.
+ scoped_refptr<ExtensionProxy> simple_extension_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ExtensionProxyUITest);
+};
+
+TEST_F(ExtensionProxyUITest, NoSuchExtension) {
+ ASSERT_TRUE(simple_extension_->Uninstall());
+
+ // A proxy referring to an uninstalled extension should return false for all
+ // calls without actually invoking the extension system.
+ ASSERT_FALSE(simple_extension_->Uninstall());
+ ASSERT_FALSE(simple_extension_->Enable());
+ ASSERT_FALSE(simple_extension_->Disable());
+ scoped_refptr<BrowserProxy> browser = automation()->GetBrowserWindow(0);
+ ASSERT_TRUE(browser.get());
+ ASSERT_FALSE(simple_extension_->
+ ExecuteActionInActiveTabAsync(browser.get()));
+ ASSERT_FALSE(simple_extension_->MoveBrowserAction(0));
+ std::string name, version;
+ int index;
+ ASSERT_FALSE(simple_extension_->GetName(&name));
+ ASSERT_FALSE(simple_extension_->GetVersion(&version));
+ ASSERT_FALSE(simple_extension_->GetBrowserActionIndex(&index));
+}
+
+TEST_F(ExtensionProxyUITest, EnableDisable) {
+ ASSERT_TRUE(simple_extension_->Disable());
+ ASSERT_TRUE(simple_extension_->Enable());
+ ASSERT_TRUE(simple_extension_->Disable());
+}
+
+TEST_F(ExtensionProxyUITest, Uninstall) {
+ ASSERT_TRUE(simple_extension_->Uninstall());
+
+ // Uninstall a disabled extension.
+ simple_extension_ = InstallSimpleBrowserActionExtension();
+ ASSERT_TRUE(simple_extension_.get());
+ ASSERT_TRUE(simple_extension_->Disable());
+ ASSERT_TRUE(simple_extension_->Uninstall());
+
+ // Uninstall a just enabled extension.
+ simple_extension_ = InstallSimpleBrowserActionExtension();
+ ASSERT_TRUE(simple_extension_.get());
+ ASSERT_TRUE(simple_extension_->Disable());
+ ASSERT_TRUE(simple_extension_->Enable());
+ ASSERT_TRUE(simple_extension_->Uninstall());
+}
+
+TEST_F(ExtensionProxyUITest, ExecuteBrowserActionInActiveTabAsync) {
+ scoped_refptr<BrowserProxy> browser = automation()->GetBrowserWindow(0);
+ ASSERT_TRUE(browser.get());
+ scoped_refptr<ExtensionProxy> rename_tab_extension =
+ InstallRenameTabExtension();
+ ASSERT_TRUE(rename_tab_extension.get());
+
+ // Need to use http in order for the extension to be able to inject
+ // javascript into the page. Extensions do not have permissions for
+ // chrome://* urls.
+ FilePath path;
+ // The root directory for the http server does not matter in this case,
+ // but we have to pick something.
+ PathService::Get(chrome::DIR_TEST_DATA, &path);
+ StartHttpServerWithPort(path, L"1365");
+ GURL localhost = GURL("http://localhost:1365");
+ NavigateToURL(localhost);
+
+ // Click the browser action, which should rename the tab title to
+ // the tab's index.
+ ASSERT_TRUE(rename_tab_extension->
+ ExecuteActionInActiveTabAsync(browser.get()));
+ ASSERT_NO_FATAL_FAILURE(automation()->EnsureExtensionTestResult());
+
+ scoped_refptr<TabProxy> display_tab = browser->GetTab(0);
+ ASSERT_TRUE(display_tab);
+ std::wstring title_wstring;
+ ASSERT_TRUE(display_tab->GetTabTitle(&title_wstring));
+ ASSERT_STREQ(L"0", title_wstring.c_str());
+
+ // Click the action again right after navigating to a new page.
+ ASSERT_TRUE(browser->AppendTab(localhost));
+ display_tab = browser->GetTab(1);
+ ASSERT_TRUE(display_tab);
+ ASSERT_TRUE(rename_tab_extension->
+ ExecuteActionInActiveTabAsync(browser.get()));
+ ASSERT_NO_FATAL_FAILURE(automation()->EnsureExtensionTestResult());
+ ASSERT_TRUE(display_tab->GetTabTitle(&title_wstring));
+ ASSERT_STREQ(L"1", title_wstring.c_str());
+
+ // Do not forget to stop the server.
+ StopHttpServer();
+}
+
+TEST_F(ExtensionProxyUITest, MoveBrowserAction) {
+ scoped_refptr<ExtensionProxy> rename_tab_extension =
+ InstallRenameTabExtension();
+ ASSERT_TRUE(rename_tab_extension.get());
+ ASSERT_NO_FATAL_FAILURE(simple_extension_->
+ EnsureBrowserActionIndexMatches(0));
+ ASSERT_NO_FATAL_FAILURE(rename_tab_extension->
+ EnsureBrowserActionIndexMatches(1));
+
+ // Move google translate to the end, then beginning, and verify.
+ ASSERT_TRUE(simple_extension_->MoveBrowserAction(1));
+ ASSERT_NO_FATAL_FAILURE(simple_extension_->
+ EnsureBrowserActionIndexMatches(1));
+ ASSERT_NO_FATAL_FAILURE(rename_tab_extension->
+ EnsureBrowserActionIndexMatches(0));
+ ASSERT_TRUE(simple_extension_->MoveBrowserAction(0));
+ ASSERT_NO_FATAL_FAILURE(simple_extension_->
+ EnsureBrowserActionIndexMatches(0));
+ ASSERT_NO_FATAL_FAILURE(rename_tab_extension->
+ EnsureBrowserActionIndexMatches(1));
+
+ // Try moving browser action to invalid index.
+ ASSERT_FALSE(simple_extension_->MoveBrowserAction(-1));
+ ASSERT_FALSE(simple_extension_->MoveBrowserAction(2));
+}
+
+TEST_F(ExtensionProxyUITest, GetProperty) {
+ ASSERT_NO_FATAL_FAILURE(simple_extension_->
+ EnsureIdMatches("aiglobglfckejlcpcbdokbkbjeemfhno"));
+ ASSERT_NO_FATAL_FAILURE(simple_extension_->
+ EnsureNameMatches("Browser Action"));
+ ASSERT_NO_FATAL_FAILURE(simple_extension_->
+ EnsureVersionMatches("0.1.1"));
+ ASSERT_NO_FATAL_FAILURE(simple_extension_->
+ EnsureBrowserActionIndexMatches(0));
+}
+
+} // namespace
diff --git a/chrome/test/pyautolib/pyautolib.cc b/chrome/test/pyautolib/pyautolib.cc
index 84bdd18..39a5b32 100644
--- a/chrome/test/pyautolib/pyautolib.cc
+++ b/chrome/test/pyautolib/pyautolib.cc
@@ -5,6 +5,7 @@
#include "base/scoped_ptr.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
+#include "chrome/test/automation/extension_proxy.h"
#include "chrome/test/automation/tab_proxy.h"
#include "chrome/test/pyautolib/pyautolib.h"
#include "googleurl/src/gurl.h"
@@ -156,7 +157,9 @@ bool PyUITestBase::OpenNewBrowserWindow(bool show) {
}
bool PyUITestBase::InstallExtension(const FilePath& crx_file) {
- return automation()->InstallExtension(crx_file);
+ scoped_refptr<ExtensionProxy> proxy =
+ automation()->InstallExtension(crx_file);
+ return proxy.get() != NULL;
}
bool PyUITestBase::GetBookmarkBarVisibility() {
@@ -274,7 +277,6 @@ scoped_refptr<BrowserProxy> PyUITestBase::GetBrowserWindow(int window_index) {
return automation()->GetBrowserWindow(window_index);
}
-
std::string PyUITestBase::_SendJSONRequest(int window_index,
std::string& request) {
scoped_refptr<BrowserProxy> browser_proxy =
@@ -286,4 +288,3 @@ std::string PyUITestBase::_SendJSONRequest(int window_index,
}
return response;
}
-