1 /* 2 * Copyright (C) 2011 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 /** 32 * @constructor 33 * @extends {WebInspector.UISourceCodeFrame} 34 * @param {WebInspector.ScriptsPanel} scriptsPanel 35 * @param {WebInspector.UISourceCode} uiSourceCode 36 */ 37 WebInspector.JavaScriptSourceFrame = function(scriptsPanel, uiSourceCode) 38 { 39 this._scriptsPanel = scriptsPanel; 40 this._breakpointManager = WebInspector.breakpointManager; 41 this._uiSourceCode = uiSourceCode; 42 43 WebInspector.UISourceCodeFrame.call(this, uiSourceCode); 44 if (uiSourceCode.project().type() === WebInspector.projectTypes.Debugger) 45 this.element.addStyleClass("source-frame-debugger-script"); 46 47 this._popoverHelper = new WebInspector.ObjectPopoverHelper(this.textEditor.element, 48 this._getPopoverAnchor.bind(this), this._resolveObjectForPopover.bind(this), this._onHidePopover.bind(this), true); 49 50 this.textEditor.element.addEventListener("keydown", this._onKeyDown.bind(this), true); 51 52 this.textEditor.addEventListener(WebInspector.TextEditor.Events.GutterClick, this._handleGutterClick.bind(this), this); 53 54 this._breakpointManager.addEventListener(WebInspector.BreakpointManager.Events.BreakpointAdded, this._breakpointAdded, this); 55 this._breakpointManager.addEventListener(WebInspector.BreakpointManager.Events.BreakpointRemoved, this._breakpointRemoved, this); 56 57 this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.ConsoleMessageAdded, this._consoleMessageAdded, this); 58 this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.ConsoleMessageRemoved, this._consoleMessageRemoved, this); 59 this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.ConsoleMessagesCleared, this._consoleMessagesCleared, this); 60 this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.SourceMappingChanged, this._onSourceMappingChanged, this); 61 this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._workingCopyChanged, this); 62 this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._workingCopyCommitted, this); 63 64 this._registerShortcuts(); 65 this._updateScriptFile(); 66 } 67 68 WebInspector.JavaScriptSourceFrame.prototype = { 69 _registerShortcuts: function() 70 { 71 var modifiers = WebInspector.KeyboardShortcut.Modifiers; 72 this.addShortcut(WebInspector.KeyboardShortcut.makeKey("e", modifiers.Shift | modifiers.Ctrl), this._evaluateSelectionInConsole.bind(this)); 73 }, 74 75 /** 76 * @param {Event=} event 77 * @return {boolean} 78 */ 79 _evaluateSelectionInConsole: function(event) 80 { 81 var selection = this.textEditor.selection(); 82 if (!selection || selection.isEmpty()) 83 return false; 84 WebInspector.evaluateInConsole(this.textEditor.copyRange(selection)); 85 return true; 86 }, 87 88 // View events 89 wasShown: function() 90 { 91 WebInspector.UISourceCodeFrame.prototype.wasShown.call(this); 92 }, 93 94 willHide: function() 95 { 96 WebInspector.UISourceCodeFrame.prototype.willHide.call(this); 97 this._popoverHelper.hidePopover(); 98 }, 99 100 onUISourceCodeContentChanged: function() 101 { 102 this._removeAllBreakpoints(); 103 WebInspector.UISourceCodeFrame.prototype.onUISourceCodeContentChanged.call(this); 104 }, 105 106 populateLineGutterContextMenu: function(contextMenu, lineNumber) 107 { 108 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Continue to here" : "Continue to Here"), this._continueToLine.bind(this, lineNumber)); 109 110 var breakpoint = this._breakpointManager.findBreakpoint(this._uiSourceCode, lineNumber); 111 if (!breakpoint) { 112 // This row doesn't have a breakpoint: We want to show Add Breakpoint and Add and Edit Breakpoint. 113 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Add breakpoint" : "Add Breakpoint"), this._setBreakpoint.bind(this, lineNumber, "", true)); 114 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Add conditional breakpoint" : "Add Conditional Breakpoint"), this._editBreakpointCondition.bind(this, lineNumber)); 115 } else { 116 // This row has a breakpoint, we want to show edit and remove breakpoint, and either disable or enable. 117 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Remove breakpoint" : "Remove Breakpoint"), breakpoint.remove.bind(breakpoint)); 118 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Edit breakpoint" : "Edit Breakpoint"), this._editBreakpointCondition.bind(this, lineNumber, breakpoint)); 119 if (breakpoint.enabled()) 120 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Disable breakpoint" : "Disable Breakpoint"), breakpoint.setEnabled.bind(breakpoint, false)); 121 else 122 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Enable breakpoint" : "Enable Breakpoint"), breakpoint.setEnabled.bind(breakpoint, true)); 123 } 124 }, 125 126 populateTextAreaContextMenu: function(contextMenu, lineNumber) 127 { 128 var textSelection = this.textEditor.selection(); 129 if (textSelection && !textSelection.isEmpty()) { 130 var selection = this.textEditor.copyRange(textSelection); 131 var addToWatchLabel = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Add to watch" : "Add to Watch"); 132 contextMenu.appendItem(addToWatchLabel, this._scriptsPanel.addToWatch.bind(this._scriptsPanel, selection)); 133 var evaluateLabel = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Evaluate in console" : "Evaluate in Console"); 134 contextMenu.appendItem(evaluateLabel, WebInspector.evaluateInConsole.bind(WebInspector, selection)); 135 contextMenu.appendSeparator(); 136 } else if (!this._uiSourceCode.isEditable() && this._uiSourceCode.contentType() === WebInspector.resourceTypes.Script) { 137 function liveEdit(event) 138 { 139 var liveEditUISourceCode = WebInspector.liveEditSupport.uiSourceCodeForLiveEdit(this._uiSourceCode); 140 this._scriptsPanel.showUISourceCode(liveEditUISourceCode, lineNumber) 141 } 142 143 // FIXME: Change condition above to explicitly check that current uiSourceCode is created by default debugger mapping 144 // and move the code adding this menu item to generic context menu provider for UISourceCode. 145 var liveEditLabel = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Live edit" : "Live Edit"); 146 contextMenu.appendItem(liveEditLabel, liveEdit.bind(this)); 147 contextMenu.appendSeparator(); 148 } 149 WebInspector.UISourceCodeFrame.prototype.populateTextAreaContextMenu.call(this, contextMenu, lineNumber); 150 }, 151 152 _workingCopyChanged: function(event) 153 { 154 if (this._supportsEnabledBreakpointsWhileEditing() || this._scriptFile) 155 return; 156 157 if (this._uiSourceCode.isDirty()) 158 this._muteBreakpointsWhileEditing(); 159 else 160 this._restoreBreakpointsAfterEditing(); 161 }, 162 163 _workingCopyCommitted: function(event) 164 { 165 if (this._supportsEnabledBreakpointsWhileEditing() || this._scriptFile) 166 return; 167 this._restoreBreakpointsAfterEditing(); 168 }, 169 170 _didMergeToVM: function() 171 { 172 if (this._supportsEnabledBreakpointsWhileEditing()) 173 return; 174 this._restoreBreakpointsAfterEditing(); 175 }, 176 177 _didDivergeFromVM: function() 178 { 179 if (this._supportsEnabledBreakpointsWhileEditing()) 180 return; 181 this._muteBreakpointsWhileEditing(); 182 }, 183 184 _muteBreakpointsWhileEditing: function() 185 { 186 if (this._muted) 187 return; 188 for (var lineNumber = 0; lineNumber < this._textEditor.linesCount; ++lineNumber) { 189 var breakpointDecoration = this._textEditor.getAttribute(lineNumber, "breakpoint"); 190 if (!breakpointDecoration) 191 continue; 192 this._removeBreakpointDecoration(lineNumber); 193 this._addBreakpointDecoration(lineNumber, breakpointDecoration.condition, breakpointDecoration.enabled, true); 194 } 195 this._muted = true; 196 }, 197 198 _supportsEnabledBreakpointsWhileEditing: function() 199 { 200 return this._uiSourceCode.project().type() === WebInspector.projectTypes.Snippets; 201 }, 202 203 _restoreBreakpointsAfterEditing: function() 204 { 205 delete this._muted; 206 var breakpoints = {}; 207 // Save and remove muted breakpoint decorations. 208 for (var lineNumber = 0; lineNumber < this._textEditor.linesCount; ++lineNumber) { 209 var breakpointDecoration = this._textEditor.getAttribute(lineNumber, "breakpoint"); 210 if (breakpointDecoration) { 211 breakpoints[lineNumber] = breakpointDecoration; 212 this._removeBreakpointDecoration(lineNumber); 213 } 214 } 215 216 // Remove all breakpoints. 217 this._removeAllBreakpoints(); 218 219 // Restore all breakpoints from saved decorations. 220 for (var lineNumberString in breakpoints) { 221 var lineNumber = parseInt(lineNumberString, 10); 222 if (isNaN(lineNumber)) 223 continue; 224 var breakpointDecoration = breakpoints[lineNumberString]; 225 this._setBreakpoint(lineNumber, breakpointDecoration.condition, breakpointDecoration.enabled); 226 } 227 }, 228 229 _removeAllBreakpoints: function() 230 { 231 var breakpoints = this._breakpointManager.breakpointsForUISourceCode(this._uiSourceCode); 232 for (var i = 0; i < breakpoints.length; ++i) 233 breakpoints[i].remove(); 234 }, 235 236 _getPopoverAnchor: function(element, event) 237 { 238 if (!WebInspector.debuggerModel.isPaused()) 239 return null; 240 241 var textPosition = this.textEditor.coordinatesToCursorPosition(event.x, event.y); 242 if (!textPosition) 243 return null; 244 var mouseLine = textPosition.startLine; 245 var mouseColumn = textPosition.startColumn; 246 var textSelection = this.textEditor.selection(); 247 if (textSelection && !textSelection.isEmpty()) { 248 if (textSelection.startLine !== textSelection.endLine || textSelection.startLine !== mouseLine || mouseColumn < textSelection.startColumn || mouseColumn > textSelection.endColumn) 249 return null; 250 251 var leftCorner = this.textEditor.cursorPositionToCoordinates(textSelection.startLine, textSelection.startColumn); 252 var rightCorner = this.textEditor.cursorPositionToCoordinates(textSelection.endLine, textSelection.endColumn); 253 var anchorBox = new AnchorBox(leftCorner.x, leftCorner.y, rightCorner.x - leftCorner.x, leftCorner.height); 254 anchorBox.highlight = { 255 lineNumber: textSelection.startLine, 256 startColumn: textSelection.startColumn, 257 endColumn: textSelection.endColumn - 1 258 }; 259 anchorBox.forSelection = true; 260 return anchorBox; 261 } 262 263 var token = this.textEditor.tokenAtTextPosition(textPosition.startLine, textPosition.startColumn); 264 if (!token) 265 return null; 266 var lineNumber = textPosition.startLine; 267 var line = this.textEditor.line(lineNumber); 268 var tokenContent = line.substring(token.startColumn, token.endColumn + 1); 269 if (token.type !== "javascript-ident" && (token.type !== "javascript-keyword" || tokenContent !== "this")) 270 return null; 271 272 var leftCorner = this.textEditor.cursorPositionToCoordinates(lineNumber, token.startColumn); 273 var rightCorner = this.textEditor.cursorPositionToCoordinates(lineNumber, token.endColumn + 1); 274 var anchorBox = new AnchorBox(leftCorner.x, leftCorner.y, rightCorner.x - leftCorner.x, leftCorner.height); 275 276 anchorBox.highlight = { 277 lineNumber: lineNumber, 278 startColumn: token.startColumn, 279 endColumn: token.endColumn 280 }; 281 282 return anchorBox; 283 }, 284 285 _resolveObjectForPopover: function(anchorBox, showCallback, objectGroupName) 286 { 287 /** 288 * @param {?RuntimeAgent.RemoteObject} result 289 * @param {boolean=} wasThrown 290 */ 291 function showObjectPopover(result, wasThrown) 292 { 293 if (!WebInspector.debuggerModel.isPaused()) { 294 this._popoverHelper.hidePopover(); 295 return; 296 } 297 this._popoverAnchorBox = anchorBox; 298 showCallback(WebInspector.RemoteObject.fromPayload(result), wasThrown, this._popoverAnchorBox); 299 // Popover may have been removed by showCallback(). 300 if (this._popoverAnchorBox) { 301 var highlightRange = new WebInspector.TextRange(lineNumber, startHighlight, lineNumber, endHighlight); 302 this._popoverAnchorBox._highlightDescriptor = this.textEditor.highlightRange(highlightRange, "source-frame-eval-expression"); 303 } 304 } 305 306 if (!WebInspector.debuggerModel.isPaused()) { 307 this._popoverHelper.hidePopover(); 308 return; 309 } 310 var lineNumber = anchorBox.highlight.lineNumber; 311 var startHighlight = anchorBox.highlight.startColumn; 312 var endHighlight = anchorBox.highlight.endColumn; 313 var line = this.textEditor.line(lineNumber); 314 if (!anchorBox.forSelection) { 315 while (startHighlight > 1 && line.charAt(startHighlight - 1) === '.') 316 startHighlight = this.textEditor.tokenAtTextPosition(lineNumber, startHighlight - 2).startColumn; 317 } 318 var evaluationText = line.substring(startHighlight, endHighlight + 1); 319 var selectedCallFrame = WebInspector.debuggerModel.selectedCallFrame(); 320 selectedCallFrame.evaluate(evaluationText, objectGroupName, false, true, false, false, showObjectPopover.bind(this)); 321 }, 322 323 _onHidePopover: function() 324 { 325 if (!this._popoverAnchorBox) 326 return; 327 if (this._popoverAnchorBox._highlightDescriptor) 328 this.textEditor.removeHighlight(this._popoverAnchorBox._highlightDescriptor); 329 delete this._popoverAnchorBox; 330 }, 331 332 /** 333 * @param {number} lineNumber 334 * @param {string} condition 335 * @param {boolean} enabled 336 * @param {boolean} mutedWhileEditing 337 */ 338 _addBreakpointDecoration: function(lineNumber, condition, enabled, mutedWhileEditing) 339 { 340 var breakpoint = { 341 condition: condition, 342 enabled: enabled 343 }; 344 345 this.textEditor.setAttribute(lineNumber, "breakpoint", breakpoint); 346 347 var disabled = !enabled || mutedWhileEditing; 348 this.textEditor.addBreakpoint(lineNumber, disabled, !!condition); 349 }, 350 351 _removeBreakpointDecoration: function(lineNumber) 352 { 353 this.textEditor.removeAttribute(lineNumber, "breakpoint"); 354 this.textEditor.removeBreakpoint(lineNumber); 355 }, 356 357 _onKeyDown: function(event) 358 { 359 if (event.keyIdentifier === "U+001B") { // Escape key 360 if (this._popoverHelper.isPopoverVisible()) { 361 this._popoverHelper.hidePopover(); 362 event.consume(); 363 } 364 } 365 }, 366 367 /** 368 * @param {number} lineNumber 369 * @param {WebInspector.BreakpointManager.Breakpoint=} breakpoint 370 */ 371 _editBreakpointCondition: function(lineNumber, breakpoint) 372 { 373 this._conditionElement = this._createConditionElement(lineNumber); 374 this.textEditor.addDecoration(lineNumber, this._conditionElement); 375 376 function finishEditing(committed, element, newText) 377 { 378 this.textEditor.removeDecoration(lineNumber, this._conditionElement); 379 delete this._conditionEditorElement; 380 delete this._conditionElement; 381 if (!committed) 382 return; 383 384 if (breakpoint) 385 breakpoint.setCondition(newText); 386 else 387 this._setBreakpoint(lineNumber, newText, true); 388 } 389 390 var config = new WebInspector.EditingConfig(finishEditing.bind(this, true), finishEditing.bind(this, false)); 391 WebInspector.startEditing(this._conditionEditorElement, config); 392 this._conditionEditorElement.value = breakpoint ? breakpoint.condition() : ""; 393 this._conditionEditorElement.select(); 394 }, 395 396 _createConditionElement: function(lineNumber) 397 { 398 var conditionElement = document.createElement("div"); 399 conditionElement.className = "source-frame-breakpoint-condition"; 400 401 var labelElement = document.createElement("label"); 402 labelElement.className = "source-frame-breakpoint-message"; 403 labelElement.htmlFor = "source-frame-breakpoint-condition"; 404 labelElement.appendChild(document.createTextNode(WebInspector.UIString("The breakpoint on line %d will stop only if this expression is true:", lineNumber))); 405 conditionElement.appendChild(labelElement); 406 407 var editorElement = document.createElement("input"); 408 editorElement.id = "source-frame-breakpoint-condition"; 409 editorElement.className = "monospace"; 410 editorElement.type = "text"; 411 conditionElement.appendChild(editorElement); 412 this._conditionEditorElement = editorElement; 413 414 return conditionElement; 415 }, 416 417 /** 418 * @param {number} lineNumber 419 */ 420 setExecutionLine: function(lineNumber) 421 { 422 this._executionLineNumber = lineNumber; 423 if (this.loaded) { 424 this.textEditor.setExecutionLine(lineNumber); 425 this.revealLine(this._executionLineNumber); 426 if (this.canEditSource()) 427 this.setSelection(WebInspector.TextRange.createFromLocation(lineNumber, 0)); 428 } 429 }, 430 431 clearExecutionLine: function() 432 { 433 if (this.loaded && typeof this._executionLineNumber === "number") 434 this.textEditor.clearExecutionLine(); 435 delete this._executionLineNumber; 436 }, 437 438 _lineNumberAfterEditing: function(lineNumber, oldRange, newRange) 439 { 440 var shiftOffset = lineNumber <= oldRange.startLine ? 0 : newRange.linesCount - oldRange.linesCount; 441 442 // Special case of editing the line itself. We should decide whether the line number should move below or not. 443 if (lineNumber === oldRange.startLine) { 444 var whiteSpacesRegex = /^[\s\xA0]*$/; 445 for (var i = 0; lineNumber + i <= newRange.endLine; ++i) { 446 if (!whiteSpacesRegex.test(this.textEditor.line(lineNumber + i))) { 447 shiftOffset = i; 448 break; 449 } 450 } 451 } 452 453 var newLineNumber = Math.max(0, lineNumber + shiftOffset); 454 if (oldRange.startLine < lineNumber && lineNumber < oldRange.endLine) 455 newLineNumber = oldRange.startLine; 456 return newLineNumber; 457 }, 458 459 /** 460 * @return {boolean} 461 */ 462 _shouldIgnoreExternalBreakpointEvents: function() 463 { 464 if (this._supportsEnabledBreakpointsWhileEditing()) 465 return false; 466 if (this._muted) 467 return true; 468 return this._scriptFile && (this._scriptFile.isDivergingFromVM() || this._scriptFile.isMergingToVM()); 469 }, 470 471 _breakpointAdded: function(event) 472 { 473 var uiLocation = /** @type {WebInspector.UILocation} */ (event.data.uiLocation); 474 if (uiLocation.uiSourceCode !== this._uiSourceCode) 475 return; 476 if (this._shouldIgnoreExternalBreakpointEvents()) 477 return; 478 479 var breakpoint = /** @type {WebInspector.BreakpointManager.Breakpoint} */ (event.data.breakpoint); 480 if (this.loaded) 481 this._addBreakpointDecoration(uiLocation.lineNumber, breakpoint.condition(), breakpoint.enabled(), false); 482 }, 483 484 _breakpointRemoved: function(event) 485 { 486 var uiLocation = /** @type {WebInspector.UILocation} */ (event.data.uiLocation); 487 if (uiLocation.uiSourceCode !== this._uiSourceCode) 488 return; 489 if (this._shouldIgnoreExternalBreakpointEvents()) 490 return; 491 492 var breakpoint = /** @type {WebInspector.BreakpointManager.Breakpoint} */ (event.data.breakpoint); 493 var remainingBreakpoint = this._breakpointManager.findBreakpoint(this._uiSourceCode, uiLocation.lineNumber); 494 if (!remainingBreakpoint && this.loaded) 495 this._removeBreakpointDecoration(uiLocation.lineNumber); 496 }, 497 498 _consoleMessageAdded: function(event) 499 { 500 var message = /** @type {WebInspector.PresentationConsoleMessage} */ (event.data); 501 if (this.loaded) 502 this.addMessageToSource(message.lineNumber, message.originalMessage); 503 }, 504 505 _consoleMessageRemoved: function(event) 506 { 507 var message = /** @type {WebInspector.PresentationConsoleMessage} */ (event.data); 508 if (this.loaded) 509 this.removeMessageFromSource(message.lineNumber, message.originalMessage); 510 }, 511 512 _consoleMessagesCleared: function(event) 513 { 514 this.clearMessages(); 515 }, 516 517 /** 518 * @param {WebInspector.Event} event 519 */ 520 _onSourceMappingChanged: function(event) 521 { 522 this._updateScriptFile(); 523 }, 524 525 _updateScriptFile: function() 526 { 527 if (this._scriptFile) { 528 this._scriptFile.removeEventListener(WebInspector.ScriptFile.Events.DidMergeToVM, this._didMergeToVM, this); 529 this._scriptFile.removeEventListener(WebInspector.ScriptFile.Events.DidDivergeFromVM, this._didDivergeFromVM, this); 530 if (this._muted && !this._uiSourceCode.isDirty()) 531 this._restoreBreakpointsAfterEditing(); 532 } 533 this._scriptFile = this._uiSourceCode.scriptFile(); 534 if (this._scriptFile) { 535 this._scriptFile.addEventListener(WebInspector.ScriptFile.Events.DidMergeToVM, this._didMergeToVM, this); 536 this._scriptFile.addEventListener(WebInspector.ScriptFile.Events.DidDivergeFromVM, this._didDivergeFromVM, this); 537 538 if (this.loaded) 539 this._scriptFile.checkMapping(); 540 } 541 }, 542 543 onTextEditorContentLoaded: function() 544 { 545 if (typeof this._executionLineNumber === "number") 546 this.setExecutionLine(this._executionLineNumber); 547 548 var breakpointLocations = this._breakpointManager.breakpointLocationsForUISourceCode(this._uiSourceCode); 549 for (var i = 0; i < breakpointLocations.length; ++i) 550 this._breakpointAdded({data:breakpointLocations[i]}); 551 552 var messages = this._uiSourceCode.consoleMessages(); 553 for (var i = 0; i < messages.length; ++i) { 554 var message = messages[i]; 555 this.addMessageToSource(message.lineNumber, message.originalMessage); 556 } 557 558 if (this._scriptFile) 559 this._scriptFile.checkMapping(); 560 }, 561 562 /** 563 * @param {Event} event 564 */ 565 _handleGutterClick: function(event) 566 { 567 if (this._muted) 568 return; 569 570 var eventData = /** @type {WebInspector.TextEditor.GutterClickEventData} */ (event.data); 571 var lineNumber = eventData.lineNumber; 572 var eventObject = /** @type {Event} */ (eventData.event); 573 574 if (eventObject.button != 0 || eventObject.altKey || eventObject.ctrlKey || eventObject.metaKey) 575 return; 576 577 this._toggleBreakpoint(lineNumber, eventObject.shiftKey); 578 eventObject.consume(true); 579 }, 580 581 /** 582 * @param {number} lineNumber 583 * @param {boolean} onlyDisable 584 */ 585 _toggleBreakpoint: function(lineNumber, onlyDisable) 586 { 587 var breakpoint = this._breakpointManager.findBreakpoint(this._uiSourceCode, lineNumber); 588 if (breakpoint) { 589 if (onlyDisable) 590 breakpoint.setEnabled(!breakpoint.enabled()); 591 else 592 breakpoint.remove(); 593 } else 594 this._setBreakpoint(lineNumber, "", true); 595 }, 596 597 toggleBreakpointOnCurrentLine: function() 598 { 599 if (this._muted) 600 return; 601 602 var selection = this.textEditor.selection(); 603 if (!selection) 604 return; 605 this._toggleBreakpoint(selection.startLine, false); 606 }, 607 608 /** 609 * @param {number} lineNumber 610 * @param {string} condition 611 * @param {boolean} enabled 612 */ 613 _setBreakpoint: function(lineNumber, condition, enabled) 614 { 615 this._breakpointManager.setBreakpoint(this._uiSourceCode, lineNumber, condition, enabled); 616 617 WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, { 618 action: WebInspector.UserMetrics.UserActionNames.SetBreakpoint, 619 url: this._uiSourceCode.originURL(), 620 line: lineNumber, 621 enabled: enabled 622 }); 623 }, 624 625 /** 626 * @param {number} lineNumber 627 */ 628 _continueToLine: function(lineNumber) 629 { 630 var rawLocation = /** @type {WebInspector.DebuggerModel.Location} */ (this._uiSourceCode.uiLocationToRawLocation(lineNumber, 0)); 631 WebInspector.debuggerModel.continueToLocation(rawLocation); 632 }, 633 634 dispose: function() 635 { 636 this._breakpointManager.removeEventListener(WebInspector.BreakpointManager.Events.BreakpointAdded, this._breakpointAdded, this); 637 this._breakpointManager.removeEventListener(WebInspector.BreakpointManager.Events.BreakpointRemoved, this._breakpointRemoved, this); 638 this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.ConsoleMessageAdded, this._consoleMessageAdded, this); 639 this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.ConsoleMessageRemoved, this._consoleMessageRemoved, this); 640 this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.ConsoleMessagesCleared, this._consoleMessagesCleared, this); 641 this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.SourceMappingChanged, this._onSourceMappingChanged, this); 642 this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._workingCopyChanged, this); 643 this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._workingCopyCommitted, this); 644 WebInspector.UISourceCodeFrame.prototype.dispose.call(this); 645 }, 646 647 __proto__: WebInspector.UISourceCodeFrame.prototype 648 } 649