Index: chrome/common/extensions/extension_unpacker.cc |
=================================================================== |
--- chrome/common/extensions/extension_unpacker.cc (revision 16736) |
+++ chrome/common/extensions/extension_unpacker.cc (working copy) |
@@ -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 @@ |
// 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,47 +203,117 @@ |
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. |
+ scoped_ptr<DictionaryValue> header_manifest(ReadPackageHeader()); |
+ if (!header_manifest.get()) { |
+ // ReadPackageHeader has already reported the extension error. |
return false; |
} |
- Extension extension; |
+ |
+ // 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 (!extension.InitFromValue(*manifest, |
- true, // require ID |
- &error)) { |
- SetError("Invalid extension manifest."); |
+ if (!header_extension.InitFromValue(*header_manifest, |
+ true, // require ID |
+ &error)) { |
+ SetError(error); |
return false; |
} |
- // ID is required for installed extensions. |
- if (extension.id().empty()) { |
- SetError("Required value 'id' is missing."); |
- 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; |
+} |
+ |
void ExtensionUnpacker::SetError(const std::string &error) { |
error_message_ = error; |
} |