Files
brave-core/components/greaselion/browser/greaselion_service_impl.cc
T
Mario Sanchez Prada b2e4de697f Migrate usage of base::Passed with base::BindOnce to std::move
A new static assertion has been added upstream and otherwise the
build will fail for Linux now, as well as for other platforms in
the next release.

Chromium change:

https://source.chromium.org/chromium/chromium/src/+/ad0da8ebec488b6b3696b44d2da80744f8a90d17
https://source.chromium.org/chromium/chromium/src/+/63714a9ffe267a0212f4120a188f45408555a993

commit ad0da8ebec488b6b3696b44d2da80744f8a90d17
Author: Daniel Cheng <dcheng@chromium.org>
Date:   Mon Feb 22 21:07:22 2021 +0000

    Disallow base::Passed() from being used with base::BindOnce().

    The assert is currently only enabled on OS_LINUX. Platforms will be
    incrementally fixed until the assert is enabled everywhere.

    Bug: 1180750

commit 63714a9ffe267a0212f4120a188f45408555a993
Author: Daniel Cheng <dcheng@chromium.org>
Date: Fri Feb 26 09:51:28 2021

    [base] Disallow base::BindOnce() + base::Passed() use on iOS.

    std::move() and base::Passed() mean the same thing for base::BindOnce(),
    so use the standard library idiom instead.

    Bug: 1180750
2021-03-23 14:55:33 -04:00

386 lines
14 KiB
C++

