mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-01-25 22:29:07 +00:00
Bug 842762: Uplift Add-on SDK changeset bf2c2f609babcea412a2349a431ae6bdbedcf05d
4e44dd094e...bf2c2f609b
This commit is contained in:
parent
316cd7cb91
commit
146d35392c
@ -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
|
||||
|
89
addon-sdk/source/app-extension/bootstrap.js
vendored
89
addon-sdk/source/app-extension/bootstrap.js
vendored
@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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 O’Shannessy
|
||||
* 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
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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 ##
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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>
|
||||
|
||||
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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"]
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -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)) {
|
||||
|
@ -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){
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
},
|
||||
|
@ -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;
|
||||
},
|
||||
|
@ -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.',
|
||||
|
@ -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.
|
||||
|
32
addon-sdk/source/lib/sdk/private-browsing/window/utils.js
Normal file
32
addon-sdk/source/lib/sdk/private-browsing/window/utils.js
Normal 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;
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
});
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
});
|
||||
|
@ -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) {
|
||||
|
@ -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 || "";
|
||||
}
|
||||
|
@ -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 = {}
|
||||
|
@ -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()
|
||||
|
@ -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 = [];
|
||||
|
@ -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;
|
||||
});
|
||||
|
@ -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;
|
||||
|
112
addon-sdk/source/lib/test.js
Normal file
112
addon-sdk/source/lib/test.js
Normal 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);
|
||||
};
|
@ -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);
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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_\-]+$')
|
||||
|
||||
|
@ -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"):
|
||||
|
@ -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):
|
||||
|
@ -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()
|
||||
|
@ -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')
|
||||
|
@ -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)
|
||||
|
@ -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):
|
||||
|
@ -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);
|
3
addon-sdk/source/test/addons/layout-change/package.json
Normal file
3
addon-sdk/source/test/addons/layout-change/package.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"id": "test-layout-change"
|
||||
}
|
20
addon-sdk/source/test/addons/packed/main.js
Normal file
20
addon-sdk/source/test/addons/packed/main.js
Normal 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);
|
4
addon-sdk/source/test/addons/packed/package.json
Normal file
4
addon-sdk/source/test/addons/packed/package.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"id": "test-url",
|
||||
"unpack": false
|
||||
}
|
@ -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);
|
@ -0,0 +1,6 @@
|
||||
{
|
||||
"id": "private-browsing-mode-test@jetpack",
|
||||
"permissions": {
|
||||
"private-browsing": true
|
||||
}
|
||||
}
|
18
addon-sdk/source/test/addons/unpacked/main.js
Normal file
18
addon-sdk/source/test/addons/unpacked/main.js
Normal 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);
|
4
addon-sdk/source/test/addons/unpacked/package.json
Normal file
4
addon-sdk/source/test/addons/unpacked/package.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"id": "test-url",
|
||||
"unpack": true
|
||||
}
|
@ -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
|
||||
|
@ -4,5 +4,5 @@
|
||||
|
||||
define(function (require, exports) {
|
||||
exports.name = 'tiger';
|
||||
exports.type = require('modules/types/cat').type;
|
||||
exports.type = require('./types/cat').type;
|
||||
});
|
||||
|
@ -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();
|
||||
});
|
||||
|
@ -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;
|
||||
|
24
addon-sdk/source/test/private-browsing/tabs.js
Normal file
24
addon-sdk/source/test/private-browsing/tabs.js
Normal 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);
|
||||
}
|
@ -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);
|
||||
|
@ -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 = [
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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, ゼãƒ!";
|
||||
|
@ -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");
|
||||
};
|
||||
|
@ -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")) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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');
|
||||
|
@ -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");
|
||||
|
@ -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());
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -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" };
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
};
|
||||
|
222
addon-sdk/source/test/test-system-events.js
Normal file
222
addon-sdk/source/test/test-system-events.js
Normal 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);
|
25
addon-sdk/source/test/test-system-runtime.js
Normal file
25
addon-sdk/source/test/test-system-runtime.js
Normal 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);
|
@ -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)");
|
||||
|
@ -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/",
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user