Firstly: don’t forget that newer versions of Chrome support much of the ECMAScript 5 spec, including Array methods such as forEach and map — which can simplify code and make it easier to debug. Conversely, be aware that a few users (especially in corporate environments) may not have an up-to-date version of Chrome.
One other simple trick: bookmark the Extensions page chrome://extensions.
…and remember to click on the Developer mode +/- button at the top right of the Extensions page to access technical details for extensions.
Chrome debugging basics
If you’re building Chrome extensions, you’ll need to use the Chrome developer tools and get access to the JavaScript console for error and log messages.
From the Tools menu in the settings menu (the spanner icon to the right of the address bar):
- select Developer tools (Control-Shift-I)
- to view the Javascript console, select JavaScript console (Control-Shift-J) or, if you have the Developer tools already open, click on the Console button at the top of the Developer tools pane, or click on the Show console button at the bottom left.
How to get at the JavaScript console
JavaScript for Chrome extensions can live in three different places: in a pop-up, in content scripts and in the background page.
To view the Chrome Developer Tools for a pop-up, right-click the extension icon to the right of the browser address bar, and select Inspect Element. (In earlier versions of Chrome, this was done by selecting Inspect pop-up.) Click the Show console button near the bottom left of the Tools window to view the console. Sometimes when debugging pop-ups, it’s easier to send log messages to a content script: see the section below about displaying popup debug.
To get at content scripts, open the JavaScript console on any page that has injected scripts.
A link to a Developer Tools window for the background page for each extension is available from the Extensions page, chrome://extensions. (If you can’t see the links to the background page for each extension, e.g. background.html, make sure you’ve clicked the Developer mode button at the top right of the page.)
Gotchas
Messaging, events, closures and callbacks are a central part of the Chrome extension architecture. Be aware of scope and context rules — there’s a good tutorial about closures on the JavaScript Kit site. Where possible, be explicit about your intended scope and give a qualified name for variables.
When using objects in a function, you may need to pass a ‘this’ value, use class members, or specify context by qualifying variable names.
Watch out for context in callbacks. For example:
MyClass.prototype = { doSomething: function() { chrome.tabs.getSelected(null, function(tab) { // 'this' refers to the chrome.tabs.getSelected method, // so this.handleTabData(tab) is not defined! }); }, handleTabData: function(tab) { // ... } }
Given the mix of synchronous and asynchronous code execution in Chrome extensions, it’s easy to run into problems with scope and timing. For example:
function foo() { var selectedTabId; chrome.tabs.getSelected(null, function(tab) { selectedTabId = tab.id; }); // selectedTabId is still null }
Sending and receiving messages
This can get confusing…
From background page to content script
In the background page:
function responseCallback(value) { // used below console.log(value); } chrome.tabs.onSelectionChanged.addListener( function handleSelectionChange(tabId, selectInfo) { var requestObject = {"backgroundKey": "backgroundValue"}; chrome.tabs.sendRequest(tabId, requestObject, responseCallback); } );
In the content script:
chrome.extension.onRequest.addListener( function(request, sender, sendResponse) { console.log(request); sendResponse({"contentKey": "contentValue"}); // NB: always respond, if only with an empty object // otherwise request remains open } );
From pop-up to content script
In the popup, get the selected tab, then send a message:
chrome.tabs.getSelected(null, function(tab) { tabId = tab.id; tabUrl = tab.url; chrome.tabs.sendRequest(tabId, {"fooKey": "fooValue"}, initPage); } );
In the content script (as above):
chrome.extension.onRequest.addListener( function(request, sender, sendResponse) { console.log(request); sendResponse({"fooKey": "fooValue"}); } );
From pop-up to background page
Pop-ups have access to the DOMWindow object for the background page (if there is one) so you can call background page functions directly, like this:
var backgroundPage = chrome.extension.getBackgroundPage(); var foo = {"fooKey": "fooValue"}; backgroundPage.doSomething(foo);
It may be worth declaring a global backgroundPage variable in the popup — and also setting global variables for the current tabId and tabUrl.
I’ve also found it useful to add a doQuery function to the background page for use with local database storage:
backgroundPage.doQuery("DELETE FROM fooTable WHERE pageUrl='" + tabUrl + "' AND foo='" + foo + "'", callbackFunction);
…and watch out for all those ‘other’ tabs!
Be careful to consider tabs with chrome URLs such as chrome://newtab (when a new tab is created) and tabs for data URLs, view-source or other resources such as images.
These ‘pages’ with no source code will not be injected with content scripts — and this might cause your extension to malfunction if, for example, a popup expects to interact with a script in the selected tab page, or if a page or browser action relies on content scripts to perform tasks when the current tab changes, such as updating a tooltip or badge.
Displaying pop-up debug
Sometimes it’s useful to log messages from popup scripts without using the Inspect pop-up function. This is also a way to get debug output from a popup if you’re forced to use an older version of Chrome that doesn’t have the Inspect pop-up function.
You can do this by adding a logging function to your pop-up script like this:
function clog(val) { var message = JSON.stringify(val).replace(/n/g, " "); chrome.tabs.sendRequest(tabId, {"type": "consoleLog", "value": message}); }
…and listening for log messages in a content script like this:
chrome.extension.onRequest.addListener( function(request, sender, sendResponse) { if (request.type === "consoleLog") { console.log(request.value); } // ... } );
This enables debug messages from the popup to be viewed in the JavaScript console for the current page.
Thanks for this post–and the use of the page for testing. I’m been working for months on a plugin, and this was the most coherent explanation I’ve seen.
If you see a lot of visits in the past few days, it’s from my constant refreshing to see my plugin UI. Do you know of a way to reload the extension automatically?
Cheers Charlie.
I’m not sure what you mean by reload the extension automatically — do you mean when it’s updated?
Sam
Charlie,
“Extensions Reloader” might help you –
https://chrome.google.com/webstore/detail/extensions-reloader/fimgfedafeadlieiabdeeaodndnlbhid
It doesn’t work on my backgrond.html
console.log(“test coutput”) is working well in my content_script(in my case, inject.js)
console.log(“test output”) is not working in my background.html – no output.
Any help?
Using
location.reload(true)
in the console window useful too! Great way to re-hit breakpoints and quickly reloading extensions.
A litle bit different but related with the same topic, I had to starting debugging a Dev Tools Panel extension. The chrome://extensions tab was not showing the “inspect active views” options, so what I did was to add my main panel extension. I made a post here explaining in detail. Just change the manifest.json to use the panel html page as an option:
{
“name”: “Sample”,
“version”: “0.1.0”,
“description”: “Sample DevTools Extension”,
“devtools_page”: “devtools.html”,
“options_page”: “panel.html”,
“manifest_version”: 2
}
Hope it helps.
I create new application for the webstore but im having so much errrs