#include "chrome/browser/extensions/component_loader.h"
#include <optional>
#include <string>
#include <string_view>
#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/json/json_string_value_serializer.h"
#include "base/metrics/histogram_macros.h"
#include "base/path_service.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "base/values.h"
#include "build/branding_buildflags.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/extensions/component_extensions_allowlist/allowlist.h"
#include "chrome/browser/extensions/data_deleter.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/launch_util.h"
#include "chrome/browser/extensions/profile_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/channel_info.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/extension_constants.h"
#include "chrome/grit/branded_strings.h"
#include "chrome/grit/browser_resources.h"
#include "chrome/grit/generated_resources.h"
#include "components/crx_file/id_util.h"
#include "components/nacl/common/buildflags.h"
#include "components/version_info/version_info.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/common/content_switches.h"
#include "extensions/browser/extension_file_task_runner.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/extension_system.h"
#include "extensions/browser/pref_names.h"
#include "extensions/common/constants.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_features.h"
#include "extensions/common/extension_id.h"
#include "extensions/common/extension_l10n_util.h"
#include "extensions/common/file_util.h"
#include "extensions/common/manifest_constants.h"
#include "pdf/buildflags.h"
#include "printing/buildflags/buildflags.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/constants/ash_switches.h"
#include "ash/keyboard/ui/grit/keyboard_resources.h"
#include "base/system/sys_info.h"
#include "chrome/browser/ash/crosapi/browser_util.h"
#include "chrome/browser/ash/file_manager/app_id.h"
#include "components/user_manager/user_manager.h"
#include "content/public/browser/site_instance.h"
#include "content/public/browser/storage_partition.h"
#include "extensions/browser/extensions_browser_client.h"
#include "extensions/common/switches.h"
#include "storage/browser/file_system/file_system_context.h"
#include "ui/accessibility/accessibility_features.h"
#include "ui/file_manager/grit/file_manager_resources.h"
#endif
#if BUILDFLAG(IS_CHROMEOS)
#include "chromeos/constants/chromeos_features.h"
#endif
#if BUILDFLAG(ENABLE_PDF)
#include "chrome/browser/pdf/pdf_extension_util.h"
#endif
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
#include "chrome/browser/defaults.h"
#endif
BrowserThread;
namespace extensions {
namespace {
#if BUILDFLAG(ENABLE_HANGOUT_SERVICES_EXTENSION)
BASE_FEATURE(kHangoutsExtensionV3,
"HangoutsExtensionV3",
base::FEATURE_DISABLED_BY_DEFAULT);
#endif
bool g_enable_background_extensions_during_testing = …;
#if BUILDFLAG(IS_CHROMEOS_ASH) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
bool g_enable_help_app = true;
#endif
ExtensionId GenerateId(const base::Value::Dict& manifest,
const base::FilePath& path) { … }
#if BUILDFLAG(IS_CHROMEOS)
std::optional<base::Value::Dict> LoadManifestOnFileThread(
const base::FilePath& root_directory,
const base::FilePath::CharType* manifest_filename,
bool localize_manifest) {
DCHECK(GetExtensionFileTaskRunner()->RunsTasksInCurrentSequence());
std::string error;
std::optional<base::Value::Dict> manifest(
file_util::LoadManifest(root_directory, manifest_filename, &error));
if (!manifest) {
LOG(ERROR) << "Can't load "
<< root_directory.Append(manifest_filename).AsUTF8Unsafe()
<< ": " << error;
return std::nullopt;
}
if (localize_manifest) {
bool localized = extension_l10n_util::LocalizeExtension(
root_directory, &manifest.value(),
extension_l10n_util::GzippedMessagesPermission::kAllowForTrustedSource,
&error);
CHECK(localized) << error;
}
return manifest;
}
#endif
#if BUILDFLAG(IS_CHROMEOS_ASH)
bool IsNormalSession() {
return !base::CommandLine::ForCurrentProcess()->HasSwitch(
ash::switches::kGuestSession) &&
user_manager::UserManager::IsInitialized() &&
user_manager::UserManager::Get()->IsUserLoggedIn();
}
#endif
}
ComponentLoader::ComponentExtensionInfo::ComponentExtensionInfo(
base::Value::Dict manifest_param,
const base::FilePath& directory)
: … { … }
ComponentLoader::ComponentExtensionInfo::ComponentExtensionInfo(
ComponentExtensionInfo&& other)
: … { … }
ComponentLoader::ComponentExtensionInfo&
ComponentLoader::ComponentExtensionInfo::operator=(
ComponentExtensionInfo&& other) { … }
ComponentLoader::ComponentExtensionInfo::~ComponentExtensionInfo() = default;
ComponentLoader::ComponentLoader(ExtensionSystem* extension_system,
Profile* profile)
: … { … }
ComponentLoader::~ComponentLoader() = default;
void ComponentLoader::LoadAll() { … }
std::optional<base::Value::Dict> ComponentLoader::ParseManifest(
std::string_view manifest_contents) const { … }
ExtensionId ComponentLoader::Add(int manifest_resource_id,
const base::FilePath& root_directory) { … }
ExtensionId ComponentLoader::Add(base::Value::Dict manifest,
const base::FilePath& root_directory) { … }
ExtensionId ComponentLoader::Add(std::string_view manifest_contents,
const base::FilePath& root_directory) { … }
ExtensionId ComponentLoader::Add(std::string_view manifest_contents,
const base::FilePath& root_directory,
bool skip_allowlist) { … }
ExtensionId ComponentLoader::Add(base::Value::Dict parsed_manifest,
const base::FilePath& root_directory,
bool skip_allowlist) { … }
ExtensionId ComponentLoader::AddOrReplace(const base::FilePath& path) { … }
void ComponentLoader::Reload(const ExtensionId& extension_id) { … }
void ComponentLoader::Load(const ComponentExtensionInfo& info) { … }
void ComponentLoader::Remove(const base::FilePath& root_directory) { … }
void ComponentLoader::Remove(const ExtensionId& id) { … }
bool ComponentLoader::Exists(const ExtensionId& id) const { … }
std::vector<ExtensionId> ComponentLoader::GetRegisteredComponentExtensionsIds()
const { … }
#if BUILDFLAG(ENABLE_HANGOUT_SERVICES_EXTENSION)
void ComponentLoader::AddHangoutServicesExtension() {
if (base::FeatureList::IsEnabled(kHangoutsExtensionV3)) {
Add(IDR_HANGOUT_SERVICES_MANIFEST_V3,
base::FilePath(FILE_PATH_LITERAL("hangout_services")));
} else {
Add(IDR_HANGOUT_SERVICES_MANIFEST_V2,
base::FilePath(FILE_PATH_LITERAL("hangout_services")));
}
}
#endif
void ComponentLoader::AddNetworkSpeechSynthesisExtension() { … }
void ComponentLoader::AddWithNameAndDescription(
int manifest_resource_id,
const base::FilePath& root_directory,
const std::string& name_string,
const std::string& description_string) { … }
void ComponentLoader::AddWebStoreApp() { … }
#if BUILDFLAG(IS_CHROMEOS_ASH)
void ComponentLoader::AddChromeApp() {
AddWithNameAndDescription(
IDR_CHROME_APP_MANIFEST, base::FilePath(FILE_PATH_LITERAL("chrome_app")),
crosapi::browser_util::IsAshWebBrowserEnabled()
? l10n_util::GetStringUTF8(IDS_SHORT_PRODUCT_NAME)
: "Ash Chrome",
l10n_util::GetStringUTF8(IDS_CHROME_SHORTCUT_DESCRIPTION));
}
void ComponentLoader::AddImageLoaderExtension() {
Add(IDR_IMAGE_LOADER_MANIFEST,
base::FilePath(FILE_PATH_LITERAL("image_loader")));
}
void ComponentLoader::AddGuestModeTestExtension(const base::FilePath& path) {
base::SysInfo::CrashIfChromeOSNonTestImage();
AddComponentFromDirWithManifestFilename(
path, extension_misc::kGuestModeTestExtensionId,
extensions::kManifestFilename, extensions::kManifestFilename,
base::RepeatingClosure());
}
void ComponentLoader::AddKeyboardApp() {
Add(IDR_KEYBOARD_MANIFEST, base::FilePath(FILE_PATH_LITERAL("keyboard")));
}
#endif
scoped_refptr<const Extension> ComponentLoader::CreateExtension(
const ComponentExtensionInfo& info,
std::string* utf8_error) { … }
void ComponentLoader::EnableBackgroundExtensionsForTesting() { … }
#if BUILDFLAG(IS_CHROMEOS_ASH) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
void ComponentLoader::DisableHelpAppForTesting() {
g_enable_help_app = false;
}
#endif
void ComponentLoader::AddDefaultComponentExtensions(
bool skip_session_components) { … }
void ComponentLoader::AddDefaultComponentExtensionsForKioskMode(
bool skip_session_components) { … }
void ComponentLoader::AddDefaultComponentExtensionsWithBackgroundPages(
bool skip_session_components) { … }
void ComponentLoader::
AddDefaultComponentExtensionsWithBackgroundPagesForKioskMode() { … }
void ComponentLoader::UnloadComponent(ComponentExtensionInfo* component) { … }
#if BUILDFLAG(IS_CHROMEOS)
void ComponentLoader::AddComponentFromDirWithManifestFilename(
const base::FilePath& root_directory,
const ExtensionId& extension_id,
const base::FilePath::CharType* manifest_file_name,
const base::FilePath::CharType* guest_manifest_file_name,
base::OnceClosure done_cb) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
#if BUILDFLAG(IS_CHROMEOS_ASH)
const base::FilePath::CharType* manifest_filename =
IsNormalSession() ? manifest_file_name : guest_manifest_file_name;
#else
const base::FilePath::CharType* manifest_filename = manifest_file_name;
#endif
GetExtensionFileTaskRunner()->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&LoadManifestOnFileThread, root_directory,
manifest_filename, true),
base::BindOnce(&ComponentLoader::FinishAddComponentFromDir,
weak_factory_.GetWeakPtr(), root_directory, extension_id,
std::nullopt, std::nullopt, std::move(done_cb)));
}
void ComponentLoader::FinishAddComponentFromDir(
const base::FilePath& root_directory,
const ExtensionId& extension_id,
const std::optional<std::string>& name_string,
const std::optional<std::string>& description_string,
base::OnceClosure done_cb,
std::optional<base::Value::Dict> manifest) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!manifest) {
return;
}
if (name_string) {
manifest->Set(manifest_keys::kName, name_string.value());
}
if (description_string) {
manifest->Set(manifest_keys::kDescription, description_string.value());
}
std::string actual_extension_id =
Add(std::move(*manifest), root_directory, false);
CHECK_EQ(extension_id, actual_extension_id);
if (done_cb) {
std::move(done_cb).Run();
}
}
#endif
#if BUILDFLAG(IS_CHROMEOS_ASH)
void ComponentLoader::AddComponentFromDir(const base::FilePath& root_directory,
const ExtensionId& extension_id,
base::OnceClosure done_cb) {
AddComponentFromDirWithManifestFilename(
root_directory, extension_id, extensions::kManifestFilename,
extension_misc::kGuestManifestFilename, std::move(done_cb));
}
void ComponentLoader::AddWithNameAndDescriptionFromDir(
const base::FilePath& root_directory,
const ExtensionId& extension_id,
const std::string& name_string,
const std::string& description_string) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
GetExtensionFileTaskRunner()->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&LoadManifestOnFileThread, root_directory,
extensions::kManifestFilename, false),
base::BindOnce(&ComponentLoader::FinishAddComponentFromDir,
weak_factory_.GetWeakPtr(), root_directory, extension_id,
name_string, description_string, base::OnceClosure()));
}
void ComponentLoader::AddChromeOsSpeechSynthesisExtensions() {
if (!Exists(extension_misc::kGoogleSpeechSynthesisExtensionId)) {
AddComponentFromDir(
base::FilePath(extension_misc::kGoogleSpeechSynthesisExtensionPath),
extension_misc::kGoogleSpeechSynthesisExtensionId,
base::BindRepeating(
&ComponentLoader::FinishLoadSpeechSynthesisExtension,
weak_factory_.GetWeakPtr(),
extension_misc::kGoogleSpeechSynthesisExtensionId));
}
if (!Exists(extension_misc::kEspeakSpeechSynthesisExtensionId)) {
AddComponentFromDir(
base::FilePath(extension_misc::kEspeakSpeechSynthesisExtensionPath),
extension_misc::kEspeakSpeechSynthesisExtensionId,
base::BindRepeating(
&ComponentLoader::FinishLoadSpeechSynthesisExtension,
weak_factory_.GetWeakPtr(),
extension_misc::kEspeakSpeechSynthesisExtensionId));
}
}
void ComponentLoader::FinishLoadSpeechSynthesisExtension(
const ExtensionId& extension_id) {
extensions::ProcessManager::Get(profile_)->WakeEventPage(extension_id,
base::DoNothing());
}
#endif
}