| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333 | "use strict";const path = require("path");const fs = require("fs").promises;const vm = require("vm");const toughCookie = require("tough-cookie");const sniffHTMLEncoding = require("html-encoding-sniffer");const whatwgURL = require("whatwg-url");const whatwgEncoding = require("whatwg-encoding");const { URL } = require("whatwg-url");const MIMEType = require("whatwg-mimetype");const idlUtils = require("./jsdom/living/generated/utils.js");const VirtualConsole = require("./jsdom/virtual-console.js");const { createWindow } = require("./jsdom/browser/Window.js");const { parseIntoDocument } = require("./jsdom/browser/parser");const { fragmentSerialization } = require("./jsdom/living/domparsing/serialization.js");const ResourceLoader = require("./jsdom/browser/resources/resource-loader.js");const NoOpResourceLoader = require("./jsdom/browser/resources/no-op-resource-loader.js");class CookieJar extends toughCookie.CookieJar {  constructor(store, options) {    // jsdom cookie jars must be loose by default    super(store, { looseMode: true, ...options });  }}const window = Symbol("window");let sharedFragmentDocument = null;class JSDOM {  constructor(input = "", options = {}) {    const mimeType = new MIMEType(options.contentType === undefined ? "text/html" : options.contentType);    const { html, encoding } = normalizeHTML(input, mimeType);    options = transformOptions(options, encoding, mimeType);    this[window] = createWindow(options.windowOptions);    const documentImpl = idlUtils.implForWrapper(this[window]._document);    options.beforeParse(this[window]._globalProxy);    parseIntoDocument(html, documentImpl);    documentImpl.close();  }  get window() {    // It's important to grab the global proxy, instead of just the result of `createWindow(...)`, since otherwise    // things like `window.eval` don't exist.    return this[window]._globalProxy;  }  get virtualConsole() {    return this[window]._virtualConsole;  }  get cookieJar() {    // TODO NEWAPI move _cookieJar to window probably    return idlUtils.implForWrapper(this[window]._document)._cookieJar;  }  serialize() {    return fragmentSerialization(idlUtils.implForWrapper(this[window]._document), { requireWellFormed: false });  }  nodeLocation(node) {    if (!idlUtils.implForWrapper(this[window]._document)._parseOptions.sourceCodeLocationInfo) {      throw new Error("Location information was not saved for this jsdom. Use includeNodeLocations during creation.");    }    return idlUtils.implForWrapper(node).sourceCodeLocation;  }  getInternalVMContext() {    if (!vm.isContext(this[window])) {      throw new TypeError("This jsdom was not configured to allow script running. " +        "Use the runScripts option during creation.");    }    return this[window];  }  reconfigure(settings) {    if ("windowTop" in settings) {      this[window]._top = settings.windowTop;    }    if ("url" in settings) {      const document = idlUtils.implForWrapper(this[window]._document);      const url = whatwgURL.parseURL(settings.url);      if (url === null) {        throw new TypeError(`Could not parse "${settings.url}" as a URL`);      }      document._URL = url;      document._origin = whatwgURL.serializeURLOrigin(document._URL);    }  }  static fragment(string = "") {    if (!sharedFragmentDocument) {      sharedFragmentDocument = (new JSDOM()).window.document;    }    const template = sharedFragmentDocument.createElement("template");    template.innerHTML = string;    return template.content;  }  static fromURL(url, options = {}) {    return Promise.resolve().then(() => {      // Remove the hash while sending this through the research loader fetch().      // It gets added back a few lines down when constructing the JSDOM object.      const parsedURL = new URL(url);      const originalHash = parsedURL.hash;      parsedURL.hash = "";      url = parsedURL.href;      options = normalizeFromURLOptions(options);      const resourceLoader = resourcesToResourceLoader(options.resources);      const resourceLoaderForInitialRequest = resourceLoader.constructor === NoOpResourceLoader ?        new ResourceLoader() :        resourceLoader;      const req = resourceLoaderForInitialRequest.fetch(url, {        accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",        cookieJar: options.cookieJar,        referrer: options.referrer      });      return req.then(body => {        const res = req.response;        options = Object.assign(options, {          url: req.href + originalHash,          contentType: res.headers["content-type"],          referrer: req.getHeader("referer")        });        return new JSDOM(body, options);      });    });  }  static async fromFile(filename, options = {}) {    options = normalizeFromFileOptions(filename, options);    const buffer = await fs.readFile(filename);    return new JSDOM(buffer, options);  }}function normalizeFromURLOptions(options) {  // Checks on options that are invalid for `fromURL`  if (options.url !== undefined) {    throw new TypeError("Cannot supply a url option when using fromURL");  }  if (options.contentType !== undefined) {    throw new TypeError("Cannot supply a contentType option when using fromURL");  }  // Normalization of options which must be done before the rest of the fromURL code can use them, because they are  // given to request()  const normalized = { ...options };  if (options.referrer !== undefined) {    normalized.referrer = (new URL(options.referrer)).href;  }  if (options.cookieJar === undefined) {    normalized.cookieJar = new CookieJar();  }  return normalized;  // All other options don't need to be processed yet, and can be taken care of in the normal course of things when  // `fromURL` calls `new JSDOM(html, options)`.}function normalizeFromFileOptions(filename, options) {  const normalized = { ...options };  if (normalized.contentType === undefined) {    const extname = path.extname(filename);    if (extname === ".xhtml" || extname === ".xht" || extname === ".xml") {      normalized.contentType = "application/xhtml+xml";    }  }  if (normalized.url === undefined) {    normalized.url = new URL("file:" + path.resolve(filename));  }  return normalized;}function transformOptions(options, encoding, mimeType) {  const transformed = {    windowOptions: {      // Defaults      url: "about:blank",      referrer: "",      contentType: "text/html",      parsingMode: "html",      parseOptions: {        sourceCodeLocationInfo: false,        scriptingEnabled: false      },      runScripts: undefined,      encoding,      pretendToBeVisual: false,      storageQuota: 5000000,      // Defaults filled in later      resourceLoader: undefined,      virtualConsole: undefined,      cookieJar: undefined    },    // Defaults    beforeParse() { }  };  // options.contentType was parsed into mimeType by the caller.  if (!mimeType.isHTML() && !mimeType.isXML()) {    throw new RangeError(`The given content type of "${options.contentType}" was not a HTML or XML content type`);  }  transformed.windowOptions.contentType = mimeType.essence;  transformed.windowOptions.parsingMode = mimeType.isHTML() ? "html" : "xml";  if (options.url !== undefined) {    transformed.windowOptions.url = (new URL(options.url)).href;  }  if (options.referrer !== undefined) {    transformed.windowOptions.referrer = (new URL(options.referrer)).href;  }  if (options.includeNodeLocations) {    if (transformed.windowOptions.parsingMode === "xml") {      throw new TypeError("Cannot set includeNodeLocations to true with an XML content type");    }    transformed.windowOptions.parseOptions = { sourceCodeLocationInfo: true };  }  transformed.windowOptions.cookieJar = options.cookieJar === undefined ?                                       new CookieJar() :                                       options.cookieJar;  transformed.windowOptions.virtualConsole = options.virtualConsole === undefined ?                                            (new VirtualConsole()).sendTo(console) :                                            options.virtualConsole;  if (!(transformed.windowOptions.virtualConsole instanceof VirtualConsole)) {    throw new TypeError("virtualConsole must be an instance of VirtualConsole");  }  transformed.windowOptions.resourceLoader = resourcesToResourceLoader(options.resources);  if (options.runScripts !== undefined) {    transformed.windowOptions.runScripts = String(options.runScripts);    if (transformed.windowOptions.runScripts === "dangerously") {      transformed.windowOptions.parseOptions.scriptingEnabled = true;    } else if (transformed.windowOptions.runScripts !== "outside-only") {      throw new RangeError(`runScripts must be undefined, "dangerously", or "outside-only"`);    }  }  if (options.beforeParse !== undefined) {    transformed.beforeParse = options.beforeParse;  }  if (options.pretendToBeVisual !== undefined) {    transformed.windowOptions.pretendToBeVisual = Boolean(options.pretendToBeVisual);  }  if (options.storageQuota !== undefined) {    transformed.windowOptions.storageQuota = Number(options.storageQuota);  }  return transformed;}function normalizeHTML(html, mimeType) {  let encoding = "UTF-8";  if (ArrayBuffer.isView(html)) {    html = Buffer.from(html.buffer, html.byteOffset, html.byteLength);  } else if (html instanceof ArrayBuffer) {    html = Buffer.from(html);  }  if (Buffer.isBuffer(html)) {    encoding = sniffHTMLEncoding(html, {      defaultEncoding: mimeType.isXML() ? "UTF-8" : "windows-1252",      transportLayerEncodingLabel: mimeType.parameters.get("charset")    });    html = whatwgEncoding.decode(html, encoding);  } else {    html = String(html);  }  return { html, encoding };}function resourcesToResourceLoader(resources) {  switch (resources) {    case undefined: {      return new NoOpResourceLoader();    }    case "usable": {      return new ResourceLoader();    }    default: {      if (!(resources instanceof ResourceLoader)) {        throw new TypeError("resources must be an instance of ResourceLoader");      }      return resources;    }  }}exports.JSDOM = JSDOM;exports.VirtualConsole = VirtualConsole;exports.CookieJar = CookieJar;exports.ResourceLoader = ResourceLoader;exports.toughCookie = toughCookie;
 |