blob: f418792f29eb5a62be557bc360c49c83b698eeb0 [file] [log] [blame]
// Copyright 2017 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 "services/preferences/pref_store_manager_impl.h"
#include <algorithm>
#include <utility>
#include "base/memory/ref_counted.h"
#include "base/stl_util.h"
#include "base/threading/sequenced_worker_pool.h"
#include "components/prefs/default_pref_store.h"
#include "components/prefs/pref_value_store.h"
#include "mojo/public/cpp/bindings/interface_request.h"
#include "services/preferences/persistent_pref_store_factory.h"
#include "services/preferences/persistent_pref_store_impl.h"
#include "services/preferences/public/cpp/pref_store_impl.h"
#include "services/service_manager/public/cpp/service_info.h"
namespace prefs {
namespace {
using ConnectCallback = mojom::PrefStoreConnector::ConnectCallback;
using PrefStorePtrs =
std::unordered_map<PrefValueStore::PrefStoreType, mojom::PrefStore*>;
// Used to make sure all pref stores have been registered before replying to any
// Connect calls.
class ConnectionBarrier : public base::RefCounted<ConnectionBarrier> {
public:
static void Create(
const PrefStorePtrs& pref_store_ptrs,
mojom::PersistentPrefStoreConnectionPtr persistent_pref_store_connection,
const std::vector<std::string>& observed_prefs,
const ConnectCallback& callback);
void Init(const PrefStorePtrs& pref_store_ptrs,
const std::vector<std::string>& observed_prefs);
private:
friend class base::RefCounted<ConnectionBarrier>;
ConnectionBarrier(
const PrefStorePtrs& pref_store_ptrs,
mojom::PersistentPrefStoreConnectionPtr persistent_pref_store_connection,
const ConnectCallback& callback);
~ConnectionBarrier() = default;
void OnConnect(PrefValueStore::PrefStoreType type,
mojom::PrefStoreConnectionPtr connection_ptr);
ConnectCallback callback_;
std::unordered_map<PrefValueStore::PrefStoreType,
mojom::PrefStoreConnectionPtr>
connections_;
const size_t expected_connections_;
mojom::PersistentPrefStoreConnectionPtr persistent_pref_store_connection_;
DISALLOW_COPY_AND_ASSIGN(ConnectionBarrier);
};
// static
void ConnectionBarrier::Create(
const PrefStorePtrs& pref_store_ptrs,
mojom::PersistentPrefStoreConnectionPtr persistent_pref_store_connection,
const std::vector<std::string>& observed_prefs,
const ConnectCallback& callback) {
make_scoped_refptr(new ConnectionBarrier(
pref_store_ptrs,
std::move(persistent_pref_store_connection), callback))
->Init(pref_store_ptrs, observed_prefs);
}
void ConnectionBarrier::Init(const PrefStorePtrs& pref_store_ptrs,
const std::vector<std::string>& observed_prefs) {
for (const auto& ptr : pref_store_ptrs) {
ptr.second->AddObserver(
observed_prefs,
base::Bind(&ConnectionBarrier::OnConnect, this, ptr.first));
}
}
ConnectionBarrier::ConnectionBarrier(
const PrefStorePtrs& pref_store_ptrs,
mojom::PersistentPrefStoreConnectionPtr persistent_pref_store_connection,
const ConnectCallback& callback)
: callback_(callback),
expected_connections_(pref_store_ptrs.size()),
persistent_pref_store_connection_(
std::move(persistent_pref_store_connection)) {}
void ConnectionBarrier::OnConnect(
PrefValueStore::PrefStoreType type,
mojom::PrefStoreConnectionPtr connection_ptr) {
connections_.insert(std::make_pair(type, std::move(connection_ptr)));
if (connections_.size() == expected_connections_) {
// After this method exits |this| will get destroyed so it's safe to move
// out the members.
callback_.Run(std::move(persistent_pref_store_connection_),
std::move(connections_));
}
}
} // namespace
PrefStoreManagerImpl::PrefStoreManagerImpl(
std::set<PrefValueStore::PrefStoreType> expected_pref_stores,
scoped_refptr<base::SequencedWorkerPool> worker_pool)
: expected_pref_stores_(std::move(expected_pref_stores)),
init_binding_(this),
defaults_(new DefaultPrefStore),
defaults_wrapper_(base::MakeUnique<PrefStoreImpl>(
defaults_,
mojo::MakeRequest(&pref_store_ptrs_[PrefValueStore::DEFAULT_STORE]))),
worker_pool_(std::move(worker_pool)) {
DCHECK(
base::ContainsValue(expected_pref_stores_, PrefValueStore::USER_STORE) &&
base::ContainsValue(expected_pref_stores_, PrefValueStore::DEFAULT_STORE))
<< "expected_pref_stores must always include PrefValueStore::USER_STORE "
"and PrefValueStore::DEFAULT_STORE.";
// The user store is not actually connected to in the implementation, but
// accessed directly.
expected_pref_stores_.erase(PrefValueStore::USER_STORE);
registry_.AddInterface<prefs::mojom::PrefStoreConnector>(this);
registry_.AddInterface<prefs::mojom::PrefStoreRegistry>(this);
registry_.AddInterface<prefs::mojom::PrefServiceControl>(this);
}
PrefStoreManagerImpl::~PrefStoreManagerImpl() = default;
struct PrefStoreManagerImpl::PendingConnect {
mojom::PrefRegistryPtr pref_registry;
// Pref stores the caller already is connected to (and hence we won't
// connect to these).
std::vector<PrefValueStore::PrefStoreType> already_connected_types;
ConnectCallback callback;
};
void PrefStoreManagerImpl::Register(PrefValueStore::PrefStoreType type,
mojom::PrefStorePtr pref_store_ptr) {
if (expected_pref_stores_.count(type) == 0) {
LOG(WARNING) << "Not registering unexpected pref store: " << type;
return;
}
DVLOG(1) << "Registering pref store: " << type;
pref_store_ptr.set_connection_error_handler(
base::Bind(&PrefStoreManagerImpl::OnPrefStoreDisconnect,
base::Unretained(this), type));
const bool success =
pref_store_ptrs_.insert(std::make_pair(type, std::move(pref_store_ptr)))
.second;
DCHECK(success) << "The same pref store registered twice: " << type;
if (AllConnected()) {
DVLOG(1) << "All pref stores registered.";
ProcessPendingConnects();
}
}
void PrefStoreManagerImpl::Connect(
mojom::PrefRegistryPtr pref_registry,
const std::vector<PrefValueStore::PrefStoreType>& already_connected_types,
const ConnectCallback& callback) {
if (AllConnected()) {
ConnectImpl(std::move(pref_registry), already_connected_types, callback);
} else {
pending_connects_.push_back(
{std::move(pref_registry), already_connected_types, callback});
}
}
void PrefStoreManagerImpl::Create(
const service_manager::Identity& remote_identity,
prefs::mojom::PrefStoreConnectorRequest request) {
connector_bindings_.AddBinding(this, std::move(request));
}
void PrefStoreManagerImpl::Create(
const service_manager::Identity& remote_identity,
prefs::mojom::PrefStoreRegistryRequest request) {
registry_bindings_.AddBinding(this, std::move(request));
}
void PrefStoreManagerImpl::Create(
const service_manager::Identity& remote_identity,
prefs::mojom::PrefServiceControlRequest request) {
if (init_binding_.is_bound()) {
LOG(ERROR)
<< "Pref service received unexpected control interface connection from "
<< remote_identity.name();
return;
}
init_binding_.Bind(std::move(request));
}
void PrefStoreManagerImpl::Init(
mojom::PersistentPrefStoreConfigurationPtr configuration) {
DCHECK(!persistent_pref_store_);
persistent_pref_store_ = CreatePersistentPrefStore(
std::move(configuration), worker_pool_.get(),
base::Bind(&PrefStoreManagerImpl::OnPersistentPrefStoreReady,
base::Unretained(this)));
DCHECK(persistent_pref_store_);
if (AllConnected())
ProcessPendingConnects();
}
void PrefStoreManagerImpl::OnStart() {}
void PrefStoreManagerImpl::OnBindInterface(
const service_manager::ServiceInfo& source_info,
const std::string& interface_name,
mojo::ScopedMessagePipeHandle interface_pipe) {
registry_.BindInterface(source_info.identity, interface_name,
std::move(interface_pipe));
}
void PrefStoreManagerImpl::OnPrefStoreDisconnect(
PrefValueStore::PrefStoreType type) {
DVLOG(1) << "Deregistering pref store: " << type;
pref_store_ptrs_.erase(type);
}
bool PrefStoreManagerImpl::AllConnected() const {
return pref_store_ptrs_.size() == expected_pref_stores_.size() &&
persistent_pref_store_ && persistent_pref_store_->initialized();
}
void PrefStoreManagerImpl::ProcessPendingConnects() {
for (auto& connect : pending_connects_)
ConnectImpl(std::move(connect.pref_registry),
connect.already_connected_types, connect.callback);
pending_connects_.clear();
}
void PrefStoreManagerImpl::ConnectImpl(
mojom::PrefRegistryPtr pref_registry,
const std::vector<PrefValueStore::PrefStoreType>& already_connected_types,
const ConnectCallback& callback) {
std::vector<std::string> observed_prefs;
for (auto& registration : pref_registry->registrations) {
observed_prefs.push_back(registration.first);
const auto& key = registration.first;
auto& default_value = registration.second->default_value;
const base::Value* old_default = nullptr;
// TODO(sammc): Once non-owning registrations are supported, disallow
// multiple owners instead of just checking for consistent defaults.
if (defaults_->GetValue(key, &old_default))
DCHECK(old_default->Equals(default_value.get()));
else
defaults_->SetDefaultValue(key, std::move(default_value));
}
// Only connect to pref stores the client isn't already connected to.
PrefStorePtrs ptrs;
for (const auto& entry : pref_store_ptrs_) {
if (!base::ContainsValue(already_connected_types, entry.first)) {
ptrs.insert(std::make_pair(entry.first, entry.second.get()));
}
}
ConnectionBarrier::Create(
ptrs,
persistent_pref_store_->CreateConnection(
PersistentPrefStoreImpl::ObservedPrefs(observed_prefs.begin(),
observed_prefs.end())),
observed_prefs, callback);
}
void PrefStoreManagerImpl::OnPersistentPrefStoreReady() {
DVLOG(1) << "PersistentPrefStore ready";
if (AllConnected()) {
ProcessPendingConnects();
}
}
} // namespace prefs