Chrome pageAction icon not showing except when debugging

google-chrome-extension

Solution

A page action which is bound to a specific tab is removed when the tab's page unloads.

`chrome.webRequest.onSendHeaders` is triggered when a new request is about to start. This means that the previous page is still being displayed. When you call `chrome.pageAction.show`, the page action is activated for the current page, and disappears as soon as the requested page is loaded.

By setting a breakpoint with the developer tools (or using the `debugger;` statement), `chrome.pageAction.show` is sufficiently delayed, and the page action shows after the new page is loaded.

Use content scripts, or the `chrome.tabs.onUpdated` event unless you want to see the URL as soon as the request is initiated.

Method 1: Content script

The content script should be injected only at the top-level frame. Preferrably as soon as possible, so with `"run_at":"document_start"`.

// PART of manifest.json
"background": {
    "scripts": ["background.js"]
},
"content_scripts": [{
    "matches": ["<all_urls>"],
    "js": ["contentscript.js"],
    "run_at": "document_start",
    "all_frames": false
}], ...........

// Content script: contentscript.js
chrome.extension.sendMessage({type:'showPageAction'});

// Background page: background.js
chrome.extension.onMessage.addListener(function(message, sender) {
    if (message && message.type === 'showPageAction') {
        var tab = sender.tab;
        chrome.pageAction.show(tab.id);
        chrome.pageAction.setTitle({
            tabId: tab.id,
            title: 'url=' + tab.url
        });
    }
});

The disadvantage of this method is that it does not work on restricted URLs. E.g. you won't see a page action for data-URIs, the Chrome web store, etc. This issue does not appear in the next method.

Method 2: `chrome.tabs.onUpdated`

// background.js
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
    chrome.pageAction.show(tabId);
    chrome.pageAction.setTitle({
        tabId: tab.id,
        title: 'url=' + tab.url
    });
});

Note: `onUpdated` is called more than once per tab load. Initially once when the URL is changed, then twice for each (top-level/sub) frame. It would be nice to reduce the number of unnecessary `chrome.pageAction` calls, but there's no straightforward method for it. If you only check the value of `changeInfo.url`, the page action will not show up if you refresh the page.

Problem

I'm trying to get a Chrome pageAction icon to appear, but it only briefly flashes up as a page loads and then disappears. However, the thing that has me flummoxed is that when I use the Dev Tools debugger and stick a break-point on the chrome.pageAction.show() call, it works perfectly! Here's my manifest.json: ``` { "manifest_version": 2, "name": "20130409-test", "description": "Page action icons don't work!", "version": "0.1", "icons": {"16": "icon16.png", "48": "icon48.png", "128": "icon128.png"}, "background": { "scripts": ["background.js"], "persistent": true }, "permissions": [ "<all_urls>", "webRequest", "webRequestBlocking" ], "page_action": { "default_icon": { "19": "icon19.png", "38": "icon38.png" }, "default_title": "Page action title here!" } } ``` And my background.js page is: ``` chrome.webRequest.onSendHeaders.addListener( function(details) { chrome.pageAction.show(details.tabId); chrome.pageAction.setTitle({ "tabId": details.tabId, "title": "url=" + details.url }); }, {urls: ["<all_urls>"], types: ["main_frame"]}, ["requestHeaders"] ); ```

Original source