OLD | NEW |
(Empty) | |
| 1 # Tab Helpers |
| 2 |
| 3 The `content/` layer of Chromium has a class called `WebContents`, which is one |
| 4 of the most basic building blocks of all of Chromium. This document describes |
| 5 how `WebContents`es are used to build tabs in browser windows. |
| 6 |
| 7 [TOC] |
| 8 |
| 9 ## Introduction |
| 10 |
| 11 What is a "tab helper"? It is a `WebContentsObserver` owned by the `WebContents` |
| 12 itself. Let's break that down. |
| 13 |
| 14 ## `WebContentsObserver` |
| 15 |
| 16 `WebContentsObserver` is a |
| 17 [simple interface](https://code.google.com/p/chromium/codesearch#chromium/src/co
ntent/public/browser/web_contents_observer.h&q=webcontentsobserver) |
| 18 that allows an object to observe events in the life of a `WebContents`. As an |
| 19 example, if we look at the `TabStripModel`, there are times when it need to |
| 20 watch out for WebContents being deleted. So it creates a |
| 21 [DeletionObserver](https://code.google.com/p/chromium/codesearch#chromium/src/ch
rome/browser/ui/tabs/tab_strip_model.cc&q=DeletionObserver). |
| 22 The `DeletionObserver` overrides `WebContentsDestroyed()`, and when a |
| 23 `WebContents` gets destroyed, the callback is called and the `DeletionObserver` |
| 24 processes the message. Note that `DeletionObserver` is not owned by the |
| 25 `WebContents`. It is owned indirectly by the `TabStripModel`. |
| 26 |
| 27 ## `SupportsUserData` and `WebContentsUserData` |
| 28 |
| 29 There is a mechanism used in Chromium called |
| 30 [`SupportsUserData`](https://code.google.com/p/chromium/codesearch#chromium/src/
base/supports_user_data.h&q=SupportsUserData) |
| 31 that allows attaching of arbitrary objects to an object. The mechanism is |
| 32 simple: host objects derive from `SupportsUserData`, and owned objects derive |
| 33 from `SupportsUserData::Data`. There are three calls to attach and detach the |
| 34 data. |
| 35 |
| 36 `WebContents` derives from `SupportsUserData`, so that mechanism works for |
| 37 attaching objects to a `WebContents`, but the `SupportsUserData` mechanism is a |
| 38 bit low-level. A higher level abstraction is |
| 39 [`WebContentsUserData`](https://code.google.com/p/chromium/codesearch#chromium/s
rc/content/public/browser/web_contents_user_data.h&q=WebContentsUserData), |
| 40 which 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 |
| 45 Let's combine `WebContentsObserver` and `WebContentsUserData` together, to log |
| 46 whenever the title of a tab changes. |
| 47 |
| 48 ``` |
| 49 class TitleLoggerTabHelper |
| 50 : public content::WebContentsObserver, |
| 51 public content::WebContentsUserData<TitleLoggerTabHelper> { |
| 52 public: |
| 53 ~TitleLoggerTabHelper() override; |
| 54 |
| 55 // content::WebContentsObserver |
| 56 void TitleWasSet(NavigationEntry* entry, bool explicit_set) override { |
| 57 LOG(INFO) << "Title: " << entry->GetTitle(); |
| 58 } |
| 59 |
| 60 private: |
| 61 explicit TitleLoggerTabHelper(content::WebContents* web_contents); |
| 62 friend class content::WebContentsUserData<TitleLoggerTabHelper>; |
| 63 |
| 64 DISALLOW_COPY_AND_ASSIGN(TitleLoggerTabHelper); |
| 65 }; |
| 66 |
| 67 DEFINE_WEB_CONTENTS_USER_DATA_KEY(TitleLoggerTabHelper); |
| 68 ``` |
| 69 |
| 70 We want each tab to have this `WebContentsObserver` attached to it, so that it |
| 71 will properly handle the events it's looking for, and when the tab goes away, |
| 72 then this tab helper will go away too. |
| 73 |
| 74 But how do you hook in to browser tab creation? How can we attach this tab |
| 75 helper to the `WebContents`es that are used for the browser tabs? |
| 76 |
| 77 ## AttachTabHelpers |
| 78 |
| 79 There is a function called |
| 80 [`AttachTabHelpers()`](https://code.google.com/p/chromium/codesearch#chromium/sr
c/chrome/browser/ui/tab_helpers.cc&q=AttachTabHelpers). |
| 81 Whenever a `WebContents` is created for use as a browser tab, |
| 82 `AttachTabHelpers()` is called. Every tab helper from around Chromium, |
| 83 from ContentSettings to Favicons to History to Prefs, all take this opportunity |
| 84 to hook into those `WebContents` used as tabs. |
| 85 |
| 86 If you are writing a feature that needs to deal with browser tabs, this is where |
| 87 you go. Create a tab helper, and add it (in alphabetical order, please!) to |
| 88 `AttachTabHelpers()`. Note, though, that you are _never_ allowed to call |
| 89 `AttachTabHelpers()` yourself. `AttachTabHelpers()` is only for `WebContents` |
| 90 that are in browser tabs, and all of those code paths are already written. |
| 91 |
| 92 ## Reusing tab helpers with non-browser tab `WebContents`es |
| 93 |
| 94 Sometimes it's useful to re-use tab helpers for `WebContents`es that aren't |
| 95 browser tabs. For example, the Chrome Apps code wants to be able to print, and |
| 96 wants to use the printing code that browser tabs use. So in |
| 97 [`ChromeAppDelegate::InitWebContents()`](https://code.google.com/p/chromium/code
search#chromium/src/chrome/browser/ui/apps/chrome_app_delegate.cc&q=ChromeAppDel
egate::InitWebContents) |
| 98 we see that whenever the Apps code creates a new `WebContents`, it attaches a |
| 99 carefully-chosen subset of tab helpers, including two printing ones. |
| 100 |
| 101 You can do that too. If you are creating a `WebContents`, make a very deliberate |
| 102 decision about which tab helpers you need. Chances are, you don't need them all; |
| 103 you probably only need a handful. In fact, most tab helpers assume they are |
| 104 attached to browser tabs, so only add the bare minimum. |
| 105 |
| 106 ## Not every `WebContents` has every tab helper |
| 107 |
| 108 The other consequence of this design is that you can't make the assumption that |
| 109 an arbitrary `WebContents` will have an arbitrary tab helper. The |
| 110 `WebContents`es used as browser tabs likely will have most tab helpers (though |
| 111 not necessarily all of them!) but a `WebContents` only has a tab helper if it is |
| 112 installed on it. |
| 113 |
| 114 The deeper (false and dangerous) assumption is that every `WebContents` is a |
| 115 browser tab. Do not assume that either! |
| 116 |
| 117 If your code handles `WebContents`es, be aware of their source. It is extremely |
| 118 rare to have to be able to handle arbitrary `WebContents`es. Know where they |
| 119 come from and what tab helpers are on them, and you'll be fine. |
OLD | NEW |