使用python+selenium谷歌浏览器驱动查排名

这篇具有很好参考价值的文章主要介绍了使用python+selenium谷歌浏览器驱动查排名。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

这套程序仅供各位同学学习,不作为商业使用工具,该工具包含隐藏浏览器指纹特征,虽然该工具减少了百度弹验证码的频率,但是还是会有一次让手动滑动验证码,作者最后也不知道百度是如何判断模拟器的。
下面介绍使用的扩展有哪些:
import time #时间
from selenium import webdriver #操作浏览器工具包
from selenium.webdriver.common.action_chains import ActionChains #操作鼠标可滑动验证码
from selenium.webdriver.chrome.service import Service as ChromeService #selenium 4.0以上使用
from selenium.webdriver.common.by import By #selenium 4.0以上使用
import json
import sys
import os
import xlrd #操作excel
import shutil #操作excel
from webdriver_manager.chrome import ChromeDriverManager #管理浏览器驱动版本

#window.navigator.webdriver #可以查看浏览器是否是驱动模拟

第一步:需要启动浏览器,如果没有浏览器驱动需要下载驱动文件

cache_dir  = appPath + "/cache"
if not os.path.exists(cache_dir):
    os.makedirs(cache_dir, 0o777)
#以下浏览器配置自己网查查
mobileEmulation = {'deviceName': 'iPhone 12 Pro'}
options = webdriver.ChromeOptions()
options.add_experimental_option('mobileEmulation', mobileEmulation)
options.add_experimental_option("detach", True)
options.add_experimental_option('useAutomationExtension', False)
options.add_experimental_option("excludeSwitches", ['enable-automation',"enable-logging"])
options.add_argument('--start-maximized')
options.add_argument('disable-infobars')
options.add_argument('--disable-infobars')
options.add_argument('--disk-cache-dir='+str(cache_dir))
options.add_argument('--disk-cache-size=52428800')
options.add_argument("--no-sandbox")
options.add_argument("--disable-extensions")
options.add_argument("--dns-prefetch-disable")
options.add_argument("--disable-gpu")
options.add_argument("--disable-browser-side-navigation")
options.add_argument('--ignore-certificate-errors')
options.add_argument('--ignore-ssl-errors')
webdriver.DesiredCapabilities.CHROME['pageLoadStrategy'] = 'eager'

try:
    service = ChromeService(executable_path=appPath + "\chromedriver.exe")
    driver = webdriver.Chrome(service=service, options=options)
except:
    new_path = ChromeDriverManager().install()
    shutil.copy(new_path, appPath)
    service = ChromeService(executable_path=new_path)
    driver = webdriver.Chrome(service=service, options=options)
#此处设置隐藏模拟浏览器的指纹特征
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
    "source": """
          Object.defineProperty(navigator, 'webdriver', {
                    get: () => undefined
          });"""
})
#此处给手机模拟器设置经纬度定位
Map_coordinates = dict({
    "latitude": 39.731499,
    "longitude": 116.170212,
    "accuracy": 100
})
driver.execute_cdp_cmd("Emulation.setGeolocationOverride", Map_coordinates)
#此处二次隐藏模拟器的指纹特征,stealth.min.js 网上流传的隐藏指纹特征的js
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
    "source": zhiwenjs()
})
#此处三次隐藏模拟器的指纹特征,该页面是以执行js来更改
js2 = '''Object.defineProperty(navigator, 'webdriver', {
                 get: () => undefined
               })'''
driver.execute_script(js2)
driver.implicitly_wait(5)
return driver  

