opal 1.6.1 → 1.7.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.
Files changed (212) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/build.yml +17 -0
  3. data/CHANGELOG.md +35 -1
  4. data/Gemfile +1 -0
  5. data/HACKING.md +47 -26
  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 +89 -50
  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