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 |