第二步:定义隐藏指纹特征的js,为了不让同学们下载,我直接粘贴出来了
下面展示 全部js

 return  """(({_utilsFns:_utilsFns,_mainFunction:_mainFunction,_args:_args})=>{const utils=Object.fromEntries(Object.entries(_utilsFns).map((([key,value])=>[key,eval(value)])));utils.init(),eval(_mainFunction)(utils,..._args)})({_utilsFns:{init:"() => {\n  utils.preloadCache()\n}",stripProxyFromErrors:"(handler = {}) => {\n  const newHandler = {\n    setPrototypeOf: function (target, proto) {\n      if (proto === null)\n        throw new TypeError('Cannot convert object to primitive value')\n      if (Object.getPrototypeOf(target) === Object.getPrototypeOf(proto)) {\n        throw new TypeError('Cyclic __proto__ value')\n      }\n      return Reflect.setPrototypeOf(target, proto)\n    }\n  }\n  // We wrap each trap in the handler in a try/catch and modify the error stack if they throw\n  const traps = Object.getOwnPropertyNames(handler)\n  traps.forEach(trap => {\n    newHandler[trap] = function () {\n      try {\n        // Forward the call to the defined proxy handler\n        return handler[trap].apply(this, arguments || [])\n      } catch (err) {\n        // Stack traces differ per browser, we only support chromium based ones currently\n        if (!err || !err.stack || !err.stack.includes(`at `)) {\n          throw err\n        }\n\n        // When something throws within one of our traps the Proxy will show up in error stacks\n        // An earlier implementation of this code would simply strip lines with a blacklist,\n        // but it makes sense to be more surgical here and only remove lines related to our Proxy.\n        // We try to use a known \"anchor\" line for that and strip it with everything above it.\n        // If the anchor line cannot be found for some reason we fall back to our blacklist approach.\n\n        const stripWithBlacklist = (stack, stripFirstLine = true) => {\n          const blacklist = [\n            `at Reflect.${trap} `, // e.g. Reflect.get or Reflect.apply\n            `at Object.${trap} `, // e.g. Object.get or Object.apply\n            `at Object.newHandler.<computed> [as ${trap}] ` // caused by this very wrapper :-)\n          ]\n          return (\n            err.stack\n              .split('\\n')\n              // Always remove the first (file) line in the stack (guaranteed to be our proxy)\n              .filter((line, index) => !(index === 1 && stripFirstLine))\n              // Check if the line starts with one of our blacklisted strings\n              .filter(line => !blacklist.some(bl => line.trim().startsWith(bl)))\n              .join('\\n')\n          )\n        }\n\n        const stripWithAnchor = (stack, anchor) => {\n          const stackArr = stack.split('\\n')\n          anchor = anchor || `at Object.newHandler.<computed> [as ${trap}] ` // Known first Proxy line in chromium\n          const anchorIndex = stackArr.findIndex(line =>\n            line.trim().startsWith(anchor)\n          )\n          if (anchorIndex === -1) {\n            return false // 404, anchor not found\n          }\n          // Strip everything from the top until we reach the anchor line\n          // Note: We're keeping the 1st line (zero index) as it's unrelated (e.g. `TypeError`)\n          stackArr.splice(1, anchorIndex)\n          return stackArr.join('\\n')\n        }\n\n        // Special cases due to our nested toString proxies\n        err.stack = err.stack.replace(\n          'at Object.toString (',\n          'at Function.toString ('\n        )\n        if ((err.stack || '').includes('at Function.toString (')) {\n          err.stack = stripWithBlacklist(err.stack, false)\n          throw err\n        }\n\n        // Try using the anchor method, fallback to blacklist if necessary\n        err.stack = stripWithAnchor(err.stack) || stripWithBlacklist(err.stack)\n\n        throw err // Re-throw our now sanitized error\n      }\n    }\n  })\n  return newHandler\n}",stripErrorWithAnchor:"(err, anchor) => {\n  const stackArr = err.stack.split('\\n')\n  const anchorIndex = stackArr.findIndex(line => line.trim().startsWith(anchor))\n  if (anchorIndex === -1) {\n    return err // 404, anchor not found\n  }\n  // Strip everything from the top until we reach the anchor line (remove anchor line as well)\n  // Note: We're keeping the 1st line (zero index) as it's unrelated (e.g. `TypeError`)\n  stackArr.splice(1, anchorIndex)\n  err.stack = stackArr.join('\\n')\n  return err\n}",replaceProperty:"(obj, propName, descriptorOverrides = {}) => {\n  return Object.defineProperty(obj, propName, {\n    // Copy over the existing descriptors (writable, enumerable, configurable, etc)\n    ...(Object.getOwnPropertyDescriptor(obj, propName) || {}),\n    // Add our overrides (e.g. value, get())\n    ...descriptorOverrides\n  })\n}",preloadCache:"() => {\n  if (utils.cache) {\n    return\n  }\n  utils.cache = {\n    // Used in our proxies\n    Reflect: {\n      get: Reflect.get.bind(Reflect),\n      apply: Reflect.apply.bind(Reflect)\n    },\n    // Used in `makeNativeString`\n    nativeToStringStr: Function.toString + '' // => `function toString() { [native code] }`\n  }\n}",makeNativeString:"(name = '') => {\n  return utils.cache.nativeToStringStr.replace('toString', name || '')\n}",patchToString:"(obj, str = '') => {\n  const handler = {\n    apply: function (target, ctx) {\n      // This fixes e.g. `HTMLMediaElement.prototype.canPlayType.toString + \"\"`\n      if (ctx === Function.prototype.toString) {\n        return utils.makeNativeString('toString')\n      }\n      // `toString` targeted at our proxied Object detected\n      if (ctx === obj) {\n        // We either return the optional string verbatim or derive the most desired result automatically\n        return str || utils.makeNativeString(obj.name)\n      }\n      // Check if the toString protype of the context is the same as the global prototype,\n      // if not indicates that we are doing a check across different windows., e.g. the iframeWithdirect` test case\n      const hasSameProto = Object.getPrototypeOf(\n        Function.prototype.toString\n      ).isPrototypeOf(ctx.toString) // eslint-disable-line no-prototype-builtins\n      if (!hasSameProto) {\n        // Pass the call on to the local Function.prototype.toString instead\n        return ctx.toString()\n      }\n      return target.call(ctx)\n    }\n  }\n\n  const toStringProxy = new Proxy(\n    Function.prototype.toString,\n    utils.stripProxyFromErrors(handler)\n  )\n  utils.replaceProperty(Function.prototype, 'toString', {\n    value: toStringProxy\n  })\n}",patchToStringNested:"(obj = {}) => {\n  return utils.execRecursively(obj, ['function'], utils.patchToString)\n}",redirectToString:"(proxyObj, originalObj) => {\n  const handler = {\n    apply: function (target, ctx) {\n      // This fixes e.g. `HTMLMediaElement.prototype.canPlayType.toString + \"\"`\n      if (ctx === Function.prototype.toString) {\n        return utils.makeNativeString('toString')\n      }\n\n      // `toString` targeted at our proxied Object detected\n      if (ctx === proxyObj) {\n        const fallback = () =>\n          originalObj && originalObj.name\n            ? utils.makeNativeString(originalObj.name)\n            : utils.makeNativeString(proxyObj.name)\n\n        // Return the toString representation of our original object if possible\n        return originalObj + '' || fallback()\n      }\n\n      if (typeof ctx === 'undefined' || ctx === null) {\n        return target.call(ctx)\n      }\n\n      // Check if the toString protype of the context is the same as the global prototype,\n      // if not indicates that we are doing a check across different windows., e.g. the iframeWithdirect` test case\n      const hasSameProto = Object.getPrototypeOf(\n        Function.prototype.toString\n      ).isPrototypeOf(ctx.toString) // eslint-disable-line no-prototype-builtins\n      if (!hasSameProto) {\n        // Pass the call on to the local Function.prototype.toString instead\n        return ctx.toString()\n      }\n\n      return target.call(ctx)\n    }\n  }\n\n  const toStringProxy = new Proxy(\n    Function.prototype.toString,\n    utils.stripProxyFromErrors(handler)\n  )\n  utils.replaceProperty(Function.prototype, 'toString', {\n    value: toStringProxy\n  })\n}",replaceWithProxy:"(obj, propName, handler) => {\n  const originalObj = obj[propName]\n  const proxyObj = new Proxy(obj[propName], utils.stripProxyFromErrors(handler))\n\n  utils.replaceProperty(obj, propName, { value: proxyObj })\n  utils.redirectToString(proxyObj, originalObj)\n\n  return true\n}",replaceGetterWithProxy:"(obj, propName, handler) => {\n  const fn = Object.getOwnPropertyDescriptor(obj, propName).get\n  const fnStr = fn.toString() // special getter function string\n  const proxyObj = new Proxy(fn, utils.stripProxyFromErrors(handler))\n\n  utils.replaceProperty(obj, propName, { get: proxyObj })\n  utils.patchToString(proxyObj, fnStr)\n\n  return true\n}",replaceGetterSetter:"(obj, propName, handlerGetterSetter) => {\n  const ownPropertyDescriptor = Object.getOwnPropertyDescriptor(obj, propName)\n  const handler = { ...ownPropertyDescriptor }\n\n  if (handlerGetterSetter.get !== undefined) {\n    const nativeFn = ownPropertyDescriptor.get\n    handler.get = function() {\n      return handlerGetterSetter.get.call(this, nativeFn.bind(this))\n    }\n    utils.redirectToString(handler.get, nativeFn)\n  }\n\n  if (handlerGetterSetter.set !== undefined) {\n    const nativeFn = ownPropertyDescriptor.set\n    handler.set = function(newValue) {\n      handlerGetterSetter.set.call(this, newValue, nativeFn.bind(this))\n    }\n    utils.redirectToString(handler.set, nativeFn)\n  }\n\n  Object.defineProperty(obj, propName, handler)\n}",mockWithProxy:"(obj, propName, pseudoTarget, handler) => {\n  const proxyObj = new Proxy(pseudoTarget, utils.stripProxyFromErrors(handler))\n\n  utils.replaceProperty(obj, propName, { value: proxyObj })\n  utils.patchToString(proxyObj)\n\n  return true\n}",createProxy:"(pseudoTarget, handler) => {\n  const proxyObj = new Proxy(pseudoTarget, utils.stripProxyFromErrors(handler))\n  utils.patchToString(proxyObj)\n\n  return proxyObj\n}",splitObjPath:"objPath => ({\n  // Remove last dot entry (property) ==> `HTMLMediaElement.prototype`\n  objName: objPath.split('.').slice(0, -1).join('.'),\n  // Extract last dot entry ==> `canPlayType`\n  propName: objPath.split('.').slice(-1)[0]\n})",replaceObjPathWithProxy:"(objPath, handler) => {\n  const { objName, propName } = utils.splitObjPath(objPath)\n  const obj = eval(objName) // eslint-disable-line no-eval\n  return utils.replaceWithProxy(obj, propName, handler)\n}",execRecursively:"(obj = {}, typeFilter = [], fn) => {\n  function recurse(obj) {\n    for (const key in obj) {\n      if (obj[key] === undefined) {\n        continue\n      }\n      if (obj[key] && typeof obj[key] === 'object') {\n        recurse(obj[key])\n      } else {\n        if (obj[key] && typeFilter.includes(typeof obj[key])) {\n          fn.call(this, obj[key])\n        }\n      }\n    }\n  }\n  recurse(obj)\n  return obj\n}",stringifyFns:"(fnObj = { hello: () => 'world' }) => {\n  // Object.fromEntries() ponyfill (in 6 lines) - supported only in Node v12+, modern browsers are fine\n  // https://github.com/feross/fromentries\n  function fromEntries(iterable) {\n    return [...iterable].reduce((obj, [key, val]) => {\n      obj[key] = val\n      return obj\n    }, {})\n  }\n  return (Object.fromEntries || fromEntries)(\n    Object.entries(fnObj)\n      .filter(([key, value]) => typeof value === 'function')\n      .map(([key, value]) => [key, value.toString()]) // eslint-disable-line no-eval\n  )\n}",materializeFns:"(fnStrObj = { hello: \"() => 'world'\" }) => {\n  return Object.fromEntries(\n    Object.entries(fnStrObj).map(([key, value]) => {\n      if (value.startsWith('function')) {\n        // some trickery is needed to make oldschool functions work :-)\n        return [key, eval(`() => ${value}`)()] // eslint-disable-line no-eval\n      } else {\n        // arrow functions just work\n        return [key, eval(value)] // eslint-disable-line no-eval\n      }\n    })\n  )\n}",makeHandler:"() => ({\n  // Used by simple `navigator` getter evasions\n  getterValue: value => ({\n    apply(target, ctx, args) {\n      // Let's fetch the value first, to trigger and escalate potential errors\n      // Illegal invocations like `navigator.__proto__.vendor` will throw here\n      utils.cache.Reflect.apply(...arguments)\n      return value\n    }\n  })\n})",arrayEquals:"(array1, array2) => {\n  if (array1.length !== array2.length) {\n    return false\n  }\n  for (let i = 0; i < array1.length; ++i) {\n    if (array1[i] !== array2[i]) {\n      return false\n    }\n  }\n  return true\n}",memoize:"fn => {\n  const cache = []\n  return function(...args) {\n    if (!cache.some(c => utils.arrayEquals(c.key, args))) {\n      cache.push({ key: args, value: fn.apply(this, args) })\n    }\n    return cache.find(c => utils.arrayEquals(c.key, args)).value\n  }\n}"},_mainFunction:'utils => {\n      if (!window.chrome) {\n        // Use the exact property descriptor found in headful Chrome\n        // fetch it via `Object.getOwnPropertyDescriptor(window, \'chrome\')`\n        Object.defineProperty(window, \'chrome\', {\n          writable: true,\n          enumerable: true,\n          configurable: false, // note!\n          value: {} // We\'ll extend that later\n        })\n      }\n\n      // That means we\'re running headful and don\'t need to mock anything\n      if (\'app\' in window.chrome) {\n        return // Nothing to do here\n      }\n\n      const makeError = {\n        ErrorInInvocation: fn => {\n          const err = new TypeError(`Error in invocation of app.${fn}()`)\n          return utils.stripErrorWithAnchor(\n            err,\n            `at ${fn} (eval at <anonymous>`\n          )\n        }\n      }\n\n      // There\'s a some static data in that property which doesn\'t seem to change,\n      // we should periodically check for updates: `JSON.stringify(window.app, null, 2)`\n      const STATIC_DATA = JSON.parse(\n        `\n{\n  "isInstalled": false,\n  "InstallState": {\n    "DISABLED": "disabled",\n    "INSTALLED": "installed",\n    "NOT_INSTALLED": "not_installed"\n  },\n  "RunningState": {\n    "CANNOT_RUN": "cannot_run",\n    "READY_TO_RUN": "ready_to_run",\n    "RUNNING": "running"\n  }\n}\n        `.trim()\n      )\n\n      window.chrome.app = {\n        ...STATIC_DATA,\n\n        get isInstalled() {\n          return false\n        },\n\n        getDetails: function getDetails() {\n          if (arguments.length) {\n            throw makeError.ErrorInInvocation(`getDetails`)\n          }\n          return null\n        },\n        getIsInstalled: function getDetails() {\n          if (arguments.length) {\n            throw makeError.ErrorInInvocation(`getIsInstalled`)\n          }\n          return false\n        },\n        runningState: function getDetails() {\n          if (arguments.length) {\n            throw makeError.ErrorInInvocation(`runningState`)\n          }\n          return \'cannot_run\'\n        }\n      }\n      utils.patchToStringNested(window.chrome.app)\n    }',_args:[]}),(({_utilsFns:_utilsFns,_mainFunction:_mainFunction,_args:_args})=>{const utils=Object.fromEntries(Object.entries(_utilsFns).map((([key,value])=>[key,eval(value)])));utils.init(),eval(_mainFunction)(utils,..._args)})({_utilsFns:{init:"() => {\n  utils.preloadCache()\n}",stripProxyFromErrors:"(handler = {}) => {\n  const newHandler = {\n    setPrototypeOf: function (target, proto) {\n      if (proto === null)\n        throw new TypeError('Cannot convert object to primitive value')\n      if (Object.getPrototypeOf(target) === Object.getPrototypeOf(proto)) {\n        throw new TypeError('Cyclic __proto__ value')\n      }\n      return Reflect.setPrototypeOf(target, proto)\n    }\n  }\n  // We wrap each trap in the handler in a try/catch and modify the error stack if they throw\n  const traps = Object.getOwnPropertyNames(handler)\n  traps.forEach(trap => {\n    newHandler[trap] = function () {\n      try {\n        // Forward the call to the defined proxy handler\n        return handler[trap].apply(this, arguments || [])\n      } catch (err) {\n        // Stack traces differ per browser, we only support chromium based ones currently\n        if (!err || !err.stack || !err.stack.includes(`at `)) {\n          throw err\n        }\n\n        // When something throws within one of our traps the Proxy will show up in error stacks\n        // An earlier implementation of this code would simply strip lines with a blacklist,\n        // but it makes sense to be more surgical here and only remove lines related to our Proxy.\n        // We try to use a known \"anchor\" line for that and strip it with everything above it.\n        // If the anchor line cannot be found for some reason we fall back to our blacklist approach.\n\n        const stripWithBlacklist = (stack, stripFirstLine = true) => {\n          const blacklist = [\n            `at Reflect.${trap} `, // e.g. Reflect.get or Reflect.apply\n            `at Object.${trap} `, // e.g. Object.get or Object.apply\n            `at Object.newHandler.<computed> [as ${trap}] ` // caused by this very wrapper :-)\n          ]\n          return (\n            err.stack\n              .split('\\n')\n              // Always remove the first (file) line in the stack (guaranteed to be our proxy)\n              .filter((line, index) => !(index === 1 && stripFirstLine))\n              // Check if the line starts with one of our blacklisted strings\n              .filter(line => !blacklist.some(bl => line.trim().startsWith(bl)))\n              .join('\\n')\n          )\n        }\n\n        const stripWithAnchor = (stack, anchor) => {\n          const stackArr = stack.split('\\n')\n          anchor = anchor || `at Object.newHandler.<computed> [as ${trap}] ` // Known first Proxy line in chromium\n          const anchorIndex = stackArr.findIndex(line =>\n            line.trim().startsWith(anchor)\n          )\n          if (anchorIndex === -1) {\n            return false // 404, anchor not found\n          }\n          // Strip everything from the top until we reach the anchor line\n          // Note: We're keeping the 1st line (zero index) as it's unrelated (e.g. `TypeError`)\n          stackArr.splice(1, anchorIndex)\n          return stackArr.join('\\n')\n        }\n\n        // Special cases due to our nested toString proxies\n        err.stack = err.stack.replace(\n          'at Object.toString (',\n          'at Function.toString ('\n        )\n        if ((err.stack || '').includes('at Function.toString (')) {\n          err.stack = stripWithBlacklist(err.stack, false)\n          throw err\n        }\n\n        // Try using the anchor method, fallback to blacklist if necessary\n        err.stack = stripWithAnchor(err.stack) || stripWithBlacklist(err.stack)\n\n        throw err // Re-throw our now sanitized error\n      }\n    }\n  })\n  return newHandler\n}",stripErrorWithAnchor:"(err, anchor) => {\n  const stackArr = err.stack.split('\\n')\n  const anchorIndex = stackArr.findIndex(line => line.trim().startsWith(anchor))\n  if (anchorIndex === -1) {\n    return err // 404, anchor not found\n  }\n  // Strip everything from the top until we reach the anchor line (remove anchor line as well)\n  // Note: We're keeping the 1st line (zero index) as it's unrelated (e.g. `TypeError`)\n  stackArr.splice(1, anchorIndex)\n  err.stack = stackArr.join('\\n')\n  return err\n}",replaceProperty:"(obj, propName, descriptorOverrides = {}) => {\n  return Object.defineProperty(obj, propName, {\n    // Copy over the existing descriptors (writable, enumerable, configurable, etc)\n    ...(Object.getOwnPropertyDescriptor(obj, propName) || {}),\n    // Add our overrides (e.g. value, get())\n    ...descriptorOverrides\n  })\n}",preloadCache:"() => {\n  if (utils.cache) {\n    return\n  }\n  utils.cache = {\n    // Used in our proxies\n    Reflect: {\n      get: Reflect.get.bind(Reflect),\n      apply: Reflect.apply.bind(Reflect)\n    },\n    // Used in `makeNativeString`\n    nativeToStringStr: Function.toString + '' // => `function toString() { [native code] }`\n  }\n}",makeNativeString:"(name = '') => {\n  return utils.cache.nativeToStringStr.replace('toString', name || '')\n}",patchToString:"(obj, str = '') => {\n  const handler = {\n    apply: function (target, ctx) {\n      // This fixes e.g. `HTMLMediaElement.prototype.canPlayType.toString + \"\"`\n      if (ctx === Function.prototype.toString) {\n        return utils.makeNativeString('toString')\n      }\n      // `toString` targeted at our proxied Object detected\n      if (ctx === obj) {\n        // We either return the optional string verbatim or derive the most desired result automatically\n        return str || utils.makeNativeString(obj.name)\n      }\n      // Check if the toString protype of the context is the same as the global prototype,\n      // if not indicates that we are doing a check across different windows., e.g. the iframeWithdirect` test case\n      const hasSameProto = Object.getPrototypeOf(\n        Function.prototype.toString\n      ).isPrototypeOf(ctx.toString) // eslint-disable-line no-prototype-builtins\n      if (!hasSameProto) {\n        // Pass the call on to the local Function.prototype.toString instead\n        return ctx.toString()\n      }\n      return target.call(ctx)\n    }\n  }\n\n  const toStringProxy = new Proxy(\n    Function.prototype.toString,\n    utils.stripProxyFromErrors(handler)\n  )\n  utils.replaceProperty(Function.prototype, 'toString', {\n    value: toStringProxy\n  })\n}",patchToStringNested:"(obj = {}) => {\n  return utils.execRecursively(obj, ['function'], utils.patchToString)\n}",redirectToString:"(proxyObj, originalObj) => {\n  const handler = {\n    apply: function (target, ctx) {\n      // This fixes e.g. `HTMLMediaElement.prototype.canPlayType.toString + \"\"`\n      if (ctx === Function.prototype.toString) {\n        return utils.makeNativeString('toString')\n      }\n\n      // `toString` targeted at our proxied Object detected\n      if (ctx === proxyObj) {\n        const fallback = () =>\n          originalObj && originalObj.name\n            ? utils.makeNativeString(originalObj.name)\n            : utils.makeNativeString(proxyObj.name)\n\n        // Return the toString representation of our original object if possible\n        return originalObj + '' || fallback()\n      }\n\n      if (typeof ctx === 'undefined' || ctx === null) {\n        return target.call(ctx)\n      }\n\n      // Check if the toString protype of the context is the same as the global prototype,\n      // if not indicates that we are doing a check across different windows., e.g. the iframeWithdirect` test case\n      const hasSameProto = Object.getPrototypeOf(\n        Function.prototype.toString\n      ).isPrototypeOf(ctx.toString) // eslint-disable-line no-prototype-builtins\n      if (!hasSameProto) {\n        // Pass the call on to the local Function.prototype.toString instead\n        return ctx.toString()\n      }\n\n      return target.call(ctx)\n    }\n  }\n\n  const toStringProxy = new Proxy(\n    Function.prototype.toString,\n    utils.stripProxyFromErrors(handler)\n  )\n  utils.replaceProperty(Function.prototype, 'toString', {\n    value: toStringProxy\n  })\n}",replaceWithProxy:"(obj, propName, handler) => {\n  const originalObj = obj[propName]\n  const proxyObj = new Proxy(obj[propName], utils.stripProxyFromErrors(handler))\n\n  utils.replaceProperty(obj, propName, { value: proxyObj })\n  utils.redirectToString(proxyObj, originalObj)\n\n  return true\n}",replaceGetterWithProxy:"(obj, propName, handler) => {\n  const fn = Object.getOwnPropertyDescriptor(obj, propName).get\n  const fnStr = fn.toString() // special getter function string\n  const proxyObj = new Proxy(fn, utils.stripProxyFromErrors(handler))\n\n  utils.replaceProperty(obj, propName, { get: proxyObj })\n  utils.patchToString(proxyObj, fnStr)\n\n  return true\n}",replaceGetterSetter:"(obj, propName, handlerGetterSetter) => {\n  const ownPropertyDescriptor = Object.getOwnPropertyDescriptor(obj, propName)\n  const handler = { ...ownPropertyDescriptor }\n\n  if (handlerGetterSetter.get !== undefined) {\n    const nativeFn = ownPropertyDescriptor.get\n    handler.get = function() {\n      return handlerGetterSetter.get.call(this, nativeFn.bind(this))\n    }\n    utils.redirectToString(handler.get, nativeFn)\n  }\n\n  if (handlerGetterSetter.set !== undefined) {\n    const nativeFn = ownPropertyDescriptor.set\n    handler.set = function(newValue) {\n      handlerGetterSetter.set.call(this, newValue, nativeFn.bind(this))\n    }\n    utils.redirectToString(handler.set, nativeFn)\n  }\n\n  Object.defineProperty(obj, propName, handler)\n}",mockWithProxy:"(obj, propName, pseudoTarget, handler) => {\n  const proxyObj = new Proxy(pseudoTarget, utils.stripProxyFromErrors(handler))\n\n  utils.replaceProperty(obj, propName, { value: proxyObj })\n  utils.patchToString(proxyObj)\n\n  return true\n}",createProxy:"(pseudoTarget, handler) => {\n  const proxyObj = new Proxy(pseudoTarget, utils.stripProxyFromErrors(handler))\n  utils.patchToString(proxyObj)\n\n  return proxyObj\n}",splitObjPath:"objPath => ({\n  // Remove last dot entry (property) ==> `HTMLMediaElement.prototype`\n  objName: objPath.split('.').slice(0, -1).join('.'),\n  // Extract last dot entry ==> `canPlayType`\n  propName: objPath.split('.').slice(-1)[0]\n})",replaceObjPathWithProxy:"(objPath, handler) => {\n  const { objName, propName } = utils.splitObjPath(objPath)\n  const obj = eval(objName) // eslint-disable-line no-eval\n  return utils.replaceWithProxy(obj, propName, handler)\n}",execRecursively:"(obj = {}, typeFilter = [], fn) => {\n  function recurse(obj) {\n    for (const key in obj) {\n      if (obj[key] === undefined) {\n        continue\n      }\n      if (obj[key] && typeof obj[key] === 'object') {\n        recurse(obj[key])\n      } else {\n        if (obj[key] && typeFilter.includes(typeof obj[key])) {\n          fn.call(this, obj[key])\n        }\n      }\n    }\n  }\n  recurse(obj)\n  return obj\n}",stringifyFns:"(fnObj = { hello: () => 'world' }) => {\n  // Object.fromEntries() ponyfill (in 6 lines) - supported only in Node v12+, modern browsers are fine\n  // https://github.com/feross/fromentries\n  function fromEntries(iterable) {\n    return [...iterable].reduce((obj, [key, val]) => {\n      obj[key] = val\n      return obj\n    }, {})\n  }\n  return (Object.fromEntries || fromEntries)(\n    Object.entries(fnObj)\n      .filter(([key, value]) => typeof value === 'function')\n      .map(([key, value]) => [key, value.toString()]) // eslint-disable-line no-eval\n  )\n}",materializeFns:"(fnStrObj = { hello: \"() => 'world'\" }) => {\n  return Object.fromEntries(\n    Object.entries(fnStrObj).map(([key, value]) => {\n      if (value.startsWith('function')) {\n        // some trickery is needed to make oldschool functions work :-)\n        return [key, eval(`() => ${value}`)()] // eslint-disable-line no-eval\n      } else {\n        // arrow functions just work\n        return [key, eval(value)] // eslint-disable-line no-eval\n      }\n    })\n  )\n}",makeHandler:"() => ({\n  // Used by simple `navigator` getter evasions\n  getterValue: value => ({\n    apply(target, ctx, args) {\n      // Let's fetch the value first, to trigger and escalate potential errors\n      // Illegal invocations like `navigator.__proto__.vendor` will throw here\n      utils.cache.Reflect.apply(...arguments)\n      return value\n    }\n  })\n})",arrayEquals:"(array1, array2) => {\n  if (array1.length !== array2.length) {\n    return false\n  }\n  for (let i = 0; i < array1.length; ++i) {\n    if (array1[i] !== array2[i]) {\n      return false\n    }\n  }\n  return true\n}",memoize:"fn => {\n  const cache = []\n  return function(...args) {\n    if (!cache.some(c => utils.arrayEquals(c.key, args))) {\n      cache.push({ key: args, value: fn.apply(this, args) })\n    }\n    return cache.find(c => utils.arrayEquals(c.key, args)).value\n  }\n}"},_mainFunction:"utils => {\n      if (!window.chrome) {\n        // Use the exact property descriptor found in headful Chrome\n        // fetch it via `Object.getOwnPropertyDescriptor(window, 'chrome')`\n        Object.defineProperty(window, 'chrome', {\n          writable: true,\n          enumerable: true,\n          configurable: false, // note!\n          value: {} // We'll extend that later\n        })\n      }\n\n      // That means we're running headful and don't need to mock anything\n      if ('csi' in window.chrome) {\n        return // Nothing to do here\n      }\n\n      // Check that the Navigation Timing API v1 is available, we need that\n      if (!window.performance || !window.performance.timing) {\n        return\n      }\n\n      const { timing } = window.performance\n\n      window.chrome.csi = function() {\n        return {\n          onloadT: timing.domContentLoadedEventEnd,\n          startE: timing.navigationStart,\n          pageT: Date.now() - timing.navigationStart,\n          tran: 15 // Transition type or something\n        }\n      }\n      utils.patchToString(window.chrome.csi)\n    }",_args:[]}),(({_utilsFns:_utilsFns,_mainFunction:_mainFunction,_args:_args})=>{const utils=Object.fromEntries(Object.entries(_utilsFns).map((([key,value])=>[key,eval(value)])));utils.init(),eval(_mainFunction)(utils,..._args)})({_utilsFns:{init:"() => {\n  utils.preloadCache()\n}",stripProxyFromErrors:"(handler = {}) => {\n  const newHandler = {\n    setPrototypeOf: function (target, proto) {\n      if (proto === null)\n        throw new TypeError('Cannot convert object to primitive value')\n      if (Object.getPrototypeOf(target) === Object.getPrototypeOf(proto)) {\n        throw new TypeError('Cyclic __proto__ value')\n      }\n      return Reflect.setPrototypeOf(target, proto)\n    }\n  }\n  // We wrap each trap in the handler in a try/catch and modify the error stack if they throw\n  const traps = Object.getOwnPropertyNames(handler)\n  traps.forEach(trap => {\n    newHandler[trap] = function () {\n      try {\n        // Forward the call to the defined proxy handler\n        return handler[trap].apply(this, arguments || [])\n      } catch (err) {\n        // Stack traces differ per browser, we only support chromium based ones currently\n        if (!err || !err.stack || !err.stack.includes(`at `)) {\n          throw err\n        }\n\n        // When something throws within one of our traps the Proxy will show up in error stacks\n        // An earlier implementation of this code would simply strip lines with a blacklist,\n        // but it makes sense to be more surgical here and only remove lines related to our Proxy.\n        // We try to use a known \"anchor\" line for that and strip it with everything above it.\n        // If the anchor line cannot be found for some reason we fall back to our blacklist approach.\n\n        const stripWithBlacklist = (stack, stripFirstLine = true) => {\n          const blacklist = [\n            `at Reflect.${trap} `, // e.g. Reflect.get or Reflect.apply\n            `at Object.${trap} `, // e.g. Object.get or Object.apply\n            `at Object.newHandler.<computed> [as ${trap}] ` // caused by this very wrapper :-)\n          ]\n          return (\n            err.stack\n              .split('\\n')\n              // Always remove the first (file) line in the stack (guaranteed to be our proxy)\n              .filter((line, index) => !(index === 1 && stripFirstLine))\n              // Check if the line starts with one of our blacklisted strings\n              .filter(line => !blacklist.some(bl => line.trim().startsWith(bl)))\n              .join('\\n')\n          )\n        }\n\n        const stripWithAnchor = (stack, anchor) => {\n          const stackArr = stack.split('\\n')\n          anchor = anchor || `at Object.newHandler.<computed> [as ${trap}] ` // Known first Proxy line in chromium\n          const anchorIndex = stackArr.findIndex(line =>\n            line.trim().startsWith(anchor)\n          )\n          if (anchorIndex === -1) {\n            return false // 404, anchor not found\n          }\n          // Strip everything from the top until we reach the anchor line\n          // Note: We're keeping the 1st line (zero index) as it's unrelated (e.g. `TypeError`)\n          stackArr.splice(1, anchorIndex)\n          return stackArr.join('\\n')\n        }\n\n        // Special cases due to our nested toString proxies\n        err.stack = err.stack.replace(\n          'at Object.toString (',\n          'at Function.toString ('\n        )\n        if ((err.stack || '').includes('at Function.toString (')) {\n          err.stack = stripWithBlacklist(err.stack, false)\n          throw err\n        }\n\n        // Try using the anchor method, fallback to blacklist if necessary\n        err.stack = stripWithAnchor(err.stack) || stripWithBlacklist(err.stack)\n\n        throw err // Re-throw our now sanitized error\n      }\n    }\n  })\n  return newHandler\n}",stripErrorWithAnchor:"(err, anchor) => {\n  const stackArr = err.stack.split('\\n')\n  const anchorIndex = stackArr.findIndex(line => line.trim().startsWith(anchor))\n  if (anchorIndex === -1) {\n    return err // 404, anchor not found\n  }\n  // Strip everything from the top until we reach the anchor line (remove anchor line as well)\n  // Note: We're keeping the 1st line (zero index) as it's unrelated (e.g. `TypeError`)\n  stackArr.splice(1, anchorIndex)\n  err.stack = stackArr.join('\\n')\n  return err\n}",replaceProperty:"(obj, propName, descriptorOverrides = {}) => {\n  return Object.defineProperty(obj, propName, {\n    // Copy over the existing descriptors (writable, enumerable, configurable, etc)\n    ...(Object.getOwnPropertyDescriptor(obj, propName) || {}),\n    // Add our overrides (e.g. value, get())\n    ...descriptorOverrides\n  })\n}",preloadCache:"() => {\n  if (utils.cache) {\n    return\n  }\n  utils.cache = {\n    // Used in our proxies\n    Reflect: {\n      get: Reflect.get.bind(Reflect),\n      apply: Reflect.apply.bind(Reflect)\n    },\n    // Used in `makeNativeString`\n    nativeToStringStr: Function.toString + '' // => `function toString() { [native code] }`\n  }\n}",makeNativeString:"(name = '') => {\n  return utils.cache.nativeToStringStr.replace('toString', name || '')\n}",patchToString:"(obj, str = '') => {\n  const handler = {\n    apply: function (target, ctx) {\n      // This fixes e.g. `HTMLMediaElement.prototype.canPlayType.toString + \"\"`\n      if (ctx === Function.prototype.toString) {\n        return utils.makeNativeString('toString')\n      }\n      // `toString` targeted at our proxied Object detected\n      if (ctx === obj) {\n        // We either return the optional string verbatim or derive the most desired result automatically\n        return str || utils.makeNativeString(obj.name)\n      }\n      // Check if the toString protype of the context is the same as the global prototype,\n      // if not indicates that we are doing a check across different windows., e.g. the iframeWithdirect` test case\n      const hasSameProto = Object.getPrototypeOf(\n        Function.prototype.toString\n      ).isPrototypeOf(ctx.toString) // eslint-disable-line no-prototype-builtins\n      if (!hasSameProto) {\n        // Pass the call on to the local Function.prototype.toString instead\n        return ctx.toString()\n      }\n      return target.call(ctx)\n    }\n  }\n\n  const toStringProxy = new Proxy(\n    Function.prototype.toString,\n    utils.stripProxyFromErrors(handler)\n  )\n  utils.replaceProperty(Function.prototype, 'toString', {\n    value: toStringProxy\n  })\n}",patchToStringNested:"(obj = {}) => {\n  return utils.execRecursively(obj, ['function'], utils.patchToString)\n}",redirectToString:"(proxyObj, originalObj) => {\n  const handler = {\n    apply: function (target, ctx) {\n      // This fixes e.g. `HTMLMediaElement.prototype.canPlayType.toString + \"\"`\n      if (ctx === Function.prototype.toString) {\n        return utils.makeNativeString('toString')\n      }\n\n      // `toString` targeted at our proxied Object detected\n      if (ctx === proxyObj) {\n        const fallback = () =>\n          originalObj && originalObj.name\n            ? utils.makeNativeString(originalObj.name)\n            : utils.makeNativeString(proxyObj.name)\n\n        // Return the toString representation of our original object if possible\n        return originalObj + '' || fallback()\n      }\n\n      if (typeof ctx === 'undefined' || ctx === null) {\n        return target.call(ctx)\n      }\n\n      // Check if the toString protype of the context is the same as the global prototype,\n      // if not indicates that we are doing a check across different windows., e.g. the iframeWithdirect` test case\n      const hasSameProto = Object.getPrototypeOf(\n        Function.prototype.toString\n      ).isPrototypeOf(ctx.toString) // eslint-disable-line no-prototype-builtins\n      if (!hasSameProto) {\n        // Pass the call on to the local Function.prototype.toString instead\n        return ctx.toString()\n      }\n\n      return target.call(ctx)\n    }\n  }\n\n  const toStringProxy = new Proxy(\n    Function.prototype.toString,\n    utils.stripProxyFromErrors(handler)\n  )\n  utils.replaceProperty(Function.prototype, 'toString', {\n    value: toStringProxy\n  })\n}",replaceWithProxy:"(obj, propName, handler) => {\n  const originalObj = obj[propName]\n  const proxyObj = new Proxy(obj[propName], utils.stripProxyFromErrors(handler))\n\n  utils.replaceProperty(obj, propName, { value: proxyObj })\n  utils.redirectToString(proxyObj, originalObj)\n\n  return true\n}",replaceGetterWithProxy:"(obj, propName, handler) => {\n  const fn = Object.getOwnPropertyDescriptor(obj, propName).get\n  const fnStr = fn.toString() // special getter function string\n  const proxyObj = new Proxy(fn, utils.stripProxyFromErrors(handler))\n\n  utils.replaceProperty(obj, propName, { get: proxyObj })\n  utils.patchToString(proxyObj, fnStr)\n\n  return true\n}",replaceGetterSetter:"(obj, propName, handlerGetterSetter) => {\n  const ownPropertyDescriptor = Object.getOwnPropertyDescriptor(obj, propName)\n  const handler = { ...ownPropertyDescriptor }\n\n  if (handlerGetterSetter.get !== undefined) {\n    const nativeFn = ownPropertyDescriptor.get\n    handler.get = function() {\n      return handlerGetterSetter.get.call(this, nativeFn.bind(this))\n    }\n    utils.redirectToString(handler.get, nativeFn)\n  }\n\n  if (handlerGetterSetter.set !== undefined) {\n    const nativeFn = ownPropertyDescriptor.set\n    handler.set = function(newValue) {\n      handlerGetterSetter.set.call(this, newValue, nativeFn.bind(this))\n    }\n    utils.redirectToString(handler.set, nativeFn)\n  }\n\n  Object.defineProperty(obj, propName, handler)\n}",mockWithProxy:"(obj, propName, pseudoTarget, handler) => {\n  const proxyObj = new Proxy(pseudoTarget, utils.stripProxyFromErrors(handler))\n\n  utils.replaceProperty(obj, propName, { value: proxyObj })\n  utils.patchToString(proxyObj)\n\n  return true\n}",createProxy:"(pseudoTarget, handler) => {\n  const proxyObj = new Proxy(pseudoTarget, utils.stripProxyFromErrors(handler))\n  utils.patchToString(proxyObj)\n\n  return proxyObj\n}",splitObjPath:"objPath => ({\n  // Remove last dot entry (property) ==> `HTMLMediaElement.prototype`\n  objName: objPath.split('.').slice(0, -1).join('.'),\n  // Extract last dot entry ==> `canPlayType`\n  propName: objPath.split('.').slice(-1)[0]\n})",replaceObjPathWithProxy:"(objPath, handler) => {\n  const { objName, propName } = utils.splitObjPath(objPath)\n  const obj = eval(objName) // eslint-disable-line no-eval\n  return utils.replaceWithProxy(obj, propName, handler)\n}",execRecursively:"(obj = {}, typeFilter = [], fn) => {\n  function recurse(obj) {\n    for (const key in obj) {\n      if (obj[key] === undefined) {\n        continue\n      }\n      if (obj[key] && typeof obj[key] === 'object') {\n        recurse(obj[key])\n      } else {\n        if (obj[key] && typeFilter.includes(typeof obj[key])) {\n          fn.call(this, obj[key])\n        }\n      }\n    }\n  }\n  recurse(obj)\n  return obj\n}",stringifyFns:"(fnObj = { hello: () => 'world' }) => {\n  // Object.fromEntries() ponyfill (in 6 lines) - supported only in Node v12+, modern browsers are fine\n  // https://github.com/feross/fromentries\n  function fromEntries(iterable) {\n    return [...iterable].reduce((obj, [key, val]) => {\n      obj[key] = val\n      return obj\n    }, {})\n  }\n  return (Object.fromEntries || fromEntries)(\n    Object.entries(fnObj)\n      .filter(([key, value]) => typeof value === 'function')\n      .map(([key, value]) => [key, value.toString()]) // eslint-disable-line no-eval\n  )\n}",materializeFns:"(fnStrObj = { hello: \"() => 'world'\" }) => {\n  return Object.fromEntries(\n    Object.entries(fnStrObj).map(([key, value]) => {\n      if (value.startsWith('function')) {\n        // some trickery is needed to make oldschool functions work :-)\n        return [key, eval(`() => ${value}`)()] // eslint-disable-line no-eval\n      } else {\n        // arrow functions just work\n        return [key, eval(value)] // eslint-disable-line no-eval\n      }\n    })\n  )\n}",makeHandler:"() => ({\n  // Used by simple `navigator` getter evasions\n  getterValue: value => ({\n    apply(target, ctx, args) {\n      // Let's fetch the value first, to trigger and escalate potential errors\n      // Illegal invocations like `navigator.__proto__.vendor` will throw here\n      utils.cache.Reflect.apply(...arguments)\n      return value\n    }\n  })\n})",arrayEquals:"(array1, array2) => {\n  if (array1.length !== array2.length) {\n    return false\n  }\n  for (let i = 0; i < array1.length; ++i) {\n    if (array1[i] !== array2[i]) {\n      return false\n    }\n  }\n  return true\n}",memoize:"fn => {\n  const cache = []\n  return function(...args) {\n    if (!cache.some(c => utils.arrayEquals(c.key, args))) {\n      cache.push({ key: args, value: fn.apply(this, args) })\n    }\n    return cache.find(c => utils.arrayEquals(c.key, args)).value\n  }\n}"},_mainFunction:"(utils, { opts }) => {\n        if (!window.chrome) {\n          // Use the exact property descriptor found in headful Chrome\n          // fetch it via `Object.getOwnPropertyDescriptor(window, 'chrome')`\n          Object.defineProperty(window, 'chrome', {\n            writable: true,\n            enumerable: true,\n            configurable: false, // note!\n            value: {} // We'll extend that later\n          })\n        }\n\n        // That means we're running headful and don't need to mock anything\n        if ('loadTimes' in window.chrome) {\n          return // Nothing to do here\n        }\n\n        // Check that the Navigation Timing API v1 + v2 is available, we need that\n        if (\n          !window.performance ||\n          !window.performance.timing ||\n          !window.PerformancePaintTiming\n        ) {\n          return\n        }\n\n        const { performance } = window\n\n        // Some stuff is not available on about:blank as it requires a navigation to occur,\n        // let's harden the code to not fail then:\n        const ntEntryFallback = {\n          nextHopProtocol: 'h2',\n          type: 'other'\n        }\n\n        // The API exposes some funky info regarding the connection\n        const protocolInfo = {\n          get connectionInfo() {\n            const ntEntry =\n              performance.getEntriesByType('navigation')[0] || ntEntryFallback\n            return ntEntry.nextHopProtocol\n          },\n          get npnNegotiatedProtocol() {\n            // NPN is deprecated in favor of ALPN, but this implementation returns the\n            // HTTP/2 or HTTP2+QUIC/39 requests negotiated via ALPN.\n            const ntEntry =\n              performance.getEntriesByType('navigation')[0] || ntEntryFallback\n            return ['h2', 'hq'].includes(ntEntry.nextHopProtocol)\n              ? ntEntry.nextHopProtocol\n              : 'unknown'\n          },\n          get navigationType() {\n            const ntEntry =\n              performance.getEntriesByType('navigation')[0] || ntEntryFallback\n            return ntEntry.type\n          },\n          get wasAlternateProtocolAvailable() {\n            // The Alternate-Protocol header is deprecated in favor of Alt-Svc\n            // (https://www.mnot.net/blog/2016/03/09/alt-svc), so technically this\n            // should always return false.\n            return false\n          },\n          get wasFetchedViaSpdy() {\n            // SPDY is deprecated in favor of HTTP/2, but this implementation returns\n            // true for HTTP/2 or HTTP2+QUIC/39 as well.\n            const ntEntry =\n              performance.getEntriesByType('navigation')[0] || ntEntryFallback\n            return ['h2', 'hq'].includes(ntEntry.nextHopProtocol)\n          },\n          get wasNpnNegotiated() {\n            // NPN is deprecated in favor of ALPN, but this implementation returns true\n            // for HTTP/2 or HTTP2+QUIC/39 requests negotiated via ALPN.\n            const ntEntry =\n              performance.getEntriesByType('navigation')[0] || ntEntryFallback\n            return ['h2', 'hq'].includes(ntEntry.nextHopProtocol)\n          }\n        }\n\n        const { timing } = window.performance\n\n        // Truncate number to specific number of decimals, most of the `loadTimes` stuff has 3\n        function toFixed(num, fixed) {\n          var re = new RegExp('^-?\\\\d+(?:.\\\\d{0,' + (fixed || -1) + '})?')\n          return num.toString().match(re)[0]\n        }\n\n        const timingInfo = {\n          get firstPaintAfterLoadTime() {\n            // This was never actually implemented and always returns 0.\n            return 0\n          },\n          get requestTime() {\n            return timing.navigationStart / 1000\n          },\n          get startLoadTime() {\n            return timing.navigationStart / 1000\n          },\n          get commitLoadTime() {\n            return timing.responseStart / 1000\n          },\n          get finishDocumentLoadTime() {\n            return timing.domContentLoadedEventEnd / 1000\n          },\n          get finishLoadTime() {\n            return timing.loadEventEnd / 1000\n          },\n          get firstPaintTime() {\n            const fpEntry = performance.getEntriesByType('paint')[0] || {\n              startTime: timing.loadEventEnd / 1000 // Fallback if no navigation occured (`about:blank`)\n            }\n            return toFixed(\n              (fpEntry.startTime + performance.timeOrigin) / 1000,\n              3\n            )\n          }\n        }\n\n        window.chrome.loadTimes = function() {\n          return {\n            ...protocolInfo,\n            ...timingInfo\n          }\n        }\n        utils.patchToString(window.chrome.loadTimes)\n      }",_args:[{opts:{}}]}),(({_utilsFns:_utilsFns,_mainFunction:_mainFunction,_args:_args})=>{const utils=Object.fromEntries(Object.entries(_utilsFns).map((([key,value])=>[key,eval(value)])));utils.init(),eval(_mainFunction)(utils,..._args)})({_utilsFns:{init:"() => {\n  utils.preloadCache()\n}",stripProxyFromErrors:"(handler = {}) => {\n  const newHandler = {\n    setPrototypeOf: function (target, proto) {\n      if (proto === null)\n        throw new TypeError('Cannot convert object to primitive value')\n      if (Object.getPrototypeOf(target) === Object.getPrototypeOf(proto)) {\n        throw new TypeError('Cyclic __proto__ value')\n      }\n      return Reflect.setPrototypeOf(target, proto)\n    }\n  }\n  // We wrap each trap in the handler in a try/catch and modify the error stack if they throw\n  const traps = Object.getOwnPropertyNames(handler)\n  traps.forEach(trap => {\n    newHandler[trap] = function () {\n      try {\n        // Forward the call to the defined proxy handler\n        return handler[trap].apply(this, arguments || [])\n      } catch (err) {\n        // Stack traces differ per browser, we only support chromium based ones currently\n        if (!err || !err.stack || !err.stack.includes(`at `)) {\n          throw err\n        }\n\n        // When something throws within one of our traps the Proxy will show up in error stacks\n        // An earlier implementation of this code would simply strip lines with a blacklist,\n        // but it makes sense to be more surgical here and only remove lines related to our Proxy.\n        // We try to use a known \"anchor\" line for that and strip it with everything above it.\n        // If the anchor line cannot be found for some reason we fall back to our blacklist approach.\n\n        const stripWithBlacklist = (stack, stripFirstLine = true) => {\n          const blacklist = [\n            `at Reflect.${trap} `, // e.g. Reflect.get or Reflect.apply\n            `at Object.${trap} `, // e.g. Object.get or Object.apply\n            `at Object.newHandler.<computed> [as ${trap}] ` // caused by this very wrapper :-)\n          ]\n          return (\n            err.stack\n              .split('\\n')\n              // Always remove the first (file) line in the stack (guaranteed to be our proxy)\n              .filter((line, index) => !(index === 1 && stripFirstLine))\n              // Check if the line starts with one of our blacklisted strings\n              .filter(line => !blacklist.some(bl => line.trim().startsWith(bl)))\n              .join('\\n')\n          )\n        }\n\n        const stripWithAnchor = (stack, anchor) => {\n          const stackArr = stack.split('\\n')\n          anchor = anchor || `at Object.newHandler.<computed> [as ${trap}] ` // Known first Proxy line in chromium\n          const anchorIndex = stackArr.findIndex(line =>\n            line.trim().startsWith(anchor)\n          )\n          if (anchorIndex === -1) {\n            return false // 404, anchor not found\n          }\n          // Strip everything from the top until we reach the anchor line\n          // Note: We're keeping the 1st line (zero index) as it's unrelated (e.g. `TypeError`)\n          stackArr.splice(1, anchorIndex)\n          return stackArr.join('\\n')\n        }\n\n        // Special cases due to our nested toString proxies\n        err.stack = err.stack.replace(\n          'at Object.toString (',\n          'at Function.toString ('\n        )\n        if ((err.stack || '').includes('at Function.toString (')) {\n          err.stack = stripWithBlacklist(err.stack, false)\n          throw err\n        }\n\n        // Try using the anchor method, fallback to blacklist if necessary\n        err.stack = stripWithAnchor(err.stack) || stripWithBlacklist(err.stack)\n\n        throw err // Re-throw our now sanitized error\n      }\n    }\n  })\n  return newHandler\n}",stripErrorWithAnchor:"(err, anchor) => {\n  const stackArr = err.stack.split('\\n')\n  const anchorIndex = stackArr.findIndex(line => line.trim().startsWith(anchor))\n  if (anchorIndex === -1) {\n    return err // 404, anchor not found\n  }\n  // Strip everything from the top until we reach the anchor line (remove anchor line as well)\n  // Note: We're keeping the 1st line (zero index) as it's unrelated (e.g. `TypeError`)\n  stackArr.splice(1, anchorIndex)\n  err.stack = stackArr.join('\\n')\n  return err\n}",replaceProperty:"(obj, propName, descriptorOverrides = {}) => {\n  return Object.defineProperty(obj, propName, {\n    // Copy over the existing descriptors (writable, enumerable, configurable, etc)\n    ...(Object.getOwnPropertyDescriptor(obj, propName) || {}),\n    // Add our overrides (e.g. value, get())\n    ...descriptorOverrides\n  })\n}",preloadCache:"() => {\n  if (utils.cache) {\n    return\n  }\n  utils.cache = {\n    // Used in our proxies\n    Reflect: {\n      get: Reflect.get.bind(Reflect),\n      apply: Reflect.apply.bind(Reflect)\n    },\n    // Used in `makeNativeString`\n    nativeToStringStr: Function.toString + '' // => `function toString() { [native code] }`\n  }\n}",makeNativeString:"(name = '') => {\n  return utils.cache.nativeToStringStr.replace('toString', name || '')\n}",patchToString:"(obj, str = '') => {\n  const handler = {\n    apply: function (target, ctx) {\n      // This fixes e.g. `HTMLMediaElement.prototype.canPlayType.toString + \"\"`\n      if (ctx === Function.prototype.toString) {\n        return utils.makeNativeString('toString')\n      }\n      // `toString` targeted at our proxied Object detected\n      if (ctx === obj) {\n        // We either return the optional string verbatim or derive the most desired result automatically\n        return str || utils.makeNativeString(obj.name)\n      }\n      // Check if the toString protype of the context is the same as the global prototype,\n      // if not indicates that we are doing a check across different windows., e.g. the iframeWithdirect` test case\n      const hasSameProto = Object.getPrototypeOf(\n        Function.prototype.toString\n      ).isPrototypeOf(ctx.toString) // eslint-disable-line no-prototype-builtins\n      if (!hasSameProto) {\n        // Pass the call on to the local Function.prototype.toString instead\n        return ctx.toString()\n      }\n      return target.call(ctx)\n    }\n  }\n\n  const toStringProxy = new Proxy(\n    Function.prototype.toString,\n    utils.stripProxyFromErrors(handler)\n  )\n  utils.replaceProperty(Function.prototype, 'toString', {\n    value: toStringProxy\n  })\n}",patchToStringNested:"(obj = {}) => {\n  return utils.execRecursively(obj, ['function'], utils.patchToString)\n}",redirectToString:"(proxyObj, originalObj) => {\n  const handler = {\n    apply: function (target, ctx) {\n      // This fixes e.g. `HTMLMediaElement.prototype.canPlayType.toString + \"\"`\n      if (ctx === Function.prototype.toString) {\n        return utils.makeNativeString('toString')\n      }\n\n      // `toString` targeted at our proxied Object detected\n      if (ctx === proxyObj) {\n        const fallback = () =>\n          originalObj && originalObj.name\n            ? utils.makeNativeString(originalObj.name)\n            : utils.makeNativeString(proxyObj.name)\n\n        // Return the toString representation of our original object if possible\n        return originalObj + '' || fallback()\n      }\n\n      if (typeof ctx === 'undefined' || ctx === null) {\n        return target.call(ctx)\n      }\n\n      // Check if the toString protype of the context is the same as the global prototype,\n      // if not indicates that we are doing a check across different windows., e.g. the iframeWithdirect` test case\n      const hasSameProto = Object.getPrototypeOf(\n        Function.prototype.toString\n      ).isPrototypeOf(ctx.toString) // eslint-disable-line no-prototype-builtins\n      if (!hasSameProto) {\n        // Pass the call on to the local Function.prototype.toString instead\n        return ctx.toString()\n      }\n\n      return target.call(ctx)\n    }\n  }\n\n  const toStringProxy = new Proxy(\n    Function.prototype.toString,\n    utils.stripProxyFromErrors(handler)\n  )\n  utils.replaceProperty(Function.prototype, 'toString', {\n    value: toStringProxy\n  })\n}",replaceWithProxy:"(obj, propName, handler) => {\n  const originalObj = obj[propName]\n  const proxyObj = new Proxy(obj[propName], utils.stripProxyFromErrors(handler))\n\n  utils.replaceProperty(obj, propName, { value: proxyObj })\n  utils.redirectToString(proxyObj, originalObj)\n\n  return true\n}",replaceGetterWithProxy:"(obj, propName, handler) => {\n  const fn = Object.getOwnPropertyDescriptor(obj, propName).get\n  const fnStr = fn.toString() // special getter function string\n  const proxyObj = new Proxy(fn, utils.stripProxyFromErrors(handler))\n\n  utils.replaceProperty(obj, propName, { get: proxyObj })\n  utils.patchToString(proxyObj, fnStr)\n\n  return true\n}",replaceGetterSetter:"(obj, propName, handlerGetterSetter) => {\n  const ownPropertyDescriptor = Object.getOwnPropertyDescriptor(obj, propName)\n  const handler = { ...ownPropertyDescriptor }\n\n  if (handlerGetterSetter.get !== undefined) {\n    const nativeFn = ownPropertyDescriptor.get\n    handler.get = function() {\n      return handlerGetterSetter.get.call(this, nativeFn.bind(this))\n    }\n    utils.redirectToString(handler.get, nativeFn)\n  }\n\n  if (handlerGetterSetter.set !== undefined) {\n    const nativeFn = ownPropertyDescriptor.set\n    handler.set = function(newValue) {\n      handlerGetterSetter.set.call(this, newValue, nativeFn.bind(this))\n    }\n    utils.redirectToString(handler.set, nativeFn)\n  }\n\n  Object.defineProperty(obj, propName, handler)\n}",mockWithProxy:"(obj, propName, pseudoTarget, handler) => {\n  const proxyObj = new Proxy(pseudoTarget, utils.stripProxyFromErrors(handler))\n\n  utils.replaceProperty(obj, propName, { value: proxyObj })\n  utils.patchToString(proxyObj)\n\n  return true\n}",createProxy:"(pseudoTarget, handler) => {\n  const proxyObj = new Proxy(pseudoTarget, utils.stripProxyFromErrors(handler))\n  utils.patchToString(proxyObj)\n\n  return proxyObj\n}",splitObjPath:"objPath => ({\n  // Remove last dot entry (property) ==> `HTMLMediaElement.prototype`\n  objName: objPath.split('.').slice(0, -1).join('.'),\n  // Extract last dot entry ==> `canPlayType`\n  propName: objPath.split('.').slice(-1)[0]\n})",replaceObjPathWithProxy:"(objPath, handler) => {\n  const { objName, propName } = utils.splitObjPath(objPath)\n  const obj = eval(objName) // eslint-disable-line no-eval\n  return utils.replaceWithProxy(obj, propName, handler)\n}",execRecursively:"(obj = {}, typeFilter = [], fn) => {\n  function recurse(obj) {\n    for (const key in obj) {\n      if (obj[key] === undefined) {\n        continue\n      }\n      if (obj[key] && typeof obj[key] === 'object') {\n        recurse(obj[key])\n      } else {\n        if (obj[key] && typeFilter.includes(typeof obj[key])) {\n          fn.call(this, obj[key])\n        }\n      }\n    }\n  }\n  recurse(obj)\n  return obj\n}",stringifyFns:"(fnObj = { hello: () => 'world' }) => {\n  // Object.fromEntries() ponyfill (in 6 lines) - supported only in Node v12+, modern browsers are fine\n  // https://github.com/feross/fromentries\n  function fromEntries(iterable) {\n    return [...iterable].reduce((obj, [key, val]) => {\n      obj[key] = val\n      return obj\n    }, {})\n  }\n  return (Object.fromEntries || fromEntries)(\n    Object.entries(fnObj)\n      .filter(([key, value]) => typeof value === 'function')\n      .map(([key, value]) => [key, value.toString()]) // eslint-disable-line no-eval\n  )\n}",materializeFns:"(fnStrObj = { hello: \"() => 'world'\" }) => {\n  return Object.fromEntries(\n    Object.entries(fnStrObj).map(([key, value]) => {\n      if (value.startsWith('function')) {\n        // some trickery is needed to make oldschool functions work :-)\n        return [key, eval(`() => ${value}`)()] // eslint-disable-line no-eval\n      } else {\n        // arrow functions just work\n        return [key, eval(value)] // eslint-disable-line no-eval\n      }\n    })\n  )\n}",makeHandler:"() => ({\n  // Used by simple `navigator` getter evasions\n  getterValue: value => ({\n    apply(target, ctx, args) {\n      // Let's fetch the value first, to trigger and escalate potential errors\n      // Illegal invocations like `navigator.__proto__.vendor` will throw here\n      utils.cache.Reflect.apply(...arguments)\n      return value\n    }\n  })\n})",arrayEquals:"(array1, array2) => {\n  if (array1.length !== array2.length) {\n    return false\n  }\n  for (let i = 0; i < array1.length; ++i) {\n    if (array1[i] !== array2[i]) {\n      return false\n    }\n  }\n  return true\n}",memoize:"fn => {\n  const cache = []\n  return function(...args) {\n    if (!cache.some(c => utils.arrayEquals(c.key, args))) {\n      cache.push({ key: args, value: fn.apply(this, args) })\n    }\n    return cache.find(c => utils.arrayEquals(c.key, args)).value\n  }\n}"},_mainFunction:"(utils, { opts, STATIC_DATA }) => {\n        if (!window.chrome) {\n          // Use the exact property descriptor found in headful Chrome\n          // fetch it via `Object.getOwnPropertyDescriptor(window, 'chrome')`\n          Object.defineProperty(window, 'chrome', {\n            writable: true,\n            enumerable: true,\n            configurable: false, // note!\n            value: {} // We'll extend that later\n          })\n        }\n\n        // That means we're running headful and don't need to mock anything\n        const existsAlready = 'runtime' in window.chrome\n        // `chrome.runtime` is only exposed on secure origins\n        const isNotSecure = !window.location.protocol.startsWith('https')\n        if (existsAlready || (isNotSecure && !opts.runOnInsecureOrigins)) {\n          return // Nothing to do here\n        }\n\n        window.chrome.runtime = {\n          // There's a bunch of static data in that property which doesn't seem to change,\n          // we should periodically check for updates: `JSON.stringify(window.chrome.runtime, null, 2)`\n          ...STATIC_DATA,\n          // `chrome.runtime.id` is extension related and returns undefined in Chrome\n          get id() {\n            return undefined\n          },\n          // These two require more sophisticated mocks\n          connect: null,\n          sendMessage: null\n        }\n\n        const makeCustomRuntimeErrors = (preamble, method, extensionId) => ({\n          NoMatchingSignature: new TypeError(\n            preamble + `No matching signature.`\n          ),\n          MustSpecifyExtensionID: new TypeError(\n            preamble +\n              `${method} called from a webpage must specify an Extension ID (string) for its first argument.`\n          ),\n          InvalidExtensionID: new TypeError(\n            preamble + `Invalid extension id: '${extensionId}'`\n          )\n        })\n\n        // Valid Extension IDs are 32 characters in length and use the letter `a` to `p`:\n        // https://source.chromium.org/chromium/chromium/src/+/master:components/crx_file/id_util.cc;drc=14a055ccb17e8c8d5d437fe080faba4c6f07beac;l=90\n        const isValidExtensionID = str =>\n          str.length === 32 && str.toLowerCase().match(/^[a-p]+$/)\n\n        /** Mock `chrome.runtime.sendMessage` */\n        const sendMessageHandler = {\n          apply: function(target, ctx, args) {\n            const [extensionId, options, responseCallback] = args || []\n\n            // Define custom errors\n            const errorPreamble = `Error in invocation of runtime.sendMessage(optional string extensionId, any message, optional object options, optional function responseCallback): `\n            const Errors = makeCustomRuntimeErrors(\n              errorPreamble,\n              `chrome.runtime.sendMessage()`,\n              extensionId\n            )\n\n            // Check if the call signature looks ok\n            const noArguments = args.length === 0\n            const tooManyArguments = args.length > 4\n            const incorrectOptions = options && typeof options !== 'object'\n            const incorrectResponseCallback =\n              responseCallback && typeof responseCallback !== 'function'\n            if (\n              noArguments ||\n              tooManyArguments ||\n              incorrectOptions ||\n              incorrectResponseCallback\n            ) {\n              throw Errors.NoMatchingSignature\n            }\n\n            // At least 2 arguments are required before we even validate the extension ID\n            if (args.length < 2) {\n              throw Errors.MustSpecifyExtensionID\n            }\n\n            // Now let's make sure we got a string as extension ID\n            if (typeof extensionId !== 'string') {\n              throw Errors.NoMatchingSignature\n            }\n\n            if (!isValidExtensionID(extensionId)) {\n              throw Errors.InvalidExtensionID\n            }\n\n            return undefined // Normal behavior\n          }\n        }\n        utils.mockWithProxy(\n          window.chrome.runtime,\n          'sendMessage',\n          function sendMessage() {},\n          sendMessageHandler\n        )\n\n        /**\n         * Mock `chrome.runtime.connect`\n         *\n         * @see https://developer.chrome.com/apps/runtime#method-connect\n         */\n        const connectHandler = {\n          apply: function(target, ctx, args) {\n            const [extensionId, connectInfo] = args || []\n\n            // Define custom errors\n            const errorPreamble = `Error in invocation of runtime.connect(optional string extensionId, optional object connectInfo): `\n            const Errors = makeCustomRuntimeErrors(\n              errorPreamble,\n              `chrome.runtime.connect()`,\n              extensionId\n            )\n\n            // Behavior differs a bit from sendMessage:\n            const noArguments = args.length === 0\n            const emptyStringArgument = args.length === 1 && extensionId === ''\n            if (noArguments || emptyStringArgument) {\n              throw Errors.MustSpecifyExtensionID\n            }\n\n            const tooManyArguments = args.length > 2\n            const incorrectConnectInfoType =\n              connectInfo && typeof connectInfo !== 'object'\n\n            if (tooManyArguments || incorrectConnectInfoType) {\n              throw Errors.NoMatchingSignature\n            }\n\n            const extensionIdIsString = typeof extensionId === 'string'\n            if (extensionIdIsString && extensionId === '') {\n              throw Errors.MustSpecifyExtensionID\n            }\n            if (extensionIdIsString && !isValidExtensionID(extensionId)) {\n              throw Errors.InvalidExtensionID\n            }\n\n            // There's another edge-case here: extensionId is optional so we might find a connectInfo object as first param, which we need to validate\n            const validateConnectInfo = ci => {\n              // More than a first param connectInfo as been provided\n              if (args.length > 1) {\n                throw Errors.NoMatchingSignature\n              }\n              // An empty connectInfo has been provided\n              if (Object.keys(ci).length === 0) {\n                throw Errors.MustSpecifyExtensionID\n              }\n              // Loop over all connectInfo props an check them\n              Object.entries(ci).forEach(([k, v]) => {\n                const isExpected = ['name', 'includeTlsChannelId'].includes(k)\n                if (!isExpected) {\n                  throw new TypeError(\n                    errorPreamble + `Unexpected property: '${k}'.`\n                  )\n                }\n                const MismatchError = (propName, expected, found) =>\n                  TypeError(\n                    errorPreamble +\n                      `Error at property '${propName}': Invalid type: expected ${expected}, found ${found}.`\n                  )\n                if (k === 'name' && typeof v !== 'string') {\n                  throw MismatchError(k, 'string', typeof v)\n                }\n                if (k === 'includeTlsChannelId' && typeof v !== 'boolean') {\n                  throw MismatchError(k, 'boolean', typeof v)\n                }\n              })\n            }\n            if (typeof extensionId === 'object') {\n              validateConnectInfo(extensionId)\n              throw Errors.MustSpecifyExtensionID\n            }\n\n            // Unfortunately even when the connect fails Chrome will return an object with methods we need to mock as well\n            return utils.patchToStringNested(makeConnectResponse())\n          }\n        }\n        utils.mockWithProxy(\n          window.chrome.runtime,\n          'connect',\n          function connect() {},\n          connectHandler\n        )\n\n        function makeConnectResponse() {\n          const onSomething = () => ({\n            addListener: function addListener() {},\n            dispatch: function dispatch() {},\n            hasListener: function hasListener() {},\n            hasListeners: function hasListeners() {\n              return false\n            },\n            removeListener: function removeListener() {}\n          })\n\n          const response = {\n            name: '',\n            sender: undefined,\n            disconnect: function disconnect() {},\n            onDisconnect: onSomething(),\n            onMessage: onSomething(),\n            postMessage: function postMessage() {\n              if (!arguments.length) {\n                throw new TypeError(`Insufficient number of arguments.`)\n              }\n              throw new Error(`Attempting to use a disconnected port object`)\n            }\n          }\n          return response\n        }\n      }",_args:[{opts:{runOnInsecureOrigins:!1},STATIC_DATA:{OnInstalledReason:{CHROME_UPDATE:"chrome_update",INSTALL:"install",SHARED_MODULE_UPDATE:"shared_module_update",UPDATE:"update"},OnRestartRequiredReason:{APP_UPDATE:"app_update",OS_UPDATE:"os_update",PERIODIC:"periodic"},PlatformArch:{ARM:"arm",ARM64:"arm64",MIPS:"mips",MIPS64:"mips64",X86_32:"x86-32",X86_64:"x86-64"},PlatformNaclArch:{ARM:"arm",MIPS:"mips",MIPS64:"mips64",X86_32:"x86-32",X86_64:"x86-64"},PlatformOs:{ANDROID:"android",CROS:"cros",LINUX:"linux",MAC:"mac",OPENBSD:"openbsd",WIN:"win"},RequestUpdateCheckStatus:{NO_UPDATE:"no_update",THROTTLED:"throttled",UPDATE_AVAILABLE:"update_available"}}}]}),(({_utilsFns:_utilsFns,_mainFunction:_mainFunction,_args:_args})=>{const utils=Object.fromEntries(Object.entries(_utilsFns).map((([key,value])=>[key,eval(value)])));utils.init(),eval(_mainFunction)(utils,..._args)})({_utilsFns:{init:"() => {\n  utils.preloadCache()\n}",stripProxyFromErrors:"(handler = {}) => {\n  const newHandler = {\n    setPrototypeOf: function (target, proto) {\n      if (proto === null)\n        throw new TypeError('Cannot convert object to primitive value')\n      if (Object.getPrototypeOf(target) === Object.getPrototypeOf(proto)) {\n        throw new TypeError('Cyclic __proto__ value')\n      }\n      return Reflect.setPrototypeOf(target, proto)\n    }\n  }\n  // We wrap each trap in the handler in a try/catch and modify the error stack if they throw\n  const traps = Object.getOwnPropertyNames(handler)\n  traps.forEach(trap => {\n    newHandler[trap] = function () {\n      try {\n        // Forward the call to the defined proxy handler\n        return handler[trap].apply(this, arguments || [])\n      } catch (err) {\n        // Stack traces differ per browser, we only support chromium based ones currently\n        if (!err || !err.stack || !err.stack.includes(`at `)) {\n          throw err\n        }\n\n        // When something throws within one of our traps the Proxy will show up in error stacks\n        // An earlier implementation of this code would simply strip lines with a blacklist,\n        // but it makes sense to be more surgical here and only remove lines related to our Proxy.\n        // We try to use a known \"anchor\" line for that and strip it with everything above it.\n        // If the anchor line cannot be found for some reason we fall back to our blacklist approach.\n\n        const stripWithBlacklist = (stack, stripFirstLine = true) => {\n          const blacklist = [\n            `at Reflect.${trap} `, // e.g. Reflect.get or Reflect.apply\n            `at Object.${trap} `, // e.g. Object.get or Object.apply\n            `at Object.newHandler.<computed> [as ${trap}] ` // caused by this very wrapper :-)\n          ]\n          return (\n            err.stack\n              .split('\\n')\n              // Always remove the first (file) line in the stack (guaranteed to be our proxy)\n              .filter((line, index) => !(index === 1 && stripFirstLine))\n              // Check if the line starts with one of our blacklisted strings\n              .filter(line => !blacklist.some(bl => line.trim().startsWith(bl)))\n              .join('\\n')\n          )\n        }\n\n        const stripWithAnchor = (stack, anchor) => {\n          const stackArr = stack.split('\\n')\n          anchor = anchor || `at Object.newHandler.<computed> [as ${trap}] ` // Known first Proxy line in chromium\n          const anchorIndex = stackArr.findIndex(line =>\n            line.trim().startsWith(anchor)\n          )\n          if (anchorIndex === -1) {\n            return false // 404, anchor not found\n          }\n          // Strip everything from the top until we reach the anchor line\n          // Note: We're keeping the 1st line (zero index) as it's unrelated (e.g. `TypeError`)\n          stackArr.splice(1, anchorIndex)\n          return stackArr.join('\\n')\n        }\n\n        // Special cases due to our nested toString proxies\n        err.stack = err.stack.replace(\n          'at Object.toString (',\n          'at Function.toString ('\n        )\n        if ((err.stack || '').includes('at Function.toString (')) {\n          err.stack = stripWithBlacklist(err.stack, false)\n          throw err\n        }\n\n        // Try using the anchor method, fallback to blacklist if necessary\n        err.stack = stripWithAnchor(err.stack) || stripWithBlacklist(err.stack)\n\n        throw err // Re-throw our now sanitized error\n      }\n    }\n  })\n  return newHandler\n}",stripErrorWithAnchor:"(err, anchor) => {\n  const stackArr = err.stack.split('\\n')\n  const anchorIndex = stackArr.findIndex(line => line.trim().startsWith(anchor))\n  if (anchorIndex === -1) {\n    return err // 404, anchor not found\n  }\n  // Strip everything from the top until we reach the anchor line (remove anchor line as well)\n  // Note: We're keeping the 1st line (zero index) as it's unrelated (e.g. `TypeError`)\n  stackArr.splice(1, anchorIndex)\n  err.stack = stackArr.join('\\n')\n  return err\n}",replaceProperty:"(obj, propName, descriptorOverrides = {}) => {\n  return Object.defineProperty(obj, propName, {\n    // Copy over the existing descriptors (writable, enumerable, configurable, etc)\n    ...(Object.getOwnPropertyDescriptor(obj, propName) || {}),\n    // Add our overrides (e.g. value, get())\n    ...descriptorOverrides\n  })\n}",preloadCache:"() => {\n  if (utils.cache) {\n    return\n  }\n  utils.cache = {\n    // Used in our proxies\n    Reflect: {\n      get: Reflect.get.bind(Reflect),\n      apply: Reflect.apply.bind(Reflect)\n    },\n    // Used in `makeNativeString`\n    nativeToStringStr: Function.toString + '' // => `function toString() { [native code] }`\n  }\n}",makeNativeString:"(name = '') => {\n  return utils.cache.nativeToStringStr.replace('toString', name || '')\n}",patchToString:"(obj, str = '') => {\n  const handler = {\n    apply: function (target, ctx) {\n      // This fixes e.g. `HTMLMediaElement.prototype.canPlayType.toString + \"\"`\n      if (ctx === Function.prototype.toString) {\n        return utils.makeNativeString('toString')\n      }\n      // `toString` targeted at our proxied Object detected\n      if (ctx === obj) {\n        // We either return the optional string verbatim or derive the most desired result automatically\n        return str || utils.makeNativeString(obj.name)\n      }\n      // Check if the toString protype of the context is the same as the global prototype,\n      // if not indicates that we are doing a check across different windows., e.g. the iframeWithdirect` test case\n      const hasSameProto = Object.getPrototypeOf(\n        Function.prototype.toString\n      ).isPrototypeOf(ctx.toString) // eslint-disable-line no-prototype-builtins\n      if (!hasSameProto) {\n        // Pass the call on to the local Function.prototype.toString instead\n        return ctx.toString()\n      }\n      return target.call(ctx)\n    }\n  }\n\n  const toStringProxy = new Proxy(\n    Function.prototype.toString,\n    utils.stripProxyFromErrors(handler)\n  )\n  utils.replaceProperty(Function.prototype, 'toString', {\n    value: toStringProxy\n  })\n}",patchToStringNested:"(obj = {}) => {\n  return utils.execRecursively(obj, ['function'], utils.patchToString)\n}",redirectToString:"(proxyObj, originalObj) => {\n  const handler = {\n    apply: function (target, ctx) {\n      // This fixes e.g. `HTMLMediaElement.prototype.canPlayType.toString + \"\"`\n      if (ctx === Function.prototype.toString) {\n        return utils.makeNativeString('toString')\n      }\n\n      // `toString` targeted at our proxied Object detected\n      if (ctx === proxyObj) {\n        const fallback = () =>\n          originalObj && originalObj.name\n            ? utils.makeNativeString(originalObj.name)\n            : utils.makeNativeString(proxyObj.name)\n\n        // Return the toString representation of our original object if possible\n        return originalObj + '' || fallback()\n      }\n\n      if (typeof ctx === 'undefined' || ctx === null) {\n        return target.call(ctx)\n      }\n\n      // Check if the toString protype of the context is the same as the global prototype,\n      // if not indicates that we are doing a check across different windows., e.g. the iframeWithdirect` test case\n      const hasSameProto = Object.getPrototypeOf(\n        Function.prototype.toString\n      ).isPrototypeOf(ctx.toString) // eslint-disable-line no-prototype-builtins\n      if (!hasSameProto) {\n        // Pass the call on to the local Function.prototype.toString instead\n        return ctx.toString()\n      }\n\n      return target.call(ctx)\n    }\n  }\n\n  const toStringProxy = new Proxy(\n    Function.prototype.toString,\n    utils.stripProxyFromErrors(handler)\n  )\n  utils.replaceProperty(Function.prototype, 'toString', {\n    value: toStringProxy\n  })\n}",replaceWithProxy:"(obj, propName, handler) => {\n  const originalObj = obj[propName]\n  const proxyObj = new Proxy(obj[propName], utils.stripProxyFromErrors(handler))\n\n  utils.replaceProperty(obj, propName, { value: proxyObj })\n  utils.redirectToString(proxyObj, originalObj)\n\n  return true\n}",replaceGetterWithProxy:"(obj, propName, handler) => {\n  const fn = Object.getOwnPropertyDescriptor(obj, propName).get\n  const fnStr = fn.toString() // special getter function string\n  const proxyObj = new Proxy(fn, utils.stripProxyFromErrors(handler))\n\n  utils.replaceProperty(obj, propName, { get: proxyObj })\n  utils.patchToString(proxyObj, fnStr)\n\n  return true\n}",replaceGetterSetter:"(obj, propName, handlerGetterSetter) => {\n  const ownPropertyDescriptor = Object.getOwnPropertyDescriptor(obj, propName)\n  const handler = { ...ownPropertyDescriptor }\n\n  if (handlerGetterSetter.get !== undefined) {\n    const nativeFn = ownPropertyDescriptor.get\n    handler.get = function() {\n      return handlerGetterSetter.get.call(this, nativeFn.bind(this))\n    }\n    utils.redirectToString(handler.get, nativeFn)\n  }\n\n  if (handlerGetterSetter.set !== undefined) {\n    const nativeFn = ownPropertyDescriptor.set\n    handler.set = function(newValue) {\n      handlerGetterSetter.set.call(this, newValue, nativeFn.bind(this))\n    }\n    utils.redirectToString(handler.set, nativeFn)\n  }\n\n  Object.defineProperty(obj, propName, handler)\n}",mockWithProxy:"(obj, propName, pseudoTarget, handler) => {\n  const proxyObj = new Proxy(pseudoTarget, utils.stripProxyFromErrors(handler))\n\n  utils.replaceProperty(obj, propName, { value: proxyObj })\n  utils.patchToString(proxyObj)\n\n  return true\n}",createProxy:"(pseudoTarget, handler) => {\n  const proxyObj = new Proxy(pseudoTarget, utils.stripProxyFromErrors(handler))\n  utils.patchToString(proxyObj)\n\n  return proxyObj\n}",splitObjPath:"objPath => ({\n  // Remove last dot entry (property) ==> `HTMLMediaElement.prototype`\n  objName: objPath.split('.').slice(0, -1).join('.'),\n  // Extract last dot entry ==> `canPlayType`\n  propName: objPath.split('.').slice(-1)[0]\n})",replaceObjPathWithProxy:"(objPath, handler) => {\n  const { objName, propName } = utils.splitObjPath(objPath)\n  const obj = eval(objName) // eslint-disable-line no-eval\n  return utils.replaceWithProxy(obj, propName, handler)\n}",execRecursively:"(obj = {}, typeFilter = [], fn) => {\n  function recurse(obj) {\n    for (const key in obj) {\n      if (obj[key] === undefined) {\n        continue\n      }\n      if (obj[key] && typeof obj[key] === 'object') {\n        recurse(obj[key])\n      } else {\n        if (obj[key] && typeFilter.includes(typeof obj[key])) {\n          fn.call(this, obj[key])\n        }\n      }\n    }\n  }\n  recurse(obj)\n  return obj\n}",stringifyFns:"(fnObj = { hello: () => 'world' }) => {\n  // Object.fromEntries() ponyfill (in 6 lines) - supported only in Node v12+, modern browsers are fine\n  // https://github.com/feross/fromentries\n  function fromEntries(iterable) {\n    return [...iterable].reduce((obj, [key, val]) => {\n      obj[key] = val\n      return obj\n    }, {})\n  }\n  return (Object.fromEntries || fromEntries)(\n    Object.entries(fnObj)\n      .filter(([key, value]) => typeof value === 'function')\n      .map(([key, value]) => [key, value.toString()]) // eslint-disable-line no-eval\n  )\n}",materializeFns:"(fnStrObj = { hello: \"() => 'world'\" }) => {\n  return Object.fromEntries(\n    Object.entries(fnStrObj).map(([key, value]) => {\n      if (value.startsWith('function')) {\n        // some trickery is needed to make oldschool functions work :-)\n        return [key, eval(`() => ${value}`)()] // eslint-disable-line no-eval\n      } else {\n        // arrow functions just work\n        return [key, eval(value)] // eslint-disable-line no-eval\n      }\n    })\n  )\n}",makeHandler:"() => ({\n  // Used by simple `navigator` getter evasions\n  getterValue: value => ({\n    apply(target, ctx, args) {\n      // Let's fetch the value first, to trigger and escalate potential errors\n      // Illegal invocations like `navigator.__proto__.vendor` will throw here\n      utils.cache.Reflect.apply(...arguments)\n      return value\n    }\n  })\n})",arrayEquals:"(array1, array2) => {\n  if (array1.length !== array2.length) {\n    return false\n  }\n  for (let i = 0; i < array1.length; ++i) {\n    if (array1[i] !== array2[i]) {\n      return false\n    }\n  }\n  return true\n}",memoize:"fn => {\n  const cache = []\n  return function(...args) {\n    if (!cache.some(c => utils.arrayEquals(c.key, args))) {\n      cache.push({ key: args, value: fn.apply(this, args) })\n    }\n    return cache.find(c => utils.arrayEquals(c.key, args)).value\n  }\n}"},_mainFunction:"utils => {\n      /**\n       * Input might look funky, we need to normalize it so e.g. whitespace isn't an issue for our spoofing.\n       *\n       * @example\n       * video/webm; codecs=\"vp8, vorbis\"\n       * video/mp4; codecs=\"avc1.42E01E\"\n       * audio/x-m4a;\n       * audio/ogg; codecs=\"vorbis\"\n       * @param {String} arg\n       */\n      const parseInput = arg => {\n        const [mime, codecStr] = arg.trim().split(';')\n        let codecs = []\n        if (codecStr && codecStr.includes('codecs=\"')) {\n          codecs = codecStr\n            .trim()\n            .replace(`codecs=\"`, '')\n            .replace(`\"`, '')\n            .trim()\n            .split(',')\n            .filter(x => !!x)\n            .map(x => x.trim())\n        }\n        return {\n          mime,\n          codecStr,\n          codecs\n        }\n      }\n\n      const canPlayType = {\n        // Intercept certain requests\n        apply: function(target, ctx, args) {\n          if (!args || !args.length) {\n            return target.apply(ctx, args)\n          }\n          const { mime, codecs } = parseInput(args[0])\n          // This specific mp4 codec is missing in Chromium\n          if (mime === 'video/mp4') {\n            if (codecs.includes('avc1.42E01E')) {\n              return 'probably'\n            }\n          }\n          // This mimetype is only supported if no codecs are specified\n          if (mime === 'audio/x-m4a' && !codecs.length) {\n            return 'maybe'\n          }\n\n          // This mimetype is only supported if no codecs are specified\n          if (mime === 'audio/aac' && !codecs.length) {\n            return 'probably'\n          }\n          // Everything else as usual\n          return target.apply(ctx, args)\n        }\n      }\n\n      /* global HTMLMediaElement */\n      utils.replaceWithProxy(\n        HTMLMediaElement.prototype,\n        'canPlayType',\n        canPlayType\n      )\n    }",_args:[]}),(({_utilsFns:_utilsFns,_mainFunction:_mainFunction,_args:_args})=>{const utils=Object.fromEntries(Object.entries(_utilsFns).map((([key,value])=>[key,eval(value)])));utils.init(),eval(_mainFunction)(utils,..._args)})({_utilsFns:{init:"() => {\n  utils.preloadCache()\n}",stripProxyFromErrors:"(handler = {}) => {\n  const newHandler = {\n    setPrototypeOf: function (target, proto) {\n      if (proto === null)\n        throw new TypeError('Cannot convert object to primitive value')\n      if (Object.getPrototypeOf(target) === Object.getPrototypeOf(proto)) {\n        throw new TypeError('Cyclic __proto__ value')\n      }\n      return Reflect.setPrototypeOf(target, proto)\n    }\n  }\n  // We wrap each trap in the handler in a try/catch and modify the error stack if they throw\n  const traps = Object.getOwnPropertyNames(handler)\n  traps.forEach(trap => {\n    newHandler[trap] = function () {\n      try {\n        // Forward the call to the defined proxy handler\n        return handler[trap].apply(this, arguments || [])\n      } catch (err) {\n        // Stack traces differ per browser, we only support chromium based ones currently\n        if (!err || !err.stack || !err.stack.includes(`at `)) {\n          throw err\n        }\n\n        // When something throws within one of our traps the Proxy will show up in error stacks\n        // An earlier implementation of this code would simply strip lines with a blacklist,\n        // but it makes sense to be more surgical here and only remove lines related to our Proxy.\n        // We try to use a known \"anchor\" line for that and strip it with everything above it.\n        // If the anchor line cannot be found for some reason we fall back to our blacklist approach.\n\n        const stripWithBlacklist = (stack, stripFirstLine = true) => {\n          const blacklist = [\n            `at Reflect.${trap} `, // e.g. Reflect.get or Reflect.apply\n            `at Object.${trap} `, // e.g. Object.get or Object.apply\n            `at Object.newHandler.<computed> [as ${trap}] ` // caused by this very wrapper :-)\n          ]\n          return (\n            err.stack\n              .split('\\n')\n              // Always remove the first (file) line in the stack (guaranteed to be our proxy)\n              .filter((line, index) => !(index === 1 && stripFirstLine))\n              // Check if the line starts with one of our blacklisted strings\n              .filter(line => !blacklist.some(bl => line.trim().startsWith(bl)))\n              .join('\\n')\n          )\n        }\n\n        const stripWithAnchor = (stack, anchor) => {\n          const stackArr = stack.split('\\n')\n          anchor = anchor || `at Object.newHandler.<computed> [as ${trap}] ` // Known first Proxy line in chromium\n          const anchorIndex = stackArr.findIndex(line =>\n            line.trim().startsWith(anchor)\n          )\n          if (anchorIndex === -1) {\n            return false // 404, anchor not found\n          }\n          // Strip everything from the top until we reach the anchor line\n          // Note: We're keeping the 1st line (zero index) as it's unrelated (e.g. `TypeError`)\n          stackArr.splice(1, anchorIndex)\n          return stackArr.join('\\n')\n        }\n\n        // Special cases due to our nested toString proxies\n        err.stack = err.stack.replace(\n          'at Object.toString (',\n          'at Function.toString ('\n        )\n        if ((err.stack || '').includes('at Function.toString (')) {\n          err.stack = stripWithBlacklist(err.stack, false)\n          throw err\n        }\n\n        // Try using the anchor method, fallback to blacklist if necessary\n        err.stack = stripWithAnchor(err.stack) || stripWithBlacklist(err.stack)\n\n        throw err // Re-throw our now sanitized error\n      }\n    }\n  })\n  return newHandler\n}",stripErrorWithAnchor:"(err, anchor) => {\n  const stackArr = err.stack.split('\\n')\n  const anchorIndex = stackArr.findIndex(line => line.trim().startsWith(anchor))\n  if (anchorIndex === -1) {\n    return err // 404, anchor not found\n  }\n  // Strip everything from the top until we reach the anchor line (remove anchor line as well)\n  // Note: We're keeping the 1st line (zero index) as it's unrelated (e.g. `TypeError`)\n  stackArr.splice(1, anchorIndex)\n  err.stack = stackArr.join('\\n')\n  return err\n}",replaceProperty:"(obj, propName, descriptorOverrides = {}) => {\n  return Object.defineProperty(obj, propName, {\n    // Copy over the existing descriptors (writable, enumerable, configurable, etc)\n    ...(Object.getOwnPropertyDescriptor(obj, propName) || {}),\n    // Add our overrides (e.g. value, get())\n    ...descriptorOverrides\n  })\n}",preloadCache:"() => {\n  if (utils.cache) {\n    return\n  }\n  utils.cache = {\n    // Used in our proxies\n    Reflect: {\n      get: Reflect.get.bind(Reflect),\n      apply: Reflect.apply.bind(Reflect)\n    },\n    // Used in `makeNativeString`\n    nativeToStringStr: Function.toString + '' // => `function toString() { [native code] }`\n  }\n}",makeNativeString:"(name = '') => {\n  return utils.cache.nativeToStringStr.replace('toString', name || '')\n}",patchToString:"(obj, str = '') => {\n  const handler = {\n    apply: function (target, ctx) {\n      // This fixes e.g. `HTMLMediaElement.prototype.canPlayType.toString + \"\"`\n      if (ctx === Function.prototype.toString) {\n        return utils.makeNativeString('toString')\n      }\n      // `toString` targeted at our proxied Object detected\n      if (ctx === obj) {\n        // We either return the optional string verbatim or derive the most desired result automatically\n        return str || utils.makeNativeString(obj.name)\n      }\n      // Check if the toString protype of the context is the same as the global prototype,\n      // if not indicates that we are doing a check across different windows., e.g. the iframeWithdirect` test case\n      const hasSameProto = Object.getPrototypeOf(\n        Function.prototype.toString\n      ).isPrototypeOf(ctx.toString) // eslint-disable-line no-prototype-builtins\n      if (!hasSameProto) {\n        // Pass the call on to the local Function.prototype.toString instead\n        return ctx.toString()\n      }\n      return target.call(ctx)\n    }\n  }\n\n  const toStringProxy = new Proxy(\n    Function.prototype.toString,\n    utils.stripProxyFromErrors(handler)\n  )\n  utils.replaceProperty(Function.prototype, 'toString', {\n    value: toStringProxy\n  })\n}",patchToStringNested:"(obj = {}) => {\n  return utils.execRecursively(obj, ['function'], utils.patchToString)\n}",redirectToString:"(proxyObj, originalObj) => {\n  const handler = {\n    apply: function (target, ctx) {\n      // This fixes e.g. `HTMLMediaElement.prototype.canPlayType.toString + \"\"`\n      if (ctx === Function.prototype.toString) {\n        return utils.makeNativeString('toString')\n      }\n\n      // `toString` targeted at our proxied Object detected\n      if (ctx === proxyObj) {\n        const fallback = () =>\n          originalObj && originalObj.name\n            ? utils.makeNativeString(originalObj.name)\n            : utils.makeNativeString(proxyObj.name)\n\n        // Return the toString representation of our original object if possible\n        return originalObj + '' || fallback()\n      }\n\n      if (typeof ctx === 'undefined' || ctx === null) {\n        return target.call(ctx)\n      }\n\n      // Check if the toString protype of the context is the same as the global prototype,\n      // if not indicates that we are doing a check across different windows., e.g. the iframeWithdirect` test case\n      const hasSameProto = Object.getPrototypeOf(\n        Function.prototype.toString\n      ).isPrototypeOf(ctx.toString) // eslint-disable-line no-prototype-builtins\n      if (!hasSameProto) {\n        // Pass the call on to the local Function.prototype.toString instead\n        return ctx.toString()\n      }\n\n      return target.call(ctx)\n    }\n  }\n\n  const toStringProxy = new Proxy(\n    Function.prototype.toString,\n    utils.stripProxyFromErrors(handler)\n  )\n  utils.replaceProperty(Function.prototype, 'toString', {\n    value: toStringProxy\n  })\n}",replaceWithProxy:"(obj, propName, handler) => {\n  const originalObj = obj[propName]\n  const proxyObj = new Proxy(obj[propName], utils.stripProxyFromErrors(handler))\n\n  utils.replaceProperty(obj, propName, { value: proxyObj })\n  utils.redirectToString(proxyObj, originalObj)\n\n  return true\n}",replaceGetterWithProxy:"(obj, propName, handler) => {\n  const fn = Object.getOwnPropertyDescriptor(obj, propName).get\n  const fnStr = fn.toString() // special getter function string\n  const proxyObj = new Proxy(fn, utils.stripProxyFromErrors(handler))\n\n  utils.replaceProperty(obj, propName, { get: proxyObj })\n  utils.patchToString(proxyObj, fnStr)\n\n  return true\n}",replaceGetterSetter:"(obj, propName, handlerGetterSetter) => {\n  const ownPropertyDescriptor = Object.getOwnPropertyDescriptor(obj, propName)\n  const handler = { ...ownPropertyDescriptor }\n\n  if (handlerGetterSetter.get !== undefined) {\n    const nativeFn = ownPropertyDescriptor.get\n    handler.get = function() {\n      return handlerGetterSetter.get.call(this, nativeFn.bind(this))\n    }\n    utils.redirectToString(handler.get, nativeFn)\n  }\n\n  if (handlerGetterSetter.set !== undefined) {\n    const nativeFn = ownPropertyDescriptor.set\n    handler.set = function(newValue) {\n      handlerGetterSetter.set.call(this, newValue, nativeFn.bind(this))\n    }\n    utils.redirectToString(handler.set, nativeFn)\n  }\n\n  Object.defineProperty(obj, propName, handler)\n}",mockWithProxy:"(obj, propName, pseudoTarget, handler) => {\n  const proxyObj = new Proxy(pseudoTarget, utils.stripProxyFromErrors(handler))\n\n  utils.replaceProperty(obj, propName, { value: proxyObj })\n  utils.patchToString(proxyObj)\n\n  return true\n}",createProxy:"(pseudoTarget, handler) => {\n  const proxyObj = new Proxy(pseudoTarget, utils.stripProxyFromErrors(handler))\n  utils.patchToString(proxyObj)\n\n  return proxyObj\n}",splitObjPath:"objPath => ({\n  // Remove last dot entry (property) ==> `HTMLMediaElement.prototype`\n  objName: objPath.split('.').slice(0, -1).join('.'),\n  // Extract last dot entry ==> `canPlayType`\n  propName: objPath.split('.').slice(-1)[0]\n})",replaceObjPathWithProxy:"(objPath, handler) => {\n  const { objName, propName } = utils.splitObjPath(objPath)\n  const obj = eval(objName) // eslint-disable-line no-eval\n  return utils.replaceWithProxy(obj, propName, handler)\n}",execRecursively:"(obj = {}, typeFilter = [], fn) => {\n  function recurse(obj) {\n    for (const key in obj) {\n      if (obj[key] === undefined) {\n        continue\n      }\n      if (obj[key] && typeof obj[key] === 'object') {\n        recurse(obj[key])\n      } else {\n        if (obj[key] && typeFilter.includes(typeof obj[key])) {\n          fn.call(this, obj[key])\n        }\n      }\n    }\n  }\n  recurse(obj)\n  return obj\n}",stringifyFns:"(fnObj = { hello: () => 'world' }) => {\n  // Object.fromEntries() ponyfill (in 6 lines) - supported only in Node v12+, modern browsers are fine\n  // https://github.com/feross/fromentries\n  function fromEntries(iterable) {\n    return [...iterable].reduce((obj, [key, val]) => {\n      obj[key] = val\n      return obj\n    }, {})\n  }\n  return (Object.fromEntries || fromEntries)(\n    Object.entries(fnObj)\n      .filter(([key, value]) => typeof value === 'function')\n      .map(([key, value]) => [key, value.toString()]) // eslint-disable-line no-eval\n  )\n}",materializeFns:"(fnStrObj = { hello: \"() => 'world'\" }) => {\n  return Object.fromEntries(\n    Object.entries(fnStrObj).map(([key, value]) => {\n      if (value.startsWith('function')) {\n        // some trickery is needed to make oldschool functions work :-)\n        return [key, eval(`() => ${value}`)()] // eslint-disable-line no-eval\n      } else {\n        // arrow functions just work\n        return [key, eval(value)] // eslint-disable-line no-eval\n      }\n    })\n  )\n}",makeHandler:"() => ({\n  // Used by simple `navigator` getter evasions\n  getterValue: value => ({\n    apply(target, ctx, args) {\n      // Let's fetch the value first, to trigger and escalate potential errors\n      // Illegal invocations like `navigator.__proto__.vendor` will throw here\n      utils.cache.Reflect.apply(...arguments)\n      return value\n    }\n  })\n})",arrayEquals:"(array1, array2) => {\n  if (array1.length !== array2.length) {\n    return false\n  }\n  for (let i = 0; i < array1.length; ++i) {\n    if (array1[i] !== array2[i]) {\n      return false\n    }\n  }\n  return true\n}",memoize:"fn => {\n  const cache = []\n  return function(...args) {\n    if (!cache.some(c => utils.arrayEquals(c.key, args))) {\n      cache.push({ key: args, value: fn.apply(this, args) })\n    }\n    return cache.find(c => utils.arrayEquals(c.key, args)).value\n  }\n}"},_mainFunction:"(utils, { opts }) => {\n        utils.replaceGetterWithProxy(\n          Object.getPrototypeOf(navigator),\n          'hardwareConcurrency',\n          utils.makeHandler().getterValue(opts.hardwareConcurrency)\n        )\n      }",_args:[{opts:{hardwareConcurrency:4}}]}),(({_utilsFns:_utilsFns,_mainFunction:_mainFunction,_args:_args})=>{const utils=Object.fromEntries(Object.entries(_utilsFns).map((([key,value])=>[key,eval(value)])));utils.init(),eval(_mainFunction)(utils,..._args)})({_utilsFns:{init:"() => {\n  utils.preloadCache()\n}",stripProxyFromErrors:"(handler = {}) => {\n  const newHandler = {\n    setPrototypeOf: function (target, proto) {\n      if (proto === null)\n        throw new TypeError('Cannot convert object to primitive value')\n      if (Object.getPrototypeOf(target) === Object.getPrototypeOf(proto)) {\n        throw new TypeError('Cyclic __proto__ value')\n      }\n      return Reflect.setPrototypeOf(target, proto)\n    }\n  }\n  // We wrap each trap in the handler in a try/catch and modify the error stack if they throw\n  const traps = Object.getOwnPropertyNames(handler)\n  traps.forEach(trap => {\n    newHandler[trap] = function () {\n      try {\n        // Forward the call to the defined proxy handler\n        return handler[trap].apply(this, arguments || [])\n      } catch (err) {\n        // Stack traces differ per browser, we only support chromium based ones currently\n        if (!err || !err.stack || !err.stack.includes(`at `)) {\n          throw err\n        }\n\n        // When something throws within one of our traps the Proxy will show up in error stacks\n        // An earlier implementation of this code would simply strip lines with a blacklist,\n        // but it makes sense to be more surgical here and only remove lines related to our Proxy.\n        // We try to use a known \"anchor\" line for that and strip it with everything above it.\n        // If the anchor line cannot be found for some reason we fall back to our blacklist approach.\n\n        const stripWithBlacklist = (stack, stripFirstLine = true) => {\n          const blacklist = [\n            `at Reflect.${trap} `, // e.g. Reflect.get or Reflect.apply\n            `at Object.${trap} `, // e.g. Object.get or Object.apply\n            `at Object.newHandler.<computed> [as ${trap}] ` // caused by this very wrapper :-)\n          ]\n          return (\n            err.stack\n              .split('\\n')\n              // Always remove the first (file) line in the stack (guaranteed to be our proxy)\n              .filter((line, index) => !(index === 1 && stripFirstLine))\n              // Check if the line starts with one of our blacklisted strings\n              .filter(line => !blacklist.some(bl => line.trim().startsWith(bl)))\n              .join('\\n')\n          )\n        }\n\n        const stripWithAnchor = (stack, anchor) => {\n          const stackArr = stack.split('\\n')\n          anchor = anchor || `at Object.newHandler.<computed> [as ${trap}] ` // Known first Proxy line in chromium\n          const anchorIndex = stackArr.findIndex(line =>\n            line.trim().startsWith(anchor)\n          )\n          if (anchorIndex === -1) {\n            return false // 404, anchor not found\n          }\n          // Strip everything from the top until we reach the anchor line\n          // Note: We're keeping the 1st line (zero index) as it's unrelated (e.g. `TypeError`)\n          stackArr.splice(1, anchorIndex)\n          return stackArr.join('\\n')\n        }\n\n        // Special cases due to our nested toString proxies\n        err.stack = err.stack.replace(\n          'at Object.toString (',\n          'at Function.toString ('\n        )\n        if ((err.stack || '').includes('at Function.toString (')) {\n          err.stack = stripWithBlacklist(err.stack, false)\n          throw err\n        }\n\n        // Try using the anchor method, fallback to blacklist if necessary\n        err.stack = stripWithAnchor(err.stack) || stripWithBlacklist(err.stack)\n\n        throw err // Re-throw our now sanitized error\n      }\n    }\n  })\n  return newHandler\n}",stripErrorWithAnchor:"(err, anchor) => {\n  const stackArr = err.stack.split('\\n')\n  const anchorIndex = stackArr.findIndex(line => line.trim().startsWith(anchor))\n  if (anchorIndex === -1) {\n    return err // 404, anchor not found\n  }\n  // Strip everything from the top until we reach the anchor line (remove anchor line as well)\n  // Note: We're keeping the 1st line (zero index) as it's unrelated (e.g. `TypeError`)\n  stackArr.splice(1, anchorIndex)\n  err.stack = stackArr.join('\\n')\n  return err\n}",replaceProperty:"(obj, propName, descriptorOverrides = {}) => {\n  return Object.defineProperty(obj, propName, {\n    // Copy over the existing descriptors (writable, enumerable, configurable, etc)\n    ...(Object.getOwnPropertyDescriptor(obj, propName) || {}),\n    // Add our overrides (e.g. value, get())\n    ...descriptorOverrides\n  })\n}",preloadCache:"() => {\n  if (utils.cache) {\n    return\n  }\n  utils.cache = {\n    // Used in our proxies\n    Reflect: {\n      get: Reflect.get.bind(Reflect),\n      apply: Reflect.apply.bind(Reflect)\n    },\n    // Used in `makeNativeString`\n    nativeToStringStr: Function.toString + '' // => `function toString() { [native code] }`\n  }\n}",makeNativeString:"(name = '') => {\n  return utils.cache.nativeToStringStr.replace('toString', name || '')\n}",patchToString:"(obj, str = '') => {\n  const handler = {\n    apply: function (target, ctx) {\n      // This fixes e.g. `HTMLMediaElement.prototype.canPlayType.toString + \"\"`\n      if (ctx === Function.prototype.toString) {\n        return utils.makeNativeString('toString')\n      }\n      // `toString` targeted at our proxied Object detected\n      if (ctx === obj) {\n        // We either return the optional string verbatim or derive the most desired result automatically\n        return str || utils.makeNativeString(obj.name)\n      }\n      // Check if the toString protype of the context is the same as the global prototype,\n      // if not indicates that we are doing a check across different windows., e.g. the iframeWithdirect` test case\n      const hasSameProto = Object.getPrototypeOf(\n        Function.prototype.toString\n      ).isPrototypeOf(ctx.toString) // eslint-disable-line no-prototype-builtins\n      if (!hasSameProto) {\n        // Pass the call on to the local Function.prototype.toString instead\n        return ctx.toString()\n      }\n      return target.call(ctx)\n    }\n  }\n\n  const toStringProxy = new Proxy(\n    Function.prototype.toString,\n    utils.stripProxyFromErrors(handler)\n  )\n  utils.replaceProperty(Function.prototype, 'toString', {\n    value: toStringProxy\n  })\n}",patchToStringNested:"(obj = {}) => {\n  return utils.execRecursively(obj, ['function'], utils.patchToString)\n}",redirectToString:"(proxyObj, originalObj) => {\n  const handler = {\n    apply: function (target, ctx) {\n      // This fixes e.g. `HTMLMediaElement.prototype.canPlayType.toString + \"\"`\n      if (ctx === Function.prototype.toString) {\n        return utils.makeNativeString('toString')\n      }\n\n      // `toString` targeted at our proxied Object detected\n      if (ctx === proxyObj) {\n        const fallback = () =>\n          originalObj && originalObj.name\n            ? utils.makeNativeString(originalObj.name)\n            : utils.makeNativeString(proxyObj.name)\n\n        // Return the toString representation of our original object if possible\n        return originalObj + '' || fallback()\n      }\n\n      if (typeof ctx === 'undefined' || ctx === null) {\n        return target.call(ctx)\n      }\n\n      // Check if the toString protype of the context is the same as the global prototype,\n      // if not indicates that we are doing a check across different windows., e.g. the iframeWithdirect` test case\n      const hasSameProto = Object.getPrototypeOf(\n        Function.prototype.toString\n      ).isPrototypeOf(ctx.toString) // eslint-disable-line no-prototype-builtins\n      if (!hasSameProto) {\n        // Pass the call on to the local Function.prototype.toString instead\n        return ctx.toString()\n      }\n\n      return target.call(ctx)\n    }\n  }\n\n  const toStringProxy = new Proxy(\n    Function.prototype.toString,\n    utils.stripProxyFromErrors(handler)\n  )\n  utils.replaceProperty(Function.prototype, 'toString', {\n    value: toStringProxy\n  })\n}",replaceWithProxy:"(obj, propName, handler) => {\n  const originalObj = obj[propName]\n  const proxyObj = new Proxy(obj[propName], utils.stripProxyFromErrors(handler))\n\n  utils.replaceProperty(obj, propName, { value: proxyObj })\n  utils.redirectToString(proxyObj, originalObj)\n\n  return true\n}",replaceGetterWithProxy:"(obj, propName, handler) => {\n  const fn = Object.getOwnPropertyDescriptor(obj, propName).get\n  const fnStr = fn.toString() // special getter function string\n  const proxyObj = new Proxy(fn, utils.stripProxyFromErrors(handler))\n\n  utils.replaceProperty(obj, propName, { get: proxyObj })\n  utils.patchToString(proxyObj, fnStr)\n\n  return true\n}",replaceGetterSetter:"(obj, propName, handlerGetterSetter) => {\n  const ownPropertyDescriptor = Object.getOwnPropertyDescriptor(obj, propName)\n  const handler = { ...ownPropertyDescriptor }\n\n  if (handlerGetterSetter.get !== undefined) {\n    const nativeFn = ownPropertyDescriptor.get\n    handler.get = function() {\n      return handlerGetterSetter.get.call(this, nativeFn.bind(this))\n    }\n    utils.redirectToString(handler.get, nativeFn)\n  }\n\n  if (handlerGetterSetter.set !== undefined) {\n    const nativeFn = ownPropertyDescriptor.set\n    handler.set = function(newValue) {\n      handlerGetterSetter.set.call(this, newValue, nativeFn.bind(this))\n    }\n    utils.redirectToString(handler.set, nativeFn)\n  }\n\n  Object.defineProperty(obj, propName, handler)\n}",mockWithProxy:"(obj, propName, pseudoTarget, handler) => {\n  const proxyObj = new Proxy(pseudoTarget, utils.stripProxyFromErrors(handler))\n\n  utils.replaceProperty(obj, propName, { value: proxyObj })\n  utils.patchToString(proxyObj)\n\n  return true\n}",createProxy:"(pseudoTarget, handler) => {\n  const proxyObj = new Proxy(pseudoTarget, utils.stripProxyFromErrors(handler))\n  utils.patchToString(proxyObj)\n\n  return proxyObj\n}",splitObjPath:"objPath => ({\n  // Remove last dot entry (property) ==> `HTMLMediaElement.prototype`\n  objName: objPath.split('.').slice(0, -1).join('.'),\n  // Extract last dot entry ==> `canPlayType`\n  propName: objPath.split('.').slice(-1)[0]\n})",replaceObjPathWithProxy:"(objPath, handler) => {\n  const { objName, propName } = utils.splitObjPath(objPath)\n  const obj = eval(objName) // eslint-disable-line no-eval\n  return utils.replaceWithProxy(obj, propName, handler)\n}",execRecursively:"(obj = {}, typeFilter = [], fn) => {\n  function recurse(obj) {\n    for (const key in obj) {\n      if (obj[key] === undefined) {\n        continue\n      }\n      if (obj[key] && typeof obj[key] === 'object') {\n        recurse(obj[key])\n      } else {\n        if (obj[key] && typeFilter.includes(typeof obj[key])) {\n          fn.call(this, obj[key])\n        }\n      }\n    }\n  }\n  recurse(obj)\n  return obj\n}",stringifyFns:"(fnObj = { hello: () => 'world' }) => {\n  // Object.fromEntries() ponyfill (in 6 lines) - supported only in Node v12+, modern browsers are fine\n  // https://github.com/feross/fromentries\n  function fromEntries(iterable) {\n    return [...iterable].reduce((obj, [key, val]) => {\n      obj[key] = val\n      return obj\n    }, {})\n  }\n  return (Object.fromEntries || fromEntries)(\n    Object.entries(fnObj)\n      .filter(([key, value]) => typeof value === 'function')\n      .map(([key, value]) => [key, value.toString()]) // eslint-disable-line no-eval\n  )\n}",materializeFns:"(fnStrObj = { hello: \"() => 'world'\" }) => {\n  return Object.fromEntries(\n    Object.entries(fnStrObj).map(([key, value]) => {\n      if (value.startsWith('function')) {\n        // some trickery is needed to make oldschool functions work :-)\n        return [key, eval(`() => ${value}`)()] // eslint-disable-line no-eval\n      } else {\n        // arrow functions just work\n        return [key, eval(value)] // eslint-disable-line no-eval\n      }\n    })\n  )\n}",makeHandler:"() => ({\n  // Used by simple `navigator` getter evasions\n  getterValue: value => ({\n    apply(target, ctx, args) {\n      // Let's fetch the value first, to trigger and escalate potential errors\n      // Illegal invocations like `navigator.__proto__.vendor` will throw here\n      utils.cache.Reflect.apply(...arguments)\n      return value\n    }\n  })\n})",arrayEquals:"(array1, array2) => {\n  if (array1.length !== array2.length) {\n    return false\n  }\n  for (let i = 0; i < array1.length; ++i) {\n    if (array1[i] !== array2[i]) {\n      return false\n    }\n  }\n  return true\n}",memoize:"fn => {\n  const cache = []\n  return function(...args) {\n    if (!cache.some(c => utils.arrayEquals(c.key, args))) {\n      cache.push({ key: args, value: fn.apply(this, args) })\n    }\n    return cache.find(c => utils.arrayEquals(c.key, args)).value\n  }\n}"},_mainFunction:"(utils, { opts }) => {\n        const languages = opts.languages.length\n          ? opts.languages\n          : ['en-US', 'en']\n        utils.replaceGetterWithProxy(\n          Object.getPrototypeOf(navigator),\n          'languages',\n          utils.makeHandler().getterValue(Object.freeze([...languages]))\n        )\n      }",_args:[{opts:{languages:[]}}]}),(({_utilsFns:_utilsFns,_mainFunction:_mainFunction,_args:_args})=>{const utils=Object.fromEntries(Object.entries(_utilsFns).map((([key,value])=>[key,eval(value)])));utils.init(),eval(_mainFunction)(utils,..._args)})({_utilsFns:{init:"() => {\n  utils.preloadCache()\n}",stripProxyFromErrors:"(handler = {}) => {\n  const newHandler = {\n    setPrototypeOf: function (target, proto) {\n      if (proto === null)\n        throw new TypeError('Cannot convert object to primitive value')\n      if (Object.getPrototypeOf(target) === Object.getPrototypeOf(proto)) {\n        throw new TypeError('Cyclic __proto__ value')\n      }\n      return Reflect.setPrototypeOf(target, proto)\n    }\n  }\n  // We wrap each trap in the handler in a try/catch and modify the error stack if they throw\n  const traps = Object.getOwnPropertyNames(handler)\n  traps.forEach(trap => {\n    newHandler[trap] = function () {\n      try {\n        // Forward the call to the defined proxy handler\n        return handler[trap].apply(this, arguments || [])\n      } catch (err) {\n        // Stack traces differ per browser, we only support chromium based ones currently\n        if (!err || !err.stack || !err.stack.includes(`at `)) {\n          throw err\n        }\n\n        // When something throws within one of our traps the Proxy will show up in error stacks\n        // An earlier implementation of this code would simply strip lines with a blacklist,\n        // but it makes sense to be more surgical here and only remove lines related to our Proxy.\n        // We try to use a known \"anchor\" line for that and strip it with everything above it.\n        // If the anchor line cannot be found for some reason we fall back to our blacklist approach.\n\n        const stripWithBlacklist = (stack, stripFirstLine = true) => {\n          const blacklist = [\n            `at Reflect.${trap} `, // e.g. Reflect.get or Reflect.apply\n            `at Object.${trap} `, // e.g. Object.get or Object.apply\n            `at Object.newHandler.<computed> [as ${trap}] ` // caused by this very wrapper :-)\n          ]\n          return (\n            err.stack\n              .split('\\n')\n              // Always remove the first (file) line in the stack (guaranteed to be our proxy)\n              .filter((line, index) => !(index === 1 && stripFirstLine))\n              // Check if the line starts with one of our blacklisted strings\n              .filter(line => !blacklist.some(bl => line.trim().startsWith(bl)))\n              .join('\\n')\n          )\n        }\n\n        const stripWithAnchor = (stack, anchor) => {\n          const stackArr = stack.split('\\n')\n          anchor = anchor || `at Object.newHandler.<computed> [as ${trap}] ` // Known first Proxy line in chromium\n          const anchorIndex = stackArr.findIndex(line =>\n            line.trim().startsWith(anchor)\n          )\n          if (anchorIndex === -1) {\n            return false // 404, anchor not found\n          }\n          // Strip everything from the top until we reach the anchor line\n          // Note: We're keeping the 1st line (zero index) as it's unrelated (e.g. `TypeError`)\n          stackArr.splice(1, anchorIndex)\n          return stackArr.join('\\n')\n        }\n\n        // Special cases due to our nested toString proxies\n        err.stack = err.stack.replace(\n          'at Object.toString (',\n          'at Function.toString ('\n        )\n        if ((err.stack || '').includes('at Function.toString (')) {\n          err.stack = stripWithBlacklist(err.stack, false)\n          throw err\n        }\n\n        // Try using the anchor method, fallback to blacklist if necessary\n        err.stack = stripWithAnchor(err.stack) || stripWithBlacklist(err.stack)\n\n        throw err // Re-throw our now sanitized error\n      }\n    }\n  })\n  return newHandler\n}",stripErrorWithAnchor:"(err, anchor) => {\n  const stackArr = err.stack.split('\\n')\n  const anchorIndex = stackArr.findIndex(line => line.trim().startsWith(anchor))\n  if (anchorIndex === -1) {\n    return err // 404, anchor not found\n  }\n  // Strip everything from the top until we reach the anchor line (remove anchor line as well)\n  // Note: We're keeping the 1st line (zero index) as it's unrelated (e.g. `TypeError`)\n  stackArr.splice(1, anchorIndex)\n  err.stack = stackArr.join('\\n')\n  return err\n}",replaceProperty:"(obj, propName, descriptorOverrides = {}) => {\n  return Object.defineProperty(obj, propName, {\n    // Copy over the existing descriptors (writable, enumerable, configurable, etc)\n    ...(Object.getOwnPropertyDescriptor(obj, propName) || {}),\n    // Add our overrides (e.g. value, get())\n    ...descriptorOverrides\n  })\n}",preloadCache:"() => {\n  if (utils.cache) {\n    return\n  }\n  utils.cache = {\n    // Used in our proxies\n    Reflect: {\n      get: Reflect.get.bind(Reflect),\n      apply: Reflect.apply.bind(Reflect)\n    },\n    // Used in `makeNativeString`\n    nativeToStringStr: Function.toString + '' // => `function toString() { [native code] }`\n  }\n}",makeNativeString:"(name = '') => {\n  return utils.cache.nativeToStringStr.replace('toString', name || '')\n}",patchToString:"(obj, str = '') => {\n  const handler = {\n    apply: function (target, ctx) {\n      // This fixes e.g. `HTMLMediaElement.prototype.canPlayType.toString + \"\"`\n      if (ctx === Function.prototype.toString) {\n        return utils.makeNativeString('toString')\n      }\n      // `toString` targeted at our proxied Object detected\n      if (ctx === obj) {\n        // We either return the optional string verbatim or derive the most desired result automatically\n        return str || utils.makeNativeString(obj.name)\n      }\n      // Check if the toString protype of the context is the same as the global prototype,\n      // if not indicates that we are doing a check across different windows., e.g. the iframeWithdirect` test case\n      const hasSameProto = Object.getPrototypeOf(\n        Function.prototype.toString\n      ).isPrototypeOf(ctx.toString) // eslint-disable-line no-prototype-builtins\n      if (!hasSameProto) {\n        // Pass the call on to the local Function.prototype.toString instead\n        return ctx.toString()\n      }\n      return target.call(ctx)\n    }\n  }\n\n  const toStringProxy = new Proxy(\n    Function.prototype.toString,\n    utils.stripProxyFromErrors(handler)\n  )\n  utils.replaceProperty(Function.prototype, 'toString', {\n    value: toStringProxy\n  })\n}",patchToStringNested:"(obj = {}) => {\n  return utils.execRecursively(obj, ['function'], utils.patchToString)\n}",redirectToString:"(proxyObj, originalObj) => {\n  const handler = {\n    apply: function (target, ctx) {\n      // This fixes e.g. `HTMLMediaElement.prototype.canPlayType.toString + \"\"`\n      if (ctx === Function.prototype.toString) {\n        return utils.makeNativeString('toString')\n      }\n\n      // `toString` targeted at our proxied Object detected\n      if (ctx === proxyObj) {\n        const fallback = () =>\n          originalObj && originalObj.name\n            ? utils.makeNativeString(originalObj.name)\n            : utils.makeNativeString(proxyObj.name)\n\n        // Return the toString representation of our original object if possible\n        return originalObj + '' || fallback()\n      }\n\n      if (typeof ctx === 'undefined' || ctx === null) {\n        return target.call(ctx)\n      }\n\n      // Check if the toString protype of the context is the same as the global prototype,\n      // if not indicates that we are doing a check across different windows., e.g. the iframeWithdirect` test case\n      const hasSameProto = Object.getPrototypeOf(\n        Function.prototype.toString\n      ).isPrototypeOf(ctx.toString) // eslint-disable-line no-prototype-builtins\n      if (!hasSameProto) {\n        // Pass the call on to the local Function.prototype.toString instead\n        return ctx.toString()\n      }\n\n      return target.call(ctx)\n    }\n  }\n\n  const toStringProxy = new Proxy(\n    Function.prototype.toString,\n    utils.stripProxyFromErrors(handler)\n  )\n  utils.replaceProperty(Function.prototype, 'toString', {\n    value: toStringProxy\n  })\n}",replaceWithProxy:"(obj, propName, handler) => {\n  const originalObj = obj[propName]\n  const proxyObj = new Proxy(obj[propName], utils.stripProxyFromErrors(handler))\n\n  utils.replaceProperty(obj, propName, { value: proxyObj })\n  utils.redirectToString(proxyObj, originalObj)\n\n  return true\n}",replaceGetterWithProxy:"(obj, propName, handler) => {\n  const fn = Object.getOwnPropertyDescriptor(obj, propName).get\n  const fnStr = fn.toString() // special getter function string\n  const proxyObj = new Proxy(fn, utils.stripProxyFromErrors(handler))\n\n  utils.replaceProperty(obj, propName, { get: proxyObj })\n  utils.patchToString(proxyObj, fnStr)\n\n  return true\n}",replaceGetterSetter:"(obj, propName, handlerGetterSetter) => {\n  const ownPropertyDescriptor = Object.getOwnPropertyDescriptor(obj, propName)\n  const handler = { ...ownPropertyDescriptor }\n\n  if (handlerGetterSetter.get !== undefined) {\n    const nativeFn = ownPropertyDescriptor.get\n    handler.get = function() {\n      return handlerGetterSetter.get.call(this, nativeFn.bind(this))\n    }\n    utils.redirectToString(handler.get, nativeFn)\n  }\n\n  if (handlerGetterSetter.set !== undefined) {\n    const nativeFn = ownPropertyDescriptor.set\n    handler.set = function(newValue) {\n      handlerGetterSetter.set.call(this, newValue, nativeFn.bind(this))\n    }\n    utils.redirectToString(handler.set, nativeFn)\n  }\n\n  Object.defineProperty(obj, propName, handler)\n}",mockWithProxy:"(obj, propName, pseudoTarget, handler) => {\n  const proxyObj = new Proxy(pseudoTarget, utils.stripProxyFromErrors(handler))\n\n  utils.replaceProperty(obj, propName, { value: proxyObj })\n  utils.patchToString(proxyObj)\n\n  return true\n}",createProxy:"(pseudoTarget, handler) => {\n  const proxyObj = new Proxy(pseudoTarget, utils.stripProxyFromErrors(handler))\n  utils.patchToString(proxyObj)\n\n  return proxyObj\n}",splitObjPath:"objPath => ({\n  // Remove last dot entry (property) ==> `HTMLMediaElement.prototype`\n  objName: objPath.split('.').slice(0, -1).join('.'),\n  // Extract last dot entry ==> `canPlayType`\n  propName: objPath.split('.').slice(-1)[0]\n})",replaceObjPathWithProxy:"(objPath, handler) => {\n  const { objName, propName } = utils.splitObjPath(objPath)\n  const obj = eval(objName) // eslint-disable-line no-eval\n  return utils.replaceWithProxy(obj, propName, handler)\n}",execRecursively:"(obj = {}, typeFilter = [], fn) => {\n  function recurse(obj) {\n    for (const key in obj) {\n      if (obj[key] === undefined) {\n        continue\n      }\n      if (obj[key] && typeof obj[key] === 'object') {\n        recurse(obj[key])\n      } else {\n        if (obj[key] && typeFilter.includes(typeof obj[key])) {\n          fn.call(this, obj[key])\n        }\n      }\n    }\n  }\n  recurse(obj)\n  return obj\n}",stringifyFns:"(fnObj = { hello: () => 'world' }) => {\n  // Object.fromEntries() ponyfill (in 6 lines) - supported only in Node v12+, modern browsers are fine\n  // https://github.com/feross/fromentries\n  function fromEntries(iterable) {\n    return [...iterable].reduce((obj, [key, val]) => {\n      obj[key] = val\n      return obj\n    }, {})\n  }\n  return (Object.fromEntries || fromEntries)(\n    Object.entries(fnObj)\n      .filter(([key, value]) => typeof value === 'function')\n      .map(([key, value]) => [key, value.toString()]) // eslint-disable-line no-eval\n  )\n}",materializeFns:"(fnStrObj = { hello: \"() => 'world'\" }) => {\n  return Object.fromEntries(\n    Object.entries(fnStrObj).map(([key, value]) => {\n      if (value.startsWith('function')) {\n        // some trickery is needed to make oldschool functions work :-)\n        return [key, eval(`() => ${value}`)()] // eslint-disable-line no-eval\n      } else {\n        // arrow functions just work\n        return [key, eval(value)] // eslint-disable-line no-eval\n      }\n    })\n  )\n}",makeHandler:"() => ({\n  // Used by simple `navigator` getter evasions\n  getterValue: value => ({\n    apply(target, ctx, args) {\n      // Let's fetch the value first, to trigger and escalate potential errors\n      // Illegal invocations like `navigator.__proto__.vendor` will throw here\n      utils.cache.Reflect.apply(...arguments)\n      return value\n    }\n  })\n})",arrayEquals:"(array1, array2) => {\n  if (array1.length !== array2.length) {\n    return false\n  }\n  for (let i = 0; i < array1.length; ++i) {\n    if (array1[i] !== array2[i]) {\n      return false\n    }\n  }\n  return true\n}",memoize:"fn => {\n  const cache = []\n  return function(...args) {\n    if (!cache.some(c => utils.arrayEquals(c.key, args))) {\n      cache.push({ key: args, value: fn.apply(this, args) })\n    }\n    return cache.find(c => utils.arrayEquals(c.key, args)).value\n  }\n}"},_mainFunction:"(utils, opts) => {\n      const isSecure = document.location.protocol.startsWith('https')\n\n      // In headful on secure origins the permission should be \"default\", not \"denied\"\n      if (isSecure) {\n        utils.replaceGetterWithProxy(Notification, 'permission', {\n          apply() {\n            return 'default'\n          }\n        })\n      }\n\n      // Another weird behavior:\n      // On insecure origins in headful the state is \"denied\",\n      // whereas in headless it's \"prompt\"\n      if (!isSecure) {\n        const handler = {\n          apply(target, ctx, args) {\n            const param = (args || [])[0]\n\n            const isNotifications =\n              param && param.name && param.name === 'notifications'\n            if (!isNotifications) {\n              return utils.cache.Reflect.apply(...arguments)\n            }\n\n            return Promise.resolve(\n              Object.setPrototypeOf(\n                {\n                  state: 'denied',\n                  onchange: null\n                },\n                PermissionStatus.prototype\n              )\n            )\n          }\n        }\n        // Note: Don't use `Object.getPrototypeOf` here\n        utils.replaceWithProxy(Permissions.prototype, 'query', handler)\n      }\n    }",_args:[{}]}),(({_utilsFns:_utilsFns,_mainFunction:_mainFunction,_args:_args})=>{const utils=Object.fromEntries(Object.entries(_utilsFns).map((([key,value])=>[key,eval(value)])));utils.init(),eval(_mainFunction)(utils,..._args)})({_utilsFns:{init:"() => {\n  utils.preloadCache()\n}",stripProxyFromErrors:"(handler = {}) => {\n  const newHandler = {\n    setPrototypeOf: function (target, proto) {\n      if (proto === null)\n        throw new TypeError('Cannot convert object to primitive value')\n      if (Object.getPrototypeOf(target) === Object.getPrototypeOf(proto)) {\n        throw new TypeError('Cyclic __proto__ value')\n      }\n      return Reflect.setPrototypeOf(target, proto)\n    }\n  }\n  // We wrap each trap in the handler in a try/catch and modify the error stack if they throw\n  const traps = Object.getOwnPropertyNames(handler)\n  traps.forEach(trap => {\n    newHandler[trap] = function () {\n      try {\n        // Forward the call to the defined proxy handler\n        return handler[trap].apply(this, arguments || [])\n      } catch (err) {\n        // Stack traces differ per browser, we only support chromium based ones currently\n        if (!err || !err.stack || !err.stack.includes(`at `)) {\n          throw err\n        }\n\n        // When something throws within one of our traps the Proxy will show up in error stacks\n        // An earlier implementation of this code would simply strip lines with a blacklist,\n        // but it makes sense to be more surgical here and only remove lines related to our Proxy.\n        // We try to use a known \"anchor\" line for that and strip it with everything above it.\n        // If the anchor line cannot be found for some reason we fall back to our blacklist approach.\n\n        const stripWithBlacklist = (stack, stripFirstLine = true) => {\n          const blacklist = [\n            `at Reflect.${trap} `, // e.g. Reflect.get or Reflect.apply\n            `at Object.${trap} `, // e.g. Object.get or Object.apply\n            `at Object.newHandler.<computed> [as ${trap}] ` // caused by this very wrapper :-)\n          ]\n          return (\n            err.stack\n              .split('\\n')\n              // Always remove the first (file) line in the stack (guaranteed to be our proxy)\n              .filter((line, index) => !(index === 1 && stripFirstLine))\n              // Check if the line starts with one of our blacklisted strings\n              .filter(line => !blacklist.some(bl => line.trim().startsWith(bl)))\n              .join('\\n')\n          )\n        }\n\n        const stripWithAnchor = (stack, anchor) => {\n          const stackArr = stack.split('\\n')\n          anchor = anchor || `at Object.newHandler.<computed> [as ${trap}] ` // Known first Proxy line in chromium\n          const anchorIndex = stackArr.findIndex(line =>\n            line.trim().startsWith(anchor)\n          )\n          if (anchorIndex === -1) {\n            return false // 404, anchor not found\n          }\n          // Strip everything from the top until we reach the anchor line\n          // Note: We're keeping the 1st line (zero index) as it's unrelated (e.g. `TypeError`)\n          stackArr.splice(1, anchorIndex)\n          return stackArr.join('\\n')\n        }\n\n        // Special cases due to our nested toString proxies\n        err.stack = err.stack.replace(\n          'at Object.toString (',\n          'at Function.toString ('\n        )\n        if ((err.stack || '').includes('at Function.toString (')) {\n          err.stack = stripWithBlacklist(err.stack, false)\n          throw err\n        }\n\n        // Try using the anchor method, fallback to blacklist if necessary\n        err.stack = stripWithAnchor(err.stack) || stripWithBlacklist(err.stack)\n\n        throw err // Re-throw our now sanitized error\n      }\n    }\n  })\n  return newHandler\n}",stripErrorWithAnchor:"(err, anchor) => {\n  const stackArr = err.stack.split('\\n')\n  const anchorIndex = stackArr.findIndex(line => line.trim().startsWith(anchor))\n  if (anchorIndex === -1) {\n    return err // 404, anchor not found\n  }\n  // Strip everything from the top until we reach the anchor line (remove anchor line as well)\n  // Note: We're keeping the 1st line (zero index) as it's unrelated (e.g. `TypeError`)\n  stackArr.splice(1, anchorIndex)\n  err.stack = stackArr.join('\\n')\n  return err\n}",replaceProperty:"(obj, propName, descriptorOverrides = {}) => {\n  return Object.defineProperty(obj, propName, {\n    // Copy over the existing descriptors (writable, enumerable, configurable, etc)\n    ...(Object.getOwnPropertyDescriptor(obj, propName) || {}),\n    // Add our overrides (e.g. value, get())\n    ...descriptorOverrides\n  })\n}",preloadCache:"() => {\n  if (utils.cache) {\n    return\n  }\n  utils.cache = {\n    // Used in our proxies\n    Reflect: {\n      get: Reflect.get.bind(Reflect),\n      apply: Reflect.apply.bind(Reflect)\n    },\n    // Used in `makeNativeString`\n    nativeToStringStr: Function.toString + '' // => `function toString() { [native code] }`\n  }\n}",makeNativeString:"(name = '') => {\n  return utils.cache.nativeToStringStr.replace('toString', name || '')\n}",patchToString:"(obj, str = '') => {\n  const handler = {\n    apply: function (target, ctx) {\n      // This fixes e.g. `HTMLMediaElement.prototype.canPlayType.toString + \"\"`\n      if (ctx === Function.prototype.toString) {\n        return utils.makeNativeString('toString')\n      }\n      // `toString` targeted at our proxied Object detected\n      if (ctx === obj) {\n        // We either return the optional string verbatim or derive the most desired result automatically\n        return str || utils.makeNativeString(obj.name)\n      }\n      // Check if the toString protype of the context is the same as the global prototype,\n      // if not indicates that we are doing a check across different windows., e.g. the iframeWithdirect` test case\n      const hasSameProto = Object.getPrototypeOf(\n        Function.prototype.toString\n      ).isPrototypeOf(ctx.toString) // eslint-disable-line no-prototype-builtins\n      if (!hasSameProto) {\n        // Pass the call on to the local Function.prototype.toString instead\n        return ctx.toString()\n      }\n      return target.call(ctx)\n    }\n  }\n\n  const toStringProxy = new Proxy(\n    Function.prototype.toString,\n    utils.stripProxyFromErrors(handler)\n  )\n  utils.replaceProperty(Function.prototype, 'toString', {\n    value: toStringProxy\n  })\n}",patchToStringNested:"(obj = {}) => {\n  return utils.execRecursively(obj, ['function'], utils.patchToString)\n}",redirectToString:"(proxyObj, originalObj) => {\n  const handler = {\n    apply: function (target, ctx) {\n      // This fixes e.g. `HTMLMediaElement.prototype.canPlayType.toString + \"\"`\n      if (ctx === Function.prototype.toString) {\n        return utils.makeNativeString('toString')\n      }\n\n      // `toString` targeted at our proxied Object detected\n      if (ctx === proxyObj) {\n        const fallback = () =>\n          originalObj && originalObj.name\n            ? utils.makeNativeString(originalObj.name)\n            : utils.makeNativeString(proxyObj.name)\n\n        // Return the toString representation of our original object if possible\n        return originalObj + '' || fallback()\n      }\n\n      if (typeof ctx === 'undefined' || ctx === null) {\n        return target.call(ctx)\n      }\n\n      // Check if the toString protype of the context is the same as the global prototype,\n      // if not indicates that we are doing a check across different windows., e.g. the iframeWithdirect` test case\n      const hasSameProto = Object.getPrototypeOf(\n        Function.prototype.toString\n      ).isPrototypeOf(ctx.toString) // eslint-disable-line no-prototype-builtins\n      if (!hasSameProto) {\n        // Pass the call on to the local Function.prototype.toString instead\n        return ctx.toString()\n      }\n\n      return target.call(ctx)\n    }\n  }\n\n  const toStringProxy = new Proxy(\n    Function.prototype.toString,\n    utils.stripProxyFromErrors(handler)\n  )\n  utils.replaceProperty(Function.prototype, 'toString', {\n    value: toStringProxy\n  })\n}",replaceWithProxy:"(obj, propName, handler) => {\n  const originalObj = obj[propName]\n  const proxyObj = new Proxy(obj[propName], utils.stripProxyFromErrors(handler))\n\n  utils.replaceProperty(obj, propName, { value: proxyObj })\n  utils.redirectToString(proxyObj, originalObj)\n\n  return true\n}",replaceGetterWithProxy:"(obj, propName, handler) => {\n  const fn = Object.getOwnPropertyDescriptor(obj, propName).get\n  const fnStr = fn.toString() // special getter function string\n  const proxyObj = new Proxy(fn, utils.stripProxyFromErrors(handler))\n\n  utils.replaceProperty(obj, propName, { get: proxyObj })\n  utils.patchToString(proxyObj, fnStr)\n\n  return true\n}",replaceGetterSetter:"(obj, propName, handlerGetterSetter) => {\n  const ownPropertyDescriptor = Object.getOwnPropertyDescriptor(obj, propName)\n  const handler = { ...ownPropertyDescriptor }\n\n  if (handlerGetterSetter.get !== undefined) {\n    const nativeFn = ownPropertyDescriptor.get\n    handler.get = function() {\n      return handlerGetterSetter.get.call(this, nativeFn.bind(this))\n    }\n    utils.redirectToString(handler.get, nativeFn)\n  }\n\n  if (handlerGetterSetter.set !== undefined) {\n    const nativeFn = ownPropertyDescriptor.set\n    handler.set = function(newValue) {\n      handlerGetterSetter.set.call(this, newValue, nativeFn.bind(this))\n    }\n    utils.redirectToString(handler.set, nativeFn)\n  }\n\n  Object.defineProperty(obj, propName, handler)\n}",mockWithProxy:"(obj, propName, pseudoTarget, handler) => {\n  const proxyObj = new Proxy(pseudoTarget, utils.stripProxyFromErrors(handler))\n\n  utils.replaceProperty(obj, propName, { value: proxyObj })\n  utils.patchToString(proxyObj)\n\n  return true\n}",createProxy:"(pseudoTarget, handler) => {\n  const proxyObj = new Proxy(pseudoTarget, utils.stripProxyFromErrors(handler))\n  utils.patchToString(proxyObj)\n\n  return proxyObj\n}",splitObjPath:"objPath => ({\n  // Remove last dot entry (property) ==> `HTMLMediaElement.prototype`\n  objName: objPath.split('.').slice(0, -1).join('.'),\n  // Extract last dot entry ==> `canPlayType`\n  propName: objPath.split('.').slice(-1)[0]\n})",replaceObjPathWithProxy:"(objPath, handler) => {\n  const { objName, propName } = utils.splitObjPath(objPath)\n  const obj = eval(objName) // eslint-disable-line no-eval\n  return utils.replaceWithProxy(obj, propName, handler)\n}",execRecursively:"(obj = {}, typeFilter = [], fn) => {\n  function recurse(obj) {\n    for (const key in obj) {\n      if (obj[key] === undefined) {\n        continue\n      }\n      if (obj[key] && typeof obj[key] === 'object') {\n        recurse(obj[key])\n      } else {\n        if (obj[key] && typeFilter.includes(typeof obj[key])) {\n          fn.call(this, obj[key])\n        }\n      }\n    }\n  }\n  recurse(obj)\n  return obj\n}",stringifyFns:"(fnObj = { hello: () => 'world' }) => {\n  // Object.fromEntries() ponyfill (in 6 lines) - supported only in Node v12+, modern browsers are fine\n  // https://github.com/feross/fromentries\n  function fromEntries(iterable) {\n    return [...iterable].reduce((obj, [key, val]) => {\n      obj[key] = val\n      return obj\n    }, {})\n  }\n  return (Object.fromEntries || fromEntries)(\n    Object.entries(fnObj)\n      .filter(([key, value]) => typeof value === 'function')\n      .map(([key, value]) => [key, value.toString()]) // eslint-disable-line no-eval\n  )\n}",materializeFns:"(fnStrObj = { hello: \"() => 'world'\" }) => {\n  return Object.fromEntries(\n    Object.entries(fnStrObj).map(([key, value]) => {\n      if (value.startsWith('function')) {\n        // some trickery is needed to make oldschool functions work :-)\n        return [key, eval(`() => ${value}`)()] // eslint-disable-line no-eval\n      } else {\n        // arrow functions just work\n        return [key, eval(value)] // eslint-disable-line no-eval\n      }\n    })\n  )\n}",makeHandler:"() => ({\n  // Used by simple `navigator` getter evasions\n  getterValue: value => ({\n    apply(target, ctx, args) {\n      // Let's fetch the value first, to trigger and escalate potential errors\n      // Illegal invocations like `navigator.__proto__.vendor` will throw here\n      utils.cache.Reflect.apply(...arguments)\n      return value\n    }\n  })\n})",arrayEquals:"(array1, array2) => {\n  if (array1.length !== array2.length) {\n    return false\n  }\n  for (let i = 0; i < array1.length; ++i) {\n    if (array1[i] !== array2[i]) {\n      return false\n    }\n  }\n  return true\n}",memoize:"fn => {\n  const cache = []\n  return function(...args) {\n    if (!cache.some(c => utils.arrayEquals(c.key, args))) {\n      cache.push({ key: args, value: fn.apply(this, args) })\n    }\n    return cache.find(c => utils.arrayEquals(c.key, args)).value\n  }\n}"},_mainFunction:"(utils, { fns, data }) => {\n        fns = utils.materializeFns(fns)\n\n        // That means we're running headful\n        const hasPlugins = 'plugins' in navigator && navigator.plugins.length\n        if (hasPlugins) {\n          return // nothing to do here\n        }\n\n        const mimeTypes = fns.generateMimeTypeArray(utils, fns)(data.mimeTypes)\n        const plugins = fns.generatePluginArray(utils, fns)(data.plugins)\n\n        // Plugin and MimeType cross-reference each other, let's do that now\n        // Note: We're looping through `data.plugins` here, not the generated `plugins`\n        for (const pluginData of data.plugins) {\n          pluginData.__mimeTypes.forEach((type, index) => {\n            plugins[pluginData.name][index] = mimeTypes[type]\n\n            Object.defineProperty(plugins[pluginData.name], type, {\n              value: mimeTypes[type],\n              writable: false,\n              enumerable: false, // Not enumerable\n              configurable: true\n            })\n            Object.defineProperty(mimeTypes[type], 'enabledPlugin', {\n              value:\n                type === 'application/x-pnacl'\n                  ? mimeTypes['application/x-nacl'].enabledPlugin // these reference the same plugin, so we need to re-use the Proxy in order to avoid leaks\n                  : new Proxy(plugins[pluginData.name], {}), // Prevent circular references\n              writable: false,\n              enumerable: false, // Important: `JSON.stringify(navigator.plugins)`\n              configurable: true\n            })\n          })\n        }\n\n        const patchNavigator = (name, value) =>\n          utils.replaceProperty(Object.getPrototypeOf(navigator), name, {\n            get() {\n              return value\n            }\n          })\n\n        patchNavigator('mimeTypes', mimeTypes)\n        patchNavigator('plugins', plugins)\n\n        // All done\n      }",_args:[{fns:{generateMimeTypeArray:"(utils, fns) => mimeTypesData => {\n  return fns.generateMagicArray(utils, fns)(\n    mimeTypesData,\n    MimeTypeArray.prototype,\n    MimeType.prototype,\n    'type'\n  )\n}",generatePluginArray:"(utils, fns) => pluginsData => {\n  return fns.generateMagicArray(utils, fns)(\n    pluginsData,\n    PluginArray.prototype,\n    Plugin.prototype,\n    'name'\n  )\n}",generateMagicArray:"(utils, fns) =>\n  function(\n    dataArray = [],\n    proto = MimeTypeArray.prototype,\n    itemProto = MimeType.prototype,\n    itemMainProp = 'type'\n  ) {\n    // Quick helper to set props with the same descriptors vanilla is using\n    const defineProp = (obj, prop, value) =>\n      Object.defineProperty(obj, prop, {\n        value,\n        writable: false,\n        enumerable: false, // Important for mimeTypes & plugins: `JSON.stringify(navigator.mimeTypes)`\n        configurable: true\n      })\n\n    // Loop over our fake data and construct items\n    const makeItem = data => {\n      const item = {}\n      for (const prop of Object.keys(data)) {\n        if (prop.startsWith('__')) {\n          continue\n        }\n        defineProp(item, prop, data[prop])\n      }\n      return patchItem(item, data)\n    }\n\n    const patchItem = (item, data) => {\n      let descriptor = Object.getOwnPropertyDescriptors(item)\n\n      // Special case: Plugins have a magic length property which is not enumerable\n      // e.g. `navigator.plugins[i].length` should always be the length of the assigned mimeTypes\n      if (itemProto === Plugin.prototype) {\n        descriptor = {\n          ...descriptor,\n          length: {\n            value: data.__mimeTypes.length,\n            writable: false,\n            enumerable: false,\n            configurable: true // Important to be able to use the ownKeys trap in a Proxy to strip `length`\n          }\n        }\n      }\n\n      // We need to spoof a specific `MimeType` or `Plugin` object\n      const obj = Object.create(itemProto, descriptor)\n\n      // Virtually all property keys are not enumerable in vanilla\n      const blacklist = [...Object.keys(data), 'length', 'enabledPlugin']\n      return new Proxy(obj, {\n        ownKeys(target) {\n          return Reflect.ownKeys(target).filter(k => !blacklist.includes(k))\n        },\n        getOwnPropertyDescriptor(target, prop) {\n          if (blacklist.includes(prop)) {\n            return undefined\n          }\n          return Reflect.getOwnPropertyDescriptor(target, prop)\n        }\n      })\n    }\n\n    const magicArray = []\n\n    // Loop through our fake data and use that to create convincing entities\n    dataArray.forEach(data => {\n      magicArray.push(makeItem(data))\n    })\n\n    // Add direct property access  based on types (e.g. `obj['application/pdf']`) afterwards\n    magicArray.forEach(entry => {\n      defineProp(magicArray, entry[itemMainProp], entry)\n    })\n\n    // This is the best way to fake the type to make sure this is false: `Array.isArray(navigator.mimeTypes)`\n    const magicArrayObj = Object.create(proto, {\n      ...Object.getOwnPropertyDescriptors(magicArray),\n\n      // There's one ugly quirk we unfortunately need to take care of:\n      // The `MimeTypeArray` prototype has an enumerable `length` property,\n      // but headful Chrome will still skip it when running `Object.getOwnPropertyNames(navigator.mimeTypes)`.\n      // To strip it we need to make it first `configurable` and can then overlay a Proxy with an `ownKeys` trap.\n      length: {\n        value: magicArray.length,\n        writable: false,\n        enumerable: false,\n        configurable: true // Important to be able to use the ownKeys trap in a Proxy to strip `length`\n      }\n    })\n\n    // Generate our functional function mocks :-)\n    const functionMocks = fns.generateFunctionMocks(utils)(\n      proto,\n      itemMainProp,\n      magicArray\n    )\n\n    // We need to overlay our custom object with a JS Proxy\n    const magicArrayObjProxy = new Proxy(magicArrayObj, {\n      get(target, key = '') {\n        // Redirect function calls to our custom proxied versions mocking the vanilla behavior\n        if (key === 'item') {\n          return functionMocks.item\n        }\n        if (key === 'namedItem') {\n          return functionMocks.namedItem\n        }\n        if (proto === PluginArray.prototype && key === 'refresh') {\n          return functionMocks.refresh\n        }\n        // Everything else can pass through as normal\n        return utils.cache.Reflect.get(...arguments)\n      },\n      ownKeys(target) {\n        // There are a couple of quirks where the original property demonstrates \"magical\" behavior that makes no sense\n        // This can be witnessed when calling `Object.getOwnPropertyNames(navigator.mimeTypes)` and the absense of `length`\n        // My guess is that it has to do with the recent change of not allowing data enumeration and this being implemented weirdly\n        // For that reason we just completely fake the available property names based on our data to match what regular Chrome is doing\n        // Specific issues when not patching this: `length` property is available, direct `types` props (e.g. `obj['application/pdf']`) are missing\n        const keys = []\n        const typeProps = magicArray.map(mt => mt[itemMainProp])\n        typeProps.forEach((_, i) => keys.push(`${i}`))\n        typeProps.forEach(propName => keys.push(propName))\n        return keys\n      },\n      getOwnPropertyDescriptor(target, prop) {\n        if (prop === 'length') {\n          return undefined\n        }\n        return Reflect.getOwnPropertyDescriptor(target, prop)\n      }\n    })\n\n    return magicArrayObjProxy\n  }",generateFunctionMocks:"utils => (\n  proto,\n  itemMainProp,\n  dataArray\n) => ({\n  /** Returns the MimeType object with the specified index. */\n  item: utils.createProxy(proto.item, {\n    apply(target, ctx, args) {\n      if (!args.length) {\n        throw new TypeError(\n          `Failed to execute 'item' on '${\n            proto[Symbol.toStringTag]\n          }': 1 argument required, but only 0 present.`\n        )\n      }\n      // Special behavior alert:\n      // - Vanilla tries to cast strings to Numbers (only integers!) and use them as property index lookup\n      // - If anything else than an integer (including as string) is provided it will return the first entry\n      const isInteger = args[0] && Number.isInteger(Number(args[0])) // Cast potential string to number first, then check for integer\n      // Note: Vanilla never returns `undefined`\n      return (isInteger ? dataArray[Number(args[0])] : dataArray[0]) || null\n    }\n  }),\n  /** Returns the MimeType object with the specified name. */\n  namedItem: utils.createProxy(proto.namedItem, {\n    apply(target, ctx, args) {\n      if (!args.length) {\n        throw new TypeError(\n          `Failed to execute 'namedItem' on '${\n            proto[Symbol.toStringTag]\n          }': 1 argument required, but only 0 present.`\n        )\n      }\n      return dataArray.find(mt => mt[itemMainProp] === args[0]) || null // Not `undefined`!\n    }\n  }),\n  /** Does nothing and shall return nothing */\n  refresh: proto.refresh\n    ? utils.createProxy(proto.refresh, {\n        apply(target, ctx, args) {\n          return undefined\n        }\n      })\n    : undefined\n})"},data:{mimeTypes:[{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"}],plugins:[{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"]}]}}]}),!1===navigator.webdriver||void 0===navigator.webdriver||delete Object.getPrototypeOf(navigator).webdriver,(({_utilsFns:_utilsFns,_mainFunction:_mainFunction,_args:_args})=>{const utils=Object.fromEntries(Object.entries(_utilsFns).map((([key,value])=>[key,eval(value)])));utils.init(),eval(_mainFunction)(utils,..._args)})({_utilsFns:{init:"() => {\n  utils.preloadCache()\n}",stripProxyFromErrors:"(handler = {}) => {\n  const newHandler = {\n    setPrototypeOf: function (target, proto) {\n      if (proto === null)\n        throw new TypeError('Cannot convert object to primitive value')\n      if (Object.getPrototypeOf(target) === Object.getPrototypeOf(proto)) {\n        throw new TypeError('Cyclic __proto__ value')\n      }\n      return Reflect.setPrototypeOf(target, proto)\n    }\n  }\n  // We wrap each trap in the handler in a try/catch and modify the error stack if they throw\n  const traps = Object.getOwnPropertyNames(handler)\n  traps.forEach(trap => {\n    newHandler[trap] = function () {\n      try {\n        // Forward the call to the defined proxy handler\n        return handler[trap].apply(this, arguments || [])\n      } catch (err) {\n        // Stack traces differ per browser, we only support chromium based ones currently\n        if (!err || !err.stack || !err.stack.includes(`at `)) {\n          throw err\n        }\n\n        // When something throws within one of our traps the Proxy will show up in error stacks\n        // An earlier implementation of this code would simply strip lines with a blacklist,\n        // but it makes sense to be more surgical here and only remove lines related to our Proxy.\n        // We try to use a known \"anchor\" line for that and strip it with everything above it.\n        // If the anchor line cannot be found for some reason we fall back to our blacklist approach.\n\n        const stripWithBlacklist = (stack, stripFirstLine = true) => {\n          const blacklist = [\n            `at Reflect.${trap} `, // e.g. Reflect.get or Reflect.apply\n            `at Object.${trap} `, // e.g. Object.get or Object.apply\n            `at Object.newHandler.<computed> [as ${trap}] ` // caused by this very wrapper :-)\n          ]\n          return (\n            err.stack\n              .split('\\n')\n              // Always remove the first (file) line in the stack (guaranteed to be our proxy)\n              .filter((line, index) => !(index === 1 && stripFirstLine))\n              // Check if the line starts with one of our blacklisted strings\n              .filter(line => !blacklist.some(bl => line.trim().startsWith(bl)))\n              .join('\\n')\n          )\n        }\n\n        const stripWithAnchor = (stack, anchor) => {\n          const stackArr = stack.split('\\n')\n          anchor = anchor || `at Object.newHandler.<computed> [as ${trap}] ` // Known first Proxy line in chromium\n          const anchorIndex = stackArr.findIndex(line =>\n            line.trim().startsWith(anchor)\n          )\n          if (anchorIndex === -1) {\n            return false // 404, anchor not found\n          }\n          // Strip everything from the top until we reach the anchor line\n          // Note: We're keeping the 1st line (zero index) as it's unrelated (e.g. `TypeError`)\n          stackArr.splice(1, anchorIndex)\n          return stackArr.join('\\n')\n        }\n\n        // Special cases due to our nested toString proxies\n        err.stack = err.stack.replace(\n          'at Object.toString (',\n          'at Function.toString ('\n        )\n        if ((err.stack || '').includes('at Function.toString (')) {\n          err.stack = stripWithBlacklist(err.stack, false)\n          throw err\n        }\n\n        // Try using the anchor method, fallback to blacklist if necessary\n        err.stack = stripWithAnchor(err.stack) || stripWithBlacklist(err.stack)\n\n        throw err // Re-throw our now sanitized error\n      }\n    }\n  })\n  return newHandler\n}",stripErrorWithAnchor:"(err, anchor) => {\n  const stackArr = err.stack.split('\\n')\n  const anchorIndex = stackArr.findIndex(line => line.trim().startsWith(anchor))\n  if (anchorIndex === -1) {\n    return err // 404, anchor not found\n  }\n  // Strip everything from the top until we reach the anchor line (remove anchor line as well)\n  // Note: We're keeping the 1st line (zero index) as it's unrelated (e.g. `TypeError`)\n  stackArr.splice(1, anchorIndex)\n  err.stack = stackArr.join('\\n')\n  return err\n}",replaceProperty:"(obj, propName, descriptorOverrides = {}) => {\n  return Object.defineProperty(obj, propName, {\n    // Copy over the existing descriptors (writable, enumerable, configurable, etc)\n    ...(Object.getOwnPropertyDescriptor(obj, propName) || {}),\n    // Add our overrides (e.g. value, get())\n    ...descriptorOverrides\n  })\n}",preloadCache:"() => {\n  if (utils.cache) {\n    return\n  }\n  utils.cache = {\n    // Used in our proxies\n    Reflect: {\n      get: Reflect.get.bind(Reflect),\n      apply: Reflect.apply.bind(Reflect)\n    },\n    // Used in `makeNativeString`\n    nativeToStringStr: Function.toString + '' // => `function toString() { [native code] }`\n  }\n}",makeNativeString:"(name = '') => {\n  return utils.cache.nativeToStringStr.replace('toString', name || '')\n}",patchToString:"(obj, str = '') => {\n  const handler = {\n    apply: function (target, ctx) {\n      // This fixes e.g. `HTMLMediaElement.prototype.canPlayType.toString + \"\"`\n      if (ctx === Function.prototype.toString) {\n        return utils.makeNativeString('toString')\n      }\n      // `toString` targeted at our proxied Object detected\n      if (ctx === obj) {\n        // We either return the optional string verbatim or derive the most desired result automatically\n        return str || utils.makeNativeString(obj.name)\n      }\n      // Check if the toString protype of the context is the same as the global prototype,\n      // if not indicates that we are doing a check across different windows., e.g. the iframeWithdirect` test case\n      const hasSameProto = Object.getPrototypeOf(\n        Function.prototype.toString\n      ).isPrototypeOf(ctx.toString) // eslint-disable-line no-prototype-builtins\n      if (!hasSameProto) {\n        // Pass the call on to the local Function.prototype.toString instead\n        return ctx.toString()\n      }\n      return target.call(ctx)\n    }\n  }\n\n  const toStringProxy = new Proxy(\n    Function.prototype.toString,\n    utils.stripProxyFromErrors(handler)\n  )\n  utils.replaceProperty(Function.prototype, 'toString', {\n    value: toStringProxy\n  })\n}",patchToStringNested:"(obj = {}) => {\n  return utils.execRecursively(obj, ['function'], utils.patchToString)\n}",redirectToString:"(proxyObj, originalObj) => {\n  const handler = {\n    apply: function (target, ctx) {\n      // This fixes e.g. `HTMLMediaElement.prototype.canPlayType.toString + \"\"`\n      if (ctx === Function.prototype.toString) {\n        return utils.makeNativeString('toString')\n      }\n\n      // `toString` targeted at our proxied Object detected\n      if (ctx === proxyObj) {\n        const fallback = () =>\n          originalObj && originalObj.name\n            ? utils.makeNativeString(originalObj.name)\n            : utils.makeNativeString(proxyObj.name)\n\n        // Return the toString representation of our original object if possible\n        return originalObj + '' || fallback()\n      }\n\n      if (typeof ctx === 'undefined' || ctx === null) {\n        return target.call(ctx)\n      }\n\n      // Check if the toString protype of the context is the same as the global prototype,\n      // if not indicates that we are doing a check across different windows., e.g. the iframeWithdirect` test case\n      const hasSameProto = Object.getPrototypeOf(\n        Function.prototype.toString\n      ).isPrototypeOf(ctx.toString) // eslint-disable-line no-prototype-builtins\n      if (!hasSameProto) {\n        // Pass the call on to the local Function.prototype.toString instead\n        return ctx.toString()\n      }\n\n      return target.call(ctx)\n    }\n  }\n\n  const toStringProxy = new Proxy(\n    Function.prototype.toString,\n    utils.stripProxyFromErrors(handler)\n  )\n  utils.replaceProperty(Function.prototype, 'toString', {\n    value: toStringProxy\n  })\n}",replaceWithProxy:"(obj, propName, handler) => {\n  const originalObj = obj[propName]\n  const proxyObj = new Proxy(obj[propName], utils.stripProxyFromErrors(handler))\n\n  utils.replaceProperty(obj, propName, { value: proxyObj })\n  utils.redirectToString(proxyObj, originalObj)\n\n  return true\n}",replaceGetterWithProxy:"(obj, propName, handler) => {\n  const fn = Object.getOwnPropertyDescriptor(obj, propName).get\n  const fnStr = fn.toString() // special getter function string\n  const proxyObj = new Proxy(fn, utils.stripProxyFromErrors(handler))\n\n  utils.replaceProperty(obj, propName, { get: proxyObj })\n  utils.patchToString(proxyObj, fnStr)\n\n  return true\n}",replaceGetterSetter:"(obj, propName, handlerGetterSetter) => {\n  const ownPropertyDescriptor = Object.getOwnPropertyDescriptor(obj, propName)\n  const handler = { ...ownPropertyDescriptor }\n\n  if (handlerGetterSetter.get !== undefined) {\n    const nativeFn = ownPropertyDescriptor.get\n    handler.get = function() {\n      return handlerGetterSetter.get.call(this, nativeFn.bind(this))\n    }\n    utils.redirectToString(handler.get, nativeFn)\n  }\n\n  if (handlerGetterSetter.set !== undefined) {\n    const nativeFn = ownPropertyDescriptor.set\n    handler.set = function(newValue) {\n      handlerGetterSetter.set.call(this, newValue, nativeFn.bind(this))\n    }\n    utils.redirectToString(handler.set, nativeFn)\n  }\n\n  Object.defineProperty(obj, propName, handler)\n}",mockWithProxy:"(obj, propName, pseudoTarget, handler) => {\n  const proxyObj = new Proxy(pseudoTarget, utils.stripProxyFromErrors(handler))\n\n  utils.replaceProperty(obj, propName, { value: proxyObj })\n  utils.patchToString(proxyObj)\n\n  return true\n}",createProxy:"(pseudoTarget, handler) => {\n  const proxyObj = new Proxy(pseudoTarget, utils.stripProxyFromErrors(handler))\n  utils.patchToString(proxyObj)\n\n  return proxyObj\n}",splitObjPath:"objPath => ({\n  // Remove last dot entry (property) ==> `HTMLMediaElement.prototype`\n  objName: objPath.split('.').slice(0, -1).join('.'),\n  // Extract last dot entry ==> `canPlayType`\n  propName: objPath.split('.').slice(-1)[0]\n})",replaceObjPathWithProxy:"(objPath, handler) => {\n  const { objName, propName } = utils.splitObjPath(objPath)\n  const obj = eval(objName) // eslint-disable-line no-eval\n  return utils.replaceWithProxy(obj, propName, handler)\n}",execRecursively:"(obj = {}, typeFilter = [], fn) => {\n  function recurse(obj) {\n    for (const key in obj) {\n      if (obj[key] === undefined) {\n        continue\n      }\n      if (obj[key] && typeof obj[key] === 'object') {\n        recurse(obj[key])\n      } else {\n        if (obj[key] && typeFilter.includes(typeof obj[key])) {\n          fn.call(this, obj[key])\n        }\n      }\n    }\n  }\n  recurse(obj)\n  return obj\n}",stringifyFns:"(fnObj = { hello: () => 'world' }) => {\n  // Object.fromEntries() ponyfill (in 6 lines) - supported only in Node v12+, modern browsers are fine\n  // https://github.com/feross/fromentries\n  function fromEntries(iterable) {\n    return [...iterable].reduce((obj, [key, val]) => {\n      obj[key] = val\n      return obj\n    }, {})\n  }\n  return (Object.fromEntries || fromEntries)(\n    Object.entries(fnObj)\n      .filter(([key, value]) => typeof value === 'function')\n      .map(([key, value]) => [key, value.toString()]) // eslint-disable-line no-eval\n  )\n}",materializeFns:"(fnStrObj = { hello: \"() => 'world'\" }) => {\n  return Object.fromEntries(\n    Object.entries(fnStrObj).map(([key, value]) => {\n      if (value.startsWith('function')) {\n        // some trickery is needed to make oldschool functions work :-)\n        return [key, eval(`() => ${value}`)()] // eslint-disable-line no-eval\n      } else {\n        // arrow functions just work\n        return [key, eval(value)] // eslint-disable-line no-eval\n      }\n    })\n  )\n}",makeHandler:"() => ({\n  // Used by simple `navigator` getter evasions\n  getterValue: value => ({\n    apply(target, ctx, args) {\n      // Let's fetch the value first, to trigger and escalate potential errors\n      // Illegal invocations like `navigator.__proto__.vendor` will throw here\n      utils.cache.Reflect.apply(...arguments)\n      return value\n    }\n  })\n})",arrayEquals:"(array1, array2) => {\n  if (array1.length !== array2.length) {\n    return false\n  }\n  for (let i = 0; i < array1.length; ++i) {\n    if (array1[i] !== array2[i]) {\n      return false\n    }\n  }\n  return true\n}",memoize:"fn => {\n  const cache = []\n  return function(...args) {\n    if (!cache.some(c => utils.arrayEquals(c.key, args))) {\n      cache.push({ key: args, value: fn.apply(this, args) })\n    }\n    return cache.find(c => utils.arrayEquals(c.key, args)).value\n  }\n}"},_mainFunction:"(utils, opts) => {\n      const getParameterProxyHandler = {\n        apply: function(target, ctx, args) {\n          const param = (args || [])[0]\n          const result = utils.cache.Reflect.apply(target, ctx, args)\n          // UNMASKED_VENDOR_WEBGL\n          if (param === 37445) {\n            return opts.vendor || 'Intel Inc.' // default in headless: Google Inc.\n          }\n          // UNMASKED_RENDERER_WEBGL\n          if (param === 37446) {\n            return opts.renderer || 'Intel Iris OpenGL Engine' // default in headless: Google SwiftShader\n          }\n          return result\n        }\n      }\n\n      // There's more than one WebGL rendering context\n      // https://developer.mozilla.org/en-US/docs/Web/API/WebGL2RenderingContext#Browser_compatibility\n      // To find out the original values here: Object.getOwnPropertyDescriptors(WebGLRenderingContext.prototype.getParameter)\n      const addProxy = (obj, propName) => {\n        utils.replaceWithProxy(obj, propName, getParameterProxyHandler)\n      }\n      // For whatever weird reason loops don't play nice with Object.defineProperty, here's the next best thing:\n      addProxy(WebGLRenderingContext.prototype, 'getParameter')\n      addProxy(WebGL2RenderingContext.prototype, 'getParameter')\n    }",_args:[{}]}),(()=>{try{if(window.outerWidth&&window.outerHeight)return;const n=85;window.outerWidth=window.innerWidth,window.outerHeight=window.innerHeight+n}catch(n){}})(),(({_utilsFns:_utilsFns,_mainFunction:_mainFunction,_args:_args})=>{const utils=Object.fromEntries(Object.entries(_utilsFns).map((([key,value])=>[key,eval(value)])));utils.init(),eval(_mainFunction)(utils,..._args)})({_utilsFns:{init:"() => {\n  utils.preloadCache()\n}",stripProxyFromErrors:"(handler = {}) => {\n  const newHandler = {\n    setPrototypeOf: function (target, proto) {\n      if (proto === null)\n        throw new TypeError('Cannot convert object to primitive value')\n      if (Object.getPrototypeOf(target) === Object.getPrototypeOf(proto)) {\n        throw new TypeError('Cyclic __proto__ value')\n      }\n      return Reflect.setPrototypeOf(target, proto)\n    }\n  }\n  // We wrap each trap in the handler in a try/catch and modify the error stack if they throw\n  const traps = Object.getOwnPropertyNames(handler)\n  traps.forEach(trap => {\n    newHandler[trap] = function () {\n      try {\n        // Forward the call to the defined proxy handler\n        return handler[trap].apply(this, arguments || [])\n      } catch (err) {\n        // Stack traces differ per browser, we only support chromium based ones currently\n        if (!err || !err.stack || !err.stack.includes(`at `)) {\n          throw err\n        }\n\n        // When something throws within one of our traps the Proxy will show up in error stacks\n        // An earlier implementation of this code would simply strip lines with a blacklist,\n        // but it makes sense to be more surgical here and only remove lines related to our Proxy.\n        // We try to use a known \"anchor\" line for that and strip it with everything above it.\n        // If the anchor line cannot be found for some reason we fall back to our blacklist approach.\n\n        const stripWithBlacklist = (stack, stripFirstLine = true) => {\n          const blacklist = [\n            `at Reflect.${trap} `, // e.g. Reflect.get or Reflect.apply\n            `at Object.${trap} `, // e.g. Object.get or Object.apply\n            `at Object.newHandler.<computed> [as ${trap}] ` // caused by this very wrapper :-)\n          ]\n          return (\n            err.stack\n              .split('\\n')\n              // Always remove the first (file) line in the stack (guaranteed to be our proxy)\n              .filter((line, index) => !(index === 1 && stripFirstLine))\n              // Check if the line starts with one of our blacklisted strings\n              .filter(line => !blacklist.some(bl => line.trim().startsWith(bl)))\n              .join('\\n')\n          )\n        }\n\n        const stripWithAnchor = (stack, anchor) => {\n          const stackArr = stack.split('\\n')\n          anchor = anchor || `at Object.newHandler.<computed> [as ${trap}] ` // Known first Proxy line in chromium\n          const anchorIndex = stackArr.findIndex(line =>\n            line.trim().startsWith(anchor)\n          )\n          if (anchorIndex === -1) {\n            return false // 404, anchor not found\n          }\n          // Strip everything from the top until we reach the anchor line\n          // Note: We're keeping the 1st line (zero index) as it's unrelated (e.g. `TypeError`)\n          stackArr.splice(1, anchorIndex)\n          return stackArr.join('\\n')\n        }\n\n        // Special cases due to our nested toString proxies\n        err.stack = err.stack.replace(\n          'at Object.toString (',\n          'at Function.toString ('\n        )\n        if ((err.stack || '').includes('at Function.toString (')) {\n          err.stack = stripWithBlacklist(err.stack, false)\n          throw err\n        }\n\n        // Try using the anchor method, fallback to blacklist if necessary\n        err.stack = stripWithAnchor(err.stack) || stripWithBlacklist(err.stack)\n\n        throw err // Re-throw our now sanitized error\n      }\n    }\n  })\n  return newHandler\n}",stripErrorWithAnchor:"(err, anchor) => {\n  const stackArr = err.stack.split('\\n')\n  const anchorIndex = stackArr.findIndex(line => line.trim().startsWith(anchor))\n  if (anchorIndex === -1) {\n    return err // 404, anchor not found\n  }\n  // Strip everything from the top until we reach the anchor line (remove anchor line as well)\n  // Note: We're keeping the 1st line (zero index) as it's unrelated (e.g. `TypeError`)\n  stackArr.splice(1, anchorIndex)\n  err.stack = stackArr.join('\\n')\n  return err\n}",replaceProperty:"(obj, propName, descriptorOverrides = {}) => {\n  return Object.defineProperty(obj, propName, {\n    // Copy over the existing descriptors (writable, enumerable, configurable, etc)\n    ...(Object.getOwnPropertyDescriptor(obj, propName) || {}),\n    // Add our overrides (e.g. value, get())\n    ...descriptorOverrides\n  })\n}",preloadCache:"() => {\n  if (utils.cache) {\n    return\n  }\n  utils.cache = {\n    // Used in our proxies\n    Reflect: {\n      get: Reflect.get.bind(Reflect),\n      apply: Reflect.apply.bind(Reflect)\n    },\n    // Used in `makeNativeString`\n    nativeToStringStr: Function.toString + '' // => `function toString() { [native code] }`\n  }\n}",makeNativeString:"(name = '') => {\n  return utils.cache.nativeToStringStr.replace('toString', name || '')\n}",patchToString:"(obj, str = '') => {\n  const handler = {\n    apply: function (target, ctx) {\n      // This fixes e.g. `HTMLMediaElement.prototype.canPlayType.toString + \"\"`\n      if (ctx === Function.prototype.toString) {\n        return utils.makeNativeString('toString')\n      }\n      // `toString` targeted at our proxied Object detected\n      if (ctx === obj) {\n        // We either return the optional string verbatim or derive the most desired result automatically\n        return str || utils.makeNativeString(obj.name)\n      }\n      // Check if the toString protype of the context is the same as the global prototype,\n      // if not indicates that we are doing a check across different windows., e.g. the iframeWithdirect` test case\n      const hasSameProto = Object.getPrototypeOf(\n        Function.prototype.toString\n      ).isPrototypeOf(ctx.toString) // eslint-disable-line no-prototype-builtins\n      if (!hasSameProto) {\n        // Pass the call on to the local Function.prototype.toString instead\n        return ctx.toString()\n      }\n      return target.call(ctx)\n    }\n  }\n\n  const toStringProxy = new Proxy(\n    Function.prototype.toString,\n    utils.stripProxyFromErrors(handler)\n  )\n  utils.replaceProperty(Function.prototype, 'toString', {\n    value: toStringProxy\n  })\n}",patchToStringNested:"(obj = {}) => {\n  return utils.execRecursively(obj, ['function'], utils.patchToString)\n}",redirectToString:"(proxyObj, originalObj) => {\n  const handler = {\n    apply: function (target, ctx) {\n      // This fixes e.g. `HTMLMediaElement.prototype.canPlayType.toString + \"\"`\n      if (ctx === Function.prototype.toString) {\n        return utils.makeNativeString('toString')\n      }\n\n      // `toString` targeted at our proxied Object detected\n      if (ctx === proxyObj) {\n        const fallback = () =>\n          originalObj && originalObj.name\n            ? utils.makeNativeString(originalObj.name)\n            : utils.makeNativeString(proxyObj.name)\n\n        // Return the toString representation of our original object if possible\n        return originalObj + '' || fallback()\n      }\n\n      if (typeof ctx === 'undefined' || ctx === null) {\n        return target.call(ctx)\n      }\n\n      // Check if the toString protype of the context is the same as the global prototype,\n      // if not indicates that we are doing a check across different windows., e.g. the iframeWithdirect` test case\n      const hasSameProto = Object.getPrototypeOf(\n        Function.prototype.toString\n      ).isPrototypeOf(ctx.toString) // eslint-disable-line no-prototype-builtins\n      if (!hasSameProto) {\n        // Pass the call on to the local Function.prototype.toString instead\n        return ctx.toString()\n      }\n\n      return target.call(ctx)\n    }\n  }\n\n  const toStringProxy = new Proxy(\n    Function.prototype.toString,\n    utils.stripProxyFromErrors(handler)\n  )\n  utils.replaceProperty(Function.prototype, 'toString', {\n    value: toStringProxy\n  })\n}",replaceWithProxy:"(obj, propName, handler) => {\n  const originalObj = obj[propName]\n  const proxyObj = new Proxy(obj[propName], utils.stripProxyFromErrors(handler))\n\n  utils.replaceProperty(obj, propName, { value: proxyObj })\n  utils.redirectToString(proxyObj, originalObj)\n\n  return true\n}",replaceGetterWithProxy:"(obj, propName, handler) => {\n  const fn = Object.getOwnPropertyDescriptor(obj, propName).get\n  const fnStr = fn.toString() // special getter function string\n  const proxyObj = new Proxy(fn, utils.stripProxyFromErrors(handler))\n\n  utils.replaceProperty(obj, propName, { get: proxyObj })\n  utils.patchToString(proxyObj, fnStr)\n\n  return true\n}",replaceGetterSetter:"(obj, propName, handlerGetterSetter) => {\n  const ownPropertyDescriptor = Object.getOwnPropertyDescriptor(obj, propName)\n  const handler = { ...ownPropertyDescriptor }\n\n  if (handlerGetterSetter.get !== undefined) {\n    const nativeFn = ownPropertyDescriptor.get\n    handler.get = function() {\n      return handlerGetterSetter.get.call(this, nativeFn.bind(this))\n    }\n    utils.redirectToString(handler.get, nativeFn)\n  }\n\n  if (handlerGetterSetter.set !== undefined) {\n    const nativeFn = ownPropertyDescriptor.set\n    handler.set = function(newValue) {\n      handlerGetterSetter.set.call(this, newValue, nativeFn.bind(this))\n    }\n    utils.redirectToString(handler.set, nativeFn)\n  }\n\n  Object.defineProperty(obj, propName, handler)\n}",mockWithProxy:"(obj, propName, pseudoTarget, handler) => {\n  const proxyObj = new Proxy(pseudoTarget, utils.stripProxyFromErrors(handler))\n\n  utils.replaceProperty(obj, propName, { value: proxyObj })\n  utils.patchToString(proxyObj)\n\n  return true\n}",createProxy:"(pseudoTarget, handler) => {\n  const proxyObj = new Proxy(pseudoTarget, utils.stripProxyFromErrors(handler))\n  utils.patchToString(proxyObj)\n\n  return proxyObj\n}",splitObjPath:"objPath => ({\n  // Remove last dot entry (property) ==> `HTMLMediaElement.prototype`\n  objName: objPath.split('.').slice(0, -1).join('.'),\n  // Extract last dot entry ==> `canPlayType`\n  propName: objPath.split('.').slice(-1)[0]\n})",replaceObjPathWithProxy:"(objPath, handler) => {\n  const { objName, propName } = utils.splitObjPath(objPath)\n  const obj = eval(objName) // eslint-disable-line no-eval\n  return utils.replaceWithProxy(obj, propName, handler)\n}",execRecursively:"(obj = {}, typeFilter = [], fn) => {\n  function recurse(obj) {\n    for (const key in obj) {\n      if (obj[key] === undefined) {\n        continue\n      }\n      if (obj[key] && typeof obj[key] === 'object') {\n        recurse(obj[key])\n      } else {\n        if (obj[key] && typeFilter.includes(typeof obj[key])) {\n          fn.call(this, obj[key])\n        }\n      }\n    }\n  }\n  recurse(obj)\n  return obj\n}",stringifyFns:"(fnObj = { hello: () => 'world' }) => {\n  // Object.fromEntries() ponyfill (in 6 lines) - supported only in Node v12+, modern browsers are fine\n  // https://github.com/feross/fromentries\n  function fromEntries(iterable) {\n    return [...iterable].reduce((obj, [key, val]) => {\n      obj[key] = val\n      return obj\n    }, {})\n  }\n  return (Object.fromEntries || fromEntries)(\n    Object.entries(fnObj)\n      .filter(([key, value]) => typeof value === 'function')\n      .map(([key, value]) => [key, value.toString()]) // eslint-disable-line no-eval\n  )\n}",materializeFns:"(fnStrObj = { hello: \"() => 'world'\" }) => {\n  return Object.fromEntries(\n    Object.entries(fnStrObj).map(([key, value]) => {\n      if (value.startsWith('function')) {\n        // some trickery is needed to make oldschool functions work :-)\n        return [key, eval(`() => ${value}`)()] // eslint-disable-line no-eval\n      } else {\n        // arrow functions just work\n        return [key, eval(value)] // eslint-disable-line no-eval\n      }\n    })\n  )\n}",makeHandler:"() => ({\n  // Used by simple `navigator` getter evasions\n  getterValue: value => ({\n    apply(target, ctx, args) {\n      // Let's fetch the value first, to trigger and escalate potential errors\n      // Illegal invocations like `navigator.__proto__.vendor` will throw here\n      utils.cache.Reflect.apply(...arguments)\n      return value\n    }\n  })\n})",arrayEquals:"(array1, array2) => {\n  if (array1.length !== array2.length) {\n    return false\n  }\n  for (let i = 0; i < array1.length; ++i) {\n    if (array1[i] !== array2[i]) {\n      return false\n    }\n  }\n  return true\n}",memoize:"fn => {\n  const cache = []\n  return function(...args) {\n    if (!cache.some(c => utils.arrayEquals(c.key, args))) {\n      cache.push({ key: args, value: fn.apply(this, args) })\n    }\n    return cache.find(c => utils.arrayEquals(c.key, args)).value\n  }\n}"},_mainFunction:"(utils, opts) => {\n      try {\n        // Adds a contentWindow proxy to the provided iframe element\n        const addContentWindowProxy = iframe => {\n          const contentWindowProxy = {\n            get(target, key) {\n              // Now to the interesting part:\n              // We actually make this thing behave like a regular iframe window,\n              // by intercepting calls to e.g. `.self` and redirect it to the correct thing. :)\n              // That makes it possible for these assertions to be correct:\n              // iframe.contentWindow.self === window.top // must be false\n              if (key === 'self') {\n                return this\n              }\n              // iframe.contentWindow.frameElement === iframe // must be true\n              if (key === 'frameElement') {\n                return iframe\n              }\n              // Intercept iframe.contentWindow[0] to hide the property 0 added by the proxy.\n              if (key === '0') {\n                return undefined\n              }\n              return Reflect.get(target, key)\n            }\n          }\n\n          if (!iframe.contentWindow) {\n            const proxy = new Proxy(window, contentWindowProxy)\n            Object.defineProperty(iframe, 'contentWindow', {\n              get() {\n                return proxy\n              },\n              set(newValue) {\n                return newValue // contentWindow is immutable\n              },\n              enumerable: true,\n              configurable: false\n            })\n          }\n        }\n\n        // Handles iframe element creation, augments `srcdoc` property so we can intercept further\n        const handleIframeCreation = (target, thisArg, args) => {\n          const iframe = target.apply(thisArg, args)\n\n          // We need to keep the originals around\n          const _iframe = iframe\n          const _srcdoc = _iframe.srcdoc\n\n          // Add hook for the srcdoc property\n          // We need to be very surgical here to not break other iframes by accident\n          Object.defineProperty(iframe, 'srcdoc', {\n            configurable: true, // Important, so we can reset this later\n            get: function() {\n              return _srcdoc\n            },\n            set: function(newValue) {\n              addContentWindowProxy(this)\n              // Reset property, the hook is only needed once\n              Object.defineProperty(iframe, 'srcdoc', {\n                configurable: false,\n                writable: false,\n                value: _srcdoc\n              })\n              _iframe.srcdoc = newValue\n            }\n          })\n          return iframe\n        }\n\n        // Adds a hook to intercept iframe creation events\n        const addIframeCreationSniffer = () => {\n          /* global document */\n          const createElementHandler = {\n            // Make toString() native\n            get(target, key) {\n              return Reflect.get(target, key)\n            },\n            apply: function(target, thisArg, args) {\n              const isIframe =\n                args && args.length && `${args[0]}`.toLowerCase() === 'iframe'\n              if (!isIframe) {\n                // Everything as usual\n                return target.apply(thisArg, args)\n              } else {\n                return handleIframeCreation(target, thisArg, args)\n              }\n            }\n          }\n          // All this just due to iframes with srcdoc bug\n          utils.replaceWithProxy(\n            document,\n            'createElement',\n            createElementHandler\n          )\n        }\n\n        // Let's go\n        addIframeCreationSniffer()\n      } catch (err) {\n        // console.warn(err)\n      }\n    }",_args:[]});"""

