Module: Applitools::Selenium::Scripts

Defined in:
lib/applitools/selenium/scripts/get_element_xpath.rb,
lib/applitools/selenium/scripts/process_page_and_poll.rb,
lib/applitools/selenium/scripts/process_page_and_serialize.rb

Constant Summary collapse

GET_ELEMENT_XPATH_JS =
"        var el = arguments[0];\n        var xpath = '';\n        do {\n          var parent = el.parentElement;\n          var index = 1;\n          if (parent !== null) {\n            var children = parent.children;\n            for (var childIdx in children) {\n              var child = children[childIdx];\n              if (child === el) break;\n              if (child.tagName === el.tagName) index++;\n            }\n          }\n          xpath = '/' + el.tagName + '[' + index + ']' + xpath;\n          el = parent;\n        } while (el !== null);\n        return '/' + xpath;\n"
PROCESS_PAGE_AND_POLL =
"// @applitools/[email protected]\nfunction __processPageAndPoll() {\n  var processPageAndPoll = (function () {\n  'use strict';\n\n  // This code was copied and modified from https://github.com/beatgammit/base64-js/blob/bf68aaa277/index.js\n  // License: https://github.com/beatgammit/base64-js/blob/bf68aaa277d9de7007cc0c58279c411bb10670ac/LICENSE\n\n  function arrayBufferToBase64(ab) {\n    const lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split('');\n\n    const uint8 = new Uint8Array(ab);\n    const len = uint8.length;\n    const extraBytes = len % 3; // if we have 1 byte left, pad 2 bytes\n    const parts = [];\n    const maxChunkLength = 16383; // must be multiple of 3\n\n    let tmp;\n\n    // go through the array every three bytes, we'll deal with trailing stuff later\n    for (let i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {\n      parts.push(encodeChunk(i, i + maxChunkLength > len2 ? len2 : i + maxChunkLength));\n    }\n\n    // pad the end with zeros, but make sure to not forget the extra bytes\n    if (extraBytes === 1) {\n      tmp = uint8[len - 1];\n      parts.push(lookup[tmp >> 2] + lookup[(tmp << 4) & 0x3f] + '==');\n    } else if (extraBytes === 2) {\n      tmp = (uint8[len - 2] << 8) + uint8[len - 1];\n      parts.push(lookup[tmp >> 10] + lookup[(tmp >> 4) & 0x3f] + lookup[(tmp << 2) & 0x3f] + '=');\n    }\n\n    return parts.join('');\n\n    function tripletToBase64(num) {\n      return (\n        lookup[(num >> 18) & 0x3f] +\n        lookup[(num >> 12) & 0x3f] +\n        lookup[(num >> 6) & 0x3f] +\n        lookup[num & 0x3f]\n      );\n    }\n\n    function encodeChunk(start, end) {\n      let tmp;\n      const output = [];\n      for (let i = start; i < end; i += 3) {\n        tmp = ((uint8[i] << 16) & 0xff0000) + ((uint8[i + 1] << 8) & 0xff00) + (uint8[i + 2] & 0xff);\n        output.push(tripletToBase64(tmp));\n      }\n      return output.join('');\n    }\n  }\n\n  var arrayBufferToBase64_1 = arrayBufferToBase64;\n\n  function extractLinks(doc = document) {\n    const srcsetUrls = [...doc.querySelectorAll('img[srcset],source[srcset]')]\n      .map(srcsetEl =>\n        srcsetEl\n          .getAttribute('srcset')\n          .split(',')\n          .map(str => str.trim().split(/\\s+/)[0]),\n      )\n      .reduce((acc, urls) => acc.concat(urls), []);\n\n    const srcUrls = [...doc.querySelectorAll('img[src],source[src]')].map(srcEl =>\n      srcEl.getAttribute('src'),\n    );\n\n    const hrefUrls = [...doc.querySelectorAll('image')]\n      .map(hrefEl => hrefEl.getAttribute('href') || hrefEl.getAttribute('xlink:href'))\n      .filter(Boolean);\n\n    const cssUrls = [...doc.querySelectorAll('link[rel=\"stylesheet\"]')].map(link =>\n      link.getAttribute('href'),\n    );\n\n    const videoPosterUrls = [...doc.querySelectorAll('video[poster]')].map(videoEl =>\n      videoEl.getAttribute('poster'),\n    );\n\n    return [...srcsetUrls, ...srcUrls, ...hrefUrls, ...cssUrls, ...videoPosterUrls];\n  }\n\n  var extractLinks_1 = extractLinks;\n\n  /* eslint-disable no-use-before-define */\n\n  function domNodesToCdt(docNode) {\n    const NODE_TYPES = {\n      ELEMENT: 1,\n      TEXT: 3,\n      DOCUMENT: 9,\n      DOCUMENT_TYPE: 10,\n      DOCUMENT_FRAGMENT_NODE: 11,\n    };\n\n    const domNodes = [\n      {\n        nodeType: NODE_TYPES.DOCUMENT,\n      },\n    ];\n    domNodes[0].childNodeIndexes = childrenFactory(domNodes, docNode.childNodes);\n    return domNodes;\n\n    function childrenFactory(domNodes, elementNodes) {\n      if (!elementNodes || elementNodes.length === 0) return null;\n\n      const childIndexes = [];\n      elementNodes.forEach(elementNode => {\n        const index = elementNodeFactory(domNodes, elementNode);\n        if (index !== null) {\n          childIndexes.push(index);\n        }\n      });\n\n      return childIndexes;\n    }\n\n    function elementNodeFactory(domNodes, elementNode) {\n      let node, manualChildNodeIndexes;\n      const {nodeType} = elementNode;\n      if ([NODE_TYPES.ELEMENT, NODE_TYPES.DOCUMENT_FRAGMENT_NODE].includes(nodeType)) {\n        if (elementNode.nodeName !== 'SCRIPT') {\n          if (\n            elementNode.nodeName === 'STYLE' &&\n            elementNode.sheet &&\n            elementNode.sheet.cssRules.length\n          ) {\n            domNodes.push({\n              nodeType: NODE_TYPES.TEXT,\n              nodeValue: [...elementNode.sheet.cssRules].map(rule => rule.cssText).join(''),\n            });\n            manualChildNodeIndexes = [domNodes.length - 1];\n          }\n\n          node = {\n            nodeType: nodeType,\n            nodeName: elementNode.nodeName,\n            attributes: nodeAttributes(elementNode).map(key => {\n              let value = elementNode.attributes[key].value;\n              const name = elementNode.attributes[key].localName;\n\n              if (/^blob:/.test(value)) {\n                value = value.replace(/^blob:/, '');\n              } else if (\n                elementNode.nodeName === 'IFRAME' &&\n                name === 'src' &&\n                !elementNode.contentDocument &&\n                !value.match(/^\\s*data:/)\n              ) {\n                value = '';\n              }\n              return {\n                name,\n                value,\n              };\n            }),\n            childNodeIndexes:\n              manualChildNodeIndexes ||\n              (elementNode.childNodes.length\n                ? childrenFactory(domNodes, elementNode.childNodes)\n                : []),\n          };\n\n          if (elementNode.shadowRoot) {\n            node.shadowRootIndex = elementNodeFactory(domNodes, elementNode.shadowRoot);\n          }\n\n          if (elementNode.checked && !elementNode.attributes.checked) {\n            node.attributes.push({name: 'checked', value: 'checked'});\n          }\n          if (\n            elementNode.value !== undefined &&\n            elementNode.attributes.value === undefined &&\n            elementNode.tagName === 'INPUT'\n          ) {\n            node.attributes.push({name: 'value', value: elementNode.value});\n          }\n        } else {\n          node = {\n            nodeType: NODE_TYPES.ELEMENT,\n            nodeName: 'SCRIPT',\n            attributes: nodeAttributes(elementNode)\n              .map(key => ({\n                name: elementNode.attributes[key].localName,\n                value: elementNode.attributes[key].value,\n              }))\n              .filter(attr => attr.name !== 'src'),\n            childNodeIndexes: [],\n          };\n        }\n      } else if (nodeType === NODE_TYPES.TEXT) {\n        node = {\n          nodeType: NODE_TYPES.TEXT,\n          nodeValue: elementNode.nodeValue,\n        };\n      } else if (nodeType === NODE_TYPES.DOCUMENT_TYPE) {\n        node = {\n          nodeType: NODE_TYPES.DOCUMENT_TYPE,\n          nodeName: elementNode.nodeName,\n        };\n      }\n\n      if (node) {\n        domNodes.push(node);\n        return domNodes.length - 1;\n      } else {\n        // console.log(`Unknown nodeType: ${nodeType}`);\n        return null;\n      }\n\n      function nodeAttributes({attributes = {}}) {\n        return Object.keys(attributes).filter(k => attributes[k].localName);\n      }\n    }\n  }\n\n  var domNodesToCdt_1 = domNodesToCdt;\n  var NODE_TYPES = {\n    ELEMENT: 1,\n    TEXT: 3,\n    DOCUMENT: 9,\n    DOCUMENT_TYPE: 10,\n  };\n  domNodesToCdt_1.NODE_TYPES = NODE_TYPES;\n\n  function extractFrames(doc = document) {\n    return [...doc.querySelectorAll('iframe[src]:not([src=\"\"])')]\n      .map(srcEl => {\n        try {\n          const contentDoc = srcEl.contentDocument;\n          return (\n            contentDoc &&\n            /^https?:$/.test(contentDoc.location.protocol) &&\n            contentDoc.defaultView &&\n            contentDoc.defaultView.frameElement &&\n            contentDoc\n          );\n        } catch (err) {\n          //for CORS frames\n        }\n      })\n      .filter(x => !!x);\n  }\n\n  var extractFrames_1 = extractFrames;\n\n  function uniq(arr) {\n    const result = [];\n    new Set(arr).forEach(v => v && result.push(v));\n    return result;\n  }\n\n  var uniq_1 = uniq;\n\n  function aggregateResourceUrlsAndBlobs(resourceUrlsAndBlobsArr) {\n    return resourceUrlsAndBlobsArr.reduce(\n      ({resourceUrls: allResourceUrls, blobsObj: allBlobsObj}, {resourceUrls, blobsObj}) => ({\n        resourceUrls: uniq_1(allResourceUrls.concat(resourceUrls)),\n        blobsObj: Object.assign(allBlobsObj, blobsObj),\n      }),\n      {resourceUrls: [], blobsObj: {}},\n    );\n  }\n\n  var aggregateResourceUrlsAndBlobs_1 = aggregateResourceUrlsAndBlobs;\n\n  function makeGetResourceUrlsAndBlobs({processResource, aggregateResourceUrlsAndBlobs}) {\n    return function getResourceUrlsAndBlobs(doc, baseUrl, urls) {\n      return Promise.all(\n        urls.map(url => processResource(url, doc, baseUrl, getResourceUrlsAndBlobs.bind(null, doc))),\n      ).then(resourceUrlsAndBlobsArr => aggregateResourceUrlsAndBlobs(resourceUrlsAndBlobsArr));\n    };\n  }\n\n  var getResourceUrlsAndBlobs = makeGetResourceUrlsAndBlobs;\n\n  function filterInlineUrl(absoluteUrl) {\n    return /^(blob|https?):/.test(absoluteUrl);\n  }\n\n  var filterInlineUrl_1 = filterInlineUrl;\n\n  function absolutizeUrl(url, absoluteUrl) {\n    return new URL(url, absoluteUrl).href;\n  }\n\n  var absolutizeUrl_1 = absolutizeUrl;\n\n  function makeProcessResource({\n    fetchUrl,\n    findStyleSheetByUrl,\n    extractResourcesFromStyleSheet,\n    isSameOrigin,\n    cache = {},\n  }) {\n    return function processResource(absoluteUrl, doc, baseUrl, getResourceUrlsAndBlobs) {\n      return cache[absoluteUrl] || (cache[absoluteUrl] = doProcessResource(absoluteUrl));\n\n      function doProcessResource(url) {\n        return fetchUrl(url)\n          .catch(e => {\n            if (probablyCORS(e, url)) {\n              return {probablyCORS: true, url};\n            } else {\n              throw e;\n            }\n          })\n          .then(({url, type, value, probablyCORS}) => {\n            if (probablyCORS) {\n              return {resourceUrls: [url]};\n            }\n            const result = {blobsObj: {[url]: {type, value}}};\n            if (/text\\/css/.test(type)) {\n              const styleSheet = findStyleSheetByUrl(url, doc);\n              if (!styleSheet) {\n                return result;\n              }\n              const resourceUrls = extractResourcesFromStyleSheet(styleSheet, doc.defaultView)\n                .map(resourceUrl => absolutizeUrl_1(resourceUrl, url.replace(/^blob:/, '')))\n                .filter(filterInlineUrl_1);\n              return getResourceUrlsAndBlobs(baseUrl, resourceUrls).then(\n                ({resourceUrls, blobsObj}) => ({\n                  resourceUrls,\n                  blobsObj: Object.assign(blobsObj, {[url]: {type, value}}),\n                }),\n              );\n            } else {\n              return result;\n            }\n          })\n          .catch(err => {\n            console.log('[dom-snapshot] error while fetching', url, err);\n            return {};\n          });\n      }\n\n      function probablyCORS(err, url) {\n        const msgCORS = err.message && err.message.includes('Failed to fetch');\n        const nameCORS = err.name && err.name.includes('TypeError');\n        return msgCORS && nameCORS && !isSameOrigin(url, baseUrl);\n      }\n    };\n  }\n\n  var processResource = makeProcessResource;\n\n  /* global window */\n\n  function fetchUrl(url, fetch = window.fetch) {\n    return fetch(url, {cache: 'force-cache', credentials: 'same-origin'}).then(resp =>\n      resp.arrayBuffer().then(buff => ({\n        url,\n        type: resp.headers.get('Content-Type'),\n        value: buff,\n      })),\n    );\n  }\n\n  var fetchUrl_1 = fetchUrl;\n\n  function makeFindStyleSheetByUrl({styleSheetCache}) {\n    return function findStyleSheetByUrl(url, doc) {\n      return styleSheetCache[url] || [...doc.styleSheets].find(styleSheet => styleSheet.href === url);\n    };\n  }\n\n  var findStyleSheetByUrl = makeFindStyleSheetByUrl;\n\n  function getUrlFromCssText(cssText) {\n    const re = /url\\((?!['\"]?:)['\"]?([^'\")]*)['\"]?\\)/g;\n    const ret = [];\n    let result;\n    while ((result = re.exec(cssText)) !== null) {\n      ret.push(result[1]);\n    }\n    return ret;\n  }\n\n  var getUrlFromCssText_1 = getUrlFromCssText;\n\n  // NOTE this code is very similar to the node part of visual-grid-client, but there is a different related to the browser's cssom with import rules\n  function makeExtractResourcesFromStyleSheet({styleSheetCache}) {\n    return function extractResourcesFromStyleSheet(styleSheet, win = window) {\n      return uniq_1(\n        [...(styleSheet.cssRules || [])].reduce((acc, rule) => {\n          if (rule instanceof win.CSSImportRule) {\n            styleSheetCache[rule.styleSheet.href] = rule.styleSheet;\n            return acc.concat(rule.href);\n          } else if (rule instanceof win.CSSFontFaceRule) {\n            return acc.concat(getUrlFromCssText_1(rule.style.getPropertyValue('src')));\n          } else if (rule instanceof win.CSSSupportsRule || rule instanceof win.CSSMediaRule) {\n            return acc.concat(extractResourcesFromStyleSheet(rule));\n          } else if (rule instanceof win.CSSStyleRule) {\n            for (let i = 0, ii = rule.style.length; i < ii; i++) {\n              const urls = getUrlFromCssText_1(rule.style.getPropertyValue(rule.style[i]));\n              urls.length && (acc = acc.concat(urls));\n            }\n          }\n          return acc;\n        }, []),\n      );\n    };\n  }\n\n  var extractResourcesFromStyleSheet = makeExtractResourcesFromStyleSheet;\n\n  function extractResourceUrlsFromStyleAttrs(cdt) {\n    return cdt.reduce((acc, node) => {\n      if (node.nodeType === 1) {\n        const styleAttr =\n          node.attributes && node.attributes.find(attr => attr.name.toUpperCase() === 'STYLE');\n\n        if (styleAttr) acc = acc.concat(getUrlFromCssText_1(styleAttr.value));\n      }\n      return acc;\n    }, []);\n  }\n\n  var extractResourceUrlsFromStyleAttrs_1 = extractResourceUrlsFromStyleAttrs;\n\n  function makeExtractResourceUrlsFromStyleTags(extractResourcesFromStyleSheet) {\n    return function extractResourceUrlsFromStyleTags(doc) {\n      return uniq_1(\n        [...doc.getElementsByTagName('style')].reduce((resourceUrls, styleEl) => {\n          const styleSheet = [...doc.styleSheets].find(\n            styleSheet => styleSheet.ownerNode === styleEl,\n          );\n          return resourceUrls.concat(extractResourcesFromStyleSheet(styleSheet, doc.defaultView));\n        }, []),\n      );\n    };\n  }\n\n  var extractResourceUrlsFromStyleTags = makeExtractResourceUrlsFromStyleTags;\n\n  function isSameOrigin(url, baseUrl) {\n    const blobOrData = /^(blob|data):/;\n    if (blobOrData.test(url)) return true;\n    if (blobOrData.test(baseUrl)) return false;\n\n    const {origin} = new URL(url, baseUrl);\n    const {origin: baseOrigin} = new URL(baseUrl);\n    return origin === baseOrigin;\n  }\n\n  var isSameOrigin_1 = isSameOrigin;\n\n  function processPage(doc = document) {\n    const styleSheetCache = {};\n    const extractResourcesFromStyleSheet$$1 = extractResourcesFromStyleSheet({styleSheetCache});\n    const findStyleSheetByUrl$$1 = findStyleSheetByUrl({styleSheetCache});\n    const processResource$$1 = processResource({\n      fetchUrl: fetchUrl_1,\n      findStyleSheetByUrl: findStyleSheetByUrl$$1,\n      extractResourcesFromStyleSheet: extractResourcesFromStyleSheet$$1,\n      absolutizeUrl: absolutizeUrl_1,\n      isSameOrigin: isSameOrigin_1,\n    });\n\n    const getResourceUrlsAndBlobs$$1 = getResourceUrlsAndBlobs({\n      processResource: processResource$$1,\n      aggregateResourceUrlsAndBlobs: aggregateResourceUrlsAndBlobs_1,\n    });\n\n    const extractResourceUrlsFromStyleTags$$1 = extractResourceUrlsFromStyleTags(\n      extractResourcesFromStyleSheet$$1,\n    );\n\n    return doProcessPage(doc);\n\n    function doProcessPage(doc) {\n      const frameElement = doc.defaultView && doc.defaultView.frameElement;\n      const url = frameElement ? frameElement.src : doc.location.href;\n\n      const cdt = domNodesToCdt_1(doc);\n\n      const links = uniq_1(\n        extractLinks_1(doc)\n          .concat(extractResourceUrlsFromStyleAttrs_1(cdt))\n          .concat(extractResourceUrlsFromStyleTags$$1(doc)),\n      )\n        .map(absolutizeThisUrl)\n        .filter(filterInlineUrlsIfExisting);\n\n      const resourceUrlsAndBlobsPromise = getResourceUrlsAndBlobs$$1(doc, url, links);\n\n      const frameDocs = extractFrames_1(doc);\n      const processFramesPromise = frameDocs.map(doProcessPage);\n\n      return Promise.all([resourceUrlsAndBlobsPromise, ...processFramesPromise]).then(\n        ([{resourceUrls, blobsObj}, ...framesResults]) => ({\n          cdt,\n          url,\n          resourceUrls,\n          blobs: blobsObjToArray(blobsObj),\n          frames: framesResults,\n          srcAttr: frameElement ? frameElement.getAttribute('src') : undefined,\n        }),\n      );\n\n      function absolutizeThisUrl(someUrl) {\n        try {\n          return absolutizeUrl_1(someUrl, url);\n        } catch (err) {\n          // can't do anything with a non-absolute url\n        }\n      }\n    }\n  }\n\n  function blobsObjToArray(blobsObj) {\n    return Object.keys(blobsObj).map(blobUrl =>\n      Object.assign(\n        {\n          url: blobUrl.replace(/^blob:/, ''),\n        },\n        blobsObj[blobUrl],\n      ),\n    );\n  }\n\n  function filterInlineUrlsIfExisting(absoluteUrl) {\n    return absoluteUrl && filterInlineUrl_1(absoluteUrl);\n  }\n\n  var processPage_1 = processPage;\n\n  function processPageAndSerialize(doc) {\n    return processPage_1(doc).then(serializeFrame);\n  }\n\n  function serializeFrame(frame) {\n    frame.blobs = frame.blobs.map(({url, type, value}) => ({\n      url,\n      type,\n      value: arrayBufferToBase64_1(value),\n    }));\n    frame.frames.forEach(serializeFrame);\n    return frame;\n  }\n\n  var processPageAndSerialize_1 = processPageAndSerialize;\n\n  const EYES_NAME_SPACE = '__EYES__APPLITOOLS__';\n\n  function processPageAndPoll(doc) {\n    if (!window[EYES_NAME_SPACE]) {\n      window[EYES_NAME_SPACE] = {};\n    }\n    if (!window[EYES_NAME_SPACE].processPageAndSerializeResult) {\n      window[EYES_NAME_SPACE].processPageAndSerializeResult = {\n        status: 'WIP',\n        value: null,\n        error: null,\n      };\n      processPageAndSerialize_1(doc)\n        .then(r => ((resultObject.status = 'SUCCESS'), (resultObject.value = r)))\n        .catch(e => ((resultObject.status = 'ERROR'), (resultObject.error = e.message)));\n    }\n\n    const resultObject = window[EYES_NAME_SPACE].processPageAndSerializeResult;\n    if (resultObject.status === 'SUCCESS') {\n      window[EYES_NAME_SPACE].processPageAndSerializeResult = null;\n    }\n\n    return JSON.stringify(resultObject);\n  }\n\n  var processPageAndPoll_1 = processPageAndPoll;\n\n  return processPageAndPoll_1;\n\n}());\n\n  return processPageAndPoll.apply(this, arguments);\n}\n"
PROCESS_RESOURCES =
"// @applitools/[email protected]\nfunction __processPageAndSerialize() {\n  var processPageAndSerialize = (function () {\n  'use strict';\n\n  // This code was copied and modified from https://github.com/beatgammit/base64-js/blob/bf68aaa277/index.js\n  // License: https://github.com/beatgammit/base64-js/blob/bf68aaa277d9de7007cc0c58279c411bb10670ac/LICENSE\n\n  function arrayBufferToBase64(ab) {\n    const lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split('');\n\n    const uint8 = new Uint8Array(ab);\n    const len = uint8.length;\n    const extraBytes = len % 3; // if we have 1 byte left, pad 2 bytes\n    const parts = [];\n    const maxChunkLength = 16383; // must be multiple of 3\n\n    let tmp;\n\n    // go through the array every three bytes, we'll deal with trailing stuff later\n    for (let i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {\n      parts.push(encodeChunk(i, i + maxChunkLength > len2 ? len2 : i + maxChunkLength));\n    }\n\n    // pad the end with zeros, but make sure to not forget the extra bytes\n    if (extraBytes === 1) {\n      tmp = uint8[len - 1];\n      parts.push(lookup[tmp >> 2] + lookup[(tmp << 4) & 0x3f] + '==');\n    } else if (extraBytes === 2) {\n      tmp = (uint8[len - 2] << 8) + uint8[len - 1];\n      parts.push(lookup[tmp >> 10] + lookup[(tmp >> 4) & 0x3f] + lookup[(tmp << 2) & 0x3f] + '=');\n    }\n\n    return parts.join('');\n\n    function tripletToBase64(num) {\n      return (\n        lookup[(num >> 18) & 0x3f] +\n        lookup[(num >> 12) & 0x3f] +\n        lookup[(num >> 6) & 0x3f] +\n        lookup[num & 0x3f]\n      );\n    }\n\n    function encodeChunk(start, end) {\n      let tmp;\n      const output = [];\n      for (let i = start; i < end; i += 3) {\n        tmp = ((uint8[i] << 16) & 0xff0000) + ((uint8[i + 1] << 8) & 0xff00) + (uint8[i + 2] & 0xff);\n        output.push(tripletToBase64(tmp));\n      }\n      return output.join('');\n    }\n  }\n\n  var arrayBufferToBase64_1 = arrayBufferToBase64;\n\n  function extractLinks(doc = document) {\n    const srcsetUrls = [...doc.querySelectorAll('img[srcset],source[srcset]')]\n      .map(srcsetEl =>\n        srcsetEl\n          .getAttribute('srcset')\n          .split(',')\n          .map(str => str.trim().split(/\\s+/)[0]),\n      )\n      .reduce((acc, urls) => acc.concat(urls), []);\n\n    const srcUrls = [...doc.querySelectorAll('img[src],source[src]')].map(srcEl =>\n      srcEl.getAttribute('src'),\n    );\n\n    const cssUrls = [...doc.querySelectorAll('link[rel=\"stylesheet\"]')].map(link =>\n      link.getAttribute('href'),\n    );\n\n    const videoPosterUrls = [...doc.querySelectorAll('video[poster]')].map(videoEl =>\n      videoEl.getAttribute('poster'),\n    );\n\n    return [...srcsetUrls, ...srcUrls, ...cssUrls, ...videoPosterUrls];\n  }\n\n  var extractLinks_1 = extractLinks;\n\n  /* eslint-disable no-use-before-define */\n\n  function domNodesToCdt(docNode) {\n    const NODE_TYPES = {\n      ELEMENT: 1,\n      TEXT: 3,\n      DOCUMENT: 9,\n      DOCUMENT_TYPE: 10,\n      DOCUMENT_FRAGMENT_NODE: 11,\n    };\n\n    const domNodes = [\n      {\n        nodeType: NODE_TYPES.DOCUMENT,\n      },\n    ];\n    domNodes[0].childNodeIndexes = childrenFactory(domNodes, docNode.childNodes);\n    return domNodes;\n\n    function childrenFactory(domNodes, elementNodes) {\n      if (!elementNodes || elementNodes.length === 0) return null;\n\n      const childIndexes = [];\n      elementNodes.forEach(elementNode => {\n        const index = elementNodeFactory(domNodes, elementNode);\n        if (index !== null) {\n          childIndexes.push(index);\n        }\n      });\n\n      return childIndexes;\n    }\n\n    function elementNodeFactory(domNodes, elementNode) {\n      let node;\n      const {nodeType} = elementNode;\n      if ([NODE_TYPES.ELEMENT, NODE_TYPES.DOCUMENT_FRAGMENT_NODE].includes(nodeType)) {\n        if (elementNode.nodeName !== 'SCRIPT') {\n          if (\n            elementNode.nodeName === 'STYLE' &&\n            !elementNode.textContent &&\n            elementNode.sheet &&\n            elementNode.sheet.cssRules.length\n          ) {\n            elementNode.appendChild(\n              docNode.createTextNode(\n                [...elementNode.sheet.cssRules].map(rule => rule.cssText).join(''),\n              ),\n            );\n          }\n\n          node = {\n            nodeType: nodeType,\n            nodeName: elementNode.nodeName,\n            attributes: Object.keys(elementNode.attributes || {}).map(key => {\n              let value = elementNode.attributes[key].value;\n              const name = elementNode.attributes[key].localName;\n\n              if (/^blob:/.test(value)) {\n                value = value.replace(/^blob:/, '');\n              }\n\n              return {\n                name,\n                value,\n              };\n            }),\n            childNodeIndexes: elementNode.childNodes.length\n              ? childrenFactory(domNodes, elementNode.childNodes)\n              : [],\n          };\n\n          if (elementNode.shadowRoot) {\n            node.shadowRootIndex = elementNodeFactory(domNodes, elementNode.shadowRoot);\n          }\n\n          if (elementNode.checked && !elementNode.attributes.checked) {\n            node.attributes.push({name: 'checked', value: 'checked'});\n          }\n          if (\n            elementNode.value !== undefined &&\n            elementNode.attributes.value === undefined &&\n            elementNode.tagName === 'INPUT'\n          ) {\n            node.attributes.push({name: 'value', value: elementNode.value});\n          }\n        }\n      } else if (nodeType === NODE_TYPES.TEXT) {\n        node = {\n          nodeType: NODE_TYPES.TEXT,\n          nodeValue: elementNode.nodeValue,\n        };\n      } else if (nodeType === NODE_TYPES.DOCUMENT_TYPE) {\n        node = {\n          nodeType: NODE_TYPES.DOCUMENT_TYPE,\n          nodeName: elementNode.nodeName,\n        };\n      }\n\n      if (node) {\n        domNodes.push(node);\n        return domNodes.length - 1;\n      } else {\n        // console.log(`Unknown nodeType: ${nodeType}`);\n        return null;\n      }\n    }\n  }\n\n  var domNodesToCdt_1 = domNodesToCdt;\n  var NODE_TYPES = {\n    ELEMENT: 1,\n    TEXT: 3,\n    DOCUMENT: 9,\n    DOCUMENT_TYPE: 10,\n  };\n  domNodesToCdt_1.NODE_TYPES = NODE_TYPES;\n\n  function extractFrames(doc = document) {\n    return [...doc.querySelectorAll('iframe[src]:not([src=\"\"])')]\n      .map(srcEl => {\n        try {\n          const contentDoc = srcEl.contentDocument;\n          return (\n            contentDoc &&\n            /^https?:$/.test(contentDoc.location.protocol) &&\n            contentDoc.defaultView &&\n            contentDoc.defaultView.frameElement &&\n            contentDoc\n          );\n        } catch (err) {\n          //for CORS frames\n        }\n      })\n      .filter(x => !!x);\n  }\n\n  var extractFrames_1 = extractFrames;\n\n  function uniq(arr) {\n    const result = [];\n    new Set(arr).forEach(v => v && result.push(v));\n    return result;\n  }\n\n  var uniq_1 = uniq;\n\n  function aggregateResourceUrlsAndBlobs(resourceUrlsAndBlobsArr) {\n    return resourceUrlsAndBlobsArr.reduce(\n      ({resourceUrls: allResourceUrls, blobsObj: allBlobsObj}, {resourceUrls, blobsObj}) => ({\n        resourceUrls: uniq_1(allResourceUrls.concat(resourceUrls)),\n        blobsObj: Object.assign(allBlobsObj, blobsObj),\n      }),\n      {resourceUrls: [], blobsObj: {}},\n    );\n  }\n\n  var aggregateResourceUrlsAndBlobs_1 = aggregateResourceUrlsAndBlobs;\n\n  function makeGetResourceUrlsAndBlobs({processResource, aggregateResourceUrlsAndBlobs}) {\n    return function getResourceUrlsAndBlobs(doc, baseUrl, urls) {\n      return Promise.all(\n        urls.map(url => processResource(url, doc, baseUrl, getResourceUrlsAndBlobs.bind(null, doc))),\n      ).then(resourceUrlsAndBlobsArr => aggregateResourceUrlsAndBlobs(resourceUrlsAndBlobsArr));\n    };\n  }\n\n  var getResourceUrlsAndBlobs = makeGetResourceUrlsAndBlobs;\n\n  function filterInlineUrl(absoluteUrl) {\n    return /^(blob|https?):/.test(absoluteUrl);\n  }\n\n  var filterInlineUrl_1 = filterInlineUrl;\n\n  function absolutizeUrl(url, absoluteUrl) {\n    return new URL(url, absoluteUrl).href;\n  }\n\n  var absolutizeUrl_1 = absolutizeUrl;\n\n  function makeProcessResource({\n    fetchUrl,\n    findStyleSheetByUrl,\n    extractResourcesFromStyleSheet,\n    isSameOrigin,\n    cache = {},\n  }) {\n    return function processResource(absoluteUrl, doc, baseUrl, getResourceUrlsAndBlobs) {\n      return cache[absoluteUrl] || (cache[absoluteUrl] = doProcessResource(absoluteUrl));\n\n      function doProcessResource(url) {\n        return fetchUrl(url)\n          .catch(e => {\n            if (probablyCORS(e, url)) {\n              return {probablyCORS: true, url};\n            } else {\n              throw e;\n            }\n          })\n          .then(({url, type, value, probablyCORS}) => {\n            if (probablyCORS) {\n              return {resourceUrls: [url]};\n            }\n            const result = {blobsObj: {[url]: {type, value}}};\n            if (/text\\/css/.test(type)) {\n              const styleSheet = findStyleSheetByUrl(url, doc);\n              if (!styleSheet) {\n                return result;\n              }\n              const resourceUrls = extractResourcesFromStyleSheet(styleSheet, doc.defaultView)\n                .map(resourceUrl => absolutizeUrl_1(resourceUrl, url.replace(/^blob:/, '')))\n                .filter(filterInlineUrl_1);\n              return getResourceUrlsAndBlobs(baseUrl, resourceUrls).then(\n                ({resourceUrls, blobsObj}) => ({\n                  resourceUrls,\n                  blobsObj: Object.assign(blobsObj, {[url]: {type, value}}),\n                }),\n              );\n            } else {\n              return result;\n            }\n          })\n          .catch(err => {\n            console.log('[dom-snapshot] error while fetching', url, err);\n            return {};\n          });\n      }\n\n      function probablyCORS(err, url) {\n        const msgCORS = err.message && err.message.includes('Failed to fetch');\n        const nameCORS = err.name && err.name.includes('TypeError');\n        return msgCORS && nameCORS && !isSameOrigin(url, baseUrl);\n      }\n    };\n  }\n\n  var processResource = makeProcessResource;\n\n  /* global window */\n\n  function fetchUrl(url, fetch = window.fetch) {\n    return fetch(url, {cache: 'force-cache', credentials: 'same-origin'}).then(resp =>\n      resp.arrayBuffer().then(buff => ({\n        url,\n        type: resp.headers.get('Content-Type'),\n        value: buff,\n      })),\n    );\n  }\n\n  var fetchUrl_1 = fetchUrl;\n\n  function makeFindStyleSheetByUrl({styleSheetCache}) {\n    return function findStyleSheetByUrl(url, doc) {\n      return styleSheetCache[url] || [...doc.styleSheets].find(styleSheet => styleSheet.href === url);\n    };\n  }\n\n  var findStyleSheetByUrl = makeFindStyleSheetByUrl;\n\n  function getUrlFromCssText(cssText) {\n    const re = /url\\((?!['\"]?:)['\"]?([^'\")]*)['\"]?\\)/g;\n    const ret = [];\n    let result;\n    while ((result = re.exec(cssText)) !== null) {\n      ret.push(result[1]);\n    }\n    return ret;\n  }\n\n  var getUrlFromCssText_1 = getUrlFromCssText;\n\n  // NOTE this code is very similar to the node part of visual-grid-client, but there is a different related to the browser's cssom with import rules\n  function makeExtractResourcesFromStyleSheet({styleSheetCache}) {\n    return function extractResourcesFromStyleSheet(styleSheet, win = window) {\n      return uniq_1(\n        [...(styleSheet.cssRules || [])].reduce((acc, rule) => {\n          if (rule instanceof win.CSSImportRule) {\n            styleSheetCache[rule.styleSheet.href] = rule.styleSheet;\n            return acc.concat(rule.href);\n          } else if (rule instanceof win.CSSFontFaceRule) {\n            return acc.concat(getUrlFromCssText_1(rule.style.getPropertyValue('src')));\n          } else if (rule instanceof win.CSSSupportsRule || rule instanceof win.CSSMediaRule) {\n            return acc.concat(extractResourcesFromStyleSheet(rule));\n          } else if (rule instanceof win.CSSStyleRule) {\n            for (let i = 0, ii = rule.style.length; i < ii; i++) {\n              const urls = getUrlFromCssText_1(rule.style.getPropertyValue(rule.style[i]));\n              urls.length && (acc = acc.concat(urls));\n            }\n          }\n          return acc;\n        }, []),\n      );\n    };\n  }\n\n  var extractResourcesFromStyleSheet = makeExtractResourcesFromStyleSheet;\n\n  function extractResourceUrlsFromStyleAttrs(cdt) {\n    return cdt.reduce((acc, node) => {\n      if (node.nodeType === 1) {\n        const styleAttr =\n          node.attributes && node.attributes.find(attr => attr.name.toUpperCase() === 'STYLE');\n\n        if (styleAttr) acc = acc.concat(getUrlFromCssText_1(styleAttr.value));\n      }\n      return acc;\n    }, []);\n  }\n\n  var extractResourceUrlsFromStyleAttrs_1 = extractResourceUrlsFromStyleAttrs;\n\n  function makeExtractResourceUrlsFromStyleTags(extractResourcesFromStyleSheet) {\n    return function extractResourceUrlsFromStyleTags(doc) {\n      return uniq_1(\n        [...doc.getElementsByTagName('style')].reduce((resourceUrls, styleEl) => {\n          const styleSheet = [...doc.styleSheets].find(\n            styleSheet => styleSheet.ownerNode === styleEl,\n          );\n          return resourceUrls.concat(extractResourcesFromStyleSheet(styleSheet, doc.defaultView));\n        }, []),\n      );\n    };\n  }\n\n  var extractResourceUrlsFromStyleTags = makeExtractResourceUrlsFromStyleTags;\n\n  function isSameOrigin(url, baseUrl) {\n    const blobOrData = /^(blob|data):/;\n    if (blobOrData.test(url)) return true;\n    if (blobOrData.test(baseUrl)) return false;\n\n    const {origin} = new URL(url, baseUrl);\n    const {origin: baseOrigin} = new URL(baseUrl);\n    return origin === baseOrigin;\n  }\n\n  var isSameOrigin_1 = isSameOrigin;\n\n  function processPage(doc = document) {\n    const styleSheetCache = {};\n    const extractResourcesFromStyleSheet$$1 = extractResourcesFromStyleSheet({styleSheetCache});\n    const findStyleSheetByUrl$$1 = findStyleSheetByUrl({styleSheetCache});\n    const processResource$$1 = processResource({\n      fetchUrl: fetchUrl_1,\n      findStyleSheetByUrl: findStyleSheetByUrl$$1,\n      extractResourcesFromStyleSheet: extractResourcesFromStyleSheet$$1,\n      absolutizeUrl: absolutizeUrl_1,\n      isSameOrigin: isSameOrigin_1,\n    });\n\n    const getResourceUrlsAndBlobs$$1 = getResourceUrlsAndBlobs({\n      processResource: processResource$$1,\n      aggregateResourceUrlsAndBlobs: aggregateResourceUrlsAndBlobs_1,\n    });\n\n    const extractResourceUrlsFromStyleTags$$1 = extractResourceUrlsFromStyleTags(\n      extractResourcesFromStyleSheet$$1,\n    );\n\n    return doProcessPage(doc);\n\n    function doProcessPage(doc) {\n      const frameElement = doc.defaultView && doc.defaultView.frameElement;\n      const url = frameElement ? frameElement.src : doc.location.href;\n\n      const cdt = domNodesToCdt_1(doc);\n\n      const links = uniq_1(\n        extractLinks_1(doc)\n          .concat(extractResourceUrlsFromStyleAttrs_1(cdt))\n          .concat(extractResourceUrlsFromStyleTags$$1(doc)),\n      )\n        .map(absolutizeThisUrl)\n        .filter(filterInlineUrlsIfExisting);\n\n      const resourceUrlsAndBlobsPromise = getResourceUrlsAndBlobs$$1(doc, url, links);\n\n      const frameDocs = extractFrames_1(doc);\n      const processFramesPromise = frameDocs.map(doProcessPage);\n\n      return Promise.all([resourceUrlsAndBlobsPromise, ...processFramesPromise]).then(\n        ([{resourceUrls, blobsObj}, ...framesResults]) => ({\n          cdt,\n          url,\n          resourceUrls,\n          blobs: blobsObjToArray(blobsObj),\n          frames: framesResults,\n          srcAttr: frameElement ? frameElement.getAttribute('src') : undefined,\n        }),\n      );\n\n      function absolutizeThisUrl(someUrl) {\n        try {\n          return absolutizeUrl_1(someUrl, url);\n        } catch (err) {\n          // can't do anything with a non-absolute url\n        }\n      }\n    }\n  }\n\n  function blobsObjToArray(blobsObj) {\n    return Object.keys(blobsObj).map(blobUrl =>\n      Object.assign(\n        {\n          url: blobUrl.replace(/^blob:/, ''),\n        },\n        blobsObj[blobUrl],\n      ),\n    );\n  }\n\n  function filterInlineUrlsIfExisting(absoluteUrl) {\n    return absoluteUrl && filterInlineUrl_1(absoluteUrl);\n  }\n\n  var processPage_1 = processPage;\n\n  function processPageAndSerialize(doc) {\n    return processPage_1(doc).then(serializeFrame);\n  }\n\n  function serializeFrame(frame) {\n    frame.blobs = frame.blobs.map(({url, type, value}) => ({\n      url,\n      type,\n      value: arrayBufferToBase64_1(value),\n    }));\n    frame.frames.forEach(serializeFrame);\n    return frame;\n  }\n\n  var processPageAndSerialize_1 = processPageAndSerialize;\n\n  return processPageAndSerialize_1;\n\n}());\n\n  return processPageAndSerialize.apply(this, arguments);\n}\n"