File size: 3,042 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
101
102
103
104
105
106
107
108
109
110
111
112
import { ScramjetClient } from "./client";
import { nativeGetOwnPropertyDescriptor } from "./natives";
import { UrlChangeEvent } from "./events";
import { iswindow } from ".";
import { rewriteUrl } from "../shared/rewriters/url";

export function createLocationProxy(

	client: ScramjetClient,

	self: typeof globalThis

) {
	const Location = iswindow ? self.Location : self.WorkerLocation;
	// location cannot be Proxy()d
	const fakeLocation: any = {};
	Object.setPrototypeOf(fakeLocation, Location.prototype);
	fakeLocation.constructor = Location;

	// for some reason it's on the object for Location and on the prototype for WorkerLocation??
	const descriptorSource = iswindow ? self.location : Location.prototype;
	const urlprops = [
		"protocol",
		"hash",
		"host",
		"hostname",
		"href",
		"origin",
		"pathname",
		"port",
		"search",
	];
	for (const prop of urlprops) {
		const native = nativeGetOwnPropertyDescriptor(descriptorSource, prop);
		if (!native) continue;

		const desc: Partial<PropertyDescriptor> = {
			configurable: true,
			enumerable: true,
		};
		if (native.get) {
			desc.get = new Proxy(native.get, {
				apply() {
					return client.url[prop];
				},
			});
		}
		if (native.set) {
			desc.set = new Proxy(native.set, {
				apply(target, that, args) {
					if (prop === "href") {
						// special case
						client.url = args[0];

						return;
					}
					if (prop === "hash") {
						self.location.hash = args[0];
						const ev = new UrlChangeEvent(client.url.href);
						if (!client.isSubframe) client.frame?.dispatchEvent(ev);

						return;
					}
					const url = new URL(client.url.href);
					url[prop] = args[0];
					client.url = url;
				},
			});
		}
		Object.defineProperty(fakeLocation, prop, desc);
	}

	// functions
	fakeLocation.toString = new Proxy(self.location.toString, {
		apply() {
			return client.url.href;
		},
	});

	if (self.location.valueOf)
		fakeLocation.valueOf = new Proxy(self.location.valueOf, {
			apply() {
				return client.url.href;
			},
		});
	if (self.location.assign)
		fakeLocation.assign = new Proxy(self.location.assign, {
			apply(target, that, args) {
				args[0] = rewriteUrl(args[0], client.meta);
				Reflect.apply(target, self.location, args);

				const urlchangeev = new UrlChangeEvent(client.url.href);
				if (!client.isSubframe) client.frame?.dispatchEvent(urlchangeev);
			},
		});
	if (self.location.reload)
		fakeLocation.reload = new Proxy(self.location.reload, {
			apply(target, that, args) {
				Reflect.apply(target, self.location, args);
			},
		});
	if (self.location.replace)
		fakeLocation.replace = new Proxy(self.location.replace, {
			apply(target, that, args) {
				args[0] = rewriteUrl(args[0], client.meta);
				Reflect.apply(target, self.location, args);

				const urlchangeev = new UrlChangeEvent(client.url.href);
				if (!client.isSubframe) client.frame?.dispatchEvent(urlchangeev);
			},
		});

	return fakeLocation;
}