Path: blob/main/plugins/default-browser-emulator/injected-scripts/_descriptorBuilder.ts
1029 views
const nativeErrorRegex = new RegExp(/^(\w+):\s/);12const globalSymbols = {};3for (const symbol of ReflectCached.ownKeys(Symbol)) {4if (typeof Symbol[symbol] === 'symbol') {5globalSymbols[`${String(Symbol[symbol])}`] = Symbol[symbol];6}7}89function createError(message: string, type?: { new (message: string): any }) {10if (!type) {11const match = nativeErrorRegex.exec(message);12if (match.length) {13message = message.replace(`${match[1]}: `, '');14try {15type = self[match[1]];16} catch (err) {17// ignore18}19}20}21if (!type) type = Error;22// eslint-disable-next-line new-cap23return new type(message);24}2526function newObjectConstructor(newProps: IDescriptor) {27return function() {28if (newProps._$constructorException) {29throw createError(newProps._$constructorException);30}31Object.setPrototypeOf(this, getObjectAtPath(newProps._$protos[0]));32const props = Object.entries(newProps);33const obj = {};34for (const [prop, value] of props) {35if (prop.startsWith('_$')) continue;36let propName: string | symbol = prop;37if (propName.startsWith('Symbol(')) {38propName = Symbol.for(propName.match(/Symbol\((.+)\)/)[1]);39}40Object.defineProperty(obj, propName, buildDescriptor(value));41}42return obj;43};44}4546function buildDescriptor(entry: IDescriptor) {47const attrs: PropertyDescriptor = {};48const flags = entry._$flags || '';49if (flags.includes('c')) attrs.configurable = true;50if (flags.includes('w')) attrs.writable = true;51if (flags.includes('e')) attrs.enumerable = true;5253if (entry._$get) {54attrs.get = new Proxy(Function.prototype.call.bind({}), {55apply() {56if (entry._$accessException) throw createError(entry._$accessException);57if (entry._$value) return entry._$value;58if (entry['_$$value()']) return entry['_$$value()']();59},60});61overriddenFns.set(attrs.get, entry._$get);62} else if (entry['_$$value()']) {63attrs.value = entry['_$$value()']();64} else if (entry._$value !== undefined) {65attrs.value = entry._$value;66}6768if (entry._$set) {69attrs.set = new Proxy(Function.prototype.call.bind({}), {70apply() {},71});72overriddenFns.set(attrs.set, entry._$set);73}7475if (entry._$function) {76const newProps = entry['new()'];77if (!newProps) {78// use function call just to get a function that doesn't create prototypes on new79// bind to an empty object so we don't modify the original80attrs.value = new Proxy(Function.prototype.call.bind({}), {81apply() {82return entry._$invocation;83},84});85} else {86attrs.value = newObjectConstructor(newProps);87}88if (entry._$invocation !== undefined) {89Object.setPrototypeOf(attrs.value, Function.prototype);90delete attrs.value.prototype;91delete attrs.value.constructor;92}93overriddenFns.set(attrs.value, entry._$function);94}9596if (typeof entry === 'object') {97const props = Object.entries(entry).filter(([prop]) => !prop.startsWith('_$'));98if (!attrs.value && (props.length || entry._$protos)) {99attrs.value = {};100}101if (entry._$protos) {102attrs.value = Object.setPrototypeOf(attrs.value, getObjectAtPath(entry._$protos[0]));103}104105for (const [prop, value] of props) {106if (prop.startsWith('_$')) continue;107if (prop === 'arguments' || prop === 'caller') continue;108let propName: string | number | symbol = prop;109if (propName.startsWith('Symbol(')) {110propName = globalSymbols[propName];111if (!propName) {112const symbolName = (propName as string).match(/Symbol\((.+)\)/)[1];113propName = Symbol.for(symbolName);114}115}116const descriptor = buildDescriptor(value);117if (propName === 'prototype') {118Object.defineProperty(descriptor.value, 'constructor', {119// Blake: changed this from props on TS. is it right?120value: newObjectConstructor(value),121writable: true,122enumerable: false,123configurable: true,124});125if (!entry.prototype._$flags || !entry.prototype._$flags.includes('w')) {126descriptor.writable = false;127}128if (entry._$function) {129overriddenFns.set(descriptor.value.constructor, entry._$function);130}131}132Object.defineProperty(attrs.value, propName, descriptor);133}134}135136return attrs;137}138139// eslint-disable-next-line @typescript-eslint/no-unused-vars140function getParentAndProperty(path: string) {141const parts = breakdownPath(path, 1);142if (!parts) return undefined;143return { parent: parts.parent, property: parts.remainder[0] };144}145146function breakdownPath(path: string, propsToLeave) {147if (!path || path.startsWith('detached')) {148// can't do these yet... need to know how to get to them (ie, super prototype of X)149return undefined;150}151152const parts = path.split(/\.Symbol\(([\w.]+)\)|\.(\w+)/).filter(Boolean);153let obj: any = self;154while (parts.length > propsToLeave) {155let next: string | symbol = parts.shift();156if (next === 'window') continue;157if (next.startsWith('Symbol.')) next = Symbol.for(next);158obj = obj[next];159if (!obj) {160throw new Error(`Property not found -> ${path} at ${String(next)}`);161}162}163return { parent: obj, remainder: parts };164}165166function getObjectAtPath(path) {167const parts = breakdownPath(path, 0);168if (!parts) return undefined;169return parts.parent;170}171172declare interface IDescriptor {173_$flags: string;174_$type: string;175_$get?: any;176_$set?: any;177_$accessException?: string;178_$constructorException?: string;179_$value?: string;180'_$$value()'?: () => string;181_$function?: string;182_$invocation?: string;183_$protos?: string[];184'new()'?: IDescriptor;185prototype: IDescriptor;186}187188189