Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: cross-origin postMessage #52

Merged
merged 1 commit into from
Jan 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,6 @@ export interface MediaSelf {
}

export interface PostMessageData {
$winId$: number;
$origin$: string;
$data$: string;
}
71 changes: 51 additions & 20 deletions src/lib/web-worker/worker-exec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { debug, SCRIPT_TYPE } from '../utils';
import { debug, len, SCRIPT_TYPE } from '../utils';
import {
EventHandler,
InitializeScriptData,
Expand All @@ -7,7 +7,7 @@ import {
WebWorkerEnvironment,
WorkerMessageType,
} from '../types';
import { environments, webWorkerCtx } from './worker-constants';
import { environments, postMessages, webWorkerCtx } from './worker-constants';
import { getEnv } from './worker-environment';
import { getInstanceStateValue, setInstanceStateValue } from './worker-state';
import { getOrCreateNodeInstance } from './worker-constructors';
Expand Down Expand Up @@ -41,7 +41,7 @@ export const initNextScriptsInWebWorker = async (initScript: InitializeScriptDat
scriptContent = await rsp.text();

env.$currentScriptId$ = instanceId;
run(env, winId, scriptContent, scriptOrgSrc || scriptSrc);
run(env, scriptContent, scriptOrgSrc || scriptSrc);
runStateLoadHandlers(instance!, StateProp.loadHandlers);
} else {
errorMsg = rsp.statusText;
Expand Down Expand Up @@ -88,7 +88,7 @@ export const runScriptContent = (
}

env.$currentScriptId$ = instanceId;
run(env, winId, scriptContent);
run(env, scriptContent);
} catch (contentError: any) {
console.error(scriptContent, contentError);
errorMsg = String(contentError.stack || contentError);
Expand All @@ -99,13 +99,56 @@ export const runScriptContent = (
return errorMsg;
};

const run = (env: WebWorkerEnvironment, winId: number, scriptContent: string, scriptUrl?: string) =>
const run = (env: WebWorkerEnvironment, scriptContent: string, scriptUrl?: string) => {
const doc: any = new Proxy(env.$document$, {
get: (target: any, propName) => {
if (propName === 'defaultView') {
return win;
} else {
return target[propName];
}
},
});

const win: any = new Proxy(env.$window$, {
get: (target: any, propName) => {
if (propName === 'document') {
return doc;
} else if (propName === 'window' || propName === 'globalThis' || propName === 'self') {
return win;
} else if (propName === 'parent' || propName === 'top') {
return new Proxy(target[propName], {
get: (targetParent: any, parentPropName) => {
if (parentPropName === 'postMessage') {
return (...args: any[]) => {
if (len(postMessages) > 20) {
postMessages.splice(0, 5);
}
postMessages.push({
$data$: JSON.stringify(args[0]),
$origin$: env.$location$.origin,
});
targetParent.postMessage(...args);
};
} else {
return targetParent[parentPropName];
}
},
});
} else {
return target[propName];
}
},
has: () => true,
});

new Function(
`with(this){(function _${winId}_(){${scriptContent
`with(this){${scriptContent
.replace(/\bthis\b/g, 'thi$(this)')
.replace(/\/\/# so/g, '//Xso')};function thi$(t){return t===this?window:t}})()}` +
.replace(/\/\/# so/g, '//Xso')};function thi$(t){return t===this?window:t}}` +
(scriptUrl ? '\n//# sourceURL=' + scriptUrl : '')
).call(env.$window$);
).call(win);
};

const runStateLoadHandlers = (
instance: WorkerInstance,
Expand All @@ -118,18 +161,6 @@ const runStateLoadHandlers = (
}
};

export const getScriptWinIdContext = () => {
try {
throw new Error();
} catch (e: any) {
const r = /_(\d+)_/gm.exec(e.stack);
if (r) {
return parseInt(r[1], 10);
}
}
return 0;
};

export const insertIframe = (winId: number, iframe: WorkerInstance) => {
// an iframe element's instanceId is also
// the winId of its contentWindow
Expand Down
6 changes: 1 addition & 5 deletions src/lib/web-worker/worker-serialization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,12 +184,8 @@ export const deserializeFromMain = (
if (obj.type === 'message' && obj.origin) {
let postMessageKey = JSON.stringify(obj.data);
let postMessageData = postMessages.find((pm) => pm.$data$ === postMessageKey);
let env: WebWorkerEnvironment;
if (postMessageData) {
env = environments[postMessageData.$winId$];
if (env) {
obj.origin = env.$window$.origin;
}
obj.origin = postMessageData.$origin$;
}
}
return new Proxy(new Event(obj.type, obj), {
Expand Down
35 changes: 9 additions & 26 deletions src/lib/web-worker/worker-window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,9 @@ import { createNavigator } from './worker-navigator';
import { createImageConstructor } from './worker-image';
import { createNodeInstance, getOrCreateNodeInstance } from './worker-constructors';
import { debug, defineConstructorName, defineProperty, len, randomId } from '../utils';
import {
envGlobalConstructors,
environments,
postMessages,
webWorkerCtx,
WinIdKey,
} from './worker-constants';
import { envGlobalConstructors, environments, webWorkerCtx, WinIdKey } from './worker-constants';
import { getEnv } from './worker-environment';
import { getScriptWinIdContext, resolveUrl } from './worker-exec';
import { resolveUrl } from './worker-exec';
import { lazyLoadMedia, windowMediaConstructors } from './worker-media';
import { Location } from './worker-location';
import { normalizedWinId } from '../log';
Expand All @@ -26,7 +20,6 @@ export class Window extends WorkerInstance {
let _this: any = this;
let globalName: string;
let value: any;
let win: any;

// assign global properties already in the web worker global
// that we can put onto the environment window
Expand Down Expand Up @@ -83,19 +76,16 @@ export class Window extends WorkerInstance {
_this.trustedTypes = (self as any).trustedTypes;
}

win = new Proxy(_this, {
has() {
// window "has" any and all props, this is especially true for global variables
// that are meant to be assigned to window, but without "window." prefix,
// like: <script>globalProp = true</script>
return true;
},
});

environments[$winId$] = {
$winId$,
$parentWinId$,
$window$: win as any,
$window$: new Proxy(_this, {
has: () =>
// window "has" any and all props, this is especially true for global variables
// that are meant to be assigned to window, but without "window." prefix,
// like: <script>globalProp = true</script>
true,
}),
$document$: createNodeInstance(
$winId$,
PlatformInstanceId.document,
Expand Down Expand Up @@ -139,8 +129,6 @@ export class Window extends WorkerInstance {
addStorageApi(_this, 'sessionStorage', webWorkerCtx.$sessionStorage$);

_this.Worker = undefined;

return win;
}

get body() {
Expand Down Expand Up @@ -214,11 +202,6 @@ export class Window extends WorkerInstance {
}

postMessage(...args: any[]) {
if (len(postMessages) > 20) {
postMessages.splice(0, 5);
}
postMessages.push({ $data$: JSON.stringify(args[0]), $winId$: getScriptWinIdContext() });

callMethod(this, ['postMessage'], args, CallType.NonBlockingNoSideEffect);
}

Expand Down
Loading