the Chromium logo

The Chromium Projects

Profile Architecture

Chromium has lots of features that hook into a Profile, a bundle of data about the current user and the current chrome session that can span multiple browser windows. When Chromium first started, the profile had only a few moving parts: the cookie jar, the history database, the bookmark database, and things to do with user preferences.

As more and more features were added, the Profile class grew bigger and bigger. In the early stages of the Chromium Project, it owned all of the profile's state, leading to things like Profile::GetInstantPromoCounter() or Profile::GetHostContentSettingsMap(). At one point, there were 58 getters in Profile.

We also struggled with startup and teardown issues during this period. When creating a new Profile object, we had to create all the services in the right order. For instance, the Sync service has a dependency on the history, bookmarks, and extensions modules. If we weren't careful, we could initialize Sync too early or destroy it too late.

The design has evolved a lot since then. In 2012, we transitioned to the new model described on this page. Rather than owning everything directly, Profile is now a sort of handle object with minimal state. To prevent startup and teardown issues, we use a DependencyManager and a two-phase shutdown model.

Design Goals

How to Add a New Service

KeyedService

A KeyedService is basically a plain object, with the following differences:

Let's say we're working on a new feature called Foo, and we want to store some state in the profile. Typically, we'll implement this as a FooService class that has a separate instance for each profile. It is generally a good idea to disable copy and assignment for this class.

class FooService : public KeyedService {
 public:
  explicit FooService(PrefService* profile_prefs, BarService* bar_service);
  virtual ~FooService() = default;

  FooService(const FooService&) = delete;
  FooService& operator=(const FooService&) = delete;

 private:
  PrefService* profile_prefs_;
  BarService* bar_service_;
};

BrowserContextKeyedServiceFactory

Now that we have implemented FooService, we need to derive BrowserContextKeyedServiceFactory (BCKSF).

Instead of having the Profile own FooService, we have a dedicated singleton FooServiceFactory. This class takes care of creating and destroying FooService. Here is a minimal example:

class FooServiceFactory : public BrowserContextKeyedServiceFactory {
 public:
  static FooService* GetForProfile(Profile* profile);
  static FooServiceFactory* GetInstance();

 private:
  FooServiceFactory();
  virtual ~FooServiceFactory();

  // BrowserContextKeyedServiceFactory:
  virtual BrowserContextKeyedService* BuildServiceInstanceFor(
      content::BrowserContext* context) const override;
};

We have a factory which performs most of the work of associating a profile with an object provided by the BuildServiceInstanceFor() method. The BrowserContextKeyedServiceFactory provides an interface to override while managing the lifetime of your Service object in response to Profile lifetime events and making sure your service is shut down before services it depends on.

An absolutely minimal factory will supply the following methods:

In addition, BCKSF provides these other knobs for controlling behavior:

Use the Service

Instead of doing profile.GetFooService() (the old way), you should access FooService via FooServiceFactory::GetForProfile(profile).

A Few Types of Factories

Not all objects have the same lifecycle and memory management. The previous paragraph was a major simplification; there is a base class BrowserContextKeyedBaseFactory that defines the most general dependency stuff while BrowserContextKeyedServiceFactory is a specialization that deals with normal objects. There is a second RefcountedBrowserContextKeyedServiceFactory that gives slightly different semantics and storage for RefCountedThreadSafe objects.

Two-phase Shutdown

Shutdown behavior is a bit subtle. For historical reasons, we have a two-phase deletion process:

  1. Every BCKSF will first have its Shutdown() method called. Use this method to drop weak references to the Profile or other service objects.
  2. Every BCKSF is deleted and its destructor is run. Minimal work should be done here. Attempts to call any *ServiceFactory::GetForProfile() will cause an assertion in debug mode.

The Shutdown() steps lets us drop weak references and observers before any KeyedService is destroyed. This is useful for "cycles" , e.g. FooService observes BarService and BarService observes FooService. Single-phase shutdown cannot accommodate these cases.

Dependency Management Overview

With that in mind, let's look at how dependency management works. There is a single BrowserContextDependencyManager singleton, which is what is alerted to Profile creation and destruction. A BCKSF will register and unregister itself with the BrowserContextDependencyManager. The job of the BrowserContextDependencyManager is to make sure that individual services are created and destroyed in a safe ordering.

Consider the case of these three service factories:

AlphaServiceFactory::AlphaServiceFactory()
    : BrowserContextKeyedServiceFactory(
          "AlphaService",
          BrowserContextDependencyManager::GetInstance()) {}

BetaServiceFactory::BetaServiceFactory()
    : BrowserContextKeyedServiceFactory(
          "BetaService",
          BrowserContextDependencyManager::GetInstance()) {
  DependsOn(AlphaServiceFactory::GetInstance());
}

GammaServiceFactory::GammaServiceFactory()
    : BrowserContextKeyedServiceFactory(
          "GammaService",
          BrowserContextDependencyManager::GetInstance()) {
  DependsOn(BetaServiceFactory::GetInstance());
}

The explicitly stated dependencies in this simplified graph mean that the only valid creation order for services is [Alpha, Beta, Gamma] and the destruction order is [Gamma, Beta, Alpha]. The above is all you, as a user of the framework, have to do to specify dependencies.

Behind the scenes, BrowserContextDependencyManager takes the stated dependency edges, performs a Kahn topological sort, and uses that in CreateBrowserContextServices() and DestroyBrowserContextServices().

Debugging Tips

Using the dependency visualizer

Chrome has a built in method to dump the profile dependency graph to a file in GraphViz format. When you run chrome with the command line flag --dump-browser-context-graph, chrome will write the dependency information to your /path/to/profile/browser-context-dependencies.dot file. You can then convert this text file with dot, which is part of GraphViz:

dot -Tpng /path/to/profile/browser-context-dependencies.dot > png-file.png

This will give you a visual graph like this (generated January 23rd, 2012, click through for full size):

Graph as of Aug 15, 2012

Crashes at Shutdown

If you get a stack that looks like this:

BrowserContextDependencyManager::AssertProfileWasntDestroyed()
BrowserContextKeyedServiceFactory::GetServiceForProfile()
MyServiceFactory::GetForProfile()
... [Probably a bunch of frames] ...
OtherService::~OtherService()
BrowserContextKeyedServiceFactory::BrowserContextDestroyed()
BrowserContextDependencyManager::DestroyProfileServices()
ProfileImpl::~ProfileImpl()

The problem is that OtherService is improperly depending on MyService. The framework asserts if you try to use a Shutdown()ed component.