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_;
};

ProfileKeyedServiceFactory

Now that we have implemented FooService, we need to derive from ProfileKeyedServiceFactory (PKSF).

ProfileKeyedServiceFactory derives from BrowserContextKeyedServiceFactory (BCKSF) where most of the following is implemented. PKSF adds the service selection logic per profile type. In the rest of the document, we will refer to PKSF as it is the main type of factory to be constructed. Differences will be noted in the BrowserContextKeyedServiceFactory section below.

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

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

 private:
  FooServiceFactory();
  ~FooServiceFactory() override;

  // BrowserContextKeyedServiceFactory:
  std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
      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, PKSF provides these other knobs for controlling behavior:

Examples of PKSF factory initialization (constructor definition):

// Initialization of a PKSF factory with default Profile selection behavior.
ChromeDefaultKeyedServiceFactory()
    : ProfileKeyedServiceFactory("DefaultKeyedService") {}

// Initialization of a PKSF factor with customized Profile selection behavior
// for different profile types with different behaviors per type.
ChromeCustomizedKeyedServiceFactory()
    : ProfileKeyedServiceFactory("CustomizedKeyedService",
        ProfileSelections::Builder()
          .WithRegular(ProfileSelection::kOwnInstance))
          .WithGuest(ProfileSelection::kOffTheRecordOnly)
          .WithAshInternals(ProfileSelection::kNone)
          .Build()) {}

BrowserContextKeyedServiceFactory

In some cases, creating a PKSF is not possible, PKSF can only be created under chrome/ directory. Other factories/services will have to derive from BCKSF, e.g implementing a factory under components/, weblayer/ or extensions/ (extensions mainly use a templated version BrowserContextKeyedAPIFactory<T>). There are some differences to note when using a BCKSF:

WARNING: handling all profile types correctly in BCKSF is very difficult as there are a lot of edge cases (OTR and non-OTR system and guest profiles, Ash-internal profiles, ...). It is recommended to use PKSF instead if possible to handle this complexity.

// Initialization of a BKCSF factory.
NonChromeDefaultKeyedServiceFactory::
NonChromeDefaultKeyedServiceFactory()
      : BrowserContextKeyedServiceFactory(
            "DefaultBrowserContextKeyedServiceFactory",
            BrowserContextDependencyManager::GetInstance()) {}

// Overriding which `context` the service will be constructed for.
content::BrowserContext*
    NonChromeDefaultKeyedServiceFactory::GetBrowserContextToUse(
        content::BrowserContext* context) const override {
          // Create the service for all profiles (contexts), including off the
          // record profiles.
      return context;
}

Java keyed services

On Android, C++ keyed services often have a corresponding Java object. In such cases, the C++ part should own the Java one. You can find more details here.

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 KeyedServiceBaseFactory that defines the most general dependency stuff while BrowserContextKeyedServiceFactory is a specialization for the content layer. ProfileKeyedServiceFactory is another specialization that takes care of the Profile selection for the services. There is a second RefcountedProfileKeyedServiceFactory that gives slightly different semantics and storage for RefCountedThreadSafe objects. Equivalent RefcountedBrowserContextKeyedServiceFactory exists.

Two-phase Shutdown

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

  1. Every PKSF will first have its Shutdown() method called. Use this method to drop weak references to the Profile or other service objects.
  2. Every PKSF 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. All constructors of PKSF register and unregister themselves with the BrowserContextDependencyManager automatically. 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()
    : ProfileKeyedServiceFactory("AlphaService") {}

BetaServiceFactory::BetaServiceFactory()
    : ProfileKeyedServiceFactory("BetaService") {
  DependsOn(AlphaServiceFactory::GetInstance());
}

GammaServiceFactory::GammaServiceFactory()
    : ProfileKeyedServiceFactory("GammaService") {
  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.