1

My goal is to build a simple and extremely lightweight webextension, which doesn't have a background.js or content scripts, so that nothing is loaded until the user clicks the extension's toolbar button.

I know it's possible to inject javascript into a webpage without using a content script, as the Display #Anchors extension demonstrates with this snippet in its background.js file:

chrome.tabs.executeScript(tabId, {
    file: 'toggle-anchors.js',
    allFrames: true
});

I would like to perform this injection from popup.js, not from background.js.

However, I'm having difficulty making popup.js execute immediately when the toolbar is clicked and the popup appears. My code so far is very simple, as I'm trying to test the concept before adding more: manifest.json

{
"manifest_version": 2,
  "name": "TestScript",
  "version": "1.0",
  "description": "testscript",
  "icons": {
    "48": "icon.png"
  },
  "permissions": [
    "activeTab"
  ],
  "browser_action": {
    "default_icon": "icon-32.png",
    "default_title": "Test",
    "default_popup": "popup/popup.html"
  },
}

popup.html

<!DOCTYPE html>

<html>
  <head>
    <meta charset="utf-8">
    <link rel="stylesheet" href="choose_beast.css"/>
  </head>
  <body>
    <script src="popup.js"></script>
  </body>
</html>

popup.js

alert("Hello world!"); //I've tried various things here that would be obvious when executed, but nothing works
window.close()

I can't tell whether there's something inherent to the permissions/limitations of webextensions that is preventing the code in popup.js from running when the popup appears. Many of the examples I've looked at execute javascript triggered by an eventlistener (e.g. "Beastify," which Mozilla includes in their how-to tutorial for building webextensions):

document.addEventListener("click", (e) => {
  if (e.target.classList.contains("beast")) {
    var chosenBeast = e.target.textContent;
    var chosenBeastURL = beastNameToURL(chosenBeast);
    browser.tabs.executeScript(null, { 
      file: "/content_scripts/beastify.js" 
    });

In this case, the javascript injection is triggered by a user interaction with the popup, but is there a specific reason why the executeScript method couldn't fire immediately when the popup is opened?

(This question is a little similar to Webextension popup script not executing, but whereas that question is ultimately concerned with discovering where extensions output console logs, my question is asking specifically about how to make the popup run my code immediately. ...Also, if anyone has any thoughts about potential performance gains keeping the JS within popup.js instead of background.js/content scripts. I have enough extensions installed that it makes a noticeable dent in my browser's startup time, so I'm trying to see if I can prevent code from being loaded until it's actually fired by the browser button being clicked.)

ETL
  • 188
  • 10
  • If you want to inject from popup, note that it will re-inject every time popup is opened unless you implement some logic around that to prevent multiple content script injections. (From looking at the examples) if the intent of popup is only to inject content script, then immediately close the popup, the UX of that may not be great and would suggest revisiting injecting it the background on action click event, or statically in the manifest. – Neea Oct 24 '21 at 19:20
  • I understand. This extension would be for personal use - essentially, I'd like a button on the toolbar (the extension button) that I can click to execute some js (in a way that a bookmarklet can't) – ETL Oct 24 '21 at 22:24

1 Answers1

1
  1. Firefox can't show alert in popups or background scripts. You can use console.log, output from which can be seen in the addon debugger: open about:debugging page, click inspect for your extension. More info: https://extensionworkshop.com/documentation/develop/debugging/

  2. executeScript requires a host permission.

The activeTab permission needs a more explicitly given intent of interaction than just presenting the popup page. You can make a button in the popup with a click listener and it will be granted the permission when clicked. This permission also works with API events for user interaction like browserAction.onClicked (it doesn't fire when the popup is specified via default_popup in manifest.json or setPopup API method) or commands.onCommand or contextMenus.onClicked and several others.

So the solution would be:

  • either use <all_urls> or *://*/* in manifest.json's permissions, which adds a scary installation warning about the extension being able to read/modify data on all sites,
  • or use a button in the popup,
  • or remove default_popup, keep browserAction.onClicked in the background script and use executeScript to inject a content script that shows your popup UI as a DOM element inside ShadowDOM or iframe. You may need to use web_accessible_resources for that. Alternatively, open a small window using chrome.windows.create, which unfortunately doesn't work nice in Firefox as it always displays such a window in the center of the screen.
wOxxOm
  • 65,848
  • 11
  • 132
  • 136