{"version":3,"file":"getNativeImplementation.js","sources":["../../src/getNativeImplementation.ts"],"sourcesContent":["import { debug, isNativeFunction } from '@sentry/core';\nimport { DEBUG_BUILD } from './debug-build';\nimport { WINDOW } from './types';\n\n/**\n * We generally want to use window.fetch / window.setTimeout.\n * However, in some cases this may be wrapped (e.g. by Zone.js for Angular),\n * so we try to get an unpatched version of this from a sandboxed iframe.\n */\n\ninterface CacheableImplementations {\n setTimeout: typeof WINDOW.setTimeout;\n fetch: typeof WINDOW.fetch;\n}\n\nconst cachedImplementations: Partial = {};\n\n/**\n * Get the native implementation of a browser function.\n *\n * This can be used to ensure we get an unwrapped version of a function, in cases where a wrapped function can lead to problems.\n *\n * The following methods can be retrieved:\n * - `setTimeout`: This can be wrapped by e.g. Angular, causing change detection to be triggered.\n * - `fetch`: This can be wrapped by e.g. ad-blockers, causing an infinite loop when a request is blocked.\n */\nexport function getNativeImplementation(\n name: T,\n): CacheableImplementations[T] {\n const cached = cachedImplementations[name];\n if (cached) {\n return cached;\n }\n\n let impl = WINDOW[name] as CacheableImplementations[T];\n\n // Fast path to avoid DOM I/O\n if (isNativeFunction(impl)) {\n return (cachedImplementations[name] = impl.bind(WINDOW) as CacheableImplementations[T]);\n }\n\n const document = WINDOW.document;\n // eslint-disable-next-line deprecation/deprecation\n if (document && typeof document.createElement === 'function') {\n try {\n const sandbox = document.createElement('iframe');\n sandbox.hidden = true;\n document.head.appendChild(sandbox);\n const contentWindow = sandbox.contentWindow;\n if (contentWindow?.[name]) {\n impl = contentWindow[name] as CacheableImplementations[T];\n }\n document.head.removeChild(sandbox);\n } catch (e) {\n // Could not create sandbox iframe, just use window.xxx\n DEBUG_BUILD && debug.warn(`Could not create sandbox iframe for ${name} check, bailing to window.${name}: `, e);\n }\n }\n\n // Sanity check: This _should_ not happen, but if it does, we just skip caching...\n // This can happen e.g. in tests where fetch may not be available in the env, or similar.\n if (!impl) {\n return impl;\n }\n\n return (cachedImplementations[name] = impl.bind(WINDOW) as CacheableImplementations[T]);\n}\n\n/** Clear a cached implementation. */\nexport function clearCachedImplementation(name: keyof CacheableImplementations): void {\n cachedImplementations[name] = undefined;\n}\n\n/**\n * A special usecase for incorrectly wrapped Fetch APIs in conjunction with ad-blockers.\n * Whenever someone wraps the Fetch API and returns the wrong promise chain,\n * this chain becomes orphaned and there is no possible way to capture it's rejections\n * other than allowing it bubble up to this very handler. eg.\n *\n * const f = window.fetch;\n * window.fetch = function () {\n * const p = f.apply(this, arguments);\n *\n * p.then(function() {\n * console.log('hi.');\n * });\n *\n * return p;\n * }\n *\n * `p.then(function () { ... })` is producing a completely separate promise chain,\n * however, what's returned is `p` - the result of original `fetch` call.\n *\n * This mean, that whenever we use the Fetch API to send our own requests, _and_\n * some ad-blocker blocks it, this orphaned chain will _always_ reject,\n * effectively causing another event to be captured.\n * This makes a whole process become an infinite loop, which we need to somehow\n * deal with, and break it in one way or another.\n *\n * To deal with this issue, we are making sure that we _always_ use the real\n * browser Fetch API, instead of relying on what `window.fetch` exposes.\n * The only downside to this would be missing our own requests as breadcrumbs,\n * but because we are already not doing this, it should be just fine.\n *\n * Possible failed fetch error messages per-browser:\n *\n * Chrome: Failed to fetch\n * Edge: Failed to Fetch\n * Firefox: NetworkError when attempting to fetch resource\n * Safari: resource blocked by content blocker\n */\nexport function fetch(...rest: Parameters): ReturnType {\n return getNativeImplementation('fetch')(...rest);\n}\n\n/**\n * Get an unwrapped `setTimeout` method.\n * This ensures that even if e.g. Angular wraps `setTimeout`, we get the native implementation,\n * avoiding triggering change detection.\n */\nexport function setTimeout(...rest: Parameters): ReturnType {\n return getNativeImplementation('setTimeout')(...rest);\n}\n"],"names":[],"mappings":";;;;AAeA,MAAM,wBAA2D,EAAC;AAW3D,SAAS,wBACd,IAAA,EAC6B;AAC7B,EAAA,MAAM,MAAA,GAAS,sBAAsB,IAAI,CAAA;AACzC,EAAA,IAAI,MAAA,EAAQ;AACV,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,IAAI,IAAA,GAAO,OAAO,IAAI,CAAA;AAGtB,EAAA,IAAI,gBAAA,CAAiB,IAAI,CAAA,EAAG;AAC1B,IAAA,OAAQ,qBAAA,CAAsB,IAAI,CAAA,GAAI,IAAA,CAAK,KAAK,MAAM,CAAA;AAAA,EACxD;AAEA,EAAA,MAAM,WAAW,MAAA,CAAO,QAAA;AAExB,EAAA,IAAI,QAAA,IAAY,OAAO,QAAA,CAAS,aAAA,KAAkB,UAAA,EAAY;AAC5D,IAAA,IAAI;AACF,MAAA,MAAM,OAAA,GAAU,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA;AAC/C,MAAA,OAAA,CAAQ,MAAA,GAAS,IAAA;AACjB,MAAA,QAAA,CAAS,IAAA,CAAK,YAAY,OAAO,CAAA;AACjC,MAAA,MAAM,gBAAgB,OAAA,CAAQ,aAAA;AAC9B,MAAA,IAAI,aAAA,GAAgB,IAAI,CAAA,EAAG;AACzB,QAAA,IAAA,GAAO,cAAc,IAAI,CAAA;AAAA,MAC3B;AACA,MAAA,QAAA,CAAS,IAAA,CAAK,YAAY,OAAO,CAAA;AAAA,IACnC,SAAS,CAAA,EAAG;AAEV,MAAA,WAAA,IAAe,MAAM,IAAA,CAAK,CAAA,oCAAA,EAAuC,IAAI,CAAA,0BAAA,EAA6B,IAAI,MAAM,CAAC,CAAA;AAAA,IAC/G;AAAA,EACF;AAIA,EAAA,IAAI,CAAC,IAAA,EAAM;AACT,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,OAAQ,qBAAA,CAAsB,IAAI,CAAA,GAAI,IAAA,CAAK,KAAK,MAAM,CAAA;AACxD;AAGO,SAAS,0BAA0B,IAAA,EAA4C;AACpF,EAAA,qBAAA,CAAsB,IAAI,CAAA,GAAI,MAAA;AAChC;AAwCO,SAAS,SAAS,IAAA,EAAwE;AAC/F,EAAA,OAAO,uBAAA,CAAwB,OAAO,CAAA,CAAE,GAAG,IAAI,CAAA;AACjD;AAOO,SAAS,cAAc,IAAA,EAAkF;AAC9G,EAAA,OAAO,uBAAA,CAAwB,YAAY,CAAA,CAAE,GAAG,IAAI,CAAA;AACtD;;;;"}