Chromium Code Reviews| Index: third_party/WebKit/Source/devtools/front_end/sources/JavaScriptSourceFrame.js |
| diff --git a/third_party/WebKit/Source/devtools/front_end/sources/JavaScriptSourceFrame.js b/third_party/WebKit/Source/devtools/front_end/sources/JavaScriptSourceFrame.js |
| index 13de635127018497c6d13123c4da6abc652c2562..61fe57f55542c18e2bc3712a0f5997c77bbba1d6 100644 |
| --- a/third_party/WebKit/Source/devtools/front_end/sources/JavaScriptSourceFrame.js |
| +++ b/third_party/WebKit/Source/devtools/front_end/sources/JavaScriptSourceFrame.js |
| @@ -67,6 +67,9 @@ Sources.JavaScriptSourceFrame = class extends Sources.UISourceCodeFrame { |
| this.uiSourceCode().addEventListener( |
| Workspace.UISourceCode.Events.TitleChanged, this._showBlackboxInfobarIfNeeded, this); |
| + /** @type {!Set<!Sources.JavaScriptSourceFrame.BreakpointDecoration>} */ |
| + this._breakpointDecorations = new Set(); |
| + |
| /** @type {!Map.<!SDK.Target, !Bindings.ResourceScriptFile>}*/ |
| this._scriptFileForTarget = new Map(); |
| var targets = SDK.targetManager.targets(); |
| @@ -251,26 +254,37 @@ Sources.JavaScriptSourceFrame = class extends Sources.UISourceCodeFrame { |
| function populate(resolve, reject) { |
| var uiLocation = new Workspace.UILocation(this.uiSourceCode(), lineNumber, 0); |
| this._scriptsPanel.appendUILocationItems(contextMenu, uiLocation); |
| - var breakpoints = this._breakpointManager.findBreakpoints(this.uiSourceCode(), lineNumber); |
| + var breakpoints = this._lineBreakpointDecorations(lineNumber).map(decoration => decoration.breakpoint); |
| if (!breakpoints.length) { |
| // This row doesn't have a breakpoint: We want to show Add Breakpoint and Add and Edit Breakpoint. |
| contextMenu.appendItem( |
| Common.UIString('Add breakpoint'), this._createNewBreakpoint.bind(this, lineNumber, '', true)); |
| contextMenu.appendItem( |
| - Common.UIString('Add conditional breakpoint…'), this._editBreakpointCondition.bind(this, lineNumber)); |
| + Common.UIString('Add conditional breakpoint\u2026'), this._editBreakpointCondition.bind(this, lineNumber)); |
| contextMenu.appendItem( |
| Common.UIString('Never pause here'), this._createNewBreakpoint.bind(this, lineNumber, 'false', true)); |
| } else { |
| - var breakpoint = breakpoints[0]; |
| - |
| - // This row has a breakpoint, we want to show edit and remove breakpoint, and either disable or enable. |
| - contextMenu.appendItem(Common.UIString('Remove breakpoint'), breakpoint.remove.bind(breakpoint)); |
| - contextMenu.appendItem( |
| - Common.UIString('Edit breakpoint…'), this._editBreakpointCondition.bind(this, lineNumber, breakpoint)); |
| - if (breakpoint.enabled()) |
| - contextMenu.appendItem(Common.UIString('Disable breakpoint'), breakpoint.setEnabled.bind(breakpoint, false)); |
| - else |
| - contextMenu.appendItem(Common.UIString('Enable breakpoint'), breakpoint.setEnabled.bind(breakpoint, true)); |
| + var hasOneBreakpoint = breakpoints.length === 1; |
| + var removeTitle = |
| + hasOneBreakpoint ? Common.UIString('Remove breakpoint') : Common.UIString('Remove all breakpoints in line'); |
| + contextMenu.appendItem(removeTitle, () => breakpoints.map(breakpoint => breakpoint.remove())); |
| + if (hasOneBreakpoint) { |
| + contextMenu.appendItem( |
| + Common.UIString('Edit breakpoint\u2026'), |
| + this._editBreakpointCondition.bind(this, lineNumber, breakpoints[0])); |
| + } |
| + var hasEnabled = breakpoints.some(breakpoint => breakpoint.enabled()); |
| + if (hasEnabled) { |
| + var title = hasOneBreakpoint ? Common.UIString('Disable breakpoint') : |
| + Common.UIString('Disable all breakpoints in line'); |
| + contextMenu.appendItem(title, () => breakpoints.map(breakpoint => breakpoint.setEnabled(false))); |
| + } |
| + var hasDisabled = breakpoints.some(breakpoint => !breakpoint.enabled()); |
| + if (hasDisabled) { |
| + var title = hasOneBreakpoint ? Common.UIString('Enable breakpoint') : |
| + Common.UIString('Enabled all breakpoints in line'); |
| + contextMenu.appendItem(title, () => breakpoints.map(breakpoint => breakpoint.setEnabled(true))); |
| + } |
| } |
| resolve(); |
| } |
| @@ -355,15 +369,8 @@ Sources.JavaScriptSourceFrame = class extends Sources.UISourceCodeFrame { |
| _muteBreakpointsWhileEditing() { |
| 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); |
| - } |
| + for (var decoration of this._breakpointDecorations) |
| + this._updateBreakpointDecoration(decoration); |
| this._muted = true; |
| } |
| @@ -403,34 +410,19 @@ Sources.JavaScriptSourceFrame = class extends Sources.UISourceCodeFrame { |
| _restoreBreakpointsAfterEditing() { |
| 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. |
| + /** @type {!Array<!{lineNumber: number, columnNumber: number, condition: string, enabled: boolean}>} */ |
| + var breakpoints = Array.from(this._breakpointDecorations) |
| + .map(decoration => decoration.serialize()) |
|
lushnikov
2016/11/18 21:44:46
let's inline serialize here
or you may even use b
kozy
2016/11/19 00:46:09
Done.
|
| + .filter(breakpoint => !!breakpoint); |
| 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); |
| - } |
| + this._breakpointDecorations.clear(); |
| + for (var breakpoint of breakpoints) |
| + this._setBreakpoint(breakpoint.lineNumber, breakpoint.columnNumber, breakpoint.condition, breakpoint.enabled); |
| } |
| _removeAllBreakpoints() { |
| - var breakpoints = this._breakpointManager.breakpointsForUISourceCode(this.uiSourceCode()); |
| - for (var i = 0; i < breakpoints.length; ++i) |
| - breakpoints[i].remove(); |
| + for (var decoration of this._breakpointDecorations) |
| + decoration.breakpoint.remove(); |
| } |
| /** |
| @@ -557,27 +549,6 @@ Sources.JavaScriptSourceFrame = class extends Sources.UISourceCodeFrame { |
| delete this._popoverAnchorBox; |
| } |
| - /** |
| - * @param {number} lineNumber |
| - * @param {number} columnNumber |
| - * @param {string} condition |
| - * @param {boolean} enabled |
| - * @param {boolean} mutedWhileEditing |
| - */ |
| - _addBreakpointDecoration(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(lineNumber) { |
| - this.textEditor.removeAttribute(lineNumber, 'breakpoint'); |
| - this.textEditor.removeBreakpoint(lineNumber); |
| - } |
| - |
| _onKeyDown(event) { |
| if (event.key === 'Escape') { |
| if (this._popoverHelper.isPopoverVisible()) { |
| @@ -827,9 +798,61 @@ Sources.JavaScriptSourceFrame = class extends Sources.UISourceCodeFrame { |
| } |
| /** |
| + * @param {number} lineNumber |
| + * @return {!Array<!Sources.JavaScriptSourceFrame.BreakpointDecoration>} |
| + */ |
| + _lineBreakpointDecorations(lineNumber) { |
| + return Array.from(this._breakpointDecorations) |
| + .filter(decoration => (decoration.handle.resolve() || {}).lineNumber === lineNumber); |
| + } |
| + |
| + /** |
| + * @param {!Sources.JavaScriptSourceFrame.BreakpointDecoration} decoration |
| + */ |
| + _updateBreakpointDecoration(decoration) { |
| + var location = decoration.handle.resolve(); |
| + if (!location) |
| + return; |
| + if (!this._scheduledBreakpointDecorationUpdates) { |
| + this._scheduledBreakpointDecorationUpdates = new Set(); |
|
lushnikov
2016/11/18 21:44:46
you should put decorations instead of lines inside
kozy
2016/11/19 00:46:09
Done.
|
| + setImmediate(() => this.textEditor.operation(update.bind(this))); |
| + } |
| + this._scheduledBreakpointDecorationUpdates.add(location.lineNumber); |
| + |
| + /** |
| + * @this {Sources.JavaScriptSourceFrame} |
| + */ |
| + function update() { |
| + for (var lineNumber of this._scheduledBreakpointDecorationUpdates) { |
| + if (lineNumber >= this._textEditor.linesCount) |
| + continue; |
| + this.textEditor.toggleLineClass(lineNumber, 'cm-breakpoint', false); |
| + this.textEditor.toggleLineClass(lineNumber, 'cm-breakpoint-disabled', false); |
| + this.textEditor.toggleLineClass(lineNumber, 'cm-breakpoint-conditional', false); |
| + var decorations = this._lineBreakpointDecorations(lineNumber); |
| + if (!decorations.length) |
| + continue; |
| + decorations.sort(Sources.JavaScriptSourceFrame.BreakpointDecoration.mostSpecificFirst); |
| + this.textEditor.toggleLineClass(lineNumber, 'cm-breakpoint', true); |
| + this.textEditor.toggleLineClass(lineNumber, 'cm-breakpoint-disabled', !decorations[0].enabled || this._muted); |
| + this.textEditor.toggleLineClass(lineNumber, 'cm-breakpoint-conditional', decorations[0].conditional); |
| + } |
| + delete this._scheduledBreakpointDecorationUpdates; |
| + this._breakpointDecorationsUpdatedForTest(); |
| + } |
| + } |
| + |
| + _breakpointDecorationsUpdatedForTest() { |
| + } |
| + |
| + /** |
| + * @param {!Common.Event} event |
| * @return {boolean} |
| */ |
| - _shouldIgnoreExternalBreakpointEvents() { |
| + _shouldIgnoreExternalBreakpointEvents(event) { |
| + var uiLocation = /** @type {!Workspace.UILocation} */ (event.data.uiLocation); |
| + if (uiLocation.uiSourceCode !== this.uiSourceCode() || !this.loaded) |
| + return true; |
| if (this._supportsEnabledBreakpointsWhileEditing()) |
| return false; |
| if (this._muted) |
| @@ -842,30 +865,33 @@ Sources.JavaScriptSourceFrame = class extends Sources.UISourceCodeFrame { |
| return false; |
| } |
| + /** |
| + * @param {!Common.Event} event |
| + */ |
| _breakpointAdded(event) { |
| - var uiLocation = /** @type {!Workspace.UILocation} */ (event.data.uiLocation); |
| - if (uiLocation.uiSourceCode !== this.uiSourceCode()) |
| - return; |
| - if (this._shouldIgnoreExternalBreakpointEvents()) |
| + if (this._shouldIgnoreExternalBreakpointEvents(event)) |
| return; |
| - |
| + var uiLocation = /** @type {!Workspace.UILocation} */ (event.data.uiLocation); |
| + var handle = this._textEditor.textEditorPositionHandle(uiLocation.lineNumber, uiLocation.columnNumber); |
| var breakpoint = /** @type {!Bindings.BreakpointManager.Breakpoint} */ (event.data.breakpoint); |
| - if (this.loaded) { |
| - this._addBreakpointDecoration( |
| - uiLocation.lineNumber, uiLocation.columnNumber, breakpoint.condition(), breakpoint.enabled(), false); |
| - } |
| + var decoration = new Sources.JavaScriptSourceFrame.BreakpointDecoration(handle, breakpoint); |
| + this._breakpointDecorations.add(decoration); |
| + breakpoint[Sources.JavaScriptSourceFrame.BreakpointDecoration._decorationSymbol] = decoration; |
| + this._updateBreakpointDecoration(decoration); |
| } |
| + /** |
| + * @param {!Common.Event} event |
| + */ |
| _breakpointRemoved(event) { |
| - var uiLocation = /** @type {!Workspace.UILocation} */ (event.data.uiLocation); |
| - if (uiLocation.uiSourceCode !== this.uiSourceCode()) |
| + if (this._shouldIgnoreExternalBreakpointEvents(event)) |
| return; |
| - if (this._shouldIgnoreExternalBreakpointEvents()) |
| + var breakpoint = /** @type {!Bindings.BreakpointManager.Breakpoint} */ (event.data.breakpoint); |
| + var decoration = breakpoint[Sources.JavaScriptSourceFrame.BreakpointDecoration._decorationSymbol]; |
| + if (!decoration) |
| return; |
| - |
| - var remainingBreakpoints = this._breakpointManager.findBreakpoints(this.uiSourceCode(), uiLocation.lineNumber); |
| - if (!remainingBreakpoints.length && this.loaded) |
| - this._removeBreakpointDecoration(uiLocation.lineNumber); |
| + this._breakpointDecorations.delete(decoration); |
|
lushnikov
2016/11/18 21:44:46
cleanup symbol as well breakpoint[Sources.JavaScri
kozy
2016/11/19 00:46:09
Done.
|
| + this._updateBreakpointDecoration(decoration); |
| } |
| /** |
| @@ -938,7 +964,7 @@ Sources.JavaScriptSourceFrame = class extends Sources.UISourceCodeFrame { |
| var breakpointLocations = this._breakpointManager.breakpointLocationsForUISourceCode(this.uiSourceCode()); |
| for (var i = 0; i < breakpointLocations.length; ++i) |
| - this._breakpointAdded({data: breakpointLocations[i]}); |
| + this._breakpointAdded(/** @type {!Common.Event} */ ({data: breakpointLocations[i]})); |
| var scriptFiles = this._scriptFileForTarget.valuesArray(); |
| for (var i = 0; i < scriptFiles.length; ++i) |
| @@ -1009,14 +1035,18 @@ Sources.JavaScriptSourceFrame = class extends Sources.UISourceCodeFrame { |
| * @param {boolean} onlyDisable |
| */ |
| _toggleBreakpoint(lineNumber, onlyDisable) { |
| - var breakpoints = this._breakpointManager.findBreakpoints(this.uiSourceCode(), lineNumber); |
| - if (breakpoints.length) { |
| + var decorations = this._lineBreakpointDecorations(lineNumber); |
| + if (!decorations.length) { |
| + this._createNewBreakpoint(lineNumber, '', true); |
| + return; |
| + } |
| + var breakpoints = decorations.map(decoration => decoration.breakpoint); |
| + var hasDisabled = decorations.some(decoration => !decoration.enabled); |
|
lushnikov
2016/11/18 21:44:46
can we check for cm-breakpoint-disabled class on g
kozy
2016/11/19 00:46:09
Done.
|
| + for (var breakpoint of breakpoints) { |
| if (onlyDisable) |
| - breakpoints[0].setEnabled(!breakpoints[0].enabled()); |
| + breakpoint.setEnabled(hasDisabled); |
| else |
| - breakpoints[0].remove(); |
| - } else { |
| - this._createNewBreakpoint(lineNumber, '', true); |
| + breakpoint.remove(); |
| } |
| } |
| @@ -1127,3 +1157,49 @@ Sources.JavaScriptSourceFrame = class extends Sources.UISourceCodeFrame { |
| super.dispose(); |
| } |
| }; |
| + |
| +/** |
| + * @unrestricted |
| + */ |
| +Sources.JavaScriptSourceFrame.BreakpointDecoration = class { |
| + /** |
| + * @param {!TextEditor.TextEditorPositionHandle} handle |
| + * @param {!Bindings.BreakpointManager.Breakpoint} breakpoint |
| + */ |
| + constructor(handle, breakpoint) { |
| + this.handle = handle; |
| + this.conditional = !!breakpoint.condition(); |
| + this.enabled = breakpoint.enabled(); |
| + this.breakpoint = breakpoint; |
| + } |
| + |
| + /** |
| + * @return {?{lineNumber: number, columnNumber: number, condition: string, enabled: boolean}} |
| + */ |
| + serialize() { |
| + var location = this.handle.resolve(); |
| + if (!location) |
| + return null; |
| + return { |
| + lineNumber: location.lineNumber, |
| + columnNumber: location.columnNumber, |
| + condition: this.breakpoint.condition(), |
| + enabled: this.breakpoint.enabled() |
| + }; |
| + } |
| + |
| + /** |
| + * @param {!Sources.JavaScriptSourceFrame.BreakpointDecoration} decoration1 |
| + * @param {!Sources.JavaScriptSourceFrame.BreakpointDecoration} decoration2 |
| + * @return {number} |
| + */ |
| + static mostSpecificFirst(decoration1, decoration2) { |
| + if (decoration1.conditional !== decoration2.conditional) |
| + return decoration1.conditional ? -1 : 1; |
| + if (decoration1.enabled !== decoration2.enabled) |
| + return decoration1.enabled ? -1 : 1; |
| + return 0; |
| + } |
| +}; |
| + |
| +Sources.JavaScriptSourceFrame.BreakpointDecoration._decorationSymbol = Symbol('decoration'); |