The `page-mod` module enables you to run scripts in the context of
specific web pages. To use it, you specify:
* one or more scripts to attach. The SDK calls these scripts "content scripts".
* a pattern that a page's URL must match, in order for the script(s)
to be attached to that page.
For example, the following add-on displays an alert whenever the user
visits any page hosted at "mozilla.org":
var pageMod = require("sdk/page-mod");
pageMod.PageMod({
include: "*.mozilla.org",
contentScript: 'window.alert("Page matches ruleset");'
});
You can modify the document in your script:
var pageMod = require("sdk/page-mod");
pageMod.PageMod({
include: "*.mozilla.org",
contentScript: 'document.body.innerHTML = ' +
' "
Page matches ruleset
";'
});
You can supply the content script(s) in one of two ways:
* as a string literal, or an array of string literals, assigned to the `contentScript` option, as above
* as separate files supplied in your add-on's "data" directory.
In this case files are specified by a URL typically constructed using the
`url()` method of the
[`self` module's `data` object](modules/sdk/self.html#data):
var data = require("sdk/self").data;
var pageMod = require("sdk/page-mod");
pageMod.PageMod({
include: "*.mozilla.org",
contentScriptFile: data.url("my-script.js")
});
var data = require("sdk/self").data;
var pageMod = require("sdk/page-mod");
pageMod.PageMod({
include: "*.mozilla.org",
contentScriptFile: [self.data.url("jquery-1.7.min.js"),
self.data.url("my-script.js")]
});
Unless your content script is extremely simple and consists only of a
static string, don't use contentScript: if you do, you may
have problems getting your add-on approved on AMO.
Instead, keep the script in a separate file and load it using
contentScriptFile. This makes your code easier to maintain,
secure, debug and review.
A page-mod only attaches scripts to documents loaded in tabs. It will not
attach scripts to add-on panels, page-workers, widgets, or Firefox hidden
windows.
To stop a page-mod from making any more modifications, call its `destroy()`
method.
The `PageMod` constructor takes a number of other options to control its
behavior, all documented in detail in the
[API Reference](modules/sdk/page-mod.html#API Reference) section below:
* `contentStyle` or `contentStyleFile` list stylesheets to attach.
* `contentScriptOptions` defines read-only values accessible to content
scripts.
* `attachTo` controls whether to attach scripts to tabs
that were already open when the page-mod was created, and whether to attach
scripts to iframes as well as the topmost document.
* `contentScriptWhen` controls the point during document load at which content
scripts are attached.
For all the details on content scripts, see the
[guide to content scripts](dev-guide/guides/content-scripts/index.html).
## Communicating With Content Scripts ##
Your add-on's "main.js" can't directly access the state of content scripts
you load, but you can communicate between your add-on and its content scripts
by exchanging messages.
To do this, you'll need to listen to the page-mod's `attach` event.
This event is triggered every time the page-mod's content script is attached
to a document. The listener is passed a
[`worker`](modules/sdk/content/worker.html) object that your add-on
can use to send and receive messages.
For example, the following add-on retrieves the HTML content of specific
tags from documents that match the pattern. The main add-on code sends the
desired tag to the content script, and the content script replies by sending
the HTML content of all the elements with that tag.
/lib/main.js:
var tag = "p";
var data = require("sdk/self").data;
var pageMod = require("sdk/page-mod");
pageMod.PageMod({
include: "*.mozilla.org",
contentScriptFile: data.url("element-getter.js"),
onAttach: function(worker) {
worker.port.emit("getElements", tag);
worker.port.on("gotElement", function(elementContent) {
console.log(elementContent);
});
}
});
/data/element-getter.js:
self.port.on("getElements", function(tag) {
var elements = document.getElementsByTagName(tag);
for (var i = 0; i < elements.length; i++) {
self.port.emit("gotElement", elements[i].innerHTML);
}
});
When the user loads a document hosted at "mozilla.org":
* The content script "element-getter.js" is attached to the document
and runs. It adds a listener to the `getElements` message.
* The `attach` event is sent to the "main.js" code. Its event handler sends
the `getElements` message to the content script, and then adds a listener
to the `gotElement` message.
* The content script receives the `getElements` message, retrieves all
elements of that type, and for each element sends a `gotElement` message
containing the element's `innerHTML`.
* The "main.js" code receives each `gotElement` message and logs the
contents.
If multiple documents that match the page-mod's `include` pattern are loaded,
then each document is loaded into its own execution context with its own copy
of the content scripts. In this case the listener assigned to `onAttach`
is called once for each loaded document, and the add-on code will have a
separate worker for each document.
To learn much more about communicating with content scripts, see the
[guide to content scripts](dev-guide/guides/content-scripts/index.html) and in
particular the chapter on
[communicating using `port`](dev-guide/guides/content-scripts/using-port.html).
## Mapping Workers to Tabs ##
The `worker` has a `tab` property which returns the tab associated with
this worker. You can use this to access
the [`tabs API`](modules/sdk/tabs.html) for the tab associated
with a specific document:
var pageMod = require("sdk/page-mod");
var tabs = require("sdk/tabs");
pageMod.PageMod({
include: ["*"],
onAttach: function onAttach(worker) {
console.log(worker.tab.title);
}
});
## Destroying Workers ##
Workers generate a `detach` event when their associated document is closed:
that is, when the tab is closed or the tab's location changes. If
you are maintaining a list of workers belonging to a page-mod, you can use
this event to remove workers that are no longer valid.
For example, if you maintain a list of workers attached to a page-mod:
var workers = [];
var pageMod = require("sdk/page-mod").PageMod({
include: ['*'],
contentScriptWhen: 'ready',
contentScriptFile: data.url('pagemod.js'),
onAttach: function(worker) {
workers.push(worker);
}
});
You can remove workers when they are no longer valid by listening to `detach`:
var workers = [];
function detachWorker(worker, workerArray) {
var index = workerArray.indexOf(worker);
if(index != -1) {
workerArray.splice(index, 1);
}
}
var pageMod = require("sdk/page-mod").PageMod({
include: ['*'],
contentScriptWhen: 'ready',
contentScriptFile: data.url('pagemod.js'),
onAttach: function(worker) {
workers.push(worker);
worker.on('detach', function () {
detachWorker(this, workers);
});
}
});
## Attaching Content Scripts to Tabs ##
We've seen that the page-mod API attaches content scripts to documents based
on their URL. Sometimes, though, we don't care about the URL: we just want
to execute a script on demand in the context of a particular tab.
For example, we might want to run a script in the context of the currently
active tab when the user clicks a widget: to block certain content, to
change the font style, or to display the document's DOM structure.
Using the `attach` method of the [`tab`](modules/sdk/tabs.html)
object, you can attach a set of content scripts to a particular tab. The
scripts are executed immediately.
The following add-on creates a widget which, when clicked, highlights all the
`div` elements in the document loaded into the active tab:
var widgets = require("sdk/widget");
var tabs = require("sdk/tabs");
var widget = widgets.Widget({
id: "div-show",
label: "Show divs",
contentURL: "http://www.mozilla.org/favicon.ico",
onClick: function() {
tabs.activeTab.attach({
contentScript:
'var divs = document.getElementsByTagName("div");' +
'for (var i = 0; i < divs.length; ++i) {' +
'divs[i].setAttribute("style", "border: solid red 1px;");' +
'}'
});
}
});
@class
A page-mod object. Once created a page-mod will execute the supplied content
scripts, and load any supplied stylesheets, in the context of any documents
matching the pattern specified by the `include` property.
@constructor
Creates a page-mod.
@param options {object}
Options for the page-mod. All these options are optional except for `include`
(although if you don't supply any scripts or stylesheets, your page-mod
won't be very useful).
@prop include {string,array}
A match pattern string or an array of match pattern strings. These define
the documents to which the page-mod applies. At least one match pattern
must be supplied.
You can specify a URL exactly:
var pageMod = require("sdk/page-mod");
pageMod.PageMod({
include: "http://www.iana.org/domains/example/",
contentScript: 'window.alert("Page matches ruleset");'
});
You can specify a number of wildcard forms, for example:
var pageMod = require("sdk/page-mod");
pageMod.PageMod({
include: "*.mozilla.org",
contentScript: 'window.alert("Page matches ruleset");'
});
You can specify a set of URLs using a
[regular expression](modules/sdk/page-mod/match-pattern.html#Regular Expressions).
The pattern must match the entire URL, not just a subset, and has
`global`, `ignoreCase`, and `multiline` disabled.
var pageMod = require("sdk/page-mod");
pageMod.PageMod({
include: /.*developer.*/,
contentScript: 'window.alert("Page matches ruleset");'
});
To specify multiple patterns, pass an array of match patterns:
var pageMod = require("sdk/page-mod");
pageMod.PageMod({
include: ["*.developer.mozilla.org", "*.addons.mozilla.org"],
contentScript: 'window.alert("Page matches ruleset");'
});
See the [match-pattern](modules/sdk/page-mod/match-pattern.html) module for
a detailed description of match pattern syntax.
@prop [contentScriptFile] {string,array}
This option specifies one or more content scripts to attach to targeted
documents.
Each script is supplied as a separate file under your add-on's "data"
directory, and is specified by a URL typically constructed using the
`url()` method of the
[`self` module's `data` object](modules/sdk/self.html#data).
var data = require("sdk/self").data;
var pageMod = require("sdk/page-mod");
pageMod.PageMod({
include: "*",
contentScriptFile: data.url("my-script.js")
});
To attach multiple scripts, pass an array of URLs.
var data = require("sdk/self").data;
var pageMod = require("sdk/page-mod");
pageMod.PageMod({
include: "*",
contentScriptFile: [self.data.url("jquery-1.7.min.js"),
self.data.url("my-script.js")]
});
Content scripts specified using this option are loaded before those
specified by the `contentScript` option.
@prop [contentScript] {string,array}
This option specifies one or more content scripts to attach to targeted
documents. Each script is supplied directly as a single string:
var pageMod = require("sdk/page-mod");
pageMod.PageMod({
include: "*",
contentScript: 'window.alert("Page matches ruleset");'
});
To attach multiple scripts, supply an array of strings.
Content scripts specified by this option are loaded after those
specified by the `contentScriptFile` option.
Unless your content script is extremely simple and consists only of a
static string, don't use contentScript: if you do, you may
have problems getting your add-on approved on AMO.
Instead, keep the script in a separate file and load it using
contentScriptFile. This makes your code easier to maintain,
secure, debug and review.
@prop [contentScriptWhen="end"] {string}
By default, content scripts are attached after all the content
(DOM, JS, CSS, images) for the document has been loaded, at the time the
[window.onload event](https://developer.mozilla.org/en/DOM/window.onload)
fires. Using this option you can customize this behavior.
The option takes a single string that may take any one of the following
values:
* `"start"`: load content scripts immediately after the document
element is inserted into the DOM, but before the DOM content
itself has been loaded
* `"ready"`: load content scripts once DOM content has been loaded,
corresponding to the
[DOMContentLoaded](https://developer.mozilla.org/en/Gecko-Specific_DOM_Events)
event
* `"end"`: the default. Load content scripts once all the content
(DOM, JS, CSS, images) has been loaded, at the time the
[window.onload event](https://developer.mozilla.org/en/DOM/window.onload)
fires
var pageMod = require("sdk/page-mod");
pageMod.PageMod({
include: "*",
contentScript: 'window.alert("Page matches ruleset");',
contentScriptWhen: "start"
});
If you specify `"start"` for `contentScriptWhen`, your scripts will not be
able to interact with the document's DOM right away (although they could
listen for `window.onload` or `DOMContentLoaded` themselves).
@prop [contentScriptOptions] {object}
You can use this option to define some read-only values for your content
scripts.
The option consists of an object literal listing `name:value` pairs for
the values you want to provide to the content script. For example:
var data = require("sdk/self").data;
var pageMod = require("sdk/page-mod");
pageMod.PageMod({
include: "*",
contentScriptFile: data.url("my-script.js"),
contentScriptOptions: {
showOptions: true,
someNumbers: [1, 2],
greeting: "Hello!"
}
});
The values are accessible to content scripts via the `self.options`
property:
// my-script.js
if (self.options.showOptions) {
window.alert(self.options.greeting);
window.alert(self.options.someNumbers[0] + self.options.someNumbers[1]);
}
The values can be any JSON-serializable value: a string, number,
boolean, null, array of JSON-serializable values, or an object whose
property values are themselves JSON-serializable. This means you can't send
functions, and if the object contains methods they won't be usable. You
also can't pass cyclic values.
@prop [contentStyleFile] {string,array}
Use this option to load one or more stylesheets into the targeted
documents as
[user stylesheets](https://developer.mozilla.org/en/CSS/Getting_Started/Cascading_and_inheritance).
Each stylesheet is supplied as a separate file under your add-on's "data"
directory, and is specified by a URL typically constructed using the
`url()` method of the
[`self` module's `data` object](modules/sdk/self.html#data).
To add multiple stylesheet files, pass an array of URLs.
var data = require("sdk/self").data;
var pageMod = require("sdk/page-mod");
pageMod.PageMod({
include: "*.org",
contentStyleFile: data.url("my-page-mod.css")
});
Content styles specified by this
option are loaded before those specified by the `contentStyle` option.
You can't currently use relative URLs in stylesheets loaded in this way.
For example, consider a CSS file that references an external file like this:
background: rgb(249, 249, 249) url('../../img/my-background.png') repeat-x top center;
If you attach this file using `contentStyleFile`, "my-background.png"
will not be found.
As a workaround for this, you can build an absolute URL to a file in your
"data" directory, and construct a `contentStyle` option embedding that URL
in your rule. For example:
var data = require("sdk/self").data;
var pageMod = require("sdk/page-mod").PageMod({
include: "*",
contentStyleFile: data.url("my-style.css"),
// contentStyle is built dynamically here to include an absolute URL
// for the file referenced by this CSS rule.
// This workaround is needed because we can't use relative URLs
// in contentStyleFile or contentStyle.
contentStyle: "h1 { background: url(" + data.url("my-pic.jpg") + ")}"
});
This add-on uses a separate file "my-style.css", loaded using
`contentStyleFile`, for all CSS rules except those that reference
an external file. For the rule that needs to refer to "my-pic.jpg",
which is stored in the add-on's "data" directory, it uses `contentStyle`.
Dynamically constructing code strings like those assigned to `contentScript`
or `contentStyle` is usually considered an unsafe practice that may cause an
add-on to fail AMO review. In this case it is safe, and should be allowed,
but including a comment like that in the example above will help to
prevent any misunderstanding.
@prop [contentStyle] {string,array}
Use this option to load one or more stylesheet rules into
the targeted documents.
Each stylesheet rule is supplied as a separate string. To supply
multiple rules, pass an array of strings:
var pageMod = require("sdk/page-mod");
pageMod.PageMod({
include: "*.org",
contentStyle: [
"div { padding: 10px; border: 1px solid silver}",
"img { display: none}"
]
});
Content styles specified by this
option are loaded after those specified by the `contentStyleFile` option.
@prop [attachTo] {string,array}
If this option is not specified, content scripts:
* are not attached to any tabs that are already open when the page-mod is
created.
* are attached to all documents whose URL matches the rule: so if your
rule matches a specific hostname and path, and the topmost document that
satisfies the rule includes ten iframes using a relative URL, then your
page-mod is applied eleven times.
You can modify this behavior using the `attachTo` option.
It accepts the following values:
* `"existing"`: the page-mod will be automatically applied on already
opened tabs.
* `"top"`: the page-mod will be applied to top-level tab documents
* `"frame"`: the page-mod will be applied to all iframes inside tab
documents
If the option is set at all, you must set at least one of `"top"` and
`"frame"`.
For example, the following page-mod will be attached to already opened
tabs, but not to any iframes:
var pageMod = require("sdk/page-mod");
pageMod.PageMod({
include: "*",
contentScript: "",
attachTo: ["existing", "top"],
onAttach: function(worker) {
console.log("attached to: " + worker.tab.url);
}
});
@prop [onAttach] {function}
Assign a listener function to this option to listen to the page-mod's
`attach` event. See the
[documentation for `attach`](modules/sdk/page-mod.html#attach) and
[Communicating With Content Scripts](modules/sdk/page-mod.html#Communicating With Content Scripts).
@property {List}
A [list](modules/sdk/util/list.html) of match pattern strings. These
define the documents to which the page-mod applies. See the documentation of
the `include` option above for details of `include` syntax.
Rules can be added to the list by calling its
`add` method and removed by calling its `remove` method.
@method
Stops the page-mod from making any more modifications. Once destroyed
the page-mod can no longer be used.
Modifications already made to open documents by content scripts
will not be undone, but stylesheets added by `contentStyle` or
`contentStyleFile`, will be unregistered immediately.
@event
This event is emitted when the page-mod's content scripts are
attached to a document whose URL matches the page-mod's `include` pattern.
The listener function is passed a
[`worker`](modules/sdk/content/worker.html) object that you
can use to
[communicate with the content scripts](modules/sdk/page-mod.html#Communicating With Content Scripts) your page-mod has
loaded into this particular document.
The `attach` event is triggered every time this page-mod's content
scripts are attached to a document. So if the user loads several
documents which match this page-mod's `include` pattern, `attach` will be
triggered for each document, each time with a distinct `worker` instance.
Each worker then represents a channel of communication with the set of
content scripts loaded by this particular page-mod into that
particular document.
@argument {Worker}
The listener function is passed a [`Worker`](modules/sdk/content/worker.html)
object that can be used to communicate with any content scripts
attached to this document.
@event
This event is emitted when an uncaught runtime error occurs in one of the
page-mod's content scripts.
@argument {Error}
Listeners are passed a single argument, the
[Error](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error)
object.