#include "chrome/browser/extensions/chrome_extension_registrar_delegate.h"
#include <set>
#include <string>
#include "base/barrier_closure.h"
#include "base/files/file_util.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/notimplemented.h"
#include "chrome/browser/extensions/component_loader.h"
#include "chrome/browser/extensions/corrupted_extension_reinstaller.h"
#include "chrome/browser/extensions/data_deleter.h"
#include "chrome/browser/extensions/extension_allowlist.h"
#include "chrome/browser/extensions/extension_assets_manager.h"
#include "chrome/browser/extensions/extension_disabled_ui.h"
#include "chrome/browser/extensions/extension_management.h"
#include "chrome/browser/extensions/extension_special_storage_policy.h"
#include "chrome/browser/extensions/external_install_manager.h"
#include "chrome/browser/extensions/install_verifier_factory.h"
#include "chrome/browser/extensions/installed_loader.h"
#include "chrome/browser/extensions/profile_util.h"
#include "chrome/browser/extensions/unpacked_installer.h"
#include "chrome/browser/extensions/updater/extension_updater.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/webui/favicon_source.h"
#include "chrome/common/webui_url_constants.h"
#include "components/favicon_base/favicon_url_parser.h"
#include "extensions/browser/delayed_install_manager.h"
#include "extensions/browser/disable_reason.h"
#include "extensions/browser/extension_file_task_runner.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_system.h"
#include "extensions/browser/extension_util.h"
#include "extensions/browser/extensions_browser_client.h"
#include "extensions/browser/install_flag.h"
#include "extensions/browser/install_verifier.h"
#include "extensions/browser/pending_extension_manager.h"
#include "extensions/browser/permissions/permissions_updater.h"
#include "extensions/buildflags/buildflags.h"
#include "extensions/common/crash_keys.h"
#include "extensions/common/extension.h"
#include "extensions/common/manifest_handlers/incognito_info.h"
#include "extensions/common/manifest_handlers/shared_module_info.h"
#include "extensions/common/mojom/manifest.mojom-shared.h"
#include "extensions/common/permissions/permission_message_provider.h"
#include "extensions/common/permissions/permission_set.h"
#include "extensions/common/permissions/permissions_data.h"
#if BUILDFLAG(IS_CHROMEOS)
#include "chrome/browser/ash/fileapi/file_system_backend.h"
#include "content/public/browser/storage_partition.h"
#include "storage/browser/file_system/file_system_context.h"
#endif
#if BUILDFLAG(ENABLE_EXTENSIONS)
#include "chrome/browser/extensions/sync/extension_sync_service.h"
#include "chrome/browser/ui/webui/theme_source.h"
#endif
static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
using extensions::mojom::ManifestLocation;
namespace extensions {
namespace {
bool SkipDeleteExtensionDir(const Extension& extension,
const base::FilePath& profile_path) {
bool is_unpacked_location =
Manifest::IsUnpackedLocation(extension.location());
bool extension_dir_not_direct_subdir_of_unpacked_extensions_install_dir =
extension.path().DirName() !=
profile_path.AppendASCII(extensions::kUnpackedInstallDirectoryName);
return is_unpacked_location &&
extension_dir_not_direct_subdir_of_unpacked_extensions_install_dir;
}
}
ChromeExtensionRegistrarDelegate::ChromeExtensionRegistrarDelegate(
Profile* profile)
: profile_(profile),
system_(ExtensionSystem::Get(profile_)),
extension_prefs_(ExtensionPrefs::Get(profile_)),
registry_(ExtensionRegistry::Get(profile_)),
component_loader_(ComponentLoader::Get(profile_)) {}
ChromeExtensionRegistrarDelegate::~ChromeExtensionRegistrarDelegate() = default;
void ChromeExtensionRegistrarDelegate::Init(ExtensionRegistrar* registrar) {
extension_registrar_ = registrar;
}
void ChromeExtensionRegistrarDelegate::Shutdown() {
profile_ = nullptr;
extension_prefs_ = nullptr;
system_ = nullptr;
registry_ = nullptr;
extension_registrar_ = nullptr;
component_loader_ = nullptr;
}
void ChromeExtensionRegistrarDelegate::PreAddExtension(
const Extension* extension,
const Extension* old_extension) {
if (old_extension && !IncognitoInfo::IsIncognitoAllowed(extension)) {
extension_prefs_->SetIsIncognitoEnabled(extension->id(), false);
}
CheckPermissionsIncrease(extension, !!old_extension);
}
void ChromeExtensionRegistrarDelegate::OnAddNewOrUpdatedExtension(
const Extension* extension) {
if (InstallVerifier::NeedsVerification(*extension, profile_)) {
InstallVerifierFactory::GetForBrowserContext(profile_)->VerifyExtension(
extension->id());
}
}
void ChromeExtensionRegistrarDelegate::PostActivateExtension(
scoped_refptr<const Extension> extension) {
PermissionsUpdater(profile_).ApplyPolicyHostRestrictions(*extension);
auto* special_storage_policy = profile_->GetExtensionSpecialStoragePolicy();
CHECK(special_storage_policy);
special_storage_policy->GrantRightsForExtension(extension.get(), profile_);
UpdateActiveExtensionsInCrashReporter();
const PermissionsData* permissions_data = extension->permissions_data();
if (permissions_data->HasHostPermission(GURL(chrome::kChromeUIFaviconURL))) {
content::URLDataSource::Add(
profile_, std::make_unique<FaviconSource>(
profile_, chrome::FaviconUrlFormat::kFaviconLegacy));
}
if (permissions_data->HasHostPermission(GURL(chrome::kChromeUIThemeURL))) {
#if BUILDFLAG(ENABLE_EXTENSIONS)
content::URLDataSource::Add(profile_,
std::make_unique<ThemeSource>(profile_));
#else
NOTIMPLEMENTED() << "Themes not yet supported on desktop Android.";
#endif
}
}
void ChromeExtensionRegistrarDelegate::PostDeactivateExtension(
scoped_refptr<const Extension> extension) {
auto* special_storage_policy = profile_->GetExtensionSpecialStoragePolicy();
CHECK(special_storage_policy);
special_storage_policy->RevokeRightsForExtension(extension.get(), profile_);
#if BUILDFLAG(IS_CHROMEOS)
storage::FileSystemContext* filesystem_context =
util::GetStoragePartitionForExtensionId(extension->id(), profile_)
->GetFileSystemContext();
if (filesystem_context && ash::FileSystemBackend::Get(*filesystem_context)) {
ash::FileSystemBackend::Get(*filesystem_context)
->RevokeAccessForOrigin(extension->origin());
}
#endif
UpdateActiveExtensionsInCrashReporter();
}
void ChromeExtensionRegistrarDelegate::PreUninstallExtension(
scoped_refptr<const Extension> extension) {
InstallVerifierFactory::GetForBrowserContext(profile_)->Remove(
extension->id());
}
void ChromeExtensionRegistrarDelegate::PostUninstallExtension(
scoped_refptr<const Extension> extension,
base::OnceClosure done_callback) {
bool is_unpacked_location =
Manifest::IsUnpackedLocation(extension->location());
base::RepeatingClosure subtask_done_callback = base::DoNothing();
if (!done_callback.is_null()) {
int num_tasks = is_unpacked_location ? 1 : 2;
subtask_done_callback =
base::BarrierClosure(num_tasks, std::move(done_callback));
}
if (!SkipDeleteExtensionDir(*extension, profile_->GetPath())) {
base::FilePath extension_dir_to_delete =
is_unpacked_location ? extension->path() : extension->path().DirName();
base::FilePath extensions_install_dir =
is_unpacked_location
? extension_registrar_->unpacked_install_directory()
: extension_registrar_->install_directory();
if (!GetExtensionFileTaskRunner()->PostTaskAndReply(
FROM_HERE,
base::BindOnce(&ChromeExtensionRegistrarDelegate::
UninstallExtensionOnFileThread,
extension->id(), profile_->GetProfileUserName(),
std::move(extensions_install_dir),
std::move(extension_dir_to_delete),
profile_->GetPath()),
subtask_done_callback)) {
NOTREACHED();
}
}
DataDeleter::StartDeleting(profile_, extension.get(), subtask_done_callback);
}
void ChromeExtensionRegistrarDelegate::DoLoadExtensionForReload(
const ExtensionId& extension_id,
const base::FilePath& path,
bool load_error_behavior_noisy) {
if (component_loader_->Exists(extension_id)) {
component_loader_->Reload(extension_id);
return;
}
std::optional<ExtensionInfo> installed_extension(
extension_prefs_->GetInstalledExtensionInfo(extension_id));
if (installed_extension && installed_extension->extension_manifest.get()) {
InstalledLoader(profile_).Load(*installed_extension, false);
} else {
CHECK(!path.empty()) << "ExtensionRegistrar should never ask to load an "
"unknown extension with no path";
scoped_refptr<UnpackedInstaller> unpacked_installer =
UnpackedInstaller::Create(profile_);
unpacked_installer->set_be_noisy_on_failure(load_error_behavior_noisy);
unpacked_installer->set_completion_callback(base::BindOnce(
&ChromeExtensionRegistrarDelegate::OnUnpackedReloadFailure,
weak_factory_.GetWeakPtr()));
unpacked_installer->Load(path);
}
}
void ChromeExtensionRegistrarDelegate::LoadExtensionForReload(
const ExtensionId& extension_id,
const base::FilePath& path) {
DoLoadExtensionForReload(extension_id, path, true);
}
void ChromeExtensionRegistrarDelegate::LoadExtensionForReloadWithQuietFailure(
const ExtensionId& extension_id,
const base::FilePath& path) {
DoLoadExtensionForReload(extension_id, path, false);
}
void ChromeExtensionRegistrarDelegate::ShowExtensionDisabledError(
const Extension* extension,
bool is_remote_install) {
AddExtensionDisabledError(profile_, extension, is_remote_install);
}
bool ChromeExtensionRegistrarDelegate::CanEnableExtension(
const Extension* extension) {
CHECK(system_->management_policy());
return !system_->management_policy()->MustRemainDisabled(extension, nullptr);
}
bool ChromeExtensionRegistrarDelegate::CanDisableExtension(
const Extension* extension) {
if (!extension) {
return true;
}
if (SharedModuleInfo::IsSharedModule(extension)) {
return false;
}
if (extension->location() == ManifestLocation::kExternalComponent) {
return true;
}
CHECK(system_->management_policy());
return system_->management_policy()->UserMayModifySettings(extension,
nullptr);
}
void ChromeExtensionRegistrarDelegate::GrantActivePermissions(
const Extension* extension) {
PermissionsUpdater(profile_).GrantActivePermissions(extension);
}
void ChromeExtensionRegistrarDelegate::UpdateExternalExtensionAlert() {
ExternalInstallManager::Get(profile_)->UpdateExternalExtensionAlert();
}
void ChromeExtensionRegistrarDelegate::OnExtensionInstalled(
const Extension* extension,
const syncer::StringOrdinal& page_ordinal,
int install_flags,
base::Value::Dict ruleset_install_prefs) {
const std::string& id = extension->id();
base::flat_set<int> disable_reasons =
extension_registrar_->GetDisableReasonsOnInstalled(extension);
std::string install_parameter;
auto* pending_extension_manager = PendingExtensionManager::Get(profile_);
const PendingExtensionInfo* pending_extension_info =
pending_extension_manager->GetById(id);
auto* corrupted_extension_reinstaller =
CorruptedExtensionReinstaller::Get(profile_);
bool is_reinstall_for_corruption =
corrupted_extension_reinstaller->IsReinstallForCorruptionExpected(id);
if (is_reinstall_for_corruption) {
corrupted_extension_reinstaller->MarkResolved(id);
}
if (pending_extension_info) {
if (!pending_extension_info->ShouldAllowInstall(extension, profile_)) {
#if BUILDFLAG(ENABLE_EXTENSIONS)
if (extension->is_theme() && pending_extension_info->is_from_sync()) {
ExtensionSyncService::Get(profile_)->DeleteThemeDoNotUse(*extension);
}
#endif
pending_extension_manager->Remove(id);
ExtensionManagement* management =
ExtensionManagementFactory::GetForBrowserContext(profile_);
LOG(WARNING) << "ShouldAllowInstall() returned false for " << id
<< " of type " << extension->GetType() << " and update URL "
<< management->GetEffectiveUpdateURL(*extension).spec()
<< "; not installing";
if (!GetExtensionFileTaskRunner()->PostTask(
FROM_HERE,
base::GetDeletePathRecursivelyCallback(extension->path()))) {
NOTREACHED();
}
return;
}
install_parameter = pending_extension_info->install_parameter();
pending_extension_manager->Remove(id);
} else if (!is_reinstall_for_corruption) {
if (extension_prefs_->IsExternalExtensionUninstalled(id)) {
disable_reasons.clear();
}
}
disable_reasons.erase(disable_reason::DISABLE_CORRUPTED);
if (install_flags & kInstallFlagHasRequirementErrors) {
disable_reasons.insert(disable_reason::DISABLE_UNSUPPORTED_REQUIREMENT);
} else {
disable_reasons.erase(disable_reason::DISABLE_UNSUPPORTED_REQUIREMENT);
}
if (ExtensionManagementFactory::GetForBrowserContext(profile_)
->CheckMinimumVersion(extension, nullptr)) {
disable_reasons.erase(disable_reason::DISABLE_UPDATE_REQUIRED_BY_POLICY);
}
if (install_flags & kInstallFlagIsBlocklistedForMalware) {
extension_prefs_->AcknowledgeBlocklistedExtension(id);
UMA_HISTOGRAM_ENUMERATION("ExtensionBlacklist.SilentInstall",
extension->location());
}
RecordInstallHistograms(extension);
ExtensionAllowlist::Get(profile_)->OnExtensionInstalled(id, install_flags);
DelayedInstallManager* delayed_install_manager =
DelayedInstallManager::Get(profile_);
ExtensionPrefs::DelayReason delay_reason;
InstallGate::Action action =
delayed_install_manager->ShouldDelayExtensionInstall(
extension, !!(install_flags & kInstallFlagInstallImmediately),
&delay_reason);
switch (action) {
case InstallGate::INSTALL:
extension_registrar_->AddNewOrUpdatedExtension(
extension, disable_reasons, install_flags, page_ordinal,
install_parameter, std::move(ruleset_install_prefs));
return;
case InstallGate::DELAY:
extension_prefs_->SetDelayedInstallInfo(
extension, disable_reasons, install_flags, delay_reason, page_ordinal,
install_parameter, std::move(ruleset_install_prefs));
delayed_install_manager->Insert(extension);
if (delay_reason == ExtensionPrefs::DelayReason::kWaitForIdle) {
ExtensionUpdater::Get(profile_)->NotifyAppUpdateAvailable(*extension);
}
return;
case InstallGate::ABORT:
return;
}
NOTREACHED() << "Unknown action for delayed install: " << action;
}
void ChromeExtensionRegistrarDelegate::CheckPermissionsIncrease(
const Extension* extension,
bool is_extension_loaded) {
PermissionsUpdater(profile_).InitializePermissions(extension);
bool auto_grant_permission =
extension->was_installed_by_default() ||
ExtensionsBrowserClient::Get()->IsRunningInForcedAppMode();
if (auto_grant_permission) {
PermissionsUpdater(profile_).GrantActivePermissions(extension);
}
bool is_privilege_increase = false;
if (extension->location() == ManifestLocation::kInternal &&
!auto_grant_permission) {
std::unique_ptr<const PermissionSet> granted_permissions =
extension_prefs_->GetGrantedPermissions(extension->id());
CHECK(granted_permissions.get());
std::unique_ptr<const PermissionSet> runtime_granted_permissions =
extension_prefs_->GetRuntimeGrantedPermissions(extension->id());
std::unique_ptr<const PermissionSet> total_permissions =
PermissionSet::CreateUnion(*granted_permissions,
*runtime_granted_permissions);
is_privilege_increase =
PermissionMessageProvider::Get()->IsPrivilegeIncrease(
*total_permissions,
extension->permissions_data()->active_permissions(),
extension->GetType());
if (!is_privilege_increase) {
PermissionsUpdater(profile_).GrantActivePermissions(extension);
}
}
const DisableReasonSet disable_reasons =
extension_prefs_->GetDisableReasons(extension->id());
if (disable_reasons.contains(disable_reason::DISABLE_PERMISSIONS_INCREASE) &&
!is_privilege_increase) {
extension_prefs_->RemoveDisableReason(
extension->id(), disable_reason::DISABLE_PERMISSIONS_INCREASE);
}
if (is_privilege_increase &&
!disable_reasons.contains(disable_reason::DISABLE_REMOTE_INSTALL)) {
extension_prefs_->AddDisableReason(
extension->id(), disable_reason::DISABLE_PERMISSIONS_INCREASE);
}
}
void ChromeExtensionRegistrarDelegate::UpdateActiveExtensionsInCrashReporter() {
std::set<std::string> extension_ids;
for (const auto& extension : registry_->enabled_extensions()) {
if (!extension->is_theme() &&
extension->location() != ManifestLocation::kComponent) {
extension_ids.insert(extension->id());
}
}
crash_keys::SetActiveExtensions(extension_ids);
}
void ChromeExtensionRegistrarDelegate::UninstallExtensionOnFileThread(
const std::string& id,
const std::string& profile_user_name,
const base::FilePath& extensions_install_dir,
const base::FilePath& extension_dir_to_delete,
const base::FilePath& profile_dir) {
ExtensionAssetsManager* assets_manager =
ExtensionAssetsManager::GetInstance();
assets_manager->UninstallExtension(id, profile_user_name,
extensions_install_dir,
extension_dir_to_delete, profile_dir);
}
void ChromeExtensionRegistrarDelegate::OnUnpackedReloadFailure(
const Extension* extension,
const base::FilePath& file_path,
const std::u16string& error) {
if (!error.empty()) {
extension_registrar_->OnUnpackedExtensionReloadFailed(file_path);
}
}
void ChromeExtensionRegistrarDelegate::RecordInstallHistograms(
const Extension* extension) {
bool is_user_profile =
extensions::profile_util::ProfileCanUseNonComponentExtensions(profile_);
if (!registry_->GetInstalledExtension(extension->id())) {
if (is_user_profile) {
UMA_HISTOGRAM_ENUMERATION("Extensions.InstallType.User",
extension->GetType(), 100);
UMA_HISTOGRAM_ENUMERATION("Extensions.InstallSource.User2",
extension->location(), 100);
InstalledLoader::RecordPermissionMessagesHistogram(extension, "Install",
profile_);
} else {
UMA_HISTOGRAM_ENUMERATION("Extensions.InstallType.NonUser",
extension->GetType(), 100);
UMA_HISTOGRAM_ENUMERATION("Extensions.InstallSource.NonUser2",
extension->location(), 100);
}
}
}
}