blob: 7dddfa986790f0555b2c8f6707b3cfce88b2bca9 [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]e2eb43112009-05-29 21:19:547#include "base/command_line.h"
[email protected]6014d672008-12-05 00:38:258#include "base/file_util.h"
[email protected]cc2c3432009-11-06 17:24:369#include "base/histogram.h"
[email protected]6014d672008-12-05 00:38:2510#include "base/string_util.h"
[email protected]cc2c3432009-11-06 17:24:3611#include "base/time.h"
[email protected]cc655912009-01-29 23:19:1912#include "base/values.h"
[email protected]15730c42009-09-03 00:03:2013#include "chrome/browser/browser_process.h"
[email protected]dbb92e0d2009-08-20 16:18:2114#include "chrome/browser/chrome_thread.h"
[email protected]4814b512009-11-07 00:12:2915#include "chrome/browser/debugger/devtools_manager.h"
[email protected]7577a5c52009-07-30 06:21:5816#include "chrome/browser/extensions/crx_installer.h"
[email protected]5cbe1e22010-01-30 01:18:5617#include "chrome/browser/extensions/extension_accessibility_api.h"
[email protected]840b0db2009-11-20 03:00:3818#include "chrome/browser/extensions/extension_bookmarks_module.h"
[email protected]b68d5ed2009-04-16 02:41:2819#include "chrome/browser/extensions/extension_browser_event_router.h"
[email protected]86c008e82009-08-28 20:26:0520#include "chrome/browser/extensions/extension_dom_ui.h"
[email protected]ab6f2b22009-07-28 23:28:3721#include "chrome/browser/extensions/extension_file_util.h"
[email protected]de768a832009-10-30 05:25:0122#include "chrome/browser/extensions/extension_history_api.h"
[email protected]b1748b1d82009-11-30 20:32:5623#include "chrome/browser/extensions/extension_host.h"
[email protected]4814b512009-11-07 00:12:2924#include "chrome/browser/extensions/extension_process_manager.h"
[email protected]93fd78f42009-07-10 16:43:1725#include "chrome/browser/extensions/extension_updater.h"
[email protected]a1257b12009-06-12 02:51:3426#include "chrome/browser/extensions/external_extension_provider.h"
27#include "chrome/browser/extensions/external_pref_extension_provider.h"
[email protected]81e63782009-02-27 19:35:0928#include "chrome/browser/profile.h"
[email protected]62d30f42009-10-01 22:36:0629#include "chrome/browser/net/chrome_url_request_context.h"
[email protected]aab98a52009-12-02 03:22:3530#include "chrome/common/child_process_logging.h"
[email protected]e2eb43112009-05-29 21:19:5431#include "chrome/common/chrome_switches.h"
[email protected]5b1a0e22009-05-26 19:00:5832#include "chrome/common/extensions/extension.h"
[email protected]d7b36dc2009-10-29 21:47:4033#include "chrome/common/extensions/extension_constants.h"
[email protected]5b1a0e22009-05-26 19:00:5834#include "chrome/common/extensions/extension_error_reporter.h"
[email protected]c6d474f82009-12-16 21:11:0635#include "chrome/common/extensions/extension_l10n_util.h"
[email protected]82891262008-12-24 00:21:2636#include "chrome/common/notification_service.h"
[email protected]4814b512009-11-07 00:12:2937#include "chrome/common/notification_type.h"
[email protected]25b34332009-06-05 21:53:1938#include "chrome/common/pref_names.h"
[email protected]894bb502009-05-21 22:39:5739#include "chrome/common/pref_service.h"
[email protected]a57209872009-05-04 22:53:1440#include "chrome/common/url_constants.h"
[email protected]c64631652009-04-29 22:24:3141
[email protected]79db6232009-02-13 20:51:2042#if defined(OS_WIN)
[email protected]a1257b12009-06-12 02:51:3443#include "chrome/browser/extensions/external_registry_extension_provider_win.h"
[email protected]79db6232009-02-13 20:51:2044#endif
[email protected]6014d672008-12-05 00:38:2545
[email protected]5ef47ec2010-01-28 05:58:0546using base::Time;
47
[email protected]c6d474f82009-12-16 21:11:0648namespace errors = extension_manifest_errors;
49
[email protected]b6ab96d2009-08-20 18:58:1950namespace {
51
52// Helper class to collect the IDs of every extension listed in the prefs.
53class InstalledExtensionSet {
54 public:
[email protected]c6d474f82009-12-16 21:11:0655 explicit InstalledExtensionSet(ExtensionPrefs* prefs) {
56 scoped_ptr<ExtensionPrefs::ExtensionsInfo> info(
57 ExtensionPrefs::CollectExtensionsInfo(prefs));
58
59 for (size_t i = 0; i < info->size(); ++i) {
60 std::string version;
61 const DictionaryValue* manifest = info->at(i)->extension_manifest.get();
62 if (!manifest ||
63 !manifest->GetString(extension_manifest_keys::kVersion, &version)) {
64 // Without a version, the extension is invalid. Ignoring it here will
65 // cause it to get garbage collected.
66 continue;
67 }
68 extensions_.insert(info->at(i)->extension_id);
69 versions_[info->at(i)->extension_id] = version;
70 }
[email protected]b6ab96d2009-08-20 18:58:1971 }
72
73 const std::set<std::string>& extensions() { return extensions_; }
[email protected]4559a7d2009-12-02 01:42:4174 const std::map<std::string, std::string>& versions() { return versions_; }
[email protected]b6ab96d2009-08-20 18:58:1975
76 private:
[email protected]b6ab96d2009-08-20 18:58:1977 std::set<std::string> extensions_;
[email protected]4559a7d2009-12-02 01:42:4178 std::map<std::string, std::string> versions_;
[email protected]b6ab96d2009-08-20 18:58:1979};
80
[email protected]c6d474f82009-12-16 21:11:0681} // namespace
[email protected]b6ab96d2009-08-20 18:58:1982
[email protected]25b34332009-06-05 21:53:1983// ExtensionsService.
[email protected]6014d672008-12-05 00:38:2584
[email protected]cc655912009-01-29 23:19:1985const char* ExtensionsService::kInstallDirectoryName = "Extensions";
86const char* ExtensionsService::kCurrentVersionFileName = "Current Version";
[email protected]494c06e2009-07-25 01:06:4287
[email protected]b7c2f252009-12-08 00:47:2388// static
89bool ExtensionsService::IsDownloadFromGallery(const GURL& download_url,
90 const GURL& referrer_url) {
91 if (StartsWithASCII(download_url.spec(),
92 extension_urls::kMiniGalleryDownloadPrefix, false) &&
93 StartsWithASCII(referrer_url.spec(),
94 extension_urls::kMiniGalleryBrowsePrefix, false)) {
95 return true;
96 }
97
98 if (StartsWithASCII(download_url.spec(),
99 extension_urls::kGalleryDownloadPrefix, false) &&
100 StartsWithASCII(referrer_url.spec(),
101 extension_urls::kGalleryBrowsePrefix, false)) {
102 return true;
103 }
104
105 return false;
106}
107
[email protected]ac025282009-12-16 19:16:38108bool ExtensionsService::IsDownloadFromMiniGallery(const GURL& download_url) {
109 return StartsWithASCII(download_url.spec(),
110 extension_urls::kMiniGalleryDownloadPrefix,
111 false); // case_sensitive
112}
113
[email protected]81e63782009-02-27 19:35:09114ExtensionsService::ExtensionsService(Profile* profile,
[email protected]36a784c2009-06-23 06:21:08115 const CommandLine* command_line,
[email protected]a9b00ac2009-06-25 21:03:23116 PrefService* prefs,
117 const FilePath& install_directory,
[email protected]93fd78f42009-07-10 16:43:17118 bool autoupdate_enabled)
[email protected]6ef635e42009-07-26 06:16:12119 : profile_(profile),
120 extension_prefs_(new ExtensionPrefs(prefs, install_directory)),
[email protected]a9b00ac2009-06-25 21:03:23121 install_directory_(install_directory),
[email protected]6d60703b2009-08-29 01:29:23122 extensions_enabled_(true),
[email protected]e81dba32009-06-19 20:19:13123 show_extensions_prompts_(true),
[email protected]e0360f2c2009-12-07 22:34:31124 ready_(false),
125 ALLOW_THIS_IN_INITIALIZER_LIST(toolbar_model_(this)) {
[email protected]36a784c2009-06-23 06:21:08126 // Figure out if extension installation should be enabled.
[email protected]6d60703b2009-08-29 01:29:23127 if (command_line->HasSwitch(switches::kDisableExtensions)) {
128 extensions_enabled_ = false;
129 } else if (profile->GetPrefs()->GetBoolean(prefs::kDisableExtensions)) {
130 extensions_enabled_ = false;
[email protected]6b75ec32009-08-14 06:37:18131 }
[email protected]36a784c2009-06-23 06:21:08132
[email protected]4814b512009-11-07 00:12:29133 registrar_.Add(this, NotificationType::EXTENSION_HOST_DID_STOP_LOADING,
134 NotificationService::AllSources());
[email protected]a4ed6282009-12-14 20:51:16135 registrar_.Add(this, NotificationType::EXTENSION_PROCESS_TERMINATED,
[email protected]31f77262009-12-02 20:48:53136 Source<Profile>(profile_));
[email protected]4814b512009-11-07 00:12:29137
[email protected]93fd78f42009-07-10 16:43:17138 // Set up the ExtensionUpdater
139 if (autoupdate_enabled) {
140 int update_frequency = kDefaultUpdateFrequencySeconds;
141 if (command_line->HasSwitch(switches::kExtensionsUpdateFrequency)) {
[email protected]c4e52f0d2009-11-06 19:55:16142 update_frequency = StringToInt(command_line->GetSwitchValueASCII(
143 switches::kExtensionsUpdateFrequency));
[email protected]93fd78f42009-07-10 16:43:17144 }
[email protected]95d29192009-10-30 01:49:06145 updater_ = new ExtensionUpdater(this, prefs, update_frequency);
[email protected]93fd78f42009-07-10 16:43:17146 }
147
[email protected]95d29192009-10-30 01:49:06148 backend_ = new ExtensionsServiceBackend(install_directory_);
[email protected]6014d672008-12-05 00:38:25149}
150
151ExtensionsService::~ExtensionsService() {
[email protected]9f1087e2009-06-15 17:29:32152 UnloadAllExtensions();
[email protected]93fd78f42009-07-10 16:43:17153 if (updater_.get()) {
154 updater_->Stop();
155 }
[email protected]6014d672008-12-05 00:38:25156}
157
[email protected]9f1087e2009-06-15 17:29:32158void ExtensionsService::Init() {
[email protected]c6e4a3412009-06-24 15:45:29159 DCHECK(!ready_);
[email protected]93fd78f42009-07-10 16:43:17160 DCHECK_EQ(extensions_.size(), 0u);
[email protected]9f1087e2009-06-15 17:29:32161
[email protected]95dd38f2009-10-20 20:09:15162 // Hack: we need to ensure the ResourceDispatcherHost is ready before we load
163 // the first extension, because its members listen for loaded notifications.
164 g_browser_process->resource_dispatcher_host();
165
[email protected]de768a832009-10-30 05:25:01166 // Start up the extension event routers.
167 ExtensionHistoryEventRouter::GetInstance()->ObserveProfile(profile_);
[email protected]5cbe1e22010-01-30 01:18:56168 ExtensionAccessibilityEventRouter::GetInstance()->ObserveProfile(profile_);
[email protected]de768a832009-10-30 05:25:01169
[email protected]9f1087e2009-06-15 17:29:32170 LoadAllExtensions();
[email protected]894bb502009-05-21 22:39:57171
[email protected]9f1087e2009-06-15 17:29:32172 // TODO(erikkay) this should probably be deferred to a future point
173 // rather than running immediately at startup.
[email protected]93fd78f42009-07-10 16:43:17174 CheckForExternalUpdates();
[email protected]894bb502009-05-21 22:39:57175
[email protected]9f1087e2009-06-15 17:29:32176 // TODO(erikkay) this should probably be deferred as well.
177 GarbageCollectExtensions();
[email protected]6014d672008-12-05 00:38:25178}
179
[email protected]3cf4f0992009-02-03 23:00:30180void ExtensionsService::InstallExtension(const FilePath& extension_path) {
[email protected]2a464a92009-08-01 17:58:35181 CrxInstaller::Start(extension_path, install_directory_, Extension::INTERNAL,
182 "", // no expected id
183 false, // don't delete crx when complete
[email protected]2a409532009-08-28 19:39:44184 true, // allow privilege increase
[email protected]2a464a92009-08-01 17:58:35185 this,
186 NULL); // no client (silent install)
[email protected]3cf4f0992009-02-03 23:00:30187}
188
[email protected]e957fe52009-06-23 16:51:05189void ExtensionsService::UpdateExtension(const std::string& id,
[email protected]7577a5c52009-07-30 06:21:58190 const FilePath& extension_path) {
[email protected]0c6da502009-08-14 22:32:39191 if (!GetExtensionByIdInternal(id, true, true)) {
[email protected]e957fe52009-06-23 16:51:05192 LOG(WARNING) << "Will not update extension " << id << " because it is not "
[email protected]4c967932009-07-31 01:15:49193 << "installed";
194 return;
[email protected]e957fe52009-06-23 16:51:05195 }
196
[email protected]2a464a92009-08-01 17:58:35197 CrxInstaller::Start(extension_path, install_directory_, Extension::INTERNAL,
198 id,
199 true, // delete crx when complete
[email protected]2a409532009-08-28 19:39:44200 false, // do not allow upgrade of privileges
[email protected]2a464a92009-08-01 17:58:35201 this,
202 NULL); // no client (silent install)
[email protected]e957fe52009-06-23 16:51:05203}
204
[email protected]9cddd4702009-07-27 22:09:40205void ExtensionsService::ReloadExtension(const std::string& extension_id) {
[email protected]b65272f2009-08-31 15:47:06206 FilePath path;
[email protected]61b411612009-11-10 23:17:41207 Extension* current_extension = GetExtensionById(extension_id, false);
[email protected]9cddd4702009-07-27 22:09:40208
[email protected]b65272f2009-08-31 15:47:06209 // Unload the extension if it's loaded. It might not be loaded if it crashed.
210 if (current_extension) {
[email protected]4814b512009-11-07 00:12:29211 // If the extension has an inspector open for its background page, detach
212 // the inspector and hang onto a cookie for it, so that we can reattach
213 // later.
214 ExtensionProcessManager* manager = profile_->GetExtensionProcessManager();
215 ExtensionHost* host = manager->GetBackgroundHostForExtension(
216 current_extension);
217 if (host) {
218 // Look for an open inspector for the background page.
219 int devtools_cookie = DevToolsManager::GetInstance()->DetachClientHost(
220 host->render_view_host());
221 if (devtools_cookie >= 0)
222 orphaned_dev_tools_[extension_id] = devtools_cookie;
223 }
224
[email protected]b65272f2009-08-31 15:47:06225 path = current_extension->path();
226 UnloadExtension(extension_id);
[email protected]1eb175082010-02-10 09:26:16227 } else {
228 path = unloaded_extension_paths_[extension_id];
[email protected]b65272f2009-08-31 15:47:06229 }
230
[email protected]1eb175082010-02-10 09:26:16231 // We should always be able to remember the extension's path. If it's not in
232 // the map, someone failed to update |unloaded_extension_paths_|.
233 CHECK(!path.empty());
[email protected]b65272f2009-08-31 15:47:06234
[email protected]1eb175082010-02-10 09:26:16235 LoadExtension(path);
[email protected]9cddd4702009-07-27 22:09:40236}
237
[email protected]27b985d2009-06-25 17:53:15238void ExtensionsService::UninstallExtension(const std::string& extension_id,
239 bool external_uninstall) {
[email protected]0c6da502009-08-14 22:32:39240 Extension* extension = GetExtensionByIdInternal(extension_id, true, true);
[email protected]631cf822009-05-15 07:01:25241
[email protected]9f1087e2009-06-15 17:29:32242 // Callers should not send us nonexistant extensions.
[email protected]e72e8eb82009-06-18 17:21:51243 DCHECK(extension);
[email protected]9f1087e2009-06-15 17:29:32244
[email protected]27b985d2009-06-25 17:53:15245 extension_prefs_->OnExtensionUninstalled(extension, external_uninstall);
[email protected]9f1087e2009-06-15 17:29:32246
247 // Tell the backend to start deleting installed extensions on the file thread.
[email protected]e72e8eb82009-06-18 17:21:51248 if (Extension::LOAD != extension->location()) {
[email protected]95d29192009-10-30 01:49:06249 ChromeThread::PostTask(
250 ChromeThread::FILE, FROM_HERE,
251 NewRunnableFunction(
252 &extension_file_util::UninstallExtension, extension_id,
253 install_directory_));
[email protected]9f1087e2009-06-15 17:29:32254 }
255
[email protected]86c008e82009-08-28 20:26:05256 ExtensionDOMUI::UnregisterChromeURLOverrides(profile_,
257 extension->GetChromeURLOverrides());
258
[email protected]9f1087e2009-06-15 17:29:32259 UnloadExtension(extension_id);
260}
261
[email protected]0c6da502009-08-14 22:32:39262void ExtensionsService::EnableExtension(const std::string& extension_id) {
263 Extension* extension = GetExtensionByIdInternal(extension_id, false, true);
264 if (!extension) {
265 NOTREACHED() << "Trying to enable an extension that isn't disabled.";
266 return;
267 }
268
[email protected]1784e83a2009-09-08 21:01:52269 // Remember that we enabled it, unless it's temporary.
270 if (extension->location() != Extension::LOAD)
271 extension_prefs_->SetExtensionState(extension, Extension::ENABLED);
272
[email protected]0c6da502009-08-14 22:32:39273 // Move it over to the enabled list.
[email protected]0c6da502009-08-14 22:32:39274 extensions_.push_back(extension);
275 ExtensionList::iterator iter = std::find(disabled_extensions_.begin(),
276 disabled_extensions_.end(),
277 extension);
278 disabled_extensions_.erase(iter);
279
[email protected]86c008e82009-08-28 20:26:05280 ExtensionDOMUI::RegisterChromeURLOverrides(profile_,
281 extension->GetChromeURLOverrides());
282
[email protected]62d30f42009-10-01 22:36:06283 NotifyExtensionLoaded(extension);
[email protected]aab98a52009-12-02 03:22:35284 UpdateActiveExtensionsInCrashReporter();
[email protected]0c6da502009-08-14 22:32:39285}
286
[email protected]1784e83a2009-09-08 21:01:52287void ExtensionsService::DisableExtension(const std::string& extension_id) {
288 Extension* extension = GetExtensionByIdInternal(extension_id, true, false);
[email protected]b2ba9962009-12-10 20:10:15289 // The extension may have been disabled already.
290 if (!extension)
[email protected]1784e83a2009-09-08 21:01:52291 return;
[email protected]1784e83a2009-09-08 21:01:52292
293 // Remember that we disabled it, unless it's temporary.
294 if (extension->location() != Extension::LOAD)
295 extension_prefs_->SetExtensionState(extension, Extension::DISABLED);
296
297 // Move it over to the disabled list.
298 disabled_extensions_.push_back(extension);
299 ExtensionList::iterator iter = std::find(extensions_.begin(),
300 extensions_.end(),
301 extension);
302 extensions_.erase(iter);
303
304 ExtensionDOMUI::UnregisterChromeURLOverrides(profile_,
305 extension->GetChromeURLOverrides());
306
[email protected]62d30f42009-10-01 22:36:06307 NotifyExtensionUnloaded(extension);
[email protected]aab98a52009-12-02 03:22:35308 UpdateActiveExtensionsInCrashReporter();
[email protected]1784e83a2009-09-08 21:01:52309}
310
[email protected]9f1087e2009-06-15 17:29:32311void ExtensionsService::LoadExtension(const FilePath& extension_path) {
[email protected]95d29192009-10-30 01:49:06312 ChromeThread::PostTask(
313 ChromeThread::FILE, FROM_HERE,
314 NewRunnableMethod(
315 backend_.get(),
316 &ExtensionsServiceBackend::LoadSingleExtension,
317 extension_path, scoped_refptr<ExtensionsService>(this)));
[email protected]9f1087e2009-06-15 17:29:32318}
319
320void ExtensionsService::LoadAllExtensions() {
[email protected]cc2c3432009-11-06 17:24:36321 base::TimeTicks start_time = base::TimeTicks::Now();
322
[email protected]e72e8eb82009-06-18 17:21:51323 // Load the previously installed extensions.
[email protected]c6d474f82009-12-16 21:11:06324 scoped_ptr<ExtensionPrefs::ExtensionsInfo> info(
325 ExtensionPrefs::CollectExtensionsInfo(extension_prefs_.get()));
326
327 // If any extensions need localization, we bounce them all to the file thread
328 // for re-reading and localization.
329 for (size_t i = 0; i < info->size(); ++i) {
330 if (extension_l10n_util::ShouldRelocalizeManifest(*info->at(i))) {
331 ChromeThread::PostTask(
332 ChromeThread::FILE, FROM_HERE, NewRunnableMethod(
333 backend_.get(),
334 &ExtensionsServiceBackend::ReloadExtensionManifestsForLocaleChanged,
335 info.release(), // Callee takes ownership of the memory.
336 start_time,
337 scoped_refptr<ExtensionsService>(this)));
338 return;
339 }
340 }
341
342 // Don't update prefs.
343 // Callee takes ownership of the memory.
344 ContinueLoadAllExtensions(info.release(), start_time, false);
345}
346
347void ExtensionsService::ContinueLoadAllExtensions(
348 ExtensionPrefs::ExtensionsInfo* extensions_info,
349 base::TimeTicks start_time,
350 bool write_to_prefs) {
351 scoped_ptr<ExtensionPrefs::ExtensionsInfo> info(extensions_info);
352
353 for (size_t i = 0; i < info->size(); ++i) {
354 LoadInstalledExtension(*info->at(i), write_to_prefs);
355 }
356
[email protected]ae09ca62009-08-21 19:46:46357 OnLoadedInstalledExtensions();
[email protected]cc2c3432009-11-06 17:24:36358
359 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadAll", extensions_.size());
360 UMA_HISTOGRAM_COUNTS_100("Extensions.Disabled", disabled_extensions_.size());
361
362 if (extensions_.size()) {
363 UMA_HISTOGRAM_TIMES("Extensions.LoadAllTime",
364 base::TimeTicks::Now() - start_time);
365
366 int user_script_count = 0;
367 int extension_count = 0;
368 int theme_count = 0;
369 int external_count = 0;
370 int page_action_count = 0;
371 int browser_action_count = 0;
372 ExtensionList::iterator ex;
373 for (ex = extensions_.begin(); ex != extensions_.end(); ++ex) {
374 if ((*ex)->IsTheme()) {
375 theme_count++;
376 } else if ((*ex)->converted_from_user_script()) {
377 user_script_count++;
378 } else {
379 extension_count++;
380 }
381 if (Extension::IsExternalLocation((*ex)->location())) {
382 external_count++;
383 }
384 if ((*ex)->page_action() != NULL) {
385 page_action_count++;
386 }
387 if ((*ex)->browser_action() != NULL) {
388 browser_action_count++;
389 }
390 }
391 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadExtension", extension_count);
392 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadUserScript", user_script_count);
393 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadTheme", theme_count);
394 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadExternal", external_count);
395 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadPageAction", page_action_count);
396 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadBrowserAction",
397 browser_action_count);
398 }
[email protected]ae09ca62009-08-21 19:46:46399}
400
[email protected]c6d474f82009-12-16 21:11:06401void ExtensionsService::LoadInstalledExtension(const ExtensionInfo& info,
402 bool write_to_prefs) {
[email protected]ae09ca62009-08-21 19:46:46403 std::string error;
404 Extension* extension = NULL;
[email protected]c6d474f82009-12-16 21:11:06405 if (info.extension_manifest.get()) {
406 scoped_ptr<Extension> tmp(new Extension(info.extension_path));
407 if (tmp->InitFromValue(*info.extension_manifest, true, &error))
[email protected]ae09ca62009-08-21 19:46:46408 extension = tmp.release();
[email protected]ae09ca62009-08-21 19:46:46409 } else {
[email protected]c6d474f82009-12-16 21:11:06410 error = errors::kManifestUnreadable;
[email protected]ae09ca62009-08-21 19:46:46411 }
412
413 if (!extension) {
[email protected]c6d474f82009-12-16 21:11:06414 ReportExtensionLoadError(info.extension_path,
[email protected]d11c8e92009-10-20 23:26:40415 error,
416 NotificationType::EXTENSION_INSTALL_ERROR,
417 false);
[email protected]ae09ca62009-08-21 19:46:46418 return;
419 }
420
[email protected]c6d474f82009-12-16 21:11:06421 extension->set_location(info.extension_location);
422
423 if (write_to_prefs)
424 extension_prefs_->UpdateManifest(extension);
425
[email protected]2a409532009-08-28 19:39:44426 OnExtensionLoaded(extension, true);
[email protected]ae09ca62009-08-21 19:46:46427
[email protected]c6d474f82009-12-16 21:11:06428 if (info.extension_location == Extension::EXTERNAL_PREF ||
429 info.extension_location == Extension::EXTERNAL_REGISTRY) {
[email protected]95d29192009-10-30 01:49:06430 ChromeThread::PostTask(
431 ChromeThread::FILE, FROM_HERE,
432 NewRunnableMethod(
[email protected]c6d474f82009-12-16 21:11:06433 backend_.get(),
434 &ExtensionsServiceBackend::CheckExternalUninstall,
435 scoped_refptr<ExtensionsService>(this),
436 info.extension_id,
437 info.extension_location));
[email protected]ae09ca62009-08-21 19:46:46438 }
[email protected]9f1087e2009-06-15 17:29:32439}
440
[email protected]62d30f42009-10-01 22:36:06441void ExtensionsService::NotifyExtensionLoaded(Extension* extension) {
442 LOG(INFO) << "Sending EXTENSION_LOADED";
443
444 // The ChromeURLRequestContext needs to be first to know that the extension
445 // was loaded, otherwise a race can arise where a renderer that is created
446 // for the extension may try to load an extension URL with an extension id
447 // that the request context doesn't yet know about.
448 if (profile_ && !profile_->IsOffTheRecord()) {
[email protected]be180c802009-10-23 06:33:31449 ChromeURLRequestContextGetter* context_getter =
450 static_cast<ChromeURLRequestContextGetter*>(
451 profile_->GetRequestContext());
452 if (context_getter) {
[email protected]95d29192009-10-30 01:49:06453 ChromeThread::PostTask(
454 ChromeThread::IO, FROM_HERE,
455 NewRunnableMethod(
456 context_getter,
457 &ChromeURLRequestContextGetter::OnNewExtensions,
458 extension->id(),
[email protected]0ce3f5982010-01-28 23:04:27459 new ChromeURLRequestContext::ExtensionInfo(
460 extension->path(),
461 extension->default_locale(),
[email protected]b30e0dd2010-01-29 23:33:21462 extension->app_extent(),
[email protected]0ce3f5982010-01-28 23:04:27463 extension->api_permissions())));
[email protected]62d30f42009-10-01 22:36:06464 }
465 }
466
467 NotificationService::current()->Notify(
468 NotificationType::EXTENSION_LOADED,
[email protected]24e7a9d2009-11-04 11:11:34469 Source<Profile>(profile_),
[email protected]62d30f42009-10-01 22:36:06470 Details<Extension>(extension));
471}
472
473void ExtensionsService::NotifyExtensionUnloaded(Extension* extension) {
474 LOG(INFO) << "Sending EXTENSION_UNLOADED";
475
476 NotificationService::current()->Notify(
477 NotificationType::EXTENSION_UNLOADED,
[email protected]24e7a9d2009-11-04 11:11:34478 Source<Profile>(profile_),
[email protected]62d30f42009-10-01 22:36:06479 Details<Extension>(extension));
480
481 if (profile_ && !profile_->IsOffTheRecord()) {
[email protected]be180c802009-10-23 06:33:31482 ChromeURLRequestContextGetter* context_getter =
483 static_cast<ChromeURLRequestContextGetter*>(
484 profile_->GetRequestContext());
485 if (context_getter) {
[email protected]95d29192009-10-30 01:49:06486 ChromeThread::PostTask(
487 ChromeThread::IO, FROM_HERE,
[email protected]be180c802009-10-23 06:33:31488 NewRunnableMethod(
489 context_getter,
490 &ChromeURLRequestContextGetter::OnUnloadedExtension,
491 extension->id()));
[email protected]62d30f42009-10-01 22:36:06492 }
493 }
494}
495
[email protected]6b75ec32009-08-14 06:37:18496void ExtensionsService::UpdateExtensionBlacklist(
497 const std::vector<std::string>& blacklist) {
498 // Use this set to indicate if an extension in the blacklist has been used.
499 std::set<std::string> blacklist_set;
500 for (unsigned int i = 0; i < blacklist.size(); ++i) {
501 if (Extension::IdIsValid(blacklist[i])) {
502 blacklist_set.insert(blacklist[i]);
503 }
504 }
505 extension_prefs_->UpdateBlacklist(blacklist_set);
506 std::vector<std::string> to_be_removed;
507 // Loop current extensions, unload installed extensions.
508 for (ExtensionList::const_iterator iter = extensions_.begin();
509 iter != extensions_.end(); ++iter) {
510 Extension* extension = (*iter);
511 if (blacklist_set.find(extension->id()) != blacklist_set.end()) {
512 to_be_removed.push_back(extension->id());
513 }
514 }
515
516 // UnloadExtension will change the extensions_ list. So, we should
517 // call it outside the iterator loop.
518 for (unsigned int i = 0; i < to_be_removed.size(); ++i) {
519 UnloadExtension(to_be_removed[i]);
520 }
521}
522
[email protected]5ef47ec2010-01-28 05:58:05523void ExtensionsService::SetLastPingDay(const std::string& extension_id,
524 const base::Time& time) {
525 extension_prefs_->SetLastPingDay(extension_id, time);
526}
527
528base::Time ExtensionsService::LastPingDay(const std::string& extension_id) {
529 return extension_prefs_->LastPingDay(extension_id);
530}
531
[email protected]93fd78f42009-07-10 16:43:17532void ExtensionsService::CheckForExternalUpdates() {
[email protected]9f1087e2009-06-15 17:29:32533 // This installs or updates externally provided extensions.
[email protected]7577a5c52009-07-30 06:21:58534 // TODO(aa): Why pass this list into the provider, why not just filter it
535 // later?
[email protected]9f1087e2009-06-15 17:29:32536 std::set<std::string> killed_extensions;
[email protected]e72e8eb82009-06-18 17:21:51537 extension_prefs_->GetKilledExtensionIds(&killed_extensions);
[email protected]95d29192009-10-30 01:49:06538 ChromeThread::PostTask(
539 ChromeThread::FILE, FROM_HERE,
540 NewRunnableMethod(
541 backend_.get(), &ExtensionsServiceBackend::CheckForExternalUpdates,
542 killed_extensions, scoped_refptr<ExtensionsService>(this)));
[email protected]9f1087e2009-06-15 17:29:32543}
544
545void ExtensionsService::UnloadExtension(const std::string& extension_id) {
[email protected]27e469a2010-01-11 20:35:09546 // Make sure the extension gets deleted after we return from this function.
[email protected]0c6da502009-08-14 22:32:39547 scoped_ptr<Extension> extension(
548 GetExtensionByIdInternal(extension_id, true, true));
[email protected]631cf822009-05-15 07:01:25549
[email protected]894bb502009-05-21 22:39:57550 // Callers should not send us nonexistant extensions.
[email protected]0c6da502009-08-14 22:32:39551 CHECK(extension.get());
552
[email protected]1eb175082010-02-10 09:26:16553 // Keep information about the extension so that we can reload it later
554 // even if it's not permanently installed.
555 unloaded_extension_paths_[extension->id()] = extension->path();
556
[email protected]86c008e82009-08-28 20:26:05557 ExtensionDOMUI::UnregisterChromeURLOverrides(profile_,
558 extension->GetChromeURLOverrides());
559
[email protected]0c6da502009-08-14 22:32:39560 ExtensionList::iterator iter = std::find(disabled_extensions_.begin(),
561 disabled_extensions_.end(),
562 extension.get());
563 if (iter != disabled_extensions_.end()) {
[email protected]0c6da502009-08-14 22:32:39564 disabled_extensions_.erase(iter);
[email protected]866930682009-08-18 22:53:47565 NotificationService::current()->Notify(
566 NotificationType::EXTENSION_UNLOADED_DISABLED,
[email protected]24e7a9d2009-11-04 11:11:34567 Source<Profile>(profile_),
[email protected]866930682009-08-18 22:53:47568 Details<Extension>(extension.get()));
[email protected]0c6da502009-08-14 22:32:39569 return;
570 }
571
572 iter = std::find(extensions_.begin(), extensions_.end(), extension.get());
[email protected]894bb502009-05-21 22:39:57573
[email protected]631cf822009-05-15 07:01:25574 // Remove the extension from our list.
575 extensions_.erase(iter);
576
[email protected]62d30f42009-10-01 22:36:06577 NotifyExtensionUnloaded(extension.get());
[email protected]aab98a52009-12-02 03:22:35578 UpdateActiveExtensionsInCrashReporter();
[email protected]631cf822009-05-15 07:01:25579}
580
[email protected]9f1087e2009-06-15 17:29:32581void ExtensionsService::UnloadAllExtensions() {
582 ExtensionList::iterator iter;
[email protected]c6e4a3412009-06-24 15:45:29583 for (iter = extensions_.begin(); iter != extensions_.end(); ++iter)
[email protected]9f1087e2009-06-15 17:29:32584 delete *iter;
[email protected]9f1087e2009-06-15 17:29:32585 extensions_.clear();
[email protected]c6e4a3412009-06-24 15:45:29586
587 // TODO(erikkay) should there be a notification for this? We can't use
588 // EXTENSION_UNLOADED since that implies that the extension has been disabled
589 // or uninstalled, and UnloadAll is just part of shutdown.
[email protected]9f1087e2009-06-15 17:29:32590}
591
592void ExtensionsService::ReloadExtensions() {
593 UnloadAllExtensions();
594 LoadAllExtensions();
595}
596
597void ExtensionsService::GarbageCollectExtensions() {
[email protected]c6d474f82009-12-16 21:11:06598 InstalledExtensionSet installed(extension_prefs_.get());
[email protected]95d29192009-10-30 01:49:06599 ChromeThread::PostTask(
600 ChromeThread::FILE, FROM_HERE,
601 NewRunnableFunction(
602 &extension_file_util::GarbageCollectExtensions, install_directory_,
[email protected]4559a7d2009-12-02 01:42:41603 installed.extensions(), installed.versions()));
[email protected]3cf4f0992009-02-03 23:00:30604}
605
[email protected]e72e8eb82009-06-18 17:21:51606void ExtensionsService::OnLoadedInstalledExtensions() {
[email protected]e81dba32009-06-19 20:19:13607 ready_ = true;
[email protected]93fd78f42009-07-10 16:43:17608 if (updater_.get()) {
609 updater_->Start();
610 }
[email protected]e72e8eb82009-06-18 17:21:51611 NotificationService::current()->Notify(
612 NotificationType::EXTENSIONS_READY,
[email protected]24e7a9d2009-11-04 11:11:34613 Source<Profile>(profile_),
[email protected]e72e8eb82009-06-18 17:21:51614 NotificationService::NoDetails());
615}
616
[email protected]2a409532009-08-28 19:39:44617void ExtensionsService::OnExtensionLoaded(Extension* extension,
618 bool allow_privilege_increase) {
[email protected]ae09ca62009-08-21 19:46:46619 // Ensure extension is deleted unless we transfer ownership.
620 scoped_ptr<Extension> scoped_extension(extension);
[email protected]9f1087e2009-06-15 17:29:32621
[email protected]1eb175082010-02-10 09:26:16622 // The extension is now loaded, remove its data from unloaded extension map.
623 unloaded_extension_paths_.erase(extension->id());
624
[email protected]ae09ca62009-08-21 19:46:46625 if (extensions_enabled() ||
626 extension->IsTheme() ||
627 extension->location() == Extension::LOAD ||
628 Extension::IsExternalLocation(extension->location())) {
629 Extension* old = GetExtensionByIdInternal(extension->id(), true, true);
630 if (old) {
631 if (extension->version()->CompareTo(*(old->version())) > 0) {
[email protected]2a409532009-08-28 19:39:44632 bool allow_silent_upgrade =
633 allow_privilege_increase || !Extension::IsPrivilegeIncrease(
634 old, extension);
[email protected]0c6da502009-08-14 22:32:39635
[email protected]1e8c93f2010-02-08 22:58:31636 // Extensions get upgraded if silent upgrades are allowed, otherwise
637 // they get disabled.
638 if (allow_silent_upgrade) {
639 old->set_being_upgraded(true);
640 extension->set_being_upgraded(true);
641 }
642
[email protected]ae09ca62009-08-21 19:46:46643 // To upgrade an extension in place, unload the old one and
644 // then load the new one.
645 UnloadExtension(old->id());
646 old = NULL;
[email protected]0c6da502009-08-14 22:32:39647
[email protected]b24d8312009-08-27 06:47:46648 if (!allow_silent_upgrade) {
[email protected]6d27a7b2009-12-18 23:25:45649 // Extension has changed permissions significantly. Disable it. We
650 // send a notification below.
[email protected]ae09ca62009-08-21 19:46:46651 extension_prefs_->SetExtensionState(extension, Extension::DISABLED);
[email protected]7d845862010-01-04 21:28:57652 extension_prefs_->SetShowInstallWarningOnEnable(extension, true);
[email protected]9f1087e2009-06-15 17:29:32653 }
[email protected]ae09ca62009-08-21 19:46:46654 } else {
655 // We already have the extension of the same or older version.
[email protected]d11c8e92009-10-20 23:26:40656 std::string error_message("Duplicate extension load attempt: ");
657 error_message += extension->id();
658 LOG(WARNING) << error_message;
659 ReportExtensionLoadError(extension->path(),
660 error_message,
661 NotificationType::EXTENSION_OVERINSTALL_ERROR,
662 false);
[email protected]ae09ca62009-08-21 19:46:46663 return;
[email protected]0c6da502009-08-14 22:32:39664 }
[email protected]ba74f352009-06-11 18:54:45665 }
[email protected]86a274072009-06-11 02:06:45666
[email protected]ae09ca62009-08-21 19:46:46667 switch (extension_prefs_->GetExtensionState(extension->id())) {
668 case Extension::ENABLED:
669 extensions_.push_back(scoped_extension.release());
670
[email protected]aeb53b32009-10-29 07:34:45671 // We delay starting up the browser event router until at least one
672 // extension that needs it is loaded.
673 if (extension->HasApiPermission(Extension::kTabPermission)) {
674 ExtensionBrowserEventRouter::GetInstance()->Init();
675 }
[email protected]840b0db2009-11-20 03:00:38676 if (extension->HasApiPermission(Extension::kBookmarkPermission)) {
677 ExtensionBookmarkEventRouter::GetSingleton()->Observe(
678 profile_->GetBookmarkModel());
679 }
[email protected]aeb53b32009-10-29 07:34:45680
[email protected]62d30f42009-10-01 22:36:06681 NotifyExtensionLoaded(extension);
[email protected]ae09ca62009-08-21 19:46:46682
683 if (extension->IsTheme() && extension->location() == Extension::LOAD) {
684 NotificationService::current()->Notify(
685 NotificationType::THEME_INSTALLED,
[email protected]24e7a9d2009-11-04 11:11:34686 Source<Profile>(profile_),
[email protected]ae09ca62009-08-21 19:46:46687 Details<Extension>(extension));
[email protected]86c008e82009-08-28 20:26:05688 } else {
689 ExtensionDOMUI::RegisterChromeURLOverrides(profile_,
690 extension->GetChromeURLOverrides());
[email protected]ae09ca62009-08-21 19:46:46691 }
692 break;
693 case Extension::DISABLED:
[email protected]6d27a7b2009-12-18 23:25:45694 disabled_extensions_.push_back(scoped_extension.release());
[email protected]d11c8e92009-10-20 23:26:40695 NotificationService::current()->Notify(
696 NotificationType::EXTENSION_UPDATE_DISABLED,
[email protected]24e7a9d2009-11-04 11:11:34697 Source<Profile>(profile_),
[email protected]d11c8e92009-10-20 23:26:40698 Details<Extension>(extension));
[email protected]ae09ca62009-08-21 19:46:46699 break;
700 default:
[email protected]d11c8e92009-10-20 23:26:40701 NOTREACHED();
[email protected]ae09ca62009-08-21 19:46:46702 break;
[email protected]811f3432009-07-25 19:38:21703 }
[email protected]e72e8eb82009-06-18 17:21:51704 }
[email protected]aab98a52009-12-02 03:22:35705
[email protected]1e8c93f2010-02-08 22:58:31706 extension->set_being_upgraded(false);
707
[email protected]aab98a52009-12-02 03:22:35708 UpdateActiveExtensionsInCrashReporter();
709}
710
711void ExtensionsService::UpdateActiveExtensionsInCrashReporter() {
[email protected]c8865962009-12-16 07:47:39712 std::set<std::string> extension_ids;
[email protected]aab98a52009-12-02 03:22:35713 for (size_t i = 0; i < extensions_.size(); ++i) {
714 if (!extensions_[i]->IsTheme())
[email protected]c8865962009-12-16 07:47:39715 extension_ids.insert(extensions_[i]->id());
[email protected]aab98a52009-12-02 03:22:35716 }
717
718 child_process_logging::SetActiveExtensions(extension_ids);
[email protected]6014d672008-12-05 00:38:25719}
720
[email protected]2a409532009-08-28 19:39:44721void ExtensionsService::OnExtensionInstalled(Extension* extension,
722 bool allow_privilege_increase) {
[email protected]b6ab96d2009-08-20 18:58:19723 extension_prefs_->OnExtensionInstalled(extension);
[email protected]25b34332009-06-05 21:53:19724
[email protected]4a190632009-05-09 01:07:42725 // If the extension is a theme, tell the profile (and therefore ThemeProvider)
726 // to apply it.
727 if (extension->IsTheme()) {
[email protected]9ceb07342009-07-26 04:09:23728 NotificationService::current()->Notify(
729 NotificationType::THEME_INSTALLED,
[email protected]24e7a9d2009-11-04 11:11:34730 Source<Profile>(profile_),
[email protected]9ceb07342009-07-26 04:09:23731 Details<Extension>(extension));
[email protected]9197f3b2009-06-02 00:49:27732 } else {
733 NotificationService::current()->Notify(
734 NotificationType::EXTENSION_INSTALLED,
[email protected]24e7a9d2009-11-04 11:11:34735 Source<Profile>(profile_),
[email protected]9197f3b2009-06-02 00:49:27736 Details<Extension>(extension));
[email protected]4a190632009-05-09 01:07:42737 }
[email protected]7577a5c52009-07-30 06:21:58738
739 // Also load the extension.
[email protected]2a409532009-08-28 19:39:44740 OnExtensionLoaded(extension, allow_privilege_increase);
[email protected]4a190632009-05-09 01:07:42741}
742
[email protected]7577a5c52009-07-30 06:21:58743void ExtensionsService::OnExtensionOverinstallAttempted(const std::string& id) {
[email protected]61b411612009-11-10 23:17:41744 Extension* extension = GetExtensionById(id, false);
[email protected]4a190632009-05-09 01:07:42745 if (extension && extension->IsTheme()) {
[email protected]9ceb07342009-07-26 04:09:23746 NotificationService::current()->Notify(
747 NotificationType::THEME_INSTALLED,
[email protected]24e7a9d2009-11-04 11:11:34748 Source<Profile>(profile_),
[email protected]9ceb07342009-07-26 04:09:23749 Details<Extension>(extension));
[email protected]91e1bd82009-09-03 22:04:40750 } else {
751 NotificationService::current()->Notify(
752 NotificationType::NO_THEME_DETECTED,
[email protected]24e7a9d2009-11-04 11:11:34753 Source<Profile>(profile_),
[email protected]91e1bd82009-09-03 22:04:40754 NotificationService::NoDetails());
[email protected]4a190632009-05-09 01:07:42755 }
[email protected]cc655912009-01-29 23:19:19756}
757
[email protected]0c6da502009-08-14 22:32:39758Extension* ExtensionsService::GetExtensionByIdInternal(const std::string& id,
759 bool include_enabled,
760 bool include_disabled) {
[email protected]e957fe52009-06-23 16:51:05761 std::string lowercase_id = StringToLowerASCII(id);
[email protected]0c6da502009-08-14 22:32:39762 if (include_enabled) {
763 for (ExtensionList::const_iterator iter = extensions_.begin();
764 iter != extensions_.end(); ++iter) {
765 if ((*iter)->id() == lowercase_id)
766 return *iter;
767 }
768 }
769 if (include_disabled) {
770 for (ExtensionList::const_iterator iter = disabled_extensions_.begin();
771 iter != disabled_extensions_.end(); ++iter) {
772 if ((*iter)->id() == lowercase_id)
773 return *iter;
774 }
[email protected]ce5c4502009-05-06 16:46:11775 }
776 return NULL;
777}
778
[email protected]9f1087e2009-06-15 17:29:32779Extension* ExtensionsService::GetExtensionByURL(const GURL& url) {
780 std::string host = url.host();
[email protected]61b411612009-11-10 23:17:41781 return GetExtensionById(host, false);
[email protected]9f1087e2009-06-15 17:29:32782}
783
[email protected]a1257b12009-06-12 02:51:34784void ExtensionsService::ClearProvidersForTesting() {
[email protected]95d29192009-10-30 01:49:06785 ChromeThread::PostTask(
786 ChromeThread::FILE, FROM_HERE,
787 NewRunnableMethod(
788 backend_.get(), &ExtensionsServiceBackend::ClearProvidersForTesting));
[email protected]a1257b12009-06-12 02:51:34789}
790
791void ExtensionsService::SetProviderForTesting(
792 Extension::Location location, ExternalExtensionProvider* test_provider) {
[email protected]95d29192009-10-30 01:49:06793 ChromeThread::PostTask(
794 ChromeThread::FILE, FROM_HERE,
795 NewRunnableMethod(
796 backend_.get(), &ExtensionsServiceBackend::SetProviderForTesting,
797 location, test_provider));
[email protected]a1257b12009-06-12 02:51:34798}
799
[email protected]7577a5c52009-07-30 06:21:58800void ExtensionsService::OnExternalExtensionFound(const std::string& id,
801 const std::string& version,
802 const FilePath& path,
803 Extension::Location location) {
804 // Before even bothering to unpack, check and see if we already have this
[email protected]4c967932009-07-31 01:15:49805 // version. This is important because these extensions are going to get
[email protected]7577a5c52009-07-30 06:21:58806 // installed on every startup.
[email protected]61b411612009-11-10 23:17:41807 Extension* existing = GetExtensionById(id, true);
[email protected]a3a63ff82009-08-04 06:44:11808 scoped_ptr<Version> other(Version::GetVersionFromString(version));
[email protected]7577a5c52009-07-30 06:21:58809 if (existing) {
[email protected]a3a63ff82009-08-04 06:44:11810 switch (existing->version()->CompareTo(*other)) {
[email protected]7577a5c52009-07-30 06:21:58811 case -1: // existing version is older, we should upgrade
812 break;
813 case 0: // existing version is same, do nothing
814 return;
815 case 1: // existing version is newer, uh-oh
816 LOG(WARNING) << "Found external version of extension " << id
817 << "that is older than current version. Current version "
818 << "is: " << existing->VersionString() << ". New version "
819 << "is: " << version << ". Keeping current version.";
820 return;
821 }
822 }
823
[email protected]2a464a92009-08-01 17:58:35824 CrxInstaller::Start(path, install_directory_, location, id,
825 false, // don't delete crx when complete
[email protected]2a409532009-08-28 19:39:44826 true, // allow privilege increase
[email protected]2a464a92009-08-01 17:58:35827 this,
828 NULL); // no client (silent install)
[email protected]7577a5c52009-07-30 06:21:58829}
830
[email protected]d11c8e92009-10-20 23:26:40831void ExtensionsService::ReportExtensionLoadError(
832 const FilePath& extension_path,
833 const std::string &error,
834 NotificationType type,
835 bool be_noisy) {
836 NotificationService* service = NotificationService::current();
837 service->Notify(type,
[email protected]24e7a9d2009-11-04 11:11:34838 Source<Profile>(profile_),
[email protected]d11c8e92009-10-20 23:26:40839 Details<const std::string>(&error));
840
841 // TODO(port): note that this isn't guaranteed to work properly on Linux.
[email protected]99efb7b12009-12-18 02:39:16842 std::string path_str = WideToUTF8(extension_path.ToWStringHack());
[email protected]d11c8e92009-10-20 23:26:40843 std::string message = StringPrintf("Could not load extension from '%s'. %s",
844 path_str.c_str(), error.c_str());
845 ExtensionErrorReporter::GetInstance()->ReportError(message, be_noisy);
846}
847
[email protected]4814b512009-11-07 00:12:29848void ExtensionsService::Observe(NotificationType type,
849 const NotificationSource& source,
850 const NotificationDetails& details) {
851 switch (type.value) {
852 case NotificationType::EXTENSION_HOST_DID_STOP_LOADING: {
853 ExtensionHost* host = Details<ExtensionHost>(details).ptr();
854 OrphanedDevTools::iterator iter =
855 orphaned_dev_tools_.find(host->extension()->id());
856 if (iter == orphaned_dev_tools_.end())
857 return;
858
859 DevToolsManager::GetInstance()->AttachClientHost(
860 iter->second, host->render_view_host());
861 orphaned_dev_tools_.erase(iter);
862 break;
863 }
864
[email protected]a4ed6282009-12-14 20:51:16865 case NotificationType::EXTENSION_PROCESS_TERMINATED: {
[email protected]31f77262009-12-02 20:48:53866 DCHECK_EQ(profile_, Source<Profile>(source).ptr());
[email protected]a4ed6282009-12-14 20:51:16867
[email protected]31f77262009-12-02 20:48:53868 ExtensionHost* host = Details<ExtensionHost>(details).ptr();
[email protected]27e469a2010-01-11 20:35:09869 // TODO(phajdan.jr): Change to DCHECK after fixing http://crbug.com/30405.
870 CHECK(profile_->GetExtensionProcessManager()->HasExtensionHost(host));
871
872 // If we hit one of these assertions it means that the host's
[email protected]b0b567bf2010-01-22 19:22:53873 // Extension pointer became invalid. http://crbug.com/30405
[email protected]27e469a2010-01-11 20:35:09874 // TODO(phajdan.jr): Remove excessive debugging after fixing bug 30405.
875 std::string extension_id(host->extension()->id());
876 CHECK(extension_id.length() == 32U);
877 Extension* extension = GetExtensionById(extension_id, true);
[email protected]b0b567bf2010-01-22 19:22:53878 CHECK(extension == host->extension());
[email protected]6f39aee92010-01-26 22:33:58879 if (!extension) {
880 NOTREACHED();
881 return;
882 }
[email protected]31f77262009-12-02 20:48:53883
884 // Unload the entire extension. We want it to be in a consistent state:
885 // either fully working or not loaded at all, but never half-crashed.
[email protected]27e469a2010-01-11 20:35:09886 UnloadExtension(extension_id);
[email protected]31f77262009-12-02 20:48:53887 break;
888 }
889
[email protected]4814b512009-11-07 00:12:29890 default:
891 NOTREACHED() << "Unexpected notification type.";
892 }
893}
894
895
[email protected]6014d672008-12-05 00:38:25896// ExtensionsServicesBackend
897
[email protected]894bb502009-05-21 22:39:57898ExtensionsServiceBackend::ExtensionsServiceBackend(
[email protected]95d29192009-10-30 01:49:06899 const FilePath& install_directory)
[email protected]0c7bc4b2009-05-30 01:47:08900 : frontend_(NULL),
901 install_directory_(install_directory),
[email protected]95d29192009-10-30 01:49:06902 alert_on_error_(false) {
[email protected]7577a5c52009-07-30 06:21:58903 // TODO(aa): This ends up doing blocking IO on the UI thread because it reads
904 // pref data in the ctor and that is called on the UI thread. Would be better
905 // to re-read data each time we list external extensions, anyway.
[email protected]a1257b12009-06-12 02:51:34906 external_extension_providers_[Extension::EXTERNAL_PREF] =
[email protected]da50530a2009-06-15 17:43:01907 linked_ptr<ExternalExtensionProvider>(
[email protected]27b985d2009-06-25 17:53:15908 new ExternalPrefExtensionProvider());
[email protected]a1257b12009-06-12 02:51:34909#if defined(OS_WIN)
910 external_extension_providers_[Extension::EXTERNAL_REGISTRY] =
[email protected]da50530a2009-06-15 17:43:01911 linked_ptr<ExternalExtensionProvider>(
912 new ExternalRegistryExtensionProvider());
[email protected]a1257b12009-06-12 02:51:34913#endif
914}
915
916ExtensionsServiceBackend::~ExtensionsServiceBackend() {
[email protected]894bb502009-05-21 22:39:57917}
918
[email protected]b0beaa662009-02-26 00:04:15919void ExtensionsServiceBackend::LoadSingleExtension(
[email protected]894bb502009-05-21 22:39:57920 const FilePath& path_in, scoped_refptr<ExtensionsService> frontend) {
[email protected]b0beaa662009-02-26 00:04:15921 frontend_ = frontend;
922
923 // Explicit UI loads are always noisy.
924 alert_on_error_ = true;
925
[email protected]cc5da332009-03-04 08:02:51926 FilePath extension_path = path_in;
[email protected]f36fa4fb2009-06-19 18:23:50927 file_util::AbsolutePath(&extension_path);
[email protected]bf24d2c2009-02-24 23:07:45928
929 LOG(INFO) << "Loading single extension from " <<
[email protected]99efb7b12009-12-18 02:39:16930 extension_path.BaseName().value();
[email protected]bf24d2c2009-02-24 23:07:45931
[email protected]ab6f2b22009-07-28 23:28:37932 std::string error;
933 Extension* extension = extension_file_util::LoadExtension(
934 extension_path,
935 false, // Don't require id
936 &error);
937
938 if (!extension) {
939 ReportExtensionLoadError(extension_path, error);
940 return;
[email protected]0877fd92009-02-03 16:34:06941 }
[email protected]ab6f2b22009-07-28 23:28:37942
943 extension->set_location(Extension::LOAD);
[email protected]ae09ca62009-08-21 19:46:46944 ReportExtensionLoaded(extension);
[email protected]0877fd92009-02-03 16:34:06945}
946
[email protected]6014d672008-12-05 00:38:25947void ExtensionsServiceBackend::ReportExtensionLoadError(
[email protected]cc5da332009-03-04 08:02:51948 const FilePath& extension_path, const std::string &error) {
[email protected]95d29192009-10-30 01:49:06949 ChromeThread::PostTask(
950 ChromeThread::UI, FROM_HERE,
951 NewRunnableMethod(
952 frontend_,
[email protected]d11c8e92009-10-20 23:26:40953 &ExtensionsService::ReportExtensionLoadError, extension_path,
954 error, NotificationType::EXTENSION_INSTALL_ERROR, alert_on_error_));
[email protected]6014d672008-12-05 00:38:25955}
956
[email protected]ae09ca62009-08-21 19:46:46957void ExtensionsServiceBackend::ReportExtensionLoaded(Extension* extension) {
[email protected]95d29192009-10-30 01:49:06958 ChromeThread::PostTask(
959 ChromeThread::UI, FROM_HERE,
960 NewRunnableMethod(
961 frontend_, &ExtensionsService::OnExtensionLoaded, extension, true));
[email protected]6014d672008-12-05 00:38:25962}
[email protected]cc655912009-01-29 23:19:19963
[email protected]a1257b12009-06-12 02:51:34964bool ExtensionsServiceBackend::LookupExternalExtension(
965 const std::string& id, Version** version, Extension::Location* location) {
966 scoped_ptr<Version> extension_version;
967 for (ProviderMap::const_iterator i = external_extension_providers_.begin();
968 i != external_extension_providers_.end(); ++i) {
[email protected]da50530a2009-06-15 17:43:01969 const ExternalExtensionProvider* provider = i->second.get();
[email protected]a1257b12009-06-12 02:51:34970 extension_version.reset(provider->RegisteredVersion(id, location));
971 if (extension_version.get()) {
972 if (version)
973 *version = extension_version.release();
974 return true;
975 }
976 }
977 return false;
978}
979
[email protected]b0beaa662009-02-26 00:04:15980// Some extensions will autoupdate themselves externally from Chrome. These
981// are typically part of some larger client application package. To support
[email protected]25b34332009-06-05 21:53:19982// these, the extension will register its location in the the preferences file
983// (and also, on Windows, in the registry) and this code will periodically
[email protected]b0beaa662009-02-26 00:04:15984// check that location for a .crx file, which it will then install locally if
985// a new version is available.
986void ExtensionsServiceBackend::CheckForExternalUpdates(
[email protected]894bb502009-05-21 22:39:57987 std::set<std::string> ids_to_ignore,
988 scoped_refptr<ExtensionsService> frontend) {
[email protected]b0beaa662009-02-26 00:04:15989 // Note that this installation is intentionally silent (since it didn't
990 // go through the front-end). Extensions that are registered in this
991 // way are effectively considered 'pre-bundled', and so implicitly
992 // trusted. In general, if something has HKLM or filesystem access,
993 // they could install an extension manually themselves anyway.
994 alert_on_error_ = false;
995 frontend_ = frontend;
[email protected]b0beaa662009-02-26 00:04:15996
[email protected]a1257b12009-06-12 02:51:34997 // Ask each external extension provider to give us a call back for each
998 // extension they know about. See OnExternalExtensionFound.
999 for (ProviderMap::const_iterator i = external_extension_providers_.begin();
1000 i != external_extension_providers_.end(); ++i) {
[email protected]da50530a2009-06-15 17:43:011001 ExternalExtensionProvider* provider = i->second.get();
[email protected]a1257b12009-06-12 02:51:341002 provider->VisitRegisteredExtension(this, ids_to_ignore);
[email protected]25b34332009-06-05 21:53:191003 }
[email protected]b0beaa662009-02-26 00:04:151004}
1005
[email protected]ae09ca62009-08-21 19:46:461006void ExtensionsServiceBackend::CheckExternalUninstall(
1007 scoped_refptr<ExtensionsService> frontend, const std::string& id,
1008 Extension::Location location) {
[email protected]a1257b12009-06-12 02:51:341009 // Check if the providers know about this extension.
1010 ProviderMap::const_iterator i = external_extension_providers_.find(location);
[email protected]ae09ca62009-08-21 19:46:461011 if (i == external_extension_providers_.end()) {
1012 NOTREACHED() << "CheckExternalUninstall called for non-external extension "
1013 << location;
1014 return;
[email protected]b0beaa662009-02-26 00:04:151015 }
[email protected]25b34332009-06-05 21:53:191016
[email protected]ae09ca62009-08-21 19:46:461017 scoped_ptr<Version> version;
1018 version.reset(i->second->RegisteredVersion(id, NULL));
1019 if (version.get())
1020 return; // Yup, known extension, don't uninstall.
1021
1022 // This is an external extension that we don't have registered. Uninstall.
[email protected]95d29192009-10-30 01:49:061023 ChromeThread::PostTask(
1024 ChromeThread::UI, FROM_HERE,
1025 NewRunnableMethod(
1026 frontend.get(), &ExtensionsService::UninstallExtension, id, true));
[email protected]b0beaa662009-02-26 00:04:151027}
1028
[email protected]a1257b12009-06-12 02:51:341029void ExtensionsServiceBackend::ClearProvidersForTesting() {
1030 external_extension_providers_.clear();
1031}
1032
1033void ExtensionsServiceBackend::SetProviderForTesting(
1034 Extension::Location location,
1035 ExternalExtensionProvider* test_provider) {
1036 DCHECK(test_provider);
[email protected]da50530a2009-06-15 17:43:011037 external_extension_providers_[location] =
1038 linked_ptr<ExternalExtensionProvider>(test_provider);
[email protected]a1257b12009-06-12 02:51:341039}
1040
1041void ExtensionsServiceBackend::OnExternalExtensionFound(
[email protected]7577a5c52009-07-30 06:21:581042 const std::string& id, const Version* version, const FilePath& path,
1043 Extension::Location location) {
[email protected]95d29192009-10-30 01:49:061044 ChromeThread::PostTask(
1045 ChromeThread::UI, FROM_HERE,
1046 NewRunnableMethod(
1047 frontend_, &ExtensionsService::OnExternalExtensionFound, id,
1048 version->GetString(), path, location));
[email protected]cc655912009-01-29 23:19:191049}
[email protected]c6d474f82009-12-16 21:11:061050
1051void ExtensionsServiceBackend::ReloadExtensionManifestsForLocaleChanged(
1052 ExtensionPrefs::ExtensionsInfo* extensions_to_reload,
1053 base::TimeTicks start_time,
1054 scoped_refptr<ExtensionsService> frontend) {
1055 frontend_ = frontend;
1056
1057 for (size_t i = 0; i < extensions_to_reload->size(); ++i) {
1058 ExtensionInfo* info = extensions_to_reload->at(i).get();
1059 if (!info->extension_manifest.get())
1060 continue;
1061
1062 if (!extension_l10n_util::ShouldRelocalizeManifest(*info))
1063 continue;
1064
1065 // We need to reload original manifest in order to localize properly.
1066 std::string error;
1067 scoped_ptr<Extension> extension(extension_file_util::LoadExtension(
1068 info->extension_path, false, &error));
1069
1070 if (extension.get())
1071 extensions_to_reload->at(i)->extension_manifest.reset(
1072 static_cast<DictionaryValue*>(
1073 extension->manifest_value()->DeepCopy()));
1074 }
1075
1076 // Finish installing on UI thread.
1077 ChromeThread::PostTask(
1078 ChromeThread::UI, FROM_HERE,
1079 NewRunnableMethod(
1080 frontend_,
1081 &ExtensionsService::ContinueLoadAllExtensions,
1082 extensions_to_reload,
1083 start_time,
1084 true));
1085}