第三步,启动百度链接,百度链接包括了pc的链接(www.baidu.com) ,wap的链接(m.baidu.com),登录状态的链接(https://m.baidu.com/ssid=7679637730dddd2317c1a/s?) 等等同学们自己研究一下百度参数的作用

def qidongbaidu(driver):
    try:
        driver.get('http://m.baidu.com/')
        cookie_dic = driver.get_cookies()
        for co in cookie_dic:
            driver.add_cookie(co)
        driver.set_window_size(350, 844)
        time.sleep(1)
        return True
    except Exception as e:
        print("错误编码:1003;" + str(e))
        driver.quit()
        return False

第四步,开始查询链接,遍历要查询的列表

def qidongchaxun(driver):
    global chaxunci
    for data in chaxunci:
        try:
            keyword = data['biaoti']
            yuming = data['yuming']
            guanjianci = data['guanjianci']
            if keyword and yuming and guanjianci:
                time.sleep(0.5)
                chaxunjieguo = kaishipipei(driver,keyword, yuming, guanjianci)
                if not chaxunjieguo:
                    qidongjieguo  = qidongbaidu(driver)
                    if not qidongjieguo:
                        driver = qidongliulanqi()
        except Exception as e:
            print("错误编码:1002;"+ str(e))
            #raise Exception("错误编码:1002;浏览器关闭了")
            continue
    return True

第四步,在查询框输入要查询的词,并使用元素匹配或者列表内容,各位同学要注意selenium4.0 匹配元素变了,要是用find_element获取元素了,,更需要注意的是 find_elements 和find_element的区别哦 ,这两个的区别我很久才发现

def kaishipipei(driver,keyword, yuming, guanjianci):
	#这里我先判断一下是第一次查询还是第二次,因为第一次查询的页面和第二次页面是不一样滴,需要判断一下,python 如果找不到元素直接报错,只能用try了,我也是半吊子不知道怎么判断了
    try:
        driver.find_element(By.ID, "index-kw")
        flag = 1
    except:
        try:
            driver.find_element(By.ID, "kw")
            flag = 2
        except:
            flag = 3
    if flag == 3:
        return False
    if flag == 1:
        shurukuang = driver.find_element(By.ID, "index-kw")
        ActionChains(driver).move_to_element(shurukuang).click().perform()
        for kw in keyword:
            shurukuang.send_keys(kw)
            time.sleep(0.05)
        time.sleep(0.3)
        tijiaoanniu = driver.find_element(By.ID, "index-bn")
        ActionChains(driver).move_to_element(tijiaoanniu).click().perform()
        time.sleep(0.2)
        panduanyanzhengma(driver)
    if flag == 2:
        shurukuang = driver.find_element(By.ID, "kw")
        ActionChains(driver).move_to_element(shurukuang).click().perform()
        shurukuang.clear()
        for kw in keyword:
            shurukuang.send_keys(kw)
            time.sleep(0.05)
        time.sleep(0.3)
        tijiaoanniu = driver.find_element(By.ID, "se-bn")
        ActionChains(driver).move_to_element(tijiaoanniu).click().perform()
        time.sleep(0.3)
        panduanyanzhengma(driver)

    line_list = driver.find_elements(By.XPATH, "//div[@class='c-result result']")
    if not line_list:
        raise Exception("错误编码:1001;没有获取到搜索信息")
        return False

    shuchunneirong(line_list, keyword, yuming, guanjianci)
    return True

第五步,判断查询列表是否有验证码,如果有那就循环等待

def panduanyanzhengma(browser):
    try:
        but = browser.find_element(By.CSS_SELECTOR, ".mod-page-tipInfo-gray1")
        if but:
            print("需要人工去验证了")
            while True:
                line_list = browser.find_elements(By.XPATH, "//div[@class='c-result result']")
                print("等待人工验证...")
                time.sleep(3)
                if line_list:
                    break
    except Exception as e:
        return True

第六步:匹配查询内容和保存符合的链接

def shuchunneirong(data, keyword, yuming, guanjianci):
    print("关键词:" + str(keyword))
    global jieguomulu
    for line in data:
        res = line.get_attribute('data-log')
        #print("关键词:" + str(keyword) + ";结果:" + str(res))
        res = json.loads(res)

        paiming = 0
        url = ""
        if res['mu'] and res['order']:
            paiming = res['order']
            url = res['mu']
            if (url.find(yuming) > -1):
                if (url.find(guanjianci) > -1):
                    jieguo = (str(keyword) + "|" + str(url) + "|" + str(paiming))
                    with open(jieguomulu, "a+", encoding='utf-8') as f:
                        f.write(str(jieguo) + "\n")
            else:
                pass
                # print("关键词:"+str(keyword)+"-域名匹配-" + str(res))
        else:
            pass
            # print("关键词:"+str(keyword)+"-链接和排名匹配-" + str(res))
    return True

直接上全部代码,这是可以直接跑通的代码,注意zhiwenjs 这个方法,重新复制一下内容,看第二步,主要是我粘贴不进去这个js,因为太长了

# coding=utf-8

"""
用谷歌驱动浏览器查排名 
陈伟强
2022-9-22
要查文件  同级目录下  chaxunci.xls
文件格式3列
标题               域名        关键词
美女图片        baidu.com      meinv
"""
import time
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.chrome.service import Service as ChromeService
from selenium.webdriver.common.by import By
import json
import sys
import os
import xlrd
import shutil
from webdriver_manager.chrome import ChromeDriverManager
#window.navigator.webdriver
def qidongliulanqi():

    cache_dir  = appPath + "/cache"
    if not os.path.exists(cache_dir):
        os.makedirs(cache_dir, 0o777)

    mobileEmulation = {'deviceName': 'iPhone 12 Pro'}
    options = webdriver.ChromeOptions()
    options.add_experimental_option('mobileEmulation', mobileEmulation)
    options.add_experimental_option("detach", True)
    options.add_experimental_option('useAutomationExtension', False)
    options.add_experimental_option("excludeSwitches", ['enable-automation',"enable-logging"])
    options.add_argument('--start-maximized')
    options.add_argument('disable-infobars')
    options.add_argument('--disable-infobars')
    options.add_argument('--disk-cache-dir='+str(cache_dir))
    options.add_argument('--disk-cache-size=52428800')
    options.add_argument("--no-sandbox")
    options.add_argument("--disable-extensions")
    options.add_argument("--dns-prefetch-disable")
    options.add_argument("--disable-gpu")
    options.add_argument("--disable-browser-side-navigation")
    options.add_argument('--ignore-certificate-errors')
    options.add_argument('--ignore-ssl-errors')
    webdriver.DesiredCapabilities.CHROME['pageLoadStrategy'] = 'eager'

    try:
        service = ChromeService(executable_path=appPath + "\chromedriver.exe")
        driver = webdriver.Chrome(service=service, options=options)
    except:
        new_path = ChromeDriverManager().install()
        shutil.copy(new_path, appPath)
        service = ChromeService(executable_path=new_path)
        driver = webdriver.Chrome(service=service, options=options)

    driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
        "source": """
              Object.defineProperty(navigator, 'webdriver', {
                        get: () => undefined
              });"""
    })
    Map_coordinates = dict({
        "latitude": 39.731499,
        "longitude": 116.170212,
        "accuracy": 100
    })
    driver.execute_cdp_cmd("Emulation.setGeolocationOverride", Map_coordinates)
    driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
        "source": zhiwenjs()
    })
    js2 = '''Object.defineProperty(navigator, 'webdriver', {
                     get: () => undefined
                   })'''
    driver.execute_script(js2)
    driver.implicitly_wait(5)
    return driver
