# Modifying Web Pages Based on URL #
To follow this tutorial you'll need to have
[installed the SDK](dev-guide/tutorials/installation.html)
and learned the
[basics of `cfx`](dev-guide/tutorials/getting-started-with-cfx.html).
To modify any pages that match a particular pattern
(for example, "http://example.org/") as they are loaded, use the
[`page-mod`](modules/sdk/page-mod.html) 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 = ' +
' "
Page matches ruleset
";'
});
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`, then run `cfx run` again
* open [ietf.org](http://www.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`](modules/sdk/page-mod/match-pattern.html)
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`:
document.body.innerHTML = "Page matches ruleset
";
We can load this script by changing the page-mod code like this:
// 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("Page matches ruleset
");
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("Page matches ruleset
");'
});
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 an 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 = "" + message + "
";
});
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](modules/sdk/page-mod.html#Working_with_Relative_URLs_in_CSS_Rules).
## Learning More ##
To learn more about `page-mod`, see its
[API reference page](modules/sdk/page-mod.html).
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](https://developer.mozilla.org/en/DOM/window.onload)
fires. To control this behavior use the `contentScriptWhen` option.
To learn more about content scripts in general, see the
[content scripts guide](dev-guide/guides/content-scripts/index.html).