Browser Components is the project of continuing to modularize the Chrome codebase. A large project to extract the content module out of the src/chrome/ directory is complete. Despite the efforts of this project, the src/chrome/ directory remains by far the largest inter-dependent block of code in the codebase, so continuing to tease the next largest modules out of that directory is the next step.
The Chrome project has grown a lot over the last few years in both size and complexity. The number of people contributing to the project has increased a lot, the size of the codebase has increased, and we have added many new target platforms and configurations that were not envisioned in the start. It has also become harder for individuals to understand the whole of the architecture, let alone the whole of the codebase.
The Browser Components project is introducing and enforcing modularity, with these objectives in mind:
A: Extracting bits of src/chrome/ into components that are separate dynamic libraries in the component build, that have a separate unit test target, and that have no cyclic dependencies.
Q: What do you mean, components that have no cyclic dependencies?
A: They know nothing about their embedder (which initially will typically be src/chrome/). If they need information or services from their embedder they either receive them at initialization time, or retrieve them at runtime via an abstract delegate interface defined by the component and implemented by the embedder.
Q: What if one component wants to depend on another?
A: It can. In this case think of one component as the embedder of the other. This precludes the possibility of dependency cycles between two (or any N) components.
Q: Do we want to componentize UI?
A: Not in the sense described above. We do want to eliminate cyclic dependencies on UI; code under src/chrome/browser/ui/ may depend on src/chrome/browser/ and any component, but not vice-versa. Also, code in src/chrome/browser/ui/ should not depend on port-specific code, e.g. src/chrome/browser/ui/cocoa/. The UI code should be a separate dynamic library in the components build. However we do not anticipate ever breaking all of UI into separate, small, reusable components.
Q: What if I’m evaluating some code for componentization and I see that it has dependencies that are currently implemented by the rest of src/chrome/?
A: Usually, that means we first need to componentize those dependencies. There are two exceptions:
Q: Where do components live?
A: If they depend on src/content/ and layers below that (which is typical) they live in a subdirectory of src/components/, e.g. a component named foo would be contained in the directory src/components/foo/. For components that depend only on lower layers than that, we evaluate each time. One example so far is src/base/prefs/, a component that depends only on src/base/.
Q: Where do components’ delegate interfaces live?
A: Their declaration lives in the same directory as the component. Their embedder-specific implementation lives in each embedder, wherever the embedder chooses.
Q: Do components provide an API for the embedder to use?
A: Generally speaking, it is fine for the embedder to use the component as a concrete set of C++ classes, in which case the “API” is simply whatever the “entry point” classes are, where the component receives pointers to implementations of its delegate interfaces from its embedder. In certain cases, we may decide to introduce a fully-abstract API for the embedder to use, that hides implementation details of the component, as we did for src/content/. In certain other cases, we might categorize the component’s concrete C++ classes into an internal set and a publicly-usable set. In either of these cases, the API or publicly-usable set of classes should reside in a subdirectory of the component’s directory named public (e.g. src/components/mycomponent/public/). If a public directory is present, the component’s delegate interface definitions should live within that directory.
Q: Is there any further standardization of the structure within a component’s directory?
A: Some components that depend on the src/content/ layer may have bits of code intended to run in different processes of the embedder, e.g. browser or renderer. The convention for these is to place the code in sub-directories with names matching those already used in src/chrome/, so for a component xyz you might have src/components/xyz/browser/ and src/components/xyz/renderer/ directories.
Q: Is this like Microsoft COM or Mozilla XPCOM?
A: No. It is a non-goal to create binary reusable components similar to Microsoft’s COM or Mozilla’s XPCOM mechanisms. We expect the components we are introducing, and all users of the components, to live in a single codebase and be built together, so we do not need complicated registration and discovery mechanisms, or to limit ourselves exclusively to APIs that are pure vtables.
For a step-by-step guide and cookbook-style recipes on how to componentize, see the Browser Components Cookbook.
The content module (
Smaller examples that illustrate how to componentize can be seen in the work done to implement the following issues:
We have also extended DEPS to allow generation of a visual graph of components and their dependencies. This is helpful when trying to find appropriate “leaves” in the dependency graph under src/chrome/, that make for good next things to componentize.
We have surveyed the available automatic refactoring tools for C++; the brief conclusions are:
We may create a tool to move classes into a different namespace, since this is another frequent and sometimes time-consuming operation. This would be a best-effort kind of tool, that could deal reasonably well with uniquely-named classes.
Because the Chrome codebase changes so rapidly, we know from prior experience that all of the above needs to be done using rapid-fire incremental changes. The ability to get quick code reviews from multiple owners and, in many cases, the ability to TBR the trivial parts of changes with wide impact, will be key.
Full-time members of the Browser Components team are Jói Sigurðsson <firstname.lastname@example.org>, Erik Wright <email@example.com>, Cait Phillips <firstname.lastname@example.org> and Kai Wang <email@example.com>.
The project started in mid-Q3 2012. Our expectation is that we can put at least a man-year or two of fast and furious work into this project before reaching diminishing returns. We will re-evaluate quarterly, involving the Chrome TLs in the discussion. We will also attempt to set quarterly goals that fulfill shorter-term needs while driving us forward on our longer-term goals (e.g. for Q4 2012 we have a goal to help the Android WebView folks reuse functionality from Chrome without depending on anything under src/chrome/).
While a full risk analysis has not yet been done, there are a couple of obvious risks: