summaryrefslogtreecommitdiffstats
path: root/chrome/common/extensions
diff options
context:
space:
mode:
authormpcomplete@google.com <mpcomplete@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-22 19:02:19 +0000
committermpcomplete@google.com <mpcomplete@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-22 19:02:19 +0000
commit902f7cd528743a286439cfca87266aa1b723a8bf (patch)
tree986e45ef007fba1dd21436bcf1a00470ca6ae1e4 /chrome/common/extensions
parent5be94dffc6b19e0ed8397879ad60fe06e498cb3a (diff)
downloadchromium_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/common/extensions')
-rw-r--r--chrome/common/extensions/extension_unpacker.cc145
-rw-r--r--chrome/common/extensions/extension_unpacker.h30
2 files changed, 155 insertions, 20 deletions
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_;