• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1<h1>Extending DevTools</h1>
2
3<h2 id="overview">Overview</h2>
4
5<p>A DevTools extension adds functionality to the Chrome DevTools. It can add new
6UI panels and sidebars, interact with the inspected page, get information about
7network requests, and more. DevTools extensions have access to an additional set
8of DevTools-specific extension APIs:</p>
9
10<ul>
11<li><code>$(ref:devtools.inspectedWindow)</code></li>
12<li><code>$(ref:devtools.network)</code></a></li>
13<li><code>$(ref:devtools.panels)</code></a></li>
14</ul>
15
16<p>A DevTools extension is structured like any other extension: it can have a
17background page, content scripts, and other items. In addition, each DevTools
18extension has a DevTools page, which has access to the DevTools APIs.</p>
19
20<p><img src="{{static}}/images/devtools-extension.png" alt="Architecture diagram
21showing DevTools page communicating with the inspected window and the background
22page. The background page is shown communicating with the content scripts and
23accessing extension APIs. The DevTools page has access to the DevTools APIs, for
24example, creating panels."/></p>
25
26<h2 id="devtools-page">The DevTools Page</h2>
27
28<p>An instance of the extension's DevTools page is created each time a DevTools
29window opens. The DevTools page exists for the lifetime of the DevTools window.
30The DevTools page has access to the DevTools APIs and a limited set of extension
31APIs. Specifically, the DevTools page can:</p>
32
33<ul>
34
35<li>Create and interact with panels using the <code>$(ref:devtools.panels)</code>
36APIs.</li>
37
38<li>Get information about the inspected window and evaluate code in the inspected
39window using the <code>$(ref:devtools.inspectedWindow)</code> APIs.</li>
40
41<li>Get information about network requests using the <code>$(ref:devtools.network)</code>
42APIs.</li>
43
44</ul>
45
46<p>The DevTools page cannot use most of the extensions APIs directly.  It has
47access to the same subset of the <code>$(ref:extension)</code>
48and <code>$(ref:runtime)</code>
49APIs that a content script has access to. Like a content script, a DevTools page
50can communicate with the background page using <a href="messaging.html">Message Passing</a>.
51For an example, see <a href="#injecting">Injecting a Content Script</a>.</p>
52
53<h2 id="creating">Creating a DevTools Extension</h2>
54
55<p>To create a DevTools page for your extension, add the <code>devtools_page</code>
56field in the extension manifest:</p>
57
58<pre>
59{
60  "name": ...
61  "version": "1.0",
62  "minimum_chrome_version": "10.0",
63  "devtools_page": "devtools.html",
64  ...
65}
66</pre>
67
68<p>An instance of the <code>devtools_page</code> specified in your extension's
69manifest is created for every DevTools window opened. The page may add other
70extension pages as panels and sidebars to the DevTools window using the
71<code>$(ref:devtools.panels)</code> API.</p>
72
73<p class="note">The <code>devtools_page</code> field must point to an HTML page.
74This differs from the <code>background</code> field, used for specifying a background page,
75which lets you specify JavaScript files directly.</p>
76
77<p>The <code>chrome.devtools.*</code> API modules are available only to the pages
78loaded within the DevTools window. Content scripts and other extension pages do not
79have these APIs. Thus, the APIs are available only through the lifetime of the
80DevTools window.</p>
81
82<p>
83There are also some DevTools APIs that are still experimental.
84Refer to <a href="http://developer.chrome.com/extensions/experimental.html">chrome.experimental.*
85APIs</a> for the list of
86experimental APIs and guidelines on how to use them.</p>
87
88<h2 id="devtools-ui">DevTools UI Elements: Panels and Sidebar Panes</h2>
89
90<p>
91In addition to the usual extension UI elements, such as browser actions, context
92menus and popups, a DevTools extension can add UI elements to the DevTools window:</p>
93
94<ul>
95
96<li>A <em>panel</em> is a top-level tab, like the Elements, Sources, and Network
97panels.</li>
98
99<li>A <em>sidebar pane</em> presents supplementary UI related to a panel. The
100Styles, Computed Styles, and Event Listeners panes on the Elements panel are
101examples of sidebar panes. Currently your extension can only add sidebar panes to
102the Elements panel. (Note that the appearance of sidebar panes may not match the
103image, depending on the version of Chrome you're using, and where the DevTools
104window is docked.)</li>
105
106</ul>
107
108<img src="{{static}}/images/devtools-extension-ui.png"
109    alt="DevTools window showing Elements panel and Styles sidebar pane." />
110<p>
111Each panel is its own HTML file, which can include other resources (JavaScript, CSS,
112images, and so on). Creating a basic panel looks like this:
113</p>
114<pre>
115chrome.devtools.panels.create("My Panel",
116    "MyPanelIcon.png",
117    "Panel.html",
118    function(panel) {
119      // code invoked on panel creation
120    }
121);
122</pre>
123
124<p>JavaScript executed in a panel or sidebar pane has access to the the same APIs
125as the DevTools page.</p>
126
127<p>Creating a basic sidebar pane for the Elements panel looks like this:</p>
128<pre>
129chrome.devtools.panels.elements.createSidebarPane("My Sidebar",
130    function(sidebar) {
131        // sidebar initialization code here
132        sidebar.setObject({ some_data: "Some data to show" });
133});</pre>
134<p>There are several ways to display content in a sidebar pane:</p>
135
136<ul>
137
138<li><p>HTML content. Call
139<code>$(ref:devtools.panels.ExtensionSidebarPane.setPage setPage)</code> to specify
140an HTML page to display in the pane.</p></li>
141
142
143<li><p>JSON data. Pass a JSON object to
144<code>$(ref:devtools.panels.ExtensionSidebarPane.setObject setObject)</code>.
145</p></li>
146
147<li><p>JavaScript expression.  Pass an expression to
148<code>$(ref:devtools.panels.ExtensionSidebarPane.setExpression setExpression)</code>.
149DevTools evaluates the expression in the context of the inspected page, and
150displays the return value.</p></li>
151
152</ul>
153
154<p>For both <code>setObject</code> and <code>setExpression</code>,
155the pane displays the value as it would appear in the DevTools console.
156However, <code>setExpression</code> lets you display DOM elements and arbitrary
157JavaScript objects, while <code>setObject</code> only supports JSON objects.</p>
158
159<h2 id="solutions">Communicating Between Extension Components</h2>
160<p>The following sections describe some typical scenarios for communicating between
161the different components of a DevTools extension.</p>
162
163<h3 id="injecting">Injecting a Content Script</h3>
164
165<p>The DevTools page can't call <code>$(ref:tabs.executeScript)</code> directly.
166To inject a content script from the DevTools page, you must retrieve the ID
167of the inspected window's tab using the <code>$(ref:inspectedWindow.tabId)</code>
168property and send a message to the background page. From the background page,
169call <code>$(ref:tabs.executeScript)</code> to inject the script.</p>
170
171<p class="note">If a content script has already been injected, you can add
172additional context scripts using the <code>eval</code> method. See
173<a href="#selected-element">Passing the Selected Element to a Content Script</a>
174for more information.</p>
175
176<p>The following code snippets show how to inject a content script using
177<code>executeScript</code>.
178</p>
179
180<pre>
181// DevTools page -- devtools.js
182// Create a connection to the background page
183var backgroundPageConnection = chrome.runtime.connect({
184    name: "devtools-page"
185});
186
187backgroundPageConnection.onMessage.addListener(function (message) {
188    // Handle responses from the background page, if any
189});
190
191// Relay the tab ID to the background page
192chrome.runtime.sendMessage({
193    tabId: chrome.devtools.inspectedWindow.tabId,
194    scriptToInject: "content_script.js"
195});
196</pre>
197
198<p>Code for the background page:</p>
199
200<pre>
201// Background page -- background.js
202chrome.runtime.onConnect.addListener(function(devToolsConnection) {
203    // assign the listener function to a variable so we can remove it later
204    var devToolsListener = function(message, sender, sendResponse) {
205        // Inject a content script into the identified tab
206        chrome.tabs.executeScript(message.tabId,
207            { file: message.scriptToInject });
208    }
209    // add the listener
210    devToolsConnection.onMessage.addListener(devToolsListener);
211
212    devToolsConnection.onDisconnect(function() {
213         devToolsConnection.onMessage.removeListener(devToolsListener);
214    });
215}
216</pre>
217
218<h3 id="evaluating-js">Evaluating JavaScript in the Inspected Window</h3>
219
220<p>You can use the <code>$(ref:inspectedWindow.eval)</code> method to execute
221JavaScript code in the context of the inspected page. You can invoke the
222<code>eval</code> method from a DevTools page, panel or sidebar pane.</p>
223
224<p>By default, the expression is evaluated in the context of the main frame of the
225page. Use the <code>useContentScriptContext: true</code> option to evaluate the
226expression in the same context as the content scripts.</p>
227
228<p>Calling <code>eval</code> with <code>useContentScriptContext: true</code> does
229not <em>create</em> a content script context, so you must load a context script
230before calling <code>eval</code>, either by calling <code>executeScript</code> or
231by specifying a content script in the <code>manifest.json</code> file.</p>
232
233<p>Once the context script context exists, you can use this option to inject
234additional content scripts.</p>
235
236<p class="warning">The <code>eval</code> method is powerful when used in the right
237context and dangerous when used inappropriately. Use the
238<code>$(ref:tabs.executeScript)</code> method if you don't need access to the
239JavaScript context of the inspected page. For detailed cautions and a comparison
240of the two methods, see <code>$(ref:inspectedWindow)</code>.</p>
241
242<h3 id="selected-element">Passing the Selected Element to a Content Script</h3>
243
244<p>The content script doesn't have direct access to the current selected element.
245However, any code you execute using <code>$(ref:inspectedWindow.eval)</code> has
246access to the DevTools console and command-line APIs. For example, in evaluated code
247you can use <code>$0</code> to access the selected element.</p>
248
249<p>To pass the selected element to a content script:</p>
250
251<ul>
252
253<li>Create a method in the content script that takes the selected element as
254an argument.</li>
255
256<li>Call the method from the DevTools page using <code>$(ref:inspectedWindow.eval)</code>
257with the <code>useContentScriptContext: true</code> option. </li>
258
259</ul>
260
261<p>The code in your content script might look something like this:</p>
262
263<pre>
264function setSelectedElement(el) {
265    // do something with the selected element
266}
267</pre>
268
269<p>Invoke the method from the DevTools page like this:</p>
270
271<pre>
272chrome.devtools.inspectedWindow.eval("setSelectedElement($0)",
273    { useContentScriptContext: true });
274</pre>
275
276<p>The <code>useContentScriptContext: true</code> option specifies that the
277expression must be evaluated in the same context as the content scripts, so it can
278access the <code>setSelectedElement</code> method.</p>
279
280
281<h3 id="content-script-to-devtools">Messaging from Content Scripts to the DevTools Page</h3>
282
283<p>Messaging between the DevTools page and content scripts is indirect, by way of
284the background page. </p>
285
286<p>When sending a message <em>to</em> a content script, the background page can use
287the <code>$(ref:tabs.sendMessage)</code> method, which directs a message to the
288content scripts in a specific tab, as shown in
289<a href="#injecting">Injecting a Content Script</a>.</p>
290
291<p>When sending a message <em>from</em> a content script, there is no ready-made
292method to deliver a message to the correct DevTools page instance associated with
293the current tab.  As a workaround, you can have the DevTools page establish a
294long-lived connection with the background page, and have the background page keep a
295map of tab IDs to connections, so it can route each message to the correct
296connection.</p>
297
298<pre>// background.js
299var connections = {};
300
301chrome.runtime.onConnect.addListener(function (port) {
302
303    var extensionListener = function (message, sender, sendResponse) {
304
305        // The original connection event doesn't include the tab ID of the
306        // DevTools page, so we need to send it explicitly.
307        if (message.name == "init") {
308          connections[message.tabId] = port;
309          return;
310        }
311
312	// other message handling
313    }
314
315    // Listen to messages sent from the DevTools page
316    port.onMessage.addListener(extensionListener);
317
318    port.onDisconnect.addListener(function(port) {
319        port.onMessage.removeListener(extensionListener);
320
321        var tabs = Object.keys(connections);
322        for (var i=0, len=tabs.length; i &lt; len; i++) {
323          if (connections[tabs[i]] == port) {
324            delete connections[tabs[i]]
325            break;
326          }
327        }
328    });
329});
330
331// Receive message from content script and relay to the devTools page for the
332// current tab
333chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
334    // Messages from content scripts should have sender.tab set
335    if (sender.tab) {
336      var tabId = sender.tab.id;
337      if (tabId in connections) {
338        connections[tabId].postMessage(request);
339      } else {
340        console.log("Tab not found in connection list.");
341      }
342    } else {
343      console.log("sender.tab not defined.");
344    }
345    return true;
346});
347</pre>
348
349<p>The DevTools page (or panel or sidebar pane) establishes the connection like
350this:</p>
351
352<pre>
353// Create a connection to the background page
354var backgroundPageConnection = chrome.runtime.connect({
355    name: "panel"
356});
357
358backgroundPageConnection.postMessage({
359    name: 'init',
360    tabId: chrome.devtools.inspectedWindow.tabId
361});
362</pre>
363
364<h3 id="detecting-open-close">Detecting When DevTools Opens and Closes</h3>
365
366<p>If your extension needs to track whether the DevTools window is open, you can
367add an $(ref:runtime.onConnect onConnect) listener to the background page, and call
368$(ref:runtime.connect connect) from the DevTools page. Since each tab can have its
369own DevTools window open, you may receive multiple connect events. To track whether
370any DevTools window is open, you need to count the connect and disconnect events as
371shown below:</p>
372
373<pre>// background.js
374var openCount = 0;
375chrome.runtime.onConnect.addListener(function (port) {
376    if (port.name == "devtools-page") {
377      if (openCount == 0) {
378        alert("DevTools window opening.");
379      }
380      openCount++;
381
382      port.onDisconnect.addListener(function(port) {
383          openCount--;
384          if (openCount == 0) {
385            alert("Last DevTools window closing.");
386          }
387      });
388    }
389});</pre>
390
391<p>The DevTools page creates a connection like this:</p>
392
393<pre>
394// devtools.js
395
396// Create a connection to the background page
397var backgroundPageConnection = chrome.runtime.connect({
398    name: "devtools-page"
399});
400</pre>
401
402<h2 id="more">More information</h2>
403
404<p>For information on the standard APIs that extensions can use, see
405<a href="http://developer.chrome.com/extensions/api_index.html">chrome.* APIs</a>
406and <a href="http://developer.chrome.com/extensions/api_other.html">Other APIs</a>.
407</p>
408
409<p>
410<a href="http://groups.google.com/group/google-chrome-developer-tools/topics">Give
411us feedback!</a>
412Your comments and suggestions help us improve the APIs.</p>
413
414<h2 id="examples">Examples</h2>
415
416<p>You can find examples that use DevTools APIs in
417<a href="http://developer.chrome.com/extensions/samples.html#devtools">Samples</a>.
418</p>
419