| # Tab Helpers |
| |
| The `content/` layer of Chromium has a class called `WebContents`, which is one |
| of the most basic building blocks of all of Chromium. This document describes |
| how `WebContents`es are used to build tabs in browser windows. |
| |
| [TOC] |
| |
| ## Introduction |
| |
| What is a "tab helper"? It is a `WebContentsObserver` owned by the `WebContents` |
| itself. Let's break that down. |
| |
| ## `WebContentsObserver` |
| |
| `WebContentsObserver` is a |
| [simple interface](https://code.google.com/p/chromium/codesearch#chromium/src/content/public/browser/web_contents_observer.h&q=webcontentsobserver) |
| that allows an object to observe events in the life of a `WebContents`. As an |
| example, if we look at the `TabStripModel`, there are times when it need to |
| watch out for WebContents being deleted. So it creates a |
| [DeletionObserver](https://code.google.com/p/chromium/codesearch#chromium/src/chrome/browser/ui/tabs/tab_strip_model.cc&q=DeletionObserver). |
| The `DeletionObserver` overrides `WebContentsDestroyed()`, and when a |
| `WebContents` gets destroyed, the callback is called and the `DeletionObserver` |
| processes the message. Note that `DeletionObserver` is not owned by the |
| `WebContents`. It is owned indirectly by the `TabStripModel`. |
| |
| ## `SupportsUserData` and `WebContentsUserData` |
| |
| There is a mechanism used in Chromium called |
| [`SupportsUserData`](https://code.google.com/p/chromium/codesearch#chromium/src/base/supports_user_data.h&q=SupportsUserData) |
| that allows attaching of arbitrary objects to an object. The mechanism is |
| simple: host objects derive from `SupportsUserData`, and owned objects derive |
| from `SupportsUserData::Data`. There are three calls to attach and detach the |
| data. |
| |
| `WebContents` derives from `SupportsUserData`, so that mechanism works for |
| attaching objects to a `WebContents`, but the `SupportsUserData` mechanism is a |
| bit low-level. A higher level abstraction is |
| [`WebContentsUserData`](https://code.google.com/p/chromium/codesearch#chromium/src/content/public/browser/web_contents_user_data.h&q=WebContentsUserData), |
| which is easy to derive from and has easy-to-use functionality in |
| `CreateForWebContents()` and `FromWebContents()`. |
| |
| ## Adding a feature to a browser tab |
| |
| Let's combine `WebContentsObserver` and `WebContentsUserData` together, to log |
| whenever the title of a tab changes. |
| |
| ``` |
| class TitleLoggerTabHelper |
| : public content::WebContentsObserver, |
| public content::WebContentsUserData<TitleLoggerTabHelper> { |
| public: |
| ~TitleLoggerTabHelper() override; |
| |
| // content::WebContentsObserver |
| void TitleWasSet(NavigationEntry* entry) override { |
| LOG(INFO) << "Title: " << entry->GetTitle(); |
| } |
| |
| private: |
| explicit TitleLoggerTabHelper(content::WebContents* web_contents); |
| friend class content::WebContentsUserData<TitleLoggerTabHelper>; |
| |
| DISALLOW_COPY_AND_ASSIGN(TitleLoggerTabHelper); |
| }; |
| |
| DEFINE_WEB_CONTENTS_USER_DATA_KEY(TitleLoggerTabHelper); |
| ``` |
| |
| We want each tab to have this `WebContentsObserver` attached to it, so that it |
| will properly handle the events it's looking for, and when the tab goes away, |
| then this tab helper will go away too. |
| |
| But how do you hook in to browser tab creation? How can we attach this tab |
| helper to the `WebContents`es that are used for the browser tabs? |
| |
| ## AttachTabHelpers |
| |
| There is a function called |
| [`AttachTabHelpers()`](https://code.google.com/p/chromium/codesearch#chromium/src/chrome/browser/ui/tab_helpers.cc&q=AttachTabHelpers). |
| Whenever a `WebContents` is created for use as a browser tab, |
| `AttachTabHelpers()` is called. Every tab helper from around Chromium, |
| from ContentSettings to Favicons to History to Prefs, all take this opportunity |
| to hook into those `WebContents` used as tabs. |
| |
| If you are writing a feature that needs to deal with browser tabs, this is where |
| you go. Create a tab helper, and add it (in alphabetical order, please!) to |
| `AttachTabHelpers()`. Note, though, that you are _never_ allowed to call |
| `AttachTabHelpers()` yourself. `AttachTabHelpers()` is only for `WebContents` |
| that are in browser tabs, and all of those code paths are already written. |
| |
| ## Reusing tab helpers with non-browser tab `WebContents`es |
| |
| Sometimes it's useful to re-use tab helpers for `WebContents`es that aren't |
| browser tabs. For example, the Chrome Apps code wants to be able to print, and |
| wants to use the printing code that browser tabs use. So in |
| [`ChromeAppDelegate::InitWebContents()`](https://code.google.com/p/chromium/codesearch#chromium/src/chrome/browser/ui/apps/chrome_app_delegate.cc&q=ChromeAppDelegate::InitWebContents) |
| we see that whenever the Apps code creates a new `WebContents`, it attaches a |
| carefully-chosen subset of tab helpers, including two printing ones. |
| |
| You can do that too. If you are creating a `WebContents`, make a very deliberate |
| decision about which tab helpers you need. Chances are, you don't need them all; |
| you probably only need a handful. In fact, most tab helpers assume they are |
| attached to browser tabs, so only add the bare minimum. |
| |
| ## Not every `WebContents` has every tab helper |
| |
| The other consequence of this design is that you can't make the assumption that |
| an arbitrary `WebContents` will have an arbitrary tab helper. The |
| `WebContents`es used as browser tabs likely will have most tab helpers (though |
| not necessarily all of them!) but a `WebContents` only has a tab helper if it is |
| installed on it. |
| |
| The deeper (false and dangerous) assumption is that every `WebContents` is a |
| browser tab. Do not assume that either! |
| |
| If your code handles `WebContents`es, be aware of their source. It is extremely |
| rare to have to be able to handle arbitrary `WebContents`es. Know where they |
| come from and what tab helpers are on them, and you'll be fine. |