OLD | NEW |
| (Empty) |
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 /** | |
6 * @fileoverview Provides communication interface to remote v8 debugger. See | |
7 * protocol decription at http://code.google.com/p/v8/wiki/DebuggerProtocol | |
8 */ | |
9 goog.provide('devtools.DebuggerAgent'); | |
10 | |
11 | |
12 /** | |
13 * @constructor | |
14 */ | |
15 devtools.DebuggerAgent = function() { | |
16 RemoteDebuggerAgent.DebuggerOutput = | |
17 goog.bind(this.handleDebuggerOutput_, this); | |
18 RemoteDebuggerAgent.SetContextId = | |
19 goog.bind(this.setContextId_, this); | |
20 RemoteDebuggerAgent.DidGetActiveProfilerModules = | |
21 goog.bind(this.didGetActiveProfilerModules_, this); | |
22 RemoteDebuggerAgent.DidGetNextLogLines = | |
23 goog.bind(this.didGetNextLogLines_, this); | |
24 | |
25 /** | |
26 * Id of the inspected page global context. It is used for filtering scripts. | |
27 * @type {number} | |
28 */ | |
29 this.contextId_ = null; | |
30 | |
31 /** | |
32 * Mapping from script id to script info. | |
33 * @type {Object} | |
34 */ | |
35 this.parsedScripts_ = null; | |
36 | |
37 /** | |
38 * Mapping from the request id to the devtools.BreakpointInfo for the | |
39 * breakpoints whose v8 ids are not set yet. These breakpoints are waiting for | |
40 * 'setbreakpoint' responses to learn their ids in the v8 debugger. | |
41 * @see #handleSetBreakpointResponse_ | |
42 * @type {Object} | |
43 */ | |
44 this.requestNumberToBreakpointInfo_ = null; | |
45 | |
46 /** | |
47 * Information on current stack frames. | |
48 * @type {Array.<devtools.CallFrame>} | |
49 */ | |
50 this.callFrames_ = []; | |
51 | |
52 /** | |
53 * Whether to stop in the debugger on the exceptions. | |
54 * @type {boolean} | |
55 */ | |
56 this.pauseOnExceptions_ = true; | |
57 | |
58 /** | |
59 * Mapping: request sequence number->callback. | |
60 * @type {Object} | |
61 */ | |
62 this.requestSeqToCallback_ = null; | |
63 | |
64 /** | |
65 * Whether the scripts list has been requested. | |
66 * @type {boolean} | |
67 */ | |
68 this.scriptsCacheInitialized_ = false; | |
69 | |
70 /** | |
71 * Whether the scripts list should be requested next time when context id is | |
72 * set. | |
73 * @type {boolean} | |
74 */ | |
75 this.requestScriptsWhenContextIdSet_ = false; | |
76 | |
77 /** | |
78 * Active profiler modules flags. | |
79 * @type {number} | |
80 */ | |
81 this.activeProfilerModules_ = | |
82 devtools.DebuggerAgent.ProfilerModules.PROFILER_MODULE_NONE; | |
83 | |
84 /** | |
85 * Profiler processor instance. | |
86 * @type {devtools.profiler.Processor} | |
87 */ | |
88 this.profilerProcessor_ = new devtools.profiler.Processor(); | |
89 | |
90 /** | |
91 * Container of all breakpoints set using resource URL. These breakpoints | |
92 * survive page reload. Breakpoints set by script id(for scripts that don't | |
93 * have URLs) are stored in ScriptInfo objects. | |
94 * @type {Object} | |
95 */ | |
96 this.urlToBreakpoints_ = {}; | |
97 | |
98 | |
99 /** | |
100 * Exception message that is shown to user while on exception break. | |
101 * @type {WebInspector.ConsoleMessage} | |
102 */ | |
103 this.currentExceptionMessage_ = null; | |
104 }; | |
105 | |
106 | |
107 /** | |
108 * A copy of the scope types from v8/src/mirror-delay.js | |
109 * @enum {number} | |
110 */ | |
111 devtools.DebuggerAgent.ScopeType = { | |
112 Global: 0, | |
113 Local: 1, | |
114 With: 2, | |
115 Closure: 3, | |
116 Catch: 4 | |
117 }; | |
118 | |
119 | |
120 /** | |
121 * A copy of enum from include/v8.h | |
122 * @enum {number} | |
123 */ | |
124 devtools.DebuggerAgent.ProfilerModules = { | |
125 PROFILER_MODULE_NONE: 0, | |
126 PROFILER_MODULE_CPU: 1, | |
127 PROFILER_MODULE_HEAP_STATS: 1 << 1, | |
128 PROFILER_MODULE_JS_CONSTRUCTORS: 1 << 2, | |
129 PROFILER_MODULE_HEAP_SNAPSHOT: 1 << 16 | |
130 }; | |
131 | |
132 | |
133 /** | |
134 * Resets debugger agent to its initial state. | |
135 */ | |
136 devtools.DebuggerAgent.prototype.reset = function() { | |
137 this.contextId_ = null; | |
138 // No need to request scripts since they all will be pushed in AfterCompile | |
139 // events. | |
140 this.requestScriptsWhenContextIdSet_ = false; | |
141 this.parsedScripts_ = {}; | |
142 this.requestNumberToBreakpointInfo_ = {}; | |
143 this.callFrames_ = []; | |
144 this.requestSeqToCallback_ = {}; | |
145 | |
146 // Profiler isn't reset because it contains no data that is | |
147 // specific for a particular V8 instance. All such data is | |
148 // managed by an agent on the Render's side. | |
149 }; | |
150 | |
151 | |
152 /** | |
153 * Initializes scripts UI. This method is called every time Scripts panel | |
154 * is shown. It will send request for context id if it's not set yet. | |
155 */ | |
156 devtools.DebuggerAgent.prototype.initUI = function() { | |
157 // Initialize scripts cache when Scripts panel is shown first time. | |
158 if (this.scriptsCacheInitialized_) { | |
159 return; | |
160 } | |
161 this.scriptsCacheInitialized_ = true; | |
162 if (this.contextId_) { | |
163 // We already have context id. This means that we are here from the | |
164 // very beginning of the page load cycle and hence will get all scripts | |
165 // via after-compile events. No need to request scripts for this session. | |
166 return; | |
167 } | |
168 // Script list should be requested only when current context id is known. | |
169 RemoteDebuggerAgent.GetContextId(); | |
170 this.requestScriptsWhenContextIdSet_ = true; | |
171 }; | |
172 | |
173 | |
174 /** | |
175 * Asynchronously requests the debugger for the script source. | |
176 * @param {number} scriptId Id of the script whose source should be resolved. | |
177 * @param {function(source:?string):void} callback Function that will be called | |
178 * when the source resolution is completed. 'source' parameter will be null | |
179 * if the resolution fails. | |
180 */ | |
181 devtools.DebuggerAgent.prototype.resolveScriptSource = function( | |
182 scriptId, callback) { | |
183 var script = this.parsedScripts_[scriptId]; | |
184 if (!script || script.isUnresolved()) { | |
185 callback(null); | |
186 return; | |
187 } | |
188 | |
189 var cmd = new devtools.DebugCommand('scripts', { | |
190 'ids': [scriptId], | |
191 'includeSource': true | |
192 }); | |
193 devtools.DebuggerAgent.sendCommand_(cmd); | |
194 // Force v8 execution so that it gets to processing the requested command. | |
195 RemoteToolsAgent.ExecuteVoidJavaScript(); | |
196 | |
197 this.requestSeqToCallback_[cmd.getSequenceNumber()] = function(msg) { | |
198 if (msg.isSuccess()) { | |
199 var scriptJson = msg.getBody()[0]; | |
200 callback(scriptJson.source); | |
201 } else { | |
202 callback(null); | |
203 } | |
204 }; | |
205 }; | |
206 | |
207 | |
208 /** | |
209 * Tells the v8 debugger to stop on as soon as possible. | |
210 */ | |
211 devtools.DebuggerAgent.prototype.pauseExecution = function() { | |
212 RemoteDebuggerAgent.DebugBreak(); | |
213 }; | |
214 | |
215 | |
216 /** | |
217 * @param {number} sourceId Id of the script fot the breakpoint. | |
218 * @param {number} line Number of the line for the breakpoint. | |
219 * @param {?string} condition The breakpoint condition. | |
220 */ | |
221 devtools.DebuggerAgent.prototype.addBreakpoint = function( | |
222 sourceId, line, condition) { | |
223 var script = this.parsedScripts_[sourceId]; | |
224 if (!script) { | |
225 return; | |
226 } | |
227 | |
228 line = devtools.DebuggerAgent.webkitToV8LineNumber_(line); | |
229 | |
230 var commandArguments; | |
231 if (script.getUrl()) { | |
232 var breakpoints = this.urlToBreakpoints_[script.getUrl()]; | |
233 if (breakpoints && breakpoints[line]) { | |
234 return; | |
235 } | |
236 if (!breakpoints) { | |
237 breakpoints = {}; | |
238 this.urlToBreakpoints_[script.getUrl()] = breakpoints; | |
239 } | |
240 | |
241 var breakpointInfo = new devtools.BreakpointInfo(line); | |
242 breakpoints[line] = breakpointInfo; | |
243 | |
244 commandArguments = { | |
245 'groupId': this.contextId_, | |
246 'type': 'script', | |
247 'target': script.getUrl(), | |
248 'line': line, | |
249 'condition': condition | |
250 }; | |
251 } else { | |
252 var breakpointInfo = script.getBreakpointInfo(line); | |
253 if (breakpointInfo) { | |
254 return; | |
255 } | |
256 | |
257 breakpointInfo = new devtools.BreakpointInfo(line); | |
258 script.addBreakpointInfo(breakpointInfo); | |
259 | |
260 commandArguments = { | |
261 'groupId': this.contextId_, | |
262 'type': 'scriptId', | |
263 'target': sourceId, | |
264 'line': line, | |
265 'condition': condition | |
266 }; | |
267 } | |
268 | |
269 var cmd = new devtools.DebugCommand('setbreakpoint', commandArguments); | |
270 | |
271 this.requestNumberToBreakpointInfo_[cmd.getSequenceNumber()] = breakpointInfo; | |
272 | |
273 devtools.DebuggerAgent.sendCommand_(cmd); | |
274 // Force v8 execution so that it gets to processing the requested command. | |
275 // It is necessary for being able to change a breakpoint just after it | |
276 // has been created (since we need an existing breakpoint id for that). | |
277 RemoteToolsAgent.ExecuteVoidJavaScript(); | |
278 }; | |
279 | |
280 | |
281 /** | |
282 * @param {number} sourceId Id of the script for the breakpoint. | |
283 * @param {number} line Number of the line for the breakpoint. | |
284 */ | |
285 devtools.DebuggerAgent.prototype.removeBreakpoint = function(sourceId, line) { | |
286 var script = this.parsedScripts_[sourceId]; | |
287 if (!script) { | |
288 return; | |
289 } | |
290 | |
291 line = devtools.DebuggerAgent.webkitToV8LineNumber_(line); | |
292 | |
293 var breakpointInfo; | |
294 if (script.getUrl()) { | |
295 var breakpoints = this.urlToBreakpoints_[script.getUrl()]; | |
296 breakpointInfo = breakpoints[line]; | |
297 delete breakpoints[line]; | |
298 } else { | |
299 breakpointInfo = script.getBreakpointInfo(line); | |
300 if (breakpointInfo) { | |
301 script.removeBreakpointInfo(breakpointInfo); | |
302 } | |
303 } | |
304 | |
305 if (!breakpointInfo) { | |
306 return; | |
307 } | |
308 | |
309 breakpointInfo.markAsRemoved(); | |
310 | |
311 var id = breakpointInfo.getV8Id(); | |
312 | |
313 // If we don't know id of this breakpoint in the v8 debugger we cannot send | |
314 // 'clearbreakpoint' request. In that case it will be removed in | |
315 // 'setbreakpoint' response handler when we learn the id. | |
316 if (id != -1) { | |
317 this.requestClearBreakpoint_(id); | |
318 } | |
319 }; | |
320 | |
321 | |
322 /** | |
323 * @param {number} sourceId Id of the script for the breakpoint. | |
324 * @param {number} line Number of the line for the breakpoint. | |
325 * @param {?string} condition New breakpoint condition. | |
326 */ | |
327 devtools.DebuggerAgent.prototype.updateBreakpoint = function( | |
328 sourceId, line, condition) { | |
329 var script = this.parsedScripts_[sourceId]; | |
330 if (!script) { | |
331 return; | |
332 } | |
333 | |
334 line = devtools.DebuggerAgent.webkitToV8LineNumber_(line); | |
335 | |
336 var breakpointInfo; | |
337 if (script.getUrl()) { | |
338 var breakpoints = this.urlToBreakpoints_[script.getUrl()]; | |
339 breakpointInfo = breakpoints[line]; | |
340 } else { | |
341 breakpointInfo = script.getBreakpointInfo(line); | |
342 } | |
343 | |
344 var id = breakpointInfo.getV8Id(); | |
345 | |
346 // If we don't know id of this breakpoint in the v8 debugger we cannot send | |
347 // the 'changebreakpoint' request. | |
348 if (id != -1) { | |
349 // TODO(apavlov): make use of the real values for 'enabled' and | |
350 // 'ignoreCount' when appropriate. | |
351 this.requestChangeBreakpoint_(id, true, condition, null); | |
352 } | |
353 }; | |
354 | |
355 | |
356 /** | |
357 * Tells the v8 debugger to step into the next statement. | |
358 */ | |
359 devtools.DebuggerAgent.prototype.stepIntoStatement = function() { | |
360 this.stepCommand_('in'); | |
361 }; | |
362 | |
363 | |
364 /** | |
365 * Tells the v8 debugger to step out of current function. | |
366 */ | |
367 devtools.DebuggerAgent.prototype.stepOutOfFunction = function() { | |
368 this.stepCommand_('out'); | |
369 }; | |
370 | |
371 | |
372 /** | |
373 * Tells the v8 debugger to step over the next statement. | |
374 */ | |
375 devtools.DebuggerAgent.prototype.stepOverStatement = function() { | |
376 this.stepCommand_('next'); | |
377 }; | |
378 | |
379 | |
380 /** | |
381 * Tells the v8 debugger to continue execution after it has been stopped on a | |
382 * breakpoint or an exception. | |
383 */ | |
384 devtools.DebuggerAgent.prototype.resumeExecution = function() { | |
385 this.clearExceptionMessage_(); | |
386 var cmd = new devtools.DebugCommand('continue'); | |
387 devtools.DebuggerAgent.sendCommand_(cmd); | |
388 }; | |
389 | |
390 | |
391 /** | |
392 * Creates exception message and schedules it for addition to the resource upon | |
393 * backtrace availability. | |
394 * @param {string} url Resource url. | |
395 * @param {number} line Resource line number. | |
396 * @param {string} message Exception text. | |
397 */ | |
398 devtools.DebuggerAgent.prototype.createExceptionMessage_ = function( | |
399 url, line, message) { | |
400 this.currentExceptionMessage_ = new WebInspector.ConsoleMessage( | |
401 WebInspector.ConsoleMessage.MessageSource.JS, | |
402 WebInspector.ConsoleMessage.MessageType.Log, | |
403 WebInspector.ConsoleMessage.MessageLevel.Error, | |
404 line, | |
405 url, | |
406 0 /* group level */, | |
407 1 /* repeat count */, | |
408 '[Exception] ' + message); | |
409 }; | |
410 | |
411 | |
412 /** | |
413 * Shows pending exception message that is created with createExceptionMessage_ | |
414 * earlier. | |
415 */ | |
416 devtools.DebuggerAgent.prototype.showPendingExceptionMessage_ = function() { | |
417 if (!this.currentExceptionMessage_) { | |
418 return; | |
419 } | |
420 var msg = this.currentExceptionMessage_; | |
421 var resource = WebInspector.resourceURLMap[msg.url]; | |
422 if (resource) { | |
423 msg.resource = resource; | |
424 WebInspector.panels.resources.addMessageToResource(resource, msg); | |
425 } else { | |
426 this.currentExceptionMessage_ = null; | |
427 } | |
428 }; | |
429 | |
430 | |
431 /** | |
432 * Clears exception message from the resource. | |
433 */ | |
434 devtools.DebuggerAgent.prototype.clearExceptionMessage_ = function() { | |
435 if (this.currentExceptionMessage_) { | |
436 var messageElement = | |
437 this.currentExceptionMessage_._resourceMessageLineElement; | |
438 var bubble = messageElement.parentElement; | |
439 bubble.removeChild(messageElement); | |
440 if (!bubble.firstChild) { | |
441 // Last message in bubble removed. | |
442 bubble.parentElement.removeChild(bubble); | |
443 } | |
444 this.currentExceptionMessage_ = null; | |
445 } | |
446 }; | |
447 | |
448 | |
449 /** | |
450 * @return {boolean} True iff the debugger will pause execution on the | |
451 * exceptions. | |
452 */ | |
453 devtools.DebuggerAgent.prototype.pauseOnExceptions = function() { | |
454 return this.pauseOnExceptions_; | |
455 }; | |
456 | |
457 | |
458 /** | |
459 * Tells whether to pause in the debugger on the exceptions or not. | |
460 * @param {boolean} value True iff execution should be stopped in the debugger | |
461 * on the exceptions. | |
462 */ | |
463 devtools.DebuggerAgent.prototype.setPauseOnExceptions = function(value) { | |
464 this.pauseOnExceptions_ = value; | |
465 }; | |
466 | |
467 | |
468 /** | |
469 * Sends 'evaluate' request to the debugger. | |
470 * @param {Object} arguments Request arguments map. | |
471 * @param {function(devtools.DebuggerMessage)} callback Callback to be called | |
472 * when response is received. | |
473 */ | |
474 devtools.DebuggerAgent.prototype.requestEvaluate = function( | |
475 arguments, callback) { | |
476 var cmd = new devtools.DebugCommand('evaluate', arguments); | |
477 devtools.DebuggerAgent.sendCommand_(cmd); | |
478 this.requestSeqToCallback_[cmd.getSequenceNumber()] = callback; | |
479 }; | |
480 | |
481 | |
482 /** | |
483 * Sends 'lookup' request for each unresolved property of the object. When | |
484 * response is received the properties will be changed with their resolved | |
485 * values. | |
486 * @param {Object} object Object whose properties should be resolved. | |
487 * @param {function(devtools.DebuggerMessage)} Callback to be called when all | |
488 * children are resolved. | |
489 * @param {boolean} noIntrinsic Whether intrinsic properties should be included. | |
490 */ | |
491 devtools.DebuggerAgent.prototype.resolveChildren = function(object, callback, | |
492 noIntrinsic) { | |
493 if ('handle' in object) { | |
494 var result = []; | |
495 devtools.DebuggerAgent.formatObjectProperties_(object, result, | |
496 noIntrinsic); | |
497 callback(result); | |
498 } else { | |
499 this.requestLookup_([object.ref], function(msg) { | |
500 var result = []; | |
501 if (msg.isSuccess()) { | |
502 var handleToObject = msg.getBody(); | |
503 var resolved = handleToObject[object.ref]; | |
504 devtools.DebuggerAgent.formatObjectProperties_(resolved, result, | |
505 noIntrinsic); | |
506 callback(result); | |
507 } else { | |
508 callback([]); | |
509 } | |
510 }); | |
511 } | |
512 }; | |
513 | |
514 | |
515 /** | |
516 * Sends 'scope' request for the scope object to resolve its variables. | |
517 * @param {Object} scope Scope to be resolved. | |
518 * @param {function(Array.<WebInspector.ObjectPropertyProxy>)} callback | |
519 * Callback to be called when all scope variables are resolved. | |
520 */ | |
521 devtools.DebuggerAgent.prototype.resolveScope = function(scope, callback) { | |
522 var cmd = new devtools.DebugCommand('scope', { | |
523 'frameNumber': scope.frameNumber, | |
524 'number': scope.index, | |
525 'compactFormat': true | |
526 }); | |
527 devtools.DebuggerAgent.sendCommand_(cmd); | |
528 this.requestSeqToCallback_[cmd.getSequenceNumber()] = function(msg) { | |
529 var result = []; | |
530 if (msg.isSuccess()) { | |
531 var scopeObjectJson = msg.getBody().object; | |
532 devtools.DebuggerAgent.formatObjectProperties_(scopeObjectJson, result, | |
533 true /* no intrinsic */); | |
534 } | |
535 callback(result); | |
536 }; | |
537 }; | |
538 | |
539 | |
540 /** | |
541 * Sends 'scopes' request for the frame object to resolve all variables | |
542 * available in the frame. | |
543 * @param {number} callFrameId Id of call frame whose variables need to | |
544 * be resolved. | |
545 * @param {function(Object)} callback Callback to be called when all frame | |
546 * variables are resolved. | |
547 */ | |
548 devtools.DebuggerAgent.prototype.resolveFrameVariables_ = function( | |
549 callFrameId, callback) { | |
550 var result = {}; | |
551 | |
552 var frame = this.callFrames_[callFrameId]; | |
553 if (!frame) { | |
554 callback(result); | |
555 return; | |
556 } | |
557 | |
558 var waitingResponses = 0; | |
559 function scopeResponseHandler(msg) { | |
560 waitingResponses--; | |
561 | |
562 if (msg.isSuccess()) { | |
563 var properties = msg.getBody().object.properties; | |
564 for (var j = 0; j < properties.length; j++) { | |
565 result[properties[j].name] = true; | |
566 } | |
567 } | |
568 | |
569 // When all scopes are resolved invoke the callback. | |
570 if (waitingResponses == 0) { | |
571 callback(result); | |
572 } | |
573 }; | |
574 | |
575 for (var i = 0; i < frame.scopeChain.length; i++) { | |
576 var scope = frame.scopeChain[i].objectId; | |
577 if (scope.type == devtools.DebuggerAgent.ScopeType.Global) { | |
578 // Do not resolve global scope since it takes for too long. | |
579 // TODO(yurys): allow to send only property names in the response. | |
580 continue; | |
581 } | |
582 var cmd = new devtools.DebugCommand('scope', { | |
583 'frameNumber': scope.frameNumber, | |
584 'number': scope.index, | |
585 'compactFormat': true | |
586 }); | |
587 devtools.DebuggerAgent.sendCommand_(cmd); | |
588 this.requestSeqToCallback_[cmd.getSequenceNumber()] = | |
589 scopeResponseHandler; | |
590 waitingResponses++; | |
591 } | |
592 }; | |
593 | |
594 /** | |
595 * Evaluates the expressionString to an object in the call frame and reports | |
596 * all its properties. | |
597 * @param{string} expressionString Expression whose properties should be | |
598 * collected. | |
599 * @param{number} callFrameId The frame id. | |
600 * @param{function(Object result,bool isException)} reportCompletions Callback | |
601 * function. | |
602 */ | |
603 devtools.DebuggerAgent.prototype.resolveCompletionsOnFrame = function( | |
604 expressionString, callFrameId, reportCompletions) { | |
605 if (expressionString) { | |
606 expressionString = 'var obj = ' + expressionString + | |
607 '; var names = {}; for (var n in obj) { names[n] = true; };' + | |
608 'names;'; | |
609 this.evaluateInCallFrame( | |
610 callFrameId, | |
611 expressionString, | |
612 function(result) { | |
613 var names = {}; | |
614 if (!result.isException) { | |
615 var props = result.value.objectId.properties; | |
616 // Put all object properties into the map. | |
617 for (var i = 0; i < props.length; i++) { | |
618 names[props[i].name] = true; | |
619 } | |
620 } | |
621 reportCompletions(names, result.isException); | |
622 }); | |
623 } else { | |
624 this.resolveFrameVariables_(callFrameId, | |
625 function(result) { | |
626 reportCompletions(result, false /* isException */); | |
627 }); | |
628 } | |
629 }; | |
630 | |
631 | |
632 /** | |
633 * Sets up callbacks that deal with profiles processing. | |
634 */ | |
635 devtools.DebuggerAgent.prototype.setupProfilerProcessorCallbacks = function() { | |
636 // A temporary icon indicating that the profile is being processed. | |
637 var processingIcon = new WebInspector.SidebarTreeElement( | |
638 'profile-sidebar-tree-item', | |
639 WebInspector.UIString('Processing...'), | |
640 '', null, false); | |
641 var profilesSidebar = WebInspector.panels.profiles.getProfileType( | |
642 WebInspector.CPUProfileType.TypeId).treeElement; | |
643 | |
644 this.profilerProcessor_.setCallbacks( | |
645 function onProfileProcessingStarted() { | |
646 // Set visually empty string. Subtitle hiding is done via styles | |
647 // manipulation which doesn't play well with dynamic append / removal. | |
648 processingIcon.subtitle = ' '; | |
649 profilesSidebar.appendChild(processingIcon); | |
650 }, | |
651 function onProfileProcessingStatus(ticksCount) { | |
652 processingIcon.subtitle = | |
653 WebInspector.UIString('%d ticks processed', ticksCount); | |
654 }, | |
655 function onProfileProcessingFinished(profile) { | |
656 profilesSidebar.removeChild(processingIcon); | |
657 WebInspector.addProfile(profile); | |
658 // If no profile is currently shown, show the new one. | |
659 var profilesPanel = WebInspector.panels.profiles; | |
660 if (!profilesPanel.visibleView) { | |
661 profilesPanel.showProfile(profile); | |
662 } | |
663 } | |
664 ); | |
665 }; | |
666 | |
667 | |
668 /** | |
669 * Initializes profiling state. | |
670 */ | |
671 devtools.DebuggerAgent.prototype.initializeProfiling = function() { | |
672 this.setupProfilerProcessorCallbacks(); | |
673 RemoteDebuggerAgent.GetActiveProfilerModules(); | |
674 }; | |
675 | |
676 | |
677 /** | |
678 * Starts profiling. | |
679 * @param {number} modules List of modules to enable. | |
680 */ | |
681 devtools.DebuggerAgent.prototype.startProfiling = function(modules) { | |
682 RemoteDebuggerAgent.StartProfiling(modules); | |
683 if (modules & | |
684 devtools.DebuggerAgent.ProfilerModules.PROFILER_MODULE_HEAP_SNAPSHOT) { | |
685 // Active modules will not change, instead, a snapshot will be logged. | |
686 RemoteDebuggerAgent.GetNextLogLines(); | |
687 } else { | |
688 RemoteDebuggerAgent.GetActiveProfilerModules(); | |
689 } | |
690 }; | |
691 | |
692 | |
693 /** | |
694 * Stops profiling. | |
695 */ | |
696 devtools.DebuggerAgent.prototype.stopProfiling = function(modules) { | |
697 RemoteDebuggerAgent.StopProfiling(modules); | |
698 }; | |
699 | |
700 | |
701 /** | |
702 * @param{number} scriptId | |
703 * @return {string} Type of the context of the script with specified id. | |
704 */ | |
705 devtools.DebuggerAgent.prototype.getScriptContextType = function(scriptId) { | |
706 return this.parsedScripts_[scriptId].getContextType(); | |
707 }; | |
708 | |
709 | |
710 /** | |
711 * Removes specified breakpoint from the v8 debugger. | |
712 * @param {number} breakpointId Id of the breakpoint in the v8 debugger. | |
713 */ | |
714 devtools.DebuggerAgent.prototype.requestClearBreakpoint_ = function( | |
715 breakpointId) { | |
716 var cmd = new devtools.DebugCommand('clearbreakpoint', { | |
717 'breakpoint': breakpointId | |
718 }); | |
719 devtools.DebuggerAgent.sendCommand_(cmd); | |
720 }; | |
721 | |
722 | |
723 /** | |
724 * Changes breakpoint parameters in the v8 debugger. | |
725 * @param {number} breakpointId Id of the breakpoint in the v8 debugger. | |
726 * @param {boolean} enabled Whether to enable the breakpoint. | |
727 * @param {?string} condition New breakpoint condition. | |
728 * @param {number} ignoreCount New ignore count for the breakpoint. | |
729 */ | |
730 devtools.DebuggerAgent.prototype.requestChangeBreakpoint_ = function( | |
731 breakpointId, enabled, condition, ignoreCount) { | |
732 var cmd = new devtools.DebugCommand('changebreakpoint', { | |
733 'breakpoint': breakpointId, | |
734 'enabled': enabled, | |
735 'condition': condition, | |
736 'ignoreCount': ignoreCount | |
737 }); | |
738 devtools.DebuggerAgent.sendCommand_(cmd); | |
739 }; | |
740 | |
741 | |
742 /** | |
743 * Sends 'backtrace' request to v8. | |
744 */ | |
745 devtools.DebuggerAgent.prototype.requestBacktrace_ = function() { | |
746 var cmd = new devtools.DebugCommand('backtrace', { | |
747 'compactFormat':true | |
748 }); | |
749 devtools.DebuggerAgent.sendCommand_(cmd); | |
750 }; | |
751 | |
752 | |
753 /** | |
754 * Sends command to v8 debugger. | |
755 * @param {devtools.DebugCommand} cmd Command to execute. | |
756 */ | |
757 devtools.DebuggerAgent.sendCommand_ = function(cmd) { | |
758 RemoteDebuggerCommandExecutor.DebuggerCommand(cmd.toJSONProtocol()); | |
759 }; | |
760 | |
761 | |
762 /** | |
763 * Tells the v8 debugger to make the next execution step. | |
764 * @param {string} action 'in', 'out' or 'next' action. | |
765 */ | |
766 devtools.DebuggerAgent.prototype.stepCommand_ = function(action) { | |
767 this.clearExceptionMessage_(); | |
768 var cmd = new devtools.DebugCommand('continue', { | |
769 'stepaction': action, | |
770 'stepcount': 1 | |
771 }); | |
772 devtools.DebuggerAgent.sendCommand_(cmd); | |
773 }; | |
774 | |
775 | |
776 /** | |
777 * Sends 'lookup' request to v8. | |
778 * @param {number} handle Handle to the object to lookup. | |
779 */ | |
780 devtools.DebuggerAgent.prototype.requestLookup_ = function(handles, callback) { | |
781 var cmd = new devtools.DebugCommand('lookup', { | |
782 'compactFormat':true, | |
783 'handles': handles | |
784 }); | |
785 devtools.DebuggerAgent.sendCommand_(cmd); | |
786 this.requestSeqToCallback_[cmd.getSequenceNumber()] = callback; | |
787 }; | |
788 | |
789 | |
790 /** | |
791 * Sets debugger context id for scripts filtering. | |
792 * @param {number} contextId Id of the inspected page global context. | |
793 */ | |
794 devtools.DebuggerAgent.prototype.setContextId_ = function(contextId) { | |
795 this.contextId_ = contextId; | |
796 | |
797 // If it's the first time context id is set request scripts list. | |
798 if (this.requestScriptsWhenContextIdSet_) { | |
799 this.requestScriptsWhenContextIdSet_ = false; | |
800 var cmd = new devtools.DebugCommand('scripts', { | |
801 'includeSource': false | |
802 }); | |
803 devtools.DebuggerAgent.sendCommand_(cmd); | |
804 // Force v8 execution so that it gets to processing the requested command. | |
805 RemoteToolsAgent.ExecuteVoidJavaScript(); | |
806 | |
807 var debuggerAgent = this; | |
808 this.requestSeqToCallback_[cmd.getSequenceNumber()] = function(msg) { | |
809 // Handle the response iff the context id hasn't changed since the request | |
810 // was issued. Otherwise if the context id did change all up-to-date | |
811 // scripts will be pushed in after compile events and there is no need to | |
812 // handle the response. | |
813 if (contextId == debuggerAgent.contextId_) { | |
814 debuggerAgent.handleScriptsResponse_(msg); | |
815 } | |
816 }; | |
817 } | |
818 }; | |
819 | |
820 | |
821 /** | |
822 * Handles output sent by v8 debugger. The output is either asynchronous event | |
823 * or response to a previously sent request. See protocol definitioun for more | |
824 * details on the output format. | |
825 * @param {string} output | |
826 */ | |
827 devtools.DebuggerAgent.prototype.handleDebuggerOutput_ = function(output) { | |
828 var msg; | |
829 try { | |
830 msg = new devtools.DebuggerMessage(output); | |
831 } catch(e) { | |
832 debugPrint('Failed to handle debugger reponse:\n' + e); | |
833 throw e; | |
834 } | |
835 | |
836 if (msg.getType() == 'event') { | |
837 if (msg.getEvent() == 'break') { | |
838 this.handleBreakEvent_(msg); | |
839 } else if (msg.getEvent() == 'exception') { | |
840 this.handleExceptionEvent_(msg); | |
841 } else if (msg.getEvent() == 'afterCompile') { | |
842 this.handleAfterCompileEvent_(msg); | |
843 } | |
844 } else if (msg.getType() == 'response') { | |
845 if (msg.getCommand() == 'scripts') { | |
846 this.invokeCallbackForResponse_(msg); | |
847 } else if (msg.getCommand() == 'setbreakpoint') { | |
848 this.handleSetBreakpointResponse_(msg); | |
849 } else if (msg.getCommand() == 'clearbreakpoint') { | |
850 this.handleClearBreakpointResponse_(msg); | |
851 } else if (msg.getCommand() == 'backtrace') { | |
852 this.handleBacktraceResponse_(msg); | |
853 } else if (msg.getCommand() == 'lookup') { | |
854 this.invokeCallbackForResponse_(msg); | |
855 } else if (msg.getCommand() == 'evaluate') { | |
856 this.invokeCallbackForResponse_(msg); | |
857 } else if (msg.getCommand() == 'scope') { | |
858 this.invokeCallbackForResponse_(msg); | |
859 } | |
860 } | |
861 }; | |
862 | |
863 | |
864 /** | |
865 * @param {devtools.DebuggerMessage} msg | |
866 */ | |
867 devtools.DebuggerAgent.prototype.handleBreakEvent_ = function(msg) { | |
868 // Force scrips panel to be shown first. | |
869 WebInspector.currentPanel = WebInspector.panels.scripts; | |
870 | |
871 var body = msg.getBody(); | |
872 | |
873 var line = devtools.DebuggerAgent.v8ToWwebkitLineNumber_(body.sourceLine); | |
874 this.requestBacktrace_(); | |
875 }; | |
876 | |
877 | |
878 /** | |
879 * @param {devtools.DebuggerMessage} msg | |
880 */ | |
881 devtools.DebuggerAgent.prototype.handleExceptionEvent_ = function(msg) { | |
882 // Force scrips panel to be shown first. | |
883 WebInspector.currentPanel = WebInspector.panels.scripts; | |
884 | |
885 var body = msg.getBody(); | |
886 // No script field in the body means that v8 failed to parse the script. We | |
887 // resume execution on parser errors automatically. | |
888 if (this.pauseOnExceptions_ && body.script) { | |
889 var line = devtools.DebuggerAgent.v8ToWwebkitLineNumber_(body.sourceLine); | |
890 this.createExceptionMessage_(body.script.name, line, body.exception.text); | |
891 this.requestBacktrace_(); | |
892 } else { | |
893 this.resumeExecution(); | |
894 } | |
895 }; | |
896 | |
897 | |
898 /** | |
899 * @param {devtools.DebuggerMessage} msg | |
900 */ | |
901 devtools.DebuggerAgent.prototype.handleScriptsResponse_ = function(msg) { | |
902 var scripts = msg.getBody(); | |
903 for (var i = 0; i < scripts.length; i++) { | |
904 var script = scripts[i]; | |
905 | |
906 // Skip scripts from other tabs. | |
907 if (!this.isScriptFromInspectedContext_(script, msg)) { | |
908 continue; | |
909 } | |
910 | |
911 // We may already have received the info in an afterCompile event. | |
912 if (script.id in this.parsedScripts_) { | |
913 continue; | |
914 } | |
915 this.addScriptInfo_(script, msg); | |
916 } | |
917 }; | |
918 | |
919 | |
920 /** | |
921 * @param {Object} script Json object representing script. | |
922 * @param {devtools.DebuggerMessage} msg Debugger response. | |
923 */ | |
924 devtools.DebuggerAgent.prototype.isScriptFromInspectedContext_ = function( | |
925 script, msg) { | |
926 if (!script.context) { | |
927 // Always ignore scripts from the utility context. | |
928 return false; | |
929 } | |
930 var context = msg.lookup(script.context.ref); | |
931 var scriptContextId = context.data; | |
932 if (!goog.isDef(scriptContextId)) { | |
933 return false; // Always ignore scripts from the utility context. | |
934 } | |
935 if (this.contextId_ === null) { | |
936 return true; | |
937 } | |
938 return (scriptContextId.value == this.contextId_); | |
939 }; | |
940 | |
941 | |
942 /** | |
943 * @param {devtools.DebuggerMessage} msg | |
944 */ | |
945 devtools.DebuggerAgent.prototype.handleSetBreakpointResponse_ = function(msg) { | |
946 var requestSeq = msg.getRequestSeq(); | |
947 var breakpointInfo = this.requestNumberToBreakpointInfo_[requestSeq]; | |
948 if (!breakpointInfo) { | |
949 // TODO(yurys): handle this case | |
950 return; | |
951 } | |
952 delete this.requestNumberToBreakpointInfo_[requestSeq]; | |
953 if (!msg.isSuccess()) { | |
954 // TODO(yurys): handle this case | |
955 return; | |
956 } | |
957 var idInV8 = msg.getBody().breakpoint; | |
958 breakpointInfo.setV8Id(idInV8); | |
959 | |
960 if (breakpointInfo.isRemoved()) { | |
961 this.requestClearBreakpoint_(idInV8); | |
962 } | |
963 }; | |
964 | |
965 | |
966 /** | |
967 * @param {devtools.DebuggerMessage} msg | |
968 */ | |
969 devtools.DebuggerAgent.prototype.handleAfterCompileEvent_ = function(msg) { | |
970 if (!this.contextId_) { | |
971 // Ignore scripts delta if main request has not been issued yet. | |
972 return; | |
973 } | |
974 var script = msg.getBody().script; | |
975 | |
976 // Ignore scripts from other tabs. | |
977 if (!this.isScriptFromInspectedContext_(script, msg)) { | |
978 return; | |
979 } | |
980 this.addScriptInfo_(script, msg); | |
981 }; | |
982 | |
983 | |
984 /** | |
985 * Handles current profiler status. | |
986 * @param {number} modules List of active (started) modules. | |
987 */ | |
988 devtools.DebuggerAgent.prototype.didGetActiveProfilerModules_ = function( | |
989 modules) { | |
990 var profModules = devtools.DebuggerAgent.ProfilerModules; | |
991 var profModuleNone = profModules.PROFILER_MODULE_NONE; | |
992 if (modules != profModuleNone && | |
993 this.activeProfilerModules_ == profModuleNone) { | |
994 // Start to query log data. | |
995 RemoteDebuggerAgent.GetNextLogLines(); | |
996 } | |
997 this.activeProfilerModules_ = modules; | |
998 // Update buttons. | |
999 WebInspector.setRecordingProfile(modules & profModules.PROFILER_MODULE_CPU); | |
1000 if (modules != profModuleNone) { | |
1001 // Monitor profiler state. It can stop itself on buffer fill-up. | |
1002 setTimeout( | |
1003 function() { RemoteDebuggerAgent.GetActiveProfilerModules(); }, 1000); | |
1004 } | |
1005 }; | |
1006 | |
1007 | |
1008 /** | |
1009 * Handles a portion of a profiler log retrieved by GetNextLogLines call. | |
1010 * @param {string} log A portion of profiler log. | |
1011 */ | |
1012 devtools.DebuggerAgent.prototype.didGetNextLogLines_ = function(log) { | |
1013 if (log.length > 0) { | |
1014 this.profilerProcessor_.processLogChunk(log); | |
1015 } else if (this.activeProfilerModules_ == | |
1016 devtools.DebuggerAgent.ProfilerModules.PROFILER_MODULE_NONE) { | |
1017 // No new data and profiling is stopped---suspend log reading. | |
1018 return; | |
1019 } | |
1020 setTimeout(function() { RemoteDebuggerAgent.GetNextLogLines(); }, 500); | |
1021 }; | |
1022 | |
1023 | |
1024 /** | |
1025 * Adds the script info to the local cache. This method assumes that the script | |
1026 * is not in the cache yet. | |
1027 * @param {Object} script Script json object from the debugger message. | |
1028 * @param {devtools.DebuggerMessage} msg Debugger message containing the script | |
1029 * data. | |
1030 */ | |
1031 devtools.DebuggerAgent.prototype.addScriptInfo_ = function(script, msg) { | |
1032 var context = msg.lookup(script.context.ref); | |
1033 var contextType = context.data.type; | |
1034 this.parsedScripts_[script.id] = new devtools.ScriptInfo( | |
1035 script.id, script.name, script.lineOffset, contextType); | |
1036 if (WebInspector.panels.scripts.element.parentElement) { | |
1037 // Only report script as parsed after scripts panel has been shown. | |
1038 WebInspector.parsedScriptSource( | |
1039 script.id, script.name, script.source, script.lineOffset); | |
1040 } | |
1041 }; | |
1042 | |
1043 | |
1044 /** | |
1045 * @param {devtools.DebuggerMessage} msg | |
1046 */ | |
1047 devtools.DebuggerAgent.prototype.handleClearBreakpointResponse_ = function( | |
1048 msg) { | |
1049 // Do nothing. | |
1050 }; | |
1051 | |
1052 | |
1053 /** | |
1054 * Handles response to 'backtrace' command. | |
1055 * @param {devtools.DebuggerMessage} msg | |
1056 */ | |
1057 devtools.DebuggerAgent.prototype.handleBacktraceResponse_ = function(msg) { | |
1058 var frames = msg.getBody().frames; | |
1059 this.callFrames_ = []; | |
1060 for (var i = 0; i < frames.length; ++i) { | |
1061 this.callFrames_.push(this.formatCallFrame_(frames[i])); | |
1062 } | |
1063 WebInspector.pausedScript(this.callFrames_); | |
1064 this.showPendingExceptionMessage_(); | |
1065 DevToolsHost.activateWindow(); | |
1066 }; | |
1067 | |
1068 | |
1069 /** | |
1070 * Evaluates code on given callframe. | |
1071 */ | |
1072 devtools.DebuggerAgent.prototype.evaluateInCallFrame = function( | |
1073 callFrameId, code, callback) { | |
1074 var callFrame = this.callFrames_[callFrameId]; | |
1075 callFrame.evaluate_(code, callback); | |
1076 }; | |
1077 | |
1078 | |
1079 /** | |
1080 * Handles response to a command by invoking its callback (if any). | |
1081 * @param {devtools.DebuggerMessage} msg | |
1082 * @return {boolean} Whether a callback for the given message was found and | |
1083 * excuted. | |
1084 */ | |
1085 devtools.DebuggerAgent.prototype.invokeCallbackForResponse_ = function(msg) { | |
1086 var callback = this.requestSeqToCallback_[msg.getRequestSeq()]; | |
1087 if (!callback) { | |
1088 // It may happend if reset was called. | |
1089 return false; | |
1090 } | |
1091 delete this.requestSeqToCallback_[msg.getRequestSeq()]; | |
1092 callback(msg); | |
1093 return true; | |
1094 }; | |
1095 | |
1096 | |
1097 /** | |
1098 * @param {Object} stackFrame Frame json object from 'backtrace' response. | |
1099 * @return {!devtools.CallFrame} Object containing information related to the | |
1100 * call frame in the format expected by ScriptsPanel and its panes. | |
1101 */ | |
1102 devtools.DebuggerAgent.prototype.formatCallFrame_ = function(stackFrame) { | |
1103 var func = stackFrame.func; | |
1104 var sourceId = func.scriptId; | |
1105 | |
1106 // Add service script if it does not exist. | |
1107 var existingScript = this.parsedScripts_[sourceId]; | |
1108 if (!existingScript) { | |
1109 this.parsedScripts_[sourceId] = new devtools.ScriptInfo( | |
1110 sourceId, null /* name */, 0 /* line */, 'unknown' /* type */, | |
1111 true /* unresolved */); | |
1112 WebInspector.parsedScriptSource(sourceId, null, null, 0); | |
1113 } | |
1114 | |
1115 var funcName = func.name || func.inferredName || '(anonymous function)'; | |
1116 var line = devtools.DebuggerAgent.v8ToWwebkitLineNumber_(stackFrame.line); | |
1117 | |
1118 // Add basic scope chain info with scope variables. | |
1119 var scopeChain = []; | |
1120 var ScopeType = devtools.DebuggerAgent.ScopeType; | |
1121 for (var i = 0; i < stackFrame.scopes.length; i++) { | |
1122 var scope = stackFrame.scopes[i]; | |
1123 scope.frameNumber = stackFrame.index; | |
1124 var scopeObjectProxy = new WebInspector.ObjectProxy(scope, [], 0, '', true); | |
1125 scopeObjectProxy.isScope = true; | |
1126 switch(scope.type) { | |
1127 case ScopeType.Global: | |
1128 scopeObjectProxy.isDocument = true; | |
1129 break; | |
1130 case ScopeType.Local: | |
1131 scopeObjectProxy.isLocal = true; | |
1132 scopeObjectProxy.thisObject = | |
1133 devtools.DebuggerAgent.formatObjectProxy_(stackFrame.receiver); | |
1134 break; | |
1135 case ScopeType.With: | |
1136 // Catch scope is treated as a regular with scope by WebKit so we | |
1137 // also treat it this way. | |
1138 case ScopeType.Catch: | |
1139 scopeObjectProxy.isWithBlock = true; | |
1140 break; | |
1141 case ScopeType.Closure: | |
1142 scopeObjectProxy.isClosure = true; | |
1143 break; | |
1144 } | |
1145 scopeChain.push(scopeObjectProxy); | |
1146 } | |
1147 return new devtools.CallFrame(stackFrame.index, 'function', funcName, | |
1148 sourceId, line, scopeChain); | |
1149 }; | |
1150 | |
1151 | |
1152 /** | |
1153 * Collects properties for an object from the debugger response. | |
1154 * @param {Object} object An object from the debugger protocol response. | |
1155 * @param {Array.<WebInspector.ObjectPropertyProxy>} result An array to put the | |
1156 * properties into. | |
1157 * @param {boolean} noIntrinsic Whether intrinsic properties should be | |
1158 * included. | |
1159 */ | |
1160 devtools.DebuggerAgent.formatObjectProperties_ = function(object, result, | |
1161 noIntrinsic) { | |
1162 devtools.DebuggerAgent.propertiesToProxies_(object.properties, result); | |
1163 if (noIntrinsic) { | |
1164 return; | |
1165 } | |
1166 | |
1167 result.push(new WebInspector.ObjectPropertyProxy('__proto__', | |
1168 devtools.DebuggerAgent.formatObjectProxy_(object.protoObject))); | |
1169 result.push(new WebInspector.ObjectPropertyProxy('constructor', | |
1170 devtools.DebuggerAgent.formatObjectProxy_(object.constructorFunction))); | |
1171 // Don't add 'prototype' property since it is one of the regualar properties. | |
1172 }; | |
1173 | |
1174 | |
1175 /** | |
1176 * For each property in 'properties' creates its proxy representative. | |
1177 * @param {Array.<Object>} properties Receiver properties or locals array from | |
1178 * 'backtrace' response. | |
1179 * @param {Array.<WebInspector.ObjectPropertyProxy>} Results holder. | |
1180 */ | |
1181 devtools.DebuggerAgent.propertiesToProxies_ = function(properties, result) { | |
1182 var map = {}; | |
1183 for (var i = 0; i < properties.length; ++i) { | |
1184 var property = properties[i]; | |
1185 var name = String(property.name); | |
1186 if (name in map) { | |
1187 continue; | |
1188 } | |
1189 map[name] = true; | |
1190 var value = devtools.DebuggerAgent.formatObjectProxy_(property.value); | |
1191 var propertyProxy = new WebInspector.ObjectPropertyProxy(name, value); | |
1192 result.push(propertyProxy); | |
1193 } | |
1194 }; | |
1195 | |
1196 | |
1197 /** | |
1198 * @param {Object} v An object reference from the debugger response. | |
1199 * @return {*} The value representation expected by ScriptsPanel. | |
1200 */ | |
1201 devtools.DebuggerAgent.formatObjectProxy_ = function(v) { | |
1202 var description; | |
1203 var hasChildren = false; | |
1204 if (v.type == 'object') { | |
1205 description = v.className; | |
1206 hasChildren = true; | |
1207 } else if (v.type == 'function') { | |
1208 if (v.source) { | |
1209 description = v.source; | |
1210 } else { | |
1211 description = 'function ' + v.name + '()'; | |
1212 } | |
1213 hasChildren = true; | |
1214 } else if (v.type == 'undefined') { | |
1215 description = 'undefined'; | |
1216 } else if (v.type == 'null') { | |
1217 description = 'null'; | |
1218 } else if (goog.isDef(v.value)) { | |
1219 // Check for undefined and null types before checking the value, otherwise | |
1220 // null/undefined may have blank value. | |
1221 description = v.value; | |
1222 } else { | |
1223 description = '<unresolved ref: ' + v.ref + ', type: ' + v.type + '>'; | |
1224 } | |
1225 var proxy = new WebInspector.ObjectProxy(v, [], 0, description, hasChildren); | |
1226 proxy.type = v.type; | |
1227 proxy.isV8Ref = true; | |
1228 return proxy; | |
1229 }; | |
1230 | |
1231 | |
1232 /** | |
1233 * Converts line number from Web Inspector UI(1-based) to v8(0-based). | |
1234 * @param {number} line Resource line number in Web Inspector UI. | |
1235 * @return {number} The line number in v8. | |
1236 */ | |
1237 devtools.DebuggerAgent.webkitToV8LineNumber_ = function(line) { | |
1238 return line - 1; | |
1239 }; | |
1240 | |
1241 | |
1242 /** | |
1243 * Converts line number from v8(0-based) to Web Inspector UI(1-based). | |
1244 * @param {number} line Resource line number in v8. | |
1245 * @return {number} The line number in Web Inspector. | |
1246 */ | |
1247 devtools.DebuggerAgent.v8ToWwebkitLineNumber_ = function(line) { | |
1248 return line + 1; | |
1249 }; | |
1250 | |
1251 | |
1252 /** | |
1253 * @param {number} scriptId Id of the script. | |
1254 * @param {?string} url Script resource URL if any. | |
1255 * @param {number} lineOffset First line 0-based offset in the containing | |
1256 * document. | |
1257 * @param {string} contextType Type of the script's context: | |
1258 * "page" - regular script from html page | |
1259 * "injected" - extension content script | |
1260 * @param {bool} opt_isUnresolved If true, script will not be resolved. | |
1261 * @constructor | |
1262 */ | |
1263 devtools.ScriptInfo = function( | |
1264 scriptId, url, lineOffset, contextType, opt_isUnresolved) { | |
1265 this.scriptId_ = scriptId; | |
1266 this.lineOffset_ = lineOffset; | |
1267 this.contextType_ = contextType; | |
1268 this.url_ = url; | |
1269 this.isUnresolved_ = opt_isUnresolved; | |
1270 | |
1271 this.lineToBreakpointInfo_ = {}; | |
1272 }; | |
1273 | |
1274 | |
1275 /** | |
1276 * @return {number} | |
1277 */ | |
1278 devtools.ScriptInfo.prototype.getLineOffset = function() { | |
1279 return this.lineOffset_; | |
1280 }; | |
1281 | |
1282 | |
1283 /** | |
1284 * @return {string} | |
1285 */ | |
1286 devtools.ScriptInfo.prototype.getContextType = function() { | |
1287 return this.contextType_; | |
1288 }; | |
1289 | |
1290 | |
1291 /** | |
1292 * @return {?string} | |
1293 */ | |
1294 devtools.ScriptInfo.prototype.getUrl = function() { | |
1295 return this.url_; | |
1296 }; | |
1297 | |
1298 | |
1299 /** | |
1300 * @return {?bool} | |
1301 */ | |
1302 devtools.ScriptInfo.prototype.isUnresolved = function() { | |
1303 return this.isUnresolved_; | |
1304 }; | |
1305 | |
1306 | |
1307 /** | |
1308 * @param {number} line 0-based line number in the script. | |
1309 * @return {?devtools.BreakpointInfo} Information on a breakpoint at the | |
1310 * specified line in the script or undefined if there is no breakpoint at | |
1311 * that line. | |
1312 */ | |
1313 devtools.ScriptInfo.prototype.getBreakpointInfo = function(line) { | |
1314 return this.lineToBreakpointInfo_[line]; | |
1315 }; | |
1316 | |
1317 | |
1318 /** | |
1319 * Adds breakpoint info to the script. | |
1320 * @param {devtools.BreakpointInfo} breakpoint | |
1321 */ | |
1322 devtools.ScriptInfo.prototype.addBreakpointInfo = function(breakpoint) { | |
1323 this.lineToBreakpointInfo_[breakpoint.getLine()] = breakpoint; | |
1324 }; | |
1325 | |
1326 | |
1327 /** | |
1328 * @param {devtools.BreakpointInfo} breakpoint Breakpoint info to be removed. | |
1329 */ | |
1330 devtools.ScriptInfo.prototype.removeBreakpointInfo = function(breakpoint) { | |
1331 var line = breakpoint.getLine(); | |
1332 delete this.lineToBreakpointInfo_[line]; | |
1333 }; | |
1334 | |
1335 | |
1336 | |
1337 /** | |
1338 * @param {number} line Breakpoint 0-based line number in the containing script. | |
1339 * @constructor | |
1340 */ | |
1341 devtools.BreakpointInfo = function(line) { | |
1342 this.line_ = line; | |
1343 this.v8id_ = -1; | |
1344 this.removed_ = false; | |
1345 }; | |
1346 | |
1347 | |
1348 /** | |
1349 * @return {number} | |
1350 */ | |
1351 devtools.BreakpointInfo.prototype.getLine = function(n) { | |
1352 return this.line_; | |
1353 }; | |
1354 | |
1355 | |
1356 /** | |
1357 * @return {number} Unique identifier of this breakpoint in the v8 debugger. | |
1358 */ | |
1359 devtools.BreakpointInfo.prototype.getV8Id = function(n) { | |
1360 return this.v8id_; | |
1361 }; | |
1362 | |
1363 | |
1364 /** | |
1365 * Sets id of this breakpoint in the v8 debugger. | |
1366 * @param {number} id | |
1367 */ | |
1368 devtools.BreakpointInfo.prototype.setV8Id = function(id) { | |
1369 this.v8id_ = id; | |
1370 }; | |
1371 | |
1372 | |
1373 /** | |
1374 * Marks this breakpoint as removed from the front-end. | |
1375 */ | |
1376 devtools.BreakpointInfo.prototype.markAsRemoved = function() { | |
1377 this.removed_ = true; | |
1378 }; | |
1379 | |
1380 | |
1381 /** | |
1382 * @return {boolean} Whether this breakpoint has been removed from the | |
1383 * front-end. | |
1384 */ | |
1385 devtools.BreakpointInfo.prototype.isRemoved = function() { | |
1386 return this.removed_; | |
1387 }; | |
1388 | |
1389 | |
1390 /** | |
1391 * Call stack frame data. | |
1392 * @param {string} id CallFrame id. | |
1393 * @param {string} type CallFrame type. | |
1394 * @param {string} functionName CallFrame type. | |
1395 * @param {string} sourceID Source id. | |
1396 * @param {number} line Source line. | |
1397 * @param {Array.<Object>} scopeChain Array of scoped objects. | |
1398 * @construnctor | |
1399 */ | |
1400 devtools.CallFrame = function(id, type, functionName, sourceID, line, | |
1401 scopeChain) { | |
1402 this.id = id; | |
1403 this.type = type; | |
1404 this.functionName = functionName; | |
1405 this.sourceID = sourceID; | |
1406 this.line = line; | |
1407 this.scopeChain = scopeChain; | |
1408 }; | |
1409 | |
1410 | |
1411 /** | |
1412 * This method issues asynchronous evaluate request, reports result to the | |
1413 * callback. | |
1414 * @param {string} expression An expression to be evaluated in the context of | |
1415 * this call frame. | |
1416 * @param {function(Object):undefined} callback Callback to report result to. | |
1417 */ | |
1418 devtools.CallFrame.prototype.evaluate_ = function(expression, callback) { | |
1419 devtools.tools.getDebuggerAgent().requestEvaluate({ | |
1420 'expression': expression, | |
1421 'frame': this.id, | |
1422 'global': false, | |
1423 'disable_break': false, | |
1424 'compactFormat': true | |
1425 }, | |
1426 function(response) { | |
1427 var result = {}; | |
1428 if (response.isSuccess()) { | |
1429 result.value = devtools.DebuggerAgent.formatObjectProxy_( | |
1430 response.getBody()); | |
1431 } else { | |
1432 result.value = response.getMessage(); | |
1433 result.isException = true; | |
1434 } | |
1435 callback(result); | |
1436 }); | |
1437 }; | |
1438 | |
1439 | |
1440 /** | |
1441 * JSON based commands sent to v8 debugger. | |
1442 * @param {string} command Name of the command to execute. | |
1443 * @param {Object} opt_arguments Command-specific arguments map. | |
1444 * @constructor | |
1445 */ | |
1446 devtools.DebugCommand = function(command, opt_arguments) { | |
1447 this.command_ = command; | |
1448 this.type_ = 'request'; | |
1449 this.seq_ = ++devtools.DebugCommand.nextSeq_; | |
1450 if (opt_arguments) { | |
1451 this.arguments_ = opt_arguments; | |
1452 } | |
1453 }; | |
1454 | |
1455 | |
1456 /** | |
1457 * Next unique number to be used as debugger request sequence number. | |
1458 * @type {number} | |
1459 */ | |
1460 devtools.DebugCommand.nextSeq_ = 1; | |
1461 | |
1462 | |
1463 /** | |
1464 * @return {number} | |
1465 */ | |
1466 devtools.DebugCommand.prototype.getSequenceNumber = function() { | |
1467 return this.seq_; | |
1468 }; | |
1469 | |
1470 | |
1471 /** | |
1472 * @return {string} | |
1473 */ | |
1474 devtools.DebugCommand.prototype.toJSONProtocol = function() { | |
1475 var json = { | |
1476 'seq': this.seq_, | |
1477 'type': this.type_, | |
1478 'command': this.command_ | |
1479 } | |
1480 if (this.arguments_) { | |
1481 json.arguments = this.arguments_; | |
1482 } | |
1483 return JSON.stringify(json); | |
1484 }; | |
1485 | |
1486 | |
1487 /** | |
1488 * JSON messages sent from v8 debugger. See protocol definition for more | |
1489 * details: http://code.google.com/p/v8/wiki/DebuggerProtocol | |
1490 * @param {string} msg Raw protocol packet as JSON string. | |
1491 * @constructor | |
1492 */ | |
1493 devtools.DebuggerMessage = function(msg) { | |
1494 this.packet_ = JSON.parse(msg); | |
1495 this.refs_ = []; | |
1496 if (this.packet_.refs) { | |
1497 for (var i = 0; i < this.packet_.refs.length; i++) { | |
1498 this.refs_[this.packet_.refs[i].handle] = this.packet_.refs[i]; | |
1499 } | |
1500 } | |
1501 }; | |
1502 | |
1503 | |
1504 /** | |
1505 * @return {string} The packet type. | |
1506 */ | |
1507 devtools.DebuggerMessage.prototype.getType = function() { | |
1508 return this.packet_.type; | |
1509 }; | |
1510 | |
1511 | |
1512 /** | |
1513 * @return {?string} The packet event if the message is an event. | |
1514 */ | |
1515 devtools.DebuggerMessage.prototype.getEvent = function() { | |
1516 return this.packet_.event; | |
1517 }; | |
1518 | |
1519 | |
1520 /** | |
1521 * @return {?string} The packet command if the message is a response to a | |
1522 * command. | |
1523 */ | |
1524 devtools.DebuggerMessage.prototype.getCommand = function() { | |
1525 return this.packet_.command; | |
1526 }; | |
1527 | |
1528 | |
1529 /** | |
1530 * @return {number} The packet request sequence. | |
1531 */ | |
1532 devtools.DebuggerMessage.prototype.getRequestSeq = function() { | |
1533 return this.packet_.request_seq; | |
1534 }; | |
1535 | |
1536 | |
1537 /** | |
1538 * @return {number} Whether the v8 is running after processing the request. | |
1539 */ | |
1540 devtools.DebuggerMessage.prototype.isRunning = function() { | |
1541 return this.packet_.running ? true : false; | |
1542 }; | |
1543 | |
1544 | |
1545 /** | |
1546 * @return {boolean} Whether the request succeeded. | |
1547 */ | |
1548 devtools.DebuggerMessage.prototype.isSuccess = function() { | |
1549 return this.packet_.success ? true : false; | |
1550 }; | |
1551 | |
1552 | |
1553 /** | |
1554 * @return {string} | |
1555 */ | |
1556 devtools.DebuggerMessage.prototype.getMessage = function() { | |
1557 return this.packet_.message; | |
1558 }; | |
1559 | |
1560 | |
1561 /** | |
1562 * @return {Object} Parsed message body json. | |
1563 */ | |
1564 devtools.DebuggerMessage.prototype.getBody = function() { | |
1565 return this.packet_.body; | |
1566 }; | |
1567 | |
1568 | |
1569 /** | |
1570 * @param {number} handle Object handle. | |
1571 * @return {?Object} Returns the object with the handle if it was sent in this | |
1572 * message(some objects referenced by handles may be missing in the message). | |
1573 */ | |
1574 devtools.DebuggerMessage.prototype.lookup = function(handle) { | |
1575 return this.refs_[handle]; | |
1576 }; | |
OLD | NEW |