Skip to content

Commit 1472534

Browse files
authored
WorkerDOM.upgrade should wait for Worker init. (#1047)
* Ensure WorkerDOM.upgrade() waits for iframe init complete * format * expand sandboxed example to cover callFunction. * fix test * extra wiggleroom
1 parent 77add80 commit 1472534

File tree

7 files changed

+49
-24
lines changed

7 files changed

+49
-24
lines changed

demo/sandboxed/index.html

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,26 +7,28 @@
77
<script src="/dist/amp-debug/main.mjs" type="module"></script>
88
</head>
99
<body>
10-
<div src="/hello-world/hello-world.js" id="upgrade-me">
10+
<div src="/sandboxed/index.js" id="upgrade-me">
1111
<div class="root">
1212
<button>Insert Hello World!</button>
1313
</div>
1414
</div>
1515
<script type="module">
1616
import { upgradeElement } from '/dist/amp-debug/main.mjs';
17-
upgradeElement(
17+
const worker = await upgradeElement(
1818
document.getElementById('upgrade-me'),
1919
'/dist/amp-debug/worker/worker.mjs',
2020
() => {},
2121
undefined,
2222
{ iframeUrl: 'sandbox-iframe.html' }
23-
).then((worker) => {
24-
worker.onmessage = (msg) =>
25-
console.log(`onmessage: ${JSON.stringify(msg)}`);
26-
worker.onmessageerror = (msg) =>
27-
console.error(`msgerror ${JSON.stringify(msg)}`);
28-
worker.onerror = (msg) =>
29-
console.error(`error: ${JSON.stringify(msg)}`);
23+
);
24+
worker.onmessage = (msg) =>
25+
console.log(`onmessage: ${JSON.stringify(msg)}`);
26+
worker.onmessageerror = (msg) =>
27+
console.error(`msgerror ${JSON.stringify(msg)}`);
28+
worker.onerror = (msg) => console.error(`error: ${JSON.stringify(msg)}`);
29+
30+
worker.callFunction('getData').then((data) => {
31+
console.log(`Received data: ${JSON.stringify(data)}`);
3032
});
3133
</script>
3234
</body>

demo/sandboxed/sandbox-iframe.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
const MESSAGE_TYPES = {
88
ready: 'iframe-ready',
99
init: 'init-worker',
10+
workerReady: 'worker-ready',
1011
onmessage: 'onmessage',
1112
onerror: 'onerror',
1213
onmessageerror: 'onmessageerror',
@@ -58,6 +59,8 @@
5859
listen(MESSAGE_TYPES.postMessage, ({ message }) =>
5960
worker.postMessage(message)
6061
);
62+
63+
send(MESSAGE_TYPES.workerReady);
6164
});
6265
</script>
6366
</html>

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@
9999
"brotli": "12 kB"
100100
},
101101
"./dist/worker/worker.js": {
102-
"brotli": "13.5 kB"
102+
"brotli": "14 kB"
103103
},
104104
"./dist/amp-production/main.mjs": {
105105
"brotli": "4.5 kB"

src/main-thread/iframe-worker.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ type MessageFromWorker = {
1818
type: 'onmessage' | 'onerror' | 'onmessageerror';
1919
message: any;
2020
};
21-
export type MessageFromIframe = { type: 'iframe-ready' } | MessageFromWorker;
21+
export type MessageFromIframe = { type: 'iframe-ready' } | { type: 'worker-ready' } | MessageFromWorker;
2222
export type MessageToIframe = { type: 'terminate' } | { type: 'init-worker'; code: string } | { type: 'postMessage'; message: any };
2323

2424
/**
@@ -29,16 +29,19 @@ export type MessageToIframe = { type: 'terminate' } | { type: 'init-worker'; cod
2929
* The iframe used for sandboxing must follow a specific contract. It:
3030
* 1. Must send a ready message to the main-thread.
3131
* 2. Must listen for a message from main-thread with the code to initialize a Worker with.
32-
* 3. Must proxy all messages between the Worker and Parent, including errors.
32+
* 3. Must send "worker-ready" once worker is initialized.
33+
* 4. Must proxy all messages between the Worker and Parent, including errors.
3334
*/
3435
class IframeWorker {
3536
// Public Worker API
3637
public onerror: (this: IframeWorker, ev: ErrorEvent) => any;
3738
public onmessage: (this: IframeWorker, ev: MessageEvent) => any;
3839
public onmessageerror: (this: IframeWorker, ev: MessageEvent) => any;
40+
public readyPromise: Promise<void>;
3941

4042
// Internal variables.
4143
private iframe: HTMLIFrameElement;
44+
private readyPromiseResolve: Function;
4245

4346
/**
4447
* @param url The URL to initiate the worker from.
@@ -50,6 +53,9 @@ class IframeWorker {
5053
this.iframe.setAttribute('style', 'display:none');
5154
this.iframe.setAttribute('src', iframeUrl);
5255
this.url = url;
56+
this.readyPromise = new Promise((resolve) => {
57+
this.readyPromiseResolve = resolve;
58+
});
5359

5460
this.setupInit();
5561
this.proxyFromWorker();
@@ -65,11 +71,14 @@ class IframeWorker {
6571
fetch(this.url.toString())
6672
.then((res) => res.text())
6773
.then((code) => {
68-
if ((event.data as MessageFromIframe).type == 'iframe-ready') {
74+
const data = event.data as MessageFromIframe;
75+
if (data.type == 'iframe-ready') {
6976
const msg: MessageToIframe = { type: 'init-worker', code };
7077
this.iframe.contentWindow!.postMessage(msg, '*');
78+
} else if (data.type === 'worker-ready') {
79+
this.readyPromiseResolve();
80+
window.removeEventListener('message', listener);
7181
}
72-
window.removeEventListener('message', listener);
7382
});
7483
};
7584
window.addEventListener('message', listener);
@@ -99,7 +108,9 @@ class IframeWorker {
99108
*/
100109
postMessage(message: any, transferables?: Array<Transferable>) {
101110
const msg: MessageToIframe = { type: 'postMessage', message };
102-
this.iframe.contentWindow!.postMessage(msg, '*', transferables);
111+
this.readyPromise.then(() => {
112+
this.iframe.contentWindow!.postMessage(msg, '*', transferables);
113+
});
103114
}
104115

105116
/**

src/main-thread/install.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ export function install(
8080
}
8181
};
8282

83-
return new ExportedWorker(workerContext, normalizedConfig);
83+
return workerContext.ready().then(() => new ExportedWorker(workerContext, normalizedConfig));
8484
}
8585
return null;
8686
});

src/main-thread/worker.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,14 @@ export class WorkerContext {
9494
}
9595
}
9696

97+
/**
98+
* Returns a Promise that resolves when the Worker is ready to receive messages.
99+
* @returns {Promise<void>}
100+
*/
101+
ready() {
102+
return (this.worker as IframeWorker).readyPromise || Promise.resolve();
103+
}
104+
97105
/**
98106
* Returns the private worker.
99107
*/

src/test/main-thread/iframe-worker.test.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,14 @@ test('Should listen for and respond to an iframe-ready signal.', async (t) => {
7878
]);
7979
});
8080

81-
test('Should proxy postMessage calls.', async (t) => {
82-
const { worker, sentToIframe } = t.context;
81+
test('Should proxy postMessage calls after init completes.', async (t) => {
82+
const { worker, sentToIframe, fakeReceiveMessage } = t.context;
83+
8384
worker.postMessage({ hello: 'world' });
85+
t.is(sentToIframe.length, 0);
8486

87+
fakeReceiveMessage({ type: 'worker-ready' });
88+
await new Promise(setTimeout as any);
8589
t.is(sentToIframe.length, 1);
8690
t.deepEqual(sentToIframe, [
8791
{
@@ -101,14 +105,11 @@ test('Should proxy iframed worker messages.', async (t) => {
101105
worker.onmessageerror = (msg: MessageEvent) => onmessageerrorMessages.push(msg);
102106
worker.onerror = (msg: ErrorEvent) => onerrorMessages.push(msg);
103107

104-
fakeReceiveMessage({ type: 'onmessage', message: { onmessage: 1 } });
105-
fakeReceiveMessage({ type: 'onmessage', message: { onmessage: 2 } });
108+
fakeReceiveMessage({ type: 'onmessage', message: {} });
109+
fakeReceiveMessage({ type: 'onmessage', message: {} });
106110

107111
fakeReceiveMessage({ type: 'onerror', message: { onerror: 1 } });
108-
fakeReceiveMessage({
109-
type: 'onmessageerror',
110-
message: { onmessageerror: 1 },
111-
});
112+
fakeReceiveMessage({ type: 'onmessageerror', message: {} });
112113

113114
t.is(onmessageMessages.length, 2);
114115
t.is(onmessageerrorMessages.length, 1);

0 commit comments

Comments
 (0)