blob: d17176700830027f6cbe96b11e8a7e987dfbddca [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]de768a832009-10-30 05:25:0121#include "chrome/browser/extensions/extension_history_api.h"
[email protected]b1748b1d82009-11-30 20:32:5622#include "chrome/browser/extensions/extension_host.h"
[email protected]4814b512009-11-07 00:12:2923#include "chrome/browser/extensions/extension_process_manager.h"
[email protected]93fd78f42009-07-10 16:43:1724#include "chrome/browser/extensions/extension_updater.h"
[email protected]a1257b12009-06-12 02:51:3425#include "chrome/browser/extensions/external_extension_provider.h"
26#include "chrome/browser/extensions/external_pref_extension_provider.h"
[email protected]052313b2010-02-19 09:43:0827#include "chrome/browser/pref_service.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]7c927b62010-02-24 09:54:1335#include "chrome/common/extensions/extension_file_util.h"
[email protected]c6d474f82009-12-16 21:11:0636#include "chrome/common/extensions/extension_l10n_util.h"
[email protected]82891262008-12-24 00:21:2637#include "chrome/common/notification_service.h"
[email protected]4814b512009-11-07 00:12:2938#include "chrome/common/notification_type.h"
[email protected]25b34332009-06-05 21:53:1939#include "chrome/common/pref_names.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]55a35692010-02-11 23:25:21532bool ExtensionsService::IsIncognitoEnabled(const std::string& extension_id) {
533 Extension* extension = GetExtensionById(extension_id, true);
534 if (!extension)
535 return false;
536
537 return extension_prefs_->IsIncognitoEnabled(extension_id) &&
538 extension->HasApiPermission(Extension::kExperimentalPermission) &&
539 extension->HasApiPermission(Extension::kIncognitoPermission);
540}
541
[email protected]93fd78f42009-07-10 16:43:17542void ExtensionsService::CheckForExternalUpdates() {
[email protected]9f1087e2009-06-15 17:29:32543 // This installs or updates externally provided extensions.
[email protected]7577a5c52009-07-30 06:21:58544 // TODO(aa): Why pass this list into the provider, why not just filter it
545 // later?
[email protected]9f1087e2009-06-15 17:29:32546 std::set<std::string> killed_extensions;
[email protected]e72e8eb82009-06-18 17:21:51547 extension_prefs_->GetKilledExtensionIds(&killed_extensions);
[email protected]95d29192009-10-30 01:49:06548 ChromeThread::PostTask(
549 ChromeThread::FILE, FROM_HERE,
550 NewRunnableMethod(
551 backend_.get(), &ExtensionsServiceBackend::CheckForExternalUpdates,
552 killed_extensions, scoped_refptr<ExtensionsService>(this)));
[email protected]9f1087e2009-06-15 17:29:32553}
554
555void ExtensionsService::UnloadExtension(const std::string& extension_id) {
[email protected]27e469a2010-01-11 20:35:09556 // Make sure the extension gets deleted after we return from this function.
[email protected]0c6da502009-08-14 22:32:39557 scoped_ptr<Extension> extension(
558 GetExtensionByIdInternal(extension_id, true, true));
[email protected]631cf822009-05-15 07:01:25559
[email protected]894bb502009-05-21 22:39:57560 // Callers should not send us nonexistant extensions.
[email protected]0c6da502009-08-14 22:32:39561 CHECK(extension.get());
562
[email protected]1eb175082010-02-10 09:26:16563 // Keep information about the extension so that we can reload it later
564 // even if it's not permanently installed.
565 unloaded_extension_paths_[extension->id()] = extension->path();
566
[email protected]86c008e82009-08-28 20:26:05567 ExtensionDOMUI::UnregisterChromeURLOverrides(profile_,
568 extension->GetChromeURLOverrides());
569
[email protected]0c6da502009-08-14 22:32:39570 ExtensionList::iterator iter = std::find(disabled_extensions_.begin(),
571 disabled_extensions_.end(),
572 extension.get());
573 if (iter != disabled_extensions_.end()) {
[email protected]0c6da502009-08-14 22:32:39574 disabled_extensions_.erase(iter);
[email protected]866930682009-08-18 22:53:47575 NotificationService::current()->Notify(
576 NotificationType::EXTENSION_UNLOADED_DISABLED,
[email protected]24e7a9d2009-11-04 11:11:34577 Source<Profile>(profile_),
[email protected]866930682009-08-18 22:53:47578 Details<Extension>(extension.get()));
[email protected]0c6da502009-08-14 22:32:39579 return;
580 }
581
582 iter = std::find(extensions_.begin(), extensions_.end(), extension.get());
[email protected]894bb502009-05-21 22:39:57583
[email protected]631cf822009-05-15 07:01:25584 // Remove the extension from our list.
585 extensions_.erase(iter);
586
[email protected]62d30f42009-10-01 22:36:06587 NotifyExtensionUnloaded(extension.get());
[email protected]aab98a52009-12-02 03:22:35588 UpdateActiveExtensionsInCrashReporter();
[email protected]631cf822009-05-15 07:01:25589}
590
[email protected]9f1087e2009-06-15 17:29:32591void ExtensionsService::UnloadAllExtensions() {
592 ExtensionList::iterator iter;
[email protected]c6e4a3412009-06-24 15:45:29593 for (iter = extensions_.begin(); iter != extensions_.end(); ++iter)
[email protected]9f1087e2009-06-15 17:29:32594 delete *iter;
[email protected]9f1087e2009-06-15 17:29:32595 extensions_.clear();
[email protected]c6e4a3412009-06-24 15:45:29596
597 // TODO(erikkay) should there be a notification for this? We can't use
598 // EXTENSION_UNLOADED since that implies that the extension has been disabled
599 // or uninstalled, and UnloadAll is just part of shutdown.
[email protected]9f1087e2009-06-15 17:29:32600}
601
602void ExtensionsService::ReloadExtensions() {
603 UnloadAllExtensions();
604 LoadAllExtensions();
605}
606
607void ExtensionsService::GarbageCollectExtensions() {
[email protected]c6d474f82009-12-16 21:11:06608 InstalledExtensionSet installed(extension_prefs_.get());
[email protected]95d29192009-10-30 01:49:06609 ChromeThread::PostTask(
610 ChromeThread::FILE, FROM_HERE,
611 NewRunnableFunction(
612 &extension_file_util::GarbageCollectExtensions, install_directory_,
[email protected]4559a7d2009-12-02 01:42:41613 installed.extensions(), installed.versions()));
[email protected]3cf4f0992009-02-03 23:00:30614}
615
[email protected]e72e8eb82009-06-18 17:21:51616void ExtensionsService::OnLoadedInstalledExtensions() {
[email protected]e81dba32009-06-19 20:19:13617 ready_ = true;
[email protected]93fd78f42009-07-10 16:43:17618 if (updater_.get()) {
619 updater_->Start();
620 }
[email protected]e72e8eb82009-06-18 17:21:51621 NotificationService::current()->Notify(
622 NotificationType::EXTENSIONS_READY,
[email protected]24e7a9d2009-11-04 11:11:34623 Source<Profile>(profile_),
[email protected]e72e8eb82009-06-18 17:21:51624 NotificationService::NoDetails());
625}
626
[email protected]2a409532009-08-28 19:39:44627void ExtensionsService::OnExtensionLoaded(Extension* extension,
628 bool allow_privilege_increase) {
[email protected]ae09ca62009-08-21 19:46:46629 // Ensure extension is deleted unless we transfer ownership.
630 scoped_ptr<Extension> scoped_extension(extension);
[email protected]9f1087e2009-06-15 17:29:32631
[email protected]1eb175082010-02-10 09:26:16632 // The extension is now loaded, remove its data from unloaded extension map.
633 unloaded_extension_paths_.erase(extension->id());
634
[email protected]ae09ca62009-08-21 19:46:46635 if (extensions_enabled() ||
636 extension->IsTheme() ||
637 extension->location() == Extension::LOAD ||
638 Extension::IsExternalLocation(extension->location())) {
639 Extension* old = GetExtensionByIdInternal(extension->id(), true, true);
640 if (old) {
641 if (extension->version()->CompareTo(*(old->version())) > 0) {
[email protected]2a409532009-08-28 19:39:44642 bool allow_silent_upgrade =
643 allow_privilege_increase || !Extension::IsPrivilegeIncrease(
644 old, extension);
[email protected]0c6da502009-08-14 22:32:39645
[email protected]1e8c93f2010-02-08 22:58:31646 // Extensions get upgraded if silent upgrades are allowed, otherwise
647 // they get disabled.
648 if (allow_silent_upgrade) {
649 old->set_being_upgraded(true);
650 extension->set_being_upgraded(true);
651 }
652
[email protected]ae09ca62009-08-21 19:46:46653 // To upgrade an extension in place, unload the old one and
654 // then load the new one.
655 UnloadExtension(old->id());
656 old = NULL;
[email protected]0c6da502009-08-14 22:32:39657
[email protected]b24d8312009-08-27 06:47:46658 if (!allow_silent_upgrade) {
[email protected]6d27a7b2009-12-18 23:25:45659 // Extension has changed permissions significantly. Disable it. We
660 // send a notification below.
[email protected]ae09ca62009-08-21 19:46:46661 extension_prefs_->SetExtensionState(extension, Extension::DISABLED);
[email protected]7d845862010-01-04 21:28:57662 extension_prefs_->SetShowInstallWarningOnEnable(extension, true);
[email protected]9f1087e2009-06-15 17:29:32663 }
[email protected]ae09ca62009-08-21 19:46:46664 } else {
665 // We already have the extension of the same or older version.
[email protected]d11c8e92009-10-20 23:26:40666 std::string error_message("Duplicate extension load attempt: ");
667 error_message += extension->id();
668 LOG(WARNING) << error_message;
669 ReportExtensionLoadError(extension->path(),
670 error_message,
671 NotificationType::EXTENSION_OVERINSTALL_ERROR,
672 false);
[email protected]ae09ca62009-08-21 19:46:46673 return;
[email protected]0c6da502009-08-14 22:32:39674 }
[email protected]ba74f352009-06-11 18:54:45675 }
[email protected]86a274072009-06-11 02:06:45676
[email protected]ae09ca62009-08-21 19:46:46677 switch (extension_prefs_->GetExtensionState(extension->id())) {
678 case Extension::ENABLED:
679 extensions_.push_back(scoped_extension.release());
680
[email protected]aeb53b32009-10-29 07:34:45681 // We delay starting up the browser event router until at least one
682 // extension that needs it is loaded.
683 if (extension->HasApiPermission(Extension::kTabPermission)) {
684 ExtensionBrowserEventRouter::GetInstance()->Init();
685 }
[email protected]840b0db2009-11-20 03:00:38686 if (extension->HasApiPermission(Extension::kBookmarkPermission)) {
687 ExtensionBookmarkEventRouter::GetSingleton()->Observe(
688 profile_->GetBookmarkModel());
689 }
[email protected]aeb53b32009-10-29 07:34:45690
[email protected]62d30f42009-10-01 22:36:06691 NotifyExtensionLoaded(extension);
[email protected]ae09ca62009-08-21 19:46:46692
693 if (extension->IsTheme() && extension->location() == Extension::LOAD) {
694 NotificationService::current()->Notify(
695 NotificationType::THEME_INSTALLED,
[email protected]24e7a9d2009-11-04 11:11:34696 Source<Profile>(profile_),
[email protected]ae09ca62009-08-21 19:46:46697 Details<Extension>(extension));
[email protected]86c008e82009-08-28 20:26:05698 } else {
699 ExtensionDOMUI::RegisterChromeURLOverrides(profile_,
700 extension->GetChromeURLOverrides());
[email protected]ae09ca62009-08-21 19:46:46701 }
702 break;
703 case Extension::DISABLED:
[email protected]6d27a7b2009-12-18 23:25:45704 disabled_extensions_.push_back(scoped_extension.release());
[email protected]d11c8e92009-10-20 23:26:40705 NotificationService::current()->Notify(
706 NotificationType::EXTENSION_UPDATE_DISABLED,
[email protected]24e7a9d2009-11-04 11:11:34707 Source<Profile>(profile_),
[email protected]d11c8e92009-10-20 23:26:40708 Details<Extension>(extension));
[email protected]ae09ca62009-08-21 19:46:46709 break;
710 default:
[email protected]d11c8e92009-10-20 23:26:40711 NOTREACHED();
[email protected]ae09ca62009-08-21 19:46:46712 break;
[email protected]811f3432009-07-25 19:38:21713 }
[email protected]e72e8eb82009-06-18 17:21:51714 }
[email protected]aab98a52009-12-02 03:22:35715
[email protected]1e8c93f2010-02-08 22:58:31716 extension->set_being_upgraded(false);
717
[email protected]aab98a52009-12-02 03:22:35718 UpdateActiveExtensionsInCrashReporter();
719}
720
721void ExtensionsService::UpdateActiveExtensionsInCrashReporter() {
[email protected]c8865962009-12-16 07:47:39722 std::set<std::string> extension_ids;
[email protected]aab98a52009-12-02 03:22:35723 for (size_t i = 0; i < extensions_.size(); ++i) {
724 if (!extensions_[i]->IsTheme())
[email protected]c8865962009-12-16 07:47:39725 extension_ids.insert(extensions_[i]->id());
[email protected]aab98a52009-12-02 03:22:35726 }
727
728 child_process_logging::SetActiveExtensions(extension_ids);
[email protected]6014d672008-12-05 00:38:25729}
730
[email protected]2a409532009-08-28 19:39:44731void ExtensionsService::OnExtensionInstalled(Extension* extension,
732 bool allow_privilege_increase) {
[email protected]b6ab96d2009-08-20 18:58:19733 extension_prefs_->OnExtensionInstalled(extension);
[email protected]25b34332009-06-05 21:53:19734
[email protected]4a190632009-05-09 01:07:42735 // If the extension is a theme, tell the profile (and therefore ThemeProvider)
736 // to apply it.
737 if (extension->IsTheme()) {
[email protected]9ceb07342009-07-26 04:09:23738 NotificationService::current()->Notify(
739 NotificationType::THEME_INSTALLED,
[email protected]24e7a9d2009-11-04 11:11:34740 Source<Profile>(profile_),
[email protected]9ceb07342009-07-26 04:09:23741 Details<Extension>(extension));
[email protected]9197f3b2009-06-02 00:49:27742 } else {
743 NotificationService::current()->Notify(
744 NotificationType::EXTENSION_INSTALLED,
[email protected]24e7a9d2009-11-04 11:11:34745 Source<Profile>(profile_),
[email protected]9197f3b2009-06-02 00:49:27746 Details<Extension>(extension));
[email protected]4a190632009-05-09 01:07:42747 }
[email protected]7577a5c52009-07-30 06:21:58748
749 // Also load the extension.
[email protected]2a409532009-08-28 19:39:44750 OnExtensionLoaded(extension, allow_privilege_increase);
[email protected]4a190632009-05-09 01:07:42751}
752
[email protected]7577a5c52009-07-30 06:21:58753void ExtensionsService::OnExtensionOverinstallAttempted(const std::string& id) {
[email protected]61b411612009-11-10 23:17:41754 Extension* extension = GetExtensionById(id, false);
[email protected]4a190632009-05-09 01:07:42755 if (extension && extension->IsTheme()) {
[email protected]9ceb07342009-07-26 04:09:23756 NotificationService::current()->Notify(
757 NotificationType::THEME_INSTALLED,
[email protected]24e7a9d2009-11-04 11:11:34758 Source<Profile>(profile_),
[email protected]9ceb07342009-07-26 04:09:23759 Details<Extension>(extension));
[email protected]91e1bd82009-09-03 22:04:40760 } else {
761 NotificationService::current()->Notify(
762 NotificationType::NO_THEME_DETECTED,
[email protected]24e7a9d2009-11-04 11:11:34763 Source<Profile>(profile_),
[email protected]91e1bd82009-09-03 22:04:40764 NotificationService::NoDetails());
[email protected]4a190632009-05-09 01:07:42765 }
[email protected]cc655912009-01-29 23:19:19766}
767
[email protected]0c6da502009-08-14 22:32:39768Extension* ExtensionsService::GetExtensionByIdInternal(const std::string& id,
769 bool include_enabled,
770 bool include_disabled) {
[email protected]e957fe52009-06-23 16:51:05771 std::string lowercase_id = StringToLowerASCII(id);
[email protected]0c6da502009-08-14 22:32:39772 if (include_enabled) {
773 for (ExtensionList::const_iterator iter = extensions_.begin();
774 iter != extensions_.end(); ++iter) {
775 if ((*iter)->id() == lowercase_id)
776 return *iter;
777 }
778 }
779 if (include_disabled) {
780 for (ExtensionList::const_iterator iter = disabled_extensions_.begin();
781 iter != disabled_extensions_.end(); ++iter) {
782 if ((*iter)->id() == lowercase_id)
783 return *iter;
784 }
[email protected]ce5c4502009-05-06 16:46:11785 }
786 return NULL;
787}
788
[email protected]9f1087e2009-06-15 17:29:32789Extension* ExtensionsService::GetExtensionByURL(const GURL& url) {
790 std::string host = url.host();
[email protected]61b411612009-11-10 23:17:41791 return GetExtensionById(host, false);
[email protected]9f1087e2009-06-15 17:29:32792}
793
[email protected]a1257b12009-06-12 02:51:34794void ExtensionsService::ClearProvidersForTesting() {
[email protected]95d29192009-10-30 01:49:06795 ChromeThread::PostTask(
796 ChromeThread::FILE, FROM_HERE,
797 NewRunnableMethod(
798 backend_.get(), &ExtensionsServiceBackend::ClearProvidersForTesting));
[email protected]a1257b12009-06-12 02:51:34799}
800
801void ExtensionsService::SetProviderForTesting(
802 Extension::Location location, ExternalExtensionProvider* test_provider) {
[email protected]95d29192009-10-30 01:49:06803 ChromeThread::PostTask(
804 ChromeThread::FILE, FROM_HERE,
805 NewRunnableMethod(
806 backend_.get(), &ExtensionsServiceBackend::SetProviderForTesting,
807 location, test_provider));
[email protected]a1257b12009-06-12 02:51:34808}
809
[email protected]7577a5c52009-07-30 06:21:58810void ExtensionsService::OnExternalExtensionFound(const std::string& id,
811 const std::string& version,
812 const FilePath& path,
813 Extension::Location location) {
814 // Before even bothering to unpack, check and see if we already have this
[email protected]4c967932009-07-31 01:15:49815 // version. This is important because these extensions are going to get
[email protected]7577a5c52009-07-30 06:21:58816 // installed on every startup.
[email protected]61b411612009-11-10 23:17:41817 Extension* existing = GetExtensionById(id, true);
[email protected]a3a63ff82009-08-04 06:44:11818 scoped_ptr<Version> other(Version::GetVersionFromString(version));
[email protected]7577a5c52009-07-30 06:21:58819 if (existing) {
[email protected]a3a63ff82009-08-04 06:44:11820 switch (existing->version()->CompareTo(*other)) {
[email protected]7577a5c52009-07-30 06:21:58821 case -1: // existing version is older, we should upgrade
822 break;
823 case 0: // existing version is same, do nothing
824 return;
825 case 1: // existing version is newer, uh-oh
826 LOG(WARNING) << "Found external version of extension " << id
827 << "that is older than current version. Current version "
828 << "is: " << existing->VersionString() << ". New version "
829 << "is: " << version << ". Keeping current version.";
830 return;
831 }
832 }
833
[email protected]2a464a92009-08-01 17:58:35834 CrxInstaller::Start(path, install_directory_, location, id,
835 false, // don't delete crx when complete
[email protected]2a409532009-08-28 19:39:44836 true, // allow privilege increase
[email protected]2a464a92009-08-01 17:58:35837 this,
838 NULL); // no client (silent install)
[email protected]7577a5c52009-07-30 06:21:58839}
840
[email protected]d11c8e92009-10-20 23:26:40841void ExtensionsService::ReportExtensionLoadError(
842 const FilePath& extension_path,
843 const std::string &error,
844 NotificationType type,
845 bool be_noisy) {
846 NotificationService* service = NotificationService::current();
847 service->Notify(type,
[email protected]24e7a9d2009-11-04 11:11:34848 Source<Profile>(profile_),
[email protected]d11c8e92009-10-20 23:26:40849 Details<const std::string>(&error));
850
851 // TODO(port): note that this isn't guaranteed to work properly on Linux.
[email protected]99efb7b12009-12-18 02:39:16852 std::string path_str = WideToUTF8(extension_path.ToWStringHack());
[email protected]d11c8e92009-10-20 23:26:40853 std::string message = StringPrintf("Could not load extension from '%s'. %s",
854 path_str.c_str(), error.c_str());
855 ExtensionErrorReporter::GetInstance()->ReportError(message, be_noisy);
856}
857
[email protected]4814b512009-11-07 00:12:29858void ExtensionsService::Observe(NotificationType type,
859 const NotificationSource& source,
860 const NotificationDetails& details) {
861 switch (type.value) {
862 case NotificationType::EXTENSION_HOST_DID_STOP_LOADING: {
863 ExtensionHost* host = Details<ExtensionHost>(details).ptr();
864 OrphanedDevTools::iterator iter =
865 orphaned_dev_tools_.find(host->extension()->id());
866 if (iter == orphaned_dev_tools_.end())
867 return;
868
869 DevToolsManager::GetInstance()->AttachClientHost(
870 iter->second, host->render_view_host());
871 orphaned_dev_tools_.erase(iter);
872 break;
873 }
874
[email protected]a4ed6282009-12-14 20:51:16875 case NotificationType::EXTENSION_PROCESS_TERMINATED: {
[email protected]31f77262009-12-02 20:48:53876 DCHECK_EQ(profile_, Source<Profile>(source).ptr());
[email protected]a4ed6282009-12-14 20:51:16877
[email protected]31f77262009-12-02 20:48:53878 ExtensionHost* host = Details<ExtensionHost>(details).ptr();
[email protected]27e469a2010-01-11 20:35:09879 // TODO(phajdan.jr): Change to DCHECK after fixing http://crbug.com/30405.
880 CHECK(profile_->GetExtensionProcessManager()->HasExtensionHost(host));
881
882 // If we hit one of these assertions it means that the host's
[email protected]b0b567bf2010-01-22 19:22:53883 // Extension pointer became invalid. http://crbug.com/30405
[email protected]27e469a2010-01-11 20:35:09884 // TODO(phajdan.jr): Remove excessive debugging after fixing bug 30405.
885 std::string extension_id(host->extension()->id());
886 CHECK(extension_id.length() == 32U);
887 Extension* extension = GetExtensionById(extension_id, true);
[email protected]b0b567bf2010-01-22 19:22:53888 CHECK(extension == host->extension());
[email protected]6f39aee92010-01-26 22:33:58889 if (!extension) {
890 NOTREACHED();
891 return;
892 }
[email protected]31f77262009-12-02 20:48:53893
894 // Unload the entire extension. We want it to be in a consistent state:
895 // either fully working or not loaded at all, but never half-crashed.
[email protected]27e469a2010-01-11 20:35:09896 UnloadExtension(extension_id);
[email protected]31f77262009-12-02 20:48:53897 break;
898 }
899
[email protected]4814b512009-11-07 00:12:29900 default:
901 NOTREACHED() << "Unexpected notification type.";
902 }
903}
904
905
[email protected]6014d672008-12-05 00:38:25906// ExtensionsServicesBackend
907
[email protected]894bb502009-05-21 22:39:57908ExtensionsServiceBackend::ExtensionsServiceBackend(
[email protected]95d29192009-10-30 01:49:06909 const FilePath& install_directory)
[email protected]0c7bc4b2009-05-30 01:47:08910 : frontend_(NULL),
911 install_directory_(install_directory),
[email protected]95d29192009-10-30 01:49:06912 alert_on_error_(false) {
[email protected]7577a5c52009-07-30 06:21:58913 // TODO(aa): This ends up doing blocking IO on the UI thread because it reads
914 // pref data in the ctor and that is called on the UI thread. Would be better
915 // to re-read data each time we list external extensions, anyway.
[email protected]a1257b12009-06-12 02:51:34916 external_extension_providers_[Extension::EXTERNAL_PREF] =
[email protected]da50530a2009-06-15 17:43:01917 linked_ptr<ExternalExtensionProvider>(
[email protected]27b985d2009-06-25 17:53:15918 new ExternalPrefExtensionProvider());
[email protected]a1257b12009-06-12 02:51:34919#if defined(OS_WIN)
920 external_extension_providers_[Extension::EXTERNAL_REGISTRY] =
[email protected]da50530a2009-06-15 17:43:01921 linked_ptr<ExternalExtensionProvider>(
922 new ExternalRegistryExtensionProvider());
[email protected]a1257b12009-06-12 02:51:34923#endif
924}
925
926ExtensionsServiceBackend::~ExtensionsServiceBackend() {
[email protected]894bb502009-05-21 22:39:57927}
928
[email protected]b0beaa662009-02-26 00:04:15929void ExtensionsServiceBackend::LoadSingleExtension(
[email protected]894bb502009-05-21 22:39:57930 const FilePath& path_in, scoped_refptr<ExtensionsService> frontend) {
[email protected]b0beaa662009-02-26 00:04:15931 frontend_ = frontend;
932
933 // Explicit UI loads are always noisy.
934 alert_on_error_ = true;
935
[email protected]cc5da332009-03-04 08:02:51936 FilePath extension_path = path_in;
[email protected]f36fa4fb2009-06-19 18:23:50937 file_util::AbsolutePath(&extension_path);
[email protected]bf24d2c2009-02-24 23:07:45938
939 LOG(INFO) << "Loading single extension from " <<
[email protected]99efb7b12009-12-18 02:39:16940 extension_path.BaseName().value();
[email protected]bf24d2c2009-02-24 23:07:45941
[email protected]ab6f2b22009-07-28 23:28:37942 std::string error;
943 Extension* extension = extension_file_util::LoadExtension(
944 extension_path,
945 false, // Don't require id
946 &error);
947
948 if (!extension) {
949 ReportExtensionLoadError(extension_path, error);
950 return;
[email protected]0877fd92009-02-03 16:34:06951 }
[email protected]ab6f2b22009-07-28 23:28:37952
953 extension->set_location(Extension::LOAD);
[email protected]ae09ca62009-08-21 19:46:46954 ReportExtensionLoaded(extension);
[email protected]0877fd92009-02-03 16:34:06955}
956
[email protected]6014d672008-12-05 00:38:25957void ExtensionsServiceBackend::ReportExtensionLoadError(
[email protected]cc5da332009-03-04 08:02:51958 const FilePath& extension_path, const std::string &error) {
[email protected]95d29192009-10-30 01:49:06959 ChromeThread::PostTask(
960 ChromeThread::UI, FROM_HERE,
961 NewRunnableMethod(
962 frontend_,
[email protected]d11c8e92009-10-20 23:26:40963 &ExtensionsService::ReportExtensionLoadError, extension_path,
964 error, NotificationType::EXTENSION_INSTALL_ERROR, alert_on_error_));
[email protected]6014d672008-12-05 00:38:25965}
966
[email protected]ae09ca62009-08-21 19:46:46967void ExtensionsServiceBackend::ReportExtensionLoaded(Extension* extension) {
[email protected]95d29192009-10-30 01:49:06968 ChromeThread::PostTask(
969 ChromeThread::UI, FROM_HERE,
970 NewRunnableMethod(
971 frontend_, &ExtensionsService::OnExtensionLoaded, extension, true));
[email protected]6014d672008-12-05 00:38:25972}
[email protected]cc655912009-01-29 23:19:19973
[email protected]a1257b12009-06-12 02:51:34974bool ExtensionsServiceBackend::LookupExternalExtension(
975 const std::string& id, Version** version, Extension::Location* location) {
976 scoped_ptr<Version> extension_version;
977 for (ProviderMap::const_iterator i = external_extension_providers_.begin();
978 i != external_extension_providers_.end(); ++i) {
[email protected]da50530a2009-06-15 17:43:01979 const ExternalExtensionProvider* provider = i->second.get();
[email protected]a1257b12009-06-12 02:51:34980 extension_version.reset(provider->RegisteredVersion(id, location));
981 if (extension_version.get()) {
982 if (version)
983 *version = extension_version.release();
984 return true;
985 }
986 }
987 return false;
988}
989
[email protected]b0beaa662009-02-26 00:04:15990// Some extensions will autoupdate themselves externally from Chrome. These
991// are typically part of some larger client application package. To support
[email protected]25b34332009-06-05 21:53:19992// these, the extension will register its location in the the preferences file
993// (and also, on Windows, in the registry) and this code will periodically
[email protected]b0beaa662009-02-26 00:04:15994// check that location for a .crx file, which it will then install locally if
995// a new version is available.
996void ExtensionsServiceBackend::CheckForExternalUpdates(
[email protected]894bb502009-05-21 22:39:57997 std::set<std::string> ids_to_ignore,
998 scoped_refptr<ExtensionsService> frontend) {
[email protected]b0beaa662009-02-26 00:04:15999 // Note that this installation is intentionally silent (since it didn't
1000 // go through the front-end). Extensions that are registered in this
1001 // way are effectively considered 'pre-bundled', and so implicitly
1002 // trusted. In general, if something has HKLM or filesystem access,
1003 // they could install an extension manually themselves anyway.
1004 alert_on_error_ = false;
1005 frontend_ = frontend;
[email protected]b0beaa662009-02-26 00:04:151006
[email protected]a1257b12009-06-12 02:51:341007 // Ask each external extension provider to give us a call back for each
1008 // extension they know about. See OnExternalExtensionFound.
1009 for (ProviderMap::const_iterator i = external_extension_providers_.begin();
1010 i != external_extension_providers_.end(); ++i) {
[email protected]da50530a2009-06-15 17:43:011011 ExternalExtensionProvider* provider = i->second.get();
[email protected]a1257b12009-06-12 02:51:341012 provider->VisitRegisteredExtension(this, ids_to_ignore);
[email protected]25b34332009-06-05 21:53:191013 }
[email protected]b0beaa662009-02-26 00:04:151014}
1015
[email protected]ae09ca62009-08-21 19:46:461016void ExtensionsServiceBackend::CheckExternalUninstall(
1017 scoped_refptr<ExtensionsService> frontend, const std::string& id,
1018 Extension::Location location) {
[email protected]a1257b12009-06-12 02:51:341019 // Check if the providers know about this extension.
1020 ProviderMap::const_iterator i = external_extension_providers_.find(location);
[email protected]ae09ca62009-08-21 19:46:461021 if (i == external_extension_providers_.end()) {
1022 NOTREACHED() << "CheckExternalUninstall called for non-external extension "
1023 << location;
1024 return;
[email protected]b0beaa662009-02-26 00:04:151025 }
[email protected]25b34332009-06-05 21:53:191026
[email protected]ae09ca62009-08-21 19:46:461027 scoped_ptr<Version> version;
1028 version.reset(i->second->RegisteredVersion(id, NULL));
1029 if (version.get())
1030 return; // Yup, known extension, don't uninstall.
1031
1032 // This is an external extension that we don't have registered. Uninstall.
[email protected]95d29192009-10-30 01:49:061033 ChromeThread::PostTask(
1034 ChromeThread::UI, FROM_HERE,
1035 NewRunnableMethod(
1036 frontend.get(), &ExtensionsService::UninstallExtension, id, true));
[email protected]b0beaa662009-02-26 00:04:151037}
1038
[email protected]a1257b12009-06-12 02:51:341039void ExtensionsServiceBackend::ClearProvidersForTesting() {
1040 external_extension_providers_.clear();
1041}
1042
1043void ExtensionsServiceBackend::SetProviderForTesting(
1044 Extension::Location location,
1045 ExternalExtensionProvider* test_provider) {
1046 DCHECK(test_provider);
[email protected]da50530a2009-06-15 17:43:011047 external_extension_providers_[location] =
1048 linked_ptr<ExternalExtensionProvider>(test_provider);
[email protected]a1257b12009-06-12 02:51:341049}
1050
1051void ExtensionsServiceBackend::OnExternalExtensionFound(
[email protected]7577a5c52009-07-30 06:21:581052 const std::string& id, const Version* version, const FilePath& path,
1053 Extension::Location location) {
[email protected]95d29192009-10-30 01:49:061054 ChromeThread::PostTask(
1055 ChromeThread::UI, FROM_HERE,
1056 NewRunnableMethod(
1057 frontend_, &ExtensionsService::OnExternalExtensionFound, id,
1058 version->GetString(), path, location));
[email protected]cc655912009-01-29 23:19:191059}
[email protected]c6d474f82009-12-16 21:11:061060
1061void ExtensionsServiceBackend::ReloadExtensionManifestsForLocaleChanged(
1062 ExtensionPrefs::ExtensionsInfo* extensions_to_reload,
1063 base::TimeTicks start_time,
1064 scoped_refptr<ExtensionsService> frontend) {
1065 frontend_ = frontend;
1066
1067 for (size_t i = 0; i < extensions_to_reload->size(); ++i) {
1068 ExtensionInfo* info = extensions_to_reload->at(i).get();
1069 if (!info->extension_manifest.get())
1070 continue;
1071
1072 if (!extension_l10n_util::ShouldRelocalizeManifest(*info))
1073 continue;
1074
1075 // We need to reload original manifest in order to localize properly.
1076 std::string error;
1077 scoped_ptr<Extension> extension(extension_file_util::LoadExtension(
1078 info->extension_path, false, &error));
1079
1080 if (extension.get())
1081 extensions_to_reload->at(i)->extension_manifest.reset(
1082 static_cast<DictionaryValue*>(
1083 extension->manifest_value()->DeepCopy()));
1084 }
1085
1086 // Finish installing on UI thread.
1087 ChromeThread::PostTask(
1088 ChromeThread::UI, FROM_HERE,
1089 NewRunnableMethod(
1090 frontend_,
1091 &ExtensionsService::ContinueLoadAllExtensions,
1092 extensions_to_reload,
1093 start_time,
1094 true));
1095}