dragonfly_chrome_headless 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.travis.yml +12 -0
- data/Gemfile +4 -0
- data/README.md +53 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/dragonfly_chrome_headless.gemspec +27 -0
- data/lib/dragonfly_chrome_headless.rb +9 -0
- data/lib/dragonfly_chrome_headless/plugin.rb +17 -0
- data/lib/dragonfly_chrome_headless/processors/rasterize.rb +33 -0
- data/lib/dragonfly_chrome_headless/version.rb +3 -0
- data/node_modules/.bin/chrome-remote-interface +1 -0
- data/node_modules/.bin/mkdirp +1 -0
- data/node_modules/.bin/rimraf +1 -0
- data/node_modules/@types/core-js/LICENSE +21 -0
- data/node_modules/@types/core-js/README.md +16 -0
- data/node_modules/@types/core-js/index.d.ts +2452 -0
- data/node_modules/@types/core-js/package.json +85 -0
- data/node_modules/@types/mkdirp/README.md +18 -0
- data/node_modules/@types/mkdirp/index.d.ts +14 -0
- data/node_modules/@types/mkdirp/package.json +77 -0
- data/node_modules/@types/mkdirp/types-metadata.json +25 -0
- data/node_modules/@types/node/README.md +16 -0
- data/node_modules/@types/node/index.d.ts +4132 -0
- data/node_modules/@types/node/package.json +84 -0
- data/node_modules/balanced-match/.npmignore +5 -0
- data/node_modules/balanced-match/LICENSE.md +21 -0
- data/node_modules/balanced-match/README.md +91 -0
- data/node_modules/balanced-match/index.js +59 -0
- data/node_modules/balanced-match/package.json +112 -0
- data/node_modules/brace-expansion/README.md +123 -0
- data/node_modules/brace-expansion/index.js +201 -0
- data/node_modules/brace-expansion/package.json +114 -0
- data/node_modules/chrome-launcher/.clang-format +6 -0
- data/node_modules/chrome-launcher/.npmignore +11 -0
- data/node_modules/chrome-launcher/README.md +123 -0
- data/node_modules/chrome-launcher/ask.js +32 -0
- data/node_modules/chrome-launcher/ask.ts +35 -0
- data/node_modules/chrome-launcher/chrome-finder.js +157 -0
- data/node_modules/chrome-launcher/chrome-finder.ts +186 -0
- data/node_modules/chrome-launcher/chrome-launcher.js +245 -0
- data/node_modules/chrome-launcher/chrome-launcher.ts +312 -0
- data/node_modules/chrome-launcher/compiled-check.js +14 -0
- data/node_modules/chrome-launcher/flags.js +27 -0
- data/node_modules/chrome-launcher/flags.ts +26 -0
- data/node_modules/chrome-launcher/index.js +7 -0
- data/node_modules/chrome-launcher/index.ts +1 -0
- data/node_modules/chrome-launcher/manual-chrome-launcher.js +30 -0
- data/node_modules/chrome-launcher/package.json +116 -0
- data/node_modules/chrome-launcher/random-port.js +24 -0
- data/node_modules/chrome-launcher/random-port.ts +23 -0
- data/node_modules/chrome-launcher/tsconfig.json +19 -0
- data/node_modules/chrome-launcher/utils.js +52 -0
- data/node_modules/chrome-launcher/utils.ts +44 -0
- data/node_modules/chrome-launcher/yarn.lock +1486 -0
- data/node_modules/chrome-remote-interface/LICENSE +18 -0
- data/node_modules/chrome-remote-interface/README.md +849 -0
- data/node_modules/chrome-remote-interface/bin/client.js +337 -0
- data/node_modules/chrome-remote-interface/chrome-remote-interface.js +11 -0
- data/node_modules/chrome-remote-interface/index.js +39 -0
- data/node_modules/chrome-remote-interface/lib/api.js +84 -0
- data/node_modules/chrome-remote-interface/lib/chrome.js +307 -0
- data/node_modules/chrome-remote-interface/lib/defaults.js +4 -0
- data/node_modules/chrome-remote-interface/lib/devtools.js +245 -0
- data/node_modules/chrome-remote-interface/lib/external-request.js +28 -0
- data/node_modules/chrome-remote-interface/lib/protocol.json +13780 -0
- data/node_modules/chrome-remote-interface/lib/websocket-wrapper.js +32 -0
- data/node_modules/chrome-remote-interface/package.json +128 -0
- data/node_modules/chrome-remote-interface/webpack.config.js +55 -0
- data/node_modules/commander/Readme.md +195 -0
- data/node_modules/commander/index.js +851 -0
- data/node_modules/commander/package.json +92 -0
- data/node_modules/concat-map/.travis.yml +4 -0
- data/node_modules/concat-map/LICENSE +18 -0
- data/node_modules/concat-map/README.markdown +62 -0
- data/node_modules/concat-map/example/map.js +6 -0
- data/node_modules/concat-map/index.js +13 -0
- data/node_modules/concat-map/package.json +117 -0
- data/node_modules/concat-map/test/map.js +39 -0
- data/node_modules/debug/.coveralls.yml +1 -0
- data/node_modules/debug/.eslintrc +11 -0
- data/node_modules/debug/.npmignore +9 -0
- data/node_modules/debug/.travis.yml +14 -0
- data/node_modules/debug/CHANGELOG.md +357 -0
- data/node_modules/debug/LICENSE +19 -0
- data/node_modules/debug/Makefile +50 -0
- data/node_modules/debug/Readme.md +312 -0
- data/node_modules/debug/component.json +19 -0
- data/node_modules/debug/karma.conf.js +70 -0
- data/node_modules/debug/node.js +1 -0
- data/node_modules/debug/package.json +124 -0
- data/node_modules/debug/src/browser.js +185 -0
- data/node_modules/debug/src/debug.js +202 -0
- data/node_modules/debug/src/index.js +10 -0
- data/node_modules/debug/src/node.js +246 -0
- data/node_modules/fs.realpath/LICENSE +43 -0
- data/node_modules/fs.realpath/README.md +33 -0
- data/node_modules/fs.realpath/index.js +66 -0
- data/node_modules/fs.realpath/old.js +303 -0
- data/node_modules/fs.realpath/package.json +94 -0
- data/node_modules/glob/LICENSE +15 -0
- data/node_modules/glob/README.md +368 -0
- data/node_modules/glob/changelog.md +67 -0
- data/node_modules/glob/common.js +240 -0
- data/node_modules/glob/glob.js +790 -0
- data/node_modules/glob/package.json +112 -0
- data/node_modules/glob/sync.js +486 -0
- data/node_modules/html-pdf-chrome/.npmignore +9 -0
- data/node_modules/html-pdf-chrome/LICENSE +21 -0
- data/node_modules/html-pdf-chrome/README.md +165 -0
- data/node_modules/html-pdf-chrome/lib/src/ChromePrintOptions.d.ts +87 -0
- data/node_modules/html-pdf-chrome/lib/src/ChromePrintOptions.js +4 -0
- data/node_modules/html-pdf-chrome/lib/src/ChromePrintOptions.js.map +1 -0
- data/node_modules/html-pdf-chrome/lib/src/CompletionTrigger.d.ts +120 -0
- data/node_modules/html-pdf-chrome/lib/src/CompletionTrigger.js +206 -0
- data/node_modules/html-pdf-chrome/lib/src/CompletionTrigger.js.map +1 -0
- data/node_modules/html-pdf-chrome/lib/src/CreateResult.d.ts +70 -0
- data/node_modules/html-pdf-chrome/lib/src/CreateResult.js +98 -0
- data/node_modules/html-pdf-chrome/lib/src/CreateResult.js.map +1 -0
- data/node_modules/html-pdf-chrome/lib/src/index.d.ts +72 -0
- data/node_modules/html-pdf-chrome/lib/src/index.js +123 -0
- data/node_modules/html-pdf-chrome/lib/src/index.js.map +1 -0
- data/node_modules/html-pdf-chrome/package.json +133 -0
- data/node_modules/html-pdf-chrome/src/ChromePrintOptions.ts +99 -0
- data/node_modules/html-pdf-chrome/src/CompletionTrigger.ts +201 -0
- data/node_modules/html-pdf-chrome/src/CreateResult.ts +100 -0
- data/node_modules/html-pdf-chrome/src/index.ts +179 -0
- data/node_modules/inflight/LICENSE +15 -0
- data/node_modules/inflight/README.md +37 -0
- data/node_modules/inflight/inflight.js +54 -0
- data/node_modules/inflight/package.json +105 -0
- data/node_modules/inherits/LICENSE +16 -0
- data/node_modules/inherits/README.md +42 -0
- data/node_modules/inherits/inherits.js +7 -0
- data/node_modules/inherits/inherits_browser.js +23 -0
- data/node_modules/inherits/package.json +97 -0
- data/node_modules/lighthouse-logger/README.md +4 -0
- data/node_modules/lighthouse-logger/index.js +212 -0
- data/node_modules/lighthouse-logger/package.json +69 -0
- data/node_modules/lighthouse-logger/yarn.lock +13 -0
- data/node_modules/minimatch/LICENSE +15 -0
- data/node_modules/minimatch/README.md +209 -0
- data/node_modules/minimatch/minimatch.js +923 -0
- data/node_modules/minimatch/package.json +99 -0
- data/node_modules/minimist/.travis.yml +4 -0
- data/node_modules/minimist/LICENSE +18 -0
- data/node_modules/minimist/example/parse.js +2 -0
- data/node_modules/minimist/index.js +187 -0
- data/node_modules/minimist/package.json +101 -0
- data/node_modules/minimist/readme.markdown +73 -0
- data/node_modules/minimist/test/dash.js +24 -0
- data/node_modules/minimist/test/default_bool.js +20 -0
- data/node_modules/minimist/test/dotted.js +16 -0
- data/node_modules/minimist/test/long.js +31 -0
- data/node_modules/minimist/test/parse.js +318 -0
- data/node_modules/minimist/test/parse_modified.js +9 -0
- data/node_modules/minimist/test/short.js +67 -0
- data/node_modules/minimist/test/whitespace.js +8 -0
- data/node_modules/mkdirp/.travis.yml +8 -0
- data/node_modules/mkdirp/LICENSE +21 -0
- data/node_modules/mkdirp/bin/cmd.js +33 -0
- data/node_modules/mkdirp/bin/usage.txt +12 -0
- data/node_modules/mkdirp/examples/pow.js +6 -0
- data/node_modules/mkdirp/index.js +98 -0
- data/node_modules/mkdirp/package.json +93 -0
- data/node_modules/mkdirp/readme.markdown +100 -0
- data/node_modules/mkdirp/test/chmod.js +41 -0
- data/node_modules/mkdirp/test/clobber.js +38 -0
- data/node_modules/mkdirp/test/mkdirp.js +28 -0
- data/node_modules/mkdirp/test/opts_fs.js +29 -0
- data/node_modules/mkdirp/test/opts_fs_sync.js +27 -0
- data/node_modules/mkdirp/test/perm.js +32 -0
- data/node_modules/mkdirp/test/perm_sync.js +36 -0
- data/node_modules/mkdirp/test/race.js +37 -0
- data/node_modules/mkdirp/test/rel.js +32 -0
- data/node_modules/mkdirp/test/return.js +25 -0
- data/node_modules/mkdirp/test/return_sync.js +24 -0
- data/node_modules/mkdirp/test/root.js +19 -0
- data/node_modules/mkdirp/test/sync.js +32 -0
- data/node_modules/mkdirp/test/umask.js +28 -0
- data/node_modules/mkdirp/test/umask_sync.js +32 -0
- data/node_modules/ms/README.md +51 -0
- data/node_modules/ms/index.js +152 -0
- data/node_modules/ms/license.md +21 -0
- data/node_modules/ms/package.json +109 -0
- data/node_modules/once/LICENSE +15 -0
- data/node_modules/once/README.md +79 -0
- data/node_modules/once/once.js +42 -0
- data/node_modules/once/package.json +101 -0
- data/node_modules/path-is-absolute/index.js +20 -0
- data/node_modules/path-is-absolute/license +21 -0
- data/node_modules/path-is-absolute/package.json +111 -0
- data/node_modules/path-is-absolute/readme.md +59 -0
- data/node_modules/rimraf/LICENSE +15 -0
- data/node_modules/rimraf/README.md +101 -0
- data/node_modules/rimraf/bin.js +50 -0
- data/node_modules/rimraf/package.json +99 -0
- data/node_modules/rimraf/rimraf.js +363 -0
- data/node_modules/ultron/LICENSE +22 -0
- data/node_modules/ultron/index.js +138 -0
- data/node_modules/ultron/package.json +112 -0
- data/node_modules/wrappy/LICENSE +15 -0
- data/node_modules/wrappy/README.md +36 -0
- data/node_modules/wrappy/package.json +97 -0
- data/node_modules/wrappy/wrappy.js +33 -0
- data/node_modules/ws/LICENSE +21 -0
- data/node_modules/ws/README.md +259 -0
- data/node_modules/ws/SECURITY.md +33 -0
- data/node_modules/ws/index.js +15 -0
- data/node_modules/ws/lib/BufferUtil.fallback.js +56 -0
- data/node_modules/ws/lib/BufferUtil.js +15 -0
- data/node_modules/ws/lib/ErrorCodes.js +28 -0
- data/node_modules/ws/lib/EventTarget.js +158 -0
- data/node_modules/ws/lib/Extensions.js +69 -0
- data/node_modules/ws/lib/PerMessageDeflate.js +339 -0
- data/node_modules/ws/lib/Receiver.js +520 -0
- data/node_modules/ws/lib/Sender.js +438 -0
- data/node_modules/ws/lib/Validation.fallback.js +9 -0
- data/node_modules/ws/lib/Validation.js +17 -0
- data/node_modules/ws/lib/WebSocket.js +705 -0
- data/node_modules/ws/lib/WebSocketServer.js +336 -0
- data/node_modules/ws/package.json +122 -0
- data/package.json +26 -0
- data/samples/sample.html +13 -0
- data/script/rasterize.js +18 -0
- metadata +325 -0
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const EventEmitter = require('events');
|
|
4
|
+
const util = require('util');
|
|
5
|
+
|
|
6
|
+
const WebSocket = require('ws');
|
|
7
|
+
|
|
8
|
+
const api = require('./api');
|
|
9
|
+
const defaults = require('./defaults');
|
|
10
|
+
const devtools = require('./devtools');
|
|
11
|
+
|
|
12
|
+
class ProtocolError extends Error {
|
|
13
|
+
constructor(response) {
|
|
14
|
+
let message = response.message;
|
|
15
|
+
if (response.data) {
|
|
16
|
+
message += ` (${response.data})`;
|
|
17
|
+
}
|
|
18
|
+
super(message);
|
|
19
|
+
// attach the original response as well
|
|
20
|
+
this.response = response;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
class Chrome extends EventEmitter {
|
|
25
|
+
constructor(options, notifier) {
|
|
26
|
+
super();
|
|
27
|
+
// options
|
|
28
|
+
const defaultTarget = function (targets) {
|
|
29
|
+
// prefer type = 'page' inspectabe targets as they represents
|
|
30
|
+
// browser tabs (fall back to the first instectable target
|
|
31
|
+
// otherwise)
|
|
32
|
+
let backup;
|
|
33
|
+
let target = targets.find((target) => {
|
|
34
|
+
if (target.webSocketDebuggerUrl) {
|
|
35
|
+
backup = backup || target;
|
|
36
|
+
return target.type === 'page';
|
|
37
|
+
} else {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
target = target || backup;
|
|
42
|
+
if (target) {
|
|
43
|
+
return target;
|
|
44
|
+
} else {
|
|
45
|
+
throw new Error('No inspectable targets');
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
options = options || {};
|
|
49
|
+
this.host = options.host || defaults.HOST;
|
|
50
|
+
this.port = options.port || defaults.PORT;
|
|
51
|
+
this.secure = !!(options.secure);
|
|
52
|
+
this.protocol = options.protocol;
|
|
53
|
+
this.remote = !!(options.remote);
|
|
54
|
+
this.target = options.target ||
|
|
55
|
+
/* backward compatibility */ options.tab || options.chooseTab
|
|
56
|
+
|| defaultTarget;
|
|
57
|
+
// locals
|
|
58
|
+
EventEmitter.call(this);
|
|
59
|
+
this._notifier = notifier;
|
|
60
|
+
this._callbacks = {};
|
|
61
|
+
this._nextCommandId = 1;
|
|
62
|
+
// properties
|
|
63
|
+
this.webSocketUrl = undefined;
|
|
64
|
+
// operations
|
|
65
|
+
start.call(this);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// avoid misinterpreting protocol's members as custom util.inspect functions
|
|
70
|
+
Chrome.prototype.inspect = function (depth, options) {
|
|
71
|
+
options.customInspect = false;
|
|
72
|
+
return util.inspect(this, options);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
Chrome.prototype.send = function (method, params, callback) {
|
|
76
|
+
const chrome = this;
|
|
77
|
+
if (typeof params === 'function') {
|
|
78
|
+
callback = params;
|
|
79
|
+
params = undefined;
|
|
80
|
+
}
|
|
81
|
+
// return a promise when a callback is not provided
|
|
82
|
+
if (typeof callback === 'function') {
|
|
83
|
+
enqueueCommand.call(chrome, method, params, callback);
|
|
84
|
+
} else {
|
|
85
|
+
return new Promise(function (fulfill, reject) {
|
|
86
|
+
enqueueCommand.call(chrome, method, params, function (error, response) {
|
|
87
|
+
if (error) {
|
|
88
|
+
reject(error instanceof Error
|
|
89
|
+
? error // low-level WebSocket error
|
|
90
|
+
: new ProtocolError(response));
|
|
91
|
+
} else {
|
|
92
|
+
fulfill(response);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
Chrome.prototype.close = function (callback) {
|
|
100
|
+
const chrome = this;
|
|
101
|
+
function closeWebSocket(callback) {
|
|
102
|
+
// don't notify on user-initiated shutdown ('disconnect' event)
|
|
103
|
+
chrome._ws.removeAllListeners('close');
|
|
104
|
+
chrome._ws.close();
|
|
105
|
+
chrome._ws.once('close', function () {
|
|
106
|
+
chrome._ws.removeAllListeners();
|
|
107
|
+
callback();
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
if (typeof callback === 'function') {
|
|
111
|
+
closeWebSocket(callback);
|
|
112
|
+
} else {
|
|
113
|
+
return new Promise(function (fulfill, reject) {
|
|
114
|
+
closeWebSocket(fulfill);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// send a command to the remote endpoint and register a callback for the reply
|
|
120
|
+
function enqueueCommand(method, params, callback) {
|
|
121
|
+
const chrome = this;
|
|
122
|
+
const id = chrome._nextCommandId++;
|
|
123
|
+
const message = {'id': id, 'method': method, 'params': params || {}};
|
|
124
|
+
chrome._ws.send(JSON.stringify(message), function (err) {
|
|
125
|
+
if (err) {
|
|
126
|
+
// handle low-level WebSocket errors
|
|
127
|
+
if (typeof callback === 'function') {
|
|
128
|
+
callback(err);
|
|
129
|
+
}
|
|
130
|
+
} else {
|
|
131
|
+
chrome._callbacks[id] = callback;
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// initiate the connection process
|
|
137
|
+
function start() {
|
|
138
|
+
const chrome = this;
|
|
139
|
+
const options = {'host': chrome.host, 'port': chrome.port, 'secure': chrome.secure};
|
|
140
|
+
Promise.all([
|
|
141
|
+
// fetch the protocol and prepare the API
|
|
142
|
+
fetchProtocol.call(chrome, options).then(api.prepare.bind(chrome)),
|
|
143
|
+
// in the meanwhile fetch the WebSocket debugger URL
|
|
144
|
+
fetchDebuggerURL.call(chrome, options)
|
|
145
|
+
]).then(function (values) {
|
|
146
|
+
// finally connect to the WebSocket
|
|
147
|
+
const url = values[1];
|
|
148
|
+
return connectToWebSocket.call(chrome, url);
|
|
149
|
+
}).then(function () {
|
|
150
|
+
// since the handler is executed synchronously, the emit() must be
|
|
151
|
+
// performed in the next tick so that uncaught errors in the client code
|
|
152
|
+
// are not intercepted by the Promise mechanism and therefore reported
|
|
153
|
+
// via the 'error' event
|
|
154
|
+
process.nextTick(function () {
|
|
155
|
+
chrome._notifier.emit('connect', chrome);
|
|
156
|
+
});
|
|
157
|
+
}).catch(function (err) {
|
|
158
|
+
chrome._notifier.emit('error', err);
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// fetch the protocol according to 'protocol' and 'remote'
|
|
163
|
+
function fetchProtocol(options) {
|
|
164
|
+
const chrome = this;
|
|
165
|
+
return new Promise(function (fulfill, reject) {
|
|
166
|
+
// if a protocol has been provided then use it
|
|
167
|
+
if (chrome.protocol) {
|
|
168
|
+
fulfill(chrome.protocol);
|
|
169
|
+
}
|
|
170
|
+
// otherwise user either the local or the remote version
|
|
171
|
+
else {
|
|
172
|
+
options.remote = chrome.remote;
|
|
173
|
+
devtools.Protocol(options).then(function (protocol) {
|
|
174
|
+
fulfill(protocol.descriptor);
|
|
175
|
+
}).catch(reject);
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// extract the debugger URL from a target-like object
|
|
181
|
+
function fetchFromObject(fulfill, reject, target) {
|
|
182
|
+
const url = (target || {}).webSocketDebuggerUrl;
|
|
183
|
+
if (url) {
|
|
184
|
+
fulfill(url);
|
|
185
|
+
} else {
|
|
186
|
+
const targetStr = JSON.stringify(target, null, 4);
|
|
187
|
+
const err = new Error('Invalid target ' + targetStr);
|
|
188
|
+
reject(err);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// fetch the WebSocket URL according to 'target'
|
|
193
|
+
function fetchDebuggerURL(options) {
|
|
194
|
+
const chrome = this;
|
|
195
|
+
return new Promise(function (fulfill, reject) {
|
|
196
|
+
// note: when DevTools are open or another WebSocket is connected to a
|
|
197
|
+
// given target the 'webSocketDebuggerUrl' field is not available
|
|
198
|
+
let userTarget = chrome.target;
|
|
199
|
+
switch (typeof userTarget) {
|
|
200
|
+
case 'string':
|
|
201
|
+
// use default host and port if omitted (and a relative URL is specified)
|
|
202
|
+
if (userTarget.startsWith('/')) {
|
|
203
|
+
const prefix = 'ws://' + chrome.host + ':' + chrome.port;
|
|
204
|
+
userTarget = prefix + userTarget;
|
|
205
|
+
}
|
|
206
|
+
// a WebSocket URL is specified by the user (e.g., node-inspector)
|
|
207
|
+
if (userTarget.match(/^wss?:/i)) {
|
|
208
|
+
fulfill(userTarget);
|
|
209
|
+
}
|
|
210
|
+
// a target id is specified by the user
|
|
211
|
+
else {
|
|
212
|
+
devtools.List(options).then(function (targets) {
|
|
213
|
+
return targets.find(function (target) {
|
|
214
|
+
return target.id === userTarget;
|
|
215
|
+
});
|
|
216
|
+
}).then(function (target) {
|
|
217
|
+
fetchFromObject(fulfill, reject, target);
|
|
218
|
+
}).catch(reject);
|
|
219
|
+
}
|
|
220
|
+
break;
|
|
221
|
+
case 'object':
|
|
222
|
+
// a target object is specified by the user
|
|
223
|
+
fetchFromObject(fulfill, reject, userTarget);
|
|
224
|
+
break;
|
|
225
|
+
case 'function':
|
|
226
|
+
// a function is specified by the user
|
|
227
|
+
devtools.List(options).then(function (targets) {
|
|
228
|
+
const result = userTarget(targets);
|
|
229
|
+
if (typeof result === 'number') {
|
|
230
|
+
return targets[result];
|
|
231
|
+
} else {
|
|
232
|
+
return result;
|
|
233
|
+
}
|
|
234
|
+
}).then(function (target) {
|
|
235
|
+
fetchFromObject(fulfill, reject, target);
|
|
236
|
+
}).catch(reject);
|
|
237
|
+
break;
|
|
238
|
+
default:
|
|
239
|
+
reject(new Error('Invalid target argument "' + chrome.target + '"'));
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// establish the WebSocket connection and start processing user commands
|
|
245
|
+
function connectToWebSocket(url) {
|
|
246
|
+
const chrome = this;
|
|
247
|
+
return new Promise(function (fulfill, reject) {
|
|
248
|
+
// create the WebSocket
|
|
249
|
+
try {
|
|
250
|
+
if (chrome.secure) {
|
|
251
|
+
url = url.replace(/^ws:/i, 'wss:');
|
|
252
|
+
}
|
|
253
|
+
chrome.webSocketUrl = url;
|
|
254
|
+
chrome._ws = new WebSocket(url);
|
|
255
|
+
} catch (err) {
|
|
256
|
+
// handles bad URLs
|
|
257
|
+
reject(err);
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
// set up event handlers
|
|
261
|
+
chrome._ws.on('open', function () {
|
|
262
|
+
fulfill();
|
|
263
|
+
});
|
|
264
|
+
chrome._ws.on('message', function (data) {
|
|
265
|
+
const message = JSON.parse(data);
|
|
266
|
+
handleMessage.call(chrome, message);
|
|
267
|
+
});
|
|
268
|
+
chrome._ws.on('close', function (code) {
|
|
269
|
+
chrome.emit('disconnect');
|
|
270
|
+
});
|
|
271
|
+
chrome._ws.on('error', function (err) {
|
|
272
|
+
reject(err);
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// handle the messages read from the WebSocket
|
|
278
|
+
function handleMessage(message) {
|
|
279
|
+
const chrome = this;
|
|
280
|
+
// command response
|
|
281
|
+
if (message.id) {
|
|
282
|
+
const callback = chrome._callbacks[message.id];
|
|
283
|
+
if (!callback) {
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
// interpret the lack of both 'error' and 'result' as success
|
|
287
|
+
// (this may happen with node-inspector)
|
|
288
|
+
if (message.error) {
|
|
289
|
+
callback(true, message.error);
|
|
290
|
+
} else {
|
|
291
|
+
callback(false, message.result || {});
|
|
292
|
+
}
|
|
293
|
+
// unregister command response callback
|
|
294
|
+
delete chrome._callbacks[message.id];
|
|
295
|
+
// notify when there are no more pending commands
|
|
296
|
+
if (Object.keys(chrome._callbacks).length === 0) {
|
|
297
|
+
chrome.emit('ready');
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
// event
|
|
301
|
+
else if (message.method) {
|
|
302
|
+
chrome.emit('event', message);
|
|
303
|
+
chrome.emit(message.method, message.params);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
module.exports = Chrome;
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const http = require('http');
|
|
4
|
+
const https = require('https');
|
|
5
|
+
|
|
6
|
+
const defaults = require('./defaults');
|
|
7
|
+
const externalRequest = require('./external-request');
|
|
8
|
+
|
|
9
|
+
// callback(err, protocol)
|
|
10
|
+
module.exports.Protocol = promisesWrapper(function (options, callback) {
|
|
11
|
+
// if the local protocol is requested
|
|
12
|
+
if (!options.remote) {
|
|
13
|
+
const localDescriptor = require('./protocol.json');
|
|
14
|
+
callback(null, {
|
|
15
|
+
'remote': false,
|
|
16
|
+
'descriptor': localDescriptor
|
|
17
|
+
});
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
// try to fecth the browser version information and the protocol (remotely)
|
|
21
|
+
module.exports.Version(options, function (err, info) {
|
|
22
|
+
if (err) {
|
|
23
|
+
callback(err);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
// fetch the reported browser info (Node.js returns an array)
|
|
27
|
+
const browser = (info[0] || info).Browser;
|
|
28
|
+
// use the proper protocol fetcher
|
|
29
|
+
let fetcher;
|
|
30
|
+
if (browser.match(/^(Headless)?Chrome\//)) {
|
|
31
|
+
// https://bugs.chromium.org/p/chromium/issues/detail?id=538300#c10
|
|
32
|
+
const firstJsonProtocolVersion = '60.0.3097.0';
|
|
33
|
+
const firstJsonProtocolBuild = explodeChromeVersion(firstJsonProtocolVersion)[2];
|
|
34
|
+
const chromeBuild = explodeChromeVersion(info.Browser.split('/')[1])[2];
|
|
35
|
+
if (chromeBuild < firstJsonProtocolBuild) {
|
|
36
|
+
fetcher = fetchFromChromeRepo;
|
|
37
|
+
} else {
|
|
38
|
+
fetcher = fetchFromHttpEndpoint;
|
|
39
|
+
}
|
|
40
|
+
} else if (browser.match(/^Microsoft Edge /)) {
|
|
41
|
+
fetcher = fetchFromHttpEndpoint;
|
|
42
|
+
} else if (browser.match(/^node.js\//)) {
|
|
43
|
+
fetcher = fetchFromHttpEndpoint;
|
|
44
|
+
} else {
|
|
45
|
+
callback(new Error('Unknown implementation'));
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
fetcher(options, info, function (err, descriptor) {
|
|
49
|
+
if (err) {
|
|
50
|
+
callback(err);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
// use the remotely fetched descriptor
|
|
54
|
+
callback(null, {
|
|
55
|
+
'remote': true,
|
|
56
|
+
'descriptor': descriptor
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
module.exports.List = promisesWrapper(function (options, callback) {
|
|
63
|
+
options.path = '/json/list';
|
|
64
|
+
devToolsInterface(options, function (err, tabs) {
|
|
65
|
+
if (err) {
|
|
66
|
+
callback(err);
|
|
67
|
+
} else {
|
|
68
|
+
callback(null, JSON.parse(tabs));
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
module.exports.New = promisesWrapper(function (options, callback) {
|
|
74
|
+
options.path = '/json/new';
|
|
75
|
+
if (Object.prototype.hasOwnProperty.call(options, 'url')) {
|
|
76
|
+
options.path += '?' + options.url;
|
|
77
|
+
}
|
|
78
|
+
devToolsInterface(options, function (err, tab) {
|
|
79
|
+
if (err) {
|
|
80
|
+
callback(err);
|
|
81
|
+
} else {
|
|
82
|
+
callback(null, JSON.parse(tab));
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
module.exports.Activate = promisesWrapper(function (options, callback) {
|
|
88
|
+
options.path = '/json/activate/' + options.id;
|
|
89
|
+
devToolsInterface(options, function (err) {
|
|
90
|
+
if (err) {
|
|
91
|
+
callback(err);
|
|
92
|
+
} else {
|
|
93
|
+
callback(null);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
module.exports.Close = promisesWrapper(function (options, callback) {
|
|
99
|
+
options.path = '/json/close/' + options.id;
|
|
100
|
+
devToolsInterface(options, function (err) {
|
|
101
|
+
if (err) {
|
|
102
|
+
callback(err);
|
|
103
|
+
} else {
|
|
104
|
+
callback(null);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
module.exports.Version = promisesWrapper(function (options, callback) {
|
|
110
|
+
options.path = '/json/version';
|
|
111
|
+
devToolsInterface(options, function (err, versionInfo) {
|
|
112
|
+
if (err) {
|
|
113
|
+
callback(err);
|
|
114
|
+
} else {
|
|
115
|
+
callback(null, JSON.parse(versionInfo));
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// options.path must be specified; callback(err, data)
|
|
121
|
+
function devToolsInterface(options, callback) {
|
|
122
|
+
options.host = options.host || defaults.HOST;
|
|
123
|
+
options.port = options.port || defaults.PORT;
|
|
124
|
+
options.secure = !!(options.secure);
|
|
125
|
+
externalRequest(options.secure ? https : http, options, callback);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// wrapper that allows to return a promise if the callback is omitted, it works
|
|
129
|
+
// for DevTools methods
|
|
130
|
+
function promisesWrapper(func) {
|
|
131
|
+
return function (options, callback) {
|
|
132
|
+
// options is an optional argument
|
|
133
|
+
if (typeof options === 'function') {
|
|
134
|
+
callback = options;
|
|
135
|
+
options = undefined;
|
|
136
|
+
}
|
|
137
|
+
options = options || {};
|
|
138
|
+
// just call the function otherwise wrap a promise around its execution
|
|
139
|
+
if (typeof callback === 'function') {
|
|
140
|
+
func(options, callback);
|
|
141
|
+
} else {
|
|
142
|
+
return new Promise(function (fulfill, reject) {
|
|
143
|
+
func(options, function (err, result) {
|
|
144
|
+
if (err) {
|
|
145
|
+
reject(err);
|
|
146
|
+
} else {
|
|
147
|
+
fulfill(result);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function explodeChromeVersion(v) {
|
|
156
|
+
return v.split('.').map(function (x) {
|
|
157
|
+
return parseInt(x);
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// callback(err, descriptor)
|
|
162
|
+
// XXX this function needs a proper refactor but the inconsistency of the
|
|
163
|
+
// fetching process makes it useless for now
|
|
164
|
+
function fetchFromChromeRepo(options, info, callback) {
|
|
165
|
+
// attempt to fetch the protocol directly from the Chromium repository
|
|
166
|
+
// according to the current version
|
|
167
|
+
//
|
|
168
|
+
// Thanks to Paul Irish.
|
|
169
|
+
// (see https://github.com/cyrus-and/chrome-remote-interface/issues/10#issuecomment-146032907)
|
|
170
|
+
const webKitVersion = info['WebKit-Version'];
|
|
171
|
+
const v8Version = info['V8-Version'];
|
|
172
|
+
const match = webKitVersion.match(/\s\(@(\b[0-9a-f]{5,40}\b)/);
|
|
173
|
+
const hash = match[1];
|
|
174
|
+
const fromChromiumDotOrg = (hash <= 202666);
|
|
175
|
+
let urls;
|
|
176
|
+
if (fromChromiumDotOrg) {
|
|
177
|
+
urls = [`https://src.chromium.org/blink/trunk/Source/devtools/protocol.json?p=${hash}`];
|
|
178
|
+
} else {
|
|
179
|
+
const lastBeforeSplitChromeVersion = '53.0.2758.1'; // before the split (https://crbug.com/580337)
|
|
180
|
+
const lastBeforeV8ChromeVersion = '55.0.2854.3'; // before using the JSON from the V8 repo
|
|
181
|
+
const chromeVersion = explodeChromeVersion(info.Browser.split('/')[1]);
|
|
182
|
+
// according to https://www.chromium.org/developers/version-numbers (patch not meaningful)
|
|
183
|
+
const beforeSplit = (chromeVersion[2] <= explodeChromeVersion(lastBeforeSplitChromeVersion)[2]);
|
|
184
|
+
const beforeFromV8 = (chromeVersion[2] <= explodeChromeVersion(lastBeforeV8ChromeVersion)[2]);
|
|
185
|
+
if (beforeSplit) {
|
|
186
|
+
urls = [`https://chromium.googlesource.com/chromium/src/+/${hash}/third_party/WebKit/Source/devtools/protocol.json?format=TEXT`];
|
|
187
|
+
} else if (beforeFromV8) {
|
|
188
|
+
urls = [`https://chromium.googlesource.com/chromium/src/+/${hash}/third_party/WebKit/Source/core/inspector/browser_protocol.json?format=TEXT`,
|
|
189
|
+
`https://chromium.googlesource.com/chromium/src/+/${hash}/third_party/WebKit/Source/platform/v8_inspector/js_protocol.json?format=TEXT`];
|
|
190
|
+
} else if (v8Version) {
|
|
191
|
+
urls = [`https://chromium.googlesource.com/chromium/src/+/${hash}/third_party/WebKit/Source/core/inspector/browser_protocol.json?format=TEXT`,
|
|
192
|
+
`https://chromium.googlesource.com/v8/v8/+/${v8Version}/src/inspector/js_protocol.json?format=TEXT`];
|
|
193
|
+
} else {
|
|
194
|
+
console.error('Warning: the protocol might be outdated, see: https://groups.google.com/d/topic/chrome-debugging-protocol/HjyOKainKus/discussion');
|
|
195
|
+
// releases which do not provide a V8 version get an old version of the V8 protocol
|
|
196
|
+
urls = [`https://chromium.googlesource.com/chromium/src/+/${hash}/third_party/WebKit/Source/core/inspector/browser_protocol.json?format=TEXT`,
|
|
197
|
+
`https://chromium.googlesource.com/chromium/src/+/${lastBeforeV8ChromeVersion}/third_party/WebKit/Source/platform/v8_inspector/js_protocol.json?format=TEXT`];
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
const descriptors = [];
|
|
201
|
+
urls.forEach(function (url) {
|
|
202
|
+
externalRequest(https, url, function (err, data) {
|
|
203
|
+
let descriptor;
|
|
204
|
+
if (!err) {
|
|
205
|
+
try {
|
|
206
|
+
// the file is served base64 encoded from googlesource.com
|
|
207
|
+
if (!fromChromiumDotOrg) {
|
|
208
|
+
data = new Buffer(data, 'base64').toString();
|
|
209
|
+
}
|
|
210
|
+
descriptor = JSON.parse(data);
|
|
211
|
+
} catch (_) {
|
|
212
|
+
// abort later
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
descriptors.push(descriptor);
|
|
216
|
+
if (descriptors.length === urls.length) {
|
|
217
|
+
// all must be defined
|
|
218
|
+
if (descriptors.indexOf(undefined) !== -1) {
|
|
219
|
+
callback(new Error('Cannot fetch from Chromium repo'));
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
// merge the domains
|
|
223
|
+
descriptors.forEach(function (descriptor, i) {
|
|
224
|
+
if (i === 0) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
Array.prototype.push.apply(descriptors[0].domains, descriptor.domains);
|
|
228
|
+
});
|
|
229
|
+
callback(null, descriptors[0]);
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// callback(err, descriptor)
|
|
236
|
+
function fetchFromHttpEndpoint(options, info, callback) {
|
|
237
|
+
options.path = '/json/protocol';
|
|
238
|
+
devToolsInterface(options, function (err, descriptor) {
|
|
239
|
+
if (err) {
|
|
240
|
+
callback(err);
|
|
241
|
+
} else {
|
|
242
|
+
callback(null, JSON.parse(descriptor));
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
}
|