mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-15 11:47:31 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
8
addon-sdk/source/test/addons/addon-manager/lib/main.js
Normal file
8
addon-sdk/source/test/addons/addon-manager/lib/main.js
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
/* 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.exports = require("./test-main.js");
|
||||
|
||||
require('sdk/test/runner').runTestsFromModule(module);
|
||||
12
addon-sdk/source/test/addons/addon-manager/lib/test-main.js
Normal file
12
addon-sdk/source/test/addons/addon-manager/lib/test-main.js
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
/* 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 { id } = require("sdk/self");
|
||||
const { getAddonByID } = require("sdk/addon/manager");
|
||||
|
||||
exports["test getAddonByID"] = function*(assert) {
|
||||
let addon = yield getAddonByID(id);
|
||||
assert.equal(addon.id, id, "getAddonByID works");
|
||||
}
|
||||
7
addon-sdk/source/test/addons/addon-manager/package.json
Normal file
7
addon-sdk/source/test/addons/addon-manager/package.json
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"id": "test-addon-manager@jetpack",
|
||||
"main": "./lib/main.js",
|
||||
"name": "test-addon-manager",
|
||||
"version": "0.0.1",
|
||||
"author": "Erik Vold"
|
||||
}
|
||||
14
addon-sdk/source/test/addons/author-email/main.js
Normal file
14
addon-sdk/source/test/addons/author-email/main.js
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
'use strict';
|
||||
|
||||
const { id } = require('sdk/self');
|
||||
const { getAddonByID } = require('sdk/addon/manager');
|
||||
|
||||
exports.testContributors = function*(assert) {
|
||||
let addon = yield getAddonByID(id);
|
||||
assert.equal(addon.creator.name, 'test <test@mozilla.com>', '< and > characters work');
|
||||
}
|
||||
|
||||
require('sdk/test/runner').runTestsFromModule(module);
|
||||
6
addon-sdk/source/test/addons/author-email/package.json
Normal file
6
addon-sdk/source/test/addons/author-email/package.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"id": "test-addon-author-email@jetpack",
|
||||
"author": "test <test@mozilla.com>",
|
||||
"version": "0.0.1",
|
||||
"main": "./main.js"
|
||||
}
|
||||
39
addon-sdk/source/test/addons/child_process/index.js
Normal file
39
addon-sdk/source/test/addons/child_process/index.js
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/* 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";
|
||||
|
||||
/**
|
||||
* Ensures using child_process and underlying subprocess.jsm
|
||||
* works within an addon
|
||||
*/
|
||||
|
||||
const { exec } = require("sdk/system/child_process");
|
||||
const { platform, pathFor } = require("sdk/system");
|
||||
const PROFILE_DIR = pathFor("ProfD");
|
||||
const isWindows = platform.toLowerCase().indexOf("win") === 0;
|
||||
const app = require("sdk/system/xul-app");
|
||||
|
||||
// Once Bug 903018 is resolved, just move the application testing to
|
||||
// module.metadata.engines
|
||||
if (app.is("Firefox")) {
|
||||
exports["test child_process in an addon"] = (assert, done) => {
|
||||
exec(isWindows ? "DIR /A-D" : "ls -al", {
|
||||
cwd: PROFILE_DIR
|
||||
}, (err, stdout, stderr) => {
|
||||
assert.equal(err, null, "no errors");
|
||||
assert.equal(stderr, "", "stderr is empty");
|
||||
assert.ok(/extensions\.ini/.test(stdout), "stdout output of `ls -al` finds files");
|
||||
|
||||
if (isWindows)
|
||||
assert.ok(!/<DIR>/.test(stdout), "passing args works");
|
||||
else
|
||||
assert.ok(/d(r[-|w][-|x]){3}/.test(stdout), "passing args works");
|
||||
done();
|
||||
});
|
||||
};
|
||||
} else {
|
||||
exports["test unsupported"] = (assert) => assert.pass("This application is unsupported.");
|
||||
}
|
||||
require("sdk/test/runner").runTestsFromModule(module);
|
||||
5
addon-sdk/source/test/addons/child_process/package.json
Normal file
5
addon-sdk/source/test/addons/child_process/package.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"id": "test-child-process@jetpack",
|
||||
"main": "./index.js",
|
||||
"version": "0.0.1"
|
||||
}
|
||||
5
addon-sdk/source/test/addons/chrome/chrome.manifest
Normal file
5
addon-sdk/source/test/addons/chrome/chrome.manifest
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
content test chrome/content/
|
||||
skin test classic/1.0 chrome/skin/
|
||||
|
||||
locale test en-US chrome/locale/en-US/
|
||||
locale test ja-JP chrome/locale/ja-JP/
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0"?>
|
||||
<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
windowtype="test:window">
|
||||
</dialog>
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<!-- 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/. -->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1 @@
|
|||
test=Test
|
||||
|
|
@ -0,0 +1 @@
|
|||
test=テスト
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
/* 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/. */
|
||||
test{}
|
||||
10
addon-sdk/source/test/addons/chrome/data/panel.js
Normal file
10
addon-sdk/source/test/addons/chrome/data/panel.js
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
/* 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';
|
||||
|
||||
self.port.on('echo', _ => {
|
||||
self.port.emit('echo', '');
|
||||
});
|
||||
|
||||
self.port.emit('start', '');
|
||||
97
addon-sdk/source/test/addons/chrome/main.js
Normal file
97
addon-sdk/source/test/addons/chrome/main.js
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
/* 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 { Cu, Cc, Ci } = require('chrome');
|
||||
const Request = require('sdk/request').Request;
|
||||
const { WindowTracker } = require('sdk/deprecated/window-utils');
|
||||
const { close, open } = require('sdk/window/helpers');
|
||||
const { data } = require('sdk/self');
|
||||
const { Panel } = require('sdk/panel');
|
||||
|
||||
const XUL_URL = 'chrome://test/content/new-window.xul'
|
||||
|
||||
const { Services } = Cu.import('resource://gre/modules/Services.jsm', {});
|
||||
const { NetUtil } = Cu.import('resource://gre/modules/NetUtil.jsm', {});
|
||||
|
||||
exports.testChromeSkin = function(assert, done) {
|
||||
let skinURL = 'chrome://test/skin/style.css';
|
||||
|
||||
Request({
|
||||
url: skinURL,
|
||||
overrideMimeType: 'text/plain',
|
||||
onComplete: function (response) {
|
||||
assert.ok(/test\{\}\s*$/.test(response.text), 'chrome.manifest skin folder was registered!');
|
||||
done();
|
||||
}
|
||||
}).get();
|
||||
|
||||
assert.pass('requesting ' + skinURL);
|
||||
}
|
||||
|
||||
exports.testChromeContent = function(assert, done) {
|
||||
let wt = WindowTracker({
|
||||
onTrack: function(window) {
|
||||
if (window.document.documentElement.getAttribute('windowtype') === 'test:window') {
|
||||
assert.pass('test xul window was opened');
|
||||
wt.unload();
|
||||
|
||||
close(window).then(done, assert.fail);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
open(XUL_URL).then(
|
||||
assert.pass.bind(assert, 'opened ' + XUL_URL),
|
||||
assert.fail);
|
||||
|
||||
assert.pass('opening ' + XUL_URL);
|
||||
}
|
||||
|
||||
exports.testChromeLocale = function(assert) {
|
||||
let jpLocalePath = Cc['@mozilla.org/chrome/chrome-registry;1'].
|
||||
getService(Ci.nsIChromeRegistry).
|
||||
convertChromeURL(NetUtil.newURI('chrome://test/locale/description.properties')).
|
||||
spec.replace(/(en\-US|ja\-JP)/, 'ja-JP');
|
||||
let enLocalePath = jpLocalePath.replace(/ja\-JP/, 'en-US');
|
||||
|
||||
let jpStringBundle = Services.strings.createBundle(jpLocalePath);
|
||||
assert.equal(jpStringBundle.GetStringFromName('test'),
|
||||
'テスト',
|
||||
'locales ja-JP folder was copied correctly');
|
||||
|
||||
let enStringBundle = Services.strings.createBundle(enLocalePath);
|
||||
assert.equal(enStringBundle.GetStringFromName('test'),
|
||||
'Test',
|
||||
'locales en-US folder was copied correctly');
|
||||
}
|
||||
|
||||
exports.testChromeInPanel = function*(assert) {
|
||||
let panel = Panel({
|
||||
contentURL: 'chrome://test/content/panel.html',
|
||||
contentScriptWhen: 'end',
|
||||
contentScriptFile: data.url('panel.js')
|
||||
});
|
||||
|
||||
yield new Promise(resolve => panel.port.once('start', resolve));
|
||||
assert.pass('start was emitted');
|
||||
|
||||
yield new Promise(resolve => {
|
||||
panel.once('show', resolve);
|
||||
panel.show();
|
||||
});
|
||||
assert.pass('panel shown');
|
||||
|
||||
yield new Promise(resolve => {
|
||||
panel.port.once('echo', resolve);
|
||||
panel.port.emit('echo');
|
||||
});
|
||||
|
||||
assert.pass('got echo');
|
||||
|
||||
panel.destroy();
|
||||
assert.pass('panel is destroyed');
|
||||
}
|
||||
|
||||
require('sdk/test/runner').runTestsFromModule(module);
|
||||
5
addon-sdk/source/test/addons/chrome/package.json
Normal file
5
addon-sdk/source/test/addons/chrome/package.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"id": "test-chrome@jetpack",
|
||||
"main": "./main.js",
|
||||
"version": "0.0.1"
|
||||
}
|
||||
5211
addon-sdk/source/test/addons/content-permissions/httpd.js
Normal file
5211
addon-sdk/source/test/addons/content-permissions/httpd.js
Normal file
File diff suppressed because it is too large
Load diff
89
addon-sdk/source/test/addons/content-permissions/main.js
Normal file
89
addon-sdk/source/test/addons/content-permissions/main.js
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
/* 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 { PageMod } = require("sdk/page-mod");
|
||||
const tabs = require("sdk/tabs");
|
||||
const { startServerAsync } = require("./httpd");
|
||||
|
||||
const serverPort = 8099;
|
||||
const TEST_TAB_URL = "about:mozilla";
|
||||
|
||||
exports.testCrossDomainIframe = function(assert, done) {
|
||||
let server = startServerAsync(serverPort);
|
||||
server.registerPathHandler("/iframe", function handle(request, response) {
|
||||
response.write("<html><body>foo</body></html>");
|
||||
});
|
||||
|
||||
let pageMod = PageMod({
|
||||
include: TEST_TAB_URL,
|
||||
contentScript: "new " + function ContentScriptScope() {
|
||||
self.on("message", function (url) {
|
||||
let iframe = document.createElement("iframe");
|
||||
iframe.addEventListener("load", function onload() {
|
||||
iframe.removeEventListener("load", onload, false);
|
||||
self.postMessage(iframe.contentWindow.document.body.innerHTML);
|
||||
}, false);
|
||||
iframe.setAttribute("src", url);
|
||||
document.documentElement.appendChild(iframe);
|
||||
});
|
||||
},
|
||||
onAttach: function(w) {
|
||||
w.on("message", function (body) {
|
||||
assert.equal(body, "foo", "received iframe html content");
|
||||
pageMod.destroy();
|
||||
w.tab.close(function() {
|
||||
server.stop(done);
|
||||
});
|
||||
});
|
||||
|
||||
w.postMessage("http://localhost:" + serverPort + "/iframe");
|
||||
}
|
||||
});
|
||||
|
||||
tabs.open({
|
||||
url: TEST_TAB_URL,
|
||||
inBackground: true
|
||||
});
|
||||
};
|
||||
|
||||
exports.testCrossDomainXHR = function(assert, done) {
|
||||
let server = startServerAsync(serverPort);
|
||||
server.registerPathHandler("/xhr", function handle(request, response) {
|
||||
response.write("foo");
|
||||
});
|
||||
|
||||
let pageMod = PageMod({
|
||||
include: TEST_TAB_URL,
|
||||
contentScript: "new " + function ContentScriptScope() {
|
||||
self.on("message", function (url) {
|
||||
let request = new XMLHttpRequest();
|
||||
request.overrideMimeType("text/plain");
|
||||
request.open("GET", url, true);
|
||||
request.onload = function () {
|
||||
self.postMessage(request.responseText);
|
||||
};
|
||||
request.send(null);
|
||||
});
|
||||
},
|
||||
onAttach: function(w) {
|
||||
w.on("message", function (body) {
|
||||
assert.equal(body, "foo", "received XHR content");
|
||||
pageMod.destroy();
|
||||
w.tab.close(function() {
|
||||
server.stop(done);
|
||||
});
|
||||
});
|
||||
|
||||
w.postMessage("http://localhost:" + serverPort + "/xhr");
|
||||
}
|
||||
});
|
||||
|
||||
tabs.open({
|
||||
url: TEST_TAB_URL,
|
||||
inBackground: true
|
||||
});
|
||||
};
|
||||
|
||||
require("sdk/test/runner").runTestsFromModule(module);
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"id": "content-permissions@jetpack",
|
||||
"permissions": {
|
||||
"cross-domain-content": ["http://localhost:8099"]
|
||||
},
|
||||
"main": "./main.js",
|
||||
"version": "0.0.1"
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,90 @@
|
|||
/* 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 { PageMod } = require("sdk/page-mod");
|
||||
const tabs = require("sdk/tabs");
|
||||
const { startServerAsync } = require("./httpd");
|
||||
const { setTimeout } = require("sdk/timers");
|
||||
|
||||
const serverPort = 8099;
|
||||
|
||||
exports.testContentScriptLatencyRegression = function*(assert) {
|
||||
let server = startServerAsync(serverPort);
|
||||
server.registerPathHandler("/", function handle(request, response) {
|
||||
response.write(`<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="/slow.css">
|
||||
</head>
|
||||
<body>
|
||||
slow loading page...
|
||||
</body>
|
||||
</html>`);
|
||||
});
|
||||
|
||||
server.registerPathHandler("/slow.css", function handle(request, response) {
|
||||
response.processAsync();
|
||||
response.setHeader('Content-Type', 'text/css', false);
|
||||
setTimeout(_ => {
|
||||
response.write("body { background: red; }");
|
||||
response.finish();
|
||||
}, 2000);
|
||||
});
|
||||
|
||||
|
||||
let pageMod;
|
||||
|
||||
let worker = yield new Promise((resolve) => {
|
||||
pageMod = PageMod({
|
||||
include: "http://localhost:8099/",
|
||||
attachTo: "top",
|
||||
contentScriptWhen: "start",
|
||||
contentScript: "new " + function ContentScriptScope() {
|
||||
self.port.on("a-port-message", function () {
|
||||
self.port.emit("document-ready-state", document.readyState);
|
||||
});
|
||||
},
|
||||
onAttach: function(w) {
|
||||
resolve(w);
|
||||
}
|
||||
});
|
||||
|
||||
tabs.open({
|
||||
url: "http://localhost:8099/",
|
||||
inBackground: true
|
||||
});
|
||||
});
|
||||
|
||||
worker.port.emit("a-port-message");
|
||||
|
||||
let waitForPortMessage = new Promise((resolve) => {
|
||||
worker.port.once("document-ready-state", (msg) => {
|
||||
resolve(msg);
|
||||
});
|
||||
});
|
||||
|
||||
let documentReadyState = yield waitForPortMessage;
|
||||
|
||||
assert.notEqual(
|
||||
"complete", documentReadyState,
|
||||
"content script received the port message when the page was still loading"
|
||||
);
|
||||
|
||||
assert.notEqual(
|
||||
"uninitialized", documentReadyState,
|
||||
"content script should be frozen if document.readyState is still uninitialized"
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
["loading", "interactive"].includes(documentReadyState),
|
||||
"content script message received with document.readyState was interactive or loading"
|
||||
);
|
||||
|
||||
// Cleanup.
|
||||
pageMod.destroy();
|
||||
yield new Promise((resolve) => worker.tab.close(resolve));
|
||||
yield new Promise((resolve) => server.stop(resolve));
|
||||
};
|
||||
|
||||
require("sdk/test/runner").runTestsFromModule(module);
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
|
||||
{
|
||||
"id": "content-script-messages-latency@jetpack",
|
||||
"main": "./main.js",
|
||||
"version": "0.0.1"
|
||||
}
|
||||
19
addon-sdk/source/test/addons/contributors/main.js
Normal file
19
addon-sdk/source/test/addons/contributors/main.js
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/* 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 { id } = require('sdk/self');
|
||||
const { getAddonByID } = require('sdk/addon/manager');
|
||||
|
||||
exports.testContributors = function*(assert) {
|
||||
let addon = yield getAddonByID(id);
|
||||
let count = 0;
|
||||
addon.contributors.forEach(({ name }) => {
|
||||
assert.equal(name, ++count == 1 ? 'A' : 'B', 'The contributors keys are correct');
|
||||
});
|
||||
assert.equal(count, 2, 'The key count is correct');
|
||||
assert.equal(addon.contributors.length, 2, 'The key length is correct');
|
||||
}
|
||||
|
||||
require('sdk/test/runner').runTestsFromModule(module);
|
||||
6
addon-sdk/source/test/addons/contributors/package.json
Normal file
6
addon-sdk/source/test/addons/contributors/package.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"id": "test-contributors@jetpack",
|
||||
"contributors": [ "A", "B" ],
|
||||
"main": "./main.js",
|
||||
"version": "0.0.1"
|
||||
}
|
||||
29
addon-sdk/source/test/addons/curly-id/lib/main.js
Normal file
29
addon-sdk/source/test/addons/curly-id/lib/main.js
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/* 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 simple = require('sdk/simple-prefs');
|
||||
const service = require('sdk/preferences/service');
|
||||
const { id, preferencesBranch } = require('sdk/self');
|
||||
const { getAddonByID } = require('sdk/addon/manager');
|
||||
|
||||
exports.testCurlyID = function(assert) {
|
||||
assert.equal(id, '{34a1eae1-c20a-464f-9b0e-000000000000}', 'curly ID is curly');
|
||||
assert.equal(simple.prefs.test13, 26, 'test13 is 26');
|
||||
|
||||
simple.prefs.test14 = '15';
|
||||
assert.equal(service.get('extensions.{34a1eae1-c20a-464f-9b0e-000000000000}.test14'), '15', 'test14 is 15');
|
||||
assert.equal(service.get('extensions.{34a1eae1-c20a-464f-9b0e-000000000000}.test14'), simple.prefs.test14, 'simple test14 also 15');
|
||||
}
|
||||
|
||||
// from `/test/test-self.js`, adapted to `sdk/test/assert` API
|
||||
exports.testSelfID = function*(assert) {
|
||||
assert.equal(typeof(id), 'string', 'self.id is a string');
|
||||
assert.ok(id.length > 0, 'self.id not empty');
|
||||
|
||||
let addon = yield getAddonByID(id);
|
||||
assert.ok(addon, 'found addon with self.id');
|
||||
}
|
||||
|
||||
require('sdk/test/runner').runTestsFromModule(module);
|
||||
13
addon-sdk/source/test/addons/curly-id/package.json
Normal file
13
addon-sdk/source/test/addons/curly-id/package.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"id": "{34a1eae1-c20a-464f-9b0e-000000000000}",
|
||||
"fullName": "curly ID test",
|
||||
"author": "Tomislav Jovanovic",
|
||||
"preferences": [{
|
||||
"name": "test13",
|
||||
"type": "integer",
|
||||
"title": "test13",
|
||||
"value": 26
|
||||
}],
|
||||
"main": "./lib/main.js",
|
||||
"version": "0.0.1"
|
||||
}
|
||||
19
addon-sdk/source/test/addons/developers/main.js
Normal file
19
addon-sdk/source/test/addons/developers/main.js
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/* 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 { id } = require('sdk/self');
|
||||
const { getAddonByID } = require('sdk/addon/manager');
|
||||
|
||||
exports.testDevelopers = function*(assert) {
|
||||
let addon = yield getAddonByID(id);
|
||||
let count = 0;
|
||||
addon.developers.forEach(({ name }) => {
|
||||
assert.equal(name, ++count == 1 ? 'A' : 'B', 'The developers keys are correct');
|
||||
});
|
||||
assert.equal(count, 2, 'The key count is correct');
|
||||
assert.equal(addon.developers.length, 2, 'The key length is correct');
|
||||
}
|
||||
|
||||
require('sdk/test/runner').runTestsFromModule(module);
|
||||
8
addon-sdk/source/test/addons/developers/package.json
Normal file
8
addon-sdk/source/test/addons/developers/package.json
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"id": "test-developers@jetpack",
|
||||
"title": "Test developers package key",
|
||||
"author": "Erik Vold",
|
||||
"developers": [ "A", "B" ],
|
||||
"main": "./main.js",
|
||||
"version": "0.0.1"
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
/* 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/. */
|
||||
|
||||
self.postMessage("msg from contentScriptFile");
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Page Worker test</title>
|
||||
</head>
|
||||
<body>
|
||||
<p id="paragraph">Lorem ipsum dolor sit amet.</p>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
/* 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/. */
|
||||
|
||||
|
||||
// get title directly
|
||||
self.postMessage(["equal", document.title, "Page Worker test",
|
||||
"Correct page title accessed directly"]);
|
||||
|
||||
// get <p> directly
|
||||
var p = document.getElementById("paragraph");
|
||||
self.postMessage(["ok", !!p, "<p> can be accessed directly"]);
|
||||
self.postMessage(["equal", p.firstChild.nodeValue,
|
||||
"Lorem ipsum dolor sit amet.",
|
||||
"Correct text node expected"]);
|
||||
|
||||
// Modify page
|
||||
var div = document.createElement("div");
|
||||
div.setAttribute("id", "block");
|
||||
div.appendChild(document.createTextNode("Test text created"));
|
||||
document.body.appendChild(div);
|
||||
|
||||
// Check back the modification
|
||||
div = document.getElementById("block");
|
||||
self.postMessage(["ok", !!div, "<div> can be accessed directly"]);
|
||||
self.postMessage(["equal", div.firstChild.nodeValue,
|
||||
"Test text created", "Correct text node expected"]);
|
||||
self.postMessage(["done"]);
|
||||
|
||||
13
addon-sdk/source/test/addons/e10s-content/data/test.html
Normal file
13
addon-sdk/source/test/addons/e10s-content/data/test.html
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>foo</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>bar</p>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
/* 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 { data } = require('sdk/self');
|
||||
|
||||
exports.url = data.url;
|
||||
5212
addon-sdk/source/test/addons/e10s-content/lib/httpd.js
Normal file
5212
addon-sdk/source/test/addons/e10s-content/lib/httpd.js
Normal file
File diff suppressed because it is too large
Load diff
22
addon-sdk/source/test/addons/e10s-content/lib/main.js
Normal file
22
addon-sdk/source/test/addons/e10s-content/lib/main.js
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
/* 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 { merge } = require('sdk/util/object');
|
||||
const { version } = require('sdk/system');
|
||||
|
||||
const SKIPPING_TESTS = {
|
||||
"test skip": (assert) => assert.pass("nothing to test here")
|
||||
};
|
||||
|
||||
merge(module.exports, require('./test-content-script'));
|
||||
merge(module.exports, require('./test-content-worker'));
|
||||
merge(module.exports, require('./test-page-worker'));
|
||||
|
||||
// run e10s tests only on builds from trunk, fx-team, Nightly..
|
||||
if (!version.endsWith('a1')) {
|
||||
module.exports = SKIPPING_TESTS;
|
||||
}
|
||||
|
||||
require('sdk/test/runner').runTestsFromModule(module);
|
||||
|
|
@ -0,0 +1,845 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const hiddenFrames = require("sdk/frame/hidden-frame");
|
||||
const { create: makeFrame } = require("sdk/frame/utils");
|
||||
const { window } = require("sdk/addon/window");
|
||||
const { Loader } = require('sdk/test/loader');
|
||||
const { URL } = require("sdk/url");
|
||||
const testURI = require("./fixtures").url("test.html");
|
||||
const testHost = URL(testURI).scheme + '://' + URL(testURI).host;
|
||||
|
||||
/*
|
||||
* Utility function that allow to easily run a proxy test with a clean
|
||||
* new HTML document. See first unit test for usage.
|
||||
*/
|
||||
function createProxyTest(html, callback) {
|
||||
return function (assert, done) {
|
||||
let url = 'data:text/html;charset=utf-8,' + encodeURIComponent(html);
|
||||
let principalLoaded = false;
|
||||
|
||||
let element = makeFrame(window.document, {
|
||||
nodeName: "iframe",
|
||||
type: "content",
|
||||
allowJavascript: true,
|
||||
allowPlugins: true,
|
||||
allowAuth: true,
|
||||
uri: testURI
|
||||
});
|
||||
|
||||
element.addEventListener("DOMContentLoaded", onDOMReady, false);
|
||||
|
||||
function onDOMReady() {
|
||||
// Reload frame after getting principal from `testURI`
|
||||
if (!principalLoaded) {
|
||||
element.setAttribute("src", url);
|
||||
principalLoaded = true;
|
||||
return;
|
||||
}
|
||||
|
||||
assert.equal(element.getAttribute("src"), url, "correct URL loaded");
|
||||
element.removeEventListener("DOMContentLoaded", onDOMReady,
|
||||
false);
|
||||
let xrayWindow = element.contentWindow;
|
||||
let rawWindow = xrayWindow.wrappedJSObject;
|
||||
|
||||
let isDone = false;
|
||||
let helper = {
|
||||
xrayWindow: xrayWindow,
|
||||
rawWindow: rawWindow,
|
||||
createWorker: function (contentScript) {
|
||||
return createWorker(assert, xrayWindow, contentScript, helper.done);
|
||||
},
|
||||
done: function () {
|
||||
if (isDone)
|
||||
return;
|
||||
isDone = true;
|
||||
element.parentNode.removeChild(element);
|
||||
done();
|
||||
}
|
||||
};
|
||||
callback(helper, assert);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function createWorker(assert, xrayWindow, contentScript, done) {
|
||||
let loader = Loader(module);
|
||||
let Worker = loader.require("sdk/content/worker").Worker;
|
||||
let worker = Worker({
|
||||
window: xrayWindow,
|
||||
contentScript: [
|
||||
'let assert, done; new ' + function () {
|
||||
assert = function assert(v, msg) {
|
||||
self.port.emit("assert", {assertion:v, msg:msg});
|
||||
}
|
||||
done = function done() {
|
||||
self.port.emit("done");
|
||||
}
|
||||
},
|
||||
contentScript
|
||||
]
|
||||
});
|
||||
|
||||
worker.port.on("done", done);
|
||||
worker.port.on("assert", function (data) {
|
||||
assert.ok(data.assertion, data.msg);
|
||||
});
|
||||
|
||||
return worker;
|
||||
}
|
||||
|
||||
/* Examples for the `createProxyTest` uses */
|
||||
|
||||
var html = "<script>var documentGlobal = true</script>";
|
||||
|
||||
exports["test Create Proxy Test"] = createProxyTest(html, function (helper, assert) {
|
||||
// You can get access to regular `test` object in second argument of
|
||||
// `createProxyTest` method:
|
||||
assert.ok(helper.rawWindow.documentGlobal,
|
||||
"You have access to a raw window reference via `helper.rawWindow`");
|
||||
assert.ok(!("documentGlobal" in helper.xrayWindow),
|
||||
"You have access to an XrayWrapper reference via `helper.xrayWindow`");
|
||||
|
||||
// If you do not create a Worker, you have to call helper.done(),
|
||||
// in order to say when your test is finished
|
||||
helper.done();
|
||||
});
|
||||
|
||||
exports["test Create Proxy Test With Worker"] = createProxyTest("", function (helper) {
|
||||
|
||||
helper.createWorker(
|
||||
"new " + function WorkerScope() {
|
||||
assert(true, "You can do assertions in your content script");
|
||||
// And if you create a worker, you either have to call `done`
|
||||
// from content script or helper.done()
|
||||
done();
|
||||
}
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
exports["test Create Proxy Test With Events"] = createProxyTest("", function (helper, assert) {
|
||||
|
||||
let worker = helper.createWorker(
|
||||
"new " + function WorkerScope() {
|
||||
self.port.emit("foo");
|
||||
}
|
||||
);
|
||||
|
||||
worker.port.on("foo", function () {
|
||||
assert.pass("You can use events");
|
||||
// And terminate your test with helper.done:
|
||||
helper.done();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
/* Disabled due to bug 1038432
|
||||
// Bug 714778: There was some issue around `toString` functions
|
||||
// that ended up being shared between content scripts
|
||||
exports["test Shared To String Proxies"] = createProxyTest("", function(helper) {
|
||||
|
||||
let worker = helper.createWorker(
|
||||
'new ' + function ContentScriptScope() {
|
||||
// We ensure that `toString` can't be modified so that nothing could
|
||||
// leak to/from the document and between content scripts
|
||||
// It only applies to JS proxies, there isn't any such issue with xrays.
|
||||
//document.location.toString = function foo() {};
|
||||
document.location.toString.foo = "bar";
|
||||
assert("foo" in document.location.toString, "document.location.toString can be modified");
|
||||
assert(document.location.toString() == "data:text/html;charset=utf-8,",
|
||||
"First document.location.toString()");
|
||||
self.postMessage("next");
|
||||
}
|
||||
);
|
||||
worker.on("message", function () {
|
||||
helper.createWorker(
|
||||
'new ' + function ContentScriptScope2() {
|
||||
assert(!("foo" in document.location.toString),
|
||||
"document.location.toString is different for each content script");
|
||||
assert(document.location.toString() == "data:text/html;charset=utf-8,",
|
||||
"Second document.location.toString()");
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
*/
|
||||
|
||||
// Ensure that postMessage is working correctly across documents with an iframe
|
||||
var html = '<iframe id="iframe" name="test" src="data:text/html;charset=utf-8," />';
|
||||
exports["test postMessage"] = createProxyTest(html, function (helper, assert) {
|
||||
let ifWindow = helper.xrayWindow.document.getElementById("iframe").contentWindow;
|
||||
// Listen without proxies, to check that it will work in regular case
|
||||
// simulate listening from a web document.
|
||||
ifWindow.addEventListener("message", function listener(event) {
|
||||
ifWindow.removeEventListener("message", listener, false);
|
||||
// As we are in system principal, event is an XrayWrapper
|
||||
// xrays use current compartments when calling postMessage method.
|
||||
// Whereas js proxies was using postMessage method compartment,
|
||||
// not the caller one.
|
||||
assert.strictEqual(event.source, helper.xrayWindow,
|
||||
"event.source is the top window");
|
||||
assert.equal(event.origin, testHost, "origin matches testHost");
|
||||
|
||||
assert.equal(event.data, "{\"foo\":\"bar\\n \\\"escaped\\\".\"}",
|
||||
"message data is correct");
|
||||
|
||||
helper.done();
|
||||
}, false);
|
||||
|
||||
helper.createWorker(
|
||||
'new ' + function ContentScriptScope() {
|
||||
var json = JSON.stringify({foo : "bar\n \"escaped\"."});
|
||||
|
||||
document.getElementById("iframe").contentWindow.postMessage(json, "*");
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
var html = '<input id="input2" type="checkbox" />';
|
||||
exports["test Object Listener"] = createProxyTest(html, function (helper) {
|
||||
|
||||
helper.createWorker(
|
||||
'new ' + function ContentScriptScope() {
|
||||
// Test objects being given as event listener
|
||||
let input = document.getElementById("input2");
|
||||
let myClickListener = {
|
||||
called: false,
|
||||
handleEvent: function(event) {
|
||||
assert(this === myClickListener, "`this` is the original object");
|
||||
assert(!this.called, "called only once");
|
||||
this.called = true;
|
||||
assert(event.target, input, "event.target is the wrapped window");
|
||||
done();
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("click", myClickListener, true);
|
||||
input.click();
|
||||
window.removeEventListener("click", myClickListener, true);
|
||||
}
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
exports["test Object Listener 2"] = createProxyTest("", function (helper) {
|
||||
|
||||
helper.createWorker(
|
||||
('new ' + function ContentScriptScope() {
|
||||
// variable replaced with `testHost`
|
||||
let testHost = "TOKEN";
|
||||
// Verify object as DOM event listener
|
||||
let myMessageListener = {
|
||||
called: false,
|
||||
handleEvent: function(event) {
|
||||
window.removeEventListener("message", myMessageListener, true);
|
||||
|
||||
assert(this == myMessageListener, "`this` is the original object");
|
||||
assert(!this.called, "called only once");
|
||||
this.called = true;
|
||||
assert(event.target == document.defaultView, "event.target is the wrapped window");
|
||||
assert(event.source == document.defaultView, "event.source is the wrapped window");
|
||||
assert(event.origin == testHost, "origin matches testHost");
|
||||
assert(event.data == "ok", "message data is correct");
|
||||
done();
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("message", myMessageListener, true);
|
||||
document.defaultView.postMessage("ok", '*');
|
||||
}
|
||||
).replace("TOKEN", testHost));
|
||||
|
||||
});
|
||||
|
||||
var html = '<input id="input" type="text" /><input id="input3" type="checkbox" />' +
|
||||
'<input id="input2" type="checkbox" />';
|
||||
|
||||
exports.testStringOverload = createProxyTest(html, function (helper, assert) {
|
||||
helper.createWorker(
|
||||
'new ' + function ContentScriptScope() {
|
||||
// RightJS is hacking around String.prototype, and do similar thing:
|
||||
// Pass `this` from a String prototype method.
|
||||
// It is funny because typeof this == "object"!
|
||||
// So that when we pass `this` to a native method,
|
||||
// our proxy code can fail on another even more crazy thing.
|
||||
// See following test to see what fails around proxies.
|
||||
String.prototype.update = function () {
|
||||
assert(typeof this == "object", "in update, `this` is an object");
|
||||
assert(this.toString() == "input", "in update, `this.toString works");
|
||||
return document.querySelectorAll(this);
|
||||
};
|
||||
assert("input".update().length == 3, "String.prototype overload works");
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
exports["test Element.matches()"] = createProxyTest("", function (helper) {
|
||||
helper.createWorker(
|
||||
'new ' + function ContentScriptScope() {
|
||||
// Check matches XrayWrappers bug (Bug 658909):
|
||||
// Test that Element.matches() does not return bad results when we are
|
||||
// not calling it from the node itself.
|
||||
assert(document.createElement( "div" ).matches("div"),
|
||||
"matches works while being called from the node");
|
||||
assert(document.documentElement.matches.call(
|
||||
document.createElement( "div" ),
|
||||
"div"
|
||||
),
|
||||
"matches works while being called from a " +
|
||||
"function reference to " +
|
||||
"document.documentElement.matches.call");
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
exports["test Events Overload"] = createProxyTest("", function (helper) {
|
||||
|
||||
helper.createWorker(
|
||||
'new ' + function ContentScriptScope() {
|
||||
// If we add a "____proxy" attribute on XrayWrappers in order to store
|
||||
// the related proxy to create an unique proxy for each wrapper;
|
||||
// we end up setting this attribute to prototype objects :x
|
||||
// And so, instances created with such prototype will be considered
|
||||
// as equal to the prototype ...
|
||||
// // Internal method that return the proxy for a given XrayWrapper
|
||||
// function proxify(obj) {
|
||||
// if (obj._proxy) return obj._proxy;
|
||||
// return obj._proxy = Proxy.create(...);
|
||||
// }
|
||||
//
|
||||
// // Get a proxy of an XrayWrapper prototype object
|
||||
// let proto = proxify(xpcProto);
|
||||
//
|
||||
// // Use this proxy as a prototype
|
||||
// function Constr() {}
|
||||
// Constr.proto = proto;
|
||||
//
|
||||
// // Try to create an instance using this prototype
|
||||
// let xpcInstance = new Constr();
|
||||
// let wrapper = proxify(xpcInstance)
|
||||
//
|
||||
// xpcProto._proxy = proto and as xpcInstance.__proto__ = xpcProto,
|
||||
// xpcInstance._proxy = proto ... and profixy(xpcInstance) = proto :(
|
||||
//
|
||||
let proto = window.document.createEvent('HTMLEvents').__proto__;
|
||||
window.Event.prototype = proto;
|
||||
let event = document.createEvent('HTMLEvents');
|
||||
assert(event !== proto, "Event should not be equal to its prototype");
|
||||
event.initEvent('dataavailable', true, true);
|
||||
assert(event.type === 'dataavailable', "Events are working fine");
|
||||
done();
|
||||
}
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
exports["test Nested Attributes"] = createProxyTest("", function (helper) {
|
||||
|
||||
helper.createWorker(
|
||||
'new ' + function ContentScriptScope() {
|
||||
// XrayWrappers has a bug when you set an attribute on it,
|
||||
// in some cases, it creates an unnecessary wrapper that introduces
|
||||
// a different object that refers to the same original object
|
||||
// Check that our wrappers don't reproduce this bug
|
||||
// SEE BUG 658560: Fix identity problem with CrossOriginWrappers
|
||||
let o = {sandboxObject:true};
|
||||
window.nested = o;
|
||||
o.foo = true;
|
||||
assert(o === window.nested, "Nested attribute to sandbox object should not be proxified");
|
||||
window.nested = document;
|
||||
assert(window.nested === document, "Nested attribute to proxy should not be double proxified");
|
||||
done();
|
||||
}
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
exports["test Form nodeName"] = createProxyTest("", function (helper) {
|
||||
|
||||
helper.createWorker(
|
||||
'new ' + function ContentScriptScope() {
|
||||
let body = document.body;
|
||||
// Check form[nodeName]
|
||||
let form = document.createElement("form");
|
||||
let input = document.createElement("input");
|
||||
input.setAttribute("name", "test");
|
||||
form.appendChild(input);
|
||||
body.appendChild(form);
|
||||
assert(form.test == input, "form[nodeName] is valid");
|
||||
body.removeChild(form);
|
||||
done();
|
||||
}
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
exports["test localStorage"] = createProxyTest("", function (helper, assert) {
|
||||
|
||||
let worker = helper.createWorker(
|
||||
'new ' + function ContentScriptScope() {
|
||||
// Check localStorage:
|
||||
assert(window.localStorage, "has access to localStorage");
|
||||
window.localStorage.name = 1;
|
||||
assert(window.localStorage.name == 1, "localStorage appears to work");
|
||||
|
||||
self.port.on("step2", function () {
|
||||
window.localStorage.clear();
|
||||
assert(window.localStorage.name == undefined, "localStorage really, really works");
|
||||
done();
|
||||
});
|
||||
self.port.emit("step1");
|
||||
}
|
||||
);
|
||||
|
||||
worker.port.on("step1", function () {
|
||||
assert.equal(helper.rawWindow.localStorage.name, 1, "localStorage really works");
|
||||
worker.port.emit("step2");
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
exports["test Auto Unwrap Custom Attributes"] = createProxyTest("", function (helper) {
|
||||
|
||||
helper.createWorker(
|
||||
'new ' + function ContentScriptScope() {
|
||||
let body = document.body;
|
||||
// Setting a custom object to a proxy attribute is not wrapped when we get it afterward
|
||||
let object = {custom: true, enumerable: false};
|
||||
body.customAttribute = object;
|
||||
assert(object === body.customAttribute, "custom JS attributes are not wrapped");
|
||||
done();
|
||||
}
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
exports["test Object Tag"] = createProxyTest("", function (helper) {
|
||||
|
||||
helper.createWorker(
|
||||
'new ' + function ContentScriptScope() {
|
||||
// <object>, <embed> and other tags return typeof 'function'
|
||||
let flash = document.createElement("object");
|
||||
assert(typeof flash == "function", "<object> is typeof 'function'");
|
||||
assert(flash.toString().match(/\[object HTMLObjectElement.*\]/), "<object> is HTMLObjectElement");
|
||||
assert("setAttribute" in flash, "<object> has a setAttribute method");
|
||||
done();
|
||||
}
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
exports["test Highlight toString Behavior"] = createProxyTest("", function (helper, assert) {
|
||||
// We do not have any workaround this particular use of toString
|
||||
// applied on <object> elements. So disable this test until we found one!
|
||||
//assert.equal(helper.rawWindow.Object.prototype.toString.call(flash), "[object HTMLObjectElement]", "<object> is HTMLObjectElement");
|
||||
function f() {};
|
||||
let funToString = Object.prototype.toString.call(f);
|
||||
assert.ok(/\[object Function.*\]/.test(funToString), "functions are functions 1");
|
||||
|
||||
// This is how jquery call toString:
|
||||
let strToString = helper.rawWindow.Object.prototype.toString.call("");
|
||||
assert.ok(/\[object String.*\]/.test(strToString), "strings are strings");
|
||||
|
||||
let o = {__exposedProps__:{}};
|
||||
let objToString = helper.rawWindow.Object.prototype.toString.call(o);
|
||||
assert.ok(/\[object Object.*\]/.test(objToString), "objects are objects");
|
||||
|
||||
// Make sure to pass a function from the same compartments
|
||||
// or toString will return [object Object] on FF8+
|
||||
let f2 = helper.rawWindow.eval("(function () {})");
|
||||
let funToString2 = helper.rawWindow.Object.prototype.toString.call(f2);
|
||||
assert.ok(/\[object Function.*\]/.test(funToString2), "functions are functions 2");
|
||||
|
||||
helper.done();
|
||||
});
|
||||
|
||||
exports["test Document TagName"] = createProxyTest("", function (helper) {
|
||||
|
||||
helper.createWorker(
|
||||
'new ' + function ContentScriptScope() {
|
||||
let body = document.body;
|
||||
// Check document[tagName]
|
||||
let div = document.createElement("div");
|
||||
div.setAttribute("name", "test");
|
||||
body.appendChild(div);
|
||||
assert(!document.test, "document[divName] is undefined");
|
||||
body.removeChild(div);
|
||||
|
||||
let form = document.createElement("form");
|
||||
form.setAttribute("name", "test");
|
||||
body.appendChild(form);
|
||||
assert(document.test == form, "document[formName] is valid");
|
||||
body.removeChild(form);
|
||||
|
||||
let img = document.createElement("img");
|
||||
img.setAttribute("name", "test");
|
||||
body.appendChild(img);
|
||||
assert(document.test == img, "document[imgName] is valid");
|
||||
body.removeChild(img);
|
||||
done();
|
||||
}
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
var html = '<iframe id="iframe" name="test" src="data:text/html;charset=utf-8," />';
|
||||
exports["test Window Frames"] = createProxyTest(html, function (helper) {
|
||||
|
||||
helper.createWorker(
|
||||
'let glob = this; new ' + function ContentScriptScope() {
|
||||
// Check window[frameName] and window.frames[i]
|
||||
let iframe = document.getElementById("iframe");
|
||||
//assert(window.frames.length == 1, "The iframe is reported in window.frames check1");
|
||||
//assert(window.frames[0] == iframe.contentWindow, "The iframe is reported in window.frames check2");
|
||||
assert(window.test == iframe.contentWindow, "window[frameName] is valid");
|
||||
done();
|
||||
}
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
exports["test Collections"] = createProxyTest("", function (helper) {
|
||||
|
||||
helper.createWorker(
|
||||
'new ' + function ContentScriptScope() {
|
||||
// Highlight XPCNativeWrapper bug with HTMLCollection
|
||||
// tds[0] is only defined on first access :o
|
||||
let body = document.body;
|
||||
let div = document.createElement("div");
|
||||
body.appendChild(div);
|
||||
div.innerHTML = "<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>";
|
||||
let tds = div.getElementsByTagName("td");
|
||||
assert(tds[0] == tds[0], "We can get array element multiple times");
|
||||
body.removeChild(div);
|
||||
done();
|
||||
}
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
var html = '<input id="input" type="text" /><input id="input3" type="checkbox" />' +
|
||||
'<input id="input2" type="checkbox" />';
|
||||
exports["test Collections 2"] = createProxyTest(html, function (helper) {
|
||||
|
||||
helper.createWorker(
|
||||
'new ' + function ContentScriptScope() {
|
||||
// Verify that NodeList/HTMLCollection are working fine
|
||||
let body = document.body;
|
||||
let inputs = body.getElementsByTagName("input");
|
||||
assert(body.childNodes.length == 3, "body.childNodes length is correct");
|
||||
assert(inputs.length == 3, "inputs.length is correct");
|
||||
assert(body.childNodes[0] == inputs[0], "body.childNodes[0] is correct");
|
||||
assert(body.childNodes[1] == inputs[1], "body.childNodes[1] is correct");
|
||||
assert(body.childNodes[2] == inputs[2], "body.childNodes[2] is correct");
|
||||
let count = 0;
|
||||
for(let i in body.childNodes) {
|
||||
count++;
|
||||
}
|
||||
|
||||
assert(count >= 3, "body.childNodes is iterable");
|
||||
done();
|
||||
}
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
exports["test XMLHttpRequest"] = createProxyTest("", function (helper) {
|
||||
|
||||
helper.createWorker(
|
||||
'new ' + function ContentScriptScope() {
|
||||
// XMLHttpRequest doesn't support XMLHttpRequest.apply,
|
||||
// that may break our proxy code
|
||||
assert(new window.XMLHttpRequest(), "we are able to instantiate XMLHttpRequest object");
|
||||
done();
|
||||
}
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
exports["test XPathResult"] = createProxyTest("", function (helper, assert) {
|
||||
const XPathResultTypes = ["ANY_TYPE",
|
||||
"NUMBER_TYPE", "STRING_TYPE", "BOOLEAN_TYPE",
|
||||
"UNORDERED_NODE_ITERATOR_TYPE",
|
||||
"ORDERED_NODE_ITERATOR_TYPE",
|
||||
"UNORDERED_NODE_SNAPSHOT_TYPE",
|
||||
"ORDERED_NODE_SNAPSHOT_TYPE",
|
||||
"ANY_UNORDERED_NODE_TYPE",
|
||||
"FIRST_ORDERED_NODE_TYPE"];
|
||||
|
||||
// Check XPathResult bug with constants being undefined on XPCNativeWrapper
|
||||
let xpcXPathResult = helper.xrayWindow.XPathResult;
|
||||
|
||||
XPathResultTypes.forEach(function(type, i) {
|
||||
assert.equal(xpcXPathResult.wrappedJSObject[type],
|
||||
helper.rawWindow.XPathResult[type],
|
||||
"XPathResult's constants are valid on unwrapped node");
|
||||
|
||||
assert.equal(xpcXPathResult[type], i,
|
||||
"XPathResult's constants are defined on " +
|
||||
"XPCNativeWrapper (platform bug #)");
|
||||
});
|
||||
|
||||
let value = helper.rawWindow.XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE;
|
||||
let worker = helper.createWorker(
|
||||
'new ' + function ContentScriptScope() {
|
||||
self.port.on("value", function (value) {
|
||||
// Check that our work around is working:
|
||||
assert(window.XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE === value,
|
||||
"XPathResult works correctly on Proxies");
|
||||
done();
|
||||
});
|
||||
}
|
||||
);
|
||||
worker.port.emit("value", value);
|
||||
});
|
||||
|
||||
exports["test Prototype Inheritance"] = createProxyTest("", function (helper) {
|
||||
|
||||
helper.createWorker(
|
||||
'new ' + function ContentScriptScope() {
|
||||
// Verify that inherited prototype function like initEvent
|
||||
// are handled correctly. (e2.type will return an error if it's not the case)
|
||||
let event1 = document.createEvent( 'MouseEvents' );
|
||||
event1.initEvent( "click", true, true );
|
||||
let event2 = document.createEvent( 'MouseEvents' );
|
||||
event2.initEvent( "click", true, true );
|
||||
assert(event2.type == "click", "We are able to create an event");
|
||||
done();
|
||||
}
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
exports["test Functions"] = createProxyTest("", function (helper) {
|
||||
|
||||
helper.rawWindow.callFunction = function callFunction(f) {
|
||||
return f();
|
||||
};
|
||||
helper.rawWindow.isEqual = function isEqual(a, b) {
|
||||
return a == b;
|
||||
};
|
||||
// bug 784116: workaround in order to allow proxy code to cache proxies on
|
||||
// these functions:
|
||||
helper.rawWindow.callFunction.__exposedProps__ = {__proxy: 'rw'};
|
||||
helper.rawWindow.isEqual.__exposedProps__ = {__proxy: 'rw'};
|
||||
|
||||
helper.createWorker(
|
||||
'new ' + function ContentScriptScope() {
|
||||
// Check basic usage of functions
|
||||
let closure2 = function () {return "ok";};
|
||||
assert(window.wrappedJSObject.callFunction(closure2) == "ok", "Function references work");
|
||||
|
||||
// Ensure that functions are cached when being wrapped to native code
|
||||
let closure = function () {};
|
||||
assert(window.wrappedJSObject.isEqual(closure, closure), "Function references are cached before being wrapped to native");
|
||||
done();
|
||||
}
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
var html = '<input id="input2" type="checkbox" />';
|
||||
exports["test Listeners"] = createProxyTest(html, function (helper) {
|
||||
|
||||
helper.createWorker(
|
||||
'new ' + function ContentScriptScope() {
|
||||
// Verify listeners:
|
||||
let input = document.getElementById("input2");
|
||||
assert(input, "proxy.getElementById works");
|
||||
|
||||
function onclick() {};
|
||||
input.onclick = onclick;
|
||||
assert(input.onclick === onclick, "on* attributes are equal to original function set");
|
||||
|
||||
let addEventListenerCalled = false;
|
||||
let expandoCalled = false;
|
||||
input.addEventListener("click", function onclick(event) {
|
||||
input.removeEventListener("click", onclick, true);
|
||||
|
||||
assert(!addEventListenerCalled, "closure given to addEventListener is called once");
|
||||
if (addEventListenerCalled)
|
||||
return;
|
||||
addEventListenerCalled = true;
|
||||
|
||||
assert(!event.target.ownerDocument.defaultView.documentGlobal, "event object is still wrapped and doesn't expose document globals");
|
||||
|
||||
let input2 = document.getElementById("input2");
|
||||
|
||||
input.onclick = function (event) {
|
||||
input.onclick = null;
|
||||
assert(!expandoCalled, "closure set to expando is called once");
|
||||
if (expandoCalled) return;
|
||||
expandoCalled = true;
|
||||
|
||||
assert(!event.target.ownerDocument.defaultView.documentGlobal, "event object is still wrapped and doesn't expose document globals");
|
||||
|
||||
setTimeout(function () {
|
||||
input.click();
|
||||
done();
|
||||
}, 0);
|
||||
|
||||
}
|
||||
|
||||
setTimeout(function () {
|
||||
input.click();
|
||||
}, 0);
|
||||
|
||||
}, true);
|
||||
|
||||
input.click();
|
||||
}
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
exports["test requestAnimationFrame"] = createProxyTest("", function (helper) {
|
||||
|
||||
helper.createWorker(
|
||||
'new ' + function ContentScriptScope() {
|
||||
var self = (function() { return this; })();
|
||||
window.requestAnimationFrame(function callback() {
|
||||
assert(self == this, "self is equal to `this`");
|
||||
done();
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
exports["testGlobalScope"] = createProxyTest("", function (helper) {
|
||||
|
||||
helper.createWorker(
|
||||
'let toplevelScope = true;' +
|
||||
'assert(window.toplevelScope, "variables in toplevel scope are set to `window` object");' +
|
||||
'assert(this.toplevelScope, "variables in toplevel scope are set to `this` object");' +
|
||||
'done();'
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
// Bug 715755: proxy code throw an exception on COW
|
||||
// Create an http server in order to simulate real cross domain documents
|
||||
exports["test Cross Domain Iframe"] = createProxyTest("", function (helper) {
|
||||
let serverPort = 8099;
|
||||
let server = require("./httpd").startServerAsync(serverPort);
|
||||
server.registerPathHandler("/", function handle(request, response) {
|
||||
// Returns the webpage that receive a message and forward it back to its
|
||||
// parent document by appending ' world'.
|
||||
let content = "<html><head><meta charset='utf-8'></head>\n";
|
||||
content += "<script>\n";
|
||||
content += " window.addEventListener('message', function (event) {\n";
|
||||
content += " parent.postMessage(event.data + ' world', '*');\n";
|
||||
content += " }, true);\n";
|
||||
content += "</script>\n";
|
||||
content += "<body></body>\n";
|
||||
content += "</html>\n";
|
||||
response.write(content);
|
||||
});
|
||||
|
||||
let worker = helper.createWorker(
|
||||
'new ' + function ContentScriptScope() {
|
||||
// Waits for the server page url
|
||||
self.on("message", function (url) {
|
||||
// Creates an iframe with this page
|
||||
let iframe = document.createElement("iframe");
|
||||
iframe.addEventListener("load", function onload() {
|
||||
iframe.removeEventListener("load", onload, true);
|
||||
try {
|
||||
// Try to communicate with iframe's content
|
||||
window.addEventListener("message", function onmessage(event) {
|
||||
window.removeEventListener("message", onmessage, true);
|
||||
|
||||
assert(event.data == "hello world", "COW works properly");
|
||||
self.port.emit("end");
|
||||
}, true);
|
||||
iframe.contentWindow.postMessage("hello", "*");
|
||||
} catch(e) {
|
||||
assert(false, "COW fails : "+e.message);
|
||||
}
|
||||
}, true);
|
||||
iframe.setAttribute("src", url);
|
||||
document.body.appendChild(iframe);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
worker.port.on("end", function () {
|
||||
server.stop(helper.done);
|
||||
});
|
||||
|
||||
worker.postMessage("http://localhost:" + serverPort + "/");
|
||||
|
||||
});
|
||||
|
||||
// Bug 769006: Ensure that MutationObserver works fine with proxies
|
||||
var html = '<a href="foo">link</a>';
|
||||
exports["test MutationObvserver"] = createProxyTest(html, function (helper) {
|
||||
|
||||
helper.createWorker(
|
||||
'new ' + function ContentScriptScope() {
|
||||
if (typeof MutationObserver == "undefined") {
|
||||
assert(true, "No MutationObserver for this FF version");
|
||||
done();
|
||||
return;
|
||||
}
|
||||
let link = document.getElementsByTagName("a")[0];
|
||||
|
||||
// Register a Mutation observer
|
||||
let obs = new MutationObserver(function(mutations){
|
||||
// Ensure that mutation data are valid
|
||||
assert(mutations.length == 1, "only one attribute mutation");
|
||||
let mutation = mutations[0];
|
||||
assert(mutation.type == "attributes", "check `type`");
|
||||
assert(mutation.target == link, "check `target`");
|
||||
assert(mutation.attributeName == "href", "check `attributeName`");
|
||||
assert(mutation.oldValue == "foo", "check `oldValue`");
|
||||
obs.disconnect();
|
||||
done();
|
||||
});
|
||||
obs.observe(document, {
|
||||
subtree: true,
|
||||
attributes: true,
|
||||
attributeOldValue: true,
|
||||
attributeFilter: ["href"]
|
||||
});
|
||||
|
||||
// Modify the DOM
|
||||
link.setAttribute("href", "bar");
|
||||
}
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
var html = '<script>' +
|
||||
'var accessCheck = function() {' +
|
||||
' assert(true, "exporting function works");' +
|
||||
' try{' +
|
||||
' exportedObj.prop;' +
|
||||
' assert(false, "content should not have access to content-script");' +
|
||||
' } catch(e) {' +
|
||||
' assert(e.toString().indexOf("Permission denied") != -1,' +
|
||||
' "content should not have access to content-script");' +
|
||||
' }' +
|
||||
'}</script>';
|
||||
exports["test nsEp for content-script"] = createProxyTest(html, function (helper) {
|
||||
|
||||
helper.createWorker(
|
||||
'let glob = this; new ' + function ContentScriptScope() {
|
||||
|
||||
exportFunction(assert, unsafeWindow, { defineAs: "assert" });
|
||||
window.wrappedJSObject.assert(true, "assert exported");
|
||||
window.wrappedJSObject.exportedObj = { prop: 42 };
|
||||
window.wrappedJSObject.accessCheck();
|
||||
done();
|
||||
}
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
// require("sdk/test").run(exports);
|
||||
1127
addon-sdk/source/test/addons/e10s-content/lib/test-content-worker.js
Normal file
1127
addon-sdk/source/test/addons/e10s-content/lib/test-content-worker.js
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,524 @@
|
|||
/* 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 { Page } = require("sdk/page-worker");
|
||||
const { URL } = require("sdk/url");
|
||||
const fixtures = require("./fixtures");
|
||||
const testURI = fixtures.url("test.html");
|
||||
|
||||
const ERR_DESTROYED =
|
||||
"Couldn't find the worker to receive this message. " +
|
||||
"The script may not be initialized yet, or may already have been unloaded.";
|
||||
|
||||
const Isolate = fn => "(" + fn + ")()";
|
||||
|
||||
exports.testSimplePageCreation = function(assert, done) {
|
||||
let page = new Page({
|
||||
contentScript: "self.postMessage(window.location.href)",
|
||||
contentScriptWhen: "end",
|
||||
onMessage: function (message) {
|
||||
assert.equal(message, "about:blank",
|
||||
"Page Worker should start with a blank page by default");
|
||||
assert.equal(this, page, "The 'this' object is the page itself.");
|
||||
done();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Tests that we can't be tricked by document overloads as we have access
|
||||
* to wrapped nodes
|
||||
*/
|
||||
exports.testWrappedDOM = function(assert, done) {
|
||||
let page = Page({
|
||||
allow: { script: true },
|
||||
contentURL: "data:text/html;charset=utf-8,<script>document.getElementById=3;window.scrollTo=3;</script>",
|
||||
contentScript: 'new ' + function() {
|
||||
function send() {
|
||||
self.postMessage([typeof(document.getElementById), typeof(window.scrollTo)]);
|
||||
}
|
||||
if (document.readyState !== 'complete')
|
||||
window.addEventListener('load', send, true)
|
||||
else
|
||||
send();
|
||||
},
|
||||
onMessage: function (message) {
|
||||
assert.equal(message[0],
|
||||
"function",
|
||||
"getElementById from content script is the native one");
|
||||
|
||||
assert.equal(message[1],
|
||||
"function",
|
||||
"scrollTo from content script is the native one");
|
||||
|
||||
done();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
// We do not offer unwrapped access to DOM since bug 601295 landed
|
||||
// See 660780 to track progress of unwrap feature
|
||||
exports.testUnwrappedDOM = function(assert, done) {
|
||||
let page = Page({
|
||||
allow: { script: true },
|
||||
contentURL: "data:text/html;charset=utf-8,<script>document.getElementById=3;window.scrollTo=3;</script>",
|
||||
contentScript: "window.addEventListener('load', function () { " +
|
||||
"return self.postMessage([typeof(unsafeWindow.document.getElementById), " +
|
||||
"typeof(unsafeWindow.scrollTo)]); }, true)",
|
||||
onMessage: function (message) {
|
||||
assert.equal(message[0],
|
||||
"number",
|
||||
"document inside page is free to be changed");
|
||||
|
||||
assert.equal(message[1],
|
||||
"number",
|
||||
"window inside page is free to be changed");
|
||||
|
||||
done();
|
||||
}
|
||||
});
|
||||
}
|
||||
*/
|
||||
|
||||
exports.testPageProperties = function(assert) {
|
||||
let page = new Page();
|
||||
|
||||
for (let prop of ['contentURL', 'allow', 'contentScriptFile',
|
||||
'contentScript', 'contentScriptWhen', 'on',
|
||||
'postMessage', 'removeListener']) {
|
||||
assert.ok(prop in page, prop + " property is defined on page.");
|
||||
}
|
||||
|
||||
assert.ok(() => page.postMessage("foo") || true,
|
||||
"postMessage doesn't throw exception on page.");
|
||||
}
|
||||
|
||||
exports.testConstructorAndDestructor = function(assert, done) {
|
||||
let loader = Loader(module);
|
||||
let { Page } = loader.require("sdk/page-worker");
|
||||
let global = loader.sandbox("sdk/page-worker");
|
||||
|
||||
let pagesReady = 0;
|
||||
|
||||
let page1 = Page({
|
||||
contentScript: "self.postMessage('')",
|
||||
contentScriptWhen: "end",
|
||||
onMessage: pageReady
|
||||
});
|
||||
let page2 = Page({
|
||||
contentScript: "self.postMessage('')",
|
||||
contentScriptWhen: "end",
|
||||
onMessage: pageReady
|
||||
});
|
||||
|
||||
assert.notEqual(page1, page2,
|
||||
"Page 1 and page 2 should be different objects.");
|
||||
|
||||
function pageReady() {
|
||||
if (++pagesReady == 2) {
|
||||
page1.destroy();
|
||||
page2.destroy();
|
||||
|
||||
assert.ok(isDestroyed(page1), "page1 correctly unloaded.");
|
||||
assert.ok(isDestroyed(page2), "page2 correctly unloaded.");
|
||||
|
||||
loader.unload();
|
||||
done();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exports.testAutoDestructor = function(assert, done) {
|
||||
let loader = Loader(module);
|
||||
let { Page } = loader.require("sdk/page-worker");
|
||||
|
||||
let page = Page({
|
||||
contentScript: "self.postMessage('')",
|
||||
contentScriptWhen: "end",
|
||||
onMessage: function() {
|
||||
loader.unload();
|
||||
assert.ok(isDestroyed(page), "Page correctly unloaded.");
|
||||
done();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
exports.testValidateOptions = function(assert) {
|
||||
assert.throws(
|
||||
() => Page({ contentURL: 'home' }),
|
||||
/The `contentURL` option must be a valid URL\./,
|
||||
"Validation correctly denied a non-URL contentURL"
|
||||
);
|
||||
|
||||
assert.throws(
|
||||
() => Page({ onMessage: "This is not a function."}),
|
||||
/The option "onMessage" must be one of the following types: function/,
|
||||
"Validation correctly denied a non-function onMessage."
|
||||
);
|
||||
|
||||
assert.pass("Options validation is working.");
|
||||
}
|
||||
|
||||
exports.testContentAndAllowGettersAndSetters = function(assert, done) {
|
||||
let content = "data:text/html;charset=utf-8,<script>window.localStorage.allowScript=3;</script>";
|
||||
|
||||
// Load up the page with testURI initially for the resource:// principal,
|
||||
// then load the actual data:* content, as data:* URIs no longer
|
||||
// have localStorage
|
||||
let page = Page({
|
||||
contentURL: testURI,
|
||||
contentScript: "if (window.location.href==='"+testURI+"')" +
|
||||
" self.postMessage('reload');" +
|
||||
"else " +
|
||||
" self.postMessage(window.localStorage.allowScript)",
|
||||
contentScriptWhen: "end",
|
||||
onMessage: step0
|
||||
});
|
||||
|
||||
function step0(message) {
|
||||
if (message === 'reload')
|
||||
return page.contentURL = content;
|
||||
assert.equal(message, "3",
|
||||
"Correct value expected for allowScript - 3");
|
||||
assert.equal(page.contentURL, content,
|
||||
"Correct content expected");
|
||||
page.removeListener('message', step0);
|
||||
page.on('message', step1);
|
||||
page.allow = { script: false };
|
||||
page.contentURL = content =
|
||||
"data:text/html;charset=utf-8,<script>window.localStorage.allowScript='f'</script>";
|
||||
}
|
||||
|
||||
function step1(message) {
|
||||
assert.equal(message, "3",
|
||||
"Correct value expected for allowScript - 3");
|
||||
assert.equal(page.contentURL, content, "Correct content expected");
|
||||
page.removeListener('message', step1);
|
||||
page.on('message', step2);
|
||||
page.allow = { script: true };
|
||||
page.contentURL = content =
|
||||
"data:text/html;charset=utf-8,<script>window.localStorage.allowScript='g'</script>";
|
||||
}
|
||||
|
||||
function step2(message) {
|
||||
assert.equal(message, "g",
|
||||
"Correct value expected for allowScript - g");
|
||||
assert.equal(page.contentURL, content, "Correct content expected");
|
||||
page.removeListener('message', step2);
|
||||
page.on('message', step3);
|
||||
page.allow.script = false;
|
||||
page.contentURL = content =
|
||||
"data:text/html;charset=utf-8,<script>window.localStorage.allowScript=3</script>";
|
||||
}
|
||||
|
||||
function step3(message) {
|
||||
assert.equal(message, "g",
|
||||
"Correct value expected for allowScript - g");
|
||||
assert.equal(page.contentURL, content, "Correct content expected");
|
||||
page.removeListener('message', step3);
|
||||
page.on('message', step4);
|
||||
page.allow.script = true;
|
||||
page.contentURL = content =
|
||||
"data:text/html;charset=utf-8,<script>window.localStorage.allowScript=4</script>";
|
||||
}
|
||||
|
||||
function step4(message) {
|
||||
assert.equal(message, "4",
|
||||
"Correct value expected for allowScript - 4");
|
||||
assert.equal(page.contentURL, content, "Correct content expected");
|
||||
done();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
exports.testOnMessageCallback = function(assert, done) {
|
||||
Page({
|
||||
contentScript: "self.postMessage('')",
|
||||
contentScriptWhen: "end",
|
||||
onMessage: function() {
|
||||
assert.pass("onMessage callback called");
|
||||
done();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
exports.testMultipleOnMessageCallbacks = function(assert, done) {
|
||||
let count = 0;
|
||||
let page = Page({
|
||||
contentScript: "self.postMessage('')",
|
||||
contentScriptWhen: "end",
|
||||
onMessage: () => count += 1
|
||||
});
|
||||
page.on('message', () => count += 2);
|
||||
page.on('message', () => count *= 3);
|
||||
page.on('message', () =>
|
||||
assert.equal(count, 9, "All callbacks were called, in order."));
|
||||
page.on('message', done);
|
||||
};
|
||||
|
||||
exports.testLoadContentPage = function(assert, done) {
|
||||
let page = Page({
|
||||
onMessage: function(message) {
|
||||
// The message is an array whose first item is the test method to call
|
||||
// and the rest of whose items are arguments to pass it.
|
||||
let msg = message.shift();
|
||||
if (msg == "done")
|
||||
return done();
|
||||
assert[msg].apply(assert, message);
|
||||
},
|
||||
contentURL: fixtures.url("test-page-worker.html"),
|
||||
contentScriptFile: fixtures.url("test-page-worker.js"),
|
||||
contentScriptWhen: "ready"
|
||||
});
|
||||
}
|
||||
|
||||
exports.testLoadContentPageRelativePath = function(assert, done) {
|
||||
let page = require("sdk/page-worker").Page({
|
||||
onMessage: function(message) {
|
||||
// The message is an array whose first item is the test method to call
|
||||
// and the rest of whose items are arguments to pass it.
|
||||
let msg = message.shift();
|
||||
if (msg == "done")
|
||||
return done();
|
||||
assert[msg].apply(assert, message);
|
||||
},
|
||||
contentURL: "./test-page-worker.html",
|
||||
contentScriptFile: "./test-page-worker.js",
|
||||
contentScriptWhen: "ready"
|
||||
});
|
||||
}
|
||||
|
||||
exports.testAllowScriptDefault = function(assert, done) {
|
||||
let page = Page({
|
||||
onMessage: function(message) {
|
||||
assert.ok(message, "Script is allowed to run by default.");
|
||||
done();
|
||||
},
|
||||
contentURL: "data:text/html;charset=utf-8,<script>document.documentElement.setAttribute('foo', 3);</script>",
|
||||
contentScript: "self.postMessage(document.documentElement.getAttribute('foo'))",
|
||||
contentScriptWhen: "ready"
|
||||
});
|
||||
}
|
||||
|
||||
exports.testAllowScript = function(assert, done) {
|
||||
let page = Page({
|
||||
onMessage: function(message) {
|
||||
assert.ok(message, "Script runs when allowed to do so.");
|
||||
done();
|
||||
},
|
||||
allow: { script: true },
|
||||
contentURL: "data:text/html;charset=utf-8,<script>document.documentElement.setAttribute('foo', 3);</script>",
|
||||
contentScript: "self.postMessage(document.documentElement.hasAttribute('foo') && " +
|
||||
" document.documentElement.getAttribute('foo') == 3)",
|
||||
contentScriptWhen: "ready"
|
||||
});
|
||||
}
|
||||
|
||||
exports.testPingPong = function(assert, done) {
|
||||
let page = Page({
|
||||
contentURL: 'data:text/html;charset=utf-8,ping-pong',
|
||||
contentScript: 'self.on("message", function(message) { return self.postMessage("pong"); });'
|
||||
+ 'self.postMessage("ready");',
|
||||
onMessage: function(message) {
|
||||
if ('ready' == message) {
|
||||
page.postMessage('ping');
|
||||
}
|
||||
else {
|
||||
assert.ok(message, 'pong', 'Callback from contentScript');
|
||||
done();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.testRedirect = function (assert, done) {
|
||||
let page = Page({
|
||||
contentURL: 'data:text/html;charset=utf-8,first-page',
|
||||
contentScriptWhen: "end",
|
||||
contentScript: '' +
|
||||
'if (/first-page/.test(document.location.href)) ' +
|
||||
' document.location.href = "data:text/html;charset=utf-8,redirect";' +
|
||||
'else ' +
|
||||
' self.port.emit("redirect", document.location.href);'
|
||||
});
|
||||
|
||||
page.port.on('redirect', function (url) {
|
||||
assert.equal(url, 'data:text/html;charset=utf-8,redirect', 'Reinjects contentScript on reload');
|
||||
done();
|
||||
});
|
||||
};
|
||||
|
||||
exports.testRedirectIncludeArrays = function (assert, done) {
|
||||
let firstURL = 'data:text/html;charset=utf-8,first-page';
|
||||
let page = Page({
|
||||
contentURL: firstURL,
|
||||
contentScript: '(function () {' +
|
||||
'self.port.emit("load", document.location.href);' +
|
||||
' self.port.on("redirect", function (url) {' +
|
||||
' document.location.href = url;' +
|
||||
' })' +
|
||||
'})();',
|
||||
include: ['about:blank', 'data:*']
|
||||
});
|
||||
|
||||
page.port.on('load', function (url) {
|
||||
if (url === firstURL) {
|
||||
page.port.emit('redirect', 'about:blank');
|
||||
} else if (url === 'about:blank') {
|
||||
page.port.emit('redirect', 'about:mozilla');
|
||||
assert.ok('`include` property handles arrays');
|
||||
assert.equal(url, 'about:blank', 'Redirects work with accepted domains');
|
||||
done();
|
||||
} else if (url === 'about:mozilla') {
|
||||
assert.fail('Should not redirect to restricted domain');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.testRedirectFromWorker = function (assert, done) {
|
||||
let firstURL = 'data:text/html;charset=utf-8,first-page';
|
||||
let secondURL = 'data:text/html;charset=utf-8,second-page';
|
||||
let thirdURL = 'data:text/html;charset=utf-8,third-page';
|
||||
let page = Page({
|
||||
contentURL: firstURL,
|
||||
contentScript: '(function () {' +
|
||||
'self.port.emit("load", document.location.href);' +
|
||||
' self.port.on("redirect", function (url) {' +
|
||||
' document.location.href = url;' +
|
||||
' })' +
|
||||
'})();',
|
||||
include: 'data:*'
|
||||
});
|
||||
|
||||
page.port.on('load', function (url) {
|
||||
if (url === firstURL) {
|
||||
page.port.emit('redirect', secondURL);
|
||||
} else if (url === secondURL) {
|
||||
page.port.emit('redirect', thirdURL);
|
||||
} else if (url === thirdURL) {
|
||||
page.port.emit('redirect', 'about:mozilla');
|
||||
assert.equal(url, thirdURL, 'Redirects work with accepted domains on include strings');
|
||||
done();
|
||||
} else {
|
||||
assert.fail('Should not redirect to unauthorized domains');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.testRedirectWithContentURL = function (assert, done) {
|
||||
let firstURL = 'data:text/html;charset=utf-8,first-page';
|
||||
let secondURL = 'data:text/html;charset=utf-8,second-page';
|
||||
let thirdURL = 'data:text/html;charset=utf-8,third-page';
|
||||
let page = Page({
|
||||
contentURL: firstURL,
|
||||
contentScript: '(function () {' +
|
||||
'self.port.emit("load", document.location.href);' +
|
||||
'})();',
|
||||
include: 'data:*'
|
||||
});
|
||||
|
||||
page.port.on('load', function (url) {
|
||||
if (url === firstURL) {
|
||||
page.contentURL = secondURL;
|
||||
} else if (url === secondURL) {
|
||||
page.contentURL = thirdURL;
|
||||
} else if (url === thirdURL) {
|
||||
page.contentURL = 'about:mozilla';
|
||||
assert.equal(url, thirdURL, 'Redirects work with accepted domains on include strings');
|
||||
done();
|
||||
} else {
|
||||
assert.fail('Should not redirect to unauthorized domains');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
exports.testMultipleDestroys = function(assert) {
|
||||
let page = Page();
|
||||
page.destroy();
|
||||
page.destroy();
|
||||
assert.pass("Multiple destroys should not cause an error");
|
||||
};
|
||||
|
||||
exports.testContentScriptOptionsOption = function(assert, done) {
|
||||
let page = new Page({
|
||||
contentScript: "self.postMessage( [typeof self.options.d, self.options] );",
|
||||
contentScriptWhen: "end",
|
||||
contentScriptOptions: {a: true, b: [1,2,3], c: "string", d: function(){ return 'test'}},
|
||||
onMessage: function(msg) {
|
||||
assert.equal(msg[0], 'undefined', 'functions are stripped from contentScriptOptions');
|
||||
assert.equal(typeof msg[1], 'object', 'object as contentScriptOptions');
|
||||
assert.equal(msg[1].a, true, 'boolean in contentScriptOptions');
|
||||
assert.equal(msg[1].b.join(), '1,2,3', 'array and numbers in contentScriptOptions');
|
||||
assert.equal(msg[1].c, 'string', 'string in contentScriptOptions');
|
||||
done();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.testMessageQueue = function (assert, done) {
|
||||
let page = new Page({
|
||||
contentScript: 'self.on("message", function (m) {' +
|
||||
'self.postMessage(m);' +
|
||||
'});',
|
||||
contentURL: 'data:text/html;charset=utf-8,',
|
||||
});
|
||||
page.postMessage('ping');
|
||||
page.on('message', function (m) {
|
||||
assert.equal(m, 'ping', 'postMessage should queue messages');
|
||||
done();
|
||||
});
|
||||
};
|
||||
|
||||
exports.testWindowStopDontBreak = function (assert, done) {
|
||||
const { Ci, Cc } = require('chrome');
|
||||
const consoleService = Cc['@mozilla.org/consoleservice;1'].
|
||||
getService(Ci.nsIConsoleService);
|
||||
const listener = {
|
||||
observe: ({message}) => {
|
||||
if (message.includes('contentWorker is null'))
|
||||
assert.fail('contentWorker is null');
|
||||
}
|
||||
};
|
||||
consoleService.registerListener(listener)
|
||||
|
||||
let page = new Page({
|
||||
contentURL: 'data:text/html;charset=utf-8,testWindowStopDontBreak',
|
||||
contentScriptWhen: 'ready',
|
||||
contentScript: Isolate(() => {
|
||||
window.stop();
|
||||
self.port.on('ping', () => self.port.emit('pong'));
|
||||
})
|
||||
});
|
||||
|
||||
page.port.on('pong', () => {
|
||||
assert.pass('page-worker works after window.stop');
|
||||
page.destroy();
|
||||
consoleService.unregisterListener(listener);
|
||||
done();
|
||||
});
|
||||
|
||||
page.port.emit("ping");
|
||||
};
|
||||
|
||||
|
||||
function isDestroyed(page) {
|
||||
try {
|
||||
page.postMessage("foo");
|
||||
}
|
||||
catch (err) {
|
||||
if (err.message == ERR_DESTROYED) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// require("sdk/test").run(exports);
|
||||
11
addon-sdk/source/test/addons/e10s-content/package.json
Normal file
11
addon-sdk/source/test/addons/e10s-content/package.json
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"name": "e10s-content",
|
||||
"title": "e10s-content",
|
||||
"id": "jid1-ZZaXFHAPlHDDD@jetpack",
|
||||
"description": "run content worker tests in e10s mode",
|
||||
"author": "Tomislav Jovanovic",
|
||||
"license": "MPL-2.0",
|
||||
"version": "0.1.0",
|
||||
"main": "./lib/main.js",
|
||||
"e10s": true
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
<!-- 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/. -->
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>HTML Localization</title>
|
||||
</head>
|
||||
<body>
|
||||
<div data-l10n-id="Not translated">Kept as-is</div>
|
||||
<ul data-l10n-id="Translated">
|
||||
<li>Inner html content is replaced,</li>
|
||||
<li data-l10n-id="text-content">
|
||||
Elements with data-l10n-id attribute whose parent element is translated
|
||||
will be replaced by the content of the translation.
|
||||
</li>
|
||||
</ul>
|
||||
<div data-l10n-id="text-content">No</div>
|
||||
<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
|
||||
38
addon-sdk/source/test/addons/e10s-l10n/locale/en.properties
Normal file
38
addon-sdk/source/test/addons/e10s-l10n/locale/en.properties
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
# 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/.
|
||||
|
||||
Translated= Yes
|
||||
|
||||
text-content=no <b>HTML</b> injection
|
||||
|
||||
downloadsCount=%d downloads
|
||||
downloadsCount[one]=one download
|
||||
|
||||
pluralTest=fallback to other
|
||||
pluralTest[zero]=optional zero form
|
||||
|
||||
explicitPlural[one]=one
|
||||
explicitPlural[other]=other
|
||||
|
||||
# You can use unicode char escaping in order to inject space at the beginning/
|
||||
# end of your string. (Regular spaces are automatically ignore by .properties
|
||||
# file parser)
|
||||
unicodeEscape = \u0020\u0040\u0020
|
||||
# this string equals to " @ "
|
||||
|
||||
# bug 1033309 plurals with multiple placeholders
|
||||
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
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
# 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/.
|
||||
|
||||
Translated= jes
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
Translated= Oui
|
||||
|
||||
placeholderString= Placeholder %s
|
||||
|
||||
# Plural forms
|
||||
%d downloads=%d téléchargements
|
||||
%d downloads[one]=%d téléchargement
|
||||
|
||||
downloadsCount=%d téléchargements
|
||||
downloadsCount[one]=%d téléchargement
|
||||
289
addon-sdk/source/test/addons/e10s-l10n/main.js
Normal file
289
addon-sdk/source/test/addons/e10s-l10n/main.js
Normal file
|
|
@ -0,0 +1,289 @@
|
|||
/* 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 prefs = require("sdk/preferences/service");
|
||||
const { Loader } = require('sdk/test/loader');
|
||||
const { resolveURI } = require('toolkit/loader');
|
||||
const { rootURI, isNative } = require("@loader/options");
|
||||
const { usingJSON } = require('sdk/l10n/json/core');
|
||||
|
||||
const PREF_MATCH_OS_LOCALE = "intl.locale.matchOS";
|
||||
const PREF_SELECTED_LOCALE = "general.useragent.locale";
|
||||
|
||||
function setLocale(locale) {
|
||||
prefs.set(PREF_MATCH_OS_LOCALE, false);
|
||||
prefs.set(PREF_SELECTED_LOCALE, locale);
|
||||
}
|
||||
|
||||
function resetLocale() {
|
||||
prefs.reset(PREF_MATCH_OS_LOCALE);
|
||||
prefs.reset(PREF_SELECTED_LOCALE);
|
||||
}
|
||||
|
||||
function definePseudo(loader, id, exports) {
|
||||
let uri = resolveURI(id, loader.mapping);
|
||||
loader.modules[uri] = { exports: exports };
|
||||
}
|
||||
|
||||
function createTest(locale, testFunction) {
|
||||
return function (assert, done) {
|
||||
let loader = Loader(module);
|
||||
// Change the locale before loading new l10n modules in order to load
|
||||
// the right .json file
|
||||
setLocale(locale);
|
||||
// Initialize main l10n module in order to load new locale files
|
||||
loader.require("sdk/l10n/loader").
|
||||
load(rootURI).
|
||||
then(null, function failure(error) {
|
||||
if (!isNative)
|
||||
assert.fail("Unable to load locales: " + error);
|
||||
}).
|
||||
then(function success(data) {
|
||||
definePseudo(loader, '@l10n/data', data ? data : null);
|
||||
// Execute the given test function
|
||||
try {
|
||||
testFunction(assert, loader, function onDone() {
|
||||
loader.unload();
|
||||
resetLocale();
|
||||
done();
|
||||
});
|
||||
}
|
||||
catch(e) {
|
||||
console.exception(e);
|
||||
}
|
||||
},
|
||||
function failure(error) {
|
||||
assert.fail("Unable to load locales: " + error);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
exports.testExactMatching = createTest("fr-FR", function(assert, loader, done) {
|
||||
let _ = loader.require("sdk/l10n").get;
|
||||
assert.equal(_("Not translated"), "Not translated",
|
||||
"Key not translated");
|
||||
assert.equal(_("Translated"), "Oui",
|
||||
"Simple key translated");
|
||||
|
||||
// Placeholders
|
||||
assert.equal(_("placeholderString", "works"), "Placeholder works",
|
||||
"Value with placeholder");
|
||||
assert.equal(_("Placeholder %s", "works"), "Placeholder works",
|
||||
"Key without value but with placeholder");
|
||||
assert.equal(_("Placeholders %2s %1s %s.", "working", "are", "correctly"),
|
||||
"Placeholders are working correctly.",
|
||||
"Multiple placeholders");
|
||||
|
||||
// Plurals
|
||||
assert.equal(_("downloadsCount", 0),
|
||||
"0 téléchargement",
|
||||
"PluralForm form 'one' for 0 in french");
|
||||
assert.equal(_("downloadsCount", 1),
|
||||
"1 téléchargement",
|
||||
"PluralForm form 'one' for 1 in french");
|
||||
assert.equal(_("downloadsCount", 2),
|
||||
"2 téléchargements",
|
||||
"PluralForm form 'other' for n > 1 in french");
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
exports.testHtmlLocalizationPageWorker = createTest("en-GB", function(assert, loader, done) {
|
||||
// Ensure initing html component that watch document creations
|
||||
// Note that this module is automatically initialized in
|
||||
// cuddlefish.js:Loader.main in regular addons. But it isn't for unit tests.
|
||||
let loaderHtmlL10n = loader.require("sdk/l10n/html");
|
||||
loaderHtmlL10n.enable();
|
||||
|
||||
let uri = require("sdk/self").data.url("test-localization.html");
|
||||
let worker = loader.require("sdk/page-worker").Page({
|
||||
contentURL: uri,
|
||||
contentScript: "new " + function ContentScriptScope() {
|
||||
let nodes = document.body.querySelectorAll("*[data-l10n-id]");
|
||||
self.postMessage([nodes[0].innerHTML,
|
||||
nodes[1].innerHTML,
|
||||
nodes[2].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(
|
||||
data[0],
|
||||
"Kept as-is",
|
||||
"Nodes with unknown id in .properties are kept 'as-is'"
|
||||
);
|
||||
assert.equal(data[1], "Yes", "HTML is translated");
|
||||
assert.equal(
|
||||
data[2],
|
||||
"no <b>HTML</b> injection",
|
||||
"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();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
exports.testHtmlLocalization = createTest("en-GB", function(assert, loader, done) {
|
||||
// Ensure initing html component that watch document creations
|
||||
// Note that this module is automatically initialized in
|
||||
// cuddlefish.js:Loader.main in regular addons. But it isn't for unit tests.
|
||||
let loaderHtmlL10n = loader.require("sdk/l10n/html");
|
||||
loaderHtmlL10n.enable();
|
||||
|
||||
let uri = require("sdk/self").data.url("test-localization.html");
|
||||
loader.require("sdk/tabs").open({
|
||||
url: uri,
|
||||
onReady: function(tab) {
|
||||
tab.attach({
|
||||
contentURL: uri,
|
||||
contentScript: "new " + function ContentScriptScope() {
|
||||
let nodes = document.body.querySelectorAll("*[data-l10n-id]");
|
||||
self.postMessage([nodes[0].innerHTML,
|
||||
nodes[1].innerHTML,
|
||||
nodes[2].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(
|
||||
data[0],
|
||||
"Kept as-is",
|
||||
"Nodes with unknown id in .properties are kept 'as-is'"
|
||||
);
|
||||
assert.equal(data[1], "Yes", "HTML is translated");
|
||||
assert.equal(
|
||||
data[2],
|
||||
"no <b>HTML</b> injection",
|
||||
"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.");
|
||||
|
||||
tab.close(done);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
exports.testEnUsLocaleName = createTest("en-US", function(assert, loader, done) {
|
||||
let _ = loader.require("sdk/l10n").get;
|
||||
|
||||
assert.equal(_("Not translated"), "Not translated",
|
||||
"String w/o translation is kept as-is");
|
||||
assert.equal(_("Translated"), "Yes",
|
||||
"String with translation is correctly translated");
|
||||
|
||||
// Check Unicode char escaping sequences
|
||||
assert.equal(_("unicodeEscape"), " @ ",
|
||||
"Unicode escaped sequances are correctly converted");
|
||||
|
||||
// Check plural forms regular matching
|
||||
assert.equal(_("downloadsCount", 0),
|
||||
"0 downloads",
|
||||
"PluralForm form 'other' for 0 in english");
|
||||
assert.equal(_("downloadsCount", 1),
|
||||
"one download",
|
||||
"PluralForm form 'one' for 1 in english");
|
||||
assert.equal(_("downloadsCount", 2),
|
||||
"2 downloads",
|
||||
"PluralForm form 'other' for n != 1 in english");
|
||||
|
||||
// Check optional plural forms
|
||||
assert.equal(_("pluralTest", 0),
|
||||
"optional zero form",
|
||||
"PluralForm form 'zero' can be optionaly specified. (Isn't mandatory in english)");
|
||||
assert.equal(_("pluralTest", 1),
|
||||
"fallback to other",
|
||||
"If the specific plural form is missing, we fallback to 'other'");
|
||||
|
||||
// Ensure that we can omit specifying the generic key without [other]
|
||||
// key[one] = ...
|
||||
// key[other] = ... # Instead of `key = ...`
|
||||
assert.equal(_("explicitPlural", 1),
|
||||
"one",
|
||||
"PluralForm form can be omitting generic key [i.e. without ...[other] at end of key)");
|
||||
assert.equal(_("explicitPlural", 10),
|
||||
"other",
|
||||
"PluralForm form can be omitting generic key [i.e. without ...[other] at end of key)");
|
||||
|
||||
assert.equal(_("first_identifier", "ONE", "TWO"), "the entries are ONE and TWO.", "first_identifier no count");
|
||||
assert.equal(_("first_identifier", 0, "ONE", "TWO"), "the entries are ONE and TWO.", "first_identifier with count = 0");
|
||||
assert.equal(_("first_identifier", 1, "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "first_identifier with count = 1");
|
||||
assert.equal(_("first_identifier", 2, "ONE", "TWO"), "the entries are ONE and TWO.", "first_identifier with count = 2");
|
||||
|
||||
assert.equal(_("second_identifier", "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "second_identifier with no count");
|
||||
assert.equal(_("second_identifier", 0, "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "second_identifier with count = 0");
|
||||
assert.equal(_("second_identifier", 1, "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "second_identifier with count = 1");
|
||||
assert.equal(_("second_identifier", 2, "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "second_identifier with count = 2");
|
||||
|
||||
assert.equal(_("third_identifier", "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "third_identifier with no count");
|
||||
assert.equal(_("third_identifier", 0, "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "third_identifier with count = 0");
|
||||
assert.equal(_("third_identifier", 2, "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "third_identifier with count = 2");
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
exports.testUsingJSON = function(assert) {
|
||||
assert.equal(usingJSON, !isNative, 'using json');
|
||||
}
|
||||
|
||||
exports.testShortLocaleName = createTest("eo", function(assert, loader, done) {
|
||||
let _ = loader.require("sdk/l10n").get;
|
||||
assert.equal(_("Not translated"), "Not translated",
|
||||
"String w/o translation is kept as-is");
|
||||
assert.equal(_("Translated"), "jes",
|
||||
"String with translation is correctly translated");
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
|
||||
// Before running tests, disable HTML service which is automatially enabled
|
||||
// in api-utils/addon/runner.js
|
||||
require('sdk/l10n/html').disable();
|
||||
|
||||
require("sdk/test/runner").runTestsFromModule(module);
|
||||
5
addon-sdk/source/test/addons/e10s-l10n/package.json
Normal file
5
addon-sdk/source/test/addons/e10s-l10n/package.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"id": "e10s-l10n@jetpack",
|
||||
"main": "./main.js",
|
||||
"version": "0.0.1"
|
||||
}
|
||||
578
addon-sdk/source/test/addons/e10s-remote/main.js
Normal file
578
addon-sdk/source/test/addons/e10s-remote/main.js
Normal file
|
|
@ -0,0 +1,578 @@
|
|||
/* 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 LOCAL_URI = "about:robots";
|
||||
const REMOTE_URI = "data:text/html;charset=utf-8,remote";
|
||||
|
||||
const { Cu } = require('chrome');
|
||||
const { Loader } = require('sdk/test/loader');
|
||||
const { getTabs, openTab, closeTab, setTabURL, getBrowserForTab, getURI } = require('sdk/tabs/utils');
|
||||
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
|
||||
const { cleanUI } = require("sdk/test/utils");
|
||||
const { setTimeout } = require("sdk/timers");
|
||||
const { promiseEvent, promiseDOMEvent, promiseEventOnItemAndContainer,
|
||||
waitForProcesses, getChildFrameCount, isE10S } = require("./utils");
|
||||
const { after } = require('sdk/test/utils');
|
||||
const { processID } = require('sdk/system/runtime');
|
||||
|
||||
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) {
|
||||
assert.pass("Skipping test in non-e10s mode");
|
||||
return;
|
||||
}
|
||||
|
||||
let window = getMostRecentBrowserWindow();
|
||||
|
||||
let tabs = getTabs(window);
|
||||
assert.equal(tabs.length, 1, "Should have just the one tab to start with");
|
||||
let tab = tabs[0];
|
||||
let browser = getBrowserForTab(tab);
|
||||
|
||||
let loader = new Loader(module);
|
||||
let { processes, frames } = yield waitForProcesses(loader);
|
||||
|
||||
let remoteProcess = Array.filter(processes, p => p.isRemote)[0];
|
||||
let localProcess = Array.filter(processes, p => !p.isRemote)[0];
|
||||
let remoteFrame = Array.filter(frames, f => f.process == remoteProcess)[0];
|
||||
|
||||
// Switch the remote tab to a local URI which should kill the remote process
|
||||
|
||||
let frameDetach = promiseEventOnItemAndContainer(assert, remoteFrame, frames, 'detach');
|
||||
let frameAttach = promiseTabFrameAttach(frames);
|
||||
let processDetach = promiseEventOnItemAndContainer(assert, remoteProcess, processes, 'detach');
|
||||
let browserLoad = promiseDOMEvent(browser, "load", true);
|
||||
setTabURL(tab, LOCAL_URI);
|
||||
// The load should kill the remote frame
|
||||
yield frameDetach;
|
||||
// And create a new frame in the local process
|
||||
let [newFrame] = yield frameAttach;
|
||||
assert.equal(newFrame.process, localProcess, "New frame should be in the local process");
|
||||
// And kill the process
|
||||
yield processDetach;
|
||||
yield browserLoad;
|
||||
|
||||
frameDetach = promiseEventOnItemAndContainer(assert, newFrame, frames, 'detach');
|
||||
let processAttach = promiseEvent(processes, 'attach');
|
||||
frameAttach = promiseTabFrameAttach(frames);
|
||||
browserLoad = promiseDOMEvent(browser, "load", true);
|
||||
setTabURL(tab, REMOTE_URI);
|
||||
// The load should kill the remote frame
|
||||
yield frameDetach;
|
||||
// And create a new remote process
|
||||
[remoteProcess] = yield processAttach;
|
||||
assert.ok(remoteProcess.isRemote, "Process should be remote");
|
||||
// And create a new frame in the remote process
|
||||
[newFrame] = yield frameAttach;
|
||||
assert.equal(newFrame.process, remoteProcess, "New frame should be in the remote process");
|
||||
yield browserLoad;
|
||||
|
||||
browserLoad = promiseDOMEvent(browser, "load", true);
|
||||
setTabURL(tab, "about:blank");
|
||||
yield browserLoad;
|
||||
|
||||
loader.unload();
|
||||
};
|
||||
|
||||
// Test that we find the right number of processes and that messaging between
|
||||
// them works and none of the streams cross
|
||||
exports["test process list"] = function*(assert) {
|
||||
let loader = new Loader(module);
|
||||
let { processes } = loader.require('sdk/remote/parent');
|
||||
|
||||
let processCount = 0;
|
||||
processes.forEvery(processes => processCount++);
|
||||
|
||||
yield waitForProcesses(loader);
|
||||
|
||||
let remoteProcesses = Array.filter(processes, process => process.isRemote);
|
||||
let localProcesses = Array.filter(processes, process => !process.isRemote);
|
||||
|
||||
assert.equal(localProcesses.length, 1, "Should always be one process");
|
||||
|
||||
if (isE10S) {
|
||||
assert.equal(remoteProcesses.length, 1, "Should be one remote process");
|
||||
}
|
||||
else {
|
||||
assert.equal(remoteProcesses.length, 0, "Should be no remote processes");
|
||||
}
|
||||
|
||||
assert.equal(processCount, processes.length, "Should have seen all processes");
|
||||
|
||||
processCount = 0;
|
||||
processes.forEvery(process => processCount++);
|
||||
|
||||
assert.equal(processCount, processes.length, "forEvery should send existing processes to the listener");
|
||||
|
||||
localProcesses[0].port.on('sdk/test/pong', (process, key) => {
|
||||
assert.equal(key, "local", "Should not have seen a pong from the local process with the wrong key");
|
||||
});
|
||||
|
||||
if (isE10S) {
|
||||
remoteProcesses[0].port.on('sdk/test/pong', (process, key) => {
|
||||
assert.equal(key, "remote", "Should not have seen a pong from the remote process with the wrong key");
|
||||
});
|
||||
}
|
||||
|
||||
let promise = promiseEventOnItemAndContainer(assert, localProcesses[0].port, processes.port, 'sdk/test/pong', localProcesses[0]);
|
||||
localProcesses[0].port.emit('sdk/test/ping', "local");
|
||||
|
||||
let reply = yield promise;
|
||||
assert.equal(reply[0], "local", "Saw the process reply with the right key");
|
||||
|
||||
if (isE10S) {
|
||||
promise = promiseEventOnItemAndContainer(assert, remoteProcesses[0].port, processes.port, 'sdk/test/pong', remoteProcesses[0]);
|
||||
remoteProcesses[0].port.emit('sdk/test/ping', "remote");
|
||||
|
||||
reply = yield promise;
|
||||
assert.equal(reply[0], "remote", "Saw the process reply with the right key");
|
||||
|
||||
assert.notEqual(localProcesses[0], remoteProcesses[0], "Processes should be different");
|
||||
}
|
||||
|
||||
loader.unload();
|
||||
};
|
||||
|
||||
// Test that the frame lists are kept up to date
|
||||
exports["test frame list"] = function*(assert) {
|
||||
function browserFrames(list) {
|
||||
return Array.filter(list, b => b.isTab).length;
|
||||
}
|
||||
|
||||
let window = getMostRecentBrowserWindow();
|
||||
|
||||
let tabs = getTabs(window);
|
||||
assert.equal(tabs.length, 1, "Should have just the one tab to start with");
|
||||
|
||||
let loader = new Loader(module);
|
||||
let { processes, frames } = yield waitForProcesses(loader);
|
||||
|
||||
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 = promiseTabFrameAttach(frames);
|
||||
let tab1 = openTab(window, LOCAL_URI);
|
||||
let [frame1] = yield promise;
|
||||
assert.ok(!!frame1, "Should have seen the new frame");
|
||||
assert.ok(!frame1.process.isRemote, "Frame should not be remote");
|
||||
|
||||
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 = promiseTabFrameAttach(frames);
|
||||
let tab2 = openTab(window, REMOTE_URI);
|
||||
let [frame2] = yield promise;
|
||||
assert.ok(!!frame2, "Should have seen the new frame");
|
||||
if (isE10S)
|
||||
assert.ok(frame2.process.isRemote, "Frame should be remote");
|
||||
else
|
||||
assert.ok(!frame2.process.isRemote, "Frame should not be remote");
|
||||
|
||||
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");
|
||||
|
||||
frames.port.emit('sdk/test/ping')
|
||||
yield new Promise(resolve => {
|
||||
let count = 0;
|
||||
let listener = () => {
|
||||
console.log("Saw pong");
|
||||
count++;
|
||||
if (count == frames.length) {
|
||||
frames.port.off('sdk/test/pong', listener);
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
frames.port.on('sdk/test/pong', listener);
|
||||
});
|
||||
|
||||
let badListener = () => {
|
||||
assert.fail("Should not have seen a response through this frame");
|
||||
}
|
||||
frame1.port.on('sdk/test/pong', badListener);
|
||||
frame2.port.emit('sdk/test/ping', 'b');
|
||||
let [key] = yield promiseEventOnItemAndContainer(assert, frame2.port, frames.port, 'sdk/test/pong', frame2);
|
||||
assert.equal(key, 'b', "Should have seen the right response");
|
||||
frame1.port.off('sdk/test/pong', badListener);
|
||||
|
||||
frame2.port.on('sdk/test/pong', badListener);
|
||||
frame1.port.emit('sdk/test/ping', 'b');
|
||||
[key] = yield promiseEventOnItemAndContainer(assert, frame1.port, frames.port, 'sdk/test/pong', frame1);
|
||||
assert.equal(key, 'b', "Should have seen the right response");
|
||||
frame2.port.off('sdk/test/pong', badListener);
|
||||
|
||||
promise = promiseEventOnItemAndContainer(assert, frame1, frames, 'detach');
|
||||
closeTab(tab1);
|
||||
yield promise;
|
||||
|
||||
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 = promiseEventOnItemAndContainer(assert, frame2, frames, 'detach');
|
||||
closeTab(tab2);
|
||||
yield promise;
|
||||
|
||||
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");
|
||||
|
||||
loader.unload();
|
||||
};
|
||||
|
||||
// Test that multiple loaders get their own loaders in the child and messages
|
||||
// don't cross. Unload should work
|
||||
exports["test new loader"] = function*(assert) {
|
||||
let loader1 = new Loader(module);
|
||||
let { processes: processes1 } = yield waitForProcesses(loader1);
|
||||
|
||||
let loader2 = new Loader(module);
|
||||
let { processes: processes2 } = yield waitForProcesses(loader2);
|
||||
|
||||
let process1 = [...processes1][0];
|
||||
let process2 = [...processes2][0];
|
||||
|
||||
process1.port.on('sdk/test/pong', (process, key) => {
|
||||
assert.equal(key, "a", "Should have seen the right pong");
|
||||
});
|
||||
|
||||
process2.port.on('sdk/test/pong', (process, key) => {
|
||||
assert.equal(key, "b", "Should have seen the right pong");
|
||||
});
|
||||
|
||||
process1.port.emit('sdk/test/ping', 'a');
|
||||
yield promiseEvent(process1.port, 'sdk/test/pong');
|
||||
|
||||
process2.port.emit('sdk/test/ping', 'b');
|
||||
yield promiseEvent(process2.port, 'sdk/test/pong');
|
||||
|
||||
loader1.unload();
|
||||
|
||||
process2.port.emit('sdk/test/ping', 'b');
|
||||
yield promiseEvent(process2.port, 'sdk/test/pong');
|
||||
|
||||
loader2.unload();
|
||||
};
|
||||
|
||||
// Test that unloading the loader unloads the child instances
|
||||
exports["test unload"] = function*(assert) {
|
||||
let window = getMostRecentBrowserWindow();
|
||||
let loader = new Loader(module);
|
||||
let { frames } = yield waitForProcesses(loader);
|
||||
|
||||
let promise = promiseTabFrameAttach(frames);
|
||||
let tab = openTab(window, "data:,<html/>");
|
||||
let browser = getBrowserForTab(tab);
|
||||
yield promiseDOMEvent(browser, "load", true);
|
||||
let [frame] = yield promise;
|
||||
assert.ok(!!frame, "Should have seen the new frame");
|
||||
|
||||
promise = promiseDOMEvent(browser, 'hashchange');
|
||||
frame.port.emit('sdk/test/testunload');
|
||||
loader.unload("shutdown");
|
||||
yield promise;
|
||||
|
||||
let hash = getURI(tab).replace(/.*#/, "");
|
||||
assert.equal(hash, "unloaded:shutdown", "Saw the correct hash change.")
|
||||
|
||||
closeTab(tab);
|
||||
}
|
||||
|
||||
// Test that unloading the loader causes the child to see frame detach events
|
||||
exports["test frame detach on unload"] = function*(assert) {
|
||||
let window = getMostRecentBrowserWindow();
|
||||
let loader = new Loader(module);
|
||||
let { frames } = yield waitForProcesses(loader);
|
||||
|
||||
let promise = promiseTabFrameAttach(frames);
|
||||
let tab = openTab(window, "data:,<html/>");
|
||||
let browser = getBrowserForTab(tab);
|
||||
yield promiseDOMEvent(browser, "load", true);
|
||||
let [frame] = yield promise;
|
||||
assert.ok(!!frame, "Should have seen the new frame");
|
||||
|
||||
promise = promiseDOMEvent(browser, 'hashchange');
|
||||
frame.port.emit('sdk/test/testdetachonunload');
|
||||
loader.unload("shutdown");
|
||||
yield promise;
|
||||
|
||||
let hash = getURI(tab).replace(/.*#/, "");
|
||||
assert.equal(hash, "unloaded", "Saw the correct hash change.")
|
||||
|
||||
closeTab(tab);
|
||||
}
|
||||
|
||||
// Test that DOM event listener on the frame object works
|
||||
exports["test frame event listeners"] = function*(assert) {
|
||||
let window = getMostRecentBrowserWindow();
|
||||
let loader = new Loader(module);
|
||||
let { frames } = yield waitForProcesses(loader);
|
||||
|
||||
let promise = promiseTabFrameAttach(frames);
|
||||
let tab = openTab(window, "data:text/html,<html></html>");
|
||||
let browser = getBrowserForTab(tab);
|
||||
yield promiseDOMEvent(browser, "load", true);
|
||||
let [frame] = yield promise;
|
||||
assert.ok(!!frame, "Should have seen the new frame");
|
||||
|
||||
frame.port.emit('sdk/test/registerframeevent');
|
||||
promise = Promise.all([
|
||||
promiseEvent(frame.port, 'sdk/test/sawreply'),
|
||||
promiseEvent(frame.port, 'sdk/test/eventsent')
|
||||
]);
|
||||
|
||||
frame.port.emit('sdk/test/sendevent');
|
||||
yield promise;
|
||||
|
||||
frame.port.emit('sdk/test/unregisterframeevent');
|
||||
promise = promiseEvent(frame.port, 'sdk/test/eventsent');
|
||||
frame.port.on('sdk/test/sawreply', () => {
|
||||
assert.fail("Should not have seen the event listener reply");
|
||||
});
|
||||
|
||||
frame.port.emit('sdk/test/sendevent');
|
||||
yield promise;
|
||||
|
||||
closeTab(tab);
|
||||
loader.unload();
|
||||
}
|
||||
|
||||
// Test that DOM event listener on the frames object works
|
||||
exports["test frames event listeners"] = function*(assert) {
|
||||
let window = getMostRecentBrowserWindow();
|
||||
let loader = new Loader(module);
|
||||
let { frames } = yield waitForProcesses(loader);
|
||||
|
||||
let promise = promiseTabFrameAttach(frames);
|
||||
let tab = openTab(window, "data:text/html,<html></html>");
|
||||
let browser = getBrowserForTab(tab);
|
||||
yield promiseDOMEvent(browser, "load", true);
|
||||
let [frame] = yield promise;
|
||||
assert.ok(!!frame, "Should have seen the new frame");
|
||||
|
||||
frame.port.emit('sdk/test/registerframesevent');
|
||||
promise = Promise.all([
|
||||
promiseEvent(frame.port, 'sdk/test/sawreply'),
|
||||
promiseEvent(frame.port, 'sdk/test/eventsent')
|
||||
]);
|
||||
|
||||
frame.port.emit('sdk/test/sendevent');
|
||||
yield promise;
|
||||
|
||||
frame.port.emit('sdk/test/unregisterframesevent');
|
||||
promise = promiseEvent(frame.port, 'sdk/test/eventsent');
|
||||
frame.port.on('sdk/test/sawreply', () => {
|
||||
assert.fail("Should not have seen the event listener reply");
|
||||
});
|
||||
|
||||
frame.port.emit('sdk/test/sendevent');
|
||||
yield promise;
|
||||
|
||||
closeTab(tab);
|
||||
loader.unload();
|
||||
}
|
||||
|
||||
// Test that unloading unregisters frame DOM events
|
||||
exports["test unload removes frame event listeners"] = function*(assert) {
|
||||
let window = getMostRecentBrowserWindow();
|
||||
let loader = new Loader(module);
|
||||
let { frames } = yield waitForProcesses(loader);
|
||||
|
||||
let loader2 = new Loader(module);
|
||||
let { frames: frames2 } = yield waitForProcesses(loader2);
|
||||
|
||||
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);
|
||||
let [frame] = yield promise;
|
||||
let [frame2] = yield promise2;
|
||||
assert.ok(!!frame && !!frame2, "Should have seen the new frame");
|
||||
|
||||
frame.port.emit('sdk/test/registerframeevent');
|
||||
promise = Promise.all([
|
||||
promiseEvent(frame2.port, 'sdk/test/sawreply'),
|
||||
promiseEvent(frame2.port, 'sdk/test/eventsent')
|
||||
]);
|
||||
|
||||
frame2.port.emit('sdk/test/sendevent');
|
||||
yield promise;
|
||||
|
||||
loader.unload();
|
||||
|
||||
promise = promiseEvent(frame2.port, 'sdk/test/eventsent');
|
||||
frame2.port.on('sdk/test/sawreply', () => {
|
||||
assert.fail("Should not have seen the event listener reply");
|
||||
});
|
||||
|
||||
frame2.port.emit('sdk/test/sendevent');
|
||||
yield promise;
|
||||
|
||||
closeTab(tab);
|
||||
loader2.unload();
|
||||
}
|
||||
|
||||
// Test that unloading unregisters frames DOM events
|
||||
exports["test unload removes frames event listeners"] = function*(assert) {
|
||||
let window = getMostRecentBrowserWindow();
|
||||
let loader = new Loader(module);
|
||||
let { frames } = yield waitForProcesses(loader);
|
||||
|
||||
let loader2 = new Loader(module);
|
||||
let { frames: frames2 } = yield waitForProcesses(loader2);
|
||||
|
||||
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);
|
||||
let [frame] = yield promise;
|
||||
let [frame2] = yield promise2;
|
||||
assert.ok(!!frame && !!frame2, "Should have seen the new frame");
|
||||
|
||||
frame.port.emit('sdk/test/registerframesevent');
|
||||
promise = Promise.all([
|
||||
promiseEvent(frame2.port, 'sdk/test/sawreply'),
|
||||
promiseEvent(frame2.port, 'sdk/test/eventsent')
|
||||
]);
|
||||
|
||||
frame2.port.emit('sdk/test/sendevent');
|
||||
yield promise;
|
||||
|
||||
loader.unload();
|
||||
|
||||
promise = promiseEvent(frame2.port, 'sdk/test/eventsent');
|
||||
frame2.port.on('sdk/test/sawreply', () => {
|
||||
assert.fail("Should not have seen the event listener reply");
|
||||
});
|
||||
|
||||
frame2.port.emit('sdk/test/sendevent');
|
||||
yield promise;
|
||||
|
||||
closeTab(tab);
|
||||
loader2.unload();
|
||||
}
|
||||
|
||||
// Check that the child frame has the right properties
|
||||
exports["test frame properties"] = function*(assert) {
|
||||
let loader = new Loader(module);
|
||||
let { frames } = yield waitForProcesses(loader);
|
||||
|
||||
let promise = new Promise(resolve => {
|
||||
let count = frames.length;
|
||||
let listener = (frame, properties) => {
|
||||
assert.equal(properties.isTab, frame.isTab,
|
||||
"Child frame should have the same isTab property");
|
||||
|
||||
if (--count == 0) {
|
||||
frames.port.off('sdk/test/replyproperties', listener);
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
|
||||
frames.port.on('sdk/test/replyproperties', listener);
|
||||
})
|
||||
|
||||
frames.port.emit('sdk/test/checkproperties');
|
||||
yield promise;
|
||||
|
||||
loader.unload();
|
||||
}
|
||||
|
||||
// Check that non-remote processes have the same process ID and remote processes
|
||||
// have different IDs
|
||||
exports["test processID"] = function*(assert) {
|
||||
let loader = new Loader(module);
|
||||
let { processes } = yield waitForProcesses(loader);
|
||||
|
||||
for (let process of processes) {
|
||||
process.port.emit('sdk/test/getprocessid');
|
||||
let [p, ID] = yield promiseEvent(process.port, 'sdk/test/processid');
|
||||
if (process.isRemote) {
|
||||
assert.notEqual(ID, processID, "Remote processes should have a different process ID");
|
||||
}
|
||||
else {
|
||||
assert.equal(ID, processID, "Remote processes should have the same process ID");
|
||||
}
|
||||
}
|
||||
|
||||
loader.unload();
|
||||
}
|
||||
|
||||
// Check that sdk/remote/parent and sdk/remote/child can only be loaded in the
|
||||
// appropriate loaders
|
||||
exports["test cannot load in wrong loader"] = function*(assert) {
|
||||
let loader = new Loader(module);
|
||||
let { processes } = yield waitForProcesses(loader);
|
||||
|
||||
try {
|
||||
require('sdk/remote/child');
|
||||
assert.fail("Should not have been able to load sdk/remote/child");
|
||||
}
|
||||
catch (e) {
|
||||
assert.ok(/Cannot load sdk\/remote\/child in a main process loader/.test(e),
|
||||
"Should have seen the right exception.");
|
||||
}
|
||||
|
||||
for (let process of processes) {
|
||||
processes.port.emit('sdk/test/parentload');
|
||||
let [_, isChildLoader, loaded, message] = yield promiseEvent(processes.port, 'sdk/test/parentload');
|
||||
assert.ok(isChildLoader, "Process should see itself in a child loader.");
|
||||
assert.ok(!loaded, "Process couldn't load sdk/remote/parent.");
|
||||
assert.ok(/Cannot load sdk\/remote\/parent in a child loader/.test(message),
|
||||
"Should have seen the right exception.");
|
||||
}
|
||||
|
||||
loader.unload();
|
||||
};
|
||||
|
||||
exports["test send cpow"] = function*(assert) {
|
||||
if (!isE10S) {
|
||||
assert.pass("Skipping test in non-e10s mode");
|
||||
return;
|
||||
}
|
||||
|
||||
let window = getMostRecentBrowserWindow();
|
||||
|
||||
let tabs = getTabs(window);
|
||||
assert.equal(tabs.length, 1, "Should have just the one tab to start with");
|
||||
let tab = tabs[0];
|
||||
let browser = getBrowserForTab(tab);
|
||||
|
||||
assert.ok(Cu.isCrossProcessWrapper(browser.contentWindow),
|
||||
"Should have a CPOW for the browser content window");
|
||||
|
||||
let loader = new Loader(module);
|
||||
let { processes } = yield waitForProcesses(loader);
|
||||
|
||||
processes.port.emitCPOW('sdk/test/cpow', ['foobar'], { window: browser.contentWindow });
|
||||
let [process, arg, id] = yield promiseEvent(processes.port, 'sdk/test/cpow');
|
||||
|
||||
assert.ok(process.isRemote, "Response should come from the remote process");
|
||||
assert.equal(arg, "foobar", "Argument should have passed through");
|
||||
assert.equal(id, browser.outerWindowID, "Should have got the ID from the child");
|
||||
};
|
||||
|
||||
after(exports, function*(name, assert) {
|
||||
yield cleanUI();
|
||||
});
|
||||
|
||||
require('sdk/test/runner').runTestsFromModule(module);
|
||||
9
addon-sdk/source/test/addons/e10s-remote/package.json
Normal file
9
addon-sdk/source/test/addons/e10s-remote/package.json
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"name": "e10s-remote",
|
||||
"title": "e10s-remote",
|
||||
"id": "remote@jetpack",
|
||||
"description": "Run remote tests",
|
||||
"version": "1.0.0",
|
||||
"main": "main.js",
|
||||
"e10s": true
|
||||
}
|
||||
129
addon-sdk/source/test/addons/e10s-remote/remote-module.js
Normal file
129
addon-sdk/source/test/addons/e10s-remote/remote-module.js
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const { when } = require('sdk/system/unload');
|
||||
const { process, frames } = require('sdk/remote/child');
|
||||
const { loaderID } = require('@loader/options');
|
||||
const { processID } = require('sdk/system/runtime');
|
||||
const system = require('sdk/system/events');
|
||||
const { Cu } = require('chrome');
|
||||
const { isChildLoader } = require('sdk/remote/core');
|
||||
const { getOuterId } = require('sdk/window/utils');
|
||||
|
||||
function log(str) {
|
||||
console.log("remote[" + loaderID + "][" + processID + "]: " + str);
|
||||
}
|
||||
|
||||
log("module loaded");
|
||||
|
||||
process.port.emit('sdk/test/load');
|
||||
|
||||
process.port.on('sdk/test/ping', (process, key) => {
|
||||
log("received process ping");
|
||||
process.port.emit('sdk/test/pong', key);
|
||||
});
|
||||
|
||||
var frameCount = 0;
|
||||
frames.forEvery(frame => {
|
||||
frameCount++;
|
||||
frame.on('detach', () => {
|
||||
frameCount--;
|
||||
});
|
||||
|
||||
frame.port.on('sdk/test/ping', (frame, key) => {
|
||||
log("received frame ping");
|
||||
frame.port.emit('sdk/test/pong', key);
|
||||
});
|
||||
});
|
||||
|
||||
frames.port.on('sdk/test/checkproperties', frame => {
|
||||
frame.port.emit('sdk/test/replyproperties', {
|
||||
isTab: frame.isTab
|
||||
});
|
||||
});
|
||||
|
||||
process.port.on('sdk/test/count', () => {
|
||||
log("received count ping");
|
||||
process.port.emit('sdk/test/count', frameCount);
|
||||
});
|
||||
|
||||
process.port.on('sdk/test/getprocessid', () => {
|
||||
process.port.emit('sdk/test/processid', processID);
|
||||
});
|
||||
|
||||
frames.port.on('sdk/test/testunload', (frame) => {
|
||||
// Cache the content since the frame will have been destroyed by the time
|
||||
// we see the unload event.
|
||||
let content = frame.content;
|
||||
when((reason) => {
|
||||
content.location = "#unloaded:" + reason;
|
||||
});
|
||||
});
|
||||
|
||||
frames.port.on('sdk/test/testdetachonunload', (frame) => {
|
||||
let content = frame.content;
|
||||
frame.on('detach', () => {
|
||||
console.log("Detach from " + frame.content.location);
|
||||
frame.content.location = "#unloaded";
|
||||
});
|
||||
});
|
||||
|
||||
frames.port.on('sdk/test/sendevent', (frame) => {
|
||||
let doc = frame.content.document;
|
||||
|
||||
let listener = () => {
|
||||
frame.port.emit('sdk/test/sawreply');
|
||||
}
|
||||
|
||||
system.on("Test:Reply", listener);
|
||||
let event = new frame.content.CustomEvent("Test:Event");
|
||||
doc.dispatchEvent(event);
|
||||
system.off("Test:Reply", listener);
|
||||
frame.port.emit('sdk/test/eventsent');
|
||||
});
|
||||
|
||||
process.port.on('sdk/test/parentload', () => {
|
||||
let loaded = false;
|
||||
let message = "";
|
||||
try {
|
||||
require('sdk/remote/parent');
|
||||
loaded = true;
|
||||
}
|
||||
catch (e) {
|
||||
message = "" + e;
|
||||
}
|
||||
|
||||
process.port.emit('sdk/test/parentload',
|
||||
isChildLoader,
|
||||
loaded,
|
||||
message
|
||||
)
|
||||
});
|
||||
|
||||
function listener(event) {
|
||||
// Use the raw observer service here since it will be usable even if the
|
||||
// loader has unloaded
|
||||
let { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
|
||||
Services.obs.notifyObservers(null, "Test:Reply", "");
|
||||
}
|
||||
|
||||
frames.port.on('sdk/test/registerframesevent', (frame) => {
|
||||
frames.addEventListener("Test:Event", listener, true);
|
||||
});
|
||||
|
||||
frames.port.on('sdk/test/unregisterframesevent', (frame) => {
|
||||
frames.removeEventListener("Test:Event", listener, true);
|
||||
});
|
||||
|
||||
frames.port.on('sdk/test/registerframeevent', (frame) => {
|
||||
frame.addEventListener("Test:Event", listener, true);
|
||||
});
|
||||
|
||||
frames.port.on('sdk/test/unregisterframeevent', (frame) => {
|
||||
frame.removeEventListener("Test:Event", listener, true);
|
||||
});
|
||||
|
||||
process.port.on('sdk/test/cpow', (process, arg, cpows) => {
|
||||
process.port.emit('sdk/test/cpow', arg, getOuterId(cpows.window));
|
||||
});
|
||||
110
addon-sdk/source/test/addons/e10s-remote/utils.js
Normal file
110
addon-sdk/source/test/addons/e10s-remote/utils.js
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
/* 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 { Cu } = require('chrome');
|
||||
const { Task: { async } } = Cu.import('resource://gre/modules/Task.jsm', {});
|
||||
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
|
||||
|
||||
const REMOTE_MODULE = "./remote-module";
|
||||
|
||||
function promiseEvent(emitter, event) {
|
||||
console.log("Waiting for " + event);
|
||||
return new Promise(resolve => {
|
||||
emitter.once(event, (...args) => {
|
||||
console.log("Saw " + event);
|
||||
resolve(args);
|
||||
});
|
||||
});
|
||||
}
|
||||
exports.promiseEvent = promiseEvent;
|
||||
|
||||
function promiseDOMEvent(target, event, isCapturing = false) {
|
||||
console.log("Waiting for " + event);
|
||||
return new Promise(resolve => {
|
||||
let listener = (event) => {
|
||||
target.removeEventListener(event, listener, isCapturing);
|
||||
resolve(event);
|
||||
};
|
||||
target.addEventListener(event, listener, isCapturing);
|
||||
})
|
||||
}
|
||||
exports.promiseDOMEvent = promiseDOMEvent;
|
||||
|
||||
const promiseEventOnItemAndContainer = async(function*(assert, itemport, container, event, item = itemport) {
|
||||
let itemEvent = promiseEvent(itemport, event);
|
||||
let containerEvent = promiseEvent(container, event);
|
||||
|
||||
let itemArgs = yield itemEvent;
|
||||
let containerArgs = yield containerEvent;
|
||||
|
||||
assert.equal(containerArgs[0], item, "Should have seen a container event for the right item");
|
||||
assert.equal(JSON.stringify(itemArgs), JSON.stringify(containerArgs), "Arguments should have matched");
|
||||
|
||||
// Strip off the item from the returned arguments
|
||||
return itemArgs.slice(1);
|
||||
});
|
||||
exports.promiseEventOnItemAndContainer = promiseEventOnItemAndContainer;
|
||||
|
||||
const waitForProcesses = async(function*(loader) {
|
||||
console.log("Starting remote");
|
||||
let { processes, frames, remoteRequire } = loader.require('sdk/remote/parent');
|
||||
remoteRequire(REMOTE_MODULE, module);
|
||||
|
||||
let events = [];
|
||||
|
||||
// In e10s we should expect to see two processes
|
||||
let expectedCount = isE10S ? 2 : 1;
|
||||
|
||||
yield new Promise(resolve => {
|
||||
let count = 0;
|
||||
|
||||
// Wait for a process to be detected
|
||||
let listener = process => {
|
||||
console.log("Saw a process attach");
|
||||
// Wait for the remote module to load in this process
|
||||
process.port.once('sdk/test/load', () => {
|
||||
console.log("Saw a remote module load");
|
||||
count++;
|
||||
if (count == expectedCount) {
|
||||
processes.off('attach', listener);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
processes.on('attach', listener);
|
||||
});
|
||||
|
||||
console.log("Remote ready");
|
||||
return { processes, frames, remoteRequire };
|
||||
});
|
||||
exports.waitForProcesses = waitForProcesses;
|
||||
|
||||
// Counts the frames in all the child processes
|
||||
const getChildFrameCount = async(function*(processes) {
|
||||
let frameCount = 0;
|
||||
|
||||
for (let process of processes) {
|
||||
process.port.emit('sdk/test/count');
|
||||
let [p, count] = yield promiseEvent(process.port, 'sdk/test/count');
|
||||
frameCount += count;
|
||||
}
|
||||
|
||||
return frameCount;
|
||||
});
|
||||
exports.getChildFrameCount = getChildFrameCount;
|
||||
|
||||
const mainWindow = getMostRecentBrowserWindow();
|
||||
const isE10S = mainWindow.gMultiProcessBrowser;
|
||||
exports.isE10S = isE10S;
|
||||
|
||||
if (isE10S) {
|
||||
console.log("Testing in E10S mode");
|
||||
// We expect a child process to already be present, make sure that is the case
|
||||
mainWindow.XULBrowserWindow.forceInitialBrowserRemote();
|
||||
}
|
||||
else {
|
||||
console.log("Testing in non-E10S mode");
|
||||
}
|
||||
23
addon-sdk/source/test/addons/e10s-tabs/lib/main.js
Normal file
23
addon-sdk/source/test/addons/e10s-tabs/lib/main.js
Normal 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';
|
||||
|
||||
const { merge } = require('sdk/util/object');
|
||||
const { version } = require('sdk/system');
|
||||
|
||||
const SKIPPING_TESTS = {
|
||||
"test skip": (assert) => assert.pass("nothing to test here")
|
||||
};
|
||||
|
||||
merge(module.exports, require('./test-tab'));
|
||||
merge(module.exports, require('./test-tab-events'));
|
||||
merge(module.exports, require('./test-tab-observer'));
|
||||
merge(module.exports, require('./test-tab-utils'));
|
||||
|
||||
// run e10s tests only on builds from trunk, fx-team, Nightly..
|
||||
if (!version.endsWith('a1')) {
|
||||
module.exports = SKIPPING_TESTS;
|
||||
}
|
||||
|
||||
require('sdk/test/runner').runTestsFromModule(module);
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
/* 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 { loader } = LoaderWithHookedConsole(module);
|
||||
|
||||
const pb = loader.require('sdk/private-browsing');
|
||||
const pbUtils = loader.require('sdk/private-browsing/utils');
|
||||
const xulApp = require("sdk/system/xul-app");
|
||||
const { open: openWindow, getMostRecentBrowserWindow } = require('sdk/window/utils');
|
||||
const { openTab, getTabContentWindow, getActiveTab, setTabURL, closeTab } = require('sdk/tabs/utils');
|
||||
const promise = require("sdk/core/promise");
|
||||
const windowHelpers = require('sdk/window/helpers');
|
||||
const events = require("sdk/system/events");
|
||||
|
||||
function LoaderWithHookedConsole(module) {
|
||||
let globals = {};
|
||||
let errors = [];
|
||||
|
||||
globals.console = Object.create(console, {
|
||||
error: {
|
||||
value: function(e) {
|
||||
errors.push(e);
|
||||
if (!/DEPRECATED:/.test(e)) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let loader = Loader(module, globals);
|
||||
|
||||
return {
|
||||
loader: loader,
|
||||
errors: errors
|
||||
}
|
||||
}
|
||||
|
||||
exports.pb = pb;
|
||||
exports.pbUtils = pbUtils;
|
||||
exports.LoaderWithHookedConsole = LoaderWithHookedConsole;
|
||||
|
||||
exports.openWebpage = function openWebpage(url, enablePrivate) {
|
||||
if (xulApp.is("Fennec")) {
|
||||
let chromeWindow = getMostRecentBrowserWindow();
|
||||
let rawTab = openTab(chromeWindow, url, {
|
||||
isPrivate: enablePrivate
|
||||
});
|
||||
|
||||
return {
|
||||
ready: promise.resolve(getTabContentWindow(rawTab)),
|
||||
close: function () {
|
||||
closeTab(rawTab);
|
||||
// Returns a resolved promise as there is no need to wait
|
||||
return promise.resolve();
|
||||
}
|
||||
};
|
||||
}
|
||||
else {
|
||||
let win = openWindow(null, {
|
||||
features: {
|
||||
private: enablePrivate
|
||||
}
|
||||
});
|
||||
let deferred = promise.defer();
|
||||
|
||||
// Wait for delayed startup code to be executed, in order to ensure
|
||||
// that the window is really ready
|
||||
events.on("browser-delayed-startup-finished", function onReady({subject}) {
|
||||
if (subject == win) {
|
||||
events.off("browser-delayed-startup-finished", onReady);
|
||||
deferred.resolve(win);
|
||||
|
||||
let rawTab = getActiveTab(win);
|
||||
setTabURL(rawTab, url);
|
||||
deferred.resolve(getTabContentWindow(rawTab));
|
||||
}
|
||||
}, true);
|
||||
|
||||
return {
|
||||
ready: deferred.promise,
|
||||
close: function () {
|
||||
return windowHelpers.close(win);
|
||||
}
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
238
addon-sdk/source/test/addons/e10s-tabs/lib/test-tab-events.js
Normal file
238
addon-sdk/source/test/addons/e10s-tabs/lib/test-tab-events.js
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
/* 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, defer } = require("sdk/core/promise");
|
||||
|
||||
var isFennec = require("sdk/system/xul-app").is("Fennec");
|
||||
|
||||
function test(options) {
|
||||
return function(assert, done) {
|
||||
let tabEvents = [];
|
||||
let tabs = [];
|
||||
let { promise, resolve: resolveP } = defer();
|
||||
let win = isFennec ? resolve(getMostRecentBrowserWindow()) :
|
||||
open(null, {
|
||||
features: { private: true, toolbar:true, chrome: true }
|
||||
});
|
||||
let window = null;
|
||||
|
||||
// Firefox events are fired sync; Fennec events async
|
||||
// this normalizes the tests
|
||||
function handler (event) {
|
||||
tabEvents.push(event);
|
||||
runIfReady();
|
||||
}
|
||||
|
||||
function runIfReady () {
|
||||
let releventEvents = getRelatedEvents(tabEvents, tabs);
|
||||
if (options.readyWhen(releventEvents))
|
||||
options.end({
|
||||
tabs: tabs,
|
||||
events: releventEvents,
|
||||
assert: assert,
|
||||
done: resolveP
|
||||
});
|
||||
}
|
||||
|
||||
win.then(function(w) {
|
||||
window = w;
|
||||
on(events, "data", handler);
|
||||
options.start({ tabs: tabs, window: window });
|
||||
|
||||
// Execute here for synchronous FF events, as the handlers
|
||||
// were called before tabs were pushed to `tabs`
|
||||
runIfReady();
|
||||
return promise;
|
||||
}).then(function() {
|
||||
off(events, "data", handler);
|
||||
return isFennec ? null : close(window);
|
||||
}).then(done, assert.fail);
|
||||
};
|
||||
}
|
||||
|
||||
// Just making sure that tab events work for already opened tabs not only
|
||||
// for new windows.
|
||||
exports["test current window"] = test({
|
||||
readyWhen: events => events.length === 3,
|
||||
start: ({ tabs, window }) => {
|
||||
let tab = utils.openTab(window, 'data:text/plain,open');
|
||||
tabs.push(tab);
|
||||
utils.closeTab(tab);
|
||||
},
|
||||
end: ({ tabs, events, assert, done }) => {
|
||||
let [open, select, close] = events;
|
||||
let tab = tabs[0];
|
||||
|
||||
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);
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
exports["test open"] = test({
|
||||
readyWhen: events => events.length === 2,
|
||||
start: ({ tabs, window }) => {
|
||||
tabs.push(utils.openTab(window, 'data:text/plain,open'));
|
||||
},
|
||||
end: ({ tabs, events, assert, done }) => {
|
||||
let [open, select] = events;
|
||||
let tab = tabs[0];
|
||||
|
||||
assert.equal(open.type, "TabOpen");
|
||||
assert.equal(open.target, tab);
|
||||
|
||||
assert.equal(select.type, "TabSelect");
|
||||
assert.equal(select.target, tab);
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
exports["test open -> close"] = test({
|
||||
readyWhen: events => events.length === 3,
|
||||
start: ({ tabs, window }) => {
|
||||
// First tab is useless we just open it so that closing second tab won't
|
||||
// close window on some platforms.
|
||||
utils.openTab(window, 'data:text/plain,ignore');
|
||||
let tab = utils.openTab(window, 'data:text/plain,open-close');
|
||||
tabs.push(tab);
|
||||
utils.closeTab(tab);
|
||||
},
|
||||
end: ({ tabs, events, assert, done }) => {
|
||||
let [open, select, close] = events;
|
||||
let tab = tabs[0];
|
||||
|
||||
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);
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
exports["test open -> open -> select"] = test({
|
||||
readyWhen: events => events.length === 5,
|
||||
start: ({tabs, window}) => {
|
||||
tabs.push(utils.openTab(window, 'data:text/plain,Tab-1'));
|
||||
tabs.push(utils.openTab(window, 'data:text/plain,Tab-2'));
|
||||
utils.activateTab(tabs[0], window);
|
||||
},
|
||||
end: ({ tabs, events, assert, done }) => {
|
||||
let [ tab1, tab2 ] = tabs;
|
||||
let tab1Events = 0;
|
||||
getRelatedEvents(events, tab1).map(event => {
|
||||
tab1Events++;
|
||||
if (tab1Events === 1)
|
||||
assert.equal(event.type, "TabOpen", "first tab opened");
|
||||
else
|
||||
assert.equal(event.type, "TabSelect", "first tab selected");
|
||||
assert.equal(event.target, tab1);
|
||||
});
|
||||
assert.equal(tab1Events, 3, "first tab has 3 events");
|
||||
|
||||
let tab2Opened;
|
||||
getRelatedEvents(events, tab2).map(event => {
|
||||
if (!tab2Opened)
|
||||
assert.equal(event.type, "TabOpen", "second tab opened");
|
||||
else
|
||||
assert.equal(event.type, "TabSelect", "second tab selected");
|
||||
tab2Opened = true;
|
||||
assert.equal(event.target, tab2);
|
||||
});
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
exports["test open -> pin -> unpin"] = test({
|
||||
readyWhen: events => events.length === (isFennec ? 2 : 5),
|
||||
start: ({ tabs, window }) => {
|
||||
tabs.push(utils.openTab(window, 'data:text/plain,pin-unpin'));
|
||||
utils.pin(tabs[0]);
|
||||
utils.unpin(tabs[0]);
|
||||
},
|
||||
end: ({ tabs, events, assert, done }) => {
|
||||
let [open, select, move, pin, unpin] = events;
|
||||
let tab = tabs[0];
|
||||
|
||||
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);
|
||||
}
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
exports["test open -> open -> move "] = test({
|
||||
readyWhen: events => events.length === (isFennec ? 4 : 5),
|
||||
start: ({tabs, window}) => {
|
||||
tabs.push(utils.openTab(window, 'data:text/plain,Tab-1'));
|
||||
tabs.push(utils.openTab(window, 'data:text/plain,Tab-2'));
|
||||
utils.move(tabs[0], 2);
|
||||
},
|
||||
end: ({ tabs, events, assert, done }) => {
|
||||
let [ tab1, tab2 ] = tabs;
|
||||
let tab1Events = 0;
|
||||
getRelatedEvents(events, tab1).map(event => {
|
||||
tab1Events++;
|
||||
if (tab1Events === 1)
|
||||
assert.equal(event.type, "TabOpen", "first tab opened");
|
||||
else if (tab1Events === 2)
|
||||
assert.equal(event.type, "TabSelect", "first tab selected");
|
||||
else if (tab1Events === 3 && isFennec)
|
||||
assert.equal(event.type, "TabMove", "first tab moved");
|
||||
assert.equal(event.target, tab1);
|
||||
});
|
||||
assert.equal(tab1Events, isFennec ? 2 : 3,
|
||||
"correct number of events for first tab");
|
||||
|
||||
let tab2Events = 0;
|
||||
getRelatedEvents(events, tab2).map(event => {
|
||||
tab2Events++;
|
||||
if (tab2Events === 1)
|
||||
assert.equal(event.type, "TabOpen", "second tab opened");
|
||||
else
|
||||
assert.equal(event.type, "TabSelect", "second tab selected");
|
||||
assert.equal(event.target, tab2);
|
||||
});
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
function getRelatedEvents (events, tabs) {
|
||||
return events.filter(({target}) => ~([].concat(tabs)).indexOf(target));
|
||||
}
|
||||
|
||||
// require("sdk/test").run(exports);
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/* 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";
|
||||
|
||||
// TODO Fennec support in Bug #894525
|
||||
module.metadata = {
|
||||
"engines": {
|
||||
"Firefox": "*"
|
||||
}
|
||||
}
|
||||
|
||||
const { openTab, closeTab } = require("sdk/tabs/utils");
|
||||
const { Loader } = require("sdk/test/loader");
|
||||
const { setTimeout } = require("sdk/timers");
|
||||
|
||||
exports["test unload tab observer"] = function(assert, done) {
|
||||
let loader = Loader(module);
|
||||
|
||||
let window = loader.require("sdk/deprecated/window-utils").activeBrowserWindow;
|
||||
let observer = loader.require("sdk/tabs/observer").observer;
|
||||
let opened = 0;
|
||||
let closed = 0;
|
||||
|
||||
observer.on("open", function onOpen(window) { opened++; });
|
||||
observer.on("close", function onClose(window) { closed++; });
|
||||
|
||||
// Open and close tab to trigger observers.
|
||||
closeTab(openTab(window, "data:text/html;charset=utf-8,tab-1"));
|
||||
|
||||
// Unload the module so that all listeners set by observer are removed.
|
||||
loader.unload();
|
||||
|
||||
// Open and close tab once again.
|
||||
closeTab(openTab(window, "data:text/html;charset=utf-8,tab-2"));
|
||||
|
||||
// Enqueuing asserts to make sure that assertion is not performed early.
|
||||
setTimeout(function () {
|
||||
assert.equal(1, opened, "observer open was called before unload only");
|
||||
assert.equal(1, closed, "observer close was called before unload only");
|
||||
done();
|
||||
}, 0);
|
||||
};
|
||||
|
||||
// require("test").run(exports);
|
||||
67
addon-sdk/source/test/addons/e10s-tabs/lib/test-tab-utils.js
Normal file
67
addon-sdk/source/test/addons/e10s-tabs/lib/test-tab-utils.js
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
/* 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 { getTabs } = require('sdk/tabs/utils');
|
||||
const { isWindowPBSupported, isTabPBSupported } = require('sdk/private-browsing/utils');
|
||||
const { browserWindows } = require('sdk/windows');
|
||||
const tabs = require('sdk/tabs');
|
||||
const { isPrivate } = require('sdk/private-browsing');
|
||||
const { openTab, closeTab, getTabContentWindow, getOwnerWindow } = require('sdk/tabs/utils');
|
||||
const { open, close } = require('sdk/window/helpers');
|
||||
const { windows } = require('sdk/window/utils');
|
||||
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
|
||||
const { fromIterator } = require('sdk/util/array');
|
||||
|
||||
if (isWindowPBSupported) {
|
||||
exports.testGetTabsPWPB = function(assert, done) {
|
||||
let tabCount = getTabs().length;
|
||||
let windowCount = browserWindows.length;
|
||||
|
||||
open(null, {
|
||||
features: {
|
||||
private: true,
|
||||
toolbar: true,
|
||||
chrome: true
|
||||
}
|
||||
}).then(function(window) {
|
||||
assert.ok(isPrivate(window), 'new tab is private');
|
||||
|
||||
assert.equal(getTabs().length, tabCount, 'there are no new tabs found');
|
||||
getTabs().forEach(function(tab) {
|
||||
assert.equal(isPrivate(tab), false, 'all found tabs are not private');
|
||||
assert.equal(isPrivate(getOwnerWindow(tab)), false, 'all found tabs are not private');
|
||||
assert.equal(isPrivate(getTabContentWindow(tab)), false, 'all found tabs are not private');
|
||||
});
|
||||
|
||||
assert.equal(browserWindows.length, windowCount, 'there are no new windows found');
|
||||
fromIterator(browserWindows).forEach(function(window) {
|
||||
assert.equal(isPrivate(window), false, 'all found windows are not private');
|
||||
});
|
||||
|
||||
assert.equal(windows(null, {includePrivate: true}).length, 2, 'there are really two windows');
|
||||
|
||||
close(window).then(done);
|
||||
});
|
||||
};
|
||||
}
|
||||
else if (isTabPBSupported) {
|
||||
exports.testGetTabsPTPB = function(assert, done) {
|
||||
let startTabCount = getTabs().length;
|
||||
let tab = openTab(getMostRecentBrowserWindow(), 'about:blank', {
|
||||
isPrivate: true
|
||||
});
|
||||
|
||||
assert.ok(isPrivate(getTabContentWindow(tab)), 'new tab is private');
|
||||
let utils_tabs = getTabs();
|
||||
assert.equal(utils_tabs.length, startTabCount + 1,
|
||||
'there are two tabs found');
|
||||
assert.equal(utils_tabs[utils_tabs.length-1], tab,
|
||||
'the last tab is the opened tab');
|
||||
assert.equal(browserWindows.length, 1, 'there is only one window');
|
||||
closeTab(tab);
|
||||
|
||||
done();
|
||||
};
|
||||
}
|
||||
87
addon-sdk/source/test/addons/e10s-tabs/lib/test-tab.js
Normal file
87
addon-sdk/source/test/addons/e10s-tabs/lib/test-tab.js
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
/* 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 tabs = require("sdk/tabs"); // From addon-kit
|
||||
const windowUtils = require("sdk/deprecated/window-utils");
|
||||
const app = require("sdk/system/xul-app");
|
||||
const { viewFor } = require("sdk/view/core");
|
||||
const { modelFor } = require("sdk/model/core");
|
||||
const { getTabId, isTab } = require("sdk/tabs/utils");
|
||||
const { defer } = require("sdk/lang/functional");
|
||||
|
||||
exports["test behavior on close"] = function(assert, done) {
|
||||
tabs.open({
|
||||
url: "about:mozilla",
|
||||
onReady: function(tab) {
|
||||
assert.equal(tab.url, "about:mozilla", "Tab has the expected url");
|
||||
// if another test ends before closing a tab then index != 1 here
|
||||
assert.ok(tab.index >= 1, "Tab has the expected index, a value greater than 0");
|
||||
tab.close(function () {
|
||||
assert.equal(tab.url, undefined,
|
||||
"After being closed, tab attributes are undefined (url)");
|
||||
assert.equal(tab.index, undefined,
|
||||
"After being closed, tab attributes are undefined (index)");
|
||||
if (app.is("Firefox")) {
|
||||
// Ensure that we can call destroy multiple times without throwing;
|
||||
// Fennec doesn't use this internal utility
|
||||
tab.destroy();
|
||||
tab.destroy();
|
||||
}
|
||||
|
||||
done();
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports["test viewFor(tab)"] = (assert, done) => {
|
||||
// Note we defer handlers as length collection is updated after
|
||||
// handler is invoked, so if test is finished before counnts are
|
||||
// updated wrong length will show up in followup tests.
|
||||
tabs.once("open", defer(tab => {
|
||||
const view = viewFor(tab);
|
||||
assert.ok(view, "view is returned");
|
||||
assert.equal(getTabId(view), tab.id, "tab has a same id");
|
||||
|
||||
tab.close(defer(done));
|
||||
}));
|
||||
|
||||
tabs.open({ url: "about:mozilla" });
|
||||
};
|
||||
|
||||
exports["test modelFor(xulTab)"] = (assert, done) => {
|
||||
tabs.open({
|
||||
url: "about:mozilla",
|
||||
onReady: tab => {
|
||||
const view = viewFor(tab);
|
||||
assert.ok(view, "view is returned");
|
||||
assert.ok(isTab(view), "view is underlaying tab");
|
||||
assert.equal(getTabId(view), tab.id, "tab has a same id");
|
||||
assert.equal(modelFor(view), tab, "modelFor(view) is SDK tab");
|
||||
|
||||
tab.close(defer(done));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports["test tab.readyState"] = (assert, done) => {
|
||||
tabs.open({
|
||||
url: "data:text/html;charset=utf-8,test_readyState",
|
||||
onOpen: (tab) => {
|
||||
assert.notEqual(["uninitialized", "loading"].indexOf(tab.readyState), -1,
|
||||
"tab is either uninitialized or loading when onOpen");
|
||||
},
|
||||
onReady: (tab) => {
|
||||
assert.notEqual(["interactive", "complete"].indexOf(tab.readyState), -1,
|
||||
"tab is either interactive or complete when onReady");
|
||||
},
|
||||
onLoad: (tab) => {
|
||||
assert.equal(tab.readyState, "complete", "tab is complete onLoad");
|
||||
tab.close(defer(done));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// require("sdk/test").run(exports);
|
||||
11
addon-sdk/source/test/addons/e10s-tabs/package.json
Normal file
11
addon-sdk/source/test/addons/e10s-tabs/package.json
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"name": "e10s-tabs",
|
||||
"title": "e10s-tabs",
|
||||
"id": "jid1-ZZaXFHAPlHwbgw@jetpack",
|
||||
"description": "run tab tests in e10s mode",
|
||||
"author": "Tomislav Jovanovic",
|
||||
"license": "MPL-2.0",
|
||||
"version": "0.1.0",
|
||||
"main": "./lib/main.js",
|
||||
"e10s": true
|
||||
}
|
||||
65
addon-sdk/source/test/addons/e10s/lib/main.js
Normal file
65
addon-sdk/source/test/addons/e10s/lib/main.js
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
/* 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 { getMostRecentBrowserWindow, isBrowser } = require('sdk/window/utils');
|
||||
const { promise: windowPromise, close, focus } = require('sdk/window/helpers');
|
||||
const { openTab, closeTab, getBrowserForTab } = require('sdk/tabs/utils');
|
||||
const { WindowTracker } = require('sdk/deprecated/window-utils');
|
||||
const { version, platform } = require('sdk/system');
|
||||
const { when } = require('sdk/system/unload');
|
||||
const tabs = require('sdk/tabs');
|
||||
|
||||
const SKIPPING_TESTS = {
|
||||
"test skip": (assert) => assert.pass("nothing to test here")
|
||||
};
|
||||
|
||||
exports.testTabIsRemote = function(assert, done) {
|
||||
const url = 'data:text/html,test-tab-is-remote';
|
||||
let tab = openTab(getMostRecentBrowserWindow(), url);
|
||||
assert.ok(tab.linkedBrowser.isRemoteBrowser, "The new tab should be remote");
|
||||
|
||||
// can't simply close a remote tab before it is loaded, bug 1006043
|
||||
let mm = getBrowserForTab(tab).messageManager;
|
||||
mm.addMessageListener('7', function listener() {
|
||||
mm.removeMessageListener('7', listener);
|
||||
tabs.once('close', done);
|
||||
closeTab(tab);
|
||||
})
|
||||
mm.loadFrameScript('data:,sendAsyncMessage("7")', true);
|
||||
}
|
||||
|
||||
// run e10s tests only on builds from trunk, fx-team, Nightly..
|
||||
if (!version.endsWith('a1')) {
|
||||
module.exports = {};
|
||||
}
|
||||
|
||||
function replaceWindow(remote) {
|
||||
let next = null;
|
||||
let old = getMostRecentBrowserWindow();
|
||||
let promise = new Promise(resolve => {
|
||||
let tracker = WindowTracker({
|
||||
onTrack: window => {
|
||||
if (window !== next)
|
||||
return;
|
||||
resolve(window);
|
||||
tracker.unload();
|
||||
}
|
||||
});
|
||||
})
|
||||
next = old.OpenBrowserWindow({ remote });
|
||||
return promise.then(focus).then(_ => close(old));
|
||||
}
|
||||
|
||||
// bug 1054482 - e10s test addons time out on linux
|
||||
if (platform === 'linux') {
|
||||
module.exports = SKIPPING_TESTS;
|
||||
require('sdk/test/runner').runTestsFromModule(module);
|
||||
}
|
||||
else {
|
||||
replaceWindow(true).then(_ =>
|
||||
require('sdk/test/runner').runTestsFromModule(module));
|
||||
|
||||
when(_ => replaceWindow(false));
|
||||
}
|
||||
10
addon-sdk/source/test/addons/e10s/package.json
Normal file
10
addon-sdk/source/test/addons/e10s/package.json
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"name": "e10s",
|
||||
"title": "e10s",
|
||||
"id": "jid1-DYaXFHAPlHwbgw@jetpack",
|
||||
"description": "a basic e10s test add-on",
|
||||
"author": "Tomislav Jovanovic",
|
||||
"license": "MPL-2.0",
|
||||
"version": "0.1.0",
|
||||
"main": "./lib/main.js"
|
||||
}
|
||||
159
addon-sdk/source/test/addons/embedded-webextension/main.js
Normal file
159
addon-sdk/source/test/addons/embedded-webextension/main.js
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
const tabs = require("sdk/tabs");
|
||||
const webExtension = require('sdk/webextension');
|
||||
|
||||
exports.testEmbeddedWebExtensionModuleInitializedException = function (assert) {
|
||||
let actualErr;
|
||||
|
||||
assert.throws(
|
||||
() => webExtension.initFromBootstrapAddonParam({webExtension: null}),
|
||||
/'sdk\/webextension' module has been already initialized/,
|
||||
"Got the expected exception if the module is initialized twice"
|
||||
);
|
||||
};
|
||||
|
||||
exports.testEmbeddedWebExtensionBackgroungPage = function* (assert) {
|
||||
try {
|
||||
const api = yield webExtension.startup();
|
||||
assert.ok(api, `webextension waitForStartup promise successfully resolved`);
|
||||
|
||||
const apiSecondStartup = yield webExtension.startup();
|
||||
assert.equal(api, apiSecondStartup, "Got the same API object from the second startup call");
|
||||
|
||||
const {browser} = api;
|
||||
|
||||
let messageListener;
|
||||
let waitForBackgroundPageMessage = new Promise((resolve, reject) => {
|
||||
let numExpectedMessage = 2;
|
||||
messageListener = (msg, sender, sendReply) => {
|
||||
numExpectedMessage -= 1;
|
||||
if (numExpectedMessage == 1) {
|
||||
assert.equal(msg, "bg->sdk message",
|
||||
"Got the expected message from the background page");
|
||||
sendReply("sdk reply");
|
||||
} else if (numExpectedMessage == 0) {
|
||||
assert.equal(msg, "sdk reply",
|
||||
"The background page received the expected reply message");
|
||||
resolve();
|
||||
} else {
|
||||
console.error("Unexpected message received", {msg,sender, numExpectedMessage});
|
||||
assert.ok(false, `unexpected message received`);
|
||||
reject();
|
||||
}
|
||||
};
|
||||
browser.runtime.onMessage.addListener(messageListener);
|
||||
});
|
||||
|
||||
let portListener;
|
||||
let waitForBackgroundPagePort = new Promise((resolve, reject) => {
|
||||
portListener = (port) => {
|
||||
let numExpectedMessages = 2;
|
||||
port.onMessage.addListener((msg) => {
|
||||
numExpectedMessages -= 1;
|
||||
|
||||
if (numExpectedMessages == 1) {
|
||||
// Check that the legacy context has been able to receive the first port message
|
||||
// and reply with a port message to the background page.
|
||||
assert.equal(msg, "bg->sdk port message",
|
||||
"Got the expected port message from the background page");
|
||||
port.postMessage("sdk->bg port message");
|
||||
} else if (numExpectedMessages == 0) {
|
||||
// Check that the background page has received the above port message.
|
||||
assert.equal(msg, "bg received sdk->bg port message",
|
||||
"The background page received the expected port message");
|
||||
}
|
||||
});
|
||||
|
||||
port.onDisconnect.addListener(() => {
|
||||
assert.equal(numExpectedMessages, 0, "Got the expected number of port messages");
|
||||
resolve();
|
||||
});
|
||||
};
|
||||
browser.runtime.onConnect.addListener(portListener);
|
||||
});
|
||||
|
||||
yield Promise.all([
|
||||
waitForBackgroundPageMessage,
|
||||
waitForBackgroundPagePort,
|
||||
]).then(() => {
|
||||
browser.runtime.onMessage.removeListener(messageListener);
|
||||
browser.runtime.onConnect.removeListener(portListener);
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
assert.fail(`Unexpected webextension startup exception: ${err} - ${err.stack}`);
|
||||
}
|
||||
};
|
||||
|
||||
exports.testEmbeddedWebExtensionContentScript = function* (assert, done) {
|
||||
try {
|
||||
const {browser} = yield webExtension.startup();
|
||||
assert.ok(browser, `webextension startup promise resolved successfully to the API object`);
|
||||
|
||||
let messageListener;
|
||||
let waitForContentScriptMessage = new Promise((resolve, reject) => {
|
||||
let numExpectedMessage = 2;
|
||||
messageListener = (msg, sender, sendReply) => {
|
||||
numExpectedMessage -= 1;
|
||||
if (numExpectedMessage == 1) {
|
||||
assert.equal(msg, "content script->sdk message",
|
||||
"Got the expected message from the content script");
|
||||
sendReply("sdk reply");
|
||||
} else if (numExpectedMessage == 0) {
|
||||
assert.equal(msg, "sdk reply",
|
||||
"The content script received the expected reply message");
|
||||
resolve();
|
||||
} else {
|
||||
console.error("Unexpected message received", {msg,sender, numExpectedMessage});
|
||||
assert.ok(false, `unexpected message received`);
|
||||
reject();
|
||||
}
|
||||
};
|
||||
browser.runtime.onMessage.addListener(messageListener);
|
||||
});
|
||||
|
||||
let portListener;
|
||||
let waitForContentScriptPort = new Promise((resolve, reject) => {
|
||||
portListener = (port) => {
|
||||
let numExpectedMessages = 2;
|
||||
port.onMessage.addListener((msg) => {
|
||||
numExpectedMessages -= 1;
|
||||
|
||||
if (numExpectedMessages == 1) {
|
||||
assert.equal(msg, "content script->sdk port message",
|
||||
"Got the expected message from the content script port");
|
||||
port.postMessage("sdk->content script port message");
|
||||
} else if (numExpectedMessages == 0) {
|
||||
assert.equal(msg, "content script received sdk->content script port message",
|
||||
"The content script received the expected port message");
|
||||
}
|
||||
});
|
||||
port.onDisconnect.addListener(() => {
|
||||
assert.equal(numExpectedMessages, 0, "Got the epected number of port messages");
|
||||
resolve();
|
||||
});
|
||||
};
|
||||
browser.runtime.onConnect.addListener(portListener);
|
||||
});
|
||||
|
||||
let url = "http://example.org/";
|
||||
|
||||
var openedTab;
|
||||
tabs.once('open', function onOpen(tab) {
|
||||
openedTab = tab;
|
||||
});
|
||||
tabs.open(url);
|
||||
|
||||
yield Promise.all([
|
||||
waitForContentScriptMessage,
|
||||
waitForContentScriptPort,
|
||||
]).then(() => {
|
||||
browser.runtime.onMessage.removeListener(messageListener);
|
||||
browser.runtime.onConnect.removeListener(portListener);
|
||||
openedTab.close();
|
||||
});
|
||||
} catch (err) {
|
||||
assert.fail(`Unexpected webextension startup exception: ${err} - ${err.stack}`);
|
||||
}
|
||||
};
|
||||
|
||||
require("sdk/test/runner").runTestsFromModule(module);
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"id": "embedded-webextension@jetpack",
|
||||
"version": "0.1.0",
|
||||
"main": "./main.js",
|
||||
"hasEmbeddedWebExtension": true
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
browser.runtime.sendMessage("bg->sdk message", (reply) => {
|
||||
browser.runtime.sendMessage(reply);
|
||||
});
|
||||
|
||||
let port = browser.runtime.connect();
|
||||
port.onMessage.addListener((msg) => {
|
||||
port.postMessage(`bg received ${msg}`);
|
||||
port.disconnect();
|
||||
});
|
||||
port.postMessage("bg->sdk port message");
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
browser.runtime.sendMessage("content script->sdk message", (reply) => {
|
||||
browser.runtime.sendMessage(reply);
|
||||
});
|
||||
|
||||
let port = browser.runtime.connect();
|
||||
port.onMessage.addListener((msg) => {
|
||||
port.postMessage(`content script received ${msg}`);
|
||||
port.disconnect();
|
||||
});
|
||||
port.postMessage("content script->sdk port message");
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"name": "Test SDK Embedded WebExtension",
|
||||
"description": "",
|
||||
"version": "0.1.0",
|
||||
"applications": {
|
||||
"gecko": {
|
||||
"id": "embedded-webextension@jetpack"
|
||||
}
|
||||
},
|
||||
"manifest_version": 2,
|
||||
"permissions": ["tabs"],
|
||||
"background": {
|
||||
"scripts": ["background-page.js"]
|
||||
},
|
||||
"content_scripts": [
|
||||
{"matches": ["<all_urls>"], "js": ["content-script.js"]}
|
||||
]
|
||||
}
|
||||
48
addon-sdk/source/test/addons/jetpack-addon.ini
Normal file
48
addon-sdk/source/test/addons/jetpack-addon.ini
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
[addon-manager.xpi]
|
||||
[author-email.xpi]
|
||||
[child_process.xpi]
|
||||
[chrome.xpi]
|
||||
[content-permissions.xpi]
|
||||
[content-script-messages-latency.xpi]
|
||||
[contributors.xpi]
|
||||
[curly-id.xpi]
|
||||
[developers.xpi]
|
||||
[e10s.xpi]
|
||||
skip-if = true
|
||||
[e10s-content.xpi]
|
||||
skip-if = true
|
||||
[e10s-l10n.xpi]
|
||||
skip-if = true
|
||||
[e10s-remote.xpi]
|
||||
skip-if = true
|
||||
[e10s-tabs.xpi]
|
||||
skip-if = true
|
||||
[embedded-webextension.xpi]
|
||||
[l10n.xpi]
|
||||
[l10n-properties.xpi]
|
||||
[layout-change.xpi]
|
||||
[main.xpi]
|
||||
[name-in-numbers.xpi]
|
||||
[name-in-numbers-plus.xpi]
|
||||
[packaging.xpi]
|
||||
[packed.xpi]
|
||||
[page-mod-debugger-post.xpi]
|
||||
[page-mod-debugger-pre.xpi]
|
||||
[page-worker.xpi]
|
||||
skip-if = true # Bug 1288619 and Bug 1288708
|
||||
[places.xpi]
|
||||
[predefined-id-with-at.xpi]
|
||||
[preferences-branch.xpi]
|
||||
[private-browsing-supported.xpi]
|
||||
skip-if = true
|
||||
[remote.xpi]
|
||||
[require.xpi]
|
||||
[self.xpi]
|
||||
[simple-prefs.xpi]
|
||||
[simple-prefs-l10n.xpi]
|
||||
[simple-prefs-regression.xpi]
|
||||
[standard-id.xpi]
|
||||
[tab-close-on-startup.xpi]
|
||||
[toolkit-require-reload.xpi]
|
||||
[translators.xpi]
|
||||
[unsafe-content-script.xpi]
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
[App]
|
||||
Vendor=Varma
|
||||
Name=Test App
|
||||
Version=1.0
|
||||
BuildID=20060101
|
||||
Copyright=Copyright (c) 2009 Atul Varma
|
||||
ID=xulapp@toolness.com
|
||||
|
||||
[Gecko]
|
||||
MinVersion=1.9.2.0
|
||||
MaxVersion=2.0.*
|
||||
339
addon-sdk/source/test/addons/l10n-properties/app-extension/bootstrap.js
vendored
Normal file
339
addon-sdk/source/test/addons/l10n-properties/app-extension/bootstrap.js
vendored
Normal file
|
|
@ -0,0 +1,339 @@
|
|||
/* 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/. */
|
||||
|
||||
// @see http://dxr.mozilla.org/mozilla-central/source/js/src/xpconnect/loader/mozJSComponentLoader.cpp
|
||||
|
||||
'use strict';
|
||||
|
||||
// IMPORTANT: Avoid adding any initialization tasks here, if you need to do
|
||||
// something before add-on is loaded consider addon/runner module instead!
|
||||
|
||||
const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu,
|
||||
results: Cr, manager: Cm } = Components;
|
||||
const ioService = Cc['@mozilla.org/network/io-service;1'].
|
||||
getService(Ci.nsIIOService);
|
||||
const resourceHandler = ioService.getProtocolHandler('resource').
|
||||
QueryInterface(Ci.nsIResProtocolHandler);
|
||||
const systemPrincipal = CC('@mozilla.org/systemprincipal;1', 'nsIPrincipal')();
|
||||
const scriptLoader = Cc['@mozilla.org/moz/jssubscript-loader;1'].
|
||||
getService(Ci.mozIJSSubScriptLoader);
|
||||
const prefService = Cc['@mozilla.org/preferences-service;1'].
|
||||
getService(Ci.nsIPrefService).
|
||||
QueryInterface(Ci.nsIPrefBranch);
|
||||
const appInfo = Cc["@mozilla.org/xre/app-info;1"].
|
||||
getService(Ci.nsIXULAppInfo);
|
||||
const vc = Cc["@mozilla.org/xpcom/version-comparator;1"].
|
||||
getService(Ci.nsIVersionComparator);
|
||||
|
||||
const { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm");
|
||||
|
||||
const REASON = [ 'unknown', 'startup', 'shutdown', 'enable', 'disable',
|
||||
'install', 'uninstall', 'upgrade', 'downgrade' ];
|
||||
|
||||
const bind = Function.call.bind(Function.bind);
|
||||
|
||||
var loader = null;
|
||||
var unload = null;
|
||||
var cuddlefishSandbox = null;
|
||||
var nukeTimer = null;
|
||||
|
||||
// Utility function that synchronously reads local resource from the given
|
||||
// `uri` and returns content string.
|
||||
function readURI(uri) {
|
||||
let channel = NetUtil.newChannel({
|
||||
uri: NetUtil.newURI(uri, "UTF-8"),
|
||||
loadUsingSystemPrincipal: true
|
||||
});
|
||||
let stream = channel.open2();
|
||||
|
||||
let cstream = Cc['@mozilla.org/intl/converter-input-stream;1'].
|
||||
createInstance(Ci.nsIConverterInputStream);
|
||||
cstream.init(stream, 'UTF-8', 0, 0);
|
||||
|
||||
let str = {};
|
||||
let data = '';
|
||||
let read = 0;
|
||||
do {
|
||||
read = cstream.readString(0xffffffff, str);
|
||||
data += str.value;
|
||||
} while (read != 0);
|
||||
|
||||
cstream.close();
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
// We don't do anything on install & uninstall yet, but in a future
|
||||
// we should allow add-ons to cleanup after uninstall.
|
||||
function install(data, reason) {}
|
||||
function uninstall(data, reason) {}
|
||||
|
||||
function startup(data, reasonCode) {
|
||||
try {
|
||||
let reason = REASON[reasonCode];
|
||||
// URI for the root of the XPI file.
|
||||
// 'jar:' URI if the addon is packed, 'file:' URI otherwise.
|
||||
// (Used by l10n module in order to fetch `locale` folder)
|
||||
let rootURI = data.resourceURI.spec;
|
||||
|
||||
// TODO: Maybe we should perform read harness-options.json asynchronously,
|
||||
// since we can't do anything until 'sessionstore-windows-restored' anyway.
|
||||
let options = JSON.parse(readURI(rootURI + './harness-options.json'));
|
||||
|
||||
let id = options.jetpackID;
|
||||
let name = options.name;
|
||||
|
||||
// Clean the metadata
|
||||
options.metadata[name]['permissions'] = options.metadata[name]['permissions'] || {};
|
||||
|
||||
// freeze the permissionss
|
||||
Object.freeze(options.metadata[name]['permissions']);
|
||||
// freeze the metadata
|
||||
Object.freeze(options.metadata[name]);
|
||||
|
||||
// Register a new resource 'domain' for this addon which is mapping to
|
||||
// XPI's `resources` folder.
|
||||
// Generate the domain name by using jetpack ID, which is the extension ID
|
||||
// by stripping common characters that doesn't work as a domain name:
|
||||
let uuidRe =
|
||||
/^\{([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\}$/;
|
||||
|
||||
let domain = id.
|
||||
toLowerCase().
|
||||
replace(/@/g, '-at-').
|
||||
replace(/\./g, '-dot-').
|
||||
replace(uuidRe, '$1');
|
||||
|
||||
let prefixURI = 'resource://' + domain + '/';
|
||||
let resourcesURI = ioService.newURI(rootURI + '/resources/', null, null);
|
||||
resourceHandler.setSubstitution(domain, resourcesURI);
|
||||
|
||||
// Create path to URLs mapping supported by loader.
|
||||
let paths = {
|
||||
// Relative modules resolve to add-on package lib
|
||||
'./': prefixURI + name + '/lib/',
|
||||
'./tests/': prefixURI + name + '/tests/',
|
||||
'': 'resource://gre/modules/commonjs/'
|
||||
};
|
||||
|
||||
// Maps addon lib and tests ressource folders for each package
|
||||
paths = Object.keys(options.metadata).reduce(function(result, name) {
|
||||
result[name + '/'] = prefixURI + name + '/lib/'
|
||||
result[name + '/tests/'] = prefixURI + name + '/tests/'
|
||||
return result;
|
||||
}, paths);
|
||||
|
||||
// We need to map tests folder when we run sdk tests whose package name
|
||||
// is stripped
|
||||
if (name == 'addon-sdk')
|
||||
paths['tests/'] = prefixURI + name + '/tests/';
|
||||
|
||||
let useBundledSDK = options['force-use-bundled-sdk'];
|
||||
if (!useBundledSDK) {
|
||||
try {
|
||||
useBundledSDK = prefService.getBoolPref("extensions.addon-sdk.useBundledSDK");
|
||||
}
|
||||
catch (e) {
|
||||
// Pref doesn't exist, allow using Firefox shipped SDK
|
||||
}
|
||||
}
|
||||
|
||||
// Starting with Firefox 21.0a1, we start using modules shipped into firefox
|
||||
// Still allow using modules from the xpi if the manifest tell us to do so.
|
||||
// And only try to look for sdk modules in xpi if the xpi actually ship them
|
||||
if (options['is-sdk-bundled'] &&
|
||||
(vc.compare(appInfo.version, '21.0a1') < 0 || useBundledSDK)) {
|
||||
// Maps sdk module folders to their resource folder
|
||||
paths[''] = prefixURI + 'addon-sdk/lib/';
|
||||
// test.js is usually found in root commonjs or SDK_ROOT/lib/ folder,
|
||||
// so that it isn't shipped in the xpi. Keep a copy of it in sdk/ folder
|
||||
// until we no longer support SDK modules in XPI:
|
||||
paths['test'] = prefixURI + 'addon-sdk/lib/sdk/test.js';
|
||||
}
|
||||
|
||||
// Retrieve list of module folder overloads based on preferences in order to
|
||||
// eventually used a local modules instead of files shipped into Firefox.
|
||||
let branch = prefService.getBranch('extensions.modules.' + id + '.path');
|
||||
paths = branch.getChildList('', {}).reduce(function (result, name) {
|
||||
// Allows overloading of any sub folder by replacing . by / in pref name
|
||||
let path = name.substr(1).split('.').join('/');
|
||||
// Only accept overloading folder by ensuring always ending with `/`
|
||||
if (path) path += '/';
|
||||
let fileURI = branch.getCharPref(name);
|
||||
|
||||
// On mobile, file URI has to end with a `/` otherwise, setSubstitution
|
||||
// takes the parent folder instead.
|
||||
if (fileURI[fileURI.length-1] !== '/')
|
||||
fileURI += '/';
|
||||
|
||||
// Maps the given file:// URI to a resource:// in order to avoid various
|
||||
// failure that happens with file:// URI and be close to production env
|
||||
let resourcesURI = ioService.newURI(fileURI, null, null);
|
||||
let resName = 'extensions.modules.' + domain + '.commonjs.path' + name;
|
||||
resourceHandler.setSubstitution(resName, resourcesURI);
|
||||
|
||||
result[path] = 'resource://' + resName + '/';
|
||||
return result;
|
||||
}, paths);
|
||||
|
||||
// Make version 2 of the manifest
|
||||
let manifest = options.manifest;
|
||||
|
||||
// Import `cuddlefish.js` module using a Sandbox and bootstrap loader.
|
||||
let cuddlefishPath = 'loader/cuddlefish.js';
|
||||
let cuddlefishURI = 'resource://gre/modules/commonjs/sdk/' + cuddlefishPath;
|
||||
if (paths['sdk/']) { // sdk folder has been overloaded
|
||||
// (from pref, or cuddlefish is still in the xpi)
|
||||
cuddlefishURI = paths['sdk/'] + cuddlefishPath;
|
||||
}
|
||||
else if (paths['']) { // root modules folder has been overloaded
|
||||
cuddlefishURI = paths[''] + 'sdk/' + cuddlefishPath;
|
||||
}
|
||||
|
||||
cuddlefishSandbox = loadSandbox(cuddlefishURI);
|
||||
let cuddlefish = cuddlefishSandbox.exports;
|
||||
|
||||
// Normalize `options.mainPath` so that it looks like one that will come
|
||||
// in a new version of linker.
|
||||
let main = options.mainPath;
|
||||
|
||||
unload = cuddlefish.unload;
|
||||
loader = cuddlefish.Loader({
|
||||
paths: paths,
|
||||
// modules manifest.
|
||||
manifest: manifest,
|
||||
|
||||
// Add-on ID used by different APIs as a unique identifier.
|
||||
id: id,
|
||||
// Add-on name.
|
||||
name: name,
|
||||
// Add-on version.
|
||||
version: options.metadata[name].version,
|
||||
// Add-on package descriptor.
|
||||
metadata: options.metadata[name],
|
||||
// Add-on load reason.
|
||||
loadReason: reason,
|
||||
|
||||
prefixURI: prefixURI,
|
||||
// Add-on URI.
|
||||
rootURI: rootURI,
|
||||
// options used by system module.
|
||||
// File to write 'OK' or 'FAIL' (exit code emulation).
|
||||
resultFile: options.resultFile,
|
||||
// Arguments passed as --static-args
|
||||
staticArgs: options.staticArgs,
|
||||
|
||||
// Arguments related to test runner.
|
||||
modules: {
|
||||
'@test/options': {
|
||||
allTestModules: options.allTestModules,
|
||||
iterations: options.iterations,
|
||||
filter: options.filter,
|
||||
profileMemory: options.profileMemory,
|
||||
stopOnError: options.stopOnError,
|
||||
verbose: options.verbose,
|
||||
parseable: options.parseable,
|
||||
checkMemory: options.check_memory,
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let module = cuddlefish.Module('sdk/loader/cuddlefish', cuddlefishURI);
|
||||
let require = cuddlefish.Require(loader, module);
|
||||
|
||||
require('sdk/addon/runner').startup(reason, {
|
||||
loader: loader,
|
||||
main: main,
|
||||
prefsURI: rootURI + 'defaults/preferences/prefs.js'
|
||||
});
|
||||
} catch (error) {
|
||||
dump('Bootstrap error: ' +
|
||||
(error.message ? error.message : String(error)) + '\n' +
|
||||
(error.stack || error.fileName + ': ' + error.lineNumber) + '\n');
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
function loadSandbox(uri) {
|
||||
let proto = {
|
||||
sandboxPrototype: {
|
||||
loadSandbox: loadSandbox,
|
||||
ChromeWorker: ChromeWorker
|
||||
}
|
||||
};
|
||||
let sandbox = Cu.Sandbox(systemPrincipal, proto);
|
||||
// Create a fake commonjs environnement just to enable loading loader.js
|
||||
// correctly
|
||||
sandbox.exports = {};
|
||||
sandbox.module = { uri: uri, exports: sandbox.exports };
|
||||
sandbox.require = function (id) {
|
||||
if (id !== "chrome")
|
||||
throw new Error("Bootstrap sandbox `require` method isn't implemented.");
|
||||
|
||||
return Object.freeze({ Cc: Cc, Ci: Ci, Cu: Cu, Cr: Cr, Cm: Cm,
|
||||
CC: bind(CC, Components), components: Components,
|
||||
ChromeWorker: ChromeWorker });
|
||||
};
|
||||
scriptLoader.loadSubScript(uri, sandbox, 'UTF-8');
|
||||
return sandbox;
|
||||
}
|
||||
|
||||
function unloadSandbox(sandbox) {
|
||||
if (Cu.getClassName(sandbox, true) == "Sandbox")
|
||||
Cu.nukeSandbox(sandbox);
|
||||
}
|
||||
|
||||
function setTimeout(callback, delay) {
|
||||
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
timer.initWithCallback({ notify: callback }, delay,
|
||||
Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
return timer;
|
||||
}
|
||||
|
||||
function shutdown(data, reasonCode) {
|
||||
let reason = REASON[reasonCode];
|
||||
if (loader) {
|
||||
unload(loader, reason);
|
||||
unload = null;
|
||||
|
||||
// Don't waste time cleaning up if the application is shutting down
|
||||
if (reason != "shutdown") {
|
||||
// Avoid leaking all modules when something goes wrong with one particular
|
||||
// module. Do not clean it up immediatly in order to allow executing some
|
||||
// actions on addon disabling.
|
||||
// We need to keep a reference to the timer, otherwise it is collected
|
||||
// and won't ever fire.
|
||||
nukeTimer = setTimeout(nukeModules, 1000);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function nukeModules() {
|
||||
nukeTimer = null;
|
||||
// module objects store `exports` which comes from sandboxes
|
||||
// We should avoid keeping link to these object to avoid leaking sandboxes
|
||||
for (let key in loader.modules) {
|
||||
delete loader.modules[key];
|
||||
}
|
||||
// Direct links to sandboxes should be removed too
|
||||
for (let key in loader.sandboxes) {
|
||||
let sandbox = loader.sandboxes[key];
|
||||
delete loader.sandboxes[key];
|
||||
// Bug 775067: From FF17 we can kill all CCW from a given sandbox
|
||||
unloadSandbox(sandbox);
|
||||
}
|
||||
loader = null;
|
||||
|
||||
// both `toolkit/loader` and `system/xul-app` are loaded as JSM's via
|
||||
// `cuddlefish.js`, and needs to be unloaded to avoid memory leaks, when
|
||||
// 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.
|
||||
unloadSandbox(cuddlefishSandbox);
|
||||
cuddlefishSandbox = null;
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>xulapp@toolness.com</em:id>
|
||||
<em:version>1.0</em:version>
|
||||
<em:type>2</em:type>
|
||||
<em:bootstrap>true</em:bootstrap>
|
||||
<em:unpack>false</em:unpack>
|
||||
|
||||
<!-- Firefox -->
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
|
||||
<em:minVersion>21.0</em:minVersion>
|
||||
<em:maxVersion>25.0a1</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
|
||||
<!-- Front End MetaData -->
|
||||
<em:name>Test App</em:name>
|
||||
<em:description>Harness for tests.</em:description>
|
||||
<em:creator>Mozilla Corporation</em:creator>
|
||||
<em:homepageURL></em:homepageURL>
|
||||
<em:optionsType></em:optionsType>
|
||||
<em:updateURL></em:updateURL>
|
||||
</Description>
|
||||
</RDF>
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
# 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/.
|
||||
|
||||
Translated= Yes
|
||||
|
||||
text-content=no <b>HTML</b> injection
|
||||
|
||||
downloadsCount=%d downloads
|
||||
downloadsCount[one]=one download
|
||||
|
||||
pluralTest=fallback to other
|
||||
pluralTest[zero]=optional zero form
|
||||
|
||||
explicitPlural[one]=one
|
||||
explicitPlural[other]=other
|
||||
|
||||
# You can use unicode char escaping in order to inject space at the beginning/
|
||||
# end of your string. (Regular spaces are automatically ignore by .properties
|
||||
# file parser)
|
||||
unicodeEscape = \u0020\u0040\u0020
|
||||
# this string equals to " @ "
|
||||
|
||||
# bug 1033309 plurals with multiple placeholders
|
||||
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.
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
# 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/.
|
||||
|
||||
Translated= Yes
|
||||
|
||||
text-content=no <b>HTML</b> injection
|
||||
|
||||
downloadsCount=%d downloads
|
||||
downloadsCount[one]=one download
|
||||
|
||||
pluralTest=fallback to other
|
||||
pluralTest[zero]=optional zero form
|
||||
|
||||
explicitPlural[one]=one
|
||||
explicitPlural[other]=other
|
||||
|
||||
# You can use unicode char escaping in order to inject space at the beginning/
|
||||
# end of your string. (Regular spaces are automatically ignore by .properties
|
||||
# file parser)
|
||||
unicodeEscape = \u0020\u0040\u0020
|
||||
# this string equals to " @ "
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
# 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/.
|
||||
|
||||
Translated= jes
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
Translated= Oui
|
||||
|
||||
placeholderString= Placeholder %s
|
||||
|
||||
# Plural forms
|
||||
%d downloads=%d téléchargements
|
||||
%d downloads[one]=%d téléchargement
|
||||
|
||||
downloadsCount=%d téléchargements
|
||||
downloadsCount[one]=%d téléchargement
|
||||
|
|
@ -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/. -->
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>HTML Localization</title>
|
||||
</head>
|
||||
<body>
|
||||
<div data-l10n-id="Not translated">Kept as-is</div>
|
||||
<ul data-l10n-id="Translated">
|
||||
<li>Inner html content is replaced,</li>
|
||||
<li data-l10n-id="text-content">
|
||||
Elements with data-l10n-id attribute whose parent element is translated
|
||||
will be replaced by the content of the translation.
|
||||
</li>
|
||||
</ul>
|
||||
<div data-l10n-id="text-content">No</div>
|
||||
<div data-l10n-id="Translated">
|
||||
A data-l10n-id value can be used in multiple elements
|
||||
</div>
|
||||
</body>
|
||||
</html
|
||||
202
addon-sdk/source/test/addons/l10n-properties/main.js
Normal file
202
addon-sdk/source/test/addons/l10n-properties/main.js
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
/* 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 prefs = require("sdk/preferences/service");
|
||||
const { Loader } = require('sdk/test/loader');
|
||||
const { resolveURI } = require('toolkit/loader');
|
||||
const { rootURI } = require("@loader/options");
|
||||
const { usingJSON } = require('sdk/l10n/json/core');
|
||||
|
||||
const PREF_MATCH_OS_LOCALE = "intl.locale.matchOS";
|
||||
const PREF_SELECTED_LOCALE = "general.useragent.locale";
|
||||
|
||||
function setLocale(locale) {
|
||||
prefs.set(PREF_MATCH_OS_LOCALE, false);
|
||||
prefs.set(PREF_SELECTED_LOCALE, locale);
|
||||
}
|
||||
|
||||
function resetLocale() {
|
||||
prefs.reset(PREF_MATCH_OS_LOCALE);
|
||||
prefs.reset(PREF_SELECTED_LOCALE);
|
||||
}
|
||||
|
||||
function definePseudo(loader, id, exports) {
|
||||
let uri = resolveURI(id, loader.mapping);
|
||||
loader.modules[uri] = { exports: exports };
|
||||
}
|
||||
|
||||
function createTest(locale, testFunction) {
|
||||
return function (assert, done) {
|
||||
let loader = Loader(module);
|
||||
// Change the locale before loading new l10n modules in order to load
|
||||
// the right .json file
|
||||
setLocale(locale);
|
||||
// Initialize main l10n module in order to load new locale files
|
||||
loader.require("sdk/l10n/loader").
|
||||
load(rootURI).
|
||||
then(function success(data) {
|
||||
definePseudo(loader, '@l10n/data', data);
|
||||
// Execute the given test function
|
||||
try {
|
||||
testFunction(assert, loader, function onDone() {
|
||||
loader.unload();
|
||||
resetLocale();
|
||||
done();
|
||||
});
|
||||
}
|
||||
catch(e) {
|
||||
console.exception(e);
|
||||
}
|
||||
},
|
||||
function failure(error) {
|
||||
assert.fail("Unable to load locales: " + error);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
exports.testExactMatching = createTest("fr-FR", function(assert, loader, done) {
|
||||
let _ = loader.require("sdk/l10n").get;
|
||||
assert.equal(_("Not translated"), "Not translated",
|
||||
"Key not translated");
|
||||
assert.equal(_("Translated"), "Oui",
|
||||
"Simple key translated");
|
||||
|
||||
// Placeholders
|
||||
assert.equal(_("placeholderString", "works"), "Placeholder works",
|
||||
"Value with placeholder");
|
||||
assert.equal(_("Placeholder %s", "works"), "Placeholder works",
|
||||
"Key without value but with placeholder");
|
||||
assert.equal(_("Placeholders %2s %1s %s.", "working", "are", "correctly"),
|
||||
"Placeholders are working correctly.",
|
||||
"Multiple placeholders");
|
||||
|
||||
// Plurals
|
||||
assert.equal(_("downloadsCount", 0),
|
||||
"0 téléchargement",
|
||||
"PluralForm form 'one' for 0 in french");
|
||||
assert.equal(_("downloadsCount", 1),
|
||||
"1 téléchargement",
|
||||
"PluralForm form 'one' for 1 in french");
|
||||
assert.equal(_("downloadsCount", 2),
|
||||
"2 téléchargements",
|
||||
"PluralForm form 'other' for n > 1 in french");
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
exports.testHtmlLocalization = createTest("en-GB", function(assert, loader, done) {
|
||||
// Ensure initing html component that watch document creations
|
||||
// Note that this module is automatically initialized in
|
||||
// cuddlefish.js:Loader.main in regular addons. But it isn't for unit tests.
|
||||
let loaderHtmlL10n = loader.require("sdk/l10n/html");
|
||||
loaderHtmlL10n.enable();
|
||||
|
||||
let uri = require("sdk/self").data.url("test-localization.html");
|
||||
let worker = loader.require("sdk/page-worker").Page({
|
||||
contentURL: uri,
|
||||
contentScript: "new " + function ContentScriptScope() {
|
||||
let nodes = document.body.querySelectorAll("*[data-l10n-id]");
|
||||
self.postMessage([nodes[0].innerHTML,
|
||||
nodes[1].innerHTML,
|
||||
nodes[2].innerHTML,
|
||||
nodes[3].innerHTML]);
|
||||
},
|
||||
onMessage: function (data) {
|
||||
assert.equal(
|
||||
data[0],
|
||||
"Kept as-is",
|
||||
"Nodes with unknown id in .properties are kept 'as-is'"
|
||||
);
|
||||
assert.equal(data[1], "Yes", "HTML is translated");
|
||||
assert.equal(
|
||||
data[2],
|
||||
"no <b>HTML</b> injection",
|
||||
"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.");
|
||||
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
exports.testEnUsLocaleName = createTest("en-GB", function(assert, loader, done) {
|
||||
let _ = loader.require("sdk/l10n").get;
|
||||
|
||||
assert.equal(_("Not translated"), "Not translated",
|
||||
"String w/o translation is kept as-is");
|
||||
assert.equal(_("Translated"), "Yes",
|
||||
"String with translation is correctly translated");
|
||||
|
||||
// Check Unicode char escaping sequences
|
||||
assert.equal(_("unicodeEscape"), " @ ",
|
||||
"Unicode escaped sequances are correctly converted");
|
||||
|
||||
// Check plural forms regular matching
|
||||
assert.equal(_("downloadsCount", 0),
|
||||
"0 downloads",
|
||||
"PluralForm form 'other' for 0 in english");
|
||||
assert.equal(_("downloadsCount", 1),
|
||||
"one download",
|
||||
"PluralForm form 'one' for 1 in english");
|
||||
assert.equal(_("downloadsCount", 2),
|
||||
"2 downloads",
|
||||
"PluralForm form 'other' for n != 1 in english");
|
||||
|
||||
// Check optional plural forms
|
||||
assert.equal(_("pluralTest", 0),
|
||||
"optional zero form",
|
||||
"PluralForm form 'zero' can be optionaly specified. (Isn't mandatory in english)");
|
||||
assert.equal(_("pluralTest", 1),
|
||||
"fallback to other",
|
||||
"If the specific plural form is missing, we fallback to 'other'");
|
||||
|
||||
// Ensure that we can omit specifying the generic key without [other]
|
||||
// key[one] = ...
|
||||
// key[other] = ... # Instead of `key = ...`
|
||||
assert.equal(_("explicitPlural", 1),
|
||||
"one",
|
||||
"PluralForm form can be omitting generic key [i.e. without ...[other] at end of key)");
|
||||
assert.equal(_("explicitPlural", 10),
|
||||
"other",
|
||||
"PluralForm form can be omitting generic key [i.e. without ...[other] at end of key)");
|
||||
|
||||
assert.equal(_("first_identifier", "ONE", "TWO"), "the entries are ONE and TWO.", "first_identifier no count");
|
||||
assert.equal(_("first_identifier", 0, "ONE", "TWO"), "the entries are ONE and TWO.", "first_identifier with count = 0");
|
||||
assert.equal(_("first_identifier", 1, "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "first_identifier with count = 1");
|
||||
assert.equal(_("first_identifier", 2, "ONE", "TWO"), "the entries are ONE and TWO.", "first_identifier with count = 2");
|
||||
|
||||
assert.equal(_("second_identifier", "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "second_identifier with no count");
|
||||
assert.equal(_("second_identifier", 0, "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "second_identifier with count = 0");
|
||||
assert.equal(_("second_identifier", 1, "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "second_identifier with count = 1");
|
||||
assert.equal(_("second_identifier", 2, "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "second_identifier with count = 2");
|
||||
|
||||
assert.equal(_("third_identifier", "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "third_identifier with no count");
|
||||
assert.equal(_("third_identifier", 0, "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "third_identifier with count = 0");
|
||||
assert.equal(_("third_identifier", 2, "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "third_identifier with count = 2");
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
exports.testUsingJSON = function(assert) {
|
||||
assert.equal(usingJSON, false, 'not using json');
|
||||
}
|
||||
|
||||
exports.testShortLocaleName = createTest("eo", function(assert, loader, done) {
|
||||
let _ = loader.require("sdk/l10n").get;
|
||||
assert.equal(_("Not translated"), "Not translated",
|
||||
"String w/o translation is kept as-is");
|
||||
assert.equal(_("Translated"), "jes",
|
||||
"String with translation is correctly translated");
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
|
||||
// Before running tests, disable HTML service which is automatially enabled
|
||||
// in api-utils/addon/runner.js
|
||||
require('sdk/l10n/html').disable();
|
||||
|
||||
require("sdk/test/runner").runTestsFromModule(module);
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"id": "test-l10n@jetpack",
|
||||
"title": "Test L10n",
|
||||
"main": "./main.js",
|
||||
"version": "0.0.1"
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
<!-- 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/. -->
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>HTML Localization</title>
|
||||
</head>
|
||||
<body>
|
||||
<div data-l10n-id="Not translated">Kept as-is</div>
|
||||
<ul data-l10n-id="Translated">
|
||||
<li>Inner html content is replaced,</li>
|
||||
<li data-l10n-id="text-content">
|
||||
Elements with data-l10n-id attribute whose parent element is translated
|
||||
will be replaced by the content of the translation.
|
||||
</li>
|
||||
</ul>
|
||||
<div data-l10n-id="text-content">No</div>
|
||||
<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
|
||||
38
addon-sdk/source/test/addons/l10n/locale/en.properties
Normal file
38
addon-sdk/source/test/addons/l10n/locale/en.properties
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
# 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/.
|
||||
|
||||
Translated= Yes
|
||||
|
||||
text-content=no <b>HTML</b> injection
|
||||
|
||||
downloadsCount=%d downloads
|
||||
downloadsCount[one]=one download
|
||||
|
||||
pluralTest=fallback to other
|
||||
pluralTest[zero]=optional zero form
|
||||
|
||||
explicitPlural[one]=one
|
||||
explicitPlural[other]=other
|
||||
|
||||
# You can use unicode char escaping in order to inject space at the beginning/
|
||||
# end of your string. (Regular spaces are automatically ignore by .properties
|
||||
# file parser)
|
||||
unicodeEscape = \u0020\u0040\u0020
|
||||
# this string equals to " @ "
|
||||
|
||||
# bug 1033309 plurals with multiple placeholders
|
||||
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
|
||||
5
addon-sdk/source/test/addons/l10n/locale/eo.properties
Normal file
5
addon-sdk/source/test/addons/l10n/locale/eo.properties
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# 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/.
|
||||
|
||||
Translated= jes
|
||||
14
addon-sdk/source/test/addons/l10n/locale/fr-FR.properties
Normal file
14
addon-sdk/source/test/addons/l10n/locale/fr-FR.properties
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
Translated= Oui
|
||||
|
||||
placeholderString= Placeholder %s
|
||||
|
||||
# Plural forms
|
||||
%d downloads=%d téléchargements
|
||||
%d downloads[one]=%d téléchargement
|
||||
|
||||
downloadsCount=%d téléchargements
|
||||
downloadsCount[one]=%d téléchargement
|
||||
289
addon-sdk/source/test/addons/l10n/main.js
Normal file
289
addon-sdk/source/test/addons/l10n/main.js
Normal file
|
|
@ -0,0 +1,289 @@
|
|||
/* 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 prefs = require("sdk/preferences/service");
|
||||
const { Loader } = require('sdk/test/loader');
|
||||
const { resolveURI } = require('toolkit/loader');
|
||||
const { rootURI, isNative } = require("@loader/options");
|
||||
const { usingJSON } = require('sdk/l10n/json/core');
|
||||
|
||||
const PREF_MATCH_OS_LOCALE = "intl.locale.matchOS";
|
||||
const PREF_SELECTED_LOCALE = "general.useragent.locale";
|
||||
|
||||
function setLocale(locale) {
|
||||
prefs.set(PREF_MATCH_OS_LOCALE, false);
|
||||
prefs.set(PREF_SELECTED_LOCALE, locale);
|
||||
}
|
||||
|
||||
function resetLocale() {
|
||||
prefs.reset(PREF_MATCH_OS_LOCALE);
|
||||
prefs.reset(PREF_SELECTED_LOCALE);
|
||||
}
|
||||
|
||||
function definePseudo(loader, id, exports) {
|
||||
let uri = resolveURI(id, loader.mapping);
|
||||
loader.modules[uri] = { exports: exports };
|
||||
}
|
||||
|
||||
function createTest(locale, testFunction) {
|
||||
return function (assert, done) {
|
||||
let loader = Loader(module);
|
||||
// Change the locale before loading new l10n modules in order to load
|
||||
// the right .json file
|
||||
setLocale(locale);
|
||||
// Initialize main l10n module in order to load new locale files
|
||||
loader.require("sdk/l10n/loader").
|
||||
load(rootURI).
|
||||
then(null, function failure(error) {
|
||||
if (!isNative)
|
||||
assert.fail("Unable to load locales: " + error);
|
||||
}).
|
||||
then(function success(data) {
|
||||
definePseudo(loader, '@l10n/data', data ? data : null);
|
||||
// Execute the given test function
|
||||
try {
|
||||
testFunction(assert, loader, function onDone() {
|
||||
loader.unload();
|
||||
resetLocale();
|
||||
done();
|
||||
});
|
||||
}
|
||||
catch(e) {
|
||||
console.exception(e);
|
||||
}
|
||||
},
|
||||
function failure(error) {
|
||||
assert.fail("Unable to load locales: " + error);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
exports.testExactMatching = createTest("fr-FR", function(assert, loader, done) {
|
||||
let _ = loader.require("sdk/l10n").get;
|
||||
assert.equal(_("Not translated"), "Not translated",
|
||||
"Key not translated");
|
||||
assert.equal(_("Translated"), "Oui",
|
||||
"Simple key translated");
|
||||
|
||||
// Placeholders
|
||||
assert.equal(_("placeholderString", "works"), "Placeholder works",
|
||||
"Value with placeholder");
|
||||
assert.equal(_("Placeholder %s", "works"), "Placeholder works",
|
||||
"Key without value but with placeholder");
|
||||
assert.equal(_("Placeholders %2s %1s %s.", "working", "are", "correctly"),
|
||||
"Placeholders are working correctly.",
|
||||
"Multiple placeholders");
|
||||
|
||||
// Plurals
|
||||
assert.equal(_("downloadsCount", 0),
|
||||
"0 téléchargement",
|
||||
"PluralForm form 'one' for 0 in french");
|
||||
assert.equal(_("downloadsCount", 1),
|
||||
"1 téléchargement",
|
||||
"PluralForm form 'one' for 1 in french");
|
||||
assert.equal(_("downloadsCount", 2),
|
||||
"2 téléchargements",
|
||||
"PluralForm form 'other' for n > 1 in french");
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
exports.testHtmlLocalizationPageWorker = createTest("en-GB", function(assert, loader, done) {
|
||||
// Ensure initing html component that watch document creations
|
||||
// Note that this module is automatically initialized in
|
||||
// cuddlefish.js:Loader.main in regular addons. But it isn't for unit tests.
|
||||
let loaderHtmlL10n = loader.require("sdk/l10n/html");
|
||||
loaderHtmlL10n.enable();
|
||||
|
||||
let uri = require("sdk/self").data.url("test-localization.html");
|
||||
let worker = loader.require("sdk/page-worker").Page({
|
||||
contentURL: uri,
|
||||
contentScript: "new " + function ContentScriptScope() {
|
||||
let nodes = document.body.querySelectorAll("*[data-l10n-id]");
|
||||
self.postMessage([nodes[0].innerHTML,
|
||||
nodes[1].innerHTML,
|
||||
nodes[2].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(
|
||||
data[0],
|
||||
"Kept as-is",
|
||||
"Nodes with unknown id in .properties are kept 'as-is'"
|
||||
);
|
||||
assert.equal(data[1], "Yes", "HTML is translated");
|
||||
assert.equal(
|
||||
data[2],
|
||||
"no <b>HTML</b> injection",
|
||||
"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();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
exports.testHtmlLocalization = createTest("en-GB", function(assert, loader, done) {
|
||||
// Ensure initing html component that watch document creations
|
||||
// Note that this module is automatically initialized in
|
||||
// cuddlefish.js:Loader.main in regular addons. But it isn't for unit tests.
|
||||
let loaderHtmlL10n = loader.require("sdk/l10n/html");
|
||||
loaderHtmlL10n.enable();
|
||||
|
||||
let uri = require("sdk/self").data.url("test-localization.html");
|
||||
loader.require("sdk/tabs").open({
|
||||
url: uri,
|
||||
onReady: function(tab) {
|
||||
tab.attach({
|
||||
contentURL: uri,
|
||||
contentScript: "new " + function ContentScriptScope() {
|
||||
let nodes = document.body.querySelectorAll("*[data-l10n-id]");
|
||||
self.postMessage([nodes[0].innerHTML,
|
||||
nodes[1].innerHTML,
|
||||
nodes[2].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(
|
||||
data[0],
|
||||
"Kept as-is",
|
||||
"Nodes with unknown id in .properties are kept 'as-is'"
|
||||
);
|
||||
assert.equal(data[1], "Yes", "HTML is translated");
|
||||
assert.equal(
|
||||
data[2],
|
||||
"no <b>HTML</b> injection",
|
||||
"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.");
|
||||
|
||||
tab.close(done);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
exports.testEnUsLocaleName = createTest("en-US", function(assert, loader, done) {
|
||||
let _ = loader.require("sdk/l10n").get;
|
||||
|
||||
assert.equal(_("Not translated"), "Not translated",
|
||||
"String w/o translation is kept as-is");
|
||||
assert.equal(_("Translated"), "Yes",
|
||||
"String with translation is correctly translated");
|
||||
|
||||
// Check Unicode char escaping sequences
|
||||
assert.equal(_("unicodeEscape"), " @ ",
|
||||
"Unicode escaped sequances are correctly converted");
|
||||
|
||||
// Check plural forms regular matching
|
||||
assert.equal(_("downloadsCount", 0),
|
||||
"0 downloads",
|
||||
"PluralForm form 'other' for 0 in english");
|
||||
assert.equal(_("downloadsCount", 1),
|
||||
"one download",
|
||||
"PluralForm form 'one' for 1 in english");
|
||||
assert.equal(_("downloadsCount", 2),
|
||||
"2 downloads",
|
||||
"PluralForm form 'other' for n != 1 in english");
|
||||
|
||||
// Check optional plural forms
|
||||
assert.equal(_("pluralTest", 0),
|
||||
"optional zero form",
|
||||
"PluralForm form 'zero' can be optionaly specified. (Isn't mandatory in english)");
|
||||
assert.equal(_("pluralTest", 1),
|
||||
"fallback to other",
|
||||
"If the specific plural form is missing, we fallback to 'other'");
|
||||
|
||||
// Ensure that we can omit specifying the generic key without [other]
|
||||
// key[one] = ...
|
||||
// key[other] = ... # Instead of `key = ...`
|
||||
assert.equal(_("explicitPlural", 1),
|
||||
"one",
|
||||
"PluralForm form can be omitting generic key [i.e. without ...[other] at end of key)");
|
||||
assert.equal(_("explicitPlural", 10),
|
||||
"other",
|
||||
"PluralForm form can be omitting generic key [i.e. without ...[other] at end of key)");
|
||||
|
||||
assert.equal(_("first_identifier", "ONE", "TWO"), "the entries are ONE and TWO.", "first_identifier no count");
|
||||
assert.equal(_("first_identifier", 0, "ONE", "TWO"), "the entries are ONE and TWO.", "first_identifier with count = 0");
|
||||
assert.equal(_("first_identifier", 1, "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "first_identifier with count = 1");
|
||||
assert.equal(_("first_identifier", 2, "ONE", "TWO"), "the entries are ONE and TWO.", "first_identifier with count = 2");
|
||||
|
||||
assert.equal(_("second_identifier", "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "second_identifier with no count");
|
||||
assert.equal(_("second_identifier", 0, "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "second_identifier with count = 0");
|
||||
assert.equal(_("second_identifier", 1, "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "second_identifier with count = 1");
|
||||
assert.equal(_("second_identifier", 2, "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "second_identifier with count = 2");
|
||||
|
||||
assert.equal(_("third_identifier", "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "third_identifier with no count");
|
||||
assert.equal(_("third_identifier", 0, "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "third_identifier with count = 0");
|
||||
assert.equal(_("third_identifier", 2, "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "third_identifier with count = 2");
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
exports.testUsingJSON = function(assert) {
|
||||
assert.equal(usingJSON, !isNative, 'using json');
|
||||
}
|
||||
|
||||
exports.testShortLocaleName = createTest("eo", function(assert, loader, done) {
|
||||
let _ = loader.require("sdk/l10n").get;
|
||||
assert.equal(_("Not translated"), "Not translated",
|
||||
"String w/o translation is kept as-is");
|
||||
assert.equal(_("Translated"), "jes",
|
||||
"String with translation is correctly translated");
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
|
||||
// Before running tests, disable HTML service which is automatially enabled
|
||||
// in api-utils/addon/runner.js
|
||||
require('sdk/l10n/html').disable();
|
||||
|
||||
require("sdk/test/runner").runTestsFromModule(module);
|
||||
5
addon-sdk/source/test/addons/l10n/package.json
Normal file
5
addon-sdk/source/test/addons/l10n/package.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"id": "test-l10n@jetpack",
|
||||
"main": "./main.js",
|
||||
"version": "0.0.1"
|
||||
}
|
||||
15
addon-sdk/source/test/addons/layout-change/lib/main.js
Normal file
15
addon-sdk/source/test/addons/layout-change/lib/main.js
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
/* 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 { isNative } = require("@loader/options");
|
||||
|
||||
if (isNative) {
|
||||
module.exports = require("./test-toolkit-loader");
|
||||
}
|
||||
else {
|
||||
module.exports = require("./test-cuddlefish-loader");
|
||||
}
|
||||
|
||||
require("sdk/test/runner").runTestsFromModule(module);
|
||||
|
|
@ -0,0 +1,164 @@
|
|||
/* 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 { LoaderWithHookedConsole } = require('sdk/test/loader');
|
||||
const { loader } = LoaderWithHookedConsole(module);
|
||||
const app = require("sdk/system/xul-app");
|
||||
|
||||
// This test makes sure that require statements used by all AMO hosted
|
||||
// add-ons will be able to use old require statements.
|
||||
// Tests are based on following usage data:
|
||||
// https://docs.google.com/spreadsheet/ccc?key=0ApEBy-GRnGxzdHlRMHJ5RXN1aWJ4RGhINkxSd0FCQXc#gid=0
|
||||
|
||||
exports["test compatibility"] = function(assert) {
|
||||
let { require } = loader;
|
||||
|
||||
assert.equal(require("self"),
|
||||
require("sdk/self"), "sdk/self -> self");
|
||||
|
||||
assert.equal(require("tabs"),
|
||||
require("sdk/tabs"), "sdk/tabs -> tabs");
|
||||
|
||||
if (app.is("Firefox")) {
|
||||
assert.throws(() => require("widget"),
|
||||
/Module `widget` is not found at/,
|
||||
"There is no widget module");
|
||||
|
||||
assert.throws(() => require("sdk/widget"),
|
||||
/Module `sdk\/widget` is not found at/,
|
||||
"There is no sdk/widget module");
|
||||
}
|
||||
|
||||
assert.equal(require("page-mod"),
|
||||
require("sdk/page-mod"), "sdk/page-mod -> page-mod");
|
||||
|
||||
if (app.is("Firefox")) {
|
||||
assert.equal(require("panel"),
|
||||
require("sdk/panel"), "sdk/panel -> panel");
|
||||
}
|
||||
|
||||
assert.equal(require("request"),
|
||||
require("sdk/request"), "sdk/request -> request");
|
||||
|
||||
assert.equal(require("chrome"),
|
||||
require("chrome"), "chrome -> chrome");
|
||||
|
||||
assert.equal(require("simple-storage"),
|
||||
require("sdk/simple-storage"), "sdk/simple-storage -> simple-storage");
|
||||
|
||||
if (app.is("Firefox")) {
|
||||
assert.equal(require("context-menu"),
|
||||
require("sdk/context-menu"), "sdk/context-menu -> context-menu");
|
||||
}
|
||||
|
||||
assert.equal(require("notifications"),
|
||||
require("sdk/notifications"), "sdk/notifications -> notifications");
|
||||
|
||||
assert.equal(require("preferences-service"),
|
||||
require("sdk/preferences/service"), "sdk/preferences/service -> preferences-service");
|
||||
|
||||
assert.equal(require("window-utils"),
|
||||
require("sdk/deprecated/window-utils"), "sdk/deprecated/window-utils -> window-utils");
|
||||
|
||||
assert.equal(require("url"),
|
||||
require("sdk/url"), "sdk/url -> url");
|
||||
|
||||
if (app.is("Firefox")) {
|
||||
assert.equal(require("selection"),
|
||||
require("sdk/selection"), "sdk/selection -> selection");
|
||||
}
|
||||
|
||||
assert.equal(require("timers"),
|
||||
require("sdk/timers"), "sdk/timers -> timers");
|
||||
|
||||
assert.equal(require("simple-prefs"),
|
||||
require("sdk/simple-prefs"), "sdk/simple-prefs -> simple-prefs");
|
||||
|
||||
assert.equal(require("traceback"),
|
||||
require("sdk/console/traceback"), "sdk/console/traceback -> traceback");
|
||||
|
||||
assert.equal(require("unload"),
|
||||
require("sdk/system/unload"), "sdk/system/unload -> unload");
|
||||
|
||||
assert.equal(require("hotkeys"),
|
||||
require("sdk/hotkeys"), "sdk/hotkeys -> hotkeys");
|
||||
|
||||
if (app.is("Firefox")) {
|
||||
assert.equal(require("clipboard"),
|
||||
require("sdk/clipboard"), "sdk/clipboard -> clipboard");
|
||||
}
|
||||
|
||||
assert.equal(require("windows"),
|
||||
require("sdk/windows"), "sdk/windows -> windows");
|
||||
|
||||
assert.equal(require("page-worker"),
|
||||
require("sdk/page-worker"), "sdk/page-worker -> page-worker");
|
||||
|
||||
assert.equal(require("timer"),
|
||||
require("sdk/timers"), "sdk/timers -> timer");
|
||||
|
||||
assert.equal(require("xhr"),
|
||||
require("sdk/net/xhr"), "sdk/io/xhr -> xhr");
|
||||
|
||||
assert.equal(require("private-browsing"),
|
||||
require("sdk/private-browsing"), "sdk/private-browsing -> private-browsing");
|
||||
|
||||
assert.equal(require("passwords"),
|
||||
require("sdk/passwords"), "sdk/passwords -> passwords");
|
||||
|
||||
assert.equal(require("match-pattern"),
|
||||
require("sdk/util/match-pattern"), "sdk/util/match-pattern -> match-pattern");
|
||||
|
||||
assert.equal(require("file"),
|
||||
require("sdk/io/file"), "sdk/io/file -> file");
|
||||
|
||||
assert.equal(require("xul-app"),
|
||||
require("sdk/system/xul-app"), "sdk/system/xul-app -> xul-app");
|
||||
|
||||
assert.equal(require("api-utils"),
|
||||
require("sdk/deprecated/api-utils"), "sdk/deprecated/api-utils -> api-utils");
|
||||
|
||||
assert.equal(require("runtime"),
|
||||
require("sdk/system/runtime"), "sdk/system/runtime -> runtime");
|
||||
|
||||
assert.equal(require("base64"),
|
||||
require("sdk/base64"), "sdk/base64 -> base64");
|
||||
|
||||
assert.equal(require("xpcom"),
|
||||
require("sdk/platform/xpcom"), "sdk/platform/xpcom -> xpcom");
|
||||
|
||||
assert.equal(require("keyboard/utils"),
|
||||
require("sdk/keyboard/utils"), "sdk/keyboard/utils -> keyboard/utils");
|
||||
|
||||
assert.equal(require("system"),
|
||||
require("sdk/system"), "sdk/system -> system");
|
||||
|
||||
assert.equal(require("querystring"),
|
||||
require("sdk/querystring"), "sdk/querystring -> querystring");
|
||||
|
||||
assert.equal(require("tabs/utils"),
|
||||
require("sdk/tabs/utils"), "sdk/tabs/utils -> tabs/utils");
|
||||
|
||||
assert.equal(require("dom/events"),
|
||||
require("sdk/dom/events-shimmed"), "sdk/dom/events-shimmed -> dom/events");
|
||||
|
||||
assert.equal(require("tabs/tab.js"),
|
||||
require("sdk/tabs/tab"), "sdk/tabs/tab -> tabs/tab.js");
|
||||
|
||||
assert.equal(require("environment"),
|
||||
require("sdk/system/environment"), "sdk/system/environment -> environment");
|
||||
|
||||
assert.equal(require("test/assert"),
|
||||
require("sdk/test/assert"), "sdk/test/assert -> test/assert");
|
||||
|
||||
assert.equal(require("hidden-frame"),
|
||||
require("sdk/frame/hidden-frame"), "sdk/frame/hidden-frame -> hidden-frame");
|
||||
|
||||
assert.equal(require("collection"),
|
||||
require("sdk/util/collection"), "sdk/util/collection -> collection");
|
||||
|
||||
assert.equal(require("array"),
|
||||
require("sdk/util/array"), "sdk/util/array -> array");
|
||||
};
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
/* 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";
|
||||
|
||||
exports["test compatibility"] = function(assert) {
|
||||
assert.throws(() => require("self"),
|
||||
/^Module `self` is not found/,
|
||||
"sdk/self -> self");
|
||||
};
|
||||
7
addon-sdk/source/test/addons/layout-change/package.json
Normal file
7
addon-sdk/source/test/addons/layout-change/package.json
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"id": "test-layout-change@jetpack",
|
||||
"name": "test-layout-change",
|
||||
"ignore-deprecated-path": true,
|
||||
"version": "0.0.2",
|
||||
"main": "./lib/main.js"
|
||||
}
|
||||
37
addon-sdk/source/test/addons/main/main.js
Normal file
37
addon-sdk/source/test/addons/main/main.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* 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 { setTimeout } = require('sdk/timers');
|
||||
|
||||
var mainStarted = false;
|
||||
|
||||
exports.main = function main(options, callbacks) {
|
||||
mainStarted = true;
|
||||
|
||||
let tests = {};
|
||||
|
||||
tests.testMainArguments = function(assert) {
|
||||
assert.ok(!!options, 'options argument provided to main');
|
||||
assert.ok('loadReason' in options, 'loadReason is in options provided by main');
|
||||
assert.equal(typeof callbacks.print, 'function', 'callbacks.print is a function');
|
||||
assert.equal(typeof callbacks.quit, 'function', 'callbacks.quit is a function');
|
||||
|
||||
// Re-enable when bug 1251664 is fixed
|
||||
//assert.equal(options.loadReason, 'install', 'options.loadReason is install');
|
||||
}
|
||||
|
||||
require('sdk/test/runner').runTestsFromModule({exports: tests});
|
||||
}
|
||||
|
||||
// this causes a fail if main does not start
|
||||
setTimeout(function() {
|
||||
if (mainStarted)
|
||||
return;
|
||||
|
||||
// main didn't start, fail..
|
||||
require("sdk/test/runner").runTestsFromModule({exports: {
|
||||
testFail: assert => assert.fail('Main did not start..')
|
||||
}});
|
||||
}, 500);
|
||||
5
addon-sdk/source/test/addons/main/package.json
Normal file
5
addon-sdk/source/test/addons/main/package.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"id": "test-main@jetpack",
|
||||
"main": "./main.js",
|
||||
"version": "0.0.1"
|
||||
}
|
||||
12
addon-sdk/source/test/addons/name-in-numbers-plus/index.js
Normal file
12
addon-sdk/source/test/addons/name-in-numbers-plus/index.js
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
/* 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 { name } = require("sdk/self");
|
||||
|
||||
exports["test self.name"] = (assert) => {
|
||||
assert.equal(name, "0-0", "using '0-0' works.");
|
||||
}
|
||||
|
||||
require("sdk/test/runner").runTestsFromModule(module);
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"id": "name-in-numbers-plus@jetpack",
|
||||
"name": "0-0",
|
||||
"main": "./index.js",
|
||||
"version": "0.0.1"
|
||||
}
|
||||
12
addon-sdk/source/test/addons/name-in-numbers/index.js
Normal file
12
addon-sdk/source/test/addons/name-in-numbers/index.js
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
/* 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 { name } = require("sdk/self");
|
||||
|
||||
exports["test self.name"] = (assert) => {
|
||||
assert.equal(name, "5", "using '5' works.");
|
||||
}
|
||||
|
||||
require("sdk/test/runner").runTestsFromModule(module);
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"id": "name-in-numbers@jetpack",
|
||||
"name": "5",
|
||||
"main": "./index.js",
|
||||
"version": "0.0.1"
|
||||
}
|
||||
57
addon-sdk/source/test/addons/packaging/main.js
Normal file
57
addon-sdk/source/test/addons/packaging/main.js
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/* 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 options = require("@loader/options");
|
||||
var metadata = {};
|
||||
if (options.isNative) {
|
||||
metadata = require("./package.json");
|
||||
metadata.permissions = {};
|
||||
Object.freeze(metadata);
|
||||
}
|
||||
else {
|
||||
metadata = options.metadata;
|
||||
}
|
||||
|
||||
exports.testPackaging = function(assert) {
|
||||
assert.equal(metadata.description,
|
||||
"Add-on development made easy.",
|
||||
"packaging metadata should be available");
|
||||
try {
|
||||
metadata.description = 'new description';
|
||||
assert.fail('should not have been able to set options.metadata property');
|
||||
}
|
||||
catch (e) {}
|
||||
|
||||
assert.equal(metadata.description,
|
||||
"Add-on development made easy.",
|
||||
"packaging metadata should be frozen");
|
||||
|
||||
assert.equal(metadata.permissions['private-browsing'], undefined,
|
||||
"private browsing metadata should be undefined");
|
||||
|
||||
assert.equal(metadata['private-browsing'], undefined,
|
||||
"private browsing metadata should be be frozen");
|
||||
|
||||
assert.equal(options['private-browsing'], undefined,
|
||||
"private browsing metadata should be be frozen");
|
||||
|
||||
try {
|
||||
metadata['private-browsing'] = true;
|
||||
assert.fail('should not have been able to set options.metadata property');
|
||||
}
|
||||
catch(e) {}
|
||||
assert.equal(metadata['private-browsing'], undefined,
|
||||
"private browsing metadata should be be frozen");
|
||||
|
||||
try {
|
||||
options.permissions['private-browsing'] = true;
|
||||
assert.fail('should not have been able to set options.metadata.permissions property');
|
||||
}
|
||||
catch (e) {}
|
||||
assert.equal(metadata.permissions['private-browsing'], undefined,
|
||||
"private browsing metadata should be be frozen");
|
||||
};
|
||||
|
||||
require("sdk/test/runner").runTestsFromModule(module);
|
||||
6
addon-sdk/source/test/addons/packaging/package.json
Normal file
6
addon-sdk/source/test/addons/packaging/package.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"id": "test-packaging@jetpack",
|
||||
"description": "Add-on development made easy.",
|
||||
"main": "./main.js",
|
||||
"version": "0.0.1"
|
||||
}
|
||||
20
addon-sdk/source/test/addons/packed/main.js
Normal file
20
addon-sdk/source/test/addons/packed/main.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
const { packed } = require("sdk/self");
|
||||
const url = require("sdk/url");
|
||||
|
||||
exports["test self.packed"] = function (assert) {
|
||||
assert.ok(packed, "require('sdk/self').packed is correct");
|
||||
}
|
||||
|
||||
exports["test url.toFilename"] = function (assert) {
|
||||
assert.throws(
|
||||
function() { url.toFilename(module.uri); },
|
||||
/cannot map to filename: /,
|
||||
"url.toFilename() can fail for packed XPIs");
|
||||
}
|
||||
|
||||
require("sdk/test/runner").runTestsFromModule(module);
|
||||
6
addon-sdk/source/test/addons/packed/package.json
Normal file
6
addon-sdk/source/test/addons/packed/package.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"id": "test-url@jetpack",
|
||||
"unpack": false,
|
||||
"main": "./main.js",
|
||||
"version": "0.0.1"
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<!-- 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/. -->
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Page Mod Debugger Test</title>
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
/* 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';
|
||||
|
||||
function runDebuggerStatement () {
|
||||
window.document.body.setAttribute('style', 'background-color: red');
|
||||
debugger;
|
||||
window.document.body.setAttribute('style', 'background-color: green');
|
||||
}
|
||||
|
||||
exportFunction(
|
||||
runDebuggerStatement,
|
||||
document.defaultView,
|
||||
{ defineAs: "runDebuggerStatement" }
|
||||
);
|
||||
136
addon-sdk/source/test/addons/page-mod-debugger-post/main.js
Normal file
136
addon-sdk/source/test/addons/page-mod-debugger-post/main.js
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
/* 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 { Cu } = require('chrome');
|
||||
const { PageMod } = require('sdk/page-mod');
|
||||
const tabs = require('sdk/tabs');
|
||||
const { closeTab } = require('sdk/tabs/utils');
|
||||
const promise = require('sdk/core/promise')
|
||||
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
|
||||
const { data } = require('sdk/self');
|
||||
const { set } = require('sdk/preferences/service');
|
||||
|
||||
const { require: devtoolsRequire } = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
||||
const { DebuggerServer } = devtoolsRequire("devtools/server/main");
|
||||
const { DebuggerClient } = devtoolsRequire("devtools/shared/client/main");
|
||||
|
||||
var gClient;
|
||||
var ok;
|
||||
var testName = 'testDebugger';
|
||||
var iframeURL = 'data:text/html;charset=utf-8,' + testName;
|
||||
var TAB_URL = 'data:text/html;charset=utf-8,' + encodeURIComponent('<iframe src="' + iframeURL + '" />');
|
||||
TAB_URL = data.url('index.html');
|
||||
var mod;
|
||||
|
||||
exports.testDebugger = function(assert, done) {
|
||||
ok = assert.ok.bind(assert);
|
||||
assert.pass('starting test');
|
||||
set('devtools.debugger.log', true);
|
||||
|
||||
if (!DebuggerServer.initialized) {
|
||||
DebuggerServer.init();
|
||||
DebuggerServer.addBrowserActors();
|
||||
}
|
||||
|
||||
let transport = DebuggerServer.connectPipe();
|
||||
gClient = new DebuggerClient(transport);
|
||||
gClient.connect((aType, aTraits) => {
|
||||
tabs.open({
|
||||
url: TAB_URL,
|
||||
onLoad: function(tab) {
|
||||
assert.pass('tab loaded');
|
||||
|
||||
attachTabActorForUrl(gClient, TAB_URL).
|
||||
then(_ => { assert.pass('attachTabActorForUrl called'); return _; }).
|
||||
then(attachThread).
|
||||
then(testDebuggerStatement).
|
||||
then(_ => { assert.pass('testDebuggerStatement called') }).
|
||||
then(closeConnection).
|
||||
then(_ => { assert.pass('closeConnection called') }).
|
||||
then(_ => { tab.close() }).
|
||||
then(done).
|
||||
then(null, aError => {
|
||||
ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function attachThread([aGrip, aResponse]) {
|
||||
let deferred = promise.defer();
|
||||
|
||||
// Now attach and resume...
|
||||
gClient.request({ to: aResponse.threadActor, type: "attach" }, () => {
|
||||
gClient.request({ to: aResponse.threadActor, type: "resume" }, () => {
|
||||
ok(true, "Pause wasn't called before we've attached.");
|
||||
deferred.resolve([aGrip, aResponse]);
|
||||
});
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function testDebuggerStatement([aGrip, aResponse]) {
|
||||
let deferred = promise.defer();
|
||||
ok(aGrip, 'aGrip existss')
|
||||
|
||||
gClient.addListener("paused", (aEvent, aPacket) => {
|
||||
ok(true, 'there was a pause event');
|
||||
gClient.request({ to: aResponse.threadActor, type: "resume" }, () => {
|
||||
ok(true, "The pause handler was triggered on a debugger statement.");
|
||||
deferred.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
mod = PageMod({
|
||||
include: TAB_URL,
|
||||
attachTo: ['existing', 'top', 'frame'],
|
||||
contentScriptFile: data.url('script.js'),
|
||||
onAttach: function(mod) {
|
||||
ok(true, 'the page-mod was attached to ' + mod.tab.url);
|
||||
|
||||
require('sdk/timers').setTimeout(function() {
|
||||
let debuggee = getMostRecentBrowserWindow().gBrowser.selectedBrowser.contentWindow.wrappedJSObject;
|
||||
debuggee.runDebuggerStatement();
|
||||
ok(true, 'called runDebuggerStatement');
|
||||
}, 500)
|
||||
}
|
||||
});
|
||||
ok(true, 'PageMod was created');
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function getTabActorForUrl(aClient, aUrl) {
|
||||
let deferred = promise.defer();
|
||||
|
||||
aClient.listTabs(aResponse => {
|
||||
let tabActor = aResponse.tabs.filter(aGrip => aGrip.url == aUrl).pop();
|
||||
deferred.resolve(tabActor);
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function attachTabActorForUrl(aClient, aUrl) {
|
||||
let deferred = promise.defer();
|
||||
|
||||
getTabActorForUrl(aClient, aUrl).then(aGrip => {
|
||||
aClient.attachTab(aGrip.actor, aResponse => {
|
||||
deferred.resolve([aGrip, aResponse]);
|
||||
});
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function closeConnection() {
|
||||
let deferred = promise.defer();
|
||||
gClient.close(deferred.resolve);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
require('sdk/test/runner').runTestsFromModule(module);
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"id": "test-page-mod-debugger@jetpack",
|
||||
"author": "Erik Vold",
|
||||
"main": "./main.js",
|
||||
"version": "0.0.1"
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<!-- 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/. -->
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Page Mod Debugger Test</title>
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
/* 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';
|
||||
|
||||
function runDebuggerStatement () {
|
||||
window.document.body.setAttribute('style', 'background-color: red');
|
||||
debugger;
|
||||
window.document.body.setAttribute('style', 'background-color: green');
|
||||
}
|
||||
|
||||
exportFunction(
|
||||
runDebuggerStatement,
|
||||
document.defaultView,
|
||||
{ defineAs: "runDebuggerStatement" }
|
||||
);
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue