diff options
author | mpcomplete@google.com <mpcomplete@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-22 19:02:19 +0000 |
---|---|---|
committer | mpcomplete@google.com <mpcomplete@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-22 19:02:19 +0000 |
commit | 902f7cd528743a286439cfca87266aa1b723a8bf (patch) | |
tree | 986e45ef007fba1dd21436bcf1a00470ca6ae1e4 /chrome | |
parent | 5be94dffc6b19e0ed8397879ad60fe06e498cb3a (diff) | |
download | chromium_src-902f7cd528743a286439cfca87266aa1b723a8bf.zip chromium_src-902f7cd528743a286439cfca87266aa1b723a8bf.tar.gz chromium_src-902f7cd528743a286439cfca87266aa1b723a8bf.tar.bz2 |
Have the browser process rewrite manifest.json and theme/page action images
that the extension unpacker process parsed.
BUG=11680
Review URL: http://codereview.chromium.org/115595
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@16768 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/browser/extensions/extensions_service.cc | 111 | ||||
-rw-r--r-- | chrome/browser/extensions/extensions_service.h | 14 | ||||
-rw-r--r-- | chrome/browser/extensions/extensions_service_unittest.cc | 16 | ||||
-rw-r--r-- | chrome/browser/utility_process_host.cc | 6 | ||||
-rw-r--r-- | chrome/browser/utility_process_host.h | 18 | ||||
-rw-r--r-- | chrome/common/extensions/extension_unpacker.cc | 145 | ||||
-rw-r--r-- | chrome/common/extensions/extension_unpacker.h | 30 | ||||
-rw-r--r-- | chrome/common/render_messages.h | 6 | ||||
-rw-r--r-- | chrome/common/render_messages_internal.h | 16 | ||||
-rw-r--r-- | chrome/test/data/extensions/page_action.crx | bin | 0 -> 9116 bytes | |||
-rw-r--r-- | chrome/utility/utility_thread.cc | 11 |
11 files changed, 304 insertions, 69 deletions
diff --git a/chrome/browser/extensions/extensions_service.cc b/chrome/browser/extensions/extensions_service.cc index 04ccd13..e62f537 100644 --- a/chrome/browser/extensions/extensions_service.cc +++ b/chrome/browser/extensions/extensions_service.cc @@ -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 @@ class ExtensionsServiceBackend::UnpackerClient // 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(); + } + + virtual void OnUnpackExtensionFailed(const std::string& error_message) { + backend_->ReportExtensionInstallError(extension_path_, error_message); Cleanup(); - Release(); // balanced in Run() } // 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 @@ void ExtensionsServiceBackend::OnExtensionUnpacked( 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 @@ void ExtensionsServiceBackend::OnExtensionUnpacked( 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 @@ void ExtensionsServiceBackend::OnExtensionUnpacked( // 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 @@ void ExtensionsServiceBackend::OnExtensionUnpacked( // Hand off ownership of the loaded extensions to the frontend. ReportExtensionsLoaded(extensions.release()); } + + scoped_version_dir.Take(); } void ExtensionsServiceBackend::ReportExtensionInstallError( diff --git a/chrome/browser/extensions/extensions_service.h b/chrome/browser/extensions/extensions_service.h index 47abcbd..56bebee 100644 --- a/chrome/browser/extensions/extensions_service.h +++ b/chrome/browser/extensions/extensions_service.h @@ -22,6 +22,7 @@ class GURL; class PrefService; class Profile; class ResourceDispatcherHost; +class SkBitmap; class SiteInstance; class UserScriptMaster; typedef std::vector<Extension*> ExtensionList; @@ -171,10 +172,15 @@ class ExtensionsServiceBackend // Finish installing an extension after it has been unpacked to // |temp_extension_dir| by our utility process. If |expected_id| is not // empty, it's verified against the extension's manifest before installation. - void OnExtensionUnpacked(const FilePath& extension_path, - const FilePath& temp_extension_dir, - const std::string expected_id, - bool from_external); + // |manifest| and |images| are parsed information from the extension that + // we want to write to disk in the browser process. + void OnExtensionUnpacked( + const FilePath& extension_path, + const FilePath& temp_extension_dir, + const std::string expected_id, + bool from_external, + const DictionaryValue& manifest, + const std::vector< Tuple2<SkBitmap, FilePath> >& images); // Notify the frontend that there was an error loading an extension. void ReportExtensionLoadError(const FilePath& extension_path, diff --git a/chrome/browser/extensions/extensions_service_unittest.cc b/chrome/browser/extensions/extensions_service_unittest.cc index 8471fbe..7b0a88f 100644 --- a/chrome/browser/extensions/extensions_service_unittest.cc +++ b/chrome/browser/extensions/extensions_service_unittest.cc @@ -77,6 +77,7 @@ class ExtensionsServiceTest profile_.reset(new TestingProfile()); service_ = new ExtensionsService(profile_.get(), &loop_, &loop_, registry_path_); + total_successes_ = 0; } static void SetUpTestCase() { @@ -126,10 +127,13 @@ class ExtensionsServiceTest loop_.RunAllPending(); std::vector<std::string> errors = GetErrors(); if (should_succeed) { + ++total_successes_; + EXPECT_TRUE(installed_) << path.value(); EXPECT_EQ(1u, loaded_.size()) << path.value(); EXPECT_EQ(0u, errors.size()) << path.value(); - EXPECT_EQ(1u, service_->extensions()->size()) << path.value(); + EXPECT_EQ(total_successes_, service_->extensions()->size()) << + path.value(); EXPECT_TRUE(service_->GetExtensionByID(loaded_[0]->id())) << path.value(); for (std::vector<std::string>::iterator err = errors.begin(); err != errors.end(); ++err) { @@ -148,6 +152,7 @@ class ExtensionsServiceTest protected: scoped_ptr<TestingProfile> profile_; scoped_refptr<ExtensionsService> service_; + size_t total_successes_; MessageLoop loop_; std::vector<Extension*> loaded_; std::string unloaded_id_; @@ -303,6 +308,14 @@ TEST_F(ExtensionsServiceTest, InstallExtension) { TestInstallExtension(path, true); // TODO(erikkay): verify the contents of the installed extension. + // An extension with theme images. + path = extensions_path.AppendASCII("theme.crx"); + TestInstallExtension(path, true); + + // An extension with page actions. + path = extensions_path.AppendASCII("page_action.crx"); + TestInstallExtension(path, true); + // 0-length extension file. path = extensions_path.AppendASCII("not_an_extension.crx"); TestInstallExtension(path, false); @@ -369,6 +382,7 @@ TEST_F(ExtensionsServiceTest, UninstallExtension) { // Uninstall it. service_->UninstallExtension(extension_id); + total_successes_ = 0; // We should get an unload notification. ASSERT_TRUE(unloaded_id_.length()); diff --git a/chrome/browser/utility_process_host.cc b/chrome/browser/utility_process_host.cc index 20b34d5..4c3d83b 100644 --- a/chrome/browser/utility_process_host.cc +++ b/chrome/browser/utility_process_host.cc @@ -81,7 +81,9 @@ void UtilityProcessHost::OnChannelError() { void UtilityProcessHost::Client::OnMessageReceived( const IPC::Message& message) { IPC_BEGIN_MESSAGE_MAP(UtilityProcessHost, message) - IPC_MESSAGE_HANDLER(UtilityHostMsg_UnpackExtension_Reply, - Client::OnUnpackExtensionReply) + IPC_MESSAGE_HANDLER(UtilityHostMsg_UnpackExtension_Succeeded, + Client::OnUnpackExtensionSucceeded) + IPC_MESSAGE_HANDLER(UtilityHostMsg_UnpackExtension_Failed, + Client::OnUnpackExtensionFailed) IPC_END_MESSAGE_MAP_EX() } diff --git a/chrome/browser/utility_process_host.h b/chrome/browser/utility_process_host.h index 8cb281e..0efaa56 100644 --- a/chrome/browser/utility_process_host.h +++ b/chrome/browser/utility_process_host.h @@ -6,6 +6,7 @@ #define CHROME_BROWSER_UTILITY_PROCESS_HOST_H_ #include <string> +#include <vector> #include "base/basictypes.h" #include "base/ref_counted.h" @@ -14,7 +15,9 @@ #include "chrome/common/ipc_channel.h" class CommandLine; +class DictionaryValue; class MessageLoop; +class SkBitmap; // This class acts as the browser-side host to a utility child process. A // utility process is a short-lived sandboxed process that is created to run @@ -32,10 +35,17 @@ class UtilityProcessHost : public ChildProcessHost { // Called when the process has crashed. virtual void OnProcessCrashed() {} - // Called when the process sends a reply to an UnpackExtension message. - // If success if false, error_message contains a description of the problem. - virtual void OnUnpackExtensionReply(bool success, - const std::string& error_message) {} + // Called when the extension has unpacked successfully. |manifest| is the + // parsed manifest.json file. |images| contains a list of decoded images + // and the associated paths where those images live on disk. + virtual void OnUnpackExtensionSucceeded( + const DictionaryValue& manifest, + const std::vector< Tuple2<SkBitmap, FilePath> >& images) {} + + // Called when an error occurred while unpacking the extension. + // |error_message| contains a description of the problem. + virtual void OnUnpackExtensionFailed(const std::string& error_message) {} + private: friend class UtilityProcessHost; void OnMessageReceived(const IPC::Message& message); diff --git a/chrome/common/extensions/extension_unpacker.cc b/chrome/common/extensions/extension_unpacker.cc index 1849ec8..c59b113 100644 --- a/chrome/common/extensions/extension_unpacker.cc +++ b/chrome/common/extensions/extension_unpacker.cc @@ -19,6 +19,8 @@ #include "chrome/common/notification_service.h" #include "chrome/common/unzip.h" #include "chrome/common/url_constants.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "webkit/glue/image_decoder.h" namespace { const char kCurrentVersionFileName[] = "Current Version"; @@ -59,12 +61,47 @@ const char kExternalInstallFile[] = "EXTERNAL_INSTALL"; // The version of the extension package that this code understands. const uint32 kExpectedVersion = 1; +} // namespace + +static SkBitmap DecodeImage(const FilePath& path) { + // Read the file from disk. + std::string file_contents; + if (!file_util::PathExists(path) || + !file_util::ReadFileToString(path, &file_contents)) { + return SkBitmap(); + } + + // Decode the image using WebKit's image decoder. + const unsigned char* data = + reinterpret_cast<const unsigned char*>(file_contents.data()); + webkit_glue::ImageDecoder decoder; + return decoder.Decode(data, file_contents.length()); +} + +static bool PathContainsParentDirectory(const FilePath& path) { + const FilePath::StringType kSeparators(FilePath::kSeparators); + const FilePath::StringType kParentDirectory(FilePath::kParentDirectory); + const size_t npos = FilePath::StringType::npos; + const FilePath::StringType& value = path.value(); + + for (size_t i = 0; i < value.length(); ) { + i = value.find(kParentDirectory, i); + if (i != npos) { + if ((i == 0 || kSeparators.find(value[i-1]) == npos) && + (i+1 < value.length() || kSeparators.find(value[i+1]) == npos)) { + return true; + } + ++i; + } + } + + return false; } // The extension file format is a header, followed by the manifest, followed // by the zip file. The header is a magic number, a version, the size of the // header, and the size of the manifest. These ints are 4 byte little endian. -DictionaryValue* ExtensionUnpacker::ReadManifest() { +DictionaryValue* ExtensionUnpacker::ReadPackageHeader() { ScopedStdioHandle file(file_util::OpenFile(extension_path_, "rb")); if (!file.get()) { SetError("no such extension file"); @@ -166,44 +203,114 @@ DictionaryValue* ExtensionUnpacker::ReadManifest() { return manifest; } +DictionaryValue* ExtensionUnpacker::ReadManifest() { + FilePath manifest_path = + temp_install_dir_.AppendASCII(Extension::kManifestFilename); + if (!file_util::PathExists(manifest_path)) { + SetError(Extension::kInvalidManifestError); + return NULL; + } + + JSONFileValueSerializer serializer(manifest_path); + std::string error; + scoped_ptr<Value> root(serializer.Deserialize(&error)); + if (!root.get()) { + SetError(error); + return NULL; + } + + if (!root->IsType(Value::TYPE_DICTIONARY)) { + SetError(Extension::kInvalidManifestError); + return NULL; + } + + return static_cast<DictionaryValue*>(root.release()); +} + bool ExtensionUnpacker::Run() { LOG(INFO) << "Installing extension " << extension_path_.value(); // Read and verify the extension. - scoped_ptr<DictionaryValue> manifest(ReadManifest()); - if (!manifest.get()) { - // ReadManifest has already reported the extension error. - return false; - } - Extension extension; - std::string error; - if (!extension.InitFromValue(*manifest, - true, // require ID - &error)) { - SetError("Invalid extension manifest."); + scoped_ptr<DictionaryValue> header_manifest(ReadPackageHeader()); + if (!header_manifest.get()) { + // ReadPackageHeader has already reported the extension error. return false; } - // ID is required for installed extensions. - if (extension.id().empty()) { - SetError("Required value 'id' is missing."); + // TODO(mpcomplete): it looks like this isn't actually necessary. We don't + // use header_extension, and we check that the unzipped manifest is valid. + Extension header_extension; + std::string error; + if (!header_extension.InitFromValue(*header_manifest, + true, // require ID + &error)) { + SetError(error); return false; } // <profile>/Extensions/INSTALL_TEMP/<version> - std::string version = extension.VersionString(); - FilePath temp_install = + temp_install_dir_ = extension_path_.DirName().AppendASCII(kTempExtensionName); - if (!file_util::CreateDirectory(temp_install)) { + if (!file_util::CreateDirectory(temp_install_dir_)) { SetError("Couldn't create directory for unzipping."); return false; } - if (!Unzip(extension_path_, temp_install, NULL)) { + if (!Unzip(extension_path_, temp_install_dir_, NULL)) { SetError("Couldn't unzip extension."); return false; } + // Parse the manifest. + parsed_manifest_.reset(ReadManifest()); + if (!parsed_manifest_.get()) + return false; // Error was already reported. + + // Re-read the actual manifest into our extension struct. + Extension extension; + if (!extension.InitFromValue(*parsed_manifest_, + true, // require ID + &error)) { + SetError(error); + return false; + } + + // Decode any images that the browser needs to display. + DictionaryValue* images = extension.GetThemeImages(); + if (images) { + for (DictionaryValue::key_iterator it = images->begin_keys(); + it != images->end_keys(); ++it) { + std::wstring val; + if (images->GetString(*it, &val)) { + if (!AddDecodedImage(FilePath::FromWStringHack(val))) + return false; // Error was already reported. + } + } + } + + for (PageActionMap::const_iterator it = extension.page_actions().begin(); + it != extension.page_actions().end(); ++it) { + if (!AddDecodedImage(it->second->icon_path())) + return false; // Error was already reported. + } + + return true; +} + +bool ExtensionUnpacker::AddDecodedImage(const FilePath& path) { + // Make sure it's not referencing a file outside the extension's subdir. + if (path.IsAbsolute() || PathContainsParentDirectory(path)) { + SetError("Path names must not be absolute or contain '..'."); + return false; + } + + SkBitmap image_bitmap = DecodeImage(temp_install_dir_.Append(path)); + if (image_bitmap.isNull()) { + SetError("Could not decode theme image."); + return false; + } + + decoded_images_.push_back(MakeTuple(image_bitmap, path)); return true; } diff --git a/chrome/common/extensions/extension_unpacker.h b/chrome/common/extensions/extension_unpacker.h index 6d5fbf3..7e83363 100644 --- a/chrome/common/extensions/extension_unpacker.h +++ b/chrome/common/extensions/extension_unpacker.h @@ -6,16 +6,22 @@ #define CHROME_COMMON_EXTENSIONS_EXTENSION_UNPACKER_H_ #include <string> +#include <vector> #include "base/file_path.h" +#include "base/scoped_ptr.h" +#include "base/tuple.h" class DictionaryValue; +class SkBitmap; // Implements IO for the ExtensionsService. // TODO(aa): Extract an interface out of this for testing the frontend, once the // frontend has significant logic to test. class ExtensionUnpacker { public: + typedef std::vector< Tuple2<SkBitmap, FilePath> > DecodedImages; + explicit ExtensionUnpacker(const FilePath& extension_path) : extension_path_(extension_path) {} @@ -24,18 +30,40 @@ class ExtensionUnpacker { bool Run(); const std::string& error_message() { return error_message_; } + DictionaryValue* parsed_manifest() { + return parsed_manifest_.get(); + } + const DecodedImages& decoded_images() { return decoded_images_; } private: - // Read the manifest from the front of the extension file. + // Parse the header on the front of the extension file and return the manifest + // inside it. Caller takes ownership of return value. + DictionaryValue* ReadPackageHeader(); + + // Parse the manifest.json file inside the extension (not in the header). // Caller takes ownership of return value. DictionaryValue* ReadManifest(); + // Decodes the image at the given path and puts it in our list of decoded + // images. + bool AddDecodedImage(const FilePath& path); + // Set the error message. void SetError(const std::string& error); // The extension to unpack. FilePath extension_path_; + // The place we unpacked the extension to. + FilePath temp_install_dir_; + + // The parsed version of the manifest JSON contained in the extension. + scoped_ptr<DictionaryValue> parsed_manifest_; + + // A list of decoded images and the paths where those images came from. Paths + // are relative to the manifest file. + DecodedImages decoded_images_; + // The last error message that was set. Empty if there were no errors. std::string error_message_; diff --git a/chrome/common/render_messages.h b/chrome/common/render_messages.h index e3028a2..5128dfd 100644 --- a/chrome/common/render_messages.h +++ b/chrome/common/render_messages.h @@ -47,6 +47,8 @@ namespace base { class Time; } +class SkBitmap; + // Parameters structure for ViewMsg_Navigate, which has too many data // parameters to be reasonably put in a predefined IPC message. struct ViewMsg_Navigate_Params { @@ -385,6 +387,10 @@ struct ViewHostMsg_ShowPopup_Params { std::vector<WebMenuItem> popup_items; }; +// Used by UtilityHostMsg_UnpackExtension_Succeeded. We must define a typedef +// because the preprocessor is stupid about commas inside macros. +typedef Tuple2<SkBitmap, FilePath> UnpackExtension_ImagePathPair; + namespace IPC { template <> diff --git a/chrome/common/render_messages_internal.h b/chrome/common/render_messages_internal.h index f359677..a62b61d 100644 --- a/chrome/common/render_messages_internal.h +++ b/chrome/common/render_messages_internal.h @@ -16,6 +16,7 @@ #include "base/gfx/rect.h" #include "base/gfx/native_widget_types.h" #include "base/shared_memory.h" +#include "base/values.h" #include "chrome/common/ipc_message_macros.h" #include "chrome/common/transport_dib.h" #include "third_party/skia/include/core/SkBitmap.h" @@ -1393,9 +1394,16 @@ IPC_BEGIN_MESSAGES(ViewHost) // These are messages from the utility process to the browser. They're here // because we ran out of spare message types. - // Reply when the utility process is done unpacking an extension. |success| - // argument is true if the extension unpacked and verified successfully. - IPC_MESSAGE_CONTROL2(UtilityHostMsg_UnpackExtension_Reply, - bool /* success */, + // Reply when the utility process is done unpacking an extension. |manifest| + // is the parsed manifest.json file. |images| is a list of decoded images + // and the path to where they should be written to, relative to the + // manifest file. + IPC_MESSAGE_CONTROL2(UtilityHostMsg_UnpackExtension_Succeeded, + DictionaryValue /* manifest */, + std::vector<UnpackExtension_ImagePathPair> /* images */) + + // Reply when the utility process has failed while unpacking an extension. + // |error_message| is a user-displayable explanation of what went wrong. + IPC_MESSAGE_CONTROL1(UtilityHostMsg_UnpackExtension_Failed, std::string /* error_message, if any */) IPC_END_MESSAGES(ViewHost) diff --git a/chrome/test/data/extensions/page_action.crx b/chrome/test/data/extensions/page_action.crx Binary files differnew file mode 100644 index 0000000..c9cba21 --- /dev/null +++ b/chrome/test/data/extensions/page_action.crx diff --git a/chrome/utility/utility_thread.cc b/chrome/utility/utility_thread.cc index 269a86b..2330fd5 100644 --- a/chrome/utility/utility_thread.cc +++ b/chrome/utility/utility_thread.cc @@ -4,6 +4,7 @@ #include "chrome/utility/utility_thread.h" +#include "base/values.h" #include "chrome/common/child_process.h" #include "chrome/common/extensions/extension_unpacker.h" #include "chrome/common/render_messages.h" @@ -32,9 +33,13 @@ void UtilityThread::OnControlMessageReceived(const IPC::Message& msg) { void UtilityThread::OnUnpackExtension(const FilePath& extension_path) { ExtensionUnpacker unpacker(extension_path); - bool success = unpacker.Run(); - Send(new UtilityHostMsg_UnpackExtension_Reply(success, - unpacker.error_message())); + if (unpacker.Run()) { + Send(new UtilityHostMsg_UnpackExtension_Succeeded( + *unpacker.parsed_manifest(), unpacker.decoded_images())); + } else { + Send(new UtilityHostMsg_UnpackExtension_Failed( + unpacker.error_message())); + } ChildProcess::current()->ReleaseProcess(); } |