summaryrefslogtreecommitdiffstats
path: root/chromium/docs/website/site/developers/design-documents/profile-architecture/index.md
blob: b1709c56ddda381b0491ea0b9527ca544c98fb36 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
---
breadcrumbs:
- - /developers
  - For Developers
- - /developers/design-documents
  - Design Documents
page_name: profile-architecture
title: Profile Architecture
---

*This page details an ongoing design refactoring, started in **January 2012**.*

Note: As of Jun 2013, this doc needs updating. The classes have been renamed
(s/ProfileKeyed/BrowserContextKeyed/) and moved into
components/browser_context_keyed_service.

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. In the three years of the Chromium Project, Profile
became the join point for every feature, leading to things like
Profile::GetInstantPromoCounter() or Profile::GetHostContentSettingsMap(). As of
this writing there are 58 pure virtual methods that start with "Get" in Profile.

Profile should be a minimal reference, a sort of handle object that doesn't own
the world.

[TOC]

## Design Goals

*   **We must be able to move to the new architecture piece-wise.** One
            service and feature at a time. We can not stop the world and convert
            everything in one operation. As of this writing, we've moved 19
            services out of Profile.
    *   We should only make small modifications at the callsite where a
                Profile is used to get the service in question.
*   **We must fix Profile shutdown.** When we started and only had a few
            objects hanging off of Profile, manual ordering was acceptable for
            destruction. Now we have over seventy five components and we know
            that our manual destruction ordering is incorrect as written today.
            We can not rely on manual ordering when we have so many components.
*   **We must allow features to be compiled in and out.** Now that we
            have chromium variants that don't contain all the features in a
            standard Windows/Mac/Linux Google Chrome build, we need a way to
            allow these variants to compile without #ifdefing profile.h and
            profile_impl.h into a mess. These variants also have their own
            services that they'd like to provide. (Letting chromium variants add
            their own services also touches on why we can't rely on manual
            ordering in Profile shutdown.)
    *   Stretch goal: Separate features go in their own .a/.so files to
                further minimize our ridiculous linking time.

## BrowserContextKeyedServiceFactory

### The Old Way: Profile interface and ProfileImpl

In the previous design, services were fetched through an accessor on Profile:

> class ProfileImpl {

> public:

> virtual FooService\* GetFooService();

> private:

> scoped_ptr<FooService> foo_service_;

> };

In the previous system, Profile was an interface with mostly pure virtual
accessors. There were separate versions of Profile for Normal, Incognito and
Testing profiles.