def panduanyanzhengma(browser):
    try:
        but = browser.find_element(By.CSS_SELECTOR, ".mod-page-tipInfo-gray1")
        if but:
            print("需要人工去验证了")
            while True:
                line_list = browser.find_elements(By.XPATH, "//div[@class='c-result result']")
                print("等待人工验证...")
                time.sleep(3)
                if line_list:
                    break
    except Exception as e:
        return True
def kaishipipei(driver,keyword, yuming, guanjianci):
    try:
        driver.find_element(By.ID, "index-kw")
        flag = 1
    except:
        try:
            driver.find_element(By.ID, "kw")
            flag = 2
        except:
            flag = 3
    if flag == 3:
        return False
    if flag == 1:
        shurukuang = driver.find_element(By.ID, "index-kw")
        ActionChains(driver).move_to_element(shurukuang).click().perform()
        for kw in keyword:
            shurukuang.send_keys(kw)
            time.sleep(0.05)
        time.sleep(0.3)
        tijiaoanniu = driver.find_element(By.ID, "index-bn")
        ActionChains(driver).move_to_element(tijiaoanniu).click().perform()
        time.sleep(0.2)
        panduanyanzhengma(driver)
    if flag == 2:
        shurukuang = driver.find_element(By.ID, "kw")
        ActionChains(driver).move_to_element(shurukuang).click().perform()
        shurukuang.clear()
        for kw in keyword:
            shurukuang.send_keys(kw)
            time.sleep(0.05)
        time.sleep(0.3)
        tijiaoanniu = driver.find_element(By.ID, "se-bn")
        ActionChains(driver).move_to_element(tijiaoanniu).click().perform()
        time.sleep(0.3)
        panduanyanzhengma(driver)

    line_list = driver.find_elements(By.XPATH, "//div[@class='c-result result']")
    if not line_list:
        raise Exception("错误编码:1001;没有获取到搜索信息")
        return False

    shuchunneirong(line_list, keyword, yuming, guanjianci)
    return True

