opal 1.6.1 → 1.7.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (212) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/build.yml +17 -0
  3. data/Gemfile +1 -0
  4. data/HACKING.md +47 -26
  5. data/UNRELEASED.md +28 -0
  6. data/benchmark/benchmarks +415 -103
  7. data/benchmark/bm_call_overhead.yml +28 -0
  8. data/benchmark/run.rb +61 -40
  9. data/docs/cdp_common.json +3364 -0
  10. data/docs/cdp_common.md +18 -0
  11. data/docs/{headless_chrome.md → headless_browsers.md} +31 -12
  12. data/lib/opal/ast/builder.rb +1 -1
  13. data/lib/opal/builder.rb +6 -1
  14. data/lib/opal/builder_processors.rb +5 -3
  15. data/lib/opal/cache.rb +1 -7
  16. data/lib/opal/cli_options.rb +72 -58
  17. data/lib/opal/cli_runners/chrome.rb +47 -9
  18. data/lib/opal/cli_runners/chrome_cdp_interface.rb +238 -112
  19. data/lib/opal/cli_runners/compiler.rb +146 -13
  20. data/lib/opal/cli_runners/deno.rb +32 -0
  21. data/lib/opal/cli_runners/firefox.rb +350 -0
  22. data/lib/opal/cli_runners/firefox_cdp_interface.rb +212 -0
  23. data/lib/opal/cli_runners/node_modules/.bin/chrome-remote-interface.cmd +17 -0
  24. data/lib/opal/cli_runners/node_modules/.bin/chrome-remote-interface.ps1 +28 -0
  25. data/lib/opal/cli_runners/node_modules/.package-lock.json +41 -0
  26. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/LICENSE +1 -1
  27. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/README.md +322 -182
  28. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/bin/client.js +99 -114
  29. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/chrome-remote-interface.js +1 -11
  30. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/index.js +16 -11
  31. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/api.js +41 -33
  32. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/chrome.js +224 -214
  33. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/devtools.js +71 -191
  34. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/external-request.js +26 -6
  35. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/protocol.json +20788 -9049
  36. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/websocket-wrapper.js +10 -3
  37. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/package.json +59 -123
  38. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/webpack.config.js +25 -32
  39. data/lib/opal/cli_runners/node_modules/commander/History.md +298 -0
  40. data/lib/opal/cli_runners/node_modules/commander/LICENSE +22 -0
  41. data/lib/opal/cli_runners/node_modules/commander/Readme.md +217 -61
  42. data/lib/opal/cli_runners/node_modules/commander/index.js +431 -145
  43. data/lib/opal/cli_runners/node_modules/commander/package.json +16 -79
  44. data/lib/opal/cli_runners/node_modules/ws/README.md +334 -98
  45. data/lib/opal/cli_runners/node_modules/ws/browser.js +8 -0
  46. data/lib/opal/cli_runners/node_modules/ws/index.js +5 -10
  47. data/lib/opal/cli_runners/node_modules/ws/lib/buffer-util.js +129 -0
  48. data/lib/opal/cli_runners/node_modules/ws/lib/constants.js +10 -0
  49. data/lib/opal/cli_runners/node_modules/ws/lib/event-target.js +184 -0
  50. data/lib/opal/cli_runners/node_modules/ws/lib/extension.js +223 -0
  51. data/lib/opal/cli_runners/node_modules/ws/lib/limiter.js +55 -0
  52. data/lib/opal/cli_runners/node_modules/ws/lib/permessage-deflate.js +518 -0
  53. data/lib/opal/cli_runners/node_modules/ws/lib/receiver.js +607 -0
  54. data/lib/opal/cli_runners/node_modules/ws/lib/sender.js +409 -0
  55. data/lib/opal/cli_runners/node_modules/ws/lib/stream.js +180 -0
  56. data/lib/opal/cli_runners/node_modules/ws/lib/validation.js +104 -0
  57. data/lib/opal/cli_runners/node_modules/ws/lib/websocket-server.js +447 -0
  58. data/lib/opal/cli_runners/node_modules/ws/lib/websocket.js +1195 -0
  59. data/lib/opal/cli_runners/node_modules/ws/package.json +40 -106
  60. data/lib/opal/cli_runners/package-lock.json +62 -0
  61. data/lib/opal/cli_runners/package.json +1 -1
  62. data/lib/opal/cli_runners.rb +26 -4
  63. data/lib/opal/nodes/args/prepare_post_args.rb +2 -2
  64. data/lib/opal/nodes/def.rb +8 -8
  65. data/lib/opal/nodes/iter.rb +12 -12
  66. data/lib/opal/nodes/logic.rb +1 -1
  67. data/lib/opal/nodes/masgn.rb +2 -2
  68. data/lib/opal/parser/with_ruby_lexer.rb +1 -1
  69. data/lib/opal/paths.rb +14 -0
  70. data/lib/opal/rewriter.rb +2 -0
  71. data/lib/opal/rewriters/forward_args.rb +52 -4
  72. data/lib/opal/rewriters/targeted_patches.rb +94 -0
  73. data/lib/opal/version.rb +1 -1
  74. data/opal/corelib/basic_object.rb +1 -1
  75. data/opal/corelib/boolean.rb +2 -2
  76. data/opal/corelib/class.rb +11 -0
  77. data/opal/corelib/constants.rb +3 -3
  78. data/opal/corelib/enumerable.rb +4 -0
  79. data/opal/corelib/enumerator.rb +1 -1
  80. data/opal/corelib/hash.rb +2 -2
  81. data/opal/corelib/helpers.rb +1 -1
  82. data/opal/corelib/kernel.rb +3 -3
  83. data/opal/corelib/method.rb +1 -1
  84. data/opal/corelib/module.rb +29 -8
  85. data/opal/corelib/proc.rb +7 -5
  86. data/opal/corelib/runtime.js +141 -78
  87. data/opal/corelib/set.rb +252 -0
  88. data/opal/corelib/string.rb +2 -1
  89. data/opal/corelib/time.rb +2 -2
  90. data/opal/opal.rb +1 -0
  91. data/opal.gemspec +1 -0
  92. data/spec/filters/bugs/array.rb +22 -13
  93. data/spec/filters/bugs/base64.rb +5 -5
  94. data/spec/filters/bugs/basicobject.rb +16 -8
  95. data/spec/filters/bugs/bigdecimal.rb +161 -160
  96. data/spec/filters/bugs/binding.rb +10 -10
  97. data/spec/filters/bugs/class.rb +8 -8
  98. data/spec/filters/bugs/complex.rb +2 -1
  99. data/spec/filters/bugs/date.rb +79 -81
  100. data/spec/filters/bugs/datetime.rb +29 -29
  101. data/spec/filters/bugs/delegate.rb +1 -3
  102. data/spec/filters/bugs/encoding.rb +69 -69
  103. data/spec/filters/bugs/enumerable.rb +22 -20
  104. data/spec/filters/bugs/enumerator.rb +88 -85
  105. data/spec/filters/bugs/exception.rb +46 -40
  106. data/spec/filters/bugs/file.rb +32 -32
  107. data/spec/filters/bugs/float.rb +26 -21
  108. data/spec/filters/bugs/freeze.rb +88 -0
  109. data/spec/filters/bugs/hash.rb +39 -38
  110. data/spec/filters/bugs/integer.rb +57 -44
  111. data/spec/filters/bugs/io.rb +1 -1
  112. data/spec/filters/bugs/kernel.rb +349 -269
  113. data/spec/filters/bugs/language.rb +220 -188
  114. data/spec/filters/bugs/main.rb +5 -3
  115. data/spec/filters/bugs/marshal.rb +38 -38
  116. data/spec/filters/bugs/math.rb +2 -1
  117. data/spec/filters/bugs/method.rb +73 -62
  118. data/spec/filters/bugs/module.rb +163 -143
  119. data/spec/filters/bugs/numeric.rb +6 -6
  120. data/spec/filters/bugs/objectspace.rb +16 -16
  121. data/spec/filters/bugs/openstruct.rb +1 -1
  122. data/spec/filters/bugs/pack_unpack.rb +51 -51
  123. data/spec/filters/bugs/pathname.rb +7 -7
  124. data/spec/filters/bugs/proc.rb +63 -63
  125. data/spec/filters/bugs/random.rb +7 -6
  126. data/spec/filters/bugs/range.rb +12 -9
  127. data/spec/filters/bugs/rational.rb +8 -7
  128. data/spec/filters/bugs/regexp.rb +49 -48
  129. data/spec/filters/bugs/ruby-32.rb +56 -0
  130. data/spec/filters/bugs/set.rb +30 -30
  131. data/spec/filters/bugs/singleton.rb +4 -4
  132. data/spec/filters/bugs/string.rb +187 -99
  133. data/spec/filters/bugs/stringio.rb +7 -0
  134. data/spec/filters/bugs/stringscanner.rb +68 -68
  135. data/spec/filters/bugs/struct.rb +11 -9
  136. data/spec/filters/bugs/symbol.rb +1 -1
  137. data/spec/filters/bugs/time.rb +78 -63
  138. data/spec/filters/bugs/trace_point.rb +4 -4
  139. data/spec/filters/bugs/unboundmethod.rb +32 -17
  140. data/spec/filters/bugs/warnings.rb +8 -12
  141. data/spec/filters/unsupported/array.rb +24 -107
  142. data/spec/filters/unsupported/basicobject.rb +12 -12
  143. data/spec/filters/unsupported/bignum.rb +27 -52
  144. data/spec/filters/unsupported/class.rb +1 -2
  145. data/spec/filters/unsupported/delegator.rb +3 -3
  146. data/spec/filters/unsupported/enumerable.rb +2 -9
  147. data/spec/filters/unsupported/enumerator.rb +2 -11
  148. data/spec/filters/unsupported/file.rb +1 -1
  149. data/spec/filters/unsupported/float.rb +28 -47
  150. data/spec/filters/unsupported/hash.rb +8 -14
  151. data/spec/filters/unsupported/integer.rb +75 -91
  152. data/spec/filters/unsupported/kernel.rb +17 -35
  153. data/spec/filters/unsupported/language.rb +11 -19
  154. data/spec/filters/unsupported/marshal.rb +22 -41
  155. data/spec/filters/unsupported/matchdata.rb +28 -52
  156. data/spec/filters/unsupported/math.rb +1 -1
  157. data/spec/filters/unsupported/privacy.rb +229 -285
  158. data/spec/filters/unsupported/range.rb +1 -5
  159. data/spec/filters/unsupported/regexp.rb +40 -66
  160. data/spec/filters/unsupported/set.rb +2 -2
  161. data/spec/filters/unsupported/singleton.rb +4 -4
  162. data/spec/filters/unsupported/string.rb +305 -508
  163. data/spec/filters/unsupported/struct.rb +3 -4
  164. data/spec/filters/unsupported/symbol.rb +15 -18
  165. data/spec/filters/unsupported/thread.rb +1 -7
  166. data/spec/filters/unsupported/time.rb +159 -202
  167. data/spec/filters/unsupported/usage_of_files.rb +170 -259
  168. data/spec/lib/builder_spec.rb +4 -4
  169. data/spec/lib/rewriters/forward_args_spec.rb +32 -12
  170. data/spec/mspec-opal/runner.rb +2 -0
  171. data/spec/ruby_specs +4 -0
  172. data/stdlib/deno/base.rb +28 -0
  173. data/stdlib/deno/file.rb +340 -0
  174. data/stdlib/{headless_chrome.rb → headless_browser/base.rb} +1 -1
  175. data/stdlib/headless_browser/file.rb +15 -0
  176. data/stdlib/headless_browser.rb +4 -0
  177. data/stdlib/native.rb +1 -1
  178. data/stdlib/nodejs/file.rb +5 -0
  179. data/stdlib/opal/platform.rb +8 -6
  180. data/stdlib/opal-platform.rb +14 -8
  181. data/stdlib/set.rb +1 -258
  182. data/tasks/benchmarking.rake +62 -19
  183. data/tasks/performance.rake +1 -1
  184. data/tasks/testing.rake +5 -3
  185. data/test/nodejs/test_file.rb +29 -10
  186. data/test/opal/http_server.rb +28 -11
  187. data/test/opal/unsupported_and_bugs.rb +2 -1
  188. metadata +91 -52
  189. data/lib/opal/cli_runners/node_modules/ultron/LICENSE +0 -22
  190. data/lib/opal/cli_runners/node_modules/ultron/index.js +0 -138
  191. data/lib/opal/cli_runners/node_modules/ultron/package.json +0 -112
  192. data/lib/opal/cli_runners/node_modules/ws/SECURITY.md +0 -33
  193. data/lib/opal/cli_runners/node_modules/ws/lib/BufferUtil.fallback.js +0 -56
  194. data/lib/opal/cli_runners/node_modules/ws/lib/BufferUtil.js +0 -15
  195. data/lib/opal/cli_runners/node_modules/ws/lib/ErrorCodes.js +0 -28
  196. data/lib/opal/cli_runners/node_modules/ws/lib/EventTarget.js +0 -158
  197. data/lib/opal/cli_runners/node_modules/ws/lib/Extensions.js +0 -69
  198. data/lib/opal/cli_runners/node_modules/ws/lib/PerMessageDeflate.js +0 -339
  199. data/lib/opal/cli_runners/node_modules/ws/lib/Receiver.js +0 -520
  200. data/lib/opal/cli_runners/node_modules/ws/lib/Sender.js +0 -438
  201. data/lib/opal/cli_runners/node_modules/ws/lib/Validation.fallback.js +0 -9
  202. data/lib/opal/cli_runners/node_modules/ws/lib/Validation.js +0 -17
  203. data/lib/opal/cli_runners/node_modules/ws/lib/WebSocket.js +0 -705
  204. data/lib/opal/cli_runners/node_modules/ws/lib/WebSocketServer.js +0 -336
  205. data/spec/filters/bugs/boolean.rb +0 -3
  206. data/spec/filters/bugs/matrix.rb +0 -3
  207. data/spec/filters/unsupported/fixnum.rb +0 -15
  208. data/spec/filters/unsupported/freeze.rb +0 -102
  209. data/spec/filters/unsupported/pathname.rb +0 -4
  210. data/spec/filters/unsupported/proc.rb +0 -4
  211. data/spec/filters/unsupported/random.rb +0 -5
  212. data/spec/filters/unsupported/taint.rb +0 -162
