"use stirct";
const { Cc, Ci } = require('chrome');
const timer = require('timer');
function makeWindow(contentURL) {
let content =
'' +
'' +
'' +
'' +
'';
var url = "data:application/vnd.mozilla.xul+xml," +
encodeURIComponent(content);
var features = ["chrome", "width=10", "height=10"];
return Cc["@mozilla.org/embedcomp/window-watcher;1"].
getService(Ci.nsIWindowWatcher).
openWindow(null, url, null, features.join(","), null);
}
const { Worker } = require('content/worker');
exports['test:sample'] = function(test) {
let window = makeWindow();
test.waitUntilDone();
// As window has just being created, its document is still loading,
// and we have about:blank document before the expected one
test.assertEqual(window.document.location.href, "about:blank",
"window starts by loading about:blank");
// We need to wait for the load/unload of temporary about:blank
// or our worker is going to be automatically destroyed
window.addEventListener("load", function onload() {
window.removeEventListener("load", onload, true);
test.assertNotEqual(window.document.location.href, "about:blank",
"window is now on the right document");
let worker = Worker({
window: window,
contentScript: 'new ' + function WorkerScope() {
// window is accessible
let myLocation = window.location.toString();
self.on('message', function(data) {
if (data == 'hi!')
self.postMessage('bye!');
});
},
contentScriptWhen: 'ready',
onMessage: function(msg) {
test.assertEqual('bye!', msg);
test.assertEqual(worker.url, window.document.location.href,
"worker.url still works");
test.done();
}
});
test.assertEqual(worker.url, window.document.location.href,
"worker.url works");
worker.postMessage('hi!');
}, true);
}
exports['test:emit'] = function(test) {
let window = makeWindow();
test.waitUntilDone();
let worker = Worker({
window: window,
contentScript: 'new ' + function WorkerScope() {
// Validate self.on and self.emit
self.port.on('addon-to-content', function (data) {
self.port.emit('content-to-addon', data);
});
// Check for global pollution
//if (typeof on != "undefined")
// self.postMessage("`on` is in globals");
if (typeof once != "undefined")
self.postMessage("`once` is in globals");
if (typeof emit != "undefined")
self.postMessage("`emit` is in globals");
},
onMessage: function(msg) {
test.fail("Got an unexpected message : "+msg);
}
});
// Validate worker.port
worker.port.on('content-to-addon', function (data) {
test.assertEqual(data, "event data");
test.done();
});
worker.port.emit('addon-to-content', 'event data');
}
exports['test:emit hack message'] = function(test) {
let window = makeWindow();
test.waitUntilDone();
let worker = Worker({
window: window,
contentScript: 'new ' + function WorkerScope() {
// Validate self.port
self.port.on('message', function (data) {
self.port.emit('message', data);
});
// We should not receive message on self, but only on self.port
self.on('message', function (data) {
self.postMessage('message', data);
});
},
onError: function(e) {
test.fail("Got exception: "+e);
}
});
worker.port.on('message', function (data) {
test.assertEqual(data, "event data");
test.done();
});
worker.on('message', function (data) {
test.fail("Got an unexpected message : "+msg);
});
worker.port.emit('message', 'event data');
}
exports['test:n-arguments emit'] = function(test) {
let window = makeWindow();
test.waitUntilDone();
let worker = Worker({
window: window,
contentScript: 'new ' + function WorkerScope() {
// Validate self.on and self.emit
self.port.on('addon-to-content', function (a1, a2, a3) {
self.port.emit('content-to-addon', a1, a2, a3);
});
}
});
// Validate worker.port
worker.port.on('content-to-addon', function (arg1, arg2, arg3) {
test.assertEqual(arg1, "first argument");
test.assertEqual(arg2, "second");
test.assertEqual(arg3, "third");
test.done();
});
worker.port.emit('addon-to-content', 'first argument', 'second', 'third');
}
exports['test:post-json-values-only'] = function(test) {
let window = makeWindow("data:text/html,");
test.waitUntilDone();
window.addEventListener("load", function onload() {
window.removeEventListener("load", onload, true);
let worker = Worker({
window: window.document.getElementById("content").contentWindow,
contentScript: 'new ' + function WorkerScope() {
self.on('message', function (message) {
self.postMessage([ message.fun === undefined,
typeof message.w,
message.w && "port" in message.w,
message.w.url,
Array.isArray(message.array),
JSON.stringify(message.array)]);
});
}
});
// Validate worker.onMessage
let array = [1, 2, 3];
worker.on('message', function (message) {
test.assert(message[0], "function becomes undefined");
test.assertEqual(message[1], "object", "object stays object");
test.assert(message[2], "object's attributes are enumerable");
test.assertEqual(message[3], "about:blank", "jsonable attributes are accessible");
// See bug 714891, Arrays may be broken over compartements:
test.assert(message[4], "Array keeps being an array");
test.assertEqual(message[5], JSON.stringify(array),
"Array is correctly serialized");
test.done();
});
worker.postMessage({ fun: function () {}, w: worker, array: array });
}, true);
};
exports['test:emit-json-values-only'] = function(test) {
let window = makeWindow("data:text/html,");
test.waitUntilDone();
window.addEventListener("load", function onload() {
window.removeEventListener("load", onload, true);
let win = window.document.getElementById("content").contentWindow;
let worker = Worker({
window: win,
contentScript: 'new ' + function WorkerScope() {
// Validate self.on and self.emit
self.port.on('addon-to-content', function (fun, w, obj, array) {
self.port.emit('content-to-addon', [
fun === null,
typeof w,
"port" in w,
w.url,
"fun" in obj,
Object.keys(obj.dom).length,
Array.isArray(array),
JSON.stringify(array)
]);
});
}
});
// Validate worker.port
let array = [1, 2, 3];
worker.port.on('content-to-addon', function (result) {
test.assert(result[0], "functions become null");
test.assertEqual(result[1], "object", "objects stay objects");
test.assert(result[2], "object's attributes are enumerable");
test.assertEqual(result[3], "about:blank", "json attribute is accessible");
test.assert(!result[4], "function as object attribute is removed");
test.assertEqual(result[5], 0, "DOM nodes are converted into empty object");
// See bug 714891, Arrays may be broken over compartements:
test.assert(result[6], "Array keeps being an array");
test.assertEqual(result[7], JSON.stringify(array),
"Array is correctly serialized");
test.done();
});
let obj = {
fun: function () {},
dom: win.document.documentElement
};
worker.port.emit('addon-to-content', function () {}, worker, obj, array);
}, true);
}
exports['test:content is wrapped'] = function(test) {
let contentURL = 'data:text/html,';
let window = makeWindow(contentURL);
test.waitUntilDone();
window.addEventListener("load", function onload() {
window.removeEventListener("load", onload, true);
let worker = Worker({
window: window.document.getElementById("content").contentWindow,
contentScript: 'new ' + function WorkerScope() {
self.postMessage(!window.documentValue);
},
contentScriptWhen: 'ready',
onMessage: function(msg) {
test.assert(msg,
"content script has a wrapped access to content document");
test.done();
}
});
}, true);
}
exports['test:chrome is unwrapped'] = function(test) {
let window = makeWindow();
test.waitUntilDone();
window.addEventListener("load", function onload() {
window.removeEventListener("load", onload, true);
let worker = Worker({
window: window,
contentScript: 'new ' + function WorkerScope() {
self.postMessage(window.documentValue);
},
contentScriptWhen: 'ready',
onMessage: function(msg) {
test.assert(msg,
"content script has an unwrapped access to chrome document");
test.done();
}
});
}, true);
}
exports['test:setTimeout can\'t be cancelled by content'] = function(test) {
let contentURL = 'data:text/html,';
let window = makeWindow(contentURL);
test.waitUntilDone();
window.addEventListener("load", function onload() {
window.removeEventListener("load", onload, true);
let worker = Worker({
window: window.document.getElementById("content").contentWindow,
contentScript: 'new ' + function WorkerScope() {
let id = setTimeout(function () {
self.postMessage("timeout");
}, 100);
unsafeWindow.eval("clearTimeout("+id+");");
},
contentScriptWhen: 'ready',
onMessage: function(msg) {
test.assert(msg,
"content didn't managed to cancel our setTimeout");
test.done();
}
});
}, true);
}
exports['test:setTimeout are unregistered on content unload'] = function(test) {
let contentURL = 'data:text/html,foo';
let window = makeWindow(contentURL);
test.waitUntilDone();
window.addEventListener("load", function onload() {
window.removeEventListener("load", onload, true);
let iframe = window.document.getElementById("content");
let originalDocument = iframe.contentDocument;
let worker = Worker({
window: iframe.contentWindow,
contentScript: 'new ' + function WorkerScope() {
document.title = "ok";
let i = 0;
setInterval(function () {
document.title = i++;
}, 10);
},
contentScriptWhen: 'ready'
});
// Change location so that content script is destroyed,
// and all setTimeout/setInterval should be unregistered.
// Wait some cycles in order to execute some intervals.
timer.setTimeout(function () {
// Bug 689621: Wait for the new document load so that we are sure that
// previous document cancelled its intervals
iframe.addEventListener("load", function onload() {
iframe.removeEventListener("load", onload, true);
let titleAfterLoad = originalDocument.title;
// Wait additional cycles to verify that intervals are really cancelled
timer.setTimeout(function () {
test.assertEqual(iframe.contentDocument.title, "final",
"New document has not been modified");
test.assertEqual(originalDocument.title, titleAfterLoad,
"Nor previous one");
test.done();
}, 100);
}, true);
iframe.setAttribute("src", "data:text/html,
final");
}, 100);
}, true);
}