// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "components/prefs/pref_member.h" #include #include "base/callback.h" #include "base/callback_helpers.h" #include "base/json/values_util.h" #include "base/location.h" #include "base/threading/sequenced_task_runner_handle.h" #include "base/values.h" #include "components/prefs/pref_service.h" using base::SequencedTaskRunner; namespace subtle { PrefMemberBase::PrefMemberBase() : prefs_(nullptr), setting_value_(false) {} PrefMemberBase::~PrefMemberBase() { Destroy(); } void PrefMemberBase::Init(const std::string& pref_name, PrefService* prefs, const NamedChangeCallback& observer) { observer_ = observer; Init(pref_name, prefs); } void PrefMemberBase::Init(const std::string& pref_name, PrefService* prefs) { DCHECK(prefs); DCHECK(pref_name_.empty()); // Check that Init is only called once. prefs_ = prefs; pref_name_ = pref_name; // Check that the preference is registered. DCHECK(prefs_->FindPreference(pref_name_)) << pref_name << " not registered."; // Add ourselves as a pref observer so we can keep our local value in sync. prefs_->AddPrefObserver(pref_name, this); } void PrefMemberBase::Destroy() { if (prefs_ && !pref_name_.empty()) { prefs_->RemovePrefObserver(pref_name_, this); prefs_ = nullptr; } } void PrefMemberBase::MoveToSequence( scoped_refptr task_runner) { VerifyValuePrefName(); // Load the value from preferences if it hasn't been loaded so far. if (!internal()) UpdateValueFromPref(base::OnceClosure()); internal()->MoveToSequence(std::move(task_runner)); } void PrefMemberBase::OnPreferenceChanged(PrefService* service, const std::string& pref_name) { VerifyValuePrefName(); UpdateValueFromPref((!setting_value_ && !observer_.is_null()) ? base::BindOnce(observer_, pref_name) : base::OnceClosure()); } void PrefMemberBase::UpdateValueFromPref(base::OnceClosure callback) const { VerifyValuePrefName(); const PrefService::Preference* pref = prefs_->FindPreference(pref_name_); DCHECK(pref); if (!internal()) CreateInternal(); internal()->UpdateValue( base::Value::ToUniquePtrValue(pref->GetValue()->Clone()).release(), pref->IsManaged(), pref->IsUserModifiable(), pref->IsDefaultValue(), std::move(callback)); } void PrefMemberBase::VerifyPref() const { VerifyValuePrefName(); if (!internal()) UpdateValueFromPref(base::OnceClosure()); } void PrefMemberBase::InvokeUnnamedCallback( const base::RepeatingClosure& callback, const std::string& pref_name) { callback.Run(); } PrefMemberBase::Internal::Internal() : owning_task_runner_(base::SequencedTaskRunnerHandle::Get()) {} PrefMemberBase::Internal::~Internal() = default; bool PrefMemberBase::Internal::IsOnCorrectSequence() const { return owning_task_runner_->RunsTasksInCurrentSequence(); } void PrefMemberBase::Internal::UpdateValue(base::Value* v, bool is_managed, bool is_user_modifiable, bool is_default_value, base::OnceClosure callback) const { std::unique_ptr value(v); base::ScopedClosureRunner closure_runner(std::move(callback)); if (IsOnCorrectSequence()) { bool rv = UpdateValueInternal(*value); DCHECK(rv); is_managed_ = is_managed; is_user_modifiable_ = is_user_modifiable; is_default_value_ = is_default_value; } else { bool may_run = owning_task_runner_->PostTask( FROM_HERE, base::BindOnce(&PrefMemberBase::Internal::UpdateValue, this, value.release(), is_managed, is_user_modifiable, is_default_value, closure_runner.Release())); DCHECK(may_run); } } void PrefMemberBase::Internal::MoveToSequence( scoped_refptr task_runner) { CheckOnCorrectSequence(); owning_task_runner_ = std::move(task_runner); } bool PrefMemberVectorStringUpdate(const base::Value& value, std::vector* string_vector) { if (!value.is_list()) return false; std::vector local_vector; for (const auto& item : value.GetListDeprecated()) { if (!item.is_string()) return false; local_vector.push_back(item.GetString()); } string_vector->swap(local_vector); return true; } } // namespace subtle template <> void PrefMember::UpdatePref(const bool& value) { prefs()->SetBoolean(pref_name(), value); } template <> bool PrefMember::Internal::UpdateValueInternal( const base::Value& value) const { if (value.is_bool()) value_ = value.GetBool(); return value.is_bool(); } template <> void PrefMember::UpdatePref(const int& value) { prefs()->SetInteger(pref_name(), value); } template <> bool PrefMember::Internal::UpdateValueInternal( const base::Value& value) const { if (value.is_int()) value_ = value.GetInt(); return value.is_int(); } template <> void PrefMember::UpdatePref(const double& value) { prefs()->SetDouble(pref_name(), value); } template <> bool PrefMember::Internal::UpdateValueInternal(const base::Value& value) const { if (value.is_double() || value.is_int()) value_ = value.GetDouble(); return value.is_double() || value.is_int(); } template <> void PrefMember::UpdatePref(const std::string& value) { prefs()->SetString(pref_name(), value); } template <> bool PrefMember::Internal::UpdateValueInternal( const base::Value& value) const { if (value.is_string()) value_ = value.GetString(); return value.is_string(); } template <> void PrefMember::UpdatePref(const base::FilePath& value) { prefs()->SetFilePath(pref_name(), value); } template <> bool PrefMember::Internal::UpdateValueInternal( const base::Value& value) const { absl::optional path = base::ValueToFilePath(value); if (!path) return false; value_ = *path; return true; } template <> void PrefMember >::UpdatePref( const std::vector& value) { base::ListValue list_value; for (const std::string& val : value) list_value.Append(val); prefs()->Set(pref_name(), list_value); } template <> bool PrefMember >::Internal::UpdateValueInternal( const base::Value& value) const { return subtle::PrefMemberVectorStringUpdate(value, &value_); }