In this world, the Profile was the center of all activity. The profile owned all
of its service and handed them out. Profile destruction was according to
whatever order the services were listed in ProfileImpl. There wasn't a way for
another variant to add its own services (or leave out ones it didn't need)
without modifying the Profile interface.

### The New Way: BrowserContextKeyedServiceFactory

Instead of having the Profile own FooService, we have a dedicated singleton
FooServiceFactory, like this minimal one:

> class FooServiceFactory : public BrowserContextKeyedServiceFactory {
> public:
> static FooService\* GetForProfile(Profile\* profile);
> static FooServiceFactory\* GetInstance();
> private:
> friend struct DefaultSingletonTraits<FooServiceFactory>;
> FooServiceFactory();
> virtual ~FooServiceFactory();
> // BrowserContextKeyedServiceFactory:
> virtual BrowserContextKeyedService\* BuildServiceInstanceFor(
> content::BrowserContext\* context) const OVERRIDE;
> };

We have a generalized BrowserContextKeyedServiceFactory which performs most of
the work of associating a profile with an object provided by your
BuildServiceInstanceFor() method. The BrowserContextKeyedServiceFactory provides
an interface for you 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:

*   A static GetInstance() method that refers to your Factory as a
            Singleton.
*   A constructor that associates this BrowserContextKeyedServiceFactory
            with the ProfileDependencyManager singleton, and makes DependsOn()
            declarations.
*   A GetForProfile() method that wraps
            BrowserContextKeyedServiceFactory, casting the result back to
            whatever type you need to return.
*   A BuildServiceInstanceFor() method which is called once by the
            framework for each |profile|, which must return a proper instance of
            your service.

In addition, BrowserContextKeyedServiceFactory provides these other knobs for
how you can control behavior:

*   RegisterUserPrefs() is called once per Profile during initialization
            and is where you can place any user pref registration.
*   By default, BCKSF return NULL when given an Incognito profile.
    *   If you override ServiceRedirectedInIncognito() to return true,
                it will return the associated normal Profile's service.
    *   If you override ServiceHasOwnInstanceInIncognito() to return
                true, it will create a new service for the incognito profile.
*   By default, BCKSF will lazily create your service. If you override
            ServiceIsCreatedWithProfile() to return true, your service will be
            created alongside the profile.
*   BCKSF gives you multiple ways to control behavior during unit tests.
            See the header for more details.
*   BCKSF gives you a way to augment and tweak the shutdown and
            deallocation behavior.

### 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.

### A Brief Interlude About Complexity

So the above, from an implementation standpoint is significantly more complex
than what came before it. Is all this really worth it?

**Yes.**

We absolutely have to address the interdependency of services. As it stands
today, we do not shut down profiles after they are no longer needed in
multiprofile mode because our crash rate when shutting down a profile is too
high to ship to users. We have about 75 components that plug into the profile
lifecycle and their dependency graph is complex enough that our naive manual
ordering can not handle the complexity. All of the overrideable behavior above
exists because it was implemented per service, ad hoc and copy pasted.

We likewise need to make it easy for other chromium variants to add their own
features/compile features out of their build.

### Dependency Management Overview

With that in mind, let's look at how dependency management works. There is a
single ProfileDependencyManager singleton, which is what is alerted to Profile
creation and destruction. A PKSF will register and unregister itself with the
ProfileDependencyManager. The job of the ProfileDependencyManager 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(ProfileDependencyManager::GetInstance()) {
> }
> BetaServiceFactory::BetaServiceFactory()
> : BrowserContextKeyedServiceFactory(ProfileDependencyManager::GetInstance()) {
> DependsOn(AlphaServiceFactory::GetInstance());

}

> GammaServiceFactory::GammaServiceFactory()
> : BrowserContextKeyedServiceFactory(ProfileDependencyManager::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, ProfileDependencyManager takes the stated dependency edges,
performs a Kahn topological sort, and uses that in CreateProfileServices() and
DestroyProfileServices().

## The Five Minute Tutorial of How to Convert Your Code

1.  **Make Your Existing FooService derive from
            BrowserContextKeyedService.**
2.  **If possible, make your FooService no longer refcounted. Most of
            the refcounted objects that hang off of Profile appear to be that
            way because they aren't using
            [base::bind/WeakPtrFactory](http://www.chromium.org/developers/design-documents/threading#Runnable_methods)
            instead of needing to own data on multiple threads. (In case you
            have a real reason for being a RefCountedThreadSafe, such as being
            accessed on multiple threads, derive your factory from**
            RefcountedBrowserContextKeyedServiceFactory **and everything should
            just work.)**
3.  Build a simple FooServiceFactory derived from
            BrowserContextKeyedServiceFactory. Your FooServiceFactory will be
            the main access point consumers will ask for FooService.
            BrowserContextKeyedServiceFactory gives you a bunch of virtual
            methods that control behavior.
    1.  BrowserContextKeyedService\*
                BrowserContextKeyedServiceFactory::BuildServiceInstanceFor(content::BrowserContext\*
                context) is the only required method. Given a BrowserContext
                handle, return a valid FooService.
    2.  You can control the incognito behavior with
                ServiceRedirectedInIncognito() and
                ServiceHasOwnInstanceInIncognito().
4.  Add your service to the
            EnsureBrowserContextKeyedServiceFactoriesBuilt() list in
            chrome_browser_main_extra_parts_profiles.cc**.**
5.  Understand shutdown behavior. For historical reasons, we have a two
            phase deletion process:
    1.  Every BrowserContextKeyedService will first have its Shutdown()
                method called. Use this method to drop weak references to the
                Profile or other service objects.
    2.  Every BrowserContextKeyedService 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.
6.  Change each instance of "profile_->GetFooService()" to
            "FooServiceFactory::GetForProfile(profile_)".

If you need an example of what the above looks like, try looking at these
patches:

*   [r100516](http://src.chromium.org/viewvc/chrome?view=rev&revision=100516):
            A simple example, adding a new ProfileKeyedService. This shows off a
            minimal ServiceFactory subclass.
*   [r104806](http://src.chromium.org/viewvc/chrome?view=rev&revision=104806):
            plugin_prefs_factory.h gives an example of how to deal with things
            that are (and have to stay) refcounted. This patch also shows off
            how to move your preferences into your ProfileKeyedServiceFactory.

## Debugging Tips

### Using the dependency visualizer

Chrome has a built in method to dump the profile dependency graph to a file in
[GraphViz](http://www.graphviz.org/) 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):

[<img alt="Graph as of Aug 15, 2012"
src="/developers/design-documents/profile-architecture/graph5.png" height=40
width=400>](/developers/design-documents/profile-architecture/graph5.png)

### Crashes at Shutdown

If you get a stack that looks like this:

> ProfileDependencyManager::AssertProfileWasntDestroyed()

> ProfileKeyedServiceFactory::GetServiceForProfile()
> MyServiceFactory::GetForProfile()
> ... \[Probably a bunch of frames\] ...
> OtherService::~OtherService()
> ProfileKeyedServiceFactory::ProfileDestroyed()
> ProfileDependencyManager::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.