blob: d749fd57350be11666b02b2df883871b29cbfcec [file] [log] [blame]
[email protected]6014d672008-12-05 00:38:251// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
2// 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
7#include "base/file_util.h"
[email protected]cc655912009-01-29 23:19:198#include "base/scoped_handle.h"
9#include "base/scoped_temp_dir.h"
[email protected]6014d672008-12-05 00:38:2510#include "base/string_util.h"
[email protected]cc655912009-01-29 23:19:1911#include "base/third_party/nss/blapi.h"
12#include "base/third_party/nss/sha256.h"
[email protected]6014d672008-12-05 00:38:2513#include "base/thread.h"
[email protected]cc655912009-01-29 23:19:1914#include "base/values.h"
15#include "net/base/file_stream.h"
[email protected]6014d672008-12-05 00:38:2516#include "chrome/browser/browser_process.h"
[email protected]bdbc87c2009-01-25 05:08:5417#include "chrome/browser/extensions/user_script_master.h"
[email protected]6014d672008-12-05 00:38:2518#include "chrome/common/json_value_serializer.h"
[email protected]82891262008-12-24 00:21:2619#include "chrome/common/notification_service.h"
[email protected]cc655912009-01-29 23:19:1920#include "chrome/common/unzip.h"
[email protected]6014d672008-12-05 00:38:2521
22// ExtensionsService
23
[email protected]cc655912009-01-29 23:19:1924const char* ExtensionsService::kInstallDirectoryName = "Extensions";
25const char* ExtensionsService::kCurrentVersionFileName = "Current Version";
26const char* ExtensionsServiceBackend::kTempExtensionName = "TEMP_INSTALL";
27// Chromium Extension magic number
28static const char kExtensionFileMagic[] = "Cr24";
29
30struct ExtensionHeader {
31 char magic[sizeof(kExtensionFileMagic) - 1];
32 uint32 version;
33 size_t header_size;
34 size_t manifest_size;
35};
36
37const size_t kZipHashBytes = 32; // SHA-256
38const size_t kZipHashHexBytes = kZipHashBytes * 2; // Hex string is 2x size.
[email protected]6014d672008-12-05 00:38:2539
[email protected]bdbc87c2009-01-25 05:08:5440ExtensionsService::ExtensionsService(const FilePath& profile_directory,
41 UserScriptMaster* user_script_master)
[email protected]6014d672008-12-05 00:38:2542 : message_loop_(MessageLoop::current()),
43 backend_(new ExtensionsServiceBackend),
[email protected]cc655912009-01-29 23:19:1944 install_directory_(profile_directory.AppendASCII(kInstallDirectoryName)),
[email protected]bdbc87c2009-01-25 05:08:5445 user_script_master_(user_script_master) {
[email protected]6014d672008-12-05 00:38:2546}
47
48ExtensionsService::~ExtensionsService() {
49 for (ExtensionList::iterator iter = extensions_.begin();
50 iter != extensions_.end(); ++iter) {
51 delete *iter;
52 }
53}
54
55bool ExtensionsService::Init() {
56 // TODO(aa): This message loop should probably come from a backend
57 // interface, similar to how the message loop for the frontend comes
58 // from the frontend interface.
59 g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE,
60 NewRunnableMethod(backend_.get(),
61 &ExtensionsServiceBackend::LoadExtensionsFromDirectory,
62 install_directory_,
63 scoped_refptr<ExtensionsServiceFrontendInterface>(this)));
64 // TODO(aa): Load extensions from other registered directories.
65
66 return true;
67}
68
69MessageLoop* ExtensionsService::GetMessageLoop() {
70 return message_loop_;
71}
72
[email protected]3cf4f0992009-02-03 23:00:3073void ExtensionsService::InstallExtension(const FilePath& extension_path) {
74 // TODO(aa): This message loop should probably come from a backend
75 // interface, similar to how the message loop for the frontend comes
76 // from the frontend interface.
77 g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE,
78 NewRunnableMethod(backend_.get(),
79 &ExtensionsServiceBackend::InstallExtension,
80 extension_path,
81 install_directory_,
82 scoped_refptr<ExtensionsServiceFrontendInterface>(this)));
83}
84
85void ExtensionsService::LoadExtension(const FilePath& extension_path) {
86 // TODO(aa): This message loop should probably come from a backend
87 // interface, similar to how the message loop for the frontend comes
88 // from the frontend interface.
89 g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE,
90 NewRunnableMethod(backend_.get(),
91 &ExtensionsServiceBackend::LoadSingleExtension,
92 extension_path,
93 scoped_refptr<ExtensionsServiceFrontendInterface>(this)));
94}
95
[email protected]6014d672008-12-05 00:38:2596void ExtensionsService::OnExtensionsLoadedFromDirectory(
[email protected]08816d0d2008-12-08 18:43:5397 ExtensionList* new_extensions) {
98 extensions_.insert(extensions_.end(), new_extensions->begin(),
99 new_extensions->end());
[email protected]6014d672008-12-05 00:38:25100
[email protected]bdbc87c2009-01-25 05:08:54101 // Tell UserScriptMaster about any scripts in the loaded extensions.
102 for (ExtensionList::iterator extension = extensions_.begin();
103 extension != extensions_.end(); ++extension) {
104 const UserScriptList& scripts = (*extension)->user_scripts();
105 for (UserScriptList::const_iterator script = scripts.begin();
106 script != scripts.end(); ++script) {
107 user_script_master_->AddLoneScript(*script);
108 }
109 }
110
111 // Tell UserScriptMaster to also watch the extensions directory for changes
112 // and then kick off the first scan.
113 // TODO(aa): This should go away when we implement the --extension flag, since
114 // developing scripts in the Extensions directory will no longer be a common
115 // use-case.
116 user_script_master_->AddWatchedPath(install_directory_);
117 user_script_master_->StartScan();
118
[email protected]bfd04a62009-02-01 18:16:56119 NotificationService::current()->Notify(
120 NotificationType::EXTENSIONS_LOADED,
[email protected]82891262008-12-24 00:21:26121 NotificationService::AllSources(),
122 Details<ExtensionList>(new_extensions));
123
124 delete new_extensions;
[email protected]6014d672008-12-05 00:38:25125}
126
[email protected]3acbd422008-12-08 18:25:00127void ExtensionsService::OnExtensionLoadError(const std::string& error) {
128 // TODO(aa): Print the error message out somewhere better. I think we are
129 // going to need some sort of 'extension inspector'.
130 LOG(WARNING) << error;
[email protected]6014d672008-12-05 00:38:25131}
132
[email protected]cc655912009-01-29 23:19:19133void ExtensionsService::OnExtensionInstallError(const std::string& error) {
134 // TODO(erikkay): Print the error message out somewhere better.
135 LOG(WARNING) << error;
136}
137
138void ExtensionsService::OnExtensionInstalled(FilePath path) {
[email protected]bfd04a62009-02-01 18:16:56139 NotificationService::current()->Notify(
140 NotificationType::EXTENSION_INSTALLED,
[email protected]cc655912009-01-29 23:19:19141 NotificationService::AllSources(),
142 Details<FilePath>(&path));
143
[email protected]0877fd92009-02-03 16:34:06144 // Immediately try to load the extension.
145 g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE,
146 NewRunnableMethod(backend_.get(),
147 &ExtensionsServiceBackend::LoadSingleExtension,
148 path,
149 scoped_refptr<ExtensionsServiceFrontendInterface>(this)));
[email protected]cc655912009-01-29 23:19:19150}
151
[email protected]6014d672008-12-05 00:38:25152
153// ExtensionsServicesBackend
154
155bool ExtensionsServiceBackend::LoadExtensionsFromDirectory(
[email protected]eab9b452009-01-23 20:48:59156 const FilePath& path_in,
[email protected]6014d672008-12-05 00:38:25157 scoped_refptr<ExtensionsServiceFrontendInterface> frontend) {
[email protected]eab9b452009-01-23 20:48:59158 FilePath path = path_in;
159 if (!file_util::AbsolutePath(&path))
160 NOTREACHED();
161
[email protected]6014d672008-12-05 00:38:25162 // Find all child directories in the install directory and load their
163 // manifests. Post errors and results to the frontend.
164 scoped_ptr<ExtensionList> extensions(new ExtensionList);
[email protected]0b733222008-12-11 14:55:12165 file_util::FileEnumerator enumerator(path,
[email protected]cc655912009-01-29 23:19:19166 false, // not recursive
[email protected]6014d672008-12-05 00:38:25167 file_util::FileEnumerator::DIRECTORIES);
[email protected]0b733222008-12-11 14:55:12168 for (FilePath child_path = enumerator.Next(); !child_path.value().empty();
[email protected]6014d672008-12-05 00:38:25169 child_path = enumerator.Next()) {
[email protected]18a12352009-01-31 01:33:28170 std::string version_str;
171 if (ReadCurrentVersion(child_path, &version_str)) {
172 child_path = child_path.AppendASCII(version_str);
173 } else {
174 // For now, continue to allow fallback to a non-versioned directory
175 // structure. This is so that we can use this same method to load
176 // from local directories that developers are just hacking in place.
177 // TODO(erikkay): perhaps we should use a different code path for this.
178 }
[email protected]6014d672008-12-05 00:38:25179
[email protected]0877fd92009-02-03 16:34:06180 Extension* extension = LoadExtension(child_path, frontend);
181 if (extension)
182 extensions->push_back(extension);
[email protected]6014d672008-12-05 00:38:25183 }
184
185 ReportExtensionsLoaded(frontend.get(), extensions.release());
186 return true;
187}
188
[email protected]0877fd92009-02-03 16:34:06189bool ExtensionsServiceBackend::LoadSingleExtension(
190 const FilePath& path_in,
191 scoped_refptr<ExtensionsServiceFrontendInterface> frontend) {
192 FilePath path = path_in;
193 if (!file_util::AbsolutePath(&path))
194 NOTREACHED();
195 Extension* extension = LoadExtension(path, frontend);
196 if (extension) {
197 ExtensionList* extensions = new ExtensionList;
198 extensions->push_back(extension);
199 ReportExtensionsLoaded(frontend.get(), extensions);
200 return true;
201 }
202 return false;
203}
204
205Extension* ExtensionsServiceBackend::LoadExtension(
206 const FilePath& path,
207 scoped_refptr<ExtensionsServiceFrontendInterface> frontend) {
208 FilePath manifest_path =
209 path.AppendASCII(Extension::kManifestFilename);
210 if (!file_util::PathExists(manifest_path)) {
211 ReportExtensionLoadError(frontend.get(), path,
212 Extension::kInvalidManifestError);
213 return NULL;
214 }
215
216 JSONFileValueSerializer serializer(manifest_path.ToWStringHack());
217 std::string error;
218 scoped_ptr<Value> root(serializer.Deserialize(&error));
219 if (!root.get()) {
220 ReportExtensionLoadError(frontend.get(), path,
221 error);
222 return NULL;
223 }
224
225 if (!root->IsType(Value::TYPE_DICTIONARY)) {
226 ReportExtensionLoadError(frontend.get(), path,
227 Extension::kInvalidManifestError);
228 return NULL;
229 }
230
231 scoped_ptr<Extension> extension(new Extension(path));
232 if (!extension->InitFromValue(*static_cast<DictionaryValue*>(root.get()),
233 &error)) {
234 ReportExtensionLoadError(frontend.get(), path, error);
235 return NULL;
236 }
237 return extension.release();
238}
239
[email protected]6014d672008-12-05 00:38:25240void ExtensionsServiceBackend::ReportExtensionLoadError(
[email protected]cc655912009-01-29 23:19:19241 ExtensionsServiceFrontendInterface *frontend, const FilePath& path,
[email protected]3acbd422008-12-08 18:25:00242 const std::string &error) {
[email protected]cc655912009-01-29 23:19:19243 // TODO(erikkay): note that this isn't guaranteed to work properly on Linux.
244 std::string path_str = WideToASCII(path.ToWStringHack());
[email protected]3acbd422008-12-08 18:25:00245 std::string message = StringPrintf("Could not load extension from '%s'. %s",
[email protected]cc655912009-01-29 23:19:19246 path_str.c_str(), error.c_str());
[email protected]6014d672008-12-05 00:38:25247 frontend->GetMessageLoop()->PostTask(FROM_HERE, NewRunnableMethod(
248 frontend, &ExtensionsServiceFrontendInterface::OnExtensionLoadError,
[email protected]3acbd422008-12-08 18:25:00249 message));
[email protected]6014d672008-12-05 00:38:25250}
251
252void ExtensionsServiceBackend::ReportExtensionsLoaded(
253 ExtensionsServiceFrontendInterface *frontend, ExtensionList* extensions) {
254 frontend->GetMessageLoop()->PostTask(FROM_HERE, NewRunnableMethod(
255 frontend,
256 &ExtensionsServiceFrontendInterface::OnExtensionsLoadedFromDirectory,
257 extensions));
258}
[email protected]cc655912009-01-29 23:19:19259
260// The extension file format is a header, followed by the manifest, followed
261// by the zip file. The header is a magic number, a version, the size of the
262// header, and the size of the manifest. These ints are 4 byte little endian.
263DictionaryValue* ExtensionsServiceBackend::ReadManifest(
264 const FilePath& extension_path,
265 scoped_refptr<ExtensionsServiceFrontendInterface> frontend) {
266 ScopedStdioHandle file(file_util::OpenFile(extension_path, "rb"));
267 if (!file.get()) {
268 ReportExtensionInstallError(frontend, extension_path,
269 "no such extension file");
270 return NULL;
271 }
272
273 // Read and verify the header.
274 ExtensionHeader header;
275 size_t len;
276 // TODO(erikkay): Yuck. I'm not a big fan of this kind of code, but it
277 // appears that we don't have any endian/alignment aware serialization
278 // code in the code base. So for now, this assumes that we're running
279 // on a little endian machine with 4 byte alignment.
280 len = fread(&header, 1, sizeof(ExtensionHeader), file.get());
281 if (len < sizeof(ExtensionHeader)) {
282 ReportExtensionInstallError(frontend, extension_path,
283 "invalid extension header");
284 return NULL;
285 }
286 if (strncmp(kExtensionFileMagic, header.magic, sizeof(header.magic))) {
287 ReportExtensionInstallError(frontend, extension_path,
288 "bad magic number");
289 return NULL;
290 }
291 if (header.version != Extension::kExpectedFormatVersion) {
292 ReportExtensionInstallError(frontend, extension_path,
293 "bad version number");
294 return NULL;
295 }
296 if (header.header_size > sizeof(ExtensionHeader))
297 fseek(file.get(), header.header_size - sizeof(ExtensionHeader), SEEK_CUR);
298
299 char buf[1 << 16];
300 std::string manifest_str;
301 size_t read_size = std::min(sizeof(buf), header.manifest_size);
302 size_t remainder = header.manifest_size;
303 while ((len = fread(buf, 1, read_size, file.get())) > 0) {
304 manifest_str.append(buf, len);
305 if (len <= remainder)
306 break;
307 remainder -= len;
308 read_size = std::min(sizeof(buf), remainder);
309 }
310
311 // Verify the JSON
312 JSONStringValueSerializer json(manifest_str);
313 std::string error;
314 Value* val = json.Deserialize(&error);
315 if (!val) {
316 ReportExtensionInstallError(frontend, extension_path, error);
317 return NULL;
318 }
319 if (!val->IsType(Value::TYPE_DICTIONARY)) {
320 ReportExtensionInstallError(frontend, extension_path,
321 "manifest isn't a JSON dictionary");
322 return NULL;
323 }
324 DictionaryValue* manifest = static_cast<DictionaryValue*>(val);
325 std::string zip_hash;
326 if (!manifest->GetString(Extension::kZipHashKey, &zip_hash)) {
327 ReportExtensionInstallError(frontend, extension_path,
328 "missing zip_hash key");
329 return NULL;
330 }
331 if (zip_hash.size() != kZipHashHexBytes) {
332 ReportExtensionInstallError(frontend, extension_path,
333 "invalid zip_hash key");
334 return NULL;
335 }
336
337 // Read the rest of the zip file and compute a hash to compare against
338 // what the manifest claims. Compute the hash incrementally since the
339 // zip file could be large.
340 const unsigned char* ubuf = reinterpret_cast<const unsigned char*>(buf);
341 SHA256Context ctx;
342 SHA256_Begin(&ctx);
343 while ((len = fread(buf, 1, sizeof(buf), file.get())) > 0)
344 SHA256_Update(&ctx, ubuf, len);
345 uint8 hash[32];
346 SHA256_End(&ctx, hash, NULL, sizeof(hash));
347
348 std::vector<uint8> zip_hash_bytes;
349 if (!HexStringToBytes(zip_hash, &zip_hash_bytes)) {
350 ReportExtensionInstallError(frontend, extension_path,
351 "invalid zip_hash key");
352 return NULL;
353 }
354 if (zip_hash_bytes.size() != kZipHashBytes) {
355 ReportExtensionInstallError(frontend, extension_path,
356 "invalid zip_hash key");
357 return NULL;
358 }
359 for (size_t i = 0; i < kZipHashBytes; ++i) {
360 if (zip_hash_bytes[i] != hash[i]) {
361 ReportExtensionInstallError(frontend, extension_path,
362 "zip_hash key didn't match zip hash");
363 return NULL;
364 }
365 }
366
367 // TODO(erikkay): The manifest will also contain a signature of the hash
368 // (or perhaps the whole manifest) for authentication purposes.
369
370 return manifest;
371}
372
[email protected]18a12352009-01-31 01:33:28373bool ExtensionsServiceBackend::ReadCurrentVersion(
374 const FilePath& extension_path,
375 std::string* version_string) {
376 FilePath current_version =
377 extension_path.AppendASCII(ExtensionsService::kCurrentVersionFileName);
378 if (file_util::PathExists(current_version)) {
379 if (file_util::ReadFileToString(current_version, version_string)) {
380 TrimWhitespace(*version_string, TRIM_ALL, version_string);
381 return true;
382 }
383 }
384 return false;
385}
386
[email protected]cc655912009-01-29 23:19:19387bool ExtensionsServiceBackend::CheckCurrentVersion(
388 const FilePath& extension_path,
389 const std::string& version,
390 const FilePath& dest_dir,
391 scoped_refptr<ExtensionsServiceFrontendInterface> frontend) {
[email protected]18a12352009-01-31 01:33:28392 std::string version_str;
393 if (ReadCurrentVersion(dest_dir, &version_str)) {
394 if (version_str == version) {
395 FilePath version_dir = dest_dir.AppendASCII(version_str);
396 if (file_util::PathExists(version_dir)) {
[email protected]cc655912009-01-29 23:19:19397 ReportExtensionInstallError(frontend, extension_path,
398 "Extension version already installed");
399 return false;
[email protected]18a12352009-01-31 01:33:28400 }
401 // If the existing version_dir doesn't exist, then we'll return true
402 // so that we attempt to repair the broken installation.
403 } else {
404 scoped_ptr<Version> cur_version(
405 Version::GetVersionFromString(version_str));
406 scoped_ptr<Version> new_version(
407 Version::GetVersionFromString(version));
408 if (cur_version->CompareTo(*new_version) >= 0) {
409 ReportExtensionInstallError(frontend, extension_path,
410 "More recent version of extension already installed");
411 return false;
[email protected]cc655912009-01-29 23:19:19412 }
413 }
414 }
415 return true;
416}
417
418bool ExtensionsServiceBackend::UnzipExtension(const FilePath& extension_path,
419 const FilePath& temp_dir,
420 scoped_refptr<ExtensionsServiceFrontendInterface> frontend) {
421 // <profile>/Extensions/INSTALL_TEMP/<version>
422 if (!file_util::CreateDirectory(temp_dir)) {
423 ReportExtensionInstallError(frontend, extension_path,
424 "Couldn't create version directory.");
425 return false;
426 }
427 if (!Unzip(extension_path, temp_dir, NULL)) {
428 // Remove what we just installed.
429 file_util::Delete(temp_dir, true);
430 ReportExtensionInstallError(frontend, extension_path,
431 "Couldn't unzip extension.");
432 return false;
433 }
434 return true;
435}
436
437bool ExtensionsServiceBackend::InstallDirSafely(
438 const FilePath& extension_path,
439 const FilePath& source_dir,
440 const FilePath& dest_dir,
441 scoped_refptr<ExtensionsServiceFrontendInterface> frontend) {
442
443 if (file_util::PathExists(dest_dir)) {
444 // By the time we get here, it should be safe to assume that this directory
445 // is not currently in use (it's not the current active version).
446 if (!file_util::Delete(dest_dir, true)) {
447 ReportExtensionInstallError(frontend, extension_path,
448 "Can't delete existing version directory.");
449 return false;
450 }
451 } else {
452 FilePath parent = dest_dir.DirName();
453 if (!file_util::DirectoryExists(parent)) {
454 if (!file_util::CreateDirectory(parent)) {
455 ReportExtensionInstallError(frontend, extension_path,
456 "Couldn't create extension directory.");
457 return false;
458 }
459 }
460 }
461 if (!file_util::Move(source_dir, dest_dir)) {
462 ReportExtensionInstallError(frontend, extension_path,
463 "Couldn't move temporary directory.");
464 return false;
465 }
466
467 return true;
468}
469
470bool ExtensionsServiceBackend::SetCurrentVersion(
471 const FilePath& extension_path,
472 const FilePath& dest_dir,
473 std::string version,
474 scoped_refptr<ExtensionsServiceFrontendInterface> frontend) {
475 // Write out the new CurrentVersion file.
476 // <profile>/Extension/<name>/CurrentVersion
477 FilePath current_version =
478 dest_dir.AppendASCII(ExtensionsService::kCurrentVersionFileName);
479 FilePath current_version_old =
480 current_version.InsertBeforeExtension(FILE_PATH_LITERAL("_old"));
481 if (file_util::PathExists(current_version_old)) {
482 if (!file_util::Delete(current_version_old, false)) {
483 ReportExtensionInstallError(frontend, extension_path,
484 "Couldn't remove CurrentVersion_old file.");
485 return false;
486 }
487 }
488 if (file_util::PathExists(current_version)) {
489 if (!file_util::Move(current_version, current_version_old)) {
490 ReportExtensionInstallError(frontend, extension_path,
491 "Couldn't move CurrentVersion file.");
492 return false;
493 }
494 }
495 net::FileStream stream;
496 int flags = base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_WRITE;
497 if (stream.Open(current_version, flags) != 0)
498 return false;
499 if (stream.Write(version.c_str(), version.size(), NULL) < 0) {
500 // Restore the old CurrentVersion.
501 if (file_util::PathExists(current_version_old)) {
502 if (!file_util::Move(current_version_old, current_version)) {
503 LOG(WARNING) << "couldn't restore " << current_version_old.value() <<
504 " to " << current_version.value();
505 // TODO(erikkay): This is an ugly state to be in. Try harder?
506 }
507 }
508 ReportExtensionInstallError(frontend, extension_path,
509 "Couldn't create CurrentVersion file.");
510 return false;
511 }
512 return true;
513}
514
515bool ExtensionsServiceBackend::InstallExtension(
516 const FilePath& extension_path,
517 const FilePath& install_dir,
518 scoped_refptr<ExtensionsServiceFrontendInterface> frontend) {
519 LOG(INFO) << "Installing extension " << extension_path.value();
520
521 // <profile>/Extensions/INSTALL_TEMP
522 FilePath temp_dir = install_dir.AppendASCII(kTempExtensionName);
523 // Ensure we're starting with a clean slate.
524 if (file_util::PathExists(temp_dir)) {
525 if (!file_util::Delete(temp_dir, true)) {
526 ReportExtensionInstallError(frontend, extension_path,
527 "Couldn't delete existing temporary directory.");
528 return false;
529 }
530 }
531 ScopedTempDir scoped_temp;
532 scoped_temp.Set(temp_dir);
533 if (!scoped_temp.IsValid()) {
534 ReportExtensionInstallError(frontend, extension_path,
535 "Couldn't create temporary directory.");
536 return false;
537 }
538
539 // Read and verify the extension.
540 scoped_ptr<DictionaryValue> manifest(ReadManifest(extension_path, frontend));
541 if (!manifest.get()) {
542 // ReadManifest has already reported the extension error.
543 return false;
544 }
545 DictionaryValue* dict = manifest.get();
546 Extension extension;
547 std::string error;
548 if (!extension.InitFromValue(*dict, &error)) {
549 ReportExtensionInstallError(frontend, extension_path,
550 "Invalid extension manifest.");
551 return false;
552 }
553
554 // <profile>/Extensions/<id>
555 FilePath dest_dir = install_dir.AppendASCII(extension.id());
556 std::string version = extension.VersionString();
557 if (!CheckCurrentVersion(extension_path, version, dest_dir, frontend))
558 return false;
559
560 // <profile>/Extensions/INSTALL_TEMP/<version>
561 FilePath temp_version = temp_dir.AppendASCII(version);
562 if (!UnzipExtension(extension_path, temp_version, frontend))
563 return false;
564
565 // <profile>/Extensions/<dir_name>/<version>
566 FilePath version_dir = dest_dir.AppendASCII(version);
567 if (!InstallDirSafely(extension_path, temp_version, version_dir, frontend))
568 return false;
569
570 if (!SetCurrentVersion(extension_path, dest_dir, version, frontend)) {
571 if (!file_util::Delete(version_dir, true))
572 LOG(WARNING) << "Can't remove " << dest_dir.value();
573 return false;
574 }
575
576 ReportExtensionInstalled(frontend, dest_dir);
577 return true;
578}
579
580void ExtensionsServiceBackend::ReportExtensionInstallError(
581 ExtensionsServiceFrontendInterface *frontend, const FilePath& path,
582 const std::string &error) {
583 // TODO(erikkay): note that this isn't guaranteed to work properly on Linux.
584 std::string path_str = WideToASCII(path.ToWStringHack());
585 std::string message =
586 StringPrintf("Could not install extension from '%s'. %s",
587 path_str.c_str(), error.c_str());
588 frontend->GetMessageLoop()->PostTask(FROM_HERE, NewRunnableMethod(
589 frontend, &ExtensionsServiceFrontendInterface::OnExtensionInstallError,
590 message));
591}
592
593void ExtensionsServiceBackend::ReportExtensionInstalled(
594 ExtensionsServiceFrontendInterface *frontend, FilePath path) {
595 frontend->GetMessageLoop()->PostTask(FROM_HERE, NewRunnableMethod(
596 frontend,
597 &ExtensionsServiceFrontendInterface::OnExtensionInstalled,
598 path));
599}