mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 11:25:00 +00:00
Bug 858326 - Uplift Add-on SDK changeset 99d7f27c7e
This commit is contained in:
parent
c6eafdb377
commit
5da2ab51d0
@ -157,6 +157,7 @@ We'd like to thank our many Jetpack project contributors! They include:
|
||||
* slash
|
||||
* Markus Stange
|
||||
* Dan Stevens
|
||||
* [J. Ryan Stinnett](https://github.com/jryans)
|
||||
* [Mihai Sucan](https://github.com/mihaisucan)
|
||||
|
||||
<!--end-->
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
<!-- 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/. -->
|
||||
@ -58,8 +57,8 @@ In this case files are specified by a URL typically constructed using the
|
||||
|
||||
pageMod.PageMod({
|
||||
include: "*.mozilla.org",
|
||||
contentScriptFile: [self.data.url("jquery-1.7.min.js"),
|
||||
self.data.url("my-script.js")]
|
||||
contentScriptFile: [data.url("jquery-1.7.min.js"),
|
||||
data.url("my-script.js")]
|
||||
});
|
||||
|
||||
<div class="warning">
|
||||
|
@ -169,6 +169,12 @@ This is an optional property.
|
||||
@prop [onReady] {function}
|
||||
A callback function that will be registered for 'ready' event.
|
||||
This is an optional property.
|
||||
@prop [onLoad] {function}
|
||||
A callback function that will be registered for 'load' event.
|
||||
This is an optional property.
|
||||
@prop [onPageShow] {function}
|
||||
A callback function that will be registered for 'pageshow' event.
|
||||
This is an optional property.
|
||||
@prop [onActivate] {function}
|
||||
A callback function that will be registered for 'activate' event.
|
||||
This is an optional property.
|
||||
@ -332,6 +338,47 @@ content can be used.
|
||||
Listeners are passed the tab object.
|
||||
</api>
|
||||
|
||||
<api name="load">
|
||||
@event
|
||||
|
||||
This event is emitted when the page for the tab's content is loaded. It is
|
||||
equivalent to the `load` event for the given content page.
|
||||
|
||||
A single tab will emit this event every time the page is loaded: so it will be
|
||||
emitted again if the tab's location changes or the content is reloaded.
|
||||
|
||||
After this event has been emitted, all properties relating to the tab's
|
||||
content can be used.
|
||||
|
||||
This is fired after the `ready` event on DOM content pages and can be used
|
||||
for pages that do not have a `DOMContentLoaded` event, like images.
|
||||
|
||||
@argument {Tab}
|
||||
Listeners are passed the tab object.
|
||||
</api>
|
||||
|
||||
<api name="pageshow">
|
||||
@event
|
||||
|
||||
This event is emitted when the page for the tab's content is potentially
|
||||
from the cache. It is equivilent to the [pageshow](https://developer.mozilla.org/en-US/docs/DOM/Mozilla_event_reference/pageshow) event for the given
|
||||
content page.
|
||||
|
||||
After this event has been emitted, all properties relating to the tab's
|
||||
content can be used.
|
||||
|
||||
While the `ready` and `load` events will not be fired when a user uses the back
|
||||
or forward buttons to navigate history, the `pageshow` event will be fired.
|
||||
If the `persisted` argument is true, then the contents were loaded from the
|
||||
bfcache.
|
||||
|
||||
@argument {Tab}
|
||||
Listeners are passed the tab object.
|
||||
@argument {persisted}
|
||||
Listeners are passed a boolean value indicating whether or not the page
|
||||
was loaded from the [bfcache](https://developer.mozilla.org/en-US/docs/Working_with_BFCache) or not.
|
||||
</api>
|
||||
|
||||
<api name="activate">
|
||||
@event
|
||||
|
||||
|
@ -16,6 +16,8 @@ const { when: unload } = require('../system/unload');
|
||||
const { loadReason } = require('../self');
|
||||
const { rootURI } = require("@loader/options");
|
||||
const globals = require('../system/globals');
|
||||
const appShellService = Cc['@mozilla.org/appshell/appShellService;1'].
|
||||
getService(Ci.nsIAppShellService);
|
||||
|
||||
const NAME2TOPIC = {
|
||||
'Firefox': 'sessionstore-windows-restored',
|
||||
@ -70,8 +72,18 @@ function wait(reason, options) {
|
||||
}
|
||||
|
||||
function startup(reason, options) {
|
||||
if (reason === 'startup')
|
||||
// Try accessing hidden window to guess if we are running during firefox
|
||||
// startup, so that we should wait for session restore event before
|
||||
// running the addon
|
||||
let initialized = false;
|
||||
try {
|
||||
appShellService.hiddenDOMWindow;
|
||||
initialized = true;
|
||||
}
|
||||
catch(e) {}
|
||||
if (reason === 'startup' || !initialized) {
|
||||
return wait(reason, options);
|
||||
}
|
||||
|
||||
// Inject globals ASAP in order to have console API working ASAP
|
||||
Object.defineProperties(options.loader.globals, descriptor(globals));
|
||||
|
20
addon-sdk/source/lib/sdk/browser/events.js
Normal file
20
addon-sdk/source/lib/sdk/browser/events.js
Normal file
@ -0,0 +1,20 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
module.metadata = {
|
||||
"stability": "unstable"
|
||||
};
|
||||
|
||||
const { events } = require("../window/events");
|
||||
const { filter } = require("../event/utils");
|
||||
const { isBrowser } = require("../window/utils");
|
||||
|
||||
// TODO: `isBrowser` detects weather window is a browser by checking
|
||||
// `windowtype` attribute, which means that all 'open' events will be
|
||||
// filtered out since document is not loaded yet. Maybe we can find a better
|
||||
// implementation for `isBrowser`. Either way it's not really needed yet
|
||||
// neither window tracker provides this event.
|
||||
|
||||
exports.events = filter(function({target}) isBrowser(target), events);
|
@ -270,6 +270,12 @@ TestRunner.prototype = {
|
||||
this.waitUntilCallback = null;
|
||||
if (this.test.passed == 0 && this.test.failed == 0) {
|
||||
this._logTestFailed("empty test");
|
||||
if ("testMessage" in this.console) {
|
||||
this.console.testMessage(false, false, this.test.name, "Empty test");
|
||||
}
|
||||
else {
|
||||
this.console.error("fail:", "Empty test")
|
||||
}
|
||||
this.failed++;
|
||||
this.test.failed++;
|
||||
}
|
||||
@ -414,6 +420,12 @@ TestRunner.prototype = {
|
||||
|
||||
function tiredOfWaiting() {
|
||||
self._logTestFailed("timed out");
|
||||
if ("testMessage" in self.console) {
|
||||
self.console.testMessage(false, false, self.test.name, "Timed out");
|
||||
}
|
||||
else {
|
||||
self.console.error("fail:", "Timed out")
|
||||
}
|
||||
if (self.waitUntilCallback) {
|
||||
self.waitUntilCallback(true);
|
||||
self.waitUntilCallback = null;
|
||||
|
@ -95,7 +95,8 @@ function emit(target, type, message /*, ...*/) {
|
||||
*/
|
||||
emit.lazy = function lazy(target, type, message /*, ...*/) {
|
||||
let args = Array.slice(arguments, 2);
|
||||
let listeners = observers(target, type).slice();
|
||||
let state = observers(target, type);
|
||||
let listeners = state.slice();
|
||||
let index = 0;
|
||||
let count = listeners.length;
|
||||
|
||||
@ -103,7 +104,11 @@ emit.lazy = function lazy(target, type, message /*, ...*/) {
|
||||
// into a console.
|
||||
if (count === 0 && type === 'error') console.exception(message);
|
||||
while (index < count) {
|
||||
try { yield listeners[index].apply(target, args); }
|
||||
try {
|
||||
let listener = listeners[index];
|
||||
// Dispatch only if listener is still registered.
|
||||
if (~state.indexOf(listener)) yield listener.apply(target, args);
|
||||
}
|
||||
catch (error) {
|
||||
// If exception is not thrown by a error listener and error listener is
|
||||
// registered emit `error` event. Otherwise dump exception to the console.
|
||||
|
26
addon-sdk/source/lib/sdk/event/dom.js
Normal file
26
addon-sdk/source/lib/sdk/event/dom.js
Normal file
@ -0,0 +1,26 @@
|
||||
/* 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"
|
||||
};
|
||||
|
||||
let { emit, on, off } = require("./core");
|
||||
|
||||
// Simple utility function takes event target, event type and optional
|
||||
// `options.capture` and returns node style event stream that emits "data"
|
||||
// events every time event of that type occurs on the given `target`.
|
||||
function open(target, type, options) {
|
||||
let output = {};
|
||||
let capture = options && options.capture ? true : false;
|
||||
|
||||
target.addEventListener(type, function(event) {
|
||||
emit(output, "data", event);
|
||||
}, capture);
|
||||
|
||||
return output;
|
||||
}
|
||||
exports.open = open;
|
@ -75,6 +75,9 @@ const EventTarget = Class({
|
||||
// than intended. This way we make sure all arguments are passed and only
|
||||
// one listener is removed at most.
|
||||
off(this, type, listener);
|
||||
},
|
||||
off: function(type, listener) {
|
||||
off(this, type, listener)
|
||||
}
|
||||
});
|
||||
exports.EventTarget = EventTarget;
|
||||
|
101
addon-sdk/source/lib/sdk/event/utils.js
Normal file
101
addon-sdk/source/lib/sdk/event/utils.js
Normal file
@ -0,0 +1,101 @@
|
||||
/* 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"
|
||||
};
|
||||
|
||||
let { emit, on, off } = require("./core");
|
||||
|
||||
// This module provides set of high order function for working with event
|
||||
// streams (streams in a NodeJS style that dispatch data, end and error
|
||||
// events).
|
||||
|
||||
// Function takes a `target` object and returns set of implicit references
|
||||
// (non property references) it keeps. This basically allows defining
|
||||
// references between objects without storing the explicitly. See transform for
|
||||
// more details.
|
||||
let refs = (function() {
|
||||
let refSets = new WeakMap();
|
||||
return function refs(target) {
|
||||
if (!refSets.has(target)) refSets.set(target, new Set());
|
||||
return refSets.get(target);
|
||||
}
|
||||
})();
|
||||
|
||||
function transform(f, input) {
|
||||
let output = {};
|
||||
|
||||
// Since event listeners don't prevent `input` to be GC-ed we wanna presrve
|
||||
// it until `output` can be GC-ed. There for we add implicit reference which
|
||||
// is removed once `input` ends.
|
||||
refs(output).add(input);
|
||||
|
||||
function next(data) emit(output, "data", data);
|
||||
on(input, "error", function(error) emit(output, "error", error));
|
||||
on(input, "end", function() {
|
||||
refs(output).delete(input);
|
||||
emit(output, "end");
|
||||
});
|
||||
on(input, "data", function(data) f(data, next));
|
||||
return output;
|
||||
}
|
||||
|
||||
// High order event transformation function that takes `input` event channel
|
||||
// and returns transformation containing only events on which `p` predicate
|
||||
// returns `true`.
|
||||
function filter(predicate, input) {
|
||||
return transform(function(data, next) {
|
||||
if (predicate(data)) next(data)
|
||||
}, input);
|
||||
}
|
||||
exports.filter = filter;
|
||||
|
||||
// High order function that takes `input` and returns input of it's values
|
||||
// mapped via given `f` function.
|
||||
function map(f, input) transform(function(data, next) next(f(data)), input)
|
||||
exports.map = map;
|
||||
|
||||
// High order function that takes `input` stream of streams and merges them
|
||||
// into single event stream. Like flatten but time based rather than order
|
||||
// based.
|
||||
function merge(inputs) {
|
||||
let output = {};
|
||||
let open = 1;
|
||||
let state = [];
|
||||
output.state = state;
|
||||
refs(output).add(inputs);
|
||||
|
||||
function end(input) {
|
||||
open = open - 1;
|
||||
refs(output).delete(input);
|
||||
if (open === 0) emit(output, "end");
|
||||
}
|
||||
function error(e) emit(output, "error", e);
|
||||
function forward(input) {
|
||||
state.push(input);
|
||||
open = open + 1;
|
||||
on(input, "end", function() end(input));
|
||||
on(input, "error", error);
|
||||
on(input, "data", function(data) emit(output, "data", data));
|
||||
}
|
||||
|
||||
// If `inputs` is an array treat it as a stream.
|
||||
if (Array.isArray(inputs)) {
|
||||
inputs.forEach(forward)
|
||||
end(inputs)
|
||||
}
|
||||
else {
|
||||
on(inputs, "end", function() end(inputs));
|
||||
on(inputs, "error", error);
|
||||
on(inputs, "data", forward);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
exports.merge = merge;
|
||||
|
||||
function expand(f, inputs) merge(map(f, inputs))
|
||||
exports.expand = expand;
|
@ -16,6 +16,7 @@ const { EventTarget } = require("./event/target");
|
||||
const { Class } = require("./core/heritage");
|
||||
const { XMLHttpRequest } = require("./net/xhr");
|
||||
const apiUtils = require("./deprecated/api-utils");
|
||||
const { isValidURI } = require("./url.js");
|
||||
|
||||
const response = ns();
|
||||
const request = ns();
|
||||
@ -24,8 +25,7 @@ const request = ns();
|
||||
// reuse it.
|
||||
const { validateOptions, validateSingleOption } = new OptionsValidator({
|
||||
url: {
|
||||
//XXXzpao should probably verify that url is a valid url as well
|
||||
is: ["string"]
|
||||
ok: isValidURI
|
||||
},
|
||||
headers: {
|
||||
map: function (v) v || {},
|
||||
|
60
addon-sdk/source/lib/sdk/tab/events.js
Normal file
60
addon-sdk/source/lib/sdk/tab/events.js
Normal file
@ -0,0 +1,60 @@
|
||||
/* 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";
|
||||
|
||||
// This module provides temporary shim until Bug 843901 is shipped.
|
||||
// It basically registers tab event listeners on all windows that get
|
||||
// opened and forwards them through observer notifications.
|
||||
|
||||
module.metadata = {
|
||||
"stability": "experimental"
|
||||
};
|
||||
|
||||
const { Ci } = require("chrome");
|
||||
const { windows, isInteractive } = require("../window/utils");
|
||||
const { events } = require("../browser/events");
|
||||
const { open } = require("../event/dom");
|
||||
const { filter, map, merge, expand } = require("../event/utils");
|
||||
|
||||
// Module provides event stream (in nodejs style) that emits data events
|
||||
// for all the tab events that happen in running firefox. At the moment
|
||||
// it does it by registering listeners on all browser windows and then
|
||||
// forwarding events when they occur to a stream. This will become obsolete
|
||||
// once Bug 843901 is fixed, and we'll just leverage observer notifications.
|
||||
|
||||
// Set of tab events that this module going to aggregate and expose.
|
||||
const TYPES = ["TabOpen","TabClose","TabSelect","TabMove","TabPinned",
|
||||
"TabUnpinned"];
|
||||
|
||||
// Utility function that given a browser `window` returns stream of above
|
||||
// defined tab events for all tabs on the given window.
|
||||
function tabEventsFor(window) {
|
||||
// Map supported event types to a streams of those events on the given
|
||||
// `window` and than merge these streams into single form stream off
|
||||
// all events.
|
||||
let channels = TYPES.map(function(type) open(window, type));
|
||||
return merge(channels);
|
||||
}
|
||||
|
||||
// Filter DOMContentLoaded events from all the browser events.
|
||||
let readyEvents = filter(function(e) e.type === "DOMContentLoaded", events);
|
||||
// Map DOMContentLoaded events to it's target browser windows.
|
||||
let futureWindows = map(function(e) e.target, readyEvents);
|
||||
// Expand all browsers that will become interactive to supported tab events
|
||||
// on these windows. Result will be a tab events from all tabs of all windows
|
||||
// that will become interactive.
|
||||
let eventsFromFuture = expand(tabEventsFor, futureWindows);
|
||||
|
||||
// Above covers only windows that will become interactive in a future, but some
|
||||
// windows may already be interactive so we pick those and expand to supported
|
||||
// tab events for them too.
|
||||
let interactiveWindows = windows("navigator:browser", { includePrivate: true }).
|
||||
filter(isInteractive);
|
||||
let eventsFromInteractive = merge(interactiveWindows.map(tabEventsFor));
|
||||
|
||||
|
||||
// Finally merge stream of tab events from future windows and current windows
|
||||
// to cover all tab events on all windows that will open.
|
||||
exports.events = merge([eventsFromInteractive, eventsFromFuture]);
|
@ -20,6 +20,8 @@ function Options(options) {
|
||||
onOpen: { is: ["undefined", "function"] },
|
||||
onClose: { is: ["undefined", "function"] },
|
||||
onReady: { is: ["undefined", "function"] },
|
||||
onLoad: { is: ["undefined", "function"] },
|
||||
onPageShow: { is: ["undefined", "function"] },
|
||||
onActivate: { is: ["undefined", "function"] },
|
||||
onDeactivate: { is: ["undefined", "function"] }
|
||||
});
|
||||
|
@ -12,6 +12,8 @@ const TAB_PREFIX = "Tab";
|
||||
|
||||
const EVENTS = {
|
||||
ready: "DOMContentLoaded",
|
||||
load: "load", // Used for non-HTML content
|
||||
pageshow: "pageshow", // Used for cached content
|
||||
open: "TabOpen",
|
||||
close: "TabClose",
|
||||
activate: "TabSelect",
|
||||
|
@ -6,6 +6,7 @@
|
||||
const { Trait } = require("../deprecated/traits");
|
||||
const { EventEmitter } = require("../deprecated/events");
|
||||
const { defer } = require("../lang/functional");
|
||||
const { has } = require("../util/array");
|
||||
const { EVENTS } = require("./events");
|
||||
const { getThumbnailURIForWindow } = require("../content/thumbnail");
|
||||
const { getFaviconURIForLocation } = require("../io/data");
|
||||
@ -33,6 +34,8 @@ const TabTrait = Trait.compose(EventEmitter, {
|
||||
window: null,
|
||||
constructor: function Tab(options) {
|
||||
this._onReady = this._onReady.bind(this);
|
||||
this._onLoad = this._onLoad.bind(this);
|
||||
this._onPageShow = this._onPageShow.bind(this);
|
||||
this._tab = options.tab;
|
||||
// TODO: Remove this dependency
|
||||
let window = this.window = options.window || require('../windows').BrowserWindow({ window: getOwnerWindow(this._tab) });
|
||||
@ -40,14 +43,19 @@ const TabTrait = Trait.compose(EventEmitter, {
|
||||
// Setting event listener if was passed.
|
||||
for each (let type in EVENTS) {
|
||||
let listener = options[type.listener];
|
||||
if (listener)
|
||||
if (listener) {
|
||||
this.on(type.name, options[type.listener]);
|
||||
if ('ready' != type.name) // window spreads this event.
|
||||
}
|
||||
// window spreads this event.
|
||||
if (!has(['ready', 'load', 'pageshow'], (type.name)))
|
||||
window.tabs.on(type.name, this._onEvent.bind(this, type.name));
|
||||
}
|
||||
|
||||
this.on(EVENTS.close.name, this.destroy.bind(this));
|
||||
|
||||
this._browser.addEventListener(EVENTS.ready.dom, this._onReady, true);
|
||||
this._browser.addEventListener(EVENTS.load.dom, this._onLoad, true);
|
||||
this._browser.addEventListener(EVENTS.pageshow.dom, this._onPageShow, true);
|
||||
|
||||
if (options.isPinned)
|
||||
this.pin();
|
||||
@ -65,8 +73,11 @@ const TabTrait = Trait.compose(EventEmitter, {
|
||||
if (this._tab) {
|
||||
let browser = this._browser;
|
||||
// The tab may already be removed from DOM -or- not yet added
|
||||
if (browser)
|
||||
if (browser) {
|
||||
browser.removeEventListener(EVENTS.ready.dom, this._onReady, true);
|
||||
browser.removeEventListener(EVENTS.load.dom, this._onLoad, true);
|
||||
browser.removeEventListener(EVENTS.pageshow.dom, this._onPageShow, true);
|
||||
}
|
||||
this._tab = null;
|
||||
TABS.splice(TABS.indexOf(this), 1);
|
||||
}
|
||||
@ -74,13 +85,35 @@ const TabTrait = Trait.compose(EventEmitter, {
|
||||
|
||||
/**
|
||||
* Internal listener that emits public event 'ready' when the page of this
|
||||
* tab is loaded.
|
||||
* tab is loaded, from DOMContentLoaded
|
||||
*/
|
||||
_onReady: function _onReady(event) {
|
||||
// IFrames events will bubble so we need to ignore those.
|
||||
if (event.target == this._contentDocument)
|
||||
this._emit(EVENTS.ready.name, this._public);
|
||||
},
|
||||
|
||||
/**
|
||||
* Internal listener that emits public event 'load' when the page of this
|
||||
* tab is loaded, for triggering on non-HTML content, bug #671305
|
||||
*/
|
||||
_onLoad: function _onLoad(event) {
|
||||
// IFrames events will bubble so we need to ignore those.
|
||||
if (event.target == this._contentDocument) {
|
||||
this._emit(EVENTS.load.name, this._public);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Internal listener that emits public event 'pageshow' when the page of this
|
||||
* tab is loaded from cache, bug #671305
|
||||
*/
|
||||
_onPageShow: function _onPageShow(event) {
|
||||
// IFrames events will bubble so we need to ignore those.
|
||||
if (event.target == this._contentDocument) {
|
||||
this._emit(EVENTS.pageshow.name, this._public, event.persisted);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Internal tab event router. Window will emit tab related events for all it's
|
||||
* tabs, this listener will propagate all the events for this tab to it's
|
||||
|
@ -303,3 +303,53 @@ function getTabForBrowser(browser) {
|
||||
}
|
||||
exports.getTabForBrowser = getTabForBrowser;
|
||||
|
||||
function pin(tab) {
|
||||
let gBrowser = getTabBrowserForTab(tab);
|
||||
// TODO: Implement Fennec support
|
||||
if (gBrowser) gBrowser.pinTab(tab);
|
||||
}
|
||||
exports.pin = pin;
|
||||
|
||||
function unpin(tab) {
|
||||
let gBrowser = getTabBrowserForTab(tab);
|
||||
// TODO: Implement Fennec support
|
||||
if (gBrowser) gBrowser.unpinTab(tab);
|
||||
}
|
||||
exports.unpin = unpin;
|
||||
|
||||
function isPinned(tab) !!tab.pinned
|
||||
exports.isPinned = isPinned;
|
||||
|
||||
function reload(tab) {
|
||||
let gBrowser = getTabBrowserForTab(tab);
|
||||
// Firefox
|
||||
if (gBrowser) gBrowser.unpinTab(tab);
|
||||
// Fennec
|
||||
else if (tab.browser) tab.browser.reload();
|
||||
}
|
||||
exports.reload = reload
|
||||
|
||||
function getIndex(tab) {
|
||||
let gBrowser = getTabBrowserForTab(tab);
|
||||
// Firefox
|
||||
if (gBrowser) {
|
||||
let document = getBrowserForTab(tab).contentDocument;
|
||||
return gBrowser.getBrowserIndexForDocument(document);
|
||||
}
|
||||
// Fennec
|
||||
else {
|
||||
let window = getWindowHoldingTab(tab)
|
||||
let tabs = window.BrowserApp.tabs;
|
||||
for (let i = tabs.length; i >= 0; i--)
|
||||
if (tabs[i] === tab) return i;
|
||||
}
|
||||
}
|
||||
exports.getIndex = getIndex;
|
||||
|
||||
function move(tab, index) {
|
||||
let gBrowser = getTabBrowserForTab(tab);
|
||||
// Firefox
|
||||
if (gBrowser) gBrowser.moveTabTo(tab, index);
|
||||
// TODO: Implement fennec support
|
||||
}
|
||||
exports.move = move;
|
||||
|
@ -26,7 +26,8 @@ function runTests(findAndRunTests) {
|
||||
stdout.write("No tests were run\n");
|
||||
exit(0);
|
||||
} else {
|
||||
printFailedTests(tests, cfxArgs.verbose, stdout.write);
|
||||
if (cfxArgs.verbose || cfxArgs.parseable)
|
||||
printFailedTests(tests, stdout.write);
|
||||
exit(1);
|
||||
}
|
||||
};
|
||||
@ -50,10 +51,7 @@ function runTests(findAndRunTests) {
|
||||
}, 0);
|
||||
}
|
||||
|
||||
function printFailedTests(tests, verbose, print) {
|
||||
if (!verbose)
|
||||
return;
|
||||
|
||||
function printFailedTests(tests, print) {
|
||||
let iterationNumber = 0;
|
||||
let singleIteration = tests.testRuns.length == 1;
|
||||
let padding = singleIteration ? "" : " ";
|
||||
|
@ -234,3 +234,12 @@ const DataURL = Class({
|
||||
});
|
||||
|
||||
exports.DataURL = DataURL;
|
||||
|
||||
let isValidURI = exports.isValidURI = function (uri) {
|
||||
try {
|
||||
newURI(uri);
|
||||
} catch(e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
80
addon-sdk/source/lib/sdk/window/events.js
Normal file
80
addon-sdk/source/lib/sdk/window/events.js
Normal file
@ -0,0 +1,80 @@
|
||||
/* 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 { Ci } = require("chrome");
|
||||
const events = require("../system/events");
|
||||
const { on, off, emit } = require("../event/core");
|
||||
const { windows } = require("../window/utils");
|
||||
|
||||
// Object represents event channel on which all top level window events
|
||||
// will be dispatched, allowing users to react to those evens.
|
||||
const channel = {};
|
||||
exports.events = channel;
|
||||
|
||||
const types = {
|
||||
domwindowopened: "open",
|
||||
domwindowclosed: "close",
|
||||
}
|
||||
|
||||
// Utility function to query observer notification subject to get DOM window.
|
||||
function nsIDOMWindow($) $.QueryInterface(Ci.nsIDOMWindow);
|
||||
|
||||
// Utility function used as system event listener that is invoked every time
|
||||
// top level window is open. This function does two things:
|
||||
// 1. Registers event listeners to track when document becomes interactive and
|
||||
// when it's done loading. This will become obsolete once Bug 843910 is
|
||||
// fixed.
|
||||
// 2. Forwards event to an exported event stream.
|
||||
function onOpen(event) {
|
||||
observe(nsIDOMWindow(event.subject));
|
||||
dispatch(event);
|
||||
}
|
||||
|
||||
// Function registers single shot event listeners for relevant window events
|
||||
// that forward events to exported event stream.
|
||||
function observe(window) {
|
||||
function listener(event) {
|
||||
if (event.target === window.document) {
|
||||
window.removeEventListener(event.type, listener, true);
|
||||
emit(channel, "data", { type: event.type, target: window });
|
||||
}
|
||||
}
|
||||
|
||||
// Note: we do not remove listeners on unload since on add-on unload we
|
||||
// nuke add-on sandbox that should allow GC-ing listeners. This also has
|
||||
// positive effects on add-on / firefox unloads.
|
||||
window.addEventListener("DOMContentLoaded", listener, true);
|
||||
window.addEventListener("load", listener, true);
|
||||
// TODO: Also add focus event listener so that can be forwarded to event
|
||||
// stream. It can be part of Bug 854982.
|
||||
}
|
||||
|
||||
// Utility function that takes system notification event and forwards it to a
|
||||
// channel in restructured form.
|
||||
function dispatch({ type: topic, subject }) {
|
||||
emit(channel, "data", {
|
||||
topic: topic,
|
||||
type: types[topic],
|
||||
target: nsIDOMWindow(subject)
|
||||
});
|
||||
}
|
||||
|
||||
// In addition to observing windows that are open we also observe windows
|
||||
// that are already already opened in case they're in process of loading.
|
||||
let opened = windows(null, { includePrivate: true });
|
||||
opened.forEach(observe);
|
||||
|
||||
// Register system event listeners to forward messages on exported event
|
||||
// stream. Note that by default only weak refs are kept by system events
|
||||
// module so they will be GC-ed once add-on unloads and no manual cleanup
|
||||
// is required. Also note that listeners are intentionally not inlined since
|
||||
// to avoid premature GC-ing. Currently refs are kept by module scope and there
|
||||
// for they remain alive.
|
||||
events.on("domwindowopened", onOpen);
|
||||
events.on("domwindowclosed", dispatch);
|
@ -275,6 +275,15 @@ function windows(type, options) {
|
||||
}
|
||||
exports.windows = windows;
|
||||
|
||||
/**
|
||||
* Check if the given window is interactive.
|
||||
* i.e. if its "DOMContentLoaded" event has already been fired.
|
||||
* @params {nsIDOMWindow} window
|
||||
*/
|
||||
function isInteractive(window)
|
||||
window.document.readyState === "interactive" || isDocumentLoaded(window)
|
||||
exports.isInteractive = isInteractive;
|
||||
|
||||
/**
|
||||
* Check if the given window is completely loaded.
|
||||
* i.e. if its "load" event has already been fired and all possible DOM content
|
||||
|
@ -52,6 +52,8 @@ const WindowTabTracker = Trait.compose({
|
||||
|
||||
// Binding all methods used as event listeners to the instance.
|
||||
this._onTabReady = this._emitEvent.bind(this, "ready");
|
||||
this._onTabLoad = this._emitEvent.bind(this, "load");
|
||||
this._onTabPageShow = this._emitEvent.bind(this, "pageshow");
|
||||
this._onTabOpen = this._onTabEvent.bind(this, "open");
|
||||
this._onTabClose = this._onTabEvent.bind(this, "close");
|
||||
this._onTabActivate = this._onTabEvent.bind(this, "activate");
|
||||
@ -109,17 +111,23 @@ const WindowTabTracker = Trait.compose({
|
||||
return;
|
||||
|
||||
// Setting up an event listener for ready events.
|
||||
if (type === "open")
|
||||
if (type === "open") {
|
||||
wrappedTab.on("ready", this._onTabReady);
|
||||
wrappedTab.on("load", this._onTabLoad);
|
||||
wrappedTab.on("pageshow", this._onTabPageShow);
|
||||
}
|
||||
|
||||
this._emitEvent(type, wrappedTab);
|
||||
}
|
||||
},
|
||||
_emitEvent: function _emitEvent(type, tab) {
|
||||
_emitEvent: function _emitEvent(type, tag) {
|
||||
// Slices additional arguments and passes them into exposed
|
||||
// listener like other events (for pageshow)
|
||||
let args = Array.slice(arguments);
|
||||
// Notifies combined tab list that tab was added / removed.
|
||||
tabs._emit(type, tab);
|
||||
tabs._emit.apply(tabs, args);
|
||||
// Notifies contained tab list that window was added / removed.
|
||||
this._tabs._emit(type, tab);
|
||||
this._tabs._emit.apply(this._tabs, args);
|
||||
}
|
||||
});
|
||||
exports.WindowTabTracker = WindowTabTracker;
|
||||
|
59
addon-sdk/source/test/event/helpers.js
Normal file
59
addon-sdk/source/test/event/helpers.js
Normal file
@ -0,0 +1,59 @@
|
||||
/* 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 { on, once, off, emit, count } = require("sdk/event/core");
|
||||
|
||||
function scenario(setup) {
|
||||
return function(unit) {
|
||||
return function(assert) {
|
||||
let actual = [];
|
||||
let input = {};
|
||||
unit(input, function(output, events, expected, message) {
|
||||
let result = setup(output, expected, actual);
|
||||
|
||||
events.forEach(function(event) emit(input, "data", event));
|
||||
|
||||
assert.deepEqual(actual, result, message);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exports.emits = scenario(function(output, expected, actual) {
|
||||
on(output, "data", function(data) actual.push(this, data));
|
||||
|
||||
return expected.reduce(function($$, $) $$.concat(output, $), []);
|
||||
});
|
||||
|
||||
exports.registerOnce = scenario(function(output, expected, actual) {
|
||||
function listener(data) actual.push(data);
|
||||
on(output, "data", listener);
|
||||
on(output, "data", listener);
|
||||
on(output, "data", listener);
|
||||
|
||||
return expected;
|
||||
});
|
||||
|
||||
exports.ignoreNew = scenario(function(output, expected, actual) {
|
||||
on(output, "data", function(data) {
|
||||
actual.push(data + "#1");
|
||||
on(output, "data", function(data) {
|
||||
actual.push(data + "#2");
|
||||
});
|
||||
});
|
||||
|
||||
return expected.map(function($) $ + "#1");
|
||||
});
|
||||
|
||||
exports.FIFO = scenario(function(target, expected, actual) {
|
||||
on(target, "data", function($) actual.push($ + "#1"));
|
||||
on(target, "data", function($) actual.push($ + "#2"));
|
||||
on(target, "data", function($) actual.push($ + "#3"));
|
||||
|
||||
return expected.reduce(function(result, value) {
|
||||
return result.concat(value + "#1", value + "#2", value + "#3");
|
||||
}, []);
|
||||
});
|
@ -8,6 +8,11 @@ const { Loader } = require('sdk/test/loader');
|
||||
const timer = require('sdk/timers');
|
||||
const { StringBundle } = require('sdk/deprecated/app-strings');
|
||||
|
||||
const base64png = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYA" +
|
||||
"AABzenr0AAAASUlEQVRYhe3O0QkAIAwD0eyqe3Q993AQ3cBSUKpygfsNTy" +
|
||||
"N5ugbQpK0BAADgP0BRDWXWlwEAAAAAgPsA3rzDaAAAAHgPcGrpgAnzQ2FG" +
|
||||
"bWRR9AAAAABJRU5ErkJggg%3D%3D";
|
||||
|
||||
// TEST: tabs.activeTab getter
|
||||
exports.testActiveTab_getter = function(test) {
|
||||
test.waitUntilDone();
|
||||
@ -178,10 +183,26 @@ exports.testTabProperties = function(test) {
|
||||
test.assertEqual(tab.index, 1, "index of the new tab matches");
|
||||
test.assertNotEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches");
|
||||
test.assertNotEqual(tab.id, null, "a tab object always has an id property.");
|
||||
closeBrowserWindow(window, function() test.done());
|
||||
onReadyOrLoad(window);
|
||||
},
|
||||
onLoad: function(tab) {
|
||||
test.assertEqual(tab.title, "foo", "title of the new tab matches");
|
||||
test.assertEqual(tab.url, url, "URL of the new tab matches");
|
||||
test.assert(tab.favicon, "favicon of the new tab is not empty");
|
||||
test.assertEqual(tab.style, null, "style of the new tab matches");
|
||||
test.assertEqual(tab.index, 1, "index of the new tab matches");
|
||||
test.assertNotEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches");
|
||||
test.assertNotEqual(tab.id, null, "a tab object always has an id property.");
|
||||
onReadyOrLoad(window);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
let count = 0;
|
||||
function onReadyOrLoad (window) {
|
||||
if (count++)
|
||||
closeBrowserWindow(window, function() test.done());
|
||||
}
|
||||
};
|
||||
|
||||
// TEST: tab properties
|
||||
@ -961,6 +982,113 @@ exports['test unique tab ids'] = function(test) {
|
||||
next(0);
|
||||
}
|
||||
|
||||
// related to Bug 671305
|
||||
exports.testOnLoadEventWithDOM = function(test) {
|
||||
test.waitUntilDone();
|
||||
|
||||
openBrowserWindow(function(window, browser) {
|
||||
let tabs = require('sdk/tabs');
|
||||
let count = 0;
|
||||
tabs.on('load', function onLoad(tab) {
|
||||
test.assertEqual(tab.title, 'tab', 'tab passed in as arg, load called');
|
||||
if (!count++) {
|
||||
tab.reload();
|
||||
}
|
||||
else {
|
||||
// end of test
|
||||
tabs.removeListener('load', onLoad);
|
||||
test.pass('onLoad event called on reload');
|
||||
closeBrowserWindow(window, function() test.done());
|
||||
}
|
||||
});
|
||||
|
||||
// open a about: url
|
||||
tabs.open({
|
||||
url: 'data:text/html;charset=utf-8,<title>tab</title>',
|
||||
inBackground: true
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// related to Bug 671305
|
||||
exports.testOnLoadEventWithImage = function(test) {
|
||||
test.waitUntilDone();
|
||||
|
||||
openBrowserWindow(function(window, browser) {
|
||||
let tabs = require('sdk/tabs');
|
||||
let count = 0;
|
||||
tabs.on('load', function onLoad(tab) {
|
||||
if (!count++) {
|
||||
tab.reload();
|
||||
}
|
||||
else {
|
||||
// end of test
|
||||
tabs.removeListener('load', onLoad);
|
||||
test.pass('onLoad event called on reload with image');
|
||||
closeBrowserWindow(window, function() test.done());
|
||||
}
|
||||
});
|
||||
|
||||
// open a image url
|
||||
tabs.open({
|
||||
url: base64png,
|
||||
inBackground: true
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
exports.testOnPageShowEvent = function (test) {
|
||||
test.waitUntilDone();
|
||||
|
||||
let firstUrl = 'about:home';
|
||||
let secondUrl = 'about:newtab';
|
||||
|
||||
openBrowserWindow(function(window, browser) {
|
||||
let tabs = require('sdk/tabs');
|
||||
|
||||
let wait = 500;
|
||||
let counter = 1;
|
||||
tabs.on('pageshow', function setup(tab, persisted) {
|
||||
if (counter === 1)
|
||||
test.assert(!persisted, 'page should not be cached on initial load');
|
||||
|
||||
if (wait > 5000) {
|
||||
test.fail('Page was not cached after 5s')
|
||||
closeBrowserWindow(window, function() test.done());
|
||||
}
|
||||
|
||||
if (tab.url === firstUrl) {
|
||||
// If first page has persisted, pass
|
||||
if (persisted) {
|
||||
tabs.removeListener('pageshow', setup);
|
||||
test.pass('pageshow event called on history.back()');
|
||||
closeBrowserWindow(window, function() test.done());
|
||||
}
|
||||
// On the first run, or if the page wasn't cached
|
||||
// the first time due to not waiting long enough,
|
||||
// try again with a longer delay (this is terrible
|
||||
// and ugly)
|
||||
else {
|
||||
counter++;
|
||||
timer.setTimeout(function () {
|
||||
tab.url = secondUrl;
|
||||
wait *= 2;
|
||||
}, wait);
|
||||
}
|
||||
}
|
||||
else {
|
||||
tab.attach({
|
||||
contentScript: 'setTimeout(function () { window.history.back(); }, 0)'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
tabs.open({
|
||||
url: firstUrl
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/******************* helpers *********************/
|
||||
|
||||
// Helper for getting the active window
|
||||
|
105
addon-sdk/source/test/test-browser-events.js
Normal file
105
addon-sdk/source/test/test-browser-events.js
Normal file
@ -0,0 +1,105 @@
|
||||
/* 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 { Loader } = require("sdk/test/loader");
|
||||
const { open, getMostRecentBrowserWindow, getOuterId } = require("sdk/window/utils");
|
||||
const { setTimeout } = require("sdk/timers");
|
||||
|
||||
exports["test browser events"] = function(assert, done) {
|
||||
let loader = Loader(module);
|
||||
let { events } = loader.require("sdk/browser/events");
|
||||
let { on, off } = loader.require("sdk/event/core");
|
||||
let actual = [];
|
||||
|
||||
on(events, "data", function handler(e) {
|
||||
actual.push(e);
|
||||
if (e.type === "load") window.close();
|
||||
if (e.type === "close") {
|
||||
// Unload the module so that all listeners set by observer are removed.
|
||||
|
||||
let [ ready, load, close ] = actual;
|
||||
|
||||
assert.equal(ready.type, "DOMContentLoaded");
|
||||
assert.equal(ready.target, window, "window ready");
|
||||
|
||||
assert.equal(load.type, "load");
|
||||
assert.equal(load.target, window, "window load");
|
||||
|
||||
assert.equal(close.type, "close");
|
||||
assert.equal(close.target, window, "window load");
|
||||
|
||||
// Note: If window is closed right after this GC won't have time
|
||||
// to claim loader and there for this listener, there for it's safer
|
||||
// to remove listener.
|
||||
off(events, "data", handler);
|
||||
loader.unload();
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
// Open window and close it to trigger observers.
|
||||
let window = open();
|
||||
};
|
||||
|
||||
exports["test browser events ignore other wins"] = function(assert, done) {
|
||||
let loader = Loader(module);
|
||||
let { events: windowEvents } = loader.require("sdk/window/events");
|
||||
let { events: browserEvents } = loader.require("sdk/browser/events");
|
||||
let { on, off } = loader.require("sdk/event/core");
|
||||
let actualBrowser = [];
|
||||
let actualWindow = [];
|
||||
|
||||
function browserEventHandler(e) actualBrowser.push(e)
|
||||
on(browserEvents, "data", browserEventHandler);
|
||||
on(windowEvents, "data", function handler(e) {
|
||||
actualWindow.push(e);
|
||||
// Delay close so that if "load" is also emitted on `browserEvents`
|
||||
// `browserEventHandler` will be invoked.
|
||||
if (e.type === "load") setTimeout(window.close);
|
||||
if (e.type === "close") {
|
||||
assert.deepEqual(actualBrowser, [], "browser events were not triggered");
|
||||
let [ open, ready, load, close ] = actualWindow;
|
||||
|
||||
assert.equal(open.type, "open");
|
||||
assert.equal(open.target, window, "window is open");
|
||||
|
||||
|
||||
|
||||
assert.equal(ready.type, "DOMContentLoaded");
|
||||
assert.equal(ready.target, window, "window ready");
|
||||
|
||||
assert.equal(load.type, "load");
|
||||
assert.equal(load.target, window, "window load");
|
||||
|
||||
assert.equal(close.type, "close");
|
||||
assert.equal(close.target, window, "window load");
|
||||
|
||||
|
||||
// Note: If window is closed right after this GC won't have time
|
||||
// to claim loader and there for this listener, there for it's safer
|
||||
// to remove listener.
|
||||
off(windowEvents, "data", handler);
|
||||
off(browserEvents, "data", browserEventHandler);
|
||||
loader.unload();
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
// Open window and close it to trigger observers.
|
||||
let window = open("data:text/html,not a browser");
|
||||
};
|
||||
|
||||
if (require("sdk/system/xul-app").is("Fennec")) {
|
||||
module.exports = {
|
||||
"test Unsupported Test": function UnsupportedTest (assert) {
|
||||
assert.pass(
|
||||
"Skipping this test until Fennec support is implemented." +
|
||||
"See bug 793071");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
require("test").run(exports);
|
@ -68,6 +68,19 @@ exports['test no side-effects in emit'] = function(assert) {
|
||||
emit(target, 'message');
|
||||
};
|
||||
|
||||
exports['test can remove next listener'] = function(assert) {
|
||||
let target = { name: 'target' };
|
||||
function fail() assert.fail('Listener should be removed');
|
||||
|
||||
on(target, 'data', function() {
|
||||
assert.pass('first litener called');
|
||||
off(target, 'data', fail);
|
||||
});
|
||||
on(target, 'data', fail);
|
||||
|
||||
emit(target, 'data', 'hello');
|
||||
};
|
||||
|
||||
exports['test order of propagation'] = function(assert) {
|
||||
let actual = [];
|
||||
let target = { name: 'target' };
|
||||
|
@ -115,7 +115,7 @@ exports['test remove a listener'] = function(assert) {
|
||||
})
|
||||
});
|
||||
|
||||
target.removeListener('message'); // must do nothing.
|
||||
target.off('message'); // must do nothing.
|
||||
emit(target, 'message');
|
||||
assert.deepEqual([ 1 ], actual, 'first listener called');
|
||||
emit(target, 'message');
|
||||
|
169
addon-sdk/source/test/test-event-utils.js
Normal file
169
addon-sdk/source/test/test-event-utils.js
Normal file
@ -0,0 +1,169 @@
|
||||
/* 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 { on, emit } = require("sdk/event/core");
|
||||
const { filter, map, merge, expand } = require("sdk/event/utils");
|
||||
const $ = require("./event/helpers");
|
||||
|
||||
function isEven(x) !(x % 2)
|
||||
function inc(x) x + 1
|
||||
|
||||
exports["test filter events"] = function(assert) {
|
||||
let input = {};
|
||||
let evens = filter(isEven, input);
|
||||
let actual = [];
|
||||
on(evens, "data", function(e) actual.push(e));
|
||||
|
||||
[1, 2, 3, 4, 5, 6, 7].forEach(function(x) emit(input, "data", x));
|
||||
|
||||
assert.deepEqual(actual, [2, 4, 6], "only even numbers passed through");
|
||||
};
|
||||
|
||||
exports["test filter emits"] = $.emits(function(input, assert) {
|
||||
let output = filter(isEven, input);
|
||||
assert(output, [1, 2, 3, 4, 5], [2, 4], "this is `output` & evens passed");
|
||||
});;
|
||||
|
||||
exports["test filter reg once"] = $.registerOnce(function(input, assert) {
|
||||
assert(filter(isEven, input), [1, 2, 3, 4, 5, 6], [2, 4, 6],
|
||||
"listener can be registered only once");
|
||||
});
|
||||
|
||||
exports["test filter ignores new"] = $.ignoreNew(function(input, assert) {
|
||||
assert(filter(isEven, input), [1, 2, 3], [2],
|
||||
"new listener is ignored")
|
||||
});
|
||||
|
||||
exports["test filter is FIFO"] = $.FIFO(function(input, assert) {
|
||||
assert(filter(isEven, input), [1, 2, 3, 4], [2, 4],
|
||||
"listeners are invoked in fifo order")
|
||||
});
|
||||
|
||||
exports["test map events"] = function(assert) {
|
||||
let input = {};
|
||||
let incs = map(inc, input);
|
||||
let actual = [];
|
||||
on(incs, "data", function(e) actual.push(e));
|
||||
|
||||
[1, 2, 3, 4].forEach(function(x) emit(input, "data", x));
|
||||
|
||||
assert.deepEqual(actual, [2, 3, 4, 5], "all numbers were incremented");
|
||||
};
|
||||
|
||||
exports["test map emits"] = $.emits(function(input, assert) {
|
||||
let output = map(inc, input);
|
||||
assert(output, [1, 2, 3], [2, 3, 4], "this is `output` & evens passed");
|
||||
});;
|
||||
|
||||
exports["test map reg once"] = $.registerOnce(function(input, assert) {
|
||||
assert(map(inc, input), [1, 2, 3], [2, 3, 4],
|
||||
"listener can be registered only once");
|
||||
});
|
||||
|
||||
exports["test map ignores new"] = $.ignoreNew(function(input, assert) {
|
||||
assert(map(inc, input), [1], [2],
|
||||
"new listener is ignored")
|
||||
});
|
||||
|
||||
exports["test map is FIFO"] = $.FIFO(function(input, assert) {
|
||||
assert(map(inc, input), [1, 2, 3, 4], [2, 3, 4, 5],
|
||||
"listeners are invoked in fifo order")
|
||||
});
|
||||
|
||||
exports["test merge stream[stream]"] = function(assert) {
|
||||
let a = {}, b = {}, c = {};
|
||||
let inputs = {};
|
||||
let actual = [];
|
||||
|
||||
on(merge(inputs), "data", function($) actual.push($))
|
||||
|
||||
emit(inputs, "data", a);
|
||||
emit(a, "data", "a1");
|
||||
emit(inputs, "data", b);
|
||||
emit(b, "data", "b1");
|
||||
emit(a, "data", "a2");
|
||||
emit(inputs, "data", c);
|
||||
emit(c, "data", "c1");
|
||||
emit(c, "data", "c2");
|
||||
emit(b, "data", "b2");
|
||||
emit(a, "data", "a3");
|
||||
|
||||
assert.deepEqual(actual, ["a1", "b1", "a2", "c1", "c2", "b2", "a3"],
|
||||
"all inputs data merged into one");
|
||||
};
|
||||
|
||||
exports["test merge array[stream]"] = function(assert) {
|
||||
let a = {}, b = {}, c = {};
|
||||
let inputs = {};
|
||||
let actual = [];
|
||||
|
||||
on(merge([a, b, c]), "data", function($) actual.push($))
|
||||
|
||||
emit(a, "data", "a1");
|
||||
emit(b, "data", "b1");
|
||||
emit(a, "data", "a2");
|
||||
emit(c, "data", "c1");
|
||||
emit(c, "data", "c2");
|
||||
emit(b, "data", "b2");
|
||||
emit(a, "data", "a3");
|
||||
|
||||
assert.deepEqual(actual, ["a1", "b1", "a2", "c1", "c2", "b2", "a3"],
|
||||
"all inputs data merged into one");
|
||||
};
|
||||
|
||||
exports["test merge emits"] = $.emits(function(input, assert) {
|
||||
let evens = filter(isEven, input)
|
||||
let output = merge([evens, input]);
|
||||
assert(output, [1, 2, 3], [1, 2, 2, 3], "this is `output` & evens passed");
|
||||
});
|
||||
|
||||
|
||||
exports["test merge reg once"] = $.registerOnce(function(input, assert) {
|
||||
let evens = filter(isEven, input)
|
||||
let output = merge([input, evens]);
|
||||
assert(output, [1, 2, 3, 4], [1, 2, 2, 3, 4, 4],
|
||||
"listener can be registered only once");
|
||||
});
|
||||
|
||||
exports["test merge ignores new"] = $.ignoreNew(function(input, assert) {
|
||||
let evens = filter(isEven, input)
|
||||
let output = merge([input, evens])
|
||||
assert(output, [1], [1],
|
||||
"new listener is ignored")
|
||||
});
|
||||
|
||||
exports["test marge is FIFO"] = $.FIFO(function(input, assert) {
|
||||
let evens = filter(isEven, input)
|
||||
let output = merge([input, evens])
|
||||
|
||||
assert(output, [1, 2, 3, 4], [1, 2, 2, 3, 4, 4],
|
||||
"listeners are invoked in fifo order")
|
||||
});
|
||||
|
||||
exports["test expand"] = function(assert) {
|
||||
let a = {}, b = {}, c = {};
|
||||
let inputs = {};
|
||||
let actual = [];
|
||||
|
||||
on(expand(function($) $(), inputs), "data", function($) actual.push($))
|
||||
|
||||
emit(inputs, "data", function() a);
|
||||
emit(a, "data", "a1");
|
||||
emit(inputs, "data", function() b);
|
||||
emit(b, "data", "b1");
|
||||
emit(a, "data", "a2");
|
||||
emit(inputs, "data", function() c);
|
||||
emit(c, "data", "c1");
|
||||
emit(c, "data", "c2");
|
||||
emit(b, "data", "b2");
|
||||
emit(a, "data", "a3");
|
||||
|
||||
assert.deepEqual(actual, ["a1", "b1", "a2", "c1", "c2", "b2", "a3"],
|
||||
"all inputs data merged into one");
|
||||
}
|
||||
|
||||
|
||||
require('test').run(exports);
|
@ -9,7 +9,7 @@ if (xulApp.versionInRange(xulApp.platformVersion, "16.0a1", "*")) {
|
||||
new function tests() {
|
||||
|
||||
const { indexedDB, IDBKeyRange, DOMException, IDBCursor, IDBTransaction,
|
||||
IDBOpenDBRequest, IDBVersionChangeEvent, IDBDatabase, IDBIndex,
|
||||
IDBOpenDBRequest, IDBVersionChangeEvent, IDBDatabase, IDBIndex,
|
||||
IDBObjectStore, IDBRequest
|
||||
} = require("sdk/indexed-db");
|
||||
|
||||
|
@ -30,7 +30,7 @@ exports.testOptionsValidator = function(test) {
|
||||
Request({
|
||||
url: null
|
||||
});
|
||||
}, 'The option "url" must be one of the following types: string');
|
||||
}, 'The option "url" is invalid.');
|
||||
|
||||
// Next we'll have a Request that doesn't throw from c'tor, but from a setter.
|
||||
let req = Request({
|
||||
@ -38,8 +38,8 @@ exports.testOptionsValidator = function(test) {
|
||||
onComplete: function () {}
|
||||
});
|
||||
test.assertRaises(function () {
|
||||
req.url = null;
|
||||
}, 'The option "url" must be one of the following types: string');
|
||||
req.url = 'www.mozilla.org';
|
||||
}, 'The option "url" is invalid.');
|
||||
// The url shouldn't have changed, so check that
|
||||
test.assertEqual(req.url, "http://playground.zpao.com/jetpack/request/text.php");
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
const {Cc, Ci, Cu, Cm, components} = require('chrome');
|
||||
Cu.import("resource://gre/modules/AddonManager.jsm", this);
|
||||
const xulApp = require("sdk/system/xul-app");
|
||||
|
||||
exports.testSelf = function(test) {
|
||||
var self = require("sdk/self");
|
||||
@ -30,8 +31,12 @@ exports.testSelf = function(test) {
|
||||
test.assert(self.name == "addon-sdk", "self.name is addon-sdk");
|
||||
|
||||
// loadReason may change here, as we change the way tests addons are installed
|
||||
test.assertEqual(self.loadReason, "startup",
|
||||
"self.loadReason is always `startup` on test runs");
|
||||
// Bug 854937 fixed loadReason and is now install
|
||||
let testLoadReason = xulApp.versionInRange(xulApp.platformVersion,
|
||||
"23.0a1", "*") ? "install"
|
||||
: "startup";
|
||||
test.assertEqual(self.loadReason, testLoadReason,
|
||||
"self.loadReason is either startup or install on test runs");
|
||||
|
||||
test.assertEqual(self.isPrivateBrowsingSupported, false,
|
||||
'usePrivateBrowsing property is false by default');
|
||||
|
175
addon-sdk/source/test/test-tab-events.js
Normal file
175
addon-sdk/source/test/test-tab-events.js
Normal file
@ -0,0 +1,175 @@
|
||||
/* 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 { Loader } = require("sdk/test/loader");
|
||||
const utils = require("sdk/tabs/utils");
|
||||
const { open, close } = require("sdk/window/helpers");
|
||||
const { getMostRecentBrowserWindow } = require("sdk/window/utils");
|
||||
const { events } = require("sdk/tab/events");
|
||||
const { on, off } = require("sdk/event/core");
|
||||
const { resolve } = require("sdk/core/promise");
|
||||
|
||||
let isFennec = require("sdk/system/xul-app").is("Fennec");
|
||||
|
||||
function test(scenario, currentWindow) {
|
||||
let useActiveWindow = isFennec || currentWindow;
|
||||
return function(assert, done) {
|
||||
let actual = [];
|
||||
function handler(event) actual.push(event)
|
||||
|
||||
let win = useActiveWindow ? resolve(getMostRecentBrowserWindow()) :
|
||||
open(null, {
|
||||
features: { private: true, toolbar:true, chrome: true }
|
||||
});
|
||||
let window = null;
|
||||
|
||||
win.then(function(w) {
|
||||
window = w;
|
||||
on(events, "data", handler);
|
||||
return scenario(assert, window, actual);
|
||||
}).then(function() {
|
||||
off(events, "data", handler);
|
||||
return useActiveWindow ? null : close(window);
|
||||
}).then(done, assert.fail);
|
||||
}
|
||||
}
|
||||
|
||||
exports["test current window"] = test(function(assert, window, events) {
|
||||
// Just making sure that tab events work for already opened tabs not only
|
||||
// for new windows.
|
||||
let tab = utils.openTab(window, 'data:text/plain,open');
|
||||
utils.closeTab(tab);
|
||||
|
||||
let [open, select, close] = events;
|
||||
|
||||
assert.equal(open.type, "TabOpen");
|
||||
assert.equal(open.target, tab);
|
||||
|
||||
assert.equal(select.type, "TabSelect");
|
||||
assert.equal(select.target, tab);
|
||||
|
||||
assert.equal(close.type, "TabClose");
|
||||
assert.equal(close.target, tab);
|
||||
});
|
||||
|
||||
exports["test open"] = test(function(assert, window, events) {
|
||||
let tab = utils.openTab(window, 'data:text/plain,open');
|
||||
let [open, select] = events;
|
||||
|
||||
assert.equal(open.type, "TabOpen");
|
||||
assert.equal(open.target, tab);
|
||||
|
||||
assert.equal(select.type, "TabSelect");
|
||||
assert.equal(select.target, tab);
|
||||
});
|
||||
|
||||
exports["test open -> close"] = test(function(assert, window, events) {
|
||||
// First tab is useless we just open it so that closing second tab won't
|
||||
// close window on some platforms.
|
||||
let _ = utils.openTab(window, 'daat:text/plain,ignore');
|
||||
let tab = utils.openTab(window, 'data:text/plain,open-close');
|
||||
utils.closeTab(tab);
|
||||
|
||||
let [_open, _select, open, select, close] = events;
|
||||
|
||||
assert.equal(open.type, "TabOpen");
|
||||
assert.equal(open.target, tab);
|
||||
|
||||
assert.equal(select.type, "TabSelect");
|
||||
assert.equal(select.target, tab);
|
||||
|
||||
assert.equal(close.type, "TabClose");
|
||||
assert.equal(close.target, tab);
|
||||
});
|
||||
|
||||
exports["test open -> open -> select"] = test(function(assert, window, events) {
|
||||
let tab1 = utils.openTab(window, 'data:text/plain,Tab-1');
|
||||
let tab2 = utils.openTab(window, 'data:text/plain,Tab-2');
|
||||
utils.activateTab(tab1, window);
|
||||
|
||||
let [open1, select1, open2, select2, select3] = events;
|
||||
|
||||
// Open first tab
|
||||
assert.equal(open1.type, "TabOpen", "first tab opened")
|
||||
assert.equal(open1.target, tab1, "event.target is first tab")
|
||||
|
||||
assert.equal(select1.type, "TabSelect", "first tab seleceted")
|
||||
assert.equal(select1.target, tab1, "event.target is first tab")
|
||||
|
||||
|
||||
// Open second tab
|
||||
assert.equal(open2.type, "TabOpen", "second tab opened");
|
||||
assert.equal(open2.target, tab2, "event.target is second tab");
|
||||
|
||||
assert.equal(select2.type, "TabSelect", "second tab seleceted");
|
||||
assert.equal(select2.target, tab2, "event.target is second tab");
|
||||
|
||||
// Select first tab
|
||||
assert.equal(select3.type, "TabSelect", "tab seleceted");
|
||||
assert.equal(select3.target, tab1, "event.target is first tab");
|
||||
});
|
||||
|
||||
exports["test open -> pin -> unpin"] = test(function(assert, window, events) {
|
||||
let tab = utils.openTab(window, 'data:text/plain,pin-unpin');
|
||||
utils.pin(tab);
|
||||
utils.unpin(tab);
|
||||
|
||||
let [open, select, move, pin, unpin] = events;
|
||||
|
||||
assert.equal(open.type, "TabOpen");
|
||||
assert.equal(open.target, tab);
|
||||
|
||||
assert.equal(select.type, "TabSelect");
|
||||
assert.equal(select.target, tab);
|
||||
|
||||
if (isFennec) {
|
||||
assert.pass("Tab pin / unpin is not supported by Fennec");
|
||||
}
|
||||
else {
|
||||
assert.equal(move.type, "TabMove");
|
||||
assert.equal(move.target, tab);
|
||||
|
||||
assert.equal(pin.type, "TabPinned");
|
||||
assert.equal(pin.target, tab);
|
||||
|
||||
assert.equal(unpin.type, "TabUnpinned");
|
||||
assert.equal(unpin.target, tab);
|
||||
}
|
||||
});
|
||||
|
||||
exports["test open -> open -> move "] = test(function(assert, window, events) {
|
||||
let tab1 = utils.openTab(window, 'data:text/plain,Tab-1');
|
||||
let tab2 = utils.openTab(window, 'data:text/plain,Tab-2');
|
||||
utils.move(tab1, 2);
|
||||
|
||||
let [open1, select1, open2, select2, move] = events;
|
||||
|
||||
// Open first tab
|
||||
assert.equal(open1.type, "TabOpen", "first tab opened");
|
||||
assert.equal(open1.target, tab1, "event.target is first tab");
|
||||
|
||||
assert.equal(select1.type, "TabSelect", "first tab seleceted")
|
||||
assert.equal(select1.target, tab1, "event.target is first tab");
|
||||
|
||||
|
||||
// Open second tab
|
||||
assert.equal(open2.type, "TabOpen", "second tab opened");
|
||||
assert.equal(open2.target, tab2, "event.target is second tab");
|
||||
|
||||
assert.equal(select2.type, "TabSelect", "second tab seleceted");
|
||||
assert.equal(select2.target, tab2, "event.target is second tab");
|
||||
|
||||
if (isFennec) {
|
||||
assert.pass("Tab index changes not supported on Fennec yet")
|
||||
}
|
||||
else {
|
||||
// Move first tab
|
||||
assert.equal(move.type, "TabMove", "tab moved");
|
||||
assert.equal(move.target, tab1, "event.target is first tab");
|
||||
}
|
||||
});
|
||||
|
||||
require("test").run(exports);
|
@ -114,57 +114,48 @@ exports.testWaitUntilErrorInCallback = function(test) {
|
||||
}
|
||||
|
||||
exports.testWaitUntilTimeoutInCallback = function(test) {
|
||||
test.waitUntilDone(1000);
|
||||
test.waitUntilDone();
|
||||
|
||||
let expected = [];
|
||||
let message = 0;
|
||||
if (require("@test/options").parseable) {
|
||||
expected.push(["print", "TEST-START | wait4ever\n"]);
|
||||
expected.push(["error", "fail:", "Timed out"]);
|
||||
expected.push(["error", "test assertion never became true:\n", "assertion failed, value is false\n"]);
|
||||
expected.push(["print", "TEST-END | wait4ever\n"]);
|
||||
}
|
||||
else {
|
||||
expected.push(["info", "executing 'wait4ever'"]);
|
||||
expected.push(["error", "fail:", "Timed out"]);
|
||||
expected.push(["error", "test assertion never became true:\n", "assertion failed, value is false\n"]);
|
||||
}
|
||||
|
||||
function checkExpected(name, args) {
|
||||
if (expected.length == 0 || expected[0][0] != name) {
|
||||
test.fail("Saw an unexpected console." + name + "() call " + args);
|
||||
return;
|
||||
}
|
||||
|
||||
message++;
|
||||
let expectedArgs = expected.shift().slice(1);
|
||||
for (let i = 0; i < expectedArgs.length; i++)
|
||||
test.assertEqual(args[i], expectedArgs[i], "Should have seen the right message in argument " + i + " of message " + message);
|
||||
if (expected.length == 0)
|
||||
test.done();
|
||||
}
|
||||
|
||||
let runner = new (require("sdk/deprecated/unit-test").TestRunner)({
|
||||
console: {
|
||||
calls: 0,
|
||||
error: function(msg) {
|
||||
this.calls++;
|
||||
if (this.calls == 2) {
|
||||
test.assertEqual(arguments[0], "test assertion never became true:\n");
|
||||
test.assertEqual(arguments[1], "assertion failed, value is false\n");
|
||||
// We could additionally check that arguments[1] contains the correct
|
||||
// stack, but it would be difficult to do so given that it contains
|
||||
// resource: URLs with a randomly generated string embedded in them
|
||||
// (the ID of the test addon created to run the tests). And in any
|
||||
// case, checking the arguments seems sufficient.
|
||||
|
||||
test.done();
|
||||
}
|
||||
else {
|
||||
test.fail("We got unexpected console.error() calls from waitUntil" +
|
||||
" assertion callback: '" + arguments[1] + "'");
|
||||
}
|
||||
error: function() {
|
||||
checkExpected("error", Array.slice(arguments));
|
||||
},
|
||||
info: function (msg) {
|
||||
this.calls++;
|
||||
if (require("@test/options").parseable) {
|
||||
test.fail("We got unexpected console.info() calls: " + msg)
|
||||
}
|
||||
else if (this.calls == 1) {
|
||||
test.assertEqual(arguments[0], "executing 'wait4ever'");
|
||||
}
|
||||
else {
|
||||
test.fail("We got unexpected console.info() calls: " + msg);
|
||||
}
|
||||
info: function () {
|
||||
checkExpected("info", Array.slice(arguments));
|
||||
},
|
||||
trace: function () {},
|
||||
exception: function () {},
|
||||
print: function (str) {
|
||||
this.calls++;
|
||||
if (!require("@test/options").parseable) {
|
||||
test.fail("We got unexpected console.print() calls: " + str)
|
||||
}
|
||||
else if (this.calls == 1) {
|
||||
test.assertEqual(str, "TEST-START | wait4ever\n");
|
||||
}
|
||||
else if (this.calls == 3) {
|
||||
test.assertEqual(str, "TEST-END | wait4ever\n");
|
||||
}
|
||||
else {
|
||||
test.fail("We got unexpected console.print() calls: " + str);
|
||||
}
|
||||
print: function () {
|
||||
checkExpected("print", Array.slice(arguments));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -242,3 +242,107 @@ exports.testDataURLparseBase64 = function (test) {
|
||||
|
||||
test.assertEqual(dataURL.toString(), "data:text/plain;base64," + encodeURIComponent(b64text));
|
||||
}
|
||||
|
||||
exports.testIsValidURI = function (test) {
|
||||
validURIs().forEach(function (aUri) {
|
||||
test.assertEqual(url.isValidURI(aUri), true, aUri + ' is a valid URL');
|
||||
});
|
||||
};
|
||||
|
||||
exports.testIsInvalidURI = function (test) {
|
||||
invalidURIs().forEach(function (aUri) {
|
||||
test.assertEqual(url.isValidURI(aUri), false, aUri + ' is an invalid URL');
|
||||
});
|
||||
};
|
||||
|
||||
function validURIs() {
|
||||
return [
|
||||
'http://foo.com/blah_blah',
|
||||
'http://foo.com/blah_blah/',
|
||||
'http://foo.com/blah_blah_(wikipedia)',
|
||||
'http://foo.com/blah_blah_(wikipedia)_(again)',
|
||||
'http://www.example.com/wpstyle/?p=364',
|
||||
'https://www.example.com/foo/?bar=baz&inga=42&quux',
|
||||
'http://✪df.ws/123',
|
||||
'http://userid:password@example.com:8080',
|
||||
'http://userid:password@example.com:8080/',
|
||||
'http://userid@example.com',
|
||||
'http://userid@example.com/',
|
||||
'http://userid@example.com:8080',
|
||||
'http://userid@example.com:8080/',
|
||||
'http://userid:password@example.com',
|
||||
'http://userid:password@example.com/',
|
||||
'http://142.42.1.1/',
|
||||
'http://142.42.1.1:8080/',
|
||||
'http://➡.ws/䨹',
|
||||
'http://⌘.ws',
|
||||
'http://⌘.ws/',
|
||||
'http://foo.com/blah_(wikipedia)#cite-1',
|
||||
'http://foo.com/blah_(wikipedia)_blah#cite-1',
|
||||
'http://foo.com/unicode_(✪)_in_parens',
|
||||
'http://foo.com/(something)?after=parens',
|
||||
'http://☺.damowmow.com/',
|
||||
'http://code.google.com/events/#&product=browser',
|
||||
'http://j.mp',
|
||||
'ftp://foo.bar/baz',
|
||||
'http://foo.bar/?q=Test%20URL-encoded%20stuff',
|
||||
'http://مثال.إختبار',
|
||||
'http://例子.测试',
|
||||
'http://उदाहरण.परीक्षा',
|
||||
'http://-.~_!$&\'()*+,;=:%40:80%2f::::::@example.com',
|
||||
'http://1337.net',
|
||||
'http://a.b-c.de',
|
||||
'http://223.255.255.254',
|
||||
// Also want to validate data-uris, localhost
|
||||
'http://localhost:8432/some-file.js',
|
||||
'data:text/plain;base64,',
|
||||
'data:text/html;charset=US-ASCII,%3Ch1%3EHello!%3C%2Fh1%3E',
|
||||
'data:text/html;charset=utf-8,'
|
||||
];
|
||||
}
|
||||
|
||||
// Some invalidURIs are valid according to the regex used,
|
||||
// can be improved in the future, but better to pass some
|
||||
// invalid URLs than prevent valid URLs
|
||||
|
||||
function invalidURIs () {
|
||||
return [
|
||||
// 'http://',
|
||||
// 'http://.',
|
||||
// 'http://..',
|
||||
// 'http://../',
|
||||
// 'http://?',
|
||||
// 'http://??',
|
||||
// 'http://??/',
|
||||
// 'http://#',
|
||||
// 'http://##',
|
||||
// 'http://##/',
|
||||
// 'http://foo.bar?q=Spaces should be encoded',
|
||||
'not a url',
|
||||
'//',
|
||||
'//a',
|
||||
'///a',
|
||||
'///',
|
||||
// 'http:///a',
|
||||
'foo.com',
|
||||
'http:// shouldfail.com',
|
||||
':// should fail',
|
||||
// 'http://foo.bar/foo(bar)baz quux',
|
||||
// 'http://-error-.invalid/',
|
||||
// 'http://a.b--c.de/',
|
||||
// 'http://-a.b.co',
|
||||
// 'http://a.b-.co',
|
||||
// 'http://0.0.0.0',
|
||||
// 'http://10.1.1.0',
|
||||
// 'http://10.1.1.255',
|
||||
// 'http://224.1.1.1',
|
||||
// 'http://1.1.1.1.1',
|
||||
// 'http://123.123.123',
|
||||
// 'http://3628126748',
|
||||
// 'http://.www.foo.bar/',
|
||||
// 'http://www.foo.bar./',
|
||||
// 'http://.www.foo.bar./',
|
||||
// 'http://10.1.1.1',
|
||||
// 'http://10.1.1.254'
|
||||
];
|
||||
}
|
||||
|
56
addon-sdk/source/test/test-window-events.js
Normal file
56
addon-sdk/source/test/test-window-events.js
Normal file
@ -0,0 +1,56 @@
|
||||
/* 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 { Loader } = require("sdk/test/loader");
|
||||
const { open, getMostRecentBrowserWindow, getOuterId } = require("sdk/window/utils");
|
||||
|
||||
exports["test browser events"] = function(assert, done) {
|
||||
let loader = Loader(module);
|
||||
let { events } = loader.require("sdk/window/events");
|
||||
let { on, off } = loader.require("sdk/event/core");
|
||||
let actual = [];
|
||||
|
||||
on(events, "data", function handler(e) {
|
||||
actual.push(e);
|
||||
if (e.type === "load") window.close();
|
||||
if (e.type === "close") {
|
||||
let [ open, ready, load, close ] = actual;
|
||||
assert.equal(open.type, "open")
|
||||
assert.equal(open.target, window, "window is open")
|
||||
|
||||
assert.equal(ready.type, "DOMContentLoaded")
|
||||
assert.equal(ready.target, window, "window ready")
|
||||
|
||||
assert.equal(load.type, "load")
|
||||
assert.equal(load.target, window, "window load")
|
||||
|
||||
assert.equal(close.type, "close")
|
||||
assert.equal(close.target, window, "window load")
|
||||
|
||||
// Note: If window is closed right after this GC won't have time
|
||||
// to claim loader and there for this listener. It's better to remove
|
||||
// remove listener here to avoid race conditions.
|
||||
off(events, "data", handler);
|
||||
loader.unload();
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
// Open window and close it to trigger observers.
|
||||
let window = open();
|
||||
};
|
||||
|
||||
if (require("sdk/system/xul-app").is("Fennec")) {
|
||||
module.exports = {
|
||||
"test Unsupported Test": function UnsupportedTest (assert) {
|
||||
assert.pass(
|
||||
"Skipping this test until Fennec support is implemented." +
|
||||
"See bug 793071");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
require("test").run(exports);
|
Loading…
Reference in New Issue
Block a user