def shuchunneirong(data, keyword, yuming, guanjianci):
    print("关键词:" + str(keyword))
    global jieguomulu
    for line in data:
        res = line.get_attribute('data-log')
        #print("关键词:" + str(keyword) + ";结果:" + str(res))
        res = json.loads(res)

        paiming = 0
        url = ""
        if res['mu'] and res['order']:
            paiming = res['order']
            url = res['mu']
            if (url.find(yuming) > -1):
                if (url.find(guanjianci) > -1):
                    jieguo = (str(keyword) + "|" + str(url) + "|" + str(paiming))
                    with open(jieguomulu, "a+", encoding='utf-8') as f:
                        f.write(str(jieguo) + "\n")
            else:
                pass
                # print("关键词:"+str(keyword)+"-域名匹配-" + str(res))
        else:
            pass
            # print("关键词:"+str(keyword)+"-链接和排名匹配-" + str(res))
    return True
def qidongbaidu(driver):
    try:
        driver.get('http://m.baidu.com/?tn=&from=#')
        cookie_dic = driver.get_cookies()
        for co in cookie_dic:
            driver.add_cookie(co)
        driver.set_window_size(350, 844)
        time.sleep(1)
        return True
    except Exception as e:
        print("错误编码:1003;" + str(e))
        driver.quit()
        return False
