const mimeTypesData = [ { type: "application/pdf", suffixes: "pdf", description: "", __pluginName: "Chrome PDF Viewer", }, { type: "application/x-google-chrome-pdf", suffixes: "pdf", description: "Portable Document Format", __pluginName: "Chrome PDF Plugin", }, { type: "application/x-nacl", suffixes: "", description: "Native Client Executable", __pluginName: "Native Client", }, { type: "application/x-pnacl", suffixes: "", description: "Portable Native Client Executable", __pluginName: "Native Client", }, ]; const pluginsData = [ { name: "Chrome PDF Plugin", filename: "internal-pdf-viewer", description: "Portable Document Format", __mimeTypes: ["application/x-google-chrome-pdf"], }, { name: "Chrome PDF Viewer", filename: "mhjfbmdgcfjbbpaeojofohoefgiehjai", description: "", __mimeTypes: ["application/pdf"], }, { name: "Native Client", filename: "internal-nacl-plugin", description: "", __mimeTypes: ["application/x-nacl", "application/x-pnacl"], }, ]; const defineProp = (obj, prop, value) => Object_defineProperty(obj, prop, { value, writable: false, enumerable: false, configurable: true, }); const generateFunctionMocks = (proto, itemMainProp, dataArray) => ({ item: new Window_Proxy(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.`, ); } const isInteger = args[0] && Number.isInteger(Number(args[0])); return (isInteger ? dataArray[Number(args[0])] : dataArray[0]) || null; }, }), namedItem: new Window_Proxy(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(item => item[itemMainProp] === args[0]) || null; }, }), refresh: proto.refresh ? new Window_Proxy(proto.refresh, { apply() { return undefined; }, }) : undefined, }); const generateMagicArray = (dataArray, proto, itemProto, itemMainProp) => { const makeItem = (data) => { const item = {}; for (const prop of Object_keys(data)) { if (prop.startsWith("__")) continue; defineProp(item, prop, data[prop]); } return patchItem(item, data); }; const patchItem = (item, data) => { let descriptor = Object_getOwnPropertyDescriptors(item); if (itemProto === Plugin.prototype) { descriptor = { ...descriptor, length: { value: data.__mimeTypes.length, writable: false, enumerable: false, configurable: true, }, }; } const obj = Object_create(itemProto, descriptor); const blacklist = [...Object_keys(data), "length", "enabledPlugin"]; return new Window_Proxy(obj, { ownKeys(target) { return Reflect.ownKeys(target).filter(key => !blacklist.includes(key)); }, getOwnPropertyDescriptor(target, prop) { if (blacklist.includes(prop)) return undefined; return Reflect.getOwnPropertyDescriptor(target, prop); }, }); }; const magicArray = []; dataArray.forEach(data => { magicArray.push(makeItem(data)); }); magicArray.forEach(entry => { defineProp(magicArray, entry[itemMainProp], entry); }); const magicArrayObj = Object_create(proto, { ...Object_getOwnPropertyDescriptors(magicArray), length: { value: magicArray.length, writable: false, enumerable: false, configurable: true, }, }); const functionMocks = generateFunctionMocks(proto, itemMainProp, magicArray); return new Window_Proxy(magicArrayObj, { get(target, key = "") { if (key === "item") return functionMocks.item; if (key === "namedItem") return functionMocks.namedItem; if (proto === PluginArray.prototype && key === "refresh") return functionMocks.refresh; return Reflect.get(target, key); }, ownKeys(target) { const keys = []; const typeProps = magicArray.map(entry => entry[itemMainProp]); typeProps.forEach((_, index) => keys.push(`${index}`)); typeProps.forEach(propName => keys.push(propName)); return keys; }, getOwnPropertyDescriptor(target, prop) { if (prop === "length") return undefined; return Reflect.getOwnPropertyDescriptor(target, prop); }, }); }; const generateMimeTypeArray = (data) => generateMagicArray(data, MimeTypeArray.prototype, MimeType.prototype, "type"); const generatePluginArray = (data) => generateMagicArray(data, PluginArray.prototype, Plugin.prototype, "name"); const mimeTypes = generateMimeTypeArray(mimeTypesData); const plugins = generatePluginArray(pluginsData); for (const pluginData of pluginsData) { pluginData.__mimeTypes.forEach((type, index) => { plugins[pluginData.name][index] = mimeTypes[type]; Object_defineProperty(plugins[pluginData.name], type, { value: mimeTypes[type], writable: false, enumerable: false, configurable: true, }); Object_defineProperty(mimeTypes[type], "enabledPlugin", { value: type === "application/x-pnacl" ? mimeTypes["application/x-nacl"].enabledPlugin : new Window_Proxy(plugins[pluginData.name], {}), writable: false, enumerable: false, configurable: true, }); }); } const patchNavigator = (name, value) => Object_defineProperty(Object_getPrototypeOf(navigator), name, { get() { return value; }, }); if (!("plugins" in navigator) || navigator.plugins.length === 0) { patchNavigator("plugins", plugins); patchNavigator("mimeTypes", mimeTypes); }