MDN may have intermittent access issues April 18 13:00 - April 19 01:00 UTC. See whistlepig.mozilla.org for all notifications.

mozilla

Revision 59009 of SpecialPowers

  • Revision slug: SpecialPowers
  • Revision title: SpecialPowers
  • Revision id: 59009
  • Created:
  • Creator: mdas
  • Is current revision? No
  • Comment 1 words added, 1 words removed

Revision Content

SpecialPowers is a set of APIs available to Mochitest tests. Mochitests are intended to be written like regular web pages. However, sometimes tests need to do things that regular web pages are not permitted to do for security reasons. In these cases, the SpecialPowers APIs can be used to perform specific actions outside of the reach of normal web pages.

If your Mochitest needs to perform a wide range of privileged actions, it should probably be a Chrome Mochitest instead.

Existing APIs

SpecialPowers.sanityCheck()

Returns the string "foo".

Preference APIs

See {{ interface("nsIPrefBranch") }} for documentation on the APIs that these methods use internally.

SpecialPowers.getBoolPref(aPrefName)

Get the value of the preference aPrefName as a boolean.

SpecialPowers.getIntPref(aPrefName)

Get the value of the preference aPrefName as an integer.

SpecialPowers.getCharPref(aPrefName)

Get the value of the preference aPrefName as a string.

SpecialPowers.getComplexValue(aPrefName)

Get the value of the preference aPrefName as an XPCOM object.

SpecialPowers.setBoolPref(aPrefName, aValue)

Set the value of the preference aPrefName to the boolean value aValue.

SpecialPowers.setIntPref(aPrefName, aValue)

Set the value of the preference aPrefName to the integer value aValue.

SpecialPowers.setCharPref(aPrefName, aValue)

Set the value of the preference aPrefName to the string value aValue.

SpecialPowers.setComplexValue(aPrefName, aValue)

Set the value of the preference aPrefName to the XPCOM object aValue.

SpecialPowers.clearUserPref(aPrefName)

Reset the preference aPrefName to its default value.

Event Listener APIs

SpecialPowers.addChromeEventListener(type, listener, capture, allowUntrusted)

Adds an event listener to the TabChildGlobal object.

SpecialPowers.removeChromeEventListener(type, listener, capture)

Removes an event listener from the TabChildGlobal object.

Other APIs

SpecialPowers.createSystemXHR()

Creates and returns a XMLHttpRequest instance which has full "system privileges". In other words, it can:

  • Make cross-site requests without any restrictions. I.e. the target server does not need to support CORS.
  • Set any header using xhr.setRequestHeader.
  • Read any response using xhr.getResponseHeader and xhr.getAllResponseHeaders.
  • Load and parse XUL contents using the xhr.responseXML property.
  • Make requests without a referer (sic) header. If you want a referer header set, you have to do so manually using xhr.setRequestHeader.

However, any document parsed by the xhr object and accessed through xhr.responseXML is created using a null principal and which limits what the document can do.

SpecialPowers.gc()

Forces a round of garbage collection to be performed.

SpecialPowers.MockFilePicker

After the completion of {{ bug|669953 }} , MockFilePicker will be available in SpecialPowers. This replaces the standard File Picker with one that can be controlled via script, for testing load and save code. To use it, add the following lines of code to your test:

var MockFilePicker = SpecialPowers.MockFilePicker;
MockFilePicker.reset(); // You must call reset before each test

This patch has examples of how to use the MockFilePicker, and also how to use it for XPCShell tests. The code in testing/mochitest/MockFilePicker.jsm might also be helpful.

Adding new APIs

If your test requires privileged functionality that's not currently present, you can add new APIs to the SpecialPowers object.

The SpecialPowers APIs are designed to be forwards-compatible with the Electrolysis project, so that they work when content is run in a separate process (such as in Firefox Mobile). Any changes you make must take this into account, or they will not be accepted.

Because of the support for out-of-process content, the SpecialPowers implementation is split into two separate files:

  • {{ Source("testing/mochitest/specialpowers/components/SpecialPowersObserver.js", "SpecialPowersObserver.js") }}, which is always run in the parent process.
  • {{ Source("testing/mochitest/specialpowers/content/specialpowers.js", "specialpowers.js") }}, which is a content script, and may be run in the content process.

