blob: 945c012714f3f2ac755c2e1e0e374d09f192eb74 [file] [log] [blame] [view]
avia41f7af2015-08-31 19:46:581# Tab Helpers
2
3The `content/` layer of Chromium has a class called `WebContents`, which is one
4of the most basic building blocks of all of Chromium. This document describes
5how `WebContents`es are used to build tabs in browser windows.
6
7[TOC]
8
9## Introduction
10
11What is a "tab helper"? It is a `WebContentsObserver` owned by the `WebContents`
12itself. Let's break that down.
13
Elly Fong-Jones8ea0fc762021-01-06 19:14:2314## WebContentsObserver
avia41f7af2015-08-31 19:46:5815
16`WebContentsObserver` is a
Elly Fong-Jones8ea0fc762021-01-06 19:14:2317[simple interface](https://source.chromium.org/chromium/chromium/src/+/HEAD:content/public/browser/web_contents_observer.h)
avia41f7af2015-08-31 19:46:5818that allows an object to observe events in the life of a `WebContents`. As an
19example, if we look at the `TabStripModel`, there are times when it need to
20watch out for WebContents being deleted. So it creates a
Elly Fong-Jones8ea0fc762021-01-06 19:14:2321[TabStripModel::WebContentsData](https://source.chromium.org/chromium/chromium/src/+/HEAD:chrome/browser/ui/tabs/tab_strip_model.cc).
Suman Nelson Kancherla1d0cc352018-12-06 20:26:2822That object overrides `WebContentsDestroyed()`, and when a
23`WebContents` gets destroyed, the callback is called and the object
24processes the message. Note that `TabStripModel::WebContentsData` object is not owned by the
avia41f7af2015-08-31 19:46:5825`WebContents`. It is owned indirectly by the `TabStripModel`.
26
Elly Fong-Jones8ea0fc762021-01-06 19:14:2327## SupportsUserData and WebContentsUserData
avia41f7af2015-08-31 19:46:5828
29There is a mechanism used in Chromium called
Josip Sokcevicba144412020-09-09 20:57:0530[`SupportsUserData`](https://source.chromium.org/chromium/chromium/src/+/HEAD:base/supports_user_data.h)
avia41f7af2015-08-31 19:46:5831that allows attaching of arbitrary objects to an object. The mechanism is
32simple: host objects derive from `SupportsUserData`, and owned objects derive
33from `SupportsUserData::Data`. There are three calls to attach and detach the
34data.
35
36`WebContents` derives from `SupportsUserData`, so that mechanism works for
37attaching objects to a `WebContents`, but the `SupportsUserData` mechanism is a
38bit low-level. A higher level abstraction is
Josip Sokcevicba144412020-09-09 20:57:0539[`WebContentsUserData`](https://source.chromium.org/chromium/chromium/src/+/HEAD:content/public/browser/web_contents_user_data.h),
avia41f7af2015-08-31 19:46:5840which is easy to derive from and has easy-to-use functionality in
41`CreateForWebContents()` and `FromWebContents()`.
42
43## Adding a feature to a browser tab
44
45Let's combine `WebContentsObserver` and `WebContentsUserData` together, to log
46whenever the title of a tab changes.
47
48```
49class TitleLoggerTabHelper
50 : public content::WebContentsObserver,
51 public content::WebContentsUserData<TitleLoggerTabHelper> {
52 public:
Johann16cf06fd2020-09-14 22:55:5553 TitleLoggerTabHelper(const TitleLoggerTabHelper&) = delete;
54 TitleLoggerTabHelper& operator=(const TitleLoggerTabHelper&) = delete;
avia41f7af2015-08-31 19:46:5855 ~TitleLoggerTabHelper() override;
56
57 // content::WebContentsObserver
Avi Drissman93002212017-09-27 03:20:5258 void TitleWasSet(NavigationEntry* entry) override {
avia41f7af2015-08-31 19:46:5859 LOG(INFO) << "Title: " << entry->GetTitle();
60 }
61
62 private:
63 explicit TitleLoggerTabHelper(content::WebContents* web_contents);
64 friend class content::WebContentsUserData<TitleLoggerTabHelper>;
avia41f7af2015-08-31 19:46:5865};
avia41f7af2015-08-31 19:46:5866```
67
68We want each tab to have this `WebContentsObserver` attached to it, so that it
69will properly handle the events it's looking for, and when the tab goes away,
70then this tab helper will go away too.
71
72But how do you hook in to browser tab creation? How can we attach this tab
73helper to the `WebContents`es that are used for the browser tabs?
74
75## AttachTabHelpers
76
77There is a function called
Josip Sokcevicba144412020-09-09 20:57:0578[`AttachTabHelpers()`](https://source.chromium.org/chromium/chromium/src/+/HEAD:chrome/browser/ui/tab_helpers.cc;).
avia41f7af2015-08-31 19:46:5879Whenever a `WebContents` is created for use as a browser tab,
80`AttachTabHelpers()` is called. Every tab helper from around Chromium,
81from ContentSettings to Favicons to History to Prefs, all take this opportunity
82to hook into those `WebContents` used as tabs.
83
84If you are writing a feature that needs to deal with browser tabs, this is where
85you go. Create a tab helper, and add it (in alphabetical order, please!) to
86`AttachTabHelpers()`. Note, though, that you are _never_ allowed to call
87`AttachTabHelpers()` yourself. `AttachTabHelpers()` is only for `WebContents`
88that are in browser tabs, and all of those code paths are already written.
89
Elly Fong-Jones8ea0fc762021-01-06 19:14:2390## Reusing tab helpers with non-browser tab WebContentses
avia41f7af2015-08-31 19:46:5891
92Sometimes it's useful to re-use tab helpers for `WebContents`es that aren't
93browser tabs. For example, the Chrome Apps code wants to be able to print, and
94wants to use the printing code that browser tabs use. So in
Josip Sokcevicba144412020-09-09 20:57:0595[`ChromeAppDelegate::InitWebContents()`](https://source.chromium.org/chromium/chromium/src/+/HEAD:chrome/browser/ui/apps/chrome_app_delegate.cc)
avia41f7af2015-08-31 19:46:5896we see that whenever the Apps code creates a new `WebContents`, it attaches a
97carefully-chosen subset of tab helpers, including two printing ones.
98
99You can do that too. If you are creating a `WebContents`, make a very deliberate
100decision about which tab helpers you need. Chances are, you don't need them all;
101you probably only need a handful. In fact, most tab helpers assume they are
102attached to browser tabs, so only add the bare minimum.
103
Elly Fong-Jones8ea0fc762021-01-06 19:14:23104## Not every WebContents has every tab helper
avia41f7af2015-08-31 19:46:58105
106The other consequence of this design is that you can't make the assumption that
107an arbitrary `WebContents` will have an arbitrary tab helper. The
108`WebContents`es used as browser tabs likely will have most tab helpers (though
109not necessarily all of them!) but a `WebContents` only has a tab helper if it is
110installed on it.
111
112The deeper (false and dangerous) assumption is that every `WebContents` is a
113browser tab. Do not assume that either!
114
115If your code handles `WebContents`es, be aware of their source. It is extremely
116rare to have to be able to handle arbitrary `WebContents`es. Know where they
117come from and what tab helpers are on them, and you'll be fine.