blob: f70d7cf942ac1340884b64c306cf40d616365410 [file] [log] [blame]
[email protected]ce5c4502009-05-06 16:46:111// Copyright (c) 2009 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]6014d672008-12-05 00:38:259#include "base/string_util.h"
[email protected]cc655912009-01-29 23:19:1910#include "base/values.h"
[email protected]7577a5c52009-07-30 06:21:5811#include "chrome/browser/extensions/crx_installer.h"
[email protected]b68d5ed2009-04-16 02:41:2812#include "chrome/browser/extensions/extension_browser_event_router.h"
[email protected]ab6f2b22009-07-28 23:28:3713#include "chrome/browser/extensions/extension_file_util.h"
[email protected]93fd78f42009-07-10 16:43:1714#include "chrome/browser/extensions/extension_updater.h"
[email protected]a1257b12009-06-12 02:51:3415#include "chrome/browser/extensions/external_extension_provider.h"
16#include "chrome/browser/extensions/external_pref_extension_provider.h"
[email protected]81e63782009-02-27 19:35:0917#include "chrome/browser/profile.h"
[email protected]e2eb43112009-05-29 21:19:5418#include "chrome/common/chrome_switches.h"
[email protected]5b1a0e22009-05-26 19:00:5819#include "chrome/common/extensions/extension.h"
[email protected]5b1a0e22009-05-26 19:00:5820#include "chrome/common/extensions/extension_error_reporter.h"
[email protected]82891262008-12-24 00:21:2621#include "chrome/common/notification_service.h"
[email protected]25b34332009-06-05 21:53:1922#include "chrome/common/pref_names.h"
[email protected]894bb502009-05-21 22:39:5723#include "chrome/common/pref_service.h"
[email protected]a57209872009-05-04 22:53:1424#include "chrome/common/url_constants.h"
[email protected]c64631652009-04-29 22:24:3125
[email protected]79db6232009-02-13 20:51:2026#if defined(OS_WIN)
[email protected]a1257b12009-06-12 02:51:3427#include "chrome/browser/extensions/external_registry_extension_provider_win.h"
[email protected]79db6232009-02-13 20:51:2028#endif
[email protected]6014d672008-12-05 00:38:2529
[email protected]25b34332009-06-05 21:53:1930// ExtensionsService.
[email protected]6014d672008-12-05 00:38:2531
[email protected]cc655912009-01-29 23:19:1932const char* ExtensionsService::kInstallDirectoryName = "Extensions";
33const char* ExtensionsService::kCurrentVersionFileName = "Current Version";
[email protected]494c06e2009-07-25 01:06:4234
[email protected]494c06e2009-07-25 01:06:4235const char* ExtensionsService::kGalleryDownloadURLPrefix =
36 "https://dl-ssl.google.com/chrome/";
[email protected]75a25672009-07-24 17:41:3937const char* ExtensionsService::kGalleryURLPrefix =
38 "https://tools.google.com/chrome/";
[email protected]75a25672009-07-24 17:41:3939
[email protected]4289d9b2009-07-25 21:17:3440// static
41bool ExtensionsService::IsDownloadFromGallery(const GURL& download_url,
42 const GURL& referrer_url) {
43 if (StartsWithASCII(download_url.spec(), kGalleryDownloadURLPrefix, false) &&
44 StartsWithASCII(referrer_url.spec(), kGalleryURLPrefix, false)) {
45 return true;
46 } else {
47 return false;
48 }
49}
50
[email protected]81e63782009-02-27 19:35:0951ExtensionsService::ExtensionsService(Profile* profile,
[email protected]36a784c2009-06-23 06:21:0852 const CommandLine* command_line,
[email protected]a9b00ac2009-06-25 21:03:2353 PrefService* prefs,
54 const FilePath& install_directory,
[email protected]894bb502009-05-21 22:39:5755 MessageLoop* frontend_loop,
[email protected]93fd78f42009-07-10 16:43:1756 MessageLoop* backend_loop,
57 bool autoupdate_enabled)
[email protected]6ef635e42009-07-26 06:16:1258 : profile_(profile),
59 extension_prefs_(new ExtensionPrefs(prefs, install_directory)),
[email protected]894bb502009-05-21 22:39:5760 backend_loop_(backend_loop),
[email protected]a9b00ac2009-06-25 21:03:2361 install_directory_(install_directory),
[email protected]abe7a8942009-06-23 05:14:2962 extensions_enabled_(false),
[email protected]e81dba32009-06-19 20:19:1363 show_extensions_prompts_(true),
64 ready_(false) {
[email protected]36a784c2009-06-23 06:21:0865 // Figure out if extension installation should be enabled.
[email protected]6b75ec32009-08-14 06:37:1866 if (command_line->HasSwitch(switches::kEnableExtensions)) {
[email protected]36a784c2009-06-23 06:21:0867 extensions_enabled_ = true;
[email protected]6b75ec32009-08-14 06:37:1868 } else if (profile->GetPrefs()->GetBoolean(prefs::kEnableExtensions)) {
[email protected]36a784c2009-06-23 06:21:0869 extensions_enabled_ = true;
[email protected]6b75ec32009-08-14 06:37:1870 }
[email protected]36a784c2009-06-23 06:21:0871
[email protected]93fd78f42009-07-10 16:43:1772 // Set up the ExtensionUpdater
73 if (autoupdate_enabled) {
74 int update_frequency = kDefaultUpdateFrequencySeconds;
75 if (command_line->HasSwitch(switches::kExtensionsUpdateFrequency)) {
76 update_frequency = StringToInt(WideToASCII(command_line->GetSwitchValue(
77 switches::kExtensionsUpdateFrequency)));
78 }
[email protected]951073e2009-08-03 17:54:3479 updater_ = new ExtensionUpdater(this, prefs, update_frequency,
80 backend_loop_);
[email protected]93fd78f42009-07-10 16:43:1781 }
82
[email protected]7577a5c52009-07-30 06:21:5883 backend_ = new ExtensionsServiceBackend(install_directory_, frontend_loop);
[email protected]6014d672008-12-05 00:38:2584}
85
86ExtensionsService::~ExtensionsService() {
[email protected]9f1087e2009-06-15 17:29:3287 UnloadAllExtensions();
[email protected]93fd78f42009-07-10 16:43:1788 if (updater_.get()) {
89 updater_->Stop();
90 }
[email protected]6014d672008-12-05 00:38:2591}
92
[email protected]9f1087e2009-06-15 17:29:3293void ExtensionsService::Init() {
[email protected]c6e4a3412009-06-24 15:45:2994 DCHECK(!ready_);
[email protected]93fd78f42009-07-10 16:43:1795 DCHECK_EQ(extensions_.size(), 0u);
[email protected]9f1087e2009-06-15 17:29:3296
[email protected]b68d5ed2009-04-16 02:41:2897 // Start up the extension event routers.
98 ExtensionBrowserEventRouter::GetInstance()->Init();
99
[email protected]9f1087e2009-06-15 17:29:32100 LoadAllExtensions();
[email protected]894bb502009-05-21 22:39:57101
[email protected]9f1087e2009-06-15 17:29:32102 // TODO(erikkay) this should probably be deferred to a future point
103 // rather than running immediately at startup.
[email protected]93fd78f42009-07-10 16:43:17104 CheckForExternalUpdates();
[email protected]894bb502009-05-21 22:39:57105
[email protected]9f1087e2009-06-15 17:29:32106 // TODO(erikkay) this should probably be deferred as well.
107 GarbageCollectExtensions();
[email protected]6014d672008-12-05 00:38:25108}
109
[email protected]3cf4f0992009-02-03 23:00:30110void ExtensionsService::InstallExtension(const FilePath& extension_path) {
[email protected]2a464a92009-08-01 17:58:35111 CrxInstaller::Start(extension_path, install_directory_, Extension::INTERNAL,
112 "", // no expected id
113 false, // don't delete crx when complete
114 backend_loop_,
115 this,
116 NULL); // no client (silent install)
[email protected]3cf4f0992009-02-03 23:00:30117}
118
[email protected]e957fe52009-06-23 16:51:05119void ExtensionsService::UpdateExtension(const std::string& id,
[email protected]7577a5c52009-07-30 06:21:58120 const FilePath& extension_path) {
[email protected]0c6da502009-08-14 22:32:39121 if (!GetExtensionByIdInternal(id, true, true)) {
[email protected]e957fe52009-06-23 16:51:05122 LOG(WARNING) << "Will not update extension " << id << " because it is not "
[email protected]4c967932009-07-31 01:15:49123 << "installed";
124 return;
[email protected]e957fe52009-06-23 16:51:05125 }
126
[email protected]2a464a92009-08-01 17:58:35127 CrxInstaller::Start(extension_path, install_directory_, Extension::INTERNAL,
128 id,
129 true, // delete crx when complete
130 backend_loop_,
131 this,
132 NULL); // no client (silent install)
[email protected]e957fe52009-06-23 16:51:05133}
134
[email protected]9cddd4702009-07-27 22:09:40135void ExtensionsService::ReloadExtension(const std::string& extension_id) {
[email protected]572479e2009-08-11 01:57:37136 Extension* extension = GetExtensionById(extension_id);
137 FilePath extension_path = extension->path();
[email protected]9cddd4702009-07-27 22:09:40138
[email protected]572479e2009-08-11 01:57:37139 UnloadExtension(extension_id);
140 LoadExtension(extension_path);
[email protected]9cddd4702009-07-27 22:09:40141}
142
[email protected]27b985d2009-06-25 17:53:15143void ExtensionsService::UninstallExtension(const std::string& extension_id,
144 bool external_uninstall) {
[email protected]0c6da502009-08-14 22:32:39145 Extension* extension = GetExtensionByIdInternal(extension_id, true, true);
[email protected]631cf822009-05-15 07:01:25146
[email protected]9f1087e2009-06-15 17:29:32147 // Callers should not send us nonexistant extensions.
[email protected]e72e8eb82009-06-18 17:21:51148 DCHECK(extension);
[email protected]9f1087e2009-06-15 17:29:32149
[email protected]27b985d2009-06-25 17:53:15150 extension_prefs_->OnExtensionUninstalled(extension, external_uninstall);
[email protected]9f1087e2009-06-15 17:29:32151
152 // Tell the backend to start deleting installed extensions on the file thread.
[email protected]e72e8eb82009-06-18 17:21:51153 if (Extension::LOAD != extension->location()) {
[email protected]ab6f2b22009-07-28 23:28:37154 backend_loop_->PostTask(FROM_HERE, NewRunnableFunction(
155 &extension_file_util::UninstallExtension, extension_id,
156 install_directory_));
[email protected]9f1087e2009-06-15 17:29:32157 }
158
159 UnloadExtension(extension_id);
160}
161
[email protected]0c6da502009-08-14 22:32:39162void ExtensionsService::EnableExtension(const std::string& extension_id) {
163 Extension* extension = GetExtensionByIdInternal(extension_id, false, true);
164 if (!extension) {
165 NOTREACHED() << "Trying to enable an extension that isn't disabled.";
166 return;
167 }
168
169 // Move it over to the enabled list.
170 extension_prefs_->SetExtensionState(extension, Extension::ENABLED);
171 extensions_.push_back(extension);
172 ExtensionList::iterator iter = std::find(disabled_extensions_.begin(),
173 disabled_extensions_.end(),
174 extension);
175 disabled_extensions_.erase(iter);
176
177 ExtensionList extensions;
178 extensions.push_back(extension);
179 NotificationService::current()->Notify(
180 NotificationType::EXTENSIONS_LOADED,
181 Source<ExtensionsService>(this),
182 Details<ExtensionList>(&extensions));
183}
184
[email protected]9f1087e2009-06-15 17:29:32185void ExtensionsService::LoadExtension(const FilePath& extension_path) {
186 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
187 &ExtensionsServiceBackend::LoadSingleExtension,
188 extension_path, scoped_refptr<ExtensionsService>(this)));
189}
190
191void ExtensionsService::LoadAllExtensions() {
[email protected]e72e8eb82009-06-18 17:21:51192 // Load the previously installed extensions.
193 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
194 &ExtensionsServiceBackend::LoadInstalledExtensions,
195 scoped_refptr<ExtensionsService>(this),
196 new InstalledExtensions(extension_prefs_.get())));
[email protected]9f1087e2009-06-15 17:29:32197}
198
[email protected]6b75ec32009-08-14 06:37:18199void ExtensionsService::UpdateExtensionBlacklist(
200 const std::vector<std::string>& blacklist) {
201 // Use this set to indicate if an extension in the blacklist has been used.
202 std::set<std::string> blacklist_set;
203 for (unsigned int i = 0; i < blacklist.size(); ++i) {
204 if (Extension::IdIsValid(blacklist[i])) {
205 blacklist_set.insert(blacklist[i]);
206 }
207 }
208 extension_prefs_->UpdateBlacklist(blacklist_set);
209 std::vector<std::string> to_be_removed;
210 // Loop current extensions, unload installed extensions.
211 for (ExtensionList::const_iterator iter = extensions_.begin();
212 iter != extensions_.end(); ++iter) {
213 Extension* extension = (*iter);
214 if (blacklist_set.find(extension->id()) != blacklist_set.end()) {
215 to_be_removed.push_back(extension->id());
216 }
217 }
218
219 // UnloadExtension will change the extensions_ list. So, we should
220 // call it outside the iterator loop.
221 for (unsigned int i = 0; i < to_be_removed.size(); ++i) {
222 UnloadExtension(to_be_removed[i]);
223 }
224}
225
[email protected]93fd78f42009-07-10 16:43:17226void ExtensionsService::CheckForExternalUpdates() {
[email protected]9f1087e2009-06-15 17:29:32227 // This installs or updates externally provided extensions.
[email protected]7577a5c52009-07-30 06:21:58228 // TODO(aa): Why pass this list into the provider, why not just filter it
229 // later?
[email protected]9f1087e2009-06-15 17:29:32230 std::set<std::string> killed_extensions;
[email protected]e72e8eb82009-06-18 17:21:51231 extension_prefs_->GetKilledExtensionIds(&killed_extensions);
[email protected]9f1087e2009-06-15 17:29:32232 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
233 &ExtensionsServiceBackend::CheckForExternalUpdates,
234 killed_extensions,
235 scoped_refptr<ExtensionsService>(this)));
236}
237
238void ExtensionsService::UnloadExtension(const std::string& extension_id) {
[email protected]0c6da502009-08-14 22:32:39239 scoped_ptr<Extension> extension(
240 GetExtensionByIdInternal(extension_id, true, true));
[email protected]631cf822009-05-15 07:01:25241
[email protected]894bb502009-05-21 22:39:57242 // Callers should not send us nonexistant extensions.
[email protected]0c6da502009-08-14 22:32:39243 CHECK(extension.get());
244
245 ExtensionList::iterator iter = std::find(disabled_extensions_.begin(),
246 disabled_extensions_.end(),
247 extension.get());
248 if (iter != disabled_extensions_.end()) {
249 // It's disabled, so don't send the unload notification.
250 disabled_extensions_.erase(iter);
251 return;
252 }
253
254 iter = std::find(extensions_.begin(), extensions_.end(), extension.get());
[email protected]894bb502009-05-21 22:39:57255
[email protected]631cf822009-05-15 07:01:25256 // Remove the extension from our list.
257 extensions_.erase(iter);
258
[email protected]631cf822009-05-15 07:01:25259 // Tell other services the extension is gone.
260 NotificationService::current()->Notify(NotificationType::EXTENSION_UNLOADED,
[email protected]c6e4a3412009-06-24 15:45:29261 Source<ExtensionsService>(this),
[email protected]0c6da502009-08-14 22:32:39262 Details<Extension>(extension.get()));
[email protected]631cf822009-05-15 07:01:25263}
264
[email protected]9f1087e2009-06-15 17:29:32265void ExtensionsService::UnloadAllExtensions() {
266 ExtensionList::iterator iter;
[email protected]c6e4a3412009-06-24 15:45:29267 for (iter = extensions_.begin(); iter != extensions_.end(); ++iter)
[email protected]9f1087e2009-06-15 17:29:32268 delete *iter;
[email protected]9f1087e2009-06-15 17:29:32269 extensions_.clear();
[email protected]c6e4a3412009-06-24 15:45:29270
271 // TODO(erikkay) should there be a notification for this? We can't use
272 // EXTENSION_UNLOADED since that implies that the extension has been disabled
273 // or uninstalled, and UnloadAll is just part of shutdown.
[email protected]9f1087e2009-06-15 17:29:32274}
275
276void ExtensionsService::ReloadExtensions() {
277 UnloadAllExtensions();
278 LoadAllExtensions();
279}
280
281void ExtensionsService::GarbageCollectExtensions() {
[email protected]ab6f2b22009-07-28 23:28:37282 backend_loop_->PostTask(FROM_HERE, NewRunnableFunction(
283 &extension_file_util::GarbageCollectExtensions, install_directory_));
[email protected]3cf4f0992009-02-03 23:00:30284}
285
[email protected]e72e8eb82009-06-18 17:21:51286void ExtensionsService::OnLoadedInstalledExtensions() {
[email protected]e81dba32009-06-19 20:19:13287 ready_ = true;
[email protected]93fd78f42009-07-10 16:43:17288 if (updater_.get()) {
289 updater_->Start();
290 }
[email protected]e72e8eb82009-06-18 17:21:51291 NotificationService::current()->Notify(
292 NotificationType::EXTENSIONS_READY,
293 Source<ExtensionsService>(this),
294 NotificationService::NoDetails());
295}
296
[email protected]4a8d3272009-03-10 19:15:08297void ExtensionsService::OnExtensionsLoaded(ExtensionList* new_extensions) {
[email protected]9f1087e2009-06-15 17:29:32298 scoped_ptr<ExtensionList> cleanup(new_extensions);
299
[email protected]75a25672009-07-24 17:41:39300 // Filter out any extensions that shouldn't be loaded. If extensions are
301 // enabled, load everything. Otherwise load only:
302 // - themes
303 // - --load-extension
304 // - externally installed extensions
[email protected]a1257b12009-06-12 02:51:34305 ExtensionList enabled_extensions;
[email protected]0c6da502009-08-14 22:32:39306 ExtensionList disabled_extensions;
[email protected]86a274072009-06-11 02:06:45307 for (ExtensionList::iterator iter = new_extensions->begin();
[email protected]25b34332009-06-05 21:53:19308 iter != new_extensions->end(); ++iter) {
[email protected]0c6da502009-08-14 22:32:39309 // Extensions that get enabled get added to extensions_ and deleted later.
310 // Anything skipped must be deleted now so we don't leak.
311 scoped_ptr<Extension> extension(*iter);
[email protected]75a25672009-07-24 17:41:39312 if (extensions_enabled() ||
[email protected]0c6da502009-08-14 22:32:39313 extension->IsTheme() ||
314 extension->location() == Extension::LOAD ||
315 Extension::IsExternalLocation(extension->location())) {
316 Extension* old = GetExtensionById(extension->id());
[email protected]9f1087e2009-06-15 17:29:32317 if (old) {
[email protected]0c6da502009-08-14 22:32:39318 if (extension->version()->CompareTo(*(old->version())) > 0) {
319 bool higher_permissions =
320 (extension->GetPermissionClass() > old->GetPermissionClass());
321
[email protected]9f1087e2009-06-15 17:29:32322 // To upgrade an extension in place, unload the old one and
323 // then load the new one.
[email protected]9f1087e2009-06-15 17:29:32324 UnloadExtension(old->id());
[email protected]0c6da502009-08-14 22:32:39325 old = NULL;
326
327 if (higher_permissions) {
328 // Extension was upgraded to a high permission class. Disable it and
329 // notify the user.
330 extension_prefs_->SetExtensionState(extension.get(),
331 Extension::DISABLED);
332 NotificationService::current()->Notify(
333 NotificationType::EXTENSION_UPDATE_DISABLED,
334 Source<ExtensionsService>(this),
335 Details<Extension>(extension.get()));
336 }
[email protected]9f1087e2009-06-15 17:29:32337 } else {
338 // We already have the extension of the same or older version.
339 LOG(WARNING) << "Duplicate extension load attempt: " << (*iter)->id();
[email protected]9f1087e2009-06-15 17:29:32340 continue;
341 }
342 }
[email protected]0c6da502009-08-14 22:32:39343
344 switch (extension_prefs_->GetExtensionState(extension->id())) {
345 case Extension::ENABLED:
346 enabled_extensions.push_back(extension.get());
347 extensions_.push_back(extension.release());
348 break;
349 case Extension::DISABLED:
350 disabled_extensions.push_back(extension.get());
351 disabled_extensions_.push_back(extension.release());
352 break;
353 default:
354 break;
355 }
[email protected]ba74f352009-06-11 18:54:45356 }
[email protected]86a274072009-06-11 02:06:45357 }
358
[email protected]e72e8eb82009-06-18 17:21:51359 if (enabled_extensions.size()) {
360 NotificationService::current()->Notify(
361 NotificationType::EXTENSIONS_LOADED,
[email protected]c6e4a3412009-06-24 15:45:29362 Source<ExtensionsService>(this),
[email protected]e72e8eb82009-06-18 17:21:51363 Details<ExtensionList>(&enabled_extensions));
[email protected]811f3432009-07-25 19:38:21364 for (ExtensionList::iterator iter = enabled_extensions.begin();
365 iter != enabled_extensions.end(); ++iter) {
366 if ((*iter)->IsTheme() && (*iter)->location() == Extension::LOAD) {
367 NotificationService::current()->Notify(
368 NotificationType::THEME_INSTALLED,
369 Source<ExtensionsService>(this),
370 Details<Extension>(*iter));
371 }
372 }
[email protected]e72e8eb82009-06-18 17:21:51373 }
[email protected]6014d672008-12-05 00:38:25374}
375
[email protected]7577a5c52009-07-30 06:21:58376void ExtensionsService::OnExtensionInstalled(Extension* extension) {
[email protected]e72e8eb82009-06-18 17:21:51377 extension_prefs_->OnExtensionInstalled(extension);
[email protected]25b34332009-06-05 21:53:19378
[email protected]4a190632009-05-09 01:07:42379 // If the extension is a theme, tell the profile (and therefore ThemeProvider)
380 // to apply it.
381 if (extension->IsTheme()) {
[email protected]9ceb07342009-07-26 04:09:23382 NotificationService::current()->Notify(
383 NotificationType::THEME_INSTALLED,
384 Source<ExtensionsService>(this),
385 Details<Extension>(extension));
[email protected]9197f3b2009-06-02 00:49:27386 } else {
387 NotificationService::current()->Notify(
388 NotificationType::EXTENSION_INSTALLED,
[email protected]c6e4a3412009-06-24 15:45:29389 Source<ExtensionsService>(this),
[email protected]9197f3b2009-06-02 00:49:27390 Details<Extension>(extension));
[email protected]4a190632009-05-09 01:07:42391 }
[email protected]7577a5c52009-07-30 06:21:58392
393 // Also load the extension.
394 ExtensionList* list = new ExtensionList;
395 list->push_back(extension);
396 OnExtensionsLoaded(list);
[email protected]4a190632009-05-09 01:07:42397}
398
[email protected]7577a5c52009-07-30 06:21:58399void ExtensionsService::OnExtensionOverinstallAttempted(const std::string& id) {
[email protected]9f1087e2009-06-15 17:29:32400 Extension* extension = GetExtensionById(id);
[email protected]4a190632009-05-09 01:07:42401 if (extension && extension->IsTheme()) {
[email protected]9ceb07342009-07-26 04:09:23402 NotificationService::current()->Notify(
403 NotificationType::THEME_INSTALLED,
404 Source<ExtensionsService>(this),
405 Details<Extension>(extension));
[email protected]4a190632009-05-09 01:07:42406 }
[email protected]cc655912009-01-29 23:19:19407}
408
[email protected]0c6da502009-08-14 22:32:39409Extension* ExtensionsService::GetExtensionByIdInternal(const std::string& id,
410 bool include_enabled,
411 bool include_disabled) {
[email protected]e957fe52009-06-23 16:51:05412 std::string lowercase_id = StringToLowerASCII(id);
[email protected]0c6da502009-08-14 22:32:39413 if (include_enabled) {
414 for (ExtensionList::const_iterator iter = extensions_.begin();
415 iter != extensions_.end(); ++iter) {
416 if ((*iter)->id() == lowercase_id)
417 return *iter;
418 }
419 }
420 if (include_disabled) {
421 for (ExtensionList::const_iterator iter = disabled_extensions_.begin();
422 iter != disabled_extensions_.end(); ++iter) {
423 if ((*iter)->id() == lowercase_id)
424 return *iter;
425 }
[email protected]ce5c4502009-05-06 16:46:11426 }
427 return NULL;
428}
429
[email protected]9f1087e2009-06-15 17:29:32430Extension* ExtensionsService::GetExtensionByURL(const GURL& url) {
431 std::string host = url.host();
432 return GetExtensionById(host);
433}
434
[email protected]a1257b12009-06-12 02:51:34435void ExtensionsService::ClearProvidersForTesting() {
436 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
437 &ExtensionsServiceBackend::ClearProvidersForTesting));
438}
439
440void ExtensionsService::SetProviderForTesting(
441 Extension::Location location, ExternalExtensionProvider* test_provider) {
442 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
443 &ExtensionsServiceBackend::SetProviderForTesting,
444 location, test_provider));
445}
446
[email protected]7577a5c52009-07-30 06:21:58447void ExtensionsService::OnExternalExtensionFound(const std::string& id,
448 const std::string& version,
449 const FilePath& path,
450 Extension::Location location) {
451 // Before even bothering to unpack, check and see if we already have this
[email protected]4c967932009-07-31 01:15:49452 // version. This is important because these extensions are going to get
[email protected]7577a5c52009-07-30 06:21:58453 // installed on every startup.
454 Extension* existing = GetExtensionById(id);
[email protected]a3a63ff82009-08-04 06:44:11455 scoped_ptr<Version> other(Version::GetVersionFromString(version));
[email protected]7577a5c52009-07-30 06:21:58456 if (existing) {
[email protected]a3a63ff82009-08-04 06:44:11457 switch (existing->version()->CompareTo(*other)) {
[email protected]7577a5c52009-07-30 06:21:58458 case -1: // existing version is older, we should upgrade
459 break;
460 case 0: // existing version is same, do nothing
461 return;
462 case 1: // existing version is newer, uh-oh
463 LOG(WARNING) << "Found external version of extension " << id
464 << "that is older than current version. Current version "
465 << "is: " << existing->VersionString() << ". New version "
466 << "is: " << version << ". Keeping current version.";
467 return;
468 }
469 }
470
[email protected]2a464a92009-08-01 17:58:35471 CrxInstaller::Start(path, install_directory_, location, id,
472 false, // don't delete crx when complete
473 backend_loop_,
474 this,
475 NULL); // no client (silent install)
[email protected]7577a5c52009-07-30 06:21:58476}
477
[email protected]7577a5c52009-07-30 06:21:58478
[email protected]6014d672008-12-05 00:38:25479// ExtensionsServicesBackend
480
[email protected]894bb502009-05-21 22:39:57481ExtensionsServiceBackend::ExtensionsServiceBackend(
[email protected]7577a5c52009-07-30 06:21:58482 const FilePath& install_directory, MessageLoop* frontend_loop)
[email protected]0c7bc4b2009-05-30 01:47:08483 : frontend_(NULL),
484 install_directory_(install_directory),
[email protected]0c7bc4b2009-05-30 01:47:08485 alert_on_error_(false),
[email protected]7577a5c52009-07-30 06:21:58486 frontend_loop_(frontend_loop) {
487 // TODO(aa): This ends up doing blocking IO on the UI thread because it reads
488 // pref data in the ctor and that is called on the UI thread. Would be better
489 // to re-read data each time we list external extensions, anyway.
[email protected]a1257b12009-06-12 02:51:34490 external_extension_providers_[Extension::EXTERNAL_PREF] =
[email protected]da50530a2009-06-15 17:43:01491 linked_ptr<ExternalExtensionProvider>(
[email protected]27b985d2009-06-25 17:53:15492 new ExternalPrefExtensionProvider());
[email protected]a1257b12009-06-12 02:51:34493#if defined(OS_WIN)
494 external_extension_providers_[Extension::EXTERNAL_REGISTRY] =
[email protected]da50530a2009-06-15 17:43:01495 linked_ptr<ExternalExtensionProvider>(
496 new ExternalRegistryExtensionProvider());
[email protected]a1257b12009-06-12 02:51:34497#endif
498}
499
500ExtensionsServiceBackend::~ExtensionsServiceBackend() {
[email protected]894bb502009-05-21 22:39:57501}
502
[email protected]e72e8eb82009-06-18 17:21:51503void ExtensionsServiceBackend::LoadInstalledExtensions(
[email protected]25b34332009-06-05 21:53:19504 scoped_refptr<ExtensionsService> frontend,
[email protected]e72e8eb82009-06-18 17:21:51505 InstalledExtensions* installed) {
506 scoped_ptr<InstalledExtensions> cleanup(installed);
[email protected]b0beaa662009-02-26 00:04:15507 frontend_ = frontend;
508 alert_on_error_ = false;
[email protected]b0beaa662009-02-26 00:04:15509
[email protected]e72e8eb82009-06-18 17:21:51510 // Call LoadInstalledExtension for each extension |installed| knows about.
511 scoped_ptr<InstalledExtensions::Callback> callback(
512 NewCallback(this, &ExtensionsServiceBackend::LoadInstalledExtension));
513 installed->VisitInstalledExtensions(callback.get());
514
515 frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(
516 frontend_, &ExtensionsService::OnLoadedInstalledExtensions));
[email protected]9f1087e2009-06-15 17:29:32517}
[email protected]b0beaa662009-02-26 00:04:15518
[email protected]b0beaa662009-02-26 00:04:15519void ExtensionsServiceBackend::LoadSingleExtension(
[email protected]894bb502009-05-21 22:39:57520 const FilePath& path_in, scoped_refptr<ExtensionsService> frontend) {
[email protected]b0beaa662009-02-26 00:04:15521 frontend_ = frontend;
522
523 // Explicit UI loads are always noisy.
524 alert_on_error_ = true;
525
[email protected]cc5da332009-03-04 08:02:51526 FilePath extension_path = path_in;
[email protected]f36fa4fb2009-06-19 18:23:50527 file_util::AbsolutePath(&extension_path);
[email protected]bf24d2c2009-02-24 23:07:45528
529 LOG(INFO) << "Loading single extension from " <<
[email protected]cc5da332009-03-04 08:02:51530 WideToASCII(extension_path.BaseName().ToWStringHack());
[email protected]bf24d2c2009-02-24 23:07:45531
[email protected]ab6f2b22009-07-28 23:28:37532 std::string error;
533 Extension* extension = extension_file_util::LoadExtension(
534 extension_path,
535 false, // Don't require id
536 &error);
537
538 if (!extension) {
539 ReportExtensionLoadError(extension_path, error);
540 return;
[email protected]0877fd92009-02-03 16:34:06541 }
[email protected]ab6f2b22009-07-28 23:28:37542
543 extension->set_location(Extension::LOAD);
544 ExtensionList* extensions = new ExtensionList;
545 extensions->push_back(extension);
546 ReportExtensionsLoaded(extensions);
[email protected]0877fd92009-02-03 16:34:06547}
548
[email protected]e72e8eb82009-06-18 17:21:51549void ExtensionsServiceBackend::LoadInstalledExtension(
550 const std::string& id, const FilePath& path, Extension::Location location) {
551 if (CheckExternalUninstall(id, location)) {
[email protected]27b985d2009-06-25 17:53:15552 frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(
553 frontend_,
554 &ExtensionsService::UninstallExtension,
555 id, true));
[email protected]e72e8eb82009-06-18 17:21:51556
557 // No error needs to be reported. The extension effectively doesn't exist.
558 return;
559 }
560
[email protected]ab6f2b22009-07-28 23:28:37561 std::string error;
562 Extension* extension = extension_file_util::LoadExtension(
563 path,
564 true, // Require id
565 &error);
566
567 if (!extension) {
568 ReportExtensionLoadError(path, error);
569 return;
570 }
[email protected]e72e8eb82009-06-18 17:21:51571
572 // TODO(erikkay) now we only report a single extension loaded at a time.
573 // Perhaps we should change the notifications to remove ExtensionList.
[email protected]ab6f2b22009-07-28 23:28:37574 extension->set_location(location);
[email protected]e72e8eb82009-06-18 17:21:51575 ExtensionList* extensions = new ExtensionList;
576 if (extension)
577 extensions->push_back(extension);
578 ReportExtensionsLoaded(extensions);
579}
580
[email protected]6014d672008-12-05 00:38:25581void ExtensionsServiceBackend::ReportExtensionLoadError(
[email protected]cc5da332009-03-04 08:02:51582 const FilePath& extension_path, const std::string &error) {
[email protected]b0beaa662009-02-26 00:04:15583 // TODO(port): note that this isn't guaranteed to work properly on Linux.
[email protected]cc5da332009-03-04 08:02:51584 std::string path_str = WideToASCII(extension_path.ToWStringHack());
[email protected]3acbd422008-12-08 18:25:00585 std::string message = StringPrintf("Could not load extension from '%s'. %s",
[email protected]cc655912009-01-29 23:19:19586 path_str.c_str(), error.c_str());
[email protected]bb28e062009-02-27 17:19:18587 ExtensionErrorReporter::GetInstance()->ReportError(message, alert_on_error_);
[email protected]6014d672008-12-05 00:38:25588}
589
590void ExtensionsServiceBackend::ReportExtensionsLoaded(
[email protected]b0beaa662009-02-26 00:04:15591 ExtensionList* extensions) {
[email protected]894bb502009-05-21 22:39:57592 frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(
593 frontend_, &ExtensionsService::OnExtensionsLoaded, extensions));
[email protected]6014d672008-12-05 00:38:25594}
[email protected]cc655912009-01-29 23:19:19595
[email protected]a1257b12009-06-12 02:51:34596bool ExtensionsServiceBackend::LookupExternalExtension(
597 const std::string& id, Version** version, Extension::Location* location) {
598 scoped_ptr<Version> extension_version;
599 for (ProviderMap::const_iterator i = external_extension_providers_.begin();
600 i != external_extension_providers_.end(); ++i) {
[email protected]da50530a2009-06-15 17:43:01601 const ExternalExtensionProvider* provider = i->second.get();
[email protected]a1257b12009-06-12 02:51:34602 extension_version.reset(provider->RegisteredVersion(id, location));
603 if (extension_version.get()) {
604 if (version)
605 *version = extension_version.release();
606 return true;
607 }
608 }
609 return false;
610}
611
[email protected]b0beaa662009-02-26 00:04:15612// Some extensions will autoupdate themselves externally from Chrome. These
613// are typically part of some larger client application package. To support
[email protected]25b34332009-06-05 21:53:19614// these, the extension will register its location in the the preferences file
615// (and also, on Windows, in the registry) and this code will periodically
[email protected]b0beaa662009-02-26 00:04:15616// check that location for a .crx file, which it will then install locally if
617// a new version is available.
618void ExtensionsServiceBackend::CheckForExternalUpdates(
[email protected]894bb502009-05-21 22:39:57619 std::set<std::string> ids_to_ignore,
620 scoped_refptr<ExtensionsService> frontend) {
[email protected]b0beaa662009-02-26 00:04:15621 // Note that this installation is intentionally silent (since it didn't
622 // go through the front-end). Extensions that are registered in this
623 // way are effectively considered 'pre-bundled', and so implicitly
624 // trusted. In general, if something has HKLM or filesystem access,
625 // they could install an extension manually themselves anyway.
626 alert_on_error_ = false;
627 frontend_ = frontend;
[email protected]b0beaa662009-02-26 00:04:15628
[email protected]a1257b12009-06-12 02:51:34629 // Ask each external extension provider to give us a call back for each
630 // extension they know about. See OnExternalExtensionFound.
631 for (ProviderMap::const_iterator i = external_extension_providers_.begin();
632 i != external_extension_providers_.end(); ++i) {
[email protected]da50530a2009-06-15 17:43:01633 ExternalExtensionProvider* provider = i->second.get();
[email protected]a1257b12009-06-12 02:51:34634 provider->VisitRegisteredExtension(this, ids_to_ignore);
[email protected]25b34332009-06-05 21:53:19635 }
[email protected]b0beaa662009-02-26 00:04:15636}
637
[email protected]25b34332009-06-05 21:53:19638bool ExtensionsServiceBackend::CheckExternalUninstall(
[email protected]e72e8eb82009-06-18 17:21:51639 const std::string& id, Extension::Location location) {
[email protected]a1257b12009-06-12 02:51:34640 // Check if the providers know about this extension.
641 ProviderMap::const_iterator i = external_extension_providers_.find(location);
642 if (i != external_extension_providers_.end()) {
643 scoped_ptr<Version> version;
644 version.reset(i->second->RegisteredVersion(id, NULL));
645 if (version.get())
646 return false; // Yup, known extension, don't uninstall.
[email protected]e72e8eb82009-06-18 17:21:51647 } else {
648 // Not from an external provider, so it's fine.
649 return false;
[email protected]b0beaa662009-02-26 00:04:15650 }
[email protected]25b34332009-06-05 21:53:19651
[email protected]a1257b12009-06-12 02:51:34652 return true; // This is not a known extension, uninstall.
[email protected]b0beaa662009-02-26 00:04:15653}
654
[email protected]a1257b12009-06-12 02:51:34655void ExtensionsServiceBackend::ClearProvidersForTesting() {
656 external_extension_providers_.clear();
657}
658
659void ExtensionsServiceBackend::SetProviderForTesting(
660 Extension::Location location,
661 ExternalExtensionProvider* test_provider) {
662 DCHECK(test_provider);
[email protected]da50530a2009-06-15 17:43:01663 external_extension_providers_[location] =
664 linked_ptr<ExternalExtensionProvider>(test_provider);
[email protected]a1257b12009-06-12 02:51:34665}
666
667void ExtensionsServiceBackend::OnExternalExtensionFound(
[email protected]7577a5c52009-07-30 06:21:58668 const std::string& id, const Version* version, const FilePath& path,
669 Extension::Location location) {
670 frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(frontend_,
671 &ExtensionsService::OnExternalExtensionFound, id, version->GetString(),
672 path, location));
[email protected]cc655912009-01-29 23:19:19673}