Home | History | Annotate | Download | only in front_end
      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