Skip to content

Commit

Permalink
refactor: iframe windows share web worker
Browse files Browse the repository at this point in the history
  • Loading branch information
adamdbradley committed Oct 19, 2021
1 parent 4ab2afc commit 32c758b
Show file tree
Hide file tree
Showing 44 changed files with 1,481 additions and 1,216 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"build.prod": "tsc && rollup -c scripts/rollup.config.js --configApi",
"build.watch": "rollup -c scripts/rollup.config.js -w --configDev",
"dev": "tsc && concurrently \"npm:build.watch\" \"npm:tsc.watch\" -n build,tsc -c magenta,yellow",
"playwright": "playwright test --browser=chromium",
"playwright": "playwright test tests --browser=chromium",
"playwright.webkit": "playwright test --browser=webkit",
"release": "npm run build && npm test && np --no-2fa --no-tests",
"serve": "sirv tests --port 4000 --dev",
Expand Down
4 changes: 3 additions & 1 deletion playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@ const config: PlaywrightTestConfig = {
use: {
baseURL: 'http://localhost:5000/',
viewport: {
width: 480,
width: 520,
height: 600,
},
contextOptions: {
recordVideo: {
dir: 'tests/videos/',
},
},
geolocation: { latitude: 88, longitude: 99 },
permissions: ['geolocation'],
},
};

Expand Down
3 changes: 2 additions & 1 deletion scripts/build-atomics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ import { writeFile } from 'fs-extra';
import { webWorkerBlobUrlPlugin } from './build-web-worker';

