blob: 0a5f1452268ce5c2208b0cdc73a0b5b5dae8fef8 [file] [log] [blame]
[email protected]1e8c93f2010-02-08 22:58:311// Copyright (c) 2010 The Chromium Authors. All rights reserved.
[email protected]6014d672008-12-05 00:38:252// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/extensions/extensions_service.h"
6
[email protected]24b538a2010-02-27 01:22:447#include "base/basictypes.h"
[email protected]e2eb43112009-05-29 21:19:548#include "base/command_line.h"
[email protected]6014d672008-12-05 00:38:259#include "base/file_util.h"
[email protected]cc2c3432009-11-06 17:24:3610#include "base/histogram.h"
[email protected]24b538a2010-02-27 01:22:4411#include "base/string16.h"
[email protected]6014d672008-12-05 00:38:2512#include "base/string_util.h"
[email protected]cc2c3432009-11-06 17:24:3613#include "base/time.h"
[email protected]cc655912009-01-29 23:19:1914#include "base/values.h"
[email protected]15730c42009-09-03 00:03:2015#include "chrome/browser/browser_process.h"
[email protected]dbb92e0d2009-08-20 16:18:2116#include "chrome/browser/chrome_thread.h"
[email protected]4814b512009-11-07 00:12:2917#include "chrome/browser/debugger/devtools_manager.h"
[email protected]7577a5c52009-07-30 06:21:5818#include "chrome/browser/extensions/crx_installer.h"
[email protected]5cbe1e22010-01-30 01:18:5619#include "chrome/browser/extensions/extension_accessibility_api.h"
[email protected]840b0db2009-11-20 03:00:3820#include "chrome/browser/extensions/extension_bookmarks_module.h"
[email protected]b68d5ed2009-04-16 02:41:2821#include "chrome/browser/extensions/extension_browser_event_router.h"
[email protected]86c008e82009-08-28 20:26:0522#include "chrome/browser/extensions/extension_dom_ui.h"
[email protected]de768a832009-10-30 05:25:0123#include "chrome/browser/extensions/extension_history_api.h"
[email protected]b1748b1d82009-11-30 20:32:5624#include "chrome/browser/extensions/extension_host.h"
[email protected]4814b512009-11-07 00:12:2925#include "chrome/browser/extensions/extension_process_manager.h"
[email protected]93fd78f42009-07-10 16:43:1726#include "chrome/browser/extensions/extension_updater.h"
[email protected]a1257b12009-06-12 02:51:3427#include "chrome/browser/extensions/external_extension_provider.h"
28#include "chrome/browser/extensions/external_pref_extension_provider.h"
[email protected]052313b2010-02-19 09:43:0829#include "chrome/browser/pref_service.h"
[email protected]81e63782009-02-27 19:35:0930#include "chrome/browser/profile.h"
[email protected]62d30f42009-10-01 22:36:0631#include "chrome/browser/net/chrome_url_request_context.h"
[email protected]aab98a52009-12-02 03:22:3532#include "chrome/common/child_process_logging.h"
[email protected]e2eb43112009-05-29 21:19:5433#include "chrome/common/chrome_switches.h"
[email protected]5b1a0e22009-05-26 19:00:5834#include "chrome/common/extensions/extension.h"
[email protected]d7b36dc2009-10-29 21:47:4035#include "chrome/common/extensions/extension_constants.h"
[email protected]5b1a0e22009-05-26 19:00:5836#include "chrome/common/extensions/extension_error_reporter.h"
[email protected]7c927b62010-02-24 09:54:1337#include "chrome/common/extensions/extension_file_util.h"
[email protected]c6d474f82009-12-16 21:11:0638#include "chrome/common/extensions/extension_l10n_util.h"
[email protected]82891262008-12-24 00:21:2639#include "chrome/common/notification_service.h"
[email protected]4814b512009-11-07 00:12:2940#include "chrome/common/notification_type.h"
[email protected]25b34332009-06-05 21:53:1941#include "chrome/common/pref_names.h"
[email protected]a57209872009-05-04 22:53:1442#include "chrome/common/url_constants.h"
[email protected]24b538a2010-02-27 01:22:4443#include "webkit/database/database_tracker.h"
44#include "webkit/database/database_util.h"
[email protected]c64631652009-04-29 22:24:3145
[email protected]79db6232009-02-13 20:51:2046#if defined(OS_WIN)
[email protected]a1257b12009-06-12 02:51:3447#include "chrome/browser/extensions/external_registry_extension_provider_win.h"
[email protected]79db6232009-02-13 20:51:2048#endif
[email protected]6014d672008-12-05 00:38:2549
[email protected]5ef47ec2010-01-28 05:58:0550using base::Time;
51
[email protected]c6d474f82009-12-16 21:11:0652namespace errors = extension_manifest_errors;
53
[email protected]b6ab96d2009-08-20 18:58:1954namespace {
55
56// Helper class to collect the IDs of every extension listed in the prefs.
57class InstalledExtensionSet {
58 public:
[email protected]c6d474f82009-12-16 21:11:0659 explicit InstalledExtensionSet(ExtensionPrefs* prefs) {
60 scoped_ptr<ExtensionPrefs::ExtensionsInfo> info(
61 ExtensionPrefs::CollectExtensionsInfo(prefs));
62
63 for (size_t i = 0; i < info->size(); ++i) {
64 std::string version;
65 const DictionaryValue* manifest = info->at(i)->extension_manifest.get();
66 if (!manifest ||
67 !manifest->GetString(extension_manifest_keys::kVersion, &version)) {
68 // Without a version, the extension is invalid. Ignoring it here will
69 // cause it to get garbage collected.
70 continue;
71 }
72 extensions_.insert(info->at(i)->extension_id);
73 versions_[info->at(i)->extension_id] = version;
74 }
[email protected]b6ab96d2009-08-20 18:58:1975 }
76
77 const std::set<std::string>& extensions() { return extensions_; }
[email protected]4559a7d2009-12-02 01:42:4178 const std::map<std::string, std::string>& versions() { return versions_; }
[email protected]b6ab96d2009-08-20 18:58:1979
80 private:
[email protected]b6ab96d2009-08-20 18:58:1981 std::set<std::string> extensions_;
[email protected]4559a7d2009-12-02 01:42:4182 std::map<std::string, std::string> versions_;
[email protected]b6ab96d2009-08-20 18:58:1983};
84
[email protected]c6d474f82009-12-16 21:11:0685} // namespace
[email protected]b6ab96d2009-08-20 18:58:1986
[email protected]25b34332009-06-05 21:53:1987// ExtensionsService.
[email protected]6014d672008-12-05 00:38:2588
[email protected]cc655912009-01-29 23:19:1989const char* ExtensionsService::kInstallDirectoryName = "Extensions";
90const char* ExtensionsService::kCurrentVersionFileName = "Current Version";
[email protected]494c06e2009-07-25 01:06:4291
[email protected]b7c2f252009-12-08 00:47:2392// static
93bool ExtensionsService::IsDownloadFromGallery(const GURL& download_url,
94 const GURL& referrer_url) {
95 if (StartsWithASCII(download_url.spec(),
96 extension_urls::kMiniGalleryDownloadPrefix, false) &&
97 StartsWithASCII(referrer_url.spec(),
98 extension_urls::kMiniGalleryBrowsePrefix, false)) {
99 return true;
100 }
101
102 if (StartsWithASCII(download_url.spec(),
103 extension_urls::kGalleryDownloadPrefix, false) &&
104 StartsWithASCII(referrer_url.spec(),
105 extension_urls::kGalleryBrowsePrefix, false)) {
106 return true;
107 }
108
109 return false;
110}
111
[email protected]ac025282009-12-16 19:16:38112bool ExtensionsService::IsDownloadFromMiniGallery(const GURL& download_url) {
113 return StartsWithASCII(download_url.spec(),
114 extension_urls::kMiniGalleryDownloadPrefix,
115 false); // case_sensitive
116}
117
[email protected]81e63782009-02-27 19:35:09118ExtensionsService::ExtensionsService(Profile* profile,
[email protected]36a784c2009-06-23 06:21:08119 const CommandLine* command_line,
[email protected]a9b00ac2009-06-25 21:03:23120 PrefService* prefs,
121 const FilePath& install_directory,
[email protected]93fd78f42009-07-10 16:43:17122 bool autoupdate_enabled)
[email protected]6ef635e42009-07-26 06:16:12123 : profile_(profile),
124 extension_prefs_(new ExtensionPrefs(prefs, install_directory)),
[email protected]a9b00ac2009-06-25 21:03:23125 install_directory_(install_directory),
[email protected]6d60703b2009-08-29 01:29:23126 extensions_enabled_(true),
[email protected]e81dba32009-06-19 20:19:13127 show_extensions_prompts_(true),
[email protected]e0360f2c2009-12-07 22:34:31128 ready_(false),
129 ALLOW_THIS_IN_INITIALIZER_LIST(toolbar_model_(this)) {
[email protected]36a784c2009-06-23 06:21:08130 // Figure out if extension installation should be enabled.
[email protected]6d60703b2009-08-29 01:29:23131 if (command_line->HasSwitch(switches::kDisableExtensions)) {
132 extensions_enabled_ = false;
133 } else if (profile->GetPrefs()->GetBoolean(prefs::kDisableExtensions)) {
134 extensions_enabled_ = false;
[email protected]6b75ec32009-08-14 06:37:18135 }
[email protected]36a784c2009-06-23 06:21:08136
[email protected]4814b512009-11-07 00:12:29137 registrar_.Add(this, NotificationType::EXTENSION_HOST_DID_STOP_LOADING,
138 NotificationService::AllSources());
[email protected]a4ed6282009-12-14 20:51:16139 registrar_.Add(this, NotificationType::EXTENSION_PROCESS_TERMINATED,
[email protected]31f77262009-12-02 20:48:53140 Source<Profile>(profile_));
[email protected]4814b512009-11-07 00:12:29141
[email protected]93fd78f42009-07-10 16:43:17142 // Set up the ExtensionUpdater
143 if (autoupdate_enabled) {
144 int update_frequency = kDefaultUpdateFrequencySeconds;
145 if (command_line->HasSwitch(switches::kExtensionsUpdateFrequency)) {
[email protected]c4e52f0d2009-11-06 19:55:16146 update_frequency = StringToInt(command_line->GetSwitchValueASCII(
147 switches::kExtensionsUpdateFrequency));
[email protected]93fd78f42009-07-10 16:43:17148 }
[email protected]95d29192009-10-30 01:49:06149 updater_ = new ExtensionUpdater(this, prefs, update_frequency);
[email protected]93fd78f42009-07-10 16:43:17150 }
151
[email protected]95d29192009-10-30 01:49:06152 backend_ = new ExtensionsServiceBackend(install_directory_);
[email protected]6014d672008-12-05 00:38:25153}
154
155ExtensionsService::~ExtensionsService() {
[email protected]9f1087e2009-06-15 17:29:32156 UnloadAllExtensions();
[email protected]93fd78f42009-07-10 16:43:17157 if (updater_.get()) {
158 updater_->Stop();
159 }
[email protected]6014d672008-12-05 00:38:25160}
161
[email protected]9f1087e2009-06-15 17:29:32162void ExtensionsService::Init() {
[email protected]c6e4a3412009-06-24 15:45:29163 DCHECK(!ready_);
[email protected]93fd78f42009-07-10 16:43:17164 DCHECK_EQ(extensions_.size(), 0u);
[email protected]9f1087e2009-06-15 17:29:32165
[email protected]95dd38f2009-10-20 20:09:15166 // Hack: we need to ensure the ResourceDispatcherHost is ready before we load
167 // the first extension, because its members listen for loaded notifications.
168 g_browser_process->resource_dispatcher_host();
169
[email protected]de768a832009-10-30 05:25:01170 // Start up the extension event routers.
171 ExtensionHistoryEventRouter::GetInstance()->ObserveProfile(profile_);
[email protected]5cbe1e22010-01-30 01:18:56172 ExtensionAccessibilityEventRouter::GetInstance()->ObserveProfile(profile_);
[email protected]de768a832009-10-30 05:25:01173
[email protected]9f1087e2009-06-15 17:29:32174 LoadAllExtensions();
[email protected]894bb502009-05-21 22:39:57175
[email protected]9f1087e2009-06-15 17:29:32176 // TODO(erikkay) this should probably be deferred to a future point
177 // rather than running immediately at startup.
[email protected]93fd78f42009-07-10 16:43:17178 CheckForExternalUpdates();
[email protected]894bb502009-05-21 22:39:57179
[email protected]9f1087e2009-06-15 17:29:32180 // TODO(erikkay) this should probably be deferred as well.
181 GarbageCollectExtensions();
[email protected]6014d672008-12-05 00:38:25182}
183
[email protected]3cf4f0992009-02-03 23:00:30184void ExtensionsService::InstallExtension(const FilePath& extension_path) {
[email protected]2a464a92009-08-01 17:58:35185 CrxInstaller::Start(extension_path, install_directory_, Extension::INTERNAL,
186 "", // no expected id
187 false, // don't delete crx when complete
[email protected]2a409532009-08-28 19:39:44188 true, // allow privilege increase
[email protected]2a464a92009-08-01 17:58:35189 this,
190 NULL); // no client (silent install)
[email protected]3cf4f0992009-02-03 23:00:30191}
192
[email protected]e957fe52009-06-23 16:51:05193void ExtensionsService::UpdateExtension(const std::string& id,
[email protected]7577a5c52009-07-30 06:21:58194 const FilePath& extension_path) {
[email protected]0c6da502009-08-14 22:32:39195 if (!GetExtensionByIdInternal(id, true, true)) {
[email protected]e957fe52009-06-23 16:51:05196 LOG(WARNING) << "Will not update extension " << id << " because it is not "
[email protected]4c967932009-07-31 01:15:49197 << "installed";
198 return;
[email protected]e957fe52009-06-23 16:51:05199 }
200
[email protected]2a464a92009-08-01 17:58:35201 CrxInstaller::Start(extension_path, install_directory_, Extension::INTERNAL,
202 id,
203 true, // delete crx when complete
[email protected]2a409532009-08-28 19:39:44204 false, // do not allow upgrade of privileges
[email protected]2a464a92009-08-01 17:58:35205 this,
206 NULL); // no client (silent install)
[email protected]e957fe52009-06-23 16:51:05207}
208
[email protected]9cddd4702009-07-27 22:09:40209void ExtensionsService::ReloadExtension(const std::string& extension_id) {
[email protected]b65272f2009-08-31 15:47:06210 FilePath path;
[email protected]61b411612009-11-10 23:17:41211 Extension* current_extension = GetExtensionById(extension_id, false);
[email protected]9cddd4702009-07-27 22:09:40212
[email protected]b65272f2009-08-31 15:47:06213 // Unload the extension if it's loaded. It might not be loaded if it crashed.
214 if (current_extension) {
[email protected]4814b512009-11-07 00:12:29215 // If the extension has an inspector open for its background page, detach
216 // the inspector and hang onto a cookie for it, so that we can reattach
217 // later.
218 ExtensionProcessManager* manager = profile_->GetExtensionProcessManager();
219 ExtensionHost* host = manager->GetBackgroundHostForExtension(
220 current_extension);
221 if (host) {
222 // Look for an open inspector for the background page.
223 int devtools_cookie = DevToolsManager::GetInstance()->DetachClientHost(
224 host->render_view_host());
225 if (devtools_cookie >= 0)
226 orphaned_dev_tools_[extension_id] = devtools_cookie;
227 }
228
[email protected]b65272f2009-08-31 15:47:06229 path = current_extension->path();
230 UnloadExtension(extension_id);
[email protected]1eb175082010-02-10 09:26:16231 } else {
232 path = unloaded_extension_paths_[extension_id];
[email protected]b65272f2009-08-31 15:47:06233 }
234
[email protected]1eb175082010-02-10 09:26:16235 // We should always be able to remember the extension's path. If it's not in
236 // the map, someone failed to update |unloaded_extension_paths_|.
237 CHECK(!path.empty());
[email protected]b65272f2009-08-31 15:47:06238
[email protected]1eb175082010-02-10 09:26:16239 LoadExtension(path);
[email protected]9cddd4702009-07-27 22:09:40240}
241
[email protected]27b985d2009-06-25 17:53:15242void ExtensionsService::UninstallExtension(const std::string& extension_id,
243 bool external_uninstall) {
[email protected]0c6da502009-08-14 22:32:39244 Extension* extension = GetExtensionByIdInternal(extension_id, true, true);
[email protected]631cf822009-05-15 07:01:25245
[email protected]9f1087e2009-06-15 17:29:32246 // Callers should not send us nonexistant extensions.
[email protected]e72e8eb82009-06-18 17:21:51247 DCHECK(extension);
[email protected]9f1087e2009-06-15 17:29:32248
[email protected]27b985d2009-06-25 17:53:15249 extension_prefs_->OnExtensionUninstalled(extension, external_uninstall);
[email protected]9f1087e2009-06-15 17:29:32250
251 // Tell the backend to start deleting installed extensions on the file thread.
[email protected]e72e8eb82009-06-18 17:21:51252 if (Extension::LOAD != extension->location()) {
[email protected]95d29192009-10-30 01:49:06253 ChromeThread::PostTask(
254 ChromeThread::FILE, FROM_HERE,
255 NewRunnableFunction(
256 &extension_file_util::UninstallExtension, extension_id,
257 install_directory_));
[email protected]9f1087e2009-06-15 17:29:32258 }
259
[email protected]86c008e82009-08-28 20:26:05260 ExtensionDOMUI::UnregisterChromeURLOverrides(profile_,
261 extension->GetChromeURLOverrides());
262
[email protected]9f1087e2009-06-15 17:29:32263 UnloadExtension(extension_id);
264}
265
[email protected]0c6da502009-08-14 22:32:39266void ExtensionsService::EnableExtension(const std::string& extension_id) {
267 Extension* extension = GetExtensionByIdInternal(extension_id, false, true);
268 if (!extension) {
269 NOTREACHED() << "Trying to enable an extension that isn't disabled.";
270 return;
271 }
272
[email protected]1784e83a2009-09-08 21:01:52273 // Remember that we enabled it, unless it's temporary.
274 if (extension->location() != Extension::LOAD)
275 extension_prefs_->SetExtensionState(extension, Extension::ENABLED);
276
[email protected]0c6da502009-08-14 22:32:39277 // Move it over to the enabled list.
[email protected]0c6da502009-08-14 22:32:39278 extensions_.push_back(extension);
279 ExtensionList::iterator iter = std::find(disabled_extensions_.begin(),
280 disabled_extensions_.end(),
281 extension);
282 disabled_extensions_.erase(iter);
283
[email protected]86c008e82009-08-28 20:26:05284 ExtensionDOMUI::RegisterChromeURLOverrides(profile_,
285 extension->GetChromeURLOverrides());
286
[email protected]62d30f42009-10-01 22:36:06287 NotifyExtensionLoaded(extension);
[email protected]aab98a52009-12-02 03:22:35288 UpdateActiveExtensionsInCrashReporter();
[email protected]0c6da502009-08-14 22:32:39289}
290
[email protected]1784e83a2009-09-08 21:01:52291void ExtensionsService::DisableExtension(const std::string& extension_id) {
292 Extension* extension = GetExtensionByIdInternal(extension_id, true, false);
[email protected]b2ba9962009-12-10 20:10:15293 // The extension may have been disabled already.
294 if (!extension)
[email protected]1784e83a2009-09-08 21:01:52295 return;
[email protected]1784e83a2009-09-08 21:01:52296
297 // Remember that we disabled it, unless it's temporary.
298 if (extension->location() != Extension::LOAD)
299 extension_prefs_->SetExtensionState(extension, Extension::DISABLED);
300
301 // Move it over to the disabled list.
302 disabled_extensions_.push_back(extension);
303 ExtensionList::iterator iter = std::find(extensions_.begin(),
304 extensions_.end(),
305 extension);
306 extensions_.erase(iter);
307
308 ExtensionDOMUI::UnregisterChromeURLOverrides(profile_,
309 extension->GetChromeURLOverrides());
310
[email protected]62d30f42009-10-01 22:36:06311 NotifyExtensionUnloaded(extension);
[email protected]aab98a52009-12-02 03:22:35312 UpdateActiveExtensionsInCrashReporter();
[email protected]1784e83a2009-09-08 21:01:52313}
314
[email protected]9f1087e2009-06-15 17:29:32315void ExtensionsService::LoadExtension(const FilePath& extension_path) {
[email protected]95d29192009-10-30 01:49:06316 ChromeThread::PostTask(
317 ChromeThread::FILE, FROM_HERE,
318 NewRunnableMethod(
319 backend_.get(),
320 &ExtensionsServiceBackend::LoadSingleExtension,
321 extension_path, scoped_refptr<ExtensionsService>(this)));
[email protected]9f1087e2009-06-15 17:29:32322}
323
324void ExtensionsService::LoadAllExtensions() {
[email protected]cc2c3432009-11-06 17:24:36325 base::TimeTicks start_time = base::TimeTicks::Now();
326
[email protected]e72e8eb82009-06-18 17:21:51327 // Load the previously installed extensions.
[email protected]c6d474f82009-12-16 21:11:06328 scoped_ptr<ExtensionPrefs::ExtensionsInfo> info(
329 ExtensionPrefs::CollectExtensionsInfo(extension_prefs_.get()));
330
331 // If any extensions need localization, we bounce them all to the file thread
332 // for re-reading and localization.
333 for (size_t i = 0; i < info->size(); ++i) {
334 if (extension_l10n_util::ShouldRelocalizeManifest(*info->at(i))) {
335 ChromeThread::PostTask(
336 ChromeThread::FILE, FROM_HERE, NewRunnableMethod(
337 backend_.get(),
338 &ExtensionsServiceBackend::ReloadExtensionManifestsForLocaleChanged,
339 info.release(), // Callee takes ownership of the memory.
340 start_time,
341 scoped_refptr<ExtensionsService>(this)));
342 return;
343 }
344 }
345
346 // Don't update prefs.
347 // Callee takes ownership of the memory.
348 ContinueLoadAllExtensions(info.release(), start_time, false);
349}
350
351void ExtensionsService::ContinueLoadAllExtensions(
352 ExtensionPrefs::ExtensionsInfo* extensions_info,
353 base::TimeTicks start_time,
354 bool write_to_prefs) {
355 scoped_ptr<ExtensionPrefs::ExtensionsInfo> info(extensions_info);
356
357 for (size_t i = 0; i < info->size(); ++i) {
358 LoadInstalledExtension(*info->at(i), write_to_prefs);
359 }
360
[email protected]ae09ca62009-08-21 19:46:46361 OnLoadedInstalledExtensions();
[email protected]cc2c3432009-11-06 17:24:36362
363 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadAll", extensions_.size());
364 UMA_HISTOGRAM_COUNTS_100("Extensions.Disabled", disabled_extensions_.size());
365
366 if (extensions_.size()) {
367 UMA_HISTOGRAM_TIMES("Extensions.LoadAllTime",
368 base::TimeTicks::Now() - start_time);
369
370 int user_script_count = 0;
371 int extension_count = 0;
372 int theme_count = 0;
373 int external_count = 0;
374 int page_action_count = 0;
375 int browser_action_count = 0;
376 ExtensionList::iterator ex;
377 for (ex = extensions_.begin(); ex != extensions_.end(); ++ex) {
378 if ((*ex)->IsTheme()) {
379 theme_count++;
380 } else if ((*ex)->converted_from_user_script()) {
381 user_script_count++;
382 } else {
383 extension_count++;
384 }
385 if (Extension::IsExternalLocation((*ex)->location())) {
386 external_count++;
387 }
388 if ((*ex)->page_action() != NULL) {
389 page_action_count++;
390 }
391 if ((*ex)->browser_action() != NULL) {
392 browser_action_count++;
393 }
394 }
395 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadExtension", extension_count);
396 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadUserScript", user_script_count);
397 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadTheme", theme_count);
398 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadExternal", external_count);
399 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadPageAction", page_action_count);
400 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadBrowserAction",
401 browser_action_count);
402 }
[email protected]ae09ca62009-08-21 19:46:46403}
404
[email protected]c6d474f82009-12-16 21:11:06405void ExtensionsService::LoadInstalledExtension(const ExtensionInfo& info,
406 bool write_to_prefs) {
[email protected]ae09ca62009-08-21 19:46:46407 std::string error;
408 Extension* extension = NULL;
[email protected]c6d474f82009-12-16 21:11:06409 if (info.extension_manifest.get()) {
410 scoped_ptr<Extension> tmp(new Extension(info.extension_path));
411 if (tmp->InitFromValue(*info.extension_manifest, true, &error))
[email protected]ae09ca62009-08-21 19:46:46412 extension = tmp.release();
[email protected]ae09ca62009-08-21 19:46:46413 } else {
[email protected]c6d474f82009-12-16 21:11:06414 error = errors::kManifestUnreadable;
[email protected]ae09ca62009-08-21 19:46:46415 }
416
417 if (!extension) {
[email protected]c6d474f82009-12-16 21:11:06418 ReportExtensionLoadError(info.extension_path,
[email protected]d11c8e92009-10-20 23:26:40419 error,
420 NotificationType::EXTENSION_INSTALL_ERROR,
421 false);
[email protected]ae09ca62009-08-21 19:46:46422 return;
423 }
424
[email protected]c6d474f82009-12-16 21:11:06425 extension->set_location(info.extension_location);
426
427 if (write_to_prefs)
428 extension_prefs_->UpdateManifest(extension);
429
[email protected]2a409532009-08-28 19:39:44430 OnExtensionLoaded(extension, true);
[email protected]ae09ca62009-08-21 19:46:46431
[email protected]c6d474f82009-12-16 21:11:06432 if (info.extension_location == Extension::EXTERNAL_PREF ||
433 info.extension_location == Extension::EXTERNAL_REGISTRY) {
[email protected]95d29192009-10-30 01:49:06434 ChromeThread::PostTask(
435 ChromeThread::FILE, FROM_HERE,
436 NewRunnableMethod(
[email protected]c6d474f82009-12-16 21:11:06437 backend_.get(),
438 &ExtensionsServiceBackend::CheckExternalUninstall,
439 scoped_refptr<ExtensionsService>(this),
440 info.extension_id,
441 info.extension_location));
[email protected]ae09ca62009-08-21 19:46:46442 }
[email protected]9f1087e2009-06-15 17:29:32443}
444
[email protected]62d30f42009-10-01 22:36:06445void ExtensionsService::NotifyExtensionLoaded(Extension* extension) {
446 LOG(INFO) << "Sending EXTENSION_LOADED";
447
448 // The ChromeURLRequestContext needs to be first to know that the extension
449 // was loaded, otherwise a race can arise where a renderer that is created
450 // for the extension may try to load an extension URL with an extension id
451 // that the request context doesn't yet know about.
452 if (profile_ && !profile_->IsOffTheRecord()) {
[email protected]be180c802009-10-23 06:33:31453 ChromeURLRequestContextGetter* context_getter =
454 static_cast<ChromeURLRequestContextGetter*>(
455 profile_->GetRequestContext());
456 if (context_getter) {
[email protected]95d29192009-10-30 01:49:06457 ChromeThread::PostTask(
458 ChromeThread::IO, FROM_HERE,
459 NewRunnableMethod(
460 context_getter,
461 &ChromeURLRequestContextGetter::OnNewExtensions,
462 extension->id(),
[email protected]0ce3f5982010-01-28 23:04:27463 new ChromeURLRequestContext::ExtensionInfo(
464 extension->path(),
465 extension->default_locale(),
[email protected]b30e0dd2010-01-29 23:33:21466 extension->app_extent(),
[email protected]0ce3f5982010-01-28 23:04:27467 extension->api_permissions())));
[email protected]62d30f42009-10-01 22:36:06468 }
[email protected]24b538a2010-02-27 01:22:44469
470 // Check if this permission requires unlimited storage quota
471 if (extension->HasApiPermission(Extension::kUnlimitedStoragePermission)) {
472 string16 origin_identifier =
473 webkit_database::DatabaseUtil::GetOriginIdentifier(extension->url());
474 ChromeThread::PostTask(
475 ChromeThread::FILE, FROM_HERE,
476 NewRunnableMethod(
477 profile_->GetDatabaseTracker(),
478 &webkit_database::DatabaseTracker::SetOriginQuotaInMemory,
479 origin_identifier,
480 kint64max));
481 }
[email protected]62d30f42009-10-01 22:36:06482 }
483
484 NotificationService::current()->Notify(
485 NotificationType::EXTENSION_LOADED,
[email protected]24e7a9d2009-11-04 11:11:34486 Source<Profile>(profile_),
[email protected]62d30f42009-10-01 22:36:06487 Details<Extension>(extension));
488}
489
490void ExtensionsService::NotifyExtensionUnloaded(Extension* extension) {
491 LOG(INFO) << "Sending EXTENSION_UNLOADED";
492
493 NotificationService::current()->Notify(
494 NotificationType::EXTENSION_UNLOADED,
[email protected]24e7a9d2009-11-04 11:11:34495 Source<Profile>(profile_),
[email protected]62d30f42009-10-01 22:36:06496 Details<Extension>(extension));
497
498 if (profile_ && !profile_->IsOffTheRecord()) {
[email protected]be180c802009-10-23 06:33:31499 ChromeURLRequestContextGetter* context_getter =
500 static_cast<ChromeURLRequestContextGetter*>(
501 profile_->GetRequestContext());
502 if (context_getter) {
[email protected]95d29192009-10-30 01:49:06503 ChromeThread::PostTask(
504 ChromeThread::IO, FROM_HERE,
[email protected]be180c802009-10-23 06:33:31505 NewRunnableMethod(
506 context_getter,
507 &ChromeURLRequestContextGetter::OnUnloadedExtension,
508 extension->id()));
[email protected]62d30f42009-10-01 22:36:06509 }
510 }
511}
512
[email protected]6b75ec32009-08-14 06:37:18513void ExtensionsService::UpdateExtensionBlacklist(
514 const std::vector<std::string>& blacklist) {
515 // Use this set to indicate if an extension in the blacklist has been used.
516 std::set<std::string> blacklist_set;
517 for (unsigned int i = 0; i < blacklist.size(); ++i) {
518 if (Extension::IdIsValid(blacklist[i])) {
519 blacklist_set.insert(blacklist[i]);
520 }
521 }
522 extension_prefs_->UpdateBlacklist(blacklist_set);
523 std::vector<std::string> to_be_removed;
524 // Loop current extensions, unload installed extensions.
525 for (ExtensionList::const_iterator iter = extensions_.begin();
526 iter != extensions_.end(); ++iter) {
527 Extension* extension = (*iter);
528 if (blacklist_set.find(extension->id()) != blacklist_set.end()) {
529 to_be_removed.push_back(extension->id());
530 }
531 }
532
533 // UnloadExtension will change the extensions_ list. So, we should
534 // call it outside the iterator loop.
535 for (unsigned int i = 0; i < to_be_removed.size(); ++i) {
536 UnloadExtension(to_be_removed[i]);
537 }
538}
539
[email protected]5ef47ec2010-01-28 05:58:05540void ExtensionsService::SetLastPingDay(const std::string& extension_id,
541 const base::Time& time) {
542 extension_prefs_->SetLastPingDay(extension_id, time);
543}
544
545base::Time ExtensionsService::LastPingDay(const std::string& extension_id) {
546 return extension_prefs_->LastPingDay(extension_id);
547}
548
[email protected]55a35692010-02-11 23:25:21549bool ExtensionsService::IsIncognitoEnabled(const std::string& extension_id) {
[email protected]db7331a2010-02-25 22:10:50550 return extension_prefs_->IsIncognitoEnabled(extension_id);
551}
[email protected]55a35692010-02-11 23:25:21552
[email protected]db7331a2010-02-25 22:10:50553void ExtensionsService::SetIsIncognitoEnabled(const std::string& extension_id,
554 bool enabled) {
555 Extension* extension = GetExtensionByIdInternal(extension_id, true, true);
556 extension_prefs_->SetIsIncognitoEnabled(extension_id, enabled);
557
558 std::pair<Extension*, bool> details(extension, enabled);
559 NotificationService::current()->Notify(
560 NotificationType::EXTENSION_INCOGNITO_CHANGED,
561 Source<Profile>(profile_),
562 Details<std::pair<Extension*, bool> >(&details));
[email protected]55a35692010-02-11 23:25:21563}
564
[email protected]93fd78f42009-07-10 16:43:17565void ExtensionsService::CheckForExternalUpdates() {
[email protected]9f1087e2009-06-15 17:29:32566 // This installs or updates externally provided extensions.
[email protected]7577a5c52009-07-30 06:21:58567 // TODO(aa): Why pass this list into the provider, why not just filter it
568 // later?
[email protected]9f1087e2009-06-15 17:29:32569 std::set<std::string> killed_extensions;
[email protected]e72e8eb82009-06-18 17:21:51570 extension_prefs_->GetKilledExtensionIds(&killed_extensions);
[email protected]95d29192009-10-30 01:49:06571 ChromeThread::PostTask(
572 ChromeThread::FILE, FROM_HERE,
573 NewRunnableMethod(
574 backend_.get(), &ExtensionsServiceBackend::CheckForExternalUpdates,
575 killed_extensions, scoped_refptr<ExtensionsService>(this)));
[email protected]9f1087e2009-06-15 17:29:32576}
577
578void ExtensionsService::UnloadExtension(const std::string& extension_id) {
[email protected]27e469a2010-01-11 20:35:09579 // Make sure the extension gets deleted after we return from this function.
[email protected]0c6da502009-08-14 22:32:39580 scoped_ptr<Extension> extension(
581 GetExtensionByIdInternal(extension_id, true, true));
[email protected]631cf822009-05-15 07:01:25582
[email protected]894bb502009-05-21 22:39:57583 // Callers should not send us nonexistant extensions.
[email protected]0c6da502009-08-14 22:32:39584 CHECK(extension.get());
585
[email protected]1eb175082010-02-10 09:26:16586 // Keep information about the extension so that we can reload it later
587 // even if it's not permanently installed.
588 unloaded_extension_paths_[extension->id()] = extension->path();
589
[email protected]86c008e82009-08-28 20:26:05590 ExtensionDOMUI::UnregisterChromeURLOverrides(profile_,
591 extension->GetChromeURLOverrides());
592
[email protected]0c6da502009-08-14 22:32:39593 ExtensionList::iterator iter = std::find(disabled_extensions_.begin(),
594 disabled_extensions_.end(),
595 extension.get());
596 if (iter != disabled_extensions_.end()) {
[email protected]0c6da502009-08-14 22:32:39597 disabled_extensions_.erase(iter);
[email protected]866930682009-08-18 22:53:47598 NotificationService::current()->Notify(
599 NotificationType::EXTENSION_UNLOADED_DISABLED,
[email protected]24e7a9d2009-11-04 11:11:34600 Source<Profile>(profile_),
[email protected]866930682009-08-18 22:53:47601 Details<Extension>(extension.get()));
[email protected]0c6da502009-08-14 22:32:39602 return;
603 }
604
605 iter = std::find(extensions_.begin(), extensions_.end(), extension.get());
[email protected]894bb502009-05-21 22:39:57606
[email protected]631cf822009-05-15 07:01:25607 // Remove the extension from our list.
608 extensions_.erase(iter);
609
[email protected]62d30f42009-10-01 22:36:06610 NotifyExtensionUnloaded(extension.get());
[email protected]aab98a52009-12-02 03:22:35611 UpdateActiveExtensionsInCrashReporter();
[email protected]631cf822009-05-15 07:01:25612}
613
[email protected]9f1087e2009-06-15 17:29:32614void ExtensionsService::UnloadAllExtensions() {
615 ExtensionList::iterator iter;
[email protected]c6e4a3412009-06-24 15:45:29616 for (iter = extensions_.begin(); iter != extensions_.end(); ++iter)
[email protected]9f1087e2009-06-15 17:29:32617 delete *iter;
[email protected]9f1087e2009-06-15 17:29:32618 extensions_.clear();
[email protected]c6e4a3412009-06-24 15:45:29619
620 // TODO(erikkay) should there be a notification for this? We can't use
621 // EXTENSION_UNLOADED since that implies that the extension has been disabled
622 // or uninstalled, and UnloadAll is just part of shutdown.
[email protected]9f1087e2009-06-15 17:29:32623}
624
625void ExtensionsService::ReloadExtensions() {
626 UnloadAllExtensions();
627 LoadAllExtensions();
628}
629
630void ExtensionsService::GarbageCollectExtensions() {
[email protected]c6d474f82009-12-16 21:11:06631 InstalledExtensionSet installed(extension_prefs_.get());
[email protected]95d29192009-10-30 01:49:06632 ChromeThread::PostTask(
633 ChromeThread::FILE, FROM_HERE,
634 NewRunnableFunction(
635 &extension_file_util::GarbageCollectExtensions, install_directory_,
[email protected]4559a7d2009-12-02 01:42:41636 installed.extensions(), installed.versions()));
[email protected]3cf4f0992009-02-03 23:00:30637}
638
[email protected]e72e8eb82009-06-18 17:21:51639void ExtensionsService::OnLoadedInstalledExtensions() {
[email protected]e81dba32009-06-19 20:19:13640 ready_ = true;
[email protected]93fd78f42009-07-10 16:43:17641 if (updater_.get()) {
642 updater_->Start();
643 }
[email protected]e72e8eb82009-06-18 17:21:51644 NotificationService::current()->Notify(
645 NotificationType::EXTENSIONS_READY,
[email protected]24e7a9d2009-11-04 11:11:34646 Source<Profile>(profile_),
[email protected]e72e8eb82009-06-18 17:21:51647 NotificationService::NoDetails());
648}
649
[email protected]2a409532009-08-28 19:39:44650void ExtensionsService::OnExtensionLoaded(Extension* extension,
651 bool allow_privilege_increase) {
[email protected]ae09ca62009-08-21 19:46:46652 // Ensure extension is deleted unless we transfer ownership.
653 scoped_ptr<Extension> scoped_extension(extension);
[email protected]9f1087e2009-06-15 17:29:32654
[email protected]1eb175082010-02-10 09:26:16655 // The extension is now loaded, remove its data from unloaded extension map.
656 unloaded_extension_paths_.erase(extension->id());
657
[email protected]ae09ca62009-08-21 19:46:46658 if (extensions_enabled() ||
659 extension->IsTheme() ||
660 extension->location() == Extension::LOAD ||
661 Extension::IsExternalLocation(extension->location())) {
662 Extension* old = GetExtensionByIdInternal(extension->id(), true, true);
663 if (old) {
664 if (extension->version()->CompareTo(*(old->version())) > 0) {
[email protected]2a409532009-08-28 19:39:44665 bool allow_silent_upgrade =
666 allow_privilege_increase || !Extension::IsPrivilegeIncrease(
667 old, extension);
[email protected]0c6da502009-08-14 22:32:39668
[email protected]1e8c93f2010-02-08 22:58:31669 // Extensions get upgraded if silent upgrades are allowed, otherwise
670 // they get disabled.
671 if (allow_silent_upgrade) {
672 old->set_being_upgraded(true);
673 extension->set_being_upgraded(true);
674 }
675
[email protected]ae09ca62009-08-21 19:46:46676 // To upgrade an extension in place, unload the old one and
677 // then load the new one.
678 UnloadExtension(old->id());
679 old = NULL;
[email protected]0c6da502009-08-14 22:32:39680
[email protected]b24d8312009-08-27 06:47:46681 if (!allow_silent_upgrade) {
[email protected]6d27a7b2009-12-18 23:25:45682 // Extension has changed permissions significantly. Disable it. We
683 // send a notification below.
[email protected]ae09ca62009-08-21 19:46:46684 extension_prefs_->SetExtensionState(extension, Extension::DISABLED);
[email protected]7d845862010-01-04 21:28:57685 extension_prefs_->SetShowInstallWarningOnEnable(extension, true);
[email protected]9f1087e2009-06-15 17:29:32686 }
[email protected]ae09ca62009-08-21 19:46:46687 } else {
688 // We already have the extension of the same or older version.
[email protected]d11c8e92009-10-20 23:26:40689 std::string error_message("Duplicate extension load attempt: ");
690 error_message += extension->id();
691 LOG(WARNING) << error_message;
692 ReportExtensionLoadError(extension->path(),
693 error_message,
694 NotificationType::EXTENSION_OVERINSTALL_ERROR,
695 false);
[email protected]ae09ca62009-08-21 19:46:46696 return;
[email protected]0c6da502009-08-14 22:32:39697 }
[email protected]ba74f352009-06-11 18:54:45698 }
[email protected]86a274072009-06-11 02:06:45699
[email protected]ae09ca62009-08-21 19:46:46700 switch (extension_prefs_->GetExtensionState(extension->id())) {
701 case Extension::ENABLED:
702 extensions_.push_back(scoped_extension.release());
703
[email protected]aeb53b32009-10-29 07:34:45704 // We delay starting up the browser event router until at least one
705 // extension that needs it is loaded.
706 if (extension->HasApiPermission(Extension::kTabPermission)) {
707 ExtensionBrowserEventRouter::GetInstance()->Init();
708 }
[email protected]840b0db2009-11-20 03:00:38709 if (extension->HasApiPermission(Extension::kBookmarkPermission)) {
710 ExtensionBookmarkEventRouter::GetSingleton()->Observe(
711 profile_->GetBookmarkModel());
712 }
[email protected]aeb53b32009-10-29 07:34:45713
[email protected]62d30f42009-10-01 22:36:06714 NotifyExtensionLoaded(extension);
[email protected]ae09ca62009-08-21 19:46:46715
716 if (extension->IsTheme() && extension->location() == Extension::LOAD) {
717 NotificationService::current()->Notify(
718 NotificationType::THEME_INSTALLED,
[email protected]24e7a9d2009-11-04 11:11:34719 Source<Profile>(profile_),
[email protected]ae09ca62009-08-21 19:46:46720 Details<Extension>(extension));
[email protected]86c008e82009-08-28 20:26:05721 } else {
722 ExtensionDOMUI::RegisterChromeURLOverrides(profile_,
723 extension->GetChromeURLOverrides());
[email protected]ae09ca62009-08-21 19:46:46724 }
725 break;
726 case Extension::DISABLED:
[email protected]6d27a7b2009-12-18 23:25:45727 disabled_extensions_.push_back(scoped_extension.release());
[email protected]d11c8e92009-10-20 23:26:40728 NotificationService::current()->Notify(
729 NotificationType::EXTENSION_UPDATE_DISABLED,
[email protected]24e7a9d2009-11-04 11:11:34730 Source<Profile>(profile_),
[email protected]d11c8e92009-10-20 23:26:40731 Details<Extension>(extension));
[email protected]ae09ca62009-08-21 19:46:46732 break;
733 default:
[email protected]d11c8e92009-10-20 23:26:40734 NOTREACHED();
[email protected]ae09ca62009-08-21 19:46:46735 break;
[email protected]811f3432009-07-25 19:38:21736 }
[email protected]e72e8eb82009-06-18 17:21:51737 }
[email protected]aab98a52009-12-02 03:22:35738
[email protected]1e8c93f2010-02-08 22:58:31739 extension->set_being_upgraded(false);
740
[email protected]aab98a52009-12-02 03:22:35741 UpdateActiveExtensionsInCrashReporter();
742}
743
744void ExtensionsService::UpdateActiveExtensionsInCrashReporter() {
[email protected]c8865962009-12-16 07:47:39745 std::set<std::string> extension_ids;
[email protected]aab98a52009-12-02 03:22:35746 for (size_t i = 0; i < extensions_.size(); ++i) {
747 if (!extensions_[i]->IsTheme())
[email protected]c8865962009-12-16 07:47:39748 extension_ids.insert(extensions_[i]->id());
[email protected]aab98a52009-12-02 03:22:35749 }
750
751 child_process_logging::SetActiveExtensions(extension_ids);
[email protected]6014d672008-12-05 00:38:25752}
753
[email protected]2a409532009-08-28 19:39:44754void ExtensionsService::OnExtensionInstalled(Extension* extension,
755 bool allow_privilege_increase) {
[email protected]b6ab96d2009-08-20 18:58:19756 extension_prefs_->OnExtensionInstalled(extension);
[email protected]25b34332009-06-05 21:53:19757
[email protected]4a190632009-05-09 01:07:42758 // If the extension is a theme, tell the profile (and therefore ThemeProvider)
759 // to apply it.
760 if (extension->IsTheme()) {
[email protected]9ceb07342009-07-26 04:09:23761 NotificationService::current()->Notify(
762 NotificationType::THEME_INSTALLED,
[email protected]24e7a9d2009-11-04 11:11:34763 Source<Profile>(profile_),
[email protected]9ceb07342009-07-26 04:09:23764 Details<Extension>(extension));
[email protected]9197f3b2009-06-02 00:49:27765 } else {
766 NotificationService::current()->Notify(
767 NotificationType::EXTENSION_INSTALLED,
[email protected]24e7a9d2009-11-04 11:11:34768 Source<Profile>(profile_),
[email protected]9197f3b2009-06-02 00:49:27769 Details<Extension>(extension));
[email protected]4a190632009-05-09 01:07:42770 }
[email protected]7577a5c52009-07-30 06:21:58771
772 // Also load the extension.
[email protected]2a409532009-08-28 19:39:44773 OnExtensionLoaded(extension, allow_privilege_increase);
[email protected]4a190632009-05-09 01:07:42774}
775
[email protected]7577a5c52009-07-30 06:21:58776void ExtensionsService::OnExtensionOverinstallAttempted(const std::string& id) {
[email protected]61b411612009-11-10 23:17:41777 Extension* extension = GetExtensionById(id, false);
[email protected]4a190632009-05-09 01:07:42778 if (extension && extension->IsTheme()) {
[email protected]9ceb07342009-07-26 04:09:23779 NotificationService::current()->Notify(
780 NotificationType::THEME_INSTALLED,
[email protected]24e7a9d2009-11-04 11:11:34781 Source<Profile>(profile_),
[email protected]9ceb07342009-07-26 04:09:23782 Details<Extension>(extension));
[email protected]91e1bd82009-09-03 22:04:40783 } else {
784 NotificationService::current()->Notify(
785 NotificationType::NO_THEME_DETECTED,
[email protected]24e7a9d2009-11-04 11:11:34786 Source<Profile>(profile_),
[email protected]91e1bd82009-09-03 22:04:40787 NotificationService::NoDetails());
[email protected]4a190632009-05-09 01:07:42788 }
[email protected]cc655912009-01-29 23:19:19789}
790
[email protected]0c6da502009-08-14 22:32:39791Extension* ExtensionsService::GetExtensionByIdInternal(const std::string& id,
792 bool include_enabled,
793 bool include_disabled) {
[email protected]e957fe52009-06-23 16:51:05794 std::string lowercase_id = StringToLowerASCII(id);
[email protected]0c6da502009-08-14 22:32:39795 if (include_enabled) {
796 for (ExtensionList::const_iterator iter = extensions_.begin();
797 iter != extensions_.end(); ++iter) {
798 if ((*iter)->id() == lowercase_id)
799 return *iter;
800 }
801 }
802 if (include_disabled) {
803 for (ExtensionList::const_iterator iter = disabled_extensions_.begin();
804 iter != disabled_extensions_.end(); ++iter) {
805 if ((*iter)->id() == lowercase_id)
806 return *iter;
807 }
[email protected]ce5c4502009-05-06 16:46:11808 }
809 return NULL;
810}
811
[email protected]9f1087e2009-06-15 17:29:32812Extension* ExtensionsService::GetExtensionByURL(const GURL& url) {
813 std::string host = url.host();
[email protected]61b411612009-11-10 23:17:41814 return GetExtensionById(host, false);
[email protected]9f1087e2009-06-15 17:29:32815}
816
[email protected]a1257b12009-06-12 02:51:34817void ExtensionsService::ClearProvidersForTesting() {
[email protected]95d29192009-10-30 01:49:06818 ChromeThread::PostTask(
819 ChromeThread::FILE, FROM_HERE,
820 NewRunnableMethod(
821 backend_.get(), &ExtensionsServiceBackend::ClearProvidersForTesting));
[email protected]a1257b12009-06-12 02:51:34822}
823
824void ExtensionsService::SetProviderForTesting(
825 Extension::Location location, ExternalExtensionProvider* test_provider) {
[email protected]95d29192009-10-30 01:49:06826 ChromeThread::PostTask(
827 ChromeThread::FILE, FROM_HERE,
828 NewRunnableMethod(
829 backend_.get(), &ExtensionsServiceBackend::SetProviderForTesting,
830 location, test_provider));
[email protected]a1257b12009-06-12 02:51:34831}
832
[email protected]7577a5c52009-07-30 06:21:58833void ExtensionsService::OnExternalExtensionFound(const std::string& id,
834 const std::string& version,
835 const FilePath& path,
836 Extension::Location location) {
837 // Before even bothering to unpack, check and see if we already have this
[email protected]4c967932009-07-31 01:15:49838 // version. This is important because these extensions are going to get
[email protected]7577a5c52009-07-30 06:21:58839 // installed on every startup.
[email protected]61b411612009-11-10 23:17:41840 Extension* existing = GetExtensionById(id, true);
[email protected]a3a63ff82009-08-04 06:44:11841 scoped_ptr<Version> other(Version::GetVersionFromString(version));
[email protected]7577a5c52009-07-30 06:21:58842 if (existing) {
[email protected]a3a63ff82009-08-04 06:44:11843 switch (existing->version()->CompareTo(*other)) {
[email protected]7577a5c52009-07-30 06:21:58844 case -1: // existing version is older, we should upgrade
845 break;
846 case 0: // existing version is same, do nothing
847 return;
848 case 1: // existing version is newer, uh-oh
849 LOG(WARNING) << "Found external version of extension " << id
850 << "that is older than current version. Current version "
851 << "is: " << existing->VersionString() << ". New version "
852 << "is: " << version << ". Keeping current version.";
853 return;
854 }
855 }
856
[email protected]2a464a92009-08-01 17:58:35857 CrxInstaller::Start(path, install_directory_, location, id,
858 false, // don't delete crx when complete
[email protected]2a409532009-08-28 19:39:44859 true, // allow privilege increase
[email protected]2a464a92009-08-01 17:58:35860 this,
861 NULL); // no client (silent install)
[email protected]7577a5c52009-07-30 06:21:58862}
863
[email protected]d11c8e92009-10-20 23:26:40864void ExtensionsService::ReportExtensionLoadError(
865 const FilePath& extension_path,
866 const std::string &error,
867 NotificationType type,
868 bool be_noisy) {
869 NotificationService* service = NotificationService::current();
870 service->Notify(type,
[email protected]24e7a9d2009-11-04 11:11:34871 Source<Profile>(profile_),
[email protected]d11c8e92009-10-20 23:26:40872 Details<const std::string>(&error));
873
874 // TODO(port): note that this isn't guaranteed to work properly on Linux.
[email protected]99efb7b12009-12-18 02:39:16875 std::string path_str = WideToUTF8(extension_path.ToWStringHack());
[email protected]d11c8e92009-10-20 23:26:40876 std::string message = StringPrintf("Could not load extension from '%s'. %s",
877 path_str.c_str(), error.c_str());
878 ExtensionErrorReporter::GetInstance()->ReportError(message, be_noisy);
879}
880
[email protected]4814b512009-11-07 00:12:29881void ExtensionsService::Observe(NotificationType type,
882 const NotificationSource& source,
883 const NotificationDetails& details) {
884 switch (type.value) {
885 case NotificationType::EXTENSION_HOST_DID_STOP_LOADING: {
886 ExtensionHost* host = Details<ExtensionHost>(details).ptr();
887 OrphanedDevTools::iterator iter =
888 orphaned_dev_tools_.find(host->extension()->id());
889 if (iter == orphaned_dev_tools_.end())
890 return;
891
892 DevToolsManager::GetInstance()->AttachClientHost(
893 iter->second, host->render_view_host());
894 orphaned_dev_tools_.erase(iter);
895 break;
896 }
897
[email protected]a4ed6282009-12-14 20:51:16898 case NotificationType::EXTENSION_PROCESS_TERMINATED: {
[email protected]31f77262009-12-02 20:48:53899 DCHECK_EQ(profile_, Source<Profile>(source).ptr());
[email protected]a4ed6282009-12-14 20:51:16900
[email protected]31f77262009-12-02 20:48:53901 ExtensionHost* host = Details<ExtensionHost>(details).ptr();
[email protected]27e469a2010-01-11 20:35:09902 // TODO(phajdan.jr): Change to DCHECK after fixing http://crbug.com/30405.
903 CHECK(profile_->GetExtensionProcessManager()->HasExtensionHost(host));
904
905 // If we hit one of these assertions it means that the host's
[email protected]b0b567bf2010-01-22 19:22:53906 // Extension pointer became invalid. http://crbug.com/30405
[email protected]27e469a2010-01-11 20:35:09907 // TODO(phajdan.jr): Remove excessive debugging after fixing bug 30405.
908 std::string extension_id(host->extension()->id());
909 CHECK(extension_id.length() == 32U);
910 Extension* extension = GetExtensionById(extension_id, true);
[email protected]b0b567bf2010-01-22 19:22:53911 CHECK(extension == host->extension());
[email protected]6f39aee92010-01-26 22:33:58912 if (!extension) {
913 NOTREACHED();
914 return;
915 }
[email protected]31f77262009-12-02 20:48:53916
917 // Unload the entire extension. We want it to be in a consistent state:
918 // either fully working or not loaded at all, but never half-crashed.
[email protected]27e469a2010-01-11 20:35:09919 UnloadExtension(extension_id);
[email protected]31f77262009-12-02 20:48:53920 break;
921 }
922
[email protected]4814b512009-11-07 00:12:29923 default:
924 NOTREACHED() << "Unexpected notification type.";
925 }
926}
927
928
[email protected]6014d672008-12-05 00:38:25929// ExtensionsServicesBackend
930
[email protected]894bb502009-05-21 22:39:57931ExtensionsServiceBackend::ExtensionsServiceBackend(
[email protected]95d29192009-10-30 01:49:06932 const FilePath& install_directory)
[email protected]0c7bc4b2009-05-30 01:47:08933 : frontend_(NULL),
934 install_directory_(install_directory),
[email protected]95d29192009-10-30 01:49:06935 alert_on_error_(false) {
[email protected]7577a5c52009-07-30 06:21:58936 // TODO(aa): This ends up doing blocking IO on the UI thread because it reads
937 // pref data in the ctor and that is called on the UI thread. Would be better
938 // to re-read data each time we list external extensions, anyway.
[email protected]a1257b12009-06-12 02:51:34939 external_extension_providers_[Extension::EXTERNAL_PREF] =
[email protected]da50530a2009-06-15 17:43:01940 linked_ptr<ExternalExtensionProvider>(
[email protected]27b985d2009-06-25 17:53:15941 new ExternalPrefExtensionProvider());
[email protected]a1257b12009-06-12 02:51:34942#if defined(OS_WIN)
943 external_extension_providers_[Extension::EXTERNAL_REGISTRY] =
[email protected]da50530a2009-06-15 17:43:01944 linked_ptr<ExternalExtensionProvider>(
945 new ExternalRegistryExtensionProvider());
[email protected]a1257b12009-06-12 02:51:34946#endif
947}
948
949ExtensionsServiceBackend::~ExtensionsServiceBackend() {
[email protected]894bb502009-05-21 22:39:57950}
951
[email protected]b0beaa662009-02-26 00:04:15952void ExtensionsServiceBackend::LoadSingleExtension(
[email protected]894bb502009-05-21 22:39:57953 const FilePath& path_in, scoped_refptr<ExtensionsService> frontend) {
[email protected]b0beaa662009-02-26 00:04:15954 frontend_ = frontend;
955
956 // Explicit UI loads are always noisy.
957 alert_on_error_ = true;
958
[email protected]cc5da332009-03-04 08:02:51959 FilePath extension_path = path_in;
[email protected]f36fa4fb2009-06-19 18:23:50960 file_util::AbsolutePath(&extension_path);
[email protected]bf24d2c2009-02-24 23:07:45961
962 LOG(INFO) << "Loading single extension from " <<
[email protected]99efb7b12009-12-18 02:39:16963 extension_path.BaseName().value();
[email protected]bf24d2c2009-02-24 23:07:45964
[email protected]ab6f2b22009-07-28 23:28:37965 std::string error;
966 Extension* extension = extension_file_util::LoadExtension(
967 extension_path,
968 false, // Don't require id
969 &error);
970
971 if (!extension) {
972 ReportExtensionLoadError(extension_path, error);
973 return;
[email protected]0877fd92009-02-03 16:34:06974 }
[email protected]ab6f2b22009-07-28 23:28:37975
976 extension->set_location(Extension::LOAD);
[email protected]ae09ca62009-08-21 19:46:46977 ReportExtensionLoaded(extension);
[email protected]0877fd92009-02-03 16:34:06978}
979
[email protected]6014d672008-12-05 00:38:25980void ExtensionsServiceBackend::ReportExtensionLoadError(
[email protected]cc5da332009-03-04 08:02:51981 const FilePath& extension_path, const std::string &error) {
[email protected]95d29192009-10-30 01:49:06982 ChromeThread::PostTask(
983 ChromeThread::UI, FROM_HERE,
984 NewRunnableMethod(
985 frontend_,
[email protected]d11c8e92009-10-20 23:26:40986 &ExtensionsService::ReportExtensionLoadError, extension_path,
987 error, NotificationType::EXTENSION_INSTALL_ERROR, alert_on_error_));
[email protected]6014d672008-12-05 00:38:25988}
989
[email protected]ae09ca62009-08-21 19:46:46990void ExtensionsServiceBackend::ReportExtensionLoaded(Extension* extension) {
[email protected]95d29192009-10-30 01:49:06991 ChromeThread::PostTask(
992 ChromeThread::UI, FROM_HERE,
993 NewRunnableMethod(
994 frontend_, &ExtensionsService::OnExtensionLoaded, extension, true));
[email protected]6014d672008-12-05 00:38:25995}
[email protected]cc655912009-01-29 23:19:19996
[email protected]a1257b12009-06-12 02:51:34997bool ExtensionsServiceBackend::LookupExternalExtension(
998 const std::string& id, Version** version, Extension::Location* location) {
999 scoped_ptr<Version> extension_version;
1000 for (ProviderMap::const_iterator i = external_extension_providers_.begin();
1001 i != external_extension_providers_.end(); ++i) {
[email protected]da50530a2009-06-15 17:43:011002 const ExternalExtensionProvider* provider = i->second.get();
[email protected]a1257b12009-06-12 02:51:341003 extension_version.reset(provider->RegisteredVersion(id, location));
1004 if (extension_version.get()) {
1005 if (version)
1006 *version = extension_version.release();
1007 return true;
1008 }
1009 }
1010 return false;
1011}
1012
[email protected]b0beaa662009-02-26 00:04:151013// Some extensions will autoupdate themselves externally from Chrome. These
1014// are typically part of some larger client application package. To support
[email protected]25b34332009-06-05 21:53:191015// these, the extension will register its location in the the preferences file
1016// (and also, on Windows, in the registry) and this code will periodically
[email protected]b0beaa662009-02-26 00:04:151017// check that location for a .crx file, which it will then install locally if
1018// a new version is available.
1019void ExtensionsServiceBackend::CheckForExternalUpdates(
[email protected]894bb502009-05-21 22:39:571020 std::set<std::string> ids_to_ignore,
1021 scoped_refptr<ExtensionsService> frontend) {
[email protected]b0beaa662009-02-26 00:04:151022 // Note that this installation is intentionally silent (since it didn't
1023 // go through the front-end). Extensions that are registered in this
1024 // way are effectively considered 'pre-bundled', and so implicitly
1025 // trusted. In general, if something has HKLM or filesystem access,
1026 // they could install an extension manually themselves anyway.
1027 alert_on_error_ = false;
1028 frontend_ = frontend;
[email protected]b0beaa662009-02-26 00:04:151029
[email protected]a1257b12009-06-12 02:51:341030 // Ask each external extension provider to give us a call back for each
1031 // extension they know about. See OnExternalExtensionFound.
1032 for (ProviderMap::const_iterator i = external_extension_providers_.begin();
1033 i != external_extension_providers_.end(); ++i) {
[email protected]da50530a2009-06-15 17:43:011034 ExternalExtensionProvider* provider = i->second.get();
[email protected]a1257b12009-06-12 02:51:341035 provider->VisitRegisteredExtension(this, ids_to_ignore);
[email protected]25b34332009-06-05 21:53:191036 }
[email protected]b0beaa662009-02-26 00:04:151037}
1038
[email protected]ae09ca62009-08-21 19:46:461039void ExtensionsServiceBackend::CheckExternalUninstall(
1040 scoped_refptr<ExtensionsService> frontend, const std::string& id,
1041 Extension::Location location) {
[email protected]a1257b12009-06-12 02:51:341042 // Check if the providers know about this extension.
1043 ProviderMap::const_iterator i = external_extension_providers_.find(location);
[email protected]ae09ca62009-08-21 19:46:461044 if (i == external_extension_providers_.end()) {
1045 NOTREACHED() << "CheckExternalUninstall called for non-external extension "
1046 << location;
1047 return;
[email protected]b0beaa662009-02-26 00:04:151048 }
[email protected]25b34332009-06-05 21:53:191049
[email protected]ae09ca62009-08-21 19:46:461050 scoped_ptr<Version> version;
1051 version.reset(i->second->RegisteredVersion(id, NULL));
1052 if (version.get())
1053 return; // Yup, known extension, don't uninstall.
1054
1055 // This is an external extension that we don't have registered. Uninstall.
[email protected]95d29192009-10-30 01:49:061056 ChromeThread::PostTask(
1057 ChromeThread::UI, FROM_HERE,
1058 NewRunnableMethod(
1059 frontend.get(), &ExtensionsService::UninstallExtension, id, true));
[email protected]b0beaa662009-02-26 00:04:151060}
1061
[email protected]a1257b12009-06-12 02:51:341062void ExtensionsServiceBackend::ClearProvidersForTesting() {
1063 external_extension_providers_.clear();
1064}
1065
1066void ExtensionsServiceBackend::SetProviderForTesting(
1067 Extension::Location location,
1068 ExternalExtensionProvider* test_provider) {
1069 DCHECK(test_provider);
[email protected]da50530a2009-06-15 17:43:011070 external_extension_providers_[location] =
1071 linked_ptr<ExternalExtensionProvider>(test_provider);
[email protected]a1257b12009-06-12 02:51:341072}
1073
1074void ExtensionsServiceBackend::OnExternalExtensionFound(
[email protected]7577a5c52009-07-30 06:21:581075 const std::string& id, const Version* version, const FilePath& path,
1076 Extension::Location location) {
[email protected]95d29192009-10-30 01:49:061077 ChromeThread::PostTask(
1078 ChromeThread::UI, FROM_HERE,
1079 NewRunnableMethod(
1080 frontend_, &ExtensionsService::OnExternalExtensionFound, id,
1081 version->GetString(), path, location));
[email protected]cc655912009-01-29 23:19:191082}
[email protected]c6d474f82009-12-16 21:11:061083
1084void ExtensionsServiceBackend::ReloadExtensionManifestsForLocaleChanged(
1085 ExtensionPrefs::ExtensionsInfo* extensions_to_reload,
1086 base::TimeTicks start_time,
1087 scoped_refptr<ExtensionsService> frontend) {
1088 frontend_ = frontend;
1089
1090 for (size_t i = 0; i < extensions_to_reload->size(); ++i) {
1091 ExtensionInfo* info = extensions_to_reload->at(i).get();
1092 if (!info->extension_manifest.get())
1093 continue;
1094
1095 if (!extension_l10n_util::ShouldRelocalizeManifest(*info))
1096 continue;
1097
1098 // We need to reload original manifest in order to localize properly.
1099 std::string error;
1100 scoped_ptr<Extension> extension(extension_file_util::LoadExtension(
1101 info->extension_path, false, &error));
1102
1103 if (extension.get())
1104 extensions_to_reload->at(i)->extension_manifest.reset(
1105 static_cast<DictionaryValue*>(
1106 extension->manifest_value()->DeepCopy()));
1107 }
1108
1109 // Finish installing on UI thread.
1110 ChromeThread::PostTask(
1111 ChromeThread::UI, FROM_HERE,
1112 NewRunnableMethod(
1113 frontend_,
1114 &ExtensionsService::ContinueLoadAllExtensions,
1115 extensions_to_reload,
1116 start_time,
1117 true));
1118}