chromium/chrome/browser/extensions/component_loader.cc

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#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  // BUILDFLAG(ENABLE_HANGOUT_SERVICES_EXTENSION)

bool g_enable_background_extensions_during_testing =;

#if BUILDFLAG(IS_CHROMEOS_ASH) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
// Whether HelpApp is enabled.
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) {
    // This is only called for Chrome OS component extensions which are loaded
    // from a read-only rootfs partition, so it is safe to set
    // |gzip_permission| to kAllowForTrustedSource.
    bool localized = extension_l10n_util::LocalizeExtension(
        root_directory, &manifest.value(),
        extension_l10n_util::GzippedMessagesPermission::kAllowForTrustedSource,
        &error);
    CHECK(localized) << error;
  }

  return manifest;
}
#endif  // BUILDFLAG(IS_CHROMEOS)

#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  // BUILDFLAG(IS_CHROMEOS_ASH)

}  // namespace

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() {
  // Finch controlled migration to a v3 Manifest - see crbug.com/326877912.
  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  // BUILDFLAG(ENABLE_HANGOUT_SERVICES_EXTENSION)

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",  // Because this is debug only, we do not need i18n.
      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  // BUILDFLAG(IS_CHROMEOS_ASH)

scoped_refptr<const Extension> ComponentLoader::CreateExtension(
    const ComponentExtensionInfo& info,
    std::string* utf8_error) {}

// static
void ComponentLoader::EnableBackgroundExtensionsForTesting() {}

#if BUILDFLAG(IS_CHROMEOS_ASH) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
// static
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;  // Error already logged.
  }

  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  // BUILDFLAG(IS_CHROMEOS_ASH)

#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) {
  // TODO(crbug.com/41449926): mitigation for extension not awake after
  // load.
  extensions::ProcessManager::Get(profile_)->WakeEventPage(extension_id,
                                                           base::DoNothing());
}
#endif  // BUILDFLAG(IS_CHROMEOS_ASH)

}  // namespace extensions