@@ -7,6 +7,7 @@ import {Locator} from './locators';
77import { Logger } from './logger' ;
88import { Ptor } from './ptor' ;
99import * as helper from './util' ;
10+ let breakpointHook = require ( './breakpointhook.js' ) ;
1011
1112declare var global : any ;
1213declare var process : any ;
@@ -25,32 +26,36 @@ export class DebugHelper {
2526
2627 constructor ( private browserUnderDebug_ : ProtractorBrowser ) { }
2728
29+
30+ initBlocking ( debuggerClientPath : string , onStartFn : Function , opt_debugPort ?: number ) {
31+ this . init_ ( debuggerClientPath , true , onStartFn , opt_debugPort ) ;
32+ }
33+
34+ init ( debuggerClientPath : string , onStartFn : Function , opt_debugPort ?: number ) {
35+ this . init_ ( debuggerClientPath , false , onStartFn , opt_debugPort ) ;
36+ }
37+
2838 /**
2939 * 1) Set up helper functions for debugger clients to call on (e.g.
30- * getControlFlowText, execute code, get autocompletion).
40+ * execute code, get autocompletion).
3141 * 2) Enter process into debugger mode. (i.e. process._debugProcess).
3242 * 3) Invoke the debugger client specified by debuggerClientPath.
3343 *
3444 * @param {string } debuggerClientPath Absolute path of debugger client to use.
45+ * @param {boolean } blockUntilExit Whether to block the flow until process exit or resume
46+ * immediately.
3547 * @param {Function } onStartFn Function to call when the debugger starts. The
3648 * function takes a single parameter, which represents whether this is the
3749 * first time that the debugger is called.
3850 * @param {number= } opt_debugPort Optional port to use for the debugging
3951 * process.
52+ *
53+ * @return {Promise } If blockUntilExit, a promise resolved when the debugger process
54+ * exits. Otherwise, resolved when the debugger process is ready to begin.
4055 */
41- init ( debuggerClientPath : string , onStartFn : Function , opt_debugPort ?: number ) {
42- ( wdpromise . ControlFlow as any ) . prototype . getControlFlowText = function ( ) {
43- let controlFlowText = this . getSchedule ( /* opt_includeStackTraces */ true ) ;
44- // This filters the entire control flow text, not just the stack trace, so
45- // unless we maintain a good (i.e. non-generic) set of keywords in
46- // STACK_SUBSTRINGS_TO_FILTER, we run the risk of filtering out non stack
47- // trace. The alternative though, which is to reimplement
48- // webdriver.promise.ControlFlow.prototype.getSchedule() here is much
49- // hackier, and involves messing with the control flow's internals /
50- // private variables.
51- return helper . filterStackTrace ( controlFlowText ) ;
52- } ;
53-
56+ init_ (
57+ debuggerClientPath : string , blockUntilExit : boolean , onStartFn : Function ,
58+ opt_debugPort ?: number ) {
5459 const vm_ = require ( 'vm' ) ;
5560 let flow = wdpromise . controlFlow ( ) ;
5661
@@ -75,8 +80,11 @@ export class DebugHelper {
7580 }
7681 let sandbox = vm_ . createContext ( context ) ;
7782
78- let debuggerReadyPromise = wdpromise . defer ( ) ;
79- flow . execute ( ( ) => {
83+ let debuggingDone = wdpromise . defer ( ) ;
84+
85+ // We run one flow.execute block for the debugging session. All
86+ // subcommands should be scheduled under this task.
87+ let executePromise = flow . execute ( ( ) => {
8088 process [ 'debugPort' ] = opt_debugPort || process [ 'debugPort' ] ;
8189 this . validatePortAvailability_ ( process [ 'debugPort' ] ) . then ( ( firstTime : boolean ) => {
8290 onStartFn ( firstTime ) ;
@@ -93,34 +101,30 @@ export class DebugHelper {
93101 . on ( 'message' ,
94102 ( m : string ) => {
95103 if ( m === 'ready' ) {
96- debuggerReadyPromise . fulfill ( ) ;
104+ breakpointHook ( ) ;
105+ if ( ! blockUntilExit ) {
106+ debuggingDone . fulfill ( ) ;
107+ }
97108 }
98109 } )
99110 . on ( 'exit' , ( ) => {
100- logger . info ( 'Debugger exiting' ) ;
101111 // Clear this so that we know it's ok to attach a debugger
102112 // again.
103113 this . dbgCodeExecutor = null ;
114+ debuggingDone . fulfill ( ) ;
104115 } ) ;
105116 } ) ;
106- } ) ;
107-
108- let pausePromise = flow . execute ( ( ) => {
109- return debuggerReadyPromise . promise . then ( ( ) => {
110- // Necessary for backward compatibility with node < 0.12.0
111- return this . browserUnderDebug_ . executeScriptWithDescription ( '' , 'empty debugger hook' ) ;
112- } ) ;
113- } ) ;
117+ return debuggingDone . promise ;
118+ } , 'debugging tasks' ) ;
114119
115120 // Helper used only by debuggers at './debugger/modes/*.js' to insert code
116- // into the control flow.
117- // In order to achieve this, we maintain a promise at the top of the control
121+ // into the control flow, via debugger 'evaluate' protocol .
122+ // In order to achieve this, we maintain a task at the top of the control
118123 // flow, so that we can insert frames into it.
119124 // To be able to simulate callback/asynchronous code, we poll this object
120- // for a result at every run of DeferredExecutor.execute.
121- let browserUnderDebug = this . browserUnderDebug_ ;
125+ // whenever `breakpointHook` is called.
122126 this . dbgCodeExecutor = {
123- execPromise_ : pausePromise , // Promise pointing to current stage of flow .
127+ execPromise_ : undefined , // Promise pointing to currently executing command .
124128 execPromiseResult_ : undefined , // Return value of promise.
125129 execPromiseError_ : undefined , // Error from promise.
126130
@@ -137,20 +141,19 @@ export class DebugHelper {
137141 execute_ : function ( execFn_ : Function ) {
138142 this . execPromiseResult_ = this . execPromiseError_ = undefined ;
139143
140- this . execPromise_ = this . execPromise_ . then ( execFn_ ) . then (
144+ this . execPromise_ = execFn_ ( ) ;
145+ // Note: This needs to be added after setting execPromise to execFn,
146+ // or else we cause this.execPromise_ to get stuck in pending mode
147+ // at our next breakpoint.
148+ this . execPromise_ . then (
141149 ( result : Object ) => {
142150 this . execPromiseResult_ = result ;
151+ breakpointHook ( ) ;
143152 } ,
144153 ( err : Error ) => {
145154 this . execPromiseError_ = err ;
155+ breakpointHook ( ) ;
146156 } ) ;
147-
148- // This dummy command is necessary so that the DeferredExecutor.execute
149- // break point can find something to stop at instead of moving on to the
150- // next real command.
151- this . execPromise_ . then ( ( ) => {
152- return browserUnderDebug . executeScriptWithDescription ( '' , 'empty debugger hook' ) ;
153- } ) ;
154157 } ,
155158
156159 // Execute a piece of code.
@@ -159,7 +162,12 @@ export class DebugHelper {
159162 let execFn_ = ( ) => {
160163 // Run code through vm so that we can maintain a local scope which is
161164 // isolated from the rest of the execution.
162- let res = vm_ . runInContext ( code , sandbox ) ;
165+ let res ;
166+ try {
167+ res = vm_ . runInContext ( code , sandbox ) ;
168+ } catch ( e ) {
169+ res = 'Error while evaluating command: ' + e ;
170+ }
163171 if ( ! wdpromise . isPromise ( res ) ) {
164172 res = wdpromise . fulfilled ( res ) ;
165173 }
@@ -190,14 +198,14 @@ export class DebugHelper {
190198 deferred . fulfill ( JSON . stringify ( res ) ) ;
191199 }
192200 } ) ;
193- return deferred ;
201+ return deferred . promise ;
194202 } ;
195203 this . execute_ ( execFn_ ) ;
196204 } ,
197205
198206 // Code finished executing.
199207 resultReady : function ( ) {
200- return ! this . execPromise_ . isPending ( ) ;
208+ return ! ( this . execPromise_ . state_ === 'pending' ) ;
201209 } ,
202210
203211 // Get asynchronous results synchronously.
@@ -213,7 +221,7 @@ export class DebugHelper {
213221 }
214222 } ;
215223
216- return pausePromise ;
224+ return executePromise ;
217225 }
218226
219227 /**
@@ -227,7 +235,7 @@ export class DebugHelper {
227235 * is done. The promise will resolve to a boolean which represents whether
228236 * this is the first time that the debugger is called.
229237 */
230- private validatePortAvailability_ ( port : number ) : wdpromise . Promise < any > {
238+ private validatePortAvailability_ ( port : number ) : wdpromise . Promise < boolean > {
231239 if ( this . debuggerValidated_ ) {
232240 return wdpromise . fulfilled ( false ) ;
233241 }
@@ -256,8 +264,9 @@ export class DebugHelper {
256264 } ) ;
257265
258266 return doneDeferred . promise . then (
259- ( ) => {
267+ ( firstTime : boolean ) => {
260268 this . debuggerValidated_ = true ;
269+ return firstTime ;
261270 } ,
262271 ( err : string ) => {
263272 console . error ( err ) ;
0 commit comments