ultraviolet/client/location.js
2022-02-19 00:10:57 -05:00

131 lines
No EOL
4.9 KiB
JavaScript

class LocationApi {
constructor(ctx) {
this.ctx = ctx;
this.window = ctx.window;
this.location = this.window.location;
this.WorkerLocation = this.ctx.worker ? this.window.WorkerLocation : null;
this.workerLocProto = this.WorkerLocation ? this.WorkerLocation.prototype : {};
this.keys = ['href', 'protocol', 'host', 'hostname', 'port', 'pathname', 'search', 'hash', 'origin'];
this.href = this.WorkerLocation ? ctx.nativeMethods.getOwnPropertyDescriptor(this.workerLocProto, 'href') :
ctx.nativeMethods.getOwnPropertyDescriptor(this.location, 'href');
};
overrideWorkerLocation(parse) {
if (!this.WorkerLocation) return false;
const uv = this;
for (const key of this.keys) {
this.ctx.overrideDescriptor(this.workerLocProto, key, {
get: (target, that) => {
return parse(
uv.href.get.call(this.location)
)[key]
},
});
};
return true;
};
emulate(parse, wrap) {
const emulation = {};
const that = this;
for (const key of that.keys) {
this.ctx.nativeMethods.defineProperty(emulation, key, {
get() {
return parse(
that.href.get.call(that.location)
)[key];
},
set: key !== 'origin' ? function (val) {
switch(key) {
case 'href':
that.location.href = wrap(val);
break;
case 'hash':
that.location.hash = val;
break;
default:
const url = new URL(emulation.href);
url[key] = val;
that.location.href = wrap(url.href);
};
} : undefined,
configurable: false,
enumerable: true,
});
};
if ('reload' in this.location) {
this.ctx.nativeMethods.defineProperty(emulation, 'reload', {
value: this.ctx.wrap(this.location, 'reload', (target, that) => {
return target.call(that === emulation ? this.location : that);
}),
writable: false,
enumerable: true,
});
};
if ('replace' in this.location) {
this.ctx.nativeMethods.defineProperty(emulation, 'replace', {
value: this.ctx.wrap(this.location, 'assign', (target, that, args) => {
if (!args.length || that !== emulation) target.call(that);
that = this.location;
let [ input ] = args;
const url = new URL(input, emulation.href);
return target.call(that === emulation ? this.location : that, wrap(url.href));
}),
writable: false,
enumerable: true,
});
};
if ('assign' in this.location) {
this.ctx.nativeMethods.defineProperty(emulation, 'assign', {
value: this.ctx.wrap(this.location, 'assign', (target, that, args) => {
if (!args.length || that !== emulation) target.call(that);
that = this.location;
let [ input ] = args;
const url = new URL(input, emulation.href);
return target.call(that === emulation ? this.location : that, wrap(url.href));
}),
writable: false,
enumerable: true,
});
};
if ('ancestorOrigins' in this.location) {
this.ctx.nativeMethods.defineProperty(emulation, 'ancestorOrigins', {
get() {
const arr = [];
if (that.window.DOMStringList) that.ctx.nativeMethods.setPrototypeOf(arr, that.window.DOMStringList.prototype);
return arr;
},
set: undefined,
enumerable: true,
});
};
this.ctx.nativeMethods.defineProperty(emulation, 'toString', {
value: this.ctx.wrap(this.location, 'toString', () => {
return emulation.href;
}),
enumerable: true,
writable: false,
});
this.ctx.nativeMethods.defineProperty(emulation, Symbol.toPrimitive, {
value: () => emulation.href,
writable: false,
enumerable: false,
});
if (this.ctx.window.Location) this.ctx.nativeMethods.setPrototypeOf(emulation, this.ctx.window.Location.prototype);
return emulation;
};
};
export default LocationApi;