| Index: docs/tab_helpers.md
|
| diff --git a/docs/tab_helpers.md b/docs/tab_helpers.md
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..c68218b3370c54ada9a48a83db7310d4e5c24d40
|
| --- /dev/null
|
| +++ b/docs/tab_helpers.md
|
| @@ -0,0 +1,119 @@
|
| +# 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, bool explicit_set) 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.
|
|
|