375 lines
13 KiB
HTML
375 lines
13 KiB
HTML
<!DOCTYPE html>
|
|
<meta charset=utf-8>
|
|
<script src="/resources/testharness.js"></script>
|
|
<script src="/resources/testharnessreport.js"></script>
|
|
<script>
|
|
|
|
async_test(t => {
|
|
let c1 = new BroadcastChannel('worker');
|
|
let c2 = new BroadcastChannel('worker');
|
|
let events = [];
|
|
|
|
c1.onmessage = e => events.push(e);
|
|
c2.onmessage = e => events.push(e);
|
|
|
|
let doneCount = 0;
|
|
c2.addEventListener('message', t.step_func(e => {
|
|
if (e.data == 'from worker') {
|
|
c2.postMessage('from c2');
|
|
c1.postMessage('done');
|
|
} else if (e.data == 'done') {
|
|
assert_equals(events.length, 4);
|
|
assert_equals(events[0].data, 'from worker');
|
|
assert_equals(events[0].target, c1);
|
|
assert_equals(events[1].data, 'from worker');
|
|
assert_equals(events[1].target, c2);
|
|
assert_equals(events[2].data, 'from c2');
|
|
assert_equals(events[3].data, 'done');
|
|
if (++doneCount == 2) t.done();
|
|
}
|
|
}));
|
|
|
|
let worker = new Worker('resources/worker.js');
|
|
worker.onmessage = t.step_func(e => {
|
|
assert_array_equals(e.data, ['from c2', 'done']);
|
|
if (++doneCount == 2) t.done();
|
|
});
|
|
worker.postMessage({channel: 'worker'});
|
|
|
|
}, 'BroadcastChannel works in workers');
|
|
|
|
async_test(t => {
|
|
let c1 = new BroadcastChannel('shared worker');
|
|
let c2 = new BroadcastChannel('shared worker');
|
|
let events = [];
|
|
|
|
c1.onmessage = e => events.push(e);
|
|
c2.onmessage = e => events.push(e);
|
|
|
|
let doneCount = 0;
|
|
c2.addEventListener('message', t.step_func(e => {
|
|
if (e.data == 'from worker') {
|
|
c2.postMessage('from c2');
|
|
c1.postMessage('done');
|
|
} else if (e.data == 'done') {
|
|
assert_equals(events.length, 4);
|
|
assert_equals(events[0].data, 'from worker');
|
|
assert_equals(events[0].target, c1);
|
|
assert_equals(events[1].data, 'from worker');
|
|
assert_equals(events[1].target, c2);
|
|
assert_equals(events[2].data, 'from c2');
|
|
assert_equals(events[3].data, 'done');
|
|
if (++doneCount == 2) t.done();
|
|
}
|
|
}));
|
|
|
|
let worker = new SharedWorker('resources/worker.js');
|
|
worker.port.onmessage = t.step_func(e => {
|
|
assert_array_equals(e.data, ['from c2', 'done']);
|
|
if (++doneCount == 2) t.done();
|
|
});
|
|
worker.port.postMessage({channel: 'shared worker'});
|
|
|
|
}, 'BroadcastChannel works in shared workers');
|
|
|
|
async_test(t => {
|
|
let c = new BroadcastChannel('worker-close');
|
|
let events = [];
|
|
|
|
c.onmessage = e => events.push('c1: ' + e.data);
|
|
|
|
let worker = new Worker('resources/worker.js');
|
|
worker.onmessage = t.step_func(e => {
|
|
assert_array_equals(events,
|
|
['c1: from worker', 'c2: ready', 'c2: echo'],
|
|
'messages in document');
|
|
assert_array_equals(e.data, ['done'], 'messages in worker');
|
|
t.done();
|
|
});
|
|
worker.onmessagerror =
|
|
t.unreached_func('Worker\'s onmessageerror handler called');
|
|
|
|
c.addEventListener('message', e => {
|
|
if (e.data == 'from worker') {
|
|
c.close();
|
|
if (self.gc) self.gc();
|
|
window.setTimeout(() => {
|
|
let c2 = new BroadcastChannel('worker-close');
|
|
c2.onmessage = e => {
|
|
events.push('c2: ' + e.data);
|
|
if (e.data === 'ready') {
|
|
worker.postMessage({ping: 'echo'});
|
|
} else {
|
|
c2.postMessage('done');
|
|
c2.close();
|
|
}
|
|
};
|
|
// For some implementations there may be a race condition between
|
|
// when the BroadcastChannel instance above is created / ready to
|
|
// receive messages and when the worker calls postMessage on it's
|
|
// BroadcastChannel instance. To avoid this, confirm that our
|
|
// instance can receive a message before indicating to the other
|
|
// thread that we are ready. For more details, see:
|
|
// https://github.com/whatwg/html/issues/7267
|
|
let c3 = new BroadcastChannel('worker-close');
|
|
c3.postMessage('ready');
|
|
c3.close();
|
|
}, 1);
|
|
}
|
|
});
|
|
|
|
worker.postMessage({channel: 'worker-close'});
|
|
t.add_cleanup(() => worker.terminate());
|
|
|
|
}, 'Closing and re-opening a channel works.');
|
|
|
|
async_test(t => {
|
|
function workerCode() {
|
|
close();
|
|
try {
|
|
var bc = new BroadcastChannel('worker-create-after-close');
|
|
} catch (e) {
|
|
postMessage(e);
|
|
return;
|
|
}
|
|
postMessage(true);
|
|
}
|
|
|
|
var workerBlob = new Blob(
|
|
[workerCode.toString() + ';workerCode();'],
|
|
{type: 'application/javascript'});
|
|
|
|
var w = new Worker(URL.createObjectURL(workerBlob));
|
|
w.onmessage = t.step_func_done(function(e) {
|
|
assert_equals(
|
|
e.data, true,
|
|
'BroadcastChannel creation in closed worker triggered exception: ' +
|
|
e.data.message);
|
|
});
|
|
t.add_cleanup(() => w.terminate());
|
|
}, 'BroadcastChannel created after a worker self.close()');
|
|
|
|
|
|
function postMessageFromWorkerWorkerCode(workerName, channelName) {
|
|
if (workerName === 'close-before-create-worker') {
|
|
close();
|
|
}
|
|
let bc = new BroadcastChannel(channelName);
|
|
if (workerName === 'close-after-create-worker') {
|
|
close();
|
|
}
|
|
bc.postMessage(workerName + ' done');
|
|
postMessage(true);
|
|
}
|
|
|
|
function doPostMessageFromWorkerTest(t, workerName, channelName) {
|
|
var bc = new BroadcastChannel(channelName);
|
|
bc.onmessage = t.step_func_done(function(e) {
|
|
assert_equals(
|
|
e.data, 'done-worker done',
|
|
'BroadcastChannel message should only be received from the second worker');
|
|
});
|
|
t.add_cleanup(() => bc.close());
|
|
|
|
var testMessageHandler = t.step_func(function(e) {
|
|
assert_equals(
|
|
e.data, true,
|
|
'Worker sent postMessage indicating it sent a BroadcastChannel message');
|
|
|
|
var w = createWorker(
|
|
postMessageFromWorkerWorkerCode, 'done-worker', channelName);
|
|
t.add_cleanup(() => w.terminate());
|
|
});
|
|
createWorker(
|
|
postMessageFromWorkerWorkerCode, workerName, channelName,
|
|
testMessageHandler);
|
|
|
|
// To avoid calling t.step_timeout here, have the worker postMessage(true)
|
|
// once it is finished and then we'll instantiate another worker that
|
|
// performs the same test steps but doesn't close. By the time the
|
|
// BroadcastChannel message in that worker gets sent successfully it should
|
|
// be safe to assume that any BroadcastChannel messages from the previous
|
|
// worker would have been sent if they were going to be.
|
|
}
|
|
|
|
function createWorker(workerCode, workerName, channelName, handler = null) {
|
|
var workerCodeStr = workerCode.toString() +
|
|
`;${workerCode.name}("${workerName}", "${channelName}");`;
|
|
var workerBlob = new Blob([workerCodeStr], {type: 'application/javascript'});
|
|
var w = new Worker(URL.createObjectURL(workerBlob));
|
|
if (handler !== null) {
|
|
w.onmessage = handler;
|
|
}
|
|
return w;
|
|
}
|
|
|
|
async_test(t => {
|
|
const workerName = 'close-after-create-worker';
|
|
const channelName = workerName + '-postmessage-from-worker';
|
|
doPostMessageFromWorkerTest(t, workerName, channelName);
|
|
}, 'BroadcastChannel messages from closed worker to parent should be ignored (BC created before closing)');
|
|
|
|
async_test(t => {
|
|
const workerName = 'close-before-create-worker';
|
|
const channelName = workerName + '-postmessage-from-worker';
|
|
doPostMessageFromWorkerTest(t, workerName, channelName);
|
|
}, 'BroadcastChannel messages from closed worker to parent should be ignored (BC created after closing)');
|
|
|
|
|
|
function postMessageToWorkerWorkerCode(workerName, channelName) {
|
|
self.addEventListener('message', () => {
|
|
if (workerName === 'close-before-create-worker') {
|
|
close();
|
|
}
|
|
try {
|
|
let bc1 = new BroadcastChannel(channelName);
|
|
bc1.onmessage = e => {
|
|
if (e.data === 'ready') {
|
|
postMessage(e.data);
|
|
} else if (e.data === 'test') {
|
|
postMessage(workerName + ' done');
|
|
}
|
|
};
|
|
bc1.onmessageerror = () => {
|
|
postMessage('onmessageerror called from worker BroadcastChannel');
|
|
};
|
|
if (workerName === 'close-after-create-worker') {
|
|
close();
|
|
}
|
|
} catch (e) {
|
|
postMessage(e);
|
|
return;
|
|
}
|
|
|
|
if (workerName === 'done-worker') {
|
|
// For some implementations there may be a race condition between when
|
|
// the BroadcastChannel instance above is created / ready to receive
|
|
// messages and when the parent calls postMessage on it's
|
|
// BroadcastChannel instance. To avoid this, confirm that our instance
|
|
// can receive a message before indicating to the other thread that we
|
|
// are ready. For more details, see:
|
|
// https://github.com/whatwg/html/issues/7267
|
|
let bc2 = new BroadcastChannel(channelName);
|
|
bc2.postMessage('ready');
|
|
bc2.close();
|
|
} else {
|
|
// Since the worker has closed, it's not expected that the
|
|
// BroadcastChannel will receive messages (there's a separate test for
|
|
// that), so just indicate directly that it's ready to test receiving
|
|
// a message from the parent dispite the possibility of a race condition.
|
|
postMessage('ready');
|
|
}
|
|
});
|
|
self.addEventListener('messageerror', () => {
|
|
postMessage('onmessageerror called from worker');
|
|
});
|
|
}
|
|
|
|
function doPostMessageToWorkerTest(t, workerName, channelName) {
|
|
var bc = new BroadcastChannel(channelName);
|
|
t.add_cleanup(() => bc.close());
|
|
|
|
var doneMessageHandler = t.step_func(function(e) {
|
|
if (e.data === 'ready') {
|
|
bc.postMessage('test');
|
|
} else if (e.data === 'done-worker done') {
|
|
t.done();
|
|
} else {
|
|
assert_unreached(
|
|
'BroadcastChannel.postMessage triggered exception within second worker: ' +
|
|
e.data.message);
|
|
}
|
|
});
|
|
var testMessageHandler = t.step_func(function(e) {
|
|
assert_equals(
|
|
e.data, 'ready',
|
|
'Worker sent postMessage indicating its BroadcastChannel instance is ready');
|
|
bc.postMessage('test');
|
|
|
|
var doneWorker = createWorker(
|
|
postMessageToWorkerWorkerCode, 'done-worker', channelName,
|
|
doneMessageHandler);
|
|
t.add_cleanup(() => {
|
|
doneWorker.terminate();
|
|
});
|
|
doneWorker.postMessage('start');
|
|
});
|
|
var testWorker = createWorker(
|
|
postMessageToWorkerWorkerCode, workerName, channelName,
|
|
testMessageHandler);
|
|
testWorker.postMessage('start');
|
|
}
|
|
|
|
async_test(t => {
|
|
const workerName = 'close-after-create-worker';
|
|
const channelName = workerName + '-postmessage-to-worker';
|
|
doPostMessageToWorkerTest(t, workerName, channelName);
|
|
}, 'BroadcastChannel messages from parent to closed worker should be ignored (BC created before closing)');
|
|
|
|
async_test(t => {
|
|
const workerName = 'close-before-create-worker';
|
|
const channelName = workerName + '-postmessage-to-worker';
|
|
doPostMessageToWorkerTest(t, workerName, channelName);
|
|
}, 'BroadcastChannel messages from parent to closed worker should be ignored (BC created after closing)');
|
|
|
|
|
|
function postMessageWithinWorkerWorkerCode(workerName, channelName) {
|
|
if (workerName === 'close-before-create-worker') {
|
|
close();
|
|
}
|
|
try {
|
|
let bc1 = new BroadcastChannel(channelName);
|
|
let bc2 = new BroadcastChannel(channelName);
|
|
bc1.onmessage = e => {
|
|
postMessage(workerName + ' done')
|
|
};
|
|
if (workerName === 'close-after-create-worker') {
|
|
close();
|
|
}
|
|
bc2.postMessage(true);
|
|
postMessage(true);
|
|
} catch (e) {
|
|
postMessage(e);
|
|
}
|
|
}
|
|
|
|
function doPostMessageWithinWorkerTest(t, workerName, channelName) {
|
|
var doneMessageHandler = t.step_func(function(e) {
|
|
if (e.data === true) {
|
|
// Done worker has finished - no action needed
|
|
} else if (e.data === 'done-worker done') {
|
|
t.done();
|
|
} else {
|
|
assert_unreached(
|
|
'BroadcastChannel.postMessage triggered exception within second worker: ' +
|
|
e.data.message);
|
|
}
|
|
});
|
|
var testMessageHandler = t.step_func(function(e) {
|
|
assert_equals(
|
|
e.data, true,
|
|
'Worker indicated that the test procedures were executed successfully');
|
|
|
|
var w = createWorker(
|
|
postMessageWithinWorkerWorkerCode, 'done-worker', channelName,
|
|
doneMessageHandler);
|
|
t.add_cleanup(() => w.terminate());
|
|
});
|
|
createWorker(
|
|
postMessageWithinWorkerWorkerCode, workerName, channelName,
|
|
testMessageHandler);
|
|
}
|
|
|
|
async_test(t => {
|
|
const workerName = 'close-after-create-worker';
|
|
const channelName = workerName + '-postmessage-within-worker';
|
|
doPostMessageWithinWorkerTest(t, workerName, channelName);
|
|
}, 'BroadcastChannel messages within closed worker should be ignored (BCs created before closing)');
|
|
|
|
async_test(t => {
|
|
const workerName = 'close-before-create-worker';
|
|
const channelName = workerName + '-postmessage-within-worker';
|
|
doPostMessageWithinWorkerTest(t, workerName, channelName);
|
|
}, 'BroadcastChannel messages within closed worker should be ignored (BCs created after closing)');
|
|
|
|
</script>
|