def qidongchaxun(driver):
    global chaxunci
    for data in chaxunci:
        try:
            keyword = data['biaoti']
            yuming = data['yuming']
            guanjianci = data['guanjianci']
            if keyword and yuming and guanjianci:
                time.sleep(0.5)
                chaxunjieguo = kaishipipei(driver,keyword, yuming, guanjianci)
                if not chaxunjieguo:
                    qidongjieguo  = qidongbaidu(driver)
                    if not qidongjieguo:
                        driver = qidongliulanqi()
        except Exception as e:
            print("错误编码:1002;"+ str(e))
            #raise Exception("错误编码:1002;浏览器关闭了")
            continue
    return True
def zhiwenjs():
	return """这个js我粘贴不进去直接复制上面的js吧"""
def run():
    driver = qidongliulanqi()
    qidongjeiguo = qidongbaidu(driver)
    if not qidongjeiguo:
        print("没有救了,启动不起来")
        exit(0)
    qidongchaxun(driver)
    print("查询结束了")
if __name__ == '__main__':
    if hasattr(sys, '_MEIPASS'):
        appPath = os.path.dirname(os.path.realpath(sys.executable))
    else:
        appPath, filename = os.path.split(os.path.abspath(__file__))
    ex_array = []
    try:
        data = xlrd.open_workbook(appPath + "\chaxunci.xls")
        table = data.sheets()[0]
        for rown in range(table.nrows):
            biaoti = table.cell_value(rown, 0)
            yuming = table.cell_value(rown, 1)
            guanjianci = table.cell_value(rown, 2)
            if (biaoti == '标题') or (yuming == '域名') or (guanjianci == '关键词'):
                continue
            array = {}
            array['biaoti'] = table.cell_value(rown, 0)
            array['yuming'] = table.cell_value(rown, 1)
            array['guanjianci'] = table.cell_value(rown, 2)
            ex_array.append(array)
    except:
        print("没有要查询词文档")
        exit(0)

    if not ex_array:
        print("没有要查询词文档")
        exit(0)
    now = time.strftime("%Y-%m-%d_%H-%M")
    chaxunci = ex_array
    jieguomulu = appPath + "\chaxuncijieguo-" + str(now) + ".txt";
    print("结果保存目录" + str(jieguomulu))
    run()

