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 < 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