chromium/chrome/browser/extensions/api/preference/preference_api.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/api/preference/preference_api.h"

#include <stddef.h>

#include <map>
#include <memory>
#include <optional>
#include <utility>

#include "base/containers/contains.h"
#include "base/functional/bind.h"
#include "base/lazy_instance.h"
#include "base/values.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/pref_mapping.h"
#include "chrome/browser/extensions/pref_transformer_interface.h"
#include "chrome/browser/extensions/preference/preference_helpers.h"
#include "components/autofill/core/common/autofill_prefs.h"
#include "components/prefs/pref_service.h"
#include "components/privacy_sandbox/tracking_protection_prefs.h"
#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
#include "extensions/browser/api/content_settings/content_settings_service.h"
#include "extensions/browser/extension_function_registry.h"
#include "extensions/browser/extension_pref_value_map.h"
#include "extensions/browser/extension_pref_value_map_factory.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/extension_prefs_factory.h"
#include "extensions/browser/extension_prefs_helper.h"
#include "extensions/browser/extension_system_provider.h"
#include "extensions/browser/extensions_browser_client.h"
#include "extensions/browser/pref_names.h"
#include "extensions/common/api/types.h"
#include "extensions/common/constants.h"
#include "extensions/common/error_utils.h"
#include "extensions/common/extension_id.h"
#include "extensions/common/permissions/api_permission.h"
#include "extensions/common/permissions/permissions_data.h"
#include "media/media_buildflags.h"
#include "third_party/blink/public/mojom/devtools/inspector_issue.mojom.h"

#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
#include "chrome/browser/chromeos/extensions/controlled_pref_mapping.h"
#include "chromeos/startup/browser_params_proxy.h"
#endif

APIPermissionID;

