2 * Copyright (C) 2011 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
33 * @extends {WebInspector.UISourceCodeFrame}
34 * @param {!WebInspector.SourcesPanel} scriptsPanel
35 * @param {!WebInspector.UISourceCode} uiSourceCode
37 WebInspector.JavaScriptSourceFrame = function(scriptsPanel, uiSourceCode)
39 this._scriptsPanel = scriptsPanel;
40 this._breakpointManager = WebInspector.breakpointManager;
41 this._uiSourceCode = uiSourceCode;
43 WebInspector.UISourceCodeFrame.call(this, uiSourceCode);
44 if (uiSourceCode.project().type() === WebInspector.projectTypes.Debugger)
45 this.element.classList.add("source-frame-debugger-script");
47 this._popoverHelper = new WebInspector.ObjectPopoverHelper(this.textEditor.element,
48 this._getPopoverAnchor.bind(this), this._resolveObjectForPopover.bind(this), this._onHidePopover.bind(this), true);
50 this.textEditor.element.addEventListener("keydown", this._onKeyDown.bind(this), true);
52 this.textEditor.addEventListener(WebInspector.TextEditor.Events.GutterClick, this._handleGutterClick.bind(this), this);
54 this.textEditor.element.addEventListener("mousedown", this._onMouseDownAndClick.bind(this, true), true);
55 this.textEditor.element.addEventListener("click", this._onMouseDownAndClick.bind(this, false), true);
58 this._breakpointManager.addEventListener(WebInspector.BreakpointManager.Events.BreakpointAdded, this._breakpointAdded, this);
59 this._breakpointManager.addEventListener(WebInspector.BreakpointManager.Events.BreakpointRemoved, this._breakpointRemoved, this);
61 this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.ConsoleMessageAdded, this._consoleMessageAdded, this);
62 this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.ConsoleMessageRemoved, this._consoleMessageRemoved, this);
63 this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.ConsoleMessagesCleared, this._consoleMessagesCleared, this);
64 this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.SourceMappingChanged, this._onSourceMappingChanged, this);
65 this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._workingCopyChanged, this);
66 this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._workingCopyCommitted, this);
68 this._registerShortcuts();
69 this._updateScriptFile();
72 WebInspector.JavaScriptSourceFrame.prototype = {
73 _registerShortcuts: function()
75 var shortcutKeys = WebInspector.ShortcutsScreen.SourcesPanelShortcuts;
76 for (var i = 0; i < shortcutKeys.EvaluateSelectionInConsole.length; ++i) {
77 var keyDescriptor = shortcutKeys.EvaluateSelectionInConsole[i];
78 this.addShortcut(keyDescriptor.key, this._evaluateSelectionInConsole.bind(this));
80 for (var i = 0; i < shortcutKeys.AddSelectionToWatch.length; ++i) {
81 var keyDescriptor = shortcutKeys.AddSelectionToWatch[i];
82 this.addShortcut(keyDescriptor.key, this._addCurrentSelectionToWatch.bind(this));
86 _addCurrentSelectionToWatch: function()
88 var textSelection = this.textEditor.selection();
89 if (textSelection && !textSelection.isEmpty())
90 this._innerAddToWatch(this.textEditor.copyRange(textSelection));
94 * @param {string} expression
96 _innerAddToWatch: function(expression)
98 this._scriptsPanel.addToWatch(expression);
104 _evaluateSelectionInConsole: function()
106 var selection = this.textEditor.selection();
107 if (!selection || selection.isEmpty())
109 WebInspector.evaluateInConsole(this.textEditor.copyRange(selection));
116 WebInspector.UISourceCodeFrame.prototype.wasShown.call(this);
121 WebInspector.UISourceCodeFrame.prototype.willHide.call(this);
122 this._popoverHelper.hidePopover();
125 onUISourceCodeContentChanged: function()
127 this._removeAllBreakpoints();
128 WebInspector.UISourceCodeFrame.prototype.onUISourceCodeContentChanged.call(this);
131 populateLineGutterContextMenu: function(contextMenu, lineNumber)
133 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Continue to here" : "Continue to Here"), this._continueToLine.bind(this, lineNumber));
134 var breakpoint = this._breakpointManager.findBreakpointOnLine(this._uiSourceCode, lineNumber);
136 // This row doesn't have a breakpoint: We want to show Add Breakpoint and Add and Edit Breakpoint.
137 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Add breakpoint" : "Add Breakpoint"), this._setBreakpoint.bind(this, lineNumber, 0, "", true));
138 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Add conditional breakpoint…" : "Add Conditional Breakpoint…"), this._editBreakpointCondition.bind(this, lineNumber));
140 // This row has a breakpoint, we want to show edit and remove breakpoint, and either disable or enable.
141 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Remove breakpoint" : "Remove Breakpoint"), breakpoint.remove.bind(breakpoint));
142 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Edit breakpoint…" : "Edit Breakpoint…"), this._editBreakpointCondition.bind(this, lineNumber, breakpoint));
143 if (breakpoint.enabled())
144 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Disable breakpoint" : "Disable Breakpoint"), breakpoint.setEnabled.bind(breakpoint, false));
146 contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Enable breakpoint" : "Enable Breakpoint"), breakpoint.setEnabled.bind(breakpoint, true));
150 populateTextAreaContextMenu: function(contextMenu, lineNumber)
152 var textSelection = this.textEditor.selection();
153 if (textSelection && !textSelection.isEmpty()) {
154 var selection = this.textEditor.copyRange(textSelection);
155 var addToWatchLabel = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Add to watch" : "Add to Watch");
156 contextMenu.appendItem(addToWatchLabel, this._innerAddToWatch.bind(this, selection));
157 var evaluateLabel = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Evaluate in console" : "Evaluate in Console");
158 contextMenu.appendItem(evaluateLabel, WebInspector.evaluateInConsole.bind(WebInspector, selection));
159 contextMenu.appendSeparator();
160 } else if (!this._uiSourceCode.isEditable() && this._uiSourceCode.contentType() === WebInspector.resourceTypes.Script) {
162 // FIXME: Change condition above to explicitly check that current uiSourceCode is created by default debugger mapping
163 // and move the code adding this menu item to generic context menu provider for UISourceCode.
164 var liveEditLabel = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Live edit" : "Live Edit");
165 contextMenu.appendItem(liveEditLabel, liveEdit.bind(this));
166 contextMenu.appendSeparator();
170 * @this {WebInspector.JavaScriptSourceFrame}
174 var liveEditUISourceCode = WebInspector.liveEditSupport.uiSourceCodeForLiveEdit(this._uiSourceCode);
175 this._scriptsPanel.showUISourceCode(liveEditUISourceCode, lineNumber)
178 WebInspector.UISourceCodeFrame.prototype.populateTextAreaContextMenu.call(this, contextMenu, lineNumber);
181 _workingCopyChanged: function(event)
183 if (this._supportsEnabledBreakpointsWhileEditing() || this._scriptFile)
186 if (this._uiSourceCode.isDirty())
187 this._muteBreakpointsWhileEditing();
189 this._restoreBreakpointsAfterEditing();
192 _workingCopyCommitted: function(event)
194 if (this._supportsEnabledBreakpointsWhileEditing() || this._scriptFile)
196 this._restoreBreakpointsAfterEditing();
199 _didMergeToVM: function()
201 if (this._supportsEnabledBreakpointsWhileEditing())
203 this._restoreBreakpointsAfterEditing();
206 _didDivergeFromVM: function()
208 if (this._supportsEnabledBreakpointsWhileEditing())
210 this._muteBreakpointsWhileEditing();
213 _muteBreakpointsWhileEditing: function()
217 for (var lineNumber = 0; lineNumber < this._textEditor.linesCount; ++lineNumber) {
218 var breakpointDecoration = this._textEditor.getAttribute(lineNumber, "breakpoint");
219 if (!breakpointDecoration)
221 this._removeBreakpointDecoration(lineNumber);
222 this._addBreakpointDecoration(lineNumber, breakpointDecoration.columnNumber, breakpointDecoration.condition, breakpointDecoration.enabled, true);
227 _supportsEnabledBreakpointsWhileEditing: function()
229 return this._uiSourceCode.project().type() === WebInspector.projectTypes.Snippets;
232 _restoreBreakpointsAfterEditing: function()
235 var breakpoints = {};
236 // Save and remove muted breakpoint decorations.
237 for (var lineNumber = 0; lineNumber < this._textEditor.linesCount; ++lineNumber) {
238 var breakpointDecoration = this._textEditor.getAttribute(lineNumber, "breakpoint");
239 if (breakpointDecoration) {
240 breakpoints[lineNumber] = breakpointDecoration;
241 this._removeBreakpointDecoration(lineNumber);
245 // Remove all breakpoints.
246 this._removeAllBreakpoints();
248 // Restore all breakpoints from saved decorations.
249 for (var lineNumberString in breakpoints) {
250 var lineNumber = parseInt(lineNumberString, 10);
251 if (isNaN(lineNumber))
253 var breakpointDecoration = breakpoints[lineNumberString];
254 this._setBreakpoint(lineNumber, breakpointDecoration.columnNumber, breakpointDecoration.condition, breakpointDecoration.enabled);
258 _removeAllBreakpoints: function()
260 var breakpoints = this._breakpointManager.breakpointsForUISourceCode(this._uiSourceCode);
261 for (var i = 0; i < breakpoints.length; ++i)
262 breakpoints[i].remove();
265 _getPopoverAnchor: function(element, event)
267 if (!WebInspector.debuggerModel.isPaused())
270 var textPosition = this.textEditor.coordinatesToCursorPosition(event.x, event.y);
273 var mouseLine = textPosition.startLine;
274 var mouseColumn = textPosition.startColumn;
275 var textSelection = this.textEditor.selection().normalize();
276 if (textSelection && !textSelection.isEmpty()) {
277 if (textSelection.startLine !== textSelection.endLine || textSelection.startLine !== mouseLine || mouseColumn < textSelection.startColumn || mouseColumn > textSelection.endColumn)
280 var leftCorner = this.textEditor.cursorPositionToCoordinates(textSelection.startLine, textSelection.startColumn);
281 var rightCorner = this.textEditor.cursorPositionToCoordinates(textSelection.endLine, textSelection.endColumn);
282 var anchorBox = new AnchorBox(leftCorner.x, leftCorner.y, rightCorner.x - leftCorner.x, leftCorner.height);
283 anchorBox.highlight = {
284 lineNumber: textSelection.startLine,
285 startColumn: textSelection.startColumn,
286 endColumn: textSelection.endColumn - 1
288 anchorBox.forSelection = true;
292 var token = this.textEditor.tokenAtTextPosition(textPosition.startLine, textPosition.startColumn);
295 var lineNumber = textPosition.startLine;
296 var line = this.textEditor.line(lineNumber);
297 var tokenContent = line.substring(token.startColumn, token.endColumn + 1);
299 var isIdentifier = token.type.startsWith("js-variable") || token.type.startsWith("js-property") || token.type == "js-def";
300 if (!isIdentifier && (token.type !== "js-keyword" || tokenContent !== "this"))
303 var leftCorner = this.textEditor.cursorPositionToCoordinates(lineNumber, token.startColumn);
304 var rightCorner = this.textEditor.cursorPositionToCoordinates(lineNumber, token.endColumn + 1);
305 var anchorBox = new AnchorBox(leftCorner.x, leftCorner.y, rightCorner.x - leftCorner.x, leftCorner.height);
307 anchorBox.highlight = {
308 lineNumber: lineNumber,
309 startColumn: token.startColumn,
310 endColumn: token.endColumn
316 _resolveObjectForPopover: function(anchorBox, showCallback, objectGroupName)
319 * @param {?RuntimeAgent.RemoteObject} result
320 * @param {boolean=} wasThrown
321 * @this {WebInspector.JavaScriptSourceFrame}
323 function showObjectPopover(result, wasThrown)
325 if (!WebInspector.debuggerModel.isPaused() || !result) {
326 this._popoverHelper.hidePopover();
329 this._popoverAnchorBox = anchorBox;
330 showCallback(WebInspector.RemoteObject.fromPayload(result), wasThrown, this._popoverAnchorBox);
331 // Popover may have been removed by showCallback().
332 if (this._popoverAnchorBox) {
333 var highlightRange = new WebInspector.TextRange(lineNumber, startHighlight, lineNumber, endHighlight);
334 this._popoverAnchorBox._highlightDescriptor = this.textEditor.highlightRange(highlightRange, "source-frame-eval-expression");
338 if (!WebInspector.debuggerModel.isPaused()) {
339 this._popoverHelper.hidePopover();
342 var lineNumber = anchorBox.highlight.lineNumber;
343 var startHighlight = anchorBox.highlight.startColumn;
344 var endHighlight = anchorBox.highlight.endColumn;
345 var line = this.textEditor.line(lineNumber);
346 if (!anchorBox.forSelection) {
347 while (startHighlight > 1 && line.charAt(startHighlight - 1) === '.') {
348 var token = this.textEditor.tokenAtTextPosition(lineNumber, startHighlight - 2);
350 this._popoverHelper.hidePopover();
353 startHighlight = token.startColumn;
356 var evaluationText = line.substring(startHighlight, endHighlight + 1);
357 var selectedCallFrame = WebInspector.debuggerModel.selectedCallFrame();
358 selectedCallFrame.evaluate(evaluationText, objectGroupName, false, true, false, false, showObjectPopover.bind(this));
361 _onHidePopover: function()
363 if (!this._popoverAnchorBox)
365 if (this._popoverAnchorBox._highlightDescriptor)
366 this.textEditor.removeHighlight(this._popoverAnchorBox._highlightDescriptor);
367 delete this._popoverAnchorBox;
371 * @param {number} lineNumber
372 * @param {number} columnNumber
373 * @param {string} condition
374 * @param {boolean} enabled
375 * @param {boolean} mutedWhileEditing
377 _addBreakpointDecoration: function(lineNumber, columnNumber, condition, enabled, mutedWhileEditing)
380 condition: condition,
382 columnNumber: columnNumber
385 this.textEditor.setAttribute(lineNumber, "breakpoint", breakpoint);
387 var disabled = !enabled || mutedWhileEditing;
388 this.textEditor.addBreakpoint(lineNumber, disabled, !!condition);
391 _removeBreakpointDecoration: function(lineNumber)
393 this.textEditor.removeAttribute(lineNumber, "breakpoint");
394 this.textEditor.removeBreakpoint(lineNumber);
397 _onKeyDown: function(event)
399 if (event.keyIdentifier === "U+001B") { // Escape key
400 if (this._popoverHelper.isPopoverVisible()) {
401 this._popoverHelper.hidePopover();
405 if (this._stepIntoMarkup && WebInspector.KeyboardShortcut.eventHasCtrlOrMeta(event)) {
406 this._stepIntoMarkup.stoptIteratingSelection();
414 * @param {number} lineNumber
415 * @param {!WebInspector.BreakpointManager.Breakpoint=} breakpoint
417 _editBreakpointCondition: function(lineNumber, breakpoint)
419 this._conditionElement = this._createConditionElement(lineNumber);
420 this.textEditor.addDecoration(lineNumber, this._conditionElement);
423 * @this {WebInspector.JavaScriptSourceFrame}
425 function finishEditing(committed, element, newText)
427 this.textEditor.removeDecoration(lineNumber, this._conditionElement);
428 delete this._conditionEditorElement;
429 delete this._conditionElement;
434 breakpoint.setCondition(newText);
436 this._setBreakpoint(lineNumber, 0, newText, true);
439 var config = new WebInspector.InplaceEditor.Config(finishEditing.bind(this, true), finishEditing.bind(this, false));
440 WebInspector.InplaceEditor.startEditing(this._conditionEditorElement, config);
441 this._conditionEditorElement.value = breakpoint ? breakpoint.condition() : "";
442 this._conditionEditorElement.select();
445 _createConditionElement: function(lineNumber)
447 var conditionElement = document.createElement("div");
448 conditionElement.className = "source-frame-breakpoint-condition";
450 var labelElement = document.createElement("label");
451 labelElement.className = "source-frame-breakpoint-message";
452 labelElement.htmlFor = "source-frame-breakpoint-condition";
453 labelElement.appendChild(document.createTextNode(WebInspector.UIString("The breakpoint on line %d will stop only if this expression is true:", lineNumber)));
454 conditionElement.appendChild(labelElement);
456 var editorElement = document.createElement("input");
457 editorElement.id = "source-frame-breakpoint-condition";
458 editorElement.className = "monospace";
459 editorElement.type = "text";
460 conditionElement.appendChild(editorElement);
461 this._conditionEditorElement = editorElement;
463 return conditionElement;
467 * @param {number} lineNumber
468 * @param {!WebInspector.DebuggerModel.CallFrame} callFrame
470 setExecutionLine: function(lineNumber, callFrame)
472 this._executionLineNumber = lineNumber;
473 this._executionCallFrame = callFrame;
475 this.textEditor.setExecutionLine(lineNumber);
477 if (WebInspector.experimentsSettings.stepIntoSelection.isEnabled())
478 callFrame.getStepIntoLocations(locationsCallback.bind(this));
482 * @param {!Array.<!DebuggerAgent.Location>} locations
483 * @this {WebInspector.JavaScriptSourceFrame}
485 function locationsCallback(locations)
487 if (this._executionCallFrame !== callFrame || this._stepIntoMarkup)
489 this._stepIntoMarkup = WebInspector.JavaScriptSourceFrame.StepIntoMarkup.create(this, locations);
490 if (this._stepIntoMarkup)
491 this._stepIntoMarkup.show();
495 clearExecutionLine: function()
497 if (this._stepIntoMarkup) {
498 this._stepIntoMarkup.dispose();
499 delete this._stepIntoMarkup;
502 if (this.loaded && typeof this._executionLineNumber === "number")
503 this.textEditor.clearExecutionLine();
504 delete this._executionLineNumber;
505 delete this._executionCallFrame;
508 _lineNumberAfterEditing: function(lineNumber, oldRange, newRange)
510 var shiftOffset = lineNumber <= oldRange.startLine ? 0 : newRange.linesCount - oldRange.linesCount;
512 // Special case of editing the line itself. We should decide whether the line number should move below or not.
513 if (lineNumber === oldRange.startLine) {
514 var whiteSpacesRegex = /^[\s\xA0]*$/;
515 for (var i = 0; lineNumber + i <= newRange.endLine; ++i) {
516 if (!whiteSpacesRegex.test(this.textEditor.line(lineNumber + i))) {
523 var newLineNumber = Math.max(0, lineNumber + shiftOffset);
524 if (oldRange.startLine < lineNumber && lineNumber < oldRange.endLine)
525 newLineNumber = oldRange.startLine;
526 return newLineNumber;
529 _onMouseDownAndClick: function(isMouseDown, event)
531 var markup = this._stepIntoMarkup;
534 var index = markup.findItemByCoordinates(event.x, event.y);
535 if (typeof index === "undefined")
539 // Do not let text editor to spoil 'click' event that is coming for us.
542 var rawLocation = markup.getRawPosition(index);
543 this._scriptsPanel.doStepIntoSelection(rawLocation);
550 _shouldIgnoreExternalBreakpointEvents: function()
552 if (this._supportsEnabledBreakpointsWhileEditing())
556 return this._scriptFile && (this._scriptFile.isDivergingFromVM() || this._scriptFile.isMergingToVM());
559 _breakpointAdded: function(event)
561 var uiLocation = /** @type {!WebInspector.UILocation} */ (event.data.uiLocation);
562 if (uiLocation.uiSourceCode !== this._uiSourceCode)
564 if (this._shouldIgnoreExternalBreakpointEvents())
567 var breakpoint = /** @type {!WebInspector.BreakpointManager.Breakpoint} */ (event.data.breakpoint);
569 this._addBreakpointDecoration(uiLocation.lineNumber, uiLocation.columnNumber, breakpoint.condition(), breakpoint.enabled(), false);
572 _breakpointRemoved: function(event)
574 var uiLocation = /** @type {!WebInspector.UILocation} */ (event.data.uiLocation);
575 if (uiLocation.uiSourceCode !== this._uiSourceCode)
577 if (this._shouldIgnoreExternalBreakpointEvents())
580 var breakpoint = /** @type {!WebInspector.BreakpointManager.Breakpoint} */ (event.data.breakpoint);
581 var remainingBreakpoint = this._breakpointManager.findBreakpointOnLine(this._uiSourceCode, uiLocation.lineNumber);
582 if (!remainingBreakpoint && this.loaded)
583 this._removeBreakpointDecoration(uiLocation.lineNumber);
586 _consoleMessageAdded: function(event)
588 var message = /** @type {!WebInspector.PresentationConsoleMessage} */ (event.data);
590 this.addMessageToSource(message.lineNumber, message.originalMessage);
593 _consoleMessageRemoved: function(event)
595 var message = /** @type {!WebInspector.PresentationConsoleMessage} */ (event.data);
597 this.removeMessageFromSource(message.lineNumber, message.originalMessage);
600 _consoleMessagesCleared: function(event)
602 this.clearMessages();
606 * @param {!WebInspector.Event} event
608 _onSourceMappingChanged: function(event)
610 this._updateScriptFile();
613 _updateScriptFile: function()
615 if (this._scriptFile) {
616 this._scriptFile.removeEventListener(WebInspector.ScriptFile.Events.DidMergeToVM, this._didMergeToVM, this);
617 this._scriptFile.removeEventListener(WebInspector.ScriptFile.Events.DidDivergeFromVM, this._didDivergeFromVM, this);
618 if (this._muted && !this._uiSourceCode.isDirty())
619 this._restoreBreakpointsAfterEditing();
621 this._scriptFile = this._uiSourceCode.scriptFile();
622 if (this._scriptFile) {
623 this._scriptFile.addEventListener(WebInspector.ScriptFile.Events.DidMergeToVM, this._didMergeToVM, this);
624 this._scriptFile.addEventListener(WebInspector.ScriptFile.Events.DidDivergeFromVM, this._didDivergeFromVM, this);
627 this._scriptFile.checkMapping();
631 beforeFormattedChange: function()
633 this.clearExecutionLine();
636 onTextEditorContentLoaded: function()
638 if (typeof this._executionLineNumber === "number")
639 this.setExecutionLine(this._executionLineNumber, this._executionCallFrame);
641 var breakpointLocations = this._breakpointManager.breakpointLocationsForUISourceCode(this._uiSourceCode);
642 for (var i = 0; i < breakpointLocations.length; ++i)
643 this._breakpointAdded({data:breakpointLocations[i]});
645 var messages = this._uiSourceCode.consoleMessages();
646 for (var i = 0; i < messages.length; ++i) {
647 var message = messages[i];
648 this.addMessageToSource(message.lineNumber, message.originalMessage);
651 if (this._scriptFile)
652 this._scriptFile.checkMapping();
656 * @param {!WebInspector.Event} event
658 _handleGutterClick: function(event)
663 var eventData = /** @type {!WebInspector.TextEditor.GutterClickEventData} */ (event.data);
664 var lineNumber = eventData.lineNumber;
665 var eventObject = /** @type {!Event} */ (eventData.event);
667 if (eventObject.button != 0 || eventObject.altKey || eventObject.ctrlKey || eventObject.metaKey)
670 this._toggleBreakpoint(lineNumber, eventObject.shiftKey);
671 eventObject.consume(true);
675 * @param {number} lineNumber
676 * @param {boolean} onlyDisable
678 _toggleBreakpoint: function(lineNumber, onlyDisable)
680 var breakpoint = this._breakpointManager.findBreakpointOnLine(this._uiSourceCode, lineNumber);
683 breakpoint.setEnabled(!breakpoint.enabled());
687 this._setBreakpoint(lineNumber, 0, "", true);
690 toggleBreakpointOnCurrentLine: function()
695 var selection = this.textEditor.selection();
698 this._toggleBreakpoint(selection.startLine, false);
702 * @param {number} lineNumber
703 * @param {number} columnNumber
704 * @param {string} condition
705 * @param {boolean} enabled
707 _setBreakpoint: function(lineNumber, columnNumber, condition, enabled)
709 this._breakpointManager.setBreakpoint(this._uiSourceCode, lineNumber, columnNumber, condition, enabled);
711 WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, {
712 action: WebInspector.UserMetrics.UserActionNames.SetBreakpoint,
713 url: this._uiSourceCode.originURL(),
720 * @param {number} lineNumber
722 _continueToLine: function(lineNumber)
724 var rawLocation = /** @type {!WebInspector.DebuggerModel.Location} */ (this._uiSourceCode.uiLocationToRawLocation(lineNumber, 0));
725 this._scriptsPanel.continueToLocation(rawLocation);
729 * @return {!WebInspector.JavaScriptSourceFrame.StepIntoMarkup|undefined}
731 stepIntoMarkup: function()
733 return this._stepIntoMarkup;
738 this._breakpointManager.removeEventListener(WebInspector.BreakpointManager.Events.BreakpointAdded, this._breakpointAdded, this);
739 this._breakpointManager.removeEventListener(WebInspector.BreakpointManager.Events.BreakpointRemoved, this._breakpointRemoved, this);
740 this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.ConsoleMessageAdded, this._consoleMessageAdded, this);
741 this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.ConsoleMessageRemoved, this._consoleMessageRemoved, this);
742 this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.ConsoleMessagesCleared, this._consoleMessagesCleared, this);
743 this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.SourceMappingChanged, this._onSourceMappingChanged, this);
744 this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._workingCopyChanged, this);
745 this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._workingCopyCommitted, this);
746 WebInspector.UISourceCodeFrame.prototype.dispose.call(this);
749 __proto__: WebInspector.UISourceCodeFrame.prototype
754 * @param {!Array.<!DebuggerAgent.Location>} rawPositions
755 * @param {!Array.<!WebInspector.TextRange>} editorRanges
756 * @param {number} firstToExecute
757 * @param {!WebInspector.JavaScriptSourceFrame} sourceFrame
759 WebInspector.JavaScriptSourceFrame.StepIntoMarkup = function(rawPositions, editorRanges, firstToExecute, sourceFrame)
761 this._positions = rawPositions;
762 this._editorRanges = editorRanges;
763 this._highlightDescriptors = new Array(rawPositions.length);
764 this._currentHighlight = undefined;
765 this._firstToExecute = firstToExecute;
766 this._currentSelection = undefined;
767 this._sourceFrame = sourceFrame;
770 WebInspector.JavaScriptSourceFrame.StepIntoMarkup.prototype = {
773 var highlight = this._getVisibleHighlight();
774 for (var i = 0; i < this._positions.length; ++i)
775 this._highlightItem(i, i === highlight);
776 this._shownVisibleHighlight = highlight;
779 startIteratingSelection: function()
781 this._currentSelection = this._positions.length
782 this._redrawHighlight();
785 stopIteratingSelection: function()
787 this._currentSelection = undefined;
788 this._redrawHighlight();
792 * @param {boolean} backward
794 iterateSelection: function(backward)
796 if (typeof this._currentSelection === "undefined")
798 var nextSelection = backward ? this._currentSelection - 1 : this._currentSelection + 1;
799 var modulo = this._positions.length + 1;
800 nextSelection = (nextSelection + modulo) % modulo;
801 this._currentSelection = nextSelection;
802 this._redrawHighlight();
805 _redrawHighlight: function()
807 var visibleHighlight = this._getVisibleHighlight();
808 if (this._shownVisibleHighlight === visibleHighlight)
810 this._hideItemHighlight(this._shownVisibleHighlight);
811 this._hideItemHighlight(visibleHighlight);
812 this._highlightItem(this._shownVisibleHighlight, false);
813 this._highlightItem(visibleHighlight, true);
814 this._shownVisibleHighlight = visibleHighlight;
820 _getVisibleHighlight: function()
822 return typeof this._currentSelection === "undefined" ? this._firstToExecute : this._currentSelection;
826 * @param {number} position
827 * @param {boolean} selected
829 _highlightItem: function(position, selected)
831 if (position === this._positions.length)
833 var styleName = selected ? "source-frame-stepin-mark-highlighted" : "source-frame-stepin-mark";
834 var textEditor = this._sourceFrame.textEditor;
835 var highlightDescriptor = textEditor.highlightRange(this._editorRanges[position], styleName);
836 this._highlightDescriptors[position] = highlightDescriptor;
840 * @param {number} position
842 _hideItemHighlight: function(position)
844 if (position === this._positions.length)
846 var highlightDescriptor = this._highlightDescriptors[position];
847 console.assert(highlightDescriptor);
848 var textEditor = this._sourceFrame.textEditor;
849 textEditor.removeHighlight(highlightDescriptor);
850 this._highlightDescriptors[position] = undefined;
855 for (var i = 0; i < this._positions.length; ++i)
856 this._hideItemHighlight(i);
862 * @return {number|undefined}
864 findItemByCoordinates: function(x, y)
866 var textPosition = this._sourceFrame.textEditor.coordinatesToCursorPosition(x, y);
870 var ranges = this._editorRanges;
872 for (var i = 0; i < ranges.length; ++i) {
873 var nextRange = ranges[i];
874 if (nextRange.startLine == textPosition.startLine && nextRange.startColumn <= textPosition.startColumn && nextRange.endColumn >= textPosition.startColumn)
880 * @return {number|undefined}
882 getSelectedItemIndex: function()
884 if (this._currentSelection === this._positions.length)
886 return this._currentSelection;
890 * @return {!WebInspector.DebuggerModel.Location}
892 getRawPosition: function(position)
894 return /** @type {!WebInspector.DebuggerModel.Location} */ (this._positions[position]);
900 * @param {!WebInspector.JavaScriptSourceFrame} sourceFrame
901 * @param {!Array.<!DebuggerAgent.Location>} stepIntoRawLocations
902 * @return {?WebInspector.JavaScriptSourceFrame.StepIntoMarkup}
904 WebInspector.JavaScriptSourceFrame.StepIntoMarkup.create = function(sourceFrame, stepIntoRawLocations)
906 if (!stepIntoRawLocations.length)
909 var firstToExecute = stepIntoRawLocations[0];
910 stepIntoRawLocations.sort(WebInspector.JavaScriptSourceFrame.StepIntoMarkup._Comparator);
911 var firstToExecuteIndex = stepIntoRawLocations.indexOf(firstToExecute);
913 var textEditor = sourceFrame.textEditor;
915 for (var i = 0; i < stepIntoRawLocations.length; ++i) {
916 var uiLocation = WebInspector.debuggerModel.rawLocationToUILocation(/** @type {!WebInspector.DebuggerModel.Location} */ (stepIntoRawLocations[i]));
918 var token = textEditor.tokenAtTextPosition(uiLocation.lineNumber, uiLocation.columnNumber);
922 startColumn = token.startColumn;
923 endColumn = token.endColumn;
925 startColumn = uiLocation.columnNumber;
926 endColumn = uiLocation.columnNumber;
928 var range = new WebInspector.TextRange(uiLocation.lineNumber, startColumn, uiLocation.lineNumber, endColumn);
929 uiRanges.push(range);
932 return new WebInspector.JavaScriptSourceFrame.StepIntoMarkup(stepIntoRawLocations, uiRanges, firstToExecuteIndex, sourceFrame);
936 * @param {!DebuggerAgent.Location} locationA
937 * @param {!DebuggerAgent.Location} locationB
940 WebInspector.JavaScriptSourceFrame.StepIntoMarkup._Comparator = function(locationA, locationB)
942 if (locationA.lineNumber === locationB.lineNumber)
943 return locationA.columnNumber - locationB.columnNumber;
945 return locationA.lineNumber - locationB.lineNumber;