@@ -2,17 +2,25 @@
2
2
 
3
3
  const EventEmitter = require('events');
4
4
  const util = require('util');
5
+ const formatUrl = require('url').format;
6
+ const parseUrl = require('url').parse;
5
7
 
6
8
  const WebSocket = require('ws');
7
9
 
8
- const api = require('./api');
9
- const defaults = require('./defaults');
10
- const devtools = require('./devtools');
10
+ const api = require('./api.js');
11
+ const defaults = require('./defaults.js');
12
+ const devtools = require('./devtools.js');
11
13
 
12
14
  class ProtocolError extends Error {
13
- constructor(response) {
14
- super(response.message);
15
- Object.assign(this, response);
15
+ constructor(request, response) {
16
+ let {message} = response;
17
+ if (response.data) {
18
+ message += ` (${response.data})`;
19
+ }
20
+ super(message);
21
+ // attach the original response as well
22
+ this.request = request;
23
+ this.response = response;
16
24
  }
17
25
  }
18
26
 
@@ -20,9 +28,9 @@ class Chrome extends EventEmitter {
20
28
  constructor(options, notifier) {
21
29
  super();
22
30
  // options
23
- const defaultTarget = function (targets) {
24
- // prefer type = 'page' inspectabe targets as they represents
25
- // browser tabs (fall back to the first instectable target
31
+ const defaultTarget = (targets) => {
32
+ // prefer type = 'page' inspectable targets as they represents
33
+ // browser tabs (fall back to the first inspectable target
26
34
  // otherwise)
27
35
  let backup;
28
36
  let target = targets.find((target) => {
@@ -44,245 +52,247 @@ class Chrome extends EventEmitter {
44
52
  this.host = options.host || defaults.HOST;
45
53
  this.port = options.port || defaults.PORT;
46
54
  this.secure = !!(options.secure);
55
+ this.useHostName = !!(options.useHostName);
56
+ this.alterPath = options.alterPath || ((path) => path);
47
57
  this.protocol = options.protocol;
48
- this.remote = !!(options.remote);
49
- this.target = options.target ||
50
- /* backward compatibility */ options.tab || options.chooseTab
51
- || defaultTarget;
58
+ this.local = !!(options.local);
59
+ this.target = options.target || defaultTarget;
52
60
  // locals
53
- EventEmitter.call(this);
54
61
  this._notifier = notifier;
55
62
  this._callbacks = {};
56
63
  this._nextCommandId = 1;
64
+ // properties
65
+ this.webSocketUrl = undefined;
57
66
  // operations
58
- start.call(this);
67
+ this._start();
59
68
  }
60
- }
61
-
62
- // avoid misinterpreting protocol's members as custom util.inspect functions
63
- Chrome.prototype.inspect = function (depth, options) {
64
- options.customInspect = false;
65
- return util.inspect(this, options);
66
- };
67
69
 
68
- Chrome.prototype.send = function (method, params, callback) {
69
- const chrome = this;
70
- if (typeof params === 'function') {
71
- callback = params;
72
- params = undefined;
70
+ // avoid misinterpreting protocol's members as custom util.inspect functions
71
+ inspect(depth, options) {
72
+ options.customInspect = false;
73
+ return util.inspect(this, options);
73
74
  }
74
- // return a promise when a callback is not provided
75
- if (typeof callback === 'function') {
76
- enqueueCommand.call(chrome, method, params, callback);
77
- } else {
78
- return new Promise(function (fulfill, reject) {
79
- enqueueCommand.call(chrome, method, params, function (error, response) {
80
- if (error) {
81
- reject(new ProtocolError(response));
82
- } else {
83
- fulfill(response);
84
- }
75
+
76
+ send(method, params, sessionId, callback) {
77
+ // handle optional arguments
78
+ const optionals = Array.from(arguments).slice(1);
79
+ params = optionals.find(x => typeof x === 'object');
80
+ sessionId = optionals.find(x => typeof x === 'string');
81
+ callback = optionals.find(x => typeof x === 'function');
82
+ // return a promise when a callback is not provided
83
+ if (typeof callback === 'function') {
84
+ this._enqueueCommand(method, params, sessionId, callback);
85
+ return undefined;
86
+ } else {
87
+ return new Promise((fulfill, reject) => {
88
+ this._enqueueCommand(method, params, sessionId, (error, response) => {
89
+ if (error) {
90
+ const request = {method, params, sessionId};
91
+ reject(
92
+ error instanceof Error
93
+ ? error // low-level WebSocket error
94
+ : new ProtocolError(request, response)
95
+ );
96
+ } else {
97
+ fulfill(response);
98
+ }
99
+ });
85
100
  });
86
- });
101
+ }
87
102
  }
88
- };
89
103
 
90
- Chrome.prototype.close = function (callback) {
91
- const chrome = this;
92
- function closeWebSocket(callback) {
93
- // don't notify on user-initiated shutdown ('disconnect' event)
94
- chrome._ws.removeAllListeners('close');
95
- chrome._ws.close();
96
- chrome._ws.once('close', function () {
97
- chrome._ws.removeAllListeners();
98
- callback();
99
- });
100
- }
101
- if (typeof callback === 'function') {
102
- closeWebSocket(callback);
103
- } else {
104
- return new Promise(function (fulfill, reject) {
105
- closeWebSocket(fulfill);
106
- });
104
+ close(callback) {
105
+ const closeWebSocket = (callback) => {
106
+ // don't close if it's already closed
107
+ if (this._ws.readyState === 3) {
108
+ callback();
109
+ } else {
110
+ // don't notify on user-initiated shutdown ('disconnect' event)
111
+ this._ws.removeAllListeners('close');
112
+ this._ws.once('close', () => {
113
+ this._ws.removeAllListeners();
114
+ callback();
115
+ });
116
+ this._ws.close();
117
+ }
118
+ };
119
+ if (typeof callback === 'function') {
120
+ closeWebSocket(callback);
121
+ return undefined;
122
+ } else {
123
+ return new Promise((fulfill, reject) => {
124
+ closeWebSocket(fulfill);
125
+ });
126
+ }
107
127
  }
108
- };
109
128
 
110
- // send a command to the remote endpoint and register a callback for the reply
111
- function enqueueCommand(method, params, callback) {
112
- const chrome = this;
113
- const id = chrome._nextCommandId++;
114
- const message = {'id': id, 'method': method, 'params': params || {}};
115
- chrome._ws.send(JSON.stringify(message));
116
- chrome._callbacks[id] = callback;
117
- }
118
-
119
- // initiate the connection process
120
- function start() {
121
- const chrome = this;
122
- const options = {'host': chrome.host, 'port': chrome.port, 'secure': chrome.secure};
123
- Promise.all([
124
- // fetch the protocol and prepare the API
125
- fetchProtocol.call(chrome, options).then(api.prepare.bind(chrome)),
126
- // in the meanwhile fetch the WebSocket debugger URL
127
- fetchDebuggerURL.call(chrome, options)
128
- ]).then(function (values) {
129
- // finally connect to the WebSocket
130
- const url = values[1];
131
- return connectToWebSocket.call(chrome, url);
132
- }).then(function () {
133
- // since the handler is executed synchronously, the emit() must be
134
- // performed in the next tick so that uncaught errors in the client code
135
- // are not intercepted by the Promise mechanism and therefore reported
136
- // via the 'error' event
137
- process.nextTick(function () {
138
- chrome._notifier.emit('connect', chrome);
139
- });
140
- }).catch(function (err) {
141
- chrome._notifier.emit('error', err);
142
- });
143
- }
144
-
145
- // fetch the protocol according to 'protocol' and 'remote'
146
- function fetchProtocol(options) {
147
- const chrome = this;
148
- return new Promise(function (fulfill, reject) {
149
- // if a protocol has been provided then use it
150
- if (chrome.protocol) {
151
- fulfill(chrome.protocol);
152
- }
153
- // otherwise user either the local or the remote version
154
- else {
155
- options.remote = chrome.remote;
156
- devtools.Protocol(options).then(function (protocol) {
157
- fulfill(protocol.descriptor);
158
- }).catch(reject);
129
+ // initiate the connection process
130
+ async _start() {
131
+ const options = {
132
+ host: this.host,
133
+ port: this.port,
134
+ secure: this.secure,
135
+ useHostName: this.useHostName,
136
+ alterPath: this.alterPath
137
+ };
138
+ try {
139
+ // fetch the WebSocket debugger URL
140
+ const url = await this._fetchDebuggerURL(options);
141
+ // allow the user to alter the URL
142
+ const urlObject = parseUrl(url);
143
+ urlObject.pathname = options.alterPath(urlObject.pathname);
144
+ this.webSocketUrl = formatUrl(urlObject);
145
+ // update the connection parameters using the debugging URL
146
+ options.host = urlObject.hostname;
147
+ options.port = urlObject.port || options.port;
148
+ // fetch the protocol and prepare the API
149
+ const protocol = await this._fetchProtocol(options);
150
+ api.prepare(this, protocol);
151
+ // finally connect to the WebSocket
152
+ await this._connectToWebSocket();
153
+ // since the handler is executed synchronously, the emit() must be
154
+ // performed in the next tick so that uncaught errors in the client code
155
+ // are not intercepted by the Promise mechanism and therefore reported
156
+ // via the 'error' event
157
+ process.nextTick(() => {
158
+ this._notifier.emit('connect', this);
159
+ });
160
+ } catch (err) {
161
+ this._notifier.emit('error', err);
159
162
  }
160
- });
161
- }
162
-
163
- // extract the debugger URL from a target-like object
164
- function fetchFromObject(fulfill, reject, target) {
165
- const url = (target || {}).webSocketDebuggerUrl;
166
- if (url) {
167
- fulfill(url);
168
- } else {
169
- const targetStr = JSON.stringify(target, null, 4);
170
- const err = new Error('Invalid target ' + targetStr);
171
- reject(err);
172
163
  }
173
- }
174
164
 
175
- // fetch the WebSocket URL according to 'target'
176
- function fetchDebuggerURL(options) {
177
- const chrome = this;
178
- return new Promise(function (fulfill, reject) {
179
- // note: when DevTools are open or another WebSocket is connected to a
180
- // given target the 'webSocketDebuggerUrl' field is not available
181
- let userTarget = chrome.target;
165
+ // fetch the WebSocket URL according to 'target'
166
+ async _fetchDebuggerURL(options) {
167
+ const userTarget = this.target;
182
168
  switch (typeof userTarget) {
183
- case 'string':
169
+ case 'string': {
170
+ let idOrUrl = userTarget;
184
171
  // use default host and port if omitted (and a relative URL is specified)
185
- if (userTarget.startsWith('/')) {
186
- const prefix = 'ws://' + chrome.host + ':' + chrome.port;
187
- userTarget = prefix + userTarget;
172
+ if (idOrUrl.startsWith('/')) {
173
+ idOrUrl = `ws://${this.host}:${this.port}${idOrUrl}`;
188
174
  }
189
175
  // a WebSocket URL is specified by the user (e.g., node-inspector)
190
- if (userTarget.match(/^wss?:/i)) {
191
- fulfill(userTarget);
176
+ if (idOrUrl.match(/^wss?:/i)) {
177
+ return idOrUrl; // done!
192
178
  }
193
179
  // a target id is specified by the user
194
180
  else {
195
- devtools.List(options).then(function (targets) {
196
- return targets.find(function (target) {
197
- return target.id === userTarget;
198
- });
199
- }).then(function (target) {
200
- fetchFromObject(fulfill, reject, target);
201
- }).catch(reject);
181
+ const targets = await devtools.List(options);
182
+ const object = targets.find((target) => target.id === idOrUrl);
183
+ return object.webSocketDebuggerUrl;
202
184
  }
203
- break;
204
- case 'object':
205
- // a target object is specified by the user
206
- fetchFromObject(fulfill, reject, userTarget);
207
- break;
208
- case 'function':
209
- // a function is specified by the user
210
- devtools.List(options).then(function (targets) {
211
- const result = userTarget(targets);
212
- if (typeof result === 'number') {
213
- return targets[result];
214
- } else {
215
- return result;
216
- }
217
- }).then(function (target) {
218
- fetchFromObject(fulfill, reject, target);
219
- }).catch(reject);
220
- break;
185
+ }
186
+ case 'object': {
187
+ const object = userTarget;
188
+ return object.webSocketDebuggerUrl;
189
+ }
190
+ case 'function': {
191
+ const func = userTarget;
192
+ const targets = await devtools.List(options);
193
+ const result = func(targets);
194
+ const object = typeof result === 'number' ? targets[result] : result;
195
+ return object.webSocketDebuggerUrl;
196
+ }
221
197
  default:
222
- reject(new Error('Invalid target argument "' + chrome.target + '"'));
198
+ throw new Error(`Invalid target argument "${this.target}"`);
223
199
  }
224
- });
225
- }
200
+ }
226
201
 
227
- // establish the WebSocket connection and start processing user commands
228
- function connectToWebSocket(url) {
229
- const chrome = this;
230
- return new Promise(function (fulfill, reject) {
231
- // create the WebSocket
232
- try {
233
- if (chrome.secure) {
234
- url = url.replace(/^ws:/i, 'wss:');
235
- }
236
- chrome._ws = new WebSocket(url);
237
- } catch (err) {
238
- // handles bad URLs
239
- reject(err);
240
- return;
202
+ // fetch the protocol according to 'protocol' and 'local'
203
+ async _fetchProtocol(options) {
204
+ // if a protocol has been provided then use it
205
+ if (this.protocol) {
206
+ return this.protocol;
241
207
  }
242
- // set up event handlers
243
- chrome._ws.on('open', function () {
244
- fulfill();
245
- });
246
- chrome._ws.on('message', function (data) {
247
- const message = JSON.parse(data);
248
- handleMessage.call(chrome, message);
249
- });
250
- chrome._ws.on('close', function (code) {
251
- chrome.emit('disconnect');
252
- });
253
- chrome._ws.on('error', function (err) {
254
- reject(err);
208
+ // otherwise user either the local or the remote version
209
+ else {
210
+ options.local = this.local;
211
+ return await devtools.Protocol(options);
212
+ }
213
+ }
214
+
215
+ // establish the WebSocket connection and start processing user commands
216
+ _connectToWebSocket() {
217
+ return new Promise((fulfill, reject) => {
218
+ // create the WebSocket
219
+ try {
220
+ if (this.secure) {
221
+ this.webSocketUrl = this.webSocketUrl.replace(/^ws:/i, 'wss:');
222
+ }
223
+ this._ws = new WebSocket(this.webSocketUrl);
224
+ } catch (err) {
225
+ // handles bad URLs
226
+ reject(err);
227
+ return;
228
+ }
229
+ // set up event handlers
230
+ this._ws.on('open', () => {
231
+ fulfill();
232
+ });
233
+ this._ws.on('message', (data) => {
234
+ const message = JSON.parse(data);
235
+ this._handleMessage(message);
236
+ });
237
+ this._ws.on('close', (code) => {
238
+ this.emit('disconnect');
239
+ });
240
+ this._ws.on('error', (err) => {
241
+ reject(err);
242
+ });
255
243
  });
256
- });
257
- }
244
+ }
258
245
 
259
- // handle the messages read from the WebSocket
260
- function handleMessage(message) {
261
- const chrome = this;
262
- // command response
263
- if (message.id) {
264
- const callback = chrome._callbacks[message.id];
265
- if (!callback) {
266
- return;
267
- }
268
- // interpret the lack of both 'error' and 'result' as success
269
- // (this may happen with node-inspector)
270
- if (message.error) {
271
- callback(true, message.error);
272
- } else {
273
- callback(false, message.result || {});
246
+ // handle the messages read from the WebSocket
247
+ _handleMessage(message) {
248
+ // command response
249
+ if (message.id) {
250
+ const callback = this._callbacks[message.id];
251
+ if (!callback) {
252
+ return;
253
+ }
254
+ // interpret the lack of both 'error' and 'result' as success
255
+ // (this may happen with node-inspector)
256
+ if (message.error) {
257
+ callback(true, message.error);
258
+ } else {
259
+ callback(false, message.result || {});
260
+ }
261
+ // unregister command response callback
262
+ delete this._callbacks[message.id];
263
+ // notify when there are no more pending commands
264
+ if (Object.keys(this._callbacks).length === 0) {
265
+ this.emit('ready');
266
+ }
274
267
  }
275
- // unregister command response callback
276
- delete chrome._callbacks[message.id];
277
- // notify when there are no more pending commands
278
- if (Object.keys(chrome._callbacks).length === 0) {
279
- chrome.emit('ready');
268
+ // event
269
+ else if (message.method) {
270
+ const {method, params, sessionId} = message;
271
+ this.emit('event', message);
272
+ this.emit(method, params, sessionId);
273
+ this.emit(`${method}.${sessionId}`, params, sessionId);
280
274
  }
281
275
  }
282
- // event
283
- else if (message.method) {
284
- chrome.emit('event', message);
285
- chrome.emit(message.method, message.params);
276
+
277
+ // send a command to the remote endpoint and register a callback for the reply
278
+ _enqueueCommand(method, params, sessionId, callback) {
279
+ const id = this._nextCommandId++;
280
+ const message = {
281
+ id,
282
+ method,
283
+ sessionId,
284
+ params: params || {}
285
+ };
286
+ this._ws.send(JSON.stringify(message), (err) => {
287
+ if (err) {
288
+ // handle low-level WebSocket errors
289
+ if (typeof callback === 'function') {
290
+ callback(err);
291
+ }
292
+ } else {
293
+ this._callbacks[id] = callback;
294
+ }
295
+ });
286
296
  }
287
297
  }
288
298