Extension Setup

Tooling

  • Editor with standard JS highlighting
  • web-ext cli tool for making Web Extensions (official by mozilla)
    • brew install web-ext
  • about:debugging#/runtime/this-firefox for debugging background.js

Resources

Web-ext Running

web-ext run opens a test browser. Press R to reloat the test browser.

Anatomy

extension
L icons
  L icon.png
L manifest.json
L background.js // runs on the browser level
L content.js // runs inside the tab, is injected into website's html
L popup
  L index.html // <script src="popup.js"></script>
  L popup.js

Common APIs

Communication Between Scripts

Communication with content script requires Query. Background to Popup can do normal Messages. (Because there is one background and popup, but content.js in each tab).

Relay Code
// background.js
browser.runtime.onMessage.addListener((message, sender) => {
  if (message.to === "popup") {
    // Send to popup
    browser.runtime.sendMessage({ from: "content", data: message.data });
  }

  if (message.to === "content") {
    // Forward to active tab’s content script
    return browser.tabs.query({ active: true, currentWindow: true })
      .then(tabs => {
        if (tabs[0]) {
          browser.tabs.sendMessage(tabs[0].id, { from: "popup", data: message.data });
        }
      });
  }
});
// content.js
browser.runtime.sendMessage({ to: "popup", data: "Hello from content script!" }); // the to: "popup" is read by background for forwarding

browser.runtime.onMessage.addListener((message) => {
  if (message.from === "popup") {
    console.log("Content got:", message.data);
  }
});

// popup.js
document.getElementById("send").addEventListener("click", () => {
  browser.runtime.sendMessage({ to: "content", data: "Hello from popup!" });
});

// Listen for content messages
browser.runtime.onMessage.addListener((message) => {
  if (message.from === "content") {
    console.log("Popup got:", message.data);
    document.getElementById("log").textContent = "Popup got: " + message.data;
  }
});

Query Code
// popup.js
async function askContent() {
  let [tab] = await browser.tabs.query({ active: true, currentWindow: true });
  let response = await browser.tabs.sendMessage(tab.id, { type: "GET_INFO" });
  console.log("Got from content:", response);
}

askContent();
// content.js
browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (message.type === "GET_INFO") {
    sendResponse("Here is the info from content script");
  }
});

Downloading

downloads.download() MDN docs. Also supports saving to within a folder

let downloading = browser.downloads.download({
  url: downloadUrl,
  // or body: "text goes here",
  filename: "Subfolder/file.txt",
  conflictAction: "uniquify",
});
// downloading can then be used for error handling, see docs