namespace extensions {

namespace {

ChromeSettingScope;

constexpr char kConversionErrorMessage[] =;
constexpr char kPermissionErrorMessage[] =;
#if BUILDFLAG(IS_CHROMEOS_LACROS)
constexpr char kInvalidPrefPathErrorMessage[] =
    "Invalid PrefPath '*' for getting extension pref with control.";
constexpr char kPrimaryProfileOnlyErrorMessage[] =
    "You may only access the preference '*' in the primary profile.";
constexpr char kAshDoesNotSupportPreference[] =
    "The browser preference is not supported.";
#endif
constexpr char kIncognitoKey[] =;
constexpr char kScopeKey[] =;
constexpr char kIncognitoSpecific[] =;
constexpr char kLevelOfControl[] =;
constexpr char kValue[] =;

#if BUILDFLAG(IS_CHROMEOS_LACROS)
// Returns true if the get, set or clear requests for the preference associated
// with `pref_path` should only be applied at browser level. Returns false if
// the requests should be forwarded to Ash.
// All preferences explicitly added to`crosapi::mojom::PrefPath` should be
// handled by Ash. The only exception is the `crosapi::mojom::PrefPath::kProxy`
// pref which, for secondary profiles only, is applied at browser scope.
bool IsBrowserScopePrefOperation(crosapi::mojom::PrefPath pref_path,
                                 Profile* profile) {
  if (pref_path == crosapi::mojom::PrefPath::kUnknown) {
    return true;
  }
  if (pref_path == crosapi::mojom::PrefPath::kProxy) {
    if (!profile->IsMainProfile()) {
      return true;
    }
    // TODO(acostinas,b/267719988) If the current version of Ash does not
    // support syncing the proxy pref via the Prefs mojo service, the proxy pref
    // can be set at browser scope only and it will be synced with Ash via the
    // NetworkSettingsService mojo API.
    static constexpr int kMinVersionProxyPref = 4;
    const int version = chromeos::LacrosService::Get()
                            ->GetInterfaceVersion<crosapi::mojom::Prefs>();
    if (version < kMinVersionProxyPref) {
      return true;
    }
  }
  return false;
}
#endif

bool StringToScope(const std::string& s, ChromeSettingScope& scope) {}

}  // namespace

PreferenceEventRouter::PreferenceEventRouter(Profile* profile)
    :{}

PreferenceEventRouter::~PreferenceEventRouter() = default;

#if BUILDFLAG(IS_CHROMEOS_LACROS)
void PreferenceEventRouter::OnControlledPrefChanged(
    PrefService* pref_service,
    const std::string& browser_pref) {
  // This pref has a corresponding value in ash. We should send the updated
  // value of the pref to ash.
  auto* lacros_service = chromeos::LacrosService::Get();
  if (!lacros_service ||
      !lacros_service->IsAvailable<crosapi::mojom::Prefs>()) {
    // Without the service, we cannot update this pref in ash.
    LOG(ERROR) << ErrorUtils::FormatErrorMessage(
        "API unavailable to set pref * in ash.", browser_pref);
    return;
  }

  crosapi::mojom::PrefPath pref_path =
      PrefMapping::GetInstance()->GetPrefPathForPrefName(browser_pref);
  // Should be a known pref path. Otherwise we would not have created this
  // observer.
  DCHECK(pref_path != crosapi::mojom::PrefPath::kUnknown);

  const PrefService::Preference* pref =
      pref_service->FindPreference(browser_pref);
  CHECK(pref);
  if (pref->IsExtensionControlled()) {
    // The pref has been set in lacros by an extension.
    // Transmit the value to ash to be stored in the standalone browser
    // prefstore.
    lacros_service->GetRemote<crosapi::mojom::Prefs>()->SetPref(
        pref_path, pref->GetValue()->Clone(), base::OnceClosure());
  } else {
    // The pref hasn't been set in lacros.
    // Remove any value from the standalone browser prefstore in ash.
    lacros_service->GetRemote<crosapi::mojom::Prefs>()
        ->ClearExtensionControlledPref(pref_path, base::OnceClosure());
  }
}

void PreferenceEventRouter::OnAshPrefChanged(crosapi::mojom::PrefPath pref_path,
                                             const std::string& extension_pref,
                                             const std::string& browser_pref,
                                             base::Value value) {
  // This pref should be read from ash.
  // We can only get here via callback from ash. So there should be a
  // LacrosService.
  auto* lacros_service = chromeos::LacrosService::Get();
  DCHECK(lacros_service);

  // It's not sufficient to have the new state of the pref - we also need
  // information about what just set it. So call Ash again to get information
  // about the control state.
  lacros_service->GetRemote<crosapi::mojom::Prefs>()
      ->GetExtensionPrefWithControl(
          pref_path, base::BindOnce(&PreferenceEventRouter::OnAshGetSuccess,
                                    weak_factory_.GetWeakPtr(), browser_pref));
}

void PreferenceEventRouter::OnAshGetSuccess(
    const std::string& browser_pref,
    std::optional<::base::Value> opt_value,
    crosapi::mojom::PrefControlState control_state) {
  // Note: crosapi::mojom::prefs::GetExtensionPrefWithControl could be called
  // with an invalid pref path, and returns empty opt_value.
  if (!opt_value.has_value()) {
    LOG(ERROR) << ErrorUtils::FormatErrorMessage(kInvalidPrefPathErrorMessage,
                                                 browser_pref);
    return;
  }

  bool incognito = false;

  std::string event_name;
  APIPermissionID permission = APIPermissionID::kInvalid;
  bool found_event = PrefMapping::GetInstance()->FindEventForBrowserPref(
      browser_pref, &event_name, &permission);
  DCHECK(found_event);

  base::Value::List args;
  PrefTransformerInterface* transformer =
      PrefMapping::GetInstance()->FindTransformerForBrowserPref(browser_pref);

  std::optional<base::Value> transformed_value =
      transformer->BrowserToExtensionPref(opt_value.value(), incognito);
  if (!transformed_value) {
    LOG(ERROR) << ErrorUtils::FormatErrorMessage(kConversionErrorMessage,
                                                 browser_pref);
    return;
  }

  base::Value::Dict dict;
  dict.Set(kValue, std::move(*transformed_value));
  args.Append(std::move(dict));

  events::HistogramValue histogram_value =
      events::TYPES_CHROME_SETTING_ON_CHANGE;
  extensions::preference_helpers::DispatchEventToExtensionsWithAshControlState(
      profile_, histogram_value, event_name, std::move(args), permission,
      incognito, browser_pref, control_state);
}
#endif

void PreferenceEventRouter::OnPrefChanged(PrefService* pref_service,
                                          const std::string& browser_pref) {}

void PreferenceEventRouter::OnOffTheRecordProfileCreated(
    Profile* off_the_record) {}

void PreferenceEventRouter::OnProfileWillBeDestroyed(Profile* profile) {}

void PreferenceEventRouter::ObserveOffTheRecordPrefs(PrefService* prefs) {}

PreferenceAPI::PreferenceAPI(content::BrowserContext* context)
    :{}

PreferenceAPI::~PreferenceAPI() = default;

void PreferenceAPI::Shutdown() {}

static base::LazyInstance<BrowserContextKeyedAPIFactory<PreferenceAPI>>::
    DestructorAtExit g_preference_api_factory =;

// static
BrowserContextKeyedAPIFactory<PreferenceAPI>*
PreferenceAPI::GetFactoryInstance() {}

// static
PreferenceAPI* PreferenceAPI::Get(content::BrowserContext* context) {}

void PreferenceAPI::OnListenerAdded(const EventListenerInfo& details) {}

void PreferenceAPI::EnsurePreferenceEventRouterCreated() {}

void PreferenceAPI::OnContentSettingChanged(const ExtensionId& extension_id,
                                            bool incognito) {}

void PreferenceAPI::ClearIncognitoSessionOnlyContentSettings() {}

scoped_refptr<ContentSettingsStore> PreferenceAPI::content_settings_store() {}

template <>
void
BrowserContextKeyedAPIFactory<PreferenceAPI>::DeclareFactoryDependencies() {}

PreferenceFunction::~PreferenceFunction() = default;

GetPreferenceFunction::~GetPreferenceFunction() = default;

ExtensionFunction::ResponseAction GetPreferenceFunction::Run() {}

void GetPreferenceFunction::ProduceGetResult(
    base::Value::Dict* result,
    const base::Value* pref_value,
    const std::string& level_of_control,
    const std::string& browser_pref,
    bool incognito) {}

#if BUILDFLAG(IS_CHROMEOS_LACROS)
void GetPreferenceFunction::OnLacrosGetSuccess(
    std::optional<::base::Value> opt_value,
    crosapi::mojom::PrefControlState control_state) {
  if (!browser_context()) {
    return;
  }

  if (!opt_value) {
    Respond(Error(kAshDoesNotSupportPreference));
    return;
  }

  // Get read/write permissions and pref name again.
  Profile* profile = Profile::FromBrowserContext(browser_context());

  std::string pref_key = args()[0].GetString();
  const base::Value& details = args()[1];

  bool incognito = false;
  if (std::optional<bool> result = details.GetDict().FindBool(kIncognitoKey)) {
    incognito = *result;
  }

  ::base::Value* pref_value = &opt_value.value();

  std::string level_of_control;
  level_of_control =
      extensions::preference_helpers::GetLevelOfControlWithAshControlState(
          control_state, profile, extension_id(), cached_browser_pref_,
          incognito);

  base::Value::Dict result;

  ProduceGetResult(&result, pref_value, level_of_control, cached_browser_pref_,
                   incognito);

  Respond(WithArguments(std::move(result)));
}
#endif

SetPreferenceFunction::~SetPreferenceFunction() = default;

ExtensionFunction::ResponseAction SetPreferenceFunction::Run() {}

#if BUILDFLAG(IS_CHROMEOS_LACROS)
void SetPreferenceFunction::OnLacrosSetSuccess() {
  Respond(NoArguments());
}
#endif

ClearPreferenceFunction::~ClearPreferenceFunction() = default;

ExtensionFunction::ResponseAction ClearPreferenceFunction::Run() {}
#if BUILDFLAG(IS_CHROMEOS_LACROS)
void ClearPreferenceFunction::OnLacrosClearSuccess() {
  Respond(NoArguments());
}
#endif
}  // namespace extensions