From 7596ce72af88e6934be4aaa9d38a2deca96b095d Mon Sep 17 00:00:00 2001 From: "asargent@chromium.org" Date: Mon, 30 Aug 2010 05:10:46 +0000 Subject: Implement events for extensions management API. BUG=51178 TEST=The events described in the experimental.management API should fire for extensions and apps being installed, uninstalled, enabled, and disabled. Review URL: http://codereview.chromium.org/3278003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@57831 0039d316-1c4b-4281-b951-d872f2087c98 --- chrome/browser/extensions/extension_event_names.cc | 7 +- chrome/browser/extensions/extension_event_names.h | 11 +++- .../browser/extensions/extension_management_api.cc | 77 +++++++++++++++++++++- .../browser/extensions/extension_management_api.h | 27 ++++++++ .../extension_management_api_browsertest.cc | 30 +++++++++ chrome/browser/extensions/extensions_service.cc | 4 +- chrome/chrome_tests.gypi | 1 + chrome/common/extensions/api/extension_api.json | 11 ++-- .../extensions/docs/experimental.management.html | 30 +++++---- .../extensions/api_test/management/test/basics.js | 29 ++++---- .../extensions/api_test/management/test/common.js | 2 + .../api_test/management/test/uninstall.js | 11 ++-- .../management/install_event/background.html | 1 + .../management/install_event/manifest.json | 7 ++ .../extensions/management/install_event/test.js | 15 +++++ 15 files changed, 222 insertions(+), 41 deletions(-) create mode 100644 chrome/browser/extensions/extension_management_api_browsertest.cc create mode 100644 chrome/test/data/extensions/management/install_event/background.html create mode 100644 chrome/test/data/extensions/management/install_event/manifest.json create mode 100644 chrome/test/data/extensions/management/install_event/test.js diff --git a/chrome/browser/extensions/extension_event_names.cc b/chrome/browser/extensions/extension_event_names.cc index 421bab8..a70f293 100644 --- a/chrome/browser/extensions/extension_event_names.cc +++ b/chrome/browser/extensions/extension_event_names.cc @@ -13,9 +13,14 @@ const char kOnTabMoved[] = "tabs.onMoved"; const char kOnTabRemoved[] = "tabs.onRemoved"; const char kOnTabSelectionChanged[] = "tabs.onSelectionChanged"; const char kOnTabUpdated[] = "tabs.onUpdated"; + const char kOnWindowCreated[] = "windows.onCreated"; const char kOnWindowFocusedChanged[] = "windows.onFocusChanged"; const char kOnWindowRemoved[] = "windows.onRemoved"; -} // namespace extension_event_names +const char kOnExtensionInstalled[] = "experimental.management.onInstalled"; +const char kOnExtensionUninstalled[] = "experimental.management.onUninstalled"; +const char kOnExtensionEnabled[] = "experimental.management.onEnabled"; +const char kOnExtensionDisabled[] = "experimental.management.onDisabled"; +} // namespace extension_event_names diff --git a/chrome/browser/extensions/extension_event_names.h b/chrome/browser/extensions/extension_event_names.h index 4cbadfc..3c63f90 100644 --- a/chrome/browser/extensions/extension_event_names.h +++ b/chrome/browser/extensions/extension_event_names.h @@ -10,6 +10,7 @@ namespace extension_event_names { +// Tabs. extern const char kOnTabAttached[]; extern const char kOnTabCreated[]; extern const char kOnTabDetached[]; @@ -17,11 +18,19 @@ extern const char kOnTabMoved[]; extern const char kOnTabRemoved[]; extern const char kOnTabSelectionChanged[]; extern const char kOnTabUpdated[]; + +// Windows. extern const char kOnWindowCreated[]; extern const char kOnWindowFocusedChanged[]; extern const char kOnWindowRemoved[]; +// Management. +extern const char kOnExtensionInstalled[]; +extern const char kOnExtensionUninstalled[]; +extern const char kOnExtensionEnabled[]; +extern const char kOnExtensionDisabled[]; + + }; // namespace extension_event_names #endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_EVENT_NAMES_H_ - diff --git a/chrome/browser/extensions/extension_management_api.cc b/chrome/browser/extensions/extension_management_api.cc index 6be0476..4b581b2 100644 --- a/chrome/browser/extensions/extension_management_api.cc +++ b/chrome/browser/extensions/extension_management_api.cc @@ -7,12 +7,19 @@ #include #include +#include "base/basictypes.h" +#include "base/json/json_writer.h" #include "base/string_number_conversions.h" #include "chrome/browser/browser.h" +#include "chrome/browser/extensions/extension_event_names.h" +#include "chrome/browser/extensions/extension_message_service.h" #include "chrome/browser/extensions/extensions_service.h" #include "chrome/common/extensions/extension_error_utils.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/notification_type.h" using base::IntToString; +namespace events = extension_event_names; const char kAppLaunchUrlKey[] = "appLaunchUrl"; const char kEnabledKey[] = "enabled"; @@ -26,7 +33,6 @@ const char kUrlKey[] = "url"; const char kNoExtensionError[] = "No extension with id *"; - ExtensionsService* ExtensionManagementFunction::service() { return profile()->GetExtensionsService(); } @@ -128,3 +134,72 @@ bool UninstallFunction::RunImpl() { service()->UninstallExtension(extension_id, false /* external_uninstall */); return true; } + + +// static +ExtensionManagementEventRouter* ExtensionManagementEventRouter::GetInstance() { + return Singleton::get(); +} + +ExtensionManagementEventRouter::ExtensionManagementEventRouter() {} + +ExtensionManagementEventRouter::~ExtensionManagementEventRouter() {} + +void ExtensionManagementEventRouter::Init() { + NotificationType::Type types[] = { + NotificationType::EXTENSION_INSTALLED, + NotificationType::EXTENSION_UNINSTALLED, + NotificationType::EXTENSION_LOADED, + NotificationType::EXTENSION_UNLOADED + }; + + for (size_t i = 0; i < arraysize(types); i++) { + registrar_.Add(this, + types[i], + NotificationService::AllSources()); + } +} + +void ExtensionManagementEventRouter::Observe( + NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + const char* event_name = NULL; + switch (type.value) { + case NotificationType::EXTENSION_INSTALLED: + event_name = events::kOnExtensionInstalled; + break; + case NotificationType::EXTENSION_UNINSTALLED: + event_name = events::kOnExtensionUninstalled; + break; + case NotificationType::EXTENSION_LOADED: + event_name = events::kOnExtensionEnabled; + break; + case NotificationType::EXTENSION_UNLOADED: + event_name = events::kOnExtensionDisabled; + break; + default: + NOTREACHED(); + return; + } + + Profile* profile = Source(source).ptr(); + Extension* extension = Details(details).ptr(); + CHECK(profile); + CHECK(extension); + + ExtensionsService* service = profile->GetExtensionsService(); + bool enabled = service->GetExtensionById(extension->id(), false) != NULL; + ListValue args; + args.Append(CreateExtensionInfo(*extension, enabled)); + + std::string args_json; + base::JSONWriter::Write(&args, false /* pretty_print */, &args_json); + + ExtensionMessageService* message_service = + profile->GetExtensionMessageService(); + message_service->DispatchEventToRenderers(event_name, + args_json, + profile->IsOffTheRecord(), + GURL()); +} diff --git a/chrome/browser/extensions/extension_management_api.h b/chrome/browser/extensions/extension_management_api.h index c32c833..9d3108d 100644 --- a/chrome/browser/extensions/extension_management_api.h +++ b/chrome/browser/extensions/extension_management_api.h @@ -6,7 +6,10 @@ #define CHROME_BROWSER_EXTENSIONS_EXTENSION_MANAGEMENT_API_H__ #pragma once +#include "base/singleton.h" #include "chrome/browser/extensions/extension_function.h" +#include "chrome/common/notification_observer.h" +#include "chrome/common/notification_registrar.h" class ExtensionsService; @@ -39,4 +42,28 @@ class UninstallFunction : public ExtensionManagementFunction { DECLARE_EXTENSION_FUNCTION_NAME("experimental.management.uninstall"); }; +class ExtensionManagementEventRouter : public NotificationObserver { + public: + // Get the singleton instance of the event router. + static ExtensionManagementEventRouter* GetInstance(); + + // Performs one-time initialization of our singleton. + void Init(); + + private: + friend struct DefaultSingletonTraits; + + ExtensionManagementEventRouter(); + virtual ~ExtensionManagementEventRouter(); + + // NotificationObserver implementation. + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + NotificationRegistrar registrar_; + + DISALLOW_COPY_AND_ASSIGN(ExtensionManagementEventRouter); +}; + #endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_MANAGEMENT_API_H__ diff --git a/chrome/browser/extensions/extension_management_api_browsertest.cc b/chrome/browser/extensions/extension_management_api_browsertest.cc new file mode 100644 index 0000000..63fdfcf --- /dev/null +++ b/chrome/browser/extensions/extension_management_api_browsertest.cc @@ -0,0 +1,30 @@ +// 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/extensions/extension_browsertest.h" +#include "chrome/browser/extensions/extension_test_message_listener.h" +#include "chrome/common/chrome_switches.h" + + +class ExtensionManagementApiBrowserTest : public ExtensionBrowserTest { + virtual void SetUpCommandLine(CommandLine* command_line) { + ExtensionBrowserTest::SetUpCommandLine(command_line); + command_line->AppendSwitch( + switches::kEnableExperimentalExtensionApis); + } +}; + +// We test this here instead of in an ExtensionApiTest because normal extensions +// are not allowed to call the install function. +IN_PROC_BROWSER_TEST_F(ExtensionManagementApiBrowserTest, InstallEvent) { + ExtensionTestMessageListener listener1("ready"); + ASSERT_TRUE(LoadExtension( + test_data_dir_.AppendASCII("management/install_event"))); + ASSERT_TRUE(listener1.WaitUntilSatisfied()); + + ExtensionTestMessageListener listener2("got_event"); + ASSERT_TRUE(LoadExtension( + test_data_dir_.AppendASCII("api_test/management/enabled_extension"))); + ASSERT_TRUE(listener2.WaitUntilSatisfied()); +} diff --git a/chrome/browser/extensions/extensions_service.cc b/chrome/browser/extensions/extensions_service.cc index 3fb4a91..5ea1ede 100644 --- a/chrome/browser/extensions/extensions_service.cc +++ b/chrome/browser/extensions/extensions_service.cc @@ -29,6 +29,7 @@ #include "chrome/browser/extensions/extension_error_reporter.h" #include "chrome/browser/extensions/extension_history_api.h" #include "chrome/browser/extensions/extension_host.h" +#include "chrome/browser/extensions/extension_management_api.h" #include "chrome/browser/extensions/extension_process_manager.h" #include "chrome/browser/extensions/extension_updater.h" #include "chrome/browser/extensions/external_extension_provider.h" @@ -238,6 +239,7 @@ void ExtensionsService::InitEventRouters() { ExtensionBookmarkEventRouter::GetSingleton()->Observe( profile_->GetBookmarkModel()); ExtensionCookiesEventRouter::GetInstance()->Init(); + ExtensionManagementEventRouter::GetInstance()->Init(); } void ExtensionsService::Init() { @@ -533,7 +535,7 @@ void ExtensionsService::LoadComponentExtensions() { // In order for the --apps-gallery-url switch to work with the gallery // process isolation, we must insert any provided value into the component // app's launch url and web extent. - if (extension->id() == extension_misc::kWebStoreAppId ) { + if (extension->id() == extension_misc::kWebStoreAppId) { GURL gallery_url(CommandLine::ForCurrentProcess() ->GetSwitchValueASCII(switches::kAppsGalleryURL)); if (gallery_url.is_valid()) { diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index cff0ea7..1a61269 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -1718,6 +1718,7 @@ 'browser/extensions/extension_input_apitest.cc', 'browser/extensions/extension_install_ui_browsertest.cc', 'browser/extensions/extension_javascript_url_apitest.cc', + 'browser/extensions/extension_management_api_browsertest.cc', 'browser/extensions/extension_management_apitest.cc', 'browser/extensions/extension_management_browsertest.cc', 'browser/extensions/extension_messages_apitest.cc', diff --git a/chrome/common/extensions/api/extension_api.json b/chrome/common/extensions/api/extension_api.json index d8b3c02..1014714 100644 --- a/chrome/common/extensions/api/extension_api.json +++ b/chrome/common/extensions/api/extension_api.json @@ -3998,6 +3998,7 @@ { "name": "callback", "type": "function", + "optional": "true", "parameters": [] } ] @@ -4046,25 +4047,25 @@ "events": [ { "name": "onInstalled", - "description": "(NOT YET IMPLEMENTED)", + "description": "Fired when an app or extension has been installed.", "type": "function", "parameters": [{"name": "info", "$ref":"ExtensionInfo"}] }, { "name": "onUninstalled", - "description": "(NOT YET IMPLEMENTED)", + "description": "Fired when an app or extension has been uninstalled.", "type": "function", - "parameters": [{"name": "id", "type": "string", "description": "The id of the extension that was uninstalled."}] + "parameters": [{"name": "info", "$ref":"ExtensionInfo"}] }, { "name": "onEnabled", - "description": "(NOT YET IMPLEMENTED)", + "description": "Fired when an app or extension has been enabled.", "type": "function", "parameters": [{"name": "info", "$ref":"ExtensionInfo"}] }, { "name": "onDisabled", - "description": "(NOT YET IMPLEMENTED)", + "description": "Fired when an app or extension has been disabled", "type": "function", "parameters": [{"name": "info", "$ref":"ExtensionInfo"}] } diff --git a/chrome/common/extensions/docs/experimental.management.html b/chrome/common/extensions/docs/experimental.management.html index 4f3ac8d..479cd35 100644 --- a/chrome/common/extensions/docs/experimental.management.html +++ b/chrome/common/extensions/docs/experimental.management.html @@ -597,7 +597,7 @@ chrome.experimental.management.setEnabled(, string id, boolean - enabled, function + enabled, function callback)
@@ -732,7 +732,7 @@
( - + optional @@ -799,11 +799,11 @@

Callback function

-

+

The callback parameter should specify a function that looks like this:

-

+

If you specify the callback parameter, it should specify a function that looks like this:

@@ -1267,7 +1267,7 @@
- chrome.experimental.management.onUninstalled.addListener(function(string id) {...}); + chrome.experimental.management.onUninstalled.addListener(function(ExtensionInfo info) {...});
@@ -1280,7 +1280,7 @@
- id + info @@ -1289,15 +1289,15 @@ - - Type - - + ExtensionInfo + + + array of - string - + paramType + ) @@ -1305,10 +1305,12 @@
-
Undocumented.
-
The id of the extension that was uninstalled.
+
+ Description of this parameter from the json schema. +
This parameter was added in version . diff --git a/chrome/test/data/extensions/api_test/management/test/basics.js b/chrome/test/data/extensions/api_test/management/test/basics.js index ea5fc23..c998ca3d 100644 --- a/chrome/test/data/extensions/api_test/management/test/basics.js +++ b/chrome/test/data/extensions/api_test/management/test/basics.js @@ -17,8 +17,7 @@ function checkIcon(item, size, path) { var tests = [ function simple() { - chrome.management.getAll(function(items) { - assertNoLastError(); + chrome.management.getAll(callback(function(items) { chrome.test.assertEq(5, items.length); checkItem(items, "Extension Management API Test", true, false); @@ -36,33 +35,35 @@ var tests = [ checkIcon(extension, 128, "icon_128.png"); checkIcon(extension, 48, "icon_48.png"); checkIcon(extension, 16, "icon_16.png"); - - succeed(); - }); + })); }, // Disables an enabled app. function disable() { - chrome.management.getAll(function(items) { - assertNoLastError(); + listenOnce(chrome.management.onDisabled, function(info) { + assertEq(info.name, "enabled_app"); + }); + + chrome.management.getAll(callback(function(items) { checkItem(items, "enabled_app", true, true); var enabled_app = getItemNamed(items, "enabled_app"); chrome.management.setEnabled(enabled_app.id, false, function() { assertNoLastError(); chrome.management.getAll(function(items2) { assertNoLastError(); - chrome.test.log("re-checking enabled_app"); checkItem(items2, "enabled_app", false, true); - succeed(); + assertTrue(event_fired); }); }); - }); + })); }, // Enables a disabled extension. function enable() { - chrome.management.getAll(function(items) { - assertNoLastError(); + listenOnce(chrome.management.onEnabled, function(info) { + assertEq(info.name, "disabled_extension"); + }); + chrome.management.getAll(callback(function(items) { checkItem(items, "disabled_extension", false, false); var disabled = getItemNamed(items, "disabled_extension"); chrome.management.setEnabled(disabled.id, true, function() { @@ -70,10 +71,10 @@ var tests = [ chrome.management.getAll(function(items2) { assertNoLastError(); checkItem(items2, "disabled_extension", true, false); - succeed(); + assertTrue(event_fired); }); }); - }); + })); } ]; diff --git a/chrome/test/data/extensions/api_test/management/test/common.js b/chrome/test/data/extensions/api_test/management/test/common.js index 6385599..744ced4 100644 --- a/chrome/test/data/extensions/api_test/management/test/common.js +++ b/chrome/test/data/extensions/api_test/management/test/common.js @@ -12,6 +12,8 @@ var assertNoLastError = chrome.test.assertNoLastError; var assertTrue = chrome.test.assertTrue; var fail = chrome.test.fail; var succeed = chrome.test.succeed; +var listenOnce = chrome.test.listenOnce; +var callback = chrome.test.callback; function getItemNamed(list, name) { for (var i = 0; i < list.length; i++) { diff --git a/chrome/test/data/extensions/api_test/management/test/uninstall.js b/chrome/test/data/extensions/api_test/management/test/uninstall.js index e4a120b..f425c8e 100644 --- a/chrome/test/data/extensions/api_test/management/test/uninstall.js +++ b/chrome/test/data/extensions/api_test/management/test/uninstall.js @@ -3,8 +3,11 @@ // found in the LICENSE file. function uninstall(name) { - chrome.management.getAll(function(items) { - assertNoLastError(); + listenOnce(chrome.management.onUninstalled, function(info) { + assertEq(info.name, name); + }); + + chrome.management.getAll(callback(function(items) { var old_count = items.length; var item = getItemNamed(items, name); chrome.management.uninstall(item.id, function() { @@ -15,10 +18,10 @@ function uninstall(name) { for (var i = 0; i < items2.length; i++) { assertFalse(items2[i].name == name); } - succeed(); + assertTrue(event_fired); }); }); - }); + })); } var tests = [ diff --git a/chrome/test/data/extensions/management/install_event/background.html b/chrome/test/data/extensions/management/install_event/background.html new file mode 100644 index 0000000..46f4d74 --- /dev/null +++ b/chrome/test/data/extensions/management/install_event/background.html @@ -0,0 +1 @@ + diff --git a/chrome/test/data/extensions/management/install_event/manifest.json b/chrome/test/data/extensions/management/install_event/manifest.json new file mode 100644 index 0000000..79a5831 --- /dev/null +++ b/chrome/test/data/extensions/management/install_event/manifest.json @@ -0,0 +1,7 @@ +{ + "name": "Install event test", + "version": "0.1", + "permissions": ["experimental"], + "background_page": "background.html" +} + diff --git a/chrome/test/data/extensions/management/install_event/test.js b/chrome/test/data/extensions/management/install_event/test.js new file mode 100644 index 0000000..09ef1c4 --- /dev/null +++ b/chrome/test/data/extensions/management/install_event/test.js @@ -0,0 +1,15 @@ +// 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. + +if (!chrome.management) { + chrome.management = chrome.experimental.management; +} + +chrome.management.onInstalled.addListener(function(info) { + if (info.name == "enabled_extension") { + chrome.test.sendMessage("got_event"); + } +}); + +chrome.test.sendMessage("ready"); -- cgit v1.1