@@ -10,6 +10,7 @@ const WebSocket = require('ws');
1010const api = require ( './api.js' ) ;
1111const defaults = require ( './defaults.js' ) ;
1212const devtools = require ( './devtools.js' ) ;
13+ const StdioWrapper = require ( './stdio-wrapper.js' ) ;
1314
1415class ProtocolError extends Error {
1516 constructor ( request , response ) {
@@ -58,6 +59,7 @@ class Chrome extends EventEmitter {
5859 this . local = ! ! ( options . local ) ;
5960 this . target = options . target || defaultTarget ;
6061 this . connectOptions = options . connectOptions ;
62+ this . process = options . process ;
6163 // locals
6264 this . _notifier = notifier ;
6365 this . _callbacks = { } ;
@@ -92,7 +94,7 @@ class Chrome extends EventEmitter {
9294 const request = { method, params, sessionId} ;
9395 reject (
9496 error instanceof Error
95- ? error // low-level WebSocket error
97+ ? error // low-level io error
9698 : new ProtocolError ( request , response )
9799 ) ;
98100 } else {
@@ -104,56 +106,52 @@ class Chrome extends EventEmitter {
104106 }
105107
106108 close ( callback ) {
107- const closeWebSocket = ( callback ) => {
108- // don't close if it's already closed
109- if ( this . _ws . readyState === 3 ) {
110- callback ( ) ;
111- } else {
112- // don't notify on user-initiated shutdown ('disconnect' event)
113- this . _ws . removeAllListeners ( 'close' ) ;
114- this . _ws . once ( 'close' , ( ) => {
115- this . _ws . removeAllListeners ( ) ;
116- this . _handleConnectionClose ( ) ;
117- callback ( ) ;
118- } ) ;
119- this . _ws . close ( ) ;
120- }
121- } ;
122109 if ( typeof callback === 'function' ) {
123- closeWebSocket ( callback ) ;
110+ this . _close ( callback ) ;
124111 return undefined ;
125112 } else {
126113 return new Promise ( ( fulfill , reject ) => {
127- closeWebSocket ( fulfill ) ;
114+ this . _close ( fulfill ) ;
128115 } ) ;
129116 }
130117 }
131118
132119 // initiate the connection process
133120 async _start ( ) {
134- const options = {
135- host : this . host ,
136- port : this . port ,
137- secure : this . secure ,
138- useHostName : this . useHostName ,
139- alterPath : this . alterPath ,
140- ...this . connectOptions ,
141- } ;
142121 try {
143- // fetch the WebSocket debugger URL
144- const url = await this . _fetchDebuggerURL ( options ) ;
145- // allow the user to alter the URL
146- const urlObject = parseUrl ( url ) ;
147- urlObject . pathname = options . alterPath ( urlObject . pathname ) ;
148- this . webSocketUrl = formatUrl ( urlObject ) ;
149- // update the connection parameters using the debugging URL
150- options . host = urlObject . hostname ;
151- options . port = urlObject . port || options . port ;
152- // fetch the protocol and prepare the API
153- const protocol = await this . _fetchProtocol ( options ) ;
154- api . prepare ( this , protocol ) ;
155- // finally connect to the WebSocket
156- await this . _connectToWebSocket ( ) ;
122+ if ( this . process )
123+ {
124+ // we first "connect" to stdio pipes, so that we can
125+ // first the protocol remotely via the pipe.
126+ await this . _connect ( ) ;
127+ const protocol = await this . _fetchProtocol ( { } ) ;
128+ api . prepare ( this , protocol ) ;
129+ }
130+ else
131+ {
132+ const options = {
133+ host : this . host ,
134+ port : this . port ,
135+ secure : this . secure ,
136+ useHostName : this . useHostName ,
137+ alterPath : this . alterPath ,
138+ ...this . connectOptions ,
139+ } ;
140+ // fetch the WebSocket debugger URL
141+ const url = await this . _fetchWsDebuggerURL ( options ) ;
142+ // allow the user to alter the URL
143+ const urlObject = parseUrl ( url ) ;
144+ urlObject . pathname = options . alterPath ( urlObject . pathname ) ;
145+ this . webSocketUrl = formatUrl ( urlObject ) ;
146+ // update the connection parameters using the debugging URL
147+ options . host = urlObject . hostname ;
148+ options . port = urlObject . port || options . port ;
149+ // fetch the protocol and prepare the API
150+ const protocol = await this . _fetchProtocol ( options ) ;
151+ api . prepare ( this , protocol ) ;
152+ // finally connect to the WebSocket
153+ await this . _connect ( ) ;
154+ }
157155 // since the handler is executed synchronously, the emit() must be
158156 // performed in the next tick so that uncaught errors in the client code
159157 // are not intercepted by the Promise mechanism and therefore reported
@@ -167,7 +165,7 @@ class Chrome extends EventEmitter {
167165 }
168166
169167 // fetch the WebSocket URL according to 'target'
170- async _fetchDebuggerURL ( options ) {
168+ async _fetchWsDebuggerURL ( options ) {
171169 const userTarget = this . target ;
172170 switch ( typeof userTarget ) {
173171 case 'string' : {
@@ -212,40 +210,71 @@ class Chrome extends EventEmitter {
212210 // otherwise user either the local or the remote version
213211 else {
214212 options . local = this . local ;
213+ if ( this . process )
214+ options . cdp = this ;
215215 return await devtools . Protocol ( options ) ;
216216 }
217217 }
218218
219- // establish the WebSocket connection and start processing user commands
220- _connectToWebSocket ( ) {
219+ _createStdioWrapper ( ) {
220+ const stdio = new StdioWrapper ( this . process . stdio [ 3 ] , this . process . stdio [ 4 ] ) ;
221+ this . _close = ( ...args ) => stdio . close ( ...args ) ;
222+ this . _send = ( ...args ) => stdio . send ( ...args ) ;
223+ return stdio ;
224+ }
225+
226+ _createWebSocketWrapper ( ) {
227+ if ( this . secure ) {
228+ this . webSocketUrl = this . webSocketUrl . replace ( / ^ w s : / i, 'wss:' ) ;
229+ }
230+ const ws = this . _ws = new WebSocket ( this . webSocketUrl , [ ] , {
231+ followRedirects : true ,
232+ ...this . connectOptions ,
233+ } ) ;
234+ this . _close = ( callback ) => {
235+ // don't close if it's already closed
236+ if ( ws . readyState === 3 ) {
237+ callback ( ) ;
238+ } else {
239+ // don't notify on user-initiated shutdown ('disconnect' event)
240+ ws . removeAllListeners ( 'close' ) ;
241+ ws . once ( 'close' , ( ) => {
242+ ws . removeAllListeners ( ) ;
243+ this . _handleConnectionClose ( ) ;
244+ callback ( ) ;
245+ } ) ;
246+ ws . close ( ) ;
247+ }
248+ } ;
249+ this . _send = ( ...args ) => ws . send ( ...args ) ;
250+ return ws ;
251+ }
252+
253+ // establish the connection wrapper and start processing user commands
254+ _connect ( ) {
221255 return new Promise ( ( fulfill , reject ) => {
222- // create the WebSocket
223256 try {
224- if ( this . secure ) {
225- this . webSocketUrl = this . webSocketUrl . replace ( / ^ w s : / i, 'wss:' ) ;
226- }
227- this . _ws = new WebSocket ( this . webSocketUrl , [ ] , {
228- followRedirects : true ,
229- ...this . connectOptions
230- } ) ;
257+ this . _transport = this . process
258+ ? this . _createStdioWrapper ( )
259+ : this . _createWebSocketWrapper ( ) ;
231260 } catch ( err ) {
232- // handles bad URLs
261+ // handle missing stdio streams, bad URLs...
233262 reject ( err ) ;
234263 return ;
235264 }
236265 // set up event handlers
237- this . _ws . on ( 'open' , ( ) => {
266+ this . _transport . on ( 'open' , ( ) => {
238267 fulfill ( ) ;
239268 } ) ;
240- this . _ws . on ( 'message' , ( data ) => {
269+ this . _transport . on ( 'message' , ( data ) => {
241270 const message = JSON . parse ( data ) ;
242271 this . _handleMessage ( message ) ;
243272 } ) ;
244- this . _ws . on ( 'close' , ( code ) => {
273+ this . _transport . on ( 'close' , ( code ) => {
245274 this . _handleConnectionClose ( ) ;
246275 this . emit ( 'disconnect' ) ;
247276 } ) ;
248- this . _ws . on ( 'error' , ( err ) => {
277+ this . _transport . on ( 'error' , ( err ) => {
249278 reject ( err ) ;
250279 } ) ;
251280 } ) ;
@@ -305,7 +334,7 @@ class Chrome extends EventEmitter {
305334 sessionId,
306335 params : params || { }
307336 } ;
308- this . _ws . send ( JSON . stringify ( message ) , ( err ) => {
337+ this . _send ( JSON . stringify ( message ) , ( err ) => {
309338 if ( err ) {
310339 // handle low-level WebSocket errors
311340 if ( typeof callback === 'function' ) {
@@ -317,6 +346,7 @@ class Chrome extends EventEmitter {
317346 }
318347 } ) ;
319348 }
349+ get transport ( ) { return this . _transport ; }
320350}
321351
322352module . exports = Chrome ;
0 commit comments