summaryrefslogtreecommitdiffstats
path: root/chrome/browser
diff options
context:
space:
mode:
authorcira@chromium.org <cira@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-11-18 18:02:47 +0000
committercira@chromium.org <cira@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-11-18 18:02:47 +0000
commit9428edc493bf5097cc74fca5fcaf4506c4068668 (patch)
treea5db8e196a4e71c71d54ded4c552b2a4d8c50361 /chrome/browser
parentecc73b2f562a20611804cc07dcff59922dbd09b4 (diff)
downloadchromium_src-9428edc493bf5097cc74fca5fcaf4506c4068668.zip
chromium_src-9428edc493bf5097cc74fca5fcaf4506c4068668.tar.gz
chromium_src-9428edc493bf5097cc74fca5fcaf4506c4068668.tar.bz2
Parse messages.json in ExtensionUnpacker (like we do for manifest) and pass them to sandboxed_extension_unpacker.
Added unittest files for unpacker and sandboxed unpacker. TEST=Try loading any of the unpacker samples added in this CL. They should either pass, or show error and fail. BUG=27362 Review URL: http://codereview.chromium.org/390019 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@32345 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
-rw-r--r--chrome/browser/extensions/extension_file_util.cc72
-rw-r--r--chrome/browser/extensions/sandboxed_extension_unpacker.cc218
-rw-r--r--chrome/browser/extensions/sandboxed_extension_unpacker.h21
-rw-r--r--chrome/browser/extensions/sandboxed_extension_unpacker_unittest.cc160
-rw-r--r--chrome/browser/utility_process_host.h11
-rw-r--r--chrome/browser/utility_process_host_unittest.cc3
6 files changed, 396 insertions, 89 deletions
diff --git a/chrome/browser/extensions/extension_file_util.cc b/chrome/browser/extensions/extension_file_util.cc
index 52c2c78..e7ecd08 100644
--- a/chrome/browser/extensions/extension_file_util.cc
+++ b/chrome/browser/extensions/extension_file_util.cc
@@ -16,8 +16,13 @@
#include "chrome/common/json_value_serializer.h"
#include "net/base/file_stream.h"
+namespace errors = extension_manifest_errors;
+
namespace extension_file_util {
+// Validates locale info. Doesn't check if messages.json files are valid.
+static bool ValidateLocaleInfo(const Extension& extension, std::string* error);
+
const char kInstallDirectoryName[] = "Extensions";
// TODO(mpcomplete): obsolete. remove after migration period.
// http://code.google.com/p/chromium/issues/detail?id=19733
@@ -270,6 +275,10 @@ bool ValidateExtension(Extension* extension, std::string* error) {
}
}
+ // Validate locale info.
+ if (!ValidateLocaleInfo(*extension, error))
+ return false;
+
// Check children of extension root to see if any of them start with _ and is
// not on the reserved list.
if (!CheckForIllegalFilenames(extension->path(), error)) {
@@ -382,6 +391,69 @@ ExtensionMessageBundle* LoadLocaleInfo(const FilePath& extension_path,
return message_bundle;
}
+static bool ValidateLocaleInfo(const Extension& extension, std::string* error) {
+ // default_locale and _locales have to be both present or both missing.
+ const FilePath path = extension.path().AppendASCII(Extension::kLocaleFolder);
+ bool path_exists = file_util::PathExists(path);
+ std::string default_locale = extension.default_locale();
+
+ // If both default locale and _locales folder are empty, skip verification.
+ if (!default_locale.empty() || path_exists) {
+ if (default_locale.empty() && path_exists) {
+ *error = errors::kLocalesNoDefaultLocaleSpecified;
+ return false;
+ } else if (!default_locale.empty() && !path_exists) {
+ *error = errors::kLocalesTreeMissing;
+ return false;
+ }
+
+ // Treat all folders under _locales as valid locales.
+ file_util::FileEnumerator locales(path,
+ false,
+ file_util::FileEnumerator::DIRECTORIES);
+
+ FilePath locale_path = locales.Next();
+ if (locale_path.empty()) {
+ *error = errors::kLocalesTreeMissing;
+ return false;
+ }
+
+ const FilePath default_locale_path = path.AppendASCII(default_locale);
+ bool has_default_locale_message_file = false;
+ do {
+ // Skip any strings with '.'. This happens sometimes, for example with
+ // '.svn' directories.
+ FilePath relative_path;
+ if (!extension.path().AppendRelativePath(locale_path, &relative_path))
+ NOTREACHED();
+ std::wstring subdir(relative_path.ToWStringHack());
+ if (std::find(subdir.begin(), subdir.end(), L'.') != subdir.end())
+ continue;
+
+ FilePath messages_path =
+ locale_path.AppendASCII(Extension::kMessagesFilename);
+
+ if (!file_util::PathExists(messages_path)) {
+ *error = StringPrintf(
+ "%s %s", errors::kLocalesMessagesFileMissing,
+ WideToUTF8(messages_path.ToWStringHack()).c_str());
+ return false;
+ }
+
+ if (locale_path == default_locale_path)
+ has_default_locale_message_file = true;
+ } while (!(locale_path = locales.Next()).empty());
+
+ // Only message file for default locale has to exist.
+ if (!has_default_locale_message_file) {
+ *error = errors::kLocalesNoDefaultMessages;
+ return false;
+ }
+ }
+
+ return true;
+}
+
bool CheckForIllegalFilenames(const FilePath& extension_path,
std::string* error) {
// Reserved underscore names.
diff --git a/chrome/browser/extensions/sandboxed_extension_unpacker.cc b/chrome/browser/extensions/sandboxed_extension_unpacker.cc
index 160a0de..65ccded 100644
--- a/chrome/browser/extensions/sandboxed_extension_unpacker.cc
+++ b/chrome/browser/extensions/sandboxed_extension_unpacker.cc
@@ -21,7 +21,6 @@
#include "chrome/common/extensions/extension_unpacker.h"
#include "chrome/common/json_value_serializer.h"
#include "net/base/base64.h"
-
#include "third_party/skia/include/core/SkBitmap.h"
const char SandboxedExtensionUnpacker::kExtensionHeaderMagic[] = "Cr24";
@@ -83,7 +82,8 @@ void SandboxedExtensionUnpacker::Start() {
// Otherwise, unpack the extension in this process.
ExtensionUnpacker unpacker(temp_crx_path);
if (unpacker.Run() && unpacker.DumpImagesToFile())
- OnUnpackExtensionSucceeded(*unpacker.parsed_manifest());
+ OnUnpackExtensionSucceeded(*unpacker.parsed_manifest(),
+ *unpacker.parsed_catalogs());
else
OnUnpackExtensionFailed(unpacker.error_message());
}
@@ -97,38 +97,16 @@ void SandboxedExtensionUnpacker::StartProcessOnIOThread(
}
void SandboxedExtensionUnpacker::OnUnpackExtensionSucceeded(
- const DictionaryValue& manifest) {
- DCHECK(ChromeThread::CurrentlyOn(thread_identifier_));
+ const DictionaryValue& manifest,
+ const DictionaryValue& catalogs) {
+ // Skip check for unittests.
+ if (thread_identifier_ != ChromeThread::ID_COUNT)
+ DCHECK(ChromeThread::CurrentlyOn(thread_identifier_));
got_response_ = true;
- ExtensionUnpacker::DecodedImages images;
- if (!ExtensionUnpacker::ReadImagesFromFile(temp_dir_.path(), &images)) {
- ReportFailure("Couldn't read image data from disk.");
+ scoped_ptr<DictionaryValue> final_manifest(RewriteManifestFile(manifest));
+ if (!final_manifest.get())
return;
- }
-
- // Add the public key extracted earlier to the parsed manifest and overwrite
- // the original manifest. We do this to ensure the manifest doesn't contain an
- // exploitable bug that could be used to compromise the browser.
- scoped_ptr<DictionaryValue> final_manifest(
- static_cast<DictionaryValue*>(manifest.DeepCopy()));
- final_manifest->SetString(extension_manifest_keys::kPublicKey, public_key_);
-
- std::string manifest_json;
- JSONStringValueSerializer serializer(&manifest_json);
- serializer.set_pretty_print(true);
- if (!serializer.Serialize(*final_manifest)) {
- ReportFailure("Error serializing manifest.json.");
- return;
- }
-
- FilePath manifest_path =
- extension_root_.AppendASCII(Extension::kManifestFilename);
- if (!file_util::WriteFile(manifest_path,
- manifest_json.data(), manifest_json.size())) {
- ReportFailure("Error saving manifest.json.");
- return;
- }
// Create an extension object that refers to the temporary location the
// extension was unpacked to. We use this until the extension is finally
@@ -144,55 +122,11 @@ void SandboxedExtensionUnpacker::OnUnpackExtensionSucceeded(
return;
}
- // Delete any images that may be used by the browser. We're going to write
- // out our own versions of the parsed images, and we want to make sure the
- // originals are gone for good.
- std::set<FilePath> image_paths = extension_->GetBrowserImages();
- if (image_paths.size() != images.size()) {
- ReportFailure("Decoded images don't match what's in the manifest.");
+ if (!RewriteImageFiles())
return;
- }
-
- for (std::set<FilePath>::iterator it = image_paths.begin();
- it != image_paths.end(); ++it) {
- FilePath path = *it;
- if (path.IsAbsolute() || path.ReferencesParent()) {
- ReportFailure("Invalid path for browser image.");
- return;
- }
- if (!file_util::Delete(extension_root_.Append(path), false)) {
- ReportFailure("Error removing old image file.");
- 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_suffix = images[i].b;
- if (path_suffix.IsAbsolute() || path_suffix.ReferencesParent()) {
- ReportFailure("Invalid path for bitmap image.");
- return;
- }
- FilePath path = extension_root_.Append(path_suffix);
-
- 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 (!gfx::PNGCodec::EncodeBGRASkBitmap(image, false, &image_data)) {
- ReportFailure("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())) {
- ReportFailure("Error saving theme image.");
- return;
- }
- }
+ if (!RewriteCatalogFiles(catalogs))
+ return;
ReportSuccess();
}
@@ -310,3 +244,131 @@ void SandboxedExtensionUnpacker::ReportSuccess() {
client_->OnUnpackSuccess(temp_dir_.Take(), extension_root_,
extension_.release());
}
+
+DictionaryValue* SandboxedExtensionUnpacker::RewriteManifestFile(
+ const DictionaryValue& manifest) {
+ // Add the public key extracted earlier to the parsed manifest and overwrite
+ // the original manifest. We do this to ensure the manifest doesn't contain an
+ // exploitable bug that could be used to compromise the browser.
+ scoped_ptr<DictionaryValue> final_manifest(
+ static_cast<DictionaryValue*>(manifest.DeepCopy()));
+ final_manifest->SetString(extension_manifest_keys::kPublicKey, public_key_);
+
+ std::string manifest_json;
+ JSONStringValueSerializer serializer(&manifest_json);
+ serializer.set_pretty_print(true);
+ if (!serializer.Serialize(*final_manifest)) {
+ ReportFailure("Error serializing manifest.json.");
+ return NULL;
+ }
+
+ FilePath manifest_path =
+ extension_root_.AppendASCII(Extension::kManifestFilename);
+ if (!file_util::WriteFile(manifest_path,
+ manifest_json.data(), manifest_json.size())) {
+ ReportFailure("Error saving manifest.json.");
+ return NULL;
+ }
+
+ return final_manifest.release();
+}
+
+bool SandboxedExtensionUnpacker::RewriteImageFiles() {
+ ExtensionUnpacker::DecodedImages images;
+ if (!ExtensionUnpacker::ReadImagesFromFile(temp_dir_.path(), &images)) {
+ ReportFailure("Couldn't read image data from disk.");
+ return false;
+ }
+
+ // Delete any images that may be used by the browser. We're going to write
+ // out our own versions of the parsed images, and we want to make sure the
+ // originals are gone for good.
+ std::set<FilePath> image_paths = extension_->GetBrowserImages();
+ if (image_paths.size() != images.size()) {
+ ReportFailure("Decoded images don't match what's in the manifest.");
+ return false;
+ }
+
+ for (std::set<FilePath>::iterator it = image_paths.begin();
+ it != image_paths.end(); ++it) {
+ FilePath path = *it;
+ if (path.IsAbsolute() || path.ReferencesParent()) {
+ ReportFailure("Invalid path for browser image.");
+ return false;
+ }
+ if (!file_util::Delete(extension_root_.Append(path), false)) {
+ ReportFailure("Error removing old image file.");
+ return false;
+ }
+ }
+
+ // 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_suffix = images[i].b;
+ if (path_suffix.IsAbsolute() || path_suffix.ReferencesParent()) {
+ ReportFailure("Invalid path for bitmap image.");
+ return false;
+ }
+ FilePath path = extension_root_.Append(path_suffix);
+
+ 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 (!gfx::PNGCodec::EncodeBGRASkBitmap(image, false, &image_data)) {
+ ReportFailure("Error re-encoding theme image.");
+ return false;
+ }
+
+ // 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())) {
+ ReportFailure("Error saving theme image.");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool SandboxedExtensionUnpacker::RewriteCatalogFiles(
+ const DictionaryValue& catalogs) {
+ // Write our parsed catalogs back to disk.
+ DictionaryValue::key_iterator key_it = catalogs.begin_keys();
+ for (; key_it != catalogs.end_keys(); ++key_it) {
+ DictionaryValue* catalog;
+ if (!catalogs.GetDictionary(*key_it, &catalog)) {
+ ReportFailure("Invalid catalog data.");
+ return false;
+ }
+
+ FilePath relative_path = FilePath::FromWStringHack(*key_it);
+ relative_path = relative_path.AppendASCII(Extension::kMessagesFilename);
+ if (relative_path.IsAbsolute() || relative_path.ReferencesParent()) {
+ ReportFailure("Invalid path for catalog.");
+ return false;
+ }
+ FilePath path = extension_root_.Append(relative_path);
+
+ std::string catalog_json;
+ JSONStringValueSerializer serializer(&catalog_json);
+ serializer.set_pretty_print(true);
+ if (!serializer.Serialize(*catalog)) {
+ ReportFailure("Error serializing catalog.");
+ return false;
+ }
+
+ // Note: we're overwriting existing files that the utility process read,
+ // so we can be sure the directory exists.
+ if (!file_util::WriteFile(path,
+ catalog_json.c_str(),
+ catalog_json.size())) {
+ ReportFailure("Error saving catalog.");
+ return false;
+ }
+ }
+
+ return true;
+}
diff --git a/chrome/browser/extensions/sandboxed_extension_unpacker.h b/chrome/browser/extensions/sandboxed_extension_unpacker.h
index 06edcbe..9c04c9f 100644
--- a/chrome/browser/extensions/sandboxed_extension_unpacker.h
+++ b/chrome/browser/extensions/sandboxed_extension_unpacker.h
@@ -20,7 +20,7 @@ class ResourceDispatcherHost;
class SandboxedExtensionUnpackerClient
: public base::RefCountedThreadSafe<SandboxedExtensionUnpackerClient> {
public:
- // temp_dir - A temporary directoy containing the results of the extension
+ // temp_dir - A temporary directory containing the results of the extension
// unpacking. The client is responsible for deleting this directory.
//
// extension_root - The path to the extension root inside of temp_dir.
@@ -44,9 +44,9 @@ class SandboxedExtensionUnpackerClient
// sources.
//
// Unpacking an extension using this class makes minor changes to its source,
-// such as transcoding all images to PNG and rewriting the manifest JSON. As
-// such, it should not be used when the output is not intended to be given back
-// to the author.
+// such as transcoding all images to PNG, parsing all message catalogs
+// and rewriting the manifest JSON. As such, it should not be used when the
+// output is not intended to be given back to the author.
//
//
// Lifetime management:
@@ -102,6 +102,7 @@ class SandboxedExtensionUnpacker : public UtilityProcessHost::Client {
private:
class ProcessHostClient;
friend class ProcessHostClient;
+ friend class SandboxedExtensionUnpackerTest;
~SandboxedExtensionUnpacker() {}
@@ -120,13 +121,23 @@ class SandboxedExtensionUnpacker : public UtilityProcessHost::Client {
void StartProcessOnIOThread(const FilePath& temp_crx_path);
// SandboxedExtensionUnpacker
- void OnUnpackExtensionSucceeded(const DictionaryValue& manifest);
+ void OnUnpackExtensionSucceeded(const DictionaryValue& manifest,
+ const DictionaryValue& catalogs);
void OnUnpackExtensionFailed(const std::string& error_message);
void OnProcessCrashed();
void ReportFailure(const std::string& message);
void ReportSuccess();
+ // Overwrites original manifest with safe result from utility process.
+ // Returns NULL on error. Caller owns the returned object.
+ DictionaryValue* RewriteManifestFile(const DictionaryValue& manifest);
+
+ // Overwrites original files with safe results from utility process.
+ // Reports error and returns false if it fails.
+ bool RewriteImageFiles();
+ bool RewriteCatalogFiles(const DictionaryValue& parsed_catalogs);
+
FilePath crx_path_;
ChromeThread::ID thread_identifier_;
ResourceDispatcherHost* rdh_;
diff --git a/chrome/browser/extensions/sandboxed_extension_unpacker_unittest.cc b/chrome/browser/extensions/sandboxed_extension_unpacker_unittest.cc
new file mode 100644
index 0000000..93f550d
--- /dev/null
+++ b/chrome/browser/extensions/sandboxed_extension_unpacker_unittest.cc
@@ -0,0 +1,160 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "base/ref_counted.h"
+#include "base/scoped_temp_dir.h"
+#include "base/string_util.h"
+#include "base/values.h"
+#include "chrome/browser/extensions/sandboxed_extension_unpacker.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/extension_constants.h"
+#include "chrome/common/extensions/extension_unpacker.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+
+namespace errors = extension_manifest_errors;
+namespace keys = extension_manifest_keys;
+
+using testing::_;
+using testing::Invoke;
+
+void OnUnpackSuccess(const FilePath& temp_dir,
+ const FilePath& extension_root,
+ Extension* extension) {
+ delete extension;
+ // Don't delete temp_dir here, we need to do some post op checking.
+}
+
+class MockSandboxedExtensionUnpackerClient
+ : public SandboxedExtensionUnpackerClient {
+ public:
+ virtual ~MockSandboxedExtensionUnpackerClient() {}
+
+ MOCK_METHOD3(OnUnpackSuccess,
+ void(const FilePath& temp_dir,
+ const FilePath& extension_root,
+ Extension* extension));
+
+ MOCK_METHOD1(OnUnpackFailure,
+ void(const std::string& error));
+
+ void DelegateToFake() {
+ ON_CALL(*this, OnUnpackSuccess(_, _, _))
+ .WillByDefault(Invoke(::OnUnpackSuccess));
+ }
+};
+
+class SandboxedExtensionUnpackerTest : public testing::Test {
+ public:
+ virtual void SetUp () {
+ // It will delete itself.
+ client_ = new MockSandboxedExtensionUnpackerClient;
+ client_->DelegateToFake();
+ }
+
+ virtual void TearDown() {
+ // Clean up finally.
+ ASSERT_TRUE(file_util::Delete(install_dir_, true)) <<
+ install_dir_.value();
+ }
+
+ void SetupUnpacker(const std::string& crx_name) {
+ FilePath original_path;
+ ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &original_path));
+ original_path = original_path.AppendASCII("extensions")
+ .AppendASCII("unpacker")
+ .AppendASCII(crx_name);
+ ASSERT_TRUE(file_util::PathExists(original_path)) << original_path.value();
+
+ // Try bots won't let us write into DIR_TEST_DATA, so we have to create
+ // a temp folder to play in.
+ ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &install_dir_));
+ install_dir_ =
+ install_dir_.AppendASCII("sandboxed_extension_unpacker_test");
+ ASSERT_TRUE(file_util::Delete(install_dir_, true)) <<
+ install_dir_.value();
+ ASSERT_TRUE(file_util::CreateDirectory(install_dir_)) <<
+ install_dir_.value();
+
+ FilePath crx_path = install_dir_.AppendASCII(crx_name);
+ ASSERT_TRUE(file_util::CopyFile(original_path, crx_path)) <<
+ "Original path: " << original_path.value() <<
+ ", Crx path: " << crx_path.value();
+
+ unpacker_.reset(new ExtensionUnpacker(crx_path));
+
+ // It will delete itself.
+ sandboxed_unpacker_ =
+ new SandboxedExtensionUnpacker(crx_path, NULL, client_);
+ PrepareUnpackerEnv();
+ }
+
+ void PrepareUnpackerEnv() {
+ sandboxed_unpacker_->extension_root_ =
+ install_dir_.AppendASCII("TEMP_INSTALL");
+
+ sandboxed_unpacker_->temp_dir_.Set(install_dir_);
+ sandboxed_unpacker_->public_key_ =
+ "ocnapchkplbmjmpfehjocmjnipfmogkh";
+ }
+
+ void OnUnpackSucceeded() {
+ sandboxed_unpacker_->OnUnpackExtensionSucceeded(
+ *unpacker_->parsed_manifest(),
+ *unpacker_->parsed_catalogs());
+ }
+
+ FilePath GetInstallPath() {
+ return install_dir_.AppendASCII("TEMP_INSTALL");
+ }
+
+ protected:
+ FilePath install_dir_;
+ MockSandboxedExtensionUnpackerClient* client_;
+ scoped_ptr<ExtensionUnpacker> unpacker_;
+ scoped_refptr<SandboxedExtensionUnpacker> sandboxed_unpacker_;
+};
+
+TEST_F(SandboxedExtensionUnpackerTest, NoCatalogsSuccess) {
+ EXPECT_CALL(*client_, OnUnpackSuccess(_, _, _));
+
+ SetupUnpacker("no_l10n.crx");
+ ASSERT_TRUE(unpacker_->Run());
+ ASSERT_TRUE(unpacker_->DumpImagesToFile());
+
+ // Check that there is no _locales folder.
+ FilePath install_path =
+ GetInstallPath().AppendASCII(Extension::kLocaleFolder);
+ EXPECT_FALSE(file_util::PathExists(install_path));
+
+ OnUnpackSucceeded();
+
+ // Check that there still is no _locales folder.
+ EXPECT_FALSE(file_util::PathExists(install_path));
+}
+
+TEST_F(SandboxedExtensionUnpackerTest, WithCatalogsSuccess) {
+ EXPECT_CALL(*client_, OnUnpackSuccess(_, _, _));
+
+ SetupUnpacker("good_l10n.crx");
+ ASSERT_TRUE(unpacker_->Run());
+ ASSERT_TRUE(unpacker_->DumpImagesToFile());
+
+ // Delete _locales/en_US/messages.json.
+ FilePath messages_file;
+ messages_file = GetInstallPath()
+ .AppendASCII(Extension::kLocaleFolder)
+ .AppendASCII("en_US")
+ .AppendASCII(Extension::kMessagesFilename);
+ EXPECT_TRUE(file_util::Delete(messages_file, false));
+
+ OnUnpackSucceeded();
+
+ // Check that there is _locales/en_US/messages.json file.
+ EXPECT_TRUE(file_util::PathExists(messages_file));
+}
diff --git a/chrome/browser/utility_process_host.h b/chrome/browser/utility_process_host.h
index a37a968..773b515 100644
--- a/chrome/browser/utility_process_host.h
+++ b/chrome/browser/utility_process_host.h
@@ -35,9 +35,11 @@ class UtilityProcessHost : public ChildProcessHost {
virtual void OnProcessCrashed() {}
// 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) {}
+ // parsed manifest.json file. |catalogs| contains list of all parsed
+ // message catalogs. |images| contains a list of decoded images and the
+ // associated paths where those images live on disk.
+ virtual void OnUnpackExtensionSucceeded(const DictionaryValue& manifest,
+ const DictionaryValue& catalogs) {}
// Called when an error occurred while unpacking the extension.
// |error_message| contains a description of the problem.
@@ -51,8 +53,7 @@ class UtilityProcessHost : public ChildProcessHost {
// Called when an error occurred while parsing the resource data.
// |error_message| contains a description of the problem.
- virtual void OnUnpackWebResourceFailed(
- const std::string& error_message) {}
+ virtual void OnUnpackWebResourceFailed(const std::string& error_message) {}
// Called when an update manifest xml file was successfully parsed.
virtual void OnParseUpdateManifestSucceeded(
diff --git a/chrome/browser/utility_process_host_unittest.cc b/chrome/browser/utility_process_host_unittest.cc
index 950c596..44fb0f8 100644
--- a/chrome/browser/utility_process_host_unittest.cc
+++ b/chrome/browser/utility_process_host_unittest.cc
@@ -43,7 +43,8 @@ class TestUtilityProcessHostClient : public UtilityProcessHost::Client {
NOTREACHED();
}
- virtual void OnUnpackExtensionSucceeded(const DictionaryValue& manifest) {
+ virtual void OnUnpackExtensionSucceeded(const DictionaryValue& manifest,
+ const DictionaryValue& catalogs) {
success_ = true;
}