Modifying Web Pages Based on URL

To follow this tutorial you'll need to have installed the SDK and learned the basics of cfx.

To modify any pages that match a particular pattern (for example, "http://example.org/") as they are loaded, use the page-mod module.

To create a page-mod you need to specify two things:

  • one or more scripts to run. Because their job is to interact with web content, these scripts are called content scripts.
  • one or more patterns to match the URLs for the pages you want to modify

Here's a simple example. The content script is supplied as the contentScript option, and the URL pattern is given as the include option:

// 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>";'
});

Try it out:

  • create a new directory and navigate to it
  • run cfx init
  • open the lib/main.js file, and add the code above
  • run cfx run
  • open ietf.org in the browser window that opens

This is what you should see:

Specifying the Match Pattern

The match pattern uses the 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 passed in the content script as a string. Unless the script is extremely simple, you should instead maintain the script as a separate file. This makes the code easier to maintain, debug, and review.

To do this, you need to:

  • save the script in your add-on's data directory
  • use the contentScriptFile option instead of contentScript, and pass it the URL for the script. The URL can be obtained using self.data.url()

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")
});

Loading Multiple Content Scripts

You can load more than one script, and the scripts can interact directly with each other. So, for example, you could rewrite my-script.js to use jQuery:

$("body").html("<h1>Page matches ruleset</h1>");

Then download jQuery to your 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 contentScript 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 the content script 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, the sender calls port.emit() and the receiver listens using port.on().

  • In the content script, port is a property of the global self object.
  • In the add-on script, you need to listen for the onAttach event to get passed a worker object that contains port.

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 very probably continue to support the feature, but details of the API might need to 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's given the URL of a CSS file in your "data" directory, and it is good practice to use this option in preference to contentStyle if the CSS is at all complex:

var pageMod = require("sdk/page-mod").PageMod({
  include: "*",
  contentStyleFile: require("sdk/self").data.url("my-style.css")
});

You can't currently use relative URLs in style sheets loaded with contentStyle or contentStyleFile. If you do, the files referenced by the relative URLs will not be found.

To learn more about this, and read about a workaround, see the relevant section in the page-mod API documentation.

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 the contentScriptWhen option.

To learn more about content scripts in general, see the content scripts guide.

Attachments

File Size Date Attached by
page-mod-ietf
20144 bytes 2013-12-06 13:40:54 Canuckistani

Document Tags and Contributors

Contributors to this page: Canuckistani, wbamberg, groovecoder, Delapouite
Last updated by: wbamberg,