File size: 2,959 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 |
import { ScramjetClient } from "../client";
import { type MessageC2W } from "../../worker";
import { flagEnabled } from "../../shared";
import { rewriteUrl } from "../../shared/rewriters/url";
// we need a late order because we're mangling with addEventListener at a higher level
export const order = 2;
export const enabled = (client: ScramjetClient) =>
flagEnabled("serviceworkers", client.url);
export function disabled(_client: ScramjetClient, _self: Self) {
Reflect.deleteProperty(Navigator.prototype, "serviceWorker");
}
type FakeRegistrationState = {
scope: string;
active: ServiceWorker;
};
export default function (client: ScramjetClient, _self: Self) {
const registrationmap: WeakMap<
ServiceWorkerRegistration,
FakeRegistrationState
> = new WeakMap();
client.Proxy("EventTarget.prototype.addEventListener", {
apply(ctx) {
if (registrationmap.get(ctx.this)) {
// do nothing
ctx.return(undefined);
}
},
});
client.Proxy("EventTarget.prototype.removeEventListener", {
apply(ctx) {
if (registrationmap.get(ctx.this)) {
// do nothing
ctx.return(undefined);
}
},
});
client.Proxy("ServiceWorkerContainer.prototype.getRegistration", {
apply(ctx) {
ctx.return(new Promise((resolve) => resolve(registration)));
},
});
client.Proxy("ServiceWorkerContainer.prototype.getRegistrations", {
apply(ctx) {
ctx.return(new Promise((resolve) => resolve([registration])));
},
});
client.Trap("ServiceWorkerContainer.prototype.ready", {
get(_ctx) {
return new Promise((resolve) => resolve(registration));
},
});
client.Trap("ServiceWorkerContainer.prototype.controller", {
get(ctx) {
return registration?.active;
},
});
client.Proxy("ServiceWorkerContainer.prototype.register", {
apply(ctx) {
const fakeRegistration = new EventTarget() as ServiceWorkerRegistration;
Object.setPrototypeOf(
fakeRegistration,
self.ServiceWorkerRegistration.prototype
);
fakeRegistration.constructor = ctx.fn;
let url = rewriteUrl(ctx.args[0], client.meta) + "?dest=serviceworker";
if (ctx.args[1] && ctx.args[1].type === "module") {
url += "&type=module";
}
const worker = client.natives.construct("SharedWorker", url);
const handle = worker.port;
const state: FakeRegistrationState = {
scope: ctx.args[0],
active: handle as ServiceWorker,
};
const controller = client.descriptors.get(
"ServiceWorkerContainer.prototype.controller",
client.serviceWorker
);
client.natives.call(
"ServiceWorker.prototype.postMessage",
controller,
{
scramjet$type: "registerServiceWorker",
port: handle,
origin: client.url.origin,
} as MessageC2W,
[handle]
);
registrationmap.set(fakeRegistration, state);
ctx.return(new Promise((resolve) => resolve(fakeRegistration)));
},
});
}
|