blob: 9ffdbbc909d665b2c6dbf3e845227a903d640a06 [file] [log] [blame]
// 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/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/strings/grit/extensions_strings.h"
#include "extensions/utility/unpacker.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 {
struct UnpackResult {
std::unique_ptr<base::DictionaryValue> parsed_manifest;
std::unique_ptr<base::ListValue> parsed_json_ruleset;
base::string16 error;
};
// Unpacks the extension on background task runner.
// On success, returns UnpackResult with |parsed_manifest| set to the parsed
// extension manifest.
// On failure returns UnpackResult with |error| set to the encountered error
// message.
UnpackResult UnpackOnBackgroundTaskRunner(const base::FilePath& path,
const std::string& extension_id,
Manifest::Location location,
int32_t creation_flags) {
Unpacker unpacker(path.DirName(), path, extension_id, location,
creation_flags);
UnpackResult result;
if (unpacker.Run()) {
result.parsed_manifest = unpacker.TakeParsedManifest();
result.parsed_json_ruleset = unpacker.TakeParsedJSONRuleset();
} else {
result.error = unpacker.error_message();
}
return result;
}
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));
}
void Unpack(version_info::Channel channel,
extensions::FeatureSessionType type,
const base::FilePath& path,
const std::string& extension_id,
Manifest::Location location,
int32_t creation_flags,
UnpackCallback callback) override {
CHECK_GT(location, Manifest::INVALID_LOCATION);
CHECK_LT(location, Manifest::NUM_LOCATIONS);
DCHECK(ExtensionsClient::Get());
content::UtilityThread::Get()->EnsureBlinkInitialized();
// Initialize extension system global state.
SetCurrentChannel(channel);
SetCurrentFeatureSessionType(type);
// Move unpack operation to background thread to prevent it from blocking
// the utility process thread for extended amount of time.
base::PostTaskWithTraitsAndReplyWithResult(
FROM_HERE,
{base::TaskPriority::USER_BLOCKING, base::MayBlock(),
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
base::BindOnce(&UnpackOnBackgroundTaskRunner, path, extension_id,
location, creation_flags),
base::BindOnce(
[](UnpackCallback callback, UnpackResult result) {
std::move(callback).Run(result.error,
std::move(result.parsed_manifest),
std::move(result.parsed_json_ruleset));
},
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::Bind(&Unpacker::IsManifestFile), false)) {
std::string error;
*manifest = Unpacker::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::Bind(&Unpacker::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());
}
} // namespace utility_handler
} // namespace extensions