Skip to content

Commit

Permalink
BroadcastChannel: Make detached iframe / closed worker tests permanent
Browse files Browse the repository at this point in the history
The spec is being updated [1] to indicate that messages from
BroadcastChannel instances associated with detached iframe /
closed worker contexts should be ignored, which means that
these tests no longer need to be marked 'tentative'.

Note that this CL doesn't introduce any changes to the tests.

[1] whatwg/html#7296

Bug: 1239274
Change-Id: I3e506493175b5e22913c35510454e8bef5f1e5e8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3259423
Commit-Queue: Ben Kelly <[email protected]>
Auto-Submit: Andrew Williams <[email protected]>
Reviewed-by: Ben Kelly <[email protected]>
Cr-Commit-Position: refs/heads/main@{#938781}
NOKEYCHECK=True
GitOrigin-RevId: f49494ed94e1a04b376707b3858c7c2b970e1148
  • Loading branch information
recvfrom authored and copybara-github committed Nov 5, 2021
1 parent 16a57cc commit 7e0ae11
Show file tree
Hide file tree
Showing 3 changed files with 249 additions and 256 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -123,4 +123,253 @@

}, '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>
Loading

0 comments on commit 7e0ae11

Please sign in to comment.