blob: 89ef1fc73d45d63a374a2fecfac0695e2c2303a9 [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]1b8ca712009-08-18 20:10:37136 Extension* extension = GetExtensionById(extension_id);
137 FilePath extension_path = extension->path();
[email protected]9cddd4702009-07-27 22:09:40138
[email protected]1b8ca712009-08-18 20:10: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()) {
[email protected]0c6da502009-08-14 22:32:39249 disabled_extensions_.erase(iter);
[email protected]866930682009-08-18 22:53:47250 NotificationService::current()->Notify(
251 NotificationType::EXTENSION_UNLOADED_DISABLED,
252 Source<ExtensionsService>(this),
253 Details<Extension>(extension.get()));
[email protected]0c6da502009-08-14 22:32:39254 return;
255 }
256
257 iter = std::find(extensions_.begin(), extensions_.end(), extension.get());
[email protected]894bb502009-05-21 22:39:57258
[email protected]631cf822009-05-15 07:01:25259 // Remove the extension from our list.
260 extensions_.erase(iter);
261
[email protected]631cf822009-05-15 07:01:25262 // Tell other services the extension is gone.
263 NotificationService::current()->Notify(NotificationType::EXTENSION_UNLOADED,
[email protected]c6e4a3412009-06-24 15:45:29264 Source<ExtensionsService>(this),
[email protected]0c6da502009-08-14 22:32:39265 Details<Extension>(extension.get()));
[email protected]631cf822009-05-15 07:01:25266}
267
[email protected]9f1087e2009-06-15 17:29:32268void ExtensionsService::UnloadAllExtensions() {
269 ExtensionList::iterator iter;
[email protected]c6e4a3412009-06-24 15:45:29270 for (iter = extensions_.begin(); iter != extensions_.end(); ++iter)
[email protected]9f1087e2009-06-15 17:29:32271 delete *iter;
[email protected]9f1087e2009-06-15 17:29:32272 extensions_.clear();
[email protected]c6e4a3412009-06-24 15:45:29273
274 // TODO(erikkay) should there be a notification for this? We can't use
275 // EXTENSION_UNLOADED since that implies that the extension has been disabled
276 // or uninstalled, and UnloadAll is just part of shutdown.
[email protected]9f1087e2009-06-15 17:29:32277}
278
279void ExtensionsService::ReloadExtensions() {
280 UnloadAllExtensions();
281 LoadAllExtensions();
282}
283
284void ExtensionsService::GarbageCollectExtensions() {
[email protected]ab6f2b22009-07-28 23:28:37285 backend_loop_->PostTask(FROM_HERE, NewRunnableFunction(
286 &extension_file_util::GarbageCollectExtensions, install_directory_));
[email protected]3cf4f0992009-02-03 23:00:30287}
288
[email protected]e72e8eb82009-06-18 17:21:51289void ExtensionsService::OnLoadedInstalledExtensions() {
[email protected]e81dba32009-06-19 20:19:13290 ready_ = true;
[email protected]93fd78f42009-07-10 16:43:17291 if (updater_.get()) {
292 updater_->Start();
293 }
[email protected]e72e8eb82009-06-18 17:21:51294 NotificationService::current()->Notify(
295 NotificationType::EXTENSIONS_READY,
296 Source<ExtensionsService>(this),
297 NotificationService::NoDetails());
298}
299
[email protected]4a8d3272009-03-10 19:15:08300void ExtensionsService::OnExtensionsLoaded(ExtensionList* new_extensions) {
[email protected]9f1087e2009-06-15 17:29:32301 scoped_ptr<ExtensionList> cleanup(new_extensions);
302
[email protected]75a25672009-07-24 17:41:39303 // Filter out any extensions that shouldn't be loaded. If extensions are
304 // enabled, load everything. Otherwise load only:
305 // - themes
306 // - --load-extension
307 // - externally installed extensions
[email protected]a1257b12009-06-12 02:51:34308 ExtensionList enabled_extensions;
[email protected]0c6da502009-08-14 22:32:39309 ExtensionList disabled_extensions;
[email protected]86a274072009-06-11 02:06:45310 for (ExtensionList::iterator iter = new_extensions->begin();
[email protected]25b34332009-06-05 21:53:19311 iter != new_extensions->end(); ++iter) {
[email protected]0c6da502009-08-14 22:32:39312 // Extensions that get enabled get added to extensions_ and deleted later.
313 // Anything skipped must be deleted now so we don't leak.
314 scoped_ptr<Extension> extension(*iter);
[email protected]75a25672009-07-24 17:41:39315 if (extensions_enabled() ||
[email protected]0c6da502009-08-14 22:32:39316 extension->IsTheme() ||
317 extension->location() == Extension::LOAD ||
318 Extension::IsExternalLocation(extension->location())) {
[email protected]866930682009-08-18 22:53:47319 Extension* old = GetExtensionByIdInternal(extension->id(), true, true);
[email protected]9f1087e2009-06-15 17:29:32320 if (old) {
[email protected]0c6da502009-08-14 22:32:39321 if (extension->version()->CompareTo(*(old->version())) > 0) {
322 bool higher_permissions =
323 (extension->GetPermissionClass() > old->GetPermissionClass());
324
[email protected]9f1087e2009-06-15 17:29:32325 // To upgrade an extension in place, unload the old one and
326 // then load the new one.
[email protected]9f1087e2009-06-15 17:29:32327 UnloadExtension(old->id());
[email protected]0c6da502009-08-14 22:32:39328 old = NULL;
329
330 if (higher_permissions) {
331 // Extension was upgraded to a high permission class. Disable it and
332 // notify the user.
333 extension_prefs_->SetExtensionState(extension.get(),
334 Extension::DISABLED);
335 NotificationService::current()->Notify(
336 NotificationType::EXTENSION_UPDATE_DISABLED,
337 Source<ExtensionsService>(this),
338 Details<Extension>(extension.get()));
339 }
[email protected]9f1087e2009-06-15 17:29:32340 } else {
341 // We already have the extension of the same or older version.
342 LOG(WARNING) << "Duplicate extension load attempt: " << (*iter)->id();
[email protected]9f1087e2009-06-15 17:29:32343 continue;
344 }
345 }
[email protected]0c6da502009-08-14 22:32:39346
347 switch (extension_prefs_->GetExtensionState(extension->id())) {
348 case Extension::ENABLED:
349 enabled_extensions.push_back(extension.get());
350 extensions_.push_back(extension.release());
351 break;
352 case Extension::DISABLED:
353 disabled_extensions.push_back(extension.get());
354 disabled_extensions_.push_back(extension.release());
355 break;
356 default:
357 break;
358 }
[email protected]ba74f352009-06-11 18:54:45359 }
[email protected]86a274072009-06-11 02:06:45360 }
361
[email protected]e72e8eb82009-06-18 17:21:51362 if (enabled_extensions.size()) {
363 NotificationService::current()->Notify(
364 NotificationType::EXTENSIONS_LOADED,
[email protected]c6e4a3412009-06-24 15:45:29365 Source<ExtensionsService>(this),
[email protected]e72e8eb82009-06-18 17:21:51366 Details<ExtensionList>(&enabled_extensions));
[email protected]811f3432009-07-25 19:38:21367 for (ExtensionList::iterator iter = enabled_extensions.begin();
368 iter != enabled_extensions.end(); ++iter) {
369 if ((*iter)->IsTheme() && (*iter)->location() == Extension::LOAD) {
370 NotificationService::current()->Notify(
371 NotificationType::THEME_INSTALLED,
372 Source<ExtensionsService>(this),
373 Details<Extension>(*iter));
374 }
375 }
[email protected]e72e8eb82009-06-18 17:21:51376 }
[email protected]6014d672008-12-05 00:38:25377}
378
[email protected]7577a5c52009-07-30 06:21:58379void ExtensionsService::OnExtensionInstalled(Extension* extension) {
[email protected]866930682009-08-18 22:53:47380 // Make sure we don't enable a disabled extension.
381 if (extension_prefs_->GetExtensionState(extension->id()) !=
382 Extension::DISABLED) {
383 extension_prefs_->OnExtensionInstalled(extension);
384 }
[email protected]25b34332009-06-05 21:53:19385
[email protected]4a190632009-05-09 01:07:42386 // If the extension is a theme, tell the profile (and therefore ThemeProvider)
387 // to apply it.
388 if (extension->IsTheme()) {
[email protected]9ceb07342009-07-26 04:09:23389 NotificationService::current()->Notify(
390 NotificationType::THEME_INSTALLED,
391 Source<ExtensionsService>(this),
392 Details<Extension>(extension));
[email protected]9197f3b2009-06-02 00:49:27393 } else {
394 NotificationService::current()->Notify(
395 NotificationType::EXTENSION_INSTALLED,
[email protected]c6e4a3412009-06-24 15:45:29396 Source<ExtensionsService>(this),
[email protected]9197f3b2009-06-02 00:49:27397 Details<Extension>(extension));
[email protected]4a190632009-05-09 01:07:42398 }
[email protected]7577a5c52009-07-30 06:21:58399
400 // Also load the extension.
401 ExtensionList* list = new ExtensionList;
402 list->push_back(extension);
403 OnExtensionsLoaded(list);
[email protected]4a190632009-05-09 01:07:42404}
405
[email protected]7577a5c52009-07-30 06:21:58406void ExtensionsService::OnExtensionOverinstallAttempted(const std::string& id) {
[email protected]9f1087e2009-06-15 17:29:32407 Extension* extension = GetExtensionById(id);
[email protected]4a190632009-05-09 01:07:42408 if (extension && extension->IsTheme()) {
[email protected]9ceb07342009-07-26 04:09:23409 NotificationService::current()->Notify(
410 NotificationType::THEME_INSTALLED,
411 Source<ExtensionsService>(this),
412 Details<Extension>(extension));
[email protected]4a190632009-05-09 01:07:42413 }
[email protected]cc655912009-01-29 23:19:19414}
415
[email protected]0c6da502009-08-14 22:32:39416Extension* ExtensionsService::GetExtensionByIdInternal(const std::string& id,
417 bool include_enabled,
418 bool include_disabled) {
[email protected]e957fe52009-06-23 16:51:05419 std::string lowercase_id = StringToLowerASCII(id);
[email protected]0c6da502009-08-14 22:32:39420 if (include_enabled) {
421 for (ExtensionList::const_iterator iter = extensions_.begin();
422 iter != extensions_.end(); ++iter) {
423 if ((*iter)->id() == lowercase_id)
424 return *iter;
425 }
426 }
427 if (include_disabled) {
428 for (ExtensionList::const_iterator iter = disabled_extensions_.begin();
429 iter != disabled_extensions_.end(); ++iter) {
430 if ((*iter)->id() == lowercase_id)
431 return *iter;
432 }
[email protected]ce5c4502009-05-06 16:46:11433 }
434 return NULL;
435}
436
[email protected]9f1087e2009-06-15 17:29:32437Extension* ExtensionsService::GetExtensionByURL(const GURL& url) {
438 std::string host = url.host();
439 return GetExtensionById(host);
440}
441
[email protected]a1257b12009-06-12 02:51:34442void ExtensionsService::ClearProvidersForTesting() {
443 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
444 &ExtensionsServiceBackend::ClearProvidersForTesting));
445}
446
447void ExtensionsService::SetProviderForTesting(
448 Extension::Location location, ExternalExtensionProvider* test_provider) {
449 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
450 &ExtensionsServiceBackend::SetProviderForTesting,
451 location, test_provider));
452}
453
[email protected]7577a5c52009-07-30 06:21:58454void ExtensionsService::OnExternalExtensionFound(const std::string& id,
455 const std::string& version,
456 const FilePath& path,
457 Extension::Location location) {
458 // Before even bothering to unpack, check and see if we already have this
[email protected]4c967932009-07-31 01:15:49459 // version. This is important because these extensions are going to get
[email protected]7577a5c52009-07-30 06:21:58460 // installed on every startup.
461 Extension* existing = GetExtensionById(id);
[email protected]a3a63ff82009-08-04 06:44:11462 scoped_ptr<Version> other(Version::GetVersionFromString(version));
[email protected]7577a5c52009-07-30 06:21:58463 if (existing) {
[email protected]a3a63ff82009-08-04 06:44:11464 switch (existing->version()->CompareTo(*other)) {
[email protected]7577a5c52009-07-30 06:21:58465 case -1: // existing version is older, we should upgrade
466 break;
467 case 0: // existing version is same, do nothing
468 return;
469 case 1: // existing version is newer, uh-oh
470 LOG(WARNING) << "Found external version of extension " << id
471 << "that is older than current version. Current version "
472 << "is: " << existing->VersionString() << ". New version "
473 << "is: " << version << ". Keeping current version.";
474 return;
475 }
476 }
477
[email protected]2a464a92009-08-01 17:58:35478 CrxInstaller::Start(path, install_directory_, location, id,
479 false, // don't delete crx when complete
480 backend_loop_,
481 this,
482 NULL); // no client (silent install)
[email protected]7577a5c52009-07-30 06:21:58483}
484
[email protected]7577a5c52009-07-30 06:21:58485
[email protected]6014d672008-12-05 00:38:25486// ExtensionsServicesBackend
487
[email protected]894bb502009-05-21 22:39:57488ExtensionsServiceBackend::ExtensionsServiceBackend(
[email protected]7577a5c52009-07-30 06:21:58489 const FilePath& install_directory, MessageLoop* frontend_loop)
[email protected]0c7bc4b2009-05-30 01:47:08490 : frontend_(NULL),
491 install_directory_(install_directory),
[email protected]0c7bc4b2009-05-30 01:47:08492 alert_on_error_(false),
[email protected]7577a5c52009-07-30 06:21:58493 frontend_loop_(frontend_loop) {
494 // TODO(aa): This ends up doing blocking IO on the UI thread because it reads
495 // pref data in the ctor and that is called on the UI thread. Would be better
496 // to re-read data each time we list external extensions, anyway.
[email protected]a1257b12009-06-12 02:51:34497 external_extension_providers_[Extension::EXTERNAL_PREF] =
[email protected]da50530a2009-06-15 17:43:01498 linked_ptr<ExternalExtensionProvider>(
[email protected]27b985d2009-06-25 17:53:15499 new ExternalPrefExtensionProvider());
[email protected]a1257b12009-06-12 02:51:34500#if defined(OS_WIN)
501 external_extension_providers_[Extension::EXTERNAL_REGISTRY] =
[email protected]da50530a2009-06-15 17:43:01502 linked_ptr<ExternalExtensionProvider>(
503 new ExternalRegistryExtensionProvider());
[email protected]a1257b12009-06-12 02:51:34504#endif
505}
506
507ExtensionsServiceBackend::~ExtensionsServiceBackend() {
[email protected]894bb502009-05-21 22:39:57508}
509
[email protected]e72e8eb82009-06-18 17:21:51510void ExtensionsServiceBackend::LoadInstalledExtensions(
[email protected]25b34332009-06-05 21:53:19511 scoped_refptr<ExtensionsService> frontend,
[email protected]e72e8eb82009-06-18 17:21:51512 InstalledExtensions* installed) {
513 scoped_ptr<InstalledExtensions> cleanup(installed);
[email protected]b0beaa662009-02-26 00:04:15514 frontend_ = frontend;
515 alert_on_error_ = false;
[email protected]b0beaa662009-02-26 00:04:15516
[email protected]e72e8eb82009-06-18 17:21:51517 // Call LoadInstalledExtension for each extension |installed| knows about.
518 scoped_ptr<InstalledExtensions::Callback> callback(
519 NewCallback(this, &ExtensionsServiceBackend::LoadInstalledExtension));
520 installed->VisitInstalledExtensions(callback.get());
521
522 frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(
523 frontend_, &ExtensionsService::OnLoadedInstalledExtensions));
[email protected]9f1087e2009-06-15 17:29:32524}
[email protected]b0beaa662009-02-26 00:04:15525
[email protected]b0beaa662009-02-26 00:04:15526void ExtensionsServiceBackend::LoadSingleExtension(
[email protected]894bb502009-05-21 22:39:57527 const FilePath& path_in, scoped_refptr<ExtensionsService> frontend) {
[email protected]b0beaa662009-02-26 00:04:15528 frontend_ = frontend;
529
530 // Explicit UI loads are always noisy.
531 alert_on_error_ = true;
532
[email protected]cc5da332009-03-04 08:02:51533 FilePath extension_path = path_in;
[email protected]f36fa4fb2009-06-19 18:23:50534 file_util::AbsolutePath(&extension_path);
[email protected]bf24d2c2009-02-24 23:07:45535
536 LOG(INFO) << "Loading single extension from " <<
[email protected]cc5da332009-03-04 08:02:51537 WideToASCII(extension_path.BaseName().ToWStringHack());
[email protected]bf24d2c2009-02-24 23:07:45538
[email protected]ab6f2b22009-07-28 23:28:37539 std::string error;
540 Extension* extension = extension_file_util::LoadExtension(
541 extension_path,
542 false, // Don't require id
543 &error);
544
545 if (!extension) {
546 ReportExtensionLoadError(extension_path, error);
547 return;
[email protected]0877fd92009-02-03 16:34:06548 }
[email protected]ab6f2b22009-07-28 23:28:37549
550 extension->set_location(Extension::LOAD);
551 ExtensionList* extensions = new ExtensionList;
552 extensions->push_back(extension);
553 ReportExtensionsLoaded(extensions);
[email protected]0877fd92009-02-03 16:34:06554}
555
[email protected]e72e8eb82009-06-18 17:21:51556void ExtensionsServiceBackend::LoadInstalledExtension(
557 const std::string& id, const FilePath& path, Extension::Location location) {
558 if (CheckExternalUninstall(id, location)) {
[email protected]27b985d2009-06-25 17:53:15559 frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(
560 frontend_,
561 &ExtensionsService::UninstallExtension,
562 id, true));
[email protected]e72e8eb82009-06-18 17:21:51563
564 // No error needs to be reported. The extension effectively doesn't exist.
565 return;
566 }
567
[email protected]ab6f2b22009-07-28 23:28:37568 std::string error;
569 Extension* extension = extension_file_util::LoadExtension(
570 path,
571 true, // Require id
572 &error);
573
574 if (!extension) {
575 ReportExtensionLoadError(path, error);
576 return;
577 }
[email protected]e72e8eb82009-06-18 17:21:51578
579 // TODO(erikkay) now we only report a single extension loaded at a time.
580 // Perhaps we should change the notifications to remove ExtensionList.
[email protected]ab6f2b22009-07-28 23:28:37581 extension->set_location(location);
[email protected]e72e8eb82009-06-18 17:21:51582 ExtensionList* extensions = new ExtensionList;
583 if (extension)
584 extensions->push_back(extension);
585 ReportExtensionsLoaded(extensions);
586}
587
[email protected]6014d672008-12-05 00:38:25588void ExtensionsServiceBackend::ReportExtensionLoadError(
[email protected]cc5da332009-03-04 08:02:51589 const FilePath& extension_path, const std::string &error) {
[email protected]b0beaa662009-02-26 00:04:15590 // TODO(port): note that this isn't guaranteed to work properly on Linux.
[email protected]cc5da332009-03-04 08:02:51591 std::string path_str = WideToASCII(extension_path.ToWStringHack());
[email protected]3acbd422008-12-08 18:25:00592 std::string message = StringPrintf("Could not load extension from '%s'. %s",
[email protected]cc655912009-01-29 23:19:19593 path_str.c_str(), error.c_str());
[email protected]bb28e062009-02-27 17:19:18594 ExtensionErrorReporter::GetInstance()->ReportError(message, alert_on_error_);
[email protected]6014d672008-12-05 00:38:25595}
596
597void ExtensionsServiceBackend::ReportExtensionsLoaded(
[email protected]b0beaa662009-02-26 00:04:15598 ExtensionList* extensions) {
[email protected]894bb502009-05-21 22:39:57599 frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(
600 frontend_, &ExtensionsService::OnExtensionsLoaded, extensions));
[email protected]6014d672008-12-05 00:38:25601}
[email protected]cc655912009-01-29 23:19:19602
[email protected]a1257b12009-06-12 02:51:34603bool ExtensionsServiceBackend::LookupExternalExtension(
604 const std::string& id, Version** version, Extension::Location* location) {
605 scoped_ptr<Version> extension_version;
606 for (ProviderMap::const_iterator i = external_extension_providers_.begin();
607 i != external_extension_providers_.end(); ++i) {
[email protected]da50530a2009-06-15 17:43:01608 const ExternalExtensionProvider* provider = i->second.get();
[email protected]a1257b12009-06-12 02:51:34609 extension_version.reset(provider->RegisteredVersion(id, location));
610 if (extension_version.get()) {
611 if (version)
612 *version = extension_version.release();
613 return true;
614 }
615 }
616 return false;
617}
618
[email protected]b0beaa662009-02-26 00:04:15619// Some extensions will autoupdate themselves externally from Chrome. These
620// are typically part of some larger client application package. To support
[email protected]25b34332009-06-05 21:53:19621// these, the extension will register its location in the the preferences file
622// (and also, on Windows, in the registry) and this code will periodically
[email protected]b0beaa662009-02-26 00:04:15623// check that location for a .crx file, which it will then install locally if
624// a new version is available.
625void ExtensionsServiceBackend::CheckForExternalUpdates(
[email protected]894bb502009-05-21 22:39:57626 std::set<std::string> ids_to_ignore,
627 scoped_refptr<ExtensionsService> frontend) {
[email protected]b0beaa662009-02-26 00:04:15628 // Note that this installation is intentionally silent (since it didn't
629 // go through the front-end). Extensions that are registered in this
630 // way are effectively considered 'pre-bundled', and so implicitly
631 // trusted. In general, if something has HKLM or filesystem access,
632 // they could install an extension manually themselves anyway.
633 alert_on_error_ = false;
634 frontend_ = frontend;
[email protected]b0beaa662009-02-26 00:04:15635
[email protected]a1257b12009-06-12 02:51:34636 // Ask each external extension provider to give us a call back for each
637 // extension they know about. See OnExternalExtensionFound.
638 for (ProviderMap::const_iterator i = external_extension_providers_.begin();
639 i != external_extension_providers_.end(); ++i) {
[email protected]da50530a2009-06-15 17:43:01640 ExternalExtensionProvider* provider = i->second.get();
[email protected]a1257b12009-06-12 02:51:34641 provider->VisitRegisteredExtension(this, ids_to_ignore);
[email protected]25b34332009-06-05 21:53:19642 }
[email protected]b0beaa662009-02-26 00:04:15643}
644
[email protected]25b34332009-06-05 21:53:19645bool ExtensionsServiceBackend::CheckExternalUninstall(
[email protected]e72e8eb82009-06-18 17:21:51646 const std::string& id, Extension::Location location) {
[email protected]a1257b12009-06-12 02:51:34647 // Check if the providers know about this extension.
648 ProviderMap::const_iterator i = external_extension_providers_.find(location);
649 if (i != external_extension_providers_.end()) {
650 scoped_ptr<Version> version;
651 version.reset(i->second->RegisteredVersion(id, NULL));
652 if (version.get())
653 return false; // Yup, known extension, don't uninstall.
[email protected]e72e8eb82009-06-18 17:21:51654 } else {
655 // Not from an external provider, so it's fine.
656 return false;
[email protected]b0beaa662009-02-26 00:04:15657 }
[email protected]25b34332009-06-05 21:53:19658
[email protected]a1257b12009-06-12 02:51:34659 return true; // This is not a known extension, uninstall.
[email protected]b0beaa662009-02-26 00:04:15660}
661
[email protected]a1257b12009-06-12 02:51:34662void ExtensionsServiceBackend::ClearProvidersForTesting() {
663 external_extension_providers_.clear();
664}
665
666void ExtensionsServiceBackend::SetProviderForTesting(
667 Extension::Location location,
668 ExternalExtensionProvider* test_provider) {
669 DCHECK(test_provider);
[email protected]da50530a2009-06-15 17:43:01670 external_extension_providers_[location] =
671 linked_ptr<ExternalExtensionProvider>(test_provider);
[email protected]a1257b12009-06-12 02:51:34672}
673
674void ExtensionsServiceBackend::OnExternalExtensionFound(
[email protected]7577a5c52009-07-30 06:21:58675 const std::string& id, const Version* version, const FilePath& path,
676 Extension::Location location) {
677 frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(frontend_,
678 &ExtensionsService::OnExternalExtensionFound, id, version->GetString(),
679 path, location));
[email protected]cc655912009-01-29 23:19:19680}