Process scripts

Process scripts are new in Firefox 38.

When you need to run code in the content process in order to access web content, then you should use frame scripts. However, a problem with frame scripts is that they can be loaded multiple times in the content process, in multiple scopes that are insulated from each other. For example, if you call the global frame message manager's loadFrameScript() function, then the script will be loaded separately into all open tabs. This can then cause a problem the frame scripts are interacting with a global service in the content process.

For example, in multiprocess Firefox, if you need to use nsIContentPolicy to register a content policy, you must do this in the content process. But if you register it in a frame script, and the frame script is loaded more than once, you'll register the content policy more than once, which probably isn't what you intend.

Similarly, some observer notifications must be registered in the content process, but if you do this in a frame script, and the frame script is loaded more than once, then you will get multiple notifications for that event.

The solution in these cases is to use a process script instead of a frame script.

You can load a process script by accessing a parent process message manager and calling its loadProcessScript() function. The following code uses the global parent process message manager, which will load the script into the the chrome process and any child processes:

// chrome code
let ppmm = Cc[";1"]
ppmm.loadProcessScript("chrome://whatever/process-script.js", true);
ppmm.addMessageListener("Hello", function(msg) { ... });

The process script's global is a ContentProcessMessageManager object, which enables the process script to receive messages from the chrome side, and to send messages to the chrome side:

// process-script.js
if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
  dump("Welcome to the process script in a content process");
} else {
  dump("Welcome to the process script in the main process");

// Message is sent using ContentProcessMessageManager

In this example, the dump() statement will run once in each content process as well as in the main process. This example also figures out whether it is running in the chrome or in a content process by looking at the process type in Services.appinfo.

In just about every respect, using process scripts is like using frame scripts:

  • you can pass aAllowDelayedLoad to loadProcessScript(). If you do, you must call removeDelayedProcessScript() when your extension is disabled or removed
  • the message-passing APIs are the same: sendAsyncMessage() is available in both directions, while sendSyncMessage() is available from content to chrome only
  • process scripts are system-privileged, and have access to the Components object. However, process scripts are subject to the same kinds of limitations as frame scripts (for example, no file system access).
  • process scripts stay loaded until their host process is closed.
  • the environment for process scripts is similar to that for frame scripts. However, you don't get access to web content or DOM events from a process script.