blob: b86c4548229f1f2e6745b8cfe5ad49958526e894 [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]902f7cd2009-05-22 19:02:1910#include "base/gfx/png_encoder.h"
[email protected]cc655912009-01-29 23:19:1911#include "base/scoped_handle.h"
12#include "base/scoped_temp_dir.h"
[email protected]6014d672008-12-05 00:38:2513#include "base/string_util.h"
[email protected]cc655912009-01-29 23:19:1914#include "base/third_party/nss/blapi.h"
15#include "base/third_party/nss/sha256.h"
[email protected]6014d672008-12-05 00:38:2516#include "base/thread.h"
[email protected]cc655912009-01-29 23:19:1917#include "base/values.h"
18#include "net/base/file_stream.h"
[email protected]a57209872009-05-04 22:53:1419#include "chrome/browser/browser.h"
20#include "chrome/browser/browser_list.h"
[email protected]6014d672008-12-05 00:38:2521#include "chrome/browser/browser_process.h"
[email protected]1fca1492009-05-15 22:23:4322#include "chrome/browser/chrome_thread.h"
[email protected]b68d5ed2009-04-16 02:41:2823#include "chrome/browser/extensions/extension_browser_event_router.h"
[email protected]481e1a42009-05-06 20:56:0524#include "chrome/browser/extensions/extension_process_manager.h"
[email protected]81e63782009-02-27 19:35:0925#include "chrome/browser/profile.h"
[email protected]1fca1492009-05-15 22:23:4326#include "chrome/browser/utility_process_host.h"
[email protected]e2eb43112009-05-29 21:19:5427#include "chrome/common/chrome_switches.h"
[email protected]5b1a0e22009-05-26 19:00:5828#include "chrome/common/extensions/extension.h"
29#include "chrome/common/extensions/extension_error_reporter.h"
[email protected]1fca1492009-05-15 22:23:4330#include "chrome/common/extensions/extension_unpacker.h"
[email protected]6014d672008-12-05 00:38:2531#include "chrome/common/json_value_serializer.h"
[email protected]82891262008-12-24 00:21:2632#include "chrome/common/notification_service.h"
[email protected]894bb502009-05-21 22:39:5733#include "chrome/common/pref_service.h"
[email protected]cc655912009-01-29 23:19:1934#include "chrome/common/unzip.h"
[email protected]a57209872009-05-04 22:53:1435#include "chrome/common/url_constants.h"
[email protected]e2eb43112009-05-29 21:19:5436#include "grit/chromium_strings.h"
37#include "grit/generated_resources.h"
[email protected]902f7cd2009-05-22 19:02:1938#include "third_party/skia/include/core/SkBitmap.h"
[email protected]c64631652009-04-29 22:24:3139
[email protected]79db6232009-02-13 20:51:2040#if defined(OS_WIN)
[email protected]e2eb43112009-05-29 21:19:5441#include "app/win_util.h"
[email protected]b0beaa662009-02-26 00:04:1542#include "base/registry.h"
[email protected]e2eb43112009-05-29 21:19:5443#include "base/win_util.h"
[email protected]79db6232009-02-13 20:51:2044#endif
[email protected]6014d672008-12-05 00:38:2545
46// ExtensionsService
47
[email protected]cc655912009-01-29 23:19:1948const char* ExtensionsService::kInstallDirectoryName = "Extensions";
49const char* ExtensionsService::kCurrentVersionFileName = "Current Version";
50const char* ExtensionsServiceBackend::kTempExtensionName = "TEMP_INSTALL";
[email protected]b0beaa662009-02-26 00:04:1551
52namespace {
[email protected]cc655912009-01-29 23:19:1953// Chromium Extension magic number
[email protected]b0beaa662009-02-26 00:04:1554const char kExtensionFileMagic[] = "Cr24";
[email protected]cc655912009-01-29 23:19:1955
56struct ExtensionHeader {
57 char magic[sizeof(kExtensionFileMagic) - 1];
58 uint32 version;
59 size_t header_size;
60 size_t manifest_size;
61};
62
63const size_t kZipHashBytes = 32; // SHA-256
64const size_t kZipHashHexBytes = kZipHashBytes * 2; // Hex string is 2x size.
[email protected]6014d672008-12-05 00:38:2565
[email protected]894bb502009-05-21 22:39:5766// A preference that keeps track of external extensions the user has
67// uninstalled.
68const wchar_t kUninstalledExternalPref[] =
69 L"extensions.uninstalled_external_ids";
[email protected]b0beaa662009-02-26 00:04:1570
71// Registry key where registry defined extension installers live.
[email protected]894bb502009-05-21 22:39:5772// TODO(port): Assuming this becomes a similar key into the appropriate
73// platform system.
74const char kRegistryExtensions[] = "Software\\Google\\Chrome\\Extensions";
75
76#if defined(OS_WIN)
[email protected]b0beaa662009-02-26 00:04:1577
78// Registry value of of that key that defines the path to the .crx file.
79const wchar_t kRegistryExtensionPath[] = L"path";
80
81// Registry value of that key that defines the current version of the .crx file.
82const wchar_t kRegistryExtensionVersion[] = L"version";
83
84#endif
85
86// A marker file to indicate that an extension was installed from an external
87// source.
88const char kExternalInstallFile[] = "EXTERNAL_INSTALL";
[email protected]0b344962009-03-31 04:21:4589
[email protected]1fca1492009-05-15 22:23:4390// A temporary subdirectory where we unpack extensions.
91const char* kUnpackExtensionDir = "TEMP_UNPACK";
92
[email protected]0b344962009-03-31 04:21:4593// The version of the extension package that this code understands.
94const uint32 kExpectedVersion = 1;
[email protected]b0beaa662009-02-26 00:04:1595}
96
[email protected]1fca1492009-05-15 22:23:4397// This class coordinates an extension unpack task which is run in a separate
98// process. Results are sent back to this class, which we route to the
99// ExtensionServiceBackend.
100class ExtensionsServiceBackend::UnpackerClient
101 : public UtilityProcessHost::Client {
102 public:
103 UnpackerClient(ExtensionsServiceBackend* backend,
104 const FilePath& extension_path,
105 const std::string& expected_id,
106 bool from_external)
107 : backend_(backend), extension_path_(extension_path),
[email protected]2b4053c2009-05-29 20:28:09108 expected_id_(expected_id), from_external_(from_external),
109 got_response_(false) {
[email protected]1fca1492009-05-15 22:23:43110 }
111
112 // Starts the unpack task. We call back to the backend when the task is done,
113 // or a problem occurs.
114 void Start() {
115 AddRef(); // balanced in OnUnpackExtensionReply()
116
117 // TODO(mpcomplete): handle multiple installs
118 FilePath temp_dir = backend_->install_directory_.AppendASCII(
119 kUnpackExtensionDir);
120 if (!file_util::CreateDirectory(temp_dir)) {
121 backend_->ReportExtensionInstallError(extension_path_,
122 "Failed to create temporary directory.");
123 return;
124 }
125
126 temp_extension_path_ = temp_dir.Append(extension_path_.BaseName());
127 if (!file_util::CopyFile(extension_path_, temp_extension_path_)) {
128 backend_->ReportExtensionInstallError(extension_path_,
129 "Failed to copy extension file to temporary directory.");
130 return;
131 }
132
133 if (backend_->resource_dispatcher_host_) {
134 ChromeThread::GetMessageLoop(ChromeThread::IO)->PostTask(FROM_HERE,
135 NewRunnableMethod(this, &UnpackerClient::StartProcessOnIOThread,
136 backend_->resource_dispatcher_host_,
137 MessageLoop::current()));
138 } else {
139 // Cheesy... but if we don't have a ResourceDispatcherHost, assume we're
140 // in a unit test and run the unpacker directly in-process.
141 ExtensionUnpacker unpacker(temp_extension_path_);
[email protected]902f7cd2009-05-22 19:02:19142 if (unpacker.Run()) {
143 OnUnpackExtensionSucceeded(*unpacker.parsed_manifest(),
144 unpacker.decoded_images());
145 } else {
146 OnUnpackExtensionFailed(unpacker.error_message());
147 }
[email protected]1fca1492009-05-15 22:23:43148 }
149 }
150
151 private:
152 // UtilityProcessHost::Client
153 virtual void OnProcessCrashed() {
[email protected]2b4053c2009-05-29 20:28:09154 // Don't report crashes if they happen after we got a response.
155 if (got_response_)
156 return;
157
158 OnUnpackExtensionFailed("Chrome crashed while trying to install.");
[email protected]1fca1492009-05-15 22:23:43159 }
160
[email protected]902f7cd2009-05-22 19:02:19161 virtual void OnUnpackExtensionSucceeded(
162 const DictionaryValue& manifest,
163 const std::vector< Tuple2<SkBitmap, FilePath> >& images) {
164 // The extension was unpacked to the temp dir inside our unpacking dir.
165 FilePath extension_dir = temp_extension_path_.DirName().AppendASCII(
166 ExtensionsServiceBackend::kTempExtensionName);
167 backend_->OnExtensionUnpacked(extension_path_, extension_dir,
168 expected_id_, from_external_,
169 manifest, images);
[email protected]1fca1492009-05-15 22:23:43170 Cleanup();
[email protected]902f7cd2009-05-22 19:02:19171 }
172
173 virtual void OnUnpackExtensionFailed(const std::string& error_message) {
174 backend_->ReportExtensionInstallError(extension_path_, error_message);
175 Cleanup();
[email protected]1fca1492009-05-15 22:23:43176 }
177
178 // Cleans up our temp directory.
179 void Cleanup() {
[email protected]2b4053c2009-05-29 20:28:09180 if (got_response_)
181 return;
182
183 got_response_ = true;
[email protected]1fca1492009-05-15 22:23:43184 file_util::Delete(temp_extension_path_.DirName(), true);
[email protected]902f7cd2009-05-22 19:02:19185 Release(); // balanced in Run()
[email protected]1fca1492009-05-15 22:23:43186 }
187
188 // Starts the utility process that unpacks our extension.
189 void StartProcessOnIOThread(ResourceDispatcherHost* rdh,
190 MessageLoop* file_loop) {
191 UtilityProcessHost* host = new UtilityProcessHost(rdh, this, file_loop);
192 host->StartExtensionUnpacker(temp_extension_path_);
193 }
194
195 scoped_refptr<ExtensionsServiceBackend> backend_;
196
197 // The path to the crx file that we're installing.
198 FilePath extension_path_;
199
200 // The path to the copy of the crx file in the temporary directory where we're
201 // unpacking it.
202 FilePath temp_extension_path_;
203
204 // The ID we expect this extension to have, if any.
205 std::string expected_id_;
206
207 // True if this is being installed from an external source.
208 bool from_external_;
[email protected]2b4053c2009-05-29 20:28:09209
210 // True if we got a response from the utility process and have cleaned up
211 // already.
212 bool got_response_;
[email protected]1fca1492009-05-15 22:23:43213};
214
[email protected]81e63782009-02-27 19:35:09215ExtensionsService::ExtensionsService(Profile* profile,
[email protected]894bb502009-05-21 22:39:57216 MessageLoop* frontend_loop,
217 MessageLoop* backend_loop,
218 const std::string& registry_path)
219 : prefs_(profile->GetPrefs()),
220 backend_loop_(backend_loop),
[email protected]81e63782009-02-27 19:35:09221 install_directory_(profile->GetPath().AppendASCII(kInstallDirectoryName)),
[email protected]e2eb43112009-05-29 21:19:54222 extensions_enabled_(
223 CommandLine::ForCurrentProcess()->
224 HasSwitch(switches::kEnableExtensions)),
225 show_extensions_disabled_notification_(true),
[email protected]1fca1492009-05-15 22:23:43226 backend_(new ExtensionsServiceBackend(
[email protected]894bb502009-05-21 22:39:57227 install_directory_, g_browser_process->resource_dispatcher_host(),
228 frontend_loop, registry_path)) {
229 prefs_->RegisterListPref(kUninstalledExternalPref);
[email protected]6014d672008-12-05 00:38:25230}
231
232ExtensionsService::~ExtensionsService() {
233 for (ExtensionList::iterator iter = extensions_.begin();
234 iter != extensions_.end(); ++iter) {
235 delete *iter;
236 }
237}
238
239bool ExtensionsService::Init() {
[email protected]b68d5ed2009-04-16 02:41:28240 // Start up the extension event routers.
241 ExtensionBrowserEventRouter::GetInstance()->Init();
242
[email protected]b0beaa662009-02-26 00:04:15243#if defined(OS_WIN)
[email protected]894bb502009-05-21 22:39:57244
245 std::set<std::string> uninstalled_external_ids;
246 const ListValue* list = prefs_->GetList(kUninstalledExternalPref);
247 if (list) {
248 for (size_t i = 0; i < list->GetSize(); ++i) {
249 std::string val;
250 bool ok = list->GetString(i, &val);
251 DCHECK(ok);
252 DCHECK(uninstalled_external_ids.find(val) ==
253 uninstalled_external_ids.end());
254 uninstalled_external_ids.insert(val);
255 }
256 }
257
[email protected]b0beaa662009-02-26 00:04:15258 // TODO(erikkay): Should we monitor the registry during run as well?
[email protected]894bb502009-05-21 22:39:57259 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
260 &ExtensionsServiceBackend::CheckForExternalUpdates,
261 uninstalled_external_ids, scoped_refptr<ExtensionsService>(this)));
262#else
263
264 // TODO(port)
265
[email protected]b0beaa662009-02-26 00:04:15266#endif
267
[email protected]894bb502009-05-21 22:39:57268 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
269 &ExtensionsServiceBackend::LoadExtensionsFromInstallDirectory,
270 scoped_refptr<ExtensionsService>(this)));
[email protected]6014d672008-12-05 00:38:25271
272 return true;
273}
274
[email protected]3cf4f0992009-02-03 23:00:30275void ExtensionsService::InstallExtension(const FilePath& extension_path) {
[email protected]894bb502009-05-21 22:39:57276 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
277 &ExtensionsServiceBackend::InstallExtension,
278 extension_path,
279 scoped_refptr<ExtensionsService>(this)));
[email protected]3cf4f0992009-02-03 23:00:30280}
281
[email protected]631cf822009-05-15 07:01:25282void ExtensionsService::UninstallExtension(const std::string& extension_id) {
283 Extension* extension = NULL;
284
285 ExtensionList::iterator iter;
286 for (iter = extensions_.begin(); iter != extensions_.end(); ++iter) {
287 if ((*iter)->id() == extension_id) {
288 extension = *iter;
289 break;
290 }
291 }
292
[email protected]894bb502009-05-21 22:39:57293 // Callers should not send us nonexistant extensions.
294 CHECK(extension);
295
[email protected]631cf822009-05-15 07:01:25296 // Remove the extension from our list.
297 extensions_.erase(iter);
298
[email protected]631cf822009-05-15 07:01:25299 // Tell other services the extension is gone.
300 NotificationService::current()->Notify(NotificationType::EXTENSION_UNLOADED,
301 NotificationService::AllSources(),
302 Details<Extension>(extension));
303
[email protected]894bb502009-05-21 22:39:57304 // For external extensions, we save a preference reminding ourself not to try
305 // and install the extension anymore.
306 if (extension->location() == Extension::EXTERNAL) {
307 ListValue* list = prefs_->GetMutableList(kUninstalledExternalPref);
308 list->Append(Value::CreateStringValue(extension->id()));
309 prefs_->ScheduleSavePersistentPrefs();
310 }
311
312 // Tell the backend to start deleting installed extensions on the file thread.
313 if (extension->location() == Extension::INTERNAL ||
314 extension->location() == Extension::EXTERNAL) {
315 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
316 &ExtensionsServiceBackend::UninstallExtension, extension_id));
317 }
[email protected]631cf822009-05-15 07:01:25318
319 delete extension;
320}
321
[email protected]3cf4f0992009-02-03 23:00:30322void ExtensionsService::LoadExtension(const FilePath& extension_path) {
[email protected]894bb502009-05-21 22:39:57323 backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
324 &ExtensionsServiceBackend::LoadSingleExtension,
325 extension_path, scoped_refptr<ExtensionsService>(this)));
[email protected]3cf4f0992009-02-03 23:00:30326}
327
[email protected]4a8d3272009-03-10 19:15:08328void ExtensionsService::OnExtensionsLoaded(ExtensionList* new_extensions) {
[email protected]08816d0d2008-12-08 18:43:53329 extensions_.insert(extensions_.end(), new_extensions->begin(),
330 new_extensions->end());
[email protected]bfd04a62009-02-01 18:16:56331 NotificationService::current()->Notify(
332 NotificationType::EXTENSIONS_LOADED,
[email protected]82891262008-12-24 00:21:26333 NotificationService::AllSources(),
334 Details<ExtensionList>(new_extensions));
[email protected]82891262008-12-24 00:21:26335 delete new_extensions;
[email protected]6014d672008-12-05 00:38:25336}
337
[email protected]a57209872009-05-04 22:53:14338void ExtensionsService::OnExtensionInstalled(Extension* extension,
339 bool update) {
[email protected]bfd04a62009-02-01 18:16:56340 NotificationService::current()->Notify(
341 NotificationType::EXTENSION_INSTALLED,
[email protected]cc655912009-01-29 23:19:19342 NotificationService::AllSources(),
[email protected]a57209872009-05-04 22:53:14343 Details<Extension>(extension));
[email protected]4a190632009-05-09 01:07:42344
345 // If the extension is a theme, tell the profile (and therefore ThemeProvider)
346 // to apply it.
347 if (extension->IsTheme()) {
348 NotificationService::current()->Notify(
349 NotificationType::THEME_INSTALLED,
350 NotificationService::AllSources(),
351 Details<Extension>(extension));
352 }
353}
354
355void ExtensionsService::OnExtensionVersionReinstalled(const std::string& id) {
356 Extension* extension = GetExtensionByID(id);
357 if (extension && extension->IsTheme()) {
358 NotificationService::current()->Notify(
359 NotificationType::THEME_INSTALLED,
360 NotificationService::AllSources(),
361 Details<Extension>(extension));
362 }
[email protected]cc655912009-01-29 23:19:19363}
364
[email protected]ce5c4502009-05-06 16:46:11365Extension* ExtensionsService::GetExtensionByID(std::string id) {
366 for (ExtensionList::const_iterator iter = extensions_.begin();
[email protected]4a190632009-05-09 01:07:42367 iter != extensions_.end(); ++iter) {
[email protected]ce5c4502009-05-06 16:46:11368 if ((*iter)->id() == id)
369 return *iter;
370 }
371 return NULL;
372}
373
[email protected]6014d672008-12-05 00:38:25374
375// ExtensionsServicesBackend
376
[email protected]894bb502009-05-21 22:39:57377ExtensionsServiceBackend::ExtensionsServiceBackend(
378 const FilePath& install_directory, ResourceDispatcherHost* rdh,
379 MessageLoop* frontend_loop, const std::string& registry_path)
[email protected]0c7bc4b2009-05-30 01:47:08380 : frontend_(NULL),
381 install_directory_(install_directory),
[email protected]894bb502009-05-21 22:39:57382 resource_dispatcher_host_(rdh),
[email protected]0c7bc4b2009-05-30 01:47:08383 alert_on_error_(false),
[email protected]894bb502009-05-21 22:39:57384 frontend_loop_(frontend_loop),
385 registry_path_(registry_path) {
386 // Default the registry path if unspecified.
387 if (registry_path_.empty()) {
388 registry_path_ = kRegistryExtensions;
389 }
390}
391
[email protected]cc5da332009-03-04 08:02:51392void ExtensionsServiceBackend::LoadExtensionsFromInstallDirectory(
[email protected]894bb502009-05-21 22:39:57393 scoped_refptr<ExtensionsService> frontend) {
[email protected]b0beaa662009-02-26 00:04:15394 frontend_ = frontend;
395 alert_on_error_ = false;
[email protected]05eb0fa2009-02-21 00:05:48396
[email protected]540f91b2009-03-26 19:37:43397#if defined(OS_WIN)
398 // On POSIX, AbsolutePath() calls realpath() which returns NULL if
399 // it does not exist. Instead we absolute-ify after creation in
400 // case that is needed.
401 // TODO(port): does this really need to happen before
402 // CreateDirectory() on Windows?
[email protected]cc5da332009-03-04 08:02:51403 if (!file_util::AbsolutePath(&install_directory_))
[email protected]eab9b452009-01-23 20:48:59404 NOTREACHED();
[email protected]540f91b2009-03-26 19:37:43405#endif
[email protected]eab9b452009-01-23 20:48:59406
[email protected]b0beaa662009-02-26 00:04:15407 scoped_ptr<ExtensionList> extensions(new ExtensionList);
408
409 // Create the <Profile>/Extensions directory if it doesn't exist.
[email protected]cc5da332009-03-04 08:02:51410 if (!file_util::DirectoryExists(install_directory_)) {
411 file_util::CreateDirectory(install_directory_);
[email protected]b0beaa662009-02-26 00:04:15412 LOG(INFO) << "Created Extensions directory. No extensions to install.";
413 ReportExtensionsLoaded(extensions.release());
414 return;
415 }
416
[email protected]540f91b2009-03-26 19:37:43417#if !defined(OS_WIN)
418 if (!file_util::AbsolutePath(&install_directory_))
419 NOTREACHED();
420#endif
421
[email protected]bf24d2c2009-02-24 23:07:45422 LOG(INFO) << "Loading installed extensions...";
423
[email protected]6014d672008-12-05 00:38:25424 // Find all child directories in the install directory and load their
425 // manifests. Post errors and results to the frontend.
[email protected]cc5da332009-03-04 08:02:51426 file_util::FileEnumerator enumerator(install_directory_,
[email protected]cc655912009-01-29 23:19:19427 false, // not recursive
[email protected]6014d672008-12-05 00:38:25428 file_util::FileEnumerator::DIRECTORIES);
[email protected]cc5da332009-03-04 08:02:51429 FilePath extension_path;
430 for (extension_path = enumerator.Next(); !extension_path.value().empty();
431 extension_path = enumerator.Next()) {
432 std::string extension_id = WideToASCII(
433 extension_path.BaseName().ToWStringHack());
[email protected]96088fb2009-05-26 19:08:13434 // The utility process might be in the middle of unpacking an extension, so
435 // ignore the temp unpacking directory.
436 if (extension_id == kUnpackExtensionDir)
437 continue;
[email protected]894bb502009-05-21 22:39:57438
439 // If there is no Current Version file, just delete the directory and move
440 // on. This can legitimately happen when an uninstall does not complete, for
441 // example, when a plugin is in use at uninstall time.
442 FilePath current_version_path = extension_path.AppendASCII(
443 ExtensionsService::kCurrentVersionFileName);
444 if (!file_util::PathExists(current_version_path)) {
445 LOG(INFO) << "Deleting incomplete install for directory "
446 << WideToASCII(extension_path.ToWStringHack()) << ".";
447 file_util::Delete(extension_path, true); // recursive;
448 continue;
449 }
450
451 std::string current_version;
452 if (!ReadCurrentVersion(extension_path, &current_version))
453 continue;
454
455 FilePath version_path = extension_path.AppendASCII(current_version);
456 if (CheckExternalUninstall(version_path, extension_id)) {
[email protected]cc5da332009-03-04 08:02:51457 // TODO(erikkay): Possibly defer this operation to avoid slowing initial
458 // load of extensions.
[email protected]631cf822009-05-15 07:01:25459 UninstallExtension(extension_id);
[email protected]cc5da332009-03-04 08:02:51460
461 // No error needs to be reported. The extension effectively doesn't
462 // exist.
463 continue;
464 }
465
[email protected]894bb502009-05-21 22:39:57466 Extension* extension = LoadExtension(version_path, true); // require id
[email protected]0877fd92009-02-03 16:34:06467 if (extension)
468 extensions->push_back(extension);
[email protected]6014d672008-12-05 00:38:25469 }
470
[email protected]bf24d2c2009-02-24 23:07:45471 LOG(INFO) << "Done.";
[email protected]b0beaa662009-02-26 00:04:15472 ReportExtensionsLoaded(extensions.release());
[email protected]6014d672008-12-05 00:38:25473}
474
[email protected]b0beaa662009-02-26 00:04:15475void ExtensionsServiceBackend::LoadSingleExtension(
[email protected]894bb502009-05-21 22:39:57476 const FilePath& path_in, scoped_refptr<ExtensionsService> frontend) {
[email protected]b0beaa662009-02-26 00:04:15477 frontend_ = frontend;
478
479 // Explicit UI loads are always noisy.
480 alert_on_error_ = true;
481
[email protected]cc5da332009-03-04 08:02:51482 FilePath extension_path = path_in;
483 if (!file_util::AbsolutePath(&extension_path))
[email protected]0877fd92009-02-03 16:34:06484 NOTREACHED();
[email protected]bf24d2c2009-02-24 23:07:45485
486 LOG(INFO) << "Loading single extension from " <<
[email protected]cc5da332009-03-04 08:02:51487 WideToASCII(extension_path.BaseName().ToWStringHack());
[email protected]bf24d2c2009-02-24 23:07:45488
[email protected]5bfb1eb0a2009-04-08 18:33:30489 Extension* extension = LoadExtension(extension_path,
490 false); // don't require ID
[email protected]0877fd92009-02-03 16:34:06491 if (extension) {
[email protected]631cf822009-05-15 07:01:25492 extension->set_location(Extension::LOAD);
[email protected]0877fd92009-02-03 16:34:06493 ExtensionList* extensions = new ExtensionList;
494 extensions->push_back(extension);
[email protected]b0beaa662009-02-26 00:04:15495 ReportExtensionsLoaded(extensions);
[email protected]0877fd92009-02-03 16:34:06496 }
[email protected]0877fd92009-02-03 16:34:06497}
498
[email protected]cc5da332009-03-04 08:02:51499Extension* ExtensionsServiceBackend::LoadExtension(
[email protected]5bfb1eb0a2009-04-08 18:33:30500 const FilePath& extension_path, bool require_id) {
[email protected]0877fd92009-02-03 16:34:06501 FilePath manifest_path =
[email protected]cc5da332009-03-04 08:02:51502 extension_path.AppendASCII(Extension::kManifestFilename);
[email protected]0877fd92009-02-03 16:34:06503 if (!file_util::PathExists(manifest_path)) {
[email protected]cc5da332009-03-04 08:02:51504 ReportExtensionLoadError(extension_path, Extension::kInvalidManifestError);
[email protected]0877fd92009-02-03 16:34:06505 return NULL;
506 }
507
[email protected]1635bc12009-04-03 17:18:27508 JSONFileValueSerializer serializer(manifest_path);
[email protected]0877fd92009-02-03 16:34:06509 std::string error;
510 scoped_ptr<Value> root(serializer.Deserialize(&error));
511 if (!root.get()) {
[email protected]cc5da332009-03-04 08:02:51512 ReportExtensionLoadError(extension_path, error);
[email protected]0877fd92009-02-03 16:34:06513 return NULL;
514 }
515
516 if (!root->IsType(Value::TYPE_DICTIONARY)) {
[email protected]cc5da332009-03-04 08:02:51517 ReportExtensionLoadError(extension_path, Extension::kInvalidManifestError);
[email protected]0877fd92009-02-03 16:34:06518 return NULL;
519 }
520
[email protected]cc5da332009-03-04 08:02:51521 scoped_ptr<Extension> extension(new Extension(extension_path));
[email protected]0877fd92009-02-03 16:34:06522 if (!extension->InitFromValue(*static_cast<DictionaryValue*>(root.get()),
[email protected]5bfb1eb0a2009-04-08 18:33:30523 require_id, &error)) {
[email protected]cc5da332009-03-04 08:02:51524 ReportExtensionLoadError(extension_path, error);
[email protected]0877fd92009-02-03 16:34:06525 return NULL;
526 }
[email protected]8d6d9ff2009-02-20 08:14:39527
[email protected]631cf822009-05-15 07:01:25528 FilePath external_marker = extension_path.AppendASCII(kExternalInstallFile);
529 if (file_util::PathExists(external_marker))
530 extension->set_location(Extension::EXTERNAL);
531 else
532 extension->set_location(Extension::INTERNAL);
533
[email protected]6706963472009-05-09 02:19:19534 // TODO(glen): Add theme resource validation here. http://crbug.com/11678
[email protected]d24070e22009-05-21 19:26:59535 // Validate that claimed script resources actually exist.
[email protected]3cfbd0e2009-03-18 21:26:24536 for (size_t i = 0; i < extension->content_scripts().size(); ++i) {
537 const UserScript& script = extension->content_scripts()[i];
538
539 for (size_t j = 0; j < script.js_scripts().size(); j++) {
540 const FilePath& path = script.js_scripts()[j].path();
541 if (!file_util::PathExists(path)) {
542 ReportExtensionLoadError(extension_path,
[email protected]d24070e22009-05-21 19:26:59543 StringPrintf("Could not load '%s' for content script.",
544 WideToUTF8(path.ToWStringHack()).c_str()));
[email protected]3cfbd0e2009-03-18 21:26:24545 return NULL;
546 }
547 }
548
549 for (size_t j = 0; j < script.css_scripts().size(); j++) {
550 const FilePath& path = script.css_scripts()[j].path();
551 if (!file_util::PathExists(path)) {
552 ReportExtensionLoadError(extension_path,
[email protected]d24070e22009-05-21 19:26:59553 StringPrintf("Could not load '%s' for content script.",
554 WideToUTF8(path.ToWStringHack()).c_str()));
[email protected]3cfbd0e2009-03-18 21:26:24555 return NULL;
556 }
[email protected]8d6d9ff2009-02-20 08:14:39557 }
558 }
559
[email protected]d24070e22009-05-21 19:26:59560 // Validate icon location for page actions.
561 const PageActionMap& page_actions = extension->page_actions();
562 for (PageActionMap::const_iterator i(page_actions.begin());
563 i != page_actions.end(); ++i) {
564 PageAction* page_action = i->second;
565 FilePath path = page_action->icon_path();
566 if (!file_util::PathExists(path)) {
567 ReportExtensionLoadError(extension_path,
568 StringPrintf("Could not load icon '%s' for page action.",
569 WideToUTF8(path.ToWStringHack()).c_str()));
570 return NULL;
571 }
572 }
573
[email protected]0877fd92009-02-03 16:34:06574 return extension.release();
575}
576
[email protected]6014d672008-12-05 00:38:25577void ExtensionsServiceBackend::ReportExtensionLoadError(
[email protected]cc5da332009-03-04 08:02:51578 const FilePath& extension_path, const std::string &error) {
[email protected]b0beaa662009-02-26 00:04:15579 // TODO(port): note that this isn't guaranteed to work properly on Linux.
[email protected]cc5da332009-03-04 08:02:51580 std::string path_str = WideToASCII(extension_path.ToWStringHack());
[email protected]3acbd422008-12-08 18:25:00581 std::string message = StringPrintf("Could not load extension from '%s'. %s",
[email protected]cc655912009-01-29 23:19:19582 path_str.c_str(), error.c_str());
[email protected]bb28e062009-02-27 17:19:18583 ExtensionErrorReporter::GetInstance()->ReportError(message, alert_on_error_);
[email protected]6014d672008-12-05 00:38:25584}
585
586void ExtensionsServiceBackend::ReportExtensionsLoaded(
[email protected]b0beaa662009-02-26 00:04:15587 ExtensionList* extensions) {
[email protected]894bb502009-05-21 22:39:57588 frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(
589 frontend_, &ExtensionsService::OnExtensionsLoaded, extensions));
[email protected]6014d672008-12-05 00:38:25590}
[email protected]cc655912009-01-29 23:19:19591
[email protected]b0beaa662009-02-26 00:04:15592bool ExtensionsServiceBackend::ReadCurrentVersion(const FilePath& dir,
593 std::string* version_string) {
[email protected]18a12352009-01-31 01:33:28594 FilePath current_version =
[email protected]b0beaa662009-02-26 00:04:15595 dir.AppendASCII(ExtensionsService::kCurrentVersionFileName);
[email protected]18a12352009-01-31 01:33:28596 if (file_util::PathExists(current_version)) {
597 if (file_util::ReadFileToString(current_version, version_string)) {
598 TrimWhitespace(*version_string, TRIM_ALL, version_string);
599 return true;
600 }
601 }
602 return false;
603}
604
[email protected]cc655912009-01-29 23:19:19605bool ExtensionsServiceBackend::CheckCurrentVersion(
[email protected]b0beaa662009-02-26 00:04:15606 const std::string& new_version_str,
607 const std::string& current_version_str,
608 const FilePath& dest_dir) {
609 scoped_ptr<Version> current_version(
610 Version::GetVersionFromString(current_version_str));
611 scoped_ptr<Version> new_version(
612 Version::GetVersionFromString(new_version_str));
613 if (current_version->CompareTo(*new_version) >= 0) {
[email protected]b0beaa662009-02-26 00:04:15614 // Verify that the directory actually exists. If it doesn't we'll return
615 // true so that the install code will repair the broken installation.
616 // TODO(erikkay): A further step would be to verify that the extension
617 // has actually loaded successfully.
618 FilePath version_dir = dest_dir.AppendASCII(current_version_str);
619 if (file_util::PathExists(version_dir)) {
[email protected]4a190632009-05-09 01:07:42620 std::string id = WideToASCII(dest_dir.BaseName().ToWStringHack());
621 StringToLowerASCII(&id);
622 ReportExtensionVersionReinstalled(id);
[email protected]b0beaa662009-02-26 00:04:15623 return false;
[email protected]cc655912009-01-29 23:19:19624 }
625 }
626 return true;
627}
628
[email protected]f0a51fb52009-03-05 12:46:38629bool ExtensionsServiceBackend::InstallDirSafely(const FilePath& source_dir,
[email protected]b0beaa662009-02-26 00:04:15630 const FilePath& dest_dir) {
[email protected]cc655912009-01-29 23:19:19631 if (file_util::PathExists(dest_dir)) {
632 // By the time we get here, it should be safe to assume that this directory
633 // is not currently in use (it's not the current active version).
634 if (!file_util::Delete(dest_dir, true)) {
[email protected]cc5da332009-03-04 08:02:51635 ReportExtensionInstallError(source_dir,
[email protected]cc655912009-01-29 23:19:19636 "Can't delete existing version directory.");
637 return false;
638 }
639 } else {
640 FilePath parent = dest_dir.DirName();
641 if (!file_util::DirectoryExists(parent)) {
642 if (!file_util::CreateDirectory(parent)) {
[email protected]cc5da332009-03-04 08:02:51643 ReportExtensionInstallError(source_dir,
644 "Couldn't create extension directory.");
[email protected]cc655912009-01-29 23:19:19645 return false;
646 }
647 }
648 }
649 if (!file_util::Move(source_dir, dest_dir)) {
[email protected]cc5da332009-03-04 08:02:51650 ReportExtensionInstallError(source_dir,
651 "Couldn't move temporary directory.");
[email protected]cc655912009-01-29 23:19:19652 return false;
653 }
654
655 return true;
656}
657
[email protected]b0beaa662009-02-26 00:04:15658bool ExtensionsServiceBackend::SetCurrentVersion(const FilePath& dest_dir,
659 std::string version) {
[email protected]cc655912009-01-29 23:19:19660 // Write out the new CurrentVersion file.
661 // <profile>/Extension/<name>/CurrentVersion
662 FilePath current_version =
663 dest_dir.AppendASCII(ExtensionsService::kCurrentVersionFileName);
664 FilePath current_version_old =
665 current_version.InsertBeforeExtension(FILE_PATH_LITERAL("_old"));
666 if (file_util::PathExists(current_version_old)) {
667 if (!file_util::Delete(current_version_old, false)) {
[email protected]cc5da332009-03-04 08:02:51668 ReportExtensionInstallError(dest_dir,
669 "Couldn't remove CurrentVersion_old file.");
[email protected]cc655912009-01-29 23:19:19670 return false;
671 }
672 }
673 if (file_util::PathExists(current_version)) {
674 if (!file_util::Move(current_version, current_version_old)) {
[email protected]cc5da332009-03-04 08:02:51675 ReportExtensionInstallError(dest_dir,
676 "Couldn't move CurrentVersion file.");
[email protected]cc655912009-01-29 23:19:19677 return false;
678 }
679 }
680 net::FileStream stream;
681 int flags = base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_WRITE;
682 if (stream.Open(current_version, flags) != 0)
683 return false;
684 if (stream.Write(version.c_str(), version.size(), NULL) < 0) {
685 // Restore the old CurrentVersion.
686 if (file_util::PathExists(current_version_old)) {
687 if (!file_util::Move(current_version_old, current_version)) {
[email protected]f0a51fb52009-03-05 12:46:38688 LOG(WARNING) << "couldn't restore " << current_version_old.value() <<
[email protected]cc655912009-01-29 23:19:19689 " to " << current_version.value();
[email protected]b0beaa662009-02-26 00:04:15690
[email protected]cc655912009-01-29 23:19:19691 // TODO(erikkay): This is an ugly state to be in. Try harder?
692 }
693 }
[email protected]f0a51fb52009-03-05 12:46:38694 ReportExtensionInstallError(dest_dir,
[email protected]cc5da332009-03-04 08:02:51695 "Couldn't create CurrentVersion file.");
[email protected]cc655912009-01-29 23:19:19696 return false;
697 }
698 return true;
699}
700
[email protected]b0beaa662009-02-26 00:04:15701void ExtensionsServiceBackend::InstallExtension(
[email protected]894bb502009-05-21 22:39:57702 const FilePath& extension_path, scoped_refptr<ExtensionsService> frontend) {
[email protected]cc655912009-01-29 23:19:19703 LOG(INFO) << "Installing extension " << extension_path.value();
704
[email protected]b0beaa662009-02-26 00:04:15705 frontend_ = frontend;
[email protected]cc5da332009-03-04 08:02:51706 alert_on_error_ = false;
[email protected]b0beaa662009-02-26 00:04:15707
[email protected]1fca1492009-05-15 22:23:43708 bool from_external = false;
709 InstallOrUpdateExtension(extension_path, std::string(), from_external);
[email protected]b0beaa662009-02-26 00:04:15710}
711
[email protected]1fca1492009-05-15 22:23:43712void ExtensionsServiceBackend::InstallOrUpdateExtension(
713 const FilePath& extension_path, const std::string& expected_id,
714 bool from_external) {
715 UnpackerClient* client =
716 new UnpackerClient(this, extension_path, expected_id, from_external);
717 client->Start();
718}
[email protected]cc655912009-01-29 23:19:19719
[email protected]1fca1492009-05-15 22:23:43720void ExtensionsServiceBackend::OnExtensionUnpacked(
721 const FilePath& extension_path,
722 const FilePath& temp_extension_dir,
723 const std::string expected_id,
[email protected]902f7cd2009-05-22 19:02:19724 bool from_external,
725 const DictionaryValue& manifest,
726 const std::vector< Tuple2<SkBitmap, FilePath> >& images) {
[email protected]1fca1492009-05-15 22:23:43727 Extension extension;
728 std::string error;
[email protected]902f7cd2009-05-22 19:02:19729 if (!extension.InitFromValue(manifest,
[email protected]1fca1492009-05-15 22:23:43730 true, // require ID
731 &error)) {
732 ReportExtensionInstallError(extension_path, "Invalid extension manifest.");
733 return;
[email protected]0b344962009-03-31 04:21:45734 }
735
[email protected]e2eb43112009-05-29 21:19:54736 if (!frontend_->extensions_enabled() && !extension.IsTheme()) {
737#if defined(OS_WIN)
738 if (frontend_->show_extensions_disabled_notification()) {
739 win_util::MessageBox(GetActiveWindow(),
740 L"Extensions are not enabled. Add --enable-extensions to the "
741 L"command-line to enable extensions.\n\n"
742 L"This is a temporary message and it will be removed when extensions "
743 L"UI is finalized.",
744 l10n_util::GetString(IDS_PRODUCT_NAME).c_str(), MB_OK);
745 }
746#endif
747 ReportExtensionInstallError(extension_path,
748 "Extensions are not enabled.");
749 return;
750 }
751
[email protected]b0beaa662009-02-26 00:04:15752 // If an expected id was provided, make sure it matches.
[email protected]1fca1492009-05-15 22:23:43753 if (!expected_id.empty() && expected_id != extension.id()) {
754 ReportExtensionInstallError(extension_path,
[email protected]b0beaa662009-02-26 00:04:15755 "ID in new extension manifest does not match expected ID.");
[email protected]1fca1492009-05-15 22:23:43756 return;
[email protected]cc655912009-01-29 23:19:19757 }
758
759 // <profile>/Extensions/<id>
[email protected]b0beaa662009-02-26 00:04:15760 FilePath dest_dir = install_directory_.AppendASCII(extension.id());
[email protected]cc655912009-01-29 23:19:19761 std::string version = extension.VersionString();
[email protected]b0beaa662009-02-26 00:04:15762 std::string current_version;
[email protected]1fca1492009-05-15 22:23:43763 bool was_update = false;
[email protected]b0beaa662009-02-26 00:04:15764 if (ReadCurrentVersion(dest_dir, &current_version)) {
765 if (!CheckCurrentVersion(version, current_version, dest_dir))
[email protected]1fca1492009-05-15 22:23:43766 return;
767 was_update = true;
[email protected]b0beaa662009-02-26 00:04:15768 }
[email protected]cc655912009-01-29 23:19:19769
[email protected]902f7cd2009-05-22 19:02:19770 // Write our parsed manifest back to disk, to ensure it doesn't contain an
771 // exploitable bug that can be used to compromise the browser.
772 std::string manifest_json;
773 JSONStringValueSerializer serializer(&manifest_json);
774 serializer.set_pretty_print(true);
775 if (!serializer.Serialize(manifest)) {
776 ReportExtensionInstallError(extension_path,
777 "Error serializing manifest.json.");
778 return;
779 }
780
781 FilePath manifest_path =
782 temp_extension_dir.AppendASCII(Extension::kManifestFilename);
783 if (!file_util::WriteFile(manifest_path,
784 manifest_json.data(), manifest_json.size())) {
785 ReportExtensionInstallError(extension_path, "Error saving manifest.json.");
786 return;
787 }
788
789 // Write our parsed images back to disk as well.
790 for (size_t i = 0; i < images.size(); ++i) {
791 const SkBitmap& image = images[i].a;
792 FilePath path = temp_extension_dir.Append(images[i].b);
793
794 std::vector<unsigned char> image_data;
795 // TODO(mpcomplete): It's lame that we're encoding all images as PNG, even
796 // though they may originally be .jpg, etc. Figure something out.
797 // http://code.google.com/p/chromium/issues/detail?id=12459
798 if (!PNGEncoder::EncodeBGRASkBitmap(image, false, &image_data)) {
799 ReportExtensionInstallError(extension_path,
800 "Error re-encoding theme image.");
801 return;
802 }
803
804 // Note: we're overwriting existing files that the utility process wrote,
805 // so we can be sure the directory exists.
806 const char* image_data_ptr = reinterpret_cast<const char*>(&image_data[0]);
807 if (!file_util::WriteFile(path, image_data_ptr, image_data.size())) {
808 ReportExtensionInstallError(extension_path, "Error saving theme image.");
809 return;
810 }
811 }
812
[email protected]cc655912009-01-29 23:19:19813 // <profile>/Extensions/<dir_name>/<version>
[email protected]1fca1492009-05-15 22:23:43814 FilePath version_dir = dest_dir.AppendASCII(version);
[email protected]902f7cd2009-05-22 19:02:19815
816 // If anything fails after this, we want to delete the extension dir.
817 ScopedTempDir scoped_version_dir;
818 scoped_version_dir.Set(version_dir);
819
[email protected]1fca1492009-05-15 22:23:43820 if (!InstallDirSafely(temp_extension_dir, version_dir))
821 return;
[email protected]cc655912009-01-29 23:19:19822
[email protected]902f7cd2009-05-22 19:02:19823 if (!SetCurrentVersion(dest_dir, version))
[email protected]1fca1492009-05-15 22:23:43824 return;
[email protected]cc655912009-01-29 23:19:19825
[email protected]1fca1492009-05-15 22:23:43826 // To mark that this extension was installed from an external source, create a
827 // zero-length file. At load time, this is used to indicate that the
828 // extension should be uninstalled.
829 // TODO(erikkay): move this into per-extension config storage when it appears.
830 if (from_external) {
831 FilePath marker = version_dir.AppendASCII(kExternalInstallFile);
832 file_util::WriteFile(marker, NULL, 0);
833 }
834
[email protected]894bb502009-05-21 22:39:57835 // Load the extension immediately and then report installation success. We
836 // don't load extensions for external installs because external installation
837 // occurs before the normal startup so we just let startup pick them up. We
[email protected]902f7cd2009-05-22 19:02:19838 // don't notify installation because there is no UI for external install so
[email protected]894bb502009-05-21 22:39:57839 // there is nobody to notify.
840 if (!from_external) {
841 Extension* extension = LoadExtension(version_dir, true); // require id
842 CHECK(extension);
843
844 frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(
845 frontend_, &ExtensionsService::OnExtensionInstalled, extension,
846 was_update));
847
848 // Only one extension, but ReportExtensionsLoaded can handle multiple,
849 // so we need to construct a list.
850 scoped_ptr<ExtensionList> extensions(new ExtensionList);
851 extensions->push_back(extension);
852 LOG(INFO) << "Done.";
853 // Hand off ownership of the loaded extensions to the frontend.
854 ReportExtensionsLoaded(extensions.release());
855 }
[email protected]902f7cd2009-05-22 19:02:19856
857 scoped_version_dir.Take();
[email protected]cc655912009-01-29 23:19:19858}
859
860void ExtensionsServiceBackend::ReportExtensionInstallError(
[email protected]cc5da332009-03-04 08:02:51861 const FilePath& extension_path, const std::string &error) {
[email protected]b0beaa662009-02-26 00:04:15862
[email protected]cc655912009-01-29 23:19:19863 // TODO(erikkay): note that this isn't guaranteed to work properly on Linux.
[email protected]cc5da332009-03-04 08:02:51864 std::string path_str = WideToASCII(extension_path.ToWStringHack());
[email protected]cc655912009-01-29 23:19:19865 std::string message =
866 StringPrintf("Could not install extension from '%s'. %s",
867 path_str.c_str(), error.c_str());
[email protected]bb28e062009-02-27 17:19:18868 ExtensionErrorReporter::GetInstance()->ReportError(message, alert_on_error_);
[email protected]cc655912009-01-29 23:19:19869}
870
[email protected]4a190632009-05-09 01:07:42871void ExtensionsServiceBackend::ReportExtensionVersionReinstalled(
872 const std::string& id) {
[email protected]894bb502009-05-21 22:39:57873 frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(
874 frontend_, &ExtensionsService::OnExtensionVersionReinstalled, id));
[email protected]b0beaa662009-02-26 00:04:15875}
876
877// Some extensions will autoupdate themselves externally from Chrome. These
878// are typically part of some larger client application package. To support
879// these, the extension will register its location in the registry on Windows
880// (TODO(port): what about on other platforms?) and this code will periodically
881// check that location for a .crx file, which it will then install locally if
882// a new version is available.
883void ExtensionsServiceBackend::CheckForExternalUpdates(
[email protected]894bb502009-05-21 22:39:57884 std::set<std::string> ids_to_ignore,
885 scoped_refptr<ExtensionsService> frontend) {
[email protected]b0beaa662009-02-26 00:04:15886
887 // Note that this installation is intentionally silent (since it didn't
888 // go through the front-end). Extensions that are registered in this
889 // way are effectively considered 'pre-bundled', and so implicitly
890 // trusted. In general, if something has HKLM or filesystem access,
891 // they could install an extension manually themselves anyway.
892 alert_on_error_ = false;
893 frontend_ = frontend;
[email protected]b0beaa662009-02-26 00:04:15894
895#if defined(OS_WIN)
[email protected]631cf822009-05-15 07:01:25896 // TODO(port): Pull this out into an interface. That will also allow us to
897 // test the behavior of external extensions.
[email protected]b0beaa662009-02-26 00:04:15898 HKEY reg_root = HKEY_LOCAL_MACHINE;
[email protected]894bb502009-05-21 22:39:57899 RegistryKeyIterator iterator(reg_root, ASCIIToWide(registry_path_).c_str());
[email protected]b0beaa662009-02-26 00:04:15900 while (iterator.Valid()) {
[email protected]e2eb43112009-05-29 21:19:54901 // Fold
[email protected]894bb502009-05-21 22:39:57902 std::string id = StringToLowerASCII(WideToASCII(iterator.Name()));
903 if (ids_to_ignore.find(id) != ids_to_ignore.end()) {
904 LOG(INFO) << "Skipping uninstalled external extension " << id;
905 ++iterator;
906 continue;
907 }
908
[email protected]b0beaa662009-02-26 00:04:15909 RegKey key;
[email protected]894bb502009-05-21 22:39:57910 std::wstring key_path = ASCIIToWide(registry_path_);
[email protected]b0beaa662009-02-26 00:04:15911 key_path.append(L"\\");
912 key_path.append(iterator.Name());
913 if (key.Open(reg_root, key_path.c_str())) {
914 std::wstring extension_path;
915 if (key.ReadValue(kRegistryExtensionPath, &extension_path)) {
[email protected]b0beaa662009-02-26 00:04:15916 std::wstring extension_version;
917 if (key.ReadValue(kRegistryExtensionVersion, &extension_version)) {
[email protected]cc5da332009-03-04 08:02:51918 if (ShouldInstall(id, WideToASCII(extension_version))) {
[email protected]1fca1492009-05-15 22:23:43919 bool from_external = true;
920 InstallOrUpdateExtension(FilePath(extension_path), id,
921 from_external);
[email protected]cc5da332009-03-04 08:02:51922 }
[email protected]b0beaa662009-02-26 00:04:15923 } else {
[email protected]b0beaa662009-02-26 00:04:15924 // TODO(erikkay): find a way to get this into about:extensions
925 LOG(WARNING) << "Missing value " << kRegistryExtensionVersion <<
926 " for key " << key_path;
927 }
928 } else {
[email protected]b0beaa662009-02-26 00:04:15929 // TODO(erikkay): find a way to get this into about:extensions
930 LOG(WARNING) << "Missing value " << kRegistryExtensionPath <<
931 " for key " << key_path;
932 }
933 }
934 ++iterator;
935 }
936#else
937 NOTREACHED();
938#endif
939}
940
[email protected]894bb502009-05-21 22:39:57941bool ExtensionsServiceBackend::CheckExternalUninstall(const FilePath& version_path,
[email protected]b0beaa662009-02-26 00:04:15942 const std::string& id) {
[email protected]894bb502009-05-21 22:39:57943 FilePath external_file = version_path.AppendASCII(kExternalInstallFile);
[email protected]b0beaa662009-02-26 00:04:15944 if (file_util::PathExists(external_file)) {
945#if defined(OS_WIN)
946 HKEY reg_root = HKEY_LOCAL_MACHINE;
947 RegKey key;
[email protected]894bb502009-05-21 22:39:57948 std::wstring key_path = ASCIIToWide(registry_path_);
[email protected]b0beaa662009-02-26 00:04:15949 key_path.append(L"\\");
950 key_path.append(ASCIIToWide(id));
951
952 // If the key doesn't exist, then we should uninstall.
953 return !key.Open(reg_root, key_path.c_str());
954#else
[email protected]894bb502009-05-21 22:39:57955 // TODO(port)
[email protected]b0beaa662009-02-26 00:04:15956#endif
957 }
958 return false;
959}
960
961// Assumes that the extension isn't currently loaded or in use.
[email protected]631cf822009-05-15 07:01:25962void ExtensionsServiceBackend::UninstallExtension(
963 const std::string& extension_id) {
964 // First, delete the Current Version file. If the directory delete fails, then
965 // at least the extension won't be loaded again.
966 FilePath extension_directory = install_directory_.AppendASCII(extension_id);
967
968 if (!file_util::PathExists(extension_directory)) {
969 LOG(WARNING) << "Asked to remove a non-existent extension " << extension_id;
[email protected]b0beaa662009-02-26 00:04:15970 return;
971 }
[email protected]631cf822009-05-15 07:01:25972
973 FilePath current_version_file = extension_directory.AppendASCII(
974 ExtensionsService::kCurrentVersionFileName);
975 if (!file_util::PathExists(current_version_file)) {
976 LOG(WARNING) << "Extension " << extension_id
977 << " does not have a Current Version file.";
978 } else {
979 if (!file_util::Delete(current_version_file, false)) {
980 LOG(WARNING) << "Could not delete Current Version file for extension "
981 << extension_id;
982 return;
983 }
984 }
985
[email protected]894bb502009-05-21 22:39:57986 // Ok, now try and delete the entire rest of the directory. One major place
987 // this can fail is if the extension contains a plugin (stupid plugins). It's
988 // not a big deal though, because we'll notice next time we startup that the
989 // Current Version file is gone and finish the delete then.
[email protected]631cf822009-05-15 07:01:25990 if (!file_util::Delete(extension_directory, true)) {
991 LOG(WARNING) << "Could not delete directory for extension "
992 << extension_id;
[email protected]b0beaa662009-02-26 00:04:15993 }
994}
995
996bool ExtensionsServiceBackend::ShouldInstall(const std::string& id,
997 const std::string& version) {
998 FilePath dir(install_directory_.AppendASCII(id.c_str()));
999 std::string current_version;
1000 if (ReadCurrentVersion(dir, &current_version)) {
[email protected]894bb502009-05-21 22:39:571001 return CheckCurrentVersion(version, current_version, dir);
[email protected]b0beaa662009-02-26 00:04:151002 }
1003 return true;
[email protected]cc655912009-01-29 23:19:191004}