Merge mozilla-central into build-system

This commit is contained in:
Gregory Szorc 2013-02-22 08:15:12 -08:00
commit e18a5a8dfd
261 changed files with 7970 additions and 9096 deletions

View File

@ -172,8 +172,8 @@ HTMLRadioButtonAccessible::GetPositionAndSizeInternal(int32_t* aPosInSet,
if (inputElm->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
type, eCaseMatters) &&
inputElm->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name,
name, eCaseMatters)) {
count++;
name, eCaseMatters) && mDoc->HasAccessible(inputElm)) {
count++;
if (inputElm == mContent)
indexOf = count;
}

View File

@ -40,6 +40,10 @@
testGroupAttrs("radio3", 1, 2);
testGroupAttrs("radio4", 2, 2);
//////////////////////////////////////////////////////////////////////////
// Hidden HTML input@type="radio"
testGroupAttrs("radio5", 1, 1);
//////////////////////////////////////////////////////////////////////////
// HTML ul/ol
testGroupAttrs("li1", 1, 3);
@ -319,5 +323,9 @@
<li id="combo1_opt4" role="option">JoeSentMe</li>
</ul>
<form>
<input type="radio" style="display: none;" name="group3">
<input type="radio" id="radio5" name="group3">
</form>
</body>
</html>

View File

@ -38,8 +38,8 @@ Bugs
* file a bug: https://bugzilla.mozilla.org/enter_bug.cgi?product=Add-on%20SDK
Style Guidelines
--------------------
* https://github.com/mozilla/addon-sdk/wiki/Coding-style-guide
* https://github.com/mozilla/addon-sdk/wiki/Coding-style-guide

View File

