Revela/src/client/location.js
2022-11-23 12:52:56 -05:00

209 lines
7.1 KiB
JavaScript

import EventEmitter from 'events';
/**
* @typedef {import('./index').default} UVClient
*/
class LocationApi extends EventEmitter {
/**
*
* @param {UVClient} ctx
*/
constructor(ctx) {
super();
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.HashChangeEvent = this.window.HashChangeEvent || null;
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: () => {
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.emit(
'hashchange',
emulation.href,
val.trim().startsWith('#')
? new URL(
val.trim(),
emulation.href
).href
: new URL(
'#' + val.trim(),
emulation.href
).href,
that
);
break;
default:
{
const url = new URL(emulation.href);
url[key] = val;
that.location.href = wrap(url.href);
}
break;
}
}
: 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;