blob: 073338096d747fac6c0fc9b9a6b6029d00438add [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 "app/l10n_util.h"
8#include "base/command_line.h"
[email protected]6014d672008-12-05 00:38:259#include "base/file_util.h"
[email protected]6014d672008-12-05 00:38:2510#include "base/string_util.h"
[email protected]cc655912009-01-29 23:19:1911#include "base/values.h"
[email protected]a57209872009-05-04 22:53:1412#include "chrome/browser/browser.h"
13#include "chrome/browser/browser_list.h"
[email protected]6014d672008-12-05 00:38:2514#include "chrome/browser/browser_process.h"
[email protected]1fca1492009-05-15 22:23:4315#include "chrome/browser/chrome_thread.h"
[email protected]b68d5ed2009-04-16 02:41:2816#include "chrome/browser/extensions/extension_browser_event_router.h"
[email protected]ab6f2b22009-07-28 23:28:3717#include "chrome/browser/extensions/extension_file_util.h"
[email protected]481e1a42009-05-06 20:56:0518#include "chrome/browser/extensions/extension_process_manager.h"
[email protected]93fd78f42009-07-10 16:43:1719#include "chrome/browser/extensions/extension_updater.h"
[email protected]a1257b12009-06-12 02:51:3420#include "chrome/browser/extensions/external_extension_provider.h"
21#include "chrome/browser/extensions/external_pref_extension_provider.h"
[email protected]6ef635e42009-07-26 06:16:1222#include "chrome/browser/extensions/theme_preview_infobar_delegate.h"
[email protected]81e63782009-02-27 19:35:0923#include "chrome/browser/profile.h"
[email protected]6ef635e42009-07-26 06:16:1224#include "chrome/browser/tab_contents/tab_contents.h"
[email protected]e2eb43112009-05-29 21:19:5425#include "chrome/common/chrome_switches.h"
[email protected]5b1a0e22009-05-26 19:00:5826#include "chrome/common/extensions/extension.h"
[email protected]5b1a0e22009-05-26 19:00:5827#include "chrome/common/extensions/extension_error_reporter.h"
[email protected]82891262008-12-24 00:21:2628#include "chrome/common/notification_service.h"
[email protected]25b34332009-06-05 21:53:1929#include "chrome/common/pref_names.h"
[email protected]894bb502009-05-21 22:39:5730#include "chrome/common/pref_service.h"
[email protected]a57209872009-05-04 22:53:1431#include "chrome/common/url_constants.h"
[email protected]e2eb43112009-05-29 21:19:5432#include "grit/chromium_strings.h"
33#include "grit/generated_resources.h"
[email protected]c64631652009-04-29 22:24:3134
[email protected]79db6232009-02-13 20:51:2035#if defined(OS_WIN)
[email protected]e2eb43112009-05-29 21:19:5436#include "app/win_util.h"
[email protected]a1257b12009-06-12 02:51:3437#include "chrome/browser/extensions/external_registry_extension_provider_win.h"
[email protected]638d3522009-06-25 14:52:0138#elif defined(OS_MACOSX)
39#include "base/scoped_cftyperef.h"
40#include "base/sys_string_conversions.h"
41#include <CoreFoundation/CFUserNotification.h>
[email protected]79db6232009-02-13 20:51:2042#endif
[email protected]6014d672008-12-05 00:38:2543
[email protected]25b34332009-06-05 21:53:1944// ExtensionsService.
[email protected]6014d672008-12-05 00:38:2545
[email protected]cc655912009-01-29 23:19:1946const char* ExtensionsService::kInstallDirectoryName = "Extensions";
47const char* ExtensionsService::kCurrentVersionFileName = "Current Version";
[email protected]494c06e2009-07-25 01:06:4248
49const char* ExtensionsService::kGalleryDownloadURLPrefix =
50 "https://dl-ssl.google.com/chrome/";
[email protected]75a25672009-07-24 17:41:3951const char* ExtensionsService::kGalleryURLPrefix =
52 "https://tools.google.com/chrome/";
53
[email protected]4289d9b2009-07-25 21:17:3454// static
55bool ExtensionsService::IsDownloadFromGallery(const GURL& download_url,
56 const GURL& referrer_url) {
57 if (StartsWithASCII(download_url.spec(), kGalleryDownloadURLPrefix, false) &&
58 StartsWithASCII(referrer_url.spec(), kGalleryURLPrefix, false)) {
59 return true;
60 } else {
61 return false;
62 }
63}
64
[email protected]af1277b2009-07-28 00:47:5365// This class hosts a SandboxedExtensionUnpacker task and routes the results
66// back to ExtensionsService. The unpack process is started immediately on
67// construction of this object.
[email protected]1fca1492009-05-15 22:23:4368class ExtensionsServiceBackend::UnpackerClient
[email protected]af1277b2009-07-28 00:47:5369 : public SandboxedExtensionUnpackerClient {
[email protected]1fca1492009-05-15 22:23:4370 public:
71 UnpackerClient(ExtensionsServiceBackend* backend,
72 const FilePath& extension_path,
[email protected]93fd78f42009-07-10 16:43:1773 const std::string& expected_id,
[email protected]c1e432a2009-07-22 21:21:4874 bool silent, bool from_gallery)
[email protected]1fca1492009-05-15 22:23:4375 : backend_(backend), extension_path_(extension_path),
[email protected]af1277b2009-07-28 00:47:5376 expected_id_(expected_id), silent_(silent), from_gallery_(from_gallery) {
77 unpacker_ = new SandboxedExtensionUnpacker(extension_path,
78 backend->resource_dispatcher_host_, this);
79 unpacker_->Start();
[email protected]1fca1492009-05-15 22:23:4380 }
81
82 private:
[email protected]af1277b2009-07-28 00:47:5383 // SandboxedExtensionUnpackerClient
84 virtual void OnUnpackSuccess(const FilePath& temp_dir,
85 const FilePath& extension_dir,
86 Extension* extension) {
87 backend_->OnExtensionUnpacked(extension_path_, extension_dir, extension,
88 expected_id_, silent_, from_gallery_);
89 file_util::Delete(temp_dir, true);
90 delete this;
[email protected]1fca1492009-05-15 22:23:4391 }
92
[email protected]af1277b2009-07-28 00:47:5393 virtual void OnUnpackFailure(const std::string& error_message) {
[email protected]902f7cd2009-05-22 19:02:1994 backend_->ReportExtensionInstallError(extension_path_, error_message);
[email protected]af1277b2009-07-28 00:47:5395 delete this;
[email protected]1fca1492009-05-15 22:23:4396 }
97
[email protected]af1277b2009-07-28 00:47:5398 scoped_refptr<SandboxedExtensionUnpacker> unpacker_;
[email protected]1fca1492009-05-15 22:23:4399
100 scoped_refptr<ExtensionsServiceBackend> backend_;
101
102 // The path to the crx file that we're installing.
103 FilePath extension_path_;
104
[email protected]1fca1492009-05-15 22:23:43105 // The path to the copy of the crx file in the temporary directory where we're
106 // unpacking it.
107 FilePath temp_extension_path_;
108
109 // The ID we expect this extension to have, if any.
110 std::string expected_id_;
111
[email protected]93fd78f42009-07-10 16:43:17112 // True if the install should be done with no confirmation dialog.
113 bool silent_;
[email protected]c1e432a2009-07-22 21:21:48114
115 // True if the install is from the gallery (and therefore should not get an
116 // alert UI if it turns out to also be a theme).
117 bool from_gallery_;
[email protected]1fca1492009-05-15 22:23:43118};
119
[email protected]81e63782009-02-27 19:35:09120ExtensionsService::ExtensionsService(Profile* profile,
[email protected]36a784c2009-06-23 06:21:08121 const CommandLine* command_line,
[email protected]a9b00ac2009-06-25 21:03:23122 PrefService* prefs,
123 const FilePath& install_directory,
[email protected]894bb502009-05-21 22:39:57124 MessageLoop* frontend_loop,
[email protected]93fd78f42009-07-10 16:43:17125 MessageLoop* backend_loop,
126 bool autoupdate_enabled)
[email protected]6ef635e42009-07-26 06:16:12127 : profile_(profile),
128 extension_prefs_(new ExtensionPrefs(prefs, install_directory)),
[email protected]894bb502009-05-21 22:39:57129 backend_loop_(backend_loop),
[email protected]a9b00ac2009-06-25 21:03:23130 install_directory_(install_directory),
[email protected]abe7a8942009-06-23 05:14:29131 extensions_enabled_(false),
[email protected]e81dba32009-06-19 20:19:13132 show_extensions_prompts_(true),
133 ready_(false) {
[email protected]36a784c2009-06-23 06:21:08134 // Figure out if extension installation should be enabled.
135 if (command_line->HasSwitch(switches::kEnableExtensions))
136 extensions_enabled_ = true;
137 else if (profile->GetPrefs()->GetBoolean(prefs::kEnableExtensions))
138 extensions_enabled_ = true;
139
[email protected]93fd78f42009-07-10 16:43:17140 // Set up the ExtensionUpdater
141 if (autoupdate_enabled) {
142 int update_frequency = kDefaultUpdateFrequencySeconds;
143 if (command_line->HasSwitch(switches::kExtensionsUpdateFrequency)) {
144 update_frequency = StringToInt(WideToASCII(command_line->GetSwitchValue(
145 switches::kExtensionsUpdateFrequency)));
146 }
147 updater_ = new ExtensionUpdater(this, update_frequency, backend_loop_);
148 }
149
[email protected]a1257b12009-06-12 02:51:34150 backend_ = new ExtensionsServiceBackend(
151 install_directory_, g_browser_process->resource_dispatcher_host(),
[email protected]27b985d2009-06-25 17:53:15152 frontend_loop, extensions_enabled());
[email protected]6014d672008-12-05 00:38:25153}
154
155ExtensionsService::~ExtensionsService() {
[email protected]9f1087e2009-06-15 17:29:32156 UnloadAllExtensions();
[email protected]93fd78f42009-07-10 16:43:17157 if (updater_.get()) {
158 updater_->Stop();
159 }
[email protected]6014d672008-12-05 00:38:25160}
161
[email protected]abe7a8942009-06-23 05:14:29162void ExtensionsService::SetExtensionsEnabled(bool enabled) {
163 extensions_enabled_ = true;
164 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
165 &ExtensionsServiceBackend::set_extensions_enabled, enabled));
166}
167
[email protected]9f1087e2009-06-15 17:29:32168void ExtensionsService::Init() {
[email protected]c6e4a3412009-06-24 15:45:29169 DCHECK(!ready_);
[email protected]93fd78f42009-07-10 16:43:17170 DCHECK_EQ(extensions_.size(), 0u);
[email protected]9f1087e2009-06-15 17:29:32171
[email protected]b68d5ed2009-04-16 02:41:28172 // Start up the extension event routers.
173 ExtensionBrowserEventRouter::GetInstance()->Init();
174
[email protected]9f1087e2009-06-15 17:29:32175 LoadAllExtensions();
[email protected]894bb502009-05-21 22:39:57176
[email protected]9f1087e2009-06-15 17:29:32177 // TODO(erikkay) this should probably be deferred to a future point
178 // rather than running immediately at startup.
[email protected]93fd78f42009-07-10 16:43:17179 CheckForExternalUpdates();
[email protected]894bb502009-05-21 22:39:57180
[email protected]9f1087e2009-06-15 17:29:32181 // TODO(erikkay) this should probably be deferred as well.
182 GarbageCollectExtensions();
[email protected]6014d672008-12-05 00:38:25183}
184
[email protected]3cf4f0992009-02-03 23:00:30185void ExtensionsService::InstallExtension(const FilePath& extension_path) {
[email protected]494c06e2009-07-25 01:06:42186 InstallExtension(extension_path, GURL(), GURL());
[email protected]c1e432a2009-07-22 21:21:48187}
188
189void ExtensionsService::InstallExtension(const FilePath& extension_path,
[email protected]494c06e2009-07-25 01:06:42190 const GURL& download_url,
[email protected]75a25672009-07-24 17:41:39191 const GURL& referrer_url) {
[email protected]894bb502009-05-21 22:39:57192 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
[email protected]4289d9b2009-07-25 21:17:34193 &ExtensionsServiceBackend::InstallExtension, extension_path,
194 IsDownloadFromGallery(download_url, referrer_url),
[email protected]894bb502009-05-21 22:39:57195 scoped_refptr<ExtensionsService>(this)));
[email protected]4289d9b2009-07-25 21:17:34196
[email protected]3cf4f0992009-02-03 23:00:30197}
198
[email protected]e957fe52009-06-23 16:51:05199void ExtensionsService::UpdateExtension(const std::string& id,
200 const FilePath& extension_path,
201 bool alert_on_error,
[email protected]d1ca0ed12009-07-01 18:24:32202 ExtensionInstallCallback* callback) {
[email protected]e957fe52009-06-23 16:51:05203 if (callback) {
204 if (install_callbacks_.find(extension_path) != install_callbacks_.end()) {
205 // We can't have multiple outstanding install requests for the same
206 // path, so immediately indicate error via the callback here.
207 LOG(WARNING) << "Dropping update request for '" <<
208 extension_path.value() << "' (already in progress)'";
209 callback->Run(extension_path, static_cast<Extension*>(NULL));
210 delete callback;
211 return;
212 }
[email protected]d1ca0ed12009-07-01 18:24:32213 install_callbacks_[extension_path] =
214 linked_ptr<ExtensionInstallCallback>(callback);
[email protected]e957fe52009-06-23 16:51:05215 }
216
217 if (!GetExtensionById(id)) {
218 LOG(WARNING) << "Will not update extension " << id << " because it is not "
219 << "installed";
220 FireInstallCallback(extension_path, NULL);
221 return;
222 }
223
224 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
225 &ExtensionsServiceBackend::UpdateExtension, id, extension_path,
226 alert_on_error, scoped_refptr<ExtensionsService>(this)));
227}
228
[email protected]9cddd4702009-07-27 22:09:40229void ExtensionsService::ReloadExtension(const std::string& extension_id) {
230 Extension* extension = GetExtensionById(extension_id);
231 FilePath extension_path = extension->path();
232
233 UnloadExtension(extension_id);
234 LoadExtension(extension_path);
235}
236
[email protected]27b985d2009-06-25 17:53:15237void ExtensionsService::UninstallExtension(const std::string& extension_id,
238 bool external_uninstall) {
[email protected]9f1087e2009-06-15 17:29:32239 Extension* extension = GetExtensionById(extension_id);
[email protected]631cf822009-05-15 07:01:25240
[email protected]9f1087e2009-06-15 17:29:32241 // Callers should not send us nonexistant extensions.
[email protected]e72e8eb82009-06-18 17:21:51242 DCHECK(extension);
[email protected]9f1087e2009-06-15 17:29:32243
[email protected]27b985d2009-06-25 17:53:15244 extension_prefs_->OnExtensionUninstalled(extension, external_uninstall);
[email protected]9f1087e2009-06-15 17:29:32245
246 // Tell the backend to start deleting installed extensions on the file thread.
[email protected]e72e8eb82009-06-18 17:21:51247 if (Extension::LOAD != extension->location()) {
[email protected]ab6f2b22009-07-28 23:28:37248 backend_loop_->PostTask(FROM_HERE, NewRunnableFunction(
249 &extension_file_util::UninstallExtension, extension_id,
250 install_directory_));
[email protected]9f1087e2009-06-15 17:29:32251 }
252
253 UnloadExtension(extension_id);
254}
255
256void ExtensionsService::LoadExtension(const FilePath& extension_path) {
257 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
258 &ExtensionsServiceBackend::LoadSingleExtension,
259 extension_path, scoped_refptr<ExtensionsService>(this)));
260}
261
262void ExtensionsService::LoadAllExtensions() {
[email protected]e72e8eb82009-06-18 17:21:51263 // Load the previously installed extensions.
264 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
265 &ExtensionsServiceBackend::LoadInstalledExtensions,
266 scoped_refptr<ExtensionsService>(this),
267 new InstalledExtensions(extension_prefs_.get())));
[email protected]9f1087e2009-06-15 17:29:32268}
269
[email protected]93fd78f42009-07-10 16:43:17270void ExtensionsService::CheckForExternalUpdates() {
[email protected]9f1087e2009-06-15 17:29:32271 // This installs or updates externally provided extensions.
272 std::set<std::string> killed_extensions;
[email protected]e72e8eb82009-06-18 17:21:51273 extension_prefs_->GetKilledExtensionIds(&killed_extensions);
[email protected]9f1087e2009-06-15 17:29:32274 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
275 &ExtensionsServiceBackend::CheckForExternalUpdates,
276 killed_extensions,
277 scoped_refptr<ExtensionsService>(this)));
278}
279
280void ExtensionsService::UnloadExtension(const std::string& extension_id) {
281 Extension* extension = NULL;
[email protected]631cf822009-05-15 07:01:25282 ExtensionList::iterator iter;
283 for (iter = extensions_.begin(); iter != extensions_.end(); ++iter) {
284 if ((*iter)->id() == extension_id) {
285 extension = *iter;
286 break;
287 }
288 }
289
[email protected]894bb502009-05-21 22:39:57290 // Callers should not send us nonexistant extensions.
291 CHECK(extension);
292
[email protected]631cf822009-05-15 07:01:25293 // Remove the extension from our list.
294 extensions_.erase(iter);
295
[email protected]631cf822009-05-15 07:01:25296 // Tell other services the extension is gone.
297 NotificationService::current()->Notify(NotificationType::EXTENSION_UNLOADED,
[email protected]c6e4a3412009-06-24 15:45:29298 Source<ExtensionsService>(this),
[email protected]631cf822009-05-15 07:01:25299 Details<Extension>(extension));
300
[email protected]631cf822009-05-15 07:01:25301 delete extension;
302}
303
[email protected]9f1087e2009-06-15 17:29:32304void ExtensionsService::UnloadAllExtensions() {
305 ExtensionList::iterator iter;
[email protected]c6e4a3412009-06-24 15:45:29306 for (iter = extensions_.begin(); iter != extensions_.end(); ++iter)
[email protected]9f1087e2009-06-15 17:29:32307 delete *iter;
[email protected]9f1087e2009-06-15 17:29:32308 extensions_.clear();
[email protected]c6e4a3412009-06-24 15:45:29309
310 // TODO(erikkay) should there be a notification for this? We can't use
311 // EXTENSION_UNLOADED since that implies that the extension has been disabled
312 // or uninstalled, and UnloadAll is just part of shutdown.
[email protected]9f1087e2009-06-15 17:29:32313}
314
315void ExtensionsService::ReloadExtensions() {
316 UnloadAllExtensions();
317 LoadAllExtensions();
318}
319
320void ExtensionsService::GarbageCollectExtensions() {
[email protected]ab6f2b22009-07-28 23:28:37321 backend_loop_->PostTask(FROM_HERE, NewRunnableFunction(
322 &extension_file_util::GarbageCollectExtensions, install_directory_));
[email protected]3cf4f0992009-02-03 23:00:30323}
324
[email protected]e72e8eb82009-06-18 17:21:51325void ExtensionsService::OnLoadedInstalledExtensions() {
[email protected]e81dba32009-06-19 20:19:13326 ready_ = true;
[email protected]93fd78f42009-07-10 16:43:17327 if (updater_.get()) {
328 updater_->Start();
329 }
[email protected]e72e8eb82009-06-18 17:21:51330 NotificationService::current()->Notify(
331 NotificationType::EXTENSIONS_READY,
332 Source<ExtensionsService>(this),
333 NotificationService::NoDetails());
334}
335
[email protected]4a8d3272009-03-10 19:15:08336void ExtensionsService::OnExtensionsLoaded(ExtensionList* new_extensions) {
[email protected]9f1087e2009-06-15 17:29:32337 scoped_ptr<ExtensionList> cleanup(new_extensions);
338
[email protected]75a25672009-07-24 17:41:39339 // Filter out any extensions that shouldn't be loaded. If extensions are
340 // enabled, load everything. Otherwise load only:
341 // - themes
342 // - --load-extension
343 // - externally installed extensions
[email protected]a1257b12009-06-12 02:51:34344 ExtensionList enabled_extensions;
[email protected]86a274072009-06-11 02:06:45345 for (ExtensionList::iterator iter = new_extensions->begin();
[email protected]25b34332009-06-05 21:53:19346 iter != new_extensions->end(); ++iter) {
[email protected]75a25672009-07-24 17:41:39347 if (extensions_enabled() ||
348 (*iter)->IsTheme() ||
[email protected]919ddc82009-07-15 04:30:12349 (*iter)->location() == Extension::LOAD ||
[email protected]75a25672009-07-24 17:41:39350 Extension::IsExternalLocation((*iter)->location())) {
[email protected]9f1087e2009-06-15 17:29:32351 Extension* old = GetExtensionById((*iter)->id());
352 if (old) {
353 if ((*iter)->version()->CompareTo(*(old->version())) > 0) {
354 // To upgrade an extension in place, unload the old one and
355 // then load the new one.
356 // TODO(erikkay) issue 12399
357 UnloadExtension(old->id());
358 } else {
359 // We already have the extension of the same or older version.
360 LOG(WARNING) << "Duplicate extension load attempt: " << (*iter)->id();
361 delete *iter;
362 continue;
363 }
364 }
[email protected]86a274072009-06-11 02:06:45365 enabled_extensions.push_back(*iter);
[email protected]9f1087e2009-06-15 17:29:32366 extensions_.push_back(*iter);
[email protected]ba74f352009-06-11 18:54:45367 } else {
368 // Extensions that get enabled get added to extensions_ and deleted later.
369 // Anything skipped must be deleted now so we don't leak.
370 delete *iter;
371 }
[email protected]86a274072009-06-11 02:06:45372 }
373
[email protected]e72e8eb82009-06-18 17:21:51374 if (enabled_extensions.size()) {
375 NotificationService::current()->Notify(
376 NotificationType::EXTENSIONS_LOADED,
[email protected]c6e4a3412009-06-24 15:45:29377 Source<ExtensionsService>(this),
[email protected]e72e8eb82009-06-18 17:21:51378 Details<ExtensionList>(&enabled_extensions));
[email protected]811f3432009-07-25 19:38:21379 for (ExtensionList::iterator iter = enabled_extensions.begin();
380 iter != enabled_extensions.end(); ++iter) {
381 if ((*iter)->IsTheme() && (*iter)->location() == Extension::LOAD) {
382 NotificationService::current()->Notify(
383 NotificationType::THEME_INSTALLED,
384 Source<ExtensionsService>(this),
385 Details<Extension>(*iter));
386 }
387 }
[email protected]e72e8eb82009-06-18 17:21:51388 }
[email protected]6014d672008-12-05 00:38:25389}
390
[email protected]e957fe52009-06-23 16:51:05391void ExtensionsService::OnExtensionInstalled(const FilePath& path,
392 Extension* extension, Extension::InstallType install_type) {
393 FireInstallCallback(path, extension);
[email protected]e72e8eb82009-06-18 17:21:51394 extension_prefs_->OnExtensionInstalled(extension);
[email protected]25b34332009-06-05 21:53:19395
[email protected]4a190632009-05-09 01:07:42396 // If the extension is a theme, tell the profile (and therefore ThemeProvider)
397 // to apply it.
398 if (extension->IsTheme()) {
[email protected]6ef635e42009-07-26 06:16:12399 ShowThemePreviewInfobar(extension);
[email protected]9ceb07342009-07-26 04:09:23400 NotificationService::current()->Notify(
401 NotificationType::THEME_INSTALLED,
402 Source<ExtensionsService>(this),
403 Details<Extension>(extension));
[email protected]9197f3b2009-06-02 00:49:27404 } else {
405 NotificationService::current()->Notify(
406 NotificationType::EXTENSION_INSTALLED,
[email protected]c6e4a3412009-06-24 15:45:29407 Source<ExtensionsService>(this),
[email protected]9197f3b2009-06-02 00:49:27408 Details<Extension>(extension));
[email protected]4a190632009-05-09 01:07:42409 }
410}
411
[email protected]e957fe52009-06-23 16:51:05412
413void ExtensionsService::OnExtenionInstallError(const FilePath& path) {
414 FireInstallCallback(path, NULL);
415}
416
417void ExtensionsService::FireInstallCallback(const FilePath& path,
418 Extension* extension) {
419 CallbackMap::iterator iter = install_callbacks_.find(path);
420 if (iter != install_callbacks_.end()) {
421 iter->second->Run(path, extension);
422 install_callbacks_.erase(iter);
423 }
424}
425
426void ExtensionsService::OnExtensionOverinstallAttempted(const std::string& id,
427 const FilePath& path) {
428 FireInstallCallback(path, NULL);
[email protected]9f1087e2009-06-15 17:29:32429 Extension* extension = GetExtensionById(id);
[email protected]4a190632009-05-09 01:07:42430 if (extension && extension->IsTheme()) {
[email protected]6ef635e42009-07-26 06:16:12431 ShowThemePreviewInfobar(extension);
[email protected]9ceb07342009-07-26 04:09:23432 NotificationService::current()->Notify(
433 NotificationType::THEME_INSTALLED,
434 Source<ExtensionsService>(this),
435 Details<Extension>(extension));
[email protected]4a190632009-05-09 01:07:42436 }
[email protected]cc655912009-01-29 23:19:19437}
438
[email protected]9f1087e2009-06-15 17:29:32439Extension* ExtensionsService::GetExtensionById(const std::string& id) {
[email protected]e957fe52009-06-23 16:51:05440 std::string lowercase_id = StringToLowerASCII(id);
[email protected]ce5c4502009-05-06 16:46:11441 for (ExtensionList::const_iterator iter = extensions_.begin();
[email protected]4a190632009-05-09 01:07:42442 iter != extensions_.end(); ++iter) {
[email protected]e957fe52009-06-23 16:51:05443 if ((*iter)->id() == lowercase_id)
[email protected]ce5c4502009-05-06 16:46:11444 return *iter;
445 }
446 return NULL;
447}
448
[email protected]9f1087e2009-06-15 17:29:32449Extension* ExtensionsService::GetExtensionByURL(const GURL& url) {
450 std::string host = url.host();
451 return GetExtensionById(host);
452}
453
[email protected]a1257b12009-06-12 02:51:34454void ExtensionsService::ClearProvidersForTesting() {
455 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
456 &ExtensionsServiceBackend::ClearProvidersForTesting));
457}
458
459void ExtensionsService::SetProviderForTesting(
460 Extension::Location location, ExternalExtensionProvider* test_provider) {
461 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
462 &ExtensionsServiceBackend::SetProviderForTesting,
463 location, test_provider));
464}
465
[email protected]6ef635e42009-07-26 06:16:12466bool ExtensionsService::ShowThemePreviewInfobar(Extension* extension) {
467 if (!profile_)
468 return false;
469
470 Browser* browser = BrowserList::GetLastActiveWithProfile(profile_);
471 if (!browser)
472 return false;
473
474 TabContents* tab_contents = browser->GetSelectedTabContents();
475 if (!tab_contents)
476 return false;
477
478 tab_contents->AddInfoBar(new ThemePreviewInfobarDelegate(tab_contents,
479 extension->name()));
480 return true;
481}
482
[email protected]6014d672008-12-05 00:38:25483// ExtensionsServicesBackend
484
[email protected]894bb502009-05-21 22:39:57485ExtensionsServiceBackend::ExtensionsServiceBackend(
486 const FilePath& install_directory, ResourceDispatcherHost* rdh,
[email protected]27b985d2009-06-25 17:53:15487 MessageLoop* frontend_loop, bool extensions_enabled)
[email protected]0c7bc4b2009-05-30 01:47:08488 : frontend_(NULL),
489 install_directory_(install_directory),
[email protected]894bb502009-05-21 22:39:57490 resource_dispatcher_host_(rdh),
[email protected]0c7bc4b2009-05-30 01:47:08491 alert_on_error_(false),
[email protected]abe7a8942009-06-23 05:14:29492 frontend_loop_(frontend_loop),
[email protected]36a784c2009-06-23 06:21:08493 extensions_enabled_(extensions_enabled) {
[email protected]a1257b12009-06-12 02:51:34494 external_extension_providers_[Extension::EXTERNAL_PREF] =
[email protected]da50530a2009-06-15 17:43:01495 linked_ptr<ExternalExtensionProvider>(
[email protected]27b985d2009-06-25 17:53:15496 new ExternalPrefExtensionProvider());
[email protected]a1257b12009-06-12 02:51:34497#if defined(OS_WIN)
498 external_extension_providers_[Extension::EXTERNAL_REGISTRY] =
[email protected]da50530a2009-06-15 17:43:01499 linked_ptr<ExternalExtensionProvider>(
500 new ExternalRegistryExtensionProvider());
[email protected]a1257b12009-06-12 02:51:34501#endif
502}
503
504ExtensionsServiceBackend::~ExtensionsServiceBackend() {
[email protected]894bb502009-05-21 22:39:57505}
506
[email protected]e72e8eb82009-06-18 17:21:51507void ExtensionsServiceBackend::LoadInstalledExtensions(
[email protected]25b34332009-06-05 21:53:19508 scoped_refptr<ExtensionsService> frontend,
[email protected]e72e8eb82009-06-18 17:21:51509 InstalledExtensions* installed) {
510 scoped_ptr<InstalledExtensions> cleanup(installed);
[email protected]b0beaa662009-02-26 00:04:15511 frontend_ = frontend;
512 alert_on_error_ = false;
[email protected]b0beaa662009-02-26 00:04:15513
[email protected]e72e8eb82009-06-18 17:21:51514 // Call LoadInstalledExtension for each extension |installed| knows about.
515 scoped_ptr<InstalledExtensions::Callback> callback(
516 NewCallback(this, &ExtensionsServiceBackend::LoadInstalledExtension));
517 installed->VisitInstalledExtensions(callback.get());
518
519 frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(
520 frontend_, &ExtensionsService::OnLoadedInstalledExtensions));
[email protected]9f1087e2009-06-15 17:29:32521}
[email protected]b0beaa662009-02-26 00:04:15522
[email protected]b0beaa662009-02-26 00:04:15523void ExtensionsServiceBackend::LoadSingleExtension(
[email protected]894bb502009-05-21 22:39:57524 const FilePath& path_in, scoped_refptr<ExtensionsService> frontend) {
[email protected]b0beaa662009-02-26 00:04:15525 frontend_ = frontend;
526
527 // Explicit UI loads are always noisy.
528 alert_on_error_ = true;
529
[email protected]cc5da332009-03-04 08:02:51530 FilePath extension_path = path_in;
[email protected]f36fa4fb2009-06-19 18:23:50531 file_util::AbsolutePath(&extension_path);
[email protected]bf24d2c2009-02-24 23:07:45532
533 LOG(INFO) << "Loading single extension from " <<
[email protected]cc5da332009-03-04 08:02:51534 WideToASCII(extension_path.BaseName().ToWStringHack());
[email protected]bf24d2c2009-02-24 23:07:45535
[email protected]ab6f2b22009-07-28 23:28:37536 std::string error;
537 Extension* extension = extension_file_util::LoadExtension(
538 extension_path,
539 false, // Don't require id
540 &error);
541
542 if (!extension) {
543 ReportExtensionLoadError(extension_path, error);
544 return;
[email protected]0877fd92009-02-03 16:34:06545 }
[email protected]ab6f2b22009-07-28 23:28:37546
547 extension->set_location(Extension::LOAD);
548 ExtensionList* extensions = new ExtensionList;
549 extensions->push_back(extension);
550 ReportExtensionsLoaded(extensions);
[email protected]0877fd92009-02-03 16:34:06551}
552
[email protected]e72e8eb82009-06-18 17:21:51553void ExtensionsServiceBackend::LoadInstalledExtension(
554 const std::string& id, const FilePath& path, Extension::Location location) {
555 if (CheckExternalUninstall(id, location)) {
[email protected]27b985d2009-06-25 17:53:15556 frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(
557 frontend_,
558 &ExtensionsService::UninstallExtension,
559 id, true));
[email protected]e72e8eb82009-06-18 17:21:51560
561 // No error needs to be reported. The extension effectively doesn't exist.
562 return;
563 }
564
[email protected]ab6f2b22009-07-28 23:28:37565 std::string error;
566 Extension* extension = extension_file_util::LoadExtension(
567 path,
568 true, // Require id
569 &error);
570
571 if (!extension) {
572 ReportExtensionLoadError(path, error);
573 return;
574 }
[email protected]e72e8eb82009-06-18 17:21:51575
576 // TODO(erikkay) now we only report a single extension loaded at a time.
577 // Perhaps we should change the notifications to remove ExtensionList.
[email protected]ab6f2b22009-07-28 23:28:37578 extension->set_location(location);
[email protected]e72e8eb82009-06-18 17:21:51579 ExtensionList* extensions = new ExtensionList;
580 if (extension)
581 extensions->push_back(extension);
582 ReportExtensionsLoaded(extensions);
583}
584
[email protected]6014d672008-12-05 00:38:25585void ExtensionsServiceBackend::ReportExtensionLoadError(
[email protected]cc5da332009-03-04 08:02:51586 const FilePath& extension_path, const std::string &error) {
[email protected]b0beaa662009-02-26 00:04:15587 // TODO(port): note that this isn't guaranteed to work properly on Linux.
[email protected]cc5da332009-03-04 08:02:51588 std::string path_str = WideToASCII(extension_path.ToWStringHack());
[email protected]3acbd422008-12-08 18:25:00589 std::string message = StringPrintf("Could not load extension from '%s'. %s",
[email protected]cc655912009-01-29 23:19:19590 path_str.c_str(), error.c_str());
[email protected]bb28e062009-02-27 17:19:18591 ExtensionErrorReporter::GetInstance()->ReportError(message, alert_on_error_);
[email protected]6014d672008-12-05 00:38:25592}
593
594void ExtensionsServiceBackend::ReportExtensionsLoaded(
[email protected]b0beaa662009-02-26 00:04:15595 ExtensionList* extensions) {
[email protected]894bb502009-05-21 22:39:57596 frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(
597 frontend_, &ExtensionsService::OnExtensionsLoaded, extensions));
[email protected]6014d672008-12-05 00:38:25598}
[email protected]cc655912009-01-29 23:19:19599
[email protected]b0beaa662009-02-26 00:04:15600void ExtensionsServiceBackend::InstallExtension(
[email protected]c1e432a2009-07-22 21:21:48601 const FilePath& extension_path, bool from_gallery,
602 scoped_refptr<ExtensionsService> frontend) {
[email protected]cc655912009-01-29 23:19:19603 LOG(INFO) << "Installing extension " << extension_path.value();
604
[email protected]b0beaa662009-02-26 00:04:15605 frontend_ = frontend;
[email protected]0e34d7892009-06-05 19:17:40606 alert_on_error_ = true;
[email protected]b0beaa662009-02-26 00:04:15607
[email protected]c1e432a2009-07-22 21:21:48608 InstallOrUpdateExtension(extension_path, from_gallery, std::string(), false);
[email protected]b0beaa662009-02-26 00:04:15609}
610
[email protected]e957fe52009-06-23 16:51:05611void ExtensionsServiceBackend::UpdateExtension(const std::string& id,
612 const FilePath& extension_path, bool alert_on_error,
613 scoped_refptr<ExtensionsService> frontend) {
614 LOG(INFO) << "Updating extension " << id << " " << extension_path.value();
615
616 frontend_ = frontend;
617 alert_on_error_ = alert_on_error;
618
[email protected]c1e432a2009-07-22 21:21:48619 InstallOrUpdateExtension(extension_path, false, id, true);
[email protected]e957fe52009-06-23 16:51:05620}
621
[email protected]1fca1492009-05-15 22:23:43622void ExtensionsServiceBackend::InstallOrUpdateExtension(
[email protected]c1e432a2009-07-22 21:21:48623 const FilePath& extension_path, bool from_gallery,
624 const std::string& expected_id, bool silent) {
[email protected]af1277b2009-07-28 00:47:53625 // NOTE: We don't need to keep a reference to this, it deletes itself when it
626 // is done.
627 new UnpackerClient(this, extension_path, expected_id, silent, from_gallery);
[email protected]fbcc40302009-06-12 20:45:45628}
629
[email protected]1fca1492009-05-15 22:23:43630void ExtensionsServiceBackend::OnExtensionUnpacked(
[email protected]af1277b2009-07-28 00:47:53631 const FilePath& crx_path, const FilePath& unpacked_path,
632 Extension* extension, const std::string expected_id, bool silent,
633 bool from_gallery) {
634 // Take ownership of the extension object.
635 scoped_ptr<Extension> extension_deleter(extension);
[email protected]0b344962009-03-31 04:21:45636
[email protected]abe7a8942009-06-23 05:14:29637 Extension::Location location = Extension::INTERNAL;
[email protected]af1277b2009-07-28 00:47:53638 LookupExternalExtension(extension->id(), NULL, &location);
639 extension->set_location(location);
[email protected]abe7a8942009-06-23 05:14:29640
[email protected]75a25672009-07-24 17:41:39641 bool allow_install = false;
642 if (extensions_enabled_)
643 allow_install = true;
644
[email protected]6ef635e42009-07-26 06:16:12645 // Always allow themes.
[email protected]af1277b2009-07-28 00:47:53646 if (extension->IsTheme())
[email protected]75a25672009-07-24 17:41:39647 allow_install = true;
648
[email protected]6ef635e42009-07-26 06:16:12649 // Always allow externally installed extensions (partners use this).
[email protected]75a25672009-07-24 17:41:39650 if (Extension::IsExternalLocation(location))
651 allow_install = true;
652
653 if (!allow_install) {
[email protected]af1277b2009-07-28 00:47:53654 ReportExtensionInstallError(crx_path, "Extensions are not enabled.");
[email protected]e2eb43112009-05-29 21:19:54655 return;
656 }
657
[email protected]638d3522009-06-25 14:52:01658 // TODO(extensions): Make better extensions UI. http://crbug.com/12116
659
[email protected]75a25672009-07-24 17:41:39660 // We also skip the dialog for a few special cases:
[email protected]6ef635e42009-07-26 06:16:12661 // - themes (because we show the preview infobar for them)
[email protected]c1e432a2009-07-22 21:21:48662 // - externally registered extensions
[email protected]75a25672009-07-24 17:41:39663 // - during tests (!frontend->show_extension_prompts())
664 // - autoupdate (silent).
[email protected]c1e432a2009-07-22 21:21:48665 bool show_dialog = true;
[email protected]af1277b2009-07-28 00:47:53666 if (extension->IsTheme())
[email protected]c1e432a2009-07-22 21:21:48667 show_dialog = false;
668
669 if (Extension::IsExternalLocation(location))
670 show_dialog = false;
671
672 if (silent || !frontend_->show_extensions_prompts())
673 show_dialog = false;
674
675 if (show_dialog) {
[email protected]638d3522009-06-25 14:52:01676#if defined(OS_WIN)
[email protected]6987f242009-07-23 08:00:35677 if (win_util::MessageBox(GetForegroundWindow(),
[email protected]638d3522009-06-25 14:52:01678 L"Are you sure you want to install this extension?\n\n"
[email protected]75a25672009-07-24 17:41:39679 L"You should only install extensions from sources you trust.",
[email protected]638d3522009-06-25 14:52:01680 l10n_util::GetString(IDS_PRODUCT_NAME).c_str(),
681 MB_OKCANCEL) != IDOK) {
[email protected]638d3522009-06-25 14:52:01682 return;
683 }
684#elif defined(OS_MACOSX)
685 // Using CoreFoundation to do this dialog is unimaginably lame but will do
686 // until the UI is redone.
687 scoped_cftyperef<CFStringRef> product_name(
688 base::SysWideToCFStringRef(l10n_util::GetString(IDS_PRODUCT_NAME)));
689 CFOptionFlags response;
690 CFUserNotificationDisplayAlert(
691 0, kCFUserNotificationCautionAlertLevel, NULL, NULL, NULL,
692 product_name,
693 CFSTR("Are you sure you want to install this extension?\n\n"
694 "This is a temporary message and it will be removed when "
695 "extensions UI is finalized."),
696 NULL, CFSTR("Cancel"), NULL, &response);
697
698 if (response == kCFUserNotificationAlternateResponse) {
[email protected]af1277b2009-07-28 00:47:53699 ReportExtensionInstallError(crx_path,
[email protected]638d3522009-06-25 14:52:01700 "User did not allow extension to be installed.");
701 return;
702 }
703#endif // OS_*
[email protected]0e34d7892009-06-05 19:17:40704 }
[email protected]0e34d7892009-06-05 19:17:40705
[email protected]b0beaa662009-02-26 00:04:15706 // If an expected id was provided, make sure it matches.
[email protected]af1277b2009-07-28 00:47:53707 if (!expected_id.empty() && expected_id != extension->id()) {
[email protected]ab6f2b22009-07-28 23:28:37708 ReportExtensionInstallError(crx_path,
709 StringPrintf("ID in new extension manifest (%s) does not match "
710 "expected id (%s)", extension->id().c_str(),
711 expected_id.c_str()));
712 return;
713 }
714
715 FilePath version_dir;
716 Extension::InstallType install_type = Extension::INSTALL_ERROR;
717 std::string error_msg;
718 if (!extension_file_util::InstallExtension(unpacked_path, install_directory_,
719 extension->id(),
720 extension->VersionString(),
721 &version_dir,
722 &install_type, &error_msg)) {
[email protected]af1277b2009-07-28 00:47:53723 ReportExtensionInstallError(crx_path, error_msg);
[email protected]1fca1492009-05-15 22:23:43724 return;
[email protected]cc655912009-01-29 23:19:19725 }
726
[email protected]fbcc40302009-06-12 20:45:45727 if (install_type == Extension::DOWNGRADE) {
[email protected]ab6f2b22009-07-28 23:28:37728 ReportExtensionInstallError(crx_path, "Attempted to downgrade extension.");
[email protected]fbcc40302009-06-12 20:45:45729 return;
730 }
731
[email protected]af1277b2009-07-28 00:47:53732 extension->set_path(version_dir);
[email protected]902f7cd2009-05-22 19:02:19733
[email protected]ab6f2b22009-07-28 23:28:37734 if (install_type == Extension::REINSTALL) {
735 // The client may use this as a signal (to switch themes, for instance).
736 ReportExtensionOverinstallAttempted(extension->id(), crx_path);
[email protected]1fca1492009-05-15 22:23:43737 return;
[email protected]ab6f2b22009-07-28 23:28:37738 }
[email protected]cc655912009-01-29 23:19:19739
[email protected]9f1087e2009-06-15 17:29:32740 frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(
[email protected]af1277b2009-07-28 00:47:53741 frontend_, &ExtensionsService::OnExtensionInstalled, crx_path,
742 extension, install_type));
[email protected]894bb502009-05-21 22:39:57743
[email protected]9f1087e2009-06-15 17:29:32744 // Only one extension, but ReportExtensionsLoaded can handle multiple,
745 // so we need to construct a list.
[email protected]af1277b2009-07-28 00:47:53746 ExtensionList* extensions = new ExtensionList;
[email protected]894bb502009-05-21 22:39:57747
[email protected]af1277b2009-07-28 00:47:53748 // Hand off ownership of the extension to the frontend.
749 extensions->push_back(extension_deleter.release());
750 ReportExtensionsLoaded(extensions);
[email protected]cc655912009-01-29 23:19:19751}
752
753void ExtensionsServiceBackend::ReportExtensionInstallError(
[email protected]cc5da332009-03-04 08:02:51754 const FilePath& extension_path, const std::string &error) {
[email protected]b0beaa662009-02-26 00:04:15755
[email protected]cc655912009-01-29 23:19:19756 // TODO(erikkay): note that this isn't guaranteed to work properly on Linux.
[email protected]cc5da332009-03-04 08:02:51757 std::string path_str = WideToASCII(extension_path.ToWStringHack());
[email protected]cc655912009-01-29 23:19:19758 std::string message =
759 StringPrintf("Could not install extension from '%s'. %s",
760 path_str.c_str(), error.c_str());
[email protected]bb28e062009-02-27 17:19:18761 ExtensionErrorReporter::GetInstance()->ReportError(message, alert_on_error_);
[email protected]e957fe52009-06-23 16:51:05762
763 frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(
764 frontend_,
765 &ExtensionsService::OnExtenionInstallError,
766 extension_path));
[email protected]cc655912009-01-29 23:19:19767}
768
[email protected]fbcc40302009-06-12 20:45:45769void ExtensionsServiceBackend::ReportExtensionOverinstallAttempted(
[email protected]e957fe52009-06-23 16:51:05770 const std::string& id, const FilePath& path) {
[email protected]894bb502009-05-21 22:39:57771 frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(
[email protected]e957fe52009-06-23 16:51:05772 frontend_, &ExtensionsService::OnExtensionOverinstallAttempted, id,
773 path));
[email protected]b0beaa662009-02-26 00:04:15774}
775
[email protected]a1257b12009-06-12 02:51:34776bool ExtensionsServiceBackend::LookupExternalExtension(
777 const std::string& id, Version** version, Extension::Location* location) {
778 scoped_ptr<Version> extension_version;
779 for (ProviderMap::const_iterator i = external_extension_providers_.begin();
780 i != external_extension_providers_.end(); ++i) {
[email protected]da50530a2009-06-15 17:43:01781 const ExternalExtensionProvider* provider = i->second.get();
[email protected]a1257b12009-06-12 02:51:34782 extension_version.reset(provider->RegisteredVersion(id, location));
783 if (extension_version.get()) {
784 if (version)
785 *version = extension_version.release();
786 return true;
787 }
788 }
789 return false;
790}
791
[email protected]b0beaa662009-02-26 00:04:15792// Some extensions will autoupdate themselves externally from Chrome. These
793// are typically part of some larger client application package. To support
[email protected]25b34332009-06-05 21:53:19794// these, the extension will register its location in the the preferences file
795// (and also, on Windows, in the registry) and this code will periodically
[email protected]b0beaa662009-02-26 00:04:15796// check that location for a .crx file, which it will then install locally if
797// a new version is available.
798void ExtensionsServiceBackend::CheckForExternalUpdates(
[email protected]894bb502009-05-21 22:39:57799 std::set<std::string> ids_to_ignore,
800 scoped_refptr<ExtensionsService> frontend) {
[email protected]b0beaa662009-02-26 00:04:15801 // Note that this installation is intentionally silent (since it didn't
802 // go through the front-end). Extensions that are registered in this
803 // way are effectively considered 'pre-bundled', and so implicitly
804 // trusted. In general, if something has HKLM or filesystem access,
805 // they could install an extension manually themselves anyway.
806 alert_on_error_ = false;
807 frontend_ = frontend;
[email protected]b0beaa662009-02-26 00:04:15808
[email protected]a1257b12009-06-12 02:51:34809 // Ask each external extension provider to give us a call back for each
810 // extension they know about. See OnExternalExtensionFound.
811 for (ProviderMap::const_iterator i = external_extension_providers_.begin();
812 i != external_extension_providers_.end(); ++i) {
[email protected]da50530a2009-06-15 17:43:01813 ExternalExtensionProvider* provider = i->second.get();
[email protected]a1257b12009-06-12 02:51:34814 provider->VisitRegisteredExtension(this, ids_to_ignore);
[email protected]25b34332009-06-05 21:53:19815 }
[email protected]b0beaa662009-02-26 00:04:15816}
817
[email protected]25b34332009-06-05 21:53:19818bool ExtensionsServiceBackend::CheckExternalUninstall(
[email protected]e72e8eb82009-06-18 17:21:51819 const std::string& id, Extension::Location location) {
[email protected]a1257b12009-06-12 02:51:34820 // Check if the providers know about this extension.
821 ProviderMap::const_iterator i = external_extension_providers_.find(location);
822 if (i != external_extension_providers_.end()) {
823 scoped_ptr<Version> version;
824 version.reset(i->second->RegisteredVersion(id, NULL));
825 if (version.get())
826 return false; // Yup, known extension, don't uninstall.
[email protected]e72e8eb82009-06-18 17:21:51827 } else {
828 // Not from an external provider, so it's fine.
829 return false;
[email protected]b0beaa662009-02-26 00:04:15830 }
[email protected]25b34332009-06-05 21:53:19831
[email protected]a1257b12009-06-12 02:51:34832 return true; // This is not a known extension, uninstall.
[email protected]b0beaa662009-02-26 00:04:15833}
834
[email protected]a1257b12009-06-12 02:51:34835void ExtensionsServiceBackend::ClearProvidersForTesting() {
836 external_extension_providers_.clear();
837}
838
839void ExtensionsServiceBackend::SetProviderForTesting(
840 Extension::Location location,
841 ExternalExtensionProvider* test_provider) {
842 DCHECK(test_provider);
[email protected]da50530a2009-06-15 17:43:01843 external_extension_providers_[location] =
844 linked_ptr<ExternalExtensionProvider>(test_provider);
[email protected]a1257b12009-06-12 02:51:34845}
846
847void ExtensionsServiceBackend::OnExternalExtensionFound(
848 const std::string& id, const Version* version, const FilePath& path) {
[email protected]ab6f2b22009-07-28 23:28:37849 InstallOrUpdateExtension(path,
850 false, // not from gallery
851 id, // expected id
852 true); // silent
[email protected]cc655912009-01-29 23:19:19853}