For Developers‎ > ‎Design Documents‎ > ‎

Multiple Users

Overview

A user's Profile holds a set of state bound to and created by using Chrome. For example, a Profile contains a user's explicitly set preferences about zoom levels, themes, home page, and so on. It also contains state created implicitly by usage of Chrome -- the user's browsing history, cookies, and window placement, to name a few. Until recently, most of the code in Chrome assumed that there was a single Default profile, which could be accessed globally through the browser process. The multiple profiles feature allows the storage of multiple distinct sets of browser state on a single machine. Because of this change, code can no longer count on being able to easily access Profile data through a global mechanism -- instead, it must be designed to access the correct Profile for each situation.

Profile Persistent State Storage: Where it lives on Disk

Before multi-profiles, Profile state was stored in a "Default" directory under the user data directory. With multi-profiles, each profile has a separate directory in the user data dir -- profile_1, profile_2, and so on. Local State holds a map of Profile names to Profile directories; by naming the Profile folders with a <tag>+<number> scheme, we avoid any problems with character sets, whitespace, and so on, that might emerge if we directly named profile directories with user names.

Profile Creation

Profiles are created through the ProfileManager, which owns all Profiles and is responsible for their destruction. The first Profile is created during startup, in BrowserMain, which will load the last used Profile from Local State, if it exists, or use the "Default" directory for new profiles. Profile creation takes place over several steps:
  1. The Profile storage directory is selected -- either as the last used Profile from Local State, the initial "Default" directory, or a multi-profile directory ("Profile 1", "Profile 2", etc.).
  2. The ProfileManager checks to see if any Profile with this path exists in its set of loaded profiles.
  3. If no Profile with this path exists, the Profile is created, along with any required services. These services may also be initialized. In browser startup, this happens synchronously. After startup, the parts of this step that access the File Thread to create the necessary new directories on disk will happen asynchronously. In the future, the order of service startup will be determined by the service's place in the ProfileDependencyManager -- please see ProfileRefactoring for more details.
  4. The ProfileIOData object is created and passed to the IO Thread.
  5. The ProfileManager registers the Profile in its set of loaded profiles.
Profile Deletion

For now, Profiles are deleted when the ProfileManager itself is deleted, when Chrome is closed. The long-term plan is to allow Profiles to be deleted when all browser windows using them have been closed (except in the case of Background Apps -- and with appropriate changes for the Mac side, which doesn't bind the closing of Chrome with the closing of browser windows). If a user wants to delete all Profile data along with the Profile, this deletion is scheduled to take place at the time of browser closing. The last Profile on a machine may not be deleted. As with Profile creation, the order of Profile deletion shall be determined by the ProfileDependencyManager for all future Profile-related services -- this can be tricky, as services often closely depend on one another, and bad shutdown orders have been the source of many Chrome crashes.

Accessing Profile Data

Each Browser object is created with a pointer to a Profile; in most cases, code which needs data from a Profile will ask the Browser with which it is associated.

Profile Data on other Threads

Profiles may only be accessed on the UI thread. Profiles are not thread-safe! The ProfileIOData object can be used to shuttle data from a Profile to the IO thread.

The Global Singleton ProfileManager

The ProfileManager is attached to g_browser_process, and can be accessed with g_browser_process->profile_manager(). You usually don't want to use the ProfileManager directly, although you may need to access the ProfileManager to get a set of all loaded profiles. You should no longer use it to get a "Default" profile, because this concept will in the future no longer exist.


Adding new Services to Profiles and Profile Refactoring

One of the ways that Profiles have been used is as the host for every possible Profile-related service. Much of the Profile creation and destruction is consumed with service initialization and tear-down, and much of the Profile code is simply getters for Services. To make Profiles more lightweight and modular, and to bring order to the creation and shutdown of services, we've started a large-scale refactoring of the relationship of Profiles to services. 

In the new work, services will no longer live in Profiles, but will be created and managed by singleton factory objects that carry vectors of service objects, each one keyed to a specific Profile. When a client needs to use a service, it will hand a Profile pointer to a ProfileKeyedService factory, which will hand back the appropriate service object. This means that Profiles can be easily tailored to only support specific services, without #ifdefs. Another feature of the ProfileKeyedService mechanism is the ProfileDependencyManager, which keeps a graph of shutdown and creation dependencies for all services. Any new Profile-related service needs to follow this model.
Comments