From ab18db097fe46ae101b9a4cffcd0fe174545a001 Mon Sep 17 00:00:00 2001 From: Kai Zhu Date: Mon, 9 Nov 2020 12:46:21 -0600 Subject: [PATCH] - remove file lib.puppeteer.js and replace with function cdpClientCreate --- README.md | 6 +- lib.apidoc.js | 45 +- lib.istanbul.js | 45 +- lib.jslint.js | 45 +- lib.puppeteer.js | 2901 ----------- lib.utility2.js | 584 ++- package.json | 2 +- raw.puppeteer.js | 12676 --------------------------------------------- test.js | 15 +- 9 files changed, 574 insertions(+), 15745 deletions(-) delete mode 100755 lib.puppeteer.js delete mode 100644 raw.puppeteer.js diff --git a/README.md b/README.md index 233246e7f7..2922293f78 100644 --- a/README.md +++ b/README.md @@ -52,9 +52,9 @@ this zero-dependency package will provide high-level functions to to build, test ![screenshot](https://kaizhu256.github.io/node-utility2/build/screenshot.npmPackageCliHelp.svg) #### changelog 2020.11.3 -- revamp file lib.puppeteer.js part 9 - merge classes Browser, Target into cdpClientCreate and begin migrating listeners from session to cdpClient +- remove file lib.puppeteer.js and replace with function cdpClientCreate - remove dependency on env-var \$CHROME_BIN -- remove functions base64FromBuffer, base64ToUtf8, cryptoAesXxxCbcRawDecrypt, cryptoAesXxxCbcRawEncrypt, gotoNext +- remove functions base64FromBuffer, base64ToUtf8, cryptoAesXxxCbcRawDecrypt, cryptoAesXxxCbcRawEncrypt, gotoNext, onErrorWithStack - jslint - fix off-by-one column in autofix-expected_a_before_b - decouple build from npm-env-variables npm_config_xxx and npm_package_xxx - update lib.utility2.sh to remove dependency on \$UTILITY2_MACRO_JS @@ -1175,7 +1175,7 @@ require("http").createServer(function (req, res) { "engines": { "node": ">=12.0" }, - "fileCount": 30, + "fileCount": 28, "homepage": "https://github.com/kaizhu256/node-utility2", "keywords": [ "continuous-integration", diff --git a/lib.apidoc.js b/lib.apidoc.js index 96c1e884c1..8b6b40ac34 100755 --- a/lib.apidoc.js +++ b/lib.apidoc.js @@ -288,30 +288,27 @@ local.cliRun = function (opt) { commandList[ii].command.push(key); return; } - try { - commandList[ii] = opt.rgxComment.exec(str); - commandList[ii] = { - argList: local.coalesce(commandList[ii][1], "").trim(), - command: [ - key - ], - description: commandList[ii][2] - }; - } catch (ignore) { - local.assertOrThrow(undefined, new Error( - "cliRun - cannot parse comment in COMMAND " - + key - + ":\nnew RegExp(" - + JSON.stringify(opt.rgxComment.source) - + ").exec(" + JSON.stringify(str).replace(( - /\\\\/g - ), "\u0000").replace(( - /\\n/g - ), "\\n\\\n").replace(( - /\u0000/g - ), "\\\\") + ");" - )); - } + commandList[ii] = opt.rgxComment.exec(str); + local.assertOrThrow(commandList[ii], ( + "cliRun - cannot parse comment in COMMAND " + + key + + ":\nnew RegExp(" + + JSON.stringify(opt.rgxComment.source) + + ").exec(" + JSON.stringify(str).replace(( + /\\\\/g + ), "\u0000").replace(( + /\\n/g + ), "\\n\\\n").replace(( + /\u0000/g + ), "\\\\") + ");" + )); + commandList[ii] = { + argList: local.coalesce(commandList[ii][1], "").trim(), + command: [ + key + ], + description: commandList[ii][2] + }; }); str = ""; str += packageJson.name + " (" + packageJson.version + ")\n\n"; diff --git a/lib.istanbul.js b/lib.istanbul.js index b2c27e6b1b..b557c77061 100755 --- a/lib.istanbul.js +++ b/lib.istanbul.js @@ -288,30 +288,27 @@ local.cliRun = function (opt) { commandList[ii].command.push(key); return; } - try { - commandList[ii] = opt.rgxComment.exec(str); - commandList[ii] = { - argList: local.coalesce(commandList[ii][1], "").trim(), - command: [ - key - ], - description: commandList[ii][2] - }; - } catch (ignore) { - local.assertOrThrow(undefined, new Error( - "cliRun - cannot parse comment in COMMAND " - + key - + ":\nnew RegExp(" - + JSON.stringify(opt.rgxComment.source) - + ").exec(" + JSON.stringify(str).replace(( - /\\\\/g - ), "\u0000").replace(( - /\\n/g - ), "\\n\\\n").replace(( - /\u0000/g - ), "\\\\") + ");" - )); - } + commandList[ii] = opt.rgxComment.exec(str); + local.assertOrThrow(commandList[ii], ( + "cliRun - cannot parse comment in COMMAND " + + key + + ":\nnew RegExp(" + + JSON.stringify(opt.rgxComment.source) + + ").exec(" + JSON.stringify(str).replace(( + /\\\\/g + ), "\u0000").replace(( + /\\n/g + ), "\\n\\\n").replace(( + /\u0000/g + ), "\\\\") + ");" + )); + commandList[ii] = { + argList: local.coalesce(commandList[ii][1], "").trim(), + command: [ + key + ], + description: commandList[ii][2] + }; }); str = ""; str += packageJson.name + " (" + packageJson.version + ")\n\n"; diff --git a/lib.jslint.js b/lib.jslint.js index c39222db7f..c97a92dec1 100755 --- a/lib.jslint.js +++ b/lib.jslint.js @@ -288,30 +288,27 @@ local.cliRun = function (opt) { commandList[ii].command.push(key); return; } - try { - commandList[ii] = opt.rgxComment.exec(str); - commandList[ii] = { - argList: local.coalesce(commandList[ii][1], "").trim(), - command: [ - key - ], - description: commandList[ii][2] - }; - } catch (ignore) { - local.assertOrThrow(undefined, new Error( - "cliRun - cannot parse comment in COMMAND " - + key - + ":\nnew RegExp(" - + JSON.stringify(opt.rgxComment.source) - + ").exec(" + JSON.stringify(str).replace(( - /\\\\/g - ), "\u0000").replace(( - /\\n/g - ), "\\n\\\n").replace(( - /\u0000/g - ), "\\\\") + ");" - )); - } + commandList[ii] = opt.rgxComment.exec(str); + local.assertOrThrow(commandList[ii], ( + "cliRun - cannot parse comment in COMMAND " + + key + + ":\nnew RegExp(" + + JSON.stringify(opt.rgxComment.source) + + ").exec(" + JSON.stringify(str).replace(( + /\\\\/g + ), "\u0000").replace(( + /\\n/g + ), "\\n\\\n").replace(( + /\u0000/g + ), "\\\\") + ");" + )); + commandList[ii] = { + argList: local.coalesce(commandList[ii][1], "").trim(), + command: [ + key + ], + description: commandList[ii][2] + }; }); str = ""; str += packageJson.name + " (" + packageJson.version + ")\n\n"; diff --git a/lib.puppeteer.js b/lib.puppeteer.js deleted file mode 100755 index 4bb3c67a76..0000000000 --- a/lib.puppeteer.js +++ /dev/null @@ -1,2901 +0,0 @@ -#!/usr/bin/env node - - -// vim -// ,$s/^ \(async \)*\(\w\w*\)(/ Connection.prototype.\2 = \1function (/gc -// ,$s/\(([^()]*)\) => {/function \1 {/gc -/* - let that = this; -*/ -// ,$s/\(\w\w*\) => {/function (\1) {/gc -// ,$s/\/that/gc -// ,$s/\(\w\w*\) => \(.*\)\([,)]\)/function (\1) { return \2; }\3/gc -// ,$s/\(\w\w*\) => \([^)]*\)\([,)]\)/function (\1) { return \2; }\3/gc -// ,$s/\(\w\w*\) => \([^,]*\)\([,)]\)/function (\1) { return \2; }\3/gc -// ,$s/\<\(if\|else\) .*[^{]$/& {/gc -// ,$s/^\( *\)\(\<\(if\|else\) .*[^{]\)\(\n.*\)/\1\2 {\4\r\1}/gc -// ,$s/^\( *\)\(\ to stderr - * and return [0] - */ - consoleError("\n\ndebugInline"); - consoleError(...argList); - consoleError("\n"); - return argList[0]; - }; - } - // init isBrowser - isBrowser = ( - typeof globalThis.XMLHttpRequest === "function" - && globalThis.navigator - && typeof globalThis.navigator.userAgent === "string" - ); - // init isWebWorker - isWebWorker = ( - isBrowser && typeof globalThis.importScripts === "function" - ); - // init function - function objectDeepCopyWithKeysSorted(obj) { - /* - * this function will recursively deep-copy with keys sorted - */ - let sorted; - if (typeof obj !== "object" || !obj) { - return obj; - } - // recursively deep-copy list with child-keys sorted - if (Array.isArray(obj)) { - return obj.map(objectDeepCopyWithKeysSorted); - } - // recursively deep-copy obj with keys sorted - sorted = {}; - Object.keys(obj).sort().forEach(function (key) { - sorted[key] = objectDeepCopyWithKeysSorted(obj[key]); - }); - return sorted; - } - function assertJsonEqual(aa, bb) { - /* - * this function will assert JSON.stringify() === JSON.stringify() - */ - aa = JSON.stringify(objectDeepCopyWithKeysSorted(aa)); - bb = JSON.stringify(objectDeepCopyWithKeysSorted(bb)); - if (aa !== bb) { - throw new Error(JSON.stringify(aa) + " !== " + JSON.stringify(bb)); - } - } - function assertOrThrow(passed, msg) { - /* - * this function will throw if is falsy - */ - if (passed) { - return; - } - throw ( - ( - msg - && typeof msg.message === "string" - && typeof msg.stack === "string" - ) - // if msg is err, then leave as is - ? msg - : new Error( - typeof msg === "string" - // if msg is string, then leave as is - ? msg - // else JSON.stringify(msg) - : JSON.stringify(msg, undefined, 4) - ) - ); - } - function coalesce(...argList) { - /* - * this function will coalesce null, undefined, or "" in - */ - let arg; - let ii; - ii = 0; - while (ii < argList.length) { - arg = argList[ii]; - if (arg !== undefined && arg !== null && arg !== "") { - return arg; - } - ii += 1; - } - return arg; - } - function identity(val) { - /* - * this function will return - */ - return val; - } - function noop() { - /* - * this function will do nothing - */ - return; - } - function objectAssignDefault(tgt = {}, src = {}, depth = 0) { - /* - * this function will if items from are null, undefined, or "", - * then overwrite them with items from - */ - let recurse; - recurse = function (tgt, src, depth) { - Object.entries(src).forEach(function ([ - key, bb - ]) { - let aa; - aa = tgt[key]; - if (aa === undefined || aa === null || aa === "") { - tgt[key] = bb; - return; - } - if ( - depth !== 0 - && typeof aa === "object" && aa && !Array.isArray(aa) - && typeof bb === "object" && bb && !Array.isArray(bb) - ) { - recurse(aa, bb, depth - 1); - } - }); - }; - recurse(tgt, src, depth | 0); - return tgt; - } - function onErrorThrow(err) { - /* - * this function will throw if exists - */ - if (err) { - throw err; - } - } - // bug-workaround - throw unhandledRejections in node-process - if ( - typeof process === "object" && process - && typeof process.on === "function" - && process.unhandledRejections !== "strict" - ) { - process.unhandledRejections = "strict"; - process.on("unhandledRejection", function (err) { - throw err; - }); - } - // init local - local = { - assertJsonEqual, - assertOrThrow, - coalesce, - identity, - isBrowser, - isWebWorker, - local, - noop, - objectAssignDefault, - objectDeepCopyWithKeysSorted, - onErrorThrow - }; - globalThis.globalLocal = local; -}()); -// assets.utility2.header.js - end - - -(function (local) { -"use strict"; - - -/* istanbul ignore next */ -// run shared js-env code - init-before -(function () { -// init local -local = ( - globalThis.utility2_rollup - // || globalThis.utility2_rollup_old - // || require("./assets.utility2.rollup.js") - || globalThis.globalLocal -); -// init exports -if (local.isBrowser) { - globalThis.utility2_puppeteer = local; -} else { - module.exports = local; - module.exports.__dirname = __dirname; -} -// init lib main -local.puppeteer = local; - - -/* validateLineSortedReset */ -local.cliRun = function (opt) { -/* - * this function will run cli with given - */ - let cliDict; - cliDict = local.cliDict; - cliDict._eval = cliDict._eval || function () { - /* - * - * will eval - */ - globalThis.local = local; - require("vm").runInThisContext(process.argv[3]); - }; - cliDict._help = cliDict._help || function () { - /* - * - * will print help - */ - let commandList; - let file; - let packageJson; - let str; - let strDict; - commandList = [ - { - argList: " ...", - description: "usage:", - command: [ - "" - ] - }, { - argList: "'console.log(\"hello world\")'", - description: "example:", - command: [ - "--eval" - ] - } - ]; - file = __filename.replace(( - /.*\// - ), ""); - opt = Object.assign({}, opt); - packageJson = require("./package.json"); - // validate comment - opt.rgxComment = opt.rgxComment || ( - /\)\u0020\{\n(?:|\u0020{4})\/\*\n(?:\u0020|\u0020{5})\*((?:\u0020<[^>]*?>|\u0020\.\.\.)*?)\n(?:\u0020|\u0020{5})\*\u0020(will\u0020.*?\S)\n(?:\u0020|\u0020{5})\*\/\n(?:\u0020{4}|\u0020{8})\S/ - ); - strDict = {}; - Object.keys(cliDict).sort().forEach(function (key, ii) { - if (key[0] === "_" && key !== "_default") { - return; - } - str = String(cliDict[key]); - if (key === "_default") { - key = ""; - } - strDict[str] = strDict[str] || (ii + 2); - ii = strDict[str]; - if (commandList[ii]) { - commandList[ii].command.push(key); - return; - } - try { - commandList[ii] = opt.rgxComment.exec(str); - commandList[ii] = { - argList: local.coalesce(commandList[ii][1], "").trim(), - command: [ - key - ], - description: commandList[ii][2] - }; - } catch (ignore) { - local.assertOrThrow(undefined, new Error( - "cliRun - cannot parse comment in COMMAND " - + key - + ":\nnew RegExp(" - + JSON.stringify(opt.rgxComment.source) - + ").exec(" + JSON.stringify(str).replace(( - /\\\\/g - ), "\u0000").replace(( - /\\n/g - ), "\\n\\\n").replace(( - /\u0000/g - ), "\\\\") + ");" - )); - } - }); - str = ""; - str += packageJson.name + " (" + packageJson.version + ")\n\n"; - str += commandList.filter(function (elem) { - return elem; - }).map(function (elem, ii) { - elem.command = elem.command.filter(function (elem) { - return elem; - }); - switch (ii) { - case 0: - case 1: - elem.argList = [ - elem.argList - ]; - break; - default: - elem.argList = elem.argList.split(" "); - elem.description = ( - "# COMMAND " - + (elem.command[0] || "") + "\n# " - + elem.description - ); - } - return ( - elem.description + "\n " + file - + " " + elem.command.sort().join("|") + " " - + elem.argList.join(" ") - ); - }).join("\n\n"); - console.log(str); - }; - cliDict["--eval"] = cliDict["--eval"] || cliDict._eval; - cliDict["--help"] = cliDict["--help"] || cliDict._help; - cliDict["-e"] = cliDict["-e"] || cliDict._eval; - cliDict["-h"] = cliDict["-h"] || cliDict._help; - cliDict._default = cliDict._default || cliDict._help; - cliDict.help = cliDict.help || cliDict._help; - cliDict._interactive = cliDict._interactive || function () { - /* - * - * will start interactive-mode - */ - globalThis.local = local; - local.identity(local.replStart || require("repl").start)({ - useGlobal: true - }); - }; - cliDict["--interactive"] = cliDict["--interactive"] || cliDict._interactive; - cliDict["-i"] = cliDict["-i"] || cliDict._interactive; - cliDict._version = cliDict._version || function () { - /* - * - * will print version - */ - console.log(require(__dirname + "/package.json").version); - }; - cliDict["--version"] = cliDict["--version"] || cliDict._version; - cliDict["-v"] = cliDict["-v"] || cliDict._version; - // default to --help command if no arguments are given - if (process.argv.length <= 2) { - cliDict._help(); - return; - } - if (cliDict[process.argv[2]]) { - cliDict[process.argv[2]](); - return; - } - cliDict._default(); -}; -}()); - - -// /* istanbul ignore next */ -// run node js-env code - function -(function () { -if (local.isBrowser) { - return; -} -/* jslint ignore:start */ -const debugError = console.error; -const mime = { - getType: function (file) { - file = path.extname(String(file).toLowerCase()); - switch (file) { - case ".png": - return "image/png"; - case ".jpe": - case ".jpeg": - case ".jpg": - return "image/jpeg"; - default: - return file; - } - } -}; -let EventEmitter = require("events"); -let URL = require("url"); -// let WebSocket = require("ws"); -// let bufferUtil = require("bufferutil"); -let childProcess = require("child_process"); -// let debugError = require("debug")(`puppeteer:error`); -// let debugProtocol = require("debug")("puppeteer:protocol"); -let fs = require("fs"); -let http = require("http"); -let https = require("https"); -// let mime = require("mime"); -let net = require("net"); -let os = require("os"); -let path = require("path"); -let readline = require("readline"); -let tls = require("tls"); -let url = require("url"); -let exports_puppeteer_puppeteer_index = {}; -let exports_puppeteer_puppeteer_lib_Browser = {}; -let exports_puppeteer_puppeteer_lib_BrowserFetcher = {}; -let exports_puppeteer_puppeteer_lib_Connection = {}; -let exports_puppeteer_puppeteer_lib_Coverage = {}; -let exports_puppeteer_puppeteer_lib_DOMWorld = {}; -let exports_puppeteer_puppeteer_lib_DeviceDescriptors = {}; -let exports_puppeteer_puppeteer_lib_EmulationManager = {}; -let exports_puppeteer_puppeteer_lib_Errors = {}; -let exports_puppeteer_puppeteer_lib_Events = {}; -let exports_puppeteer_puppeteer_lib_ExecutionContext = {}; -let exports_puppeteer_puppeteer_lib_FrameManager = {}; -let exports_puppeteer_puppeteer_lib_Input = {}; -let exports_puppeteer_puppeteer_lib_JSHandle = {}; -let exports_puppeteer_puppeteer_lib_LifecycleWatcher = {}; -let exports_puppeteer_puppeteer_lib_NetworkManager = {}; -let exports_puppeteer_puppeteer_lib_Page = {}; -let exports_puppeteer_puppeteer_lib_Puppeteer = {}; -let exports_puppeteer_puppeteer_lib_Target = {}; -let exports_puppeteer_puppeteer_lib_TimeoutSettings = {}; -let exports_puppeteer_puppeteer_lib_api = {}; -let exports_puppeteer_puppeteer_lib_helper = {}; -let exports_puppeteer_puppeteer_node6_lib_Puppeteer = {}; -let exports_websockets_ws_index = {}; -let exports_websockets_ws_lib_buffer_util = {}; -let exports_websockets_ws_lib_constants = {}; -let exports_websockets_ws_lib_event_target = {}; -let exports_websockets_ws_lib_validation = {}; -/* -repo https://github.com/websockets/ws/tree/6.2.1 -committed 2019-03-27T08:34:10Z -*/ -/* -file https://github.com/websockets/ws/blob/6.2.1/lib/sender.js -*/ -/* jslint ignore:end */ -/* -file https://github.com/websockets/ws/blob/6.2.1/lib/websocket.js -*/ -/** - * @param {!Connection} connection - * @param {string} targetType - * @param {string} sessionId - */ -function CDPSession(sck2, targetType, sessionId) { - let ssn = this; - require("stream").EventEmitter.call(ssn); - ssn.rpc = sck2.rpc; - ssn._targetType = targetType; - ssn._sessionId = sessionId; -} -require("util").inherits(CDPSession, require("stream").EventEmitter); - - -function cdpClientCreate({ - websocketUrl -}) { -/* - * this function with create chrome-devtools-protocol-client from - */ - let WS_READ_HEADER; - let WS_READ_LENGTH16; - let WS_READ_LENGTH63; - let WS_READ_PAYLOAD; - let cdpCallbackDict; - let cdpCallbackId; - let cdpClient; - let cdpOnError; - let cdpSessionDict; - let secWebsocketKey; - let websocket; - let wsBufList; - let wsPayloadLength; - let wsReadState; - let wsReader; - // init var - return Promise.resolve().then(function () { - WS_READ_HEADER = 0; - WS_READ_LENGTH16 = 1; - WS_READ_LENGTH63 = 2; - WS_READ_PAYLOAD = 3; - cdpCallbackDict = {}; - cdpCallbackId = 0; - cdpSessionDict = {}; - secWebsocketKey = require("crypto").randomBytes(16).toString("base64"); - wsBufList = []; - wsPayloadLength = 0; - wsReadState = WS_READ_HEADER; - // init cdpClient - }).then(function () { - function CdpClient() { - /* - * this function will construct cdpClient - */ - require("stream").Duplex.call(this); - } - require("util").inherits(CdpClient, require("stream").Duplex); - cdpClient = new CdpClient(); - cdpClient.__proto__._read = function () { - /* - * this function will implement stream.Duplex.prototype._read - */ - if (websocket && websocket.readable) { - websocket.resume(); - } - }; - cdpClient.__proto__._write = function (payload, ignore, callback) { - /* - * this function will implement stream.Duplex.prototype._write - */ - let header; - let maskKey; - let result; - // console.error("SEND ► " + payload.slice(0, 256).toString()); - // init header - header = Buffer.alloc(2 + 8 + 4); - // init fin = true - header[0] |= 0x80; - // init opcode = text-frame - header[0] |= 1; - // init mask = true - header[1] |= 0x80; - // init wsPayloadLength - if (payload.length < 126) { - header = header.slice(0, 2 + 0 + 4); - header[1] |= payload.length; - } else if (payload.length < 65536) { - header = header.slice(0, 2 + 2 + 4); - header[1] |= 126; - header.writeUInt16BE(payload.length, 2); - } else { - header[1] |= 127; - header.writeUInt32BE(payload.length, 6); - } - // init maskKey - maskKey = require("crypto").randomBytes(4); - maskKey.copy(header, header.length - 4); - // send header - websocket.cork(); - websocket.write(header); - // send payload ^ maskKey - payload.forEach(function (ignore, ii) { - payload[ii] ^= maskKey[ii & 3]; - }); - // return write-result - result = websocket.write(payload, callback); - websocket.uncork(); - return result; - }; - cdpClient.rpc = function (method, params, sessionId) { - /* - * this function will message-pass - * JSON.stringify({id, , , }) - * to chrome-browser using chrome-devtools-protocol - */ - cdpCallbackId = (cdpCallbackId % 256) + 1; - cdpClient.write(Buffer.from(JSON.stringify({ - id: cdpCallbackId, - method, - params, - sessionId - }))); - return new Promise(function (resolve, reject) { - cdpCallbackDict[cdpCallbackId] = { - err: new Error(), - method, - reject, - resolve - }; - }); - }; - cdpOnError = cdpClient._destroy.bind(cdpClient); - // init evt-handling - }).then(function () { - cdpClient.on("data", function (evt) { - /* - * this function will handle callback for - * received from chrome-browser using chrome-devtools-protocol - */ - // console.error("◀ RECV " + evt.slice(0, 256).toString()); - let callback; - let ssn; - // init evt - evt = JSON.parse(evt); - local.assertOrThrow(!evt.method || ( - /^[A-Z]\w*?\.[a-z]\w*?$/ - ).test(evt.method), new Error( - "cdp-rpc-error - invalid evt.method " + evt.method - )); - cdpClient.emit(evt.method, evt.params); - // init callback - callback = cdpCallbackDict[evt.id]; - delete cdpCallbackDict[evt.id]; - // callback.reject - if (callback && evt.error) { - callback.err.evt = ( - // "Protocol error (" + evt.method + "): " - "cdp-rpc-error - " + evt.method - + " - " + JSON.stringify(evt.error) - ); - callback.reject(callback.err); - return; - } - // callback.resolve - if (callback) { - callback.resolve(evt.result); - return; - } - if (evt.sessionId) { - ssn = cdpSessionDict[evt.sessionId]; - if (!ssn) { - return; - } - ssn.emit(evt.method, evt.params); - return; - } - }); - cdpClient.on("Target.attachedToTarget", function (evt) { - let ssn = new CDPSession( - cdpClient, - evt.targetInfo.type, - evt.sessionId - ); - cdpSessionDict[evt.sessionId] = ssn; - }); - cdpClient.on("Target.detachedFromTarget", function (evt) { - let ssn = cdpSessionDict[evt.sessionId]; - delete cdpSessionDict[evt.sessionId]; - if (ssn) { - ssn._onClosed(); - } - }); - // init wsReader - }).then(function () { -/* -https://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-13#section-5.2 -+---------------------------------------------------------------+ -|0 1 2 3 | -|0 1 2 3 4 5 6 7 8 9 a b c d e f 0 1 2 3 4 5 6 7 8 9 a b c d e f| -+-+-+-+-+-------+-+-------------+-------------------------------+ -|F|R|R|R| opcode|M| Payload len | Extended payload length | -|I|S|S|S| (4) |A| (7) | (16/63) | -|N|V|V|V| |S| | (if payload len==126/127) | -| |1|2|3| |K| | | -+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + -| Extended payload length continued, if payload len == 127 | -+ - - - - - - - - - - - - - - - +-------------------------------+ -| |Masking-key, if MASK set to 1 | -+-------------------------------+-------------------------------+ -| Masking-key (continued) | Payload Data | -+-------------------------------- - - - - - - - - - - - - - - - + -: Payload Data continued ... : -+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + -| Payload Data continued ... | -+---------------------------------------------------------------+ -FIN: 1 bit - Indicates that this is the final fragment in a message. The first - fragment MAY also be the final fragment. -RSV1, RSV2, RSV3: 1 bit each - MUST be 0 unless an extension is negotiated which defines meanings - for non-zero values. If a nonzero value is received and none of - the negotiated extensions defines the meaning of such a nonzero - value, the receiving endpoint MUST _Fail the WebSocket - Connection_. -Opcode: 4 bits - Defines the interpretation of the payload data. If an unknown - opcode is received, the receiving endpoint MUST _Fail the - WebSocket Connection_. The following values are defined. - * %x0 denotes a continuation frame - * %x1 denotes a text frame - * %x2 denotes a binary frame - * %x3-7 are reserved for further non-control frames - * %x8 denotes a connection close - * %x9 denotes a ping - * %xA denotes a pong - * %xB-F are reserved for further control frames -Mask: 1 bit - Defines whether the payload data is masked. If set to 1, a - masking key is present in masking-key, and this is used to unmask - the payload data as per Section 5.3. All frames sent from client - to server have this bit set to 1. -Payload length: 7 bits, 7+16 bits, or 7+64 bits - The length of the payload data, in bytes: if 0-125, that is the - payload length. If 126, the following 2 bytes interpreted as a 16 - bit unsigned integer are the payload length. If 127, the - following 8 bytes interpreted as a 64-bit unsigned integer (the - most significant bit MUST be 0) are the payload length. Multibyte - length quantities are expressed in network byte order. The - payload length is the length of the extension data + the length of - the application data. The length of the extension data may be - zero, in which case the payload length is the length of the - application data. -Masking-key: 0 or 4 bytes - All frames sent from the client to the server are masked by a 32- - bit value that is contained within the frame. This field is - present if the mask bit is set to 1, and is absent if the mask bit - is set to 0. See Section 5.3 for further information on client- - to-server masking. -Payload data: (x+y) bytes - The payload data is defined as extension data concatenated with - application data. -Extension data: x bytes - The extension data is 0 bytes unless an extension has been - negotiated. Any extension MUST specify the length of the - extension data, or how that length may be calculated, and how the - extension use MUST be negotiated during the opening handshake. If - present, the extension data is included in the total payload - length. -Application data: y bytes - Arbitrary application data, taking up the remainder of the frame - after any extension data. The length of the application data is - equal to the payload length minus the length of the extension - data. -*/ - function wsBufListRead(nn) { - /* - * this function will read bytes from - */ - let buf; - wsBufList = ( - wsBufList.length === 1 - ? wsBufList[0] - : Buffer.concat(wsBufList) - ); - buf = wsBufList.slice(0, nn); - wsBufList = [ - wsBufList.slice(nn) - ]; - return buf; - } - function wsFrameRead() { - /* - * this function will read from websocket-data-frame - */ - let buf; - let opcode; - if (wsBufList.reduce(function (aa, bb) { - return aa + bb.length; - }, 0) < ( - wsReadState === WS_READ_PAYLOAD - ? Math.max(wsPayloadLength, 1) - : wsReadState === WS_READ_LENGTH63 - ? 8 - : 2 - )) { - return; - } - switch (wsReadState) { - // read frame-header - case WS_READ_HEADER: - buf = wsBufListRead(2); - // validate opcode - opcode = buf[0] & 0x0f; - local.assertOrThrow(opcode === 0x01, ( - "Invalid WebSocket frame: opcode must be 0x01, not 0x0" - + opcode.toString(16) - )); - wsPayloadLength = buf[1] & 0x7f; - wsReadState = ( - wsPayloadLength === 126 - ? WS_READ_LENGTH16 - : wsPayloadLength === 127 - ? WS_READ_LENGTH63 - : WS_READ_PAYLOAD - ); - break; - // read frame-payload-length-16 - case WS_READ_LENGTH16: - wsPayloadLength = wsBufListRead(2).readUInt16BE(0); - wsReadState = WS_READ_PAYLOAD; - break; - // read frame-payload-length-63 - case WS_READ_LENGTH63: - buf = wsBufListRead(8); - wsPayloadLength = ( - buf.readUInt32BE(0) * 0x100000000 + buf.readUInt32BE(4) - ); - wsReadState = WS_READ_PAYLOAD; - break; - // read frame-payload-data - case WS_READ_PAYLOAD: - buf = ( - wsPayloadLength - ? wsBufListRead(wsPayloadLength) - : Buffer.alloc(0) - ); - wsReadState = WS_READ_HEADER; - cdpClient.push(buf); - break; - } - local.assertOrThrow( - 0 <= wsPayloadLength && wsPayloadLength <= 256 * 1024 * 1024, - "payload-length must be between 0 and 256 MiB, not " - + wsPayloadLength - ); - return true; - } - function WsReader() { - /* - * this function will construct wsReader - */ - require("stream").Transform.call(this); - } - require("util").inherits(WsReader, require("stream").Transform); - wsReader = new WsReader(); - wsReader.__proto__._transform = function (chunk, ignore, callback) { - /* - * this function will implement Transform.prototype._transform - */ - try { - wsBufList.push(chunk); - while (true) { - if (!wsFrameRead()) { - break; - } - } - callback(); - } catch (errCaught) { - this.destroy(errCaught); - } - }; - // init websocket - }).then(function () { - return new Promise(function (resolve) { - require("http").get(Object.assign(require("url").parse( - websocketUrl - ), { - "createConnection": function (opt) { - opt.path = opt.socketPath; - return require("net").connect(opt); - }, - "headers": { - "Connection": "Upgrade", - "Sec-WebSocket-Key": secWebsocketKey, - "Sec-WebSocket-Version": 13, - "Upgrade": "websocket" - }, - "protocol": "http:", - "protocolVersion": 13 - })).once("upgrade", function (res, _websocket, head) { - local.assertOrThrow( - ( - res.headers["sec-websocket-accept"] - === require("crypto").createHash("sha1").update( - secWebsocketKey - + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" - ).digest("base64") - ), - "Invalid Sec-WebSocket-Accept header" - ); - websocket = _websocket; - websocket.unshift(head); - websocket.on("error", cdpOnError); - // websocket - disable timeout - websocket.setTimeout(0); - // websocket - disable nagle's algorithm - websocket.setNoDelay(); - /* - websocket.on("end", websocket.end); - websocket.once("error", function () { - websocket.destroy(); - }); - */ - // pipe websocket to wsReader - websocket.pipe(wsReader); - resolve(); - }).on("error", cdpOnError); - }); - // create blank-page - }).then(function () { - return Promise.resolve().then(function () { - return cdpClient.rpc("Target.createTarget", { - url: "about:blank" - }); - }).then(function ({ - targetId - }) { - return cdpClient.rpc("Target.attachToTarget", { - targetId, - flatten: true - }).then(function ({ - sessionId - }) { - return Page.create(cdpSessionDict[sessionId], targetId); // jslint ignore:line - }); - }).then(function (page) { - cdpClient.page = page; - }); - // resolve cdpClient - }).then(function () { - return cdpClient; - }); -} -local.noop(cdpClientCreate); -/* jslint ignore:start */ -/* -repo https://github.com/puppeteer/puppeteer/tree/v1.19.0 -committed 2019-07-23T05:02:45Z -*/ -/* -file https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/helper.js -*/ -/** - * Copyright 2017 Google Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -// const {TimeoutError} = exports_puppeteer_puppeteer_lib_Errors; -// const debugError = require("debug")(`puppeteer:error`); -// const fs = require("fs"); -class Helper { - /** - * @param {Function|string} fun - * @param {!Array<*>} args - * @return {string} - */ - static evaluationString(fun, ...args) { - if (Helper.isString(fun)) { - assert(args.length === 0, "Cannot evaluate a string with arguments"); - return /** @type {string} */ (fun); - } - return `(${fun})(${args.map(serializeArgument).join(",")})`; - /** - * @param {*} arg - * @return {string} - */ - function serializeArgument(arg) { - if (Object.is(arg, undefined)) { - return "undefined"; - } - return JSON.stringify(arg); - } - } - /** - * @param {!Protocol.Runtime.ExceptionDetails} exceptionDetails - * @return {string} - */ - static getExceptionMessage(exceptionDetails) { - if (exceptionDetails.exception) { - return exceptionDetails.exception.description || exceptionDetails.exception.value; - } - let message = exceptionDetails.text; - if (exceptionDetails.stackTrace) { - for (const callframe of exceptionDetails.stackTrace.callFrames) { - const location = callframe.url + ":" + callframe.lineNumber + ":" + callframe.columnNumber; - const functionName = callframe.functionName || ""; - message += `\n at ${functionName} (${location})`; - } - } - return message; - } - /** - * @param {!Protocol.Runtime.RemoteObject} remoteObject - * @return {*} - */ - static valueFromRemoteObject(remoteObject) { - assert(!remoteObject.objectId, "Cannot extract value when objectId is given"); - if (remoteObject.unserializableValue) { - if (remoteObject.type === "bigint" && typeof BigInt !== "undefined") { - return BigInt(remoteObject.unserializableValue.replace("n", "")); - } - switch (remoteObject.unserializableValue) { - case "-0": - return -0; - case "NaN": - return NaN; - case "Infinity": - return Infinity; - case "-Infinity": - return -Infinity; - default: - throw new Error("Unsupported unserializable value: " + remoteObject.unserializableValue); - } - } - return remoteObject.value; - } - /** - * @param {!Object} classType - */ - static installAsyncStackHooks(classType) { - for (const methodName of Reflect.ownKeys(classType.prototype)) { - const method = Reflect.get(classType.prototype, methodName); - if (methodName === "constructor" || typeof methodName !== "string" || methodName.startsWith("_") || typeof method !== "function" || method.constructor.name !== "AsyncFunction") { - continue; - } - Reflect.set(classType.prototype, methodName, function(...args) { - const syncStack = {}; - Error.captureStackTrace(syncStack); - return method.call(this, ...args).catch(function (e) { - const stack = syncStack.stack.substring(syncStack.stack.indexOf("\n") + 1); - const clientStack = stack.substring(stack.indexOf("\n")); - if (e instanceof Error && e.stack && !e.stack.includes(clientStack)) { - e.stack += "\n -- ASYNC --\n" + stack; - } - throw e; - }); - }); - } - } - /** - * @param {!NodeJS.EventEmitter} emitter - * @param {(string|symbol)} eventName - * @param {function(?):void} handler - * @return {{emitter: !NodeJS.EventEmitter, eventName: (string|symbol), handler: function(?)}} - */ - static addEventListener(emitter, eventName, handler) { - emitter.on(eventName, handler); - return { emitter, eventName, handler }; - } - /** - * @param {!Array<{emitter: !NodeJS.EventEmitter, eventName: (string|symbol), handler: function(?):void}>} listeners - */ - static removeEventListeners(listeners) { - listeners.forEach(function (listener) { - listener.emitter.removeListener(listener.eventName, listener.handler); - }); - listeners.splice(0, listeners.length); - } - /** - * @param {!Object} obj - * @return {boolean} - */ - static isString(obj) { - return typeof obj === "string" || obj instanceof String; - } - /** - * @param {!Object} obj - * @return {boolean} - */ - static isNumber(obj) { - return typeof obj === "number" || obj instanceof Number; - } - static promisify(nodeFunction) { - function promisified(...args) { - return new Promise(function (resolve, reject) { - function callback(err, ...result) { - if (err) { - return reject(err); - } - if (result.length === 1) { - return resolve(result[0]); - } - return resolve(result); - } - nodeFunction.call(null, ...args, callback); - }); - } - return promisified; - } - /** - * @template T - * @param {!Promise} promise - * @param {string} taskName - * @param {number} timeout - * @return {!Promise} - */ - static async waitWithTimeout(promise, taskName, timeout) { - let reject; - const timeoutError = new TimeoutError(`waiting for ${taskName} failed: timeout ${timeout}ms exceeded`); - const timeoutPromise = new Promise(function (resolve, x) { return reject = x; }); - let timeoutTimer = null; - if (timeout) { - timeoutTimer = setTimeout(function () { return reject(timeoutError); }, timeout); - } - try { - return await Promise.race([promise, timeoutPromise]); - } finally { - if (timeoutTimer) { - clearTimeout(timeoutTimer); - } - } - } -} -const openAsync = Helper.promisify(fs.open); -const writeAsync = Helper.promisify(fs.write); -const closeAsync = Helper.promisify(fs.close); -/** - * @param {*} value - * @param {string=} message - */ -function assert(value, message) { - if (!value) { - throw new Error(message); - } -} -exports_puppeteer_puppeteer_lib_helper = { - helper: Helper, - assert, - debugError -}; -// hack-puppeteer - init helper -let helper = exports_puppeteer_puppeteer_lib_helper.helper; -/* -file https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/Browser.js -*/ -/** - * Copyright 2017 Google Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -// const { helper, assert } = exports_puppeteer_puppeteer_lib_helper; -// const {Target} = exports_puppeteer_puppeteer_lib_Target; -// const EventEmitter = require("events"); -// const {Events} = exports_puppeteer_puppeteer_lib_Events; - - -/* jslint ignore:end */ -/* -file https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/Events.js -*/ -/** - * Copyright 2019 Google Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -const Events = { - Page: { - Close: "close", - Console: "console", - Dialog: "dialog", - DOMContentLoaded: "domcontentloaded", - Error: "error", - // Can't use just "error" due to node.js special treatment - // of error events. - // @see https://nodejs.org/api/events.html#events_error_events - PageError: "pageerror", - Request: "request", - Response: "response", - RequestFailed: "requestfailed", - RequestFinished: "requestfinished", - FrameAttached: "frameattached", - FrameDetached: "framedetached", - FrameNavigated: "framenavigated", - Load: "load", - Metrics: "metrics", - Popup: "popup", - WorkerCreated: "workercreated", - WorkerDestroyed: "workerdestroyed" - }, - Browser: { - TargetCreated: "targetcreated", - TargetDestroyed: "targetdestroyed", - TargetChanged: "targetchanged", - Disconnected: "disconnected" - }, - BrowserContext: { - TargetCreated: "targetcreated", - TargetDestroyed: "targetdestroyed", - TargetChanged: "targetchanged" - }, - NetworkManager: { - Request: Symbol("Events.NetworkManager.Request"), - Response: Symbol("Events.NetworkManager.Response"), - RequestFailed: Symbol("Events.NetworkManager.RequestFailed"), - RequestFinished: Symbol("Events.NetworkManager.RequestFinished") - }, - FrameManager: { - FrameAttached: Symbol("Events.FrameManager.FrameAttached"), - FrameNavigated: Symbol("Events.FrameManager.FrameNavigated"), - FrameDetached: Symbol("Events.FrameManager.FrameDetached"), - LifecycleEvent: Symbol("Events.FrameManager.LifecycleEvent"), - FrameNavigatedWithinDocument: Symbol( - "Events.FrameManager.FrameNavigatedWithinDocument" - ), - ExecutionContextCreated: Symbol( - "Events.FrameManager.ExecutionContextCreated" - ), - ExecutionContextDestroyed: Symbol( - "Events.FrameManager.ExecutionContextDestroyed" - ) - }, - Connection: { - Disconnected: Symbol("Events.Connection.Disconnected") - }, - CDPSession: { - Disconnected: Symbol("Events.CDPSession.Disconnected") - } -}; -local.noop(Events); -/* jslint ignore:start */ -exports_puppeteer_puppeteer_lib_Events = { Events }; -/* -file https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/Coverage.js -*/ -/** - * Copyright 2017 Google Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -// const {helper, debugError, assert} = exports_puppeteer_puppeteer_lib_helper; -/** - * @typedef {Object} CoverageEntry - * @property {string} url - * @property {string} text - * @property {!Array} ranges - */ -class Coverage { - /** - * @param {!Puppeteer.CDPSession} ssn - */ - constructor(ssn) { - this._jsCoverage = new JSCoverage(ssn); - this._cssCoverage = new CSSCoverage(ssn); - } -} -exports_puppeteer_puppeteer_lib_Coverage = {Coverage}; -class JSCoverage { - /** - * @param {!Puppeteer.CDPSession} ssn - */ - constructor(ssn) { - this.ssn = ssn; - this._enabled = false; - this._scriptURLs = new Map(); - this._scriptSources = new Map(); - this._eventListeners = []; - this._resetOnNavigation = false; - } -} -class CSSCoverage { - /** - * @param {!Puppeteer.CDPSession} ssn - */ - constructor(ssn) { - this.ssn = ssn; - this._enabled = false; - this._stylesheetURLs = new Map(); - this._stylesheetSources = new Map(); - this._eventListeners = []; - this._resetOnNavigation = false; - } -} -/* -file https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/DOMWorld.js -*/ -/** - * Copyright 2019 Google Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -// const fs = require("fs"); -// const {helper, assert} = exports_puppeteer_puppeteer_lib_helper; -// const {LifecycleWatcher} = exports_puppeteer_puppeteer_lib_LifecycleWatcher; -// const {TimeoutError} = exports_puppeteer_puppeteer_lib_Errors; -const readFileAsync = helper.promisify(fs.readFile); -/** - * @unrestricted - */ -class DOMWorld { - /** - * @param {!Puppeteer.FrameManager} frameManager - * @param {!Puppeteer.Frame} frame - * @param {!Puppeteer.TimeoutSettings} timeoutSettings - */ - constructor(frameManager, frame, timeoutSettings) { - let dom2 = this; - dom2._frameManager = frameManager; - dom2._frame = frame; - dom2._timeoutSettings = timeoutSettings; - dom2._documentPromise = null; - /** @type {!Promise} */ - dom2._contextPromise; - dom2._contextResolveCallback = null; - dom2._setContext(null); - /** @type {!Set} */ - dom2._waitTasks = new Set(); - dom2._detached = false; - } - /** - * @return {!Puppeteer.Frame} - */ - frame() { - return this._frame; - } - /** - * @param {?Puppeteer.ExecutionContext} context - */ - _setContext(context) { - let dom2 = this; - if (context) { - dom2._contextResolveCallback.call(null, context); - dom2._contextResolveCallback = null; - dom2._waitTasks.forEach(function (waitTask) { - waitTask.rerun(); - }); - } else { - dom2._documentPromise = null; - dom2._contextPromise = new Promise(function (resolve) { - dom2._contextResolveCallback = resolve; - }); - } - } - /** - * @return {boolean} - */ - _hasContext() { - return !this._contextResolveCallback; - } -} -exports_puppeteer_puppeteer_lib_DOMWorld = {DOMWorld}; -/* -file https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/EmulationManager.js -*/ -/** - * Copyright 2017 Google Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -class EmulationManager { - /** - * @param {!Puppeteer.CDPSession} ssn - */ - constructor(ssn) { - this.ssn = ssn; - this._emulatingMobile = false; - this._hasTouch = false; - } - /** - * @param {!Puppeteer.Viewport} viewport - * @return {Promise} - */ - async emulateViewport(viewport) { - const mobile = viewport.isMobile || false; - const width = viewport.width; - const height = viewport.height; - const deviceScaleFactor = viewport.deviceScaleFactor || 1; - /** @type {Protocol.Emulation.ScreenOrientation} */ - const screenOrientation = viewport.isLandscape ? { angle: 90, type: "landscapePrimary" } : { angle: 0, type: "portraitPrimary" }; - const hasTouch = viewport.hasTouch || false; - await Promise.all([ - this.ssn.rpc("Emulation.setDeviceMetricsOverride", { mobile, width, height, deviceScaleFactor, screenOrientation }, this.ssn._sessionId), - this.ssn.rpc("Emulation.setTouchEmulationEnabled", { - enabled: hasTouch - }, this.ssn._sessionId) - ]); - const reloadNeeded = this._emulatingMobile !== mobile || this._hasTouch !== hasTouch; - this._emulatingMobile = mobile; - this._hasTouch = hasTouch; - return reloadNeeded; - } -} -exports_puppeteer_puppeteer_lib_EmulationManager = {EmulationManager}; -/* -file https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/Errors.js -*/ -/** - * Copyright 2018 Google Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -class CustomError extends Error { - constructor(message) { - super(message); - this.name = this.constructor.name; - Error.captureStackTrace(this, this.constructor); - } -} -class TimeoutError extends CustomError {} -exports_puppeteer_puppeteer_lib_Errors = { - TimeoutError, -}; -/* -file https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/ExecutionContext.js -*/ -/** - * Copyright 2017 Google Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -// const {helper, assert} = exports_puppeteer_puppeteer_lib_helper; -// const {createJSHandle, JSHandle} = exports_puppeteer_puppeteer_lib_JSHandle; -class ExecutionContext { - /** - * @param {!Puppeteer.CDPSession} ssn - * @param {!Protocol.Runtime.ExecutionContextDescription} contextPayload - * @param {?Puppeteer.DOMWorld} world - */ - constructor(ssn, contextPayload, world) { - this.ssn = ssn; - this._world = world; - this._contextId = contextPayload.id; - } - /** - * @return {?Puppeteer.Frame} - */ - frame() { - return this._world ? this._world.frame() : null; - } -} -exports_puppeteer_puppeteer_lib_ExecutionContext = {ExecutionContext}; -/* -file https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/FrameManager.js -*/ -/** - * Copyright 2017 Google Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -// const EventEmitter = require("events"); -// const {helper, assert, debugError} = exports_puppeteer_puppeteer_lib_helper; -// const {Events} = exports_puppeteer_puppeteer_lib_Events; -// const {LifecycleWatcher} = exports_puppeteer_puppeteer_lib_LifecycleWatcher; -// const {DOMWorld} = exports_puppeteer_puppeteer_lib_DOMWorld; -// const {NetworkManager} = exports_puppeteer_puppeteer_lib_NetworkManager; -const UTILITY_WORLD_NAME = "__puppeteer_utility_world__"; -class FrameManager extends EventEmitter { - /** - * @param {!Puppeteer.CDPSession} ssn - * @param {!Puppeteer.Page} page - * @param {!Puppeteer.TimeoutSettings} timeoutSettings - */ - constructor(ssn, page, timeoutSettings) { - super(); - let fmgr = this; - fmgr.ssn = ssn; - fmgr._page = page; - fmgr._networkManager = new NetworkManager(ssn); - fmgr._networkManager.setFrameManager(fmgr); - fmgr._timeoutSettings = timeoutSettings; - /** @type {!Map} */ - fmgr._frames = new Map(); - /** @type {!Map} */ - fmgr._contextIdToContext = new Map(); - /** @type {!Set} */ - fmgr._isolatedWorlds = new Set(); - fmgr.ssn.on("Page.frameNavigated", function (event) { return fmgr._onFrameNavigated(event.frame); }); - fmgr.ssn.on("Page.frameStoppedLoading", function (event) { return fmgr._onFrameStoppedLoading(event.frameId); }); - fmgr.ssn.on("Runtime.executionContextCreated", function (data) { - data = data.context; - const frame = fmgr._frames.get( - data.auxData && data.auxData.frameId - ); - let dom2 = ( - frame && data.auxData && !!data.auxData["isDefault"] - ? frame._mainWorld - : ( - frame - && data.name === UTILITY_WORLD_NAME - && !frame._secondaryWorld._hasContext() - ) - // In case of multiple sessions to the same target, - // there's a race between connections so we might end up - // creating multiple isolated worlds. We can use either. - ? frame._secondaryWorld - : undefined - ); - if (data.auxData && data.auxData["type"] === "isolated") { - fmgr._isolatedWorlds.add(data.name); - } - /** @type {!ExecutionContext} */ - const context = new ExecutionContext(fmgr.ssn, data, dom2); - if (dom2) { - dom2._setContext(context); - } - fmgr._contextIdToContext.set(data.id, context); - }); - fmgr.ssn.on("Runtime.executionContextDestroyed", function (event) { return fmgr._onExecutionContextDestroyed(event.executionContextId); }); - fmgr.ssn.on("Runtime.executionContextsCleared", function (event) { return fmgr._onExecutionContextsCleared(); }); - fmgr.ssn.on("Page.lifecycleEvent", function (event) { return fmgr._onLifecycleEvent(event); }); - } - async initialize() { - let fmgr = this; - const [,{frameTree}] = await Promise.all([ - fmgr.ssn.rpc("Page.enable", undefined, fmgr.ssn._sessionId), - fmgr.ssn.rpc("Page.getFrameTree", undefined, fmgr.ssn._sessionId), - ]); - fmgr._handleFrameTree(frameTree); - await Promise.all([ - fmgr.ssn.rpc("Page.setLifecycleEventsEnabled", { enabled: true }, fmgr.ssn._sessionId), - fmgr.ssn.rpc("Runtime.enable", {}, fmgr.ssn._sessionId).then(function () { return fmgr._ensureIsolatedWorld(UTILITY_WORLD_NAME); }), - fmgr._networkManager.initialize(), - ]); - } - /** - * @return {!NetworkManager} - */ - networkManager() { - return this._networkManager; - } - /** - * @param {!Puppeteer.Frame} frame - * @param {string} url - * @param {!{referer?: string, timeout?: number, waitUntil?: string|!Array}=} options - * @return {!Promise} - */ - async navigateFrame(frame, url, options = {}) { - assertNoLegacyNavigationOptions(options); - const { - referer = this._networkManager.extraHTTPHeaders()["referer"], - waitUntil = ["load"], - timeout = this._timeoutSettings.navigationTimeout(), - } = options; - const watcher = new LifecycleWatcher(this, frame, waitUntil, timeout); - let ensureNewDocumentNavigation = false; - let error = await Promise.race([ - navigate(this.ssn, url, referer, frame._id), - watcher.timeoutOrTerminationPromise(), - ]); - if (!error) { - error = await Promise.race([ - watcher.timeoutOrTerminationPromise(), - ensureNewDocumentNavigation ? watcher.newDocumentNavigationPromise() : watcher.sameDocumentNavigationPromise(), - ]); - } - watcher.dispose(); - if (error) { - throw error; - } - return watcher.navigationResponse(); - /** - * @param {!Puppeteer.CDPSession} ssn - * @param {string} url - * @param {string} referrer - * @param {string} frameId - * @return {!Promise} - */ - async function navigate(ssn, url, referrer, frameId) { - try { - const response = await ssn.rpc("Page.navigate", {url, referrer, frameId}, ssn._sessionId); - ensureNewDocumentNavigation = !!response.loaderId; - return response.errorText ? new Error(`${response.errorText} at ${url}`) : null; - } catch (error) { - return error; - } - } - } - /** - * @param {!Protocol.Page.lifecycleEventPayload} event - */ - _onLifecycleEvent(event) { - const frame = this._frames.get(event.frameId); - if (!frame) { - return; - } - frame._onLifecycleEvent(event.loaderId, event.name); - this.emit(Events.FrameManager.LifecycleEvent, frame); - } - /** - * @param {string} frameId - */ - _onFrameStoppedLoading(frameId) { - const frame = this._frames.get(frameId); - if (!frame) { - return; - } - frame._onLoadingStopped(); - this.emit(Events.FrameManager.LifecycleEvent, frame); - } - /** - * @param {!Protocol.Page.FrameTree} frameTree - */ - _handleFrameTree(frameTree) { - let fmgr = this; - if (frameTree.frame.parentId) { - fmgr._onFrameAttached(frameTree.frame.id, frameTree.frame.parentId); - } - fmgr._onFrameNavigated(frameTree.frame); - if (!frameTree.childFrames) { - return; - } - frameTree.childFrames.forEach(function (child) { - fmgr._handleFrameTree(child); - }); - } - /** - * @return {!Frame} - */ - mainFrame() { - return this._mainFrame; - } - /** - * @return {!Array} - */ - frames() { - return Array.from(this._frames.values()); - } - /** - * @param {!string} frameId - * @return {?Frame} - */ - frame(frameId) { - return this._frames.get(frameId) || null; - } - /** - * @param {!Protocol.Page.Frame} framePayload - */ - _onFrameNavigated(framePayload) { - let fmgr = this; - const isMainFrame = !framePayload.parentId; - let frame = isMainFrame ? fmgr._mainFrame : fmgr._frames.get(framePayload.id); - assert(isMainFrame || frame, "We either navigate top level or have old version of the navigated frame"); - // Detach all child frames first. - if (frame) { - frame.childFrames().forEach(function (child) { - fmgr._removeFramesRecursively(child); - }); - } - // Update or create main frame. - if (isMainFrame) { - if (frame) { - // Update frame id to retain frame identity on cross-process navigation. - fmgr._frames.delete(frame._id); - frame._id = framePayload.id; - } else { - // Initial main frame navigation. - frame = new Frame(fmgr, fmgr.ssn, null, framePayload.id); - } - fmgr._frames.set(framePayload.id, frame); - fmgr._mainFrame = frame; - } - // Update frame payload. - frame._navigated(framePayload); - fmgr.emit(Events.FrameManager.FrameNavigated, frame); - } - /** - * @param {string} name - */ - async _ensureIsolatedWorld(name) { - let fmgr = this; - if (fmgr._isolatedWorlds.has(name)) { - return; - } - fmgr._isolatedWorlds.add(name); - await fmgr.ssn.rpc("Page.addScriptToEvaluateOnNewDocument", { - source: "//# sourceURL=__puppeteer_evaluation_script__", - worldName: name, - }, fmgr.ssn._sessionId), - await Promise.all(fmgr.frames().map(function (frame) { return fmgr.ssn.rpc("Page.createIsolatedWorld", { - frameId: frame._id, - grantUniveralAccess: true, - worldName: name, - }, fmgr.ssn._sessionId).catch(debugError); })); // frames might be removed before we send this - } - /** - * @param {number} executionContextId - */ - _onExecutionContextDestroyed(executionContextId) { - const context = this._contextIdToContext.get(executionContextId); - if (!context) { - return; - } - this._contextIdToContext.delete(executionContextId); - if (context._world) { - context._world._setContext(null); - } - } - _onExecutionContextsCleared() { - for (const context of this._contextIdToContext.values()) { - if (context._world) { - context._world._setContext(null); - } - } - this._contextIdToContext.clear(); - } - /** - * @param {number} contextId - * @return {!ExecutionContext} - */ - executionContextById(contextId) { - const context = this._contextIdToContext.get(contextId); - assert(context, "INTERNAL ERROR: missing context with id = " + contextId); - return context; - } -} -/** - * @unrestricted - */ -class Frame { - /** - * @param {!FrameManager} frameManager - * @param {!Puppeteer.CDPSession} ssn - * @param {?Frame} parentFrame - * @param {string} frameId - */ - constructor(frameManager, ssn, parentFrame, frameId) { - let frm2 = this; - frm2._frameManager = frameManager; - frm2.ssn = ssn; - frm2._parentFrame = parentFrame; - frm2._url = ""; - frm2._id = frameId; - frm2._detached = false; - frm2._loaderId = ""; - /** @type {!Set} */ - frm2._lifecycleEvents = new Set(); - /** @type {!DOMWorld} */ - frm2._mainWorld = new DOMWorld(frameManager, frm2, frameManager._timeoutSettings); - /** @type {!DOMWorld} */ - frm2._secondaryWorld = new DOMWorld(frameManager, frm2, frameManager._timeoutSettings); - /** @type {!Set} */ - frm2._childFrames = new Set(); - if (frm2._parentFrame) { - frm2._parentFrame._childFrames.add(frm2); - } - } - /** - * @param {string} url - * @param {!{referer?: string, timeout?: number, waitUntil?: string|!Array}=} options - * @return {!Promise} - */ - async goto(url, options) { - return await this._frameManager.navigateFrame(this, url, options); - } - /** - * @return {!Array.} - */ - childFrames() { - return Array.from(this._childFrames); - } - /** - * @param {!Protocol.Page.Frame} framePayload - */ - _navigated(framePayload) { - this._name = framePayload.name; - // TODO(lushnikov): remove this once requestInterception has loaderId exposed. - this._navigationURL = framePayload.url; - this._url = framePayload.url; - } - /** - * @param {string} loaderId - * @param {string} name - */ - _onLifecycleEvent(loaderId, name) { - if (name === "init") { - this._loaderId = loaderId; - this._lifecycleEvents.clear(); - } - this._lifecycleEvents.add(name); - } - _onLoadingStopped() { - this._lifecycleEvents.add("DOMContentLoaded"); - this._lifecycleEvents.add("load"); - } -} -function assertNoLegacyNavigationOptions(options) { - assert(options["networkIdleTimeout"] === undefined, "ERROR: networkIdleTimeout option is no longer supported."); - assert(options["networkIdleInflight"] === undefined, "ERROR: networkIdleInflight option is no longer supported."); - assert(options.waitUntil !== "networkidle", "ERROR: \"networkidle\" option is no longer supported. Use \"networkidle2\" instead"); -} -exports_puppeteer_puppeteer_lib_FrameManager = {FrameManager, Frame}; -/* -file https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/JSHandle.js -*/ -/** - * Copyright 2019 Google Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -// const {helper, assert, debugError} = exports_puppeteer_puppeteer_lib_helper; -// const path = require("path"); -function createJSHandle(context, remoteObject) { - const frame = context.frame(); - if (remoteObject.subtype === "node" && frame) { - const frameManager = frame._frameManager; - return new ElementHandle(context, context.ssn, remoteObject, frameManager.page(), frameManager); - } - return new JSHandle(context, context.ssn, remoteObject); -} -class JSHandle { - /** - * @param {!Puppeteer.ExecutionContext} context - * @param {!Puppeteer.CDPSession} ssn - * @param {!Protocol.Runtime.RemoteObject} remoteObject - */ - constructor(context, ssn, remoteObject) { - this._context = context; - this.ssn = ssn; - this._remoteObject = remoteObject; - this._disposed = false; - } - async dispose() { - if (this._disposed) { - return; - } - this._disposed = true; - } -} -exports_puppeteer_puppeteer_lib_JSHandle = {createJSHandle, JSHandle}; -/* -file https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/LifecycleWatcher.js -*/ -/** - * Copyright 2019 Google Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -// const {helper, assert} = exports_puppeteer_puppeteer_lib_helper; -// const {Events} = exports_puppeteer_puppeteer_lib_Events; -// const {TimeoutError} = exports_puppeteer_puppeteer_lib_Errors; -class LifecycleWatcher { - /** - * @param {!Puppeteer.FrameManager} frameManager - * @param {!Puppeteer.Frame} frame - * @param {string|!Array} waitUntil - * @param {number} timeout - */ - constructor(frameManager, frame, waitUntil, timeout) { - let that = this; - if (Array.isArray(waitUntil)) { - waitUntil = waitUntil.slice(); - } - else if (typeof waitUntil === "string") { - waitUntil = [waitUntil]; - } - that._expectedLifecycle = waitUntil.map(function (value) { - const protocolEvent = puppeteerToProtocolLifecycle[value]; - assert(protocolEvent, "Unknown value for options.waitUntil: " + value); - return protocolEvent; - }); - that._frameManager = frameManager; - that._frame = frame; - that._initialLoaderId = frame._loaderId; - that._timeout = timeout; - /** @type {?Puppeteer.Request} */ - that._navigationRequest = null; - that._eventListeners = [ - helper.addEventListener(frameManager.ssn, Events.CDPSession.Disconnected, function () { return that._terminate(new Error("Navigation failed because browser has disconnected!")); }), - helper.addEventListener(that._frameManager, Events.FrameManager.LifecycleEvent, that._checkLifecycleComplete.bind(that)), - helper.addEventListener(that._frameManager.networkManager(), Events.NetworkManager.Request, that._onRequest.bind(that)), - ]; - that._sameDocumentNavigationPromise = new Promise(function (resolve) { - that._sameDocumentNavigationCompleteCallback = resolve; - }); - that._lifecyclePromise = new Promise(function (resolve) { - that._lifecycleCallback = resolve; - }); - that._newDocumentNavigationPromise = new Promise(function (resolve) { - that._newDocumentNavigationCompleteCallback = resolve; - }); - that._timeoutPromise = that._createTimeoutPromise(); - that._terminationPromise = new Promise(function (resolve) { - that._terminationCallback = resolve; - }); - that._checkLifecycleComplete(); - } - /** - * @param {!Puppeteer.Request} request - */ - _onRequest(request) { - if (request.frame() !== this._frame || !request.isNavigationRequest()) { - return; - } - this._navigationRequest = request; - } - /** - * @return {?Puppeteer.Response} - */ - navigationResponse() { - return this._navigationRequest ? this._navigationRequest.response() : null; - } - /** - * @param {!Error} error - */ - _terminate(error) { - this._terminationCallback.call(null, error); - } - /** - * @return {!Promise} - */ - sameDocumentNavigationPromise() { - return this._sameDocumentNavigationPromise; - } - /** - * @return {!Promise} - */ - newDocumentNavigationPromise() { - return this._newDocumentNavigationPromise; - } - /** - * @return {!Promise} - */ - timeoutOrTerminationPromise() { - return Promise.race([this._timeoutPromise, this._terminationPromise]); - } - /** - * @return {!Promise} - */ - _createTimeoutPromise() { - let that = this; - if (!that._timeout) { - return new Promise(function () {}); - } - const errorMessage = "Navigation Timeout Exceeded: " + that._timeout + "ms exceeded"; - return new Promise(function (resolve) { return that._maximumTimer = setTimeout(resolve, that._timeout); }) - .then(function () { return new TimeoutError(errorMessage); }); - } - _checkLifecycleComplete() { - // We expect navigation to commit. - if (!checkLifecycle(this._frame, this._expectedLifecycle)) { - return; - } - this._lifecycleCallback(); - if (this._frame._loaderId === this._initialLoaderId && !this._hasSameDocumentNavigation) { - return; - } - if (this._hasSameDocumentNavigation) { - this._sameDocumentNavigationCompleteCallback(); - } - if (this._frame._loaderId !== this._initialLoaderId) { - this._newDocumentNavigationCompleteCallback(); - } - /** - * @param {!Puppeteer.Frame} frame - * @param {!Array} expectedLifecycle - * @return {boolean} - */ - function checkLifecycle(frame, expectedLifecycle) { - for (const event of expectedLifecycle) { - if (!frame._lifecycleEvents.has(event)) { - return false; - } - } - for (const child of frame.childFrames()) { - if (!checkLifecycle(child, expectedLifecycle)) { - return false; - } - } - return true; - } - } - dispose() { - helper.removeEventListeners(this._eventListeners); - clearTimeout(this._maximumTimer); - } -} -const puppeteerToProtocolLifecycle = { - "load": "load", - "domcontentloaded": "DOMContentLoaded", - "networkidle0": "networkIdle", - "networkidle2": "networkAlmostIdle", -}; -exports_puppeteer_puppeteer_lib_LifecycleWatcher = {LifecycleWatcher}; -/* -file https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/NetworkManager.js -*/ -/** - * Copyright 2017 Google Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -// const EventEmitter = require("events"); -// const {helper, assert, debugError} = exports_puppeteer_puppeteer_lib_helper; -// const {Events} = exports_puppeteer_puppeteer_lib_Events; -class NetworkManager extends EventEmitter { - /** - * @param {!Puppeteer.CDPSession} ssn - */ - constructor(ssn) { - super(); - this.ssn = ssn; - this._frameManager = null; - /** @type {!Map} */ - this._requestIdToRequest = new Map(); - /** @type {!Map} */ - this._requestIdToRequestWillBeSentEvent = new Map(); - /** @type {!Object} */ - this._extraHTTPHeaders = {}; - this._offline = false; - /** @type {?{username: string, password: string}} */ - this._credentials = null; - /** @type {!Set} */ - this._attemptedAuthentications = new Set(); - this._userRequestInterceptionEnabled = false; - this._protocolRequestInterceptionEnabled = false; - this._userCacheDisabled = false; - /** @type {!Map} */ - this._requestIdToInterceptionId = new Map(); - this.ssn.on("Network.requestWillBeSent", this._onRequestWillBeSent.bind(this)); - this.ssn.on("Network.responseReceived", this._onResponseReceived.bind(this)); - this.ssn.on("Network.loadingFinished", this._onLoadingFinished.bind(this)); - } - async initialize() { - await this.ssn.rpc("Network.enable", undefined, this.ssn._sessionId); - if (this._ignoreHTTPSErrors) { - await this.ssn.rpc("Security.setIgnoreCertificateErrors", {ignore: true}, this.ssn._sessionId); - } - } - /** - * @param {!Puppeteer.FrameManager} frameManager - */ - setFrameManager(frameManager) { - this._frameManager = frameManager; - } - /** - * @return {!Object} - */ - extraHTTPHeaders() { - return Object.assign({}, this._extraHTTPHeaders); - } - /** - * @param {!Protocol.Network.requestWillBeSentPayload} event - */ - _onRequestWillBeSent(event) { - // Request interception doesn't happen for data URLs with Network Service. - if (this._protocolRequestInterceptionEnabled && !event.request.url.startsWith("data:")) { - const requestId = event.requestId; - const interceptionId = this._requestIdToInterceptionId.get(requestId); - if (interceptionId) { - this._onRequest(event, interceptionId); - this._requestIdToInterceptionId.delete(requestId); - } else { - this._requestIdToRequestWillBeSentEvent.set(event.requestId, event); - } - return; - } - this._onRequest(event, null); - } - /** - * @param {!Protocol.Network.requestWillBeSentPayload} event - * @param {?string} interceptionId - */ - _onRequest(event, interceptionId) { - const frame = event.frameId && this._frameManager ? this._frameManager.frame(event.frameId) : null; - const request = new Request(this.ssn, frame, interceptionId, this._userRequestInterceptionEnabled, event); - this._requestIdToRequest.set(event.requestId, request); - this.emit(Events.NetworkManager.Request, request); - } - /** - * @param {!Protocol.Network.responseReceivedPayload} event - */ - _onResponseReceived(event) { - const request = this._requestIdToRequest.get(event.requestId); - // FileUpload sends a response without a matching request. - if (!request) { - return; - } - const response = new Response(this.ssn, request, event.response); - request._response = response; - this.emit(Events.NetworkManager.Response, response); - } - /** - * @param {!Protocol.Network.loadingFinishedPayload} event - */ - _onLoadingFinished(event) { - const request = this._requestIdToRequest.get(event.requestId); - // For certain requestIds we never receive requestWillBeSent event. - // @see https://crbug.com/750469 - if (!request) { - return; - } - // Under certain conditions we never get the Network.responseReceived - // event from protocol. @see https://crbug.com/883475 - if (request.response()) { - request.response()._bodyLoadedPromiseFulfill.call(null); - } - this._requestIdToRequest.delete(request._requestId); - this._attemptedAuthentications.delete(request._interceptionId); - this.emit(Events.NetworkManager.RequestFinished, request); - } -} -class Request { - /** - * @param {!Puppeteer.CDPSession} ssn - * @param {?Puppeteer.Frame} frame - * @param {string} interceptionId - * @param {boolean} allowInterception - * @param {!Protocol.Network.requestWillBeSentPayload} event - */ - constructor(ssn, frame, interceptionId, allowInterception, event) { - let that = this; - that.ssn = ssn; - that._requestId = event.requestId; - that._isNavigationRequest = event.requestId === event.loaderId && event.type === "Document"; - that._interceptionId = interceptionId; - that._allowInterception = allowInterception; - that._interceptionHandled = false; - that._response = null; - that._failureText = null; - that._url = event.request.url; - that._resourceType = event.type.toLowerCase(); - that._method = event.request.method; - that._postData = event.request.postData; - that._headers = {}; - that._frame = frame; - Object.keys(event.request.headers).forEach(function (key) { - that._headers[key.toLowerCase()] = event.request.headers[key]; - }); - that._fromMemoryCache = false; - } - /** - * @return {string} - */ - url() { - return this._url; - } - /** - * @return {?Response} - */ - response() { - return this._response; - } - /** - * @return {?Puppeteer.Frame} - */ - frame() { - return this._frame; - } - /** - * @return {boolean} - */ - isNavigationRequest() { - return this._isNavigationRequest; - } - /** - * @return {?{errorText: string}} - */ - failure() { - if (!this._failureText) { - return null; - } - return { - errorText: this._failureText - }; - } -} -const errorReasons = { - "aborted": "Aborted", - "accessdenied": "AccessDenied", - "addressunreachable": "AddressUnreachable", - "blockedbyclient": "BlockedByClient", - "blockedbyresponse": "BlockedByResponse", - "connectionaborted": "ConnectionAborted", - "connectionclosed": "ConnectionClosed", - "connectionfailed": "ConnectionFailed", - "connectionrefused": "ConnectionRefused", - "connectionreset": "ConnectionReset", - "internetdisconnected": "InternetDisconnected", - "namenotresolved": "NameNotResolved", - "timedout": "TimedOut", - "failed": "Failed", -}; -class Response { - /** - * @param {!Puppeteer.CDPSession} ssn - * @param {!Request} request - * @param {!Protocol.Network.Response} responsePayload - */ - constructor(ssn, request, responsePayload) { - let that = this; - that.ssn = ssn; - that._request = request; - that._contentPromise = null; - that._bodyLoadedPromise = new Promise(function (resolve) { - that._bodyLoadedPromiseFulfill = resolve; - }); - that._remoteAddress = { - ip: responsePayload.remoteIPAddress, - port: responsePayload.remotePort, - }; - that._status = responsePayload.status; - that._statusText = responsePayload.statusText; - that._url = request.url(); - that._fromDiskCache = !!responsePayload.fromDiskCache; - that._fromServiceWorker = !!responsePayload.fromServiceWorker; - that._headers = {}; - Object.keys(responsePayload.headers).forEach(function (key) { - that._headers[key.toLowerCase()] = responsePayload.headers[key]; - }); - } -} -// List taken from https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml with extra 306 and 418 codes. -const STATUS_TEXTS = { - "100": "Continue", - "101": "Switching Protocols", - "102": "Processing", - "103": "Early Hints", - "200": "OK", - "201": "Created", - "202": "Accepted", - "203": "Non-Authoritative Information", - "204": "No Content", - "205": "Reset Content", - "206": "Partial Content", - "207": "Multi-Status", - "208": "Already Reported", - "226": "IM Used", - "300": "Multiple Choices", - "301": "Moved Permanently", - "302": "Found", - "303": "See Other", - "304": "Not Modified", - "305": "Use Proxy", - "306": "Switch Proxy", - "307": "Temporary Redirect", - "308": "Permanent Redirect", - "400": "Bad Request", - "401": "Unauthorized", - "402": "Payment Required", - "403": "Forbidden", - "404": "Not Found", - "405": "Method Not Allowed", - "406": "Not Acceptable", - "407": "Proxy Authentication Required", - "408": "Request Timeout", - "409": "Conflict", - "410": "Gone", - "411": "Length Required", - "412": "Precondition Failed", - "413": "Payload Too Large", - "414": "URI Too Long", - "415": "Unsupported Media Type", - "416": "Range Not Satisfiable", - "417": "Expectation Failed", - "418": "I'm a teapot", - "421": "Misdirected Request", - "422": "Unprocessable Entity", - "423": "Locked", - "424": "Failed Dependency", - "425": "Too Early", - "426": "Upgrade Required", - "428": "Precondition Required", - "429": "Too Many Requests", - "431": "Request Header Fields Too Large", - "451": "Unavailable For Legal Reasons", - "500": "Internal Server Error", - "501": "Not Implemented", - "502": "Bad Gateway", - "503": "Service Unavailable", - "504": "Gateway Timeout", - "505": "HTTP Version Not Supported", - "506": "Variant Also Negotiates", - "507": "Insufficient Storage", - "508": "Loop Detected", - "510": "Not Extended", - "511": "Network Authentication Required", -}; -exports_puppeteer_puppeteer_lib_NetworkManager = {Request, Response, NetworkManager}; -/* -file https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/Page.js -*/ -/** - * Copyright 2017 Google Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -// const fs = require("fs"); -// const path = require("path"); -// const EventEmitter = require("events"); -// const mime = require("mime"); -// const {Events} = exports_puppeteer_puppeteer_lib_Events; -// const {Connection} = exports_puppeteer_puppeteer_lib_Connection; -// const {EmulationManager} = exports_puppeteer_puppeteer_lib_EmulationManager; -// const {FrameManager} = exports_puppeteer_puppeteer_lib_FrameManager; -// const {helper, debugError, assert} = exports_puppeteer_puppeteer_lib_helper; -// const {Coverage} = exports_puppeteer_puppeteer_lib_Coverage; -// const {createJSHandle} = exports_puppeteer_puppeteer_lib_JSHandle; -// const {TimeoutSettings} = exports_puppeteer_puppeteer_lib_TimeoutSettings; -const writeFileAsync = helper.promisify(fs.writeFile); -class Page extends EventEmitter { - /** - * @param {!Puppeteer.CDPSession} ssn - * @param {!Puppeteer.Target} target - * @return {!Promise} - */ - static async create(ssn, targetId) { - const page = new Page(ssn, targetId); - await page._initialize(); - // defaultViewport - await page._emulationManager.emulateViewport({ - width: 800, - height: 600 - }) - return page; - } - /** - * @param {!Puppeteer.CDPSession} ssn - * @param {!Puppeteer.Target} targetId - */ - constructor(ssn, targetId) { - super(); - let pg2 = this; - pg2._closed = false; - pg2.ssn = ssn; - pg2.rpc = ssn.rpc; - pg2._targetId = targetId; - pg2._timeoutSettings = new TimeoutSettings(); - /** @type {!FrameManager} */ - pg2._frameManager = new FrameManager(ssn, pg2, pg2._timeoutSettings); - pg2._emulationManager = new EmulationManager(ssn); - /** @type {!Map} */ - pg2._pageBindings = new Map(); - pg2._coverage = new Coverage(ssn); - pg2._javascriptEnabled = true; - /** @type {!Map} */ - pg2._workers = new Map(); - pg2._frameManager.on(Events.FrameManager.FrameAttached, function (event) { return pg2.emit(Events.Page.FrameAttached, event); }); - pg2._frameManager.on(Events.FrameManager.FrameNavigated, function (event) { return pg2.emit(Events.Page.FrameNavigated, event); }); - const networkManager = pg2._frameManager.networkManager(); - networkManager.on(Events.NetworkManager.Request, function (event) { return pg2.emit(Events.Page.Request, event); }); - networkManager.on(Events.NetworkManager.Response, function (event) { return pg2.emit(Events.Page.Response, event); }); - networkManager.on(Events.NetworkManager.RequestFailed, function (event) { return pg2.emit(Events.Page.RequestFailed, event); }); - networkManager.on(Events.NetworkManager.RequestFinished, function (event) { return pg2.emit(Events.Page.RequestFinished, event); }); - ssn.on("Page.domContentEventFired", function (event) { return pg2.emit(Events.Page.DOMContentLoaded); }); - ssn.on("Page.loadEventFired", function (event) { return pg2.emit(Events.Page.Load); }); - ssn.on("Runtime.consoleAPICalled", function (event) { return pg2._onConsoleAPI(event); }); - ssn.on("Runtime.bindingCalled", function (event) { return pg2._onBindingCalled(event); }); - ssn.on("Page.javascriptDialogOpening", function (event) { return pg2._onDialog(event); }); - ssn.on("Runtime.exceptionThrown", function (exception) { return pg2._handleException(exception.exceptionDetails); }); - ssn.on("Inspector.targetCrashed", function (event) { return pg2._onTargetCrashed(); }); - ssn.on("Log.entryAdded", function (event) { return pg2._onLogEntryAdded(event); }); - } - async _initialize() { - let pg2 = this; - await Promise.all([ - pg2._frameManager.initialize(), - pg2.ssn.rpc("Target.setAutoAttach", {autoAttach: true, waitForDebuggerOnStart: false, flatten: true}, this.ssn._sessionId), - pg2.ssn.rpc("Performance.enable", {}, this.ssn._sessionId), - pg2.ssn.rpc("Log.enable", {}, this.ssn._sessionId), - ]); - } - /** - * @param {!Protocol.Log.entryAddedPayload} event - */ - _onLogEntryAdded(event) { - let pg2 = this; - const {level, text, args, source, url, lineNumber} = event.entry; - if (source !== "worker") { - pg2.emit(Events.Page.Console, new ConsoleMessage(level, text, [], {url, lineNumber})); - } - } - /** - * @return {!Touchscreen} - */ - get touchscreen() { - return this._touchscreen; - } - /** - * @return {!Coverage} - */ - get coverage() { - return this._coverage; - } - /** - * @param {?Array} metrics - * @return {!Metrics} - */ - _buildMetricsObject(metrics) { - const result = {}; - for (const metric of metrics || []) { - if (supportedMetrics.has(metric.name)) { - result[metric.name] = metric.value; - } - } - return result; - } - /** - * @param {!Protocol.Runtime.ExceptionDetails} exceptionDetails - */ - _handleException(exceptionDetails) { - const message = helper.getExceptionMessage(exceptionDetails); - const err = new Error(message); - err.stack = ""; // Don't report clientside error with a node stack attached - this.emit(Events.Page.PageError, err); - } - /** - * @param {!Protocol.Runtime.consoleAPICalledPayload} event - */ - async _onConsoleAPI(event) { - if (event.executionContextId === 0) { - // DevTools protocol stores the last 1000 console messages. These - // messages are always reported even for removed execution contexts. In - // this case, they are marked with executionContextId = 0 and are - // reported upon enabling Runtime agent. - // - // Ignore these messages since: - // - there's no execution context we can use to operate with message - // arguments - // - these messages are reported before Puppeteer clients can subscribe - // to the "console" - // page event. - // - // @see https://github.com/GoogleChrome/puppeteer/issues/3865 - return; - } - const context = this._frameManager.executionContextById(event.executionContextId); - const values = event.args.map(function (arg) { return createJSHandle(context, arg); }); - this._addConsoleMessage(event.type, values, event.stackTrace); - } - /** - * @param {string} type - * @param {!Array} args - * @param {Protocol.Runtime.StackTrace=} stackTrace - */ - _addConsoleMessage(type, args, stackTrace) { - if (!this.listenerCount(Events.Page.Console)) { - args.forEach(function (arg) { return arg.dispose(); }); - return; - } - const textTokens = []; - for (const arg of args) { - const remoteObject = arg._remoteObject; - if (remoteObject.objectId) { - textTokens.push(arg.toString()); - } - else { - textTokens.push(helper.valueFromRemoteObject(remoteObject)); - } - } - const location = stackTrace && stackTrace.callFrames.length ? { - url: stackTrace.callFrames[0].url, - lineNumber: stackTrace.callFrames[0].lineNumber, - columnNumber: stackTrace.callFrames[0].columnNumber, - } : {}; - const message = new ConsoleMessage(type, textTokens.join(" "), args, location); - this.emit(Events.Page.Console, message); - } - /** - * @param {string} url - * @param {!{referer?: string, timeout?: number, waitUntil?: string|!Array}=} options - * @return {!Promise} - */ - async goto(url, options) { - return await this._frameManager.mainFrame().goto(url, options); - } - async evaluate(expr) { - let pg2 = this; - let dom2 = pg2._frameManager.mainFrame()._mainWorld; - let ctx = await dom2._contextPromise; - //!! let ctx = await pg2._frameManager.mainFrame()._mainWorld._contextPromise; - const { - exceptionDetails, - result: remoteObject - } = await pg2.rpc("Runtime.evaluate", { - awaitPromise: true, - contextId: ctx._contextId, - expression: ( - ( - /^[\040\t]*\/\/[@#] sourceURL=\s*(\S*?)\s*$/m - ).test(expr) - ? expr - : expr + "\n" + "//# sourceURL=__puppeteer_evaluation_script__" - ), - returnByValue: false, - userGesture: true - }, ctx.ssn._sessionId); - local.assertOrThrow(!exceptionDetails, exceptionDetails); - return helper.valueFromRemoteObject(remoteObject); - } - /** - * @param {!ScreenshotOptions=} options - * @return {!Promise} - */ - async screenshot(options = {}) { - let screenshotType = null; - // options.type takes precedence over inferring the type from options.path - // because it may be a 0-length file with no extension created beforehand (i.e. as a temp file). - if (options.type) { - assert(options.type === "png" || options.type === "jpeg", "Unknown options.type value: " + options.type); - screenshotType = options.type; - } else if (options.path) { - const mimeType = mime.getType(options.path); - if (mimeType === "image/png") { - screenshotType = "png"; - } - else if (mimeType === "image/jpeg") { - screenshotType = "jpeg"; - } - assert(screenshotType, "Unsupported screenshot mime type: " + mimeType); - } - if (!screenshotType) { - screenshotType = "png"; - } - if (options.quality) { - assert(screenshotType === "jpeg", "options.quality is unsupported for the " + screenshotType + " screenshots"); - assert(typeof options.quality === "number", "Expected options.quality to be a number but found " + (typeof options.quality)); - assert(Number.isInteger(options.quality), "Expected options.quality to be an integer"); - assert(options.quality >= 0 && options.quality <= 100, "Expected options.quality to be between 0 and 100 (inclusive), got " + options.quality); - } - return Promise.resolve().then(this._screenshotTask.bind(this, screenshotType, options)); - } - /** - * @param {"png"|"jpeg"} format - * @param {!ScreenshotOptions=} options - * @return {!Promise} - */ - async _screenshotTask(format, options) { - await this.ssn.rpc("Target.activateTarget", {targetId: this._targetId}, this.ssn._sessionId); - let clip; - const shouldSetDefaultBackground = options.omitBackground && format === "png"; - const result = await this.ssn.rpc("Page.captureScreenshot", { format, quality: options.quality, clip }, this.ssn._sessionId); - const buffer = options.encoding === "base64" ? result.data : Buffer.from(result.data, "base64"); - if (options.path) { - await writeFileAsync(options.path, buffer); - } - return buffer; - } -} -/** - * @typedef {Object} PDFOptions - * @property {number=} scale - * @property {boolean=} displayHeaderFooter - * @property {string=} headerTemplate - * @property {string=} footerTemplate - * @property {boolean=} printBackground - * @property {boolean=} landscape - * @property {string=} pageRanges - * @property {string=} format - * @property {string|number=} width - * @property {string|number=} height - * @property {boolean=} preferCSSPageSize - * @property {!{top?: string|number, bottom?: string|number, left?: string|number, right?: string|number}=} margin - * @property {string=} path - */ -/** - * @typedef {Object} Metrics - * @property {number=} Timestamp - * @property {number=} Documents - * @property {number=} Frames - * @property {number=} JSEventListeners - * @property {number=} Nodes - * @property {number=} LayoutCount - * @property {number=} RecalcStyleCount - * @property {number=} LayoutDuration - * @property {number=} RecalcStyleDuration - * @property {number=} ScriptDuration - * @property {number=} TaskDuration - * @property {number=} JSHeapUsedSize - * @property {number=} JSHeapTotalSize - */ -/** - * @typedef {Object} ScreenshotOptions - * @property {string=} type - * @property {string=} path - * @property {{x: number, y: number, width: number, height: number}=} clip - * @property {number=} quality - * @property {boolean=} omitBackground - * @property {string=} encoding - */ -/** @type {!Set} */ -const supportedMetrics = new Set([ - "Timestamp", - "Documents", - "Frames", - "JSEventListeners", - "Nodes", - "LayoutCount", - "RecalcStyleCount", - "LayoutDuration", - "RecalcStyleDuration", - "ScriptDuration", - "TaskDuration", - "JSHeapUsedSize", - "JSHeapTotalSize", -]); -/** - * @typedef {Object} ConsoleMessage.Location - * @property {string=} url - * @property {number=} lineNumber - * @property {number=} columnNumber - */ -class ConsoleMessage { - /** - * @param {string} type - * @param {string} text - * @param {!Array} args - * @param {ConsoleMessage.Location} location - */ - constructor(type, text, args, location = {}) { - this._type = type; - this._text = text; - this._args = args; - this._location = location; - } -} -exports_puppeteer_puppeteer_lib_Page = {Page, ConsoleMessage}; -/* -file https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/Target.js -*/ -/** - * Copyright 2019 Google Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* -file https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/TimeoutSettings.js -*/ -/** - * Copyright 2019 Google Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -const DEFAULT_TIMEOUT = 30000; -class TimeoutSettings { - constructor() { - this._defaultTimeout = null; - this._defaultNavigationTimeout = null; - } - /** - * @return {number} - */ - navigationTimeout() { - if (this._defaultNavigationTimeout !== null) { - return this._defaultNavigationTimeout; - } - if (this._defaultTimeout !== null) { - return this._defaultTimeout; - } - return DEFAULT_TIMEOUT; - } -} -exports_puppeteer_puppeteer_lib_TimeoutSettings = {TimeoutSettings}; -/* -file https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/api.js -*/ -/** - * Copyright 2019 Google Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -exports_puppeteer_puppeteer_lib_api = { - Browser: exports_puppeteer_puppeteer_lib_Browser.Browser, - BrowserContext: exports_puppeteer_puppeteer_lib_Browser.BrowserContext, - BrowserFetcher: exports_puppeteer_puppeteer_lib_BrowserFetcher, - CDPSession: exports_puppeteer_puppeteer_lib_Connection.CDPSession, - ConsoleMessage: exports_puppeteer_puppeteer_lib_Page.ConsoleMessage, - Coverage: exports_puppeteer_puppeteer_lib_Coverage.Coverage, - ExecutionContext: exports_puppeteer_puppeteer_lib_ExecutionContext.ExecutionContext, - Frame: exports_puppeteer_puppeteer_lib_FrameManager.Frame, - JSHandle: exports_puppeteer_puppeteer_lib_JSHandle.JSHandle, - Mouse: exports_puppeteer_puppeteer_lib_Input.Mouse, - Page: exports_puppeteer_puppeteer_lib_Page.Page, - Puppeteer: exports_puppeteer_puppeteer_lib_Puppeteer, - Request: exports_puppeteer_puppeteer_lib_NetworkManager.Request, - Response: exports_puppeteer_puppeteer_lib_NetworkManager.Response, - Target: exports_puppeteer_puppeteer_lib_Target.Target, - TimeoutError: exports_puppeteer_puppeteer_lib_Errors.TimeoutError, - Touchscreen: exports_puppeteer_puppeteer_lib_Input.Touchscreen, -}; -/* -file https://github.com/puppeteer/puppeteer/blob/v1.19.0/index.js -*/ -/** - * Copyright 2017 Google Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -const api = exports_puppeteer_puppeteer_lib_api; -Object.entries(api).forEach(function ([ - className, val -]) { - // Puppeteer-web excludes certain classes from bundle, e.g. BrowserFetcher. - if (typeof val === "function") { - helper.installAsyncStackHooks(val); - } -}); -// If node does not support async await, use the compiled version. -const Puppeteer = exports_puppeteer_puppeteer_lib_Puppeteer -// let Browser = exports_puppeteer_puppeteer_lib_Browser.Browser; -let BrowserFetcher = exports_puppeteer_puppeteer_lib_BrowserFetcher; -// let Connection = exports_puppeteer_puppeteer_lib_Connection.Connection; -// let Coverage = exports_puppeteer_puppeteer_lib_Coverage.Coverage; -// let DOMWorld = exports_puppeteer_puppeteer_lib_DOMWorld.DOMWorld; -// let DeviceDescriptors = exports_puppeteer_puppeteer_lib_DeviceDescriptors; -// let EmulationManager = exports_puppeteer_puppeteer_lib_EmulationManager.EmulationManager; -// let TimeoutError = exports_puppeteer_puppeteer_lib_Errors.TimeoutError; -// let Errors = exports_puppeteer_puppeteer_lib_Errors; -// let Events = exports_puppeteer_puppeteer_lib_Events.Events; -// let ExecutionContext = exports_puppeteer_puppeteer_lib_ExecutionContext.ExecutionContext; -// let FrameManager = exports_puppeteer_puppeteer_lib_FrameManager.FrameManager; -// let Mouse = exports_puppeteer_puppeteer_lib_Input.Mouse; -// let Touchscreen = exports_puppeteer_puppeteer_lib_Input.Touchscreen; -// let JSHandle = exports_puppeteer_puppeteer_lib_JSHandle.JSHandle; -// let createJSHandle = exports_puppeteer_puppeteer_lib_JSHandle.createJSHandle; -// let LifecycleWatcher = exports_puppeteer_puppeteer_lib_LifecycleWatcher.LifecycleWatcher; -// let NetworkManager = exports_puppeteer_puppeteer_lib_NetworkManager.NetworkManager; -// let Page = exports_puppeteer_puppeteer_lib_Page.Page; -// let Target = exports_puppeteer_puppeteer_lib_Target.Target; -// let TimeoutSettings = exports_puppeteer_puppeteer_lib_TimeoutSettings.TimeoutSettings; -// let assert = exports_puppeteer_puppeteer_lib_helper.assert; -// let debugError = exports_puppeteer_puppeteer_lib_helper.debugError; -// let helper = exports_puppeteer_puppeteer_lib_helper.helper; -// let concat = exports_websockets_ws_lib_buffer_util.concat; -local._puppeteer = exports_puppeteer_puppeteer_index; -local.puppeteerApi = exports_puppeteer_puppeteer_lib_api; -/* -file none -*/ -local.puppeteerApi.cdpClientCreate = cdpClientCreate; -/* - - -*/ -/* jslint ignore:end */ -}()); -}()); diff --git a/lib.utility2.js b/lib.utility2.js index bbf78e60d5..3ee2543dbb 100755 --- a/lib.utility2.js +++ b/lib.utility2.js @@ -232,8 +232,7 @@ globalThis.utility2 = local; "apidoc", "istanbul", "jslint", - "marked", - "puppeteer" + "marked" ].forEach(function (key) { try { local[key] = ( @@ -2239,7 +2238,6 @@ local.browserTest = function ({ let chromeUserDataDir; let fileScreenshot; let isDone; - let page; let promiseList; let testId; let testName; @@ -2354,9 +2352,10 @@ local.browserTest = function ({ let stderr; stderr = ""; chromeProcess.stderr.on("data", function onData(chunk) { - local.assertOrThrow(stderr.length < 65536, new Error( + local.assertOrThrow( + stderr.length < 65536, "chrome-devtools-websocket - buffer-overflow" - )); + ); stderr += chunk; stderr.replace(( /^DevTools\u0020listening\u0020on\u0020(ws:\/\/.*)$/m @@ -2369,30 +2368,19 @@ local.browserTest = function ({ }); }).then(function (data) { chromeSocket = data; - return local.puppeteer.puppeteerApi.cdpClientCreate({ - chromeKill, + return local.cdpClientCreate({ + url, websocketUrl: chromeSocket }); }).then(function (data) { cdpClient = data; - page = cdpClient.page; - return page.goto(url); }).then(function () { promiseList = []; - promiseList.push(new Promise(function (resolve) { - setTimeout(function () { - page.screenshot({ - path: fileScreenshot - }).then(function () { - console.error( - "\nbrowserTest - created screenshot file " - + fileScreenshot + "\n" - ); - resolve(); - }); - }, 100); + promiseList.push(cdpClient.screenshot({ + delay: 100, + path: fileScreenshot })); - page.evaluate( + cdpClient.evaluate( // coverage-hack "console.timeStamp();\n" + "window.utility2_testId=\"" + testId + "\";\n" @@ -2406,7 +2394,7 @@ local.browserTest = function ({ return; } isDone = true; - resolve(page.evaluate( + resolve(cdpClient.evaluate( "JSON.stringify(\n" + "window.utility2_testReport\n" + "||{testPlatformList:[{}]}\n" @@ -2432,7 +2420,7 @@ local.browserTest = function ({ ), JSON.stringify(globalThis.utility2_testReport), function (err) { - local.onErrorThrow(err); + onErrorThrow(err); console.error( "\nbrowserTest - merged test-report " + process.env.npm_config_dir_build + "/test-report.json" @@ -3158,6 +3146,478 @@ local.buildApp = function ({ }); }; +local.cdpClientCreate = function ({ + url, + websocketUrl +}) { +/* + * this function with create chrome-devtools-protocol-client from + */ + let WS_READ_HEADER; + let WS_READ_LENGTH16; + let WS_READ_LENGTH63; + let WS_READ_PAYLOAD; + let cdpCallbackDict; + let cdpCallbackId; + let cdpClient; + let cdpSessionId; + let secWebsocketKey; + let websocket; + let wsBufList; + let wsPayloadLength; + let wsReadState; + let wsReader; + /* + * init var + */ + WS_READ_HEADER = 0; + WS_READ_LENGTH16 = 1; + WS_READ_LENGTH63 = 2; + WS_READ_PAYLOAD = 3; + cdpCallbackDict = {}; + cdpCallbackId = 0; + secWebsocketKey = require("crypto").randomBytes(16).toString("base64"); + wsBufList = []; + wsPayloadLength = 0; + wsReadState = WS_READ_HEADER; + /* + * init cdpClient + */ + function CdpClient() { + /* + * this function will construct cdpClient + */ + require("stream").Duplex.call(this); + } + require("util").inherits(CdpClient, require("stream").Duplex); + cdpClient = new CdpClient(); + cdpClient.__proto__._read = function () { + /* + * this function will implement stream.Duplex.prototype._read + */ + if (websocket && websocket.readable) { + websocket.resume(); + } + }; + cdpClient.__proto__._write = function (payload, ignore, callback) { + /* + * this function will implement stream.Duplex.prototype._write + */ + let header; + let maskKey; + let result; + // console.error("SEND ► " + payload.slice(0, 256).toString()); + // init header + header = Buffer.alloc(2 + 8 + 4); + // init fin = true + header[0] |= 0x80; + // init opcode = text-frame + header[0] |= 1; + // init mask = true + header[1] |= 0x80; + // init wsPayloadLength + if (payload.length < 126) { + header = header.slice(0, 2 + 0 + 4); + header[1] |= payload.length; + } else if (payload.length < 65536) { + header = header.slice(0, 2 + 2 + 4); + header[1] |= 126; + header.writeUInt16BE(payload.length, 2); + } else { + header[1] |= 127; + header.writeUInt32BE(payload.length, 6); + } + // init maskKey + maskKey = require("crypto").randomBytes(4); + maskKey.copy(header, header.length - 4); + // send header + websocket.cork(); + websocket.write(header); + // send payload ^ maskKey + payload.forEach(function (ignore, ii) { + payload[ii] ^= maskKey[ii & 3]; + }); + // return write-result + result = websocket.write(payload, callback); + websocket.uncork(); + return result; + }; + cdpClient.evaluate = async function (expression) { + const { + exceptionDetails, + result + } = await cdpClient.rpc("Runtime.evaluate", { + awaitPromise: true, + expression, + returnByValue: false, + userGesture: true + }, cdpSessionId); + local.assertOrThrow(!exceptionDetails, exceptionDetails); + return result.value; + }; + cdpClient.on("data", function (evt) { + /* + * this function will handle callback for + * received from chrome-browser using chrome-devtools-protocol + */ + // console.error("◀ RECV " + evt.slice(0, 256).toString()); + let callback; + // init evt + evt = JSON.parse(evt); + local.assertOrThrow(!evt.method || ( + /^[A-Z]\w*?\.[a-z]\w*?$/ + ).test(evt.method), new Error( + "cdp-rpc-error - invalid evt.method " + evt.method + )); + // init callback + callback = cdpCallbackDict[evt.id]; + delete cdpCallbackDict[evt.id]; + // callback.resolve + if (callback) { + callback.err.message = JSON.stringify(evt.error); + local.assertOrThrow(!evt.error, callback.err); + callback.resolve(evt.result); + return; + } + local.assertOrThrow(!evt.error, evt.error); + cdpClient.emit(evt.method, evt.params); + }); + cdpClient.rpc = function (method, params) { + /* + * this function will message-pass + * JSON.stringify({id, , , }) + * to chrome-browser using chrome-devtools-protocol + */ + cdpCallbackId = (cdpCallbackId % 256) + 1; + cdpClient.write(Buffer.from(JSON.stringify({ + id: cdpCallbackId, + method, + params, + sessionId: cdpSessionId + }))); + return new Promise(function (resolve) { + cdpCallbackDict[cdpCallbackId] = { + err: new Error(), + method, + resolve + }; + }); + }; + cdpClient.screenshot = function ({ + delay, + path + }) { + /* + * this function will screenshot browser to given + */ + local.assertOrThrow(path, "path required"); + return new Promise(function (resolve) { + setTimeout(function () { + cdpClient.rpc("Page.captureScreenshot", { + format: "png" + }).then(function ({ + data + }) { + require("fs").writeFile(( + path + ), Buffer.from(data, "base64"), function (err) { + local.onErrorThrow(err); + console.error( + "[cdpClient] - Page.captureScreenshot -" + + path + ); + resolve(); + }); + }); + }, delay); + }); + }; + /* + * init wsReader + */ +/* +https://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-13#section-5.2 ++---------------------------------------------------------------+ +|0 1 2 3 | +|0 1 2 3 4 5 6 7 8 9 a b c d e f 0 1 2 3 4 5 6 7 8 9 a b c d e f| ++-+-+-+-+-------+-+-------------+-------------------------------+ +|F|R|R|R| opcode|M| Payload len | Extended payload length | +|I|S|S|S| (4) |A| (7) | (16/63) | +|N|V|V|V| |S| | (if payload len==126/127) | +| |1|2|3| |K| | | ++-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + +| Extended payload length continued, if payload len == 127 | ++ - - - - - - - - - - - - - - - +-------------------------------+ +| |Masking-key, if MASK set to 1 | ++-------------------------------+-------------------------------+ +| Masking-key (continued) | Payload Data | ++-------------------------------- - - - - - - - - - - - - - - - + +: Payload Data continued ... : ++ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +| Payload Data continued ... | ++---------------------------------------------------------------+ +FIN: 1 bit + Indicates that this is the final fragment in a message. The first + fragment MAY also be the final fragment. +RSV1, RSV2, RSV3: 1 bit each + MUST be 0 unless an extension is negotiated which defines meanings + for non-zero values. If a nonzero value is received and none of + the negotiated extensions defines the meaning of such a nonzero + value, the receiving endpoint MUST _Fail the WebSocket + Connection_. +Opcode: 4 bits + Defines the interpretation of the payload data. If an unknown + opcode is received, the receiving endpoint MUST _Fail the + WebSocket Connection_. The following values are defined. + * %x0 denotes a continuation frame + * %x1 denotes a text frame + * %x2 denotes a binary frame + * %x3-7 are reserved for further non-control frames + * %x8 denotes a connection close + * %x9 denotes a ping + * %xA denotes a pong + * %xB-F are reserved for further control frames +Mask: 1 bit + Defines whether the payload data is masked. If set to 1, a + masking key is present in masking-key, and this is used to unmask + the payload data as per Section 5.3. All frames sent from client + to server have this bit set to 1. +Payload length: 7 bits, 7+16 bits, or 7+64 bits + The length of the payload data, in bytes: if 0-125, that is the + payload length. If 126, the following 2 bytes interpreted as a 16 + bit unsigned integer are the payload length. If 127, the + following 8 bytes interpreted as a 64-bit unsigned integer (the + most significant bit MUST be 0) are the payload length. Multibyte + length quantities are expressed in network byte order. The + payload length is the length of the extension data + the length of + the application data. The length of the extension data may be + zero, in which case the payload length is the length of the + application data. +Masking-key: 0 or 4 bytes + All frames sent from the client to the server are masked by a 32- + bit value that is contained within the frame. This field is + present if the mask bit is set to 1, and is absent if the mask bit + is set to 0. See Section 5.3 for further information on client- + to-server masking. +Payload data: (x+y) bytes + The payload data is defined as extension data concatenated with + application data. +Extension data: x bytes + The extension data is 0 bytes unless an extension has been + negotiated. Any extension MUST specify the length of the + extension data, or how that length may be calculated, and how the + extension use MUST be negotiated during the opening handshake. If + present, the extension data is included in the total payload + length. +Application data: y bytes + Arbitrary application data, taking up the remainder of the frame + after any extension data. The length of the application data is + equal to the payload length minus the length of the extension + data. +*/ + function wsBufListRead(nn) { + /* + * this function will read bytes from + */ + let buf; + wsBufList = ( + wsBufList.length === 1 + ? wsBufList[0] + : Buffer.concat(wsBufList) + ); + buf = wsBufList.slice(0, nn); + wsBufList = [ + wsBufList.slice(nn) + ]; + return buf; + } + function wsFrameRead() { + /* + * this function will read from websocket-data-frame + */ + let buf; + let opcode; + if (wsBufList.reduce(function (aa, bb) { + return aa + bb.length; + }, 0) < ( + wsReadState === WS_READ_PAYLOAD + ? Math.max(wsPayloadLength, 1) + : wsReadState === WS_READ_LENGTH63 + ? 8 + : 2 + )) { + return; + } + switch (wsReadState) { + // read frame-header + case WS_READ_HEADER: + buf = wsBufListRead(2); + // validate opcode + opcode = buf[0] & 0x0f; + local.assertOrThrow(opcode === 0x01, ( + "Invalid WebSocket frame: opcode must be 0x01, not 0x0" + + opcode.toString(16) + )); + wsPayloadLength = buf[1] & 0x7f; + wsReadState = ( + wsPayloadLength === 126 + ? WS_READ_LENGTH16 + : wsPayloadLength === 127 + ? WS_READ_LENGTH63 + : WS_READ_PAYLOAD + ); + break; + // read frame-payload-length-16 + case WS_READ_LENGTH16: + wsPayloadLength = wsBufListRead(2).readUInt16BE(0); + wsReadState = WS_READ_PAYLOAD; + break; + // read frame-payload-length-63 + case WS_READ_LENGTH63: + buf = wsBufListRead(8); + wsPayloadLength = ( + buf.readUInt32BE(0) * 0x100000000 + buf.readUInt32BE(4) + ); + wsReadState = WS_READ_PAYLOAD; + break; + // read frame-payload-data + case WS_READ_PAYLOAD: + local.assertOrThrow( + wsPayloadLength > 0, + "wsPayloadLength must be greater than 0, not " + wsPayloadLength + ); + buf = wsBufListRead(wsPayloadLength); + wsReadState = WS_READ_HEADER; + cdpClient.push(buf); + break; + } + local.assertOrThrow( + 0 <= wsPayloadLength && wsPayloadLength <= 256 * 1024 * 1024, + "payload-length must be between 0 and 256 MiB, not " + + wsPayloadLength + ); + return true; + } + function WsReader() { + /* + * this function will construct wsReader + */ + require("stream").Transform.call(this); + } + require("util").inherits(WsReader, require("stream").Transform); + wsReader = new WsReader(); + wsReader.__proto__._transform = function (chunk, ignore, callback) { + /* + * this function will implement Transform.prototype._transform + */ + wsBufList.push(chunk); + while (true) { + if (!wsFrameRead()) { + break; + } + } + callback(); + }; + /* + * init websocket + */ + return Promise.resolve().then(function () { + return new Promise(function (resolve) { + require("http").get(Object.assign(require("url").parse( + websocketUrl + ), { + "createConnection": function (opt) { + opt.path = opt.socketPath; + return require("net").connect(opt); + }, + "headers": { + "Connection": "Upgrade", + "Sec-WebSocket-Key": secWebsocketKey, + "Sec-WebSocket-Version": 13, + "Upgrade": "websocket" + }, + "protocol": "http:", + "protocolVersion": 13 + })).once("upgrade", function (res, _websocket, head) { + local.assertOrThrow( + ( + res.headers["sec-websocket-accept"] + === require("crypto").createHash("sha1").update( + secWebsocketKey + + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" + ).digest("base64") + ), + "Invalid Sec-WebSocket-Accept header" + ); + websocket = _websocket; + websocket.unshift(head); + // websocket - disable timeout + websocket.setTimeout(0); + // websocket - disable nagle's algorithm + websocket.setNoDelay(); + websocket.on("end", websocket.end.bind(websocket)); + // pipe websocket to wsReader + websocket.pipe(wsReader); + resolve(); + }); + }); + /* + * navigate to url + */ + }).then(async function () { + let frameId; + let targetId; + // init cdpSessionId + targetId = (await cdpClient.rpc("Target.createTarget", { + url: "about:blank" + })).targetId; + cdpSessionId = (await cdpClient.rpc("Target.attachToTarget", { + targetId, + flatten: true + })).sessionId; + // init screensize + cdpClient.rpc("Emulation.setDeviceMetricsOverride", { + deviceScaleFactor: 1, + height: 600, + mobile: false, + screenOrientation: { + angle: 0, + type: "portraitPrimary" + }, + width: 800 + }); + // init page + cdpClient.rpc("Page.enable", undefined); + cdpClient.rpc("Page.setLifecycleEventsEnabled", { + enabled: true + }); + cdpClient.rpc("Performance.enable", undefined); + // navigate page to url + cdpClient.rpc("Page.navigate", { + url + }); + // wait for page to load + cdpClient.rpc("Page.getFrameTree").then(function ({ + frameTree + }) { + frameId = frameTree.frame.id; + }); + await new Promise(function (resolve) { + cdpClient.on("Page.lifecycleEvent", function onLoad(evt) { + if (evt.frameId === frameId && evt.name === "load") { + cdpClient.removeListener("Page.lifecycleEvent", onLoad); + resolve(); + } + }); + }); + /* + * resolve cdpClient + */ + }).then(function () { + return cdpClient; + }); +}; + local.cliRun = function (opt) { /* * this function will run cli with given @@ -3221,30 +3681,27 @@ local.cliRun = function (opt) { commandList[ii].command.push(key); return; } - try { - commandList[ii] = opt.rgxComment.exec(str); - commandList[ii] = { - argList: local.coalesce(commandList[ii][1], "").trim(), - command: [ - key - ], - description: commandList[ii][2] - }; - } catch (ignore) { - local.assertOrThrow(undefined, new Error( - "cliRun - cannot parse comment in COMMAND " - + key - + ":\nnew RegExp(" - + JSON.stringify(opt.rgxComment.source) - + ").exec(" + JSON.stringify(str).replace(( - /\\\\/g - ), "\u0000").replace(( - /\\n/g - ), "\\n\\\n").replace(( - /\u0000/g - ), "\\\\") + ");" - )); - } + commandList[ii] = opt.rgxComment.exec(str); + local.assertOrThrow(commandList[ii], ( + "cliRun - cannot parse comment in COMMAND " + + key + + ":\nnew RegExp(" + + JSON.stringify(opt.rgxComment.source) + + ").exec(" + JSON.stringify(str).replace(( + /\\\\/g + ), "\u0000").replace(( + /\\n/g + ), "\\n\\\n").replace(( + /\u0000/g + ), "\\\\") + ");" + )); + commandList[ii] = { + argList: local.coalesce(commandList[ii][1], "").trim(), + command: [ + key + ], + description: commandList[ii][2] + }; }); str = ""; str += packageJson.name + " (" + packageJson.version + ")\n\n"; @@ -3588,7 +4045,6 @@ local.jslintAutofixLocalFunction = function (code, file) { case "lib.istanbul.js": case "lib.jslint.js": case "lib.marked.js": - case "lib.puppeteer.js": case "npm_scripts.sh": case "test.js": break; @@ -4080,27 +4536,6 @@ local.middlewareUtility2StateInit = function (req, res, next) { })); }; -local.onErrorWithStack = function (onError) { -/* - * this function will wrap with wrapper preserving current-stack - */ - let onError2; - let stack; - stack = new Error().stack; - onError2 = function (err, data, meta) { - // append current-stack to err.stack - if (err && typeof err.stack === "string") { - err.stack += "\n" + stack; - } - onError(err, data, meta); - }; - // debug onError - onError2.toString = function () { - return String(onError); - }; - return onError2; -}; - local.onParallel = function (onError, onEach, onRetry) { /* * this function will create function that will @@ -4108,7 +4543,6 @@ local.onParallel = function (onError, onEach, onRetry) { * 2. if cnt === 0 or err occurred, then call onError(err) */ let onParallel; - onError = local.onErrorWithStack(onError); onEach = onEach || local.noop; onRetry = onRetry || local.noop; onParallel = function (err, data) { @@ -5890,18 +6324,12 @@ local.testRunServer = function (opt) { /* * this function will emulate express-like middleware-chaining */ - let errStack; let gotoState; let isDone; - errStack = new Error().stack; gotoState = -1; (function gotoNext(err) { try { gotoState += 1; - // preserve stack-trace - if (err) { - err.stack += "\n" + errStack; - } if (err || gotoState >= local.middlewareList.length) { local.middlewareError(err, req, res); return; @@ -6403,7 +6831,6 @@ if (globalThis.utility2_rollup) { "lib.istanbul.js", "lib.jslint.js", "lib.marked.js", - "lib.puppeteer.js", "lib.utility2.js", "test.js" ].forEach(function (key) { @@ -6503,7 +6930,6 @@ local.assetsDict["/assets.utility2.rollup.js"] = [ "lib.istanbul.js", "lib.jslint.js", "lib.marked.js", - "lib.puppeteer.js", "lib.utility2.js", "/assets.utility2.example.js", "/assets.utility2.html", diff --git a/package.json b/package.json index 7ca37226fe..4116687a14 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "engines": { "node": ">=12.0" }, - "fileCount": 30, + "fileCount": 28, "homepage": "https://github.com/kaizhu256/node-utility2", "keywords": [ "continuous-integration", diff --git a/raw.puppeteer.js b/raw.puppeteer.js deleted file mode 100644 index acc5e349dd..0000000000 --- a/raw.puppeteer.js +++ /dev/null @@ -1,12676 +0,0 @@ -/* -shRawLibFetch -{ - "fetchList": [ - { - "url": "https://github.com/websockets/ws/blob/6.2.1/package.json" - }, - { - "url": "https://github.com/websockets/ws/blob/6.2.1/lib/constants.js" - }, - { - "url": "https://github.com/websockets/ws/blob/6.2.1/lib/validation.js" - }, - { - "url": "https://github.com/websockets/ws/blob/6.2.1/lib/buffer-util.js" - }, - { - "url": "https://github.com/websockets/ws/blob/6.2.1/lib/event-target.js" - }, - { - "url": "https://github.com/websockets/ws/blob/6.2.1/lib/extension.js" - }, - { - "urlIgnore": "https://github.com/websockets/ws/blob/6.2.1/lib/permessage-deflate.js" - }, - { - "url": "https://github.com/websockets/ws/blob/6.2.1/lib/receiver.js" - }, - { - "url": "https://github.com/websockets/ws/blob/6.2.1/lib/sender.js" - }, - { - "url": "https://github.com/websockets/ws/blob/6.2.1/lib/websocket-server.js" - }, - { - "url": "https://github.com/websockets/ws/blob/6.2.1/lib/websocket.js" - }, - { - "url": "https://github.com/websockets/ws/blob/6.2.1/index.js" - }, - { - "url": "https://github.com/puppeteer/puppeteer/blob/v1.19.0/package.json" - }, - { - "url": "https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/helper.js" - }, - { - "url": "https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/Accessibility.js" - }, - { - "url": "https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/Browser.js" - }, - { - "urlIgnore": "https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/BrowserFetcher.js" - }, - { - "url": "https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/Connection.js" - }, - { - "url": "https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/Coverage.js" - }, - { - "url": "https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/DOMWorld.js" - }, - { - "url": "https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/DeviceDescriptors.js" - }, - { - "url": "https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/Dialog.js" - }, - { - "url": "https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/EmulationManager.js" - }, - { - "url": "https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/Errors.js" - }, - { - "url": "https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/Events.js" - }, - { - "url": "https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/ExecutionContext.js" - }, - { - "url": "https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/FrameManager.js" - }, - { - "url": "https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/Input.js" - }, - { - "url": "https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/JSHandle.js" - }, - { - "url": "https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/Launcher.js" - }, - { - "url": "https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/LifecycleWatcher.js" - }, - { - "url": "https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/Multimap.js" - }, - { - "url": "https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/NetworkManager.js" - }, - { - "url": "https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/Page.js" - }, - { - "url": "https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/PipeTransport.js" - }, - { - "url": "https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/Puppeteer.js" - }, - { - "url": "https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/Target.js" - }, - { - "url": "https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/TaskQueue.js" - }, - { - "url": "https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/TimeoutSettings.js" - }, - { - "url": "https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/Tracing.js" - }, - { - "url": "https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/USKeyboardLayout.js" - }, - { - "url": "https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/WebSocketTransport.js" - }, - { - "url": "https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/Worker.js" - }, - { - "url": "https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/api.js" - }, - { - "urlIgnore": "https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/externs.d.ts" - }, - { - "url": "https://github.com/puppeteer/puppeteer/blob/v1.19.0/index.js" - } - ], - "isRollupCommonJs": true -} -- let functionText = pageFunction.toString(); -+ let functionText = pageFunction.toString(); -+ // hack-coverage - un-instrument -+ functionText = functionText.replace((/\b__cov_.*?\+\+/g), "0"); - --// const api = exports_puppeteer_puppeteer_lib_api; -+ const api = exports_puppeteer_puppeteer_lib_api; - --// const Errors = exports_puppeteer_puppeteer_lib_Errors; --// const DeviceDescriptors = exports_puppeteer_puppeteer_lib_DeviceDescriptors; -+const Errors = exports_puppeteer_puppeteer_lib_Errors; -+const DeviceDescriptors = exports_puppeteer_puppeteer_lib_DeviceDescriptors; - --// const packageJson = exports_puppeteer_puppeteer_package_json; -+const packageJson = exports_puppeteer_puppeteer_package_json; - --exports_puppeteer_puppeteer_index = new Puppeteer(__dirname, preferredRevision, isPuppeteerCore); --// let Accessibility = exports_puppeteer_puppeteer_lib_Accessibility.Accessibility; --// let Browser = exports_puppeteer_puppeteer_lib_Browser.Browser; --let BrowserFetcher = exports_puppeteer_puppeteer_lib_BrowserFetcher; --// let Connection = exports_puppeteer_puppeteer_lib_Connection.Connection; --// let Coverage = exports_puppeteer_puppeteer_lib_Coverage.Coverage; --// let DOMWorld = exports_puppeteer_puppeteer_lib_DOMWorld.DOMWorld; --let DeviceDescriptors = exports_puppeteer_puppeteer_lib_DeviceDescriptors; --// let Dialog = exports_puppeteer_puppeteer_lib_Dialog.Dialog; --// let EmulationManager = exports_puppeteer_puppeteer_lib_EmulationManager.EmulationManager; --// let TimeoutError = exports_puppeteer_puppeteer_lib_Errors.TimeoutError; --let Errors = exports_puppeteer_puppeteer_lib_Errors; --// let Events = exports_puppeteer_puppeteer_lib_Events.Events; --// let EVALUATION_SCRIPT_URL = exports_puppeteer_puppeteer_lib_ExecutionContext.EVALUATION_SCRIPT_URL; --// let ExecutionContext = exports_puppeteer_puppeteer_lib_ExecutionContext.ExecutionContext; --// let FrameManager = exports_puppeteer_puppeteer_lib_FrameManager.FrameManager; --// let Keyboard = exports_puppeteer_puppeteer_lib_Input.Keyboard; --// let Mouse = exports_puppeteer_puppeteer_lib_Input.Mouse; --// let Touchscreen = exports_puppeteer_puppeteer_lib_Input.Touchscreen; --// let JSHandle = exports_puppeteer_puppeteer_lib_JSHandle.JSHandle; --// let createJSHandle = exports_puppeteer_puppeteer_lib_JSHandle.createJSHandle; --// let Launcher = exports_puppeteer_puppeteer_lib_Launcher; --// let LifecycleWatcher = exports_puppeteer_puppeteer_lib_LifecycleWatcher.LifecycleWatcher; --// let NetworkManager = exports_puppeteer_puppeteer_lib_NetworkManager.NetworkManager; --// let Page = exports_puppeteer_puppeteer_lib_Page.Page; --// let PipeTransport = exports_puppeteer_puppeteer_lib_PipeTransport; --// let Target = exports_puppeteer_puppeteer_lib_Target.Target; --// let TaskQueue = exports_puppeteer_puppeteer_lib_TaskQueue.TaskQueue; --// let TimeoutSettings = exports_puppeteer_puppeteer_lib_TimeoutSettings.TimeoutSettings; --// let Tracing = exports_puppeteer_puppeteer_lib_Tracing; --let keyDefinitions = exports_puppeteer_puppeteer_lib_USKeyboardLayout; --// let WebSocketTransport = exports_puppeteer_puppeteer_lib_WebSocketTransport; --// let Worker = exports_puppeteer_puppeteer_lib_Worker.Worker; --let api = exports_puppeteer_puppeteer_lib_api; --// let assert = exports_puppeteer_puppeteer_lib_helper.assert; --let debugError = exports_puppeteer_puppeteer_lib_helper.debugError; --let helper = exports_puppeteer_puppeteer_lib_helper.helper; --let packageJson = exports_puppeteer_puppeteer_package_json; --let applyMask = exports_websockets_ws_lib_buffer_util.applyMask; --// let concat = exports_websockets_ws_lib_buffer_util.concat; --// let mask = exports_websockets_ws_lib_buffer_util.mask; --// let toArrayBuffer = exports_websockets_ws_lib_buffer_util.toArrayBuffer; --// let toBuffer = exports_websockets_ws_lib_buffer_util.toBuffer; --let unmask = exports_websockets_ws_lib_buffer_util.unmask; --let BINARY_TYPES = exports_websockets_ws_lib_constants.BINARY_TYPES; --let EMPTY_BUFFER = exports_websockets_ws_lib_constants.EMPTY_BUFFER; --let GUID = exports_websockets_ws_lib_constants.GUID; --let NOOP = exports_websockets_ws_lib_constants.NOOP; --let kStatusCode = exports_websockets_ws_lib_constants.kStatusCode; --let kWebSocket = exports_websockets_ws_lib_constants.kWebSocket; --// let EventTarget = exports_websockets_ws_lib_event_target; --let extension = exports_websockets_ws_lib_extension; --let PerMessageDeflate = exports_websockets_ws_lib_permessage_deflate; --// let Receiver = exports_websockets_ws_lib_receiver; --// let Sender = exports_websockets_ws_lib_sender; --let isValidStatusCode = exports_websockets_ws_lib_validation.isValidStatusCode; --let isValidUTF8 = exports_websockets_ws_lib_validation.isValidUTF8; --// let WebSocket = exports_websockets_ws_lib_websocket; --}()); -+exports_puppeteer_puppeteer_index = new Puppeteer(__dirname, preferredRevision, isPuppeteerCore); -+// let Accessibility = exports_puppeteer_puppeteer_lib_Accessibility.Accessibility; -+// let Browser = exports_puppeteer_puppeteer_lib_Browser.Browser; -+let BrowserFetcher = exports_puppeteer_puppeteer_lib_BrowserFetcher; -+// let Connection = exports_puppeteer_puppeteer_lib_Connection.Connection; -+// let Coverage = exports_puppeteer_puppeteer_lib_Coverage.Coverage; -+// let DOMWorld = exports_puppeteer_puppeteer_lib_DOMWorld.DOMWorld; -+// let DeviceDescriptors = exports_puppeteer_puppeteer_lib_DeviceDescriptors; -+// let Dialog = exports_puppeteer_puppeteer_lib_Dialog.Dialog; -+// let EmulationManager = exports_puppeteer_puppeteer_lib_EmulationManager.EmulationManager; -+// let TimeoutError = exports_puppeteer_puppeteer_lib_Errors.TimeoutError; -+// let Errors = exports_puppeteer_puppeteer_lib_Errors; -+// let Events = exports_puppeteer_puppeteer_lib_Events.Events; -+// let EVALUATION_SCRIPT_URL = exports_puppeteer_puppeteer_lib_ExecutionContext.EVALUATION_SCRIPT_URL; -+// let ExecutionContext = exports_puppeteer_puppeteer_lib_ExecutionContext.ExecutionContext; -+// let FrameManager = exports_puppeteer_puppeteer_lib_FrameManager.FrameManager; -+// let Keyboard = exports_puppeteer_puppeteer_lib_Input.Keyboard; -+// let Mouse = exports_puppeteer_puppeteer_lib_Input.Mouse; -+// let Touchscreen = exports_puppeteer_puppeteer_lib_Input.Touchscreen; -+// let JSHandle = exports_puppeteer_puppeteer_lib_JSHandle.JSHandle; -+// let createJSHandle = exports_puppeteer_puppeteer_lib_JSHandle.createJSHandle; -+// let Launcher = exports_puppeteer_puppeteer_lib_Launcher; -+// let LifecycleWatcher = exports_puppeteer_puppeteer_lib_LifecycleWatcher.LifecycleWatcher; -+// let NetworkManager = exports_puppeteer_puppeteer_lib_NetworkManager.NetworkManager; -+// let Page = exports_puppeteer_puppeteer_lib_Page.Page; -+// let PipeTransport = exports_puppeteer_puppeteer_lib_PipeTransport; -+// let Target = exports_puppeteer_puppeteer_lib_Target.Target; -+// let TaskQueue = exports_puppeteer_puppeteer_lib_TaskQueue.TaskQueue; -+// let TimeoutSettings = exports_puppeteer_puppeteer_lib_TimeoutSettings.TimeoutSettings; -+// let Tracing = exports_puppeteer_puppeteer_lib_Tracing; -+let keyDefinitions = exports_puppeteer_puppeteer_lib_USKeyboardLayout; -+// let WebSocketTransport = exports_puppeteer_puppeteer_lib_WebSocketTransport; -+// let Worker = exports_puppeteer_puppeteer_lib_Worker.Worker; -+let api = exports_puppeteer_puppeteer_lib_api; -+// let assert = exports_puppeteer_puppeteer_lib_helper.assert; -+// let debugError = exports_puppeteer_puppeteer_lib_helper.debugError; -+// let helper = exports_puppeteer_puppeteer_lib_helper.helper; -+// let packageJson = exports_puppeteer_puppeteer_package_json; -+let applyMask = exports_websockets_ws_lib_buffer_util.mask; -+// let concat = exports_websockets_ws_lib_buffer_util.concat; -+// let mask = exports_websockets_ws_lib_buffer_util.mask; -+// let toArrayBuffer = exports_websockets_ws_lib_buffer_util.toArrayBuffer; -+// let toBuffer = exports_websockets_ws_lib_buffer_util.toBuffer; -+let unmask = exports_websockets_ws_lib_buffer_util.unmask; -+let BINARY_TYPES = exports_websockets_ws_lib_constants.BINARY_TYPES; -+let EMPTY_BUFFER = exports_websockets_ws_lib_constants.EMPTY_BUFFER; -+let GUID = exports_websockets_ws_lib_constants.GUID; -+let NOOP = exports_websockets_ws_lib_constants.NOOP; -+let kStatusCode = exports_websockets_ws_lib_constants.kStatusCode; -+let kWebSocket = exports_websockets_ws_lib_constants.kWebSocket; -+// let EventTarget = exports_websockets_ws_lib_event_target; -+let extension = exports_websockets_ws_lib_extension; -+let PerMessageDeflate = exports_websockets_ws_lib_permessage_deflate; -+// let Receiver = exports_websockets_ws_lib_receiver; -+// let Sender = exports_websockets_ws_lib_sender; -+let isValidStatusCode = exports_websockets_ws_lib_validation.isValidStatusCode; -+let isValidUTF8 = exports_websockets_ws_lib_validation.isValidUTF8; -+// let WebSocket = exports_websockets_ws_lib_websocket; -+local._puppeteer = exports_puppeteer_puppeteer_index; -+local.puppeteerApi = exports_puppeteer_puppeteer_lib_api; -+local.puppeteerLaunch = local._puppeteer.launch.bind(local._puppeteer); -+local.noop(local.puppeteerLaunch); - --exports_puppeteer_puppeteer_lib_helper = { -- helper: Helper, -- assert, -- debugError --}; -+exports_puppeteer_puppeteer_lib_helper = { -+ helper: Helper, -+ assert, -+ debugError -+}; -+// hack-puppeteer - init helper -+let helper = exports_puppeteer_puppeteer_lib_helper.helper; - --let EventEmitter = require('events'); --let URL = require('url'); --// let WebSocket = require('ws'); --let Writable = require('stream').Writable; --let bufferUtil = require('bufferutil'); --let childProcess = require('child_process'); --let crypto = require('crypto'); --// let debugError = require('debug')(`puppeteer:error`); --let debugProtocol = require('debug')('puppeteer:protocol'); --let fs = require('fs'); --let http = require('http'); --let https = require('https'); --// let isValidUTF8 = require('utf-8-validate'); --let mime = require('mime'); --let net = require('net'); --let os = require('os'); --// let path = require('path'); --let randomBytes = require('crypto').randomBytes; --let readline = require('readline'); --let removeFolder = require('rimraf'); --let tls = require('tls'); --// let url = require('url'); -+let EventEmitter = require('events'); -+let URL = require('url'); -+// let WebSocket = require('ws'); -+let Writable = require('stream').Writable; -+// let bufferUtil = require('bufferutil'); -+let childProcess = require('child_process'); -+let crypto = require('crypto'); -+// let debugError = require('debug')(`puppeteer:error`); -+// let debugProtocol = require('debug')('puppeteer:protocol'); -+let fs = require('fs'); -+let http = require('http'); -+let https = require('https'); -+// let isValidUTF8 = require('utf-8-validate'); -+// let mime = require('mime'); -+let net = require('net'); -+let os = require('os'); -+let path = require('path'); -+let randomBytes = require('crypto').randomBytes; -+let readline = require('readline'); -+// let removeFolder = require('rimraf'); -+let tls = require('tls'); -+let url = require('url'); -*/ - - -(function () { -"use strict"; -let EventEmitter = require('events'); -let URL = require('url'); -// let WebSocket = require('ws'); -let Writable = require('stream').Writable; -// let bufferUtil = require('bufferutil'); -let childProcess = require('child_process'); -let crypto = require('crypto'); -// let debugError = require('debug')(`puppeteer:error`); -// let debugProtocol = require('debug')('puppeteer:protocol'); -let fs = require('fs'); -let http = require('http'); -let https = require('https'); -// let isValidUTF8 = require('utf-8-validate'); -// let mime = require('mime'); -let net = require('net'); -let os = require('os'); -let path = require('path'); -let randomBytes = require('crypto').randomBytes; -let readline = require('readline'); -// let removeFolder = require('rimraf'); -let tls = require('tls'); -let url = require('url'); -let exports_puppeteer_puppeteer_index = {}; -let exports_puppeteer_puppeteer_lib_Accessibility = {}; -let exports_puppeteer_puppeteer_lib_Browser = {}; -let exports_puppeteer_puppeteer_lib_BrowserFetcher = {}; -let exports_puppeteer_puppeteer_lib_Connection = {}; -let exports_puppeteer_puppeteer_lib_Coverage = {}; -let exports_puppeteer_puppeteer_lib_DOMWorld = {}; -let exports_puppeteer_puppeteer_lib_DeviceDescriptors = {}; -let exports_puppeteer_puppeteer_lib_Dialog = {}; -let exports_puppeteer_puppeteer_lib_EmulationManager = {}; -let exports_puppeteer_puppeteer_lib_Errors = {}; -let exports_puppeteer_puppeteer_lib_Events = {}; -let exports_puppeteer_puppeteer_lib_ExecutionContext = {}; -let exports_puppeteer_puppeteer_lib_FrameManager = {}; -let exports_puppeteer_puppeteer_lib_Input = {}; -let exports_puppeteer_puppeteer_lib_JSHandle = {}; -let exports_puppeteer_puppeteer_lib_Launcher = {}; -let exports_puppeteer_puppeteer_lib_LifecycleWatcher = {}; -let exports_puppeteer_puppeteer_lib_Multimap = {}; -let exports_puppeteer_puppeteer_lib_NetworkManager = {}; -let exports_puppeteer_puppeteer_lib_Page = {}; -let exports_puppeteer_puppeteer_lib_PipeTransport = {}; -let exports_puppeteer_puppeteer_lib_Puppeteer = {}; -let exports_puppeteer_puppeteer_lib_Target = {}; -let exports_puppeteer_puppeteer_lib_TaskQueue = {}; -let exports_puppeteer_puppeteer_lib_TimeoutSettings = {}; -let exports_puppeteer_puppeteer_lib_Tracing = {}; -let exports_puppeteer_puppeteer_lib_USKeyboardLayout = {}; -let exports_puppeteer_puppeteer_lib_WebSocketTransport = {}; -let exports_puppeteer_puppeteer_lib_Worker = {}; -let exports_puppeteer_puppeteer_lib_api = {}; -let exports_puppeteer_puppeteer_lib_helper = {}; -let exports_puppeteer_puppeteer_node6_lib_Puppeteer = {}; -let exports_puppeteer_puppeteer_package_json = {}; -let exports_websockets_ws_index = {}; -let exports_websockets_ws_lib_buffer_util = {}; -let exports_websockets_ws_lib_constants = {}; -let exports_websockets_ws_lib_event_target = {}; -let exports_websockets_ws_lib_extension = {}; -let exports_websockets_ws_lib_permessage_deflate = {}; -let exports_websockets_ws_lib_receiver = {}; -let exports_websockets_ws_lib_sender = {}; -let exports_websockets_ws_lib_validation = {}; -let exports_websockets_ws_lib_websocket = {}; -let exports_websockets_ws_lib_websocket_server = {}; -let exports_websockets_ws_package_json = {}; -/* -repo https://github.com/websockets/ws/tree/6.2.1 -committed 2019-03-27T08:34:10Z -*/ - - -/* -file https://github.com/websockets/ws/blob/6.2.1/package.json -*/ -exports_websockets_ws_package_json = { - "name": "ws", - "version": "6.2.1", - "description": "Simple to use, blazing fast and thoroughly tested websocket client and server for Node.js", - "keywords": [ - "HyBi", - "Push", - "RFC-6455", - "WebSocket", - "WebSockets", - "real-time" - ], - "homepage": "https://github.com/websockets/ws", - "bugs": "https://github.com/websockets/ws/issues", - "repository": "websockets/ws", - "author": "Einar Otto Stangvik (http://2x.io)", - "license": "MIT", - "main": "index.js", - "browser": "browser.js", - "files": [ - "browser.js", - "index.js", - "lib/*.js" - ], - "scripts": { - "test": "npm run lint && nyc --reporter=html --reporter=text mocha test/*.test.js", - "integration": "npm run lint && mocha test/*.integration.js", - "lint": "eslint --ignore-path .gitignore . && prettier --check --ignore-path .gitignore \"**/*.{json,md,yml}\"" - }, - "dependencies": { - "async-limiter": "~1.0.0" - }, - "devDependencies": { - "benchmark": "~2.1.4", - "bufferutil": "~4.0.0", - "coveralls": "~3.0.3", - "eslint": "~5.15.0", - "eslint-config-prettier": "~4.1.0", - "eslint-plugin-prettier": "~3.0.0", - "mocha": "~6.0.0", - "nyc": "~13.3.0", - "prettier": "~1.16.1", - "utf-8-validate": "~5.0.0" - } -} - - -/* -file https://github.com/websockets/ws/blob/6.2.1/lib/constants.js -*/ -'use strict'; - -exports_websockets_ws_lib_constants = { - BINARY_TYPES: ['nodebuffer', 'arraybuffer', 'fragments'], - GUID: '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', - kStatusCode: Symbol('status-code'), - kWebSocket: Symbol('websocket'), - EMPTY_BUFFER: Buffer.alloc(0), - NOOP: () => {} -}; - - -/* -file https://github.com/websockets/ws/blob/6.2.1/lib/validation.js -*/ -'use strict'; - -try { -// const isValidUTF8 = require('utf-8-validate'); - - exports_websockets_ws_lib_validation.isValidUTF8 = - typeof isValidUTF8 === 'object' - ? isValidUTF8.Validation.isValidUTF8 // utf-8-validate@<3.0.0 - : isValidUTF8; -} catch (e) /* istanbul ignore next */ { - exports_websockets_ws_lib_validation.isValidUTF8 = () => true; -} - -/** - * Checks if a status code is allowed in a close frame. - * - * @param {Number} code The status code - * @return {Boolean} `true` if the status code is valid, else `false` - * @public - */ -exports_websockets_ws_lib_validation.isValidStatusCode = (code) => { - return ( - (code >= 1000 && - code <= 1013 && - code !== 1004 && - code !== 1005 && - code !== 1006) || - (code >= 3000 && code <= 4999) - ); -}; - - -/* -file https://github.com/websockets/ws/blob/6.2.1/lib/buffer-util.js -*/ -'use strict'; - -// const { EMPTY_BUFFER } = exports_websockets_ws_lib_constants; - -/** - * Merges an array of buffers into a new buffer. - * - * @param {Buffer[]} list The array of buffers to concat - * @param {Number} totalLength The total length of buffers in the list - * @return {Buffer} The resulting buffer - * @public - */ -function concat(list, totalLength) { - if (list.length === 0) return EMPTY_BUFFER; - if (list.length === 1) return list[0]; - - const target = Buffer.allocUnsafe(totalLength); - var offset = 0; - - for (var i = 0; i < list.length; i++) { - const buf = list[i]; - buf.copy(target, offset); - offset += buf.length; - } - - return target; -} - -/** - * Masks a buffer using the given mask. - * - * @param {Buffer} source The buffer to mask - * @param {Buffer} mask The mask to use - * @param {Buffer} output The buffer where to store the result - * @param {Number} offset The offset at which to start writing - * @param {Number} length The number of bytes to mask. - * @public - */ -function _mask(source, mask, output, offset, length) { - for (var i = 0; i < length; i++) { - output[offset + i] = source[i] ^ mask[i & 3]; - } -} - -/** - * Unmasks a buffer using the given mask. - * - * @param {Buffer} buffer The buffer to unmask - * @param {Buffer} mask The mask to use - * @public - */ -function _unmask(buffer, mask) { - // Required until https://github.com/nodejs/node/issues/9006 is resolved. - const length = buffer.length; - for (var i = 0; i < length; i++) { - buffer[i] ^= mask[i & 3]; - } -} - -/** - * Converts a buffer to an `ArrayBuffer`. - * - * @param {Buffer} buf The buffer to convert - * @return {ArrayBuffer} Converted buffer - * @public - */ -function toArrayBuffer(buf) { - if (buf.byteLength === buf.buffer.byteLength) { - return buf.buffer; - } - - return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength); -} - -/** - * Converts `data` to a `Buffer`. - * - * @param {*} data The data to convert - * @return {Buffer} The buffer - * @throws {TypeError} - * @public - */ -function toBuffer(data) { - toBuffer.readOnly = true; - - if (Buffer.isBuffer(data)) return data; - - var buf; - - if (data instanceof ArrayBuffer) { - buf = Buffer.from(data); - } else if (ArrayBuffer.isView(data)) { - buf = viewToBuffer(data); - } else { - buf = Buffer.from(data); - toBuffer.readOnly = false; - } - - return buf; -} - -/** - * Converts an `ArrayBuffer` view into a buffer. - * - * @param {(DataView|TypedArray)} view The view to convert - * @return {Buffer} Converted view - * @private - */ -function viewToBuffer(view) { - const buf = Buffer.from(view.buffer); - - if (view.byteLength !== view.buffer.byteLength) { - return buf.slice(view.byteOffset, view.byteOffset + view.byteLength); - } - - return buf; -} - -try { -// const bufferUtil = require('bufferutil'); - const bu = bufferUtil.BufferUtil || bufferUtil; - - exports_websockets_ws_lib_buffer_util = { - concat, - mask(source, mask, output, offset, length) { - if (length < 48) _mask(source, mask, output, offset, length); - else bu.mask(source, mask, output, offset, length); - }, - toArrayBuffer, - toBuffer, - unmask(buffer, mask) { - if (buffer.length < 32) _unmask(buffer, mask); - else bu.unmask(buffer, mask); - } - }; -} catch (e) /* istanbul ignore next */ { - exports_websockets_ws_lib_buffer_util = { - concat, - mask: _mask, - toArrayBuffer, - toBuffer, - unmask: _unmask - }; -} - - -/* -file https://github.com/websockets/ws/blob/6.2.1/lib/event-target.js -*/ -'use strict'; - -/** - * Class representing an event. - * - * @private - */ -class Event { - /** - * Create a new `Event`. - * - * @param {String} type The name of the event - * @param {Object} target A reference to the target to which the event was dispatched - */ - constructor(type, target) { - this.target = target; - this.type = type; - } -} - -/** - * Class representing a message event. - * - * @extends Event - * @private - */ -class MessageEvent extends Event { - /** - * Create a new `MessageEvent`. - * - * @param {(String|Buffer|ArrayBuffer|Buffer[])} data The received data - * @param {WebSocket} target A reference to the target to which the event was dispatched - */ - constructor(data, target) { - super('message', target); - - this.data = data; - } -} - -/** - * Class representing a close event. - * - * @extends Event - * @private - */ -class CloseEvent extends Event { - /** - * Create a new `CloseEvent`. - * - * @param {Number} code The status code explaining why the connection is being closed - * @param {String} reason A human-readable string explaining why the connection is closing - * @param {WebSocket} target A reference to the target to which the event was dispatched - */ - constructor(code, reason, target) { - super('close', target); - - this.wasClean = target._closeFrameReceived && target._closeFrameSent; - this.reason = reason; - this.code = code; - } -} - -/** - * Class representing an open event. - * - * @extends Event - * @private - */ -class OpenEvent extends Event { - /** - * Create a new `OpenEvent`. - * - * @param {WebSocket} target A reference to the target to which the event was dispatched - */ - constructor(target) { - super('open', target); - } -} - -/** - * Class representing an error event. - * - * @extends Event - * @private - */ -class ErrorEvent extends Event { - /** - * Create a new `ErrorEvent`. - * - * @param {Object} error The error that generated this event - * @param {WebSocket} target A reference to the target to which the event was dispatched - */ - constructor(error, target) { - super('error', target); - - this.message = error.message; - this.error = error; - } -} - -/** - * This provides methods for emulating the `EventTarget` interface. It's not - * meant to be used directly. - * - * @mixin - */ -const EventTarget = { - /** - * Register an event listener. - * - * @param {String} method A string representing the event type to listen for - * @param {Function} listener The listener to add - * @public - */ - addEventListener(method, listener) { - if (typeof listener !== 'function') return; - - function onMessage(data) { - listener.call(this, new MessageEvent(data, this)); - } - - function onClose(code, message) { - listener.call(this, new CloseEvent(code, message, this)); - } - - function onError(error) { - listener.call(this, new ErrorEvent(error, this)); - } - - function onOpen() { - listener.call(this, new OpenEvent(this)); - } - - if (method === 'message') { - onMessage._listener = listener; - this.on(method, onMessage); - } else if (method === 'close') { - onClose._listener = listener; - this.on(method, onClose); - } else if (method === 'error') { - onError._listener = listener; - this.on(method, onError); - } else if (method === 'open') { - onOpen._listener = listener; - this.on(method, onOpen); - } else { - this.on(method, listener); - } - }, - - /** - * Remove an event listener. - * - * @param {String} method A string representing the event type to remove - * @param {Function} listener The listener to remove - * @public - */ - removeEventListener(method, listener) { - const listeners = this.listeners(method); - - for (var i = 0; i < listeners.length; i++) { - if (listeners[i] === listener || listeners[i]._listener === listener) { - this.removeListener(method, listeners[i]); - } - } - } -}; - -exports_websockets_ws_lib_event_target = EventTarget; - - -/* -file https://github.com/websockets/ws/blob/6.2.1/lib/extension.js -*/ -'use strict'; - -// -// Allowed token characters: -// -// '!', '#', '$', '%', '&', ''', '*', '+', '-', -// '.', 0-9, A-Z, '^', '_', '`', a-z, '|', '~' -// -// tokenChars[32] === 0 // ' ' -// tokenChars[33] === 1 // '!' -// tokenChars[34] === 0 // '"' -// ... -// -// prettier-ignore -const tokenChars = [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 15 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 31 - 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, // 32 - 47 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48 - 63 - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64 - 79 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, // 80 - 95 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96 - 111 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0 // 112 - 127 -]; - -/** - * Adds an offer to the map of extension offers or a parameter to the map of - * parameters. - * - * @param {Object} dest The map of extension offers or parameters - * @param {String} name The extension or parameter name - * @param {(Object|Boolean|String)} elem The extension parameters or the - * parameter value - * @private - */ -function push(dest, name, elem) { - if (Object.prototype.hasOwnProperty.call(dest, name)) dest[name].push(elem); - else dest[name] = [elem]; -} - -/** - * Parses the `Sec-WebSocket-Extensions` header into an object. - * - * @param {String} header The field value of the header - * @return {Object} The parsed object - * @public - */ -function parse(header) { - const offers = {}; - - if (header === undefined || header === '') return offers; - - var params = {}; - var mustUnescape = false; - var isEscaping = false; - var inQuotes = false; - var extensionName; - var paramName; - var start = -1; - var end = -1; - - for (var i = 0; i < header.length; i++) { - const code = header.charCodeAt(i); - - if (extensionName === undefined) { - if (end === -1 && tokenChars[code] === 1) { - if (start === -1) start = i; - } else if (code === 0x20 /* ' ' */ || code === 0x09 /* '\t' */) { - if (end === -1 && start !== -1) end = i; - } else if (code === 0x3b /* ';' */ || code === 0x2c /* ',' */) { - if (start === -1) { - throw new SyntaxError(`Unexpected character at index ${i}`); - } - - if (end === -1) end = i; - const name = header.slice(start, end); - if (code === 0x2c) { - push(offers, name, params); - params = {}; - } else { - extensionName = name; - } - - start = end = -1; - } else { - throw new SyntaxError(`Unexpected character at index ${i}`); - } - } else if (paramName === undefined) { - if (end === -1 && tokenChars[code] === 1) { - if (start === -1) start = i; - } else if (code === 0x20 || code === 0x09) { - if (end === -1 && start !== -1) end = i; - } else if (code === 0x3b || code === 0x2c) { - if (start === -1) { - throw new SyntaxError(`Unexpected character at index ${i}`); - } - - if (end === -1) end = i; - push(params, header.slice(start, end), true); - if (code === 0x2c) { - push(offers, extensionName, params); - params = {}; - extensionName = undefined; - } - - start = end = -1; - } else if (code === 0x3d /* '=' */ && start !== -1 && end === -1) { - paramName = header.slice(start, i); - start = end = -1; - } else { - throw new SyntaxError(`Unexpected character at index ${i}`); - } - } else { - // - // The value of a quoted-string after unescaping must conform to the - // token ABNF, so only token characters are valid. - // Ref: https://tools.ietf.org/html/rfc6455#section-9.1 - // - if (isEscaping) { - if (tokenChars[code] !== 1) { - throw new SyntaxError(`Unexpected character at index ${i}`); - } - if (start === -1) start = i; - else if (!mustUnescape) mustUnescape = true; - isEscaping = false; - } else if (inQuotes) { - if (tokenChars[code] === 1) { - if (start === -1) start = i; - } else if (code === 0x22 /* '"' */ && start !== -1) { - inQuotes = false; - end = i; - } else if (code === 0x5c /* '\' */) { - isEscaping = true; - } else { - throw new SyntaxError(`Unexpected character at index ${i}`); - } - } else if (code === 0x22 && header.charCodeAt(i - 1) === 0x3d) { - inQuotes = true; - } else if (end === -1 && tokenChars[code] === 1) { - if (start === -1) start = i; - } else if (start !== -1 && (code === 0x20 || code === 0x09)) { - if (end === -1) end = i; - } else if (code === 0x3b || code === 0x2c) { - if (start === -1) { - throw new SyntaxError(`Unexpected character at index ${i}`); - } - - if (end === -1) end = i; - var value = header.slice(start, end); - if (mustUnescape) { - value = value.replace(/\\/g, ''); - mustUnescape = false; - } - push(params, paramName, value); - if (code === 0x2c) { - push(offers, extensionName, params); - params = {}; - extensionName = undefined; - } - - paramName = undefined; - start = end = -1; - } else { - throw new SyntaxError(`Unexpected character at index ${i}`); - } - } - } - - if (start === -1 || inQuotes) { - throw new SyntaxError('Unexpected end of input'); - } - - if (end === -1) end = i; - const token = header.slice(start, end); - if (extensionName === undefined) { - push(offers, token, {}); - } else { - if (paramName === undefined) { - push(params, token, true); - } else if (mustUnescape) { - push(params, paramName, token.replace(/\\/g, '')); - } else { - push(params, paramName, token); - } - push(offers, extensionName, params); - } - - return offers; -} - -/** - * Builds the `Sec-WebSocket-Extensions` header field value. - * - * @param {Object} extensions The map of extensions and parameters to format - * @return {String} A string representing the given object - * @public - */ -function format(extensions) { - return Object.keys(extensions) - .map((extension) => { - var configurations = extensions[extension]; - if (!Array.isArray(configurations)) configurations = [configurations]; - return configurations - .map((params) => { - return [extension] - .concat( - Object.keys(params).map((k) => { - var values = params[k]; - if (!Array.isArray(values)) values = [values]; - return values - .map((v) => (v === true ? k : `${k}=${v}`)) - .join('; '); - }) - ) - .join('; '); - }) - .join(', '); - }) - .join(', '); -} - -exports_websockets_ws_lib_extension = { format, parse }; - - -/* -file https://github.com/websockets/ws/blob/6.2.1/lib/receiver.js -*/ -'use strict'; - -// const { Writable } = require('stream'); - -// const PerMessageDeflate = exports_websockets_ws_lib_permessage_deflate; -// const { -// BINARY_TYPES, -// EMPTY_BUFFER, -// kStatusCode, -// kWebSocket -// } = exports_websockets_ws_lib_constants; -// const { concat, toArrayBuffer, unmask } = exports_websockets_ws_lib_buffer_util; -// const { isValidStatusCode, isValidUTF8 } = exports_websockets_ws_lib_validation; - -const GET_INFO = 0; -const GET_PAYLOAD_LENGTH_16 = 1; -const GET_PAYLOAD_LENGTH_64 = 2; -const GET_MASK = 3; -const GET_DATA = 4; -const INFLATING = 5; - -/** - * HyBi Receiver implementation. - * - * @extends stream.Writable - */ -class Receiver extends Writable { - /** - * Creates a Receiver instance. - * - * @param {String} binaryType The type for binary data - * @param {Object} extensions An object containing the negotiated extensions - * @param {Number} maxPayload The maximum allowed message length - */ - constructor(binaryType, extensions, maxPayload) { - super(); - - this._binaryType = binaryType || BINARY_TYPES[0]; - this[kWebSocket] = undefined; - this._extensions = extensions || {}; - this._maxPayload = maxPayload | 0; - - this._bufferedBytes = 0; - this._buffers = []; - - this._compressed = false; - this._payloadLength = 0; - this._mask = undefined; - this._fragmented = 0; - this._masked = false; - this._fin = false; - this._opcode = 0; - - this._totalPayloadLength = 0; - this._messageLength = 0; - this._fragments = []; - - this._state = GET_INFO; - this._loop = false; - } - - /** - * Implements `Writable.prototype._write()`. - * - * @param {Buffer} chunk The chunk of data to write - * @param {String} encoding The character encoding of `chunk` - * @param {Function} cb Callback - */ - _write(chunk, encoding, cb) { - if (this._opcode === 0x08 && this._state == GET_INFO) return cb(); - - this._bufferedBytes += chunk.length; - this._buffers.push(chunk); - this.startLoop(cb); - } - - /** - * Consumes `n` bytes from the buffered data. - * - * @param {Number} n The number of bytes to consume - * @return {Buffer} The consumed bytes - * @private - */ - consume(n) { - this._bufferedBytes -= n; - - if (n === this._buffers[0].length) return this._buffers.shift(); - - if (n < this._buffers[0].length) { - const buf = this._buffers[0]; - this._buffers[0] = buf.slice(n); - return buf.slice(0, n); - } - - const dst = Buffer.allocUnsafe(n); - - do { - const buf = this._buffers[0]; - - if (n >= buf.length) { - this._buffers.shift().copy(dst, dst.length - n); - } else { - buf.copy(dst, dst.length - n, 0, n); - this._buffers[0] = buf.slice(n); - } - - n -= buf.length; - } while (n > 0); - - return dst; - } - - /** - * Starts the parsing loop. - * - * @param {Function} cb Callback - * @private - */ - startLoop(cb) { - var err; - this._loop = true; - - do { - switch (this._state) { - case GET_INFO: - err = this.getInfo(); - break; - case GET_PAYLOAD_LENGTH_16: - err = this.getPayloadLength16(); - break; - case GET_PAYLOAD_LENGTH_64: - err = this.getPayloadLength64(); - break; - case GET_MASK: - this.getMask(); - break; - case GET_DATA: - err = this.getData(cb); - break; - default: - // `INFLATING` - this._loop = false; - return; - } - } while (this._loop); - - cb(err); - } - - /** - * Reads the first two bytes of a frame. - * - * @return {(RangeError|undefined)} A possible error - * @private - */ - getInfo() { - if (this._bufferedBytes < 2) { - this._loop = false; - return; - } - - const buf = this.consume(2); - - if ((buf[0] & 0x30) !== 0x00) { - this._loop = false; - return error(RangeError, 'RSV2 and RSV3 must be clear', true, 1002); - } - - const compressed = (buf[0] & 0x40) === 0x40; - - if (compressed && !this._extensions[PerMessageDeflate.extensionName]) { - this._loop = false; - return error(RangeError, 'RSV1 must be clear', true, 1002); - } - - this._fin = (buf[0] & 0x80) === 0x80; - this._opcode = buf[0] & 0x0f; - this._payloadLength = buf[1] & 0x7f; - - if (this._opcode === 0x00) { - if (compressed) { - this._loop = false; - return error(RangeError, 'RSV1 must be clear', true, 1002); - } - - if (!this._fragmented) { - this._loop = false; - return error(RangeError, 'invalid opcode 0', true, 1002); - } - - this._opcode = this._fragmented; - } else if (this._opcode === 0x01 || this._opcode === 0x02) { - if (this._fragmented) { - this._loop = false; - return error(RangeError, `invalid opcode ${this._opcode}`, true, 1002); - } - - this._compressed = compressed; - } else if (this._opcode > 0x07 && this._opcode < 0x0b) { - if (!this._fin) { - this._loop = false; - return error(RangeError, 'FIN must be set', true, 1002); - } - - if (compressed) { - this._loop = false; - return error(RangeError, 'RSV1 must be clear', true, 1002); - } - - if (this._payloadLength > 0x7d) { - this._loop = false; - return error( - RangeError, - `invalid payload length ${this._payloadLength}`, - true, - 1002 - ); - } - } else { - this._loop = false; - return error(RangeError, `invalid opcode ${this._opcode}`, true, 1002); - } - - if (!this._fin && !this._fragmented) this._fragmented = this._opcode; - this._masked = (buf[1] & 0x80) === 0x80; - - if (this._payloadLength === 126) this._state = GET_PAYLOAD_LENGTH_16; - else if (this._payloadLength === 127) this._state = GET_PAYLOAD_LENGTH_64; - else return this.haveLength(); - } - - /** - * Gets extended payload length (7+16). - * - * @return {(RangeError|undefined)} A possible error - * @private - */ - getPayloadLength16() { - if (this._bufferedBytes < 2) { - this._loop = false; - return; - } - - this._payloadLength = this.consume(2).readUInt16BE(0); - return this.haveLength(); - } - - /** - * Gets extended payload length (7+64). - * - * @return {(RangeError|undefined)} A possible error - * @private - */ - getPayloadLength64() { - if (this._bufferedBytes < 8) { - this._loop = false; - return; - } - - const buf = this.consume(8); - const num = buf.readUInt32BE(0); - - // - // The maximum safe integer in JavaScript is 2^53 - 1. An error is returned - // if payload length is greater than this number. - // - if (num > Math.pow(2, 53 - 32) - 1) { - this._loop = false; - return error( - RangeError, - 'Unsupported WebSocket frame: payload length > 2^53 - 1', - false, - 1009 - ); - } - - this._payloadLength = num * Math.pow(2, 32) + buf.readUInt32BE(4); - return this.haveLength(); - } - - /** - * Payload length has been read. - * - * @return {(RangeError|undefined)} A possible error - * @private - */ - haveLength() { - if (this._payloadLength && this._opcode < 0x08) { - this._totalPayloadLength += this._payloadLength; - if (this._totalPayloadLength > this._maxPayload && this._maxPayload > 0) { - this._loop = false; - return error(RangeError, 'Max payload size exceeded', false, 1009); - } - } - - if (this._masked) this._state = GET_MASK; - else this._state = GET_DATA; - } - - /** - * Reads mask bytes. - * - * @private - */ - getMask() { - if (this._bufferedBytes < 4) { - this._loop = false; - return; - } - - this._mask = this.consume(4); - this._state = GET_DATA; - } - - /** - * Reads data bytes. - * - * @param {Function} cb Callback - * @return {(Error|RangeError|undefined)} A possible error - * @private - */ - getData(cb) { - var data = EMPTY_BUFFER; - - if (this._payloadLength) { - if (this._bufferedBytes < this._payloadLength) { - this._loop = false; - return; - } - - data = this.consume(this._payloadLength); - if (this._masked) unmask(data, this._mask); - } - - if (this._opcode > 0x07) return this.controlMessage(data); - - if (this._compressed) { - this._state = INFLATING; - this.decompress(data, cb); - return; - } - - if (data.length) { - // - // This message is not compressed so its lenght is the sum of the payload - // length of all fragments. - // - this._messageLength = this._totalPayloadLength; - this._fragments.push(data); - } - - return this.dataMessage(); - } - - /** - * Decompresses data. - * - * @param {Buffer} data Compressed data - * @param {Function} cb Callback - * @private - */ - decompress(data, cb) { - const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName]; - - perMessageDeflate.decompress(data, this._fin, (err, buf) => { - if (err) return cb(err); - - if (buf.length) { - this._messageLength += buf.length; - if (this._messageLength > this._maxPayload && this._maxPayload > 0) { - return cb( - error(RangeError, 'Max payload size exceeded', false, 1009) - ); - } - - this._fragments.push(buf); - } - - const er = this.dataMessage(); - if (er) return cb(er); - - this.startLoop(cb); - }); - } - - /** - * Handles a data message. - * - * @return {(Error|undefined)} A possible error - * @private - */ - dataMessage() { - if (this._fin) { - const messageLength = this._messageLength; - const fragments = this._fragments; - - this._totalPayloadLength = 0; - this._messageLength = 0; - this._fragmented = 0; - this._fragments = []; - - if (this._opcode === 2) { - var data; - - if (this._binaryType === 'nodebuffer') { - data = concat(fragments, messageLength); - } else if (this._binaryType === 'arraybuffer') { - data = toArrayBuffer(concat(fragments, messageLength)); - } else { - data = fragments; - } - - this.emit('message', data); - } else { - const buf = concat(fragments, messageLength); - - if (!isValidUTF8(buf)) { - this._loop = false; - return error(Error, 'invalid UTF-8 sequence', true, 1007); - } - - this.emit('message', buf.toString()); - } - } - - this._state = GET_INFO; - } - - /** - * Handles a control message. - * - * @param {Buffer} data Data to handle - * @return {(Error|RangeError|undefined)} A possible error - * @private - */ - controlMessage(data) { - if (this._opcode === 0x08) { - this._loop = false; - - if (data.length === 0) { - this.emit('conclude', 1005, ''); - this.end(); - } else if (data.length === 1) { - return error(RangeError, 'invalid payload length 1', true, 1002); - } else { - const code = data.readUInt16BE(0); - - if (!isValidStatusCode(code)) { - return error(RangeError, `invalid status code ${code}`, true, 1002); - } - - const buf = data.slice(2); - - if (!isValidUTF8(buf)) { - return error(Error, 'invalid UTF-8 sequence', true, 1007); - } - - this.emit('conclude', code, buf.toString()); - this.end(); - } - } else if (this._opcode === 0x09) { - this.emit('ping', data); - } else { - this.emit('pong', data); - } - - this._state = GET_INFO; - } -} - -exports_websockets_ws_lib_receiver = Receiver; - -/** - * Builds an error object. - * - * @param {(Error|RangeError)} ErrorCtor The error constructor - * @param {String} message The error message - * @param {Boolean} prefix Specifies whether or not to add a default prefix to - * `message` - * @param {Number} statusCode The status code - * @return {(Error|RangeError)} The error - * @private - */ -function error(ErrorCtor, message, prefix, statusCode) { - const err = new ErrorCtor( - prefix ? `Invalid WebSocket frame: ${message}` : message - ); - - Error.captureStackTrace(err, error); - err[kStatusCode] = statusCode; - return err; -} - - -/* -file https://github.com/websockets/ws/blob/6.2.1/lib/sender.js -*/ -'use strict'; - -// const { randomBytes } = require('crypto'); - -// const PerMessageDeflate = exports_websockets_ws_lib_permessage_deflate; -// const { EMPTY_BUFFER } = exports_websockets_ws_lib_constants; -// const { isValidStatusCode } = exports_websockets_ws_lib_validation; -// const { mask: applyMask, toBuffer } = exports_websockets_ws_lib_buffer_util; - -/** - * HyBi Sender implementation. - */ -class Sender { - /** - * Creates a Sender instance. - * - * @param {net.Socket} socket The connection socket - * @param {Object} extensions An object containing the negotiated extensions - */ - constructor(socket, extensions) { - this._extensions = extensions || {}; - this._socket = socket; - - this._firstFragment = true; - this._compress = false; - - this._bufferedBytes = 0; - this._deflating = false; - this._queue = []; - } - - /** - * Frames a piece of data according to the HyBi WebSocket protocol. - * - * @param {Buffer} data The data to frame - * @param {Object} options Options object - * @param {Number} options.opcode The opcode - * @param {Boolean} options.readOnly Specifies whether `data` can be modified - * @param {Boolean} options.fin Specifies whether or not to set the FIN bit - * @param {Boolean} options.mask Specifies whether or not to mask `data` - * @param {Boolean} options.rsv1 Specifies whether or not to set the RSV1 bit - * @return {Buffer[]} The framed data as a list of `Buffer` instances - * @public - */ - static frame(data, options) { - const merge = options.mask && options.readOnly; - var offset = options.mask ? 6 : 2; - var payloadLength = data.length; - - if (data.length >= 65536) { - offset += 8; - payloadLength = 127; - } else if (data.length > 125) { - offset += 2; - payloadLength = 126; - } - - const target = Buffer.allocUnsafe(merge ? data.length + offset : offset); - - target[0] = options.fin ? options.opcode | 0x80 : options.opcode; - if (options.rsv1) target[0] |= 0x40; - - target[1] = payloadLength; - - if (payloadLength === 126) { - target.writeUInt16BE(data.length, 2); - } else if (payloadLength === 127) { - target.writeUInt32BE(0, 2); - target.writeUInt32BE(data.length, 6); - } - - if (!options.mask) return [target, data]; - - const mask = randomBytes(4); - - target[1] |= 0x80; - target[offset - 4] = mask[0]; - target[offset - 3] = mask[1]; - target[offset - 2] = mask[2]; - target[offset - 1] = mask[3]; - - if (merge) { - applyMask(data, mask, target, offset, data.length); - return [target]; - } - - applyMask(data, mask, data, 0, data.length); - return [target, data]; - } - - /** - * Sends a close message to the other peer. - * - * @param {(Number|undefined)} code The status code component of the body - * @param {String} data The message component of the body - * @param {Boolean} mask Specifies whether or not to mask the message - * @param {Function} cb Callback - * @public - */ - close(code, data, mask, cb) { - var buf; - - if (code === undefined) { - buf = EMPTY_BUFFER; - } else if (typeof code !== 'number' || !isValidStatusCode(code)) { - throw new TypeError('First argument must be a valid error code number'); - } else if (data === undefined || data === '') { - buf = Buffer.allocUnsafe(2); - buf.writeUInt16BE(code, 0); - } else { - buf = Buffer.allocUnsafe(2 + Buffer.byteLength(data)); - buf.writeUInt16BE(code, 0); - buf.write(data, 2); - } - - if (this._deflating) { - this.enqueue([this.doClose, buf, mask, cb]); - } else { - this.doClose(buf, mask, cb); - } - } - - /** - * Frames and sends a close message. - * - * @param {Buffer} data The message to send - * @param {Boolean} mask Specifies whether or not to mask `data` - * @param {Function} cb Callback - * @private - */ - doClose(data, mask, cb) { - this.sendFrame( - Sender.frame(data, { - fin: true, - rsv1: false, - opcode: 0x08, - mask, - readOnly: false - }), - cb - ); - } - - /** - * Sends a ping message to the other peer. - * - * @param {*} data The message to send - * @param {Boolean} mask Specifies whether or not to mask `data` - * @param {Function} cb Callback - * @public - */ - ping(data, mask, cb) { - const buf = toBuffer(data); - - if (this._deflating) { - this.enqueue([this.doPing, buf, mask, toBuffer.readOnly, cb]); - } else { - this.doPing(buf, mask, toBuffer.readOnly, cb); - } - } - - /** - * Frames and sends a ping message. - * - * @param {*} data The message to send - * @param {Boolean} mask Specifies whether or not to mask `data` - * @param {Boolean} readOnly Specifies whether `data` can be modified - * @param {Function} cb Callback - * @private - */ - doPing(data, mask, readOnly, cb) { - this.sendFrame( - Sender.frame(data, { - fin: true, - rsv1: false, - opcode: 0x09, - mask, - readOnly - }), - cb - ); - } - - /** - * Sends a pong message to the other peer. - * - * @param {*} data The message to send - * @param {Boolean} mask Specifies whether or not to mask `data` - * @param {Function} cb Callback - * @public - */ - pong(data, mask, cb) { - const buf = toBuffer(data); - - if (this._deflating) { - this.enqueue([this.doPong, buf, mask, toBuffer.readOnly, cb]); - } else { - this.doPong(buf, mask, toBuffer.readOnly, cb); - } - } - - /** - * Frames and sends a pong message. - * - * @param {*} data The message to send - * @param {Boolean} mask Specifies whether or not to mask `data` - * @param {Boolean} readOnly Specifies whether `data` can be modified - * @param {Function} cb Callback - * @private - */ - doPong(data, mask, readOnly, cb) { - this.sendFrame( - Sender.frame(data, { - fin: true, - rsv1: false, - opcode: 0x0a, - mask, - readOnly - }), - cb - ); - } - - /** - * Sends a data message to the other peer. - * - * @param {*} data The message to send - * @param {Object} options Options object - * @param {Boolean} options.compress Specifies whether or not to compress `data` - * @param {Boolean} options.binary Specifies whether `data` is binary or text - * @param {Boolean} options.fin Specifies whether the fragment is the last one - * @param {Boolean} options.mask Specifies whether or not to mask `data` - * @param {Function} cb Callback - * @public - */ - send(data, options, cb) { - const buf = toBuffer(data); - const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName]; - var opcode = options.binary ? 2 : 1; - var rsv1 = options.compress; - - if (this._firstFragment) { - this._firstFragment = false; - if (rsv1 && perMessageDeflate) { - rsv1 = buf.length >= perMessageDeflate._threshold; - } - this._compress = rsv1; - } else { - rsv1 = false; - opcode = 0; - } - - if (options.fin) this._firstFragment = true; - - if (perMessageDeflate) { - const opts = { - fin: options.fin, - rsv1, - opcode, - mask: options.mask, - readOnly: toBuffer.readOnly - }; - - if (this._deflating) { - this.enqueue([this.dispatch, buf, this._compress, opts, cb]); - } else { - this.dispatch(buf, this._compress, opts, cb); - } - } else { - this.sendFrame( - Sender.frame(buf, { - fin: options.fin, - rsv1: false, - opcode, - mask: options.mask, - readOnly: toBuffer.readOnly - }), - cb - ); - } - } - - /** - * Dispatches a data message. - * - * @param {Buffer} data The message to send - * @param {Boolean} compress Specifies whether or not to compress `data` - * @param {Object} options Options object - * @param {Number} options.opcode The opcode - * @param {Boolean} options.readOnly Specifies whether `data` can be modified - * @param {Boolean} options.fin Specifies whether or not to set the FIN bit - * @param {Boolean} options.mask Specifies whether or not to mask `data` - * @param {Boolean} options.rsv1 Specifies whether or not to set the RSV1 bit - * @param {Function} cb Callback - * @private - */ - dispatch(data, compress, options, cb) { - if (!compress) { - this.sendFrame(Sender.frame(data, options), cb); - return; - } - - const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName]; - - this._deflating = true; - perMessageDeflate.compress(data, options.fin, (_, buf) => { - this._deflating = false; - options.readOnly = false; - this.sendFrame(Sender.frame(buf, options), cb); - this.dequeue(); - }); - } - - /** - * Executes queued send operations. - * - * @private - */ - dequeue() { - while (!this._deflating && this._queue.length) { - const params = this._queue.shift(); - - this._bufferedBytes -= params[1].length; - params[0].apply(this, params.slice(1)); - } - } - - /** - * Enqueues a send operation. - * - * @param {Array} params Send operation parameters. - * @private - */ - enqueue(params) { - this._bufferedBytes += params[1].length; - this._queue.push(params); - } - - /** - * Sends a frame. - * - * @param {Buffer[]} list The frame to send - * @param {Function} cb Callback - * @private - */ - sendFrame(list, cb) { - if (list.length === 2) { - this._socket.cork(); - this._socket.write(list[0]); - this._socket.write(list[1], cb); - this._socket.uncork(); - } else { - this._socket.write(list[0], cb); - } - } -} - -exports_websockets_ws_lib_sender = Sender; - - -/* -file https://github.com/websockets/ws/blob/6.2.1/lib/websocket-server.js -*/ -'use strict'; - -// const EventEmitter = require('events'); -// const crypto = require('crypto'); -// const http = require('http'); - -// const PerMessageDeflate = exports_websockets_ws_lib_permessage_deflate; -// const extension = exports_websockets_ws_lib_extension; -// const WebSocket = exports_websockets_ws_lib_websocket; -// const { GUID } = exports_websockets_ws_lib_constants; - -const keyRegex = /^[+/0-9A-Za-z]{22}==$/; - -/** - * Class representing a WebSocket server. - * - * @extends EventEmitter - */ -class WebSocketServer extends EventEmitter { - /** - * Create a `WebSocketServer` instance. - * - * @param {Object} options Configuration options - * @param {Number} options.backlog The maximum length of the queue of pending - * connections - * @param {Boolean} options.clientTracking Specifies whether or not to track - * clients - * @param {Function} options.handleProtocols An hook to handle protocols - * @param {String} options.host The hostname where to bind the server - * @param {Number} options.maxPayload The maximum allowed message size - * @param {Boolean} options.noServer Enable no server mode - * @param {String} options.path Accept only connections matching this path - * @param {(Boolean|Object)} options.perMessageDeflate Enable/disable - * permessage-deflate - * @param {Number} options.port The port where to bind the server - * @param {http.Server} options.server A pre-created HTTP/S server to use - * @param {Function} options.verifyClient An hook to reject connections - * @param {Function} callback A listener for the `listening` event - */ - constructor(options, callback) { - super(); - - options = Object.assign( - { - maxPayload: 100 * 1024 * 1024, - perMessageDeflate: false, - handleProtocols: null, - clientTracking: true, - verifyClient: null, - noServer: false, - backlog: null, // use default (511 as implemented in net.js) - server: null, - host: null, - path: null, - port: null - }, - options - ); - - if (options.port == null && !options.server && !options.noServer) { - throw new TypeError( - 'One of the "port", "server", or "noServer" options must be specified' - ); - } - - if (options.port != null) { - this._server = http.createServer((req, res) => { - const body = http.STATUS_CODES[426]; - - res.writeHead(426, { - 'Content-Length': body.length, - 'Content-Type': 'text/plain' - }); - res.end(body); - }); - this._server.listen( - options.port, - options.host, - options.backlog, - callback - ); - } else if (options.server) { - this._server = options.server; - } - - if (this._server) { - this._removeListeners = addListeners(this._server, { - listening: this.emit.bind(this, 'listening'), - error: this.emit.bind(this, 'error'), - upgrade: (req, socket, head) => { - this.handleUpgrade(req, socket, head, (ws) => { - this.emit('connection', ws, req); - }); - } - }); - } - - if (options.perMessageDeflate === true) options.perMessageDeflate = {}; - if (options.clientTracking) this.clients = new Set(); - this.options = options; - } - - /** - * Returns the bound address, the address family name, and port of the server - * as reported by the operating system if listening on an IP socket. - * If the server is listening on a pipe or UNIX domain socket, the name is - * returned as a string. - * - * @return {(Object|String|null)} The address of the server - * @public - */ - address() { - if (this.options.noServer) { - throw new Error('The server is operating in "noServer" mode'); - } - - if (!this._server) return null; - return this._server.address(); - } - - /** - * Close the server. - * - * @param {Function} cb Callback - * @public - */ - close(cb) { - if (cb) this.once('close', cb); - - // - // Terminate all associated clients. - // - if (this.clients) { - for (const client of this.clients) client.terminate(); - } - - const server = this._server; - - if (server) { - this._removeListeners(); - this._removeListeners = this._server = null; - - // - // Close the http server if it was internally created. - // - if (this.options.port != null) { - server.close(() => this.emit('close')); - return; - } - } - - process.nextTick(emitClose, this); - } - - /** - * See if a given request should be handled by this server instance. - * - * @param {http.IncomingMessage} req Request object to inspect - * @return {Boolean} `true` if the request is valid, else `false` - * @public - */ - shouldHandle(req) { - if (this.options.path) { - const index = req.url.indexOf('?'); - const pathname = index !== -1 ? req.url.slice(0, index) : req.url; - - if (pathname !== this.options.path) return false; - } - - return true; - } - - /** - * Handle a HTTP Upgrade request. - * - * @param {http.IncomingMessage} req The request object - * @param {net.Socket} socket The network socket between the server and client - * @param {Buffer} head The first packet of the upgraded stream - * @param {Function} cb Callback - * @public - */ - handleUpgrade(req, socket, head, cb) { - socket.on('error', socketOnError); - - const key = - req.headers['sec-websocket-key'] !== undefined - ? req.headers['sec-websocket-key'].trim() - : false; - const version = +req.headers['sec-websocket-version']; - const extensions = {}; - - if ( - req.method !== 'GET' || - req.headers.upgrade.toLowerCase() !== 'websocket' || - !key || - !keyRegex.test(key) || - (version !== 8 && version !== 13) || - !this.shouldHandle(req) - ) { - return abortHandshake(socket, 400); - } - - if (this.options.perMessageDeflate) { - const perMessageDeflate = new PerMessageDeflate( - this.options.perMessageDeflate, - true, - this.options.maxPayload - ); - - try { - const offers = extension.parse(req.headers['sec-websocket-extensions']); - - if (offers[PerMessageDeflate.extensionName]) { - perMessageDeflate.accept(offers[PerMessageDeflate.extensionName]); - extensions[PerMessageDeflate.extensionName] = perMessageDeflate; - } - } catch (err) { - return abortHandshake(socket, 400); - } - } - - // - // Optionally call external client verification handler. - // - if (this.options.verifyClient) { - const info = { - origin: - req.headers[`${version === 8 ? 'sec-websocket-origin' : 'origin'}`], - secure: !!(req.connection.authorized || req.connection.encrypted), - req - }; - - if (this.options.verifyClient.length === 2) { - this.options.verifyClient(info, (verified, code, message, headers) => { - if (!verified) { - return abortHandshake(socket, code || 401, message, headers); - } - - this.completeUpgrade(key, extensions, req, socket, head, cb); - }); - return; - } - - if (!this.options.verifyClient(info)) return abortHandshake(socket, 401); - } - - this.completeUpgrade(key, extensions, req, socket, head, cb); - } - - /** - * Upgrade the connection to WebSocket. - * - * @param {String} key The value of the `Sec-WebSocket-Key` header - * @param {Object} extensions The accepted extensions - * @param {http.IncomingMessage} req The request object - * @param {net.Socket} socket The network socket between the server and client - * @param {Buffer} head The first packet of the upgraded stream - * @param {Function} cb Callback - * @private - */ - completeUpgrade(key, extensions, req, socket, head, cb) { - // - // Destroy the socket if the client has already sent a FIN packet. - // - if (!socket.readable || !socket.writable) return socket.destroy(); - - const digest = crypto - .createHash('sha1') - .update(key + GUID) - .digest('base64'); - - const headers = [ - 'HTTP/1.1 101 Switching Protocols', - 'Upgrade: websocket', - 'Connection: Upgrade', - `Sec-WebSocket-Accept: ${digest}` - ]; - - const ws = new WebSocket(null); - var protocol = req.headers['sec-websocket-protocol']; - - if (protocol) { - protocol = protocol.trim().split(/ *, */); - - // - // Optionally call external protocol selection handler. - // - if (this.options.handleProtocols) { - protocol = this.options.handleProtocols(protocol, req); - } else { - protocol = protocol[0]; - } - - if (protocol) { - headers.push(`Sec-WebSocket-Protocol: ${protocol}`); - ws.protocol = protocol; - } - } - - if (extensions[PerMessageDeflate.extensionName]) { - const params = extensions[PerMessageDeflate.extensionName].params; - const value = extension.format({ - [PerMessageDeflate.extensionName]: [params] - }); - headers.push(`Sec-WebSocket-Extensions: ${value}`); - ws._extensions = extensions; - } - - // - // Allow external modification/inspection of handshake headers. - // - this.emit('headers', headers, req); - - socket.write(headers.concat('\r\n').join('\r\n')); - socket.removeListener('error', socketOnError); - - ws.setSocket(socket, head, this.options.maxPayload); - - if (this.clients) { - this.clients.add(ws); - ws.on('close', () => this.clients.delete(ws)); - } - - cb(ws); - } -} - -exports_websockets_ws_lib_websocket_server = WebSocketServer; - -/** - * Add event listeners on an `EventEmitter` using a map of - * pairs. - * - * @param {EventEmitter} server The event emitter - * @param {Object.} map The listeners to add - * @return {Function} A function that will remove the added listeners when called - * @private - */ -function addListeners(server, map) { - for (const event of Object.keys(map)) server.on(event, map[event]); - - return function removeListeners() { - for (const event of Object.keys(map)) { - server.removeListener(event, map[event]); - } - }; -} - -/** - * Emit a `'close'` event on an `EventEmitter`. - * - * @param {EventEmitter} server The event emitter - * @private - */ -function emitClose(server) { - server.emit('close'); -} - -/** - * Handle premature socket errors. - * - * @private - */ -function socketOnError() { - this.destroy(); -} - -/** - * Close the connection when preconditions are not fulfilled. - * - * @param {net.Socket} socket The socket of the upgrade request - * @param {Number} code The HTTP response status code - * @param {String} [message] The HTTP response body - * @param {Object} [headers] Additional HTTP response headers - * @private - */ -function abortHandshake(socket, code, message, headers) { - if (socket.writable) { - message = message || http.STATUS_CODES[code]; - headers = Object.assign( - { - Connection: 'close', - 'Content-type': 'text/html', - 'Content-Length': Buffer.byteLength(message) - }, - headers - ); - - socket.write( - `HTTP/1.1 ${code} ${http.STATUS_CODES[code]}\r\n` + - Object.keys(headers) - .map((h) => `${h}: ${headers[h]}`) - .join('\r\n') + - '\r\n\r\n' + - message - ); - } - - socket.removeListener('error', socketOnError); - socket.destroy(); -} - - -/* -file https://github.com/websockets/ws/blob/6.2.1/lib/websocket.js -*/ -'use strict'; - -// const EventEmitter = require('events'); -// const crypto = require('crypto'); -// const https = require('https'); -// const http = require('http'); -// const net = require('net'); -// const tls = require('tls'); -// const url = require('url'); - -// const PerMessageDeflate = exports_websockets_ws_lib_permessage_deflate; -// const EventTarget = exports_websockets_ws_lib_event_target; -// const extension = exports_websockets_ws_lib_extension; -// const Receiver = exports_websockets_ws_lib_receiver; -// const Sender = exports_websockets_ws_lib_sender; -// const { -// BINARY_TYPES, -// EMPTY_BUFFER, -// GUID, -// kStatusCode, -// kWebSocket, -// NOOP -// } = exports_websockets_ws_lib_constants; - -const readyStates = ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED']; -const protocolVersions = [8, 13]; -const closeTimeout = 30 * 1000; - -/** - * Class representing a WebSocket. - * - * @extends EventEmitter - */ -class WebSocket extends EventEmitter { - /** - * Create a new `WebSocket`. - * - * @param {(String|url.Url|url.URL)} address The URL to which to connect - * @param {(String|String[])} protocols The subprotocols - * @param {Object} options Connection options - */ - constructor(address, protocols, options) { - super(); - - this.readyState = WebSocket.CONNECTING; - this.protocol = ''; - - this._binaryType = BINARY_TYPES[0]; - this._closeFrameReceived = false; - this._closeFrameSent = false; - this._closeMessage = ''; - this._closeTimer = null; - this._closeCode = 1006; - this._extensions = {}; - this._receiver = null; - this._sender = null; - this._socket = null; - - if (address !== null) { - this._isServer = false; - this._redirects = 0; - - if (Array.isArray(protocols)) { - protocols = protocols.join(', '); - } else if (typeof protocols === 'object' && protocols !== null) { - options = protocols; - protocols = undefined; - } - - initAsClient(this, address, protocols, options); - } else { - this._isServer = true; - } - } - - get CONNECTING() { - return WebSocket.CONNECTING; - } - get CLOSING() { - return WebSocket.CLOSING; - } - get CLOSED() { - return WebSocket.CLOSED; - } - get OPEN() { - return WebSocket.OPEN; - } - - /** - * This deviates from the WHATWG interface since ws doesn't support the - * required default "blob" type (instead we define a custom "nodebuffer" - * type). - * - * @type {String} - */ - get binaryType() { - return this._binaryType; - } - - set binaryType(type) { - if (!BINARY_TYPES.includes(type)) return; - - this._binaryType = type; - - // - // Allow to change `binaryType` on the fly. - // - if (this._receiver) this._receiver._binaryType = type; - } - - /** - * @type {Number} - */ - get bufferedAmount() { - if (!this._socket) return 0; - - // - // `socket.bufferSize` is `undefined` if the socket is closed. - // - return (this._socket.bufferSize || 0) + this._sender._bufferedBytes; - } - - /** - * @type {String} - */ - get extensions() { - return Object.keys(this._extensions).join(); - } - - /** - * Set up the socket and the internal resources. - * - * @param {net.Socket} socket The network socket between the server and client - * @param {Buffer} head The first packet of the upgraded stream - * @param {Number} maxPayload The maximum allowed message size - * @private - */ - setSocket(socket, head, maxPayload) { - const receiver = new Receiver( - this._binaryType, - this._extensions, - maxPayload - ); - - this._sender = new Sender(socket, this._extensions); - this._receiver = receiver; - this._socket = socket; - - receiver[kWebSocket] = this; - socket[kWebSocket] = this; - - receiver.on('conclude', receiverOnConclude); - receiver.on('drain', receiverOnDrain); - receiver.on('error', receiverOnError); - receiver.on('message', receiverOnMessage); - receiver.on('ping', receiverOnPing); - receiver.on('pong', receiverOnPong); - - socket.setTimeout(0); - socket.setNoDelay(); - - if (head.length > 0) socket.unshift(head); - - socket.on('close', socketOnClose); - socket.on('data', socketOnData); - socket.on('end', socketOnEnd); - socket.on('error', socketOnError); - - this.readyState = WebSocket.OPEN; - this.emit('open'); - } - - /** - * Emit the `'close'` event. - * - * @private - */ - emitClose() { - this.readyState = WebSocket.CLOSED; - - if (!this._socket) { - this.emit('close', this._closeCode, this._closeMessage); - return; - } - - if (this._extensions[PerMessageDeflate.extensionName]) { - this._extensions[PerMessageDeflate.extensionName].cleanup(); - } - - this._receiver.removeAllListeners(); - this.emit('close', this._closeCode, this._closeMessage); - } - - /** - * Start a closing handshake. - * - * +----------+ +-----------+ +----------+ - * - - -|ws.close()|-->|close frame|-->|ws.close()|- - - - * | +----------+ +-----------+ +----------+ | - * +----------+ +-----------+ | - * CLOSING |ws.close()|<--|close frame|<--+-----+ CLOSING - * +----------+ +-----------+ | - * | | | +---+ | - * +------------------------+-->|fin| - - - - - * | +---+ | +---+ - * - - - - -|fin|<---------------------+ - * +---+ - * - * @param {Number} code Status code explaining why the connection is closing - * @param {String} data A string explaining why the connection is closing - * @public - */ - close(code, data) { - if (this.readyState === WebSocket.CLOSED) return; - if (this.readyState === WebSocket.CONNECTING) { - const msg = 'WebSocket was closed before the connection was established'; - return abortHandshake(this, this._req, msg); - } - - if (this.readyState === WebSocket.CLOSING) { - if (this._closeFrameSent && this._closeFrameReceived) this._socket.end(); - return; - } - - this.readyState = WebSocket.CLOSING; - this._sender.close(code, data, !this._isServer, (err) => { - // - // This error is handled by the `'error'` listener on the socket. We only - // want to know if the close frame has been sent here. - // - if (err) return; - - this._closeFrameSent = true; - if (this._closeFrameReceived) this._socket.end(); - }); - - // - // Specify a timeout for the closing handshake to complete. - // - this._closeTimer = setTimeout( - this._socket.destroy.bind(this._socket), - closeTimeout - ); - } - - /** - * Send a ping. - * - * @param {*} data The data to send - * @param {Boolean} mask Indicates whether or not to mask `data` - * @param {Function} cb Callback which is executed when the ping is sent - * @public - */ - ping(data, mask, cb) { - if (typeof data === 'function') { - cb = data; - data = mask = undefined; - } else if (typeof mask === 'function') { - cb = mask; - mask = undefined; - } - - if (this.readyState !== WebSocket.OPEN) { - const err = new Error( - `WebSocket is not open: readyState ${this.readyState} ` + - `(${readyStates[this.readyState]})` - ); - - if (cb) return cb(err); - throw err; - } - - if (typeof data === 'number') data = data.toString(); - if (mask === undefined) mask = !this._isServer; - this._sender.ping(data || EMPTY_BUFFER, mask, cb); - } - - /** - * Send a pong. - * - * @param {*} data The data to send - * @param {Boolean} mask Indicates whether or not to mask `data` - * @param {Function} cb Callback which is executed when the pong is sent - * @public - */ - pong(data, mask, cb) { - if (typeof data === 'function') { - cb = data; - data = mask = undefined; - } else if (typeof mask === 'function') { - cb = mask; - mask = undefined; - } - - if (this.readyState !== WebSocket.OPEN) { - const err = new Error( - `WebSocket is not open: readyState ${this.readyState} ` + - `(${readyStates[this.readyState]})` - ); - - if (cb) return cb(err); - throw err; - } - - if (typeof data === 'number') data = data.toString(); - if (mask === undefined) mask = !this._isServer; - this._sender.pong(data || EMPTY_BUFFER, mask, cb); - } - - /** - * Send a data message. - * - * @param {*} data The message to send - * @param {Object} options Options object - * @param {Boolean} options.compress Specifies whether or not to compress `data` - * @param {Boolean} options.binary Specifies whether `data` is binary or text - * @param {Boolean} options.fin Specifies whether the fragment is the last one - * @param {Boolean} options.mask Specifies whether or not to mask `data` - * @param {Function} cb Callback which is executed when data is written out - * @public - */ - send(data, options, cb) { - if (typeof options === 'function') { - cb = options; - options = {}; - } - - if (this.readyState !== WebSocket.OPEN) { - const err = new Error( - `WebSocket is not open: readyState ${this.readyState} ` + - `(${readyStates[this.readyState]})` - ); - - if (cb) return cb(err); - throw err; - } - - if (typeof data === 'number') data = data.toString(); - - const opts = Object.assign( - { - binary: typeof data !== 'string', - mask: !this._isServer, - compress: true, - fin: true - }, - options - ); - - if (!this._extensions[PerMessageDeflate.extensionName]) { - opts.compress = false; - } - - this._sender.send(data || EMPTY_BUFFER, opts, cb); - } - - /** - * Forcibly close the connection. - * - * @public - */ - terminate() { - if (this.readyState === WebSocket.CLOSED) return; - if (this.readyState === WebSocket.CONNECTING) { - const msg = 'WebSocket was closed before the connection was established'; - return abortHandshake(this, this._req, msg); - } - - if (this._socket) { - this.readyState = WebSocket.CLOSING; - this._socket.destroy(); - } - } -} - -readyStates.forEach((readyState, i) => { - WebSocket[readyState] = i; -}); - -// -// Add the `onopen`, `onerror`, `onclose`, and `onmessage` attributes. -// See https://html.spec.whatwg.org/multipage/comms.html#the-websocket-interface -// -['open', 'error', 'close', 'message'].forEach((method) => { - Object.defineProperty(WebSocket.prototype, `on${method}`, { - /** - * Return the listener of the event. - * - * @return {(Function|undefined)} The event listener or `undefined` - * @public - */ - get() { - const listeners = this.listeners(method); - for (var i = 0; i < listeners.length; i++) { - if (listeners[i]._listener) return listeners[i]._listener; - } - - return undefined; - }, - /** - * Add a listener for the event. - * - * @param {Function} listener The listener to add - * @public - */ - set(listener) { - const listeners = this.listeners(method); - for (var i = 0; i < listeners.length; i++) { - // - // Remove only the listeners added via `addEventListener`. - // - if (listeners[i]._listener) this.removeListener(method, listeners[i]); - } - this.addEventListener(method, listener); - } - }); -}); - -WebSocket.prototype.addEventListener = EventTarget.addEventListener; -WebSocket.prototype.removeEventListener = EventTarget.removeEventListener; - -exports_websockets_ws_lib_websocket = WebSocket; - -/** - * Initialize a WebSocket client. - * - * @param {WebSocket} websocket The client to initialize - * @param {(String|url.Url|url.URL)} address The URL to which to connect - * @param {String} protocols The subprotocols - * @param {Object} options Connection options - * @param {(Boolean|Object)} options.perMessageDeflate Enable/disable - * permessage-deflate - * @param {Number} options.handshakeTimeout Timeout in milliseconds for the - * handshake request - * @param {Number} options.protocolVersion Value of the `Sec-WebSocket-Version` - * header - * @param {String} options.origin Value of the `Origin` or - * `Sec-WebSocket-Origin` header - * @param {Number} options.maxPayload The maximum allowed message size - * @param {Boolean} options.followRedirects Whether or not to follow redirects - * @param {Number} options.maxRedirects The maximum number of redirects allowed - * @private - */ -function initAsClient(websocket, address, protocols, options) { - const opts = Object.assign( - { - protocolVersion: protocolVersions[1], - maxPayload: 100 * 1024 * 1024, - perMessageDeflate: true, - followRedirects: false, - maxRedirects: 10 - }, - options, - { - createConnection: undefined, - socketPath: undefined, - hostname: undefined, - protocol: undefined, - timeout: undefined, - method: undefined, - auth: undefined, - host: undefined, - path: undefined, - port: undefined - } - ); - - if (!protocolVersions.includes(opts.protocolVersion)) { - throw new RangeError( - `Unsupported protocol version: ${opts.protocolVersion} ` + - `(supported versions: ${protocolVersions.join(', ')})` - ); - } - - var parsedUrl; - - if (typeof address === 'object' && address.href !== undefined) { - parsedUrl = address; - websocket.url = address.href; - } else { - // - // The WHATWG URL constructor is not available on Node.js < 6.13.0 - // - parsedUrl = url.URL ? new url.URL(address) : url.parse(address); - websocket.url = address; - } - - const isUnixSocket = parsedUrl.protocol === 'ws+unix:'; - - if (!parsedUrl.host && (!isUnixSocket || !parsedUrl.pathname)) { - throw new Error(`Invalid URL: ${websocket.url}`); - } - - const isSecure = - parsedUrl.protocol === 'wss:' || parsedUrl.protocol === 'https:'; - const defaultPort = isSecure ? 443 : 80; - const key = crypto.randomBytes(16).toString('base64'); - const get = isSecure ? https.get : http.get; - const path = parsedUrl.search - ? `${parsedUrl.pathname || '/'}${parsedUrl.search}` - : parsedUrl.pathname || '/'; - var perMessageDeflate; - - opts.createConnection = isSecure ? tlsConnect : netConnect; - opts.defaultPort = opts.defaultPort || defaultPort; - opts.port = parsedUrl.port || defaultPort; - opts.host = parsedUrl.hostname.startsWith('[') - ? parsedUrl.hostname.slice(1, -1) - : parsedUrl.hostname; - opts.headers = Object.assign( - { - 'Sec-WebSocket-Version': opts.protocolVersion, - 'Sec-WebSocket-Key': key, - Connection: 'Upgrade', - Upgrade: 'websocket' - }, - opts.headers - ); - opts.path = path; - opts.timeout = opts.handshakeTimeout; - - if (opts.perMessageDeflate) { - perMessageDeflate = new PerMessageDeflate( - opts.perMessageDeflate !== true ? opts.perMessageDeflate : {}, - false, - opts.maxPayload - ); - opts.headers['Sec-WebSocket-Extensions'] = extension.format({ - [PerMessageDeflate.extensionName]: perMessageDeflate.offer() - }); - } - if (protocols) { - opts.headers['Sec-WebSocket-Protocol'] = protocols; - } - if (opts.origin) { - if (opts.protocolVersion < 13) { - opts.headers['Sec-WebSocket-Origin'] = opts.origin; - } else { - opts.headers.Origin = opts.origin; - } - } - if (parsedUrl.auth) { - opts.auth = parsedUrl.auth; - } else if (parsedUrl.username || parsedUrl.password) { - opts.auth = `${parsedUrl.username}:${parsedUrl.password}`; - } - - if (isUnixSocket) { - const parts = path.split(':'); - - opts.socketPath = parts[0]; - opts.path = parts[1]; - } - - var req = (websocket._req = get(opts)); - - if (opts.timeout) { - req.on('timeout', () => { - abortHandshake(websocket, req, 'Opening handshake has timed out'); - }); - } - - req.on('error', (err) => { - if (websocket._req.aborted) return; - - req = websocket._req = null; - websocket.readyState = WebSocket.CLOSING; - websocket.emit('error', err); - websocket.emitClose(); - }); - - req.on('response', (res) => { - const location = res.headers.location; - const statusCode = res.statusCode; - - if ( - location && - opts.followRedirects && - statusCode >= 300 && - statusCode < 400 - ) { - if (++websocket._redirects > opts.maxRedirects) { - abortHandshake(websocket, req, 'Maximum redirects exceeded'); - return; - } - - req.abort(); - - const addr = url.URL - ? new url.URL(location, address) - : url.resolve(address, location); - - initAsClient(websocket, addr, protocols, options); - } else if (!websocket.emit('unexpected-response', req, res)) { - abortHandshake( - websocket, - req, - `Unexpected server response: ${res.statusCode}` - ); - } - }); - - req.on('upgrade', (res, socket, head) => { - websocket.emit('upgrade', res); - - // - // The user may have closed the connection from a listener of the `upgrade` - // event. - // - if (websocket.readyState !== WebSocket.CONNECTING) return; - - req = websocket._req = null; - - const digest = crypto - .createHash('sha1') - .update(key + GUID) - .digest('base64'); - - if (res.headers['sec-websocket-accept'] !== digest) { - abortHandshake(websocket, socket, 'Invalid Sec-WebSocket-Accept header'); - return; - } - - const serverProt = res.headers['sec-websocket-protocol']; - const protList = (protocols || '').split(/, */); - var protError; - - if (!protocols && serverProt) { - protError = 'Server sent a subprotocol but none was requested'; - } else if (protocols && !serverProt) { - protError = 'Server sent no subprotocol'; - } else if (serverProt && !protList.includes(serverProt)) { - protError = 'Server sent an invalid subprotocol'; - } - - if (protError) { - abortHandshake(websocket, socket, protError); - return; - } - - if (serverProt) websocket.protocol = serverProt; - - if (perMessageDeflate) { - try { - const extensions = extension.parse( - res.headers['sec-websocket-extensions'] - ); - - if (extensions[PerMessageDeflate.extensionName]) { - perMessageDeflate.accept(extensions[PerMessageDeflate.extensionName]); - websocket._extensions[ - PerMessageDeflate.extensionName - ] = perMessageDeflate; - } - } catch (err) { - abortHandshake( - websocket, - socket, - 'Invalid Sec-WebSocket-Extensions header' - ); - return; - } - } - - websocket.setSocket(socket, head, opts.maxPayload); - }); -} - -/** - * Create a `net.Socket` and initiate a connection. - * - * @param {Object} options Connection options - * @return {net.Socket} The newly created socket used to start the connection - * @private - */ -function netConnect(options) { - // - // Override `options.path` only if `options` is a copy of the original options - // object. This is always true on Node.js >= 8 but not on Node.js 6 where - // `options.socketPath` might be `undefined` even if the `socketPath` option - // was originally set. - // - if (options.protocolVersion) options.path = options.socketPath; - return net.connect(options); -} - -/** - * Create a `tls.TLSSocket` and initiate a connection. - * - * @param {Object} options Connection options - * @return {tls.TLSSocket} The newly created socket used to start the connection - * @private - */ -function tlsConnect(options) { - options.path = undefined; - options.servername = options.servername || options.host; - return tls.connect(options); -} - -/** - * Abort the handshake and emit an error. - * - * @param {WebSocket} websocket The WebSocket instance - * @param {(http.ClientRequest|net.Socket)} stream The request to abort or the - * socket to destroy - * @param {String} message The error message - * @private - */ -function abortHandshake(websocket, stream, message) { - websocket.readyState = WebSocket.CLOSING; - - const err = new Error(message); - Error.captureStackTrace(err, abortHandshake); - - if (stream.setHeader) { - stream.abort(); - stream.once('abort', websocket.emitClose.bind(websocket)); - websocket.emit('error', err); - } else { - stream.destroy(err); - stream.once('error', websocket.emit.bind(websocket, 'error')); - stream.once('close', websocket.emitClose.bind(websocket)); - } -} - -/** - * The listener of the `Receiver` `'conclude'` event. - * - * @param {Number} code The status code - * @param {String} reason The reason for closing - * @private - */ -function receiverOnConclude(code, reason) { - const websocket = this[kWebSocket]; - - websocket._socket.removeListener('data', socketOnData); - websocket._socket.resume(); - - websocket._closeFrameReceived = true; - websocket._closeMessage = reason; - websocket._closeCode = code; - - if (code === 1005) websocket.close(); - else websocket.close(code, reason); -} - -/** - * The listener of the `Receiver` `'drain'` event. - * - * @private - */ -function receiverOnDrain() { - this[kWebSocket]._socket.resume(); -} - -/** - * The listener of the `Receiver` `'error'` event. - * - * @param {(RangeError|Error)} err The emitted error - * @private - */ -function receiverOnError(err) { - const websocket = this[kWebSocket]; - - websocket._socket.removeListener('data', socketOnData); - - websocket.readyState = WebSocket.CLOSING; - websocket._closeCode = err[kStatusCode]; - websocket.emit('error', err); - websocket._socket.destroy(); -} - -/** - * The listener of the `Receiver` `'finish'` event. - * - * @private - */ -function receiverOnFinish() { - this[kWebSocket].emitClose(); -} - -/** - * The listener of the `Receiver` `'message'` event. - * - * @param {(String|Buffer|ArrayBuffer|Buffer[])} data The message - * @private - */ -function receiverOnMessage(data) { - this[kWebSocket].emit('message', data); -} - -/** - * The listener of the `Receiver` `'ping'` event. - * - * @param {Buffer} data The data included in the ping frame - * @private - */ -function receiverOnPing(data) { - const websocket = this[kWebSocket]; - - websocket.pong(data, !websocket._isServer, NOOP); - websocket.emit('ping', data); -} - -/** - * The listener of the `Receiver` `'pong'` event. - * - * @param {Buffer} data The data included in the pong frame - * @private - */ -function receiverOnPong(data) { - this[kWebSocket].emit('pong', data); -} - -/** - * The listener of the `net.Socket` `'close'` event. - * - * @private - */ -function socketOnClose() { - const websocket = this[kWebSocket]; - - this.removeListener('close', socketOnClose); - this.removeListener('end', socketOnEnd); - - websocket.readyState = WebSocket.CLOSING; - - // - // The close frame might not have been received or the `'end'` event emitted, - // for example, if the socket was destroyed due to an error. Ensure that the - // `receiver` stream is closed after writing any remaining buffered data to - // it. If the readable side of the socket is in flowing mode then there is no - // buffered data as everything has been already written and `readable.read()` - // will return `null`. If instead, the socket is paused, any possible buffered - // data will be read as a single chunk and emitted synchronously in a single - // `'data'` event. - // - websocket._socket.read(); - websocket._receiver.end(); - - this.removeListener('data', socketOnData); - this[kWebSocket] = undefined; - - clearTimeout(websocket._closeTimer); - - if ( - websocket._receiver._writableState.finished || - websocket._receiver._writableState.errorEmitted - ) { - websocket.emitClose(); - } else { - websocket._receiver.on('error', receiverOnFinish); - websocket._receiver.on('finish', receiverOnFinish); - } -} - -/** - * The listener of the `net.Socket` `'data'` event. - * - * @param {Buffer} chunk A chunk of data - * @private - */ -function socketOnData(chunk) { - if (!this[kWebSocket]._receiver.write(chunk)) { - this.pause(); - } -} - -/** - * The listener of the `net.Socket` `'end'` event. - * - * @private - */ -function socketOnEnd() { - const websocket = this[kWebSocket]; - - websocket.readyState = WebSocket.CLOSING; - websocket._receiver.end(); - this.end(); -} - -/** - * The listener of the `net.Socket` `'error'` event. - * - * @private - */ -function socketOnError() { - const websocket = this[kWebSocket]; - - this.removeListener('error', socketOnError); - this.on('error', NOOP); - - websocket.readyState = WebSocket.CLOSING; - this.destroy(); -} - - -/* -file https://github.com/websockets/ws/blob/6.2.1/index.js -*/ -'use strict'; - -// const WebSocket = exports_websockets_ws_lib_websocket; - -WebSocket.Server = exports_websockets_ws_lib_websocket_server; -WebSocket.Receiver = exports_websockets_ws_lib_receiver; -WebSocket.Sender = exports_websockets_ws_lib_sender; - -exports_websockets_ws_index = WebSocket; - - -/* -repo https://github.com/puppeteer/puppeteer/tree/v1.19.0 -committed 2019-07-23T05:02:45Z -*/ - - -/* -file https://github.com/puppeteer/puppeteer/blob/v1.19.0/package.json -*/ -exports_puppeteer_puppeteer_package_json = { - "name": "puppeteer", - "version": "1.19.0", - "description": "A high-level API to control headless Chrome over the DevTools Protocol", - "main": "index.js", - "repository": "github:GoogleChrome/puppeteer", - "engines": { - "node": ">=6.4.0" - }, - "puppeteer": { - "chromium_revision": "674921" - }, - "scripts": { - "unit": "node test/test.js", - "funit": "BROWSER=firefox node test/test.js", - "debug-unit": "node --inspect-brk test/test.js", - "test-doclint": "node utils/doclint/check_public_api/test/test.js && node utils/doclint/preprocessor/test.js", - "test": "npm run lint --silent && npm run coverage && npm run test-doclint && npm run test-node6-transformer && npm run test-types", - "install": "node install.js", - "lint": "([ \"$CI\" = true ] && eslint --quiet -f codeframe . || eslint .) && npm run tsc && npm run doc", - "doc": "node utils/doclint/cli.js", - "coverage": "cross-env COVERAGE=true npm run unit", - "test-node6-transformer": "node utils/node6-transform/test/test.js", - "build": "node utils/node6-transform/index.js && node utils/doclint/generate_types", - "unit-node6": "node node6/test/test.js", - "tsc": "tsc -p .", - "prepublishOnly": "npm run build", - "apply-next-version": "node utils/apply_next_version.js", - "bundle": "npx browserify -r ./index.js:puppeteer -o utils/browser/puppeteer-web.js", - "test-types": "node utils/doclint/generate_types && npx -p typescript@2.1 tsc -p utils/doclint/generate_types/test/", - "unit-bundle": "node utils/browser/test.js" - }, - "author": "The Chromium Authors", - "license": "Apache-2.0", - "dependencies": { - "debug": "^4.1.0", - "extract-zip": "^1.6.6", - "https-proxy-agent": "^2.2.1", - "mime": "^2.0.3", - "progress": "^2.0.1", - "proxy-from-env": "^1.0.0", - "rimraf": "^2.6.1", - "ws": "^6.1.0" - }, - "devDependencies": { - "@types/debug": "0.0.31", - "@types/extract-zip": "^1.6.2", - "@types/mime": "^2.0.0", - "@types/node": "^8.10.34", - "@types/rimraf": "^2.0.2", - "@types/ws": "^6.0.1", - "commonmark": "^0.28.1", - "cross-env": "^5.0.5", - "eslint": "^5.15.1", - "esprima": "^4.0.0", - "jpeg-js": "^0.3.4", - "minimist": "^1.2.0", - "ncp": "^2.0.0", - "pixelmatch": "^4.0.2", - "pngjs": "^3.3.3", - "text-diff": "^1.0.1", - "typescript": "3.2.2" - }, - "browser": { - "./lib/BrowserFetcher.js": false, - "./node6/lib/Puppeteer": false, - "ws": "./utils/browser/WebSocket", - "fs": false, - "child_process": false, - "rimraf": false, - "readline": false - } -} - - -/* -file https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/helper.js -*/ -/** - * Copyright 2017 Google Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -// const {TimeoutError} = exports_puppeteer_puppeteer_lib_Errors; -// const debugError = require('debug')(`puppeteer:error`); -// const fs = require('fs'); - -class Helper { - /** - * @param {Function|string} fun - * @param {!Array<*>} args - * @return {string} - */ - static evaluationString(fun, ...args) { - if (Helper.isString(fun)) { - assert(args.length === 0, 'Cannot evaluate a string with arguments'); - return /** @type {string} */ (fun); - } - return `(${fun})(${args.map(serializeArgument).join(',')})`; - - /** - * @param {*} arg - * @return {string} - */ - function serializeArgument(arg) { - if (Object.is(arg, undefined)) - return 'undefined'; - return JSON.stringify(arg); - } - } - - /** - * @param {!Protocol.Runtime.ExceptionDetails} exceptionDetails - * @return {string} - */ - static getExceptionMessage(exceptionDetails) { - if (exceptionDetails.exception) - return exceptionDetails.exception.description || exceptionDetails.exception.value; - let message = exceptionDetails.text; - if (exceptionDetails.stackTrace) { - for (const callframe of exceptionDetails.stackTrace.callFrames) { - const location = callframe.url + ':' + callframe.lineNumber + ':' + callframe.columnNumber; - const functionName = callframe.functionName || ''; - message += `\n at ${functionName} (${location})`; - } - } - return message; - } - - /** - * @param {!Protocol.Runtime.RemoteObject} remoteObject - * @return {*} - */ - static valueFromRemoteObject(remoteObject) { - assert(!remoteObject.objectId, 'Cannot extract value when objectId is given'); - if (remoteObject.unserializableValue) { - if (remoteObject.type === 'bigint' && typeof BigInt !== 'undefined') - return BigInt(remoteObject.unserializableValue.replace('n', '')); - switch (remoteObject.unserializableValue) { - case '-0': - return -0; - case 'NaN': - return NaN; - case 'Infinity': - return Infinity; - case '-Infinity': - return -Infinity; - default: - throw new Error('Unsupported unserializable value: ' + remoteObject.unserializableValue); - } - } - return remoteObject.value; - } - - /** - * @param {!Puppeteer.CDPSession} client - * @param {!Protocol.Runtime.RemoteObject} remoteObject - */ - static async releaseObject(client, remoteObject) { - if (!remoteObject.objectId) - return; - await client.send('Runtime.releaseObject', {objectId: remoteObject.objectId}).catch(error => { - // Exceptions might happen in case of a page been navigated or closed. - // Swallow these since they are harmless and we don't leak anything in this case. - debugError(error); - }); - } - - /** - * @param {!Object} classType - */ - static installAsyncStackHooks(classType) { - for (const methodName of Reflect.ownKeys(classType.prototype)) { - const method = Reflect.get(classType.prototype, methodName); - if (methodName === 'constructor' || typeof methodName !== 'string' || methodName.startsWith('_') || typeof method !== 'function' || method.constructor.name !== 'AsyncFunction') - continue; - Reflect.set(classType.prototype, methodName, function(...args) { - const syncStack = {}; - Error.captureStackTrace(syncStack); - return method.call(this, ...args).catch(e => { - const stack = syncStack.stack.substring(syncStack.stack.indexOf('\n') + 1); - const clientStack = stack.substring(stack.indexOf('\n')); - if (e instanceof Error && e.stack && !e.stack.includes(clientStack)) - e.stack += '\n -- ASYNC --\n' + stack; - throw e; - }); - }); - } - } - - /** - * @param {!NodeJS.EventEmitter} emitter - * @param {(string|symbol)} eventName - * @param {function(?):void} handler - * @return {{emitter: !NodeJS.EventEmitter, eventName: (string|symbol), handler: function(?)}} - */ - static addEventListener(emitter, eventName, handler) { - emitter.on(eventName, handler); - return { emitter, eventName, handler }; - } - - /** - * @param {!Array<{emitter: !NodeJS.EventEmitter, eventName: (string|symbol), handler: function(?):void}>} listeners - */ - static removeEventListeners(listeners) { - for (const listener of listeners) - listener.emitter.removeListener(listener.eventName, listener.handler); - listeners.splice(0, listeners.length); - } - - /** - * @param {!Object} obj - * @return {boolean} - */ - static isString(obj) { - return typeof obj === 'string' || obj instanceof String; - } - - /** - * @param {!Object} obj - * @return {boolean} - */ - static isNumber(obj) { - return typeof obj === 'number' || obj instanceof Number; - } - - static promisify(nodeFunction) { - function promisified(...args) { - return new Promise((resolve, reject) => { - function callback(err, ...result) { - if (err) - return reject(err); - if (result.length === 1) - return resolve(result[0]); - return resolve(result); - } - nodeFunction.call(null, ...args, callback); - }); - } - return promisified; - } - - /** - * @param {!NodeJS.EventEmitter} emitter - * @param {(string|symbol)} eventName - * @param {function} predicate - * @return {!Promise} - */ - static waitForEvent(emitter, eventName, predicate, timeout) { - let eventTimeout, resolveCallback, rejectCallback; - const promise = new Promise((resolve, reject) => { - resolveCallback = resolve; - rejectCallback = reject; - }); - const listener = Helper.addEventListener(emitter, eventName, event => { - if (!predicate(event)) - return; - cleanup(); - resolveCallback(event); - }); - if (timeout) { - eventTimeout = setTimeout(() => { - cleanup(); - rejectCallback(new TimeoutError('Timeout exceeded while waiting for event')); - }, timeout); - } - function cleanup() { - Helper.removeEventListeners([listener]); - clearTimeout(eventTimeout); - } - return promise; - } - - /** - * @template T - * @param {!Promise} promise - * @param {string} taskName - * @param {number} timeout - * @return {!Promise} - */ - static async waitWithTimeout(promise, taskName, timeout) { - let reject; - const timeoutError = new TimeoutError(`waiting for ${taskName} failed: timeout ${timeout}ms exceeded`); - const timeoutPromise = new Promise((resolve, x) => reject = x); - let timeoutTimer = null; - if (timeout) - timeoutTimer = setTimeout(() => reject(timeoutError), timeout); - try { - return await Promise.race([promise, timeoutPromise]); - } finally { - if (timeoutTimer) - clearTimeout(timeoutTimer); - } - } - - /** - * @param {!Puppeteer.CDPSession} client - * @param {string} handle - * @param {?string} path - * @return {!Promise} - */ - static async readProtocolStream(client, handle, path) { - let eof = false; - let file; - if (path) - file = await openAsync(path, 'w'); - const bufs = []; - while (!eof) { - const response = await client.send('IO.read', {handle}); - eof = response.eof; - const buf = Buffer.from(response.data, response.base64Encoded ? 'base64' : undefined); - bufs.push(buf); - if (path) - await writeAsync(file, buf); - } - if (path) - await closeAsync(file); - await client.send('IO.close', {handle}); - let resultBuffer = null; - try { - resultBuffer = Buffer.concat(bufs); - } finally { - return resultBuffer; - } - } -} - -const openAsync = Helper.promisify(fs.open); -const writeAsync = Helper.promisify(fs.write); -const closeAsync = Helper.promisify(fs.close); - -/** - * @param {*} value - * @param {string=} message - */ -function assert(value, message) { - if (!value) - throw new Error(message); -} - -exports_puppeteer_puppeteer_lib_helper = { - helper: Helper, - assert, - debugError -}; -// hack-puppeteer - init helper -let helper = exports_puppeteer_puppeteer_lib_helper.helper; - - -/* -file https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/Accessibility.js -*/ -/** - * Copyright 2018 Google Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the 'License'); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an 'AS IS' BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @typedef {Object} SerializedAXNode - * @property {string} role - * - * @property {string=} name - * @property {string|number=} value - * @property {string=} description - * - * @property {string=} keyshortcuts - * @property {string=} roledescription - * @property {string=} valuetext - * - * @property {boolean=} disabled - * @property {boolean=} expanded - * @property {boolean=} focused - * @property {boolean=} modal - * @property {boolean=} multiline - * @property {boolean=} multiselectable - * @property {boolean=} readonly - * @property {boolean=} required - * @property {boolean=} selected - * - * @property {boolean|"mixed"=} checked - * @property {boolean|"mixed"=} pressed - * - * @property {number=} level - * @property {number=} valuemin - * @property {number=} valuemax - * - * @property {string=} autocomplete - * @property {string=} haspopup - * @property {string=} invalid - * @property {string=} orientation - * - * @property {Array=} children - */ - -class Accessibility { - /** - * @param {!Puppeteer.CDPSession} client - */ - constructor(client) { - this._client = client; - } - - /** - * @param {{interestingOnly?: boolean, root?: ?Puppeteer.ElementHandle}=} options - * @return {!Promise} - */ - async snapshot(options = {}) { - const { - interestingOnly = true, - root = null, - } = options; - const {nodes} = await this._client.send('Accessibility.getFullAXTree'); - let backendNodeId = null; - if (root) { - const {node} = await this._client.send('DOM.describeNode', {objectId: root._remoteObject.objectId}); - backendNodeId = node.backendNodeId; - } - const defaultRoot = AXNode.createTree(nodes); - let needle = defaultRoot; - if (backendNodeId) { - needle = defaultRoot.find(node => node._payload.backendDOMNodeId === backendNodeId); - if (!needle) - return null; - } - if (!interestingOnly) - return serializeTree(needle)[0]; - - /** @type {!Set} */ - const interestingNodes = new Set(); - collectInterestingNodes(interestingNodes, defaultRoot, false); - if (!interestingNodes.has(needle)) - return null; - return serializeTree(needle, interestingNodes)[0]; - } -} - -/** - * @param {!Set} collection - * @param {!AXNode} node - * @param {boolean} insideControl - */ -function collectInterestingNodes(collection, node, insideControl) { - if (node.isInteresting(insideControl)) - collection.add(node); - if (node.isLeafNode()) - return; - insideControl = insideControl || node.isControl(); - for (const child of node._children) - collectInterestingNodes(collection, child, insideControl); -} - -/** - * @param {!AXNode} node - * @param {!Set=} whitelistedNodes - * @return {!Array} - */ -function serializeTree(node, whitelistedNodes) { - /** @type {!Array} */ - const children = []; - for (const child of node._children) - children.push(...serializeTree(child, whitelistedNodes)); - - if (whitelistedNodes && !whitelistedNodes.has(node)) - return children; - - const serializedNode = node.serialize(); - if (children.length) - serializedNode.children = children; - return [serializedNode]; -} - - -class AXNode { - /** - * @param {!Protocol.Accessibility.AXNode} payload - */ - constructor(payload) { - this._payload = payload; - - /** @type {!Array} */ - this._children = []; - - this._richlyEditable = false; - this._editable = false; - this._focusable = false; - this._expanded = false; - this._name = this._payload.name ? this._payload.name.value : ''; - this._role = this._payload.role ? this._payload.role.value : 'Unknown'; - this._cachedHasFocusableChild; - - for (const property of this._payload.properties || []) { - if (property.name === 'editable') { - this._richlyEditable = property.value.value === 'richtext'; - this._editable = true; - } - if (property.name === 'focusable') - this._focusable = property.value.value; - if (property.name === 'expanded') - this._expanded = property.value.value; - } - } - - /** - * @return {boolean} - */ - _isPlainTextField() { - if (this._richlyEditable) - return false; - if (this._editable) - return true; - return this._role === 'textbox' || this._role === 'ComboBox' || this._role === 'searchbox'; - } - - /** - * @return {boolean} - */ - _isTextOnlyObject() { - const role = this._role; - return (role === 'LineBreak' || role === 'text' || - role === 'InlineTextBox'); - } - - /** - * @return {boolean} - */ - _hasFocusableChild() { - if (this._cachedHasFocusableChild === undefined) { - this._cachedHasFocusableChild = false; - for (const child of this._children) { - if (child._focusable || child._hasFocusableChild()) { - this._cachedHasFocusableChild = true; - break; - } - } - } - return this._cachedHasFocusableChild; - } - - /** - * @param {function(AXNode):boolean} predicate - * @return {?AXNode} - */ - find(predicate) { - if (predicate(this)) - return this; - for (const child of this._children) { - const result = child.find(predicate); - if (result) - return result; - } - return null; - } - - /** - * @return {boolean} - */ - isLeafNode() { - if (!this._children.length) - return true; - - // These types of objects may have children that we use as internal - // implementation details, but we want to expose them as leaves to platform - // accessibility APIs because screen readers might be confused if they find - // any children. - if (this._isPlainTextField() || this._isTextOnlyObject()) - return true; - - // Roles whose children are only presentational according to the ARIA and - // HTML5 Specs should be hidden from screen readers. - // (Note that whilst ARIA buttons can have only presentational children, HTML5 - // buttons are allowed to have content.) - switch (this._role) { - case 'doc-cover': - case 'graphics-symbol': - case 'img': - case 'Meter': - case 'scrollbar': - case 'slider': - case 'separator': - case 'progressbar': - return true; - default: - break; - } - - // Here and below: Android heuristics - if (this._hasFocusableChild()) - return false; - if (this._focusable && this._name) - return true; - if (this._role === 'heading' && this._name) - return true; - return false; - } - - /** - * @return {boolean} - */ - isControl() { - switch (this._role) { - case 'button': - case 'checkbox': - case 'ColorWell': - case 'combobox': - case 'DisclosureTriangle': - case 'listbox': - case 'menu': - case 'menubar': - case 'menuitem': - case 'menuitemcheckbox': - case 'menuitemradio': - case 'radio': - case 'scrollbar': - case 'searchbox': - case 'slider': - case 'spinbutton': - case 'switch': - case 'tab': - case 'textbox': - case 'tree': - return true; - default: - return false; - } - } - - /** - * @param {boolean} insideControl - * @return {boolean} - */ - isInteresting(insideControl) { - const role = this._role; - if (role === 'Ignored') - return false; - - if (this._focusable || this._richlyEditable) - return true; - - // If it's not focusable but has a control role, then it's interesting. - if (this.isControl()) - return true; - - // A non focusable child of a control is not interesting - if (insideControl) - return false; - - return this.isLeafNode() && !!this._name; - } - - /** - * @return {!SerializedAXNode} - */ - serialize() { - /** @type {!Map} */ - const properties = new Map(); - for (const property of this._payload.properties || []) - properties.set(property.name.toLowerCase(), property.value.value); - if (this._payload.name) - properties.set('name', this._payload.name.value); - if (this._payload.value) - properties.set('value', this._payload.value.value); - if (this._payload.description) - properties.set('description', this._payload.description.value); - - /** @type {SerializedAXNode} */ - const node = { - role: this._role - }; - - /** @type {!Array} */ - const userStringProperties = [ - 'name', - 'value', - 'description', - 'keyshortcuts', - 'roledescription', - 'valuetext', - ]; - for (const userStringProperty of userStringProperties) { - if (!properties.has(userStringProperty)) - continue; - node[userStringProperty] = properties.get(userStringProperty); - } - - /** @type {!Array} */ - const booleanProperties = [ - 'disabled', - 'expanded', - 'focused', - 'modal', - 'multiline', - 'multiselectable', - 'readonly', - 'required', - 'selected', - ]; - for (const booleanProperty of booleanProperties) { - // WebArea's treat focus differently than other nodes. They report whether their frame has focus, - // not whether focus is specifically on the root node. - if (booleanProperty === 'focused' && this._role === 'WebArea') - continue; - const value = properties.get(booleanProperty); - if (!value) - continue; - node[booleanProperty] = value; - } - - /** @type {!Array} */ - const tristateProperties = [ - 'checked', - 'pressed', - ]; - for (const tristateProperty of tristateProperties) { - if (!properties.has(tristateProperty)) - continue; - const value = properties.get(tristateProperty); - node[tristateProperty] = value === 'mixed' ? 'mixed' : value === 'true' ? true : false; - } - /** @type {!Array} */ - const numericalProperties = [ - 'level', - 'valuemax', - 'valuemin', - ]; - for (const numericalProperty of numericalProperties) { - if (!properties.has(numericalProperty)) - continue; - node[numericalProperty] = properties.get(numericalProperty); - } - /** @type {!Array} */ - const tokenProperties = [ - 'autocomplete', - 'haspopup', - 'invalid', - 'orientation', - ]; - for (const tokenProperty of tokenProperties) { - const value = properties.get(tokenProperty); - if (!value || value === 'false') - continue; - node[tokenProperty] = value; - } - return node; - } - - /** - * @param {!Array} payloads - * @return {!AXNode} - */ - static createTree(payloads) { - /** @type {!Map} */ - const nodeById = new Map(); - for (const payload of payloads) - nodeById.set(payload.nodeId, new AXNode(payload)); - for (const node of nodeById.values()) { - for (const childId of node._payload.childIds || []) - node._children.push(nodeById.get(childId)); - } - return nodeById.values().next().value; - } -} - -exports_puppeteer_puppeteer_lib_Accessibility = {Accessibility}; - - -/* -file https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/Browser.js -*/ -/** - * Copyright 2017 Google Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// const { helper, assert } = exports_puppeteer_puppeteer_lib_helper; -// const {Target} = exports_puppeteer_puppeteer_lib_Target; -// const EventEmitter = require('events'); -// const {TaskQueue} = exports_puppeteer_puppeteer_lib_TaskQueue; -// const {Events} = exports_puppeteer_puppeteer_lib_Events; - -class Browser extends EventEmitter { - /** - * @param {!Puppeteer.Connection} connection - * @param {!Array} contextIds - * @param {boolean} ignoreHTTPSErrors - * @param {?Puppeteer.Viewport} defaultViewport - * @param {?Puppeteer.ChildProcess} process - * @param {function()=} closeCallback - */ - static async create(connection, contextIds, ignoreHTTPSErrors, defaultViewport, process, closeCallback) { - const browser = new Browser(connection, contextIds, ignoreHTTPSErrors, defaultViewport, process, closeCallback); - await connection.send('Target.setDiscoverTargets', {discover: true}); - return browser; - } - - /** - * @param {!Puppeteer.Connection} connection - * @param {!Array} contextIds - * @param {boolean} ignoreHTTPSErrors - * @param {?Puppeteer.Viewport} defaultViewport - * @param {?Puppeteer.ChildProcess} process - * @param {(function():Promise)=} closeCallback - */ - constructor(connection, contextIds, ignoreHTTPSErrors, defaultViewport, process, closeCallback) { - super(); - this._ignoreHTTPSErrors = ignoreHTTPSErrors; - this._defaultViewport = defaultViewport; - this._process = process; - this._screenshotTaskQueue = new TaskQueue(); - this._connection = connection; - this._closeCallback = closeCallback || new Function(); - - this._defaultContext = new BrowserContext(this._connection, this, null); - /** @type {Map} */ - this._contexts = new Map(); - for (const contextId of contextIds) - this._contexts.set(contextId, new BrowserContext(this._connection, this, contextId)); - - /** @type {Map} */ - this._targets = new Map(); - this._connection.on(Events.Connection.Disconnected, () => this.emit(Events.Browser.Disconnected)); - this._connection.on('Target.targetCreated', this._targetCreated.bind(this)); - this._connection.on('Target.targetDestroyed', this._targetDestroyed.bind(this)); - this._connection.on('Target.targetInfoChanged', this._targetInfoChanged.bind(this)); - } - - /** - * @return {?Puppeteer.ChildProcess} - */ - process() { - return this._process; - } - - /** - * @return {!Promise} - */ - async createIncognitoBrowserContext() { - const {browserContextId} = await this._connection.send('Target.createBrowserContext'); - const context = new BrowserContext(this._connection, this, browserContextId); - this._contexts.set(browserContextId, context); - return context; - } - - /** - * @return {!Array} - */ - browserContexts() { - return [this._defaultContext, ...Array.from(this._contexts.values())]; - } - - /** - * @return {!BrowserContext} - */ - defaultBrowserContext() { - return this._defaultContext; - } - - /** - * @param {?string} contextId - */ - async _disposeContext(contextId) { - await this._connection.send('Target.disposeBrowserContext', {browserContextId: contextId || undefined}); - this._contexts.delete(contextId); - } - - /** - * @param {!Protocol.Target.targetCreatedPayload} event - */ - async _targetCreated(event) { - const targetInfo = event.targetInfo; - const {browserContextId} = targetInfo; - const context = (browserContextId && this._contexts.has(browserContextId)) ? this._contexts.get(browserContextId) : this._defaultContext; - - const target = new Target(targetInfo, context, () => this._connection.createSession(targetInfo), this._ignoreHTTPSErrors, this._defaultViewport, this._screenshotTaskQueue); - assert(!this._targets.has(event.targetInfo.targetId), 'Target should not exist before targetCreated'); - this._targets.set(event.targetInfo.targetId, target); - - if (await target._initializedPromise) { - this.emit(Events.Browser.TargetCreated, target); - context.emit(Events.BrowserContext.TargetCreated, target); - } - } - - /** - * @param {{targetId: string}} event - */ - async _targetDestroyed(event) { - const target = this._targets.get(event.targetId); - target._initializedCallback(false); - this._targets.delete(event.targetId); - target._closedCallback(); - if (await target._initializedPromise) { - this.emit(Events.Browser.TargetDestroyed, target); - target.browserContext().emit(Events.BrowserContext.TargetDestroyed, target); - } - } - - /** - * @param {!Protocol.Target.targetInfoChangedPayload} event - */ - _targetInfoChanged(event) { - const target = this._targets.get(event.targetInfo.targetId); - assert(target, 'target should exist before targetInfoChanged'); - const previousURL = target.url(); - const wasInitialized = target._isInitialized; - target._targetInfoChanged(event.targetInfo); - if (wasInitialized && previousURL !== target.url()) { - this.emit(Events.Browser.TargetChanged, target); - target.browserContext().emit(Events.BrowserContext.TargetChanged, target); - } - } - - /** - * @return {string} - */ - wsEndpoint() { - return this._connection.url(); - } - - /** - * @return {!Promise} - */ - async newPage() { - return this._defaultContext.newPage(); - } - - /** - * @param {?string} contextId - * @return {!Promise} - */ - async _createPageInContext(contextId) { - const {targetId} = await this._connection.send('Target.createTarget', {url: 'about:blank', browserContextId: contextId || undefined}); - const target = await this._targets.get(targetId); - assert(await target._initializedPromise, 'Failed to create target for page'); - const page = await target.page(); - return page; - } - - /** - * @return {!Array} - */ - targets() { - return Array.from(this._targets.values()).filter(target => target._isInitialized); - } - - /** - * @return {!Target} - */ - target() { - return this.targets().find(target => target.type() === 'browser'); - } - - /** - * @param {function(!Target):boolean} predicate - * @param {{timeout?: number}=} options - * @return {!Promise} - */ - async waitForTarget(predicate, options = {}) { - const { - timeout = 30000 - } = options; - const existingTarget = this.targets().find(predicate); - if (existingTarget) - return existingTarget; - let resolve; - const targetPromise = new Promise(x => resolve = x); - this.on(Events.Browser.TargetCreated, check); - this.on(Events.Browser.TargetChanged, check); - try { - if (!timeout) - return await targetPromise; - return await helper.waitWithTimeout(targetPromise, 'target', timeout); - } finally { - this.removeListener(Events.Browser.TargetCreated, check); - this.removeListener(Events.Browser.TargetChanged, check); - } - - /** - * @param {!Target} target - */ - function check(target) { - if (predicate(target)) - resolve(target); - } - } - - /** - * @return {!Promise>} - */ - async pages() { - const contextPages = await Promise.all(this.browserContexts().map(context => context.pages())); - // Flatten array. - return contextPages.reduce((acc, x) => acc.concat(x), []); - } - - /** - * @return {!Promise} - */ - async version() { - const version = await this._getVersion(); - return version.product; - } - - /** - * @return {!Promise} - */ - async userAgent() { - const version = await this._getVersion(); - return version.userAgent; - } - - async close() { - await this._closeCallback.call(null); - this.disconnect(); - } - - disconnect() { - this._connection.dispose(); - } - - /** - * @return {boolean} - */ - isConnected() { - return !this._connection._closed; - } - - /** - * @return {!Promise} - */ - _getVersion() { - return this._connection.send('Browser.getVersion'); - } -} - -class BrowserContext extends EventEmitter { - /** - * @param {!Puppeteer.Connection} connection - * @param {!Browser} browser - * @param {?string} contextId - */ - constructor(connection, browser, contextId) { - super(); - this._connection = connection; - this._browser = browser; - this._id = contextId; - } - - /** - * @return {!Array} target - */ - targets() { - return this._browser.targets().filter(target => target.browserContext() === this); - } - - /** - * @param {function(!Target):boolean} predicate - * @param {{timeout?: number}=} options - * @return {!Promise} - */ - waitForTarget(predicate, options) { - return this._browser.waitForTarget(target => target.browserContext() === this && predicate(target), options); - } - - /** - * @return {!Promise>} - */ - async pages() { - const pages = await Promise.all( - this.targets() - .filter(target => target.type() === 'page') - .map(target => target.page()) - ); - return pages.filter(page => !!page); - } - - /** - * @return {boolean} - */ - isIncognito() { - return !!this._id; - } - - /** - * @param {string} origin - * @param {!Array} permissions - */ - async overridePermissions(origin, permissions) { - const webPermissionToProtocol = new Map([ - ['geolocation', 'geolocation'], - ['midi', 'midi'], - ['notifications', 'notifications'], - ['push', 'push'], - ['camera', 'videoCapture'], - ['microphone', 'audioCapture'], - ['background-sync', 'backgroundSync'], - ['ambient-light-sensor', 'sensors'], - ['accelerometer', 'sensors'], - ['gyroscope', 'sensors'], - ['magnetometer', 'sensors'], - ['accessibility-events', 'accessibilityEvents'], - ['clipboard-read', 'clipboardRead'], - ['clipboard-write', 'clipboardWrite'], - ['payment-handler', 'paymentHandler'], - // chrome-specific permissions we have. - ['midi-sysex', 'midiSysex'], - ]); - permissions = permissions.map(permission => { - const protocolPermission = webPermissionToProtocol.get(permission); - if (!protocolPermission) - throw new Error('Unknown permission: ' + permission); - return protocolPermission; - }); - await this._connection.send('Browser.grantPermissions', {origin, browserContextId: this._id || undefined, permissions}); - } - - async clearPermissionOverrides() { - await this._connection.send('Browser.resetPermissions', {browserContextId: this._id || undefined}); - } - - /** - * @return {!Promise} - */ - newPage() { - return this._browser._createPageInContext(this._id); - } - - /** - * @return {!Browser} - */ - browser() { - return this._browser; - } - - async close() { - assert(this._id, 'Non-incognito profiles cannot be closed!'); - await this._browser._disposeContext(this._id); - } -} - -exports_puppeteer_puppeteer_lib_Browser = {Browser, BrowserContext}; - - -/* -file https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/Connection.js -*/ -/** - * Copyright 2017 Google Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -// const {assert} = exports_puppeteer_puppeteer_lib_helper; -// const {Events} = exports_puppeteer_puppeteer_lib_Events; -// const debugProtocol = require('debug')('puppeteer:protocol'); -// const EventEmitter = require('events'); - -class Connection extends EventEmitter { - /** - * @param {string} url - * @param {!Puppeteer.ConnectionTransport} transport - * @param {number=} delay - */ - constructor(url, transport, delay = 0) { - super(); - this._url = url; - this._lastId = 0; - /** @type {!Map}*/ - this._callbacks = new Map(); - this._delay = delay; - - this._transport = transport; - this._transport.onmessage = this._onMessage.bind(this); - this._transport.onclose = this._onClose.bind(this); - /** @type {!Map}*/ - this._sessions = new Map(); - this._closed = false; - } - - /** - * @param {!CDPSession} session - * @return {!Connection} - */ - static fromSession(session) { - return session._connection; - } - - /** - * @param {string} sessionId - * @return {?CDPSession} - */ - session(sessionId) { - return this._sessions.get(sessionId) || null; - } - - /** - * @return {string} - */ - url() { - return this._url; - } - - /** - * @param {string} method - * @param {!Object=} params - * @return {!Promise} - */ - send(method, params = {}) { - const id = this._rawSend({method, params}); - return new Promise((resolve, reject) => { - this._callbacks.set(id, {resolve, reject, error: new Error(), method}); - }); - } - - /** - * @param {*} message - * @return {number} - */ - _rawSend(message) { - const id = ++this._lastId; - message = JSON.stringify(Object.assign({}, message, {id})); - debugProtocol('SEND ► ' + message); - this._transport.send(message); - return id; - } - - /** - * @param {string} message - */ - async _onMessage(message) { - if (this._delay) - await new Promise(f => setTimeout(f, this._delay)); - debugProtocol('◀ RECV ' + message); - const object = JSON.parse(message); - if (object.method === 'Target.attachedToTarget') { - const sessionId = object.params.sessionId; - const session = new CDPSession(this, object.params.targetInfo.type, sessionId); - this._sessions.set(sessionId, session); - } else if (object.method === 'Target.detachedFromTarget') { - const session = this._sessions.get(object.params.sessionId); - if (session) { - session._onClosed(); - this._sessions.delete(object.params.sessionId); - } - } - if (object.sessionId) { - const session = this._sessions.get(object.sessionId); - if (session) - session._onMessage(object); - } else if (object.id) { - const callback = this._callbacks.get(object.id); - // Callbacks could be all rejected if someone has called `.dispose()`. - if (callback) { - this._callbacks.delete(object.id); - if (object.error) - callback.reject(createProtocolError(callback.error, callback.method, object)); - else - callback.resolve(object.result); - } - } else { - this.emit(object.method, object.params); - } - } - - _onClose() { - if (this._closed) - return; - this._closed = true; - this._transport.onmessage = null; - this._transport.onclose = null; - for (const callback of this._callbacks.values()) - callback.reject(rewriteError(callback.error, `Protocol error (${callback.method}): Target closed.`)); - this._callbacks.clear(); - for (const session of this._sessions.values()) - session._onClosed(); - this._sessions.clear(); - this.emit(Events.Connection.Disconnected); - } - - dispose() { - this._onClose(); - this._transport.close(); - } - - /** - * @param {Protocol.Target.TargetInfo} targetInfo - * @return {!Promise} - */ - async createSession(targetInfo) { - const {sessionId} = await this.send('Target.attachToTarget', {targetId: targetInfo.targetId, flatten: true}); - return this._sessions.get(sessionId); - } -} - -class CDPSession extends EventEmitter { - /** - * @param {!Connection} connection - * @param {string} targetType - * @param {string} sessionId - */ - constructor(connection, targetType, sessionId) { - super(); - /** @type {!Map}*/ - this._callbacks = new Map(); - this._connection = connection; - this._targetType = targetType; - this._sessionId = sessionId; - } - - /** - * @param {string} method - * @param {!Object=} params - * @return {!Promise} - */ - send(method, params = {}) { - if (!this._connection) - return Promise.reject(new Error(`Protocol error (${method}): Session closed. Most likely the ${this._targetType} has been closed.`)); - const id = this._connection._rawSend({sessionId: this._sessionId, method, params}); - return new Promise((resolve, reject) => { - this._callbacks.set(id, {resolve, reject, error: new Error(), method}); - }); - } - - /** - * @param {{id?: number, method: string, params: Object, error: {message: string, data: any}, result?: *}} object - */ - _onMessage(object) { - if (object.id && this._callbacks.has(object.id)) { - const callback = this._callbacks.get(object.id); - this._callbacks.delete(object.id); - if (object.error) - callback.reject(createProtocolError(callback.error, callback.method, object)); - else - callback.resolve(object.result); - } else { - assert(!object.id); - this.emit(object.method, object.params); - } - } - - async detach() { - if (!this._connection) - throw new Error(`Session already detached. Most likely the ${this._targetType} has been closed.`); - await this._connection.send('Target.detachFromTarget', {sessionId: this._sessionId}); - } - - _onClosed() { - for (const callback of this._callbacks.values()) - callback.reject(rewriteError(callback.error, `Protocol error (${callback.method}): Target closed.`)); - this._callbacks.clear(); - this._connection = null; - this.emit(Events.CDPSession.Disconnected); - } -} - -/** - * @param {!Error} error - * @param {string} method - * @param {{error: {message: string, data: any}}} object - * @return {!Error} - */ -function createProtocolError(error, method, object) { - let message = `Protocol error (${method}): ${object.error.message}`; - if ('data' in object.error) - message += ` ${object.error.data}`; - return rewriteError(error, message); -} - -/** - * @param {!Error} error - * @param {string} message - * @return {!Error} - */ -function rewriteError(error, message) { - error.message = message; - return error; -} - -exports_puppeteer_puppeteer_lib_Connection = {Connection, CDPSession}; - - -/* -file https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/Coverage.js -*/ -/** - * Copyright 2017 Google Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// const {helper, debugError, assert} = exports_puppeteer_puppeteer_lib_helper; - -// const {EVALUATION_SCRIPT_URL} = exports_puppeteer_puppeteer_lib_ExecutionContext; - -/** - * @typedef {Object} CoverageEntry - * @property {string} url - * @property {string} text - * @property {!Array} ranges - */ - -class Coverage { - /** - * @param {!Puppeteer.CDPSession} client - */ - constructor(client) { - this._jsCoverage = new JSCoverage(client); - this._cssCoverage = new CSSCoverage(client); - } - - /** - * @param {!{resetOnNavigation?: boolean, reportAnonymousScripts?: boolean}} options - */ - async startJSCoverage(options) { - return await this._jsCoverage.start(options); - } - - /** - * @return {!Promise>} - */ - async stopJSCoverage() { - return await this._jsCoverage.stop(); - } - - /** - * @param {{resetOnNavigation?: boolean}=} options - */ - async startCSSCoverage(options) { - return await this._cssCoverage.start(options); - } - - /** - * @return {!Promise>} - */ - async stopCSSCoverage() { - return await this._cssCoverage.stop(); - } -} - -exports_puppeteer_puppeteer_lib_Coverage = {Coverage}; - -class JSCoverage { - /** - * @param {!Puppeteer.CDPSession} client - */ - constructor(client) { - this._client = client; - this._enabled = false; - this._scriptURLs = new Map(); - this._scriptSources = new Map(); - this._eventListeners = []; - this._resetOnNavigation = false; - } - - /** - * @param {!{resetOnNavigation?: boolean, reportAnonymousScripts?: boolean}} options - */ - async start(options = {}) { - assert(!this._enabled, 'JSCoverage is already enabled'); - const { - resetOnNavigation = true, - reportAnonymousScripts = false - } = options; - this._resetOnNavigation = resetOnNavigation; - this._reportAnonymousScripts = reportAnonymousScripts; - this._enabled = true; - this._scriptURLs.clear(); - this._scriptSources.clear(); - this._eventListeners = [ - helper.addEventListener(this._client, 'Debugger.scriptParsed', this._onScriptParsed.bind(this)), - helper.addEventListener(this._client, 'Runtime.executionContextsCleared', this._onExecutionContextsCleared.bind(this)), - ]; - await Promise.all([ - this._client.send('Profiler.enable'), - this._client.send('Profiler.startPreciseCoverage', {callCount: false, detailed: true}), - this._client.send('Debugger.enable'), - this._client.send('Debugger.setSkipAllPauses', {skip: true}) - ]); - } - - _onExecutionContextsCleared() { - if (!this._resetOnNavigation) - return; - this._scriptURLs.clear(); - this._scriptSources.clear(); - } - - /** - * @param {!Protocol.Debugger.scriptParsedPayload} event - */ - async _onScriptParsed(event) { - // Ignore puppeteer-injected scripts - if (event.url === EVALUATION_SCRIPT_URL) - return; - // Ignore other anonymous scripts unless the reportAnonymousScripts option is true. - if (!event.url && !this._reportAnonymousScripts) - return; - try { - const response = await this._client.send('Debugger.getScriptSource', {scriptId: event.scriptId}); - this._scriptURLs.set(event.scriptId, event.url); - this._scriptSources.set(event.scriptId, response.scriptSource); - } catch (e) { - // This might happen if the page has already navigated away. - debugError(e); - } - } - - /** - * @return {!Promise>} - */ - async stop() { - assert(this._enabled, 'JSCoverage is not enabled'); - this._enabled = false; - const [profileResponse] = await Promise.all([ - this._client.send('Profiler.takePreciseCoverage'), - this._client.send('Profiler.stopPreciseCoverage'), - this._client.send('Profiler.disable'), - this._client.send('Debugger.disable'), - ]); - helper.removeEventListeners(this._eventListeners); - - const coverage = []; - for (const entry of profileResponse.result) { - let url = this._scriptURLs.get(entry.scriptId); - if (!url && this._reportAnonymousScripts) - url = 'debugger://VM' + entry.scriptId; - const text = this._scriptSources.get(entry.scriptId); - if (text === undefined || url === undefined) - continue; - const flattenRanges = []; - for (const func of entry.functions) - flattenRanges.push(...func.ranges); - const ranges = convertToDisjointRanges(flattenRanges); - coverage.push({url, ranges, text}); - } - return coverage; - } -} - -class CSSCoverage { - /** - * @param {!Puppeteer.CDPSession} client - */ - constructor(client) { - this._client = client; - this._enabled = false; - this._stylesheetURLs = new Map(); - this._stylesheetSources = new Map(); - this._eventListeners = []; - this._resetOnNavigation = false; - } - - /** - * @param {{resetOnNavigation?: boolean}=} options - */ - async start(options = {}) { - assert(!this._enabled, 'CSSCoverage is already enabled'); - const {resetOnNavigation = true} = options; - this._resetOnNavigation = resetOnNavigation; - this._enabled = true; - this._stylesheetURLs.clear(); - this._stylesheetSources.clear(); - this._eventListeners = [ - helper.addEventListener(this._client, 'CSS.styleSheetAdded', this._onStyleSheet.bind(this)), - helper.addEventListener(this._client, 'Runtime.executionContextsCleared', this._onExecutionContextsCleared.bind(this)), - ]; - await Promise.all([ - this._client.send('DOM.enable'), - this._client.send('CSS.enable'), - this._client.send('CSS.startRuleUsageTracking'), - ]); - } - - _onExecutionContextsCleared() { - if (!this._resetOnNavigation) - return; - this._stylesheetURLs.clear(); - this._stylesheetSources.clear(); - } - - /** - * @param {!Protocol.CSS.styleSheetAddedPayload} event - */ - async _onStyleSheet(event) { - const header = event.header; - // Ignore anonymous scripts - if (!header.sourceURL) - return; - try { - const response = await this._client.send('CSS.getStyleSheetText', {styleSheetId: header.styleSheetId}); - this._stylesheetURLs.set(header.styleSheetId, header.sourceURL); - this._stylesheetSources.set(header.styleSheetId, response.text); - } catch (e) { - // This might happen if the page has already navigated away. - debugError(e); - } - } - - /** - * @return {!Promise>} - */ - async stop() { - assert(this._enabled, 'CSSCoverage is not enabled'); - this._enabled = false; - const ruleTrackingResponse = await this._client.send('CSS.stopRuleUsageTracking'); - await Promise.all([ - this._client.send('CSS.disable'), - this._client.send('DOM.disable'), - ]); - helper.removeEventListeners(this._eventListeners); - - // aggregate by styleSheetId - const styleSheetIdToCoverage = new Map(); - for (const entry of ruleTrackingResponse.ruleUsage) { - let ranges = styleSheetIdToCoverage.get(entry.styleSheetId); - if (!ranges) { - ranges = []; - styleSheetIdToCoverage.set(entry.styleSheetId, ranges); - } - ranges.push({ - startOffset: entry.startOffset, - endOffset: entry.endOffset, - count: entry.used ? 1 : 0, - }); - } - - const coverage = []; - for (const styleSheetId of this._stylesheetURLs.keys()) { - const url = this._stylesheetURLs.get(styleSheetId); - const text = this._stylesheetSources.get(styleSheetId); - const ranges = convertToDisjointRanges(styleSheetIdToCoverage.get(styleSheetId) || []); - coverage.push({url, ranges, text}); - } - - return coverage; - } -} - -/** - * @param {!Array} nestedRanges - * @return {!Array} - */ -function convertToDisjointRanges(nestedRanges) { - const points = []; - for (const range of nestedRanges) { - points.push({ offset: range.startOffset, type: 0, range }); - points.push({ offset: range.endOffset, type: 1, range }); - } - // Sort points to form a valid parenthesis sequence. - points.sort((a, b) => { - // Sort with increasing offsets. - if (a.offset !== b.offset) - return a.offset - b.offset; - // All "end" points should go before "start" points. - if (a.type !== b.type) - return b.type - a.type; - const aLength = a.range.endOffset - a.range.startOffset; - const bLength = b.range.endOffset - b.range.startOffset; - // For two "start" points, the one with longer range goes first. - if (a.type === 0) - return bLength - aLength; - // For two "end" points, the one with shorter range goes first. - return aLength - bLength; - }); - - const hitCountStack = []; - const results = []; - let lastOffset = 0; - // Run scanning line to intersect all ranges. - for (const point of points) { - if (hitCountStack.length && lastOffset < point.offset && hitCountStack[hitCountStack.length - 1] > 0) { - const lastResult = results.length ? results[results.length - 1] : null; - if (lastResult && lastResult.end === lastOffset) - lastResult.end = point.offset; - else - results.push({start: lastOffset, end: point.offset}); - } - lastOffset = point.offset; - if (point.type === 0) - hitCountStack.push(point.range.count); - else - hitCountStack.pop(); - } - // Filter out empty ranges. - return results.filter(range => range.end - range.start > 1); -} - - -/* -file https://github.com/puppeteer/puppeteer/blob/v1.19.0/lib/DOMWorld.js -*/ -/** - * Copyright 2019 Google Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// const fs = require('fs'); -// const {helper, assert} = exports_puppeteer_puppeteer_lib_helper; -// const {LifecycleWatcher} = exports_puppeteer_puppeteer_lib_LifecycleWatcher; -// const {TimeoutError} = exports_puppeteer_puppeteer_lib_Errors; -const readFileAsync = helper.promisify(fs.readFile); - -/** - * @unrestricted - */ -class DOMWorld { - /** - * @param {!Puppeteer.FrameManager} frameManager - * @param {!Puppeteer.Frame} frame - * @param {!Puppeteer.TimeoutSettings} timeoutSettings - */ - constructor(frameManager, frame, timeoutSettings) { - this._frameManager = frameManager; - this._frame = frame; - this._timeoutSettings = timeoutSettings; - - /** @type {?Promise} */ - this._documentPromise = null; - /** @type {!Promise} */ - this._contextPromise; - this._contextResolveCallback = null; - this._setContext(null); - - /** @type {!Set} */ - this._waitTasks = new Set(); - this._detached = false; - } - - /** - * @return {!Puppeteer.Frame} - */ - frame() { - return this._frame; - } - - /** - * @param {?Puppeteer.ExecutionContext} context - */ - _setContext(context) { - if (context) { - this._contextResolveCallback.call(null, context); - this._contextResolveCallback = null; - for (const waitTask of this._waitTasks) - waitTask.rerun(); - } else { - this._documentPromise = null; - this._contextPromise = new Promise(fulfill => { - this._contextResolveCallback = fulfill; - }); - } - } - - /** - * @return {boolean} - */ - _hasContext() { - return !this._contextResolveCallback; - } - - _detach() { - this._detached = true; - for (const waitTask of this._waitTasks) - waitTask.terminate(new Error('waitForFunction failed: frame got detached.')); - } - - /** - * @return {!Promise} - */ - executionContext() { - if (this._detached) - throw new Error(`Execution Context is not available in detached frame "${this._frame.url()}" (are you trying to evaluate?)`); - return this._contextPromise; - } - - /** - * @param {Function|string} pageFunction - * @param {!Array<*>} args - * @return {!Promise} - */ - async evaluateHandle(pageFunction, ...args) { - const context = await this.executionContext(); - return context.evaluateHandle(pageFunction, ...args); - } - - /** - * @param {Function|string} pageFunction - * @param {!Array<*>} args - * @return {!Promise<*>} - */ - async evaluate(pageFunction, ...args) { - const context = await this.executionContext(); - return context.evaluate(pageFunction, ...args); - } - - /** - * @param {string} selector - * @return {!Promise} - */ - async $(selector) { - const document = await this._document(); - const value = await document.$(selector); - return value; - } - - /** - * @return {!Promise} - */ - async _document() { - if (this._documentPromise) - return this._documentPromise; - this._documentPromise = this.executionContext().then(async context => { - const document = await context.evaluateHandle('document'); - return document.asElement(); - }); - return this._documentPromise; - } - - /** - * @param {string} expression - * @return {!Promise>} - */ - async $x(expression) { - const document = await this._document(); - const value = await document.$x(expression); - return value; - } - - /** - * @param {string} selector - * @param {Function|string} pageFunction - * @param {!Array<*>} args - * @return {!Promise<(!Object|undefined)>} - */ - async $eval(selector, pageFunction, ...args) { - const document = await this._document(); - return document.$eval(selector, pageFunction, ...args); - } - - /** - * @param {string} selector - * @param {Function|string} pageFunction - * @param {!Array<*>} args - * @return {!Promise<(!Object|undefined)>} - */ - async $$eval(selector, pageFunction, ...args) { - const document = await this._document(); - const value = await document.$$eval(selector, pageFunction, ...args); - return value; - } - - /** - * @param {string} selector - * @return {!Promise>} - */ - async $$(selector) { - const document = await this._document(); - const value = await document.$$(selector); - return value; - } - - /** - * @return {!Promise} - */ - async content() { - return await this.evaluate(() => { - let retVal = ''; - if (document.doctype) - retVal = new XMLSerializer().serializeToString(document.doctype); - if (document.documentElement) - retVal += document.documentElement.outerHTML; - return retVal; - }); - } - - /** - * @param {string} html - * @param {!{timeout?: number, waitUntil?: string|!Array}=} options - */ - async setContent(html, options = {}) { - const { - waitUntil = ['load'], - timeout = this._timeoutSettings.navigationTimeout(), - } = options; - // We rely upon the fact that document.open() will reset frame lifecycle with "init" - // lifecycle event. @see https://crrev.com/608658 - await this.evaluate(html => { - document.open(); - document.write(html); - document.close(); - }, html); - const watcher = new LifecycleWatcher(this._frameManager, this._frame, waitUntil, timeout); - const error = await Promise.race([ - watcher.timeoutOrTerminationPromise(), - watcher.lifecyclePromise(), - ]); - watcher.dispose(); - if (error) - throw error; - } - - /** - * @param {!{url?: string, path?: string, content?: string, type?: string}} options - * @return {!Promise} - */ - async addScriptTag(options) { - const { - url = null, - path = null, - content = null, - type = '' - } = options; - if (url !== null) { - try { - const context = await this.executionContext(); - return (await context.evaluateHandle(addScriptUrl, url, type)).asElement(); - } catch (error) { - throw new Error(`Loading script from ${url} failed`); - } - } - - if (path !== null) { - let contents = await readFileAsync(path, 'utf8'); - contents += '//# sourceURL=' + path.replace(/\n/g, ''); - const context = await this.executionContext(); - return (await context.evaluateHandle(addScriptContent, contents, type)).asElement(); - } - - if (content !== null) { - const context = await this.executionContext(); - return (await context.evaluateHandle(addScriptContent, content, type)).asElement(); - } - - throw new Error('Provide an object with a `url`, `path` or `content` property'); - - /** - * @param {string} url - * @param {string} type - * @return {!Promise} - */ - async function addScriptUrl(url, type) { - const script = document.createElement('script'); - script.src = url; - if (type) - script.type = type; - const promise = new Promise((res, rej) => { - script.onload = res; - script.onerror = rej; - }); - document.head.appendChild(script); - await promise; - return script; - } - - /** - * @param {string} content - * @param {string} type - * @return {!HTMLElement} - */ - function addScriptContent(content, type = 'text/javascript') { - const script = document.createElement('script'); - script.type = type; - script.text = content; - let error = null; - script.onerror = e => error = e; - document.head.appendChild(script); - if (error) - throw error; - return script; - } - } - - /** - * @param {!{url?: string, path?: string, content?: string}} options - * @return {!Promise} - */ - async addStyleTag(options) { - const { - url = null, - path = null, - content = null - } = options; - if (url !== null) { - try { - const context = await this.executionContext(); - return (await context.evaluateHandle(addStyleUrl, url)).asElement(); - } catch (error) { - throw new Error(`Loading style from ${url} failed`); - } - } - - if (path !== null) { - let contents = await readFileAsync(path, 'utf8'); - contents += '/*# sourceURL=' + path.replace(/\n/g, '') + '*/'; - const context = await this.executionContext(); - return (await context.evaluateHandle(addStyleContent, contents)).asElement(); - } - - if (content !== null) { - const context = await this.executionContext(); - return (await context.evaluateHandle(addStyleContent, content)).asElement(); - } - - throw new Error('Provide an object with a `url`, `path` or `content` property'); - - /** - * @param {string} url - * @return {!Promise} - */ - async function addStyleUrl(url) { - const link = document.createElement('link'); - link.rel = 'stylesheet'; - link.href = url; - const promise = new Promise((res, rej) => { - link.onload = res; - link.onerror = rej; - }); - document.head.appendChild(link); - await promise; - return link; - } - - /** - * @param {string} content - * @return {!Promise} - */ - async function addStyleContent(content) { - const style = document.createElement('style'); - style.type = 'text/css'; - style.appendChild(document.createTextNode(content)); - const promise = new Promise((res, rej) => { - style.onload = res; - style.onerror = rej; - }); - document.head.appendChild(style); - await promise; - return style; - } - } - - /** - * @param {string} selector - * @param {!{delay?: number, button?: "left"|"right"|"middle", clickCount?: number}=} options - */ - async click(selector, options) { - const handle = await this.$(selector); - assert(handle, 'No node found for selector: ' + selector); - await handle.click(options); - await handle.dispose(); - } - - /** - * @param {string} selector - */ - async focus(selector) { - const handle = await this.$(selector); - assert(handle, 'No node found for selector: ' + selector); - await handle.focus(); - await handle.dispose(); - } - - /** - * @param {string} selector - */ - async hover(selector) { - const handle = await this.$(selector); - assert(handle, 'No node found for selector: ' + selector); - await handle.hover(); - await handle.dispose(); - } - - /** - * @param {string} selector - * @param {!Array} values - * @return {!Promise>} - */ - select(selector, ...values){ - for (const value of values) - assert(helper.isString(value), 'Values must be strings. Found value "' + value + '" of type "' + (typeof value) + '"'); - return this.$eval(selector, (element, values) => { - if (element.nodeName.toLowerCase() !== 'select') - throw new Error('Element is not a