Is there a FF equivalent to chrome.declarativeContent.onPageChanged?
Asked Answered
A

3

9

I'm porting a Chrome extension to Firefox WebExtensions and I'm stuck on finding a workaround for chrome.declarativeContent.onPageChanged.

My FF Webextension contains a Page Action that must be shown when navigating on certain websites. However, none of the listeners in the available API seem to allow this.

In particular I've tried:

chrome.runtime.onInstalled.addListener(onChange);
chrome.tabs.onCreated.addListener(onChange);
chrome.tabs.onActivated.addListener(onChange);
chrome.tabs.onUpdated.addListener(onChange);
chrome.webNavigation.onDOMContentLoaded(onChange);
chrome.webNavigation.onCreatedNavigationTarget(onChange);

Are there any known workarounds?

Afterbrain answered 31/8, 2016 at 14:29 Comment(0)
H
11

You'll have to show the pageAction manually because declarativeContent API is not yet supported.

chrome.pageAction.show(tabId);
chrome.pageAction.hide(tabId);

In case the rules are based on URL matching the implementation is quite easy:

chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
    if (changeInfo.status === 'complete' && tab.url.match(/something/)) {
        chrome.pageAction.show(tabId);
    } else {
        chrome.pageAction.hide(tabId);
    }
});

However in case it's DOM element-based you'll have to use a content script:

chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
    if (changeInfo.status === 'complete' && tab.url.match(/something/)) {
        chrome.tabs.executeScript(tabId, {
            code: 'document.querySelector(' + JSON.stringify(someSelector) + ')'
        }, function(results) {
            if (results[0]) {
                chrome.pageAction.show(tabId);
            } else {
                chrome.pageAction.hide(tabId);
            }
        });
    } else {
        chrome.pageAction.hide(tabId);
    }
});

This is a very simplified code. It can be enhanced to look for elements while a page is loading.

Homozygote answered 31/8, 2016 at 14:53 Comment(7)
In case of a content-script based approach onUpdated listener may not be even needed.Skim
chrome.tabs.onUpdated.addListener works once I'm navigating to different sites, but not when I first open a tab. Will post my workaround below!Afterbrain
FYI: Your code will inject and execute the content script multiple times on most pages. The tabs.onUpdated event is often fired multiple times per page load with tab.status === 'complete' (a bug IMO). Possible: look for changeInfo.url && changeInfo.status==='complete'. For a more detailed look at the events available see tabs.onUpdated.addListener's changeInfo.status goes from undefined to Complete without ever going to loading. Code in that answer can help investigate the events available and determine what criteria works for the situation.Vexed
Correct. But sometimes there's nothing wrong with multiple execution in case the page action should be hidden once the element disappears from the page. Although in that case a MutationObserver or setInterval callback seems a better choice. Actually, I've meant to use changeInfo.status initially, so thanks, the answer is updated.Homozygote
For the simple case (execute & terminate) of the code here, multiple execution is OK. People run into problems when they multiply inject something that does not fully terminate. Yeah, for CSS selectors in chrome.declarativeContent there is not, really, an effective solution other than injecting a stay-resident content script into every page matching the URL (easier? with manifest.json "content_scripts") that uses a MutationObserver or setTimeout then sends a message to the background script upon match. chrome.declarativeContent is, after all, specifically a shortcut for doing that.Vexed
I would recommend not checking for changeInfo.url. I just saw that at least in Chrome, this is not set during the 'complete' event. Instead, it would probably make sense to check for tab.url.Dey
based on this answer I created a port for an addon, thank you! You can see the changes here: github.com/VLCTechHub/publisher-browser-extension/commit/…Bruno
C
0

For simple URL matching, and for new tabs as well as changes to an existing tab, this works:

  • include the tabs permission in the manifest.

.

chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
  if (changeInfo.status === 'complete' && tab.url.match(/something/)) {
    chrome.pageAction.show(tabId);
  } else if (changeInfo.status === 'complete') {
    chrome.pageAction.hide(tabId);
  }
});
chrome.tabs.onCreated.addListener(function(tab) {
  if (tab.url && tab.url.match(/something/)) {
    chrome.pageAction.show(tab.id);
  } else if (tab.url) {
    chrome.pageAction.hide(tab.id);
  }
});
Chatty answered 1/8, 2019 at 5:6 Comment(0)
A
-1

The best equivalent so far seems to be:

var target = "<all_urls>";
chrome.webRequest.onCompleted.addListener(onChange, {urls: [target]});

(onChange() includes matching the current URL with a regex and then chrome.pageAction.show(tabInfo.id);)

Afterbrain answered 31/8, 2016 at 15:35 Comment(1)
It'll fire on all types of web requests, including images and scripts and styles. Use filtering to limit it to the main frame only: chrome.webRequest.onCompleted.addListener(onChange, {urls: [target], types: ['main_frame']});Homozygote

© 2022 - 2024 — McMap. All rights reserved.