export function buildAtomics(opts: BuildOptions): RollupOptions[] {
const rollups: RollupOptions[] = [buildAtomicsDebug(opts)];
const rollups: RollupOptions[] = [];

if (!opts.isDev) {
rollups.push(buildAtomicsDebug(opts));
rollups.push(buildAtomicsMin(opts));
}

Expand Down
27 changes: 15 additions & 12 deletions scripts/minify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,45 +63,48 @@ function managlePropsPlugin(): Plugin {
const mangleProps: { [key: string]: string } = {
$accessType$: '',
$args$: '',
$cleanupInc$: '',
$assignInstanceId$: '',
$body$: '',
$config$: '',
$content$: '',
$contextWinId$: '',
$currentScriptId$: '',
$currentScriptUrl$: '',
$data$: '',
$documentCompatMode$: '',
$documentReadyState$: '',
$documentReferrer$: '',
$document$: '',
$documentElement$: '',
$error$: '',
$firstScriptId$: '',
$forward$: '',
$forwardToWorkerAccess$: '',
$forwardedTriggers$: '',
$head$: '',
$htmlConstructors$: '',
$immediateSetters$: '',
$importScripts$: '',
$implementation$: '',
$interfaces$: '',
$instanceId$: '',
$instanceIds$: '',
$instances$: '',
$interfaces$: '',
$interfaceType$: '',
$items: '',
$isInitialized$: '',
$isPromise$: '',
$isTop$: '',
$items$: '',
$libPath$: '',
$location$: '',
$memberName$: '',
$memberPath$: '',
$msgId$: '',
$newInstanceId$: '',
$nodeName$: '',
$parentWinId$: '',
$postMessage$: '',
$refId$: '',
$rtnValue$: '',
$run$: '',
$thisArg$: '',
$url$: '',
$window$: '',
$windowMembers$: '',
$windowMemberNames$: '',
$winId$: '',
$worker$: '',
};

if (chars.length < Object.keys(mangleProps).length) {
Expand Down
2 changes: 1 addition & 1 deletion src/lib/atomics/sync-send-message-to-main-atomics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const syncSendMessageToMainAtomics = (
): MainAccessResponse => {
const accessRsp: MainAccessResponse = {
$msgId$: accessReq.$msgId$,
$winId$: webWorkerCtx.$winId$,
$winId$: accessReq.$winId$,
$error$: `Atomics not implemented (yet)`,
};
return accessRsp;
Expand Down
7 changes: 4 additions & 3 deletions src/lib/main/loader.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { debug, PT_INITIALIZED_EVENT, SCRIPT_TYPE } from '../utils';
import { debug, PT_IFRAME_APPENDED, PT_INITIALIZED_EVENT, SCRIPT_TYPE } from '../utils';
import type { MainWindow } from '../types';

export function loader(
Expand Down Expand Up @@ -44,8 +44,9 @@ export function loader(

scripts = doc.querySelectorAll(`script[type="${SCRIPT_TYPE}"]`);

if (location !== parent.location) {
(parent as MainWindow)._ptWin!(win);
if (top !== win) {
// this is an iframe
top!.dispatchEvent(new CustomEvent(PT_IFRAME_APPENDED, { detail: win }));
} else {
if (scripts!.length) {
timeout = setTimeout(fallback, debug ? 60000 : 10000);
Expand Down
25 changes: 0 additions & 25 deletions src/lib/sandbox/create-web-worker.ts

This file was deleted.

3 changes: 1 addition & 2 deletions src/lib/sandbox/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { initSandbox } from './init-sandbox';
import { TOP_WIN_ID } from '../utils';

initSandbox(window, TOP_WIN_ID);
initSandbox(window);
89 changes: 41 additions & 48 deletions src/lib/sandbox/init-sandbox.ts
Original file line number Diff line number Diff line change
@@ -1,62 +1,55 @@
import { createWebWorker } from './create-web-worker';
import { debug } from '../utils';
import { debug, logMain, PT_IFRAME_APPENDED } from '../utils';
import { getAndSetInstanceId } from './main-instances';
import { mainAccessHandler } from './main-access-handler';
import {
import type {
MainWindow,
MainWindowContext,
MessageFromWorkerToSandbox,
MessengerRequestCallback,
PlatformInstanceId,
PartytownWebWorker,
} from '../types';
import { readNextScript } from './read-main-scripts';
import { setInstanceId } from './main-instances';
import { onMessageFromWebWorker } from './on-messenge-from-worker';
import { registerWindow } from './main-register-window';
import syncCreateMessenger from '@sync-create-messenger';
import { winCtxs, windows } from './main-constants';
import WebWorkerBlob from '@web-worker-blob';
import WebWorkerUrl from '@web-worker-url';
import { winCtxs, windowIds } from './main-constants';

export const initSandbox = async (sandboxWindow: Window, winIds: number) => {
const mainWindow: MainWindow = sandboxWindow.parent as any;
const $config$ = mainWindow.partytown || {};
const $libPath$ = ($config$.lib || '/~partytown/') + (debug ? 'debug/' : '');
export const initSandbox = async (sandboxWindow: any) => {
let worker: PartytownWebWorker;

const registerWindow = (win: MainWindow) => {
if (!windows.has(win)) {
windows.add(win);
const mainWindow: MainWindow = sandboxWindow.parent;

const parentWin = win.parent;
const winCtx: MainWindowContext = {
$winId$: (win._ptId = winIds++),
$parentWinId$: parentWin._ptId!,
$cleanupInc$: 0,
$config$,
$libPath$,
$url$: win.document.baseURI,
$window$: win,
};

winCtxs.set(winCtx.$winId$, winCtx);

if (debug) {
winCtx.$startTime$ = performance.now();
}

setInstanceId(winCtx, win, PlatformInstanceId.window);

createWebWorker(winCtx);

win.addEventListener('load', () => readNextScript(winCtx));
}
};

mainWindow._ptWin = registerWindow;

const receiveMessage: MessengerRequestCallback = (accessReq, responseCallback) => {
const accessWinId = accessReq.$winId$;
const winCtx = winCtxs.get(accessWinId)!;
mainAccessHandler(winCtx, accessReq).then(responseCallback);
};
const receiveMessage: MessengerRequestCallback = (accessReq, responseCallback) =>
mainAccessHandler(worker, accessReq).then(responseCallback);

const success = await syncCreateMessenger(sandboxWindow, receiveMessage);

if (success) {
registerWindow(mainWindow);
worker = new Worker(
debug
? WebWorkerUrl
: URL.createObjectURL(
new Blob([WebWorkerBlob], {
type: 'text/javascript',
})
),
{ name: `Partytown 🎉` }
);

worker.onmessage = (ev: MessageEvent<MessageFromWorkerToSandbox>) =>
onMessageFromWebWorker(worker, mainWindow, ev.data);

if (debug) {
logMain(`Created web worker`);
worker.onerror = (ev) => console.error(`Web Worker Error`, ev);
}

mainWindow.addEventListener<any>(PT_IFRAME_APPENDED, (ev: CustomEvent) => {
const win: MainWindow = ev.detail;
const parentWinId = windowIds.get(win.parent);
const parentWinCtx = winCtxs[parentWinId!]!;
const winId = getAndSetInstanceId(parentWinCtx, win.frameElement);
registerWindow(worker, winId, win);
});
}
};
71 changes: 16 additions & 55 deletions src/lib/sandbox/main-access-handler.ts
Original file line number Diff line number Diff line change
@@ -1,70 +1,49 @@
import {
AccessType,
MainAccessRequest,
MainAccessResponse,
MainWindowContext,
WorkerMessageType,
} from '../types';
import { AccessType, MainAccessRequest, MainAccessResponse, PartytownWebWorker } from '../types';
import { deserializeFromWorker, serializeForWorker } from './main-serialization';
import { EMPTY_ARRAY, isPromise, len } from '../utils';
import { forwardMsgResolves, winCtxs } from './main-constants';
import { getInstance, setInstanceId } from './main-instances';
import { getWinCtx } from './main-register-window';

export const mainAccessHandler = async (
winCtx: MainWindowContext,
worker: PartytownWebWorker,
accessReq: MainAccessRequest
) => {
let $winId$ = accessReq.$winId$;
let accessRsp: MainAccessResponse = {
$msgId$: accessReq.$msgId$,
$winId$: accessReq.$winId$,
$winId$,
};

let instanceId = accessReq.$instanceId$;
let accessType = accessReq.$accessType$;
let memberPath = accessReq.$memberPath$;
let memberPathLength = len(memberPath);
let lastMemberName = memberPath[memberPathLength - 1];
let immediateSetters = accessReq.$immediateSetters$ || EMPTY_ARRAY;
let winCtx = await getWinCtx($winId$);
let instance: any;
let rtnValue: any;
let data: any;
let i: number;
let count: number;
let tmr: any;
let immediateSetterTarget: any;
let immediateSetterMemberPath;
let immediateSetterMemberNameLen;

try {
// deserialize the data, such as a getter value or function arguments
data = deserializeFromWorker(accessReq.$data$);
data = deserializeFromWorker(worker, accessReq.$data$);

if (accessReq.$forwardToWorkerAccess$) {
// same as continue;
} else if (accessType === AccessType.GlobalConstructor) {
if (accessType === AccessType.GlobalConstructor) {
// create a new instance of a global constructor
setInstanceId(winCtx, new (winCtx.$window$ as any)[lastMemberName](...data), instanceId);
setInstanceId(winCtx, new (winCtx!.$window$ as any)[lastMemberName](...data), instanceId);
} else {
// get the existing instance
instance = getInstance(accessRsp.$winId$, instanceId);
instance = getInstance($winId$, instanceId);
if (instance) {
for (i = 0; i < memberPathLength - 1; i++) {
instance = instance[memberPath[i]];
}

if (accessType === AccessType.Get) {
if (lastMemberName === '_ptId') {
await new Promise<void>((resolve) => {
count = 0;
tmr = setInterval(() => {
if (isMemberInInstance(instance, memberPath) || count > 99) {
clearInterval(tmr);
resolve();
}
count++;
}, 40);
});
}
rtnValue = instance[lastMemberName];
} else if (accessType === AccessType.Set) {
instance[lastMemberName] = data;
Expand All @@ -81,44 +60,26 @@ export const mainAccessHandler = async (
}

immediateSetterTarget[immediateSetterMemberPath[immediateSetterMemberNameLen - 1]] =
deserializeFromWorker(immediateSetter[1]);
deserializeFromWorker(worker, immediateSetter[1]);
});

if (accessReq.$newInstanceId$) {
setInstanceId(winCtx, rtnValue, accessReq.$newInstanceId$);
if (accessReq.$assignInstanceId$) {
setInstanceId(winCtx, rtnValue, accessReq.$assignInstanceId$);
}
}

if (isPromise(rtnValue)) {
rtnValue = await rtnValue;
accessRsp.$isPromise$ = true;
}
accessRsp.$rtnValue$ = serializeForWorker(winCtx, rtnValue);
accessRsp.$rtnValue$ = serializeForWorker($winId$, rtnValue);
} else {
accessRsp.$error$ = `${instanceId} not found`;
accessRsp.$error$ = instanceId + ' not found';
}
}
} catch (e: any) {
accessRsp.$error$ = String(e.stack || e);
}

if (accessReq.$forwardToWorkerAccess$) {
return new Promise<MainAccessResponse>((resolve) => {
const forwardToWinId = accessReq.$contextWinId$ || accessReq.$winId$;
const otherWinCtx = winCtxs.get(forwardToWinId);

tmr = setTimeout(() => {
forwardMsgResolves.delete(accessReq.$msgId$);
accessRsp.$error$ = `Timeout`;
resolve(accessRsp);
}, 30000);

forwardMsgResolves.set(accessReq.$msgId$, [resolve, tmr]);
otherWinCtx!.$worker$!.postMessage([WorkerMessageType.ForwardWorkerAccessRequest, accessReq]);
});
} else {
return accessRsp;
}
return accessRsp;
};

const isMemberInInstance = (instance: any, memberPath: string[]) => memberPath[0] in instance;
Loading

0 comments on commit 32c758b

Please sign in to comment.