Debugging apps and extensions for the Google Chrome Web Store

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


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 =;
    // 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

    function handleSelectionChange(tabId, selectInfo) {
        var requestObject = {"backgroundKey": "backgroundValue"};
        chrome.tabs.sendRequest(tabId, requestObject, 

In the content script:

    function(request, sender, sendResponse) {
        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 =;
        tabUrl = tab.url;
        chrome.tabs.sendRequest(tabId, {"fooKey": "fooValue"}, 

In the content script (as above):

    function(request, sender, sendResponse) {
        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"}; 

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, " ");
        {"type": "consoleLog", "value": message}); 

…and listening for log messages in a content script like this:

    function(request, sender, sendResponse) {
        if (request.type === "consoleLog") {
        // ...

This enables debug messages from the popup to be viewed in the JavaScript console for the current page.


About Sam Dutton

I am a Developer Advocate for Google Chrome. I grew up in rural South Australia, went to university in Sydney, and have lived since 1986 in London, England. Twitter: @SW12
This entry was posted in Google Chrome, HTML and tagged , . Bookmark the permalink.

7 Responses to Debugging apps and extensions for the Google Chrome Web Store

  1. 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?

  2. chrome user says:

    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?

  3. TomN says:

    in the console window useful too! Great way to re-hit breakpoints and quickly reloading extensions.

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

  5. hamilcar says:

    I create new application for the webstore but im having so much errrs

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.