blob: e5d365ea959bd1022a90042e57b8bb6a6f39f54d [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)
380 : install_directory_(install_directory),
381 resource_dispatcher_host_(rdh),
382 frontend_loop_(frontend_loop),
383 registry_path_(registry_path) {
384 // Default the registry path if unspecified.
385 if (registry_path_.empty()) {
386 registry_path_ = kRegistryExtensions;
387 }
388}
389
[email protected]cc5da332009-03-04 08:02:51390void ExtensionsServiceBackend::LoadExtensionsFromInstallDirectory(
[email protected]894bb502009-05-21 22:39:57391 scoped_refptr<ExtensionsService> frontend) {
[email protected]b0beaa662009-02-26 00:04:15392 frontend_ = frontend;
393 alert_on_error_ = false;
[email protected]05eb0fa2009-02-21 00:05:48394
[email protected]540f91b2009-03-26 19:37:43395#if defined(OS_WIN)
396 // On POSIX, AbsolutePath() calls realpath() which returns NULL if
397 // it does not exist. Instead we absolute-ify after creation in
398 // case that is needed.
399 // TODO(port): does this really need to happen before
400 // CreateDirectory() on Windows?
[email protected]cc5da332009-03-04 08:02:51401 if (!file_util::AbsolutePath(&install_directory_))
[email protected]eab9b452009-01-23 20:48:59402 NOTREACHED();
[email protected]540f91b2009-03-26 19:37:43403#endif
[email protected]eab9b452009-01-23 20:48:59404
[email protected]b0beaa662009-02-26 00:04:15405 scoped_ptr<ExtensionList> extensions(new ExtensionList);
406
407 // Create the <Profile>/Extensions directory if it doesn't exist.
[email protected]cc5da332009-03-04 08:02:51408 if (!file_util::DirectoryExists(install_directory_)) {
409 file_util::CreateDirectory(install_directory_);
[email protected]b0beaa662009-02-26 00:04:15410 LOG(INFO) << "Created Extensions directory. No extensions to install.";
411 ReportExtensionsLoaded(extensions.release());
412 return;
413 }
414
[email protected]540f91b2009-03-26 19:37:43415#if !defined(OS_WIN)
416 if (!file_util::AbsolutePath(&install_directory_))
417 NOTREACHED();
418#endif
419
[email protected]bf24d2c2009-02-24 23:07:45420 LOG(INFO) << "Loading installed extensions...";
421
[email protected]6014d672008-12-05 00:38:25422 // Find all child directories in the install directory and load their
423 // manifests. Post errors and results to the frontend.
[email protected]cc5da332009-03-04 08:02:51424 file_util::FileEnumerator enumerator(install_directory_,
[email protected]cc655912009-01-29 23:19:19425 false, // not recursive
[email protected]6014d672008-12-05 00:38:25426 file_util::FileEnumerator::DIRECTORIES);
[email protected]cc5da332009-03-04 08:02:51427 FilePath extension_path;
428 for (extension_path = enumerator.Next(); !extension_path.value().empty();
429 extension_path = enumerator.Next()) {
430 std::string extension_id = WideToASCII(
431 extension_path.BaseName().ToWStringHack());
[email protected]96088fb2009-05-26 19:08:13432 // The utility process might be in the middle of unpacking an extension, so
433 // ignore the temp unpacking directory.
434 if (extension_id == kUnpackExtensionDir)
435 continue;
[email protected]894bb502009-05-21 22:39:57436
437 // If there is no Current Version file, just delete the directory and move
438 // on. This can legitimately happen when an uninstall does not complete, for
439 // example, when a plugin is in use at uninstall time.
440 FilePath current_version_path = extension_path.AppendASCII(
441 ExtensionsService::kCurrentVersionFileName);
442 if (!file_util::PathExists(current_version_path)) {
443 LOG(INFO) << "Deleting incomplete install for directory "
444 << WideToASCII(extension_path.ToWStringHack()) << ".";
445 file_util::Delete(extension_path, true); // recursive;
446 continue;
447 }
448
449 std::string current_version;
450 if (!ReadCurrentVersion(extension_path, &current_version))
451 continue;
452
453 FilePath version_path = extension_path.AppendASCII(current_version);
454 if (CheckExternalUninstall(version_path, extension_id)) {
[email protected]cc5da332009-03-04 08:02:51455 // TODO(erikkay): Possibly defer this operation to avoid slowing initial
456 // load of extensions.
[email protected]631cf822009-05-15 07:01:25457 UninstallExtension(extension_id);
[email protected]cc5da332009-03-04 08:02:51458
459 // No error needs to be reported. The extension effectively doesn't
460 // exist.
461 continue;
462 }
463
[email protected]894bb502009-05-21 22:39:57464 Extension* extension = LoadExtension(version_path, true); // require id
[email protected]0877fd92009-02-03 16:34:06465 if (extension)
466 extensions->push_back(extension);
[email protected]6014d672008-12-05 00:38:25467 }
468
[email protected]bf24d2c2009-02-24 23:07:45469 LOG(INFO) << "Done.";
[email protected]b0beaa662009-02-26 00:04:15470 ReportExtensionsLoaded(extensions.release());
[email protected]6014d672008-12-05 00:38:25471}
472
[email protected]b0beaa662009-02-26 00:04:15473void ExtensionsServiceBackend::LoadSingleExtension(
[email protected]894bb502009-05-21 22:39:57474 const FilePath& path_in, scoped_refptr<ExtensionsService> frontend) {
[email protected]b0beaa662009-02-26 00:04:15475 frontend_ = frontend;
476
477 // Explicit UI loads are always noisy.
478 alert_on_error_ = true;
479
[email protected]cc5da332009-03-04 08:02:51480 FilePath extension_path = path_in;
481 if (!file_util::AbsolutePath(&extension_path))
[email protected]0877fd92009-02-03 16:34:06482 NOTREACHED();
[email protected]bf24d2c2009-02-24 23:07:45483
484 LOG(INFO) << "Loading single extension from " <<
[email protected]cc5da332009-03-04 08:02:51485 WideToASCII(extension_path.BaseName().ToWStringHack());
[email protected]bf24d2c2009-02-24 23:07:45486
[email protected]5bfb1eb0a2009-04-08 18:33:30487 Extension* extension = LoadExtension(extension_path,
488 false); // don't require ID
[email protected]0877fd92009-02-03 16:34:06489 if (extension) {
[email protected]631cf822009-05-15 07:01:25490 extension->set_location(Extension::LOAD);
[email protected]0877fd92009-02-03 16:34:06491 ExtensionList* extensions = new ExtensionList;
492 extensions->push_back(extension);
[email protected]b0beaa662009-02-26 00:04:15493 ReportExtensionsLoaded(extensions);
[email protected]0877fd92009-02-03 16:34:06494 }
[email protected]0877fd92009-02-03 16:34:06495}
496
[email protected]cc5da332009-03-04 08:02:51497Extension* ExtensionsServiceBackend::LoadExtension(
[email protected]5bfb1eb0a2009-04-08 18:33:30498 const FilePath& extension_path, bool require_id) {
[email protected]0877fd92009-02-03 16:34:06499 FilePath manifest_path =
[email protected]cc5da332009-03-04 08:02:51500 extension_path.AppendASCII(Extension::kManifestFilename);
[email protected]0877fd92009-02-03 16:34:06501 if (!file_util::PathExists(manifest_path)) {
[email protected]cc5da332009-03-04 08:02:51502 ReportExtensionLoadError(extension_path, Extension::kInvalidManifestError);
[email protected]0877fd92009-02-03 16:34:06503 return NULL;
504 }
505
[email protected]1635bc12009-04-03 17:18:27506 JSONFileValueSerializer serializer(manifest_path);
[email protected]0877fd92009-02-03 16:34:06507 std::string error;
508 scoped_ptr<Value> root(serializer.Deserialize(&error));
509 if (!root.get()) {
[email protected]cc5da332009-03-04 08:02:51510 ReportExtensionLoadError(extension_path, error);
[email protected]0877fd92009-02-03 16:34:06511 return NULL;
512 }
513
514 if (!root->IsType(Value::TYPE_DICTIONARY)) {
[email protected]cc5da332009-03-04 08:02:51515 ReportExtensionLoadError(extension_path, Extension::kInvalidManifestError);
[email protected]0877fd92009-02-03 16:34:06516 return NULL;
517 }
518
[email protected]cc5da332009-03-04 08:02:51519 scoped_ptr<Extension> extension(new Extension(extension_path));
[email protected]0877fd92009-02-03 16:34:06520 if (!extension->InitFromValue(*static_cast<DictionaryValue*>(root.get()),
[email protected]5bfb1eb0a2009-04-08 18:33:30521 require_id, &error)) {
[email protected]cc5da332009-03-04 08:02:51522 ReportExtensionLoadError(extension_path, error);
[email protected]0877fd92009-02-03 16:34:06523 return NULL;
524 }
[email protected]8d6d9ff2009-02-20 08:14:39525
[email protected]631cf822009-05-15 07:01:25526 FilePath external_marker = extension_path.AppendASCII(kExternalInstallFile);
527 if (file_util::PathExists(external_marker))
528 extension->set_location(Extension::EXTERNAL);
529 else
530 extension->set_location(Extension::INTERNAL);
531
[email protected]6706963472009-05-09 02:19:19532 // TODO(glen): Add theme resource validation here. http://crbug.com/11678
[email protected]d24070e22009-05-21 19:26:59533 // Validate that claimed script resources actually exist.
[email protected]3cfbd0e2009-03-18 21:26:24534 for (size_t i = 0; i < extension->content_scripts().size(); ++i) {
535 const UserScript& script = extension->content_scripts()[i];
536
537 for (size_t j = 0; j < script.js_scripts().size(); j++) {
538 const FilePath& path = script.js_scripts()[j].path();
539 if (!file_util::PathExists(path)) {
540 ReportExtensionLoadError(extension_path,
[email protected]d24070e22009-05-21 19:26:59541 StringPrintf("Could not load '%s' for content script.",
542 WideToUTF8(path.ToWStringHack()).c_str()));
[email protected]3cfbd0e2009-03-18 21:26:24543 return NULL;
544 }
545 }
546
547 for (size_t j = 0; j < script.css_scripts().size(); j++) {
548 const FilePath& path = script.css_scripts()[j].path();
549 if (!file_util::PathExists(path)) {
550 ReportExtensionLoadError(extension_path,
[email protected]d24070e22009-05-21 19:26:59551 StringPrintf("Could not load '%s' for content script.",
552 WideToUTF8(path.ToWStringHack()).c_str()));
[email protected]3cfbd0e2009-03-18 21:26:24553 return NULL;
554 }
[email protected]8d6d9ff2009-02-20 08:14:39555 }
556 }
557
[email protected]d24070e22009-05-21 19:26:59558 // Validate icon location for page actions.
559 const PageActionMap& page_actions = extension->page_actions();
560 for (PageActionMap::const_iterator i(page_actions.begin());
561 i != page_actions.end(); ++i) {
562 PageAction* page_action = i->second;
563 FilePath path = page_action->icon_path();
564 if (!file_util::PathExists(path)) {
565 ReportExtensionLoadError(extension_path,
566 StringPrintf("Could not load icon '%s' for page action.",
567 WideToUTF8(path.ToWStringHack()).c_str()));
568 return NULL;
569 }
570 }
571
[email protected]0877fd92009-02-03 16:34:06572 return extension.release();
573}
574
[email protected]6014d672008-12-05 00:38:25575void ExtensionsServiceBackend::ReportExtensionLoadError(
[email protected]cc5da332009-03-04 08:02:51576 const FilePath& extension_path, const std::string &error) {
[email protected]b0beaa662009-02-26 00:04:15577 // TODO(port): note that this isn't guaranteed to work properly on Linux.
[email protected]cc5da332009-03-04 08:02:51578 std::string path_str = WideToASCII(extension_path.ToWStringHack());
[email protected]3acbd422008-12-08 18:25:00579 std::string message = StringPrintf("Could not load extension from '%s'. %s",
[email protected]cc655912009-01-29 23:19:19580 path_str.c_str(), error.c_str());
[email protected]bb28e062009-02-27 17:19:18581 ExtensionErrorReporter::GetInstance()->ReportError(message, alert_on_error_);
[email protected]6014d672008-12-05 00:38:25582}
583
584void ExtensionsServiceBackend::ReportExtensionsLoaded(
[email protected]b0beaa662009-02-26 00:04:15585 ExtensionList* extensions) {
[email protected]894bb502009-05-21 22:39:57586 frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(
587 frontend_, &ExtensionsService::OnExtensionsLoaded, extensions));
[email protected]6014d672008-12-05 00:38:25588}
[email protected]cc655912009-01-29 23:19:19589
[email protected]b0beaa662009-02-26 00:04:15590bool ExtensionsServiceBackend::ReadCurrentVersion(const FilePath& dir,
591 std::string* version_string) {
[email protected]18a12352009-01-31 01:33:28592 FilePath current_version =
[email protected]b0beaa662009-02-26 00:04:15593 dir.AppendASCII(ExtensionsService::kCurrentVersionFileName);
[email protected]18a12352009-01-31 01:33:28594 if (file_util::PathExists(current_version)) {
595 if (file_util::ReadFileToString(current_version, version_string)) {
596 TrimWhitespace(*version_string, TRIM_ALL, version_string);
597 return true;
598 }
599 }
600 return false;
601}
602
[email protected]cc655912009-01-29 23:19:19603bool ExtensionsServiceBackend::CheckCurrentVersion(
[email protected]b0beaa662009-02-26 00:04:15604 const std::string& new_version_str,
605 const std::string& current_version_str,
606 const FilePath& dest_dir) {
607 scoped_ptr<Version> current_version(
608 Version::GetVersionFromString(current_version_str));
609 scoped_ptr<Version> new_version(
610 Version::GetVersionFromString(new_version_str));
611 if (current_version->CompareTo(*new_version) >= 0) {
[email protected]b0beaa662009-02-26 00:04:15612 // Verify that the directory actually exists. If it doesn't we'll return
613 // true so that the install code will repair the broken installation.
614 // TODO(erikkay): A further step would be to verify that the extension
615 // has actually loaded successfully.
616 FilePath version_dir = dest_dir.AppendASCII(current_version_str);
617 if (file_util::PathExists(version_dir)) {
[email protected]4a190632009-05-09 01:07:42618 std::string id = WideToASCII(dest_dir.BaseName().ToWStringHack());
619 StringToLowerASCII(&id);
620 ReportExtensionVersionReinstalled(id);
[email protected]b0beaa662009-02-26 00:04:15621 return false;
[email protected]cc655912009-01-29 23:19:19622 }
623 }
624 return true;
625}
626
[email protected]f0a51fb52009-03-05 12:46:38627bool ExtensionsServiceBackend::InstallDirSafely(const FilePath& source_dir,
[email protected]b0beaa662009-02-26 00:04:15628 const FilePath& dest_dir) {
[email protected]cc655912009-01-29 23:19:19629 if (file_util::PathExists(dest_dir)) {
630 // By the time we get here, it should be safe to assume that this directory
631 // is not currently in use (it's not the current active version).
632 if (!file_util::Delete(dest_dir, true)) {
[email protected]cc5da332009-03-04 08:02:51633 ReportExtensionInstallError(source_dir,
[email protected]cc655912009-01-29 23:19:19634 "Can't delete existing version directory.");
635 return false;
636 }
637 } else {
638 FilePath parent = dest_dir.DirName();
639 if (!file_util::DirectoryExists(parent)) {
640 if (!file_util::CreateDirectory(parent)) {
[email protected]cc5da332009-03-04 08:02:51641 ReportExtensionInstallError(source_dir,
642 "Couldn't create extension directory.");
[email protected]cc655912009-01-29 23:19:19643 return false;
644 }
645 }
646 }
647 if (!file_util::Move(source_dir, dest_dir)) {
[email protected]cc5da332009-03-04 08:02:51648 ReportExtensionInstallError(source_dir,
649 "Couldn't move temporary directory.");
[email protected]cc655912009-01-29 23:19:19650 return false;
651 }
652
653 return true;
654}
655
[email protected]b0beaa662009-02-26 00:04:15656bool ExtensionsServiceBackend::SetCurrentVersion(const FilePath& dest_dir,
657 std::string version) {
[email protected]cc655912009-01-29 23:19:19658 // Write out the new CurrentVersion file.
659 // <profile>/Extension/<name>/CurrentVersion
660 FilePath current_version =
661 dest_dir.AppendASCII(ExtensionsService::kCurrentVersionFileName);
662 FilePath current_version_old =
663 current_version.InsertBeforeExtension(FILE_PATH_LITERAL("_old"));
664 if (file_util::PathExists(current_version_old)) {
665 if (!file_util::Delete(current_version_old, false)) {
[email protected]cc5da332009-03-04 08:02:51666 ReportExtensionInstallError(dest_dir,
667 "Couldn't remove CurrentVersion_old file.");
[email protected]cc655912009-01-29 23:19:19668 return false;
669 }
670 }
671 if (file_util::PathExists(current_version)) {
672 if (!file_util::Move(current_version, current_version_old)) {
[email protected]cc5da332009-03-04 08:02:51673 ReportExtensionInstallError(dest_dir,
674 "Couldn't move CurrentVersion file.");
[email protected]cc655912009-01-29 23:19:19675 return false;
676 }
677 }
678 net::FileStream stream;
679 int flags = base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_WRITE;
680 if (stream.Open(current_version, flags) != 0)
681 return false;
682 if (stream.Write(version.c_str(), version.size(), NULL) < 0) {
683 // Restore the old CurrentVersion.
684 if (file_util::PathExists(current_version_old)) {
685 if (!file_util::Move(current_version_old, current_version)) {
[email protected]f0a51fb52009-03-05 12:46:38686 LOG(WARNING) << "couldn't restore " << current_version_old.value() <<
[email protected]cc655912009-01-29 23:19:19687 " to " << current_version.value();
[email protected]b0beaa662009-02-26 00:04:15688
[email protected]cc655912009-01-29 23:19:19689 // TODO(erikkay): This is an ugly state to be in. Try harder?
690 }
691 }
[email protected]f0a51fb52009-03-05 12:46:38692 ReportExtensionInstallError(dest_dir,
[email protected]cc5da332009-03-04 08:02:51693 "Couldn't create CurrentVersion file.");
[email protected]cc655912009-01-29 23:19:19694 return false;
695 }
696 return true;
697}
698
[email protected]b0beaa662009-02-26 00:04:15699void ExtensionsServiceBackend::InstallExtension(
[email protected]894bb502009-05-21 22:39:57700 const FilePath& extension_path, scoped_refptr<ExtensionsService> frontend) {
[email protected]cc655912009-01-29 23:19:19701 LOG(INFO) << "Installing extension " << extension_path.value();
702
[email protected]b0beaa662009-02-26 00:04:15703 frontend_ = frontend;
[email protected]cc5da332009-03-04 08:02:51704 alert_on_error_ = false;
[email protected]b0beaa662009-02-26 00:04:15705
[email protected]1fca1492009-05-15 22:23:43706 bool from_external = false;
707 InstallOrUpdateExtension(extension_path, std::string(), from_external);
[email protected]b0beaa662009-02-26 00:04:15708}
709
[email protected]1fca1492009-05-15 22:23:43710void ExtensionsServiceBackend::InstallOrUpdateExtension(
711 const FilePath& extension_path, const std::string& expected_id,
712 bool from_external) {
713 UnpackerClient* client =
714 new UnpackerClient(this, extension_path, expected_id, from_external);
715 client->Start();
716}
[email protected]cc655912009-01-29 23:19:19717
[email protected]1fca1492009-05-15 22:23:43718void ExtensionsServiceBackend::OnExtensionUnpacked(
719 const FilePath& extension_path,
720 const FilePath& temp_extension_dir,
721 const std::string expected_id,
[email protected]902f7cd2009-05-22 19:02:19722 bool from_external,
723 const DictionaryValue& manifest,
724 const std::vector< Tuple2<SkBitmap, FilePath> >& images) {
[email protected]1fca1492009-05-15 22:23:43725 Extension extension;
726 std::string error;
[email protected]902f7cd2009-05-22 19:02:19727 if (!extension.InitFromValue(manifest,
[email protected]1fca1492009-05-15 22:23:43728 true, // require ID
729 &error)) {
730 ReportExtensionInstallError(extension_path, "Invalid extension manifest.");
731 return;
[email protected]0b344962009-03-31 04:21:45732 }
733
[email protected]e2eb43112009-05-29 21:19:54734 if (!frontend_->extensions_enabled() && !extension.IsTheme()) {
735#if defined(OS_WIN)
736 if (frontend_->show_extensions_disabled_notification()) {
737 win_util::MessageBox(GetActiveWindow(),
738 L"Extensions are not enabled. Add --enable-extensions to the "
739 L"command-line to enable extensions.\n\n"
740 L"This is a temporary message and it will be removed when extensions "
741 L"UI is finalized.",
742 l10n_util::GetString(IDS_PRODUCT_NAME).c_str(), MB_OK);
743 }
744#endif
745 ReportExtensionInstallError(extension_path,
746 "Extensions are not enabled.");
747 return;
748 }
749
[email protected]b0beaa662009-02-26 00:04:15750 // If an expected id was provided, make sure it matches.
[email protected]1fca1492009-05-15 22:23:43751 if (!expected_id.empty() && expected_id != extension.id()) {
752 ReportExtensionInstallError(extension_path,
[email protected]b0beaa662009-02-26 00:04:15753 "ID in new extension manifest does not match expected ID.");
[email protected]1fca1492009-05-15 22:23:43754 return;
[email protected]cc655912009-01-29 23:19:19755 }
756
757 // <profile>/Extensions/<id>
[email protected]b0beaa662009-02-26 00:04:15758 FilePath dest_dir = install_directory_.AppendASCII(extension.id());
[email protected]cc655912009-01-29 23:19:19759 std::string version = extension.VersionString();
[email protected]b0beaa662009-02-26 00:04:15760 std::string current_version;
[email protected]1fca1492009-05-15 22:23:43761 bool was_update = false;
[email protected]b0beaa662009-02-26 00:04:15762 if (ReadCurrentVersion(dest_dir, &current_version)) {
763 if (!CheckCurrentVersion(version, current_version, dest_dir))
[email protected]1fca1492009-05-15 22:23:43764 return;
765 was_update = true;
[email protected]b0beaa662009-02-26 00:04:15766 }
[email protected]cc655912009-01-29 23:19:19767
[email protected]902f7cd2009-05-22 19:02:19768 // Write our parsed manifest back to disk, to ensure it doesn't contain an
769 // exploitable bug that can be used to compromise the browser.
770 std::string manifest_json;
771 JSONStringValueSerializer serializer(&manifest_json);
772 serializer.set_pretty_print(true);
773 if (!serializer.Serialize(manifest)) {
774 ReportExtensionInstallError(extension_path,
775 "Error serializing manifest.json.");
776 return;
777 }
778
779 FilePath manifest_path =
780 temp_extension_dir.AppendASCII(Extension::kManifestFilename);
781 if (!file_util::WriteFile(manifest_path,
782 manifest_json.data(), manifest_json.size())) {
783 ReportExtensionInstallError(extension_path, "Error saving manifest.json.");
784 return;
785 }
786
787 // Write our parsed images back to disk as well.
788 for (size_t i = 0; i < images.size(); ++i) {
789 const SkBitmap& image = images[i].a;
790 FilePath path = temp_extension_dir.Append(images[i].b);
791
792 std::vector<unsigned char> image_data;
793 // TODO(mpcomplete): It's lame that we're encoding all images as PNG, even
794 // though they may originally be .jpg, etc. Figure something out.
795 // http://code.google.com/p/chromium/issues/detail?id=12459
796 if (!PNGEncoder::EncodeBGRASkBitmap(image, false, &image_data)) {
797 ReportExtensionInstallError(extension_path,
798 "Error re-encoding theme image.");
799 return;
800 }
801
802 // Note: we're overwriting existing files that the utility process wrote,
803 // so we can be sure the directory exists.
804 const char* image_data_ptr = reinterpret_cast<const char*>(&image_data[0]);
805 if (!file_util::WriteFile(path, image_data_ptr, image_data.size())) {
806 ReportExtensionInstallError(extension_path, "Error saving theme image.");
807 return;
808 }
809 }
810
[email protected]cc655912009-01-29 23:19:19811 // <profile>/Extensions/<dir_name>/<version>
[email protected]1fca1492009-05-15 22:23:43812 FilePath version_dir = dest_dir.AppendASCII(version);
[email protected]902f7cd2009-05-22 19:02:19813
814 // If anything fails after this, we want to delete the extension dir.
815 ScopedTempDir scoped_version_dir;
816 scoped_version_dir.Set(version_dir);
817
[email protected]1fca1492009-05-15 22:23:43818 if (!InstallDirSafely(temp_extension_dir, version_dir))
819 return;
[email protected]cc655912009-01-29 23:19:19820
[email protected]902f7cd2009-05-22 19:02:19821 if (!SetCurrentVersion(dest_dir, version))
[email protected]1fca1492009-05-15 22:23:43822 return;
[email protected]cc655912009-01-29 23:19:19823
[email protected]1fca1492009-05-15 22:23:43824 // To mark that this extension was installed from an external source, create a
825 // zero-length file. At load time, this is used to indicate that the
826 // extension should be uninstalled.
827 // TODO(erikkay): move this into per-extension config storage when it appears.
828 if (from_external) {
829 FilePath marker = version_dir.AppendASCII(kExternalInstallFile);
830 file_util::WriteFile(marker, NULL, 0);
831 }
832
[email protected]894bb502009-05-21 22:39:57833 // Load the extension immediately and then report installation success. We
834 // don't load extensions for external installs because external installation
835 // occurs before the normal startup so we just let startup pick them up. We
[email protected]902f7cd2009-05-22 19:02:19836 // don't notify installation because there is no UI for external install so
[email protected]894bb502009-05-21 22:39:57837 // there is nobody to notify.
838 if (!from_external) {
839 Extension* extension = LoadExtension(version_dir, true); // require id
840 CHECK(extension);
841
842 frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(
843 frontend_, &ExtensionsService::OnExtensionInstalled, extension,
844 was_update));
845
846 // Only one extension, but ReportExtensionsLoaded can handle multiple,
847 // so we need to construct a list.
848 scoped_ptr<ExtensionList> extensions(new ExtensionList);
849 extensions->push_back(extension);
850 LOG(INFO) << "Done.";
851 // Hand off ownership of the loaded extensions to the frontend.
852 ReportExtensionsLoaded(extensions.release());
853 }
[email protected]902f7cd2009-05-22 19:02:19854
855 scoped_version_dir.Take();
[email protected]cc655912009-01-29 23:19:19856}
857
858void ExtensionsServiceBackend::ReportExtensionInstallError(
[email protected]cc5da332009-03-04 08:02:51859 const FilePath& extension_path, const std::string &error) {
[email protected]b0beaa662009-02-26 00:04:15860
[email protected]cc655912009-01-29 23:19:19861 // TODO(erikkay): note that this isn't guaranteed to work properly on Linux.
[email protected]cc5da332009-03-04 08:02:51862 std::string path_str = WideToASCII(extension_path.ToWStringHack());
[email protected]cc655912009-01-29 23:19:19863 std::string message =
864 StringPrintf("Could not install extension from '%s'. %s",
865 path_str.c_str(), error.c_str());
[email protected]bb28e062009-02-27 17:19:18866 ExtensionErrorReporter::GetInstance()->ReportError(message, alert_on_error_);
[email protected]cc655912009-01-29 23:19:19867}
868
[email protected]4a190632009-05-09 01:07:42869void ExtensionsServiceBackend::ReportExtensionVersionReinstalled(
870 const std::string& id) {
[email protected]894bb502009-05-21 22:39:57871 frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(
872 frontend_, &ExtensionsService::OnExtensionVersionReinstalled, id));
[email protected]b0beaa662009-02-26 00:04:15873}
874
875// Some extensions will autoupdate themselves externally from Chrome. These
876// are typically part of some larger client application package. To support
877// these, the extension will register its location in the registry on Windows
878// (TODO(port): what about on other platforms?) and this code will periodically
879// check that location for a .crx file, which it will then install locally if
880// a new version is available.
881void ExtensionsServiceBackend::CheckForExternalUpdates(
[email protected]894bb502009-05-21 22:39:57882 std::set<std::string> ids_to_ignore,
883 scoped_refptr<ExtensionsService> frontend) {
[email protected]b0beaa662009-02-26 00:04:15884
885 // Note that this installation is intentionally silent (since it didn't
886 // go through the front-end). Extensions that are registered in this
887 // way are effectively considered 'pre-bundled', and so implicitly
888 // trusted. In general, if something has HKLM or filesystem access,
889 // they could install an extension manually themselves anyway.
890 alert_on_error_ = false;
891 frontend_ = frontend;
[email protected]b0beaa662009-02-26 00:04:15892
893#if defined(OS_WIN)
[email protected]631cf822009-05-15 07:01:25894 // TODO(port): Pull this out into an interface. That will also allow us to
895 // test the behavior of external extensions.
[email protected]b0beaa662009-02-26 00:04:15896 HKEY reg_root = HKEY_LOCAL_MACHINE;
[email protected]894bb502009-05-21 22:39:57897 RegistryKeyIterator iterator(reg_root, ASCIIToWide(registry_path_).c_str());
[email protected]b0beaa662009-02-26 00:04:15898 while (iterator.Valid()) {
[email protected]e2eb43112009-05-29 21:19:54899 // Fold
[email protected]894bb502009-05-21 22:39:57900 std::string id = StringToLowerASCII(WideToASCII(iterator.Name()));
901 if (ids_to_ignore.find(id) != ids_to_ignore.end()) {
902 LOG(INFO) << "Skipping uninstalled external extension " << id;
903 ++iterator;
904 continue;
905 }
906
[email protected]b0beaa662009-02-26 00:04:15907 RegKey key;
[email protected]894bb502009-05-21 22:39:57908 std::wstring key_path = ASCIIToWide(registry_path_);
[email protected]b0beaa662009-02-26 00:04:15909 key_path.append(L"\\");
910 key_path.append(iterator.Name());
911 if (key.Open(reg_root, key_path.c_str())) {
912 std::wstring extension_path;
913 if (key.ReadValue(kRegistryExtensionPath, &extension_path)) {
[email protected]b0beaa662009-02-26 00:04:15914 std::wstring extension_version;
915 if (key.ReadValue(kRegistryExtensionVersion, &extension_version)) {
[email protected]cc5da332009-03-04 08:02:51916 if (ShouldInstall(id, WideToASCII(extension_version))) {
[email protected]1fca1492009-05-15 22:23:43917 bool from_external = true;
918 InstallOrUpdateExtension(FilePath(extension_path), id,
919 from_external);
[email protected]cc5da332009-03-04 08:02:51920 }
[email protected]b0beaa662009-02-26 00:04:15921 } else {
[email protected]b0beaa662009-02-26 00:04:15922 // TODO(erikkay): find a way to get this into about:extensions
923 LOG(WARNING) << "Missing value " << kRegistryExtensionVersion <<
924 " for key " << key_path;
925 }
926 } else {
[email protected]b0beaa662009-02-26 00:04:15927 // TODO(erikkay): find a way to get this into about:extensions
928 LOG(WARNING) << "Missing value " << kRegistryExtensionPath <<
929 " for key " << key_path;
930 }
931 }
932 ++iterator;
933 }
934#else
935 NOTREACHED();
936#endif
937}
938
[email protected]894bb502009-05-21 22:39:57939bool ExtensionsServiceBackend::CheckExternalUninstall(const FilePath& version_path,
[email protected]b0beaa662009-02-26 00:04:15940 const std::string& id) {
[email protected]894bb502009-05-21 22:39:57941 FilePath external_file = version_path.AppendASCII(kExternalInstallFile);
[email protected]b0beaa662009-02-26 00:04:15942 if (file_util::PathExists(external_file)) {
943#if defined(OS_WIN)
944 HKEY reg_root = HKEY_LOCAL_MACHINE;
945 RegKey key;
[email protected]894bb502009-05-21 22:39:57946 std::wstring key_path = ASCIIToWide(registry_path_);
[email protected]b0beaa662009-02-26 00:04:15947 key_path.append(L"\\");
948 key_path.append(ASCIIToWide(id));
949
950 // If the key doesn't exist, then we should uninstall.
951 return !key.Open(reg_root, key_path.c_str());
952#else
[email protected]894bb502009-05-21 22:39:57953 // TODO(port)
[email protected]b0beaa662009-02-26 00:04:15954#endif
955 }
956 return false;
957}
958
959// Assumes that the extension isn't currently loaded or in use.
[email protected]631cf822009-05-15 07:01:25960void ExtensionsServiceBackend::UninstallExtension(
961 const std::string& extension_id) {
962 // First, delete the Current Version file. If the directory delete fails, then
963 // at least the extension won't be loaded again.
964 FilePath extension_directory = install_directory_.AppendASCII(extension_id);
965
966 if (!file_util::PathExists(extension_directory)) {
967 LOG(WARNING) << "Asked to remove a non-existent extension " << extension_id;
[email protected]b0beaa662009-02-26 00:04:15968 return;
969 }
[email protected]631cf822009-05-15 07:01:25970
971 FilePath current_version_file = extension_directory.AppendASCII(
972 ExtensionsService::kCurrentVersionFileName);
973 if (!file_util::PathExists(current_version_file)) {
974 LOG(WARNING) << "Extension " << extension_id
975 << " does not have a Current Version file.";
976 } else {
977 if (!file_util::Delete(current_version_file, false)) {
978 LOG(WARNING) << "Could not delete Current Version file for extension "
979 << extension_id;
980 return;
981 }
982 }
983
[email protected]894bb502009-05-21 22:39:57984 // Ok, now try and delete the entire rest of the directory. One major place
985 // this can fail is if the extension contains a plugin (stupid plugins). It's
986 // not a big deal though, because we'll notice next time we startup that the
987 // Current Version file is gone and finish the delete then.
[email protected]631cf822009-05-15 07:01:25988 if (!file_util::Delete(extension_directory, true)) {
989 LOG(WARNING) << "Could not delete directory for extension "
990 << extension_id;
[email protected]b0beaa662009-02-26 00:04:15991 }
992}
993
994bool ExtensionsServiceBackend::ShouldInstall(const std::string& id,
995 const std::string& version) {
996 FilePath dir(install_directory_.AppendASCII(id.c_str()));
997 std::string current_version;
998 if (ReadCurrentVersion(dir, &current_version)) {
[email protected]894bb502009-05-21 22:39:57999 return CheckCurrentVersion(version, current_version, dir);
[email protected]b0beaa662009-02-26 00:04:151000 }
1001 return true;
[email protected]cc655912009-01-29 23:19:191002}