@@ -7,6 +7,7 @@ import {Locator} from './locators';
7
7
import { Logger } from './logger' ;
8
8
import { Ptor } from './ptor' ;
9
9
import * as helper from './util' ;
10
+ let breakpointHook = require ( './breakpointhook.js' ) ;
10
11
11
12
declare var global : any ;
12
13
declare var process : any ;
@@ -25,32 +26,36 @@ export class DebugHelper {
25
26
26
27
constructor ( private browserUnderDebug_ : ProtractorBrowser ) { }
27
28
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
+
28
38
/**
29
39
* 1) Set up helper functions for debugger clients to call on (e.g.
30
- * getControlFlowText, execute code, get autocompletion).
40
+ * execute code, get autocompletion).
31
41
* 2) Enter process into debugger mode. (i.e. process._debugProcess).
32
42
* 3) Invoke the debugger client specified by debuggerClientPath.
33
43
*
34
44
* @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.
35
47
* @param {Function } onStartFn Function to call when the debugger starts. The
36
48
* function takes a single parameter, which represents whether this is the
37
49
* first time that the debugger is called.
38
50
* @param {number= } opt_debugPort Optional port to use for the debugging
39
51
* 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.
40
55
*/
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 ) {
54
59
const vm_ = require ( 'vm' ) ;
55
60
let flow = wdpromise . controlFlow ( ) ;
56
61
@@ -75,8 +80,11 @@ export class DebugHelper {
75
80
}
76
81
let sandbox = vm_ . createContext ( context ) ;
77
82
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 ( ( ) => {
80
88
process [ 'debugPort' ] = opt_debugPort || process [ 'debugPort' ] ;
81
89
this . validatePortAvailability_ ( process [ 'debugPort' ] ) . then ( ( firstTime : boolean ) => {
82
90
onStartFn ( firstTime ) ;
@@ -93,34 +101,30 @@ export class DebugHelper {
93
101
. on ( 'message' ,
94
102
( m : string ) => {
95
103
if ( m === 'ready' ) {
96
- debuggerReadyPromise . fulfill ( ) ;
104
+ breakpointHook ( ) ;
105
+ if ( ! blockUntilExit ) {
106
+ debuggingDone . fulfill ( ) ;
107
+ }
97
108
}
98
109
} )
99
110
. on ( 'exit' , ( ) => {
100
- logger . info ( 'Debugger exiting' ) ;
101
111
// Clear this so that we know it's ok to attach a debugger
102
112
// again.
103
113
this . dbgCodeExecutor = null ;
114
+ debuggingDone . fulfill ( ) ;
104
115
} ) ;
105
116
} ) ;
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' ) ;
114
119
115
120
// 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
118
123
// flow, so that we can insert frames into it.
119
124
// 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.
122
126
this . dbgCodeExecutor = {
123
- execPromise_ : pausePromise , // Promise pointing to current stage of flow .
127
+ execPromise_ : undefined , // Promise pointing to currently executing command .
124
128
execPromiseResult_ : undefined , // Return value of promise.
125
129
execPromiseError_ : undefined , // Error from promise.
126
130
@@ -137,20 +141,19 @@ export class DebugHelper {
137
141
execute_ : function ( execFn_ : Function ) {
138
142
this . execPromiseResult_ = this . execPromiseError_ = undefined ;
139
143
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 (
141
149
( result : Object ) => {
142
150
this . execPromiseResult_ = result ;
151
+ breakpointHook ( ) ;
143
152
} ,
144
153
( err : Error ) => {
145
154
this . execPromiseError_ = err ;
155
+ breakpointHook ( ) ;
146
156
} ) ;
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
- } ) ;
154
157
} ,
155
158
156
159
// Execute a piece of code.
@@ -159,7 +162,12 @@ export class DebugHelper {
159
162
let execFn_ = ( ) => {
160
163
// Run code through vm so that we can maintain a local scope which is
161
164
// 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
+ }
163
171
if ( ! wdpromise . isPromise ( res ) ) {
164
172
res = wdpromise . fulfilled ( res ) ;
165
173
}
@@ -190,14 +198,14 @@ export class DebugHelper {
190
198
deferred . fulfill ( JSON . stringify ( res ) ) ;
191
199
}
192
200
} ) ;
193
- return deferred ;
201
+ return deferred . promise ;
194
202
} ;
195
203
this . execute_ ( execFn_ ) ;
196
204
} ,
197
205
198
206
// Code finished executing.
199
207
resultReady : function ( ) {
200
- return ! this . execPromise_ . isPending ( ) ;
208
+ return ! ( this . execPromise_ . state_ === 'pending' ) ;
201
209
} ,
202
210
203
211
// Get asynchronous results synchronously.
@@ -213,7 +221,7 @@ export class DebugHelper {
213
221
}
214
222
} ;
215
223
216
- return pausePromise ;
224
+ return executePromise ;
217
225
}
218
226
219
227
/**
@@ -227,7 +235,7 @@ export class DebugHelper {
227
235
* is done. The promise will resolve to a boolean which represents whether
228
236
* this is the first time that the debugger is called.
229
237
*/
230
- private validatePortAvailability_ ( port : number ) : wdpromise . Promise < any > {
238
+ private validatePortAvailability_ ( port : number ) : wdpromise . Promise < boolean > {
231
239
if ( this . debuggerValidated_ ) {
232
240
return wdpromise . fulfilled ( false ) ;
233
241
}
@@ -256,8 +264,9 @@ export class DebugHelper {
256
264
} ) ;
257
265
258
266
return doneDeferred . promise . then (
259
- ( ) => {
267
+ ( firstTime : boolean ) => {
260
268
this . debuggerValidated_ = true ;
269
+ return firstTime ;
261
270
} ,
262
271
( err : string ) => {
263
272
console . error ( err ) ;
0 commit comments