Both files execute with chrome privileges, but certain XPCOM APIs are not available in content processes. Consult with an Electrolysis or Mobile peer if you are unsure if you can use a specific API there. You may also want to read the Message Manager documentation for more detailed information on how cross-process messaging works.

A Simple Example

Let's say you wanted to expose the numberOfScreens attribute from the {{ interface("nsIScreenManager") }} interface. Let's also suppose that this interface is not available in the content process, just to make our example more complete. To start, first you would need to define the new API on the SpecialPowers object that is exposed to content. This object is defined in {{ Source("testing/mochitest/specialpowers/content/specialpowers.js", "the content script") }}.

Since SpecialPowers is just a normal JavaScript object, you can add functions, attributes, getters and setters to it like any other object. It does use the special __exposedProps__ property to hide any property beginning with an underscore ("_") from content scripts, so names starting with an underscore function like private members.

Let's first add a numberOfScreens getter to SpecialPowers. It will simply send a blocking message to the chrome process, asking it to return the value in its response:

var SpecialPowers = {
// existing APIs
//...

// Provide nsIScreenManager.numberOfScreens
get numberOfScreens() {
// You could pass additional parameters in the second parameter, consult the message manager documentation for more details.
// Ideally this would be a memoizing getter, that's somewhat out of scope for this document.
return sendSyncMessage("SPNumberOfScreens", {})[0];
}
};

Now you need to provide a handler for this message in the {{ Source("testing/mochitest/specialpowers/components/SpecialPowersObserver.js", "chrome observer script") }}. In the SpecialPowersObserver.observe function, register your message underneath the existing messages:

// Register for any messages our API needs us to handle
messageManager.addMessageListener("SPPrefService", this);
messageManager.addMessageListener("SPNumberOfScreens", this);

Then, in the SpecialPowersObserver.receiveMessage function, add a branch to handle your new message and return the result:

