Back out a104642698a0 (bug 842762) for b2g test bustage

CLOSED TREE
This commit is contained in:
Phil Ringnalda 2013-02-19 19:49:41 -08:00
Родитель 9c635d6d0f
Коммит 6eda84a60d
74 изменённых файлов: 496 добавлений и 1338 удалений

Просмотреть файл

@ -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

57
addon-sdk/source/app-extension/bootstrap.js поставляемый
Просмотреть файл

@ -55,6 +55,29 @@ 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) {}
@ -92,31 +115,19 @@ function startup(data, reasonCode) {
resourceHandler.setSubstitution(domain, resourcesURI);
// Create path to URLs mapping supported by loader.
let paths = {
let paths = Object.keys(options.metadata).reduce(function(result, name) {
result[name + '/'] = prefixURI + name + '/lib/'
result[name + '/tests/'] = prefixURI + name + '/tests/'
return result
}, {
// Relative modules resolve to add-on package lib
'./': prefixURI + name + '/lib/',
'./tests/': prefixURI + name + '/tests/',
'': 'resource://gre/modules/commonjs/'
};
// Maps addon lib and tests ressource folders
paths[name + '/'] = prefixURI + name + '/lib/';
paths[name + '/tests/'] = prefixURI + name + '/tests/';
// 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';
'toolkit/': 'resource://gre/modules/toolkit/',
'': 'resources:///modules/'
});
// Make version 2 of the manifest
let manifest = options.manifest;
let manifest = manifestV2(options.manifest);
// Import `cuddlefish.js` module using a Sandbox and bootstrap loader.
let cuddlefishURI = prefixURI + options.loader;
@ -125,7 +136,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 = options.mainPath;
let main = path2id(options.mainPath);
unload = cuddlefish.unload;
loader = cuddlefish.Loader({
@ -169,7 +180,7 @@ function startup(data, reasonCode) {
}
});
let module = cuddlefish.Module('sdk/loader/cuddlefish', cuddlefishURI);
let module = cuddlefish.Module('addon-sdk/sdk/loader/cuddlefish', cuddlefishURI);
let require = cuddlefish.Require(loader, module);
require('sdk/addon/runner').startup(reason, {

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -11,144 +11,58 @@ We'd like to thank our many Jetpack project contributors! They include:
* arky
* [Heather Arthur](https://github.com/harthur)
* Dietrich Ayala
<!--end-->
* [Romain B](https://github.com/Niamor)
* [Louis-Rémi Babé](https://github.com/louisremi)
* Will Bamberg
* Thomas Bassetto
* Tomaz Bevec
* Zbigniew Braniecki
* Daniel Buchner
* James Burke
<!--end-->
* [Shane Caraveo](https://github.com/mixedpuppy)
* [Matěj Cepl](https://github.com/mcepl)
* Marc Chevrier
* Hernán Rodriguez Colmeiro
* [David Creswick](https://github.com/dcrewi)
<!--end-->
* dexter
* Christopher Dorn
* Connor Dunn
* dynamis
<!--end-->
* [Matteo Ferretti (ZER0)](https://github.com/ZER0)
* fuzzykiller
<!--end-->
* [Marcio Galli](https://github.com/taboca)
* [Ben Gillbanks](http://www.iconfinder.com/browse/iconset/circular_icons/)
* Felipe Gomes
* Irakli Gozalishvili
* Luca Greco
* Jeff Griffiths
* [David Guo](https://github.com/dglol)
<!--end-->
* Mark Hammond
* Mark A. Hershberger
* Lloyd Hilaiel
* Bobby Holley
<!--end-->
* Shun Ikejima
<!--end-->
* Eric H. Jung
<!--end-->
* Hrishikesh Kale
* Wes Kocher
* Lajos Koszti
<!--end-->
* Edward Lee
* Gregg Lind
<!--end-->
* [Nils Maier](https://github.com/nmaier)
* Gervase Markham
* Dave Mason
* Myk Melez
* Zandr Milewski
* Noelle Murata
<!--end-->
* Siavash Askari Nasr
* Joe R. Nassimian ([placidrage](https://github.com/placidrage))
* Dương H. Nguyễn
* Nick Nguyen
<!--end-->
* [ongaeshi](https://github.com/ongaeshi)
* Paul OShannessy
* Les Orchard
<!--end-->
* Robert Pankowecki
* l.m.orchard
* 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
* Tim Taubert
* Shane Tomlinson
* Thomas
* 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
* Brett Zamir
* [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)

Просмотреть файл

@ -6,18 +6,23 @@
# 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.
each end of the conversation has access to a `port` object which defines two
functions:
* to send messages from one side to the other, use `port.emit()`
* to receive messages sent from the other side, use `port.on()`
**`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>
<img class="image-center" src="static-files/media/content-scripting-events.png"
alt="Content script events">
port.emit("myEvent", myEventPayload);
Messages are asynchronous: that is, the sender does not wait for a reply from
the recipient but just emits the message and continues processing.
**`on()`** takes two parameters: the name of the event and a function to handle it:
Here's a simple add-on that sends a message to a content script using `port`:
port.on("myEvent", function handleMyEvent(myEventPayload) {
// Handle the event
});
Here's simple add-on that sends a message to a content script using `port`:
var tabs = require("sdk/tabs");
@ -34,49 +39,45 @@ Here's a simple add-on that sends a message to a content script using `port`:
tabs.open("http://www.mozilla.org");
In total, the `port` object defines four functions:
We could depict the interface between add-on code and content script code like
this:
* [`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.
<img class="image-center" src="static-files/media/content-scripting-events.png"
alt="Content script events">
## Accessing `port` ##
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` 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 a message from a content script:
global `self` object. Thus, to emit an event from a content script:
self.port.emit("myContentScriptMessage", myContentScriptMessagePayload);
self.port.emit("myContentScriptEvent", myContentScriptEventPayload);
To receive a message from the add-on code:
To receive an event from the add-on code:
self.port.on("myAddonMessage", function(myAddonMessagePayload) {
// Handle the message
self.port.on("myAddonEvent", function(myAddonEventPayload) {
// Handle the event
});
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
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
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 message
// Handle the event
});
So the `port` property is essentially used here as a namespace for
user-defined messages.
user-defined events.
### 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
@ -85,7 +86,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 messages from a content script associated
worker API directly. So to receive events from a content script associated
with a panel you use `panel.port.on()`:
var panel = require("sdk/panel").Panel({
@ -98,7 +99,7 @@ with a panel you use `panel.port.on()`:
panel.show();
Conversely, to emit user-defined messages from your add-on you can just call
Conversely, to emit user-defined events from your add-on you can just call
`panel.port.emit()`:
var panel = require("sdk/panel").Panel({
@ -120,8 +121,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
messages from the content script, and take a reference to the worker so as to
emit messages to the content script.
events from the content script, and take a reference to the worker so as to
emit events to it.
var pageModScript = "window.addEventListener('click', function(event) {" +
" self.port.emit('click', event.target.toString());" +
@ -142,126 +143,18 @@ emit messages to the content script.
}
});
In the add-on above there are two user-defined messages:
In the add-on above there are two user-defined events:
* `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 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.
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.
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 four of the six values defined by node.js:
The SDK uses only three of the six values defined by node.js:
<table>
<tr>
@ -33,14 +33,6 @@ The SDK uses only four 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>

Просмотреть файл

@ -240,96 +240,111 @@ rm my-addon.xpi
<a name="modules-compatibility"></a>
## Module Compatibility
Modules not yet supported in Firefox Mobile are
<span class="unsupported-on-mobile">**marked**</span> in the tables below.
Modules not yet supported in Firefox Mobile are <span class="unsupported-on-mobile">highlighted</span> in the tables below.
### High-Level APIs ###
- [**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)
<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>
### Low-Level APIs ###
- [/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)
<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>

Просмотреть файл

@ -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 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 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,18 +179,16 @@ 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, and your
item is shown only when all declarative contexts are current and your
context listener returns true.
script. In that case, the declarative contexts are evaluated first. If they
are not current, then your context listener is never called.
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:
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({
@ -201,26 +199,8 @@ can be assured that `node` will always be an image:
'});'
});
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;' +
'});'
});
Your item is shown only when all declarative contexts are current and your
context listener returns 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
@ -232,7 +212,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.
@ -246,68 +226,10 @@ item's content script like so:
'});'
});
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
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
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
@ -326,13 +248,11 @@ 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");
@ -354,7 +274,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;
@ -368,13 +288,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:
@ -392,7 +312,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.
@ -401,9 +321,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.
@ -516,14 +436,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
@ -539,43 +459,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>
@ -594,7 +514,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>
@ -624,13 +544,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
@ -646,43 +566,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>
@ -700,9 +620,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.
@ -710,7 +630,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.
@ -719,7 +639,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>
@ -741,7 +661,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">
@ -751,14 +671,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>
@ -769,7 +689,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>
@ -777,7 +697,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>
@ -786,7 +706,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.
@ -797,7 +717,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 asyncSum = promised(sum);
function sum(x, y) { return x + y }
var sumAsync = promised(sum);
var c = sum(a, b);
var cAsync = asyncSum(aAsync(), bAsync());
var cAsync = asyncSum(aAsync(), bAsinc());
`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,23 +27,14 @@ 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.

Просмотреть файл

@ -45,6 +45,7 @@
<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>
@ -162,8 +163,6 @@
<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-index,
h2[id="Module Compatibility"] ~ ul {
ul.module-list,
ul#module-index {
border-color: #a0d0fb;
border: solid 1px #a0d0fb;
padding: 1em;
@ -455,8 +455,8 @@ h2[id="Module Compatibility"] ~ ul {
column-gap: 1em;
}
span.unsupported-on-mobile strong,
h2[id="Module Compatibility"] ~ ul strong {
font-weight: normal;
text-decoration: line-through;
span.unsupported-on-mobile,
ul.module-list li.unsupported-on-mobile,
ul.module-list li.unsupported-on-mobile > a {
color: maroon;
}

Просмотреть файл

@ -86,7 +86,8 @@ 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
definePseudo(options.loader, '@l10n/data', data ? data : null);
if (data)
definePseudo(options.loader, '@l10n/data', data);
run(options);
});
}

Просмотреть файл

@ -9,11 +9,10 @@
factory.call(this, require, exports, module);
} else if (~String(this).indexOf('BackstagePass')) { // JSM
this[factory.name] = {};
var Cu = this.Components["utils"];
this.console = Cu.import("resource://gre/modules/devtools/Console.jsm", {})
.console;
factory(function require(uri) {
return Cu.import(uri, {});
var imports = {};
this['Components'].utils.import(uri, imports);
return imports;
}, this[factory.name], { uri: __URI__, id: id });
this.EXPORTED_SYMBOLS = [factory.name];
} else { // Browser or alike
@ -54,12 +53,7 @@ function attempt(f) {
**/
return function effort(options) {
try { return f(options) }
catch(error) {
if (exports._reportErrors && typeof(console) == 'object') {
console.error(error)
}
return rejection(error)
}
catch(error) { return rejection(error) }
}
}

Просмотреть файл

@ -11,8 +11,7 @@ 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'];
@ -53,11 +52,7 @@ TestFinder.prototype = {
suites.forEach(
function(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);
var module = require(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,15 +8,11 @@ module.metadata = {
"stability": "unstable"
};
const { Ci, Cu } = require("chrome");
const { Ci } = 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
@ -45,15 +41,8 @@ function onDocumentReady2Translate(event) {
translateElement(document);
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);
}
// Finally display document when we finished replacing all text content
document.documentElement.style.visibility = "visible";
}
function onContentWindow(event) {
@ -72,16 +61,12 @@ function onContentWindow(event) {
if (document.location.href.indexOf(assetsURI) !== 0)
return;
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);
}
// 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";
// Wait for DOM tree to be built before applying localization
document.addEventListener("DOMContentLoaded", onDocumentReady2Translate,
false);
@ -107,4 +92,4 @@ function disable() {
}
exports.disable = disable;
require("sdk/system/unload").when(disable);
require("api-utils/unload").when(disable);

Просмотреть файл

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

Просмотреть файл

@ -114,10 +114,6 @@ 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,11 +7,10 @@ module.metadata = {
"stability": "stable"
};
const { setMode, getMode, on: onStateChange, isWindowPrivate } = require('./private-browsing/utils');
const { setMode, getMode, on: onStateChange } = 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');
@ -37,19 +36,6 @@ 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,14 +15,13 @@ 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 (isOneOf(['Firefox', 'Fennec'])) {
if (require("../system/xul-app").is("Firefox")) {
// get the nsIPrivateBrowsingService if it exists
try {
pbService = Cc["@mozilla.org/privatebrowsing;1"].
@ -42,36 +41,20 @@ if (isOneOf(['Firefox', 'Fennec'])) {
}
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.
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;
return win instanceof Ci.nsIDOMWindow &&
isWindowPBSupported &&
PrivateBrowsingUtils.isWindowPrivate(win);
}
exports.isWindowPrivate = isWindowPrivate;
// checks that global private browsing is implemented
let isGlobalPBSupported = exports.isGlobalPBSupported = !!pbService && is('Firefox');
let isGlobalPBSupported = exports.isGlobalPBSupported = !!pbService;
// checks that per-window private browsing is implemented
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*');
let isWindowPBSupported = exports.isWindowPBSupported = !isGlobalPBSupported && !!PrivateBrowsingUtils;
function onChange() {
// Emit event with in next turn of event loop.

Просмотреть файл

@ -1,32 +0,0 @@
/* 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,8 +71,6 @@ 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'),
{ getMostRecentBrowserWindow,
{ getInnerId, getMostRecentBrowserWindow,
windows, getFocusedWindow, getFocusedElement } = require("./window/utils"),
events = require("./system/events");
@ -321,6 +321,17 @@ 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);
}
});
};
/**
@ -371,7 +382,7 @@ getAllTabContentWindows().forEach(addSelectionListener);
//
// See bug 665386 for further details.
function onShown(event) {
events.on("document-shown", function (event) {
let window = event.subject.defaultView;
// We are not interested in documents without valid defaultView.
@ -397,21 +408,17 @@ function onShown(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);
});

Просмотреть файл

@ -1,6 +1,7 @@
/* 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 = {
@ -102,6 +103,7 @@ function normalizeRange(range) {
* A version to compare
*/
function compareVersion(version, comparison, compareVersion) {
let hasWildcard = compareVersion.indexOf("*") !== -1;
comparison = comparison || "=";
@ -179,4 +181,5 @@ function satisfiesVersion(version, versionRange) {
: true);
});
}
exports.satisfiesVersion = satisfiesVersion;

Просмотреть файл

@ -7,10 +7,9 @@ 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, getContentWindowForTab,
const { activateTab, getTabTitle, setTabTitle, closeTab, getTabURL,
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');
@ -144,7 +143,3 @@ const Tab = Class({
}
});
exports.Tab = Tab;
getPBOwnerWindow.define(Tab, function(tab) {
return getContentWindowForTab(tabNS(tab).tab);
});

Просмотреть файл

@ -11,8 +11,6 @@ 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 = [];
@ -52,9 +50,6 @@ 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.
@ -217,10 +212,6 @@ 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,8 +80,7 @@ exports.getOwnerWindow = getOwnerWindow;
// fennec
function getWindowHoldingTab(rawTab) {
for each (let window in windows()) {
// this function may be called when not using fennec,
// but BrowserApp is only defined on Fennec
// this function may be called when not using fennec
if (!window.BrowserApp)
continue;
@ -101,8 +100,7 @@ function openTab(window, url, options) {
if (window.BrowserApp) {
return window.BrowserApp.addTab(url, {
selected: options.inBackground ? false : true,
pinned: options.isPinned || false,
isPrivate: options.private || false
pinned: options.isPinned || false
});
}
return window.gBrowser.addTab(url);
@ -152,12 +150,6 @@ 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
@ -166,6 +158,7 @@ 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("sdk/test/assert").Assert;
const { isFunction, isObject } = require("sdk/lang/type");
const BaseAssert = require("./test/assert").Assert;
const { isFunction, isObject } = require("./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,7 +11,6 @@ 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';
@ -40,7 +39,3 @@ const BrowserWindow = Class({
get isPrivateBrowsing() getMode(windowNS(this).window),
});
exports.BrowserWindow = BrowserWindow;
getPBOwnerWindow.define(BrowserWindow, function(window) {
return windowNS(window).window;
});

Просмотреть файл

@ -17,9 +17,7 @@ const { Cc, Ci, Cr } = require('chrome'),
windowUtils = require('../deprecated/window-utils'),
{ WindowTrackerTrait } = windowUtils,
{ ns } = require('../core/namespace'),
{ observer: windowObserver } = require('./observer'),
{ getOwnerWindow } = require('../private-browsing/window/utils'),
viewNS = require('sdk/core/namespace').ns();
{ observer: windowObserver } = require('./observer');
/**
* Window trait composes safe wrappers for browser window that are E10S
@ -71,10 +69,6 @@ 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(),
@ -245,8 +239,4 @@ const browserWindows = Trait.resolve({ toString: null }).compose(
}).resolve({ toString: null })
)();
function getChromeWindow(window) {
return viewNS(window).window;
}
exports.browserWindows = browserWindows;

Просмотреть файл

@ -1,112 +0,0 @@
/* 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,6 +206,7 @@ const evaluate = iced(function evaluate(sandbox, uri, options) {
source: null
}, options);
return source ? Cu.evalInSandbox(source, sandbox, version, uri, line)
: loadSubScript(uri, sandbox, encoding);
});
@ -280,7 +281,6 @@ 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,6 +320,7 @@ 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'] = 'sdk/test/runner'
harness_options['mainPath'] = manifest.get_manifest_entry("addon-sdk", "lib", "sdk/test/runner").get_path()
else:
harness_options['main'] = target_cfg.get('main')
harness_options['mainPath'] = manifest.top_path

Просмотреть файл

@ -196,3 +196,4 @@ 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,22 +58,11 @@ class ManifestEntry:
self.datamap = None
def get_path(self):
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)
path = "%s/%s/%s" % \
(self.packageName, self.sectionName, self.moduleName)
if not path.endswith(".js"):
path += ".js"
return path
def get_entry_for_manifest(self):
entry = { "packageName": self.packageName,
@ -86,13 +75,13 @@ class ManifestEntry:
for req in self.requirements:
if isinstance(self.requirements[req], ManifestEntry):
them = self.requirements[req] # this is another ManifestEntry
entry["requirements"][req] = them.get_path()
them_path = them.get_path()
entry["requirements"][req] = {"path": them_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], unicode) or \
isinstance(entry["requirements"][req], str)
assert isinstance(entry["requirements"][req], dict)
return entry
def add_js(self, js_filename):
@ -237,8 +226,7 @@ class ManifestBuilder:
# search for all tests. self.test_modules will be passed
# through the harness-options.json file in the
# .allTestModules property.
# Pass the absolute module path.
self.test_modules.append(tme.get_path())
self.test_modules.append(testname)
# include files used by the loader
for em in self.extra_modules:
@ -390,7 +378,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, reqname)
me.add_requirement(reqname, {"path": reqname})
else:
# when two modules require() the same name, do they get a
# shared instance? This is a deep question. For now say yes.

Просмотреть файл

@ -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("21.0a1"))
elem.appendChild(dom.createTextNode("20.*"))
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/%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")
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")
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/%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")
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")
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/main"]["requirements"]
reqs = m["five/lib/main.js"]["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(["three/tests/test-one", "three/tests/test-two"]))
sorted(["test-one", "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(["three/tests/test-one"]))
sorted(["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()

Просмотреть файл

@ -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,10 +453,8 @@ 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, 'Aurora', 'firefox.exe'),
(os.environ.get("ProgramFiles(x86)"),'Aurora', 'firefox.exe')
(program_files,'Nightly', 'firefox.exe'),
(os.environ.get("ProgramFiles(x86)"),'Nightly', 'firefox.exe')
]:
path = os.path.join(*bin)
if os.path.isfile(path):

Просмотреть файл

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

Просмотреть файл

@ -1,20 +0,0 @@
/* 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);

Просмотреть файл

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

Просмотреть файл

@ -1,18 +0,0 @@
/* 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);

Просмотреть файл

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

Просмотреть файл

@ -4,7 +4,7 @@
"use strict";
const AssertBase = require("sdk/test/assert").Assert;
const AssertBase = require("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('./types/cat').type;
exports.type = require('modules/types/cat').type;
});

Просмотреть файл

@ -47,14 +47,10 @@ 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();
});
@ -63,8 +59,6 @@ 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();
});
};
@ -78,8 +72,6 @@ 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());
});
@ -94,8 +86,6 @@ 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();
@ -116,8 +106,6 @@ 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);
@ -133,8 +121,6 @@ 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();
@ -151,8 +137,6 @@ 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);
@ -161,7 +145,6 @@ 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,15 +7,11 @@ 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("sdk/windows").browserWindows;
const windows = require("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 = [];
@ -44,8 +40,6 @@ function deactivate(callback) {
}
exports.deactivate = deactivate;
exports.loader = loader;
exports.pb = pb;
exports.pbUtils = pbUtils;
exports.getOwnerWindow = getOwnerWindow;
exports.LoaderWithHookedConsole = LoaderWithHookedConsole;

Просмотреть файл

@ -1,24 +0,0 @@
'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,11 +4,9 @@
'use strict';
const { pb, pbUtils } = require('./helper');
const { openDialog } = require('sdk/window/utils');
const { isPrivate } = require('sdk/private-browsing');
const { browserWindows: windows } = require('sdk/windows');
const { openDialog } = require('window/utils');
exports.testPerWindowPrivateBrowsingGetter = function(assert, done) {
exports["test Per Window Private Browsing getter"] = function(assert, done) {
let win = openDialog({
private: true
});
@ -25,30 +23,8 @@ exports.testPerWindowPrivateBrowsingGetter = 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('sdk/windows').browserWindows,
var windows = require('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('sdk/addon-page');
loader.require('addon-kit/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('sdk/addon-page');
loader.require('addon-kit/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('sdk/addon-page');
loader.require('addon-kit/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('sdk/addon-page');
loader.require('addon-kit/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('sdk/addon-page');
loader.require('addon-kit/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('test').run(exports);
require('sdk/test').run(exports);

Просмотреть файл

@ -1360,7 +1360,7 @@ exports.testMultipleModulesOrderOverflow = function (test) {
let loader0 = test.newLoader();
let loader1 = test.newLoader();
let prefs = loader0.loader.require("sdk/preferences/service");
let prefs = loader0.loader.require("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("sdk/preferences/service");
let prefs = loader0.loader.require("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("sdk/preferences/service");
let prefs = loader0.loader.require("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("sdk/preferences/service");
let prefs = loader.loader.require("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("sdk/preferences/service");
let prefs = loader0.loader.require("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("sdk/preferences/service");
let prefs = loader0.loader.require("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("sdk/preferences/service");
let prefs = loader0.loader.require("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("sdk/preferences/service");
let prefs = loader.loader.require("preferences-service");
prefs.set(OVERFLOW_THRESH_PREF, 2);
let pItems = [
@ -1718,6 +1718,7 @@ 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' +
@ -1797,6 +1798,7 @@ 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' +
@ -1844,6 +1846,7 @@ 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' +

Просмотреть файл

@ -5,13 +5,6 @@
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
@ -23,7 +16,8 @@ exports.testBasicHTTPServer = function(test) {
fileStream.write(content);
fileStream.close();
let srv = httpd.startServerAsync(port, basePath);
let { startServerAsync } = require("sdk/test/httpd");
let srv = startServerAsync(port, basePath);
test.waitUntilDone();
@ -47,7 +41,8 @@ exports.testBasicHTTPServer = function(test) {
exports.testDynamicServer = function (test) {
let content = "This is the HTTPD test file.\n";
let srv = httpd.startServerAsync(port);
let { startServerAsync } = require("sdk/test/httpd");
let srv = startServerAsync(port);
// See documentation here:
//http://doxygen.db48x.net/mozilla/html/interfacensIHttpServer.html#a81fc7e7e29d82aac5ce7d56d0bedfb3a

Просмотреть файл

@ -4,14 +4,14 @@
"use strict";
let xulApp = require("sdk/system/xul-app");
let xulApp = require("api-utils/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("sdk/indexed-db");
} = require("indexed-db");
exports["test indexedDB is frozen"] = function(assert){
let original = indexedDB.open;

Просмотреть файл

@ -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("sdk/test/runner").runTestsFromModule(module);
require("test").run(exports);

Просмотреть файл

@ -6,10 +6,10 @@
let { Loader, main, unload, parseStack } = require('toolkit/loader');
let root = module.uri.substr(0, module.uri.lastIndexOf('/'))
exports['test dependency cycles'] = function(assert) {
let uri = root + '/fixtures/loader/cycles/';
let uri = module.uri.substr(0, module.uri.lastIndexOf('/')) +
'/fixtures/loader/cycles/'
let loader = Loader({ paths: { '': uri } });
let program = main(loader, 'main');
@ -19,10 +19,12 @@ 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 = root + '/fixtures/loader/syntax-error/';
let uri = module.uri.substr(0, module.uri.lastIndexOf('/')) +
'/fixtures/loader/syntax-error/';
let loader = Loader({ paths: { '': uri } });
try {
@ -41,10 +43,12 @@ exports['test syntax errors'] = function(assert) {
} finally {
unload(loader);
}
}
};
exports['test missing module'] = function(assert) {
let uri = root + '/fixtures/loader/missing/'
let uri = module.uri.substr(0, module.uri.lastIndexOf('/')) +
'/fixtures/loader/missing/'
let loader = Loader({ paths: { '': uri } });
try {
@ -71,7 +75,8 @@ exports['test missing module'] = function(assert) {
}
exports['test exceptions in modules'] = function(assert) {
let uri = root + '/fixtures/loader/exceptions/'
let uri = module.uri.substr(0, module.uri.lastIndexOf('/')) +
'/fixtures/loader/exceptions/'
let loader = Loader({ paths: { '': uri } });
@ -107,7 +112,9 @@ exports['test exceptions in modules'] = function(assert) {
}
exports['test early errors in module'] = function(assert) {
let uri = root + '/fixtures/loader/errors/';
let uri = module.uri.substr(0, module.uri.lastIndexOf('/')) +
'/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("sdk/self");
const { data } = require("self");
const utf8text = "Hello, ゼロ!";
const latin1text = "Hello, ゼロ!";

Просмотреть файл

@ -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('sdk/self');
const { data } = require('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,27 +357,6 @@ 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();
@ -654,7 +633,7 @@ exports['test111 attachTo [frame]'] = function(test) {
this.destroy();
if (++messageCount == 2) {
mod.destroy();
require('sdk/tabs').activeTab.close(function() {
require('tabs').activeTab.close(function() {
test.done();
});
}
@ -813,7 +792,7 @@ exports.testPageModCssAutomaticDestroy = function(test) {
test.waitUntilDone();
let loader = Loader(module);
let pageMod = loader.require("sdk/page-mod").PageMod({
let pageMod = loader.require("page-mod").PageMod({
include: "data:*",
contentStyle: "div { width: 100px!important; }"
});
@ -853,7 +832,7 @@ exports.testPageModTimeout = function(test) {
test.waitUntilDone();
let tab = null
let loader = Loader(module);
let { PageMod } = loader.require("sdk/page-mod");
let { PageMod } = loader.require("page-mod");
let mod = PageMod({
include: "data:*",
@ -888,7 +867,7 @@ exports.testPageModcancelTimeout = function(test) {
test.waitUntilDone();
let tab = null
let loader = Loader(module);
let { PageMod } = loader.require("sdk/page-mod");
let { PageMod } = loader.require("page-mod");
let mod = PageMod({
include: "data:*",
@ -1050,3 +1029,4 @@ 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('sdk/self');
const self = require('self');
exports["test Panel"] = function(assert, done) {
const { Panel } = require('sdk/panel');

Просмотреть файл

@ -3,46 +3,18 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
'use strict';
const { Ci } = require('chrome');
const { pb, pbUtils, getOwnerWindow } = require('./private-browsing/helper');
const { pb, pbUtils } = require('./private-browsing/helper');
const { merge } = require('sdk/util/object');
const windows = require('sdk/windows').browserWindows;
const winUtils = require('sdk/window/utils');
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');
@ -57,31 +29,3 @@ exports.testIsActiveDefault = function(test) {
'pb.isActive returns false when private browsing isn\'t supported');
};
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,9 +15,6 @@ 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")
@ -143,55 +140,6 @@ 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,16 +14,12 @@ 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
@ -86,55 +82,6 @@ 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.
*/
@ -227,7 +174,7 @@ function dispatchOnSelectEvent(window) {
event.initUIEvent("select", true, true, window, 1);
textarea.dispatchEvent(event);
textarea.dispatchEvent(event)
}
/**
@ -783,42 +730,6 @@ 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

Просмотреть файл

@ -199,7 +199,7 @@ exports.testPrefUnloadWildcardListener = function(test) {
test.waitUntilDone();
let testpref = "test-wildcard-unload-listener";
let loader = Loader(module);
let sp = loader.require("sdk/simple-prefs");
let sp = loader.require("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("sdk/simple-prefs").prefs[testpref] = false;
require("simple-prefs").prefs[testpref] = false;
test.done();
};

Просмотреть файл

@ -115,8 +115,7 @@ 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");
// 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");
assert.equal(tab.index, 1, "Tab has the expected index");
tab.close(function () {
assert.equal(tab.url, undefined,
"After being closed, tab attributes are undefined (url)");

Просмотреть файл

@ -3,6 +3,7 @@
* 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(),
@ -85,6 +86,16 @@ 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("sdk/self");
const self = require("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("sdk/widget").Widget({
let widget = require("widget").Widget({
id: "mozilla-svg-logo",
label: "moz foundation logo",
contentURL: SVG_URL,

Просмотреть файл

@ -10,8 +10,7 @@ const wm = Cc['@mozilla.org/appshell/window-mediator;1'].
getService(Ci.nsIWindowMediator);
const { browserWindows } = require("sdk/windows");
const tabs = require("sdk/tabs");
const { WindowTracker } = require("sdk/deprecated/window-utils");
const tabs = require("tabs");
// TEST: open & close window
exports.testOpenAndCloseWindow = function(test) {
@ -204,24 +203,33 @@ exports.testActiveWindow = function(test) {
count++;
test.assertEqual(count, 3, "Correct number of windows returned by iterator");
test.assertEqual(windows.activeWindow.title, window3.title, "Correct active window - 3");
continueAfterFocus(rawWindow2);
rawWindow2.focus();
continueAfterFocus(rawWindow2);
},
function() {
nextStep();
},
function() {
test.assertEqual(windows.activeWindow.title, window2.title, "Correct active window - 2");
/**
* 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.
continueAfterFocus(rawWindow2);
// 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);
},
function() {
test.assertEqual(windows.activeWindow.title, window2.title, "Correct active window - 2");
continueAfterFocus(rawWindow3);
window3.activate();
continueAfterFocus(rawWindow3);
},
function() {
test.assertEqual(windows.activeWindow.title, window3.title, "Correct active window - 3");
@ -229,39 +237,21 @@ 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) {
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");
window2 = window;
rawWindow2 = wm.getMostRecentWindow("navigator:browser");
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();
});
}
});
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()
});
}
});
}
});
@ -288,11 +278,11 @@ exports.testActiveWindow = function(test) {
var focused = (focusedChildWindow == childTargetWindow);
if (focused) {
setTimeout(nextStep, 0);
nextStep();
} else {
childTargetWindow.addEventListener("focus", function focusListener() {
childTargetWindow.removeEventListener("focus", focusListener, true);
setTimeout(nextStep, 0);
nextStep();
}, true);
}