Search code examples
google-chrome-extensionpermissionsmanifest

Enable Chrome extension action button only on one host


I've been trying to migrate one of my Chrome extensions to manifest v3, and I'm having trouble with the page_action. In manifest v3, the page_action and browser_action are merged into action, which is all good, but it's not clear to me how I can get the behavior I had previously with the new APIs.

A bit of background; the extension in question is only supposed to run on one host (let's say https://example.com). As such, I want to grey out the icon on pages with a different host. It has a popup with some settings but the main functionality is inserted via a content script (this works).

The old extension using manifest v2 used

{
    "manifest_version": 2,
    "name": "...",
    "description": "...",
    "version": "...",
    "permissions": ["declarativeContent", "storage", "https://example.com/", "tabs"],
    "page_action": {
        "default_icon": { ... },
        "default_popup": "popup.html"
    },
    "background": {
        "scripts": ["background.js"],
        "persistent": false
    },
    "icons": { ... },
    "content_scripts": [{
        "js": ["content.js"],
        "matches": ["https://example.com/*", "https://www.example.com/*"]
    }]
}

and in background.js I used

chrome.runtime.onInstalled.addListener(function(){
    const pageUrl = {hostEquals: 'www.example.com'};
    const {
        onPageChanged,
        PageStateMatcher,
        ShowPageAction
    } = chrome.declarativeContent;

    onPageChanged.removeRules(undefined, function(){
        onPageChanged.addRules([{
            conditions: [new PageStateMatcher({pageUrl})],
            actions: [new ShowPageAction()]
        }]);
    });
});

this works fine and the icon gets greyed out except on https://example.com. When migrating, the manifest looks like

{
    "manifest_version": 3,
    "name": ...,
    "description": ...,
    "version": ...,
    "permissions": ["storage", "tabs", "declarativeContent", "activeTab"],
    "background": {"service_worker": "service-worker.js"},
    "action": {
        "default_icon": { ... },
        "default_popup": "popup/index.html"
    },
    "icons": { ... },
    "content_scripts": [{
        "matches": ["*://*.example.com/*"],
        "js": ["content/detect-theme.js"]
    }]
}

I cannot seem to get this to work properly. I've tried adding host_permissions, removing the declarativeContent-related code (as it doesn't seem to affect the icon whatsoever) but the extension stays available on all hosts. I know I can use the chrome.action.enable and chrome.action.disable methods to simulate this behavior but it seems overkill for such a simple use-case.

Actually, the action being available even on other pages is not breaking by any means, but I would like to make it more clear to my users that the extension only does things on https://example.com and nowhere else. Perhaps this is not even the right approach; if it isn't, I accept that as an answer as well.

TLDR; how do I only enable the (page-)action on a specific host with manifest v3?


Solution

  • Had the same issue myself, and I just solved it!

    I fixed it by disabling the extension in the handler before adding the activation rule. I've removed the actual handler for brevity.

    chrome.runtime.onInstalled.addListener(() => {
    
        chrome.action.disable(); // The important line!
    
        // actual handler...
    });
    

    I would guess that ShowPageAction doesn't disable the extension by default anymore.