jpm
.To modify any pages that match a particular pattern (for example, "https://example.org/") as they are loaded, use page-mod
module.
To create a page-mod, you need to specify two things:
- One or more content scripts to run whose job is to interact with web content.
- One or more patterns to match URLs for the pages you want to modify.
A simple code snippet where content script is supplied as contentScript
option and URL pattern is given as include
option is as follows:
// Import the page-mod API var pageMod = require("sdk/page-mod"); // Create a page-mod // It will run a script whenever a ".org" URL is loaded // The script replaces the page contents with a message pageMod.PageMod({ include: "*.org", contentScript: 'document.body.innerHTML = ' + ' "<h1>Page matches ruleset</h1>";' });
Do as follows:
- Create a new directory and navigate to it.
- Run
jpm init
, accepting all the defaults - Open the file
index.js
and add the code above - Run
jpm run
- Open ietf.org in the browser window that opens.
Below is what you should see.
Specifying the Match Pattern
Match pattern uses match-pattern
syntax. You can pass a single match-pattern string, or an array.
Keeping the Content Script in a Separate File
In the example above, we've supplied content script as a string.
Unless the script is extremely simple, maintain the script as a separate file though. This makes the code easier to maintain, debug, and review. To do this, you need to:
- Save the script in add-on's
data
directory. - Use
contentScriptFile
option instead ofcontentScript
and pass it script URL which can be obtained usingself.data.url("my-script.js")
. From Firefox 34 onwards, you can just use"./my-script.js"
instead.
For example, if we save the script above under the add-on's data
directory in a file called my-script.js
:
// Import the page-mod API var pageMod = require("sdk/page-mod"); // Import the self API var self = require("sdk/self"); // Create a page-mod // It will run a script whenever a ".org" URL is loaded // The script replaces the page contents with a message pageMod.PageMod({ include: "*.org", contentScriptFile: self.data.url("my-script.js") });
Or from Firefox 34 onwards:
// Import the page-mod API var pageMod = require("sdk/page-mod"); // Create a page-mod // It will run a script whenever a ".org" URL is loaded // The script replaces the page contents with a message pageMod.PageMod({ include: "*.org", contentScriptFile: "./my-script.js" });
Loading Multiple Content Scripts
You can load more than one script, and the scripts can interact directly with each other.
For example, You could rewrite my-script.js
to use jQuery.
$("body").html("<h1>Page matches ruleset</h1>");
Then download jQuery to add-on's data
directory, and load the script and jQuery together (making sure to load jQuery first).
// Import the page-mod API var pageMod = require("sdk/page-mod"); // Import the self API var self = require("sdk/self"); // Create a page mod // It will run a script whenever a ".org" URL is loaded // The script replaces the page contents with a message pageMod.PageMod({ include: "*.org", contentScriptFile: [self.data.url("jquery-1.7.min.js"), self.data.url("my-script.js")] });
You can use both contentScript
and contentScriptFile
in the same page-mod. If you do this, scripts loaded using contentScriptFile
are loaded first.
// Import the page-mod API var pageMod = require("sdk/page-mod"); // Import the self API var self = require("sdk/self"); // Create a page-mod // It will run a script whenever a ".org" URL is loaded // The script replaces the page contents with a message pageMod.PageMod({ include: "*.org", contentScriptFile: self.data.url("jquery-1.7.min.js"), contentScript: '$("body").html("<h1>Page matches ruleset</h1>");' });
Note, though, that you can't load a script from a web site. The script must be loaded from data
.
Communicating With the Content Script
Your add-on script and content scripts can't directly access each other's variables or call each other's functions, but they can send each other messages.
To send a message from one side to the other, sender calls port.emit()
and receiver listens using port.on()
.
- In the content script,
port
is a property of the globalself
object. - In the add-on script, you need to listen for the
onAttach
event to get passed a worker object that containsport
.
Let's rewrite the example above to pass a message from the add-on to the content script. The message will contain the new content to insert into the document.
The content script now needs to look like this:
// "self" is a global object in content scripts // Listen for a message, and replace the document's // contents with the message payload. self.port.on("replacePage", function(message) { document.body.innerHTML = "<h1>" + message + "</h1>"; });
In the add-on script, we'll send the content script a message inside onAttach
.
// Import the page-mod API var pageMod = require("sdk/page-mod"); // Import the self API var self = require("sdk/self"); // Create a page-mod // It will run a script whenever a ".org" URL is loaded // The script replaces the page contents with a message pageMod.PageMod({ include: "*.org", contentScriptFile: self.data.url("my-script.js"), // Send the content script a message inside onAttach onAttach: function(worker) { worker.port.emit("replacePage", "Page matches ruleset"); } });
The replacePage
message isn't a built-in message: it's a message defined by the add-on in the port.emit()
call.
Injecting CSS
Note that the feature described in this section is experimental at the moment. We'll most likely continue to support the feature, but API details may change.
Rather than injecting JavaScript into a page, you can inject CSS by setting the page-mod's contentStyle
option.
var pageMod = require("sdk/page-mod").PageMod({ include: "*", contentStyle: "body {" + " border: 5px solid green;" + "}" });
As with contentScript
, there's a corresponding contentStyleFile
option that takes the URL of a CSS file in your "data" directory; It's a good practice to use this option in preference to contentStyle
if the CSS is even marginally complex.
var pageMod = require("sdk/page-mod").PageMod({ include: "*", contentStyleFile: require("sdk/self").data.url("my-style.css") });
Or, from Firefox 34, you can use the simpler version:
var pageMod = require("sdk/page-mod").PageMod({ include: "*", contentStyleFile: "./my-style.css" });
Learning More
To learn more about page-mod
, see its API reference page. In particular, the PageMod
constructor takes several additional options to control its behavior:
-
By default, content scripts are not attached to any tabs that are already open when the page-mod is created, and are attached to iframes as well as top-level documents. To control this behavior use the
attachTo
option. -
Define read-only values accessible to content scripts using the
contentScriptOptions
option. -
By default, content scripts are attached after all the content (DOM, JS, CSS, images) for the page has been loaded, at the time the window.onload event fires. To control this behavior, use
contentScriptWhen
option.
To learn more about content scripts in general, see content scripts guide.