diff options
Diffstat (limited to 'chromium/third_party/WebKit/Source/devtools/front_end/sources/JavaScriptSourceFrame.js')
-rw-r--r-- | chromium/third_party/WebKit/Source/devtools/front_end/sources/JavaScriptSourceFrame.js | 845 |
1 files changed, 845 insertions, 0 deletions
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/sources/JavaScriptSourceFrame.js b/chromium/third_party/WebKit/Source/devtools/front_end/sources/JavaScriptSourceFrame.js new file mode 100644 index 00000000000..f2331b8913c --- /dev/null +++ b/chromium/third_party/WebKit/Source/devtools/front_end/sources/JavaScriptSourceFrame.js @@ -0,0 +1,845 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @constructor + * @extends {WebInspector.UISourceCodeFrame} + * @param {!WebInspector.SourcesPanel} scriptsPanel + * @param {!WebInspector.UISourceCode} uiSourceCode + */ +WebInspector.JavaScriptSourceFrame = function(scriptsPanel, uiSourceCode) +{ + this._scriptsPanel = scriptsPanel; + this._breakpointManager = WebInspector.breakpointManager; + this._uiSourceCode = uiSourceCode; + + WebInspector.UISourceCodeFrame.call(this, uiSourceCode); + if (uiSourceCode.project().type() === WebInspector.projectTypes.Debugger) + this.element.classList.add("source-frame-debugger-script"); + + this._popoverHelper = new WebInspector.ObjectPopoverHelper(this.textEditor.element, + this._getPopoverAnchor.bind(this), this._resolveObjectForPopover.bind(this), this._onHidePopover.bind(this), true); + + this.textEditor.element.addEventListener("keydown", this._onKeyDown.bind(this), true); + + this.textEditor.addEventListener(WebInspector.TextEditor.Events.GutterClick, this._handleGutterClick.bind(this), this); + + this._breakpointManager.addEventListener(WebInspector.BreakpointManager.Events.BreakpointAdded, this._breakpointAdded, this); + this._breakpointManager.addEventListener(WebInspector.BreakpointManager.Events.BreakpointRemoved, this._breakpointRemoved, this); + + this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.ConsoleMessageAdded, this._consoleMessageAdded, this); + this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.ConsoleMessageRemoved, this._consoleMessageRemoved, this); + this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.ConsoleMessagesCleared, this._consoleMessagesCleared, this); + this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.SourceMappingChanged, this._onSourceMappingChanged, this); + this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._workingCopyChanged, this); + this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._workingCopyCommitted, this); + + /** @type {!Map.<!WebInspector.Target, !WebInspector.ScriptFile>}*/ + this._scriptFileForTarget = new Map(); + this._registerShortcuts(); + var targets = WebInspector.targetManager.targets(); + for (var i = 0; i < targets.length; ++i) { + var scriptFile = uiSourceCode.scriptFileForTarget(targets[i]); + if (scriptFile) + this._updateScriptFile(targets[i]); + } +} + +WebInspector.JavaScriptSourceFrame.prototype = { + /** + * @param {!Element} infobarElement + */ + _showInfobar: function(infobarElement) + { + if (this._infobarElement) + this._infobarElement.remove(); + this._infobarElement = infobarElement; + this._infobarElement.classList.add("java-script-source-frame-infobar"); + this.element.insertBefore(this._infobarElement, this.element.children[0]); + this.doResize(); + }, + + /** + * @param {!Element} infobarElement + */ + _hideInfobar: function(infobarElement) + { + infobarElement.remove(); + this.doResize(); + }, + + _showDivergedInfobar: function() + { + if (this._uiSourceCode.contentType() !== WebInspector.resourceTypes.Script) + return; + + this._divergedInfobarElement = document.createElement("div"); + var infobarMainRow = this._divergedInfobarElement.createChild("div", "java-script-source-frame-infobar-main-row"); + var infobarDetailsContainer = this._divergedInfobarElement.createChild("span", "java-script-source-frame-infobar-details-container"); + + infobarMainRow.createChild("span", "java-script-source-frame-infobar-warning-icon"); + var infobarMessage = infobarMainRow.createChild("span", "java-script-source-frame-infobar-row-message"); + infobarMessage.textContent = WebInspector.UIString("Workspace mapping mismatch"); + + /** + * @this {WebInspector.JavaScriptSourceFrame} + */ + function updateDetailsVisibility() + { + detailsToggleElement.textContent = detailsToggleElement._toggled ? WebInspector.UIString("less") : WebInspector.UIString("more"); + infobarDetailsContainer.classList.toggle("hidden", !detailsToggleElement._toggled); + this.doResize(); + } + + /** + * @this {WebInspector.JavaScriptSourceFrame} + */ + function toggleDetails() + { + detailsToggleElement._toggled = !detailsToggleElement._toggled; + updateDetailsVisibility.call(this); + } + + infobarMainRow.appendChild(document.createTextNode("\u00a0")); + var detailsToggleElement = infobarMainRow.createChild("div", "java-script-source-frame-infobar-toggle"); + detailsToggleElement.addEventListener("click", toggleDetails.bind(this), false); + updateDetailsVisibility.call(this); + + function createDetailsRowMessage() + { + var infobarDetailsRow = infobarDetailsContainer.createChild("div", "java-script-source-frame-infobar-details-row"); + return infobarDetailsRow.createChild("span", "java-script-source-frame-infobar-row-message"); + } + + var infobarDetailsRowMessage; + + infobarDetailsRowMessage = createDetailsRowMessage(); + infobarDetailsRowMessage.appendChild(document.createTextNode(WebInspector.UIString("The content of this file on the file system:\u00a0"))); + var fileURL = this._uiSourceCode.originURL(); + infobarDetailsRowMessage.appendChild(WebInspector.linkifyURLAsNode(fileURL, fileURL, "java-script-source-frame-infobar-details-url", true, fileURL)); + + infobarDetailsRowMessage = createDetailsRowMessage(); + infobarDetailsRowMessage.appendChild(document.createTextNode(WebInspector.UIString("does not match the loaded script:\u00a0"))); + var scriptURL = this._uiSourceCode.url; + infobarDetailsRowMessage.appendChild(WebInspector.linkifyURLAsNode(scriptURL, scriptURL, "java-script-source-frame-infobar-details-url", true, scriptURL)); + + // Add an empty row + createDetailsRowMessage(); + + createDetailsRowMessage().textContent = WebInspector.UIString("Possible solutions are:");; + + function createDetailsRowMessageAction(title) + { + infobarDetailsRowMessage = createDetailsRowMessage(); + infobarDetailsRowMessage.appendChild(document.createTextNode(" - ")); + infobarDetailsRowMessage.appendChild(document.createTextNode(title)); + } + + if (WebInspector.settings.cacheDisabled.get()) + createDetailsRowMessageAction(WebInspector.UIString("Reload inspected page")); + else + createDetailsRowMessageAction(WebInspector.UIString("Check \"Disable cache\" in settings and reload inspected page (recommended setup for authoring and debugging)")); + createDetailsRowMessageAction(WebInspector.UIString("Check that your file and script are both loaded from the correct source and their contents match.")); + + this._showInfobar(this._divergedInfobarElement); + }, + + _hideDivergedInfobar: function() + { + if (!this._divergedInfobarElement) + return; + this._hideInfobar(this._divergedInfobarElement); + delete this._divergedInfobarElement; + }, + + _registerShortcuts: function() + { + var shortcutKeys = WebInspector.ShortcutsScreen.SourcesPanelShortcuts; + for (var i = 0; i < shortcutKeys.EvaluateSelectionInConsole.length; ++i) { + var keyDescriptor = shortcutKeys.EvaluateSelectionInConsole[i]; + this.addShortcut(keyDescriptor.key, this._evaluateSelectionInConsole.bind(this)); + } + for (var i = 0; i < shortcutKeys.AddSelectionToWatch.length; ++i) { + var keyDescriptor = shortcutKeys.AddSelectionToWatch[i]; + this.addShortcut(keyDescriptor.key, this._addCurrentSelectionToWatch.bind(this)); + } + }, + + _addCurrentSelectionToWatch: function() + { + var textSelection = this.textEditor.selection(); + if (textSelection && !textSelection.isEmpty()) + this._innerAddToWatch(this.textEditor.copyRange(textSelection)); + }, + + /** + * @param {string} expression + */ + _innerAddToWatch: function(expression) + { + this._scriptsPanel.addToWatch(expression); + }, + + /** + * @return {boolean} + */ + _evaluateSelectionInConsole: function() + { + var selection = this.textEditor.selection(); + if (!selection || selection.isEmpty()) + return false; + this._evaluateInConsole(this.textEditor.copyRange(selection)); + return true; + }, + + /** + * @param {string} expression + */ + _evaluateInConsole: function(expression) + { + var currentExecutionContext = WebInspector.context.flavor(WebInspector.ExecutionContext); + if (currentExecutionContext) + WebInspector.ConsoleModel.evaluateCommandInConsole(currentExecutionContext, expression); + }, + + // View events + wasShown: function() + { + WebInspector.UISourceCodeFrame.prototype.wasShown.call(this); + }, + + willHide: function() + { + WebInspector.UISourceCodeFrame.prototype.willHide.call(this); + this._popoverHelper.hidePopover(); + }, + + onUISourceCodeContentChanged: function() + { + this._removeAllBreakpoints(); + WebInspector.UISourceCodeFrame.prototype.onUISourceCodeContentChanged.call(this); + }, + + populateLineGutterContextMenu: function(contextMenu, lineNumber) + { + contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Continue to here" : "Continue to Here"), this._continueToLine.bind(this, lineNumber)); + var breakpoint = this._breakpointManager.findBreakpointOnLine(this._uiSourceCode, lineNumber); + if (!breakpoint) { + // This row doesn't have a breakpoint: We want to show Add Breakpoint and Add and Edit Breakpoint. + contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Add breakpoint" : "Add Breakpoint"), this._setBreakpoint.bind(this, lineNumber, 0, "", true)); + contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Add conditional breakpoint…" : "Add Conditional Breakpoint…"), this._editBreakpointCondition.bind(this, lineNumber)); + } else { + // This row has a breakpoint, we want to show edit and remove breakpoint, and either disable or enable. + contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Remove breakpoint" : "Remove Breakpoint"), breakpoint.remove.bind(breakpoint)); + contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Edit breakpoint…" : "Edit Breakpoint…"), this._editBreakpointCondition.bind(this, lineNumber, breakpoint)); + if (breakpoint.enabled()) + contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Disable breakpoint" : "Disable Breakpoint"), breakpoint.setEnabled.bind(breakpoint, false)); + else + contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Enable breakpoint" : "Enable Breakpoint"), breakpoint.setEnabled.bind(breakpoint, true)); + } + }, + + populateTextAreaContextMenu: function(contextMenu, lineNumber) + { + var textSelection = this.textEditor.selection(); + if (textSelection && !textSelection.isEmpty()) { + var selection = this.textEditor.copyRange(textSelection); + var addToWatchLabel = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Add to watch" : "Add to Watch"); + contextMenu.appendItem(addToWatchLabel, this._innerAddToWatch.bind(this, selection)); + var evaluateLabel = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Evaluate in console" : "Evaluate in Console"); + contextMenu.appendItem(evaluateLabel, this._evaluateInConsole.bind(this, selection)); + contextMenu.appendSeparator(); + } else if (this._uiSourceCode.project().type() === WebInspector.projectTypes.Debugger) { + // FIXME: Change condition above to explicitly check that current uiSourceCode is created by default debugger mapping + // and move the code adding this menu item to generic context menu provider for UISourceCode. + var liveEditLabel = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Live edit" : "Live Edit"); + contextMenu.appendItem(liveEditLabel, liveEdit.bind(this)); + contextMenu.appendSeparator(); + } + + /** + * @this {WebInspector.JavaScriptSourceFrame} + */ + function liveEdit() + { + var liveEditUISourceCode = WebInspector.liveEditSupport.uiSourceCodeForLiveEdit(this._uiSourceCode); + WebInspector.Revealer.reveal(liveEditUISourceCode.uiLocation(lineNumber)); + } + + WebInspector.UISourceCodeFrame.prototype.populateTextAreaContextMenu.call(this, contextMenu, lineNumber); + }, + + _workingCopyChanged: function(event) + { + if (this._supportsEnabledBreakpointsWhileEditing() || this._scriptFileForTarget.size()) + return; + + if (this._uiSourceCode.isDirty()) + this._muteBreakpointsWhileEditing(); + else + this._restoreBreakpointsAfterEditing(); + }, + + _workingCopyCommitted: function(event) + { + if (this._supportsEnabledBreakpointsWhileEditing()) + return; + if (this._scriptFileForTarget.size()) { + this._hasCommittedLiveEdit = true; + var scriptFiles = this._scriptFileForTarget.values(); + for (var i = 0; i < scriptFiles.length; ++i) + scriptFiles[i].commitLiveEdit(); + return; + } + this._restoreBreakpointsAfterEditing(); + }, + + _didMergeToVM: function() + { + if (this._supportsEnabledBreakpointsWhileEditing()) + return; + this._updateDivergedInfobar(); + this._restoreBreakpointsIfConsistentScripts(); + }, + + _didDivergeFromVM: function() + { + if (this._supportsEnabledBreakpointsWhileEditing()) + return; + this._updateDivergedInfobar(); + this._muteBreakpointsWhileEditing(); + }, + + _muteBreakpointsWhileEditing: function() + { + if (this._muted) + return; + for (var lineNumber = 0; lineNumber < this._textEditor.linesCount; ++lineNumber) { + var breakpointDecoration = this._textEditor.getAttribute(lineNumber, "breakpoint"); + if (!breakpointDecoration) + continue; + this._removeBreakpointDecoration(lineNumber); + this._addBreakpointDecoration(lineNumber, breakpointDecoration.columnNumber, breakpointDecoration.condition, breakpointDecoration.enabled, true); + } + this._muted = true; + }, + + _updateDivergedInfobar: function() + { + if (this._uiSourceCode.project().type() !== WebInspector.projectTypes.FileSystem) { + this._hideDivergedInfobar(); + return; + } + + var scriptFiles = this._scriptFileForTarget.values(); + var hasDivergedScript = false; + for (var i = 0; i < scriptFiles.length; ++i) + hasDivergedScript = hasDivergedScript || scriptFiles[i].hasDivergedFromVM(); + + if (this._divergedInfobarElement) { + if (!hasDivergedScript || this._hasCommittedLiveEdit) + this._hideDivergedInfobar(); + } else { + if (hasDivergedScript && !this._uiSourceCode.isDirty() && !this._hasCommittedLiveEdit) + this._showDivergedInfobar(); + } + }, + + _supportsEnabledBreakpointsWhileEditing: function() + { + return this._uiSourceCode.project().type() === WebInspector.projectTypes.Snippets; + }, + + _restoreBreakpointsIfConsistentScripts: function() + { + var scriptFiles = this._scriptFileForTarget.values(); + for (var i = 0; i < scriptFiles.length; ++i) + if (scriptFiles[i].hasDivergedFromVM() || scriptFiles[i].isMergingToVM()) + return; + + this._restoreBreakpointsAfterEditing(); + }, + + _restoreBreakpointsAfterEditing: function() + { + delete this._muted; + var breakpoints = {}; + // Save and remove muted breakpoint decorations. + for (var lineNumber = 0; lineNumber < this._textEditor.linesCount; ++lineNumber) { + var breakpointDecoration = this._textEditor.getAttribute(lineNumber, "breakpoint"); + if (breakpointDecoration) { + breakpoints[lineNumber] = breakpointDecoration; + this._removeBreakpointDecoration(lineNumber); + } + } + + // Remove all breakpoints. + this._removeAllBreakpoints(); + + // Restore all breakpoints from saved decorations. + for (var lineNumberString in breakpoints) { + var lineNumber = parseInt(lineNumberString, 10); + if (isNaN(lineNumber)) + continue; + var breakpointDecoration = breakpoints[lineNumberString]; + this._setBreakpoint(lineNumber, breakpointDecoration.columnNumber, breakpointDecoration.condition, breakpointDecoration.enabled); + } + }, + + _removeAllBreakpoints: function() + { + var breakpoints = this._breakpointManager.breakpointsForUISourceCode(this._uiSourceCode); + for (var i = 0; i < breakpoints.length; ++i) + breakpoints[i].remove(); + }, + + _getPopoverAnchor: function(element, event) + { + if (!WebInspector.debuggerModel.isPaused()) + return null; + + var textPosition = this.textEditor.coordinatesToCursorPosition(event.x, event.y); + if (!textPosition) + return null; + var mouseLine = textPosition.startLine; + var mouseColumn = textPosition.startColumn; + var textSelection = this.textEditor.selection().normalize(); + if (textSelection && !textSelection.isEmpty()) { + if (textSelection.startLine !== textSelection.endLine || textSelection.startLine !== mouseLine || mouseColumn < textSelection.startColumn || mouseColumn > textSelection.endColumn) + return null; + + var leftCorner = this.textEditor.cursorPositionToCoordinates(textSelection.startLine, textSelection.startColumn); + var rightCorner = this.textEditor.cursorPositionToCoordinates(textSelection.endLine, textSelection.endColumn); + var anchorBox = new AnchorBox(leftCorner.x, leftCorner.y, rightCorner.x - leftCorner.x, leftCorner.height); + anchorBox.highlight = { + lineNumber: textSelection.startLine, + startColumn: textSelection.startColumn, + endColumn: textSelection.endColumn - 1 + }; + anchorBox.forSelection = true; + return anchorBox; + } + + var token = this.textEditor.tokenAtTextPosition(textPosition.startLine, textPosition.startColumn); + if (!token) + return null; + var lineNumber = textPosition.startLine; + var line = this.textEditor.line(lineNumber); + var tokenContent = line.substring(token.startColumn, token.endColumn + 1); + + var isIdentifier = token.type.startsWith("js-variable") || token.type.startsWith("js-property") || token.type == "js-def"; + if (!isIdentifier && (token.type !== "js-keyword" || tokenContent !== "this")) + return null; + + var leftCorner = this.textEditor.cursorPositionToCoordinates(lineNumber, token.startColumn); + var rightCorner = this.textEditor.cursorPositionToCoordinates(lineNumber, token.endColumn + 1); + var anchorBox = new AnchorBox(leftCorner.x, leftCorner.y, rightCorner.x - leftCorner.x, leftCorner.height); + + anchorBox.highlight = { + lineNumber: lineNumber, + startColumn: token.startColumn, + endColumn: token.endColumn + }; + + return anchorBox; + }, + + _resolveObjectForPopover: function(anchorBox, showCallback, objectGroupName) + { + if (!WebInspector.debuggerModel.isPaused()) { + this._popoverHelper.hidePopover(); + return; + } + var lineNumber = anchorBox.highlight.lineNumber; + var startHighlight = anchorBox.highlight.startColumn; + var endHighlight = anchorBox.highlight.endColumn; + var line = this.textEditor.line(lineNumber); + if (!anchorBox.forSelection) { + while (startHighlight > 1 && line.charAt(startHighlight - 1) === '.') { + var token = this.textEditor.tokenAtTextPosition(lineNumber, startHighlight - 2); + if (!token) { + this._popoverHelper.hidePopover(); + return; + } + startHighlight = token.startColumn; + } + } + var evaluationText = line.substring(startHighlight, endHighlight + 1); + var selectedCallFrame = WebInspector.debuggerModel.selectedCallFrame(); + selectedCallFrame.evaluate(evaluationText, objectGroupName, false, true, false, false, showObjectPopover.bind(this)); + + /** + * @param {?RuntimeAgent.RemoteObject} result + * @param {boolean=} wasThrown + * @this {WebInspector.JavaScriptSourceFrame} + */ + function showObjectPopover(result, wasThrown) + { + if (!WebInspector.debuggerModel.isPaused() || !result) { + this._popoverHelper.hidePopover(); + return; + } + this._popoverAnchorBox = anchorBox; + showCallback(selectedCallFrame.target().runtimeModel.createRemoteObject(result), wasThrown, this._popoverAnchorBox); + // Popover may have been removed by showCallback(). + if (this._popoverAnchorBox) { + var highlightRange = new WebInspector.TextRange(lineNumber, startHighlight, lineNumber, endHighlight); + this._popoverAnchorBox._highlightDescriptor = this.textEditor.highlightRange(highlightRange, "source-frame-eval-expression"); + } + } + }, + + _onHidePopover: function() + { + if (!this._popoverAnchorBox) + return; + if (this._popoverAnchorBox._highlightDescriptor) + this.textEditor.removeHighlight(this._popoverAnchorBox._highlightDescriptor); + delete this._popoverAnchorBox; + }, + + /** + * @param {number} lineNumber + * @param {number} columnNumber + * @param {string} condition + * @param {boolean} enabled + * @param {boolean} mutedWhileEditing + */ + _addBreakpointDecoration: function(lineNumber, columnNumber, condition, enabled, mutedWhileEditing) + { + var breakpoint = { + condition: condition, + enabled: enabled, + columnNumber: columnNumber + }; + + this.textEditor.setAttribute(lineNumber, "breakpoint", breakpoint); + + var disabled = !enabled || mutedWhileEditing; + this.textEditor.addBreakpoint(lineNumber, disabled, !!condition); + }, + + _removeBreakpointDecoration: function(lineNumber) + { + this.textEditor.removeAttribute(lineNumber, "breakpoint"); + this.textEditor.removeBreakpoint(lineNumber); + }, + + _onKeyDown: function(event) + { + if (event.keyIdentifier === "U+001B") { // Escape key + if (this._popoverHelper.isPopoverVisible()) { + this._popoverHelper.hidePopover(); + event.consume(); + } + } + }, + + /** + * @param {number} lineNumber + * @param {!WebInspector.BreakpointManager.Breakpoint=} breakpoint + */ + _editBreakpointCondition: function(lineNumber, breakpoint) + { + this._conditionElement = this._createConditionElement(lineNumber); + this.textEditor.addDecoration(lineNumber, this._conditionElement); + + /** + * @this {WebInspector.JavaScriptSourceFrame} + */ + function finishEditing(committed, element, newText) + { + this.textEditor.removeDecoration(lineNumber, this._conditionElement); + delete this._conditionEditorElement; + delete this._conditionElement; + if (!committed) + return; + + if (breakpoint) + breakpoint.setCondition(newText); + else + this._setBreakpoint(lineNumber, 0, newText, true); + } + + var config = new WebInspector.InplaceEditor.Config(finishEditing.bind(this, true), finishEditing.bind(this, false)); + WebInspector.InplaceEditor.startEditing(this._conditionEditorElement, config); + this._conditionEditorElement.value = breakpoint ? breakpoint.condition() : ""; + this._conditionEditorElement.select(); + }, + + _createConditionElement: function(lineNumber) + { + var conditionElement = document.createElement("div"); + conditionElement.className = "source-frame-breakpoint-condition"; + + var labelElement = document.createElement("label"); + labelElement.className = "source-frame-breakpoint-message"; + labelElement.htmlFor = "source-frame-breakpoint-condition"; + labelElement.appendChild(document.createTextNode(WebInspector.UIString("The breakpoint on line %d will stop only if this expression is true:", lineNumber + 1))); + conditionElement.appendChild(labelElement); + + var editorElement = document.createElement("input"); + editorElement.id = "source-frame-breakpoint-condition"; + editorElement.className = "monospace"; + editorElement.type = "text"; + conditionElement.appendChild(editorElement); + this._conditionEditorElement = editorElement; + + return conditionElement; + }, + + /** + * @param {number} lineNumber + */ + setExecutionLine: function(lineNumber) + { + this._executionLineNumber = lineNumber; + if (this.loaded) + this.textEditor.setExecutionLine(lineNumber); + }, + + clearExecutionLine: function() + { + if (this.loaded && typeof this._executionLineNumber === "number") + this.textEditor.clearExecutionLine(); + delete this._executionLineNumber; + }, + + /** + * @return {boolean} + */ + _shouldIgnoreExternalBreakpointEvents: function() + { + if (this._supportsEnabledBreakpointsWhileEditing()) + return false; + if (this._muted) + return true; + var scriptFiles = this._scriptFileForTarget.values(); + var hasDivergingOrMergingFile = false; + for (var i = 0; i < scriptFiles.length; ++i) + if (scriptFiles[i].isDivergingFromVM() || scriptFiles[i].isMergingToVM()) + return true; + return false; + }, + + _breakpointAdded: function(event) + { + var uiLocation = /** @type {!WebInspector.UILocation} */ (event.data.uiLocation); + if (uiLocation.uiSourceCode !== this._uiSourceCode) + return; + if (this._shouldIgnoreExternalBreakpointEvents()) + return; + + var breakpoint = /** @type {!WebInspector.BreakpointManager.Breakpoint} */ (event.data.breakpoint); + if (this.loaded) + this._addBreakpointDecoration(uiLocation.lineNumber, uiLocation.columnNumber, breakpoint.condition(), breakpoint.enabled(), false); + }, + + _breakpointRemoved: function(event) + { + var uiLocation = /** @type {!WebInspector.UILocation} */ (event.data.uiLocation); + if (uiLocation.uiSourceCode !== this._uiSourceCode) + return; + if (this._shouldIgnoreExternalBreakpointEvents()) + return; + + var breakpoint = /** @type {!WebInspector.BreakpointManager.Breakpoint} */ (event.data.breakpoint); + var remainingBreakpoint = this._breakpointManager.findBreakpointOnLine(this._uiSourceCode, uiLocation.lineNumber); + if (!remainingBreakpoint && this.loaded) + this._removeBreakpointDecoration(uiLocation.lineNumber); + }, + + _consoleMessageAdded: function(event) + { + var message = /** @type {!WebInspector.PresentationConsoleMessage} */ (event.data); + if (this.loaded) + this.addMessageToSource(message.lineNumber, message.originalMessage); + }, + + _consoleMessageRemoved: function(event) + { + var message = /** @type {!WebInspector.PresentationConsoleMessage} */ (event.data); + if (this.loaded) + this.removeMessageFromSource(message.lineNumber, message.originalMessage); + }, + + _consoleMessagesCleared: function(event) + { + this.clearMessages(); + }, + + /** + * @param {!WebInspector.Event} event + */ + _onSourceMappingChanged: function(event) + { + var data = /** @type {{target: !WebInspector.Target}} */ (event.data); + this._updateScriptFile(data.target); + }, + + /** + * @param {!WebInspector.Target} target + */ + _updateScriptFile: function(target) + { + var oldScriptFile = this._scriptFileForTarget.get(target); + var newScriptFile = this._uiSourceCode.scriptFileForTarget(target); + this._scriptFileForTarget.remove(target); + if (oldScriptFile) { + oldScriptFile.removeEventListener(WebInspector.ScriptFile.Events.DidMergeToVM, this._didMergeToVM, this); + oldScriptFile.removeEventListener(WebInspector.ScriptFile.Events.DidDivergeFromVM, this._didDivergeFromVM, this); + if (this._muted && !this._uiSourceCode.isDirty()) + this._restoreBreakpointsIfConsistentScripts(); + } + if (newScriptFile) + this._scriptFileForTarget.put(target, newScriptFile); + + delete this._hasCommittedLiveEdit; + this._updateDivergedInfobar(); + + if (newScriptFile) { + newScriptFile.addEventListener(WebInspector.ScriptFile.Events.DidMergeToVM, this._didMergeToVM, this); + newScriptFile.addEventListener(WebInspector.ScriptFile.Events.DidDivergeFromVM, this._didDivergeFromVM, this); + if (this.loaded) + newScriptFile.checkMapping(); + } + }, + + onTextEditorContentLoaded: function() + { + if (typeof this._executionLineNumber === "number") + this.setExecutionLine(this._executionLineNumber); + + var breakpointLocations = this._breakpointManager.breakpointLocationsForUISourceCode(this._uiSourceCode); + for (var i = 0; i < breakpointLocations.length; ++i) + this._breakpointAdded({data:breakpointLocations[i]}); + + var messages = this._uiSourceCode.consoleMessages(); + for (var i = 0; i < messages.length; ++i) { + var message = messages[i]; + this.addMessageToSource(message.lineNumber, message.originalMessage); + } + + var scriptFiles = this._scriptFileForTarget.values(); + for (var i = 0; i < scriptFiles.length; ++i) + scriptFiles[i].checkMapping(); + }, + + /** + * @param {!WebInspector.Event} event + */ + _handleGutterClick: function(event) + { + if (this._muted) + return; + + var eventData = /** @type {!WebInspector.TextEditor.GutterClickEventData} */ (event.data); + var lineNumber = eventData.lineNumber; + var eventObject = /** @type {!Event} */ (eventData.event); + + if (eventObject.button != 0 || eventObject.altKey || eventObject.ctrlKey || eventObject.metaKey) + return; + + this._toggleBreakpoint(lineNumber, eventObject.shiftKey); + eventObject.consume(true); + }, + + /** + * @param {number} lineNumber + * @param {boolean} onlyDisable + */ + _toggleBreakpoint: function(lineNumber, onlyDisable) + { + var breakpoint = this._breakpointManager.findBreakpointOnLine(this._uiSourceCode, lineNumber); + if (breakpoint) { + if (onlyDisable) + breakpoint.setEnabled(!breakpoint.enabled()); + else + breakpoint.remove(); + } else + this._setBreakpoint(lineNumber, 0, "", true); + }, + + toggleBreakpointOnCurrentLine: function() + { + if (this._muted) + return; + + var selection = this.textEditor.selection(); + if (!selection) + return; + this._toggleBreakpoint(selection.startLine, false); + }, + + /** + * @param {number} lineNumber + * @param {number} columnNumber + * @param {string} condition + * @param {boolean} enabled + */ + _setBreakpoint: function(lineNumber, columnNumber, condition, enabled) + { + this._breakpointManager.setBreakpoint(this._uiSourceCode, lineNumber, columnNumber, condition, enabled); + + WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, { + action: WebInspector.UserMetrics.UserActionNames.SetBreakpoint, + url: this._uiSourceCode.originURL(), + line: lineNumber, + enabled: enabled + }); + }, + + /** + * @param {number} lineNumber + */ + _continueToLine: function(lineNumber) + { + var executionContext = WebInspector.context.flavor(WebInspector.ExecutionContext); + if (!executionContext) + return; + var rawLocation = /** @type {!WebInspector.DebuggerModel.Location} */ (this._uiSourceCode.uiLocationToRawLocation(executionContext.target(), lineNumber, 0)); + this._scriptsPanel.continueToLocation(rawLocation); + }, + + dispose: function() + { + this._breakpointManager.removeEventListener(WebInspector.BreakpointManager.Events.BreakpointAdded, this._breakpointAdded, this); + this._breakpointManager.removeEventListener(WebInspector.BreakpointManager.Events.BreakpointRemoved, this._breakpointRemoved, this); + this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.ConsoleMessageAdded, this._consoleMessageAdded, this); + this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.ConsoleMessageRemoved, this._consoleMessageRemoved, this); + this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.ConsoleMessagesCleared, this._consoleMessagesCleared, this); + this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.SourceMappingChanged, this._onSourceMappingChanged, this); + this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._workingCopyChanged, this); + this._uiSourceCode.removeEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._workingCopyCommitted, this); + WebInspector.UISourceCodeFrame.prototype.dispose.call(this); + }, + + __proto__: WebInspector.UISourceCodeFrame.prototype +} |