| Index: chrome/browser/extensions/extensions_service.cc
|
| ===================================================================
|
| --- chrome/browser/extensions/extensions_service.cc (revision 16736)
|
| +++ chrome/browser/extensions/extensions_service.cc (working copy)
|
| @@ -5,6 +5,7 @@
|
| #include "chrome/browser/extensions/extensions_service.h"
|
|
|
| #include "base/file_util.h"
|
| +#include "base/gfx/png_encoder.h"
|
| #include "base/scoped_handle.h"
|
| #include "base/scoped_temp_dir.h"
|
| #include "base/string_util.h"
|
| @@ -29,6 +30,7 @@
|
| #include "chrome/common/pref_service.h"
|
| #include "chrome/common/unzip.h"
|
| #include "chrome/common/url_constants.h"
|
| +#include "third_party/skia/include/core/SkBitmap.h"
|
|
|
| #if defined(OS_WIN)
|
| #include "base/registry.h"
|
| @@ -129,35 +131,42 @@
|
| // Cheesy... but if we don't have a ResourceDispatcherHost, assume we're
|
| // in a unit test and run the unpacker directly in-process.
|
| ExtensionUnpacker unpacker(temp_extension_path_);
|
| - bool success = unpacker.Run();
|
| - OnUnpackExtensionReply(success, unpacker.error_message());
|
| + if (unpacker.Run()) {
|
| + OnUnpackExtensionSucceeded(*unpacker.parsed_manifest(),
|
| + unpacker.decoded_images());
|
| + } else {
|
| + OnUnpackExtensionFailed(unpacker.error_message());
|
| + }
|
| }
|
| }
|
|
|
| private:
|
| // UtilityProcessHost::Client
|
| virtual void OnProcessCrashed() {
|
| - OnUnpackExtensionReply(false, "Chrome crashed while trying to install");
|
| + OnUnpackExtensionFailed("Chrome crashed while trying to install");
|
| }
|
|
|
| - virtual void OnUnpackExtensionReply(bool success,
|
| - const std::string& error_message) {
|
| - if (success) {
|
| - // The extension was unpacked to the temp dir inside our unpacking dir.
|
| - FilePath extension_dir = temp_extension_path_.DirName().AppendASCII(
|
| - ExtensionsServiceBackend::kTempExtensionName);
|
| - backend_->OnExtensionUnpacked(extension_path_, extension_dir,
|
| - expected_id_, from_external_);
|
| - } else {
|
| - backend_->ReportExtensionInstallError(extension_path_, error_message);
|
| - }
|
| + virtual void OnUnpackExtensionSucceeded(
|
| + const DictionaryValue& manifest,
|
| + const std::vector< Tuple2<SkBitmap, FilePath> >& images) {
|
| + // The extension was unpacked to the temp dir inside our unpacking dir.
|
| + FilePath extension_dir = temp_extension_path_.DirName().AppendASCII(
|
| + ExtensionsServiceBackend::kTempExtensionName);
|
| + backend_->OnExtensionUnpacked(extension_path_, extension_dir,
|
| + expected_id_, from_external_,
|
| + manifest, images);
|
| Cleanup();
|
| - Release(); // balanced in Run()
|
| }
|
|
|
| + virtual void OnUnpackExtensionFailed(const std::string& error_message) {
|
| + backend_->ReportExtensionInstallError(extension_path_, error_message);
|
| + Cleanup();
|
| + }
|
| +
|
| // Cleans up our temp directory.
|
| void Cleanup() {
|
| file_util::Delete(temp_extension_path_.DirName(), true);
|
| + Release(); // balanced in Run()
|
| }
|
|
|
| // Starts the utility process that unpacks our extension.
|
| @@ -812,19 +821,12 @@
|
| const FilePath& extension_path,
|
| const FilePath& temp_extension_dir,
|
| const std::string expected_id,
|
| - bool from_external) {
|
| - // TODO(mpcomplete): the utility process should pass up a parsed manifest that
|
| - // we rewrite in the browser.
|
| - // Bug http://code.google.com/p/chromium/issues/detail?id=11680
|
| - scoped_ptr<DictionaryValue> manifest(ReadManifest(extension_path));
|
| - if (!manifest.get()) {
|
| - // ReadManifest has already reported the extension error.
|
| - return;
|
| - }
|
| -
|
| + bool from_external,
|
| + const DictionaryValue& manifest,
|
| + const std::vector< Tuple2<SkBitmap, FilePath> >& images) {
|
| Extension extension;
|
| std::string error;
|
| - if (!extension.InitFromValue(*manifest,
|
| + if (!extension.InitFromValue(manifest,
|
| true, // require ID
|
| &error)) {
|
| ReportExtensionInstallError(extension_path, "Invalid extension manifest.");
|
| @@ -849,16 +851,61 @@
|
| was_update = true;
|
| }
|
|
|
| + // Write our parsed manifest back to disk, to ensure it doesn't contain an
|
| + // exploitable bug that can be used to compromise the browser.
|
| + std::string manifest_json;
|
| + JSONStringValueSerializer serializer(&manifest_json);
|
| + serializer.set_pretty_print(true);
|
| + if (!serializer.Serialize(manifest)) {
|
| + ReportExtensionInstallError(extension_path,
|
| + "Error serializing manifest.json.");
|
| + return;
|
| + }
|
| +
|
| + FilePath manifest_path =
|
| + temp_extension_dir.AppendASCII(Extension::kManifestFilename);
|
| + if (!file_util::WriteFile(manifest_path,
|
| + manifest_json.data(), manifest_json.size())) {
|
| + ReportExtensionInstallError(extension_path, "Error saving manifest.json.");
|
| + return;
|
| + }
|
| +
|
| + // Write our parsed images back to disk as well.
|
| + for (size_t i = 0; i < images.size(); ++i) {
|
| + const SkBitmap& image = images[i].a;
|
| + FilePath path = temp_extension_dir.Append(images[i].b);
|
| +
|
| + std::vector<unsigned char> image_data;
|
| + // TODO(mpcomplete): It's lame that we're encoding all images as PNG, even
|
| + // though they may originally be .jpg, etc. Figure something out.
|
| + // http://code.google.com/p/chromium/issues/detail?id=12459
|
| + if (!PNGEncoder::EncodeBGRASkBitmap(image, false, &image_data)) {
|
| + ReportExtensionInstallError(extension_path,
|
| + "Error re-encoding theme image.");
|
| + return;
|
| + }
|
| +
|
| + // Note: we're overwriting existing files that the utility process wrote,
|
| + // so we can be sure the directory exists.
|
| + const char* image_data_ptr = reinterpret_cast<const char*>(&image_data[0]);
|
| + if (!file_util::WriteFile(path, image_data_ptr, image_data.size())) {
|
| + ReportExtensionInstallError(extension_path, "Error saving theme image.");
|
| + return;
|
| + }
|
| + }
|
| +
|
| // <profile>/Extensions/<dir_name>/<version>
|
| FilePath version_dir = dest_dir.AppendASCII(version);
|
| +
|
| + // If anything fails after this, we want to delete the extension dir.
|
| + ScopedTempDir scoped_version_dir;
|
| + scoped_version_dir.Set(version_dir);
|
| +
|
| if (!InstallDirSafely(temp_extension_dir, version_dir))
|
| return;
|
|
|
| - if (!SetCurrentVersion(dest_dir, version)) {
|
| - if (!file_util::Delete(version_dir, true))
|
| - LOG(WARNING) << "Can't remove " << dest_dir.value();
|
| + if (!SetCurrentVersion(dest_dir, version))
|
| return;
|
| - }
|
|
|
| // To mark that this extension was installed from an external source, create a
|
| // zero-length file. At load time, this is used to indicate that the
|
| @@ -872,7 +919,7 @@
|
| // Load the extension immediately and then report installation success. We
|
| // don't load extensions for external installs because external installation
|
| // occurs before the normal startup so we just let startup pick them up. We
|
| - // don't notify installation because there is no UI or external install so
|
| + // don't notify installation because there is no UI for external install so
|
| // there is nobody to notify.
|
| if (!from_external) {
|
| Extension* extension = LoadExtension(version_dir, true); // require id
|
| @@ -890,6 +937,8 @@
|
| // Hand off ownership of the loaded extensions to the frontend.
|
| ReportExtensionsLoaded(extensions.release());
|
| }
|
| +
|
| + scoped_version_dir.Take();
|
| }
|
|
|
| void ExtensionsServiceBackend::ReportExtensionInstallError(
|
|
|