receiveMessage: function(aMessage) {
switch(aMessage.name) {
 case "SPPrefService":
// existing code... case "SPNumberOfScreens": var screenManager = Components.classes["@mozilla.org/gfx/screenmanager;1"] .getService(Components.interfaces.nsIScreenManager);}
return screenManager.numberOfScreens;

default:

That's it! You will need to rebuild in the testing/mochitest directory in your object directory for your changes to show up, then you should be able to use your new SpecialPowers.numberOfScreens property in Mochitests.

Don't forget to document your new API on this page after you land it!

Revision Source

<p>SpecialPowers is a set of APIs available to <a href="/en/Mochitest" title="en/Mochitest">Mochitest</a> tests. Mochitests are intended to be written like regular web pages. However, sometimes tests need to do things that regular web pages are not permitted to do for security reasons. In these cases, the SpecialPowers APIs can be used to perform specific actions outside of the reach of normal web pages.</p>
<div class="note">If your Mochitest needs to perform a wide range of privileged actions, it should probably be a <a href="/en/Chrome_tests" title="en/Chrome tests">Chrome Mochitest</a> instead.</div>
<h2>Existing APIs</h2>
<h3>SpecialPowers.sanityCheck()</h3>
<p>Returns the string <code>"foo"</code>.</p>
<h3>Preference APIs</h3>
<p>See {{ interface("nsIPrefBranch") }} for documentation on the APIs that these methods use internally.</p>
<h4>SpecialPowers.getBoolPref(aPrefName)</h4>
<p>Get the value of the preference <code>aPrefName</code> as a boolean.</p>
<h4>SpecialPowers.getIntPref(aPrefName)</h4>
<p>Get the value of the preference <code>aPrefName</code> as an integer.</p>
<h4>SpecialPowers.getCharPref(aPrefName)</h4>
<p>Get the value of the preference <code>aPrefName</code> as a string.</p>
<h4>SpecialPowers.getComplexValue(aPrefName)</h4>
<p>Get the value of the preference <code>aPrefName</code> as an XPCOM object.</p>
<h4>SpecialPowers.setBoolPref(aPrefName, aValue)</h4>
<p>Set the value of the preference <code>aPrefName</code> to the boolean value <code>aValue</code>.</p>
<h4>SpecialPowers.setIntPref(aPrefName, aValue)</h4>
<p>Set the value of the preference <code>aPrefName</code> to the integer value <code>aValue</code>.</p>
<h4>SpecialPowers.setCharPref(aPrefName, aValue)</h4>
<p>Set the value of the preference <code>aPrefName</code> to the string value <code>aValue</code>.</p>
<h4>SpecialPowers.setComplexValue(aPrefName, aValue)</h4>
<p>Set the value of the preference <code>aPrefName</code> to the XPCOM object <code>aValue</code>.</p>
<h4>SpecialPowers.clearUserPref(aPrefName)</h4>
<p>Reset the preference <code>aPrefName </code>to its default value.</p>
<h3>Event Listener APIs</h3>
<h4>SpecialPowers.addChromeEventListener(type, listener, capture, allowUntrusted)</h4>
<p>Adds an event listener to the TabChildGlobal object.</p>
<h4>SpecialPowers.removeChromeEventListener(type, listener, capture)</h4>
<p>Removes an event listener from the TabChildGlobal object.</p>
<h3>Other APIs</h3>
<h4>SpecialPowers.createSystemXHR()</h4>
<p>Creates and returns a XMLHttpRequest instance which has full "system privileges". In other words, it can:</p>
<ul> <li>Make cross-site requests without any restrictions. I.e. the target server does not need to support CORS.</li> <li>Set any header using xhr.setRequestHeader.</li> <li>Read any response using xhr.getResponseHeader and xhr.getAllResponseHeaders.</li> <li>Load and parse XUL contents using the xhr.responseXML property.</li> <li>Make requests without a <code>referer</code> (sic) header. If you want a <code>referer</code> header set, you have to do so manually using xhr.setRequestHeader.</li>
</ul>
<p>However, any document parsed by the xhr object and accessed through xhr.responseXML is created using a null principal and which limits what the document can do.</p>
<h4>SpecialPowers.gc()</h4>
<p>Forces a round of garbage collection to be performed.</p>
<h4>SpecialPowers.MockFilePicker</h4>
<p>After the completion of {{ bug|669953 }} , MockFilePicker will be available in SpecialPowers. This replaces the standard File Picker with one that can be controlled via script, for testing load and save code. To use it, add the following lines of code to your test:</p>
<pre class="brush: js">var MockFilePicker = SpecialPowers.MockFilePicker;
MockFilePicker.reset(); // You must call reset before each test
</pre>
<p><a class=" link-https" href="https://bugzilla.mozilla.org/attachment.cgi?id=544963&amp;action=diff" title="https://bugzilla.mozilla.org/attachment.cgi?id=544963&amp;action=diff">This patch</a> has examples of how to use the MockFilePicker, and also how to use it for XPCShell tests. The code in <code>testing/mochitest/MockFilePicker.jsm</code> might also be helpful.</p><h2>Adding new APIs</h2>
<p>If your test requires privileged functionality that's not currently present, you can add new APIs to the SpecialPowers object.</p>
<div class="note">The SpecialPowers APIs are designed to be forwards-compatible with the Electrolysis project, so that they work when content is run in a separate process (such as in Firefox Mobile). Any changes you make must take this into account, or they will not be accepted.</div>
<p>Because of the support for out-of-process content, the SpecialPowers implementation is split into two separate files:</p>
<ul> <li>{{ Source("testing/mochitest/specialpowers/components/SpecialPowersObserver.js", "SpecialPowersObserver.js") }}, which is always run in the parent process.</li> <li>{{ Source("testing/mochitest/specialpowers/content/specialpowers.js", "specialpowers.js") }}, which is a <a href="/en/The_message_manager#The_content_script" title="en/The message manager#The content script">content script</a>, and may be run in the content process.</li>
</ul>
<p>Both files execute with chrome privileges, but certain XPCOM APIs are not available in content processes. Consult with an Electrolysis or Mobile peer if you are unsure if you can use a specific API there. You may also want to read the <a href="/en/The_message_manager" title="en/The message manager">Message Manager</a> documentation for more detailed information on how cross-process messaging works.</p>
<h3>A Simple Example</h3>
<p>Let's say you wanted to expose the <code>numberOfScreens</code> attribute from the {{ interface("nsIScreenManager") }} interface. Let's also suppose that this interface is not available in the content process, just to make our example more complete. To start, first you would need to define the new API on the <code>SpecialPowers</code> object that is exposed to content. This object is defined in {{ Source("testing/mochitest/specialpowers/content/specialpowers.js", "the content script") }}.</p>
<div class="note">Since <code>SpecialPowers</code> is just a normal JavaScript object, you can add functions, attributes, getters and setters to it like any other object. It does use the special<span id="the-code"><span class="a"> <code>__exposedProps__</code> property to hide any property beginning with an underscore ("_") from content scripts, so names starting with an underscore function like private members.<br>
</span></span></div>
<p>Let's first add a <code>numberOfScreens</code> getter to <code>SpecialPowers</code>. It will simply send a blocking message to the chrome process, asking it to return the value in its response:</p>
<pre lang="en"><span id="the-code"><span class="v">var </span><a class="d" href="http://mxr.mozilla.org/mozilla-central/ident?i=SpecialPowers">SpecialPowers</a> = {<br>  // existing APIs<br>  //...<br><br></span><span id="the-code">  // Provide nsIScreenManager.numberOfScreens<br>  get numberOfScreens() {<br>    // You could pass additional parameters in the second parameter, consult the <a href="mks://localhost/en/The_message_manager" title="en/The message manager">message manager</a> documentation for more details.<br>    // Ideally this would be a memoizing getter, that's somewhat out of scope for this document.<br>    return sendSyncMessage("SPNumberOfScreens", {})[0];<br>  }</span>
<span id="the-code">};<br></span></pre>
<p>Now you need to provide a handler for this message in the {{ Source("testing/mochitest/specialpowers/components/SpecialPowersObserver.js", "chrome observer script") }}. In the <span id="the-code"><code>SpecialPowersObserver.observe</code> function, register your message underneath the existing messages:</span></p>
<pre lang="en"><span id="the-code"><span class="c">// Register for any messages our API needs us to handle<br></span></span><span id="the-code"><a class="d" href="http://mxr.mozilla.org/mozilla-central/ident?i=messageManager">messageManager</a>.<a class="d" href="http://mxr.mozilla.org/mozilla-central/ident?i=addMessageListener">addMessageListener</a>(<span class="s">"SPPrefService"</span>, <span class="v">this)</span>;<br></span><span id="the-code"><a class="d" href="http://mxr.mozilla.org/mozilla-central/ident?i=messageManager">messageManager</a>.<a class="d" href="http://mxr.mozilla.org/mozilla-central/ident?i=addMessageListener">addMessageListener</a>(<span class="s">"SPNumberOfScreens"</span>, <span class="v">this)</span>;</span>
</pre>
<p>Then, in the <code><span id="the-code">SpecialPowersObserver.receiveMessage</span></code> function, add a branch to handle your new message and return the result:</p>
<pre lang="en"><span id="the-code">receiveMessage: <span class="v">function(</span>aMessage) {<br></span><span id="the-code"><span class="v">  switch(aMessage.name) </span>{<br>    <span class="v">case </span><span class="s">"SPPrefService"</span>:</span>
    // existing code...

    case "SPNumberOfScreens":
      var screenManager = Components.classes["@mozilla.org/gfx/screenmanager;1"]
                    .getService(Components.interfaces.nsIScreenManager);<span id="the-code">}<br>      return screenManager.numberOfScreens;<br><br>    default:<br></span></pre>
<p>That's it! You will need to rebuild in the testing/mochitest directory in your object directory for your changes to show up, then you should be able to use your new <code>SpecialPowers.numberOfScreens</code> property in Mochitests.</p>
<div class="note">Don't forget to document your new API on this page after you land it!</div>
Revert to this revision