剧终了。
有什么问题,欢迎大家一起讨论,三人行必有我师焉。文章来源地址https://www.toymoban.com/news/detail-500753.html

到了这里,关于使用python+selenium谷歌浏览器驱动查排名的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Selenium安装WebDriver:ChromeDriver谷歌浏览器驱动下载安装与使用最新版118/119/120

    在使用之前的代码通过selenium操作Chrome浏览器时,安装中遇到了Chrome版本与浏览器驱动不匹配的的问题,在此记录安装下过程 我们首先确定自己的Chrome版本 Chrome设置-关于Chrome 可以看到,当前chrome是最新版本:119.0.6045.124(正式版本) (64 位)。 或者在浏览器的地址栏,输入

    2024年02月05日
    浏览(68)
  • 免费 Selenium各大浏览器驱动【谷歌chrme、火狐Firefox、IE浏览器】

    aardio群 625494397 废话不多说 直接开整! 竟然还有脸收费 服了 下载对应版本的浏览器驱动 目标网址 应用场景 Selenium库涉及到 安装selenium库 下载对应浏览器驱动 找到浏览器对应版本 最后直接上代码

    2024年02月16日
    浏览(75)
  • Python--使用selenium通过chromedriver调用谷歌浏览器

    原文网址:Python--使用selenium通过chromedriver调用谷歌浏览器_IT利刃出鞘的博客-CSDN博客         本文用实例介绍Python如何使用selenium通过chromedriver调用谷歌浏览器。 谷歌浏览器访问:chrome://version/ 注意:通过【帮助= 关于Google Chrome 】这种方式来查看会导致谷歌浏览器自动更新

    2023年04月21日
    浏览(104)
  • python使用selenium 打开谷歌浏览器闪退, 怎么解决

    在使用 Selenium 操作 Chrome 浏览器时,如果 Chrome 浏览器闪退,则可能是以下几个方面出现了问题: 1. Chromedriver 版本与 Chrome 浏览器版本不匹配 你需要确保你正在使用的 Chromedriver 版本与你的 Chrome 浏览器版本匹配。你可以在 Chromedriver 官网上找到最新版本的 Chromedriver,并下载

    2024年02月11日
    浏览(63)
  • python使用selenium 打开谷歌浏览器闪退解决办法

    在使用 Selenium 操作 Chrome 浏览器时,如果 Chrome 浏览器闪退,则可能是以下几个方面出现了问题: 1. Chromedriver 版本与 Chrome 浏览器版本不匹配 你需要确保你正在使用的 Chromedriver 版本与你的 Chrome 浏览器版本匹配。你可以在 Chromedriver 官网上找到最新版本的 Chromedriver,并下载

    2024年02月08日
    浏览(66)
  • python用selenium模拟谷歌浏览器点页面

    1、cmd安装selenium,输入pip install selenium 2、模拟点击热搜第一条进去,连接如下 https://weibo.com/newlogin?tabtype=weibogid=102803openLoginLayer=0url=https%3A%2F%2Fweibo.com%2F 3、查看谷歌版本   4、并去下面下载对应版本的webdriver,解压后把chromedriver.exe放入python目录 CNPM Binaries Mirror https://regist

    2024年01月21日
    浏览(58)
  • 使用selenium启动谷歌Chrome浏览器打开指定网站,页面空白,而使用其它浏览器手动打开该网站则正常

    在使用python实现自动化网络爬虫时,我使用到selenium来驱动谷歌Chrome浏览器来打开某一个网页,然后爬取数据,当使用Python中的selenium库驱动Chrome浏览器打开特定网站时,页面内容为空白,但在其他浏览器中手动访问该网站则显示正常。

    2024年02月07日
    浏览(77)
  • 使用Selenium需要浏览器驱动与浏览器版本统一

    安装浏览器驱动 针对不同的浏览器,需要安装不同的驱动。 这里以安装 Chrome 驱动作为演示。 确定浏览器版本 进入设置界面,然后选择 【关于 Chrome】 查看自己的版本信息。这里我的版本是114,下载对应版本的 Chrome 驱动即可。 下载驱动 打开Chrome驱动。单击对应的版本。(

    2024年02月08日
    浏览(53)
  • 【已解决】使用selenium启动谷歌Chrome浏览器打开指定网站,页面空白,而使用其它浏览器手动打开该网站则正常

    1、在使用python实现自动化网络爬虫时,我使用到selenium来驱动谷歌Chrome浏览器来打开某一个网页,然后爬取数据,代码如下:    2、但是当执行到driver.get(url)访问网站时,页面是空白的,如下所示,没有正常显示该网站的数据    print输出的网页html也只有如下数据: htmlhea

    2024年02月12日
    浏览(74)
  • python + selenium 自动下载浏览器驱动文件

    python + selenium 自动下载浏览器驱动文件

    2024年02月02日
    浏览(56)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包