File size: 3,589 Bytes
bee6636
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import { iswindow } from "..";
import { SCRAMJETCLIENT } from "../../symbols";
import { ScramjetClient } from "../client";
import { config } from "../../shared";
// import { argdbg } from "./err";
import { indirectEval } from "./eval";

export function createWrapFn(client: ScramjetClient, self: typeof globalThis) {
	return function (identifier: any, strict: boolean) {
		if (identifier === self) return client.globalProxy;
		if (identifier === self.location) return client.locationProxy;
		if (identifier === eval) return indirectEval.bind(client, strict);

		if (iswindow) {
			if (identifier === self.parent) {
				if (SCRAMJETCLIENT in self.parent) {
					// ... then we're in a subframe, and the parent frame is also in a proxy context, so we should return its proxy
					return self.parent[SCRAMJETCLIENT].globalProxy;
				} else {
					// ... then we should pretend we aren't nested and return the current window
					return client.globalProxy;
				}
			} else if (identifier === self.document) {
				return client.documentProxy;
			} else if (identifier === self.top) {
				// instead of returning top, we need to return the uppermost parent that's inside a scramjet context
				let current = self;

				for (;;) {
					const test = current.parent.self;
					if (test === current) break; // there is no parent, actual or emulated.

					// ... then `test` represents a window outside of the proxy context, and therefore `current` is the topmost window in the proxy context
					if (!(SCRAMJETCLIENT in test)) break;

					// test is also insde a proxy, so we should continue up the chain
					current = test;
				}

				return current[SCRAMJETCLIENT].globalProxy;
			}
		}

		return identifier;
	};
}

export const order = 4;
export default function (client: ScramjetClient, self: typeof globalThis) {
	// the main magic of the proxy. all attempts to access any "banned objects" will be redirected here, and instead served a proxy object
	// this contrasts from how other proxies will leave the root object alone and instead attempt to catch every member access
	// this presents some issues (see element.ts), but makes us a good bit faster at runtime!
	Object.defineProperty(self, config.globals.wrapfn, {
		value: client.wrapfn,
		writable: false,
		configurable: false,
	});
	Object.defineProperty(self, config.globals.wrapthisfn, {
		value: function (i) {
			if (i === self) return client.globalProxy;

			return i;
		},
		writable: false,
		configurable: false,
	});

	self.$scramitize = function (v) {
		if (v === self) debugger;
		if (v === location) debugger;
		if (iswindow) {
			if (v === self.parent) debugger;
			if (v === self.document) debugger;
			if (v === self.top) debugger;
		}

		if (typeof v === "string" && v.includes("scramjet")) debugger;
		if (typeof v === "string" && v.includes(location.origin)) debugger;

		return v;
	};

	// location = "..." can't be rewritten as wrapfn(location) = ..., so instead it will actually be rewritten as
	// ((t)=>$scramjet$tryset(location,"+=",t)||location+=t)(...);
	// it has to be a discrete function because there's always the possibility that "location" is a local variable
	// we have to use an IIFE to avoid duplicating side-effects in the getter
	Object.defineProperty(self, config.globals.trysetfn, {
		value: function (lhs: any, op: string, rhs: any) {
			if (lhs instanceof Location) {
				// @ts-ignore
				locationProxy.href = rhs;

				return true;
			}
		},
		writable: false,
		configurable: false,
	});
}