blob: f7c4f9c642a489631dcc2c8dfc41691d75835431 [file] [log] [blame]
// Copyright 2014 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/keyed_service/core/refcounted_keyed_service_factory.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include "components/keyed_service/core/dependency_manager.h"
#include "components/keyed_service/core/refcounted_keyed_service.h"
RefcountedKeyedServiceFactory::RefcountedKeyedServiceFactory(
const char* name,
DependencyManager* manager)
: KeyedServiceBaseFactory(name, manager) {
}
RefcountedKeyedServiceFactory::~RefcountedKeyedServiceFactory() {
DCHECK(mapping_.empty());
}
void RefcountedKeyedServiceFactory::SetTestingFactory(
base::SupportsUserData* context,
TestingFactory testing_factory) {
// Destroying the context may cause us to lose data about whether |context|
// has our preferences registered on it (since the context object itself
// isn't dead). See if we need to readd it once we've gone through normal
// destruction.
bool add_context = ArePreferencesSetOn(context);
// Ensure that |context| is not marked as stale (e.g., due to it aliasing an
// instance that was destroyed in an earlier test) in order to avoid accesses
// to |context| in |ContextShutdown| from causing
// |AssertBrowserContextWasntDestroyed| to raise an error.
MarkContextLive(context);
// We have to go through the shutdown and destroy mechanisms because there
// are unit tests that create a service on a context and then change the
// testing service mid-test.
ContextShutdown(context);
ContextDestroyed(context);
if (add_context)
MarkPreferencesSetOn(context);
testing_factories_.emplace(context, std::move(testing_factory));
}
scoped_refptr<RefcountedKeyedService>
RefcountedKeyedServiceFactory::SetTestingFactoryAndUse(
base::SupportsUserData* context,
TestingFactory testing_factory) {
DCHECK(testing_factory);
SetTestingFactory(context, std::move(testing_factory));
return GetServiceForContext(context, true);
}
scoped_refptr<RefcountedKeyedService>
RefcountedKeyedServiceFactory::GetServiceForContext(
base::SupportsUserData* context,
bool create) {
context = GetContextToUse(context);
if (!context)
return nullptr;
// NOTE: If you modify any of the logic below, make sure to update the
// non-refcounted version in context_keyed_service_factory.cc!
auto iterator = mapping_.find(context);
if (iterator != mapping_.end())
return iterator->second;
// Object not found.
if (!create)
return nullptr; // And we're forbidden from creating one.
// Create new object.
// Check to see if we have a per-BrowserContext testing factory that we should
// use instead of default behavior.
scoped_refptr<RefcountedKeyedService> service;
auto factory_iterator = testing_factories_.find(context);
if (factory_iterator != testing_factories_.end()) {
if (factory_iterator->second) {
if (!IsOffTheRecord(context))
RegisterUserPrefsOnContextForTest(context);
service = factory_iterator->second.Run(context);
}
} else {
service = BuildServiceInstanceFor(context);
}
return Associate(context, std::move(service));
}
scoped_refptr<RefcountedKeyedService> RefcountedKeyedServiceFactory::Associate(
base::SupportsUserData* context,
scoped_refptr<RefcountedKeyedService> service) {
DCHECK(!base::ContainsKey(mapping_, context));
auto iterator = mapping_.emplace(context, std::move(service)).first;
return iterator->second;
}
void RefcountedKeyedServiceFactory::Disassociate(
base::SupportsUserData* context) {
// We "merely" drop our reference to the service. Hopefully this will cause
// the service to be destroyed. If not, oh well.
auto iterator = mapping_.find(context);
if (iterator != mapping_.end())
mapping_.erase(iterator);
}
void RefcountedKeyedServiceFactory::ContextShutdown(
base::SupportsUserData* context) {
auto iterator = mapping_.find(context);
if (iterator != mapping_.end() && iterator->second.get())
iterator->second->ShutdownOnUIThread();
}
void RefcountedKeyedServiceFactory::ContextDestroyed(
base::SupportsUserData* context) {
Disassociate(context);
// For unit tests, we also remove the factory function both so we don't
// maintain a big map of dead pointers, but also since we may have a second
// object that lives at the same address (see other comments about unit tests
// in this file).
testing_factories_.erase(context);
KeyedServiceBaseFactory::ContextDestroyed(context);
}
void RefcountedKeyedServiceFactory::SetEmptyTestingFactory(
base::SupportsUserData* context) {
SetTestingFactory(context, TestingFactory());
}
bool RefcountedKeyedServiceFactory::HasTestingFactory(
base::SupportsUserData* context) {
return base::ContainsKey(testing_factories_, context);
}
void RefcountedKeyedServiceFactory::CreateServiceNow(
base::SupportsUserData* context) {
GetServiceForContext(context, true);
}