blob: 0baa6c4b8ad7306cffab5347fa47ed40357f8146 [file] [log] [blame]
// 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/overlay_user_pref_store.h"
#include <memory>
#include <utility>
#include "base/memory/ptr_util.h"
#include "base/values.h"
#include "components/prefs/in_memory_pref_store.h"
// Allows us to monitor two pref stores and tell updates from them apart. It
// essentially mimics a Callback for the Observer interface (e.g. it allows
// binding additional arguments).
class OverlayUserPrefStore::ObserverAdapter : public PrefStore::Observer {
public:
ObserverAdapter(bool ephemeral, OverlayUserPrefStore* parent)
: ephemeral_user_pref_store_(ephemeral), parent_(parent) {}
// Methods of PrefStore::Observer.
void OnPrefValueChanged(const std::string& key) override {
parent_->OnPrefValueChanged(ephemeral_user_pref_store_, key);
}
void OnInitializationCompleted(bool succeeded) override {
parent_->OnInitializationCompleted(ephemeral_user_pref_store_, succeeded);
}
private:
// Is the update for the ephemeral?
const bool ephemeral_user_pref_store_;
OverlayUserPrefStore* const parent_;
};
OverlayUserPrefStore::OverlayUserPrefStore(PersistentPrefStore* persistent)
: OverlayUserPrefStore(new InMemoryPrefStore(), persistent) {}
OverlayUserPrefStore::OverlayUserPrefStore(PersistentPrefStore* ephemeral,
PersistentPrefStore* persistent)
: ephemeral_pref_store_observer_(
std::make_unique<OverlayUserPrefStore::ObserverAdapter>(true, this)),
persistent_pref_store_observer_(
std::make_unique<OverlayUserPrefStore::ObserverAdapter>(false, this)),
ephemeral_user_pref_store_(ephemeral),
persistent_user_pref_store_(persistent) {
DCHECK(ephemeral->IsInitializationComplete());
ephemeral_user_pref_store_->AddObserver(ephemeral_pref_store_observer_.get());
persistent_user_pref_store_->AddObserver(
persistent_pref_store_observer_.get());
}
bool OverlayUserPrefStore::IsSetInOverlay(const std::string& key) const {
return ephemeral_user_pref_store_->GetValue(key, nullptr);
}
void OverlayUserPrefStore::AddObserver(PrefStore::Observer* observer) {
observers_.AddObserver(observer);
}
void OverlayUserPrefStore::RemoveObserver(PrefStore::Observer* observer) {
observers_.RemoveObserver(observer);
}
bool OverlayUserPrefStore::HasObservers() const {
return observers_.might_have_observers();
}
bool OverlayUserPrefStore::IsInitializationComplete() const {
return persistent_user_pref_store_->IsInitializationComplete() &&
ephemeral_user_pref_store_->IsInitializationComplete();
}
bool OverlayUserPrefStore::GetValue(const std::string& key,
const base::Value** result) const {
// If the |key| shall NOT be stored in the ephemeral store, there must not
// be an entry.
DCHECK(!ShallBeStoredInPersistent(key) ||
!ephemeral_user_pref_store_->GetValue(key, nullptr));
if (ephemeral_user_pref_store_->GetValue(key, result))
return true;
return persistent_user_pref_store_->GetValue(key, result);
}
std::unique_ptr<base::DictionaryValue> OverlayUserPrefStore::GetValues() const {
auto values = ephemeral_user_pref_store_->GetValues();
auto persistent_values = persistent_user_pref_store_->GetValues();
// Output |values| are read from |ephemeral_user_pref_store_| (in-memory
// store). Then the values of preferences in |persistent_names_set_| are
// overwritten by the content of |persistent_user_pref_store_| (the persistent
// store).
for (const auto& key : persistent_names_set_) {
std::unique_ptr<base::Value> out_value;
persistent_values->Remove(key, &out_value);
if (out_value) {
values->Set(key, std::move(out_value));
}
}
return values;
}
bool OverlayUserPrefStore::GetMutableValue(const std::string& key,
base::Value** result) {
if (ShallBeStoredInPersistent(key))
return persistent_user_pref_store_->GetMutableValue(key, result);
written_ephemeral_names_.insert(key);
if (ephemeral_user_pref_store_->GetMutableValue(key, result))
return true;
// Try to create copy of persistent if the ephemeral does not contain a value.
base::Value* persistent_value = nullptr;
if (!persistent_user_pref_store_->GetMutableValue(key, &persistent_value))
return false;
ephemeral_user_pref_store_->SetValue(
key, persistent_value->CreateDeepCopy(),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
ephemeral_user_pref_store_->GetMutableValue(key, result);
return true;
}
void OverlayUserPrefStore::SetValue(const std::string& key,
std::unique_ptr<base::Value> value,
uint32_t flags) {
if (ShallBeStoredInPersistent(key)) {
persistent_user_pref_store_->SetValue(key, std::move(value), flags);
return;
}
// TODO(https://crbug.com/861722): If we always store in in-memory storage
// and conditionally also stored in persistent one, we wouldn't have to do a
// complex merge in GetValues().
written_ephemeral_names_.insert(key);
ephemeral_user_pref_store_->SetValue(key, std::move(value), flags);
}
void OverlayUserPrefStore::SetValueSilently(const std::string& key,
std::unique_ptr<base::Value> value,
uint32_t flags) {
if (ShallBeStoredInPersistent(key)) {
persistent_user_pref_store_->SetValueSilently(key, std::move(value), flags);
return;
}
written_ephemeral_names_.insert(key);
ephemeral_user_pref_store_->SetValueSilently(key, std::move(value), flags);
}
void OverlayUserPrefStore::RemoveValue(const std::string& key, uint32_t flags) {
if (ShallBeStoredInPersistent(key)) {
persistent_user_pref_store_->RemoveValue(key, flags);
return;
}
written_ephemeral_names_.insert(key);
ephemeral_user_pref_store_->RemoveValue(key, flags);
}
bool OverlayUserPrefStore::ReadOnly() const {
return false;
}
PersistentPrefStore::PrefReadError OverlayUserPrefStore::GetReadError() const {
return PersistentPrefStore::PREF_READ_ERROR_NONE;
}
PersistentPrefStore::PrefReadError OverlayUserPrefStore::ReadPrefs() {
// We do not read intentionally.
OnInitializationCompleted(/* ephemeral */ false, true);
return PersistentPrefStore::PREF_READ_ERROR_NONE;
}
void OverlayUserPrefStore::ReadPrefsAsync(
ReadErrorDelegate* error_delegate_raw) {
std::unique_ptr<ReadErrorDelegate> error_delegate(error_delegate_raw);
// We do not read intentionally.
OnInitializationCompleted(/* ephemeral */ false, true);
}
void OverlayUserPrefStore::CommitPendingWrite(
base::OnceClosure reply_callback,
base::OnceClosure synchronous_done_callback) {
persistent_user_pref_store_->CommitPendingWrite(
std::move(reply_callback), std::move(synchronous_done_callback));
// We do not write our content intentionally.
}
void OverlayUserPrefStore::SchedulePendingLossyWrites() {
persistent_user_pref_store_->SchedulePendingLossyWrites();
}
void OverlayUserPrefStore::ReportValueChanged(const std::string& key,
uint32_t flags) {
for (PrefStore::Observer& observer : observers_)
observer.OnPrefValueChanged(key);
}
void OverlayUserPrefStore::RegisterPersistentPref(const std::string& key) {
DCHECK(!key.empty()) << "Key is empty";
DCHECK(persistent_names_set_.find(key) == persistent_names_set_.end())
<< "Key already registered: " << key;
persistent_names_set_.insert(key);
}
void OverlayUserPrefStore::ClearMutableValues() {
for (const auto& key : written_ephemeral_names_) {
ephemeral_user_pref_store_->RemoveValue(
key, WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
}
}
void OverlayUserPrefStore::OnStoreDeletionFromDisk() {
persistent_user_pref_store_->OnStoreDeletionFromDisk();
}
OverlayUserPrefStore::~OverlayUserPrefStore() {
ephemeral_user_pref_store_->RemoveObserver(
ephemeral_pref_store_observer_.get());
persistent_user_pref_store_->RemoveObserver(
persistent_pref_store_observer_.get());
}
void OverlayUserPrefStore::OnPrefValueChanged(bool ephemeral,
const std::string& key) {
if (ephemeral) {
ReportValueChanged(key, DEFAULT_PREF_WRITE_FLAGS);
} else {
if (!ephemeral_user_pref_store_->GetValue(key, nullptr))
ReportValueChanged(key, DEFAULT_PREF_WRITE_FLAGS);
}
}
void OverlayUserPrefStore::OnInitializationCompleted(bool ephemeral,
bool succeeded) {
if (!IsInitializationComplete())
return;
for (PrefStore::Observer& observer : observers_)
observer.OnInitializationCompleted(succeeded);
}
bool OverlayUserPrefStore::ShallBeStoredInPersistent(
const std::string& key) const {
return persistent_names_set_.find(key) != persistent_names_set_.end();
}