Spaces:
Runtime error
Runtime error
generate_magic_arrays = """ | |
generateFunctionMocks = ( | |
proto, | |
itemMainProp, | |
dataArray | |
) => ({ | |
item: utils.createProxy(proto.item, { | |
apply(target, ctx, args) { | |
if (!args.length) { | |
throw new TypeError( | |
`Failed to execute 'item' on '${ | |
proto[Symbol.toStringTag] | |
}': 1 argument required, but only 0 present.` | |
) | |
} | |
// Special behavior alert: | |
// - Vanilla tries to cast strings to Numbers (only integers!) and use them as property index lookup | |
// - If anything else than an integer (including as string) is provided it will return the first entry | |
const isInteger = args[0] && Number.isInteger(Number(args[0])) // Cast potential string to number first, then check for integer | |
// Note: Vanilla never returns `undefined` | |
return (isInteger ? dataArray[Number(args[0])] : dataArray[0]) || null | |
} | |
}), | |
/** Returns the MimeType object with the specified name. */ | |
namedItem: utils.createProxy(proto.namedItem, { | |
apply(target, ctx, args) { | |
if (!args.length) { | |
throw new TypeError( | |
`Failed to execute 'namedItem' on '${ | |
proto[Symbol.toStringTag] | |
}': 1 argument required, but only 0 present.` | |
) | |
} | |
return dataArray.find(mt => mt[itemMainProp] === args[0]) || null // Not `undefined`! | |
} | |
}), | |
/** Does nothing and shall return nothing */ | |
refresh: proto.refresh | |
? utils.createProxy(proto.refresh, { | |
apply(target, ctx, args) { | |
return undefined | |
} | |
}) | |
: undefined | |
}) | |
function generateMagicArray( | |
dataArray = [], | |
proto = MimeTypeArray.prototype, | |
itemProto = MimeType.prototype, | |
itemMainProp = 'type' | |
) { | |
// Quick helper to set props with the same descriptors vanilla is using | |
const defineProp = (obj, prop, value) => | |
Object.defineProperty(obj, prop, { | |
value, | |
writable: false, | |
enumerable: false, // Important for mimeTypes & plugins: `JSON.stringify(navigator.mimeTypes)` | |
configurable: false | |
}) | |
// Loop over our fake data and construct items | |
const makeItem = data => { | |
const item = {} | |
for (const prop of Object.keys(data)) { | |
if (prop.startsWith('__')) { | |
continue | |
} | |
defineProp(item, prop, data[prop]) | |
} | |
// navigator.plugins[i].length should always be 1 | |
if (itemProto === Plugin.prototype) { | |
defineProp(item, 'length', 1) | |
} | |
// We need to spoof a specific `MimeType` or `Plugin` object | |
return Object.create(itemProto, Object.getOwnPropertyDescriptors(item)) | |
} | |
const magicArray = [] | |
// Loop through our fake data and use that to create convincing entities | |
dataArray.forEach(data => { | |
magicArray.push(makeItem(data)) | |
}) | |
// Add direct property access based on types (e.g. `obj['application/pdf']`) afterwards | |
magicArray.forEach(entry => { | |
defineProp(magicArray, entry[itemMainProp], entry) | |
}) | |
// This is the best way to fake the type to make sure this is false: `Array.isArray(navigator.mimeTypes)` | |
const magicArrayObj = Object.create(proto, { | |
...Object.getOwnPropertyDescriptors(magicArray), | |
// There's one ugly quirk we unfortunately need to take care of: | |
// The `MimeTypeArray` prototype has an enumerable `length` property, | |
// but headful Chrome will still skip it when running `Object.getOwnPropertyNames(navigator.mimeTypes)`. | |
// To strip it we need to make it first `configurable` and can then overlay a Proxy with an `ownKeys` trap. | |
length: { | |
value: magicArray.length, | |
writable: false, | |
enumerable: false, | |
configurable: true // Important to be able to use the ownKeys trap in a Proxy to strip `length` | |
} | |
}) | |
// Generate our functional function mocks :-) | |
const functionMocks = generateFunctionMocks( | |
proto, | |
itemMainProp, | |
magicArray | |
) | |
// Override custom object with proxy | |
return new Proxy(magicArrayObj, { | |
get(target, key = '') { | |
// Redirect function calls to our custom proxied versions mocking the vanilla behavior | |
if (key === 'item') { | |
return functionMocks.item | |
} | |
if (key === 'namedItem') { | |
return functionMocks.namedItem | |
} | |
if (proto === PluginArray.prototype && key === 'refresh') { | |
return functionMocks.refresh | |
} | |
// Everything else can pass through as normal | |
return utils.cache.Reflect.get(...arguments) | |
}, | |
ownKeys(target) { | |
// There are a couple of quirks where the original property demonstrates "magical" behavior that makes no sense | |
// This can be witnessed when calling `Object.getOwnPropertyNames(navigator.mimeTypes)` and the absense of `length` | |
// My guess is that it has to do with the recent change of not allowing data enumeration and this being implemented weirdly | |
// For that reason we just completely fake the available property names based on our data to match what regular Chrome is doing | |
// Specific issues when not patching this: `length` property is available, direct `types` props (e.g. `obj['application/pdf']`) are missing | |
const keys = [] | |
const typeProps = magicArray.map(mt => mt[itemMainProp]) | |
typeProps.forEach((_, i) => keys.push(`${i}`)) | |
typeProps.forEach(propName => keys.push(propName)) | |
return keys | |
} | |
}) | |
} | |
""" | |