blob: 5bbb524f0c5e94a24ac15ad583c3a53c25d5f4fa [file] [log] [blame]
[email protected]1e8c93f2010-02-08 22:58:311// Copyright (c) 2010 The Chromium Authors. All rights reserved.
[email protected]6014d672008-12-05 00:38:252// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/extensions/extensions_service.h"
6
[email protected]24b538a2010-02-27 01:22:447#include "base/basictypes.h"
[email protected]e2eb43112009-05-29 21:19:548#include "base/command_line.h"
[email protected]6014d672008-12-05 00:38:259#include "base/file_util.h"
[email protected]cc2c3432009-11-06 17:24:3610#include "base/histogram.h"
[email protected]24b538a2010-02-27 01:22:4411#include "base/string16.h"
[email protected]6014d672008-12-05 00:38:2512#include "base/string_util.h"
[email protected]cc2c3432009-11-06 17:24:3613#include "base/time.h"
[email protected]cc655912009-01-29 23:19:1914#include "base/values.h"
[email protected]aa142702010-03-26 01:26:3315#include "base/version.h"
[email protected]15730c42009-09-03 00:03:2016#include "chrome/browser/browser_process.h"
[email protected]dbb92e0d2009-08-20 16:18:2117#include "chrome/browser/chrome_thread.h"
[email protected]4814b512009-11-07 00:12:2918#include "chrome/browser/debugger/devtools_manager.h"
[email protected]7577a5c52009-07-30 06:21:5819#include "chrome/browser/extensions/crx_installer.h"
[email protected]5cbe1e22010-01-30 01:18:5620#include "chrome/browser/extensions/extension_accessibility_api.h"
[email protected]840b0db2009-11-20 03:00:3821#include "chrome/browser/extensions/extension_bookmarks_module.h"
[email protected]b68d5ed2009-04-16 02:41:2822#include "chrome/browser/extensions/extension_browser_event_router.h"
[email protected]c10da4b02010-03-25 14:38:3223#include "chrome/browser/extensions/extension_data_deleter.h"
[email protected]86c008e82009-08-28 20:26:0524#include "chrome/browser/extensions/extension_dom_ui.h"
[email protected]de768a832009-10-30 05:25:0125#include "chrome/browser/extensions/extension_history_api.h"
[email protected]b1748b1d82009-11-30 20:32:5626#include "chrome/browser/extensions/extension_host.h"
[email protected]4814b512009-11-07 00:12:2927#include "chrome/browser/extensions/extension_process_manager.h"
[email protected]93fd78f42009-07-10 16:43:1728#include "chrome/browser/extensions/extension_updater.h"
[email protected]a1257b12009-06-12 02:51:3429#include "chrome/browser/extensions/external_extension_provider.h"
30#include "chrome/browser/extensions/external_pref_extension_provider.h"
[email protected]052313b2010-02-19 09:43:0831#include "chrome/browser/pref_service.h"
[email protected]81e63782009-02-27 19:35:0932#include "chrome/browser/profile.h"
[email protected]62d30f42009-10-01 22:36:0633#include "chrome/browser/net/chrome_url_request_context.h"
[email protected]aab98a52009-12-02 03:22:3534#include "chrome/common/child_process_logging.h"
[email protected]e2eb43112009-05-29 21:19:5435#include "chrome/common/chrome_switches.h"
[email protected]5b1a0e22009-05-26 19:00:5836#include "chrome/common/extensions/extension.h"
[email protected]d7b36dc2009-10-29 21:47:4037#include "chrome/common/extensions/extension_constants.h"
[email protected]5b1a0e22009-05-26 19:00:5838#include "chrome/common/extensions/extension_error_reporter.h"
[email protected]7c927b62010-02-24 09:54:1339#include "chrome/common/extensions/extension_file_util.h"
[email protected]c6d474f82009-12-16 21:11:0640#include "chrome/common/extensions/extension_l10n_util.h"
[email protected]82891262008-12-24 00:21:2641#include "chrome/common/notification_service.h"
[email protected]4814b512009-11-07 00:12:2942#include "chrome/common/notification_type.h"
[email protected]1952c7d2010-03-04 23:48:3443#include "chrome/common/json_value_serializer.h"
[email protected]25b34332009-06-05 21:53:1944#include "chrome/common/pref_names.h"
[email protected]a57209872009-05-04 22:53:1445#include "chrome/common/url_constants.h"
[email protected]c10da4b02010-03-25 14:38:3246#include "googleurl/src/gurl.h"
[email protected]24b538a2010-02-27 01:22:4447#include "webkit/database/database_tracker.h"
48#include "webkit/database/database_util.h"
[email protected]c64631652009-04-29 22:24:3149
[email protected]79db6232009-02-13 20:51:2050#if defined(OS_WIN)
[email protected]a1257b12009-06-12 02:51:3451#include "chrome/browser/extensions/external_registry_extension_provider_win.h"
[email protected]79db6232009-02-13 20:51:2052#endif
[email protected]6014d672008-12-05 00:38:2553
[email protected]5ef47ec2010-01-28 05:58:0554using base::Time;
55
[email protected]c6d474f82009-12-16 21:11:0656namespace errors = extension_manifest_errors;
57
[email protected]b6ab96d2009-08-20 18:58:1958namespace {
59
60// Helper class to collect the IDs of every extension listed in the prefs.
61class InstalledExtensionSet {
62 public:
[email protected]c6d474f82009-12-16 21:11:0663 explicit InstalledExtensionSet(ExtensionPrefs* prefs) {
64 scoped_ptr<ExtensionPrefs::ExtensionsInfo> info(
[email protected]e6090e42010-03-23 22:44:0865 prefs->GetInstalledExtensionsInfo());
[email protected]c6d474f82009-12-16 21:11:0666
67 for (size_t i = 0; i < info->size(); ++i) {
68 std::string version;
69 const DictionaryValue* manifest = info->at(i)->extension_manifest.get();
70 if (!manifest ||
71 !manifest->GetString(extension_manifest_keys::kVersion, &version)) {
72 // Without a version, the extension is invalid. Ignoring it here will
73 // cause it to get garbage collected.
74 continue;
75 }
76 extensions_.insert(info->at(i)->extension_id);
77 versions_[info->at(i)->extension_id] = version;
78 }
[email protected]b6ab96d2009-08-20 18:58:1979 }
80
81 const std::set<std::string>& extensions() { return extensions_; }
[email protected]4559a7d2009-12-02 01:42:4182 const std::map<std::string, std::string>& versions() { return versions_; }
[email protected]b6ab96d2009-08-20 18:58:1983
84 private:
[email protected]b6ab96d2009-08-20 18:58:1985 std::set<std::string> extensions_;
[email protected]4559a7d2009-12-02 01:42:4186 std::map<std::string, std::string> versions_;
[email protected]b6ab96d2009-08-20 18:58:1987};
88
[email protected]2111b1a2010-03-12 18:12:4489static bool ShouldReloadExtensionManifest(const ExtensionInfo& info) {
90 // Always reload LOAD extension manifests, because they can change on disk
91 // independent of the manifest in our prefs.
92 if (info.extension_location == Extension::LOAD)
93 return true;
94
95 // Otherwise, reload the manifest it needs to be relocalized.
96 return extension_l10n_util::ShouldRelocalizeManifest(info);
97}
98
[email protected]c6d474f82009-12-16 21:11:0699} // namespace
[email protected]b6ab96d2009-08-20 18:58:19100
[email protected]aa142702010-03-26 01:26:33101PendingExtensionInfo::PendingExtensionInfo(const GURL& update_url,
102 const Version& version,
103 bool is_theme,
104 bool install_silently)
105 : update_url(update_url),
106 version(version),
107 is_theme(is_theme),
108 install_silently(install_silently) {}
109
110PendingExtensionInfo::PendingExtensionInfo()
111 : update_url(),
112 version(),
113 is_theme(false),
114 install_silently(false) {}
115
[email protected]25b34332009-06-05 21:53:19116// ExtensionsService.
[email protected]6014d672008-12-05 00:38:25117
[email protected]cc655912009-01-29 23:19:19118const char* ExtensionsService::kInstallDirectoryName = "Extensions";
119const char* ExtensionsService::kCurrentVersionFileName = "Current Version";
[email protected]494c06e2009-07-25 01:06:42120
[email protected]b7c2f252009-12-08 00:47:23121// static
122bool ExtensionsService::IsDownloadFromGallery(const GURL& download_url,
123 const GURL& referrer_url) {
124 if (StartsWithASCII(download_url.spec(),
125 extension_urls::kMiniGalleryDownloadPrefix, false) &&
126 StartsWithASCII(referrer_url.spec(),
127 extension_urls::kMiniGalleryBrowsePrefix, false)) {
128 return true;
129 }
130
131 if (StartsWithASCII(download_url.spec(),
132 extension_urls::kGalleryDownloadPrefix, false) &&
133 StartsWithASCII(referrer_url.spec(),
134 extension_urls::kGalleryBrowsePrefix, false)) {
135 return true;
136 }
137
138 return false;
139}
140
[email protected]ac025282009-12-16 19:16:38141bool ExtensionsService::IsDownloadFromMiniGallery(const GURL& download_url) {
142 return StartsWithASCII(download_url.spec(),
143 extension_urls::kMiniGalleryDownloadPrefix,
144 false); // case_sensitive
145}
146
[email protected]81e63782009-02-27 19:35:09147ExtensionsService::ExtensionsService(Profile* profile,
[email protected]36a784c2009-06-23 06:21:08148 const CommandLine* command_line,
[email protected]a9b00ac2009-06-25 21:03:23149 PrefService* prefs,
150 const FilePath& install_directory,
[email protected]93fd78f42009-07-10 16:43:17151 bool autoupdate_enabled)
[email protected]6ef635e42009-07-26 06:16:12152 : profile_(profile),
153 extension_prefs_(new ExtensionPrefs(prefs, install_directory)),
[email protected]a9b00ac2009-06-25 21:03:23154 install_directory_(install_directory),
[email protected]6d60703b2009-08-29 01:29:23155 extensions_enabled_(true),
[email protected]e81dba32009-06-19 20:19:13156 show_extensions_prompts_(true),
[email protected]e0360f2c2009-12-07 22:34:31157 ready_(false),
158 ALLOW_THIS_IN_INITIALIZER_LIST(toolbar_model_(this)) {
[email protected]36a784c2009-06-23 06:21:08159 // Figure out if extension installation should be enabled.
[email protected]6d60703b2009-08-29 01:29:23160 if (command_line->HasSwitch(switches::kDisableExtensions)) {
161 extensions_enabled_ = false;
162 } else if (profile->GetPrefs()->GetBoolean(prefs::kDisableExtensions)) {
163 extensions_enabled_ = false;
[email protected]6b75ec32009-08-14 06:37:18164 }
[email protected]36a784c2009-06-23 06:21:08165
[email protected]4814b512009-11-07 00:12:29166 registrar_.Add(this, NotificationType::EXTENSION_HOST_DID_STOP_LOADING,
167 NotificationService::AllSources());
[email protected]a4ed6282009-12-14 20:51:16168 registrar_.Add(this, NotificationType::EXTENSION_PROCESS_TERMINATED,
[email protected]31f77262009-12-02 20:48:53169 Source<Profile>(profile_));
[email protected]4814b512009-11-07 00:12:29170
[email protected]93fd78f42009-07-10 16:43:17171 // Set up the ExtensionUpdater
172 if (autoupdate_enabled) {
173 int update_frequency = kDefaultUpdateFrequencySeconds;
174 if (command_line->HasSwitch(switches::kExtensionsUpdateFrequency)) {
[email protected]c4e52f0d2009-11-06 19:55:16175 update_frequency = StringToInt(command_line->GetSwitchValueASCII(
176 switches::kExtensionsUpdateFrequency));
[email protected]93fd78f42009-07-10 16:43:17177 }
[email protected]95d29192009-10-30 01:49:06178 updater_ = new ExtensionUpdater(this, prefs, update_frequency);
[email protected]93fd78f42009-07-10 16:43:17179 }
180
[email protected]95d29192009-10-30 01:49:06181 backend_ = new ExtensionsServiceBackend(install_directory_);
[email protected]6014d672008-12-05 00:38:25182}
183
184ExtensionsService::~ExtensionsService() {
[email protected]9f1087e2009-06-15 17:29:32185 UnloadAllExtensions();
[email protected]93fd78f42009-07-10 16:43:17186 if (updater_.get()) {
187 updater_->Stop();
188 }
[email protected]6014d672008-12-05 00:38:25189}
190
[email protected]9f1087e2009-06-15 17:29:32191void ExtensionsService::Init() {
[email protected]c6e4a3412009-06-24 15:45:29192 DCHECK(!ready_);
[email protected]93fd78f42009-07-10 16:43:17193 DCHECK_EQ(extensions_.size(), 0u);
[email protected]9f1087e2009-06-15 17:29:32194
[email protected]95dd38f2009-10-20 20:09:15195 // Hack: we need to ensure the ResourceDispatcherHost is ready before we load
196 // the first extension, because its members listen for loaded notifications.
197 g_browser_process->resource_dispatcher_host();
198
[email protected]de768a832009-10-30 05:25:01199 // Start up the extension event routers.
200 ExtensionHistoryEventRouter::GetInstance()->ObserveProfile(profile_);
[email protected]5cbe1e22010-01-30 01:18:56201 ExtensionAccessibilityEventRouter::GetInstance()->ObserveProfile(profile_);
[email protected]de768a832009-10-30 05:25:01202
[email protected]9f1087e2009-06-15 17:29:32203 LoadAllExtensions();
[email protected]894bb502009-05-21 22:39:57204
[email protected]9f1087e2009-06-15 17:29:32205 // TODO(erikkay) this should probably be deferred to a future point
206 // rather than running immediately at startup.
[email protected]93fd78f42009-07-10 16:43:17207 CheckForExternalUpdates();
[email protected]894bb502009-05-21 22:39:57208
[email protected]9f1087e2009-06-15 17:29:32209 // TODO(erikkay) this should probably be deferred as well.
210 GarbageCollectExtensions();
[email protected]6014d672008-12-05 00:38:25211}
212
[email protected]3cf4f0992009-02-03 23:00:30213void ExtensionsService::InstallExtension(const FilePath& extension_path) {
[email protected]6dfbbf82010-03-12 23:09:16214 scoped_refptr<CrxInstaller> installer(
215 new CrxInstaller(install_directory_,
216 this, // frontend
217 NULL)); // no client (silent install)
218 installer->set_allow_privilege_increase(true);
219 installer->InstallCrx(extension_path);
[email protected]3cf4f0992009-02-03 23:00:30220}
221
[email protected]aa142702010-03-26 01:26:33222namespace {
223 // TODO(akalin): Put this somewhere where both crx_installer.cc and
224 // this file can use it.
225 void DeleteFileHelper(const FilePath& path, bool recursive) {
226 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE));
227 file_util::Delete(path, recursive);
228 }
229} // namespace
230
[email protected]e957fe52009-06-23 16:51:05231void ExtensionsService::UpdateExtension(const std::string& id,
[email protected]5c8516202010-03-18 21:43:34232 const FilePath& extension_path,
233 const GURL& download_url) {
[email protected]aa142702010-03-26 01:26:33234 PendingExtensionMap::const_iterator it = pending_extensions_.find(id);
235 if ((it == pending_extensions_.end()) &&
236 !GetExtensionByIdInternal(id, true, true)) {
237 LOG(WARNING) << "Will not update extension " << id
238 << " because it is not installed or pending";
239 // Delete extension_path since we're not creating a CrxInstaller
240 // that would do it for us.
241 ChromeThread::PostTask(
242 ChromeThread::FILE, FROM_HERE,
243 NewRunnableFunction(&DeleteFileHelper, extension_path, false));
[email protected]4c967932009-07-31 01:15:49244 return;
[email protected]e957fe52009-06-23 16:51:05245 }
246
[email protected]aa142702010-03-26 01:26:33247 // We want a silent install only for non-pending extensions and
248 // pending extensions that have install_silently set.
249 ExtensionInstallUI* client =
250 ((it == pending_extensions_.end()) || it->second.install_silently) ?
251 NULL : new ExtensionInstallUI(profile_);
252
[email protected]6dfbbf82010-03-12 23:09:16253 scoped_refptr<CrxInstaller> installer(
254 new CrxInstaller(install_directory_,
255 this, // frontend
[email protected]aa142702010-03-26 01:26:33256 client));
[email protected]6dfbbf82010-03-12 23:09:16257 installer->set_expected_id(id);
258 installer->set_delete_source(true);
[email protected]867a73e12010-03-19 20:45:46259 installer->set_force_web_origin_to_download_url(true);
[email protected]5c8516202010-03-18 21:43:34260 installer->set_original_url(download_url);
[email protected]6dfbbf82010-03-12 23:09:16261 installer->InstallCrx(extension_path);
[email protected]e957fe52009-06-23 16:51:05262}
263
[email protected]aa142702010-03-26 01:26:33264void ExtensionsService::AddPendingExtension(
265 const std::string& id, const GURL& update_url,
266 const Version& version, bool is_theme, bool install_silently) {
267 if (GetExtensionByIdInternal(id, true, true)) {
[email protected]efee9f262010-03-29 21:26:25268 LOG(DFATAL) << "Trying to add pending extension " << id
269 << " which already exists";
[email protected]aa142702010-03-26 01:26:33270 return;
271 }
272 AddPendingExtensionInternal(id, update_url, version,
273 is_theme, install_silently);
274}
275
276void ExtensionsService::AddPendingExtensionInternal(
277 const std::string& id, const GURL& update_url,
278 const Version& version, bool is_theme, bool install_silently) {
279 pending_extensions_[id] =
280 PendingExtensionInfo(update_url, version, is_theme, install_silently);
281}
282
[email protected]9cddd4702009-07-27 22:09:40283void ExtensionsService::ReloadExtension(const std::string& extension_id) {
[email protected]b65272f2009-08-31 15:47:06284 FilePath path;
[email protected]61b411612009-11-10 23:17:41285 Extension* current_extension = GetExtensionById(extension_id, false);
[email protected]9cddd4702009-07-27 22:09:40286
[email protected]b65272f2009-08-31 15:47:06287 // Unload the extension if it's loaded. It might not be loaded if it crashed.
288 if (current_extension) {
[email protected]4814b512009-11-07 00:12:29289 // If the extension has an inspector open for its background page, detach
290 // the inspector and hang onto a cookie for it, so that we can reattach
291 // later.
292 ExtensionProcessManager* manager = profile_->GetExtensionProcessManager();
293 ExtensionHost* host = manager->GetBackgroundHostForExtension(
294 current_extension);
295 if (host) {
296 // Look for an open inspector for the background page.
297 int devtools_cookie = DevToolsManager::GetInstance()->DetachClientHost(
298 host->render_view_host());
299 if (devtools_cookie >= 0)
300 orphaned_dev_tools_[extension_id] = devtools_cookie;
301 }
302
[email protected]b65272f2009-08-31 15:47:06303 path = current_extension->path();
304 UnloadExtension(extension_id);
[email protected]1eb175082010-02-10 09:26:16305 } else {
306 path = unloaded_extension_paths_[extension_id];
[email protected]b65272f2009-08-31 15:47:06307 }
308
[email protected]e6090e42010-03-23 22:44:08309 // Check the installed extensions to see if what we're reloading was already
310 // installed.
311 scoped_ptr<ExtensionInfo> installed_extension(
312 extension_prefs_->GetInstalledExtensionInfo(extension_id));
313 if (installed_extension.get() &&
314 installed_extension->extension_manifest.get()) {
315 LoadInstalledExtension(*installed_extension, false);
316 } else {
317 // We should always be able to remember the extension's path. If it's not in
318 // the map, someone failed to update |unloaded_extension_paths_|.
319 CHECK(!path.empty());
320 LoadExtension(path);
321 }
[email protected]9cddd4702009-07-27 22:09:40322}
323
[email protected]27b985d2009-06-25 17:53:15324void ExtensionsService::UninstallExtension(const std::string& extension_id,
325 bool external_uninstall) {
[email protected]0c6da502009-08-14 22:32:39326 Extension* extension = GetExtensionByIdInternal(extension_id, true, true);
[email protected]631cf822009-05-15 07:01:25327
[email protected]9f1087e2009-06-15 17:29:32328 // Callers should not send us nonexistant extensions.
[email protected]e72e8eb82009-06-18 17:21:51329 DCHECK(extension);
[email protected]9f1087e2009-06-15 17:29:32330
[email protected]831aa212010-03-26 13:55:19331 // Get hold of information we need after unloading, since the extension
332 // pointer will be invalid then.
333 GURL extension_url(extension->url());
334 Extension::Location location(extension->location());
335
336 // Also copy the extension identifier since the reference might have been
337 // obtained via Extension::id().
338 std::string extension_id_copy(extension_id);
339
340 // Unload before doing more cleanup to ensure that nothing is hanging on to
341 // any of these resources.
342 UnloadExtension(extension_id);
343
344 extension_prefs_->OnExtensionUninstalled(extension_id_copy, location,
345 external_uninstall);
[email protected]9f1087e2009-06-15 17:29:32346
347 // Tell the backend to start deleting installed extensions on the file thread.
[email protected]831aa212010-03-26 13:55:19348 if (Extension::LOAD != location) {
[email protected]95d29192009-10-30 01:49:06349 ChromeThread::PostTask(
350 ChromeThread::FILE, FROM_HERE,
351 NewRunnableFunction(
[email protected]831aa212010-03-26 13:55:19352 &extension_file_util::UninstallExtension, extension_id_copy,
[email protected]95d29192009-10-30 01:49:06353 install_directory_));
[email protected]9f1087e2009-06-15 17:29:32354 }
355
[email protected]c10da4b02010-03-25 14:38:32356 ClearExtensionData(extension_url);
357}
358
359void ExtensionsService::ClearExtensionData(const GURL& extension_url) {
360 scoped_refptr<ExtensionDataDeleter> deleter(
361 new ExtensionDataDeleter(profile_, extension_url));
362 deleter->StartDeleting();
[email protected]9f1087e2009-06-15 17:29:32363}
364
[email protected]0c6da502009-08-14 22:32:39365void ExtensionsService::EnableExtension(const std::string& extension_id) {
366 Extension* extension = GetExtensionByIdInternal(extension_id, false, true);
367 if (!extension) {
368 NOTREACHED() << "Trying to enable an extension that isn't disabled.";
369 return;
370 }
371
[email protected]e8c729a2010-03-09 19:55:19372 extension_prefs_->SetExtensionState(extension, Extension::ENABLED);
[email protected]1784e83a2009-09-08 21:01:52373
[email protected]0c6da502009-08-14 22:32:39374 // Move it over to the enabled list.
[email protected]0c6da502009-08-14 22:32:39375 extensions_.push_back(extension);
376 ExtensionList::iterator iter = std::find(disabled_extensions_.begin(),
377 disabled_extensions_.end(),
378 extension);
379 disabled_extensions_.erase(iter);
380
[email protected]86c008e82009-08-28 20:26:05381 ExtensionDOMUI::RegisterChromeURLOverrides(profile_,
382 extension->GetChromeURLOverrides());
383
[email protected]62d30f42009-10-01 22:36:06384 NotifyExtensionLoaded(extension);
[email protected]aab98a52009-12-02 03:22:35385 UpdateActiveExtensionsInCrashReporter();
[email protected]0c6da502009-08-14 22:32:39386}
387
[email protected]1784e83a2009-09-08 21:01:52388void ExtensionsService::DisableExtension(const std::string& extension_id) {
389 Extension* extension = GetExtensionByIdInternal(extension_id, true, false);
[email protected]b2ba9962009-12-10 20:10:15390 // The extension may have been disabled already.
391 if (!extension)
[email protected]1784e83a2009-09-08 21:01:52392 return;
[email protected]1784e83a2009-09-08 21:01:52393
[email protected]e8c729a2010-03-09 19:55:19394 extension_prefs_->SetExtensionState(extension, Extension::DISABLED);
[email protected]1784e83a2009-09-08 21:01:52395
396 // Move it over to the disabled list.
397 disabled_extensions_.push_back(extension);
398 ExtensionList::iterator iter = std::find(extensions_.begin(),
399 extensions_.end(),
400 extension);
401 extensions_.erase(iter);
402
403 ExtensionDOMUI::UnregisterChromeURLOverrides(profile_,
404 extension->GetChromeURLOverrides());
405
[email protected]62d30f42009-10-01 22:36:06406 NotifyExtensionUnloaded(extension);
[email protected]aab98a52009-12-02 03:22:35407 UpdateActiveExtensionsInCrashReporter();
[email protected]1784e83a2009-09-08 21:01:52408}
409
[email protected]9f1087e2009-06-15 17:29:32410void ExtensionsService::LoadExtension(const FilePath& extension_path) {
[email protected]95d29192009-10-30 01:49:06411 ChromeThread::PostTask(
412 ChromeThread::FILE, FROM_HERE,
413 NewRunnableMethod(
414 backend_.get(),
415 &ExtensionsServiceBackend::LoadSingleExtension,
416 extension_path, scoped_refptr<ExtensionsService>(this)));
[email protected]9f1087e2009-06-15 17:29:32417}
418
[email protected]1952c7d2010-03-04 23:48:34419void ExtensionsService::LoadComponentExtensions() {
420 for (RegisteredComponentExtensions::iterator it =
421 component_extension_manifests_.begin();
422 it != component_extension_manifests_.end(); ++it) {
423 JSONStringValueSerializer serializer(it->manifest);
424 scoped_ptr<Value> manifest(serializer.Deserialize(NULL));
[email protected]999731f2010-03-22 19:13:53425 if (!manifest.get()) {
426 NOTREACHED() << "Failed to retrieve manifest for extension";
427 continue;
428 }
[email protected]1952c7d2010-03-04 23:48:34429
430 scoped_ptr<Extension> extension(new Extension(it->root_directory));
431 extension->set_location(Extension::COMPONENT);
432
433 std::string error;
434 if (!extension->InitFromValue(
435 *static_cast<DictionaryValue*>(manifest.get()),
436 true, // require key
437 &error)) {
438 NOTREACHED();
439 return;
440 }
441
442 OnExtensionLoaded(extension.release(), false); // Don't allow privilege
443 // increase.
444 }
445}
446
[email protected]9f1087e2009-06-15 17:29:32447void ExtensionsService::LoadAllExtensions() {
[email protected]cc2c3432009-11-06 17:24:36448 base::TimeTicks start_time = base::TimeTicks::Now();
449
[email protected]1952c7d2010-03-04 23:48:34450 // Load any component extensions.
451 LoadComponentExtensions();
452
[email protected]e72e8eb82009-06-18 17:21:51453 // Load the previously installed extensions.
[email protected]c6d474f82009-12-16 21:11:06454 scoped_ptr<ExtensionPrefs::ExtensionsInfo> info(
[email protected]e6090e42010-03-23 22:44:08455 extension_prefs_->GetInstalledExtensionsInfo());
[email protected]c6d474f82009-12-16 21:11:06456
457 // If any extensions need localization, we bounce them all to the file thread
458 // for re-reading and localization.
459 for (size_t i = 0; i < info->size(); ++i) {
[email protected]2111b1a2010-03-12 18:12:44460 if (ShouldReloadExtensionManifest(*info->at(i))) {
[email protected]c6d474f82009-12-16 21:11:06461 ChromeThread::PostTask(
462 ChromeThread::FILE, FROM_HERE, NewRunnableMethod(
463 backend_.get(),
[email protected]2111b1a2010-03-12 18:12:44464 &ExtensionsServiceBackend::ReloadExtensionManifests,
[email protected]c6d474f82009-12-16 21:11:06465 info.release(), // Callee takes ownership of the memory.
466 start_time,
467 scoped_refptr<ExtensionsService>(this)));
468 return;
469 }
470 }
471
472 // Don't update prefs.
473 // Callee takes ownership of the memory.
474 ContinueLoadAllExtensions(info.release(), start_time, false);
475}
476
477void ExtensionsService::ContinueLoadAllExtensions(
478 ExtensionPrefs::ExtensionsInfo* extensions_info,
479 base::TimeTicks start_time,
480 bool write_to_prefs) {
481 scoped_ptr<ExtensionPrefs::ExtensionsInfo> info(extensions_info);
482
483 for (size_t i = 0; i < info->size(); ++i) {
484 LoadInstalledExtension(*info->at(i), write_to_prefs);
485 }
486
[email protected]ae09ca62009-08-21 19:46:46487 OnLoadedInstalledExtensions();
[email protected]cc2c3432009-11-06 17:24:36488
489 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadAll", extensions_.size());
490 UMA_HISTOGRAM_COUNTS_100("Extensions.Disabled", disabled_extensions_.size());
491
[email protected]1952c7d2010-03-04 23:48:34492 UMA_HISTOGRAM_TIMES("Extensions.LoadAllTime",
493 base::TimeTicks::Now() - start_time);
[email protected]cc2c3432009-11-06 17:24:36494
[email protected]1952c7d2010-03-04 23:48:34495 int user_script_count = 0;
496 int extension_count = 0;
497 int theme_count = 0;
498 int external_count = 0;
499 int page_action_count = 0;
500 int browser_action_count = 0;
501 ExtensionList::iterator ex;
502 for (ex = extensions_.begin(); ex != extensions_.end(); ++ex) {
503 // Don't count component extensions, since they are only extensions as an
504 // implementation detail.
505 if ((*ex)->location() == Extension::COMPONENT)
506 continue;
507
[email protected]e8c729a2010-03-09 19:55:19508 // Don't count unpacked extensions, since they're a developer-specific
509 // feature.
510 if ((*ex)->location() == Extension::LOAD)
511 continue;
512
[email protected]1952c7d2010-03-04 23:48:34513 if ((*ex)->IsTheme()) {
514 theme_count++;
515 } else if ((*ex)->converted_from_user_script()) {
516 user_script_count++;
517 } else {
518 extension_count++;
[email protected]cc2c3432009-11-06 17:24:36519 }
[email protected]1952c7d2010-03-04 23:48:34520 if (Extension::IsExternalLocation((*ex)->location())) {
521 external_count++;
522 }
523 if ((*ex)->page_action() != NULL) {
524 page_action_count++;
525 }
526 if ((*ex)->browser_action() != NULL) {
527 browser_action_count++;
528 }
[email protected]cc2c3432009-11-06 17:24:36529 }
[email protected]1952c7d2010-03-04 23:48:34530 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadExtension", extension_count);
531 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadUserScript", user_script_count);
532 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadTheme", theme_count);
533 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadExternal", external_count);
534 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadPageAction", page_action_count);
535 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadBrowserAction",
536 browser_action_count);
[email protected]ae09ca62009-08-21 19:46:46537}
538
[email protected]c6d474f82009-12-16 21:11:06539void ExtensionsService::LoadInstalledExtension(const ExtensionInfo& info,
540 bool write_to_prefs) {
[email protected]ae09ca62009-08-21 19:46:46541 std::string error;
542 Extension* extension = NULL;
[email protected]c6d474f82009-12-16 21:11:06543 if (info.extension_manifest.get()) {
544 scoped_ptr<Extension> tmp(new Extension(info.extension_path));
[email protected]e8c729a2010-03-09 19:55:19545 bool require_key = info.extension_location != Extension::LOAD;
546 if (tmp->InitFromValue(*info.extension_manifest, require_key, &error))
[email protected]ae09ca62009-08-21 19:46:46547 extension = tmp.release();
[email protected]ae09ca62009-08-21 19:46:46548 } else {
[email protected]c6d474f82009-12-16 21:11:06549 error = errors::kManifestUnreadable;
[email protected]ae09ca62009-08-21 19:46:46550 }
551
552 if (!extension) {
[email protected]c6d474f82009-12-16 21:11:06553 ReportExtensionLoadError(info.extension_path,
[email protected]d11c8e92009-10-20 23:26:40554 error,
555 NotificationType::EXTENSION_INSTALL_ERROR,
556 false);
[email protected]ae09ca62009-08-21 19:46:46557 return;
558 }
559
[email protected]c6d474f82009-12-16 21:11:06560 extension->set_location(info.extension_location);
561
562 if (write_to_prefs)
563 extension_prefs_->UpdateManifest(extension);
564
[email protected]2a409532009-08-28 19:39:44565 OnExtensionLoaded(extension, true);
[email protected]ae09ca62009-08-21 19:46:46566
[email protected]c6d474f82009-12-16 21:11:06567 if (info.extension_location == Extension::EXTERNAL_PREF ||
568 info.extension_location == Extension::EXTERNAL_REGISTRY) {
[email protected]95d29192009-10-30 01:49:06569 ChromeThread::PostTask(
570 ChromeThread::FILE, FROM_HERE,
571 NewRunnableMethod(
[email protected]c6d474f82009-12-16 21:11:06572 backend_.get(),
573 &ExtensionsServiceBackend::CheckExternalUninstall,
574 scoped_refptr<ExtensionsService>(this),
575 info.extension_id,
576 info.extension_location));
[email protected]ae09ca62009-08-21 19:46:46577 }
[email protected]9f1087e2009-06-15 17:29:32578}
579
[email protected]62d30f42009-10-01 22:36:06580void ExtensionsService::NotifyExtensionLoaded(Extension* extension) {
581 LOG(INFO) << "Sending EXTENSION_LOADED";
582
583 // The ChromeURLRequestContext needs to be first to know that the extension
584 // was loaded, otherwise a race can arise where a renderer that is created
585 // for the extension may try to load an extension URL with an extension id
586 // that the request context doesn't yet know about.
587 if (profile_ && !profile_->IsOffTheRecord()) {
[email protected]be180c802009-10-23 06:33:31588 ChromeURLRequestContextGetter* context_getter =
589 static_cast<ChromeURLRequestContextGetter*>(
590 profile_->GetRequestContext());
591 if (context_getter) {
[email protected]95d29192009-10-30 01:49:06592 ChromeThread::PostTask(
593 ChromeThread::IO, FROM_HERE,
594 NewRunnableMethod(
595 context_getter,
596 &ChromeURLRequestContextGetter::OnNewExtensions,
597 extension->id(),
[email protected]0ce3f5982010-01-28 23:04:27598 new ChromeURLRequestContext::ExtensionInfo(
599 extension->path(),
600 extension->default_locale(),
[email protected]867a73e12010-03-19 20:45:46601 std::vector<URLPattern>(),
[email protected]0ce3f5982010-01-28 23:04:27602 extension->api_permissions())));
[email protected]62d30f42009-10-01 22:36:06603 }
[email protected]24b538a2010-02-27 01:22:44604
605 // Check if this permission requires unlimited storage quota
606 if (extension->HasApiPermission(Extension::kUnlimitedStoragePermission)) {
607 string16 origin_identifier =
608 webkit_database::DatabaseUtil::GetOriginIdentifier(extension->url());
609 ChromeThread::PostTask(
610 ChromeThread::FILE, FROM_HERE,
611 NewRunnableMethod(
612 profile_->GetDatabaseTracker(),
613 &webkit_database::DatabaseTracker::SetOriginQuotaInMemory,
614 origin_identifier,
615 kint64max));
616 }
[email protected]62d30f42009-10-01 22:36:06617 }
618
619 NotificationService::current()->Notify(
620 NotificationType::EXTENSION_LOADED,
[email protected]24e7a9d2009-11-04 11:11:34621 Source<Profile>(profile_),
[email protected]62d30f42009-10-01 22:36:06622 Details<Extension>(extension));
623}
624
625void ExtensionsService::NotifyExtensionUnloaded(Extension* extension) {
626 LOG(INFO) << "Sending EXTENSION_UNLOADED";
627
628 NotificationService::current()->Notify(
629 NotificationType::EXTENSION_UNLOADED,
[email protected]24e7a9d2009-11-04 11:11:34630 Source<Profile>(profile_),
[email protected]62d30f42009-10-01 22:36:06631 Details<Extension>(extension));
632
633 if (profile_ && !profile_->IsOffTheRecord()) {
[email protected]be180c802009-10-23 06:33:31634 ChromeURLRequestContextGetter* context_getter =
635 static_cast<ChromeURLRequestContextGetter*>(
636 profile_->GetRequestContext());
637 if (context_getter) {
[email protected]95d29192009-10-30 01:49:06638 ChromeThread::PostTask(
639 ChromeThread::IO, FROM_HERE,
[email protected]be180c802009-10-23 06:33:31640 NewRunnableMethod(
641 context_getter,
642 &ChromeURLRequestContextGetter::OnUnloadedExtension,
643 extension->id()));
[email protected]62d30f42009-10-01 22:36:06644 }
645 }
646}
647
[email protected]6b75ec32009-08-14 06:37:18648void ExtensionsService::UpdateExtensionBlacklist(
649 const std::vector<std::string>& blacklist) {
650 // Use this set to indicate if an extension in the blacklist has been used.
651 std::set<std::string> blacklist_set;
652 for (unsigned int i = 0; i < blacklist.size(); ++i) {
653 if (Extension::IdIsValid(blacklist[i])) {
654 blacklist_set.insert(blacklist[i]);
655 }
656 }
657 extension_prefs_->UpdateBlacklist(blacklist_set);
658 std::vector<std::string> to_be_removed;
659 // Loop current extensions, unload installed extensions.
660 for (ExtensionList::const_iterator iter = extensions_.begin();
661 iter != extensions_.end(); ++iter) {
662 Extension* extension = (*iter);
663 if (blacklist_set.find(extension->id()) != blacklist_set.end()) {
664 to_be_removed.push_back(extension->id());
665 }
666 }
667
668 // UnloadExtension will change the extensions_ list. So, we should
669 // call it outside the iterator loop.
670 for (unsigned int i = 0; i < to_be_removed.size(); ++i) {
671 UnloadExtension(to_be_removed[i]);
672 }
673}
674
[email protected]5ef47ec2010-01-28 05:58:05675void ExtensionsService::SetLastPingDay(const std::string& extension_id,
676 const base::Time& time) {
677 extension_prefs_->SetLastPingDay(extension_id, time);
678}
679
680base::Time ExtensionsService::LastPingDay(const std::string& extension_id) {
681 return extension_prefs_->LastPingDay(extension_id);
682}
683
[email protected]cb0ce1e022010-03-10 19:54:41684bool ExtensionsService::IsIncognitoEnabled(const Extension* extension) {
685 // If this is a component extension we always allow it to work in incognito
686 // mode.
687 if (extension->location() == Extension::COMPONENT)
688 return true;
689
690 // Check the prefs.
691 return extension_prefs_->IsIncognitoEnabled(extension->id());
[email protected]db7331a2010-02-25 22:10:50692}
[email protected]55a35692010-02-11 23:25:21693
[email protected]cb0ce1e022010-03-10 19:54:41694void ExtensionsService::SetIsIncognitoEnabled(Extension* extension,
[email protected]db7331a2010-02-25 22:10:50695 bool enabled) {
[email protected]cb0ce1e022010-03-10 19:54:41696 extension_prefs_->SetIsIncognitoEnabled(extension->id(), enabled);
[email protected]c1499f3d2010-03-05 00:33:24697
698 // Broadcast unloaded and loaded events to update browser state.
699 NotifyExtensionUnloaded(extension);
700 NotifyExtensionLoaded(extension);
[email protected]55a35692010-02-11 23:25:21701}
702
[email protected]93fd78f42009-07-10 16:43:17703void ExtensionsService::CheckForExternalUpdates() {
[email protected]9f1087e2009-06-15 17:29:32704 // This installs or updates externally provided extensions.
[email protected]7577a5c52009-07-30 06:21:58705 // TODO(aa): Why pass this list into the provider, why not just filter it
706 // later?
[email protected]9f1087e2009-06-15 17:29:32707 std::set<std::string> killed_extensions;
[email protected]e72e8eb82009-06-18 17:21:51708 extension_prefs_->GetKilledExtensionIds(&killed_extensions);
[email protected]95d29192009-10-30 01:49:06709 ChromeThread::PostTask(
710 ChromeThread::FILE, FROM_HERE,
711 NewRunnableMethod(
712 backend_.get(), &ExtensionsServiceBackend::CheckForExternalUpdates,
713 killed_extensions, scoped_refptr<ExtensionsService>(this)));
[email protected]9f1087e2009-06-15 17:29:32714}
715
716void ExtensionsService::UnloadExtension(const std::string& extension_id) {
[email protected]27e469a2010-01-11 20:35:09717 // Make sure the extension gets deleted after we return from this function.
[email protected]0c6da502009-08-14 22:32:39718 scoped_ptr<Extension> extension(
719 GetExtensionByIdInternal(extension_id, true, true));
[email protected]631cf822009-05-15 07:01:25720
[email protected]894bb502009-05-21 22:39:57721 // Callers should not send us nonexistant extensions.
[email protected]0c6da502009-08-14 22:32:39722 CHECK(extension.get());
723
[email protected]1eb175082010-02-10 09:26:16724 // Keep information about the extension so that we can reload it later
725 // even if it's not permanently installed.
726 unloaded_extension_paths_[extension->id()] = extension->path();
727
[email protected]86c008e82009-08-28 20:26:05728 ExtensionDOMUI::UnregisterChromeURLOverrides(profile_,
729 extension->GetChromeURLOverrides());
730
[email protected]0c6da502009-08-14 22:32:39731 ExtensionList::iterator iter = std::find(disabled_extensions_.begin(),
732 disabled_extensions_.end(),
733 extension.get());
734 if (iter != disabled_extensions_.end()) {
[email protected]0c6da502009-08-14 22:32:39735 disabled_extensions_.erase(iter);
[email protected]866930682009-08-18 22:53:47736 NotificationService::current()->Notify(
737 NotificationType::EXTENSION_UNLOADED_DISABLED,
[email protected]24e7a9d2009-11-04 11:11:34738 Source<Profile>(profile_),
[email protected]866930682009-08-18 22:53:47739 Details<Extension>(extension.get()));
[email protected]0c6da502009-08-14 22:32:39740 return;
741 }
742
743 iter = std::find(extensions_.begin(), extensions_.end(), extension.get());
[email protected]894bb502009-05-21 22:39:57744
[email protected]631cf822009-05-15 07:01:25745 // Remove the extension from our list.
746 extensions_.erase(iter);
747
[email protected]62d30f42009-10-01 22:36:06748 NotifyExtensionUnloaded(extension.get());
[email protected]aab98a52009-12-02 03:22:35749 UpdateActiveExtensionsInCrashReporter();
[email protected]631cf822009-05-15 07:01:25750}
751
[email protected]9f1087e2009-06-15 17:29:32752void ExtensionsService::UnloadAllExtensions() {
753 ExtensionList::iterator iter;
[email protected]c6e4a3412009-06-24 15:45:29754 for (iter = extensions_.begin(); iter != extensions_.end(); ++iter)
[email protected]9f1087e2009-06-15 17:29:32755 delete *iter;
[email protected]9f1087e2009-06-15 17:29:32756 extensions_.clear();
[email protected]c6e4a3412009-06-24 15:45:29757
758 // TODO(erikkay) should there be a notification for this? We can't use
759 // EXTENSION_UNLOADED since that implies that the extension has been disabled
760 // or uninstalled, and UnloadAll is just part of shutdown.
[email protected]9f1087e2009-06-15 17:29:32761}
762
763void ExtensionsService::ReloadExtensions() {
764 UnloadAllExtensions();
765 LoadAllExtensions();
766}
767
768void ExtensionsService::GarbageCollectExtensions() {
[email protected]c6d474f82009-12-16 21:11:06769 InstalledExtensionSet installed(extension_prefs_.get());
[email protected]95d29192009-10-30 01:49:06770 ChromeThread::PostTask(
771 ChromeThread::FILE, FROM_HERE,
772 NewRunnableFunction(
773 &extension_file_util::GarbageCollectExtensions, install_directory_,
[email protected]4559a7d2009-12-02 01:42:41774 installed.extensions(), installed.versions()));
[email protected]3cf4f0992009-02-03 23:00:30775}
776
[email protected]e72e8eb82009-06-18 17:21:51777void ExtensionsService::OnLoadedInstalledExtensions() {
[email protected]e81dba32009-06-19 20:19:13778 ready_ = true;
[email protected]93fd78f42009-07-10 16:43:17779 if (updater_.get()) {
780 updater_->Start();
781 }
[email protected]e72e8eb82009-06-18 17:21:51782 NotificationService::current()->Notify(
783 NotificationType::EXTENSIONS_READY,
[email protected]24e7a9d2009-11-04 11:11:34784 Source<Profile>(profile_),
[email protected]e72e8eb82009-06-18 17:21:51785 NotificationService::NoDetails());
786}
787
[email protected]2a409532009-08-28 19:39:44788void ExtensionsService::OnExtensionLoaded(Extension* extension,
789 bool allow_privilege_increase) {
[email protected]ae09ca62009-08-21 19:46:46790 // Ensure extension is deleted unless we transfer ownership.
791 scoped_ptr<Extension> scoped_extension(extension);
[email protected]9f1087e2009-06-15 17:29:32792
[email protected]1eb175082010-02-10 09:26:16793 // The extension is now loaded, remove its data from unloaded extension map.
794 unloaded_extension_paths_.erase(extension->id());
795
[email protected]ceefd3d2010-03-12 09:10:29796 // TODO(aa): Need to re-evaluate this branch. Does this still make sense now
797 // that extensions are enabled by default?
[email protected]ae09ca62009-08-21 19:46:46798 if (extensions_enabled() ||
799 extension->IsTheme() ||
800 extension->location() == Extension::LOAD ||
801 Extension::IsExternalLocation(extension->location())) {
802 Extension* old = GetExtensionByIdInternal(extension->id(), true, true);
803 if (old) {
804 if (extension->version()->CompareTo(*(old->version())) > 0) {
[email protected]2a409532009-08-28 19:39:44805 bool allow_silent_upgrade =
806 allow_privilege_increase || !Extension::IsPrivilegeIncrease(
807 old, extension);
[email protected]0c6da502009-08-14 22:32:39808
[email protected]1e8c93f2010-02-08 22:58:31809 // Extensions get upgraded if silent upgrades are allowed, otherwise
810 // they get disabled.
811 if (allow_silent_upgrade) {
812 old->set_being_upgraded(true);
813 extension->set_being_upgraded(true);
814 }
815
[email protected]ae09ca62009-08-21 19:46:46816 // To upgrade an extension in place, unload the old one and
817 // then load the new one.
818 UnloadExtension(old->id());
819 old = NULL;
[email protected]0c6da502009-08-14 22:32:39820
[email protected]b24d8312009-08-27 06:47:46821 if (!allow_silent_upgrade) {
[email protected]6d27a7b2009-12-18 23:25:45822 // Extension has changed permissions significantly. Disable it. We
823 // send a notification below.
[email protected]ae09ca62009-08-21 19:46:46824 extension_prefs_->SetExtensionState(extension, Extension::DISABLED);
[email protected]e6090e42010-03-23 22:44:08825 extension_prefs_->SetDidExtensionEscalatePermissions(extension, true);
[email protected]9f1087e2009-06-15 17:29:32826 }
[email protected]ae09ca62009-08-21 19:46:46827 } else {
828 // We already have the extension of the same or older version.
[email protected]d11c8e92009-10-20 23:26:40829 std::string error_message("Duplicate extension load attempt: ");
830 error_message += extension->id();
831 LOG(WARNING) << error_message;
832 ReportExtensionLoadError(extension->path(),
833 error_message,
834 NotificationType::EXTENSION_OVERINSTALL_ERROR,
835 false);
[email protected]ae09ca62009-08-21 19:46:46836 return;
[email protected]0c6da502009-08-14 22:32:39837 }
[email protected]ba74f352009-06-11 18:54:45838 }
[email protected]86a274072009-06-11 02:06:45839
[email protected]ae09ca62009-08-21 19:46:46840 switch (extension_prefs_->GetExtensionState(extension->id())) {
841 case Extension::ENABLED:
842 extensions_.push_back(scoped_extension.release());
843
[email protected]aeb53b32009-10-29 07:34:45844 // We delay starting up the browser event router until at least one
845 // extension that needs it is loaded.
846 if (extension->HasApiPermission(Extension::kTabPermission)) {
847 ExtensionBrowserEventRouter::GetInstance()->Init();
848 }
[email protected]840b0db2009-11-20 03:00:38849 if (extension->HasApiPermission(Extension::kBookmarkPermission)) {
850 ExtensionBookmarkEventRouter::GetSingleton()->Observe(
851 profile_->GetBookmarkModel());
852 }
[email protected]aeb53b32009-10-29 07:34:45853
[email protected]62d30f42009-10-01 22:36:06854 NotifyExtensionLoaded(extension);
[email protected]ae09ca62009-08-21 19:46:46855
[email protected]e8c729a2010-03-09 19:55:19856 ExtensionDOMUI::RegisterChromeURLOverrides(profile_,
857 extension->GetChromeURLOverrides());
[email protected]ae09ca62009-08-21 19:46:46858 break;
859 case Extension::DISABLED:
[email protected]6d27a7b2009-12-18 23:25:45860 disabled_extensions_.push_back(scoped_extension.release());
[email protected]d11c8e92009-10-20 23:26:40861 NotificationService::current()->Notify(
862 NotificationType::EXTENSION_UPDATE_DISABLED,
[email protected]24e7a9d2009-11-04 11:11:34863 Source<Profile>(profile_),
[email protected]d11c8e92009-10-20 23:26:40864 Details<Extension>(extension));
[email protected]ae09ca62009-08-21 19:46:46865 break;
866 default:
[email protected]d11c8e92009-10-20 23:26:40867 NOTREACHED();
[email protected]ae09ca62009-08-21 19:46:46868 break;
[email protected]811f3432009-07-25 19:38:21869 }
[email protected]e72e8eb82009-06-18 17:21:51870 }
[email protected]aab98a52009-12-02 03:22:35871
[email protected]1e8c93f2010-02-08 22:58:31872 extension->set_being_upgraded(false);
873
[email protected]aab98a52009-12-02 03:22:35874 UpdateActiveExtensionsInCrashReporter();
875}
876
877void ExtensionsService::UpdateActiveExtensionsInCrashReporter() {
[email protected]c8865962009-12-16 07:47:39878 std::set<std::string> extension_ids;
[email protected]aab98a52009-12-02 03:22:35879 for (size_t i = 0; i < extensions_.size(); ++i) {
880 if (!extensions_[i]->IsTheme())
[email protected]c8865962009-12-16 07:47:39881 extension_ids.insert(extensions_[i]->id());
[email protected]aab98a52009-12-02 03:22:35882 }
883
884 child_process_logging::SetActiveExtensions(extension_ids);
[email protected]6014d672008-12-05 00:38:25885}
886
[email protected]2a409532009-08-28 19:39:44887void ExtensionsService::OnExtensionInstalled(Extension* extension,
888 bool allow_privilege_increase) {
[email protected]aa142702010-03-26 01:26:33889 PendingExtensionMap::iterator it =
890 pending_extensions_.find(extension->id());
891 if (it != pending_extensions_.end() &&
892 (it->second.is_theme != extension->IsTheme())) {
893 LOG(WARNING) << "Not installing pending extension " << extension->id()
894 << " with is_theme = " << extension->IsTheme()
895 << "; expected is_theme = " << it->second.is_theme;
896 // Delete the extension directory since we're not going to load
897 // it.
898 ChromeThread::PostTask(
899 ChromeThread::FILE, FROM_HERE,
900 NewRunnableFunction(&DeleteFileHelper, extension->path(), true));
901 delete extension;
902 return;
903 }
904
[email protected]b6ab96d2009-08-20 18:58:19905 extension_prefs_->OnExtensionInstalled(extension);
[email protected]25b34332009-06-05 21:53:19906
[email protected]4a190632009-05-09 01:07:42907 // If the extension is a theme, tell the profile (and therefore ThemeProvider)
908 // to apply it.
909 if (extension->IsTheme()) {
[email protected]9ceb07342009-07-26 04:09:23910 NotificationService::current()->Notify(
911 NotificationType::THEME_INSTALLED,
[email protected]24e7a9d2009-11-04 11:11:34912 Source<Profile>(profile_),
[email protected]9ceb07342009-07-26 04:09:23913 Details<Extension>(extension));
[email protected]9197f3b2009-06-02 00:49:27914 } else {
915 NotificationService::current()->Notify(
916 NotificationType::EXTENSION_INSTALLED,
[email protected]24e7a9d2009-11-04 11:11:34917 Source<Profile>(profile_),
[email protected]9197f3b2009-06-02 00:49:27918 Details<Extension>(extension));
[email protected]4a190632009-05-09 01:07:42919 }
[email protected]7577a5c52009-07-30 06:21:58920
921 // Also load the extension.
[email protected]2a409532009-08-28 19:39:44922 OnExtensionLoaded(extension, allow_privilege_increase);
[email protected]aa142702010-03-26 01:26:33923
924 // Erase any pending extension.
925 if (it != pending_extensions_.end()) {
926 pending_extensions_.erase(it);
927 }
[email protected]4a190632009-05-09 01:07:42928}
929
[email protected]7577a5c52009-07-30 06:21:58930void ExtensionsService::OnExtensionOverinstallAttempted(const std::string& id) {
[email protected]61b411612009-11-10 23:17:41931 Extension* extension = GetExtensionById(id, false);
[email protected]4a190632009-05-09 01:07:42932 if (extension && extension->IsTheme()) {
[email protected]9ceb07342009-07-26 04:09:23933 NotificationService::current()->Notify(
934 NotificationType::THEME_INSTALLED,
[email protected]24e7a9d2009-11-04 11:11:34935 Source<Profile>(profile_),
[email protected]9ceb07342009-07-26 04:09:23936 Details<Extension>(extension));
[email protected]91e1bd82009-09-03 22:04:40937 } else {
938 NotificationService::current()->Notify(
939 NotificationType::NO_THEME_DETECTED,
[email protected]24e7a9d2009-11-04 11:11:34940 Source<Profile>(profile_),
[email protected]91e1bd82009-09-03 22:04:40941 NotificationService::NoDetails());
[email protected]4a190632009-05-09 01:07:42942 }
[email protected]cc655912009-01-29 23:19:19943}
944
[email protected]0c6da502009-08-14 22:32:39945Extension* ExtensionsService::GetExtensionByIdInternal(const std::string& id,
946 bool include_enabled,
947 bool include_disabled) {
[email protected]e957fe52009-06-23 16:51:05948 std::string lowercase_id = StringToLowerASCII(id);
[email protected]0c6da502009-08-14 22:32:39949 if (include_enabled) {
950 for (ExtensionList::const_iterator iter = extensions_.begin();
951 iter != extensions_.end(); ++iter) {
952 if ((*iter)->id() == lowercase_id)
953 return *iter;
954 }
955 }
956 if (include_disabled) {
957 for (ExtensionList::const_iterator iter = disabled_extensions_.begin();
958 iter != disabled_extensions_.end(); ++iter) {
959 if ((*iter)->id() == lowercase_id)
960 return *iter;
961 }
[email protected]ce5c4502009-05-06 16:46:11962 }
963 return NULL;
964}
965
[email protected]9f1087e2009-06-15 17:29:32966Extension* ExtensionsService::GetExtensionByURL(const GURL& url) {
967 std::string host = url.host();
[email protected]61b411612009-11-10 23:17:41968 return GetExtensionById(host, false);
[email protected]9f1087e2009-06-15 17:29:32969}
970
[email protected]a1257b12009-06-12 02:51:34971void ExtensionsService::ClearProvidersForTesting() {
[email protected]95d29192009-10-30 01:49:06972 ChromeThread::PostTask(
973 ChromeThread::FILE, FROM_HERE,
974 NewRunnableMethod(
975 backend_.get(), &ExtensionsServiceBackend::ClearProvidersForTesting));
[email protected]a1257b12009-06-12 02:51:34976}
977
978void ExtensionsService::SetProviderForTesting(
979 Extension::Location location, ExternalExtensionProvider* test_provider) {
[email protected]95d29192009-10-30 01:49:06980 ChromeThread::PostTask(
981 ChromeThread::FILE, FROM_HERE,
982 NewRunnableMethod(
983 backend_.get(), &ExtensionsServiceBackend::SetProviderForTesting,
984 location, test_provider));
[email protected]a1257b12009-06-12 02:51:34985}
986
[email protected]7577a5c52009-07-30 06:21:58987void ExtensionsService::OnExternalExtensionFound(const std::string& id,
988 const std::string& version,
989 const FilePath& path,
990 Extension::Location location) {
991 // Before even bothering to unpack, check and see if we already have this
[email protected]4c967932009-07-31 01:15:49992 // version. This is important because these extensions are going to get
[email protected]7577a5c52009-07-30 06:21:58993 // installed on every startup.
[email protected]61b411612009-11-10 23:17:41994 Extension* existing = GetExtensionById(id, true);
[email protected]a3a63ff82009-08-04 06:44:11995 scoped_ptr<Version> other(Version::GetVersionFromString(version));
[email protected]7577a5c52009-07-30 06:21:58996 if (existing) {
[email protected]a3a63ff82009-08-04 06:44:11997 switch (existing->version()->CompareTo(*other)) {
[email protected]7577a5c52009-07-30 06:21:58998 case -1: // existing version is older, we should upgrade
999 break;
1000 case 0: // existing version is same, do nothing
1001 return;
1002 case 1: // existing version is newer, uh-oh
1003 LOG(WARNING) << "Found external version of extension " << id
1004 << "that is older than current version. Current version "
1005 << "is: " << existing->VersionString() << ". New version "
1006 << "is: " << version << ". Keeping current version.";
1007 return;
1008 }
1009 }
1010
[email protected]6dfbbf82010-03-12 23:09:161011 scoped_refptr<CrxInstaller> installer(
1012 new CrxInstaller(install_directory_,
1013 this, // frontend
1014 NULL)); // no client (silent install)
1015 installer->set_install_source(location);
1016 installer->set_expected_id(id);
1017 installer->set_allow_privilege_increase(true);
1018 installer->InstallCrx(path);
[email protected]7577a5c52009-07-30 06:21:581019}
1020
[email protected]d11c8e92009-10-20 23:26:401021void ExtensionsService::ReportExtensionLoadError(
1022 const FilePath& extension_path,
1023 const std::string &error,
1024 NotificationType type,
1025 bool be_noisy) {
1026 NotificationService* service = NotificationService::current();
1027 service->Notify(type,
[email protected]24e7a9d2009-11-04 11:11:341028 Source<Profile>(profile_),
[email protected]d11c8e92009-10-20 23:26:401029 Details<const std::string>(&error));
1030
1031 // TODO(port): note that this isn't guaranteed to work properly on Linux.
[email protected]99efb7b12009-12-18 02:39:161032 std::string path_str = WideToUTF8(extension_path.ToWStringHack());
[email protected]d11c8e92009-10-20 23:26:401033 std::string message = StringPrintf("Could not load extension from '%s'. %s",
1034 path_str.c_str(), error.c_str());
1035 ExtensionErrorReporter::GetInstance()->ReportError(message, be_noisy);
1036}
1037
[email protected]4814b512009-11-07 00:12:291038void ExtensionsService::Observe(NotificationType type,
1039 const NotificationSource& source,
1040 const NotificationDetails& details) {
1041 switch (type.value) {
1042 case NotificationType::EXTENSION_HOST_DID_STOP_LOADING: {
1043 ExtensionHost* host = Details<ExtensionHost>(details).ptr();
1044 OrphanedDevTools::iterator iter =
1045 orphaned_dev_tools_.find(host->extension()->id());
1046 if (iter == orphaned_dev_tools_.end())
1047 return;
1048
1049 DevToolsManager::GetInstance()->AttachClientHost(
1050 iter->second, host->render_view_host());
1051 orphaned_dev_tools_.erase(iter);
1052 break;
1053 }
1054
[email protected]a4ed6282009-12-14 20:51:161055 case NotificationType::EXTENSION_PROCESS_TERMINATED: {
[email protected]31f77262009-12-02 20:48:531056 DCHECK_EQ(profile_, Source<Profile>(source).ptr());
[email protected]a4ed6282009-12-14 20:51:161057
[email protected]31f77262009-12-02 20:48:531058 // Unload the entire extension. We want it to be in a consistent state:
1059 // either fully working or not loaded at all, but never half-crashed.
[email protected]597cceb2010-03-22 16:27:011060 UnloadExtension(Details<ExtensionHost>(details).ptr()->extension()->id());
[email protected]31f77262009-12-02 20:48:531061 break;
1062 }
1063
[email protected]4814b512009-11-07 00:12:291064 default:
1065 NOTREACHED() << "Unexpected notification type.";
1066 }
1067}
1068
1069
[email protected]6014d672008-12-05 00:38:251070// ExtensionsServicesBackend
1071
[email protected]894bb502009-05-21 22:39:571072ExtensionsServiceBackend::ExtensionsServiceBackend(
[email protected]95d29192009-10-30 01:49:061073 const FilePath& install_directory)
[email protected]0c7bc4b2009-05-30 01:47:081074 : frontend_(NULL),
1075 install_directory_(install_directory),
[email protected]95d29192009-10-30 01:49:061076 alert_on_error_(false) {
[email protected]7577a5c52009-07-30 06:21:581077 // TODO(aa): This ends up doing blocking IO on the UI thread because it reads
1078 // pref data in the ctor and that is called on the UI thread. Would be better
1079 // to re-read data each time we list external extensions, anyway.
[email protected]a1257b12009-06-12 02:51:341080 external_extension_providers_[Extension::EXTERNAL_PREF] =
[email protected]da50530a2009-06-15 17:43:011081 linked_ptr<ExternalExtensionProvider>(
[email protected]27b985d2009-06-25 17:53:151082 new ExternalPrefExtensionProvider());
[email protected]a1257b12009-06-12 02:51:341083#if defined(OS_WIN)
1084 external_extension_providers_[Extension::EXTERNAL_REGISTRY] =
[email protected]da50530a2009-06-15 17:43:011085 linked_ptr<ExternalExtensionProvider>(
1086 new ExternalRegistryExtensionProvider());
[email protected]a1257b12009-06-12 02:51:341087#endif
1088}
1089
1090ExtensionsServiceBackend::~ExtensionsServiceBackend() {
[email protected]894bb502009-05-21 22:39:571091}
1092
[email protected]b0beaa662009-02-26 00:04:151093void ExtensionsServiceBackend::LoadSingleExtension(
[email protected]894bb502009-05-21 22:39:571094 const FilePath& path_in, scoped_refptr<ExtensionsService> frontend) {
[email protected]b0beaa662009-02-26 00:04:151095 frontend_ = frontend;
1096
1097 // Explicit UI loads are always noisy.
1098 alert_on_error_ = true;
1099
[email protected]cc5da332009-03-04 08:02:511100 FilePath extension_path = path_in;
[email protected]f36fa4fb2009-06-19 18:23:501101 file_util::AbsolutePath(&extension_path);
[email protected]bf24d2c2009-02-24 23:07:451102
1103 LOG(INFO) << "Loading single extension from " <<
[email protected]99efb7b12009-12-18 02:39:161104 extension_path.BaseName().value();
[email protected]bf24d2c2009-02-24 23:07:451105
[email protected]ab6f2b22009-07-28 23:28:371106 std::string error;
1107 Extension* extension = extension_file_util::LoadExtension(
1108 extension_path,
1109 false, // Don't require id
1110 &error);
1111
1112 if (!extension) {
1113 ReportExtensionLoadError(extension_path, error);
1114 return;
[email protected]0877fd92009-02-03 16:34:061115 }
[email protected]ab6f2b22009-07-28 23:28:371116
1117 extension->set_location(Extension::LOAD);
[email protected]e8c729a2010-03-09 19:55:191118
1119 // Report this as an installed extension so that it gets remembered in the
1120 // prefs.
1121 ChromeThread::PostTask(
1122 ChromeThread::UI, FROM_HERE,
1123 NewRunnableMethod(frontend_, &ExtensionsService::OnExtensionInstalled,
1124 extension, true));
[email protected]0877fd92009-02-03 16:34:061125}
1126
[email protected]6014d672008-12-05 00:38:251127void ExtensionsServiceBackend::ReportExtensionLoadError(
[email protected]cc5da332009-03-04 08:02:511128 const FilePath& extension_path, const std::string &error) {
[email protected]95d29192009-10-30 01:49:061129 ChromeThread::PostTask(
1130 ChromeThread::UI, FROM_HERE,
1131 NewRunnableMethod(
1132 frontend_,
[email protected]d11c8e92009-10-20 23:26:401133 &ExtensionsService::ReportExtensionLoadError, extension_path,
1134 error, NotificationType::EXTENSION_INSTALL_ERROR, alert_on_error_));
[email protected]6014d672008-12-05 00:38:251135}
1136
[email protected]a1257b12009-06-12 02:51:341137bool ExtensionsServiceBackend::LookupExternalExtension(
1138 const std::string& id, Version** version, Extension::Location* location) {
1139 scoped_ptr<Version> extension_version;
1140 for (ProviderMap::const_iterator i = external_extension_providers_.begin();
1141 i != external_extension_providers_.end(); ++i) {
[email protected]da50530a2009-06-15 17:43:011142 const ExternalExtensionProvider* provider = i->second.get();
[email protected]a1257b12009-06-12 02:51:341143 extension_version.reset(provider->RegisteredVersion(id, location));
1144 if (extension_version.get()) {
1145 if (version)
1146 *version = extension_version.release();
1147 return true;
1148 }
1149 }
1150 return false;
1151}
1152
[email protected]b0beaa662009-02-26 00:04:151153// Some extensions will autoupdate themselves externally from Chrome. These
1154// are typically part of some larger client application package. To support
[email protected]25b34332009-06-05 21:53:191155// these, the extension will register its location in the the preferences file
1156// (and also, on Windows, in the registry) and this code will periodically
[email protected]b0beaa662009-02-26 00:04:151157// check that location for a .crx file, which it will then install locally if
1158// a new version is available.
1159void ExtensionsServiceBackend::CheckForExternalUpdates(
[email protected]894bb502009-05-21 22:39:571160 std::set<std::string> ids_to_ignore,
1161 scoped_refptr<ExtensionsService> frontend) {
[email protected]b0beaa662009-02-26 00:04:151162 // Note that this installation is intentionally silent (since it didn't
1163 // go through the front-end). Extensions that are registered in this
1164 // way are effectively considered 'pre-bundled', and so implicitly
1165 // trusted. In general, if something has HKLM or filesystem access,
1166 // they could install an extension manually themselves anyway.
1167 alert_on_error_ = false;
1168 frontend_ = frontend;
[email protected]b0beaa662009-02-26 00:04:151169
[email protected]a1257b12009-06-12 02:51:341170 // Ask each external extension provider to give us a call back for each
1171 // extension they know about. See OnExternalExtensionFound.
1172 for (ProviderMap::const_iterator i = external_extension_providers_.begin();
1173 i != external_extension_providers_.end(); ++i) {
[email protected]da50530a2009-06-15 17:43:011174 ExternalExtensionProvider* provider = i->second.get();
[email protected]a1257b12009-06-12 02:51:341175 provider->VisitRegisteredExtension(this, ids_to_ignore);
[email protected]25b34332009-06-05 21:53:191176 }
[email protected]b0beaa662009-02-26 00:04:151177}
1178
[email protected]ae09ca62009-08-21 19:46:461179void ExtensionsServiceBackend::CheckExternalUninstall(
1180 scoped_refptr<ExtensionsService> frontend, const std::string& id,
1181 Extension::Location location) {
[email protected]a1257b12009-06-12 02:51:341182 // Check if the providers know about this extension.
1183 ProviderMap::const_iterator i = external_extension_providers_.find(location);
[email protected]ae09ca62009-08-21 19:46:461184 if (i == external_extension_providers_.end()) {
1185 NOTREACHED() << "CheckExternalUninstall called for non-external extension "
1186 << location;
1187 return;
[email protected]b0beaa662009-02-26 00:04:151188 }
[email protected]25b34332009-06-05 21:53:191189
[email protected]ae09ca62009-08-21 19:46:461190 scoped_ptr<Version> version;
1191 version.reset(i->second->RegisteredVersion(id, NULL));
1192 if (version.get())
1193 return; // Yup, known extension, don't uninstall.
1194
1195 // This is an external extension that we don't have registered. Uninstall.
[email protected]95d29192009-10-30 01:49:061196 ChromeThread::PostTask(
1197 ChromeThread::UI, FROM_HERE,
1198 NewRunnableMethod(
1199 frontend.get(), &ExtensionsService::UninstallExtension, id, true));
[email protected]b0beaa662009-02-26 00:04:151200}
1201
[email protected]a1257b12009-06-12 02:51:341202void ExtensionsServiceBackend::ClearProvidersForTesting() {
1203 external_extension_providers_.clear();
1204}
1205
1206void ExtensionsServiceBackend::SetProviderForTesting(
1207 Extension::Location location,
1208 ExternalExtensionProvider* test_provider) {
1209 DCHECK(test_provider);
[email protected]da50530a2009-06-15 17:43:011210 external_extension_providers_[location] =
1211 linked_ptr<ExternalExtensionProvider>(test_provider);
[email protected]a1257b12009-06-12 02:51:341212}
1213
1214void ExtensionsServiceBackend::OnExternalExtensionFound(
[email protected]7577a5c52009-07-30 06:21:581215 const std::string& id, const Version* version, const FilePath& path,
1216 Extension::Location location) {
[email protected]95d29192009-10-30 01:49:061217 ChromeThread::PostTask(
1218 ChromeThread::UI, FROM_HERE,
1219 NewRunnableMethod(
1220 frontend_, &ExtensionsService::OnExternalExtensionFound, id,
1221 version->GetString(), path, location));
[email protected]cc655912009-01-29 23:19:191222}
[email protected]c6d474f82009-12-16 21:11:061223
[email protected]2111b1a2010-03-12 18:12:441224void ExtensionsServiceBackend::ReloadExtensionManifests(
[email protected]c6d474f82009-12-16 21:11:061225 ExtensionPrefs::ExtensionsInfo* extensions_to_reload,
1226 base::TimeTicks start_time,
1227 scoped_refptr<ExtensionsService> frontend) {
1228 frontend_ = frontend;
1229
1230 for (size_t i = 0; i < extensions_to_reload->size(); ++i) {
1231 ExtensionInfo* info = extensions_to_reload->at(i).get();
[email protected]2111b1a2010-03-12 18:12:441232 if (!ShouldReloadExtensionManifest(*info))
[email protected]c6d474f82009-12-16 21:11:061233 continue;
1234
1235 // We need to reload original manifest in order to localize properly.
1236 std::string error;
1237 scoped_ptr<Extension> extension(extension_file_util::LoadExtension(
1238 info->extension_path, false, &error));
1239
1240 if (extension.get())
1241 extensions_to_reload->at(i)->extension_manifest.reset(
1242 static_cast<DictionaryValue*>(
1243 extension->manifest_value()->DeepCopy()));
1244 }
1245
1246 // Finish installing on UI thread.
1247 ChromeThread::PostTask(
1248 ChromeThread::UI, FROM_HERE,
1249 NewRunnableMethod(
1250 frontend_,
1251 &ExtensionsService::ContinueLoadAllExtensions,
1252 extensions_to_reload,
1253 start_time,
1254 true));
1255}