dragonfly_chrome_headless 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (228) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.travis.yml +12 -0
  4. data/Gemfile +4 -0
  5. data/README.md +53 -0
  6. data/Rakefile +10 -0
  7. data/bin/console +14 -0
  8. data/bin/setup +8 -0
  9. data/dragonfly_chrome_headless.gemspec +27 -0
  10. data/lib/dragonfly_chrome_headless.rb +9 -0
  11. data/lib/dragonfly_chrome_headless/plugin.rb +17 -0
  12. data/lib/dragonfly_chrome_headless/processors/rasterize.rb +33 -0
  13. data/lib/dragonfly_chrome_headless/version.rb +3 -0
  14. data/node_modules/.bin/chrome-remote-interface +1 -0
  15. data/node_modules/.bin/mkdirp +1 -0
  16. data/node_modules/.bin/rimraf +1 -0
  17. data/node_modules/@types/core-js/LICENSE +21 -0
  18. data/node_modules/@types/core-js/README.md +16 -0
  19. data/node_modules/@types/core-js/index.d.ts +2452 -0
  20. data/node_modules/@types/core-js/package.json +85 -0
  21. data/node_modules/@types/mkdirp/README.md +18 -0
  22. data/node_modules/@types/mkdirp/index.d.ts +14 -0
  23. data/node_modules/@types/mkdirp/package.json +77 -0
  24. data/node_modules/@types/mkdirp/types-metadata.json +25 -0
  25. data/node_modules/@types/node/README.md +16 -0
  26. data/node_modules/@types/node/index.d.ts +4132 -0
  27. data/node_modules/@types/node/package.json +84 -0
  28. data/node_modules/balanced-match/.npmignore +5 -0
  29. data/node_modules/balanced-match/LICENSE.md +21 -0
  30. data/node_modules/balanced-match/README.md +91 -0
  31. data/node_modules/balanced-match/index.js +59 -0
  32. data/node_modules/balanced-match/package.json +112 -0
  33. data/node_modules/brace-expansion/README.md +123 -0
  34. data/node_modules/brace-expansion/index.js +201 -0
  35. data/node_modules/brace-expansion/package.json +114 -0
  36. data/node_modules/chrome-launcher/.clang-format +6 -0
  37. data/node_modules/chrome-launcher/.npmignore +11 -0
  38. data/node_modules/chrome-launcher/README.md +123 -0
  39. data/node_modules/chrome-launcher/ask.js +32 -0
  40. data/node_modules/chrome-launcher/ask.ts +35 -0
  41. data/node_modules/chrome-launcher/chrome-finder.js +157 -0
  42. data/node_modules/chrome-launcher/chrome-finder.ts +186 -0
  43. data/node_modules/chrome-launcher/chrome-launcher.js +245 -0
  44. data/node_modules/chrome-launcher/chrome-launcher.ts +312 -0
  45. data/node_modules/chrome-launcher/compiled-check.js +14 -0
  46. data/node_modules/chrome-launcher/flags.js +27 -0
  47. data/node_modules/chrome-launcher/flags.ts +26 -0
  48. data/node_modules/chrome-launcher/index.js +7 -0
  49. data/node_modules/chrome-launcher/index.ts +1 -0
  50. data/node_modules/chrome-launcher/manual-chrome-launcher.js +30 -0
  51. data/node_modules/chrome-launcher/package.json +116 -0
  52. data/node_modules/chrome-launcher/random-port.js +24 -0
  53. data/node_modules/chrome-launcher/random-port.ts +23 -0
  54. data/node_modules/chrome-launcher/tsconfig.json +19 -0
  55. data/node_modules/chrome-launcher/utils.js +52 -0
  56. data/node_modules/chrome-launcher/utils.ts +44 -0
  57. data/node_modules/chrome-launcher/yarn.lock +1486 -0
  58. data/node_modules/chrome-remote-interface/LICENSE +18 -0
  59. data/node_modules/chrome-remote-interface/README.md +849 -0
  60. data/node_modules/chrome-remote-interface/bin/client.js +337 -0
  61. data/node_modules/chrome-remote-interface/chrome-remote-interface.js +11 -0
  62. data/node_modules/chrome-remote-interface/index.js +39 -0
  63. data/node_modules/chrome-remote-interface/lib/api.js +84 -0
  64. data/node_modules/chrome-remote-interface/lib/chrome.js +307 -0
  65. data/node_modules/chrome-remote-interface/lib/defaults.js +4 -0
  66. data/node_modules/chrome-remote-interface/lib/devtools.js +245 -0
  67. data/node_modules/chrome-remote-interface/lib/external-request.js +28 -0
  68. data/node_modules/chrome-remote-interface/lib/protocol.json +13780 -0
  69. data/node_modules/chrome-remote-interface/lib/websocket-wrapper.js +32 -0
  70. data/node_modules/chrome-remote-interface/package.json +128 -0
  71. data/node_modules/chrome-remote-interface/webpack.config.js +55 -0
  72. data/node_modules/commander/Readme.md +195 -0
  73. data/node_modules/commander/index.js +851 -0
  74. data/node_modules/commander/package.json +92 -0
  75. data/node_modules/concat-map/.travis.yml +4 -0
  76. data/node_modules/concat-map/LICENSE +18 -0
  77. data/node_modules/concat-map/README.markdown +62 -0
  78. data/node_modules/concat-map/example/map.js +6 -0
  79. data/node_modules/concat-map/index.js +13 -0
  80. data/node_modules/concat-map/package.json +117 -0
  81. data/node_modules/concat-map/test/map.js +39 -0
  82. data/node_modules/debug/.coveralls.yml +1 -0
  83. data/node_modules/debug/.eslintrc +11 -0
  84. data/node_modules/debug/.npmignore +9 -0
  85. data/node_modules/debug/.travis.yml +14 -0
  86. data/node_modules/debug/CHANGELOG.md +357 -0
  87. data/node_modules/debug/LICENSE +19 -0
  88. data/node_modules/debug/Makefile +50 -0
  89. data/node_modules/debug/Readme.md +312 -0
  90. data/node_modules/debug/component.json +19 -0
  91. data/node_modules/debug/karma.conf.js +70 -0
  92. data/node_modules/debug/node.js +1 -0
  93. data/node_modules/debug/package.json +124 -0
  94. data/node_modules/debug/src/browser.js +185 -0
  95. data/node_modules/debug/src/debug.js +202 -0
  96. data/node_modules/debug/src/index.js +10 -0
  97. data/node_modules/debug/src/node.js +246 -0
  98. data/node_modules/fs.realpath/LICENSE +43 -0
  99. data/node_modules/fs.realpath/README.md +33 -0
  100. data/node_modules/fs.realpath/index.js +66 -0
  101. data/node_modules/fs.realpath/old.js +303 -0
  102. data/node_modules/fs.realpath/package.json +94 -0
  103. data/node_modules/glob/LICENSE +15 -0
  104. data/node_modules/glob/README.md +368 -0
  105. data/node_modules/glob/changelog.md +67 -0
  106. data/node_modules/glob/common.js +240 -0
  107. data/node_modules/glob/glob.js +790 -0
  108. data/node_modules/glob/package.json +112 -0
  109. data/node_modules/glob/sync.js +486 -0
  110. data/node_modules/html-pdf-chrome/.npmignore +9 -0
  111. data/node_modules/html-pdf-chrome/LICENSE +21 -0
  112. data/node_modules/html-pdf-chrome/README.md +165 -0
  113. data/node_modules/html-pdf-chrome/lib/src/ChromePrintOptions.d.ts +87 -0
  114. data/node_modules/html-pdf-chrome/lib/src/ChromePrintOptions.js +4 -0
  115. data/node_modules/html-pdf-chrome/lib/src/ChromePrintOptions.js.map +1 -0
  116. data/node_modules/html-pdf-chrome/lib/src/CompletionTrigger.d.ts +120 -0
  117. data/node_modules/html-pdf-chrome/lib/src/CompletionTrigger.js +206 -0
  118. data/node_modules/html-pdf-chrome/lib/src/CompletionTrigger.js.map +1 -0
  119. data/node_modules/html-pdf-chrome/lib/src/CreateResult.d.ts +70 -0
  120. data/node_modules/html-pdf-chrome/lib/src/CreateResult.js +98 -0
  121. data/node_modules/html-pdf-chrome/lib/src/CreateResult.js.map +1 -0
  122. data/node_modules/html-pdf-chrome/lib/src/index.d.ts +72 -0
  123. data/node_modules/html-pdf-chrome/lib/src/index.js +123 -0
  124. data/node_modules/html-pdf-chrome/lib/src/index.js.map +1 -0
  125. data/node_modules/html-pdf-chrome/package.json +133 -0
  126. data/node_modules/html-pdf-chrome/src/ChromePrintOptions.ts +99 -0
  127. data/node_modules/html-pdf-chrome/src/CompletionTrigger.ts +201 -0
  128. data/node_modules/html-pdf-chrome/src/CreateResult.ts +100 -0
  129. data/node_modules/html-pdf-chrome/src/index.ts +179 -0
  130. data/node_modules/inflight/LICENSE +15 -0
  131. data/node_modules/inflight/README.md +37 -0
  132. data/node_modules/inflight/inflight.js +54 -0
  133. data/node_modules/inflight/package.json +105 -0
  134. data/node_modules/inherits/LICENSE +16 -0
  135. data/node_modules/inherits/README.md +42 -0
  136. data/node_modules/inherits/inherits.js +7 -0
  137. data/node_modules/inherits/inherits_browser.js +23 -0
  138. data/node_modules/inherits/package.json +97 -0
  139. data/node_modules/lighthouse-logger/README.md +4 -0
  140. data/node_modules/lighthouse-logger/index.js +212 -0
  141. data/node_modules/lighthouse-logger/package.json +69 -0
  142. data/node_modules/lighthouse-logger/yarn.lock +13 -0
  143. data/node_modules/minimatch/LICENSE +15 -0
  144. data/node_modules/minimatch/README.md +209 -0
  145. data/node_modules/minimatch/minimatch.js +923 -0
  146. data/node_modules/minimatch/package.json +99 -0
  147. data/node_modules/minimist/.travis.yml +4 -0
  148. data/node_modules/minimist/LICENSE +18 -0
  149. data/node_modules/minimist/example/parse.js +2 -0
  150. data/node_modules/minimist/index.js +187 -0
  151. data/node_modules/minimist/package.json +101 -0
  152. data/node_modules/minimist/readme.markdown +73 -0
  153. data/node_modules/minimist/test/dash.js +24 -0
  154. data/node_modules/minimist/test/default_bool.js +20 -0
  155. data/node_modules/minimist/test/dotted.js +16 -0
  156. data/node_modules/minimist/test/long.js +31 -0
  157. data/node_modules/minimist/test/parse.js +318 -0
  158. data/node_modules/minimist/test/parse_modified.js +9 -0
  159. data/node_modules/minimist/test/short.js +67 -0
  160. data/node_modules/minimist/test/whitespace.js +8 -0
  161. data/node_modules/mkdirp/.travis.yml +8 -0
  162. data/node_modules/mkdirp/LICENSE +21 -0
  163. data/node_modules/mkdirp/bin/cmd.js +33 -0
  164. data/node_modules/mkdirp/bin/usage.txt +12 -0
  165. data/node_modules/mkdirp/examples/pow.js +6 -0
  166. data/node_modules/mkdirp/index.js +98 -0
  167. data/node_modules/mkdirp/package.json +93 -0
  168. data/node_modules/mkdirp/readme.markdown +100 -0
  169. data/node_modules/mkdirp/test/chmod.js +41 -0
  170. data/node_modules/mkdirp/test/clobber.js +38 -0
  171. data/node_modules/mkdirp/test/mkdirp.js +28 -0
  172. data/node_modules/mkdirp/test/opts_fs.js +29 -0
  173. data/node_modules/mkdirp/test/opts_fs_sync.js +27 -0
  174. data/node_modules/mkdirp/test/perm.js +32 -0
  175. data/node_modules/mkdirp/test/perm_sync.js +36 -0
  176. data/node_modules/mkdirp/test/race.js +37 -0
  177. data/node_modules/mkdirp/test/rel.js +32 -0
  178. data/node_modules/mkdirp/test/return.js +25 -0
  179. data/node_modules/mkdirp/test/return_sync.js +24 -0
  180. data/node_modules/mkdirp/test/root.js +19 -0
  181. data/node_modules/mkdirp/test/sync.js +32 -0
  182. data/node_modules/mkdirp/test/umask.js +28 -0
  183. data/node_modules/mkdirp/test/umask_sync.js +32 -0
  184. data/node_modules/ms/README.md +51 -0
  185. data/node_modules/ms/index.js +152 -0
  186. data/node_modules/ms/license.md +21 -0
  187. data/node_modules/ms/package.json +109 -0
  188. data/node_modules/once/LICENSE +15 -0
  189. data/node_modules/once/README.md +79 -0
  190. data/node_modules/once/once.js +42 -0
  191. data/node_modules/once/package.json +101 -0
  192. data/node_modules/path-is-absolute/index.js +20 -0
  193. data/node_modules/path-is-absolute/license +21 -0
  194. data/node_modules/path-is-absolute/package.json +111 -0
  195. data/node_modules/path-is-absolute/readme.md +59 -0
  196. data/node_modules/rimraf/LICENSE +15 -0
  197. data/node_modules/rimraf/README.md +101 -0
  198. data/node_modules/rimraf/bin.js +50 -0
  199. data/node_modules/rimraf/package.json +99 -0
  200. data/node_modules/rimraf/rimraf.js +363 -0
  201. data/node_modules/ultron/LICENSE +22 -0
  202. data/node_modules/ultron/index.js +138 -0
  203. data/node_modules/ultron/package.json +112 -0
  204. data/node_modules/wrappy/LICENSE +15 -0
  205. data/node_modules/wrappy/README.md +36 -0
  206. data/node_modules/wrappy/package.json +97 -0
  207. data/node_modules/wrappy/wrappy.js +33 -0
  208. data/node_modules/ws/LICENSE +21 -0
  209. data/node_modules/ws/README.md +259 -0
  210. data/node_modules/ws/SECURITY.md +33 -0
  211. data/node_modules/ws/index.js +15 -0
  212. data/node_modules/ws/lib/BufferUtil.fallback.js +56 -0
  213. data/node_modules/ws/lib/BufferUtil.js +15 -0
  214. data/node_modules/ws/lib/ErrorCodes.js +28 -0
  215. data/node_modules/ws/lib/EventTarget.js +158 -0
  216. data/node_modules/ws/lib/Extensions.js +69 -0
  217. data/node_modules/ws/lib/PerMessageDeflate.js +339 -0
  218. data/node_modules/ws/lib/Receiver.js +520 -0
  219. data/node_modules/ws/lib/Sender.js +438 -0
  220. data/node_modules/ws/lib/Validation.fallback.js +9 -0
  221. data/node_modules/ws/lib/Validation.js +17 -0
  222. data/node_modules/ws/lib/WebSocket.js +705 -0
  223. data/node_modules/ws/lib/WebSocketServer.js +336 -0
  224. data/node_modules/ws/package.json +122 -0
  225. data/package.json +26 -0
  226. data/samples/sample.html +13 -0
  227. data/script/rasterize.js +18 -0
  228. 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,4 @@
1
+ 'use strict';
2
+
3
+ module.exports.HOST = 'localhost';
4
+ module.exports.PORT = 9222;
@@ -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
+ }