@ -55,29 +55,6 @@ function readURI(uri) {
return data;
}
// Utility function that converts cfx-py generated paths to a
// module ids.
function path2id(path) {
// Strips out `/lib` and `.js` from package/lib/path.js
return path.replace(/([^\/]*)\/lib/, '$1').replace(/.js$/, '');
}
// Utility function that takes old manifest format and creates a manifest
// in a new format: https://github.com/mozilla/addon-sdk/wiki/JEP-Linker
function manifestV2(manifest) {
return Object.keys(manifest).reduce(function(result, path) {
let entry = manifest[path];
let id = path2id(path);
let requirements = entry.requirements || {};
result[id] = {
requirements: Object.keys(requirements).reduce(function(result, path) {
result[path] = path2id(requirements[path].path);
return result;
}, {})
};
return result
}, {});
}
// We don't do anything on install & uninstall yet, but in a future
// we should allow add-ons to cleanup after uninstall.
function install(data, reason) {}
@ -97,6 +74,15 @@ function startup(data, reasonCode) {
let id = options.jetpackID;
let name = options.name;
// Clean the metadata
options.metadata[name]['permissions'] = options.metadata[name]['permissions'] || {};
// freeze the permissionss
Object.freeze(options.metadata[name]['permissions']);
// freeze the metadata
Object.freeze(options.metadata[name]);
// Register a new resource 'domain' for this addon which is mapping to
// XPI's `resources` folder.
// Generate the domain name by using jetpack ID, which is the extension ID
@ -115,19 +101,35 @@ function startup(data, reasonCode) {
resourceHandler.setSubstitution(domain, resourcesURI);
// Create path to URLs mapping supported by loader.
let paths = Object.keys(options.metadata).reduce(function(result, name) {
result[name + '/'] = prefixURI + name + '/lib/'
result[name + '/tests/'] = prefixURI + name + '/tests/'
return result
}, {
let paths = {
// Relative modules resolve to add-on package lib
'./': prefixURI + name + '/lib/',
'toolkit/': 'resource://gre/modules/toolkit/',
'': 'resources:///modules/'
});
'./tests/': prefixURI + name + '/tests/',
'': 'resource://gre/modules/commonjs/'
};
// Maps addon lib and tests ressource folders for each package
paths = Object.keys(options.metadata).reduce(function(result, name) {
result[name + '/'] = prefixURI + name + '/lib/'
result[name + '/tests/'] = prefixURI + name + '/tests/'
return result;
}, paths);
// We need to map tests folder when we run sdk tests whose package name
// is stripped
if (name == 'addon-sdk')
paths['tests/'] = prefixURI + name + '/tests/';
// Maps sdk module folders to their resource folder
paths['sdk/'] = prefixURI + 'addon-sdk/lib/sdk/';
paths['toolkit/'] = prefixURI + 'addon-sdk/lib/toolkit/';
// test.js is usually found in root commonjs or SDK_ROOT/lib/ folder,
// so that it isn't shipped in the xpi. Keep a copy of it in sdk/ folder
// until we no longer support SDK modules in XPI:
paths['test'] = prefixURI + 'addon-sdk/lib/sdk/test.js';
// Make version 2 of the manifest
let manifest = manifestV2(options.manifest);
let manifest = options.manifest;
// Import `cuddlefish.js` module using a Sandbox and bootstrap loader.
let cuddlefishURI = prefixURI + options.loader;
@ -136,7 +138,7 @@ function startup(data, reasonCode) {
// Normalize `options.mainPath` so that it looks like one that will come
// in a new version of linker.
let main = path2id(options.mainPath);
let main = options.mainPath;
unload = cuddlefish.unload;
loader = cuddlefish.Loader({
@ -180,7 +182,7 @@ function startup(data, reasonCode) {
}
});
let module = cuddlefish.Module('addon-sdk/sdk/loader/cuddlefish', cuddlefishURI);
let module = cuddlefish.Module('sdk/loader/cuddlefish', cuddlefishURI);
let require = cuddlefish.Require(loader, module);
require('sdk/addon/runner').startup(reason, {
@ -189,7 +191,8 @@ function startup(data, reasonCode) {
prefsURI: rootURI + 'defaults/preferences/prefs.js'
});
} catch (error) {
dump('Bootstrap error: ' + error.message + '\n' +
dump('Bootstrap error: ' +
(error.message ? error.message : String(error)) + '\n' +
(error.stack || error.fileName + ': ' + error.lineNumber) + '\n');
throw error;
}
@ -236,12 +239,16 @@ function shutdown(data, reasonCode) {
if (loader) {
unload(loader, reason);
unload = null;
// Avoid leaking all modules when something goes wrong with one particular
// module. Do not clean it up immediatly in order to allow executing some
// actions on addon disabling.
// We need to keep a reference to the timer, otherwise it is collected
// and won't ever fire.
nukeTimer = setTimeout(nukeModules, 1000);
// Don't waste time cleaning up if the application is shutting down
if (reason != "shutdown") {
// Avoid leaking all modules when something goes wrong with one particular
// module. Do not clean it up immediatly in order to allow executing some
// actions on addon disabling.
// We need to keep a reference to the timer, otherwise it is collected
// and won't ever fire.
nukeTimer = setTimeout(nukeModules, 1000);
}
}
};

View File

@ -18,7 +18,7 @@
<Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
<em:minVersion>18.0</em:minVersion>
<em:maxVersion>20.*</em:maxVersion>
<em:maxVersion>21.0a1</em:maxVersion>
</Description>
</em:targetApplication>

View File

@ -10,9 +10,11 @@
<body>
<p id="paragraph">Lorem ipsum dolor sit amet.</p>
<script>
addon.port.on('addon-to-document', function (arg) {
addon.port.emit('document-to-addon', arg);
});
if ("addon" in window) {
addon.port.on('addon-to-document', function (arg) {
addon.port.emit('document-to-addon', arg);
});
}
</script>
</body>
</html>

View File

@ -11,58 +11,144 @@ We'd like to thank our many Jetpack project contributors! They include:
* arky
* [Heather Arthur](https://github.com/harthur)
* Dietrich Ayala
<!--end-->
* [Romain B](https://github.com/Niamor)
* [Louis-Rémi Babé](https://github.com/louisremi)
* Will Bamberg
* Thomas Bassetto
* Tomaz Bevec
* Zbigniew Braniecki
* Daniel Buchner
* James Burke
<!--end-->
* [Shane Caraveo](https://github.com/mixedpuppy)
* [Matěj Cepl](https://github.com/mcepl)
* Marc Chevrier
* Hernán Rodriguez Colmeiro
* [David Creswick](https://github.com/dcrewi)
<!--end-->
* dexter
* Christopher Dorn
* Connor Dunn
* dynamis
<!--end-->
* [Matteo Ferretti (ZER0)](https://github.com/ZER0)
* fuzzykiller
<!--end-->
* [Marcio Galli](https://github.com/taboca)
* [Ben Gillbanks](http://www.iconfinder.com/browse/iconset/circular_icons/)
* Felipe Gomes
* Irakli Gozalishvili
* Luca Greco
* Jeff Griffiths
* [David Guo](https://github.com/dglol)
<!--end-->
* Mark Hammond
* Mark A. Hershberger
* Lloyd Hilaiel
* Bobby Holley
<!--end-->
* Shun Ikejima
<!--end-->
* Eric H. Jung
<!--end-->
* Hrishikesh Kale
* Wes Kocher
* Lajos Koszti
<!--end-->
* Edward Lee
* Gregg Lind
<!--end-->
* [Nils Maier](https://github.com/nmaier)
* Gervase Markham
* Dave Mason
* Myk Melez
* Zandr Milewski
* Noelle Murata
<!--end-->
* Siavash Askari Nasr
* Joe R. Nassimian ([placidrage](https://github.com/placidrage))
* Dương H. Nguyễn
* Nick Nguyen
<!--end-->
* [ongaeshi](https://github.com/ongaeshi)
* Paul OShannessy
* l.m.orchard
* Les Orchard
<!--end-->
* Robert Pankowecki
* Alexandre Poirot
* Nickolay Ponomarev
<!--end-->
* Aza Raskin
<!--end-->
* Till Schneidereit
* Justin Scott
* Ayan Shah
* [skratchdot](https://github.com/skratchdot)
* Henrik Skupin
* slash
* Markus Stange
* Dan Stevens
* [Mihai Sucan](https://github.com/mihaisucan)
<!--end-->
* taku0
* Clint Talbert
* Thomas
* Tim Taubert
* Shane Tomlinson
* Dave Townsend
* [Matthias Tylkowski](https://github.com/tylkomat)
<!--end-->
* Peter Van der Beken
* Sander van Veen
* Atul Varma
* [Erik Vold](https://github.com/erikvold)
* Vladimir Vukicevic
<!--end-->
* Brian Warner
* [Henri Wiechers](https://github.com/hwiechers)
* Drew Willcoxon
* Blake Winton
* Michal Wojciechowski
<!--end-->
* Piotr Zalewa
* [David Guo](https://github.com/dglol)
* [Nils Maier](https://github.com/nmaier)
* [Louis-Rémi Babé](https://github.com/louisremi)
* [Matthias Tylkowski](https://github.com/tylkomat)
* Brett Zamir

View File

@ -6,23 +6,18 @@
# Communicating using "port" #
To enable add-on scripts and content scripts to communicate with each other,
each end of the conversation has access to a `port` object which defines two
functions:
each end of the conversation has access to a `port` object.
**`emit()`** is used to emit an event. It may be called with any number of
parameters, but is most likely to be called with a name for the event and
an optional payload. The payload can be any value that is
<a href = "dev-guide/guides/content-scripts/using-port.html#json_serializable">serializable to JSON</a>
* to send messages from one side to the other, use `port.emit()`
* to receive messages sent from the other side, use `port.on()`
port.emit("myEvent", myEventPayload);
<img class="image-center" src="static-files/media/content-scripting-events.png"
alt="Content script events">
**`on()`** takes two parameters: the name of the event and a function to handle it:
Messages are asynchronous: that is, the sender does not wait for a reply from
the recipient but just emits the message and continues processing.
port.on("myEvent", function handleMyEvent(myEventPayload) {
// Handle the event
});
Here's simple add-on that sends a message to a content script using `port`:
Here's a simple add-on that sends a message to a content script using `port`:
var tabs = require("sdk/tabs");
@ -39,45 +34,49 @@ Here's simple add-on that sends a message to a content script using `port`:
tabs.open("http://www.mozilla.org");
We could depict the interface between add-on code and content script code like
this:
In total, the `port` object defines four functions:
<img class="image-center" src="static-files/media/content-scripting-events.png"
alt="Content script events">
* [`emit()`](dev-guide/guides/content-scripts/using-port.html#port.emit()):
emit a message.
* [`on()`](dev-guide/guides/content-scripts/using-port.html#port.on()):
listen to a message.
* [`removeListener()`](dev-guide/guides/content-scripts/using-port.html#port.removeListener()):
stop listening to a message.
* [`once()`](dev-guide/guides/content-scripts/using-port.html#port.once()):
listen to only the first occurrence of a message.
Events are asynchronous: that is, the sender does not wait for a reply from
the recipient but just emits the event and continues processing.
## Accessing `port` ##
## Accessing `port` in the Content Script ##
### Accessing `port` in the Content Script ###
<span class="aside">Note that the global `self` object is completely
different from the [`self` module](modules/sdk/self.html), which
provides an API for an add-on to access its data files and ID.</span>
In the content script the `port` object is available as a property of the
global `self` object. Thus, to emit an event from a content script:
global `self` object. Thus, to emit a message from a content script:
self.port.emit("myContentScriptEvent", myContentScriptEventPayload);
self.port.emit("myContentScriptMessage", myContentScriptMessagePayload);
To receive an event from the add-on code:
To receive a message from the add-on code:
self.port.on("myAddonEvent", function(myAddonEventPayload) {
// Handle the event
self.port.on("myAddonMessage", function(myAddonMessagePayload) {
// Handle the message
});
Compare this to the technique used to receive _built-in_ events in the
content script. For example, to receive the `context` event in a content script
Compare this to the technique used to receive _built-in_ messages in the
content script. For example, to receive the `context` message in a content script
associated with a [context menu](modules/sdk/context-menu.html)
object, you would call the `on` function attached to the global `self` object:
self.on("context", function() {
// Handle the event
// Handle the message
});
So the `port` property is essentially used here as a namespace for
user-defined events.
user-defined messages.
## Accessing `port` in the Add-on Script ##
### Accessing `port` in the Add-on Script ###
In the add-on code, the channel of communication between the add-on and a
particular content script context is encapsulated by the `worker` object. Thus
@ -86,7 +85,7 @@ corresponding `worker` object.
However, the worker is not exposed to add-on code in quite the same way
in all modules. The `panel` and `page-worker` objects integrate the
worker API directly. So to receive events from a content script associated
worker API directly. So to receive messages from a content script associated
with a panel you use `panel.port.on()`:
var panel = require("sdk/panel").Panel({
@ -99,7 +98,7 @@ with a panel you use `panel.port.on()`:
panel.show();
Conversely, to emit user-defined events from your add-on you can just call
Conversely, to emit user-defined messages from your add-on you can just call
`panel.port.emit()`:
var panel = require("sdk/panel").Panel({
@ -121,8 +120,8 @@ So `page-mod` does not integrate the worker API directly: instead, each time a
content script is attached to a page, the worker associated with the page is
supplied to the page-mod in its `onAttach` function. By supplying a target for
this function in the page-mod's constructor you can register to receive
events from the content script, and take a reference to the worker so as to
emit events to it.
messages from the content script, and take a reference to the worker so as to
emit messages to the content script.
var pageModScript = "window.addEventListener('click', function(event) {" +
" self.port.emit('click', event.target.toString());" +
@ -143,18 +142,126 @@ emit events to it.
}
});
In the add-on above there are two user-defined events:
In the add-on above there are two user-defined messages:
* `click` is sent from the page-mod to the add-on, when the user clicks an
element in the page
* `warning` sends a silly string back to the page-mod
## port.emit() ##
The `port.emit()` function sends a message from the "main.js", or another
add-on module, to a content script, or vice versa.
It may be called with any number of parameters, but is most likely to be
called with a name for the message and an optional payload.
The payload can be any value that is
<a href = "dev-guide/guides/content-scripts/using-port.html#json_serializable">serializable to JSON</a>.
From the content script to the main add-on code:
var myMessagePayload = "some data";
self.port.emit("myMessage", myMessagePayload);
From the main add-on code (in this case a panel instance)
to the content script:
var myMessagePayload = "some data";
panel.port.emit("myMessage", myMessagePayload);
## port.on() ##
The `port.on()` function registers a function as a listener for a specific
named message sent from the other side using `port.emit()`.
It takes two parameters: the name of the message and a function to handle it.
In a content script, to listen for "myMessage" sent from the main
add-on code:
self.port.on("myMessage", function handleMyMessage(myMessagePayload) {
// Handle the message
});
In the main add-on code (in this case a panel instance), to listen for
"myMessage" sent from a a content script:
panel.port.on("myMessage", function handleMyMessage(myMessagePayload) {
// Handle the message
});
## port.removeListener() ##
You can uses `port.on()` to listen for messages. To stop listening for a
particular message, use `port.removeListener()`. This takes the
same two parameters as `port.on()`: the name of the message, and the name
of the listener function.
For example, here's an add-on that creates a page-worker and a widget.
The page-worker loads
[http://en.wikipedia.org/wiki/Chalk](http://en.wikipedia.org/wiki/Chalk)
alongside a content script "listener.js". The widget sends the content script
a message called "get-first-para" when it is clicked:
pageWorker = require("sdk/page-worker").Page({
contentScriptFile: require("sdk/self").data.url("listener.js"),
contentURL: "http://en.wikipedia.org/wiki/Chalk"
});
require("sdk/widget").Widget({
id: "mozilla-icon",
label: "My Mozilla Widget",
contentURL: "http://www.mozilla.org/favicon.ico",
onClick: function() {
console.log("sending 'get-first-para'");
pageWorker.port.emit("get-first-para");
}
});
The content script "listener.js" listens for "get-first-para". When it
receives this message, the script logs the first paragraph of the document
and then calls `removeListener()` to stop listening.
function getFirstParagraph() {
var paras = document.getElementsByTagName('p');
console.log(paras[0].textContent);
self.port.removeListener("get-first-para", getFirstParagraph);
}
self.port.on("get-first-para", getFirstParagraph);
The result is that the paragraph is only logged the first time the widget
is clicked.
Due to [bug 816272](https://bugzilla.mozilla.org/show_bug.cgi?id=816272)
the [`page-mod`](modules/sdk/page-mod.html)'s `removeListener()` function
does not prevent the listener from receiving messages that are already queued.
This means that if "main.js" sends the message twice in successive lines, and
the listener stops listening as soon as it receives the first message, then
the listener will still receive the second message.
## port.once() ##
Often you'll want to receive a message just once, then stop listening. The
`port` object offers a shortcut to do this: the `once()` method.
This example rewrites the "listener.js" content script in the
[`port.removeListener()` example](dev-guide/guides/content-scripts/using-port.html#port.removeListener())
so that it uses `once()`:
function getFirstParagraph() {
var paras = document.getElementsByTagName('p');
console.log(paras[0].textContent);
}
self.port.once("get-first-para", getFirstParagraph);
## <a name="json_serializable">JSON-Serializable Values</a> ##
The payload for an event can be any JSON-serializable value. When events are
sent their payloads are automatically serialized, and when events are received
their payloads are automatically deserialized, so you don't need to worry
about serialization.
The payload for an message can be any JSON-serializable value. When messages
are sent their payloads are automatically serialized, and when messages are
received their payloads are automatically deserialized, so you don't need to
worry about serialization.
However, you _do_ have to ensure that the payload can be serialized to JSON.
This means that it needs to be a string, number, boolean, null, array of

View File

@ -24,7 +24,7 @@ their code.
The stability index is adopted from
[node.js](http://nodejs.org/api/documentation.html#documentation_stability_index).
The SDK uses only three of the six values defined by node.js:
The SDK uses only four of the six values defined by node.js:
<table>
<tr>
@ -33,6 +33,14 @@ The SDK uses only three of the six values defined by node.js:
You can try it out and provide feedback, but we may change or remove
it in future versions without having to pass through a formal
deprecation process.</td>
</tr>
<tr>
<td>Unstable</td>
<td>The API is in the process of settling, but has not yet had sufficient
real-world testing to be considered stable.
Backwards-compatibility will be maintained if reasonable.
If we do have to make backwards-incompatible changes, we will not guarantee
to go through the formal deprecation process.</td>
</tr>
<tr>
<td>Stable</td>

View File

@ -93,7 +93,11 @@ called `package.json`. This file is also referred to as the
`xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`, where `x` represents a single
hexadecimal digit. It is used as a `classID` (CID) of the "harness service"
XPCOM component. Defaults to a random GUID generated by `cfx`.
* `permissions` - a set of permissions that the add-on needs.
* `private-browsing` - A Boolean indicating whether or not the
package supports private browsing. If this value is not `true`
then the package will not see private windows.
## Documentation ##

View File

@ -240,111 +240,96 @@ rm my-addon.xpi
<a name="modules-compatibility"></a>
## Module Compatibility
Modules not yet supported in Firefox Mobile are <span class="unsupported-on-mobile">highlighted</span> in the tables below.
Modules not yet supported in Firefox Mobile are
<span class="unsupported-on-mobile">**marked**</span> in the tables below.
### High-Level APIs ###
<ul class="module-list">
<li class="unsupported-on-mobile"><a href="modules/sdk/addon-page.html">addon-page</a></li>
<li><a href="modules/sdk/base64.html">base64</a></li>
<li class="unsupported-on-mobile"><a href="modules/sdk/clipboard.html">clipboard</a></li>
<li class="unsupported-on-mobile"><a href="modules/sdk/context-menu.html">context-menu</a></li>
<li><a href="modules/sdk/hotkeys.html">hotkeys</a></li>
<!-- test-l10n-locale, test-l10n-plural-rules -->
<li><a href="modules/sdk/l10n.html">l10n</a></li>
<li><a href="modules/sdk/notifications.html">notifications</a></li>
<!-- test-page-mod fails, but we know the module works -->
<li><a href="modules/sdk/page-mod.html">page-mod</a></li>
<li class="unsupported-on-mobile"><a href="modules/sdk/panel.html">panel</a></li>
<!-- test-passwords, test-passwords-utils (with exceptions / warning from js console) -->
<li><a href="modules/sdk/passwords.html">passwords</a></li>
<li class="unsupported-on-mobile"><a href="modules/sdk/private-browsing.html">private-browsing</a></li>
<li><a href="modules/sdk/querystring.html">querystring</a></li>
<li><a href="modules/sdk/request.html">request</a></li>
<li class="unsupported-on-mobile"><a href="modules/sdk/selection.html">selection</a></li>
<li><a href="modules/sdk/self.html">self</a></li>
<li><a href="modules/sdk/simple-prefs.html">simple-prefs</a></li>
<li><a href="modules/sdk/simple-storage.html">simple-storage</a></li>
<!-- test-tabs, test-tabs-common -->
<li><a href="modules/sdk/tabs.html">tabs</a></li>
<!-- test-timer -->
<li><a href="modules/sdk/timers.html">timers</a></li>
<li><a href="modules/sdk/url.html">url</a></li>
<li><a href="modules/sdk/widget.html">widget</a></li>
<!-- test-windows-common, test-windows -->
<li><a href="modules/sdk/windows.html">windows</a></li>
</ul>
- [**addon-page**](modules/sdk/addon-page.html)
- [base64](modules/sdk/clipboard.html)
- [**clipboard**](modules/sdk/clipboard.html)
- [**context-menu**](modules/sdk/context-menu.html)
- [hotkeys](modules/sdk/hotkeys.html)
- [indexed-db](modules/sdk/indexed-db.html)
- [l10n](modules/sdk/l10n.html)
- [notifications](modules/sdk/notifications.html)
- [page-mod](modules/sdk/notifications.html)
- [page-worker](modules/sdk/page-worker.html)
- [**panel**](modules/sdk/panel.html)
- [passwords](modules/sdk/passwords.html)
- [**private-browsing**](modules/sdk/private-browsing.html)
- [querystring](modules/sdk/querystring.html)
- [request](modules/sdk/request.html)
- [**selection**](modules/sdk/selection.html)
- [self](modules/sdk/self.html)
- [simple-prefs](modules/sdk/simple-prefs.html)
- [simple-storage](modules/sdk/simple-storage.html)
- [system](modules/sdk/system.html)
- [tabs](modules/sdk/tabs.html)
- [timers](modules/sdk/timers.html)
- [url](modules/sdk/url.html)
- [**widget**](modules/sdk/widget.html)
- [windows](modules/sdk/windows.html)
### Low-Level APIs ###
<ul class="module-list">
<li><a href="modules/toolkit/loader.html">/loader</a></li>
<li><a href="dev-guide/tutorials/chrome.html">chrome</a></li>
<li><a href="modules/sdk/console/plain-text.html">console/plain-text</a></li>
<li><a href="modules/sdk/console/traceback.html">console/traceback</a></li>
<li class="unsupported-on-mobile"><a href="modules/sdk/content/content.html">content/content</a></li>
<li><a href="modules/sdk/content/loader.html">content/loader</a></li>
<li class="unsupported-on-mobile"><a href="modules/sdk/content/symbiont.html">content/symbiont</a></li>
<li class="unsupported-on-mobile"><a href="modules/sdk/content/worker.html">content/worker</a></li>
<li>core/disposable</li>
<li><a href="modules/sdk/core/heritage.html">core/heritage</a></li>
<li><a href="modules/sdk/core/namespace.html">core/namespace</a></li>
<li><a href="modules/sdk/core/promise.html">core/promise</a></li>
<li><a href="modules/sdk/deprecated/api-utils.html">deprecated/api-utils</a></li>
<li><a href="modules/sdk/deprecated/app-strings.html">deprecated/app-strings</a></li>
<li><a href="modules/sdk/deprecated/cortex.html">deprecated/cortex</a></li>
<li><a href="modules/sdk/deprecated/errors.html">deprecated/errors</a></li>
<li><a href="modules/sdk/deprecated/events.html">deprecated/events</a></li>
<li><a href="modules/sdk/deprecated/light-traits.html">deprecated/light-traits</a></li>
<li>deprecated/list</li>
<li><a href="modules/sdk/deprecated/observer-service.html">deprecated/observer-service</a></li>
<li class="unsupported-on-mobile"><a href="modules/sdk/deprecated/tab-browser.html">deprecated/tab-browser</a></li>
<!-- test-traits-core, test-traits -->
<li><a href="modules/sdk/deprecated/traits.html">deprecated/traits</a></li>
<li class="unsupported-on-mobile"><a href="modules/sdk/deprecated/window-utils.html">deprecated/window-utils</a></li>
<!-- test-dom -->
<li>dom/events</li>
<li><a href="modules/sdk/event/core.html">event/core</a></li>
<li><a href="modules/sdk/event/target.html">event/target</a></li>
<li><a href="modules/sdk/frame/hidden-frame.html">frame/hidden-frame</a></li>
<li><a href="modules/sdk/frame/utils.html">frame/utils</a></li>
<li><a href="modules/sdk/io/byte-streams.html">io/byte-streams</a></li>
<li><a href="modules/sdk/io/file.html">io/file</a></li>
<li><a href="modules/sdk/io/text-streams.html">io/text-streams</a></li>
<li>keyboard/observer</li>
<li>keyboard/utils</li>
<li>lang/functional</li>
<li>lang/type</li>
<li><a href="modules/sdk/loader/cuddlefish.html">loader/cuddlefish</a></li>
<li><a href="modules/sdk/loader/sandbox.html">loader/sandbox</a></li>
<li><a href="modules/sdk/net/url.html">net/url</a></li>
<li><a href="modules/sdk/net/xhr.html">net/xhr</a></li>
<li><a href="modules/sdk/page-mod/match-pattern.html">page-mod/match-pattern</a></li>
<li><a href="modules/sdk/platform/xpcom.html">platform/xpcom</a></li>
<!-- test-preferences-service, test-preferences-target -->
<li><a href="modules/sdk/preferences/service.html">preferences/service</a></li>
<li><a href="modules/sdk/system/environment.html">system/environment</a></li>
<!-- No test for `system/events`, assuming it works because other compatible modules are using it -->
<li><a href="modules/sdk/system/events.html">system/events</a></li>
<li>system/globals</li>
<!-- No test for `system/events`, assuming it works because other compatible modules are using it -->
<li><a href="modules/sdk/system/runtime.html">system/runtime</a></li>
<li><a href="modules/sdk/system/unload.html">system/unload</a></li>
<li><a href="modules/sdk/system/xul-app.html">system/xul-app</a></li>
<!-- No test for `assert`, assuming it works because the test are using it -->
<li><a href="modules/sdk/test/assert.html">test/assert</a></li>
<!-- No test for `harness`, assuming it works because the test are using it -->
<li><a href="modules/sdk/test/harness.html">test/harness</a></li>
<li><a href="modules/sdk/test/httpd.html">test/httpd</a></li>
<!-- No test for `runner`, assuming it works because the test are using it -->
<li><a href="modules/sdk/test/runner.html">test/runner</a></li>
<li>test/tmp-file</li>
<li>util/array</li>
<li><a href="modules/sdk/util/collection.html">util/collection</a></li>
<li><a href="modules/sdk/util/deprecate.html">util/deprecate</a></li>
<li><a href="modules/sdk/util/list.html">util/list</a></li>
<li>util/registry</li>
<li><a href="modules/sdk/util/uuid.html">util/uuid</a></li>
<!-- test-window-utils2 -->
<li><a href="modules/sdk/window/utils.html">window/utils</a></li>
</ul>
- [/loader](modules/toolkit/loader.html)
- [chrome](dev-guide/tutorials/chrome.html)
- [console/plain-text](modules/sdk/console/plain-text.html)
- [console/traceback](modules/sdk/console/traceback.html)
- [**content/content**](modules/sdk/content/content.html)
- [content/loader](modules/sdk/content/loader.html)
- [**content/symbiont**](modules/sdk/content/symbiont.html)
- [**content/worker**](modules/sdk/content/worker.html)
- core/disposable
- [core/heritage](modules/sdk/core/heritage.html)
- [core/namespace](modules/sdk/core/namespace.html)
- [core/promise](modules/sdk/core/promise.html)
- [deprecated/api-utils](modules/sdk/deprecated/api-utils.html)
- [deprecated/app-strings](modules/sdk/deprecated/app-strings.html)
- [deprecated/cortex](modules/sdk/deprecated/cortex.html)
- [deprecated/errors](modules/sdk/deprecated/errors.html)
- [deprecated/events](modules/sdk/deprecated/events.html)
- [deprecated/light-traits](modules/sdk/deprecated/light-traits.html)
- deprecated/list
- [deprecated/observer-service](modules/sdk/deprecated/observer-service.html)
- [**deprecated/tab-browser**](modules/sdk/deprecated/tab-browser.html)
- [deprecated/traits](modules/sdk/deprecated/traits.html)
- [**deprecated/window-utils**](modules/sdk/deprecated/window-utils.html)
- dom/events
- [event/core](modules/sdk/event/core.html)
- [event/target](modules/sdk/event/target.html)
- [frame/hidden-frame](modules/sdk/frame/hidden-frame.html)
- [frame/utils](modules/sdk/frame/utils.html)
- [io/byte-streams](modules/sdk/io/byte-streams.html)
- [io/file](modules/sdk/io/file.html)
- [io/text-streams](modules/sdk/io/text-streams.html)
- keyboard/observer
- keyboard/utils
- lang/functional
- lang/type
- [loader/cuddlefish](modules/sdk/loader/cuddlefish.html)
- [loader/sandbox](modules/sdk/loader/sandbox.html)
- [net/url](modules/sdk/net/url.html)
- [net/xhr](modules/sdk/net/xhr.html)
- [page-mod/match-pattern](modules/sdk/page-mod/match-pattern.html)
- [platform/xpcom](modules/sdk/platform/xpcom.html)
- [preferences/service](modules/sdk/preferences/service.html)
- [system/environment](modules/sdk/system/environment.html)
- [system/events](modules/sdk/system/events.html)
- system/globals
- [system/runtime](modules/sdk/system/runtime.html)
- [system/unload](modules/sdk/system/unload.html)
- [system/xul-app](modules/sdk/system/xul-app.html)
- [test/assert](modules/sdk/test/assert.html)
- [test/harness](modules/sdk/test/harness.html)
- [test/httpd](modules/sdk/test/httpd.html)
- [test/runner](modules/sdk/test/runner.html)
- test/tmp-file
- util/array
- [util/collection](modules/sdk/util/collection.html)
- [util/deprecate](modules/sdk/util/deprecate.html)
- [util/list](modules/sdk/util/list.html)
- util/registry
- [util/uuid](modules/sdk/util/uuid.html)
- [window/utils](modules/sdk/window/utils.html)

View File

@ -12,23 +12,23 @@ Introduction
------------
The `context-menu` API provides a simple, declarative way to add items to the
page's context menu. You can add items that perform an action when clicked,
page's context menu. You can add items that perform an action when clicked,
submenus, and menu separators.
Instead of manually adding items when particular contexts occur and then
removing them when those contexts go away, you *bind* items to contexts, and the
adding and removing is automatically handled for you. Items are bound to
contexts in much the same way that event listeners are bound to events. When
adding and removing is automatically handled for you. Items are bound to
contexts in much the same way that event listeners are bound to events. When
the user invokes the context menu, all of the items bound to the current context
are automatically added to the menu. If no items are bound, none are added.
are automatically added to the menu. If no items are bound, none are added.
Likewise, any items that were previously in the menu but are not bound to the
current context are automatically removed from the menu. You never need to
current context are automatically removed from the menu. You never need to
manually remove your items from the menu unless you want them to never appear
again.
For example, if your add-on needs to add a context menu item whenever the
user visits a certain page, don't create the item when that page loads, and
don't remove it when the page unloads. Rather, create your item only once and
don't remove it when the page unloads. Rather, create your item only once and
supply a context that matches the target URL.
Context menu items are displayed in the order created or in the case of sub
@ -43,27 +43,27 @@ Specifying Contexts
-------------------
As its name implies, the context menu should be reserved for the occurrence of
specific contexts. Contexts can be related to page content or the page itself,
specific contexts. Contexts can be related to page content or the page itself,
but they should never be external to the page.
For example, a good use of the menu would be to show an "Edit Image" item when
the user right-clicks an image in the page. A bad use would be to show a
the user right-clicks an image in the page. A bad use would be to show a
submenu that listed all the user's tabs, since tabs aren't related to the page
or the node the user clicked to open the menu.
### The Page Context
First of all, you may not need to specify a context at all. When a top-level
item does not specify a context, the page context applies. An item that is in a
First of all, you may not need to specify a context at all. When a top-level
item does not specify a context, the page context applies. An item that is in a
submenu is visible unless you specify a context.
The *page context* occurs when the user invokes the context menu on a
non-interactive portion of the page. Try right-clicking a blank spot in this
page, or on text. Make sure that no text is selected. The menu that appears
should contain the items "Back", "Forward", "Reload", "Stop", and so on. This
non-interactive portion of the page. Try right-clicking a blank spot in this
page, or on text. Make sure that no text is selected. The menu that appears
should contain the items "Back", "Forward", "Reload", "Stop", and so on. This
is the page context.
The page context is appropriate when your item acts on the page as a whole. It
The page context is appropriate when your item acts on the page as a whole. It
does not occur when the user invokes the context menu on a link, image, or other
non-text node, or while a selection exists.
@ -79,7 +79,7 @@ like this:
context: cm.URLContext("*.mozilla.org")
});
These contexts may be specified by calling the following constructors. Each is
These contexts may be specified by calling the following constructors. Each is
exported by the `context-menu` module.
<table>
@ -121,10 +121,10 @@ exported by the `context-menu` module.
</code></td>
<td>
This context occurs when the menu is invoked on pages with particular
URLs. <code>matchPattern</code> is a match pattern string or an array of
match pattern strings. When <code>matchPattern</code> is an array, the
URLs. <code>matchPattern</code> is a match pattern string or an array of
match pattern strings. When <code>matchPattern</code> is an array, the
context occurs when the menu is invoked on a page whose URL matches any of
the patterns. These are the same match pattern strings that you use with
the patterns. These are the same match pattern strings that you use with
the <a href="modules/sdk/page-mod.html"><code>page-mod</code></a>
<code>include</code> property.
<a href="modules/sdk/page-mod/match-pattern.html">Read more about patterns</a>.
@ -135,14 +135,14 @@ exported by the `context-menu` module.
array
</td>
<td>
An array of any of the other types. This context occurs when all contexts
An array of any of the other types. This context occurs when all contexts
in the array occur.
</td>
</tr>
</table>
Menu items also have a `context` property that can be used to add and remove
declarative contexts after construction. For example:
declarative contexts after construction. For example:
var context = require("sdk/context-menu").SelectorContext("img");
myMenuItem.context.add(context);
@ -153,19 +153,19 @@ all of those contexts occur.
### In Content Scripts
The declarative contexts are handy but not very powerful. For instance, you
The declarative contexts are handy but not very powerful. For instance, you
might want your menu item to appear for any page that has at least one image,
but declarative contexts won't help you there.
When you need more control control over the context in which your menu items are
shown, you can use content scripts. Like other APIs in the SDK, the
When you need more control over the context in which your menu items are
shown, you can use content scripts. Like other APIs in the SDK, the
`context-menu` API uses
[content scripts](dev-guide/guides/content-scripts/index.html) to let your
add-on interact with pages in the browser. Each menu item you create in the
add-on interact with pages in the browser. Each menu item you create in the
top-level context menu can have a content script.
A special event named `"context"` is emitted in your content scripts whenever
the context menu is about to be shown. If you register a listener function for
the context menu is about to be shown. If you register a listener function for
this event and it returns true, the menu item associated with the listener's
content script is shown in the menu.
@ -179,16 +179,18 @@ that contains at least one image:
'});'
});
Note that the listener function has a parameter called `node`. This is the node
in the page that the user context-clicked to invoke the menu. You can use it to
Note that the listener function has a parameter called `node`. This is the node
in the page that the user context-clicked to invoke the menu. You can use it to
determine whether your item should be shown.
You can both specify declarative contexts and listen for contexts in a content
script. In that case, the declarative contexts are evaluated first. If they
are not current, then your context listener is never called.
script. In that case, the declarative contexts are evaluated first, and your
item is shown only when all declarative contexts are current and your
context listener returns true.
This example takes advantage of that fact. The listener can be assured that
`node` will always be an image:
If any declarative contexts are not current, then your context listener
is never called. This example takes advantage of that fact. The listener
can be assured that `node` will always be an image:
var cm = require("sdk/context-menu");
cm.Item({
@ -199,8 +201,26 @@ This example takes advantage of that fact. The listener can be assured that
'});'
});
Your item is shown only when all declarative contexts are current and your
context listener returns true.
However, if you do combine `SelectorContext` and the `"context"` event,
be aware that the `node` argument passed to the `"context"` event will
not always match the type specified in `SelectorContext`.
`SelectorContext` will match if the menu is invoked on the node specified
*or any descendant of that node*, but the `"context"` event handler is
passed *the actual node* on which the menu was invoked. The example above
works because `<IMG>` elements can't contain other elements, but in the
example below, `node.nodeName` is not guaranteed to be "P" - for example,
it won't be "P" if the user context-clicked a link inside a paragraph:
var cm = require("sdk/context-menu");
cm.Item({
label: "A Paragraph",
context: cm.SelectorContext("p"),
contentScript: 'self.on("context", function (node) {' +
' console.log(node.nodeName);' +
' return true;' +
'});'
});
The content script is executed for every page that a context menu is shown for.
It will be executed the first time it is needed (i.e. when the context menu is
@ -212,7 +232,7 @@ Handling Menu Item Clicks
-------------------------
In addition to using content scripts to listen for the `"context"` event as
described above, you can use content scripts to handle item clicks. When the
described above, you can use content scripts to handle item clicks. When the
user clicks your menu item, an event named `"click"` is emitted in the item's
content script.
@ -226,10 +246,68 @@ item's content script like so:
'});'
});
Note that the listener function has parameters called `node` and `data`. `node`
is the node that the user context-clicked to invoke the menu. You can use it
when performing some action. `data` is the `data` property of the menu item
that was clicked. Note that when you have a hierarchy of menu items the click
Note that the listener function has parameters called `node` and `data`.
### The "node" Argument ###
`node` is the node that the user context-clicked to invoke the menu.
* If you did not use `SelectorContext` to decide whether to show the menu item,
then this is the actual node clicked.
* If you did use `SelectorContext`, then this is the node that matched your
selector.
For example, suppose your add-on looks like this:
var script = "self.on('click', function (node, data) {" +
" console.log('clicked: ' + node.nodeName);" +
"});";
var cm = require("sdk/context-menu");
cm.Item({
label: "body context",
context: cm.SelectorContext("body"),
contentScript: script
});
This add-on creates a context-menu item that uses `SelectorContext` to display
the item whenever the context menu is activated on any descendant of the
`<BODY>` element. When clicked, the item just logs the
[`nodeName`](https://developer.mozilla.org/en-US/docs/DOM/Node.nodeName)
property for the node passed to the click handler.
If you run this add-on you'll see that it always logs "BODY", even if you
click on a paragraph element inside the page:
<pre>
info: contextmenu-example: clicked: BODY
</pre>
By contrast, this add-on uses the `PageContext`:
var script = "self.on('click', function (node, data) {" +
" console.log('clicked: ' + node.nodeName);" +
"});";
var cm = require("sdk/context-menu");
cm.Item({
label: "body context",
context: cm.PageContext(),
contentScript: script
});
It will log the name of the actual node clicked:
<pre>
info: contextmenu-example: clicked: P
</pre>
### The "data" Argument ###
`data` is the `data` property of the menu item
that was clicked. Note that when you have a hierarchy of menu items the click
event will be sent to the content script of the item clicked and all ancestors
so be sure to verify that the `data` value passed matches the item you expect.
You can use this to simplify click handling by providing just a single click
@ -248,11 +326,13 @@ listener on a `Menu` that reacts to clicks for any child items.:
]
});
### Communicating With the Add-on ###
Often you will need to collect some kind of information in the click listener
and perform an action unrelated to content. To communicate to the menu item
and perform an action unrelated to content. To communicate to the menu item
associated with the content script, the content script can call the
`postMessage` function attached to the global `self` object, passing it some
JSON-able data. The menu item's `"message"` event listener will be called with
JSON-able data. The menu item's `"message"` event listener will be called with
that data.
var cm = require("sdk/context-menu");
@ -274,7 +354,7 @@ Updating a Menu Item's Label
Each menu item must be created with a label, but you can change its label later
using a couple of methods.
The simplest method is to set the menu item's `label` property. This example
The simplest method is to set the menu item's `label` property. This example
updates the item's label based on the number of times it's been clicked:
var numClicks = 0;
@ -288,13 +368,13 @@ updates the item's label based on the number of times it's been clicked:
}
});
Sometimes you might want to update the label based on the context. For
Sometimes you might want to update the label based on the context. For
instance, if your item performs a search with the user's selected text, it would
be nice to display the text in the item to provide feedback to the user. In
these cases you can use the second method. Recall that your content scripts can
be nice to display the text in the item to provide feedback to the user. In
these cases you can use the second method. Recall that your content scripts can
listen for the `"context"` event and if your listeners return true, the items
associated with the content scripts are shown in the menu. In addition to
returning true, your `"context"` listeners can also return strings. When a
associated with the content scripts are shown in the menu. In addition to
returning true, your `"context"` listeners can also return strings. When a
`"context"` listener returns a string, it becomes the item's new label.
This item implements the aforementioned search example:
@ -312,7 +392,7 @@ This item implements the aforementioned search example:
});
The `"context"` listener gets the window's current selection, truncating it if
it's too long, and includes it in the returned string. When the item is shown,
it's too long, and includes it in the returned string. When the item is shown,
its label will be "Search Google for `text`", where `text` is the truncated
selection.
@ -321,9 +401,9 @@ More Examples
-------------
For conciseness, these examples create their content scripts as strings and use
the `contentScript` property. In your own add-on, you will probably want to
the `contentScript` property. In your own add-on, you will probably want to
create your content scripts in separate files and pass their URLs using the
`contentScriptFile` property. See
`contentScriptFile` property. See
[Working with Content Scripts](dev-guide/guides/content-scripts/index.html)
for more information.
@ -436,14 +516,14 @@ A labeled menu item that can perform an action when clicked.
@param options {object}
An object with the following keys:
@prop label {string}
The item's label. It must either be a string or an object that implements
The item's label. It must either be a string or an object that implements
`toString()`.
@prop [image] {string}
The item's icon, a string URL. The URL can be remote, a reference to an
The item's icon, a string URL. The URL can be remote, a reference to an
image in the add-on's `data` directory, or a data URI.
@prop [data] {string}
An optional arbitrary value to associate with the item. It must be either a
string or an object that implements `toString()`. It will be passed to
An optional arbitrary value to associate with the item. It must be either a
string or an object that implements `toString()`. It will be passed to
click listeners.
@prop [context] {value}
If the item is contained in the top-level context menu, this declaratively
@ -459,43 +539,43 @@ A labeled menu item that can perform an action when clicked.
use to interact with the page.
@prop [onMessage] {function}
If the item is contained in the top-level context menu, this function will
be called when the content script calls `self.postMessage`. It will be
be called when the content script calls `self.postMessage`. It will be
passed the data that was passed to `postMessage`.
</api>
<api name="label">
@property {string}
The menu item's label. You can set this after creating the item to update its
The menu item's label. You can set this after creating the item to update its
label later.
</api>
<api name="image">
@property {string}
The item's icon, a string URL. The URL can be remote, a reference to an image
in the add-on's `data` directory, or a data URI. You can set this after
creating the item to update its image later. To remove the item's image, set
The item's icon, a string URL. The URL can be remote, a reference to an image
in the add-on's `data` directory, or a data URI. You can set this after
creating the item to update its image later. To remove the item's image, set
it to `null`.
</api>
<api name="data">
@property {string}
An optional arbitrary value to associate with the item. It must be either a
string or an object that implements `toString()`. It will be passed to
click listeners. You can set this after creating the item to update its data
An optional arbitrary value to associate with the item. It must be either a
string or an object that implements `toString()`. It will be passed to
click listeners. You can set this after creating the item to update its data
later.
</api>
<api name="context">
@property {list}
A list of declarative contexts for which the menu item will appear in the
context menu. Contexts can be added by calling `context.add()` and removed by
context menu. Contexts can be added by calling `context.add()` and removed by
called `context.remove()`.
</api>
<api name="parentMenu">
@property {Menu}
The item's parent `Menu`, or `null` if the item is contained in the top-level
context menu. This property is read-only. To add the item to a new menu,
context menu. This property is read-only. To add the item to a new menu,
call that menu's `addItem()` method.
</api>
@ -514,7 +594,7 @@ A labeled menu item that can perform an action when clicked.
<api name="destroy">
@method
Permanently removes the item from its parent menu and frees its resources.
The item must not be used afterward. If you need to remove the item from its
The item must not be used afterward. If you need to remove the item from its
parent menu but use it afterward, call `removeItem()` on the parent menu
instead.
</api>
@ -544,13 +624,13 @@ A labeled menu item that expands into a submenu.
@param options {object}
An object with the following keys:
@prop label {string}
The item's label. It must either be a string or an object that implements
The item's label. It must either be a string or an object that implements
`toString()`.
@prop items {array}
An array of menu items that the menu will contain. Each must be an `Item`,
An array of menu items that the menu will contain. Each must be an `Item`,
`Menu`, or `Separator`.
@prop [image] {string}
The menu's icon, a string URL. The URL can be remote, a reference to an
The menu's icon, a string URL. The URL can be remote, a reference to an
image in the add-on's `data` directory, or a data URI.
@prop [context] {value}
If the menu is contained in the top-level context menu, this declaratively
@ -566,43 +646,43 @@ A labeled menu item that expands into a submenu.
use to interact with the page.
@prop [onMessage] {function}
If the menu is contained in the top-level context menu, this function will
be called when the content script calls `self.postMessage`. It will be
be called when the content script calls `self.postMessage`. It will be
passed the data that was passed to `postMessage`.
</api>
<api name="label">
@property {string}
The menu's label. You can set this after creating the menu to update its
The menu's label. You can set this after creating the menu to update its
label later.
</api>
<api name="items">
@property {array}
An array containing the items in the menu. The array is read-only, meaning
that modifications to it will not affect the menu. However, setting this
An array containing the items in the menu. The array is read-only, meaning
that modifications to it will not affect the menu. However, setting this
property to a new array will replace all the items currently in the menu with
the items in the new array.
</api>
<api name="image">
@property {string}
The menu's icon, a string URL. The URL can be remote, a reference to an image
in the add-on's `data` directory, or a data URI. You can set this after
creating the menu to update its image later. To remove the menu's image, set
The menu's icon, a string URL. The URL can be remote, a reference to an image
in the add-on's `data` directory, or a data URI. You can set this after
creating the menu to update its image later. To remove the menu's image, set
it to `null`.
</api>
<api name="context">
@property {list}
A list of declarative contexts for which the menu will appear in the context
menu. Contexts can be added by calling `context.add()` and removed by called
menu. Contexts can be added by calling `context.add()` and removed by called
`context.remove()`.
</api>
<api name="parentMenu">
@property {Menu}
The menu's parent `Menu`, or `null` if the menu is contained in the top-level
context menu. This property is read-only. To add the menu to a new menu,
context menu. This property is read-only. To add the menu to a new menu,
call that menu's `addItem()` method.
</api>
@ -620,9 +700,9 @@ A labeled menu item that expands into a submenu.
<api name="addItem">
@method
Appends a menu item to the end of the menu. If the item is already contained
Appends a menu item to the end of the menu. If the item is already contained
in another menu or in the top-level context menu, it's automatically removed
first. If the item is already contained in this menu it will just be moved
first. If the item is already contained in this menu it will just be moved
to the end of the menu.
@param item {Item,Menu,Separator}
The `Item`, `Menu`, or `Separator` to add to the menu.
@ -630,7 +710,7 @@ A labeled menu item that expands into a submenu.
<api name="removeItem">
@method
Removes the given menu item from the menu. If the menu does not contain the
Removes the given menu item from the menu. If the menu does not contain the
item, this method does nothing.
@param item {Item,Menu,Separator}
The menu item to remove from the menu.
@ -639,7 +719,7 @@ A labeled menu item that expands into a submenu.
<api name="destroy">
@method
Permanently removes the menu from its parent menu and frees its resources.
The menu must not be used afterward. If you need to remove the menu from its
The menu must not be used afterward. If you need to remove the menu from its
parent menu but use it afterward, call `removeItem()` on the parent menu
instead.
</api>
@ -661,7 +741,7 @@ from the content script. The message can be any
<api name="Separator">
@class
A menu separator. Separators can be contained only in `Menu`s, not in the
A menu separator. Separators can be contained only in `Menu`s, not in the
top-level context menu.
<api name="Separator">
@ -671,14 +751,14 @@ top-level context menu.
<api name="parentMenu">
@property {Menu}
The separator's parent `Menu`. This property is read-only. To add the
The separator's parent `Menu`. This property is read-only. To add the
separator to a new menu, call that menu's `addItem()` method.
</api>
<api name="destroy">
@method
Permanently removes the separator from its parent menu and frees its
resources. The separator must not be used afterward. If you need to remove
resources. The separator must not be used afterward. If you need to remove
the separator from its parent menu but use it afterward, call `removeItem()`
on the parent menu instead.
</api>
@ -689,7 +769,7 @@ top-level context menu.
@class
<api name="PageContext">
@constructor
Creates a page context. See Specifying Contexts above.
Creates a page context. See Specifying Contexts above.
</api>
</api>
@ -697,7 +777,7 @@ top-level context menu.
@class
<api name="SelectionContext">
@constructor
Creates a context that occurs when a page contains a selection. See
Creates a context that occurs when a page contains a selection. See
Specifying Contexts above.
</api>
</api>
@ -706,7 +786,7 @@ top-level context menu.
@class
<api name="SelectorContext">
@constructor
Creates a context that matches a given CSS selector. See Specifying Contexts
Creates a context that matches a given CSS selector. See Specifying Contexts
above.
@param selector {string}
A CSS selector.
@ -717,7 +797,7 @@ top-level context menu.
@class
<api name="URLContext">
@constructor
Creates a context that matches pages with particular URLs. See Specifying
Creates a context that matches pages with particular URLs. See Specifying
Contexts above.
@param matchPattern {string,array}
A [match pattern](modules/sdk/page-mod/match-pattern.html) string, regexp or an

View File

@ -201,11 +201,11 @@ functions to asynchronous by making it aware of promises. Module exports
`promised` function to do exactly that:
const { promised } = require('sdk/core/promise');
function sum(x, y) { return x + y }
var sumAsync = promised(sum);
function sum(x, y) { return x + y };
var asyncSum = promised(sum);
var c = sum(a, b);
var cAsync = asyncSum(aAsync(), bAsinc());
var cAsync = asyncSum(aAsync(), bAsync());
`promised` takes normal function and composes new promise aware version of it
that may take both normal values and promises as arguments and returns promise

View File

@ -7,7 +7,7 @@
<!-- contributed by Atul Varma [atul@mozilla.com] -->
<!-- edited by Noelle Murata [fiveinchpixie@gmail.com] -->
The `preferences-service` module provides access to the
The `preferences/service` module provides access to the
application-wide preferences service singleton.

View File

@ -27,14 +27,23 @@ but will have no effect when called.
<api name="isActive">
@property {boolean}
This read-only boolean is true if global private browsing mode is turned on.
This read-only boolean is `true` if global private browsing mode is turned on.
<div class="warning">
This property is deprecated. It will continue to work until version 1.13 of the SDK.
From version 1.13 onwards it will always return false.
From version 1.13 onwards it will always return `false`.
</div>
</api>
<api name="isPrivate">
@function
Returns `true` if the argument is a private window or tab.
@param [thing] {any}
The thing to check if it is private, only handles windows and tabs at the moment.
Everything else returns `false` automatically.
In global private browsing mode, this method returns the same value as `isActive`.
</api>
<api name="activate">
@function
Turns on global private browsing mode.

View File

@ -51,6 +51,12 @@ downgrade
</api>
<api name="isPrivateBrowsingSupported">
@property {boolean}
This property indicates add-on's support for private browsing. It comes from the
`private-browsing` property set in the `package.json` file in the main package.
</api>
<api name="data">
@property {object}
The `data` object is used to access data that was bundled with the add-on.
@ -92,3 +98,5 @@ as the Panel:
@returns {String}
</api>
</api>

View File

@ -45,7 +45,6 @@
<input type="text" name="q" size="31" id="search-box" />
</div>
</form>
<script type="text/javascript" src="https://www.google.com/cse/brand?form=cse-search-box&lang=en"></script>
<!-- Google CSE Search Box Ends -->
</div>
</div>
@ -163,6 +162,8 @@
<script type="text/javascript" src="static-files/js/jquery.js"></script>
<script type="text/javascript" src="static-files/js/main.js"></script>
<!-- load the google JS last, in case we're offline ( bug 836955 ) -->
<script type="text/javascript" src="https://www.google.com/cse/brand?form=cse-search-box&lang=en"></script>
</body>

View File

@ -441,8 +441,8 @@ ul.tree ul li:last-child {
background: #fff url("../media/icons/lastnode.png") no-repeat;
}
ul.module-list,
ul#module-index {
ul#module-index,
h2[id="Module Compatibility"] ~ ul {
border-color: #a0d0fb;
border: solid 1px #a0d0fb;
padding: 1em;
@ -455,8 +455,8 @@ ul#module-index {
column-gap: 1em;
}
span.unsupported-on-mobile,
ul.module-list li.unsupported-on-mobile,
ul.module-list li.unsupported-on-mobile > a {
color: maroon;
span.unsupported-on-mobile strong,
h2[id="Module Compatibility"] ~ ul strong {
font-weight: normal;
text-decoration: line-through;
}

View File

@ -86,8 +86,7 @@ function startup(reason, options) {
then(function onLocalizationReady(data) {
// Exports data to a pseudo module so that api-utils/l10n/core
// can get access to it
if (data)
definePseudo(options.loader, '@l10n/data', data);
definePseudo(options.loader, '@l10n/data', data ? data : null);
run(options);
});
}

View File

@ -69,11 +69,15 @@ function message(print, level) {
}
function errorMessage(print, e) {
// Some platform exception doesn't have name nor message but
// can be stringified to a meaningfull message
var fullString = ("An exception occurred.\n" +
e.name + ": " + e.message + "\n" +
traceback.sourceURI(e.fileName) + " " +
e.lineNumber + "\n" +
traceback.format(e));
(e.name ? e.name + ": " : "") +
(e.message ? e.message : e.toString()) + "\n" +
(e.fileName ? traceback.sourceURI(e.fileName) + " " +
e.lineNumber + "\n"
: "") +
traceback.format(e));
message(print, "error", fullString);
}

View File

@ -204,6 +204,9 @@ function removeItemFromArray(array, item) {
return array.filter(function(i) i !== item);
}
// Converts anything that isn't false, null or undefined into a string
function stringOrNull(val) val ? String(val) : val;
// Shared option validation rules for Item and Menu
let baseItemRules = {
parentMenu: {
@ -254,13 +257,13 @@ let baseItemRules = {
let labelledItemRules = mix(baseItemRules, {
label: {
map: String,
map: stringOrNull,
is: ["string"],
ok: function (v) !!v,
msg: "The item must have a non-empty string label."
},
image: {
map: String,
map: stringOrNull,
is: ["string", "undefined", "null"]
}
});
@ -268,8 +271,8 @@ let labelledItemRules = mix(baseItemRules, {
// Additional validation rules for Item
let itemRules = mix(labelledItemRules, {
data: {
map: String,
is: ["string", "undefined"]
map: stringOrNull,
is: ["string", "undefined", "null"]
}
});

View File

@ -11,7 +11,8 @@ module.metadata = {
const file = require("../io/file");
const memory = require('./memory');
const suites = require('@test/options').allTestModules;
const { Loader } = require("sdk/test/loader");
const cuddlefish = require("sdk/loader/cuddlefish");
const NOT_TESTS = ['setup', 'teardown'];
@ -52,7 +53,11 @@ TestFinder.prototype = {
suites.forEach(
function(suite) {
var module = require(suite);
// Load each test file as a main module in its own loader instance
// `suite` is defined by cuddlefish/manifest.py:ManifestBuilder.build
var loader = Loader(module);
var module = cuddlefish.main(loader, suite);
if (self.testInProcess)
for each (let name in Object.keys(module).sort()) {
if(NOT_TESTS.indexOf(name) === -1 && filter(suite, name)) {

View File

@ -10,7 +10,7 @@ module.metadata = {
const { Cc, Ci } = require("chrome");
const { extend } = require("./core/heritage");
const { id } = require("self");
const { id } = require("./self");
// placeholder, copied from bootstrap.js
let sanitizeId = function(id){

View File

@ -8,11 +8,15 @@ module.metadata = {
"stability": "unstable"
};
const { Ci } = require("chrome");
const { Ci, Cu } = require("chrome");
const events = require("../system/events");
const core = require("./core");
const assetsURI = require('../self').data.url();
const { Services } = Cu.import("resource://gre/modules/Services.jsm");
const hideContentStyle = "data:text/css,:root {visibility: hidden !important;}";
const hideSheetUri = Services.io.newURI(hideContentStyle, null, null);
// Taken from Gaia:
// https://github.com/andreasgal/gaia/blob/04fde2640a7f40314643016a5a6c98bf3755f5fd/webapi.js#L1470
@ -41,8 +45,15 @@ function onDocumentReady2Translate(event) {
translateElement(document);
// Finally display document when we finished replacing all text content
document.documentElement.style.visibility = "visible";
try {
// Finally display document when we finished replacing all text content
let winUtils = document.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
winUtils.removeSheet(hideSheetUri, winUtils.USER_SHEET);
}
catch(e) {
console.exception(e);
}
}
function onContentWindow(event) {
@ -61,12 +72,16 @@ function onContentWindow(event) {
if (document.location.href.indexOf(assetsURI) !== 0)
return;
// First hide content of the document in order to have content blinking
// between untranslated and translated states
// TODO: use result of bug 737003 discussion in order to avoid any conflict
// with document CSS
document.documentElement.style.visibility = "hidden";
try {
// First hide content of the document in order to have content blinking
// between untranslated and translated states
let winUtils = document.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
winUtils.loadSheet(hideSheetUri, winUtils.USER_SHEET);
}
catch(e) {
console.exception(e);
}
// Wait for DOM tree to be built before applying localization
document.addEventListener("DOMContentLoaded", onDocumentReady2Translate,
false);
@ -92,4 +107,4 @@ function disable() {
}
exports.disable = disable;
require("api-utils/unload").when(disable);
require("sdk/system/unload").when(disable);

View File

@ -88,8 +88,8 @@ function CuddlefishLoader(options) {
// cache to avoid subsequent loads via `require`.
modules: override({
'toolkit/loader': loaderModule,
'addon-sdk/sdk/loader/cuddlefish': exports,
'addon-sdk/sdk/system/xul-app': xulappModule
'sdk/loader/cuddlefish': exports,
'sdk/system/xul-app': xulappModule
}, options.modules),
resolve: function resolve(id, requirer) {
let entry = requirer && requirer in manifest && manifest[requirer];
@ -103,15 +103,14 @@ function CuddlefishLoader(options) {
// If requirer entry is in manifest and it's requirement is not, than
// it has no authority to load since linker was not able to find it.
if (!requirement)
throw Error('Module: ' + requirer.id + ' located at ' + requirer.uri
+ ' has no authority to load: ' + id, requirer.uri);
throw Error('Module: ' + requirer + ' has no authority to load: '
+ id, requirer);
uri = requirement;
}
// If requirer is off manifest than it's a system module and we allow it
// to go off manifest.
else {
uri = id;
} else {
// If requirer is off manifest than it's a system module and we allow it
// to go off manifest by resolving a relative path.
uri = loaderModule.resolve(id, requirer);
}
return uri;
},

View File

@ -114,6 +114,10 @@ XMLHttpRequest.prototype = {
set upload(newValue) {
throw new Error("not implemented");
},
forceAllowThirdPartyCookie: function forceAllowThirdPartyCookie() {
if (this._req.channel instanceof Ci.nsIHttpChannelInternal)
this._req.channel.forceAllowThirdPartyCookie = true;
},
get onreadystatechange() {
return this._orsc;
},

View File

@ -7,10 +7,11 @@ module.metadata = {
"stability": "stable"
};
const { setMode, getMode, on: onStateChange } = require('./private-browsing/utils');
const { setMode, getMode, on: onStateChange, isWindowPrivate } = require('./private-browsing/utils');
const { emit, on, once, off } = require('./event/core');
const { when: unload } = require('./system/unload');
const { deprecateUsage, deprecateFunction, deprecateEvent } = require('./util/deprecate');
const { getOwnerWindow } = require('./private-browsing/window/utils');
onStateChange('start', function onStart() {
emit(exports, 'start');
@ -36,6 +37,19 @@ exports.removeListener = deprecateEvents(function removeListener(type, listener)
off(exports, type, listener);
});
exports.isPrivate = function(thing) {
if (!!thing) {
if (isWindowPrivate(thing)) {
return true;
}
let window = getOwnerWindow(thing);
if (window)
return isWindowPrivate(window);
}
return getMode();
};
function deprecateEvents(func) deprecateEvent(
func,
'The require("private-browsing") module\'s "start" and "stop" events are deprecated.',

View File

@ -15,13 +15,14 @@ const { getWindowLoadingContext, windows } = require('../window/utils');
const { WindowTracker } = require("../deprecated/window-utils");
const events = require('../system/events');
const { deprecateFunction } = require('../util/deprecate');
const { isOneOf, is, satisfiesVersion, version } = require('../system/xul-app');
let deferredEmit = defer(emit);
let pbService;
let PrivateBrowsingUtils;
// Private browsing is only supported in Fx
if (require("../system/xul-app").is("Firefox")) {
if (isOneOf(['Firefox', 'Fennec'])) {
// get the nsIPrivateBrowsingService if it exists
try {
pbService = Cc["@mozilla.org/privatebrowsing;1"].
@ -41,20 +42,36 @@ if (require("../system/xul-app").is("Firefox")) {
}
function isWindowPrivate(win) {
if (!PrivateBrowsingUtils || !win)
return false;
// if the pbService is undefined, the PrivateBrowsingUtils.jsm is available,
// and the app is Firefox, then assume per-window private browsing is
// enabled.
return win instanceof Ci.nsIDOMWindow &&
isWindowPBSupported &&
PrivateBrowsingUtils.isWindowPrivate(win);
if (win instanceof Ci.nsIDOMWindow) {
return PrivateBrowsingUtils.isWindowPrivate(win);
}
// Sometimes the input is not a nsIDOMWindow.. but it is still a winodw.
try {
return !!win.docShell.QueryInterface(Ci.nsILoadContext).usePrivateBrowsing;
}
catch (e) {}
return false;
}
exports.isWindowPrivate = isWindowPrivate;
// checks that global private browsing is implemented
let isGlobalPBSupported = exports.isGlobalPBSupported = !!pbService;
let isGlobalPBSupported = exports.isGlobalPBSupported = !!pbService && is('Firefox');
// checks that per-window private browsing is implemented
let isWindowPBSupported = exports.isWindowPBSupported = !isGlobalPBSupported && !!PrivateBrowsingUtils;
let isWindowPBSupported = exports.isWindowPBSupported =
!pbService && !!PrivateBrowsingUtils && is('Firefox');
// checks that per-tab private browsing is implemented
let isTabPBSupported = exports.isTabPBSupported =
!pbService && !!PrivateBrowsingUtils && is('Fennec') && satisfiesVersion(version, '>=20.0*');
function onChange() {
// Emit event with in next turn of event loop.

View File

@ -0,0 +1,32 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
'use strict';
module.metadata = {
'stability': 'unstable'
};
const privateNS = require('../../core/namespace').ns();
function getOwnerWindow(thing) {
try {
// check for and return associated window
let fn = (privateNS(thing.prototype) || privateNS(thing) || {}).getOwnerWindow;
if (fn)
return fn.apply(fn, [thing].concat(arguments));
}
// stuff like numbers and strings throw errors with namespaces
catch(e) {}
// default
return undefined;
}
getOwnerWindow.define = function(Type, fn) {
privateNS(Type.prototype).getOwnerWindow = fn;
}
getOwnerWindow.implement = function(instance, fn) {
privateNS(instance).getOwnerWindow = fn;
}
exports.getOwnerWindow = getOwnerWindow;

View File

@ -71,6 +71,8 @@ function runRequest(mode, target) {
// open the request
xhr.open(mode, url);
xhr.forceAllowThirdPartyCookie();
// request header must be set after open, but before send
xhr.setRequestHeader("Content-Type", contentType);

View File

@ -20,7 +20,7 @@ const { Ci, Cc } = require("chrome"),
{ when: unload } = require("./system/unload"),
{ getTabs, getTabContentWindow, getTabForContentWindow,
getAllTabContentWindows } = require('./tabs/utils'),
{ getInnerId, getMostRecentBrowserWindow,
{ getMostRecentBrowserWindow,
windows, getFocusedWindow, getFocusedElement } = require("./window/utils"),
events = require("./system/events");
@ -321,17 +321,6 @@ function addSelectionListener(window) {
window.addEventListener("select", selectionListener.onSelect, true);
selections(window).selection = selection;
let innerId = getInnerId(window);
events.on("inner-window-destroyed", function destroyed (event) {
let destroyedId = event.subject.QueryInterface(Ci.nsISupportsPRUint64).data;
if (destroyedId === innerId) {
removeSelectionListener(window);
events.off("inner-window-destroyed", destroyed);
}
});
};
/**
@ -382,7 +371,7 @@ getAllTabContentWindows().forEach(addSelectionListener);
//
// See bug 665386 for further details.
events.on("document-shown", function (event) {
function onShown(event) {
let window = event.subject.defaultView;
// We are not interested in documents without valid defaultView.
@ -408,17 +397,21 @@ events.on("document-shown", function (event) {
if (currentSelection instanceof Ci.nsISelectionPrivate &&
currentSelection !== selection) {
window.addEventListener("select", selectionListener.onSelect, true);
currentSelection.addSelectionListener(selectionListener);
selections(window).selection = currentSelection;
}
}
});
}
events.on("document-shown", onShown, true);
// Removes Selection listeners when the add-on is unloaded
unload(function(){
getAllTabContentWindows().forEach(removeSelectionListener);
events.off("document-element-inserted", onContent);
events.off("document-shown", onShown);
off(exports);
});

View File

@ -2,16 +2,14 @@
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
module.metadata = {
"stability": "stable"
};
const { CC } = require('chrome');
const { id, name, prefixURI, rootURI,
const { id, name, prefixURI, rootURI, metadata,
version, loadReason } = require('@loader/options');
const { readURISync } = require('./net/url');
@ -39,3 +37,5 @@ exports.data = Object.freeze({
return readURISync(uri(path));
}
});
exports.isPrivateBrowsingSupported = ((metadata.permissions || {})['private-browsing'] === true) ?
true : false;

View File

@ -16,4 +16,13 @@ exports.inSafeMode = runtime.inSafeMode;
exports.OS = runtime.OS;
exports.processType = runtime.processType;
exports.widgetToolkit = runtime.widgetToolkit;
exports.XPCOMABI = runtime.XPCOMABI;
// Attempt to access `XPCOMABI` may throw exception, in which case exported
// `XPCOMABI` will be set to `null`.
// https://mxr.mozilla.org/mozilla-central/source/toolkit/xre/nsAppRunner.cpp#732
try {
exports.XPCOMABI = runtime.XPCOMABI;
}
catch (error) {
exports.XPCOMABI = null;
}

View File

@ -1,7 +1,6 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
module.metadata = {
@ -103,7 +102,6 @@ function normalizeRange(range) {
* A version to compare
*/
function compareVersion(version, comparison, compareVersion) {
let hasWildcard = compareVersion.indexOf("*") !== -1;
comparison = comparison || "=";
@ -181,5 +179,4 @@ function satisfiesVersion(version, versionRange) {
: true);
});
}
exports.satisfiesVersion = satisfiesVersion;

View File

@ -7,9 +7,10 @@ const { Cc, Ci } = require('chrome');
const { Class } = require('../core/heritage');
const { tabNS } = require('./namespace');
const { EventTarget } = require('../event/target');
const { activateTab, getTabTitle, setTabTitle, closeTab, getTabURL,
const { activateTab, getTabTitle, setTabTitle, closeTab, getTabURL, getContentWindowForTab,
setTabURL, getOwnerWindow, getTabContentType, getTabId } = require('./utils');
const { emit } = require('../event/core');
const { getOwnerWindow: getPBOwnerWindow } = require('../private-browsing/window/utils');
const { when: unload } = require('../system/unload');
const { EVENTS } = require('./events');
@ -143,3 +144,7 @@ const Tab = Class({
}
});
exports.Tab = Tab;
getPBOwnerWindow.define(Tab, function(tab) {
return getContentWindowForTab(tabNS(tab).tab);
});

View File

@ -11,6 +11,8 @@ const { getThumbnailURIForWindow } = require("../content/thumbnail");
const { getFaviconURIForLocation } = require("../io/data");
const { activateTab, getOwnerWindow, getBrowserForTab, getTabTitle, setTabTitle,
getTabURL, setTabURL, getTabContentType, getTabId } = require('./utils');
const { getOwnerWindow: getPBOwnerWindow } = require('../private-browsing/window/utils');
const viewNS = require('sdk/core/namespace').ns();
// Array of the inner instances of all the wrapped tabs.
const TABS = [];
@ -50,6 +52,9 @@ const TabTrait = Trait.compose(EventEmitter, {
if (options.isPinned)
this.pin();
viewNS(this._public).tab = this._tab;
getPBOwnerWindow.implement(this._public, getChromeTab);
// Since we will have to identify tabs by a DOM elements facade function
// is used as constructor that collects all the instances and makes sure
// that they more then one wrapper is not created per tab.
@ -212,6 +217,10 @@ const TabTrait = Trait.compose(EventEmitter, {
}
});
function getChromeTab(tab) {
return getOwnerWindow(viewNS(tab).tab);
}
function Tab(options) {
let chromeTab = options.tab;
for each (let tab in TABS) {

View File

@ -80,7 +80,8 @@ exports.getOwnerWindow = getOwnerWindow;
// fennec
function getWindowHoldingTab(rawTab) {
for each (let window in windows()) {
// this function may be called when not using fennec
// this function may be called when not using fennec,
// but BrowserApp is only defined on Fennec
if (!window.BrowserApp)
continue;
@ -100,7 +101,8 @@ function openTab(window, url, options) {
if (window.BrowserApp) {
return window.BrowserApp.addTab(url, {
selected: options.inBackground ? false : true,
pinned: options.isPinned || false
pinned: options.isPinned || false,
isPrivate: options.private || false
});
}
return window.gBrowser.addTab(url);
@ -150,6 +152,12 @@ function getBrowserForTab(tab) {
}
exports.getBrowserForTab = getBrowserForTab;
function getContentWindowForTab(tab) {
return getBrowserForTab(tab).contentWindow;
}
exports.getContentWindowForTab = getContentWindowForTab;
function getTabId(tab) {
if (tab.browser) // fennec
return tab.id
@ -158,7 +166,6 @@ function getTabId(tab) {
}
exports.getTabId = getTabId;
function getTabTitle(tab) {
return getBrowserForTab(tab).contentDocument.title || tab.label || "";
}

View File

@ -9,8 +9,8 @@ module.metadata = {
"stability": "unstable"
};
const BaseAssert = require("./test/assert").Assert;
const { isFunction, isObject } = require("./lang/type");
const BaseAssert = require("sdk/test/assert").Assert;
const { isFunction, isObject } = require("sdk/lang/type");
function extend(target) {
let descriptor = {}

View File

@ -20,7 +20,7 @@ const system = require("../system");
// Trick manifest builder to make it think we need these modules ?
const unit = require("../deprecated/unit-test");
const test = require("../test");
const test = require("../../test");
const url = require("../url");
var cService = Cc['@mozilla.org/consoleservice;1'].getService()

View File

@ -105,7 +105,7 @@ exports.runTestsFromModule = function runTestsFromModule(module) {
runTests(function findAndRunTests(loader, nextIteration) {
// Consider that all these tests are CommonJS ones
loader.require('../test').run(exports);
loader.require('../../test').run(exports);
// Reproduce what is done in unit-test-finder.findTests()
let tests = [];

View File

@ -11,6 +11,7 @@ const { getWindowTitle } = require('./utils');
const unload = require('../system/unload');
const { getMode } = require('../private-browsing/utils');
const { EventTarget } = require('../event/target');
const { getOwnerWindow: getPBOwnerWindow } = require('../private-browsing/window/utils');
const ERR_FENNEC_MSG = 'This method is not yet supported by Fennec, consider using require("tabs") instead';
@ -39,3 +40,7 @@ const BrowserWindow = Class({
get isPrivateBrowsing() getMode(windowNS(this).window),
});
exports.BrowserWindow = BrowserWindow;
getPBOwnerWindow.define(BrowserWindow, function(window) {
return windowNS(window).window;
});

View File

@ -17,7 +17,9 @@ const { Cc, Ci, Cr } = require('chrome'),
windowUtils = require('../deprecated/window-utils'),
{ WindowTrackerTrait } = windowUtils,
{ ns } = require('../core/namespace'),
{ observer: windowObserver } = require('./observer');
{ observer: windowObserver } = require('./observer'),
{ getOwnerWindow } = require('../private-browsing/window/utils'),
viewNS = require('sdk/core/namespace').ns();
/**
* Window trait composes safe wrappers for browser window that are E10S
@ -69,6 +71,10 @@ const BrowserWindowTrait = Trait.compose(
this._private = !!options.private;
this._load();
viewNS(this._public).window = this._window;
getOwnerWindow.implement(this._public, getChromeWindow);
return this;
},
destroy: function () this._onUnload(),
@ -239,4 +245,8 @@ const browserWindows = Trait.resolve({ toString: null }).compose(
}).resolve({ toString: null })
)();
function getChromeWindow(window) {
return viewNS(window).window;
}
exports.browserWindows = browserWindows;

View File

@ -0,0 +1,112 @@
/* vim:ts=2:sts=2:sw=2:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
module.metadata = {
"stability": "unstable"
};
const BaseAssert = require("sdk/test/assert").Assert;
const { isFunction, isObject } = require("sdk/lang/type");
function extend(target) {
let descriptor = {}
Array.slice(arguments, 1).forEach(function(source) {
Object.getOwnPropertyNames(source).forEach(function onEach(name) {
descriptor[name] = Object.getOwnPropertyDescriptor(source, name);
});
});
return Object.create(target, descriptor);
}
/**
* Function takes test `suite` object in CommonJS format and defines all of the
* tests from that suite and nested suites in a jetpack format on a given
* `target` object. Optionally third argument `prefix` can be passed to prefix
* all the test names.
*/
function defineTestSuite(target, suite, prefix) {
prefix = prefix || "";
// If suite defines `Assert` that's what `assert` object have to be created
// from and passed to a test function (This allows custom assertion functions)
// See for details: http://wiki.commonjs.org/wiki/Unit_Testing/1.1
let Assert = suite.Assert || BaseAssert;
// Going through each item in the test suite and wrapping it into a
// Jetpack test format.
Object.keys(suite).forEach(function(key) {
// If name starts with test then it's a test function or suite.
if (key.indexOf("test") === 0) {
let test = suite[key];
// For each test function so we create a wrapper test function in a
// jetpack format and copy that to a `target` exports.
if (isFunction(test)) {
// Since names of the test may match across suites we use full object
// path as a name to avoid overriding same function.
target[prefix + key] = function(options) {
// Creating `assert` functions for this test.
let assert = Assert(options);
// If CommonJS test function expects more than one argument
// it means that test is async and second argument is a callback
// to notify that test is finished.
if (1 < test.length) {
// Letting test runner know that test is executed async and
// creating a callback function that CommonJS tests will call
// once it's done.
options.waitUntilDone();
test(assert, function() {
options.done();
});
}
// Otherwise CommonJS test is synchronous so we call it only with
// one argument.
else {
test(assert);
}
}
}
// If it's an object then it's a test suite containing test function
// and / or nested test suites. In that case we just extend prefix used
// and call this function to copy and wrap tests from nested suite.
else if (isObject(test)) {
// We need to clone `tests` instead of modifying it, since it's very
// likely that it is frozen (usually test suites imported modules).
test = extend(Object.prototype, test, {
Assert: test.Assert || Assert
});
defineTestSuite(target, test, prefix + key + ".");
}
}
});
}
/**
* This function is a CommonJS test runner function, but since Jetpack test
* runner and test format is different from CommonJS this function shims given
* `exports` with all its tests into a Jetpack test format so that the built-in
* test runner will be able to run CommonJS test without manual changes.
*/
exports.run = function run(exports) {
// We can't leave old properties on exports since those are test in a CommonJS
// format that why we move everything to a new `suite` object.
let suite = {};
Object.keys(exports).forEach(function(key) {
suite[key] = exports[key];
delete exports[key];
});
// Now we wrap all the CommonJS tests to a Jetpack format and define
// those to a given `exports` object since that where jetpack test runner
// will look for them.
defineTestSuite(exports, suite);
};

View File

@ -206,7 +206,6 @@ const evaluate = iced(function evaluate(sandbox, uri, options) {
source: null
}, options);
return source ? Cu.evalInSandbox(source, sandbox, version, uri, line)
: loadSubScript(uri, sandbox, encoding);
});
@ -281,6 +280,7 @@ function normalize(uri) { return uri.substr(-3) === '.js' ? uri : uri + '.js'; }
// `requirer.uri` but in some cases it may be `baseURI`. In order to
// avoid complexity we require `baseURI` with a trailing `/`.
const resolve = iced(function resolve(id, base) {
if (!isRelative(id)) return id;
let paths = id.split('/');
let result = base.split('/');
result.pop();
@ -320,7 +320,6 @@ const Require = iced(function Require(loader, requirer) {
// Resolve `id` to its requirer if it's relative.
let requirement = requirer ? resolve(id, requirer.id) : id;
// Resolves `uri` of module using loaders resolve function.
let uri = resolveURI(requirement, mapping);

View File

@ -769,7 +769,7 @@ def run(arguments=sys.argv[1:], target_cfg=None, pkg_cfg=None,
# This should be contained in the test runner package.
# maybe just do: target_cfg.main = 'test-harness/run-tests'
harness_options['main'] = 'sdk/test/runner'
harness_options['mainPath'] = manifest.get_manifest_entry("addon-sdk", "lib", "sdk/test/runner").get_path()
harness_options['mainPath'] = 'sdk/test/runner'
else:
harness_options['main'] = target_cfg.get('main')
harness_options['mainPath'] = manifest.top_path
@ -832,10 +832,12 @@ def run(arguments=sys.argv[1:], target_cfg=None, pkg_cfg=None,
if command == 'xpi':
from cuddlefish.xpi import build_xpi
# Generate extra options
extra_harness_options = {}
for kv in options.extra_harness_option_args:
key,value = kv.split("=", 1)
extra_harness_options[key] = value
# Generate xpi filepath
xpi_path = XPI_FILENAME % target_cfg.name
print >>stdout, "Exporting extension to %s." % xpi_path
build_xpi(template_root_dir=app_extension_dir,

View File

@ -196,4 +196,3 @@ def replace_file(env_root, dest_path, file_contents, must_rewrite_links):
if must_rewrite_links and dest_path.endswith(".html"):
file_contents = rewrite_links(env_root, get_sdk_docs_path(env_root), file_contents, dest_path)
open(dest_path, "w").write(file_contents)

View File

@ -58,11 +58,22 @@ class ManifestEntry:
self.datamap = None
def get_path(self):
path = "%s/%s/%s" % \
(self.packageName, self.sectionName, self.moduleName)
if not path.endswith(".js"):
path += ".js"
return path
name = self.moduleName
if name.endswith(".js"):
name = name[:-3]
items = []
# Only add package name for addons, so that system module paths match
# the path from the commonjs root directory and also match the loader
# mappings.
if self.packageName != "addon-sdk":
items.append(self.packageName)
# And for the same reason, do not append `lib/`.
if self.sectionName == "tests":
items.append(self.sectionName)
items.append(name)
return "/".join(items)
def get_entry_for_manifest(self):
entry = { "packageName": self.packageName,
@ -75,13 +86,13 @@ class ManifestEntry:
for req in self.requirements:
if isinstance(self.requirements[req], ManifestEntry):
them = self.requirements[req] # this is another ManifestEntry
them_path = them.get_path()
entry["requirements"][req] = {"path": them_path}
entry["requirements"][req] = them.get_path()
else:
# something magic. The manifest entry indicates that they're
# allowed to require() it
entry["requirements"][req] = self.requirements[req]
assert isinstance(entry["requirements"][req], dict)
assert isinstance(entry["requirements"][req], unicode) or \
isinstance(entry["requirements"][req], str)
return entry
def add_js(self, js_filename):
@ -226,7 +237,8 @@ class ManifestBuilder:
# search for all tests. self.test_modules will be passed
# through the harness-options.json file in the
# .allTestModules property.
self.test_modules.append(testname)
# Pass the absolute module path.
self.test_modules.append(tme.get_path())
# include files used by the loader
for em in self.extra_modules:
@ -378,7 +390,7 @@ class ManifestBuilder:
# If requirement is chrome or a pseudo-module (starts with @) make
# path a requirement name.
if reqname == "chrome" or reqname.startswith("@"):
me.add_requirement(reqname, {"path": reqname})
me.add_requirement(reqname, reqname)
else:
# when two modules require() the same name, do they get a
# shared instance? This is a deep question. For now say yes.

View File

@ -23,7 +23,7 @@ DEFAULT_ICON64 = 'icon64.png'
METADATA_PROPS = ['name', 'description', 'keywords', 'author', 'version',
'translators', 'contributors', 'license', 'homepage', 'icon',
'icon64', 'main', 'directories']
'icon64', 'main', 'directories', 'permissions']
RESOURCE_HOSTNAME_RE = re.compile(r'^[a-z0-9_\-]+$')

View File

@ -169,7 +169,7 @@ def gen_manifest(template_root_dir, target_cfg, jid,
ta_desc.appendChild(elem)
elem = dom.createElement("em:maxVersion")
elem.appendChild(dom.createTextNode("20.*"))
elem.appendChild(dom.createTextNode("21.0a1"))
ta_desc.appendChild(elem)
if target_cfg.get("homepage"):

View File

@ -46,15 +46,15 @@ class Basic(unittest.TestCase):
m = m.get_harness_options_manifest()
def assertReqIs(modname, reqname, path):
reqs = m["one/lib/%s.js" % modname]["requirements"]
self.failUnlessEqual(reqs[reqname]["path"], path)
assertReqIs("main", "panel", "addon-sdk/lib/sdk/panel.js")
assertReqIs("main", "two.js", "one/lib/two.js")
assertReqIs("main", "./two", "one/lib/two.js")
assertReqIs("main", "sdk/tabs.js", "addon-sdk/lib/sdk/tabs.js")
assertReqIs("main", "./subdir/three", "one/lib/subdir/three.js")
assertReqIs("two", "main", "one/lib/main.js")
assertReqIs("subdir/three", "../main", "one/lib/main.js")
reqs = m["one/%s" % modname]["requirements"]
self.failUnlessEqual(reqs[reqname], path)
assertReqIs("main", "panel", "sdk/panel")
assertReqIs("main", "two.js", "one/two")
assertReqIs("main", "./two", "one/two")
assertReqIs("main", "sdk/tabs.js", "sdk/tabs")
assertReqIs("main", "./subdir/three", "one/subdir/three")
assertReqIs("two", "main", "one/main")
assertReqIs("subdir/three", "../main", "one/main")
target_cfg.dependencies = []
# now, because .dependencies *is* provided, we won't search 'deps',
@ -74,11 +74,11 @@ class Basic(unittest.TestCase):
m = manifest.build_manifest(target_cfg, pkg_cfg, deps, scan_tests=False)
m = m.get_harness_options_manifest()
def assertReqIs(modname, reqname, path):
reqs = m["three/lib/%s.js" % modname]["requirements"]
self.failUnlessEqual(reqs[reqname]["path"], path)
assertReqIs("main", "three-a", "three-a/lib/main.js")
assertReqIs("main", "three-b", "three-b/lib/main.js")
assertReqIs("main", "three-c", "three-c/lib/main.js")
reqs = m["three/%s" % modname]["requirements"]
self.failUnlessEqual(reqs[reqname], path)
assertReqIs("main", "three-a", "three-a/main")
assertReqIs("main", "three-b", "three-b/main")
assertReqIs("main", "three-c", "three-c/main")
def test_relative_main_in_top(self):
target_cfg = self.get_pkg("five")
@ -91,7 +91,7 @@ class Basic(unittest.TestCase):
# all we care about is that this next call doesn't raise an exception
m = manifest.build_manifest(target_cfg, pkg_cfg, deps, scan_tests=False)
m = m.get_harness_options_manifest()
reqs = m["five/lib/main.js"]["requirements"]
reqs = m["five/main"]["requirements"]
self.failUnlessEqual(reqs, {});
def test_unreachable_relative_main_in_top(self):

View File

@ -328,7 +328,7 @@ class SmallXPI(unittest.TestCase):
[target_cfg.name, "addon-sdk"])
m = manifest.build_manifest(target_cfg, pkg_cfg, deps, scan_tests=True)
self.failUnlessEqual(sorted(m.get_all_test_modules()),
sorted(["test-one", "test-two"]))
sorted(["three/tests/test-one", "three/tests/test-two"]))
# the current __init__.py code omits limit_to=used_files for 'cfx
# test', so all test files are included in the XPI. But the test
# runner will only execute the tests that m.get_all_test_modules()
@ -371,7 +371,7 @@ class SmallXPI(unittest.TestCase):
m = manifest.build_manifest(target_cfg, pkg_cfg, deps, scan_tests=True,
test_filter_re=FILTER)
self.failUnlessEqual(sorted(m.get_all_test_modules()),
sorted(["test-one"]))
sorted(["three/tests/test-one"]))
# the current __init__.py code omits limit_to=used_files for 'cfx
# test', so all test files are included in the XPI. But the test
# runner will only execute the tests that m.get_all_test_modules()

View File

@ -29,6 +29,7 @@ def build_xpi(template_root_dir, manifest, xpi_path,
zf.write('.install.rdf', 'install.rdf')
os.remove('.install.rdf')
# Handle add-on icon
if 'icon' in harness_options:
zf.write(str(harness_options['icon']), 'icon.png')
del harness_options['icon']
@ -37,6 +38,7 @@ def build_xpi(template_root_dir, manifest, xpi_path,
zf.write(str(harness_options['icon64']), 'icon64.png')
del harness_options['icon64']
# Handle simple-prefs
if 'preferences' in harness_options:
from options_xul import parse_options, validate_prefs
@ -133,7 +135,7 @@ def build_xpi(template_root_dir, manifest, xpi_path,
parentpath = ZIPSEP.join(bits[0:i])
dirs_to_create.add(parentpath)
# create zipfile in alphabetical order, with each directory before its
# Create zipfile in alphabetical order, with each directory before its
# files
for name in sorted(dirs_to_create.union(set(files_to_copy))):
if name in dirs_to_create:
@ -141,12 +143,15 @@ def build_xpi(template_root_dir, manifest, xpi_path,
if name in files_to_copy:
zf.write(files_to_copy[name], name)
# Add extra harness options
harness_options = harness_options.copy()
for key,value in extra_harness_options.items():
if key in harness_options:
msg = "Can't use --harness-option for existing key '%s'" % key
raise HarnessOptionAlreadyDefinedError(msg)
harness_options[key] = value
# Write harness-options.json
open('.options.json', 'w').write(json.dumps(harness_options, indent=1,
sort_keys=True))
zf.write('.options.json', 'harness-options.json')

View File

@ -182,7 +182,7 @@ class Markdown:
def __init__(self,
extensions=[],
extension_configs={},
safe_mode = False,
safe_mode = False,
output_format=DEFAULT_OUTPUT_FORMAT):
"""
Creates a new Markdown instance.
@ -200,12 +200,12 @@ class Markdown:
* "xhtml": Outputs latest supported version of XHTML (currently XHTML 1.1).
* "html4": Outputs HTML 4
* "html": Outputs latest supported version of HTML (currently HTML 4).
Note that it is suggested that the more specific formats ("xhtml1"
Note that it is suggested that the more specific formats ("xhtml1"
and "html4") be used as "xhtml" or "html" may change in the future
if it makes sense at that time.
if it makes sense at that time.
"""
self.safeMode = safe_mode
self.registeredExtensions = []
self.docType = ""
@ -300,9 +300,9 @@ class Markdown:
# Map format keys to serializers
self.output_formats = {
'html' : html4.to_html_string,
'html' : html4.to_html_string,
'html4' : html4.to_html_string,
'xhtml' : etree.tostring,
'xhtml' : etree.tostring,
'xhtml1': etree.tostring,
}
@ -332,7 +332,7 @@ class Markdown:
except AttributeError:
message(ERROR, "Incorrect type! Extension '%s' is "
"neither a string or an Extension." %(repr(ext)))
def registerExtension(self, extension):
""" This gets called by the extension """
@ -574,15 +574,15 @@ def markdown(text,
* "xhtml": Outputs latest supported version of XHTML (currently XHTML 1.1).
* "html4": Outputs HTML 4
* "html": Outputs latest supported version of HTML (currently HTML 4).
Note that it is suggested that the more specific formats ("xhtml1"
Note that it is suggested that the more specific formats ("xhtml1"
and "html4") be used as "xhtml" or "html" may change in the future
if it makes sense at that time.
if it makes sense at that time.
Returns: An HTML document as a string.
"""
md = Markdown(extensions=load_extensions(extensions),
safe_mode=safe_mode,
safe_mode=safe_mode,
output_format=output_format)
return md.convert(text)
@ -594,7 +594,7 @@ def markdownFromFile(input = None,
safe_mode = False,
output_format = DEFAULT_OUTPUT_FORMAT):
"""Read markdown code from a file and write it to a file or a stream."""
md = Markdown(extensions=load_extensions(extensions),
md = Markdown(extensions=load_extensions(extensions),
safe_mode=safe_mode,
output_format=output_format)
md.convertFile(input, output, encoding)

View File

@ -453,8 +453,10 @@ class Runner(object):
if binary is None:
for bin in [(program_files, 'Mozilla Firefox', 'firefox.exe'),
(os.environ.get("ProgramFiles(x86)"),'Mozilla Firefox', 'firefox.exe'),
(program_files,'Nightly', 'firefox.exe'),
(os.environ.get("ProgramFiles(x86)"),'Nightly', 'firefox.exe')
(program_files, 'Nightly', 'firefox.exe'),
(os.environ.get("ProgramFiles(x86)"),'Nightly', 'firefox.exe'),
(program_files, 'Aurora', 'firefox.exe'),
(os.environ.get("ProgramFiles(x86)"),'Aurora', 'firefox.exe')
]:
path = os.path.join(*bin)
if os.path.isfile(path):

View File

@ -1,4 +1,4 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@ -184,4 +184,4 @@ if (require("sdk/system/xul-app").is("Fennec")) {
}
}
require("test").run(exports);
require("sdk/test/runner").runTestsFromModule(module);

View File

@ -0,0 +1,3 @@
{
"id": "test-layout-change"
}

View File

@ -0,0 +1,20 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { packed } = require("sdk/self");
const url = require("sdk/url");
exports["test self.packed"] = function (assert) {
assert.ok(packed, "require('sdk/self').packed is correct");
}
exports["test url.toFilename"] = function (assert) {
assert.throws(
function() { url.toFilename(module.uri); },
/cannot map to filename: /,
"url.toFilename() can fail for packed XPIs");
}
require("sdk/test/runner").runTestsFromModule(module);

View File

@ -0,0 +1,4 @@
{
"id": "test-url",
"unpack": false
}

View File

@ -0,0 +1,13 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
'use strict';
const { isPrivateBrowsingSupported } = require('sdk/self');
exports.testIsPrivateBrowsingTrue = function(assert) {
assert.ok(isPrivateBrowsingSupported,
'isPrivateBrowsingSupported property is true');
};
require('sdk/test/runner').runTestsFromModule(module);

View File

@ -0,0 +1,6 @@
{
"id": "private-browsing-mode-test@jetpack",
"permissions": {
"private-browsing": true
}
}

View File

@ -0,0 +1,18 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { packed } = require("sdk/self");
const url = require("sdk/url");
exports["test self.packed"] = function (assert) {
assert.ok(!packed, "require('sdk/self').packed is correct");
}
exports["test url.toFilename"] = function (assert) {
assert.ok(/.*main\.js$/.test(url.toFilename(module.uri)),
"url.toFilename() on resource: URIs should work");
}
require("sdk/test/runner").runTestsFromModule(module);

View File

@ -0,0 +1,4 @@
{
"id": "test-url",
"unpack": true
}

View File

@ -4,7 +4,7 @@
"use strict";
const AssertBase = require("test/assert").Assert;
const AssertBase = require("sdk/test/assert").Assert;
/**
* Generates custom assertion constructors that may be bundled with a test

View File

@ -4,5 +4,5 @@
define(function (require, exports) {
exports.name = 'tiger';
exports.type = require('modules/types/cat').type;
exports.type = require('./types/cat').type;
});

View File

@ -47,10 +47,14 @@ exports.testGetIsActive = function (test) {
test.assertEqual(pb.isActive, false,
"private-browsing.isActive is correct without modifying PB service");
test.assertEqual(pb.isPrivate(), false,
"private-browsing.sPrivate() is correct without modifying PB service");
pb.once("start", function() {
test.assert(pb.isActive,
"private-browsing.isActive is correct after modifying PB service");
test.assert(pb.isPrivate(),
"private-browsing.sPrivate() is correct after modifying PB service");
// Switch back to normal mode.
pb.deactivate();
});
@ -59,6 +63,8 @@ exports.testGetIsActive = function (test) {
pb.once("stop", function() {
test.assert(!pb.isActive,
"private-browsing.isActive is correct after modifying PB service");
test.assert(!pb.isPrivate(),
"private-browsing.sPrivate() is correct after modifying PB service");
test.done();
});
};
@ -72,6 +78,8 @@ exports.testStart = function(test) {
'private mode is active when "start" event is emitted');
test.assert(pb.isActive,
'`isActive` is `true` when "start" event is emitted');
test.assert(pb.isPrivate(),
'`isPrivate` is `true` when "start" event is emitted');
pb.removeListener("start", onStart);
deactivate(function() test.done());
});
@ -86,6 +94,8 @@ exports.testStop = function(test) {
"private mode is disabled when stop event is emitted");
test.assertEqual(pb.isActive, false,
"`isActive` is `false` when stop event is emitted");
test.assertEqual(pb.isPrivate(), false,
"`isPrivate()` is `false` when stop event is emitted");
test.done();
});
pb.activate();
@ -106,6 +116,8 @@ exports.testBothListeners = function(test) {
"private mode is disabled when stop event is emitted");
test.assertEqual(pb.isActive, false,
"`isActive` is `false` when stop event is emitted");
test.assertEqual(pb.isPrivate(), false,
"`isPrivate()` is `false` when stop event is emitted");
pb.on("start", finish);
pb.removeListener("start", onStart);
@ -121,6 +133,8 @@ exports.testBothListeners = function(test) {
"private mode is active when start event is emitted");
test.assert(pb.isActive,
"`isActive` is `true` when start event is emitted");
test.assert(pb.isPrivate(),
"`isPrivate()` is `true` when start event is emitted");
pb.on("stop", onStop);
pb.deactivate();
@ -137,6 +151,8 @@ exports.testBothListeners = function(test) {
"private mode is active when start event is emitted");
test.assert(pb.isActive,
"`isActive` is `true` when start event is emitted");
test.assert(pb.isPrivate(),
"`isPrivate()` is `true` when start event is emitted");
pb.removeListener("start", finish);
pb.removeListener("stop", onStop);
@ -145,6 +161,7 @@ exports.testBothListeners = function(test) {
pb.once("stop", function () {
test.assertEqual(pbUtils.getMode(), false);
test.assertEqual(pb.isActive, false);
test.assertEqual(pb.isPrivate(), false);
test.done();
});

View File

@ -7,11 +7,15 @@ let { Cc,Ci } = require('chrome');
const unload = require("sdk/system/unload");
const { Loader } = require('sdk/test/loader');
const { windows: windowsIterator } = require("sdk/window/utils");
const windows = require("windows").browserWindows;
const windows = require("sdk/windows").browserWindows;
let { loader } = LoaderWithHookedConsole();
const pb = loader.require('sdk/private-browsing');
const pbUtils = loader.require('sdk/private-browsing/utils');
const { getOwnerWindow } = require('sdk/private-browsing/window/utils');
require('sdk/tabs/utils');
require('sdk/windows');
function LoaderWithHookedConsole() {
let errors = [];
@ -40,6 +44,8 @@ function deactivate(callback) {
}
exports.deactivate = deactivate;
exports.loader = loader;
exports.pb = pb;
exports.pbUtils = pbUtils;
exports.getOwnerWindow = getOwnerWindow;
exports.LoaderWithHookedConsole = LoaderWithHookedConsole;

View File

@ -0,0 +1,24 @@
'use strict';
const { Ci } = require('chrome');
const { pb, pbUtils, loader: pbLoader, getOwnerWindow } = require('./helper');
exports.testIsPrivateOnTab = function(test) {
const { openTab, closeTab } = pbLoader.require('sdk/tabs/utils');
let window = pbLoader.require('sdk/windows').browserWindows.activeWindow;
let chromeWindow = pbLoader.require('sdk/private-browsing/window/utils').getOwnerWindow(window);
test.assert(chromeWindow instanceof Ci.nsIDOMWindow, 'associated window is found');
test.assert(!pb.isPrivate(chromeWindow), 'the top level window is not private');
let rawTab = openTab(chromeWindow, 'data:text/html,<h1>Hi!</h1>', {
private: true
});
// test that the tab is private
test.assert(rawTab.browser.docShell.QueryInterface(Ci.nsILoadContext).usePrivateBrowsing);
test.assert(pb.isPrivate(rawTab.browser.contentWindow));
test.assert(pb.isPrivate(rawTab.browser));
closeTab(rawTab);
}

View File

@ -4,9 +4,11 @@
'use strict';
const { pb, pbUtils } = require('./helper');
const { openDialog } = require('window/utils');
const { openDialog } = require('sdk/window/utils');
const { isPrivate } = require('sdk/private-browsing');
const { browserWindows: windows } = require('sdk/windows');
exports["test Per Window Private Browsing getter"] = function(assert, done) {
exports.testPerWindowPrivateBrowsingGetter = function(assert, done) {
let win = openDialog({
private: true
});
@ -23,8 +25,30 @@ exports["test Per Window Private Browsing getter"] = function(assert, done) {
assert.equal(pb.isActive, false, 'PB mode is not active');
done();
}, false);
win.close();
}, false);
}
exports.testIsPrivateOnWindowOn = function(assert, done) {
windows.open({
private: true,
onOpen: function(window) {
assert.equal(isPrivate(window), true, 'isPrivate for a window is true when it should be');
assert.equal(isPrivate(window.tabs[0]), true, 'isPrivate for a tab is false when it should be');
window.close(done);
}
});
}
exports.testIsPrivateOnWindowOff = function(assert, done) {
windows.open({
onOpen: function(window) {
assert.equal(isPrivate(window), false, 'isPrivate for a window is false when it should be');
assert.equal(isPrivate(window.tabs[0]), false, 'isPrivate for a tab is false when it should be');
window.close(done);
}
})
}
require("test").run(exports);

View File

@ -918,7 +918,7 @@ exports['test ready event on new window tab'] = function(test) {
exports['test unique tab ids'] = function(test) {
test.waitUntilDone();
var windows = require('windows').browserWindows,
var windows = require('sdk/windows').browserWindows,
tabIds = {}, win1, win2;
let steps = [

View File

@ -21,7 +21,7 @@ function isChromeVisible(window) {
exports['test that add-on page has no chrome'] = function(assert, done) {
let loader = Loader(module);
loader.require('addon-kit/addon-page');
loader.require('sdk/addon-page');
let window = windows.activeBrowserWindow;
let tab = openTab(window, uri);
@ -44,7 +44,7 @@ exports['test that add-on page has no chrome'] = function(assert, done) {
exports['test that add-on page with hash has no chrome'] = function(assert, done) {
let loader = Loader(module);
loader.require('addon-kit/addon-page');
loader.require('sdk/addon-page');
let window = windows.activeBrowserWindow;
let tab = openTab(window, uri + "#foo");
@ -67,7 +67,7 @@ exports['test that add-on page with hash has no chrome'] = function(assert, done
exports['test that add-on page with querystring has no chrome'] = function(assert, done) {
let loader = Loader(module);
loader.require('addon-kit/addon-page');
loader.require('sdk/addon-page');
let window = windows.activeBrowserWindow;
let tab = openTab(window, uri + '?foo=bar');
@ -90,7 +90,7 @@ exports['test that add-on page with querystring has no chrome'] = function(asser
exports['test that add-on page with hash and querystring has no chrome'] = function(assert, done) {
let loader = Loader(module);
loader.require('addon-kit/addon-page');
loader.require('sdk/addon-page');
let window = windows.activeBrowserWindow;
let tab = openTab(window, uri + '#foo?foo=bar');
@ -113,7 +113,7 @@ exports['test that add-on page with hash and querystring has no chrome'] = funct
exports['test that malformed uri is not an addon-page'] = function(assert, done) {
let loader = Loader(module);
loader.require('addon-kit/addon-page');
loader.require('sdk/addon-page');
let window = windows.activeBrowserWindow;
let tab = openTab(window, uri + 'anguage');
@ -146,4 +146,4 @@ exports['test that add-on pages are closed on unload'] = function(assert, done)
});
};
require('sdk/test').run(exports);
require('test').run(exports);

View File

@ -1360,7 +1360,7 @@ exports.testMultipleModulesOrderOverflow = function (test) {
let loader0 = test.newLoader();
let loader1 = test.newLoader();
let prefs = loader0.loader.require("preferences-service");
let prefs = loader0.loader.require("sdk/preferences/service");
prefs.set(OVERFLOW_THRESH_PREF, 0);
// Use each module to add an item, then unload each module in turn.
@ -1401,7 +1401,7 @@ exports.testMultipleModulesOverflowHidden = function (test) {
let loader0 = test.newLoader();
let loader1 = test.newLoader();
let prefs = loader0.loader.require("preferences-service");
let prefs = loader0.loader.require("sdk/preferences/service");
prefs.set(OVERFLOW_THRESH_PREF, 0);
// Use each module to add an item, then unload each module in turn.
@ -1426,7 +1426,7 @@ exports.testMultipleModulesOverflowHidden2 = function (test) {
let loader0 = test.newLoader();
let loader1 = test.newLoader();
let prefs = loader0.loader.require("preferences-service");
let prefs = loader0.loader.require("sdk/preferences/service");
prefs.set(OVERFLOW_THRESH_PREF, 0);
// Use each module to add an item, then unload each module in turn.
@ -1450,7 +1450,7 @@ exports.testOverflowIgnoresHidden = function (test) {
test = new TestHelper(test);
let loader = test.newLoader();
let prefs = loader.loader.require("preferences-service");
let prefs = loader.loader.require("sdk/preferences/service");
prefs.set(OVERFLOW_THRESH_PREF, 2);
let allItems = [
@ -1481,7 +1481,7 @@ exports.testOverflowIgnoresHiddenMultipleModules1 = function (test) {
let loader0 = test.newLoader();
let loader1 = test.newLoader();
let prefs = loader0.loader.require("preferences-service");
let prefs = loader0.loader.require("sdk/preferences/service");
prefs.set(OVERFLOW_THRESH_PREF, 2);
let allItems = [
@ -1516,7 +1516,7 @@ exports.testOverflowIgnoresHiddenMultipleModules2 = function (test) {
let loader0 = test.newLoader();
let loader1 = test.newLoader();
let prefs = loader0.loader.require("preferences-service");
let prefs = loader0.loader.require("sdk/preferences/service");
prefs.set(OVERFLOW_THRESH_PREF, 2);
let allItems = [
@ -1551,7 +1551,7 @@ exports.testOverflowIgnoresHiddenMultipleModules3 = function (test) {
let loader0 = test.newLoader();
let loader1 = test.newLoader();
let prefs = loader0.loader.require("preferences-service");
let prefs = loader0.loader.require("sdk/preferences/service");
prefs.set(OVERFLOW_THRESH_PREF, 2);
let allItems = [
@ -1585,7 +1585,7 @@ exports.testOverflowTransition = function (test) {
test = new TestHelper(test);
let loader = test.newLoader();
let prefs = loader.loader.require("preferences-service");
let prefs = loader.loader.require("sdk/preferences/service");
prefs.set(OVERFLOW_THRESH_PREF, 2);
let pItems = [
@ -1718,7 +1718,6 @@ exports.testMenuCommand = function (test) {
let topMenu = new loader.cm.Menu({
label: "top menu",
contentScript: 'self.on("click", function (node, data) {' +
' let Ci = Components["interfaces"];' +
' self.postMessage({' +
' tagName: node.tagName,' +
' data: data' +
@ -1798,7 +1797,6 @@ exports.testItemClick = function (test) {
label: "item",
data: "item data",
contentScript: 'self.on("click", function (node, data) {' +
' let Ci = Components["interfaces"];' +
' self.postMessage({' +
' tagName: node.tagName,' +
' data: data' +
@ -1846,7 +1844,6 @@ exports.testMenuClick = function (test) {
let topMenu = new loader.cm.Menu({
label: "top menu",
contentScript: 'self.on("click", function (node, data) {' +
' let Ci = Components["interfaces"];' +
' self.postMessage({' +
' tagName: node.tagName,' +
' data: data' +
@ -2480,6 +2477,124 @@ exports.testAlreadyOpenIframe = function (test) {
};
// Tests that a missing label throws an exception
exports.testItemNoLabel = function (test) {
test = new TestHelper(test);
let loader = test.newLoader();
try {
new loader.cm.Item({});
test.assert(false, "Should have seen exception");
}
catch (e) {
test.assert(true, "Should have seen exception");
}
try {
new loader.cm.Item({ label: null });
test.assert(false, "Should have seen exception");
}
catch (e) {
test.assert(true, "Should have seen exception");
}
try {
new loader.cm.Item({ label: undefined });
test.assert(false, "Should have seen exception");
}
catch (e) {
test.assert(true, "Should have seen exception");
}
try {
new loader.cm.Item({ label: "" });
test.assert(false, "Should have seen exception");
}
catch (e) {
test.assert(true, "Should have seen exception");
}
test.done();
}
// Tests that items can have an empty data property
exports.testItemNoData = function (test) {
test = new TestHelper(test);
let loader = test.newLoader();
function checkData(data) {
test.assertEqual(data, undefined, "Data should be undefined");
}
let item1 = new loader.cm.Item({
label: "item 1",
contentScript: 'self.on("click", function(node, data) self.postMessage(data))',
onMessage: checkData
});
let item2 = new loader.cm.Item({
label: "item 2",
data: null,
contentScript: 'self.on("click", function(node, data) self.postMessage(data))',
onMessage: checkData
});
let item3 = new loader.cm.Item({
label: "item 3",
data: undefined,
contentScript: 'self.on("click", function(node, data) self.postMessage(data))',
onMessage: checkData
});
test.assertEqual(item1.data, undefined, "Should be no defined data");
test.assertEqual(item2.data, null, "Should be no defined data");
test.assertEqual(item3.data, undefined, "Should be no defined data");
test.showMenu(null, function (popup) {
test.checkMenu([item1, item2, item3], [], []);
let itemElt = test.getItemElt(popup, item1);
itemElt.click();
test.hideMenu(function() {
test.showMenu(null, function (popup) {
let itemElt = test.getItemElt(popup, item2);
itemElt.click();
test.hideMenu(function() {
test.showMenu(null, function (popup) {
let itemElt = test.getItemElt(popup, item3);
itemElt.click();
test.done();
});
});
});
});
});
}
// Tests that items without an image don't attempt to show one
exports.testItemNoImage = function (test) {
test = new TestHelper(test);
let loader = test.newLoader();
let item1 = new loader.cm.Item({ label: "item 1" });
let item2 = new loader.cm.Item({ label: "item 2", image: null });
let item3 = new loader.cm.Item({ label: "item 3", image: undefined });
test.assertEqual(item1.image, undefined, "Should be no defined image");
test.assertEqual(item2.image, null, "Should be no defined image");
test.assertEqual(item3.image, undefined, "Should be no defined image");
test.showMenu(null, function (popup) {
test.checkMenu([item1, item2, item3], [], []);
test.done();
});
}
// Test image support.
exports.testItemImage = function (test) {
test = new TestHelper(test);
@ -2490,6 +2605,8 @@ exports.testItemImage = function (test) {
let menu = new loader.cm.Menu({ label: "menu", image: imageURL, items: [
loader.cm.Item({ label: "subitem" })
]});
test.assertEqual(item.image, imageURL, "Should have set the image correctly");
test.assertEqual(menu.image, imageURL, "Should have set the image correctly");
test.showMenu(null, function (popup) {
test.checkMenu([item, menu], [], []);
@ -2497,10 +2614,14 @@ exports.testItemImage = function (test) {
let imageURL2 = require("sdk/self").data.url("dummy.ico");
item.image = imageURL2;
menu.image = imageURL2;
test.assertEqual(item.image, imageURL2, "Should have set the image correctly");
test.assertEqual(menu.image, imageURL2, "Should have set the image correctly");
test.checkMenu([item, menu], [], []);
item.image = null;
menu.image = null;
test.assertEqual(item.image, null, "Should have set the image correctly");
test.assertEqual(menu.image, null, "Should have set the image correctly");
test.checkMenu([item, menu], [], []);
test.done();

View File

@ -5,6 +5,13 @@
const port = 8099;
const file = require("sdk/io/file");
const { pathFor } = require("sdk/system");
const { Loader } = require("sdk/test/loader");
const options = require("@test/options");
const loader = Loader(module);
const httpd = loader.require("sdk/test/httpd");
if (options.parseable || options.verbose)
loader.sandbox("sdk/test/httpd").DEBUG = true;
exports.testBasicHTTPServer = function(test) {
// Use the profile directory for the temporary file as that will be deleted
@ -16,8 +23,7 @@ exports.testBasicHTTPServer = function(test) {
fileStream.write(content);
fileStream.close();
let { startServerAsync } = require("sdk/test/httpd");
let srv = startServerAsync(port, basePath);
let srv = httpd.startServerAsync(port, basePath);
test.waitUntilDone();
@ -41,8 +47,7 @@ exports.testBasicHTTPServer = function(test) {
exports.testDynamicServer = function (test) {
let content = "This is the HTTPD test file.\n";
let { startServerAsync } = require("sdk/test/httpd");
let srv = startServerAsync(port);
let srv = httpd.startServerAsync(port);
// See documentation here:
//http://doxygen.db48x.net/mozilla/html/interfacensIHttpServer.html#a81fc7e7e29d82aac5ce7d56d0bedfb3a

View File

@ -4,14 +4,14 @@
"use strict";
let xulApp = require("api-utils/xul-app");
let xulApp = require("sdk/system/xul-app");
if (xulApp.versionInRange(xulApp.platformVersion, "16.0a1", "*")) {
new function tests() {
const { indexedDB, IDBKeyRange, DOMException, IDBCursor, IDBTransaction,
IDBOpenDBRequest, IDBVersionChangeEvent, IDBDatabase, IDBFactory,
IDBIndex, IDBObjectStore, IDBRequest
} = require("indexed-db");
} = require("sdk/indexed-db");
exports["test indexedDB is frozen"] = function(assert){
let original = indexedDB.open;

View File

@ -6,10 +6,10 @@
let { Loader, main, unload, parseStack } = require('toolkit/loader');
exports['test dependency cycles'] = function(assert) {
let uri = module.uri.substr(0, module.uri.lastIndexOf('/')) +
'/fixtures/loader/cycles/'
let root = module.uri.substr(0, module.uri.lastIndexOf('/'))
exports['test dependency cycles'] = function(assert) {
let uri = root + '/fixtures/loader/cycles/';
let loader = Loader({ paths: { '': uri } });
let program = main(loader, 'main');
@ -19,12 +19,10 @@ exports['test dependency cycles'] = function(assert) {
assert.equal(program.c.main, program, 'module `c` gets correct `main`');
unload(loader);
};
}
exports['test syntax errors'] = function(assert) {
let uri = module.uri.substr(0, module.uri.lastIndexOf('/')) +
'/fixtures/loader/syntax-error/';
let uri = root + '/fixtures/loader/syntax-error/';
let loader = Loader({ paths: { '': uri } });
try {
@ -43,12 +41,10 @@ exports['test syntax errors'] = function(assert) {
} finally {
unload(loader);
}
};
}
exports['test missing module'] = function(assert) {
let uri = module.uri.substr(0, module.uri.lastIndexOf('/')) +
'/fixtures/loader/missing/'
let uri = root + '/fixtures/loader/missing/'
let loader = Loader({ paths: { '': uri } });
try {
@ -75,8 +71,7 @@ exports['test missing module'] = function(assert) {
}
exports['test exceptions in modules'] = function(assert) {
let uri = module.uri.substr(0, module.uri.lastIndexOf('/')) +
'/fixtures/loader/exceptions/'
let uri = root + '/fixtures/loader/exceptions/'
let loader = Loader({ paths: { '': uri } });
@ -112,9 +107,7 @@ exports['test exceptions in modules'] = function(assert) {
}
exports['test early errors in module'] = function(assert) {
let uri = module.uri.substr(0, module.uri.lastIndexOf('/')) +
'/fixtures/loader/errors/'
let uri = root + '/fixtures/loader/errors/';
let loader = Loader({ paths: { '': uri } });
try {

View File

@ -5,7 +5,7 @@
"use strict";
const { readURI, readURISync } = require("sdk/net/url");
const { data } = require("self");
const { data } = require("sdk/self");
const utf8text = "Hello, ゼロ!";
const latin1text = "Hello, ゼロ!";

View File

@ -1,15 +1,44 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
var url = require("sdk/url");
var file = require("sdk/io/file");
var {Cm,Ci} = require("chrome");
var options = require("@loader/options");
exports.testPackaging = function(test) {
test.assertEqual(options.metadata.description,
"Add-on development made easy.",
"packaging metadata should be available");
try {
options.metadata.description = 'new description';
test.fail('should not have been able to set options.metadata property');
}
catch (e) {}
test.assertEqual(options.metadata.description,
"Add-on development made easy.",
"packaging metadata should be frozen");
test.assertEqual(options.metadata.permissions['private-browsing'], undefined,
"private browsing metadata should be undefined");
test.assertEqual(options.metadata['private-browsing'], undefined,
"private browsing metadata should be be frozen");
test.assertEqual(options['private-browsing'], undefined,
"private browsing metadata should be be frozen");
try {
options.metadata['private-browsing'] = true;
test.fail('should not have been able to set options.metadata property');
}
catch(e) {}
test.assertEqual(options.metadata['private-browsing'], undefined,
"private browsing metadata should be be frozen");
try {
options.metadata.permissions['private-browsing'] = true;
test.fail('should not have been able to set options.metadata.permissions property');
}
catch (e) {}
test.assertEqual(options.metadata.permissions['private-browsing'], undefined,
"private browsing metadata should be be frozen");
};

View File

@ -12,7 +12,7 @@ const { Cc, Ci } = require("chrome");
const { open, getFrames, getMostRecentBrowserWindow } = require('sdk/window/utils');
const windowUtils = require('sdk/deprecated/window-utils');
const { getTabContentWindow, getActiveTab, openTab, closeTab } = require('sdk/tabs/utils');
const { data } = require('self');
const { data } = require('sdk/self');
/* XXX This can be used to delay closing the test Firefox instance for interactive
* testing or visual inspection. This test is registered first so that it runs
@ -357,6 +357,27 @@ exports.testRelatedTab = function(test) {
});
};
exports.testRelatedTabNoRequireTab = function(test) {
test.waitUntilDone();
let loader = Loader(module);
let tab;
let url = "data:text/html;charset=utf-8," + encodeURI("Test related worker tab 2");
let { PageMod } = loader.require("sdk/page-mod");
let pageMod = new PageMod({
include: url,
onAttach: function(worker) {
test.assertEqual(worker.tab.url, url, "Worker.tab.url is valid");
worker.tab.close();
pageMod.destroy();
loader.unload();
test.done();
}
});
tabs.open(url);
};
exports.testRelatedTabNoOtherReqs = function(test) {
test.waitUntilDone();
@ -633,7 +654,7 @@ exports['test111 attachTo [frame]'] = function(test) {
this.destroy();
if (++messageCount == 2) {
mod.destroy();
require('tabs').activeTab.close(function() {
require('sdk/tabs').activeTab.close(function() {
test.done();
});
}
@ -792,7 +813,7 @@ exports.testPageModCssAutomaticDestroy = function(test) {
test.waitUntilDone();
let loader = Loader(module);
let pageMod = loader.require("page-mod").PageMod({
let pageMod = loader.require("sdk/page-mod").PageMod({
include: "data:*",
contentStyle: "div { width: 100px!important; }"
});
@ -832,7 +853,7 @@ exports.testPageModTimeout = function(test) {
test.waitUntilDone();
let tab = null
let loader = Loader(module);
let { PageMod } = loader.require("page-mod");
let { PageMod } = loader.require("sdk/page-mod");
let mod = PageMod({
include: "data:*",
@ -867,7 +888,7 @@ exports.testPageModcancelTimeout = function(test) {
test.waitUntilDone();
let tab = null
let loader = Loader(module);
let { PageMod } = loader.require("page-mod");
let { PageMod } = loader.require("sdk/page-mod");
let mod = PageMod({
include: "data:*",
@ -1029,4 +1050,3 @@ if (require("sdk/system/xul-app").is("Fennec")) {
}
}
}

View File

@ -5,7 +5,7 @@
let { Cc, Ci } = require("chrome");
const { Loader } = require('sdk/test/loader');
const timer = require("sdk/timers");
const self = require('self');
const self = require('sdk/self');
exports["test Panel"] = function(assert, done) {
const { Panel } = require('sdk/panel');

View File

@ -4,6 +4,9 @@
const prefs = require("sdk/preferences/service");
const { id, name } = require("sdk/self");
const { Cc, Cu, Ci } = require("chrome");
const { loadSubScript } = Cc['@mozilla.org/moz/jssubscript-loader;1'].
getService(Ci.mozIJSSubScriptLoader);
const ADDON_LOG_LEVEL_PREF = "extensions." + id + ".sdk.console.logLevel";
const SDK_LOG_LEVEL_PREF = "extensions.sdk.console.logLevel";
@ -73,9 +76,22 @@ exports.testPlainTextConsole = function(test) {
var tbLines = prints[0].split("\n");
test.assertEqual(tbLines[0], "error: " + name + ": An exception occurred.");
test.assertEqual(tbLines[1], "Error: blah");
test.assertEqual(tbLines[2], module.uri + " 71");
test.assertEqual(tbLines[2], module.uri + " 74");
test.assertEqual(tbLines[3], "Traceback (most recent call last):");
prints = [];
try {
loadSubScript("invalid-url", {});
test.fail("successed in calling loadSubScript with invalid-url");
}
catch(e) {
con.exception(e);
}
var tbLines = prints[0].split("\n");
test.assertEqual(tbLines[0], "error: " + name + ": An exception occurred.");
test.assertEqual(tbLines[1], "Error creating URI (invalid URL scheme?)");
test.assertEqual(tbLines[2], "Traceback (most recent call last):");
prints = [];
con.trace();
tbLines = prints[0].split("\n");

View File

@ -3,18 +3,47 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
'use strict';
const { pb, pbUtils } = require('./private-browsing/helper');
const { Ci } = require('chrome');
const { pb, pbUtils, getOwnerWindow } = require('./private-browsing/helper');
const { merge } = require('sdk/util/object');
const windows = require('sdk/windows').browserWindows;
const winUtils = require('sdk/window/utils');
const { isPrivateBrowsingSupported } = require('sdk/self');
const { is } = require('sdk/system/xul-app');
const { isPrivate } = require('sdk/private-browsing');
// is global pb is enabled?
if (pbUtils.isGlobalPBSupported) {
merge(module.exports, require('./private-browsing/global'));
exports.testGlobalOnlyOnFirefox = function(test) {
test.assert(is("Firefox"), "isGlobalPBSupported is only true on Firefox");
}
}
else if (pbUtils.isWindowPBSupported) {
merge(module.exports, require('./private-browsing/windows'));
exports.testPWOnlyOnFirefox = function(test) {
test.assert(is("Firefox"), "isWindowPBSupported is only true on Firefox");
}
}
// only on Fennec
else if (pbUtils.isTabPBSupported) {
merge(module.exports, require('./private-browsing/tabs'));
exports.testPTOnlyOnFennec = function(test) {
test.assert(is("Fennec"), "isTabPBSupported is only true on Fennec");
}
}
exports.testIsPrivateDefaults = function(test) {
test.assertEqual(pb.isPrivate(), false, 'undefined is not private');
test.assertEqual(pb.isPrivate('test'), false, 'strings are not private');
test.assertEqual(pb.isPrivate({}), false, 'random objects are not private');
test.assertEqual(pb.isPrivate(4), false, 'numbers are not private');
test.assertEqual(pb.isPrivate(/abc/), false, 'regex are not private');
test.assertEqual(pb.isPrivate(function() {}), false, 'functions are not private');
};
exports.testWindowDefaults = function(test) {
test.assertEqual(windows.activeWindow.isPrivateBrowsing, false, 'window is not private browsing by default');
@ -29,3 +58,36 @@ exports.testIsActiveDefault = function(test) {
'pb.isActive returns false when private browsing isn\'t supported');
};
exports.testIsPrivateBrowsingFalseDefault = function(test) {
test.assertEqual(isPrivateBrowsingSupported, false,
'usePrivateBrowsing property is false by default');
};
exports.testGetOwnerWindow = function(test) {
test.waitUntilDone();
let window = windows.activeWindow;
let chromeWindow = getOwnerWindow(window);
test.assert(chromeWindow instanceof Ci.nsIDOMWindow, 'associated window is found');
window.tabs.open({
url: 'about:blank',
private: true, // should be ignored in this case
onOpen: function(tab) {
// test that getOwnerWindow works as expected
if (is('Fennec')) {
test.assertNotStrictEqual(chromeWindow, getOwnerWindow(tab));
test.assert(getOwnerWindow(tab) instanceof Ci.nsIDOMWindow);
}
else {
test.assertStrictEqual(chromeWindow, getOwnerWindow(tab), 'associated window is the same for window and window\'s tab');
}
// test that the tab is not private
// private flag should be ignored by default
test.assert(!isPrivate(tab));
tab.close(function() test.done());
}
});
};

View File

@ -15,6 +15,9 @@ if (options.parseable || options.verbose)
loader.sandbox("sdk/test/httpd").DEBUG = true;
const { startServerAsync } = httpd;
const { Cc, Ci, Cu } = require("chrome");
const { Services } = Cu.import("resource://gre/modules/Services.jsm");
// Use the profile directory for the temporary files as that will be deleted
// when tests are complete
const basePath = pathFor("ProfD")
@ -140,6 +143,55 @@ exports.testComplexHeader = function (test) {
}).get();
}
// Force Allow Third Party cookies
exports.test3rdPartyCookies = function (test) {
let srv = startServerAsync(port, basePath);
let basename = "test-request-3rd-party-cookies.sjs";
// Function to handle the requests in the server
let content = function handleRequest(request, response) {
var cookiePresent = request.hasHeader("Cookie");
// If no cookie, set it
if(!cookiePresent) {
response.setHeader("Set-Cookie", "cookie=monster;", "true");
response.setHeader("x-jetpack-3rd-party", "false", "true");
} else {
// We got the cookie, say so
response.setHeader("x-jetpack-3rd-party", "true", "true");
}
response.write("<html><body>This tests 3rd party cookies.</body></html>");
}.toString()
prepareFile(basename, content);
// Disable the 3rd party cookies
Services.prefs.setIntPref("network.cookie.cookieBehavior", 1);
test.waitUntilDone();
Request({
url: "http://localhost:" + port + "/test-request-3rd-party-cookies.sjs",
onComplete: function (response) {
// Check that the server created the cookie
test.assertEqual(response.headers['Set-Cookie'], 'cookie=monster;');
// Check it wasn't there before
test.assertEqual(response.headers['x-jetpack-3rd-party'], 'false');
// Make a second request, and check that the server this time
// got the cookie
Request({
url: "http://localhost:" + port + "/test-request-3rd-party-cookies.sjs",
onComplete: function (response) {
test.assertEqual(response.headers['x-jetpack-3rd-party'], 'true');
srv.stop(function() test.done());
}
}).get();
}
}).get();
}
exports.testSimpleJSON = function (test) {
let srv = startServerAsync(port, basePath);
let json = { foo: "bar" };

View File

@ -14,12 +14,16 @@ const HTML = "<html>\
const URL = "data:text/html;charset=utf-8," + encodeURIComponent(HTML);
const FRAME_HTML = "<iframe src='" + URL + "'><iframe>";
const FRAME_URL = "data:text/html;charset=utf-8," + encodeURIComponent(FRAME_HTML);
const { defer } = require("sdk/core/promise");
const tabs = require("sdk/tabs");
const { getActiveTab, getTabContentWindow, closeTab } = require("sdk/tabs/utils")
const { getMostRecentBrowserWindow } = require("sdk/window/utils");
const { Loader } = require("sdk/test/loader");
const { setTimeout } = require("sdk/timers");
const { Cu } = require("chrome");
// General purpose utility functions
@ -82,6 +86,55 @@ function reload(window) {
// Selection's unit test utility function
/**
* Returns the frame's window once the document is loaded
*/
function getFrameWindow(window) {
let { promise, resolve } = defer();
let frame = window.frames[0];
let { document } = frame;
frame.focus();
if (document.readyState === "complete")
return frame;
document.addEventListener("readystatechange", function readystate() {
if (this.readyState === "complete") {
this.removeEventListener("readystatechange", readystate);
frame.focus();
resolve(frame);
}
});
return promise;
}
/**
* Hide the frame in order to destroy the selection object, and show it again
* after ~500 msec, to give time to attach the code on `document-shown`
* notification.
* In the process, call `Cu.forgeGC` to ensure that the `document-shown` code
* is not garbaged.
*/
function hideAndShowFrame(window) {
let { promise, resolve } = defer();
let iframe = window.document.querySelector("iframe");
iframe.style.display = "none";
Cu.forceGC();
setTimeout(function(){
iframe.style.display = "";
setTimeout(resolve, 500, window);
}, 0)
return promise;
}
/**
* Select the first div in the page, adding the range to the selection.
*/
@ -174,7 +227,7 @@ function dispatchOnSelectEvent(window) {
event.initUIEvent("select", true, true, window, 1);
textarea.dispatchEvent(event)
textarea.dispatchEvent(event);
}
/**
@ -730,6 +783,42 @@ exports["test Textarea OnSelect Listener on document reload"] = function(assert,
then(loader.unload);
};
exports["test Selection Listener on frame"] = function(assert, done) {
let loader = Loader(module);
let selection = loader.require("sdk/selection");
selection.once("select", function() {
assert.equal(selection.text, "fo");
done();
});
open(FRAME_URL).
then(hideAndShowFrame).
then(getFrameWindow).
then(selectContentFirstDiv).
then(dispatchSelectionEvent).
then(close).
then(loader.unload)
};
exports["test Textarea onSelect Listener on frame"] = function(assert, done) {
let loader = Loader(module);
let selection = loader.require("sdk/selection");
selection.once("select", function() {
assert.equal(selection.text, "noodles");
done();
});
open(FRAME_URL).
then(hideAndShowFrame).
then(getFrameWindow).
then(selectTextarea).
then(dispatchOnSelectEvent).
then(close).
then(loader.unload)
};
// TODO: test Selection Listener on long-held connection (Bug 661884)
//
// I didn't find a way to do so with httpd, using `processAsync` I'm able to

View File

@ -32,6 +32,9 @@ exports.testSelf = function(test) {
// loadReason may change here, as we change the way tests addons are installed
test.assertEqual(self.loadReason, "startup",
"self.loadReason is always `startup` on test runs");
test.assertEqual(self.isPrivateBrowsingSupported, false,
'usePrivateBrowsing property is false by default');
};
exports.testSelfID = function(test) {

View File

@ -199,7 +199,7 @@ exports.testPrefUnloadWildcardListener = function(test) {
test.waitUntilDone();
let testpref = "test-wildcard-unload-listener";
let loader = Loader(module);
let sp = loader.require("simple-prefs");
let sp = loader.require("sdk/simple-prefs");
let counter = 0;
let listener = function() {
@ -210,7 +210,7 @@ exports.testPrefUnloadWildcardListener = function(test) {
// this may not execute after unload, but definitely shouldn't fire listener
sp.prefs[testpref] = false;
// this should execute, but also definitely shouldn't fire listener
require("simple-prefs").prefs[testpref] = false;
require("sdk/simple-prefs").prefs[testpref] = false;
test.done();
};

View File

@ -0,0 +1,222 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const events = require("sdk/system/events");
const self = require("sdk/self");
const { Cc, Ci, Cu } = require("chrome");
const { setTimeout } = require("sdk/timers");
const { Loader } = require("sdk/test/loader");
const { PlainTextConsole } = require("sdk/console/plain-text");
const nsIObserverService = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
exports["test basic"] = function(assert) {
let type = Date.now().toString(32);
let timesCalled = 0;
function handler(subject, data) { timesCalled++; };
events.on(type, handler);
events.emit(type, { data: "yo yo" });
assert.equal(timesCalled, 1, "event handler was called");
events.off(type, handler);
events.emit(type, { data: "no way" });
assert.equal(timesCalled, 1, "removed handler is no longer called");
events.once(type, handler);
events.emit(type, { data: "and we meet again" });
events.emit(type, { data: "it's always hard to say bye" });
assert.equal(timesCalled, 2, "handlers added via once are triggered once");
}
exports["test error reporting"] = function(assert) {
let prints = [];
let loader = Loader(module, {
console: new PlainTextConsole(function(_) {
prints.push(_);
})
});
let events = loader.require("sdk/system/events");
function brokenHandler(subject, data) { throw new Error("foo"); };
let lineNumber;
try { brokenHandler() } catch (error) { lineNumber = error.lineNumber }
let errorType = Date.now().toString(32);
events.on(errorType, brokenHandler);
events.emit(errorType, { data: "yo yo" });
assert.ok(prints[0].indexOf(self.name + ": An exception occurred.") >= 0,
"error is logged");
assert.ok(prints[0].indexOf("Error: foo") >= 0, "error message is logged");
assert.ok(prints[0].indexOf(module.uri) >= 0, "module uri is logged");
assert.ok(prints[0].indexOf(lineNumber) >= 0, "error line is logged");
events.off(errorType, brokenHandler);
loader.unload();
};
exports["test listeners are GC-ed"] = function(assert, done) {
let receivedFromWeak = [];
let receivedFromStrong = [];
let type = Date.now().toString(32);
function handler(event) { receivedFromStrong.push(event); }
function weakHandler(event) { receivedFromWeak.push(event); }
events.on(type, handler, true);
events.on(type, weakHandler);
events.emit(type, { data: 1 });
assert.equal(receivedFromStrong.length, 1, "strong listener invoked");
assert.equal(receivedFromWeak.length, 1, "weak listener invoked");
handler = weakHandler = null;
Cu.forceGC();
setTimeout(function() {
Cu.forceGC();
events.emit(type, { data: 2 });
assert.equal(receivedFromWeak.length, 1, "weak listener was GC-ed");
assert.equal(receivedFromStrong.length, 2, "strong listener was invoked");
done();
}, 300);
};
exports["test handle nsIObserverService notifications"] = function(assert) {
let ios = Cc['@mozilla.org/network/io-service;1']
.getService(Ci.nsIIOService);
let uri = ios.newURI("http://www.foo.com", null, null);
let type = Date.now().toString(32);
let timesCalled = 0;
let lastSubject = null;
let lastData = null;
let lastType = null;
function handler({ subject, data, type }) {
timesCalled++;
lastSubject = subject;
lastData = data;
lastType = type;
};
events.on(type, handler);
nsIObserverService.notifyObservers(uri, type, "some data");
assert.equal(timesCalled, 1, "notification invokes handler");
assert.equal(lastType, type, "event.type is notification topic");
assert.equal(lastSubject, uri, "event.subject is notification subject");
assert.equal(lastData, "some data", "event.data is notification data");
function customSubject() {}
function customData() {}
events.emit(type, { data: customData, subject: customSubject });
assert.equal(timesCalled, 2, "notification invokes handler");
assert.equal(lastType, type, "event.type is notification topic");
assert.equal(lastSubject, customSubject,
"event.subject is wrapped & unwrapped");
assert.equal(lastData, customData, "event.data is wrapped & unwrapped");
events.off(type, handler);
nsIObserverService.notifyObservers(null, type, "some data");
assert.equal(timesCalled, 2, "event handler is removed");
events.on("*", handler);
nsIObserverService.notifyObservers(null, type, "more data");
assert.equal(timesCalled, 3, "notification invokes * handler");
assert.equal(lastType, type, "event.type is notification topic");
assert.equal(lastSubject, null,
"event.subject is notification subject");
assert.equal(lastData, "more data", "event.data is notification data");
events.off("*", handler);
nsIObserverService.notifyObservers(null, type, "last data");
assert.equal(timesCalled, 3, "* event handler is removed");
};
exports["test emit to nsIObserverService observers"] = function(assert) {
let ios = Cc['@mozilla.org/network/io-service;1']
.getService(Ci.nsIIOService);
let uri = ios.newURI("http://www.foo.com", null, null);
let timesCalled = 0;
let lastSubject = null;
let lastData = null;
let lastTopic = null;
var topic = Date.now().toString(32)
let nsIObserver = {
QueryInterface: function() {
return nsIObserver;
},
observe: function(subject, topic, data) {
timesCalled = timesCalled + 1;
lastSubject = subject;
lastData = data;
lastTopic = topic;
}
};
nsIObserverService.addObserver(nsIObserver, topic, false);
events.emit(topic, { subject: uri, data: "some data" });
assert.equal(timesCalled, 1, "emit notifies observers");
assert.equal(lastTopic, topic, "event type is notification topic");
assert.equal(lastSubject.wrappedJSObject.object, uri,
"event.subject is notification subject");
assert.equal(lastData, "some data", "event.data is notification data");
function customSubject() {}
function customData() {}
events.emit(topic, { subject: customSubject, data: customData });
assert.equal(timesCalled, 2, "emit notifies observers");
assert.equal(lastTopic, topic, "event.type is notification");
assert.equal(lastSubject.wrappedJSObject.object, customSubject,
"event.subject is notification subject");
assert.equal(lastData, customData, "event.data is notification data");
nsIObserverService.removeObserver(nsIObserver, topic, false);
events.emit(topic, { data: "more data" });
assert.equal(timesCalled, 2, "removed observers no longer invoked");
nsIObserverService.addObserver(nsIObserver, "*", false);
events.emit(topic, { data: "data again" });
assert.equal(timesCalled, 3, "emit notifies * observers");
assert.equal(lastTopic, topic, "event.type is notification");
assert.equal(lastSubject, null,
"event.subject is notification subject");
assert.equal(lastData, "data again", "event.data is notification data");
nsIObserverService.removeObserver(nsIObserver, "*");
events.emit(topic, { data: "last data" });
assert.equal(timesCalled, 3, "removed observers no longer invoked");
}
require("test").run(exports);

View File

@ -0,0 +1,25 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
"use strict";
var runtime = require("sdk/system/runtime");
exports["test system runtime"] = function(assert) {
assert.equal(typeof(runtime.inSafeMode), "boolean",
"inSafeMode is boolean");
assert.equal(typeof(runtime.OS), "string",
"runtime.OS is string");
assert.equal(typeof(runtime.processType), "number",
"runtime.processType is a number");
assert.equal(typeof(runtime.widgetToolkit), "string",
"runtime.widgetToolkit is string");
var XPCOMABI = typeof(runtime.XPCOMABI);
assert.ok(XPCOMABI === null || typeof(XPCOMABI) === "string",
"runtime.XPCOMABI is string or null if not supported by platform");
};
require("test").run(exports);

View File

@ -115,7 +115,8 @@ exports["test behavior on close"] = function(assert, done) {
url: "about:mozilla",
onReady: function(tab) {
assert.equal(tab.url, "about:mozilla", "Tab has the expected url");
assert.equal(tab.index, 1, "Tab has the expected index");
// if another test ends before closing a tab then index != 1 here
assert.ok(tab.index >= 1, "Tab has the expected index, a value greater than 0");
tab.close(function () {
assert.equal(tab.url, undefined,
"After being closed, tab attributes are undefined (url)");

View File

@ -3,7 +3,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
var url = require("sdk/url");
var { packed } = require("sdk/self");
exports.testResolve = function(test) {
test.assertEqual(url.URL("bar", "http://www.foo.com/").toString(),
@ -86,16 +85,6 @@ exports.testToFilename = function(test) {
"url.toFilename() on nonexistent resources should throw"
);
if (!packed)
test.assertMatches(url.toFilename(module.uri),
/.*test-url\.js$/,
"url.toFilename() on resource: URIs should work");
else
test.assertRaises(
function() { url.toFilename(module.uri); },
"cannot map to filename: "+module.uri,
"url.toFilename() can fail for packed XPIs");
test.assertRaises(
function() { url.toFilename("http://foo.com/"); },
"cannot map to filename: http://foo.com/",

View File

@ -8,7 +8,7 @@ const { Cc, Ci } = require("chrome");
const { Loader } = require('sdk/test/loader');
const url = require("sdk/url");
const timer = require("sdk/timers");
const self = require("self");
const self = require("sdk/self");
const windowUtils = require("sdk/deprecated/window-utils");
exports.testConstructor = function(test) {
@ -1029,7 +1029,7 @@ exports.testSVGWidget = function(test) {
// use of capital SVG here is intended, that was failing..
let SVG_URL = self.data.url("mofo_logo.SVG");
let widget = require("widget").Widget({
let widget = require("sdk/widget").Widget({
id: "mozilla-svg-logo",
label: "moz foundation logo",
contentURL: SVG_URL,

View File

@ -10,7 +10,8 @@ const wm = Cc['@mozilla.org/appshell/window-mediator;1'].
getService(Ci.nsIWindowMediator);
const { browserWindows } = require("sdk/windows");
const tabs = require("tabs");
const tabs = require("sdk/tabs");
const { WindowTracker } = require("sdk/deprecated/window-utils");
// TEST: open & close window
exports.testOpenAndCloseWindow = function(test) {
@ -203,33 +204,24 @@ exports.testActiveWindow = function(test) {
count++;
test.assertEqual(count, 3, "Correct number of windows returned by iterator");
rawWindow2.focus();
test.assertEqual(windows.activeWindow.title, window3.title, "Correct active window - 3");
continueAfterFocus(rawWindow2);
rawWindow2.focus();
},
function() {
nextStep();
},
function() {
/**
* Bug 614079: This test fails intermittently on some specific linux
* environnements, without being able to reproduce it in same
* distribution with same window manager.
* Disable it until being able to reproduce it easily.
test.assertEqual(windows.activeWindow.title, window2.title, "Correct active window - 2");
// On linux, focus is not consistent, so we can't be sure
// what window will be on top.
// Here when we focus "non-browser" window,
// Any Browser window may be selected as "active".
test.assert(windows.activeWindow == window2 || windows.activeWindow == window3,
"Non-browser windows aren't handled by this module");
*/
window2.activate();
continueAfterFocus(rawWindow2);
window2.activate();
},
function() {
test.assertEqual(windows.activeWindow.title, window2.title, "Correct active window - 2");
window3.activate();
continueAfterFocus(rawWindow3);
window3.activate();
},
function() {
test.assertEqual(windows.activeWindow.title, window3.title, "Correct active window - 3");
@ -237,21 +229,39 @@ exports.testActiveWindow = function(test) {
}
];
let newWindow = null;
let tracker = new WindowTracker({
onTrack: function(window) {
newWindow = window;
}
});
windows.open({
url: "data:text/html;charset=utf-8,<title>window 2</title>",
onOpen: function(window) {
window2 = window;
rawWindow2 = wm.getMostRecentWindow("navigator:browser");
window.tabs.activeTab.on('ready', function() {
window2 = window;
test.assert(newWindow, "A new window was opened");
rawWindow2 = newWindow;
newWindow = null;
test.assertEqual(rawWindow2.content.document.title, "window 2", "Got correct raw window 2");
test.assertEqual(rawWindow2.document.title, window2.title, "Saw correct title on window 2");
windows.open({
url: "data:text/html;charset=utf-8,<title>window 3</title>",
onOpen: function(window) {
window.tabs.activeTab.on('ready', function onReady() {
window3 = window;
rawWindow3 = wm.getMostRecentWindow("navigator:browser");
nextStep()
});
}
windows.open({
url: "data:text/html;charset=utf-8,<title>window 3</title>",
onOpen: function(window) {
window.tabs.activeTab.on('ready', function onReady() {
window3 = window;
test.assert(newWindow, "A new window was opened");
rawWindow3 = newWindow;
tracker.unload();
test.assertEqual(rawWindow3.content.document.title, "window 3", "Got correct raw window 3");
test.assertEqual(rawWindow3.document.title, window3.title, "Saw correct title on window 3");
continueAfterFocus(rawWindow3);
rawWindow3.focus();
});
}
});
});
}
});
@ -278,11 +288,11 @@ exports.testActiveWindow = function(test) {
var focused = (focusedChildWindow == childTargetWindow);
if (focused) {
nextStep();
setTimeout(nextStep, 0);
} else {
childTargetWindow.addEventListener("focus", function focusListener() {
childTargetWindow.removeEventListener("focus", focusListener, true);
nextStep();
setTimeout(nextStep, 0);
}, true);
}

View File

@ -4759,22 +4759,21 @@ var TabsProgressListener = {
onLocationChange: function (aBrowser, aWebProgress, aRequest, aLocationURI,
aFlags) {
// Filter out any sub-frame loads
if (aBrowser.contentWindow == aWebProgress.DOMWindow) {
// Filter out any onLocationChanges triggered by anchor navigation
// or history.push/pop/replaceState.
if (!(aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT)) {
// Initialize the click-to-play state.
aBrowser._clickToPlayPluginsActivated = new Map();
aBrowser._clickToPlayAllPluginsActivated = false;
aBrowser._pluginScriptedState = gPluginHandler.PLUGIN_SCRIPTED_STATE_NONE;
// Filter out sub-frame loads and location changes caused by anchor
// navigation or history.push/pop/replaceState.
if (aBrowser.contentWindow == aWebProgress.DOMWindow &&
!(aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT)) {
// Initialize the click-to-play state.
aBrowser._clickToPlayPluginsActivated = new Map();
aBrowser._clickToPlayAllPluginsActivated = false;
aBrowser._pluginScriptedState = gPluginHandler.PLUGIN_SCRIPTED_STATE_NONE;
// Only need to call locationChange if the PopupNotifications object
// for this window has already been initialized (i.e. its getter no
// longer exists)
if (!Object.getOwnPropertyDescriptor(window, "PopupNotifications").get)
PopupNotifications.locationChange(aBrowser);
// Only need to call locationChange if the PopupNotifications object
// for this window has already been initialized (i.e. its getter no
// longer exists)
if (!Object.getOwnPropertyDescriptor(window, "PopupNotifications").get)
PopupNotifications.locationChange(aBrowser);
}
FullZoom.onLocationChange(aLocationURI, false, aBrowser);
}
},

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 67 KiB

View File

@ -3,8 +3,14 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
function test() {
TestRunner.run();
}
function runTests() {
/** Test (B) for Bug 248970 **/
waitForExplicitFinish();
// Let runTests() be a generator function.
yield executeSoon(next);
let windowsToClose = [];
let file = Services.dirsvc.get("TmpD", Ci.nsIFile);
@ -157,7 +163,7 @@ function test() {
aWin.gBrowser.removeTab(tab_C);
aWin.gBrowser.removeTab(tab_B);
finish();
next();
});
});
});

Some files were not shown because too many files have changed in this diff Show More