/* Copyright (c) 2019 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "brave/components/greaselion/browser/greaselion_service_impl.h"
#include <stddef.h>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/base64.h"
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/json/json_file_value_serializer.h"
#include "base/one_shot_event.h"
#include "base/sequenced_task_runner.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/post_task.h"
#include "base/task_runner_util.h"
#include "base/values.h"
#include "base/version.h"
#include "brave/browser/version_info.h"
#include "brave/components/brave_component_updater/browser/features.h"
#include "brave/components/brave_component_updater/browser/switches.h"
#include "brave/components/greaselion/browser/greaselion_download_service.h"
#include "chrome/browser/extensions/extension_service.h"
#include "components/version_info/version_info.h"
#include "crypto/sha2.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_system.h"
#include "extensions/common/api/content_scripts.h"
#include "extensions/common/constants.h"
#include "extensions/common/extension.h"
#include "extensions/common/file_util.h"
#include "extensions/common/manifest_constants.h"
#include "url/gurl.h"
using extensions::Extension;
using extensions::Manifest;
namespace {
constexpr char kRunAtDocumentStart[] = "document_start";
// Wraps a Greaselion rule in a component. The component is stored as
// an unpacked extension in the user data dir. Returns a valid
// extension that the caller should take ownership of, or nullptr.
//
// NOTE: This function does file IO and should not be called on the UI thread.
// NOTE: The caller takes ownership of the directory at extension->path() on the
// returned object.
base::Optional<greaselion::GreaselionServiceImpl::GreaselionConvertedExtension>
ConvertGreaselionRuleToExtensionOnTaskRunner(
const greaselion::GreaselionRule& rule,
const base::FilePath& install_dir) {
base::FilePath install_temp_dir =
extensions::file_util::GetInstallTempDir(install_dir);
if (install_temp_dir.empty()) {
LOG(ERROR) << "Could not get path to profile temp directory";
return base::nullopt;
}
base::ScopedTempDir temp_dir;
if (!temp_dir.CreateUniqueTempDirUnderPath(install_temp_dir)) {
LOG(ERROR) << "Could not create Greaselion temp directory";
return base::nullopt;
}
// Create the manifest
std::unique_ptr<base::DictionaryValue> root(new base::DictionaryValue);
// manifest version is always 2
// see kModernManifestVersion in src/extensions/common/extension.cc
root->SetIntPath(extensions::manifest_keys::kManifestVersion, 2);
// Create the public key.
// Greaselion scripts are not signed, but the public key for an extension
// doubles as its unique identity, and we need one of those, so we add the
// rule name to a known Brave domain and hash the result to create a
// public key.
char raw[crypto::kSHA256Length] = {0};
std::string key;
std::string script_name = rule.name();
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
if (!command_line.HasSwitch(brave_component_updater::kUseGoUpdateDev) &&
!base::FeatureList::IsEnabled(
brave_component_updater::kUseDevUpdaterUrl)) {
crypto::SHA256HashString(UPDATER_DEV_ENDPOINT + script_name,
raw,
crypto::kSHA256Length);
} else {
crypto::SHA256HashString(UPDATER_PROD_ENDPOINT + script_name,
raw,
crypto::kSHA256Length);
}
base::Base64Encode(base::StringPiece(raw, crypto::kSHA256Length), &key);
root->SetStringPath(extensions::manifest_keys::kName, script_name);
root->SetStringPath(extensions::manifest_keys::kVersion, "1.0");
root->SetStringPath(extensions::manifest_keys::kDescription, "");
root->SetStringPath(extensions::manifest_keys::kPublicKey, key);
root->SetStringPath("incognito",
extensions::manifest_values::kIncognitoNotAllowed);
std::vector<std::string> matches;
matches.reserve(rule.url_patterns().size());
for (auto url_pattern : rule.url_patterns())
matches.push_back(url_pattern);
extensions::api::content_scripts::ContentScript content_script;
content_script.matches = std::move(matches);
content_script.js = std::make_unique<std::vector<std::string>>();
for (auto script : rule.scripts())
content_script.js->push_back(script.BaseName().AsUTF8Unsafe());
// All Greaselion scripts default to document end.
content_script.run_at =
rule.run_at() == kRunAtDocumentStart
? extensions::api::content_scripts::RUN_AT_DOCUMENT_START
: extensions::api::content_scripts::RUN_AT_DOCUMENT_END;
if (!rule.messages().empty()) {
root->SetStringPath(extensions::manifest_keys::kDefaultLocale, "en_US");
}
auto content_scripts = std::make_unique<base::ListValue>();
content_scripts->Append(content_script.ToValue());
root->Set(extensions::api::content_scripts::ManifestKeys::kContentScripts,
std::move(content_scripts));
base::FilePath manifest_path =
temp_dir.GetPath().Append(extensions::kManifestFilename);
JSONFileValueSerializer serializer(manifest_path);
// If you read the header file for this function, it says not to use it
// outside unit tests because it writes to disk (which blocks the thread). I
// just want to assure you that it's okay. We want to write to disk here, and
// we're already on a task runner specifically for writing extension-related
// files to disk.
if (!serializer.Serialize(*root)) {
LOG(ERROR) << "Could not write Greaselion manifest";
return base::nullopt;
}
// Copy the messages directory to our extension directory.
if (!rule.messages().empty()) {
if (!base::CopyDirectory(
rule.messages(),
temp_dir.GetPath().AppendASCII("_locales"), true)) {
LOG(ERROR) << "Could not copy Greaselion messages directory at path: "
<< rule.messages().LossyDisplayName();
return base::nullopt;
}
}
// Copy the script files to our extension directory.
for (auto script : rule.scripts()) {
if (!base::CopyFile(script,
temp_dir.GetPath().Append(script.BaseName()))) {
LOG(ERROR) << "Could not copy Greaselion script at path: "
<< script.LossyDisplayName();
return base::nullopt;
}
}
std::string error;
scoped_refptr<Extension> extension = extensions::file_util::LoadExtension(
temp_dir.GetPath(), Manifest::COMPONENT, Extension::NO_FLAGS, &error);
if (!extension.get()) {
LOG(ERROR) << "Could not load Greaselion extension";
LOG(ERROR) << error;
return base::nullopt;
}
// Take ownership of this temporary directory so it's deleted when
// the service exits
return std::make_pair(extension, std::move(temp_dir));
}
} // namespace
namespace greaselion {
GreaselionServiceImpl::GreaselionServiceImpl(
GreaselionDownloadService* download_service,
const base::FilePath& install_directory,
extensions::ExtensionSystem* extension_system,
extensions::ExtensionRegistry* extension_registry,
scoped_refptr<base::SequencedTaskRunner> task_runner)
: download_service_(download_service),
install_directory_(install_directory),
extension_system_(extension_system),
extension_service_(extension_system->extension_service()),
extension_registry_(extension_registry),
all_rules_installed_successfully_(true),
update_in_progress_(false),
update_pending_(false),
pending_installs_(0),
task_runner_(std::move(task_runner)),
browser_version_(
version_info::GetBraveVersionWithoutChromiumMajorVersion()),
weak_factory_(this) {
extension_registry_->AddObserver(this);
for (int i = FIRST_FEATURE; i != LAST_FEATURE; i++)
state_[static_cast<GreaselionFeature>(i)] = false;
// Static-value features
state_[GreaselionFeature::SUPPORTS_MINIMUM_BRAVE_VERSION] = true;
}
GreaselionServiceImpl::~GreaselionServiceImpl() {
extension_registry_->RemoveObserver(this);
}
bool GreaselionServiceImpl::IsGreaselionExtension(const std::string& id) {
return std::find(greaselion_extensions_.begin(), greaselion_extensions_.end(),
id) != greaselion_extensions_.end();
}
std::vector<extensions::ExtensionId>
GreaselionServiceImpl::GetExtensionIdsForTesting() {
return greaselion_extensions_;
}
void GreaselionServiceImpl::UpdateInstalledExtensions() {
if (update_in_progress_) {
update_pending_ = true;
return;
}
update_in_progress_ = true;
if (greaselion_extensions_.empty()) {
// No Greaselion extensions are currently installed, so we can move on to
// the install phase immediately.
CreateAndInstallExtensions();
return;
}
// Make a copy of greaselion_extensions_ to iterate while the original vector
// changes.
std::vector<extensions::ExtensionId> extensions = greaselion_extensions_;
for (auto id : extensions) {
// We need to unload all the Greaselion extensions that are already
// installed. OnExtensionUnloaded will be called on each extension, where we
// will update the greaselion_extensions_ set. Once it's empty, that
// callback will call CreateAndInstallExtensions().
extension_service_->UnloadExtension(
id, extensions::UnloadedExtensionReason::UPDATE);
}
}
void GreaselionServiceImpl::CreateAndInstallExtensions() {
DCHECK(greaselion_extensions_.empty());
DCHECK(update_in_progress_);
all_rules_installed_successfully_ = true;
pending_installs_ = 0;
std::vector<std::unique_ptr<GreaselionRule>>* rules =
download_service_->rules();
for (const std::unique_ptr<GreaselionRule>& rule : *rules) {
if (rule->Matches(state_, browser_version_) &&
rule->has_unknown_preconditions() == false) {
pending_installs_ += 1;
}
}
if (!pending_installs_) {
// no rules match, nothing else to do
MaybeNotifyObservers();
return;
}
for (const std::unique_ptr<GreaselionRule>& rule : *rules) {
if (rule->Matches(state_, browser_version_) &&
rule->has_unknown_preconditions() == false) {
// Convert script file to component extension. This must run on extension
// file task runner, which was passed in in the constructor.
GreaselionRule rule_copy(*rule);
base::PostTaskAndReplyWithResult(
task_runner_.get(), FROM_HERE,
base::BindOnce(&ConvertGreaselionRuleToExtensionOnTaskRunner,
rule_copy, install_directory_),
base::BindOnce(&GreaselionServiceImpl::PostConvert,
weak_factory_.GetWeakPtr()));
}
}
}
void GreaselionServiceImpl::PostConvert(
base::Optional<GreaselionConvertedExtension> converted_extension) {
if (!converted_extension) {
all_rules_installed_successfully_ = false;
pending_installs_ -= 1;
MaybeNotifyObservers();
LOG(ERROR) << "Could not load Greaselion script";
} else {
greaselion_extensions_.push_back(converted_extension->first->id());
extension_dirs_.push_back(std::move(converted_extension->second));
extension_system_->ready().Post(
FROM_HERE, base::BindOnce(&GreaselionServiceImpl::Install,
weak_factory_.GetWeakPtr(),
std::move(converted_extension->first)));
}
}
void GreaselionServiceImpl::Install(
scoped_refptr<extensions::Extension> extension) {
extension_service_->AddExtension(extension.get());
}
void GreaselionServiceImpl::OnExtensionReady(
content::BrowserContext* browser_context,
const extensions::Extension* extension) {
auto index = std::find(greaselion_extensions_.begin(),
greaselion_extensions_.end(), extension->id());
if (index == greaselion_extensions_.end()) {
// not one of ours
return;
}
pending_installs_ -= 1;
MaybeNotifyObservers();
}
void GreaselionServiceImpl::OnExtensionUnloaded(
content::BrowserContext* browser_context,
const extensions::Extension* extension,
extensions::UnloadedExtensionReason reason) {
auto index = std::find(greaselion_extensions_.begin(),
greaselion_extensions_.end(), extension->id());
if (index == greaselion_extensions_.end()) {
// not one of ours
return;
}
greaselion_extensions_.erase(index);
if (update_in_progress_ && greaselion_extensions_.empty()) {
// It's time!
CreateAndInstallExtensions();
}
}
void GreaselionServiceImpl::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
}
void GreaselionServiceImpl::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer);
}
void GreaselionServiceImpl::MaybeNotifyObservers() {
if (!pending_installs_) {
update_in_progress_ = false;
if (update_pending_) {
update_pending_ = false;
UpdateInstalledExtensions();
} else {
for (Observer& observer : observers_)
observer.OnExtensionsReady(this, all_rules_installed_successfully_);
}
}
}
void GreaselionServiceImpl::SetFeatureEnabled(GreaselionFeature feature,
bool enabled) {
DCHECK(feature >= 0 && feature < LAST_FEATURE);
state_[feature] = enabled;
UpdateInstalledExtensions();
}
bool GreaselionServiceImpl::ready() {
return !update_in_progress_;
}
void GreaselionServiceImpl::SetBrowserVersionForTesting(
const base::Version& version) {
CHECK(version.IsValid());
browser_version_ = version;
UpdateInstalledExtensions();
}
} // namespace greaselion