blob: d681e83d5f58023b830c0f64c5f4ddc672896044 [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]15730c42009-09-03 00:03:2011#include "chrome/browser/browser_process.h"
[email protected]dbb92e0d2009-08-20 16:18:2112#include "chrome/browser/chrome_thread.h"
[email protected]7577a5c52009-07-30 06:21:5813#include "chrome/browser/extensions/crx_installer.h"
[email protected]b68d5ed2009-04-16 02:41:2814#include "chrome/browser/extensions/extension_browser_event_router.h"
[email protected]86c008e82009-08-28 20:26:0515#include "chrome/browser/extensions/extension_dom_ui.h"
[email protected]ab6f2b22009-07-28 23:28:3716#include "chrome/browser/extensions/extension_file_util.h"
[email protected]93fd78f42009-07-10 16:43:1717#include "chrome/browser/extensions/extension_updater.h"
[email protected]a1257b12009-06-12 02:51:3418#include "chrome/browser/extensions/external_extension_provider.h"
19#include "chrome/browser/extensions/external_pref_extension_provider.h"
[email protected]81e63782009-02-27 19:35:0920#include "chrome/browser/profile.h"
[email protected]62d30f42009-10-01 22:36:0621#include "chrome/browser/net/chrome_url_request_context.h"
[email protected]e2eb43112009-05-29 21:19:5422#include "chrome/common/chrome_switches.h"
[email protected]5b1a0e22009-05-26 19:00:5823#include "chrome/common/extensions/extension.h"
24#include "chrome/common/extensions/extension_error_reporter.h"
[email protected]82891262008-12-24 00:21:2625#include "chrome/common/notification_service.h"
[email protected]25b34332009-06-05 21:53:1926#include "chrome/common/pref_names.h"
[email protected]894bb502009-05-21 22:39:5727#include "chrome/common/pref_service.h"
[email protected]a57209872009-05-04 22:53:1428#include "chrome/common/url_constants.h"
[email protected]c64631652009-04-29 22:24:3129
[email protected]79db6232009-02-13 20:51:2030#if defined(OS_WIN)
[email protected]a1257b12009-06-12 02:51:3431#include "chrome/browser/extensions/external_registry_extension_provider_win.h"
[email protected]79db6232009-02-13 20:51:2032#endif
[email protected]6014d672008-12-05 00:38:2533
[email protected]b6ab96d2009-08-20 18:58:1934namespace {
35
36// Helper class to collect the IDs of every extension listed in the prefs.
37class InstalledExtensionSet {
38 public:
[email protected]038d52e12009-10-14 16:53:4139 explicit InstalledExtensionSet(InstalledExtensions* installed) {
[email protected]b6ab96d2009-08-20 18:58:1940 scoped_ptr<InstalledExtensions> cleanup(installed);
41 installed->VisitInstalledExtensions(
42 NewCallback(this, &InstalledExtensionSet::ExtensionVisited));
43 }
44
45 const std::set<std::string>& extensions() { return extensions_; }
46
47 private:
48 void ExtensionVisited(
49 DictionaryValue* manifest, const std::string& id,
50 const FilePath& path, Extension::Location location) {
51 extensions_.insert(id);
52 }
53
54 std::set<std::string> extensions_;
55};
56
57} // namespace
58
[email protected]25b34332009-06-05 21:53:1959// ExtensionsService.
[email protected]6014d672008-12-05 00:38:2560
[email protected]cc655912009-01-29 23:19:1961const char* ExtensionsService::kInstallDirectoryName = "Extensions";
62const char* ExtensionsService::kCurrentVersionFileName = "Current Version";
[email protected]494c06e2009-07-25 01:06:4263
64const char* ExtensionsService::kGalleryDownloadURLPrefix =
65 "https://dl-ssl.google.com/chrome/";
[email protected]75a25672009-07-24 17:41:3966const char* ExtensionsService::kGalleryURLPrefix =
67 "https://tools.google.com/chrome/";
68
[email protected]4289d9b2009-07-25 21:17:3469// static
70bool ExtensionsService::IsDownloadFromGallery(const GURL& download_url,
71 const GURL& referrer_url) {
72 if (StartsWithASCII(download_url.spec(), kGalleryDownloadURLPrefix, false) &&
73 StartsWithASCII(referrer_url.spec(), kGalleryURLPrefix, false)) {
74 return true;
75 } else {
76 return false;
77 }
78}
79
[email protected]81e63782009-02-27 19:35:0980ExtensionsService::ExtensionsService(Profile* profile,
[email protected]36a784c2009-06-23 06:21:0881 const CommandLine* command_line,
[email protected]a9b00ac2009-06-25 21:03:2382 PrefService* prefs,
83 const FilePath& install_directory,
[email protected]894bb502009-05-21 22:39:5784 MessageLoop* frontend_loop,
[email protected]93fd78f42009-07-10 16:43:1785 MessageLoop* backend_loop,
86 bool autoupdate_enabled)
[email protected]6ef635e42009-07-26 06:16:1287 : profile_(profile),
88 extension_prefs_(new ExtensionPrefs(prefs, install_directory)),
[email protected]894bb502009-05-21 22:39:5789 backend_loop_(backend_loop),
[email protected]a9b00ac2009-06-25 21:03:2390 install_directory_(install_directory),
[email protected]6d60703b2009-08-29 01:29:2391 extensions_enabled_(true),
[email protected]e81dba32009-06-19 20:19:1392 show_extensions_prompts_(true),
93 ready_(false) {
[email protected]36a784c2009-06-23 06:21:0894 // Figure out if extension installation should be enabled.
[email protected]6d60703b2009-08-29 01:29:2395 if (command_line->HasSwitch(switches::kDisableExtensions)) {
96 extensions_enabled_ = false;
97 } else if (profile->GetPrefs()->GetBoolean(prefs::kDisableExtensions)) {
98 extensions_enabled_ = false;
[email protected]6b75ec32009-08-14 06:37:1899 }
[email protected]36a784c2009-06-23 06:21:08100
[email protected]93fd78f42009-07-10 16:43:17101 // Set up the ExtensionUpdater
102 if (autoupdate_enabled) {
103 int update_frequency = kDefaultUpdateFrequencySeconds;
104 if (command_line->HasSwitch(switches::kExtensionsUpdateFrequency)) {
105 update_frequency = StringToInt(WideToASCII(command_line->GetSwitchValue(
106 switches::kExtensionsUpdateFrequency)));
107 }
[email protected]951073e2009-08-03 17:54:34108 updater_ = new ExtensionUpdater(this, prefs, update_frequency,
[email protected]15730c42009-09-03 00:03:20109 backend_loop_, g_browser_process->io_thread()->message_loop());
[email protected]93fd78f42009-07-10 16:43:17110 }
111
[email protected]7577a5c52009-07-30 06:21:58112 backend_ = new ExtensionsServiceBackend(install_directory_, frontend_loop);
[email protected]6014d672008-12-05 00:38:25113}
114
115ExtensionsService::~ExtensionsService() {
[email protected]9f1087e2009-06-15 17:29:32116 UnloadAllExtensions();
[email protected]93fd78f42009-07-10 16:43:17117 if (updater_.get()) {
118 updater_->Stop();
119 }
[email protected]6014d672008-12-05 00:38:25120}
121
[email protected]9f1087e2009-06-15 17:29:32122void ExtensionsService::Init() {
[email protected]c6e4a3412009-06-24 15:45:29123 DCHECK(!ready_);
[email protected]93fd78f42009-07-10 16:43:17124 DCHECK_EQ(extensions_.size(), 0u);
[email protected]9f1087e2009-06-15 17:29:32125
[email protected]95dd38f2009-10-20 20:09:15126 // Hack: we need to ensure the ResourceDispatcherHost is ready before we load
127 // the first extension, because its members listen for loaded notifications.
128 g_browser_process->resource_dispatcher_host();
129
[email protected]9f1087e2009-06-15 17:29:32130 LoadAllExtensions();
[email protected]894bb502009-05-21 22:39:57131
[email protected]9f1087e2009-06-15 17:29:32132 // TODO(erikkay) this should probably be deferred to a future point
133 // rather than running immediately at startup.
[email protected]93fd78f42009-07-10 16:43:17134 CheckForExternalUpdates();
[email protected]894bb502009-05-21 22:39:57135
[email protected]9f1087e2009-06-15 17:29:32136 // TODO(erikkay) this should probably be deferred as well.
137 GarbageCollectExtensions();
[email protected]6014d672008-12-05 00:38:25138}
139
[email protected]3cf4f0992009-02-03 23:00:30140void ExtensionsService::InstallExtension(const FilePath& extension_path) {
[email protected]2a464a92009-08-01 17:58:35141 CrxInstaller::Start(extension_path, install_directory_, Extension::INTERNAL,
142 "", // no expected id
143 false, // don't delete crx when complete
[email protected]2a409532009-08-28 19:39:44144 true, // allow privilege increase
[email protected]2a464a92009-08-01 17:58:35145 backend_loop_,
146 this,
147 NULL); // no client (silent install)
[email protected]3cf4f0992009-02-03 23:00:30148}
149
[email protected]e957fe52009-06-23 16:51:05150void ExtensionsService::UpdateExtension(const std::string& id,
[email protected]7577a5c52009-07-30 06:21:58151 const FilePath& extension_path) {
[email protected]0c6da502009-08-14 22:32:39152 if (!GetExtensionByIdInternal(id, true, true)) {
[email protected]e957fe52009-06-23 16:51:05153 LOG(WARNING) << "Will not update extension " << id << " because it is not "
[email protected]4c967932009-07-31 01:15:49154 << "installed";
155 return;
[email protected]e957fe52009-06-23 16:51:05156 }
157
[email protected]2a464a92009-08-01 17:58:35158 CrxInstaller::Start(extension_path, install_directory_, Extension::INTERNAL,
159 id,
160 true, // delete crx when complete
[email protected]2a409532009-08-28 19:39:44161 false, // do not allow upgrade of privileges
[email protected]2a464a92009-08-01 17:58:35162 backend_loop_,
163 this,
164 NULL); // no client (silent install)
[email protected]e957fe52009-06-23 16:51:05165}
166
[email protected]9cddd4702009-07-27 22:09:40167void ExtensionsService::ReloadExtension(const std::string& extension_id) {
[email protected]b65272f2009-08-31 15:47:06168 FilePath path;
169 Extension* current_extension = GetExtensionById(extension_id);
[email protected]9cddd4702009-07-27 22:09:40170
[email protected]b65272f2009-08-31 15:47:06171 // Unload the extension if it's loaded. It might not be loaded if it crashed.
172 if (current_extension) {
173 path = current_extension->path();
174 UnloadExtension(extension_id);
175 }
176
177 if (path.empty()) {
178 // At this point we have to reconstruct the path from prefs, because
179 // we have no information about this extension in memory.
180 path = extension_prefs_->GetExtensionPath(extension_id);
181 }
182
183 if (!path.empty())
184 LoadExtension(path);
[email protected]9cddd4702009-07-27 22:09:40185}
186
[email protected]27b985d2009-06-25 17:53:15187void ExtensionsService::UninstallExtension(const std::string& extension_id,
188 bool external_uninstall) {
[email protected]0c6da502009-08-14 22:32:39189 Extension* extension = GetExtensionByIdInternal(extension_id, true, true);
[email protected]631cf822009-05-15 07:01:25190
[email protected]9f1087e2009-06-15 17:29:32191 // Callers should not send us nonexistant extensions.
[email protected]e72e8eb82009-06-18 17:21:51192 DCHECK(extension);
[email protected]9f1087e2009-06-15 17:29:32193
[email protected]27b985d2009-06-25 17:53:15194 extension_prefs_->OnExtensionUninstalled(extension, external_uninstall);
[email protected]9f1087e2009-06-15 17:29:32195
196 // Tell the backend to start deleting installed extensions on the file thread.
[email protected]e72e8eb82009-06-18 17:21:51197 if (Extension::LOAD != extension->location()) {
[email protected]ab6f2b22009-07-28 23:28:37198 backend_loop_->PostTask(FROM_HERE, NewRunnableFunction(
199 &extension_file_util::UninstallExtension, extension_id,
200 install_directory_));
[email protected]9f1087e2009-06-15 17:29:32201 }
202
[email protected]86c008e82009-08-28 20:26:05203 ExtensionDOMUI::UnregisterChromeURLOverrides(profile_,
204 extension->GetChromeURLOverrides());
205
[email protected]9f1087e2009-06-15 17:29:32206 UnloadExtension(extension_id);
207}
208
[email protected]0c6da502009-08-14 22:32:39209void ExtensionsService::EnableExtension(const std::string& extension_id) {
210 Extension* extension = GetExtensionByIdInternal(extension_id, false, true);
211 if (!extension) {
212 NOTREACHED() << "Trying to enable an extension that isn't disabled.";
213 return;
214 }
215
[email protected]1784e83a2009-09-08 21:01:52216 // Remember that we enabled it, unless it's temporary.
217 if (extension->location() != Extension::LOAD)
218 extension_prefs_->SetExtensionState(extension, Extension::ENABLED);
219
[email protected]0c6da502009-08-14 22:32:39220 // Move it over to the enabled list.
[email protected]0c6da502009-08-14 22:32:39221 extensions_.push_back(extension);
222 ExtensionList::iterator iter = std::find(disabled_extensions_.begin(),
223 disabled_extensions_.end(),
224 extension);
225 disabled_extensions_.erase(iter);
226
[email protected]86c008e82009-08-28 20:26:05227 ExtensionDOMUI::RegisterChromeURLOverrides(profile_,
228 extension->GetChromeURLOverrides());
229
[email protected]62d30f42009-10-01 22:36:06230 NotifyExtensionLoaded(extension);
[email protected]0c6da502009-08-14 22:32:39231}
232
[email protected]1784e83a2009-09-08 21:01:52233void ExtensionsService::DisableExtension(const std::string& extension_id) {
234 Extension* extension = GetExtensionByIdInternal(extension_id, true, false);
235 if (!extension) {
236 NOTREACHED() << "Trying to disable an extension that isn't enabled.";
237 return;
238 }
239
240 // Remember that we disabled it, unless it's temporary.
241 if (extension->location() != Extension::LOAD)
242 extension_prefs_->SetExtensionState(extension, Extension::DISABLED);
243
244 // Move it over to the disabled list.
245 disabled_extensions_.push_back(extension);
246 ExtensionList::iterator iter = std::find(extensions_.begin(),
247 extensions_.end(),
248 extension);
249 extensions_.erase(iter);
250
251 ExtensionDOMUI::UnregisterChromeURLOverrides(profile_,
252 extension->GetChromeURLOverrides());
253
[email protected]62d30f42009-10-01 22:36:06254 NotifyExtensionUnloaded(extension);
[email protected]1784e83a2009-09-08 21:01:52255}
256
[email protected]9f1087e2009-06-15 17:29:32257void ExtensionsService::LoadExtension(const FilePath& extension_path) {
258 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
259 &ExtensionsServiceBackend::LoadSingleExtension,
260 extension_path, scoped_refptr<ExtensionsService>(this)));
261}
262
263void ExtensionsService::LoadAllExtensions() {
[email protected]e72e8eb82009-06-18 17:21:51264 // Load the previously installed extensions.
[email protected]ae09ca62009-08-21 19:46:46265 scoped_ptr<InstalledExtensions> installed(
266 new InstalledExtensions(extension_prefs_.get()));
267 installed->VisitInstalledExtensions(
268 NewCallback(this, &ExtensionsService::LoadInstalledExtension));
269 OnLoadedInstalledExtensions();
270}
271
272void ExtensionsService::LoadInstalledExtension(
273 DictionaryValue* manifest, const std::string& id,
274 const FilePath& path, Extension::Location location) {
275 std::string error;
276 Extension* extension = NULL;
277 if (manifest) {
278 scoped_ptr<Extension> tmp(new Extension(path));
279 if (tmp->InitFromValue(*manifest, true, &error)) {
280 extension = tmp.release();
281 }
282 } else {
283 // TODO(mpcomplete): obsolete. remove after migration period.
284 // http://code.google.com/p/chromium/issues/detail?id=19733
285 extension = extension_file_util::LoadExtension(path,
286 true, // Require id
287 &error);
288 }
289
290 if (!extension) {
[email protected]d11c8e92009-10-20 23:26:40291 ReportExtensionLoadError(path,
292 error,
293 NotificationType::EXTENSION_INSTALL_ERROR,
294 false);
[email protected]ae09ca62009-08-21 19:46:46295 return;
296 }
297
298 extension->set_location(location);
[email protected]2a409532009-08-28 19:39:44299 OnExtensionLoaded(extension, true);
[email protected]ae09ca62009-08-21 19:46:46300
301 if (location == Extension::EXTERNAL_PREF ||
302 location == Extension::EXTERNAL_REGISTRY) {
303 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
304 &ExtensionsServiceBackend::CheckExternalUninstall,
305 scoped_refptr<ExtensionsService>(this), id, location));
306 }
[email protected]9f1087e2009-06-15 17:29:32307}
308
[email protected]62d30f42009-10-01 22:36:06309void ExtensionsService::NotifyExtensionLoaded(Extension* extension) {
310 LOG(INFO) << "Sending EXTENSION_LOADED";
311
312 // The ChromeURLRequestContext needs to be first to know that the extension
313 // was loaded, otherwise a race can arise where a renderer that is created
314 // for the extension may try to load an extension URL with an extension id
315 // that the request context doesn't yet know about.
316 if (profile_ && !profile_->IsOffTheRecord()) {
[email protected]be180c802009-10-23 06:33:31317 ChromeURLRequestContextGetter* context_getter =
318 static_cast<ChromeURLRequestContextGetter*>(
319 profile_->GetRequestContext());
320 if (context_getter) {
[email protected]62d30f42009-10-01 22:36:06321 g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE,
[email protected]be180c802009-10-23 06:33:31322 NewRunnableMethod(context_getter,
323 &ChromeURLRequestContextGetter::OnNewExtensions,
[email protected]62d30f42009-10-01 22:36:06324 extension->id(),
325 extension->path()));
326 }
327 }
328
329 NotificationService::current()->Notify(
330 NotificationType::EXTENSION_LOADED,
331 Source<ExtensionsService>(this),
332 Details<Extension>(extension));
333}
334
335void ExtensionsService::NotifyExtensionUnloaded(Extension* extension) {
336 LOG(INFO) << "Sending EXTENSION_UNLOADED";
337
338 NotificationService::current()->Notify(
339 NotificationType::EXTENSION_UNLOADED,
340 Source<ExtensionsService>(this),
341 Details<Extension>(extension));
342
343 if (profile_ && !profile_->IsOffTheRecord()) {
[email protected]be180c802009-10-23 06:33:31344 ChromeURLRequestContextGetter* context_getter =
345 static_cast<ChromeURLRequestContextGetter*>(
346 profile_->GetRequestContext());
347 if (context_getter) {
[email protected]62d30f42009-10-01 22:36:06348 g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE,
[email protected]be180c802009-10-23 06:33:31349 NewRunnableMethod(
350 context_getter,
351 &ChromeURLRequestContextGetter::OnUnloadedExtension,
352 extension->id()));
[email protected]62d30f42009-10-01 22:36:06353 }
354 }
355}
356
[email protected]6b75ec32009-08-14 06:37:18357void ExtensionsService::UpdateExtensionBlacklist(
358 const std::vector<std::string>& blacklist) {
359 // Use this set to indicate if an extension in the blacklist has been used.
360 std::set<std::string> blacklist_set;
361 for (unsigned int i = 0; i < blacklist.size(); ++i) {
362 if (Extension::IdIsValid(blacklist[i])) {
363 blacklist_set.insert(blacklist[i]);
364 }
365 }
366 extension_prefs_->UpdateBlacklist(blacklist_set);
367 std::vector<std::string> to_be_removed;
368 // Loop current extensions, unload installed extensions.
369 for (ExtensionList::const_iterator iter = extensions_.begin();
370 iter != extensions_.end(); ++iter) {
371 Extension* extension = (*iter);
372 if (blacklist_set.find(extension->id()) != blacklist_set.end()) {
373 to_be_removed.push_back(extension->id());
374 }
375 }
376
377 // UnloadExtension will change the extensions_ list. So, we should
378 // call it outside the iterator loop.
379 for (unsigned int i = 0; i < to_be_removed.size(); ++i) {
380 UnloadExtension(to_be_removed[i]);
381 }
382}
383
[email protected]93fd78f42009-07-10 16:43:17384void ExtensionsService::CheckForExternalUpdates() {
[email protected]9f1087e2009-06-15 17:29:32385 // This installs or updates externally provided extensions.
[email protected]7577a5c52009-07-30 06:21:58386 // TODO(aa): Why pass this list into the provider, why not just filter it
387 // later?
[email protected]9f1087e2009-06-15 17:29:32388 std::set<std::string> killed_extensions;
[email protected]e72e8eb82009-06-18 17:21:51389 extension_prefs_->GetKilledExtensionIds(&killed_extensions);
[email protected]9f1087e2009-06-15 17:29:32390 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
391 &ExtensionsServiceBackend::CheckForExternalUpdates,
392 killed_extensions,
393 scoped_refptr<ExtensionsService>(this)));
394}
395
396void ExtensionsService::UnloadExtension(const std::string& extension_id) {
[email protected]0c6da502009-08-14 22:32:39397 scoped_ptr<Extension> extension(
398 GetExtensionByIdInternal(extension_id, true, true));
[email protected]631cf822009-05-15 07:01:25399
[email protected]894bb502009-05-21 22:39:57400 // Callers should not send us nonexistant extensions.
[email protected]0c6da502009-08-14 22:32:39401 CHECK(extension.get());
402
[email protected]86c008e82009-08-28 20:26:05403 ExtensionDOMUI::UnregisterChromeURLOverrides(profile_,
404 extension->GetChromeURLOverrides());
405
[email protected]0c6da502009-08-14 22:32:39406 ExtensionList::iterator iter = std::find(disabled_extensions_.begin(),
407 disabled_extensions_.end(),
408 extension.get());
409 if (iter != disabled_extensions_.end()) {
[email protected]0c6da502009-08-14 22:32:39410 disabled_extensions_.erase(iter);
[email protected]866930682009-08-18 22:53:47411 NotificationService::current()->Notify(
412 NotificationType::EXTENSION_UNLOADED_DISABLED,
413 Source<ExtensionsService>(this),
414 Details<Extension>(extension.get()));
[email protected]0c6da502009-08-14 22:32:39415 return;
416 }
417
418 iter = std::find(extensions_.begin(), extensions_.end(), extension.get());
[email protected]894bb502009-05-21 22:39:57419
[email protected]631cf822009-05-15 07:01:25420 // Remove the extension from our list.
421 extensions_.erase(iter);
422
[email protected]62d30f42009-10-01 22:36:06423 NotifyExtensionUnloaded(extension.get());
[email protected]631cf822009-05-15 07:01:25424}
425
[email protected]9f1087e2009-06-15 17:29:32426void ExtensionsService::UnloadAllExtensions() {
427 ExtensionList::iterator iter;
[email protected]c6e4a3412009-06-24 15:45:29428 for (iter = extensions_.begin(); iter != extensions_.end(); ++iter)
[email protected]9f1087e2009-06-15 17:29:32429 delete *iter;
[email protected]9f1087e2009-06-15 17:29:32430 extensions_.clear();
[email protected]c6e4a3412009-06-24 15:45:29431
432 // TODO(erikkay) should there be a notification for this? We can't use
433 // EXTENSION_UNLOADED since that implies that the extension has been disabled
434 // or uninstalled, and UnloadAll is just part of shutdown.
[email protected]9f1087e2009-06-15 17:29:32435}
436
437void ExtensionsService::ReloadExtensions() {
438 UnloadAllExtensions();
439 LoadAllExtensions();
440}
441
442void ExtensionsService::GarbageCollectExtensions() {
[email protected]b6ab96d2009-08-20 18:58:19443 InstalledExtensionSet installed(
444 new InstalledExtensions(extension_prefs_.get()));
[email protected]ab6f2b22009-07-28 23:28:37445 backend_loop_->PostTask(FROM_HERE, NewRunnableFunction(
[email protected]b6ab96d2009-08-20 18:58:19446 &extension_file_util::GarbageCollectExtensions, install_directory_,
447 installed.extensions()));
[email protected]3cf4f0992009-02-03 23:00:30448}
449
[email protected]e72e8eb82009-06-18 17:21:51450void ExtensionsService::OnLoadedInstalledExtensions() {
[email protected]e81dba32009-06-19 20:19:13451 ready_ = true;
[email protected]93fd78f42009-07-10 16:43:17452 if (updater_.get()) {
453 updater_->Start();
454 }
[email protected]e72e8eb82009-06-18 17:21:51455 NotificationService::current()->Notify(
456 NotificationType::EXTENSIONS_READY,
457 Source<ExtensionsService>(this),
458 NotificationService::NoDetails());
459}
460
[email protected]2a409532009-08-28 19:39:44461void ExtensionsService::OnExtensionLoaded(Extension* extension,
462 bool allow_privilege_increase) {
[email protected]ae09ca62009-08-21 19:46:46463 // Ensure extension is deleted unless we transfer ownership.
464 scoped_ptr<Extension> scoped_extension(extension);
[email protected]9f1087e2009-06-15 17:29:32465
[email protected]ae09ca62009-08-21 19:46:46466 if (extensions_enabled() ||
467 extension->IsTheme() ||
468 extension->location() == Extension::LOAD ||
469 Extension::IsExternalLocation(extension->location())) {
470 Extension* old = GetExtensionByIdInternal(extension->id(), true, true);
471 if (old) {
472 if (extension->version()->CompareTo(*(old->version())) > 0) {
[email protected]2a409532009-08-28 19:39:44473 bool allow_silent_upgrade =
474 allow_privilege_increase || !Extension::IsPrivilegeIncrease(
475 old, extension);
[email protected]0c6da502009-08-14 22:32:39476
[email protected]ae09ca62009-08-21 19:46:46477 // To upgrade an extension in place, unload the old one and
478 // then load the new one.
479 UnloadExtension(old->id());
480 old = NULL;
[email protected]0c6da502009-08-14 22:32:39481
[email protected]b24d8312009-08-27 06:47:46482 if (!allow_silent_upgrade) {
483 // Extension has changed permissions significantly. Disable it and
[email protected]ae09ca62009-08-21 19:46:46484 // notify the user.
485 extension_prefs_->SetExtensionState(extension, Extension::DISABLED);
486 NotificationService::current()->Notify(
487 NotificationType::EXTENSION_UPDATE_DISABLED,
488 Source<ExtensionsService>(this),
489 Details<Extension>(extension));
[email protected]9f1087e2009-06-15 17:29:32490 }
[email protected]ae09ca62009-08-21 19:46:46491 } else {
492 // We already have the extension of the same or older version.
[email protected]d11c8e92009-10-20 23:26:40493 std::string error_message("Duplicate extension load attempt: ");
494 error_message += extension->id();
495 LOG(WARNING) << error_message;
496 ReportExtensionLoadError(extension->path(),
497 error_message,
498 NotificationType::EXTENSION_OVERINSTALL_ERROR,
499 false);
[email protected]ae09ca62009-08-21 19:46:46500 return;
[email protected]0c6da502009-08-14 22:32:39501 }
[email protected]ba74f352009-06-11 18:54:45502 }
[email protected]86a274072009-06-11 02:06:45503
[email protected]ae09ca62009-08-21 19:46:46504 switch (extension_prefs_->GetExtensionState(extension->id())) {
505 case Extension::ENABLED:
506 extensions_.push_back(scoped_extension.release());
507
[email protected]aeb53b32009-10-29 07:34:45508 // We delay starting up the browser event router until at least one
509 // extension that needs it is loaded.
510 if (extension->HasApiPermission(Extension::kTabPermission)) {
511 ExtensionBrowserEventRouter::GetInstance()->Init();
512 }
513
[email protected]ae09ca62009-08-21 19:46:46514 if (extension->location() != Extension::LOAD)
515 extension_prefs_->MigrateToPrefs(extension);
516
[email protected]62d30f42009-10-01 22:36:06517 NotifyExtensionLoaded(extension);
[email protected]ae09ca62009-08-21 19:46:46518
519 if (extension->IsTheme() && extension->location() == Extension::LOAD) {
520 NotificationService::current()->Notify(
521 NotificationType::THEME_INSTALLED,
522 Source<ExtensionsService>(this),
523 Details<Extension>(extension));
[email protected]86c008e82009-08-28 20:26:05524 } else {
525 ExtensionDOMUI::RegisterChromeURLOverrides(profile_,
526 extension->GetChromeURLOverrides());
[email protected]ae09ca62009-08-21 19:46:46527 }
528 break;
529 case Extension::DISABLED:
[email protected]d11c8e92009-10-20 23:26:40530 NotificationService::current()->Notify(
531 NotificationType::EXTENSION_UPDATE_DISABLED,
532 Source<ExtensionsService>(this),
533 Details<Extension>(extension));
[email protected]ae09ca62009-08-21 19:46:46534 disabled_extensions_.push_back(scoped_extension.release());
535 break;
536 default:
[email protected]d11c8e92009-10-20 23:26:40537 NOTREACHED();
[email protected]ae09ca62009-08-21 19:46:46538 break;
[email protected]811f3432009-07-25 19:38:21539 }
[email protected]e72e8eb82009-06-18 17:21:51540 }
[email protected]6014d672008-12-05 00:38:25541}
542
[email protected]2a409532009-08-28 19:39:44543void ExtensionsService::OnExtensionInstalled(Extension* extension,
544 bool allow_privilege_increase) {
[email protected]b6ab96d2009-08-20 18:58:19545 extension_prefs_->OnExtensionInstalled(extension);
[email protected]25b34332009-06-05 21:53:19546
[email protected]4a190632009-05-09 01:07:42547 // If the extension is a theme, tell the profile (and therefore ThemeProvider)
548 // to apply it.
549 if (extension->IsTheme()) {
[email protected]9ceb07342009-07-26 04:09:23550 NotificationService::current()->Notify(
551 NotificationType::THEME_INSTALLED,
552 Source<ExtensionsService>(this),
553 Details<Extension>(extension));
[email protected]9197f3b2009-06-02 00:49:27554 } else {
555 NotificationService::current()->Notify(
556 NotificationType::EXTENSION_INSTALLED,
[email protected]c6e4a3412009-06-24 15:45:29557 Source<ExtensionsService>(this),
[email protected]9197f3b2009-06-02 00:49:27558 Details<Extension>(extension));
[email protected]4a190632009-05-09 01:07:42559 }
[email protected]7577a5c52009-07-30 06:21:58560
561 // Also load the extension.
[email protected]2a409532009-08-28 19:39:44562 OnExtensionLoaded(extension, allow_privilege_increase);
[email protected]4a190632009-05-09 01:07:42563}
564
[email protected]7577a5c52009-07-30 06:21:58565void ExtensionsService::OnExtensionOverinstallAttempted(const std::string& id) {
[email protected]9f1087e2009-06-15 17:29:32566 Extension* extension = GetExtensionById(id);
[email protected]4a190632009-05-09 01:07:42567 if (extension && extension->IsTheme()) {
[email protected]9ceb07342009-07-26 04:09:23568 NotificationService::current()->Notify(
569 NotificationType::THEME_INSTALLED,
570 Source<ExtensionsService>(this),
571 Details<Extension>(extension));
[email protected]91e1bd82009-09-03 22:04:40572 } else {
573 NotificationService::current()->Notify(
574 NotificationType::NO_THEME_DETECTED,
575 Source<ExtensionsService>(this),
576 NotificationService::NoDetails());
[email protected]4a190632009-05-09 01:07:42577 }
[email protected]cc655912009-01-29 23:19:19578}
579
[email protected]0c6da502009-08-14 22:32:39580Extension* ExtensionsService::GetExtensionByIdInternal(const std::string& id,
581 bool include_enabled,
582 bool include_disabled) {
[email protected]e957fe52009-06-23 16:51:05583 std::string lowercase_id = StringToLowerASCII(id);
[email protected]0c6da502009-08-14 22:32:39584 if (include_enabled) {
585 for (ExtensionList::const_iterator iter = extensions_.begin();
586 iter != extensions_.end(); ++iter) {
587 if ((*iter)->id() == lowercase_id)
588 return *iter;
589 }
590 }
591 if (include_disabled) {
592 for (ExtensionList::const_iterator iter = disabled_extensions_.begin();
593 iter != disabled_extensions_.end(); ++iter) {
594 if ((*iter)->id() == lowercase_id)
595 return *iter;
596 }
[email protected]ce5c4502009-05-06 16:46:11597 }
598 return NULL;
599}
600
[email protected]9f1087e2009-06-15 17:29:32601Extension* ExtensionsService::GetExtensionByURL(const GURL& url) {
602 std::string host = url.host();
603 return GetExtensionById(host);
604}
605
[email protected]a1257b12009-06-12 02:51:34606void ExtensionsService::ClearProvidersForTesting() {
607 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
608 &ExtensionsServiceBackend::ClearProvidersForTesting));
609}
610
611void ExtensionsService::SetProviderForTesting(
612 Extension::Location location, ExternalExtensionProvider* test_provider) {
613 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
614 &ExtensionsServiceBackend::SetProviderForTesting,
615 location, test_provider));
616}
617
[email protected]7577a5c52009-07-30 06:21:58618void ExtensionsService::OnExternalExtensionFound(const std::string& id,
619 const std::string& version,
620 const FilePath& path,
621 Extension::Location location) {
622 // Before even bothering to unpack, check and see if we already have this
[email protected]4c967932009-07-31 01:15:49623 // version. This is important because these extensions are going to get
[email protected]7577a5c52009-07-30 06:21:58624 // installed on every startup.
625 Extension* existing = GetExtensionById(id);
[email protected]a3a63ff82009-08-04 06:44:11626 scoped_ptr<Version> other(Version::GetVersionFromString(version));
[email protected]7577a5c52009-07-30 06:21:58627 if (existing) {
[email protected]a3a63ff82009-08-04 06:44:11628 switch (existing->version()->CompareTo(*other)) {
[email protected]7577a5c52009-07-30 06:21:58629 case -1: // existing version is older, we should upgrade
630 break;
631 case 0: // existing version is same, do nothing
632 return;
633 case 1: // existing version is newer, uh-oh
634 LOG(WARNING) << "Found external version of extension " << id
635 << "that is older than current version. Current version "
636 << "is: " << existing->VersionString() << ". New version "
637 << "is: " << version << ". Keeping current version.";
638 return;
639 }
640 }
641
[email protected]2a464a92009-08-01 17:58:35642 CrxInstaller::Start(path, install_directory_, location, id,
643 false, // don't delete crx when complete
[email protected]2a409532009-08-28 19:39:44644 true, // allow privilege increase
[email protected]2a464a92009-08-01 17:58:35645 backend_loop_,
646 this,
647 NULL); // no client (silent install)
[email protected]7577a5c52009-07-30 06:21:58648}
649
[email protected]d11c8e92009-10-20 23:26:40650void ExtensionsService::ReportExtensionLoadError(
651 const FilePath& extension_path,
652 const std::string &error,
653 NotificationType type,
654 bool be_noisy) {
655 NotificationService* service = NotificationService::current();
656 service->Notify(type,
657 Source<ExtensionsService>(this),
658 Details<const std::string>(&error));
659
660 // TODO(port): note that this isn't guaranteed to work properly on Linux.
661 std::string path_str = WideToASCII(extension_path.ToWStringHack());
662 std::string message = StringPrintf("Could not load extension from '%s'. %s",
663 path_str.c_str(), error.c_str());
664 ExtensionErrorReporter::GetInstance()->ReportError(message, be_noisy);
665}
666
[email protected]6014d672008-12-05 00:38:25667// ExtensionsServicesBackend
668
[email protected]894bb502009-05-21 22:39:57669ExtensionsServiceBackend::ExtensionsServiceBackend(
[email protected]7577a5c52009-07-30 06:21:58670 const FilePath& install_directory, MessageLoop* frontend_loop)
[email protected]0c7bc4b2009-05-30 01:47:08671 : frontend_(NULL),
672 install_directory_(install_directory),
[email protected]0c7bc4b2009-05-30 01:47:08673 alert_on_error_(false),
[email protected]7577a5c52009-07-30 06:21:58674 frontend_loop_(frontend_loop) {
675 // TODO(aa): This ends up doing blocking IO on the UI thread because it reads
676 // pref data in the ctor and that is called on the UI thread. Would be better
677 // to re-read data each time we list external extensions, anyway.
[email protected]a1257b12009-06-12 02:51:34678 external_extension_providers_[Extension::EXTERNAL_PREF] =
[email protected]da50530a2009-06-15 17:43:01679 linked_ptr<ExternalExtensionProvider>(
[email protected]27b985d2009-06-25 17:53:15680 new ExternalPrefExtensionProvider());
[email protected]a1257b12009-06-12 02:51:34681#if defined(OS_WIN)
682 external_extension_providers_[Extension::EXTERNAL_REGISTRY] =
[email protected]da50530a2009-06-15 17:43:01683 linked_ptr<ExternalExtensionProvider>(
684 new ExternalRegistryExtensionProvider());
[email protected]a1257b12009-06-12 02:51:34685#endif
686}
687
688ExtensionsServiceBackend::~ExtensionsServiceBackend() {
[email protected]894bb502009-05-21 22:39:57689}
690
[email protected]b0beaa662009-02-26 00:04:15691void ExtensionsServiceBackend::LoadSingleExtension(
[email protected]894bb502009-05-21 22:39:57692 const FilePath& path_in, scoped_refptr<ExtensionsService> frontend) {
[email protected]b0beaa662009-02-26 00:04:15693 frontend_ = frontend;
694
695 // Explicit UI loads are always noisy.
696 alert_on_error_ = true;
697
[email protected]cc5da332009-03-04 08:02:51698 FilePath extension_path = path_in;
[email protected]f36fa4fb2009-06-19 18:23:50699 file_util::AbsolutePath(&extension_path);
[email protected]bf24d2c2009-02-24 23:07:45700
701 LOG(INFO) << "Loading single extension from " <<
[email protected]cc5da332009-03-04 08:02:51702 WideToASCII(extension_path.BaseName().ToWStringHack());
[email protected]bf24d2c2009-02-24 23:07:45703
[email protected]ab6f2b22009-07-28 23:28:37704 std::string error;
705 Extension* extension = extension_file_util::LoadExtension(
706 extension_path,
707 false, // Don't require id
708 &error);
709
710 if (!extension) {
711 ReportExtensionLoadError(extension_path, error);
712 return;
[email protected]0877fd92009-02-03 16:34:06713 }
[email protected]ab6f2b22009-07-28 23:28:37714
715 extension->set_location(Extension::LOAD);
[email protected]ae09ca62009-08-21 19:46:46716 ReportExtensionLoaded(extension);
[email protected]0877fd92009-02-03 16:34:06717}
718
[email protected]6014d672008-12-05 00:38:25719void ExtensionsServiceBackend::ReportExtensionLoadError(
[email protected]cc5da332009-03-04 08:02:51720 const FilePath& extension_path, const std::string &error) {
[email protected]d11c8e92009-10-20 23:26:40721 // In the unit tests, frontend_loop_ may be null.
722 if (frontend_loop_ == NULL) {
723 frontend_->ReportExtensionLoadError(
724 extension_path,
725 error,
726 NotificationType::EXTENSION_INSTALL_ERROR,
727 alert_on_error_);
728 return;
729 }
730
731 frontend_loop_->PostTask(FROM_HERE,
732 NewRunnableMethod(frontend_,
733 &ExtensionsService::ReportExtensionLoadError, extension_path,
734 error, NotificationType::EXTENSION_INSTALL_ERROR, alert_on_error_));
[email protected]6014d672008-12-05 00:38:25735}
736
[email protected]ae09ca62009-08-21 19:46:46737void ExtensionsServiceBackend::ReportExtensionLoaded(Extension* extension) {
[email protected]894bb502009-05-21 22:39:57738 frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(
[email protected]2a409532009-08-28 19:39:44739 frontend_, &ExtensionsService::OnExtensionLoaded, extension, true));
[email protected]6014d672008-12-05 00:38:25740}
[email protected]cc655912009-01-29 23:19:19741
[email protected]a1257b12009-06-12 02:51:34742bool ExtensionsServiceBackend::LookupExternalExtension(
743 const std::string& id, Version** version, Extension::Location* location) {
744 scoped_ptr<Version> extension_version;
745 for (ProviderMap::const_iterator i = external_extension_providers_.begin();
746 i != external_extension_providers_.end(); ++i) {
[email protected]da50530a2009-06-15 17:43:01747 const ExternalExtensionProvider* provider = i->second.get();
[email protected]a1257b12009-06-12 02:51:34748 extension_version.reset(provider->RegisteredVersion(id, location));
749 if (extension_version.get()) {
750 if (version)
751 *version = extension_version.release();
752 return true;
753 }
754 }
755 return false;
756}
757
[email protected]b0beaa662009-02-26 00:04:15758// Some extensions will autoupdate themselves externally from Chrome. These
759// are typically part of some larger client application package. To support
[email protected]25b34332009-06-05 21:53:19760// these, the extension will register its location in the the preferences file
761// (and also, on Windows, in the registry) and this code will periodically
[email protected]b0beaa662009-02-26 00:04:15762// check that location for a .crx file, which it will then install locally if
763// a new version is available.
764void ExtensionsServiceBackend::CheckForExternalUpdates(
[email protected]894bb502009-05-21 22:39:57765 std::set<std::string> ids_to_ignore,
766 scoped_refptr<ExtensionsService> frontend) {
[email protected]b0beaa662009-02-26 00:04:15767 // Note that this installation is intentionally silent (since it didn't
768 // go through the front-end). Extensions that are registered in this
769 // way are effectively considered 'pre-bundled', and so implicitly
770 // trusted. In general, if something has HKLM or filesystem access,
771 // they could install an extension manually themselves anyway.
772 alert_on_error_ = false;
773 frontend_ = frontend;
[email protected]b0beaa662009-02-26 00:04:15774
[email protected]a1257b12009-06-12 02:51:34775 // Ask each external extension provider to give us a call back for each
776 // extension they know about. See OnExternalExtensionFound.
777 for (ProviderMap::const_iterator i = external_extension_providers_.begin();
778 i != external_extension_providers_.end(); ++i) {
[email protected]da50530a2009-06-15 17:43:01779 ExternalExtensionProvider* provider = i->second.get();
[email protected]a1257b12009-06-12 02:51:34780 provider->VisitRegisteredExtension(this, ids_to_ignore);
[email protected]25b34332009-06-05 21:53:19781 }
[email protected]b0beaa662009-02-26 00:04:15782}
783
[email protected]ae09ca62009-08-21 19:46:46784void ExtensionsServiceBackend::CheckExternalUninstall(
785 scoped_refptr<ExtensionsService> frontend, const std::string& id,
786 Extension::Location location) {
[email protected]a1257b12009-06-12 02:51:34787 // Check if the providers know about this extension.
788 ProviderMap::const_iterator i = external_extension_providers_.find(location);
[email protected]ae09ca62009-08-21 19:46:46789 if (i == external_extension_providers_.end()) {
790 NOTREACHED() << "CheckExternalUninstall called for non-external extension "
791 << location;
792 return;
[email protected]b0beaa662009-02-26 00:04:15793 }
[email protected]25b34332009-06-05 21:53:19794
[email protected]ae09ca62009-08-21 19:46:46795 scoped_ptr<Version> version;
796 version.reset(i->second->RegisteredVersion(id, NULL));
797 if (version.get())
798 return; // Yup, known extension, don't uninstall.
799
800 // This is an external extension that we don't have registered. Uninstall.
801 frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(
802 frontend.get(), &ExtensionsService::UninstallExtension,
803 id, true));
[email protected]b0beaa662009-02-26 00:04:15804}
805
[email protected]a1257b12009-06-12 02:51:34806void ExtensionsServiceBackend::ClearProvidersForTesting() {
807 external_extension_providers_.clear();
808}
809
810void ExtensionsServiceBackend::SetProviderForTesting(
811 Extension::Location location,
812 ExternalExtensionProvider* test_provider) {
813 DCHECK(test_provider);
[email protected]da50530a2009-06-15 17:43:01814 external_extension_providers_[location] =
815 linked_ptr<ExternalExtensionProvider>(test_provider);
[email protected]a1257b12009-06-12 02:51:34816}
817
818void ExtensionsServiceBackend::OnExternalExtensionFound(
[email protected]7577a5c52009-07-30 06:21:58819 const std::string& id, const Version* version, const FilePath& path,
820 Extension::Location location) {
821 frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(frontend_,
822 &ExtensionsService::OnExternalExtensionFound, id, version->GetString(),
823 path, location));
[email protected]cc655912009-01-29 23:19:19824}