Changing ZipFileInstaller to use the Unzip service.
As part of the effort to deprecate UtilityProcessHost, changing
extensions::UnzipInstaller to use the new Unzip service instead of
UtilityProcessMojoClient.
This remove the last use of a utility process for extensions, and as a
result the extensions/utility directory can be removed.
Tbr: tsepez@chromium.org,finnur@chromium.org
Bug: 799220
Change-Id: Ibe4c7f0c16909c99ed572822718ac56a6bcb57fa
Reviewed-on: https://chromium-review.googlesource.com/937902
Reviewed-by: Jay Civelli <jcivelli@chromium.org>
Reviewed-by: John Budorick <jbudorick@chromium.org>
Reviewed-by: Ken Rockot <rockot@chromium.org>
Reviewed-by: Avi Drissman <avi@chromium.org>
Reviewed-by: Joshua Pawlicki <waffles@chromium.org>
Reviewed-by: Scott Violet <sky@chromium.org>
Reviewed-by: Reilly Grant <reillyg@chromium.org>
Commit-Queue: Jay Civelli <jcivelli@chromium.org>
Cr-Commit-Position: refs/heads/master@{#542303}
diff --git a/.gn b/.gn
index 50a8a2f..375c60c 100644
--- a/.gn
+++ b/.gn
@@ -113,7 +113,6 @@
"//extensions/renderer:unit_tests",
"//extensions/shell:browser_tests",
"//extensions/shell:unit_tests",
- "//extensions/utility:unit_tests",
"//gin/*",
"//google_apis/*",
"//google_update/*",
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn
index 2fbe7a4..fdcee0a 100644
--- a/chrome/browser/extensions/BUILD.gn
+++ b/chrome/browser/extensions/BUILD.gn
@@ -853,6 +853,7 @@
"//components/sync_sessions",
"//components/translate/core/browser",
"//components/undo",
+ "//components/unzip_service/public/cpp",
"//components/update_client",
"//components/url_matcher",
"//components/user_prefs",
diff --git a/chrome/browser/extensions/DEPS b/chrome/browser/extensions/DEPS
index 5e0ad1d..73dab839 100644
--- a/chrome/browser/extensions/DEPS
+++ b/chrome/browser/extensions/DEPS
@@ -37,6 +37,9 @@
# TODO(mash): Remove. http://crbug.com/678705
"+ash/shell.h",
],
+ "zipfile_installer_unittest.cc": [
+ "+services/data_decoder",
+ ],
"test_extension_system.cc": [
"+services/data_decoder",
],
diff --git a/chrome/browser/extensions/zipfile_installer_unittest.cc b/chrome/browser/extensions/zipfile_installer_unittest.cc
index b347b3f7..43994cc 100644
--- a/chrome/browser/extensions/zipfile_installer_unittest.cc
+++ b/chrome/browser/extensions/zipfile_installer_unittest.cc
@@ -2,8 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <vector>
+
#include "base/bind.h"
#include "base/command_line.h"
+#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/memory/ref_counted.h"
#include "base/message_loop/message_loop.h"
@@ -19,12 +22,16 @@
#include "chrome/browser/extensions/test_extension_system.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/test/base/testing_profile.h"
+#include "components/unzip_service/unzip_service.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "content/public/test/test_utils.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_registry_observer.h"
#include "extensions/common/constants.h"
#include "extensions/common/extension.h"
+#include "services/data_decoder/data_decoder_service.h"
+#include "services/service_manager/public/cpp/connector.h"
+#include "services/service_manager/public/cpp/test/test_connector_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
#if defined(OS_CHROMEOS)
@@ -81,12 +88,27 @@
base::Closure quit_closure;
};
+struct UnzipFileFilterTestCase {
+ const base::FilePath::CharType* input;
+ const bool should_unzip;
+};
+
} // namespace
class ZipFileInstallerTest : public testing::Test {
public:
ZipFileInstallerTest()
- : browser_threads_(content::TestBrowserThreadBundle::IO_MAINLOOP) {}
+ : browser_threads_(content::TestBrowserThreadBundle::IO_MAINLOOP) {
+ service_manager::TestConnectorFactory::NameToServiceMap services;
+ services.insert(std::make_pair("data_decoder",
+ data_decoder::DataDecoderService::Create()));
+ services.insert(
+ std::make_pair("unzip_service", unzip::UnzipService::CreateService()));
+ test_connector_factory_ =
+ service_manager::TestConnectorFactory::CreateForServices(
+ std::move(services));
+ connector_ = test_connector_factory_->CreateConnector();
+ }
void SetUp() override {
extensions::LoadErrorReporter::Init(/*enable_noisy_errors=*/false);
@@ -121,8 +143,8 @@
.AppendASCII("zipfile_installer")
.AppendASCII(zip_name);
ASSERT_TRUE(base::PathExists(original_path)) << original_path.value();
-
zipfile_installer_ = ZipFileInstaller::Create(
+ connector_.get(),
MakeRegisterInExtensionServiceCallback(extension_service_));
base::ThreadTaskRunnerHandle::Get()->PostTask(
@@ -131,6 +153,17 @@
observer_.WaitForInstall(expect_error);
}
+ void RunZipFileFilterTest(
+ const std::vector<UnzipFileFilterTestCase>& cases,
+ base::RepeatingCallback<bool(const base::FilePath&)>& filter) {
+ for (size_t i = 0; i < cases.size(); ++i) {
+ base::FilePath input(cases[i].input);
+ bool observed = filter.Run(input);
+ EXPECT_EQ(cases[i].should_unzip, observed)
+ << "i: " << i << ", input: " << input.value();
+ }
+ }
+
protected:
scoped_refptr<ZipFileInstaller> zipfile_installer_;
@@ -148,6 +181,11 @@
// ChromeOS needs a user manager to instantiate an extension service.
chromeos::ScopedTestUserManager test_user_manager_;
#endif
+
+ private:
+ std::unique_ptr<service_manager::TestConnectorFactory>
+ test_connector_factory_;
+ std::unique_ptr<service_manager::Connector> connector_;
};
TEST_F(ZipFileInstallerTest, GoodZip) {
@@ -165,4 +203,49 @@
EXPECT_EQ(observer_.last_extension_installed, kIdForPublicKey);
}
+TEST_F(ZipFileInstallerTest, NonTheme_FileExtractionFilter) {
+ const std::vector<UnzipFileFilterTestCase> cases = {
+ {FILE_PATH_LITERAL("foo"), true},
+ {FILE_PATH_LITERAL("foo.nexe"), true},
+ {FILE_PATH_LITERAL("foo.dll"), true},
+ {FILE_PATH_LITERAL("foo.jpg.exe"), false},
+ {FILE_PATH_LITERAL("foo.exe"), false},
+ {FILE_PATH_LITERAL("foo.EXE"), false},
+ {FILE_PATH_LITERAL("file_without_extension"), true},
+ };
+ base::RepeatingCallback<bool(const base::FilePath&)> filter =
+ base::BindRepeating(&ZipFileInstaller::ShouldExtractFile, false);
+ RunZipFileFilterTest(cases, filter);
+}
+
+TEST_F(ZipFileInstallerTest, Theme_FileExtractionFilter) {
+ const std::vector<UnzipFileFilterTestCase> cases = {
+ {FILE_PATH_LITERAL("image.jpg"), true},
+ {FILE_PATH_LITERAL("IMAGE.JPEG"), true},
+ {FILE_PATH_LITERAL("test/image.bmp"), true},
+ {FILE_PATH_LITERAL("test/IMAGE.gif"), true},
+ {FILE_PATH_LITERAL("test/image.WEBP"), true},
+ {FILE_PATH_LITERAL("test/dir/file.image.png"), true},
+ {FILE_PATH_LITERAL("manifest.json"), true},
+ {FILE_PATH_LITERAL("other.html"), false},
+ {FILE_PATH_LITERAL("file_without_extension"), true},
+ };
+ base::RepeatingCallback<bool(const base::FilePath&)> filter =
+ base::BindRepeating(&ZipFileInstaller::ShouldExtractFile, true);
+ RunZipFileFilterTest(cases, filter);
+}
+
+TEST_F(ZipFileInstallerTest, ManifestExtractionFilter) {
+ const std::vector<UnzipFileFilterTestCase> cases = {
+ {FILE_PATH_LITERAL("manifest.json"), true},
+ {FILE_PATH_LITERAL("MANIFEST.JSON"), true},
+ {FILE_PATH_LITERAL("test/manifest.json"), false},
+ {FILE_PATH_LITERAL("manifest.json/test"), false},
+ {FILE_PATH_LITERAL("other.file"), false},
+ };
+ base::RepeatingCallback<bool(const base::FilePath&)> filter =
+ base::BindRepeating(&ZipFileInstaller::IsManifestFile);
+ RunZipFileFilterTest(cases, filter);
+}
+
} // namespace extensions
diff --git a/chrome/browser/ui/webui/extensions/install_extension_handler.cc b/chrome/browser/ui/webui/extensions/install_extension_handler.cc
index 953306e..cafe0181 100644
--- a/chrome/browser/ui/webui/extensions/install_extension_handler.cc
+++ b/chrome/browser/ui/webui/extensions/install_extension_handler.cc
@@ -20,6 +20,7 @@
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_data_source.h"
#include "content/public/common/drop_data.h"
+#include "content/public/common/service_manager_connection.h"
#include "extensions/browser/extension_system.h"
#include "extensions/common/feature_switch.h"
#include "net/base/filename_util.h"
@@ -100,6 +101,7 @@
if (file_display_name_.MatchesExtension(FILE_PATH_LITERAL(".zip"))) {
ZipFileInstaller::Create(
+ content::ServiceManagerConnection::GetForProcess()->GetConnector(),
MakeRegisterInExtensionServiceCallback(
ExtensionSystem::Get(profile)->extension_service()))
->LoadFromZipFile(file_to_install_);
diff --git a/chrome/utility/BUILD.gn b/chrome/utility/BUILD.gn
index 2481d41..4dba9dbd 100644
--- a/chrome/utility/BUILD.gn
+++ b/chrome/utility/BUILD.gn
@@ -116,7 +116,6 @@
"//chrome/common/extensions/api",
"//chrome/services/media_gallery_util:lib",
"//chrome/services/removable_storage_writer:lib",
- "//extensions/utility",
]
public_deps += [ "//chrome/common/extensions/api" ]
diff --git a/chrome/utility/DEPS b/chrome/utility/DEPS
index 37bfc09..4f9afedc1 100644
--- a/chrome/utility/DEPS
+++ b/chrome/utility/DEPS
@@ -29,7 +29,6 @@
"+content/public/utility",
"+extensions/common",
"+extensions/buildflags",
- "+extensions/utility",
"+media",
"+services/network/public",
"+services/network/url_request_context_builder_mojo.h",
diff --git a/chrome/utility/chrome_content_utility_client.cc b/chrome/utility/chrome_content_utility_client.cc
index aabd610..c3a84e6 100644
--- a/chrome/utility/chrome_content_utility_client.cc
+++ b/chrome/utility/chrome_content_utility_client.cc
@@ -51,7 +51,6 @@
#include "chrome/services/removable_storage_writer/public/mojom/constants.mojom.h"
#include "chrome/services/removable_storage_writer/removable_storage_writer_service.h"
#include "chrome/utility/extensions/extensions_handler.h"
-#include "extensions/utility/utility_handler.h"
#if defined(OS_WIN)
#include "chrome/services/wifi_util_win/public/mojom/constants.mojom.h"
#include "chrome/services/wifi_util_win/wifi_util_win_service.h"
@@ -118,10 +117,6 @@
ChromeContentUtilityClient::~ChromeContentUtilityClient() = default;
void ChromeContentUtilityClient::UtilityThreadStarted() {
-#if BUILDFLAG(ENABLE_EXTENSIONS)
- extensions::utility_handler::UtilityThreadStarted();
-#endif
-
#if defined(OS_WIN)
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
utility_process_running_elevated_ = command_line->HasSwitch(
@@ -137,10 +132,6 @@
return;
auto registry = std::make_unique<service_manager::BinderRegistry>();
-#if BUILDFLAG(ENABLE_EXTENSIONS)
- extensions::utility_handler::ExposeInterfacesToBrowser(
- registry.get(), utility_process_running_elevated_);
-#endif
// If our process runs with elevated privileges, only add elevated Mojo
// interfaces to the interface registry.
if (!utility_process_running_elevated_) {
diff --git a/chrome/utility/extensions/DEPS b/chrome/utility/extensions/DEPS
index a62f289..4959379 100644
--- a/chrome/utility/extensions/DEPS
+++ b/chrome/utility/extensions/DEPS
@@ -1,5 +1,5 @@
include_rules = [
"+extensions/buildflags",
"+extensions/common",
- "+extensions/utility",
+ "+extensions/features",
]
diff --git a/extensions/BUILD.gn b/extensions/BUILD.gn
index a071e57..bf9ba62 100644
--- a/extensions/BUILD.gn
+++ b/extensions/BUILD.gn
@@ -223,7 +223,6 @@
"//extensions/common:unit_tests",
"//extensions/renderer:unit_tests",
"//extensions/shell:unit_tests",
- "//extensions/utility:unit_tests",
"//services/data_decoder:lib",
"//services/service_manager/public/cpp/test:test_support",
"//ui/gl:test_support",
diff --git a/extensions/browser/BUILD.gn b/extensions/browser/BUILD.gn
index 28da43b..8dc8564 100644
--- a/extensions/browser/BUILD.gn
+++ b/extensions/browser/BUILD.gn
@@ -615,6 +615,8 @@
"//components/pref_registry:pref_registry",
"//components/prefs:test_support",
"//components/sync_preferences:test_support",
+ "//components/unzip_service:lib",
+ "//components/unzip_service/public/cpp:test_support",
"//components/update_client",
"//components/url_matcher",
"//components/user_prefs",
@@ -629,8 +631,10 @@
"//extensions/strings",
"//ipc:test_support",
"//net:test_support",
+ "//services/data_decoder:lib",
"//services/data_decoder/public/cpp:test_support",
"//services/device/public/mojom",
+ "//services/service_manager/public/cpp/test:test_support",
"//storage/browser:test_support",
"//third_party/leveldatabase",
"//third_party/zlib/google:zip",
diff --git a/extensions/browser/DEPS b/extensions/browser/DEPS
index 3d5d48d..5d4816a 100644
--- a/extensions/browser/DEPS
+++ b/extensions/browser/DEPS
@@ -8,6 +8,7 @@
"+components/sync",
"+components/sync_preferences",
"+components/update_client",
+ "+components/unzip_service/public",
"+components/variations",
"+components/version_info",
"+components/web_cache",
@@ -56,4 +57,8 @@
"+chrome/test/base/testing_profile.h",
"+chrome/test/base/ui_test_utils.h",
],
+ "sandboxed_unpacker_unittest.cc": [
+ "+components/unzip_service",
+ "+services/data_decoder",
+ ],
}
diff --git a/extensions/browser/sandboxed_unpacker.cc b/extensions/browser/sandboxed_unpacker.cc
index afec71d..54ee0eb 100644
--- a/extensions/browser/sandboxed_unpacker.cc
+++ b/extensions/browser/sandboxed_unpacker.cc
@@ -25,15 +25,16 @@
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "components/crx_file/crx_verifier.h"
+#include "components/unzip_service/public/cpp/unzip.h"
#include "content/public/browser/browser_thread.h"
#include "extensions/browser/api/declarative_net_request/utils.h"
#include "extensions/browser/extension_file_task_runner.h"
+#include "extensions/browser/zipfile_installer.h"
#include "extensions/common/api/declarative_net_request/dnr_manifest_data.h"
#include "extensions/common/constants.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_l10n_util.h"
#include "extensions/common/extension_resource_path_normalizer.h"
-#include "extensions/common/extension_unpacker.mojom.h"
#include "extensions/common/extension_utility_types.h"
#include "extensions/common/extensions_client.h"
#include "extensions/common/features/feature_channel.h"
@@ -233,6 +234,9 @@
CHECK_GT(location, Manifest::INVALID_LOCATION);
CHECK_LT(location, Manifest::NUM_LOCATIONS);
+ // The connector should not be bound to any thread yet.
+ DCHECK(!connector_->IsBound());
+
// Use a random instance ID to guarantee the connection is to a new data
// decoder service (running in its own process).
data_decoder_identity_ = service_manager::Identity(
@@ -323,9 +327,20 @@
PATH_LENGTH_HISTOGRAM("Extensions.SandboxUnpackLinkFreeCrxPathLength",
link_free_crx_path);
- BrowserThread::PostTask(
- BrowserThread::IO, FROM_HERE,
- base::BindOnce(&SandboxedUnpacker::Unzip, this, link_free_crx_path));
+ // Make sure to create the directory where the extension will be unzipped, as
+ // the unzipper service requires it.
+ base::FilePath unzipped_dir =
+ link_free_crx_path.DirName().AppendASCII(kTempExtensionName);
+ base::File::Error error;
+ if (!base::CreateDirectoryAndGetError(unzipped_dir, &error)) {
+ LOG(ERROR) << "Failed to created directory " << unzipped_dir.value()
+ << " with error " << error;
+ ReportFailure(UNZIP_FAILED,
+ l10n_util::GetStringUTF16(IDS_EXTENSION_PACKAGE_UNZIP_ERROR));
+ return;
+ }
+
+ Unzip(link_free_crx_path, unzipped_dir);
}
void SandboxedUnpacker::StartWithDirectory(const std::string& extension_id,
@@ -348,8 +363,8 @@
return;
}
- BrowserThread::PostTask(
- BrowserThread::IO, FROM_HERE,
+ unpacker_io_task_runner_->PostTask(
+ FROM_HERE,
base::BindOnce(&SandboxedUnpacker::Unpack, this, extension_root_));
}
@@ -373,63 +388,23 @@
unpacker_io_task_runner_->DeleteSoon(FROM_HERE, std::move(connector_));
}
-void SandboxedUnpacker::StartUtilityProcessIfNeeded() {
- DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-
- if (utility_process_mojo_client_)
- return;
-
- utility_process_mojo_client_ = std::make_unique<
- content::UtilityProcessMojoClient<mojom::ExtensionUnpacker>>(
- l10n_util::GetStringUTF16(IDS_UTILITY_PROCESS_EXTENSION_UNPACKER_NAME));
- utility_process_mojo_client_->set_error_callback(
- base::Bind(&SandboxedUnpacker::UtilityProcessCrashed, this));
-
- utility_process_mojo_client_->set_exposed_directory(temp_dir_.GetPath());
-
- utility_process_mojo_client_->Start();
-}
-
-void SandboxedUnpacker::UtilityProcessCrashed() {
- DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-
- utility_process_mojo_client_.reset();
-
- unpacker_io_task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(
- &SandboxedUnpacker::ReportFailure, this,
- UTILITY_PROCESS_CRASHED_WHILE_TRYING_TO_INSTALL,
- l10n_util::GetStringFUTF16(
- IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
- ASCIIToUTF16("UTILITY_PROCESS_CRASHED_WHILE_TRYING_TO_INSTALL")) +
- ASCIIToUTF16(". ") +
- l10n_util::GetStringUTF16(
- IDS_EXTENSION_INSTALL_PROCESS_CRASHED)));
-}
-
-void SandboxedUnpacker::Unzip(const base::FilePath& crx_path) {
- DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
- StartUtilityProcessIfNeeded();
+void SandboxedUnpacker::Unzip(const base::FilePath& crx_path,
+ const base::FilePath& unzipped_dir) {
+ DCHECK(unpacker_io_task_runner_->RunsTasksInCurrentSequence());
DCHECK(crx_path.DirName() == temp_dir_.GetPath());
- base::FilePath unzipped_dir =
- crx_path.DirName().AppendASCII(kTempExtensionName);
- utility_process_mojo_client_->service()->Unzip(
- crx_path, unzipped_dir,
- base::BindOnce(&SandboxedUnpacker::UnzipDone, this, unzipped_dir));
+ ZipFileInstaller::Create(connector_.get(),
+ base::BindOnce(&SandboxedUnpacker::UnzipDone, this))
+ ->LoadFromZipFileInDir(crx_path, unzipped_dir);
}
-void SandboxedUnpacker::UnzipDone(const base::FilePath& directory,
- bool success) {
- DCHECK_CURRENTLY_ON(BrowserThread::IO);
+void SandboxedUnpacker::UnzipDone(const base::FilePath& zip_file,
+ const base::FilePath& unzip_dir,
+ const std::string& error) {
+ DCHECK(unpacker_io_task_runner_->RunsTasksInCurrentSequence());
- utility_process_mojo_client_.reset();
-
- if (!success) {
- utility_process_mojo_client_.reset();
+ if (!error.empty()) {
unpacker_io_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
@@ -438,22 +413,18 @@
return;
}
- BrowserThread::PostTask(
- BrowserThread::IO, FROM_HERE,
- base::BindOnce(&SandboxedUnpacker::Unpack, this, directory));
+ Unpack(unzip_dir);
}
void SandboxedUnpacker::Unpack(const base::FilePath& directory) {
- DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ DCHECK(unpacker_io_task_runner_->RunsTasksInCurrentSequence());
DCHECK(directory.DirName() == temp_dir_.GetPath());
base::FilePath manifest_path = extension_root_.Append(kManifestFilename);
- unpacker_io_task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(
- &SandboxedUnpacker::ParseJsonFile, this, manifest_path,
- base::BindOnce(&SandboxedUnpacker::ReadManifestDone, this)));
+
+ ParseJsonFile(manifest_path,
+ base::BindOnce(&SandboxedUnpacker::ReadManifestDone, this));
}
void SandboxedUnpacker::ReadManifestDone(
@@ -607,6 +578,12 @@
l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
ASCIIToUTF16("ERROR_SAVING_THEME_IMAGE"));
break;
+ case ImageSanitizer::Status::kServiceError:
+ failure_reason = UTILITY_PROCESS_CRASHED_WHILE_TRYING_TO_INSTALL;
+ error = l10n_util::GetStringFUTF16(
+ IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
+ ASCIIToUTF16("ERROR_UTILITY_PROCESS_CRASH"));
+ break;
default:
NOTREACHED();
break;
@@ -786,7 +763,6 @@
void SandboxedUnpacker::UnpackExtensionFailed(const base::string16& error) {
DCHECK(unpacker_io_task_runner_->RunsTasksInCurrentSequence());
-
ReportFailure(
UNPACKER_CLIENT_FAILED,
l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_ERROR_MESSAGE, error));
diff --git a/extensions/browser/sandboxed_unpacker.h b/extensions/browser/sandboxed_unpacker.h
index 9ec05ce..24ea37f 100644
--- a/extensions/browser/sandboxed_unpacker.h
+++ b/extensions/browser/sandboxed_unpacker.h
@@ -16,7 +16,6 @@
#include "base/optional.h"
#include "base/strings/string_piece.h"
#include "base/time/time.h"
-#include "content/public/browser/utility_process_mojo_client.h"
#include "extensions/browser/crx_file_info.h"
#include "extensions/browser/image_sanitizer.h"
#include "extensions/browser/install/crx_install_error.h"
@@ -40,10 +39,6 @@
namespace extensions {
class Extension;
-namespace mojom {
-class ExtensionUnpacker;
-}
-
class SandboxedUnpackerClient
: public base::RefCountedDeleteOnSequence<SandboxedUnpackerClient> {
public:
@@ -85,9 +80,8 @@
// SandboxedUnpacker does work to optionally unpack and then validate/sanitize
// an extension, either starting from a crx file, or else an already unzipped
-// directory (eg., from a differential update). This is done in a sandboxed
-// subprocess to protect the browser process from parsing complex data formats
-// like JPEG or JSON from untrusted sources.
+// directory (eg., from a differential update). The parsing of complex data
+// formats like JPEG or JSON is performed in specific, sandboxed services.
//
// Unpacking an extension using this class makes changes to its source, such as
// transcoding all images to PNG, parsing all message catalogs, and rewriting
@@ -96,8 +90,7 @@
//
// Lifetime management:
//
-// This class is ref-counted by each call it makes to itself on another thread,
-// and by UtilityProcessMojoClient.
+// This class is ref-counted by each call it makes to itself on another thread.
//
// Additionally, we hold a reference to our own client so that the client lives
// long enough to receive the result of unpacking.
@@ -229,15 +222,12 @@
bool ValidateSignature(const base::FilePath& crx_path,
const std::string& expected_hash);
- // Ensures the utility process is created.
- void StartUtilityProcessIfNeeded();
-
- // Utility process crashed or failed while trying to install.
- void UtilityProcessCrashed();
-
// Unzips the extension into directory.
- void Unzip(const base::FilePath& crx_path);
- void UnzipDone(const base::FilePath& directory, bool success);
+ void Unzip(const base::FilePath& crx_path,
+ const base::FilePath& unzipped_dir);
+ void UnzipDone(const base::FilePath& zip_file,
+ const base::FilePath& unzip_dir,
+ const std::string& error);
// Unpacks the extension in directory and returns the manifest.
void Unpack(const base::FilePath& directory);
@@ -342,10 +332,6 @@
// Sequenced task runner where file I/O operations will be performed.
scoped_refptr<base::SequencedTaskRunner> unpacker_io_task_runner_;
- // Utility client used for sending tasks to the utility process.
- std::unique_ptr<content::UtilityProcessMojoClient<mojom::ExtensionUnpacker>>
- utility_process_mojo_client_;
-
// The normalized path of the install icon path, retrieved from the manifest.
base::FilePath install_icon_path_;
diff --git a/extensions/browser/sandboxed_unpacker_unittest.cc b/extensions/browser/sandboxed_unpacker_unittest.cc
index a61be79..a378d30 100644
--- a/extensions/browser/sandboxed_unpacker_unittest.cc
+++ b/extensions/browser/sandboxed_unpacker_unittest.cc
@@ -17,6 +17,8 @@
#include "base/threading/thread_task_runner_handle.h"
#include "base/values.h"
#include "components/crx_file/id_util.h"
+#include "components/unzip_service/public/cpp/test_unzip_service.h"
+#include "components/unzip_service/unzip_service.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "content/public/test/test_utils.h"
@@ -28,8 +30,10 @@
#include "extensions/common/switches.h"
#include "extensions/strings/grit/extensions_strings.h"
#include "extensions/test/test_extensions_client.h"
+#include "services/data_decoder/data_decoder_service.h"
#include "services/data_decoder/public/cpp/test_data_decoder_service.h"
#include "services/service_manager/public/cpp/connector.h"
+#include "services/service_manager/public/cpp/test/test_connector_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/zlib/google/zip.h"
@@ -128,10 +132,30 @@
// It will delete itself.
client_ = new MockSandboxedUnpackerClient;
- sandboxed_unpacker_ = new SandboxedUnpacker(
- test_data_decoder_service_.connector()->Clone(), Manifest::INTERNAL,
- Extension::NO_FLAGS, extensions_dir_.GetPath(),
- base::ThreadTaskRunnerHandle::Get(), client_);
+ InitSanboxedUnpacker(/*data_decode_service=*/nullptr,
+ /*unzip_service=*/nullptr);
+ }
+
+ void InitSanboxedUnpacker(
+ std::unique_ptr<service_manager::Service> data_decode_service,
+ std::unique_ptr<service_manager::Service> unzip_service) {
+ service_manager::TestConnectorFactory::NameToServiceMap services;
+ if (!data_decode_service)
+ data_decode_service = data_decoder::DataDecoderService::Create();
+ if (!unzip_service)
+ unzip_service = unzip::UnzipService::CreateService();
+ services.insert(
+ std::make_pair("data_decoder", std::move(data_decode_service)));
+ services.insert(std::make_pair("unzip_service", std::move(unzip_service)));
+ test_connector_factory_ =
+ service_manager::TestConnectorFactory::CreateForServices(
+ std::move(services));
+ connector_ = test_connector_factory_->CreateConnector();
+
+ sandboxed_unpacker_ =
+ new SandboxedUnpacker(connector_->Clone(), Manifest::INTERNAL,
+ Extension::NO_FLAGS, extensions_dir_.GetPath(),
+ base::ThreadTaskRunnerHandle::Get(), client_);
}
void TearDown() override {
@@ -178,20 +202,6 @@
client_->WaitForUnpack();
}
- void SimulateUtilityProcessCrash() {
- sandboxed_unpacker_->CreateTempDirectory();
-
- content::BrowserThread::PostTask(
- content::BrowserThread::IO, FROM_HERE,
- base::Bind(&SandboxedUnpacker::StartUtilityProcessIfNeeded,
- sandboxed_unpacker_));
-
- content::BrowserThread::PostTask(
- content::BrowserThread::IO, FROM_HERE,
- base::Bind(&SandboxedUnpacker::UtilityProcessCrashed,
- sandboxed_unpacker_));
- }
-
bool InstallSucceeded() const { return !client_->temp_dir().empty(); }
base::FilePath GetInstallPath() const {
@@ -222,12 +232,14 @@
}
protected:
- data_decoder::TestDataDecoderService test_data_decoder_service_;
base::ScopedTempDir extensions_dir_;
MockSandboxedUnpackerClient* client_;
scoped_refptr<SandboxedUnpacker> sandboxed_unpacker_;
std::unique_ptr<content::InProcessUtilityThreadHelper>
in_process_utility_thread_helper_;
+ std::unique_ptr<service_manager::TestConnectorFactory>
+ test_connector_factory_;
+ std::unique_ptr<service_manager::Connector> connector_;
};
TEST_F(SandboxedUnpackerTest, EmptyDefaultLocale) {
@@ -353,6 +365,34 @@
EXPECT_EQ(base::string16(), GetInstallError());
}
+// The following tests simulate the utility services failling.
+TEST_F(SandboxedUnpackerTest, UnzipperServiceFails) {
+ InitSanboxedUnpacker(
+ /*data_decoder_service=*/nullptr,
+ std::make_unique<unzip::CrashyUnzipService>());
+ SetupUnpacker("good_package.crx", "");
+ EXPECT_FALSE(InstallSucceeded());
+ EXPECT_FALSE(GetInstallError().empty());
+}
+
+TEST_F(SandboxedUnpackerTest, JsonParserFails) {
+ InitSanboxedUnpacker(std::make_unique<data_decoder::CrashyDataDecoderService>(
+ /*crash_json=*/true, /*crash_image=*/false),
+ /*unzip_service=*/nullptr);
+ SetupUnpacker("good_package.crx", "");
+ EXPECT_FALSE(InstallSucceeded());
+ EXPECT_FALSE(GetInstallError().empty());
+}
+
+TEST_F(SandboxedUnpackerTest, ImageDecoderFails) {
+ InitSanboxedUnpacker(std::make_unique<data_decoder::CrashyDataDecoderService>(
+ /*crash_json=*/false, /*crash_image=*/true),
+ /*unzip_service=*/nullptr);
+ SetupUnpacker("good_package.crx", "");
+ EXPECT_FALSE(InstallSucceeded());
+ EXPECT_FALSE(GetInstallError().empty());
+}
+
// SandboxedUnpacker is ref counted and is reference by callbacks and
// InterfacePtrs. This tests that it gets deleted as expected (so that no extra
// refs are left).
@@ -364,25 +404,4 @@
TestSandboxedUnpackerDeleted("bad_image.crx", /*expect_success=*/false);
}
-class SandboxedUnpackerTestWithRealIOThread : public SandboxedUnpackerTest {
- public:
- SandboxedUnpackerTestWithRealIOThread()
- : SandboxedUnpackerTest(
- content::TestBrowserThreadBundle::REAL_IO_THREAD) {}
-
- void TearDown() override {
- // The utility process task could still be running. Ensure it is fully
- // finished before ending the test.
- content::RunAllPendingInMessageLoop(content::BrowserThread::IO);
- SandboxedUnpackerTest::TearDown();
- }
-};
-
-TEST_F(SandboxedUnpackerTestWithRealIOThread, UtilityProcessCrash) {
- SimulateUtilityProcessCrash();
- client_->WaitForUnpack();
- // Check that there is an error message.
- EXPECT_NE(base::string16(), GetInstallError());
-}
-
} // namespace extensions
diff --git a/extensions/browser/zipfile_installer.cc b/extensions/browser/zipfile_installer.cc
index b24bbab..b18e9588 100644
--- a/extensions/browser/zipfile_installer.cc
+++ b/extensions/browser/zipfile_installer.cc
@@ -6,19 +6,33 @@
#include "base/files/file_util.h"
#include "base/path_service.h"
+#include "base/task_runner_util.h"
#include "base/task_scheduler/post_task.h"
+#include "components/unzip_service/public/cpp/unzip.h"
+#include "components/unzip_service/public/interfaces/unzipper.mojom.h"
#include "extensions/browser/extension_file_task_runner.h"
-#include "extensions/common/extension_unpacker.mojom.h"
+#include "extensions/common/constants.h"
+#include "extensions/common/manifest.h"
#include "extensions/strings/grit/extensions_strings.h"
+#include "services/data_decoder/public/cpp/safe_json_parser.h"
+#include "services/service_manager/public/cpp/connector.h"
#include "ui/base/l10n/l10n_util.h"
+namespace extensions {
+
namespace {
-const char kExtensionHandlerTempDirError[] =
+constexpr char kExtensionHandlerTempDirError[] =
"Could not create temporary directory for zipped extension.";
-const char kExtensionHandlerFileUnzipError[] =
+constexpr char kExtensionHandlerFileUnzipError[] =
"Could not unzip extension for install.";
+constexpr const base::FilePath::CharType* kAllowedThemeFiletypes[] = {
+ FILE_PATH_LITERAL(".bmp"), FILE_PATH_LITERAL(".gif"),
+ FILE_PATH_LITERAL(".jpeg"), FILE_PATH_LITERAL(".jpg"),
+ FILE_PATH_LITERAL(".json"), FILE_PATH_LITERAL(".png"),
+ FILE_PATH_LITERAL(".webp")};
+
base::Optional<base::FilePath> PrepareAndGetUnzipDir(
const base::FilePath& zip_file) {
base::AssertBlockingAllowed();
@@ -36,31 +50,59 @@
return unzip_dir;
}
-} // namespace
+base::Optional<std::string> ReadFileContent(const base::FilePath& path) {
+ base::AssertBlockingAllowed();
-namespace extensions {
+ std::string content;
+ return base::ReadFileToString(path, &content) ? content
+ : base::Optional<std::string>();
+}
+
+} // namespace
// static
scoped_refptr<ZipFileInstaller> ZipFileInstaller::Create(
+ service_manager::Connector* connector,
DoneCallback done_callback) {
+ DCHECK(connector);
DCHECK(done_callback);
- return base::WrapRefCounted(new ZipFileInstaller(std::move(done_callback)));
+ return base::WrapRefCounted(
+ new ZipFileInstaller(connector, std::move(done_callback)));
}
void ZipFileInstaller::LoadFromZipFile(const base::FilePath& zip_file) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ LoadFromZipFileImpl(zip_file, base::FilePath());
+}
+
+void ZipFileInstaller::LoadFromZipFileInDir(const base::FilePath& zip_file,
+ const base::FilePath& unzip_dir) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(!unzip_dir.empty());
+ LoadFromZipFileImpl(zip_file, unzip_dir);
+}
+
+void ZipFileInstaller::LoadFromZipFileImpl(const base::FilePath& zip_file,
+ const base::FilePath& unzip_dir) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!zip_file.empty());
zip_file_ = zip_file;
+ if (!unzip_dir.empty()) {
+ Unzip(unzip_dir);
+ return;
+ }
+
base::PostTaskAndReplyWithResult(
GetExtensionFileTaskRunner().get(), FROM_HERE,
base::BindOnce(&PrepareAndGetUnzipDir, zip_file),
base::BindOnce(&ZipFileInstaller::Unzip, this));
}
-ZipFileInstaller::ZipFileInstaller(DoneCallback done_callback)
- : done_callback_(std::move(done_callback)) {}
+ZipFileInstaller::ZipFileInstaller(service_manager::Connector* connector,
+ DoneCallback done_callback)
+ : done_callback_(std::move(done_callback)), connector_(connector) {}
ZipFileInstaller::~ZipFileInstaller() = default;
@@ -71,29 +113,76 @@
ReportFailure(std::string(kExtensionHandlerTempDirError));
return;
}
- DCHECK(!utility_process_mojo_client_);
- utility_process_mojo_client_ = std::make_unique<
- content::UtilityProcessMojoClient<mojom::ExtensionUnpacker>>(
- l10n_util::GetStringUTF16(IDS_UTILITY_PROCESS_ZIP_FILE_INSTALLER_NAME));
- utility_process_mojo_client_->set_error_callback(
- base::Bind(&ZipFileInstaller::UnzipDone, this, *unzip_dir, false));
+ unzip::UnzipWithFilter(
+ connector_->Clone(), zip_file_, *unzip_dir,
+ base::BindRepeating(&ZipFileInstaller::IsManifestFile),
+ base::BindOnce(&ZipFileInstaller::ManifestUnzipped, this, *unzip_dir));
+}
- utility_process_mojo_client_->set_exposed_directory(*unzip_dir);
+void ZipFileInstaller::ManifestUnzipped(const base::FilePath& unzip_dir,
+ bool success) {
+ if (!success) {
+ ReportFailure(kExtensionHandlerFileUnzipError);
+ return;
+ }
- utility_process_mojo_client_->Start();
+ base::PostTaskAndReplyWithResult(
+ GetExtensionFileTaskRunner().get(), FROM_HERE,
+ base::BindOnce(&ReadFileContent, unzip_dir.Append(kManifestFilename)),
+ base::BindOnce(&ZipFileInstaller::ManifestRead, this, unzip_dir));
+}
- utility_process_mojo_client_->service()->Unzip(
- zip_file_, *unzip_dir,
- base::BindOnce(&ZipFileInstaller::UnzipDone, this, *unzip_dir));
+void ZipFileInstaller::ManifestRead(
+ const base::FilePath& unzip_dir,
+ base::Optional<std::string> manifest_content) {
+ if (!manifest_content) {
+ ReportFailure(std::string(kExtensionHandlerFileUnzipError));
+ return;
+ }
+
+ data_decoder::SafeJsonParser::Parse(
+ connector_, *manifest_content,
+ base::Bind(&ZipFileInstaller::ManifestParsed, this, unzip_dir),
+ base::Bind(&ZipFileInstaller::ManifestParsingFailed, this));
+}
+
+void ZipFileInstaller::ManifestParsingFailed(const std::string& error) {
+ ReportFailure(std::string(kExtensionHandlerFileUnzipError));
+}
+
+void ZipFileInstaller::ManifestParsed(
+ const base::FilePath& unzip_dir,
+ std::unique_ptr<base::Value> manifest_value) {
+ std::unique_ptr<base::DictionaryValue> manifest_dictionary =
+ base::DictionaryValue::From(std::move(manifest_value));
+ if (!manifest_dictionary) {
+ ReportFailure(std::string(kExtensionHandlerFileUnzipError));
+ return;
+ }
+
+ Manifest manifest(Manifest::INTERNAL, std::move(manifest_dictionary));
+
+ unzip::UnzipFilterCallback filter = base::BindRepeating(
+ [](bool is_theme, const base::FilePath& file_path) -> bool {
+ // Note that we ignore the manifest as it has already been extracted and
+ // would cause the unzipping to fail.
+ return ZipFileInstaller::ShouldExtractFile(is_theme, file_path) &&
+ !ZipFileInstaller::IsManifestFile(file_path);
+ },
+ manifest.is_theme());
+
+ // TODO(crbug.com/645263): This silently ignores blocked file types.
+ // Add install warnings.
+ unzip::UnzipWithFilter(
+ connector_->Clone(), zip_file_, unzip_dir, filter,
+ base::BindOnce(&ZipFileInstaller::UnzipDone, this, unzip_dir));
}
void ZipFileInstaller::UnzipDone(const base::FilePath& unzip_dir,
bool success) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- utility_process_mojo_client_.reset();
-
if (!success) {
ReportFailure(kExtensionHandlerFileUnzipError);
return;
@@ -108,4 +197,26 @@
std::move(done_callback_).Run(zip_file_, base::FilePath(), error);
}
+// static
+bool ZipFileInstaller::ShouldExtractFile(bool is_theme,
+ const base::FilePath& file_path) {
+ if (is_theme) {
+ const base::FilePath::StringType extension =
+ base::ToLowerASCII(file_path.FinalExtension());
+ // Allow filenames with no extension.
+ if (extension.empty())
+ return true;
+ return base::ContainsValue(kAllowedThemeFiletypes, extension);
+ }
+ return !base::FilePath::CompareEqualIgnoreCase(file_path.FinalExtension(),
+ FILE_PATH_LITERAL(".exe"));
+}
+
+// static
+bool ZipFileInstaller::IsManifestFile(const base::FilePath& file_path) {
+ CHECK(!file_path.IsAbsolute());
+ return base::FilePath::CompareEqualIgnoreCase(file_path.value(),
+ kManifestFilename);
+}
+
} // namespace extensions
diff --git a/extensions/browser/zipfile_installer.h b/extensions/browser/zipfile_installer.h
index 944e99a..9d44b5f 100644
--- a/extensions/browser/zipfile_installer.h
+++ b/extensions/browser/zipfile_installer.h
@@ -8,20 +8,24 @@
#include <memory>
#include <string>
+#include "base/callback.h"
#include "base/files/file_path.h"
+#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
#include "base/sequence_checker.h"
-#include "content/public/browser/utility_process_mojo_client.h"
+#include "base/values.h"
+
+namespace service_manager {
+class Connector;
+}
namespace extensions {
-namespace mojom {
-class ExtensionUnpacker;
-}
-
-// ZipFileInstaller unzips an extension in a utility process.
+// ZipFileInstaller unzips an extension safely using the Unzipper and
+// SafeJSONParser services.
// This class is not thread-safe: it is bound to the sequence it is created on.
class ZipFileInstaller : public base::RefCountedThreadSafe<ZipFileInstaller> {
public:
@@ -34,18 +38,38 @@
const std::string& error)>;
// Creates a ZipFileInstaller that invokes |done_callback| when done.
- static scoped_refptr<ZipFileInstaller> Create(DoneCallback done_callback);
+ static scoped_refptr<ZipFileInstaller> Create(
+ service_manager::Connector* connector,
+ DoneCallback done_callback);
+ // Creates a temporary directory and unzips the extension in it.
void LoadFromZipFile(const base::FilePath& zip_file);
+ // Unzips the extension in |unzip_dir|.
+ void LoadFromZipFileInDir(const base::FilePath& zip_file,
+ const base::FilePath& unzip_dir);
+
private:
friend class base::RefCountedThreadSafe<ZipFileInstaller>;
+ FRIEND_TEST_ALL_PREFIXES(ZipFileInstallerTest, NonTheme_FileExtractionFilter);
+ FRIEND_TEST_ALL_PREFIXES(ZipFileInstallerTest, Theme_FileExtractionFilter);
+ FRIEND_TEST_ALL_PREFIXES(ZipFileInstallerTest, ManifestExtractionFilter);
- explicit ZipFileInstaller(DoneCallback done_callback);
+ ZipFileInstaller(service_manager::Connector* connector,
+ DoneCallback done_callback);
~ZipFileInstaller();
+ void LoadFromZipFileImpl(const base::FilePath& zip_file,
+ const base::FilePath& unzip_dir);
+
// Unzip an extension into |unzip_dir| and load it with an UnpackedInstaller.
void Unzip(base::Optional<base::FilePath> unzip_dir);
+ void ManifestUnzipped(const base::FilePath& unzip_dir, bool success);
+ void ManifestRead(const base::FilePath& unzip_dir,
+ base::Optional<std::string> manifest_content);
+ void ManifestParsingFailed(const std::string& error);
+ void ManifestParsed(const base::FilePath& unzip_dir,
+ std::unique_ptr<base::Value> manifest);
void UnzipDone(const base::FilePath& unzip_dir, bool success);
// On failure, report the |error| reason.
@@ -54,12 +78,18 @@
// Callback invoked when unzipping has finished.
DoneCallback done_callback_;
+ // Whether a file should be extracted as part of installing an
+ // extension/theme. Protects against unused or potentially hamrful files.
+ static bool ShouldExtractFile(bool is_theme, const base::FilePath& file_path);
+
+ // Returns true if |file_path| points to an extension manifest.
+ static bool IsManifestFile(const base::FilePath& file_path);
+
// File containing the extension to unzip.
base::FilePath zip_file_;
- // Utility process used to perform the unzip.
- std::unique_ptr<content::UtilityProcessMojoClient<mojom::ExtensionUnpacker>>
- utility_process_mojo_client_;
+ // Connector to the ServiceManager. Bound to the UI thread.
+ service_manager::Connector* connector_;
SEQUENCE_CHECKER(sequence_checker_);
diff --git a/extensions/common/BUILD.gn b/extensions/common/BUILD.gn
index 0450e04..eb30402e 100644
--- a/extensions/common/BUILD.gn
+++ b/extensions/common/BUILD.gn
@@ -23,7 +23,6 @@
if (enable_extensions) {
mojom("mojo") {
sources = [
- "extension_unpacker.mojom",
"mojo/app_window.mojom",
"mojo/guest_view.mojom",
"mojo/keep_alive.mojom",
@@ -35,7 +34,6 @@
public_deps = [
"//content/public/common:interfaces",
- "//mojo/common:common_custom_types",
"//ui/gfx/geometry/mojo",
"//url/mojom:url_mojom_gurl",
]
diff --git a/extensions/common/extension_unpacker.mojom b/extensions/common/extension_unpacker.mojom
deleted file mode 100644
index f29ce63..0000000
--- a/extensions/common/extension_unpacker.mojom
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2017 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.
-
-// Safe chrome extension unpacker service provided by the utility process
-// and exposed by mojo policy to the chrome browser process.
-
-module extensions.mojom;
-
-import "mojo/common/file_path.mojom";
-
-interface ExtensionUnpacker {
- // Unzip |file| into the directory |path|.
- Unzip(mojo.common.mojom.FilePath file,
- mojo.common.mojom.FilePath path) => (bool success);
-};
diff --git a/extensions/shell/BUILD.gn b/extensions/shell/BUILD.gn
index c02e77fb..5b493a18 100644
--- a/extensions/shell/BUILD.gn
+++ b/extensions/shell/BUILD.gn
@@ -65,7 +65,6 @@
"//extensions/shell/common/api",
"//extensions/shell/common/api:api_registration",
"//extensions/shell/common/api:extensions_features",
- "//extensions/utility",
"//skia",
"//third_party/WebKit/public:blink",
"//third_party/cld_3/src/src:cld_3",
@@ -183,8 +182,6 @@
"renderer/shell_content_renderer_client.h",
"renderer/shell_extensions_renderer_client.cc",
"renderer/shell_extensions_renderer_client.h",
- "utility/shell_content_utility_client.cc",
- "utility/shell_content_utility_client.h",
]
if (use_aura) {
diff --git a/extensions/shell/app/DEPS b/extensions/shell/app/DEPS
index 5b7cd97..a92aad2 100644
--- a/extensions/shell/app/DEPS
+++ b/extensions/shell/app/DEPS
@@ -4,5 +4,6 @@
"+components/nacl",
"+content/public/app",
"+content/public/browser",
+ "+content/public/utility",
"+sandbox",
]
diff --git a/extensions/shell/app/shell_main_delegate.cc b/extensions/shell/app/shell_main_delegate.cc
index 823dbfc5..42c0f22 100644
--- a/extensions/shell/app/shell_main_delegate.cc
+++ b/extensions/shell/app/shell_main_delegate.cc
@@ -12,13 +12,13 @@
#include "components/nacl/common/buildflags.h"
#include "content/public/browser/browser_main_runner.h"
#include "content/public/common/content_switches.h"
+#include "content/public/utility/content_utility_client.h"
#include "content/shell/common/shell_switches.h"
#include "extensions/common/extension_paths.h"
#include "extensions/shell/browser/default_shell_browser_main_delegate.h"
#include "extensions/shell/browser/shell_content_browser_client.h"
#include "extensions/shell/common/shell_content_client.h"
#include "extensions/shell/renderer/shell_content_renderer_client.h"
-#include "extensions/shell/utility/shell_content_utility_client.h"
#include "ui/base/resource/resource_bundle.h"
#if defined(OS_CHROMEOS)
@@ -168,11 +168,6 @@
return renderer_client_.get();
}
-content::ContentUtilityClient* ShellMainDelegate::CreateContentUtilityClient() {
- utility_client_.reset(CreateShellContentUtilityClient());
- return utility_client_.get();
-}
-
void ShellMainDelegate::ProcessExiting(const std::string& process_type) {
logging::CloseLogFile();
}
@@ -202,7 +197,7 @@
content::ContentUtilityClient*
ShellMainDelegate::CreateShellContentUtilityClient() {
- return new ShellContentUtilityClient();
+ return new content::ContentUtilityClient();
}
void ShellMainDelegate::InitializeResourceBundle() {
diff --git a/extensions/shell/app/shell_main_delegate.h b/extensions/shell/app/shell_main_delegate.h
index 1f005c6..c43f620 100644
--- a/extensions/shell/app/shell_main_delegate.h
+++ b/extensions/shell/app/shell_main_delegate.h
@@ -31,7 +31,6 @@
void PreSandboxStartup() override;
content::ContentBrowserClient* CreateContentBrowserClient() override;
content::ContentRendererClient* CreateContentRendererClient() override;
- content::ContentUtilityClient* CreateContentUtilityClient() override;
void ProcessExiting(const std::string& process_type) override;
#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
void ZygoteStarting(std::vector<std::unique_ptr<content::ZygoteForkDelegate>>*
diff --git a/extensions/shell/utility/DEPS b/extensions/shell/utility/DEPS
deleted file mode 100644
index 8ad521e..0000000
--- a/extensions/shell/utility/DEPS
+++ /dev/null
@@ -1,3 +0,0 @@
-include_rules = [
- "+content/public/utility",
-]
diff --git a/extensions/shell/utility/shell_content_utility_client.cc b/extensions/shell/utility/shell_content_utility_client.cc
deleted file mode 100644
index 75d72622..0000000
--- a/extensions/shell/utility/shell_content_utility_client.cc
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2014 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 "extensions/shell/utility/shell_content_utility_client.h"
-
-namespace extensions {
-
-ShellContentUtilityClient::ShellContentUtilityClient() = default;
-
-ShellContentUtilityClient::~ShellContentUtilityClient() = default;
-
-void ShellContentUtilityClient::UtilityThreadStarted() {
- utility_handler::UtilityThreadStarted();
-}
-
-} // namespace extensions
diff --git a/extensions/shell/utility/shell_content_utility_client.h b/extensions/shell/utility/shell_content_utility_client.h
deleted file mode 100644
index 4baaf17..0000000
--- a/extensions/shell/utility/shell_content_utility_client.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2014 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.
-
-#ifndef EXTENSIONS_SHELL_UTILITY_SHELL_CONTENT_UTILITY_CLIENT_H_
-#define EXTENSIONS_SHELL_UTILITY_SHELL_CONTENT_UTILITY_CLIENT_H_
-
-#include "content/public/utility/content_utility_client.h"
-#include "extensions/utility/utility_handler.h"
-
-namespace extensions {
-
-class ShellContentUtilityClient : public content::ContentUtilityClient {
- public:
- ShellContentUtilityClient();
- ~ShellContentUtilityClient() override;
-
- // content::ContentUtilityClient:
- void UtilityThreadStarted() override;
-};
-
-} // namespace extensions
-
-#endif // EXTENSIONS_SHELL_UTILITY_SHELL_CONTENT_UTILITY_CLIENT_H_
diff --git a/extensions/strings/extensions_strings.grd b/extensions/strings/extensions_strings.grd
index f3ea305..0fe77a51d 100644
--- a/extensions/strings/extensions_strings.grd
+++ b/extensions/strings/extensions_strings.grd
@@ -385,17 +385,6 @@
Shaped windows are not supported.
</message>
</if>
-
- <!-- Utility process names. Please keep alphabetized. -->
- <message name="IDS_UTILITY_PROCESS_EXTENSION_UNPACKER_NAME" desc="The name of the utility process used for unpacking extensions.">
- Extension Unpacker
- </message>
- <message name="IDS_UTILITY_PROCESS_MANIFEST_PARSER_NAME" desc="The name of the utility process used for parsing extension manifests.">
- Extension Manifest Parser
- </message>
- <message name="IDS_UTILITY_PROCESS_ZIP_FILE_INSTALLER_NAME" desc="The name of the utility process used for unpacking zip files.">
- Zip File Installer
- </message>
</messages>
</release>
</grit>
diff --git a/extensions/test/test_content_utility_client.cc b/extensions/test/test_content_utility_client.cc
index 10c2c16..69895ce 100644
--- a/extensions/test/test_content_utility_client.cc
+++ b/extensions/test/test_content_utility_client.cc
@@ -17,10 +17,7 @@
TestContentUtilityClient::~TestContentUtilityClient() = default;
void TestContentUtilityClient::UtilityThreadStarted() {
- utility_handler::UtilityThreadStarted();
-
auto registry = std::make_unique<service_manager::BinderRegistry>();
- utility_handler::ExposeInterfacesToBrowser(registry.get(), false);
content::ChildThread::Get()
->GetServiceManagerConnection()
->AddConnectionFilter(std::make_unique<content::SimpleConnectionFilter>(
diff --git a/extensions/test/test_content_utility_client.h b/extensions/test/test_content_utility_client.h
index 18531eb..b2edbd6b 100644
--- a/extensions/test/test_content_utility_client.h
+++ b/extensions/test/test_content_utility_client.h
@@ -6,7 +6,6 @@
#define EXTENSIONS_TEST_TEST_CONTENT_UTILITY_CLIENT_H_
#include "content/public/utility/content_utility_client.h"
-#include "extensions/utility/utility_handler.h"
namespace extensions {
diff --git a/extensions/utility/BUILD.gn b/extensions/utility/BUILD.gn
deleted file mode 100644
index de71ac1..0000000
--- a/extensions/utility/BUILD.gn
+++ /dev/null
@@ -1,42 +0,0 @@
-# Copyright 2014 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.
-
-import("//build/config/features.gni")
-import("//extensions/buildflags/buildflags.gni")
-
-assert(enable_extensions)
-
-source_set("utility") {
- sources = [
- "utility_handler.cc",
- "utility_handler.h",
- ]
-
- # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
- configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
-
- deps = [
- "//content/public/common",
- "//content/public/utility",
- "//extensions/common",
- "//skia",
- ]
-}
-
-source_set("unit_tests") {
- testonly = true
- sources = [
- "utility_handler_unittest.cc",
- ]
- deps = [
- ":utility",
- "//base",
- "//extensions:test_support",
- "//extensions/common",
- "//extensions/strings",
- "//testing/gtest",
- "//third_party/zlib/google:zip",
- "//ui/base",
- ]
-}
diff --git a/extensions/utility/DEPS b/extensions/utility/DEPS
deleted file mode 100644
index 48d2f8d..0000000
--- a/extensions/utility/DEPS
+++ /dev/null
@@ -1,6 +0,0 @@
-include_rules = [
- "+content/public/utility",
- "+content/public/child",
- "+net",
- "+third_party/zlib/google",
-]
diff --git a/extensions/utility/utility_handler.cc b/extensions/utility/utility_handler.cc
deleted file mode 100644
index 40d7116e..0000000
--- a/extensions/utility/utility_handler.cc
+++ /dev/null
@@ -1,182 +0,0 @@
-// Copyright 2014 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 "extensions/utility/utility_handler.h"
-
-#include <memory>
-#include <string>
-#include <utility>
-
-#include "base/command_line.h"
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/json/json_file_value_serializer.h"
-#include "base/location.h"
-#include "base/task_scheduler/post_task.h"
-#include "content/public/utility/utility_thread.h"
-#include "extensions/common/constants.h"
-#include "extensions/common/extension_l10n_util.h"
-#include "extensions/common/extension_unpacker.mojom.h"
-#include "extensions/common/extensions_client.h"
-#include "extensions/common/features/feature_channel.h"
-#include "extensions/common/features/feature_session_type.h"
-#include "extensions/common/manifest.h"
-#include "extensions/common/manifest_constants.h"
-#include "extensions/strings/grit/extensions_strings.h"
-#include "mojo/public/cpp/bindings/strong_binding.h"
-#include "third_party/zlib/google/zip.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/base/ui_base_switches.h"
-
-namespace extensions {
-
-namespace {
-
-constexpr const base::FilePath::CharType* kAllowedThemeFiletypes[] = {
- FILE_PATH_LITERAL(".bmp"), FILE_PATH_LITERAL(".gif"),
- FILE_PATH_LITERAL(".jpeg"), FILE_PATH_LITERAL(".jpg"),
- FILE_PATH_LITERAL(".json"), FILE_PATH_LITERAL(".png"),
- FILE_PATH_LITERAL(".webp")};
-
-std::unique_ptr<base::DictionaryValue> ReadManifest(
- const base::FilePath& extension_dir,
- std::string* error) {
- DCHECK(error);
- base::FilePath manifest_path = extension_dir.Append(kManifestFilename);
- if (!base::PathExists(manifest_path)) {
- *error = manifest_errors::kInvalidManifest;
- return nullptr;
- }
-
- JSONFileValueDeserializer deserializer(manifest_path);
- std::unique_ptr<base::Value> root = deserializer.Deserialize(NULL, error);
- if (!root) {
- return nullptr;
- }
-
- if (!root->is_dict()) {
- *error = manifest_errors::kInvalidManifest;
- return nullptr;
- }
-
- return base::DictionaryValue::From(std::move(root));
-}
-
-class ExtensionUnpackerImpl : public extensions::mojom::ExtensionUnpacker {
- public:
- ExtensionUnpackerImpl() = default;
- ~ExtensionUnpackerImpl() override = default;
-
- static void Create(extensions::mojom::ExtensionUnpackerRequest request) {
- mojo::MakeStrongBinding(std::make_unique<ExtensionUnpackerImpl>(),
- std::move(request));
- }
-
- private:
- // extensions::mojom::ExtensionUnpacker:
- void Unzip(const base::FilePath& file,
- const base::FilePath& path,
- UnzipCallback callback) override {
- // Move unzip operation to background thread to avoid blocking the main
- // utility thread for extended amont of time. For example, this prevents
- // extension unzipping block receipt of the connection complete
- // notification for the utility process channel to the browser process,
- // which could cause the utility process to terminate itself due to browser
- // process being considered unreachable.
- base::PostTaskWithTraitsAndReplyWithResult(
- FROM_HERE,
- {base::TaskPriority::USER_BLOCKING, base::MayBlock(),
- base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
- base::BindOnce(&ExtensionUnpackerImpl::UnzipOnBackgroundTaskRunner,
- file, path),
- std::move(callback));
- }
-
- static bool UnzipFileManifestIntoPath(
- const base::FilePath& file,
- const base::FilePath& path,
- std::unique_ptr<base::DictionaryValue>* manifest) {
- if (zip::UnzipWithFilterCallback(
- file, path, base::BindRepeating(&utility_handler::IsManifestFile),
- false)) {
- std::string error;
- *manifest = ReadManifest(path, &error);
- return error.empty() && manifest->get();
- }
-
- return false;
- }
-
- static bool UnzipFileIntoPath(
- const base::FilePath& file,
- const base::FilePath& path,
- std::unique_ptr<base::DictionaryValue> manifest) {
- Manifest internal(Manifest::INTERNAL, std::move(manifest));
- // TODO(crbug.com/645263): This silently ignores blocked file types.
- // Add install warnings.
- return zip::UnzipWithFilterCallback(
- file, path,
- base::BindRepeating(&utility_handler::ShouldExtractFile,
- internal.is_theme()),
- true /* log_skipped_files */);
- }
-
- // Unzips the extension from |file| to |path|.
- // Returns whether the unzip operation succeeded.
- static bool UnzipOnBackgroundTaskRunner(const base::FilePath& file,
- const base::FilePath& path) {
- std::unique_ptr<base::DictionaryValue> manifest;
- if (!UnzipFileManifestIntoPath(file, path, &manifest))
- return false;
-
- return UnzipFileIntoPath(file, path, std::move(manifest));
- }
-
- DISALLOW_COPY_AND_ASSIGN(ExtensionUnpackerImpl);
-};
-
-} // namespace
-
-namespace utility_handler {
-
-void UtilityThreadStarted() {
- base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
- std::string lang = command_line->GetSwitchValueASCII(switches::kLang);
- if (!lang.empty())
- extension_l10n_util::SetProcessLocale(lang);
-}
-
-void ExposeInterfacesToBrowser(service_manager::BinderRegistry* registry,
- bool running_elevated) {
- // If our process runs with elevated privileges, only add elevated Mojo
- // interfaces to the interface registry.
- if (running_elevated)
- return;
-
- registry->AddInterface(base::Bind(&ExtensionUnpackerImpl::Create),
- base::ThreadTaskRunnerHandle::Get());
-}
-
-bool ShouldExtractFile(bool is_theme, const base::FilePath& file_path) {
- if (is_theme) {
- const base::FilePath::StringType extension =
- base::ToLowerASCII(file_path.FinalExtension());
- // Allow filenames with no extension.
- if (extension.empty())
- return true;
- return base::ContainsValue(kAllowedThemeFiletypes, extension);
- }
- return !base::FilePath::CompareEqualIgnoreCase(file_path.FinalExtension(),
- FILE_PATH_LITERAL(".exe"));
-}
-
-bool IsManifestFile(const base::FilePath& file_path) {
- CHECK(!file_path.IsAbsolute());
- return base::FilePath::CompareEqualIgnoreCase(file_path.value(),
- kManifestFilename);
-}
-
-} // namespace utility_handler
-
-} // namespace extensions
diff --git a/extensions/utility/utility_handler.h b/extensions/utility/utility_handler.h
deleted file mode 100644
index 7a361d9..0000000
--- a/extensions/utility/utility_handler.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2014 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.
-
-#ifndef EXTENSIONS_UTILITY_UTILITY_HANDLER_H_
-#define EXTENSIONS_UTILITY_UTILITY_HANDLER_H_
-
-#include "services/service_manager/public/cpp/binder_registry.h"
-
-namespace base {
-class FilePath;
-}
-
-namespace extensions {
-
-namespace utility_handler {
-
-void UtilityThreadStarted();
-
-void ExposeInterfacesToBrowser(service_manager::BinderRegistry* registry,
- bool running_elevated);
-
-bool ShouldExtractFile(bool is_theme, const base::FilePath& file_path);
-
-bool IsManifestFile(const base::FilePath& file_path);
-
-} // namespace utility_handler
-
-} // namespace extensions
-
-#endif // EXTENSIONS_UTILITY_UTILITY_HANDLER_H_
diff --git a/extensions/utility/utility_handler_unittest.cc b/extensions/utility/utility_handler_unittest.cc
deleted file mode 100644
index db2c0293..0000000
--- a/extensions/utility/utility_handler_unittest.cc
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2018 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 <vector>
-
-#include "base/bind.h"
-#include "base/files/file_path.h"
-#include "extensions/utility/utility_handler.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace extensions {
-
-using UtilityHandlerTest = testing::Test;
-
-struct UnzipFileFilterTestCase {
- const base::FilePath::CharType* input;
- const bool should_unzip;
-};
-
-void RunZipFileFilterTest(
- const std::vector<UnzipFileFilterTestCase>& cases,
- base::RepeatingCallback<bool(const base::FilePath&)>& filter) {
- for (size_t i = 0; i < cases.size(); ++i) {
- base::FilePath input(cases[i].input);
- bool observed = filter.Run(input);
- EXPECT_EQ(cases[i].should_unzip, observed)
- << "i: " << i << ", input: " << input.value();
- }
-}
-
-TEST_F(UtilityHandlerTest, NonTheme_FileExtractionFilter) {
- const std::vector<UnzipFileFilterTestCase> cases = {
- {FILE_PATH_LITERAL("foo"), true},
- {FILE_PATH_LITERAL("foo.nexe"), true},
- {FILE_PATH_LITERAL("foo.dll"), true},
- {FILE_PATH_LITERAL("foo.jpg.exe"), false},
- {FILE_PATH_LITERAL("foo.exe"), false},
- {FILE_PATH_LITERAL("foo.EXE"), false},
- {FILE_PATH_LITERAL("file_without_extension"), true},
- };
- base::RepeatingCallback<bool(const base::FilePath&)> filter =
- base::BindRepeating(&utility_handler::ShouldExtractFile, false);
- RunZipFileFilterTest(cases, filter);
-}
-
-TEST_F(UtilityHandlerTest, Theme_FileExtractionFilter) {
- const std::vector<UnzipFileFilterTestCase> cases = {
- {FILE_PATH_LITERAL("image.jpg"), true},
- {FILE_PATH_LITERAL("IMAGE.JPEG"), true},
- {FILE_PATH_LITERAL("test/image.bmp"), true},
- {FILE_PATH_LITERAL("test/IMAGE.gif"), true},
- {FILE_PATH_LITERAL("test/image.WEBP"), true},
- {FILE_PATH_LITERAL("test/dir/file.image.png"), true},
- {FILE_PATH_LITERAL("manifest.json"), true},
- {FILE_PATH_LITERAL("other.html"), false},
- {FILE_PATH_LITERAL("file_without_extension"), true},
- };
- base::RepeatingCallback<bool(const base::FilePath&)> filter =
- base::BindRepeating(&utility_handler::ShouldExtractFile, true);
- RunZipFileFilterTest(cases, filter);
-}
-
-TEST_F(UtilityHandlerTest, ManifestExtractionFilter) {
- const std::vector<UnzipFileFilterTestCase> cases = {
- {FILE_PATH_LITERAL("manifest.json"), true},
- {FILE_PATH_LITERAL("MANIFEST.JSON"), true},
- {FILE_PATH_LITERAL("test/manifest.json"), false},
- {FILE_PATH_LITERAL("manifest.json/test"), false},
- {FILE_PATH_LITERAL("other.file"), false},
- };
- base::RepeatingCallback<bool(const base::FilePath&)> filter =
- base::BindRepeating(&utility_handler::IsManifestFile);
- RunZipFileFilterTest(cases, filter);
-}
-
-} // namespace extensions