Bug 1146943: Uplift Add-on SDK. a=me

3394ad5d1d...af7133332a
This commit is contained in:
Dave Townsend 2015-03-23 15:24:56 -07:00
parent d9a130bd86
commit f37d6f0fd6
29 changed files with 505 additions and 153 deletions

View File

@ -354,7 +354,6 @@ function nukeModules() {
// the addon is unload.
unloadSandbox(cuddlefishSandbox.loaderSandbox);
unloadSandbox(cuddlefishSandbox.xulappSandbox);
// Bug 764840: We need to unload cuddlefish otherwise it will stay alive
// and keep a reference to this compartment.

View File

@ -3,8 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
var readParam = require("./node-scripts/utils").readParam;
var path = require("path");
var Promise = require("promise");
var Mocha = require("mocha");
var mocha = new Mocha({
ui: "bdd",
@ -12,16 +11,19 @@ var mocha = new Mocha({
timeout: 900000
});
var type = readParam("type");
exports.run = function(type) {
return new Promise(function(resolve) {
type = type || "";
[
(/^(modules)?$/.test(type)) && require.resolve("../bin/node-scripts/test.modules"),
(/^(addons)?$/.test(type)) && require.resolve("../bin/node-scripts/test.addons"),
(/^(examples)?$/.test(type)) && require.resolve("../bin/node-scripts/test.examples"),
].sort().forEach(function(filepath) {
filepath && mocha.addFile(filepath);
})
[
(!type || type == "modules") && require.resolve("../bin/node-scripts/test.modules"),
(!type || type == "addons") && require.resolve("../bin/node-scripts/test.addons"),
(!type || type == "examples") && require.resolve("../bin/node-scripts/test.examples"),
].sort().forEach(function(filepath) {
filepath && mocha.addFile(filepath);
})
mocha.run(function (failures) {
process.exit(failures);
});
mocha.run(function(failures) {
resolve(failures);
});
});
}

View File

@ -202,7 +202,7 @@ var Connection = Class({
},
poolFor: function(id) {
for (let pool of this.pools.values()) {
if (pool.has(id))
if pool.has(id)
return pool;
}
},
@ -797,7 +797,7 @@ var Tab = Client.from({
"storageActor": "storage",
"gcliActor": "gcli",
"memoryActor": "memory",
"eventLoopLag": "eventLoopLag",
"eventLoopLag": "eventLoopLag"
"trace": "trace", // missing
}

View File

@ -0,0 +1,23 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
var gulp = require('gulp');
gulp.task('test', function(done) {
require("./bin/jpm-test").run().then(done);
});
gulp.task('test:addons', function(done) {
require("./bin/jpm-test").run("addons").then(done);
});
gulp.task('test:examples', function(done) {
require("./bin/jpm-test").run("examples").then(done);
});
gulp.task('test:modules', function(done) {
require("./bin/jpm-test").run("modules").then(done);
});

View File

@ -16,6 +16,31 @@ const assetsURI = require('../self').data.url();
const hideSheetUri = "data:text/css,:root {visibility: hidden !important;}";
function translateElementAttributes(element) {
// Translateable attributes
const attrList = ['title', 'accesskey', 'alt', 'label', 'placeholder'];
const ariaAttrMap = {
'ariaLabel': 'aria-label',
'ariaValueText': 'aria-valuetext',
'ariaMozHint': 'aria-moz-hint'
};
const attrSeparator = '.';
// Try to translate each of the attributes
for (let attribute of attrList) {
const data = core.get(element.dataset.l10nId + attrSeparator + attribute);
if (data)
element.setAttribute(attribute, data);
}
// Look for the aria attribute translations that match fxOS's aliases
for (let attrAlias in ariaAttrMap) {
const data = core.get(element.dataset.l10nId + attrSeparator + attrAlias);
if (data)
element.setAttribute(ariaAttrMap[attrAlias], data);
}
}
// Taken from Gaia:
// https://github.com/andreasgal/gaia/blob/04fde2640a7f40314643016a5a6c98bf3755f5fd/webapi.js#L1470
function translateElement(element) {
@ -32,6 +57,8 @@ function translateElement(element) {
var data = core.get(key);
if (data)
child.textContent = data;
translateElementAttributes(child);
}
}
exports.translateElement = translateElement;

View File

@ -320,6 +320,8 @@ TestRunner.prototype = {
});
PromiseDebugging.flushUncaughtErrors();
PromiseDebugging.removeUncaughtErrorObserver(this._uncaughtErrorObserver);
return all(winPromises).then(() => {
let browserWins = wins.filter(isBrowser);
@ -537,7 +539,8 @@ TestRunner.prototype = {
this.test.errors = {};
this.test.last = 'START';
PromiseDebugging.clearUncaughtErrorObservers();
PromiseDebugging.addUncaughtErrorObserver(this._uncaughtErrorObserver.bind(this));
this._uncaughtErrorObserver = this._uncaughtErrorObserver.bind(this);
PromiseDebugging.addUncaughtErrorObserver(this._uncaughtErrorObserver);
this.isDone = false;
this.onDone = function(self) {

View File

@ -21,6 +21,10 @@ let getWindowFrom = x =>
null;
function removeFromListeners() {
this.removeEventListener("DOMWindowClose", removeFromListeners);
for (let cleaner of listeners.get(this))
cleaner();
listeners.delete(this);
}
@ -45,26 +49,25 @@ function open(target, type, options) {
if (!window)
throw new Error("Unable to obtain the owner window from the target given.");
let cleaners = listeners.get(window) || [];
let cleaners = listeners.get(window);
if (!cleaners) {
cleaners = [];
listeners.set(window, cleaners);
// We need to remove from our map the `window` once is closed, to prevent
// memory leak
window.addEventListener("DOMWindowClose", removeFromListeners);
}
cleaners.push(() => target.removeEventListener(type, listener, capture));
listeners.set(window, cleaners);
// We need to remove from our map the `window` once is closed, to prevent
// memory leak
window.addEventListener("DOMWindowClose", removeFromListeners);
target.addEventListener(type, listener, capture);
return output;
}
unload(() => {
for (let [window, cleaners] of listeners) {
cleaners.forEach(callback => callback())
}
listeners.clear();
for (let window of listeners.keys())
removeFromListeners.call(window);
});
exports.open = open;

View File

@ -692,8 +692,6 @@ function writeSync(fd, buffer, offset, length, position) {
else if (length + offset !== buffer.length) {
buffer = buffer.slice(offset, offset + length);
}
let writeStream = new WriteStream(fd, { position: position,
length: length });
let output = BinaryOutputStream(nsIFileOutputStream(fd));
nsIBinaryOutputStream(fd, output);

View File

@ -23,6 +23,7 @@ const historyService = Cc['@mozilla.org/browser/nav-history-service;1']
const { mapBookmarkItemType } = require('./utils');
const { EventTarget } = require('../event/target');
const { emit } = require('../event/core');
const { when } = require('../system/unload');
const emitter = EventTarget();
@ -119,4 +120,9 @@ historyService.addObserver(historyObserver, false);
let bookmarkObserver = createObserverInstance(BOOKMARK_EVENTS, BOOKMARK_ARGS);
bookmarkService.addObserver(bookmarkObserver, false);
when(() => {
historyService.removeObserver(historyObserver);
bookmarkService.removeObserver(bookmarkObserver);
});
exports.events = emitter;

View File

@ -169,6 +169,15 @@ function serializeStack(frames) {
Loader.serializeStack = serializeStack;
function readURI(uri) {
let nsURI = NetUtil.newURI(uri);
if (nsURI.scheme == "resource") {
// Resolve to a real URI, this will catch any obvious bad paths without
// logging assertions in debug builds, see bug 1135219
let proto = Cc["@mozilla.org/network/protocol;1?name=resource"].
getService(Ci.nsIResProtocolHandler);
uri = proto.resolveURI(nsURI);
}
let stream = NetUtil.newChannel2(uri,
'UTF-8',
null,
@ -420,6 +429,10 @@ const nodeResolve = iced(function nodeResolve(id, requirer, { rootURI }) {
// Resolve again
id = Loader.resolve(id, requirer);
// If this is already an absolute URI then there is no resolution to do
if (isAbsoluteURI(id))
return void 0;
// we assume that extensions are correct, i.e., a directory doesnt't have '.js'
// and a js file isn't named 'file.json.js'
let fullId = join(rootURI, id);
@ -431,9 +444,14 @@ const nodeResolve = iced(function nodeResolve(id, requirer, { rootURI }) {
if ((resolvedPath = loadAsDirectory(fullId)))
return stripBase(rootURI, resolvedPath);
// If the requirer is an absolute URI then the node module resolution below
// won't work correctly as we prefix everything with rootURI
if (isAbsoluteURI(requirer))
return void 0;
// If manifest has dependencies, attempt to look up node modules
// in the `dependencies` list
let dirs = getNodeModulePaths(dirname(join(rootURI, requirer))).map(dir => join(dir, id));
let dirs = getNodeModulePaths(dirname(requirer)).map(dir => join(rootURI, dir, id));
for (let i = 0; i < dirs.length; i++) {
if ((resolvedPath = loadAsFile(dirs[i])))
return stripBase(rootURI, resolvedPath);
@ -509,6 +527,7 @@ function getNodeModulePaths (start) {
let dir = join(parts.slice(0, i + 1).join('/'), moduleDir);
dirs.push(dir);
}
dirs.push(moduleDir);
return dirs;
}

View File

@ -8,10 +8,7 @@
"license": "MPL 2.0",
"unpack": true,
"scripts": {
"test": "node ./bin/jpm-test.js",
"modules": "node ./bin/jpm-test.js --type modules",
"addons": "node ./bin/jpm-test.js --type addons",
"examples": "node ./bin/jpm-test.js --type examples"
"test": "gulp test"
},
"homepage": "https://github.com/mozilla/addon-sdk",
"repository": {
@ -25,6 +22,7 @@
"async": "0.9.0",
"chai": "2.1.1",
"glob": "4.4.2",
"gulp": "3.8.11",
"jpm": "0.0.29",
"lodash": "3.3.1",
"mocha": "2.1.0",

View File

@ -56,6 +56,7 @@ DEFAULT_NO_CONNECTIONS_PREFS = {
'browser.aboutHomeSnippets.updateUrl': 'https://localhost/snippet-dummy',
'browser.newtab.url' : 'about:blank',
'browser.search.update': False,
'browser.search.suggest.enabled' : False,
'browser.safebrowsing.enabled' : False,
'browser.safebrowsing.updateURL': 'http://localhost/safebrowsing-dummy/update',
'browser.safebrowsing.gethashURL': 'http://localhost/safebrowsing-dummy/gethash',

View File

@ -20,5 +20,10 @@
<div data-l10n-id="Translated">
A data-l10n-id value can be used in multiple elements
</div>
<a data-l10n-id="link-attributes" title="Certain whitelisted attributes get translated too" alt="No" accesskey="A"></a>
<input data-l10n-id="input" type="text" placeholder="Form placeholders are translateable">
<menu>
<menuitem data-l10n-id="contextitem" label="Labels of select options and context menus are translateable">
</menu>
</body>
</html

View File

@ -26,3 +26,13 @@ first_identifier[one]=first entry is %s and the second one is %s.
first_identifier=the entries are %s and %s.
second_identifier[other]=first entry is %s and the second one is %s.
third_identifier=first entry is %s and the second one is %s.
# bug 824489 allow translation of element attributes
link-attributes.title=Yes
link-attributes.alt=Yes
link-attributes.accesskey=B
input.placeholder=Yes
contextitem.label=Yes
link-attributes.ariaLabel=Yes
link-attributes.ariaValueText=Value
link-attributes.ariaMozHint=Hint

View File

@ -105,7 +105,15 @@ exports.testHtmlLocalizationPageWorker = createTest("en-GB", function(assert, lo
self.postMessage([nodes[0].innerHTML,
nodes[1].innerHTML,
nodes[2].innerHTML,
nodes[3].innerHTML]);
nodes[3].innerHTML,
nodes[4].title,
nodes[4].getAttribute("alt"),
nodes[4].getAttribute("accesskey"),
nodes[4].getAttribute("aria-label"),
nodes[4].getAttribute("aria-valuetext"),
nodes[4].getAttribute("aria-moz-hint"),
nodes[5].placeholder,
nodes[6].label]);
},
onMessage: function (data) {
assert.equal(
@ -121,6 +129,19 @@ exports.testHtmlLocalizationPageWorker = createTest("en-GB", function(assert, lo
);
assert.equal(data[3], "Yes", "Multiple elements with same data-l10n-id are accepted.");
// Attribute translation tests
assert.equal(data[4], "Yes", "Title attributes gets translated.");
assert.equal(data[5], "Yes", "Alt attributes gets translated.");
assert.equal(data[6], "B", "Accesskey gets translated.");
assert.equal(data[7], "Yes", "Aria-Label gets translated.");
assert.equal(data[8], "Value", "Aria-valuetext gets translated.");
assert.equal(data[9], "Hint", "Aria-moz-hint gets translated.");
assert.equal(data[10], "Yes", "Form placeholders are translateable.");
assert.equal(data[11], "Yes", "Labels of select options and context menus are translateable.");
done();
}
});
@ -144,7 +165,15 @@ exports.testHtmlLocalization = createTest("en-GB", function(assert, loader, done
self.postMessage([nodes[0].innerHTML,
nodes[1].innerHTML,
nodes[2].innerHTML,
nodes[3].innerHTML]);
nodes[3].innerHTML,
nodes[4].title,
nodes[4].getAttribute("alt"),
nodes[4].getAttribute("accesskey"),
nodes[4].getAttribute("aria-label"),
nodes[4].getAttribute("aria-valuetext"),
nodes[4].getAttribute("aria-moz-hint"),
nodes[5].placeholder,
nodes[6].label]);
},
onMessage: function (data) {
assert.equal(
@ -160,6 +189,19 @@ exports.testHtmlLocalization = createTest("en-GB", function(assert, loader, done
);
assert.equal(data[3], "Yes", "Multiple elements with same data-l10n-id are accepted.");
// Attribute translation tests
assert.equal(data[4], "Yes", "Title attributes gets translated.");
assert.equal(data[5], "Yes", "Alt attributes gets translated.");
assert.equal(data[6], "B", "Accesskey gets translated.");
assert.equal(data[7], "Yes", "Aria-Label gets translated.");
assert.equal(data[8], "Value", "Aria-valuetext gets translated.");
assert.equal(data[9], "Hint", "Aria-moz-hint gets translated.");
assert.equal(data[10], "Yes", "Form placeholders are translateable.");
assert.equal(data[11], "Yes", "Labels of select options and context menus are translateable.");
tab.close(done);
}
});

View File

@ -5,7 +5,7 @@
"use strict";
const LOCAL_URI = "about:robots";
const REMOTE_URI = "about:home";
const REMOTE_URI = "data:text/html;charset=utf-8,remote";
const { Loader } = require('sdk/test/loader');
const { getTabs, openTab, closeTab, setTabURL, getBrowserForTab, getURI } = require('sdk/tabs/utils');
@ -21,6 +21,19 @@ const { set } = require('sdk/preferences/service');
// The hidden preload browser messes up our frame counts
set('browser.newtab.preload', false);
function promiseTabFrameAttach(frames) {
return new Promise(resolve => {
let listener = function(frame, ...args) {
if (!frame.isTab)
return;
frames.off("attach", listener);
resolve([frame, ...args]);
}
frames.on("attach", listener);
});
}
// Check that we see a process stop and start
exports["test process restart"] = function*(assert) {
if (!isE10S) {
@ -44,7 +57,7 @@ exports["test process restart"] = function*(assert) {
// Switch the remote tab to a local URI which should kill the remote process
let frameDetach = promiseEventOnItemAndContainer(assert, remoteFrame, frames, 'detach');
let frameAttach = promiseEvent(frames, 'attach');
let frameAttach = promiseTabFrameAttach(frames);
let processDetach = promiseEventOnItemAndContainer(assert, remoteProcess, processes, 'detach');
setTabURL(tab, LOCAL_URI);
// The load should kill the remote frame
@ -57,7 +70,7 @@ exports["test process restart"] = function*(assert) {
frameDetach = promiseEventOnItemAndContainer(assert, newFrame, frames, 'detach');
let processAttach = promiseEvent(processes, 'attach');
frameAttach = promiseEvent(frames, 'attach');
frameAttach = promiseTabFrameAttach(frames);
setTabURL(tab, REMOTE_URI);
// The load should kill the remote frame
yield frameDetach;
@ -149,7 +162,7 @@ exports["test frame list"] = function*(assert) {
assert.equal(browserFrames(frames), getTabs(window).length, "Should be the right number of browser frames.");
assert.equal((yield getChildFrameCount(processes)), frames.length, "Child processes should have the right number of frames");
let promise = promiseEvent(frames, 'attach');
let promise = promiseTabFrameAttach(frames);
let tab1 = openTab(window, LOCAL_URI);
let [frame1] = yield promise;
assert.ok(!!frame1, "Should have seen the new frame");
@ -158,7 +171,7 @@ exports["test frame list"] = function*(assert) {
assert.equal(browserFrames(frames), getTabs(window).length, "Should be the right number of browser frames.");
assert.equal((yield getChildFrameCount(processes)), frames.length, "Child processes should have the right number of frames");
promise = promiseEvent(frames, 'attach');
promise = promiseTabFrameAttach(frames);
let tab2 = openTab(window, REMOTE_URI);
let [frame2] = yield promise;
assert.ok(!!frame2, "Should have seen the new frame");
@ -256,7 +269,7 @@ exports["test unload"] = function*(assert) {
let loader = new Loader(module);
let { frames } = yield waitForProcesses(loader);
let promise = promiseEvent(frames, 'attach');
let promise = promiseTabFrameAttach(frames);
let tab = openTab(window, "data:,<html/>");
let browser = getBrowserForTab(tab);
yield promiseDOMEvent(browser, "load", true);
@ -280,7 +293,7 @@ exports["test frame detach on unload"] = function*(assert) {
let loader = new Loader(module);
let { frames } = yield waitForProcesses(loader);
let promise = promiseEvent(frames, 'attach');
let promise = promiseTabFrameAttach(frames);
let tab = openTab(window, "data:,<html/>");
let browser = getBrowserForTab(tab);
yield promiseDOMEvent(browser, "load", true);
@ -304,7 +317,7 @@ exports["test frame event listeners"] = function*(assert) {
let loader = new Loader(module);
let { frames } = yield waitForProcesses(loader);
let promise = promiseEvent(frames, 'attach');
let promise = promiseTabFrameAttach(frames);
let tab = openTab(window, "data:text/html,<html></html>");
let browser = getBrowserForTab(tab);
yield promiseDOMEvent(browser, "load", true);
@ -339,7 +352,7 @@ exports["test frames event listeners"] = function*(assert) {
let loader = new Loader(module);
let { frames } = yield waitForProcesses(loader);
let promise = promiseEvent(frames, 'attach');
let promise = promiseTabFrameAttach(frames);
let tab = openTab(window, "data:text/html,<html></html>");
let browser = getBrowserForTab(tab);
yield promiseDOMEvent(browser, "load", true);
@ -377,8 +390,8 @@ exports["test unload removes frame event listeners"] = function*(assert) {
let loader2 = new Loader(module);
let { frames: frames2 } = yield waitForProcesses(loader2);
let promise = promiseEvent(frames, 'attach');
let promise2 = promiseEvent(frames2, 'attach');
let promise = promiseTabFrameAttach(frames);
let promise2 = promiseTabFrameAttach(frames2);
let tab = openTab(window, "data:text/html,<html></html>");
let browser = getBrowserForTab(tab);
yield promiseDOMEvent(browser, "load", true);
@ -418,8 +431,8 @@ exports["test unload removes frames event listeners"] = function*(assert) {
let loader2 = new Loader(module);
let { frames: frames2 } = yield waitForProcesses(loader2);
let promise = promiseEvent(frames, 'attach');
let promise2 = promiseEvent(frames2, 'attach');
let promise = promiseTabFrameAttach(frames);
let promise2 = promiseTabFrameAttach(frames2);
let tab = openTab(window, "data:text/html,<html></html>");
let browser = getBrowserForTab(tab);
yield promiseDOMEvent(browser, "load", true);

View File

@ -20,5 +20,10 @@
<div data-l10n-id="Translated">
A data-l10n-id value can be used in multiple elements
</div>
<a data-l10n-id="link-attributes" title="Certain whitelisted attributes get translated too" alt="No" accesskey="A"></a>
<input data-l10n-id="input" type="text" placeholder="Form placeholders are translateable">
<menu>
<menuitem data-l10n-id="contextitem" label="Labels of select options and context menus are translateable">
</menu>
</body>
</html

View File

@ -26,3 +26,13 @@ first_identifier[one]=first entry is %s and the second one is %s.
first_identifier=the entries are %s and %s.
second_identifier[other]=first entry is %s and the second one is %s.
third_identifier=first entry is %s and the second one is %s.
# bug 824489 allow translation of element attributes
link-attributes.title=Yes
link-attributes.alt=Yes
link-attributes.accesskey=B
input.placeholder=Yes
contextitem.label=Yes
link-attributes.ariaLabel=Yes
link-attributes.ariaValueText=Value
link-attributes.ariaMozHint=Hint

View File

@ -105,7 +105,15 @@ exports.testHtmlLocalizationPageWorker = createTest("en-GB", function(assert, lo
self.postMessage([nodes[0].innerHTML,
nodes[1].innerHTML,
nodes[2].innerHTML,
nodes[3].innerHTML]);
nodes[3].innerHTML,
nodes[4].title,
nodes[4].getAttribute("alt"),
nodes[4].getAttribute("accesskey"),
nodes[4].getAttribute("aria-label"),
nodes[4].getAttribute("aria-valuetext"),
nodes[4].getAttribute("aria-moz-hint"),
nodes[5].placeholder,
nodes[6].label]);
},
onMessage: function (data) {
assert.equal(
@ -120,6 +128,19 @@ exports.testHtmlLocalizationPageWorker = createTest("en-GB", function(assert, lo
"Content from .properties is text content; HTML can't be injected."
);
assert.equal(data[3], "Yes", "Multiple elements with same data-l10n-id are accepted.");
// Attribute translation tests
assert.equal(data[4], "Yes", "Title attributes gets translated.");
assert.equal(data[5], "Yes", "Alt attributes gets translated.");
assert.equal(data[6], "B", "Accesskey gets translated.");
assert.equal(data[7], "Yes", "Aria-Label gets translated.");
assert.equal(data[8], "Value", "Aria-valuetext gets translated.");
assert.equal(data[9], "Hint", "Aria-moz-hint gets translated.");
assert.equal(data[10], "Yes", "Form placeholders are translateable.");
assert.equal(data[11], "Yes", "Labels of select options and context menus are translateable.");
done();
}
@ -144,7 +165,15 @@ exports.testHtmlLocalization = createTest("en-GB", function(assert, loader, done
self.postMessage([nodes[0].innerHTML,
nodes[1].innerHTML,
nodes[2].innerHTML,
nodes[3].innerHTML]);
nodes[3].innerHTML,
nodes[4].title,
nodes[4].getAttribute("alt"),
nodes[4].getAttribute("accesskey"),
nodes[4].getAttribute("aria-label"),
nodes[4].getAttribute("aria-valuetext"),
nodes[4].getAttribute("aria-moz-hint"),
nodes[5].placeholder,
nodes[6].label]);
},
onMessage: function (data) {
assert.equal(
@ -160,6 +189,19 @@ exports.testHtmlLocalization = createTest("en-GB", function(assert, loader, done
);
assert.equal(data[3], "Yes", "Multiple elements with same data-l10n-id are accepted.");
// Attribute translation tests
assert.equal(data[4], "Yes", "Title attributes gets translated.");
assert.equal(data[5], "Yes", "Alt attributes gets translated.");
assert.equal(data[6], "B", "Accesskey gets translated.");
assert.equal(data[7], "Yes", "Aria-Label gets translated.");
assert.equal(data[8], "Value", "Aria-valuetext gets translated.");
assert.equal(data[9], "Hint", "Aria-moz-hint gets translated.");
assert.equal(data[10], "Yes", "Form placeholders are translateable.");
assert.equal(data[11], "Yes", "Labels of select options and context menus are translateable.");
tab.close(done);
}
});

View File

@ -5,7 +5,7 @@
"use strict";
const LOCAL_URI = "about:robots";
const REMOTE_URI = "about:home";
const REMOTE_URI = "data:text/html;charset=utf-8,remote";
const { Loader } = require('sdk/test/loader');
const { getTabs, openTab, closeTab, setTabURL, getBrowserForTab, getURI } = require('sdk/tabs/utils');
@ -21,6 +21,19 @@ const { set } = require('sdk/preferences/service');
// The hidden preload browser messes up our frame counts
set('browser.newtab.preload', false);
function promiseTabFrameAttach(frames) {
return new Promise(resolve => {
let listener = function(frame, ...args) {
if (!frame.isTab)
return;
frames.off("attach", listener);
resolve([frame, ...args]);
}
frames.on("attach", listener);
});
}
// Check that we see a process stop and start
exports["test process restart"] = function*(assert) {
if (!isE10S) {
@ -44,7 +57,7 @@ exports["test process restart"] = function*(assert) {
// Switch the remote tab to a local URI which should kill the remote process
let frameDetach = promiseEventOnItemAndContainer(assert, remoteFrame, frames, 'detach');
let frameAttach = promiseEvent(frames, 'attach');
let frameAttach = promiseTabFrameAttach(frames);
let processDetach = promiseEventOnItemAndContainer(assert, remoteProcess, processes, 'detach');
setTabURL(tab, LOCAL_URI);
// The load should kill the remote frame
@ -57,7 +70,7 @@ exports["test process restart"] = function*(assert) {
frameDetach = promiseEventOnItemAndContainer(assert, newFrame, frames, 'detach');
let processAttach = promiseEvent(processes, 'attach');
frameAttach = promiseEvent(frames, 'attach');
frameAttach = promiseTabFrameAttach(frames);
setTabURL(tab, REMOTE_URI);
// The load should kill the remote frame
yield frameDetach;
@ -149,7 +162,7 @@ exports["test frame list"] = function*(assert) {
assert.equal(browserFrames(frames), getTabs(window).length, "Should be the right number of browser frames.");
assert.equal((yield getChildFrameCount(processes)), frames.length, "Child processes should have the right number of frames");
let promise = promiseEvent(frames, 'attach');
let promise = promiseTabFrameAttach(frames);
let tab1 = openTab(window, LOCAL_URI);
let [frame1] = yield promise;
assert.ok(!!frame1, "Should have seen the new frame");
@ -158,7 +171,7 @@ exports["test frame list"] = function*(assert) {
assert.equal(browserFrames(frames), getTabs(window).length, "Should be the right number of browser frames.");
assert.equal((yield getChildFrameCount(processes)), frames.length, "Child processes should have the right number of frames");
promise = promiseEvent(frames, 'attach');
promise = promiseTabFrameAttach(frames);
let tab2 = openTab(window, REMOTE_URI);
let [frame2] = yield promise;
assert.ok(!!frame2, "Should have seen the new frame");
@ -256,7 +269,7 @@ exports["test unload"] = function*(assert) {
let loader = new Loader(module);
let { frames } = yield waitForProcesses(loader);
let promise = promiseEvent(frames, 'attach');
let promise = promiseTabFrameAttach(frames);
let tab = openTab(window, "data:,<html/>");
let browser = getBrowserForTab(tab);
yield promiseDOMEvent(browser, "load", true);
@ -280,7 +293,7 @@ exports["test frame detach on unload"] = function*(assert) {
let loader = new Loader(module);
let { frames } = yield waitForProcesses(loader);
let promise = promiseEvent(frames, 'attach');
let promise = promiseTabFrameAttach(frames);
let tab = openTab(window, "data:,<html/>");
let browser = getBrowserForTab(tab);
yield promiseDOMEvent(browser, "load", true);
@ -304,7 +317,7 @@ exports["test frame event listeners"] = function*(assert) {
let loader = new Loader(module);
let { frames } = yield waitForProcesses(loader);
let promise = promiseEvent(frames, 'attach');
let promise = promiseTabFrameAttach(frames);
let tab = openTab(window, "data:text/html,<html></html>");
let browser = getBrowserForTab(tab);
yield promiseDOMEvent(browser, "load", true);
@ -339,7 +352,7 @@ exports["test frames event listeners"] = function*(assert) {
let loader = new Loader(module);
let { frames } = yield waitForProcesses(loader);
let promise = promiseEvent(frames, 'attach');
let promise = promiseTabFrameAttach(frames);
let tab = openTab(window, "data:text/html,<html></html>");
let browser = getBrowserForTab(tab);
yield promiseDOMEvent(browser, "load", true);
@ -377,8 +390,8 @@ exports["test unload removes frame event listeners"] = function*(assert) {
let loader2 = new Loader(module);
let { frames: frames2 } = yield waitForProcesses(loader2);
let promise = promiseEvent(frames, 'attach');
let promise2 = promiseEvent(frames2, 'attach');
let promise = promiseTabFrameAttach(frames);
let promise2 = promiseTabFrameAttach(frames2);
let tab = openTab(window, "data:text/html,<html></html>");
let browser = getBrowserForTab(tab);
yield promiseDOMEvent(browser, "load", true);
@ -418,8 +431,8 @@ exports["test unload removes frames event listeners"] = function*(assert) {
let loader2 = new Loader(module);
let { frames: frames2 } = yield waitForProcesses(loader2);
let promise = promiseEvent(frames, 'attach');
let promise2 = promiseEvent(frames2, 'attach');
let promise = promiseTabFrameAttach(frames);
let promise2 = promiseTabFrameAttach(frames2);
let tab = openTab(window, "data:text/html,<html></html>");
let browser = getBrowserForTab(tab);
yield promiseDOMEvent(browser, "load", true);

View File

@ -62,6 +62,7 @@ skip-if = true
[test-environment.js]
[test-errors.js]
[test-event-core.js]
[test-event-dom.js]
[test-event-target.js]
[test-event-utils.js]
[test-events.js]

View File

@ -13,6 +13,7 @@
"browser.aboutHomeSnippets.updateUrl": "https://localhost/snippet-dummy",
"browser.newtab.url": "about:blank",
"browser.search.update": false,
"browser.search.suggest.enabled": false,
"browser.safebrowsing.enabled": false,
"browser.safebrowsing.updateURL": "http://localhost/safebrowsing-dummy/update",
"browser.safebrowsing.gethashURL": "http://localhost/safebrowsing-dummy/gethash",

View File

@ -10,6 +10,7 @@ const { getMode } = require('sdk/private-browsing/utils');
const { browserWindows: windows } = require('sdk/windows');
const { defer } = require('sdk/core/promise');
const tabs = require('sdk/tabs');
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
// test openDialog() from window/utils with private option
// test isActive state in pwpb case
@ -80,27 +81,22 @@ exports.testIsPrivateOnWindowOpenFromPrivate = function(assert, done) {
};
exports.testOpenTabWithPrivateWindow = function*(assert) {
let { promise, resolve } = defer();
let window = getMostRecentBrowserWindow().OpenBrowserWindow({ private: true });
let window = yield openPromise(null, {
features: {
private: true,
toolbar: true
}
});
yield focus(window);
assert.pass("loading new private window");
yield promise(window, 'load').then(focus);
assert.equal(isPrivate(window), true, 'the focused window is private');
tabs.open({
yield new Promise(resolve => tabs.open({
url: 'about:blank',
onOpen: (tab) => {
assert.equal(isPrivate(tab), false, 'the opened tab is not private');
tab.close(resolve);
}
});
}));
yield promise;
yield close(window);
};

View File

@ -0,0 +1,24 @@
/* 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 { openTab: makeTab, getTabContentWindow } = require("sdk/tabs/utils");
function openTab(rawWindow, url) {
return new Promise(resolve => {
let tab = makeTab(rawWindow, url);
let window = getTabContentWindow(tab);
if (window.document.readyState == "complete") {
return resolve();
}
window.addEventListener("load", function onLoad() {
window.removeEventListener("load", onLoad, true);
resolve();
}, true);
return null;
})
}
exports.openTab = openTab;

View File

@ -0,0 +1,92 @@
/* 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 { openWindow, closeWindow } = require('./util');
const { Loader } = require('sdk/test/loader');
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
const { Cc, Ci } = require('chrome');
const els = Cc["@mozilla.org/eventlistenerservice;1"].
getService(Ci.nsIEventListenerService);
function countListeners(target, type) {
let listeners = els.getListenerInfoFor(target, {});
return listeners.filter(listener => listener.type == type).length;
}
exports['test window close clears listeners'] = function(assert) {
let window = yield openWindow();
let loader = Loader(module);
// Any element will do here
let gBrowser = window.gBrowser;
// Other parts of the app may be listening for this
let windowListeners = countListeners(window, "DOMWindowClose");
// We can assume we're the only ones using the test events
assert.equal(countListeners(gBrowser, "TestEvent1"), 0, "Should be no listener for test event 1");
assert.equal(countListeners(gBrowser, "TestEvent2"), 0, "Should be no listener for test event 2");
let { open } = loader.require('sdk/event/dom');
open(gBrowser, "TestEvent1");
assert.equal(countListeners(window, "DOMWindowClose"), windowListeners + 1,
"Should have added a DOMWindowClose listener");
assert.equal(countListeners(gBrowser, "TestEvent1"), 1, "Should be a listener for test event 1");
assert.equal(countListeners(gBrowser, "TestEvent2"), 0, "Should be no listener for test event 2");
open(gBrowser, "TestEvent2");
assert.equal(countListeners(window, "DOMWindowClose"), windowListeners + 1,
"Should not have added another DOMWindowClose listener");
assert.equal(countListeners(gBrowser, "TestEvent1"), 1, "Should be a listener for test event 1");
assert.equal(countListeners(gBrowser, "TestEvent2"), 1, "Should be a listener for test event 2");
window = yield closeWindow(window);
assert.equal(countListeners(window, "DOMWindowClose"), windowListeners,
"Should have removed a DOMWindowClose listener");
assert.equal(countListeners(gBrowser, "TestEvent1"), 0, "Should be no listener for test event 1");
assert.equal(countListeners(gBrowser, "TestEvent2"), 0, "Should be no listener for test event 2");
loader.unload();
};
exports['test unload clears listeners'] = function(assert) {
let window = getMostRecentBrowserWindow();
let loader = Loader(module);
// Any element will do here
let gBrowser = window.gBrowser;
// Other parts of the app may be listening for this
let windowListeners = countListeners(window, "DOMWindowClose");
// We can assume we're the only ones using the test events
assert.equal(countListeners(gBrowser, "TestEvent1"), 0, "Should be no listener for test event 1");
assert.equal(countListeners(gBrowser, "TestEvent2"), 0, "Should be no listener for test event 2");
let { open } = loader.require('sdk/event/dom');
open(gBrowser, "TestEvent1");
assert.equal(countListeners(window, "DOMWindowClose"), windowListeners + 1,
"Should have added a DOMWindowClose listener");
assert.equal(countListeners(gBrowser, "TestEvent1"), 1, "Should be a listener for test event 1");
assert.equal(countListeners(gBrowser, "TestEvent2"), 0, "Should be no listener for test event 2");
open(gBrowser, "TestEvent2");
assert.equal(countListeners(window, "DOMWindowClose"), windowListeners + 1,
"Should not have added another DOMWindowClose listener");
assert.equal(countListeners(gBrowser, "TestEvent1"), 1, "Should be a listener for test event 1");
assert.equal(countListeners(gBrowser, "TestEvent2"), 1, "Should be a listener for test event 2");
loader.unload();
assert.equal(countListeners(window, "DOMWindowClose"), windowListeners,
"Should have removed a DOMWindowClose listener");
assert.equal(countListeners(gBrowser, "TestEvent1"), 0, "Should be no listener for test event 1");
assert.equal(countListeners(gBrowser, "TestEvent2"), 0, "Should be no listener for test event 2");
};
require('sdk/test').run(exports);

View File

@ -48,6 +48,14 @@ exports['test nodeResolve'] = function (assert) {
'./node_modules/test-math/node_modules/test-add/index.js',
'Dependencies\' dependencies can be found');
resolveTest('resource://gre/modules/commonjs/sdk/tabs.js', './index.js', undefined,
'correctly ignores absolute URIs.');
resolveTest('../tabs', 'resource://gre/modules/commonjs/sdk/addon/bootstrap.js', undefined,
'correctly ignores attempts to resolve from a module at an absolute URI.');
resolveTest('sdk/tabs', 'resource://gre/modules/commonjs/sdk/addon/bootstrap.js', undefined,
'correctly ignores attempts to resolve from a module at an absolute URI.');
function resolveTest (id, requirer, expected, msg) {
let result = nodeResolve(id, requirer, { manifest: manifest, rootURI: rootURI });

View File

@ -1491,12 +1491,17 @@ exports.testShowHideRawWindowArg = function*(assert) {
const { Sidebar } = require('sdk/ui/sidebar');
let testName = 'testShowHideRawWindowArg';
assert.pass("Creating sidebar");
let sidebar = Sidebar({
id: testName,
title: testName,
url: 'data:text/html;charset=utf-8,' + testName
});
assert.pass("Created sidebar");
let mainWindow = getMostRecentBrowserWindow();
let newWindow = yield windowPromise(mainWindow.OpenBrowserWindow(), 'load');
assert.pass("Created the new window");
@ -1504,21 +1509,26 @@ exports.testShowHideRawWindowArg = function*(assert) {
yield focus(newWindow);
assert.pass("Focused the new window");
yield focus(mainWindow);
assert.pass("Focused the old window");
let newWindow2 = yield windowPromise(mainWindow.OpenBrowserWindow(), 'load');
assert.pass("Created the second new window");
yield focus(newWindow2);
assert.pass("Focused the second new window");
yield sidebar.show(newWindow);
assert.pass('the sidebar was shown');
assert.equal(isSidebarShowing(mainWindow), false, 'sidebar is not showing in main window');
assert.equal(isSidebarShowing(newWindow2), false, 'sidebar is not showing in second window');
assert.equal(isSidebarShowing(newWindow), true, 'sidebar is showing in new window');
assert.ok(isFocused(mainWindow), 'main window is still focused');
assert.ok(isFocused(newWindow2), 'main window is still focused');
yield sidebar.hide(newWindow);
assert.equal(isFocused(mainWindow), true, 'main window is still focused');
assert.equal(isFocused(newWindow2), true, 'second window is still focused');
assert.equal(isSidebarShowing(mainWindow), false, 'sidebar is not showing in main window');
assert.equal(isSidebarShowing(newWindow2), false, 'sidebar is not showing in second window');
assert.equal(isSidebarShowing(newWindow), false, 'sidebar is not showing in new window');
sidebar.destroy();

View File

@ -31,7 +31,7 @@ const openWindow = () => {
exports.openWindow = openWindow;
const closeWindow = (window) => {
const closed = when(window, "unload", true).then(_target);
const closed = when(window, "unload", true).then(_ => window);
window.close();
return closed;
};

View File

@ -7,7 +7,7 @@ const { Cc, Ci } = require('chrome');
const { setTimeout } = require('sdk/timers');
const { Loader } = require('sdk/test/loader');
const { onFocus, getMostRecentWindow, windows, isBrowser, getWindowTitle, isFocused } = require('sdk/window/utils');
const { open, close, focus } = require('sdk/window/helpers');
const { open, close, focus, promise: windowPromise } = require('sdk/window/helpers');
const { browserWindows } = require("sdk/windows");
const tabs = require("sdk/tabs");
const winUtils = require("sdk/deprecated/window-utils");
@ -17,6 +17,9 @@ const { viewFor } = require("sdk/view/core");
const { defer } = require("sdk/lang/functional");
const { cleanUI } = require("sdk/test/utils");
const { after } = require("sdk/test/utils");
const { merge } = require("sdk/util/object");
const self = require("sdk/self");
const { openTab } = require("../tabs/utils");
// TEST: open & close window
exports.testOpenAndCloseWindow = function(assert, done) {
@ -59,12 +62,9 @@ exports.testNeWindowIsFocused = function(assert, done) {
});
}
exports.testOpenRelativePathWindow = function(assert, done) {
exports.testOpenRelativePathWindow = function*(assert) {
assert.equal(browserWindows.length, 1, "Only one window open");
const { merge } = require("sdk/util/object");
const self = require("sdk/self");
let loader = Loader(module, null, null, {
modules: {
"sdk/self": merge({}, self, {
@ -72,17 +72,31 @@ exports.testOpenRelativePathWindow = function(assert, done) {
})
}
});
assert.pass("Created a new loader");
loader.require("sdk/windows").browserWindows.open({
url: "./test.html",
onOpen: (window) => {
window.tabs.activeTab.once("ready", (tab) => {
assert.equal(tab.title, "foo",
"tab opened a document with relative path");
done();
});
}
})
let tabReady = new Promise(resolve => {
loader.require("sdk/tabs").on("ready", (tab) => {
if (!/test\.html$/.test(tab.url))
return;
assert.equal(tab.title, "foo",
"tab opened a document with relative path");
resolve();
});
});
yield new Promise(resolve => {
loader.require("sdk/windows").browserWindows.open({
url: "./test.html",
onOpen: (window) => {
assert.pass("Created a new window");
resolve();
}
})
});
yield tabReady;
loader.unload();
}
exports.testAutomaticDestroy = function(assert, done) {
@ -218,52 +232,26 @@ exports.testOnOpenOnCloseListeners = function(assert, done) {
exports.testActiveWindow = function*(assert) {
let windows = browserWindows;
// API window objects
let window2, window3;
let window = getMostRecentWindow();
// Raw window objects
let rawWindow2, rawWindow3;
let rawWindow2 = yield windowPromise(window.OpenBrowserWindow(), "load").then(focus);
assert.pass("Window 2 was created");
yield new Promise(resolve => {
windows.open({
url: "data:text/html;charset=utf-8,<title>window 2</title>",
onOpen: (window) => {
assert.pass('window 2 open');
// open a tab in window 2
yield openTab(rawWindow2, "data:text/html;charset=utf-8,<title>window 2</title>");
window.tabs.activeTab.once('ready', () => {
assert.pass('window 2 tab activated');
assert.equal(rawWindow2.content.document.title, "window 2", "Got correct raw window 2");
assert.equal(rawWindow2.document.title, windows[1].title, "Saw correct title on window 2");
window2 = window;
rawWindow2 = viewFor(window);
let rawWindow3 = yield windowPromise(window.OpenBrowserWindow(), "load").then(focus);;
assert.pass("Window 3 was created");
assert.equal(rawWindow2.content.document.title, "window 2", "Got correct raw window 2");
assert.equal(rawWindow2.document.title, window2.title, "Saw correct title on window 2");
// open a tab in window 3
yield openTab(rawWindow3, "data:text/html;charset=utf-8,<title>window 3</title>");
windows.open({
url: "data:text/html;charset=utf-8,<title>window 3</title>",
onOpen: (window) => {
assert.pass('window 3 open');
window.tabs.activeTab.once('ready', () => {
assert.pass('window 3 tab activated');
window3 = window;
rawWindow3 = viewFor(window);
assert.equal(rawWindow3.content.document.title, "window 3", "Got correct raw window 3");
assert.equal(rawWindow3.document.title, window3.title, "Saw correct title on window 3");
resolve();
});
}
});
});
}
});
});
yield focus(rawWindow3);
assert.equal(rawWindow3.content.document.title, "window 3", "Got correct raw window 3");
assert.equal(rawWindow3.document.title, windows[2].title, "Saw correct title on window 3");
assert.equal(windows.length, 3, "Correct number of browser windows");
@ -272,11 +260,13 @@ exports.testActiveWindow = function*(assert) {
count++;
}
assert.equal(count, 3, "Correct number of windows returned by iterator");
assert.equal(windows.activeWindow.title, window3.title, "Correct active window title - 3");
assert.equal(windows.activeWindow.title, windows[2].title, "Correct active window title - 3");
let window3 = windows[2];
yield focus(rawWindow2);
assert.equal(windows.activeWindow.title, window2.title, "Correct active window title - 2");
assert.equal(windows.activeWindow.title, windows[1].title, "Correct active window title - 2");
let window2 = windows[1];
yield new Promise(resolve => {
onFocus(rawWindow2).then(resolve);
@ -284,7 +274,7 @@ exports.testActiveWindow = function*(assert) {
assert.pass("activating window2");
});
assert.equal(windows.activeWindow.title, window2.title, "Correct active window - 2");
assert.equal(windows.activeWindow.title, windows[1].title, "Correct active window - 2");
yield new Promise(resolve => {
onFocus(rawWindow3).then(resolve);
@ -391,21 +381,32 @@ exports.testTrackWindows = function(assert, done) {
}
// test that it is not possible to open a private window by default
exports.testWindowOpenPrivateDefault = function(assert, done) {
browserWindows.open({
url: 'about:mozilla',
exports.testWindowOpenPrivateDefault = function*(assert) {
const TITLE = "yo";
const URL = "data:text/html,<title>" + TITLE + "</title>";
let tabReady = new Promise(resolve => {
tabs.on('ready', function onTabReady(tab) {
if (tab.url != URL)
return;
tabs.removeListener('ready', onTabReady);
assert.equal(tab.title, TITLE, 'opened correct tab');
assert.equal(isPrivate(tab), false, 'tab is not private');
resolve();
});
})
yield new Promise(resolve => browserWindows.open({
url: URL,
isPrivate: true,
onOpen: function(window) {
let tab = window.tabs[0];
tab.once('ready', function() {
assert.equal(tab.url, 'about:mozilla', 'opened correct tab');
assert.equal(isPrivate(tab), false, 'tab is not private');
done();
});
assert.pass("the new window was opened");
resolve();
}
});
}));
yield tabReady;
}
// test that it is not possible to find a private window in