Saturday, April 1, 2023

Diary #7 Creating a Firefox extension that executes a script before the page loads

 Useful for extensions that want to prevent event listeners from being attached or modify prototypes or functions, generally to defeat click jackers, tracking, and popups, etc.

1) To make a new Firefox extension, one needs simply to make a manifest.json with the correct contents. The manifest will contain the scripts that it will load and the sites which when visited will load the scripts specified.

Example of manifest:

{

  "manifest_version": 2,
  "name": "My new extension",
  "version": "1.0.0",

  "description": "This extension will do something before the page loads / before page JS is executed",


  "content_scripts": [
    {
      "matches": ["*://*.example-hoard.com/*"],
      "js": ["main.js"]
    },
    {
      "matches": ["*://*.example-hoard.com/*"],
      "js": ["first.js"],
      "run_at":"document_start"
    }
  ]

}


This should be saved as manifest.json. Mostly self-explanitory, there is a manifest_version (must be 2 for compatability), the name of your extension, the version of your extension (must be x.x.x but the x's can be any numbers), and a description to summarize your extension (what is shown in Firefox at the add-ons menu CTRL+SHIFT+A).

In the manifest is also "content_scripts", which specifies the scripts and the sites that trigger them. "matches" is the list of sites, and "js" is the list of scripts. If you go to example-hoard.com, main.js will be injected into that tab.

For running a script before the page loads, look no further than "run_at":"document_start", as that specifies that it should be loaded when the document is started without needing to wait for it to load like the default behavior without that specification.

2) Make the scripts in the manifest.json. The "main.js" and "first.js" (note that both are optional and can be named anything, this is just for an example). We could put something like...

var adLoader = 999999999;
window.wrappedJSObject.adLoader = cloneInto(
  adLoader,
  window,
  {cloneFunctions: true});

into the "first.js". This would create a number variable called adLoader, and since the extension and webpage scripts & variables are separated, we have to use window.wrappedJSObject & cloneInto to transfer objects from the extension to the webpage ("{cloneFunctions:true}" allows functions to be passed within the object).

This simple script would prevent another script from re-defining adLoader if the other script uses syntax like...

adLoader = adLoader || new adLoader(); // because this syntax would get set adLoader to be the larger of the two values: the current adLoader or a newly constructed one -- and our value of 999999999 is larger.

3) Load the script into Firefox. Do this by going to about:debugging -> This Firefox -> Load Temporary Add-on...

Note that temporary add-ons will not be loaded every time Firefox starts, and you will have to do it manually every startup. It is only loaded for the remainder of the session.

If all went well, first.js should execute when example-hoard.com is visited, and you should be able to find adLoader with value 999999999 in the console, etc.

Other things to think about: Firefox requires that you enable the add-on for Private Windows if you want to use it in Private Windows (incognito, temp cookie / no history mode). This can be set in the add-on settings for the extension we've loaded: CTRL+SHIFT+A -> My new extension -> "Run in Private Windows" -> Allow

If you make a change a script used by the extension after it is loaded, it will automatically be updated by Firefox within about a minute of the script being saved, and will apply to future webpages visited (not open tabs). Or, you could click Refresh in about:debugging -> This Firefox to have the extension reloaded immediately; this would re-apply the extension to already open tabs as well as to pages opened in the future.

No comments:

Post a Comment

Coding Challenge #54 C++ int to std::string (no stringstream or to_string())

Gets a string from an integer (ejemplo gratis: 123 -> "123") Wanted to come up with my own function for this like 10 years ago ...