summaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
authorphajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-05-20 23:33:40 +0000
committerphajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-05-20 23:33:40 +0000
commit0dd6f20318b09983933fa8aab1573eb057cc1088 (patch)
tree4b561c910850f3df5f51b1916164969f6f46b0c4 /components
parentb55475898c3d74ec5810e4b60bccce4183cb17d9 (diff)
downloadchromium_src-0dd6f20318b09983933fa8aab1573eb057cc1088.zip
chromium_src-0dd6f20318b09983933fa8aab1573eb057cc1088.tar.gz
chromium_src-0dd6f20318b09983933fa8aab1573eb057cc1088.tar.bz2
Move ProfileKeyedService infrastructure to a component
Renames of the classes and methods will follow in separate patch(es). BUG=227219 R=erg@chromium.org, joi@chromium.org Review URL: https://codereview.chromium.org/14743010 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@201167 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'components')
-rw-r--r--components/browser_context_keyed_service.gypi38
-rw-r--r--components/browser_context_keyed_service/DEPS3
-rw-r--r--components/browser_context_keyed_service/browser_context_dependency_manager.cc149
-rw-r--r--components/browser_context_keyed_service/browser_context_dependency_manager.h86
-rw-r--r--components/browser_context_keyed_service/browser_context_dependency_manager_unittest.cc173
-rw-r--r--components/browser_context_keyed_service/browser_context_keyed_base_factory.cc110
-rw-r--r--components/browser_context_keyed_service/browser_context_keyed_base_factory.h133
-rw-r--r--components/browser_context_keyed_service/browser_context_keyed_service.h30
-rw-r--r--components/browser_context_keyed_service/browser_context_keyed_service_factory.cc126
-rw-r--r--components/browser_context_keyed_service/browser_context_keyed_service_factory.h115
-rw-r--r--components/browser_context_keyed_service/dependency_graph.cc166
-rw-r--r--components/browser_context_keyed_service/dependency_graph.h67
-rw-r--r--components/browser_context_keyed_service/dependency_graph_unittest.cc161
-rw-r--r--components/browser_context_keyed_service/dependency_node.h16
-rw-r--r--components/browser_context_keyed_service/refcounted_browser_context_keyed_service.cc34
-rw-r--r--components/browser_context_keyed_service/refcounted_browser_context_keyed_service.h70
-rw-r--r--components/browser_context_keyed_service/refcounted_browser_context_keyed_service_factory.cc127
-rw-r--r--components/browser_context_keyed_service/refcounted_browser_context_keyed_service_factory.h88
-rw-r--r--components/components.gyp1
-rw-r--r--components/components_tests.gypi5
20 files changed, 1698 insertions, 0 deletions
diff --git a/components/browser_context_keyed_service.gypi b/components/browser_context_keyed_service.gypi
new file mode 100644
index 0000000..03bd8f2
--- /dev/null
+++ b/components/browser_context_keyed_service.gypi
@@ -0,0 +1,38 @@
+# Copyright 2013 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.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'browser_context_keyed_service',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '..',
+ ],
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../base/base.gyp:base_prefs',
+ '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+ '../content/content.gyp:content_common',
+ 'user_prefs',
+ ],
+ 'sources': [
+ 'browser_context_keyed_service/browser_context_dependency_manager.cc',
+ 'browser_context_keyed_service/browser_context_dependency_manager.h',
+ 'browser_context_keyed_service/browser_context_keyed_base_factory.h',
+ 'browser_context_keyed_service/browser_context_keyed_base_factory.cc',
+ 'browser_context_keyed_service/browser_context_keyed_service.h',
+ 'browser_context_keyed_service/browser_context_keyed_service_factory.cc',
+ 'browser_context_keyed_service/browser_context_keyed_service_factory.h',
+ 'browser_context_keyed_service/dependency_graph.cc',
+ 'browser_context_keyed_service/dependency_graph.h',
+ 'browser_context_keyed_service/dependency_node.h',
+ 'browser_context_keyed_service/refcounted_browser_context_keyed_service.cc',
+ 'browser_context_keyed_service/refcounted_browser_context_keyed_service.h',
+ 'browser_context_keyed_service/refcounted_browser_context_keyed_service_factory.cc',
+ 'browser_context_keyed_service/refcounted_browser_context_keyed_service_factory.h',
+ ],
+ },
+ ],
+}
diff --git a/components/browser_context_keyed_service/DEPS b/components/browser_context_keyed_service/DEPS
new file mode 100644
index 0000000..1c35d9c
--- /dev/null
+++ b/components/browser_context_keyed_service/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+content/public/browser",
+]
diff --git a/components/browser_context_keyed_service/browser_context_dependency_manager.cc b/components/browser_context_keyed_service/browser_context_dependency_manager.cc
new file mode 100644
index 0000000..44d40b4
--- /dev/null
+++ b/components/browser_context_keyed_service/browser_context_dependency_manager.cc
@@ -0,0 +1,149 @@
+// Copyright (c) 2013 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/browser_context_keyed_service/browser_context_dependency_manager.h"
+
+#include <algorithm>
+#include <deque>
+#include <iterator>
+
+#include "base/bind.h"
+#include "components/browser_context_keyed_service/browser_context_keyed_base_factory.h"
+#include "content/public/browser/browser_context.h"
+
+#ifndef NDEBUG
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "content/public/common/content_switches.h"
+#endif
+
+class Profile;
+
+void ProfileDependencyManager::AddComponent(
+ ProfileKeyedBaseFactory* component) {
+ dependency_graph_.AddNode(component);
+}
+
+void ProfileDependencyManager::RemoveComponent(
+ ProfileKeyedBaseFactory* component) {
+ dependency_graph_.RemoveNode(component);
+}
+
+void ProfileDependencyManager::AddEdge(ProfileKeyedBaseFactory* depended,
+ ProfileKeyedBaseFactory* dependee) {
+ dependency_graph_.AddEdge(depended, dependee);
+}
+
+void ProfileDependencyManager::CreateProfileServices(
+ content::BrowserContext* profile, bool is_testing_profile) {
+#ifndef NDEBUG
+ // Unmark |profile| as dead. This exists because of unit tests, which will
+ // often have similar stack structures. 0xWhatever might be created, go out
+ // of scope, and then a new Profile object might be created at 0xWhatever.
+ dead_profile_pointers_.erase(profile);
+#endif
+
+ std::vector<DependencyNode*> construction_order;
+ if (!dependency_graph_.GetConstructionOrder(&construction_order)) {
+ NOTREACHED();
+ }
+
+#ifndef NDEBUG
+ DumpProfileDependencies(profile);
+#endif
+
+ for (size_t i = 0; i < construction_order.size(); i++) {
+ ProfileKeyedBaseFactory* factory =
+ static_cast<ProfileKeyedBaseFactory*>(construction_order[i]);
+
+ if (!profile->IsOffTheRecord()) {
+ // We only register preferences on normal profiles because the incognito
+ // profile shares the pref service with the normal one.
+ factory->RegisterUserPrefsOnProfile(profile);
+ }
+
+ if (is_testing_profile && factory->ServiceIsNULLWhileTesting()) {
+ factory->SetEmptyTestingFactory(profile);
+ } else if (factory->ServiceIsCreatedWithProfile()) {
+ // Create the service.
+ factory->CreateServiceNow(profile);
+ }
+ }
+}
+
+void ProfileDependencyManager::DestroyProfileServices(
+ content::BrowserContext* profile) {
+ std::vector<DependencyNode*> destruction_order;
+ if (!dependency_graph_.GetDestructionOrder(&destruction_order)) {
+ NOTREACHED();
+ }
+
+#ifndef NDEBUG
+ DumpProfileDependencies(profile);
+#endif
+
+ for (size_t i = 0; i < destruction_order.size(); i++) {
+ ProfileKeyedBaseFactory* factory =
+ static_cast<ProfileKeyedBaseFactory*>(destruction_order[i]);
+ factory->ProfileShutdown(profile);
+ }
+
+#ifndef NDEBUG
+ // The profile is now dead to the rest of the program.
+ dead_profile_pointers_.insert(profile);
+#endif
+
+ for (size_t i = 0; i < destruction_order.size(); i++) {
+ ProfileKeyedBaseFactory* factory =
+ static_cast<ProfileKeyedBaseFactory*>(destruction_order[i]);
+ factory->ProfileDestroyed(profile);
+ }
+}
+
+#ifndef NDEBUG
+void ProfileDependencyManager::AssertProfileWasntDestroyed(
+ content::BrowserContext* profile) {
+ if (dead_profile_pointers_.find(profile) != dead_profile_pointers_.end()) {
+ NOTREACHED() << "Attempted to access a Profile that was ShutDown(). This "
+ << "is most likely a heap smasher in progress. After "
+ << "ProfileKeyedService::Shutdown() completes, your service "
+ << "MUST NOT refer to depended Profile services again.";
+ }
+}
+#endif
+
+// static
+ProfileDependencyManager* ProfileDependencyManager::GetInstance() {
+ return Singleton<ProfileDependencyManager>::get();
+}
+
+ProfileDependencyManager::ProfileDependencyManager() {
+}
+
+ProfileDependencyManager::~ProfileDependencyManager() {
+}
+
+#ifndef NDEBUG
+namespace {
+
+std::string ProfileKeyedBaseFactoryGetNodeName(DependencyNode* node) {
+ return static_cast<ProfileKeyedBaseFactory*>(node)->name();
+}
+
+} // namespace
+
+void ProfileDependencyManager::DumpProfileDependencies(
+ content::BrowserContext* profile) {
+ // Whenever we try to build a destruction ordering, we should also dump a
+ // dependency graph to "/path/to/profile/profile-dependencies.dot".
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDumpBrowserContextDependencyGraph)) {
+ base::FilePath dot_file =
+ profile->GetPath().AppendASCII("browser-context-dependencies.dot");
+ std::string contents = dependency_graph_.DumpAsGraphviz(
+ "Profile", base::Bind(&ProfileKeyedBaseFactoryGetNodeName));
+ file_util::WriteFile(dot_file, contents.c_str(), contents.size());
+ }
+}
+#endif // NDEBUG
diff --git a/components/browser_context_keyed_service/browser_context_dependency_manager.h b/components/browser_context_keyed_service/browser_context_dependency_manager.h
new file mode 100644
index 0000000..2b5ba0e
--- /dev/null
+++ b/components/browser_context_keyed_service/browser_context_dependency_manager.h
@@ -0,0 +1,86 @@
+// 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.
+
+#ifndef COMPONENTS_BROWSER_CONTEXT_KEYED_SERVICE_BROWSER_CONTEXT_DEPENDENCY_MANAGER_H_
+#define COMPONENTS_BROWSER_CONTEXT_KEYED_SERVICE_BROWSER_CONTEXT_DEPENDENCY_MANAGER_H_
+
+#include "base/memory/singleton.h"
+#include "components/browser_context_keyed_service/dependency_graph.h"
+
+#ifndef NDEBUG
+#include <set>
+#endif
+
+class ProfileKeyedBaseFactory;
+
+namespace content {
+class BrowserContext;
+}
+
+// A singleton that listens for profile destruction notifications and
+// rebroadcasts them to each ProfileKeyedBaseFactory in a safe order based
+// on the stated dependencies by each service.
+class ProfileDependencyManager {
+ public:
+ // Adds/Removes a component from our list of live components. Removing will
+ // also remove live dependency links.
+ void AddComponent(ProfileKeyedBaseFactory* component);
+ void RemoveComponent(ProfileKeyedBaseFactory* component);
+
+ // Adds a dependency between two factories.
+ void AddEdge(ProfileKeyedBaseFactory* depended,
+ ProfileKeyedBaseFactory* dependee);
+
+ // Called by each Profile to alert us of its creation. Several services want
+ // to be started when a profile is created. Testing configuration is also
+ // done at this time. (If you want your ProfileKeyedService to be started
+ // with the Profile, override ProfileKeyedBaseFactory::
+ // ServiceIsCreatedWithProfile() to return true.)
+ void CreateProfileServices(content::BrowserContext* profile,
+ bool is_testing_profile);
+
+ // Called by each Profile to alert us that we should destroy services
+ // associated with it.
+ //
+ // Why not use the existing PROFILE_DESTROYED notification?
+ //
+ // - Because we need to do everything here after the application has handled
+ // being notified about PROFILE_DESTROYED.
+ // - Because this class is a singleton and Singletons can't rely on
+ // NotificationService in unit tests because NotificationService is
+ // replaced in many tests.
+ void DestroyProfileServices(content::BrowserContext* profile);
+
+#ifndef NDEBUG
+ // Debugging assertion called as part of GetServiceForProfile in debug
+ // mode. This will NOTREACHED() whenever the user is trying to access a stale
+ // Profile*.
+ void AssertProfileWasntDestroyed(content::BrowserContext* profile);
+#endif
+
+ static ProfileDependencyManager* GetInstance();
+
+ private:
+ friend class ProfileDependencyManagerUnittests;
+ friend struct DefaultSingletonTraits<ProfileDependencyManager>;
+
+ ProfileDependencyManager();
+ virtual ~ProfileDependencyManager();
+
+#ifndef NDEBUG
+ void DumpProfileDependencies(content::BrowserContext* profile);
+#endif
+
+ DependencyGraph dependency_graph_;
+
+#ifndef NDEBUG
+ // A list of profile objects that have gone through the Shutdown()
+ // phase. These pointers are most likely invalid, but we keep track of their
+ // locations in memory so we can nicely assert if we're asked to do anything
+ // with them.
+ std::set<content::BrowserContext*> dead_profile_pointers_;
+#endif
+};
+
+#endif // COMPONENTS_BROWSER_CONTEXT_KEYED_SERVICE_BROWSER_CONTEXT_DEPENDENCY_MANAGER_H_
diff --git a/components/browser_context_keyed_service/browser_context_dependency_manager_unittest.cc b/components/browser_context_keyed_service/browser_context_dependency_manager_unittest.cc
new file mode 100644
index 0000000..a0c03ff
--- /dev/null
+++ b/components/browser_context_keyed_service/browser_context_dependency_manager_unittest.cc
@@ -0,0 +1,173 @@
+// 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 "testing/gtest/include/gtest/gtest.h"
+
+#include "components/browser_context_keyed_service/browser_context_dependency_manager.h"
+#include "components/browser_context_keyed_service/browser_context_keyed_service_factory.h"
+
+class ProfileDependencyManagerUnittests : public ::testing::Test {
+ protected:
+ // To get around class access:
+ void DependOn(ProfileKeyedServiceFactory* child,
+ ProfileKeyedServiceFactory* parent) {
+ child->DependsOn(parent);
+ }
+
+ ProfileDependencyManager* manager() { return &dependency_manager_; }
+
+ std::vector<std::string>* shutdown_order() { return &shutdown_order_; }
+
+ private:
+ ProfileDependencyManager dependency_manager_;
+
+ std::vector<std::string> shutdown_order_;
+};
+
+class TestService : public ProfileKeyedServiceFactory {
+ public:
+ TestService(const std::string& name,
+ std::vector<std::string>* fill_on_shutdown,
+ ProfileDependencyManager* manager)
+ : ProfileKeyedServiceFactory("TestService", manager),
+ name_(name),
+ fill_on_shutdown_(fill_on_shutdown) {
+ }
+
+ virtual ProfileKeyedService* BuildServiceInstanceFor(
+ content::BrowserContext* profile) const OVERRIDE {
+ ADD_FAILURE() << "This isn't part of the tests!";
+ return NULL;
+ }
+
+ virtual void ProfileShutdown(content::BrowserContext* profile) OVERRIDE {
+ fill_on_shutdown_->push_back(name_);
+ }
+
+ private:
+ const std::string name_;
+ std::vector<std::string>* fill_on_shutdown_;
+};
+
+// Tests that we can deal with a single component.
+TEST_F(ProfileDependencyManagerUnittests, SingleCase) {
+ TestService service("service", shutdown_order(), manager());
+
+ manager()->DestroyProfileServices(NULL);
+
+ ASSERT_EQ(1U, shutdown_order()->size());
+ EXPECT_STREQ("service", (*shutdown_order())[0].c_str());
+}
+
+// Tests that we get a simple one component depends on the other case.
+TEST_F(ProfileDependencyManagerUnittests, SimpleDependency) {
+ TestService parent("parent", shutdown_order(), manager());
+ TestService child("child", shutdown_order(), manager());
+ DependOn(&child, &parent);
+
+ manager()->DestroyProfileServices(NULL);
+
+ ASSERT_EQ(2U, shutdown_order()->size());
+ EXPECT_STREQ("child", (*shutdown_order())[0].c_str());
+ EXPECT_STREQ("parent", (*shutdown_order())[1].c_str());
+}
+
+// Tests two children, one parent
+TEST_F(ProfileDependencyManagerUnittests, TwoChildrenOneParent) {
+ TestService parent("parent", shutdown_order(), manager());
+ TestService child1("child1", shutdown_order(), manager());
+ TestService child2("child2", shutdown_order(), manager());
+ DependOn(&child1, &parent);
+ DependOn(&child2, &parent);
+
+ manager()->DestroyProfileServices(NULL);
+
+ ASSERT_EQ(3U, shutdown_order()->size());
+ EXPECT_STREQ("child2", (*shutdown_order())[0].c_str());
+ EXPECT_STREQ("child1", (*shutdown_order())[1].c_str());
+ EXPECT_STREQ("parent", (*shutdown_order())[2].c_str());
+}
+
+// Tests an M configuration
+TEST_F(ProfileDependencyManagerUnittests, MConfiguration) {
+ TestService parent1("parent1", shutdown_order(), manager());
+ TestService parent2("parent2", shutdown_order(), manager());
+
+ TestService child_of_1("child_of_1", shutdown_order(), manager());
+ DependOn(&child_of_1, &parent1);
+
+ TestService child_of_12("child_of_12", shutdown_order(), manager());
+ DependOn(&child_of_12, &parent1);
+ DependOn(&child_of_12, &parent2);
+
+ TestService child_of_2("child_of_2", shutdown_order(), manager());
+ DependOn(&child_of_2, &parent2);
+
+ manager()->DestroyProfileServices(NULL);
+
+ ASSERT_EQ(5U, shutdown_order()->size());
+ EXPECT_STREQ("child_of_2", (*shutdown_order())[0].c_str());
+ EXPECT_STREQ("child_of_12", (*shutdown_order())[1].c_str());
+ EXPECT_STREQ("child_of_1", (*shutdown_order())[2].c_str());
+ EXPECT_STREQ("parent2", (*shutdown_order())[3].c_str());
+ EXPECT_STREQ("parent1", (*shutdown_order())[4].c_str());
+}
+
+// Tests that it can deal with a simple diamond.
+TEST_F(ProfileDependencyManagerUnittests, DiamondConfiguration) {
+ TestService parent("parent", shutdown_order(), manager());
+
+ TestService middle_row_1("middle_row_1", shutdown_order(), manager());
+ DependOn(&middle_row_1, &parent);
+
+ TestService middle_row_2("middle_row_2", shutdown_order(), manager());
+ DependOn(&middle_row_2, &parent);
+
+ TestService bottom("bottom", shutdown_order(), manager());
+ DependOn(&bottom, &middle_row_1);
+ DependOn(&bottom, &middle_row_2);
+
+ manager()->DestroyProfileServices(NULL);
+
+ ASSERT_EQ(4U, shutdown_order()->size());
+ EXPECT_STREQ("bottom", (*shutdown_order())[0].c_str());
+ EXPECT_STREQ("middle_row_2", (*shutdown_order())[1].c_str());
+ EXPECT_STREQ("middle_row_1", (*shutdown_order())[2].c_str());
+ EXPECT_STREQ("parent", (*shutdown_order())[3].c_str());
+}
+
+// A final test that works with a more complex graph.
+TEST_F(ProfileDependencyManagerUnittests, ComplexGraph) {
+ TestService everything_depends_on_me("everything_depends_on_me",
+ shutdown_order(), manager());
+
+ TestService intermediary_service("intermediary_service",
+ shutdown_order(), manager());
+ DependOn(&intermediary_service, &everything_depends_on_me);
+
+ TestService specialized_service("specialized_service",
+ shutdown_order(), manager());
+ DependOn(&specialized_service, &everything_depends_on_me);
+ DependOn(&specialized_service, &intermediary_service);
+
+ TestService other_root("other_root", shutdown_order(), manager());
+
+ TestService other_intermediary("other_intermediary",
+ shutdown_order(), manager());
+ DependOn(&other_intermediary, &other_root);
+
+ TestService bottom("bottom", shutdown_order(), manager());
+ DependOn(&bottom, &specialized_service);
+ DependOn(&bottom, &other_intermediary);
+
+ manager()->DestroyProfileServices(NULL);
+
+ ASSERT_EQ(6U, shutdown_order()->size());
+ EXPECT_STREQ("bottom", (*shutdown_order())[0].c_str());
+ EXPECT_STREQ("specialized_service", (*shutdown_order())[1].c_str());
+ EXPECT_STREQ("other_intermediary", (*shutdown_order())[2].c_str());
+ EXPECT_STREQ("intermediary_service", (*shutdown_order())[3].c_str());
+ EXPECT_STREQ("other_root", (*shutdown_order())[4].c_str());
+ EXPECT_STREQ("everything_depends_on_me", (*shutdown_order())[5].c_str());
+}
diff --git a/components/browser_context_keyed_service/browser_context_keyed_base_factory.cc b/components/browser_context_keyed_service/browser_context_keyed_base_factory.cc
new file mode 100644
index 0000000..9228400
--- /dev/null
+++ b/components/browser_context_keyed_service/browser_context_keyed_base_factory.cc
@@ -0,0 +1,110 @@
+// 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/browser_context_keyed_service/browser_context_keyed_base_factory.h"
+
+#include "base/prefs/pref_service.h"
+#include "components/browser_context_keyed_service/browser_context_dependency_manager.h"
+#include "components/user_prefs/pref_registry_syncable.h"
+#include "components/user_prefs/user_prefs.h"
+#include "content/public/browser/browser_context.h"
+
+ProfileKeyedBaseFactory::ProfileKeyedBaseFactory(
+ const char* name, ProfileDependencyManager* manager)
+ : dependency_manager_(manager)
+#ifndef NDEBUG
+ , service_name_(name)
+#endif
+{
+ dependency_manager_->AddComponent(this);
+}
+
+ProfileKeyedBaseFactory::~ProfileKeyedBaseFactory() {
+ dependency_manager_->RemoveComponent(this);
+}
+
+void ProfileKeyedBaseFactory::DependsOn(ProfileKeyedBaseFactory* rhs) {
+ dependency_manager_->AddEdge(rhs, this);
+}
+
+content::BrowserContext* ProfileKeyedBaseFactory::GetBrowserContextToUse(
+ content::BrowserContext* context) const {
+ DCHECK(CalledOnValidThread());
+
+#ifndef NDEBUG
+ dependency_manager_->AssertProfileWasntDestroyed(context);
+#endif
+
+ // Safe default for the Incognito mode: no service.
+ if (context->IsOffTheRecord())
+ return NULL;
+
+ return context;
+}
+
+void ProfileKeyedBaseFactory::RegisterUserPrefsOnProfile(
+ content::BrowserContext* profile) {
+ // Safe timing for pref registration is hard. Previously, we made Profile
+ // responsible for all pref registration on every service that used
+ // Profile. Now we don't and there are timing issues.
+ //
+ // With normal profiles, prefs can simply be registered at
+ // ProfileDependencyManager::CreateProfileServices time. With incognito
+ // profiles, we just never register since incognito profiles share the same
+ // pref services with their parent profiles.
+ //
+ // TestingProfiles throw a wrench into the mix, in that some tests will
+ // swap out the PrefService after we've registered user prefs on the original
+ // PrefService. Test code that does this is responsible for either manually
+ // invoking RegisterUserPrefs() on the appropriate ProfileKeyedServiceFactory
+ // associated with the prefs they need, or they can use SetTestingFactory()
+ // and create a service (since service creation with a factory method causes
+ // registration to happen at service creation time).
+ //
+ // Now that services are responsible for declaring their preferences, we have
+ // to enforce a uniquenes check here because some tests create one profile and
+ // multiple services of the same type attached to that profile (serially, not
+ // parallel) and we don't want to register multiple times on the same profile.
+ DCHECK(!profile->IsOffTheRecord());
+
+ std::set<content::BrowserContext*>::iterator it =
+ registered_preferences_.find(profile);
+ if (it == registered_preferences_.end()) {
+ PrefService* prefs = components::UserPrefs::Get(profile);
+ user_prefs::PrefRegistrySyncable* registry =
+ static_cast<user_prefs::PrefRegistrySyncable*>(
+ prefs->DeprecatedGetPrefRegistry());
+ RegisterUserPrefs(registry);
+ registered_preferences_.insert(profile);
+ }
+}
+
+bool ProfileKeyedBaseFactory::ServiceIsCreatedWithProfile() const {
+ return false;
+}
+
+bool ProfileKeyedBaseFactory::ServiceIsNULLWhileTesting() const {
+ return false;
+}
+
+void ProfileKeyedBaseFactory::ProfileDestroyed(
+ content::BrowserContext* profile) {
+ // While object destruction can be customized in ways where the object is
+ // only dereferenced, this still must run on the UI thread.
+ DCHECK(CalledOnValidThread());
+
+ registered_preferences_.erase(profile);
+}
+
+bool ProfileKeyedBaseFactory::ArePreferencesSetOn(
+ content::BrowserContext* profile) const {
+ return registered_preferences_.find(profile) !=
+ registered_preferences_.end();
+}
+
+void ProfileKeyedBaseFactory::MarkPreferencesSetOn(
+ content::BrowserContext* profile) {
+ DCHECK(!ArePreferencesSetOn(profile));
+ registered_preferences_.insert(profile);
+}
diff --git a/components/browser_context_keyed_service/browser_context_keyed_base_factory.h b/components/browser_context_keyed_service/browser_context_keyed_base_factory.h
new file mode 100644
index 0000000..30a0c32
--- /dev/null
+++ b/components/browser_context_keyed_service/browser_context_keyed_base_factory.h
@@ -0,0 +1,133 @@
+// 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.
+
+#ifndef COMPONENTS_BROWSER_CONTEXT_KEYED_SERVICE_BROWSER_CONTEXT_KEYED_BASE_FACTORY_H_
+#define COMPONENTS_BROWSER_CONTEXT_KEYED_SERVICE_BROWSER_CONTEXT_KEYED_BASE_FACTORY_H_
+
+#include <set>
+
+#include "base/threading/non_thread_safe.h"
+#include "components/browser_context_keyed_service/dependency_node.h"
+
+class PrefService;
+class ProfileDependencyManager;
+
+namespace content {
+class BrowserContext;
+}
+
+namespace user_prefs {
+class PrefRegistrySyncable;
+}
+// Base class for Factories that take a Profile object and return some service.
+//
+// Unless you're trying to make a new type of Factory, you probably don't want
+// this class, but its subclasses: ProfileKeyedServiceFactory and
+// RefcountedProfileKeyedServiceFactory. This object describes general
+// dependency management between Factories; subclasses react to lifecycle
+// events and implement memory management.
+class ProfileKeyedBaseFactory : public base::NonThreadSafe,
+ public DependencyNode {
+ public:
+ // Registers preferences used in this service on the pref service of
+ // |profile|. This is the public interface and is safe to be called multiple
+ // times because testing code can have multiple services of the same type
+ // attached to a single |profile|.
+ void RegisterUserPrefsOnProfile(content::BrowserContext* profile);
+
+#ifndef NDEBUG
+ // Returns our name. We don't keep track of this in release mode.
+ const char* name() const { return service_name_; }
+#endif
+
+ protected:
+ ProfileKeyedBaseFactory(const char* name,
+ ProfileDependencyManager* manager);
+ virtual ~ProfileKeyedBaseFactory();
+
+ // The main public interface for declaring dependencies between services
+ // created by factories.
+ void DependsOn(ProfileKeyedBaseFactory* rhs);
+
+ // Interface for people building a concrete FooServiceFactory: --------------
+
+ // Finds which browser context (if any) to use.
+ virtual content::BrowserContext* GetBrowserContextToUse(
+ content::BrowserContext* context) const;
+
+ // Register any user preferences on this service. This is called during
+ // CreateProfileService() since preferences are registered on a per Profile
+ // basis.
+ virtual void RegisterUserPrefs(user_prefs::PrefRegistrySyncable* registry) {}
+
+ // By default, we create instances of a service lazily and wait until
+ // GetForProfile() is called on our subclass. Some services need to be
+ // created as soon as the Profile has been brought up.
+ virtual bool ServiceIsCreatedWithProfile() const;
+
+ // By default, TestingProfiles will be treated like normal profiles. You can
+ // override this so that by default, the service associated with the
+ // TestingProfile is NULL. (This is just a shortcut around
+ // SetTestingFactory() to make sure our profiles don't directly refer to the
+ // services they use.)
+ virtual bool ServiceIsNULLWhileTesting() const;
+
+ // Interface for people building a type of ProfileKeyedFactory: -------------
+
+ // A helper object actually listens for notifications about Profile
+ // destruction, calculates the order in which things are destroyed and then
+ // does a two pass shutdown.
+ //
+ // It is up to the individual factory types to determine what this two pass
+ // shutdown means. The general framework guarantees the following:
+ //
+ // - Each ProfileShutdown() is called in dependency order (and you may reach
+ // out to other services during this phase).
+ //
+ // - Each ProfileDestroyed() is called in dependency order. We will
+ // NOTREACHED() if you attempt to GetForProfile() any other service. You
+ // should delete/deref/do other final memory management things during this
+ // phase. You must also call the base class method as the last thing you
+ // do.
+ virtual void ProfileShutdown(content::BrowserContext* profile) = 0;
+ virtual void ProfileDestroyed(content::BrowserContext* profile);
+
+ // Returns whether we've registered the preferences on this profile.
+ bool ArePreferencesSetOn(content::BrowserContext* profile) const;
+
+ // Mark profile as Preferences set.
+ void MarkPreferencesSetOn(content::BrowserContext* profile);
+
+ private:
+ friend class ProfileDependencyManager;
+ friend class ProfileDependencyManagerUnittests;
+
+ // These two methods are for tight integration with the
+ // ProfileDependencyManager.
+
+ // Because of ServiceIsNULLWhileTesting(), we need a way to tell different
+ // subclasses that they should disable testing.
+ virtual void SetEmptyTestingFactory(content::BrowserContext* profile) = 0;
+
+ // We also need a generalized, non-returning method that generates the object
+ // now for when we're creating the profile.
+ virtual void CreateServiceNow(content::BrowserContext* profile) = 0;
+
+ // Which ProfileDependencyManager we should communicate with. In real code,
+ // this will always be ProfileDependencyManager::GetInstance(), but unit
+ // tests will want to use their own copy.
+ ProfileDependencyManager* dependency_manager_;
+
+ // Profiles that have this service's preferences registered on them.
+ std::set<content::BrowserContext*> registered_preferences_;
+
+#if !defined(NDEBUG)
+ // A static string passed in to our constructor. Should be unique across all
+ // services. This is used only for debugging in debug mode. (We can print
+ // pretty graphs with GraphViz with this information.)
+ const char* service_name_;
+#endif
+};
+
+#endif // COMPONENTS_BROWSER_CONTEXT_KEYED_SERVICE_BROWSER_CONTEXT_KEYED_BASE_FACTORY_H_
diff --git a/components/browser_context_keyed_service/browser_context_keyed_service.h b/components/browser_context_keyed_service/browser_context_keyed_service.h
new file mode 100644
index 0000000..b87aeac
--- /dev/null
+++ b/components/browser_context_keyed_service/browser_context_keyed_service.h
@@ -0,0 +1,30 @@
+// 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.
+
+#ifndef COMPONENTS_BROWSER_CONTEXT_KEYED_SERVICE_BROWSER_CONTEXT_KEYED_SERVICE_H_
+#define COMPONENTS_BROWSER_CONTEXT_KEYED_SERVICE_BROWSER_CONTEXT_KEYED_SERVICE_H_
+
+class ProfileKeyedServiceFactory;
+
+// Base class for all ProfileKeyedServices to allow for correct destruction
+// order.
+//
+// Many services that hang off Profile have a two-pass shutdown. Many
+// subsystems need a first pass shutdown phase where they drop references. Not
+// all services will need this, so there's a default implementation. Only once
+// every system has been given a chance to drop references do we start deleting
+// objects.
+class ProfileKeyedService {
+ public:
+ // The first pass is to call Shutdown on a ProfileKeyedService.
+ virtual void Shutdown() {}
+
+ protected:
+ friend class ProfileKeyedServiceFactory;
+
+ // The second pass is the actual deletion of each object.
+ virtual ~ProfileKeyedService() {}
+};
+
+#endif // COMPONENTS_BROWSER_CONTEXT_KEYED_SERVICE_BROWSER_CONTEXT_KEYED_SERVICE_H_
diff --git a/components/browser_context_keyed_service/browser_context_keyed_service_factory.cc b/components/browser_context_keyed_service/browser_context_keyed_service_factory.cc
new file mode 100644
index 0000000..bfc1c74
--- /dev/null
+++ b/components/browser_context_keyed_service/browser_context_keyed_service_factory.cc
@@ -0,0 +1,126 @@
+// 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/browser_context_keyed_service/browser_context_keyed_service_factory.h"
+
+#include <map>
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "components/browser_context_keyed_service/browser_context_dependency_manager.h"
+#include "components/browser_context_keyed_service/browser_context_keyed_service.h"
+#include "content/public/browser/browser_context.h"
+
+void ProfileKeyedServiceFactory::SetTestingFactory(
+ content::BrowserContext* profile, FactoryFunction factory) {
+ // Destroying the profile may cause us to lose data about whether |profile|
+ // has our preferences registered on it (since the profile object itself
+ // isn't dead). See if we need to readd it once we've gone through normal
+ // destruction.
+ bool add_profile = ArePreferencesSetOn(profile);
+
+ // We have to go through the shutdown and destroy mechanisms because there
+ // are unit tests that create a service on a profile and then change the
+ // testing service mid-test.
+ ProfileShutdown(profile);
+ ProfileDestroyed(profile);
+
+ if (add_profile)
+ MarkPreferencesSetOn(profile);
+
+ factories_[profile] = factory;
+}
+
+ProfileKeyedService* ProfileKeyedServiceFactory::SetTestingFactoryAndUse(
+ content::BrowserContext* profile,
+ FactoryFunction factory) {
+ DCHECK(factory);
+ SetTestingFactory(profile, factory);
+ return GetServiceForProfile(profile, true);
+}
+
+ProfileKeyedServiceFactory::ProfileKeyedServiceFactory(
+ const char* name, ProfileDependencyManager* manager)
+ : ProfileKeyedBaseFactory(name, manager) {
+}
+
+ProfileKeyedServiceFactory::~ProfileKeyedServiceFactory() {
+ DCHECK(mapping_.empty());
+}
+
+ProfileKeyedService* ProfileKeyedServiceFactory::GetServiceForProfile(
+ content::BrowserContext* profile,
+ bool create) {
+ profile = GetBrowserContextToUse(profile);
+ if (!profile)
+ return NULL;
+
+ // NOTE: If you modify any of the logic below, make sure to update the
+ // refcounted version in refcounted_profile_keyed_service_factory.cc!
+ ProfileKeyedServices::const_iterator it = mapping_.find(profile);
+ if (it != mapping_.end())
+ return it->second;
+
+ // Object not found.
+ if (!create)
+ return NULL; // And we're forbidden from creating one.
+
+ // Create new object.
+ // Check to see if we have a per-Profile testing factory that we should use
+ // instead of default behavior.
+ ProfileKeyedService* service = NULL;
+ ProfileOverriddenFunctions::const_iterator jt = factories_.find(profile);
+ if (jt != factories_.end()) {
+ if (jt->second) {
+ if (!profile->IsOffTheRecord())
+ RegisterUserPrefsOnProfile(profile);
+ service = jt->second(profile);
+ }
+ } else {
+ service = BuildServiceInstanceFor(profile);
+ }
+
+ Associate(profile, service);
+ return service;
+}
+
+void ProfileKeyedServiceFactory::Associate(content::BrowserContext* profile,
+ ProfileKeyedService* service) {
+ DCHECK(!ContainsKey(mapping_, profile));
+ mapping_.insert(std::make_pair(profile, service));
+}
+
+void ProfileKeyedServiceFactory::ProfileShutdown(
+ content::BrowserContext* profile) {
+ ProfileKeyedServices::iterator it = mapping_.find(profile);
+ if (it != mapping_.end() && it->second)
+ it->second->Shutdown();
+}
+
+void ProfileKeyedServiceFactory::ProfileDestroyed(
+ content::BrowserContext* profile) {
+ ProfileKeyedServices::iterator it = mapping_.find(profile);
+ if (it != mapping_.end()) {
+ delete it->second;
+ mapping_.erase(it);
+ }
+
+ // 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).
+ factories_.erase(profile);
+
+ ProfileKeyedBaseFactory::ProfileDestroyed(profile);
+}
+
+void ProfileKeyedServiceFactory::SetEmptyTestingFactory(
+ content::BrowserContext* profile) {
+ SetTestingFactory(profile, NULL);
+}
+
+void ProfileKeyedServiceFactory::CreateServiceNow(
+ content::BrowserContext* profile) {
+ GetServiceForProfile(profile, true);
+}
diff --git a/components/browser_context_keyed_service/browser_context_keyed_service_factory.h b/components/browser_context_keyed_service/browser_context_keyed_service_factory.h
new file mode 100644
index 0000000..f222c42
--- /dev/null
+++ b/components/browser_context_keyed_service/browser_context_keyed_service_factory.h
@@ -0,0 +1,115 @@
+// 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.
+
+#ifndef COMPONENTS_BROWSER_CONTEXT_KEYED_SERVICE_BROWSER_CONTEXT_KEYED_SERVICE_FACTORY_H_
+#define COMPONENTS_BROWSER_CONTEXT_KEYED_SERVICE_BROWSER_CONTEXT_KEYED_SERVICE_FACTORY_H_
+
+#include <map>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "components/browser_context_keyed_service/browser_context_keyed_base_factory.h"
+#include "components/browser_context_keyed_service/browser_context_keyed_service.h"
+
+class ProfileDependencyManager;
+class ProfileKeyedService;
+
+// Base class for Factories that take a Profile object and return some service
+// on a one-to-one mapping. Each factory that derives from this class *must*
+// be a Singleton (only unit tests don't do that). See ThemeServiceFactory as
+// an example of how to derive from this class.
+//
+// We do this because services depend on each other and we need to control
+// shutdown/destruction order. In each derived classes' constructors, the
+// implementors must explicitly state which services are depended on.
+class ProfileKeyedServiceFactory : public ProfileKeyedBaseFactory {
+ public:
+ // A function that supplies the instance of a ProfileKeyedService for a given
+ // Profile. This is used primarily for testing, where we want to feed a
+ // specific mock into the PKSF system.
+ typedef ProfileKeyedService*
+ (*FactoryFunction)(content::BrowserContext* profile);
+
+ // Associates |factory| with |profile| so that |factory| is used to create
+ // the ProfileKeyedService when requested. |factory| can be NULL to signal
+ // that ProfileKeyedService should be NULL. Multiple calls to
+ // SetTestingFactory() are allowed; previous services will be shut down.
+ void SetTestingFactory(content::BrowserContext* profile,
+ FactoryFunction factory);
+
+ // Associates |factory| with |profile| and immediately returns the created
+ // ProfileKeyedService. Since the factory will be used immediately, it may
+ // not be NULL.
+ ProfileKeyedService* SetTestingFactoryAndUse(content::BrowserContext* profile,
+ FactoryFunction factory);
+
+ protected:
+ // ProfileKeyedServiceFactories must communicate with a
+ // ProfileDependencyManager. For all non-test code, write your subclass
+ // constructors like this:
+ //
+ // MyServiceFactory::MyServiceFactory()
+ // : ProfileKeyedServiceFactory(
+ // "MyService",
+ // ProfileDependencyManager::GetInstance())
+ // {}
+ ProfileKeyedServiceFactory(const char* name,
+ ProfileDependencyManager* manager);
+ virtual ~ProfileKeyedServiceFactory();
+
+ // Common implementation that maps |profile| to some service object. Deals
+ // with incognito profiles per subclass instructions with
+ // ServiceRedirectedInIncognito() and ServiceHasOwnInstanceInIncognito()
+ // through the GetProfileToUse() method on the base. If |create| is true,
+ // the service will be created using BuildServiceInstanceFor() if it doesn't
+ // already exist.
+ ProfileKeyedService* GetServiceForProfile(content::BrowserContext* profile,
+ bool create);
+
+ // Maps |profile| to |service| with debug checks to prevent duplication.
+ void Associate(content::BrowserContext* profile,
+ ProfileKeyedService* service);
+
+ // All subclasses of ProfileKeyedServiceFactory must return a
+ // ProfileKeyedService instead of just a ProfileKeyedBase.
+ virtual ProfileKeyedService* BuildServiceInstanceFor(
+ content::BrowserContext* profile) const = 0;
+
+ // A helper object actually listens for notifications about Profile
+ // destruction, calculates the order in which things are destroyed and then
+ // does a two pass shutdown.
+ //
+ // First, ProfileShutdown() is called on every ServiceFactory and will
+ // usually call ProfileKeyedService::Shutdown(), which gives each
+ // ProfileKeyedService a chance to remove dependencies on other services that
+ // it may be holding.
+ //
+ // Secondly, ProfileDestroyed() is called on every ServiceFactory and the
+ // default implementation removes it from |mapping_| and deletes the pointer.
+ virtual void ProfileShutdown(content::BrowserContext* profile) OVERRIDE;
+ virtual void ProfileDestroyed(content::BrowserContext* profile) OVERRIDE;
+
+ virtual void SetEmptyTestingFactory(
+ content::BrowserContext* profile) OVERRIDE;
+ virtual void CreateServiceNow(content::BrowserContext* profile) OVERRIDE;
+
+ private:
+ friend class ProfileDependencyManager;
+ friend class ProfileDependencyManagerUnittests;
+
+ typedef std::map<content::BrowserContext*, ProfileKeyedService*>
+ ProfileKeyedServices;
+ typedef std::map<content::BrowserContext*, FactoryFunction>
+ ProfileOverriddenFunctions;
+
+ // The mapping between a Profile and its service.
+ std::map<content::BrowserContext*, ProfileKeyedService*> mapping_;
+
+ // The mapping between a Profile and its overridden FactoryFunction.
+ std::map<content::BrowserContext*, FactoryFunction> factories_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProfileKeyedServiceFactory);
+};
+
+#endif // COMPONENTS_BROWSER_CONTEXT_KEYED_SERVICE_BROWSER_CONTEXT_KEYED_SERVICE_FACTORY_H_
diff --git a/components/browser_context_keyed_service/dependency_graph.cc b/components/browser_context_keyed_service/dependency_graph.cc
new file mode 100644
index 0000000..253f5dd
--- /dev/null
+++ b/components/browser_context_keyed_service/dependency_graph.cc
@@ -0,0 +1,166 @@
+// Copyright 2013 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/browser_context_keyed_service/dependency_graph.h"
+
+#include <algorithm>
+#include <deque>
+#include <iterator>
+
+DependencyGraph::DependencyGraph() {
+}
+
+DependencyGraph::~DependencyGraph() {
+}
+
+void DependencyGraph::AddNode(DependencyNode* node) {
+ all_nodes_.push_back(node);
+ construction_order_.clear();
+}
+
+void DependencyGraph::RemoveNode(DependencyNode* node) {
+ all_nodes_.erase(std::remove(all_nodes_.begin(),
+ all_nodes_.end(),
+ node),
+ all_nodes_.end());
+
+ // Remove all dependency edges that contain this node.
+ EdgeMap::iterator it = edges_.begin();
+ while (it != edges_.end()) {
+ EdgeMap::iterator temp = it;
+ ++it;
+
+ if (temp->first == node || temp->second == node)
+ edges_.erase(temp);
+ }
+
+ construction_order_.clear();
+}
+
+void DependencyGraph::AddEdge(DependencyNode* depended,
+ DependencyNode* dependee) {
+ edges_.insert(std::make_pair(depended, dependee));
+ construction_order_.clear();
+}
+
+bool DependencyGraph::GetConstructionOrder(
+ std::vector<DependencyNode*>* order) {
+ if (construction_order_.empty() && !BuildConstructionOrder())
+ return false;
+
+ *order = construction_order_;
+ return true;
+}
+
+bool DependencyGraph::GetDestructionOrder(
+ std::vector<DependencyNode*>* order) {
+ if (construction_order_.empty() && !BuildConstructionOrder())
+ return false;
+
+ *order = construction_order_;
+
+ // Destroy nodes in reverse order.
+ std::reverse(order->begin(), order->end());
+
+ return true;
+}
+
+bool DependencyGraph::BuildConstructionOrder() {
+ // Step 1: Build a set of nodes with no incoming edges.
+ std::deque<DependencyNode*> queue;
+ std::copy(all_nodes_.begin(),
+ all_nodes_.end(),
+ std::back_inserter(queue));
+
+ std::deque<DependencyNode*>::iterator queue_end = queue.end();
+ for (EdgeMap::const_iterator it = edges_.begin();
+ it != edges_.end(); ++it) {
+ queue_end = std::remove(queue.begin(), queue_end, it->second);
+ }
+ queue.erase(queue_end, queue.end());
+
+ // Step 2: Do the Kahn topological sort.
+ std::vector<DependencyNode*> output;
+ EdgeMap edges(edges_);
+ while (!queue.empty()) {
+ DependencyNode* node = queue.front();
+ queue.pop_front();
+ output.push_back(node);
+
+ std::pair<EdgeMap::iterator, EdgeMap::iterator> range =
+ edges.equal_range(node);
+ EdgeMap::iterator it = range.first;
+ while (it != range.second) {
+ DependencyNode* dest = it->second;
+ EdgeMap::iterator temp = it;
+ it++;
+ edges.erase(temp);
+
+ bool has_incoming_edges = false;
+ for (EdgeMap::iterator jt = edges.begin(); jt != edges.end(); ++jt) {
+ if (jt->second == dest) {
+ has_incoming_edges = true;
+ break;
+ }
+ }
+
+ if (!has_incoming_edges)
+ queue.push_back(dest);
+ }
+ }
+
+ if (!edges.empty()) {
+ // Dependency graph has a cycle.
+ return false;
+ }
+
+ construction_order_ = output;
+ return true;
+}
+
+std::string DependencyGraph::DumpAsGraphviz(
+ const std::string& toplevel_name,
+ const base::Callback<std::string(DependencyNode*)>&
+ node_name_callback) const {
+ std::string result("digraph {\n");
+
+ // Make a copy of all nodes.
+ std::deque<DependencyNode*> nodes;
+ std::copy(all_nodes_.begin(), all_nodes_.end(), std::back_inserter(nodes));
+
+ // State all dependencies and remove |second| so we don't generate an
+ // implicit dependency on the top level node.
+ std::deque<DependencyNode*>::iterator nodes_end(nodes.end());
+ result.append(" /* Dependencies */\n");
+ for (EdgeMap::const_iterator it = edges_.begin(); it != edges_.end(); ++it) {
+ result.append(" ");
+ result.append(node_name_callback.Run(it->second));
+ result.append(" -> ");
+ result.append(node_name_callback.Run(it->first));
+ result.append(";\n");
+
+ nodes_end = std::remove(nodes.begin(), nodes_end, it->second);
+ }
+ nodes.erase(nodes_end, nodes.end());
+
+ // Every node that doesn't depend on anything else will implicitly depend on
+ // the top level node.
+ result.append("\n /* Toplevel attachments */\n");
+ for (std::deque<DependencyNode*>::const_iterator it =
+ nodes.begin(); it != nodes.end(); ++it) {
+ result.append(" ");
+ result.append(node_name_callback.Run(*it));
+ result.append(" -> ");
+ result.append(toplevel_name);
+ result.append(";\n");
+ }
+
+ result.append("\n /* Toplevel node */\n");
+ result.append(" ");
+ result.append(toplevel_name);
+ result.append(" [shape=box];\n");
+
+ result.append("}\n");
+ return result;
+}
diff --git a/components/browser_context_keyed_service/dependency_graph.h b/components/browser_context_keyed_service/dependency_graph.h
new file mode 100644
index 0000000..0f5d66a
--- /dev/null
+++ b/components/browser_context_keyed_service/dependency_graph.h
@@ -0,0 +1,67 @@
+// Copyright 2013 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.
+
+#ifndef COMPONENTS_BROWSER_CONTEXT_KEYED_SERVICE_DEPENDENCY_GRAPH_H_
+#define COMPONENTS_BROWSER_CONTEXT_KEYED_SERVICE_DEPENDENCY_GRAPH_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+
+class DependencyNode;
+
+// Dynamic graph of dependencies between nodes.
+class DependencyGraph {
+ public:
+ DependencyGraph();
+ ~DependencyGraph();
+
+ // Adds/Removes a node from our list of live nodes. Removing will
+ // also remove live dependency links.
+ void AddNode(DependencyNode* node);
+ void RemoveNode(DependencyNode* node);
+
+ // Adds a dependency between two nodes.
+ void AddEdge(DependencyNode* depended, DependencyNode* dependee);
+
+ // Topologically sorts nodes to produce a safe construction order
+ // (all nodes after their dependees).
+ bool GetConstructionOrder(
+ std::vector<DependencyNode*>* order) WARN_UNUSED_RESULT;
+
+ // Topologically sorts nodes to produce a safe destruction order
+ // (all nodes before their dependees).
+ bool GetDestructionOrder(
+ std::vector<DependencyNode*>* order) WARN_UNUSED_RESULT;
+
+ // Returns representation of the dependency graph in graphviz format.
+ std::string DumpAsGraphviz(
+ const std::string& toplevel_name,
+ const base::Callback<std::string(DependencyNode*)>&
+ node_name_callback) const;
+
+ private:
+ typedef std::multimap<DependencyNode*, DependencyNode*> EdgeMap;
+
+ // Populates |construction_order_| with computed construction order.
+ // Returns true on success.
+ bool BuildConstructionOrder() WARN_UNUSED_RESULT;
+
+ // Keeps track of all live nodes (see AddNode, RemoveNode).
+ std::vector<DependencyNode*> all_nodes_;
+
+ // Keeps track of edges of the dependency graph.
+ EdgeMap edges_;
+
+ // Cached construction order (needs rebuild with BuildConstructionOrder
+ // when empty).
+ std::vector<DependencyNode*> construction_order_;
+
+ DISALLOW_COPY_AND_ASSIGN(DependencyGraph);
+};
+
+#endif // COMPONENTS_BROWSER_CONTEXT_KEYED_SERVICE_DEPENDENCY_GRAPH_H_
diff --git a/components/browser_context_keyed_service/dependency_graph_unittest.cc b/components/browser_context_keyed_service/dependency_graph_unittest.cc
new file mode 100644
index 0000000..541ada7
--- /dev/null
+++ b/components/browser_context_keyed_service/dependency_graph_unittest.cc
@@ -0,0 +1,161 @@
+// Copyright 2013 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/browser_context_keyed_service/dependency_graph.h"
+#include "components/browser_context_keyed_service/dependency_node.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class DependencyGraphTest : public testing::Test {
+};
+
+class DummyNode : public DependencyNode {
+ public:
+ explicit DummyNode(DependencyGraph* graph) : dependency_graph_(graph) {
+ dependency_graph_->AddNode(this);
+ }
+
+ ~DummyNode() {
+ dependency_graph_->RemoveNode(this);
+ }
+
+ private:
+ DependencyGraph* dependency_graph_;
+
+ DISALLOW_COPY_AND_ASSIGN(DummyNode);
+};
+
+// Tests that we can deal with a single component.
+TEST_F(DependencyGraphTest, SingleCase) {
+ DependencyGraph graph;
+ DummyNode node(&graph);
+
+ std::vector<DependencyNode*> construction_order;
+ EXPECT_TRUE(graph.GetConstructionOrder(&construction_order));
+ ASSERT_EQ(1U, construction_order.size());
+ EXPECT_EQ(&node, construction_order[0]);
+
+ std::vector<DependencyNode*> destruction_order;
+ EXPECT_TRUE(graph.GetDestructionOrder(&destruction_order));
+ ASSERT_EQ(1U, destruction_order.size());
+ EXPECT_EQ(&node, destruction_order[0]);
+}
+
+// Tests that we get a simple one component depends on the other case.
+TEST_F(DependencyGraphTest, SimpleDependency) {
+ DependencyGraph graph;
+ DummyNode parent(&graph);
+ DummyNode child(&graph);
+
+ graph.AddEdge(&parent, &child);
+
+ std::vector<DependencyNode*> construction_order;
+ EXPECT_TRUE(graph.GetConstructionOrder(&construction_order));
+ ASSERT_EQ(2U, construction_order.size());
+ EXPECT_EQ(&parent, construction_order[0]);
+ EXPECT_EQ(&child, construction_order[1]);
+
+ std::vector<DependencyNode*> destruction_order;
+ EXPECT_TRUE(graph.GetDestructionOrder(&destruction_order));
+ ASSERT_EQ(2U, destruction_order.size());
+ EXPECT_EQ(&child, destruction_order[0]);
+ EXPECT_EQ(&parent, destruction_order[1]);
+}
+
+// Tests two children, one parent.
+TEST_F(DependencyGraphTest, TwoChildrenOneParent) {
+ DependencyGraph graph;
+ DummyNode parent(&graph);
+ DummyNode child1(&graph);
+ DummyNode child2(&graph);
+
+ graph.AddEdge(&parent, &child1);
+ graph.AddEdge(&parent, &child2);
+
+ std::vector<DependencyNode*> construction_order;
+ EXPECT_TRUE(graph.GetConstructionOrder(&construction_order));
+ ASSERT_EQ(3U, construction_order.size());
+ EXPECT_EQ(&parent, construction_order[0]);
+ EXPECT_EQ(&child1, construction_order[1]);
+ EXPECT_EQ(&child2, construction_order[2]);
+
+ std::vector<DependencyNode*> destruction_order;
+ EXPECT_TRUE(graph.GetDestructionOrder(&destruction_order));
+ ASSERT_EQ(3U, destruction_order.size());
+ EXPECT_EQ(&child2, destruction_order[0]);
+ EXPECT_EQ(&child1, destruction_order[1]);
+ EXPECT_EQ(&parent, destruction_order[2]);
+}
+
+// Tests an M configuration.
+TEST_F(DependencyGraphTest, MConfiguration) {
+ DependencyGraph graph;
+
+ DummyNode parent1(&graph);
+ DummyNode parent2(&graph);
+
+ DummyNode child_of_1(&graph);
+ graph.AddEdge(&parent1, &child_of_1);
+
+ DummyNode child_of_12(&graph);
+ graph.AddEdge(&parent1, &child_of_12);
+ graph.AddEdge(&parent2, &child_of_12);
+
+ DummyNode child_of_2(&graph);
+ graph.AddEdge(&parent2, &child_of_2);
+
+ std::vector<DependencyNode*> construction_order;
+ EXPECT_TRUE(graph.GetConstructionOrder(&construction_order));
+ ASSERT_EQ(5U, construction_order.size());
+ EXPECT_EQ(&parent1, construction_order[0]);
+ EXPECT_EQ(&parent2, construction_order[1]);
+ EXPECT_EQ(&child_of_1, construction_order[2]);
+ EXPECT_EQ(&child_of_12, construction_order[3]);
+ EXPECT_EQ(&child_of_2, construction_order[4]);
+
+ std::vector<DependencyNode*> destruction_order;
+ EXPECT_TRUE(graph.GetDestructionOrder(&destruction_order));
+ ASSERT_EQ(5U, destruction_order.size());
+ EXPECT_EQ(&child_of_2, destruction_order[0]);
+ EXPECT_EQ(&child_of_12, destruction_order[1]);
+ EXPECT_EQ(&child_of_1, destruction_order[2]);
+ EXPECT_EQ(&parent2, destruction_order[3]);
+ EXPECT_EQ(&parent1, destruction_order[4]);
+}
+
+// Tests that it can deal with a simple diamond.
+TEST_F(DependencyGraphTest, DiamondConfiguration) {
+ DependencyGraph graph;
+
+ DummyNode parent(&graph);
+
+ DummyNode middle1(&graph);
+ graph.AddEdge(&parent, &middle1);
+
+ DummyNode middle2(&graph);
+ graph.AddEdge(&parent, &middle2);
+
+ DummyNode bottom(&graph);
+ graph.AddEdge(&middle1, &bottom);
+ graph.AddEdge(&middle2, &bottom);
+
+ std::vector<DependencyNode*> construction_order;
+ EXPECT_TRUE(graph.GetConstructionOrder(&construction_order));
+ ASSERT_EQ(4U, construction_order.size());
+ EXPECT_EQ(&parent, construction_order[0]);
+ EXPECT_EQ(&middle1, construction_order[1]);
+ EXPECT_EQ(&middle2, construction_order[2]);
+ EXPECT_EQ(&bottom, construction_order[3]);
+
+ std::vector<DependencyNode*> destruction_order;
+ EXPECT_TRUE(graph.GetDestructionOrder(&destruction_order));
+ ASSERT_EQ(4U, destruction_order.size());
+ EXPECT_EQ(&bottom, destruction_order[0]);
+ EXPECT_EQ(&middle2, destruction_order[1]);
+ EXPECT_EQ(&middle1, destruction_order[2]);
+ EXPECT_EQ(&parent, destruction_order[3]);
+}
+
+} // namespace
diff --git a/components/browser_context_keyed_service/dependency_node.h b/components/browser_context_keyed_service/dependency_node.h
new file mode 100644
index 0000000..d870b67
--- /dev/null
+++ b/components/browser_context_keyed_service/dependency_node.h
@@ -0,0 +1,16 @@
+// Copyright 2013 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.
+
+#ifndef COMPONENTS_BROWSER_CONTEXT_KEYED_SERVICE_DEPENDENCY_NODE_H_
+#define COMPONENTS_BROWSER_CONTEXT_KEYED_SERVICE_DEPENDENCY_NODE_H_
+
+// Base class representing a node in a DependencyGraph.
+class DependencyNode {
+ protected:
+ // This is intended to be used by the subclasses, not directly.
+ DependencyNode() {}
+ ~DependencyNode() {}
+};
+
+#endif // COMPONENTS_BROWSER_CONTEXT_KEYED_SERVICE_DEPENDENCY_NODE_H_
diff --git a/components/browser_context_keyed_service/refcounted_browser_context_keyed_service.cc b/components/browser_context_keyed_service/refcounted_browser_context_keyed_service.cc
new file mode 100644
index 0000000..da92a6d
--- /dev/null
+++ b/components/browser_context_keyed_service/refcounted_browser_context_keyed_service.cc
@@ -0,0 +1,34 @@
+// 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/browser_context_keyed_service/refcounted_browser_context_keyed_service.h"
+
+namespace impl {
+
+// static
+void RefcountedProfileKeyedServiceTraits::Destruct(
+ const RefcountedProfileKeyedService* obj) {
+ if (obj->requires_destruction_on_thread_ &&
+ !content::BrowserThread::CurrentlyOn(obj->thread_id_)) {
+ content::BrowserThread::DeleteSoon(obj->thread_id_, FROM_HERE, obj);
+ } else {
+ delete obj;
+ }
+}
+
+} // namespace impl
+
+RefcountedProfileKeyedService::RefcountedProfileKeyedService()
+ : requires_destruction_on_thread_(false),
+ thread_id_(content::BrowserThread::UI) {
+}
+
+RefcountedProfileKeyedService::RefcountedProfileKeyedService(
+ const content::BrowserThread::ID thread_id)
+ : requires_destruction_on_thread_(true),
+ thread_id_(thread_id) {
+}
+
+RefcountedProfileKeyedService::~RefcountedProfileKeyedService() {}
+
diff --git a/components/browser_context_keyed_service/refcounted_browser_context_keyed_service.h b/components/browser_context_keyed_service/refcounted_browser_context_keyed_service.h
new file mode 100644
index 0000000..1d19f72
--- /dev/null
+++ b/components/browser_context_keyed_service/refcounted_browser_context_keyed_service.h
@@ -0,0 +1,70 @@
+// 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.
+
+#ifndef COMPONENTS_BROWSER_CONTEXT_KEYED_SERVICE_REFCOUNTED_BROWSER_CONTEXT_KEYED_SERVICE_H_
+#define COMPONENTS_BROWSER_CONTEXT_KEYED_SERVICE_REFCOUNTED_BROWSER_CONTEXT_KEYED_SERVICE_H_
+
+#include "base/memory/ref_counted.h"
+#include "base/sequenced_task_runner_helpers.h"
+#include "content/public/browser/browser_thread.h"
+
+class RefcountedProfileKeyedService;
+
+namespace impl {
+
+struct RefcountedProfileKeyedServiceTraits {
+ static void Destruct(const RefcountedProfileKeyedService* obj);
+};
+
+} // namespace impl
+
+// Base class for refcounted objects that hang off the Profile.
+//
+// The two pass shutdown described in ProfileKeyedService works a bit
+// differently because there could be outstanding references on other
+// threads. ShutdownOnUIThread() will be called on the UI thread, and then the
+// destructor will run when the last reference is dropped, which may or may not
+// be after the corresponding Profile has been destroyed.
+//
+// Optionally, if you initialize your service with the constructor that takes a
+// thread ID, your service will be deleted on that thread. We can't use
+// content::DeleteOnThread<> directly because RefcountedProfileKeyedService
+// must be one type that RefcountedProfileKeyedServiceFactory can use.
+class RefcountedProfileKeyedService
+ : public base::RefCountedThreadSafe<
+ RefcountedProfileKeyedService,
+ impl::RefcountedProfileKeyedServiceTraits> {
+ public:
+ // Unlike ProfileKeyedService, ShutdownOnUI is not optional. You must do
+ // something to drop references during the first pass Shutdown() because this
+ // is the only point where you are guaranteed that something is running on
+ // the UI thread. The PKSF framework will ensure that this is only called on
+ // the UI thread; you do not need to check for that yourself.
+ virtual void ShutdownOnUIThread() = 0;
+
+ protected:
+ // If your service does not need to be deleted on a specific thread, use the
+ // default constructor.
+ RefcountedProfileKeyedService();
+
+ // If you need your service to be deleted on a specific thread (for example,
+ // you're converting a service that used content::DeleteOnThread<IO>), then
+ // use this constructor with the ID of the thread.
+ explicit RefcountedProfileKeyedService(
+ const content::BrowserThread::ID thread_id);
+
+ // The second pass destruction can happen anywhere unless you specify which
+ // thread this service must be destroyed on by using the second constructor.
+ virtual ~RefcountedProfileKeyedService();
+
+ private:
+ friend struct impl::RefcountedProfileKeyedServiceTraits;
+ friend class base::DeleteHelper<RefcountedProfileKeyedService>;
+
+ // Do we have to delete this object on a specific thread?
+ bool requires_destruction_on_thread_;
+ content::BrowserThread::ID thread_id_;
+};
+
+#endif // COMPONENTS_BROWSER_CONTEXT_KEYED_SERVICE_REFCOUNTED_BROWSER_CONTEXT_KEYED_SERVICE_H_
diff --git a/components/browser_context_keyed_service/refcounted_browser_context_keyed_service_factory.cc b/components/browser_context_keyed_service/refcounted_browser_context_keyed_service_factory.cc
new file mode 100644
index 0000000..560180d
--- /dev/null
+++ b/components/browser_context_keyed_service/refcounted_browser_context_keyed_service_factory.cc
@@ -0,0 +1,127 @@
+// 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/browser_context_keyed_service/refcounted_browser_context_keyed_service_factory.h"
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "content/public/browser/browser_context.h"
+#include "components/browser_context_keyed_service/browser_context_keyed_service.h"
+#include "components/browser_context_keyed_service/refcounted_browser_context_keyed_service.h"
+
+void RefcountedProfileKeyedServiceFactory::SetTestingFactory(
+ content::BrowserContext* profile,
+ FactoryFunction factory) {
+ // Destroying the profile may cause us to lose data about whether |profile|
+ // has our preferences registered on it (since the profile object itself
+ // isn't dead). See if we need to readd it once we've gone through normal
+ // destruction.
+ bool add_profile = ArePreferencesSetOn(profile);
+
+ // We have to go through the shutdown and destroy mechanisms because there
+ // are unit tests that create a service on a profile and then change the
+ // testing service mid-test.
+ ProfileShutdown(profile);
+ ProfileDestroyed(profile);
+
+ if (add_profile)
+ MarkPreferencesSetOn(profile);
+
+ factories_[profile] = factory;
+}
+
+scoped_refptr<RefcountedProfileKeyedService>
+RefcountedProfileKeyedServiceFactory::SetTestingFactoryAndUse(
+ content::BrowserContext* profile,
+ FactoryFunction factory) {
+ DCHECK(factory);
+ SetTestingFactory(profile, factory);
+ return GetServiceForProfile(profile, true);
+}
+
+RefcountedProfileKeyedServiceFactory::RefcountedProfileKeyedServiceFactory(
+ const char* name,
+ ProfileDependencyManager* manager)
+ : ProfileKeyedBaseFactory(name, manager) {
+}
+
+RefcountedProfileKeyedServiceFactory::~RefcountedProfileKeyedServiceFactory() {
+ DCHECK(mapping_.empty());
+}
+
+scoped_refptr<RefcountedProfileKeyedService>
+RefcountedProfileKeyedServiceFactory::GetServiceForProfile(
+ content::BrowserContext* profile,
+ bool create) {
+ profile = GetBrowserContextToUse(profile);
+ if (!profile)
+ return NULL;
+
+ // NOTE: If you modify any of the logic below, make sure to update the
+ // non-refcounted version in profile_keyed_service_factory.cc!
+ RefCountedStorage::const_iterator it = mapping_.find(profile);
+ if (it != mapping_.end())
+ return it->second;
+
+ // Object not found.
+ if (!create)
+ return NULL; // And we're forbidden from creating one.
+
+ // Create new object.
+ // Check to see if we have a per-Profile testing factory that we should use
+ // instead of default behavior.
+ scoped_refptr<RefcountedProfileKeyedService> service;
+ ProfileOverriddenFunctions::const_iterator jt = factories_.find(profile);
+ if (jt != factories_.end()) {
+ if (jt->second) {
+ if (!profile->IsOffTheRecord())
+ RegisterUserPrefsOnProfile(profile);
+ service = jt->second(profile);
+ }
+ } else {
+ service = BuildServiceInstanceFor(profile);
+ }
+
+ Associate(profile, service);
+ return service;
+}
+
+void RefcountedProfileKeyedServiceFactory::Associate(
+ content::BrowserContext* profile,
+ const scoped_refptr<RefcountedProfileKeyedService>& service) {
+ DCHECK(!ContainsKey(mapping_, profile));
+ mapping_.insert(std::make_pair(profile, service));
+}
+
+void RefcountedProfileKeyedServiceFactory::ProfileShutdown(
+ content::BrowserContext* profile) {
+ RefCountedStorage::iterator it = mapping_.find(profile);
+ if (it != mapping_.end() && it->second)
+ it->second->ShutdownOnUIThread();
+}
+
+void RefcountedProfileKeyedServiceFactory::ProfileDestroyed(
+ content::BrowserContext* profile) {
+ // We "merely" drop our reference to the service. Hopefully this will cause
+ // the service to be destroyed. If not, oh well.
+ mapping_.erase(profile);
+
+ // 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).
+ factories_.erase(profile);
+
+ ProfileKeyedBaseFactory::ProfileDestroyed(profile);
+}
+
+void RefcountedProfileKeyedServiceFactory::SetEmptyTestingFactory(
+ content::BrowserContext* profile) {
+ SetTestingFactory(profile, NULL);
+}
+
+void RefcountedProfileKeyedServiceFactory::CreateServiceNow(
+ content::BrowserContext* profile) {
+ GetServiceForProfile(profile, true);
+}
diff --git a/components/browser_context_keyed_service/refcounted_browser_context_keyed_service_factory.h b/components/browser_context_keyed_service/refcounted_browser_context_keyed_service_factory.h
new file mode 100644
index 0000000..195c657
--- /dev/null
+++ b/components/browser_context_keyed_service/refcounted_browser_context_keyed_service_factory.h
@@ -0,0 +1,88 @@
+// 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.
+
+#ifndef COMPONENTS_BROWSER_CONTEXT_KEYED_SERVICE_REFCOUNTED_BROWSER_CONTEXT_KEYED_SERVICE_FACTORY_H_
+#define COMPONENTS_BROWSER_CONTEXT_KEYED_SERVICE_REFCOUNTED_BROWSER_CONTEXT_KEYED_SERVICE_FACTORY_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "components/browser_context_keyed_service/browser_context_keyed_service_factory.h"
+#include "components/browser_context_keyed_service/refcounted_browser_context_keyed_service.h"
+
+class Profile;
+class RefcountedProfileKeyedService;
+
+// A specialized ProfileKeyedServiceFactory that manages a
+// RefcountedThreadSafe<>.
+//
+// While the factory returns RefcountedThreadSafe<>s, the factory itself is a
+// base::NotThreadSafe. Only call methods on this object on the UI thread.
+//
+// Implementers of RefcountedProfileKeyedService should note that we guarantee
+// that ShutdownOnUIThread() is called on the UI thread, but actual object
+// destruction can happen anywhere.
+class RefcountedProfileKeyedServiceFactory : public ProfileKeyedBaseFactory {
+ public:
+ // A function that supplies the instance of a ProfileKeyedService for a given
+ // Profile. This is used primarily for testing, where we want to feed a
+ // specific mock into the PKSF system.
+ typedef scoped_refptr<RefcountedProfileKeyedService>
+ (*FactoryFunction)(content::BrowserContext* profile);
+
+ // Associates |factory| with |profile| so that |factory| is used to create
+ // the ProfileKeyedService when requested. |factory| can be NULL to signal
+ // that ProfileKeyedService should be NULL. Multiple calls to
+ // SetTestingFactory() are allowed; previous services will be shut down.
+ void SetTestingFactory(content::BrowserContext* profile,
+ FactoryFunction factory);
+
+ // Associates |factory| with |profile| and immediately returns the created
+ // ProfileKeyedService. Since the factory will be used immediately, it may
+ // not be NULL.
+ scoped_refptr<RefcountedProfileKeyedService> SetTestingFactoryAndUse(
+ content::BrowserContext* profile,
+ FactoryFunction factory);
+
+ protected:
+ RefcountedProfileKeyedServiceFactory(const char* name,
+ ProfileDependencyManager* manager);
+ virtual ~RefcountedProfileKeyedServiceFactory();
+
+ scoped_refptr<RefcountedProfileKeyedService> GetServiceForProfile(
+ content::BrowserContext* profile,
+ bool create);
+
+ // Maps |profile| to |service| with debug checks to prevent duplication.
+ void Associate(content::BrowserContext* profile,
+ const scoped_refptr<RefcountedProfileKeyedService>& service);
+
+ // All subclasses of RefcountedProfileKeyedServiceFactory must return a
+ // RefcountedProfileKeyedService instead of just a ProfileKeyedBase.
+ virtual scoped_refptr<RefcountedProfileKeyedService> BuildServiceInstanceFor(
+ content::BrowserContext* profile) const = 0;
+
+ virtual void ProfileShutdown(content::BrowserContext* profile) OVERRIDE;
+ virtual void ProfileDestroyed(content::BrowserContext* profile) OVERRIDE;
+ virtual void SetEmptyTestingFactory(
+ content::BrowserContext* profile) OVERRIDE;
+ virtual void CreateServiceNow(content::BrowserContext* profile) OVERRIDE;
+
+ private:
+ typedef std::map<content::BrowserContext*,
+ scoped_refptr<RefcountedProfileKeyedService> >
+ RefCountedStorage;
+ typedef std::map<content::BrowserContext*,
+ FactoryFunction> ProfileOverriddenFunctions;
+
+ // The mapping between a Profile and its refcounted service.
+ RefCountedStorage mapping_;
+
+ // The mapping between a Profile and its overridden FactoryFunction.
+ ProfileOverriddenFunctions factories_;
+
+ DISALLOW_COPY_AND_ASSIGN(RefcountedProfileKeyedServiceFactory);
+};
+
+#endif // COMPONENTS_BROWSER_CONTEXT_KEYED_SERVICE_REFCOUNTED_BROWSER_CONTEXT_KEYED_SERVICE_FACTORY_H_
diff --git a/components/components.gyp b/components/components.gyp
index 7243700..c361739 100644
--- a/components/components.gyp
+++ b/components/components.gyp
@@ -12,6 +12,7 @@
'includes': [
'autofill.gypi',
'auto_login_parser.gypi',
+ 'browser_context_keyed_service.gypi',
'components_tests.gypi',
'navigation_interception.gypi',
'sessions.gypi',
diff --git a/components/components_tests.gypi b/components/components_tests.gypi
index 8fa321b..2de146b 100644
--- a/components/components_tests.gypi
+++ b/components/components_tests.gypi
@@ -11,6 +11,8 @@
'type': '<(gtest_target_type)',
'sources': [
'auto_login_parser/auto_login_parser_unittest.cc',
+ 'browser_context_keyed_service/browser_context_dependency_manager_unittest.cc',
+ 'browser_context_keyed_service/dependency_graph_unittest.cc',
'navigation_interception/intercept_navigation_resource_throttle_unittest.cc',
'sessions/serialized_navigation_entry_unittest.cc',
'test/run_all_unittests.cc',
@@ -30,6 +32,9 @@
# Dependencies of auto_login_parser
'auto_login_parser',
+ # Dependencies of browser_context_keyed_service
+ 'browser_context_keyed_service',
+
# Dependencies of encryptor
'encryptor',