Chromium Code Reviews| Index: third_party/WebKit/Source/devtools/front_end/sources/JavaScriptBreakpointsSidebarPane.js |
| diff --git a/third_party/WebKit/Source/devtools/front_end/sources/JavaScriptBreakpointsSidebarPane.js b/third_party/WebKit/Source/devtools/front_end/sources/JavaScriptBreakpointsSidebarPane.js |
| index 51d5121b2bb07ed924355ad79c65443b9a33bb8e..cfc2ab901f2c9973709610a43f2d4846ea5582cf 100644 |
| --- a/third_party/WebKit/Source/devtools/front_end/sources/JavaScriptBreakpointsSidebarPane.js |
| +++ b/third_party/WebKit/Source/devtools/front_end/sources/JavaScriptBreakpointsSidebarPane.js |
| @@ -7,261 +7,227 @@ |
| */ |
| WebInspector.JavaScriptBreakpointsSidebarPane = class extends WebInspector.VBox { |
|
lushnikov
2016/11/12 02:57:06
Use WebInspector.ThrottledWidget!!!
kozy
2016/11/12 03:39:05
Done.
|
| constructor() { |
| - super(); |
| + super(true); |
| this.registerRequiredCSS('components/breakpointsList.css'); |
| this._breakpointManager = WebInspector.breakpointManager; |
| - |
| - this._listElement = createElementWithClass('ol', 'breakpoint-list'); |
| - |
| - this.emptyElement = this.element.createChild('div', 'gray-info-message'); |
| - this.emptyElement.textContent = WebInspector.UIString('No Breakpoints'); |
| - |
| - this._items = new Map(); |
| - |
| - var breakpointLocations = this._breakpointManager.allBreakpointLocations(); |
| - for (var i = 0; i < breakpointLocations.length; ++i) |
| - this._addBreakpoint(breakpointLocations[i].breakpoint, breakpointLocations[i].uiLocation); |
| - |
| this._breakpointManager.addEventListener( |
| - WebInspector.BreakpointManager.Events.BreakpointAdded, this._breakpointAdded, this); |
| + WebInspector.BreakpointManager.Events.BreakpointAdded, this._scheduleUpdate, this); |
| this._breakpointManager.addEventListener( |
| - WebInspector.BreakpointManager.Events.BreakpointRemoved, this._breakpointRemoved, this); |
| - |
| - this.emptyElement.addEventListener('contextmenu', this._emptyElementContextMenu.bind(this), true); |
| + WebInspector.BreakpointManager.Events.BreakpointRemoved, this._scheduleUpdate, this); |
| this._breakpointManager.addEventListener( |
| - WebInspector.BreakpointManager.Events.BreakpointsActiveStateChanged, this._breakpointsActiveStateChanged, this); |
| - this._breakpointsActiveStateChanged(); |
| - this._update(); |
| - } |
| + WebInspector.BreakpointManager.Events.BreakpointsActiveStateChanged, this._scheduleUpdate, this); |
| - _emptyElementContextMenu(event) { |
| - var contextMenu = new WebInspector.ContextMenu(event); |
| - this._appendBreakpointActiveItem(contextMenu); |
| - contextMenu.show(); |
| + /** @type {?Element} */ |
| + this._emptyElement = null; |
| + /** @type {?Element} */ |
| + this._listElement = null; |
| + |
| + this._scheduleUpdate(); |
| } |
| - /** |
| - * @param {!WebInspector.ContextMenu} contextMenu |
| - */ |
| - _appendBreakpointActiveItem(contextMenu) { |
| - var breakpointActive = this._breakpointManager.breakpointsActive(); |
| - var breakpointActiveTitle = breakpointActive ? WebInspector.UIString.capitalize('Deactivate ^breakpoints') : |
| - WebInspector.UIString.capitalize('Activate ^breakpoints'); |
| - contextMenu.appendItem( |
| - breakpointActiveTitle, |
| - this._breakpointManager.setBreakpointsActive.bind(this._breakpointManager, !breakpointActive)); |
| + _scheduleUpdate() { |
| + if (this._updateScheduled) |
| + return; |
| + setImmediate(this._update.bind(this)); |
| + this._updateScheduled = true; |
| } |
| - /** |
| - * @param {!WebInspector.Event} event |
| - */ |
| - _breakpointAdded(event) { |
| - this._breakpointRemoved(event); |
| + _update() { |
| + this._updateScheduled = false; |
| + |
| + var breakpointLocations = this._breakpointManager.allBreakpointLocations(); |
| + if (!breakpointLocations.length) { |
| + if (!this._emptyElement) { |
|
lushnikov
2016/11/12 02:57:07
let's create it right away in the constructor! You
kozy
2016/11/12 03:39:05
Done.
|
| + var emptyElement = this.contentElement.createChild('div', 'gray-info-message'); |
| + emptyElement.textContent = WebInspector.UIString('No Breakpoints'); |
| + emptyElement.addEventListener('contextmenu', this._emptyElementContextMenu.bind(this), true); |
|
lushnikov
2016/11/12 02:57:06
do we need it??
kozy
2016/11/12 03:39:05
Removed.
|
| + this._emptyElement = emptyElement; |
| + } |
| + if (this._listElement) { |
| + this.contentElement.removeChild(this._listElement); |
| + this._listElement = null; |
| + } |
| + this.contentElement.appendChild(this._emptyElement); |
| + return; |
| + } |
| + |
| + if (!this._listElement) |
| + this._listElement = this.contentElement.createChild('div'); |
| + if (this._emptyElement) { |
| + this.contentElement.removeChild(this._emptyElement); |
| + this._emptyElement = null; |
| + } |
| + this.contentElement.appendChild(this._listElement); |
| + |
| + breakpointLocations.sort((item1, item2) => item1.uiLocation.compareTo(item2.uiLocation)); |
| + |
| + /** @type {!Map<string, !Array<!{breakpoint: !WebInspector.BreakpointManager.Breakpoint, uiLocation: !WebInspector.UILocation}>} */ |
|
lushnikov
2016/11/12 02:57:06
Multimap!
|
| + var locationForEntry = new Map(); |
| + for (var breakpointLocation of breakpointLocations) { |
| + var uiLocation = breakpointLocation.uiLocation; |
| + var entryDescriptor = uiLocation.uiSourceCode.url() + ':' + uiLocation.lineNumber; |
| + if (!locationForEntry.has(entryDescriptor)) |
| + locationForEntry.set(entryDescriptor, []); |
| + locationForEntry.get(entryDescriptor).push(breakpointLocation); |
| + } |
| + |
| + var details = WebInspector.context.flavor(WebInspector.DebuggerPausedDetails); |
| + var selectedUILocation = details && details.callFrames.length ? |
| + WebInspector.debuggerWorkspaceBinding.rawLocationToUILocation(details.callFrames[0].location()) : |
| + null; |
| - var breakpoint = /** @type {!WebInspector.BreakpointManager.Breakpoint} */ (event.data.breakpoint); |
| - var uiLocation = /** @type {!WebInspector.UILocation} */ (event.data.uiLocation); |
| - this._addBreakpoint(breakpoint, uiLocation); |
| + var shouldShowView = false; |
| + var locationsPerEntry = locationForEntry.valuesArray(); |
| + for (var i = 0; i < locationsPerEntry.length; ++i) { |
| + var locations = locationsPerEntry[i]; |
| + var entry = this._getOrCreateContainerForEntry(i); |
| + this._resetEntry(entry, locations); |
| + |
| + var isSelected = selectedUILocation && locations.some(location => location.uiLocation.id() === selectedUILocation.id()); |
|
lushnikov
2016/11/12 02:57:06
if (isSelected)
shouldShowView = true;
kozy
2016/11/12 03:39:05
Done.
|
| + entry.classList.toggle('breakpoint-hit', isSelected); |
| + } |
| + var entriesToRemove = this._listElement.children.length - locationsPerEntry.length; |
| + while (entriesToRemove--) |
| + this._listElement.removeChild(this._listElement.lastChild); |
| + if (shouldShowView) |
| + WebInspector.viewManager.showView('sources.jsBreakpoints'); |
| + this._listElement.classList.toggle('breakpoints-list-deactivated', !this._breakpointManager.breakpointsActive()); |
| + this.didUpdateForTest(); |
| } |
| /** |
| - * @param {!WebInspector.BreakpointManager.Breakpoint} breakpoint |
| - * @param {!WebInspector.UILocation} uiLocation |
| + * @param {number} index |
| + * @return {!Element} |
| */ |
| - _addBreakpoint(breakpoint, uiLocation) { |
| - var element = createElementWithClass('li', 'cursor-pointer'); |
| - element.addEventListener('contextmenu', this._breakpointContextMenu.bind(this, breakpoint), true); |
| - element.addEventListener('click', this._breakpointClicked.bind(this, uiLocation), false); |
| - |
| - var checkboxLabel = createCheckboxLabel(uiLocation.linkText(), breakpoint.enabled()); |
| + _getOrCreateContainerForEntry(index) { |
|
lushnikov
2016/11/12 02:57:06
"I don't need this!" (Alexey)
kozy
2016/11/12 03:39:05
Done.
|
| + var entries = this._listElement.children; |
| + console.assert(index <= entries.length); |
| + if (index < entries.length) |
| + return entries[index]; |
| + var entry = createElement('div'); |
| + this._listElement.appendChild(entry); |
| + return entry; |
| + } |
| + |
| + _resetEntry(element, locations) { |
|
lushnikov
2016/11/12 02:57:06
jsdoc!!
lushnikov
2016/11/12 02:57:06
element, uiLocation, hasEnabled, hasDisabled, isSe
kozy
2016/11/12 03:39:05
Done.
kozy
2016/11/12 03:39:05
Done.
|
| + element.removeChildren(); |
| + element.classList.toggle('breakpoint-entry', true); |
| + |
| + var uiLocation = locations[0].uiLocation; |
| + element.addEventListener('contextmenu', this._breakpointContextMenu.bind(this, uiLocation), true); |
| + element.addEventListener('click', () => WebInspector.Revealer.reveal(uiLocation), false); |
| + |
| + var hasEnabled = locations.some(location => location.breakpoint.enabled()); |
| + var hasDisabled = locations.some(location => !location.breakpoint.enabled()); |
| + |
| + var checkboxLabel = createCheckboxLabel(uiLocation.linkText()); |
| + checkboxLabel.addEventListener('click', this._breakpointCheckboxClicked.bind(this, uiLocation), false); |
| + checkboxLabel.checkboxElement.checked = hasEnabled; |
| + checkboxLabel.checkboxElement.indeterminate = hasEnabled && hasDisabled; |
|
lushnikov
2016/11/12 02:57:07
hasEnabled && hasDisabled -- crazy!!
kozy
2016/11/12 03:39:05
It's new inline breakpoints world.. :)
|
| element.appendChild(checkboxLabel); |
| - checkboxLabel.addEventListener('click', this._breakpointCheckboxClicked.bind(this, breakpoint), false); |
| var snippetElement = element.createChild('div', 'source-text monospace'); |
| + uiLocation.uiSourceCode.requestContent().then(fillSnippetElement.bind(this, snippetElement)); |
| /** |
| + * @param {!Element} snippetElement |
| * @param {?string} content |
| * @this {WebInspector.JavaScriptBreakpointsSidebarPane} |
| */ |
| - function didRequestContent(content) { |
| + function fillSnippetElement(snippetElement, content) { |
| var lineNumber = uiLocation.lineNumber; |
| - var columnNumber = uiLocation.columnNumber; |
| var text = new WebInspector.Text(content || ''); |
| if (lineNumber < text.lineCount()) { |
| var lineText = text.lineAt(lineNumber); |
| var maxSnippetLength = 200; |
| - var snippetStartIndex = columnNumber > 100 ? columnNumber : 0; |
| - snippetElement.textContent = lineText.substr(snippetStartIndex).trimEnd(maxSnippetLength); |
| + snippetElement.textContent = lineText.substr(0).trimEnd(maxSnippetLength); |
| } |
| - this.didReceiveBreakpointLineForTest(uiLocation.uiSourceCode, lineNumber, columnNumber); |
| - } |
| - |
| - uiLocation.uiSourceCode.requestContent().then(didRequestContent.bind(this)); |
| - |
| - element._data = uiLocation; |
| - var currentElement = this._listElement.firstChild; |
| - while (currentElement) { |
| - if (currentElement._data && this._compareBreakpoints(currentElement._data, element._data) > 0) |
| - break; |
| - currentElement = currentElement.nextSibling; |
| + this.didReceiveBreakpointLineForTest(uiLocation.uiSourceCode, lineNumber, 0); |
| } |
| - this._addListElement(element, currentElement); |
| - |
| - var breakpointItem = {element: element, checkbox: checkboxLabel.checkboxElement}; |
| - this._items.set(breakpoint, breakpointItem); |
| } |
| /** |
| - * @param {!WebInspector.UISourceCode} uiSourceCode |
| - * @param {number} lineNumber |
| - * @param {number} columnNumber |
| - */ |
| - didReceiveBreakpointLineForTest(uiSourceCode, lineNumber, columnNumber) { |
| - } |
| - |
| - /** |
| - * @param {!WebInspector.Event} event |
| + * @param {!Event} event |
| */ |
| - _breakpointRemoved(event) { |
| - var breakpoint = /** @type {!WebInspector.BreakpointManager.Breakpoint} */ (event.data.breakpoint); |
| - var breakpointItem = this._items.get(breakpoint); |
| - if (!breakpointItem) |
| - return; |
| - this._items.remove(breakpoint); |
| - this._removeListElement(breakpointItem.element); |
| + _emptyElementContextMenu(event) { |
| + var contextMenu = new WebInspector.ContextMenu(event); |
| + this._appendBreakpointActiveItem(contextMenu); |
| + contextMenu.show(); |
| } |
| /** |
| - * @override |
| - * @param {?Object} object |
| + * @param {!WebInspector.ContextMenu} contextMenu |
| */ |
| - flavorChanged(object) { |
| - this._update(); |
| - } |
| - |
| - _update() { |
| - var details = WebInspector.context.flavor(WebInspector.DebuggerPausedDetails); |
| - var uiLocation = details && details.callFrames.length ? |
| - WebInspector.debuggerWorkspaceBinding.rawLocationToUILocation(details.callFrames[0].location()) : |
| - null; |
| - var breakpoint = uiLocation ? |
| - this._breakpointManager.findBreakpoint( |
| - uiLocation.uiSourceCode, uiLocation.lineNumber, uiLocation.columnNumber) : |
| - null; |
| - var breakpointItem = this._items.get(breakpoint); |
| - if (!breakpointItem) { |
| - if (this._highlightedBreakpointItem) { |
| - this._highlightedBreakpointItem.element.classList.remove('breakpoint-hit'); |
| - delete this._highlightedBreakpointItem; |
| - } |
| - return; |
| - } |
| - |
| - breakpointItem.element.classList.add('breakpoint-hit'); |
| - this._highlightedBreakpointItem = breakpointItem; |
| - WebInspector.viewManager.showView('sources.jsBreakpoints'); |
| - } |
| - |
| - _breakpointsActiveStateChanged() { |
| - this._listElement.classList.toggle('breakpoints-list-deactivated', !this._breakpointManager.breakpointsActive()); |
| + _appendBreakpointActiveItem(contextMenu) { |
| + var breakpointActive = this._breakpointManager.breakpointsActive(); |
| + var breakpointActiveTitle = breakpointActive ? WebInspector.UIString('Deactivate breakpoints') : |
| + WebInspector.UIString('Activate breakpoints'); |
| + contextMenu.appendItem( |
| + breakpointActiveTitle, |
| + this._breakpointManager.setBreakpointsActive.bind(this._breakpointManager, !breakpointActive)); |
| } |
| /** |
| * @param {!WebInspector.UILocation} uiLocation |
| - */ |
| - _breakpointClicked(uiLocation) { |
| - WebInspector.Revealer.reveal(uiLocation); |
| - } |
| - |
| - /** |
| - * @param {!WebInspector.BreakpointManager.Breakpoint} breakpoint |
| * @param {!Event} event |
| */ |
| - _breakpointCheckboxClicked(breakpoint, event) { |
| - // Breakpoint element has it's own click handler. |
| + _breakpointCheckboxClicked(uiLocation, event) { |
| + var breakpoints = this._breakpointManager.findBreakpoints(uiLocation.uiSourceCode, uiLocation.lineNumber); |
| + var newState = event.target.checkboxElement.checked; |
| + for (var breakpoint of breakpoints) |
| + breakpoint.setEnabled(newState); |
| event.consume(); |
| - breakpoint.setEnabled(event.target.checkboxElement.checked); |
| } |
| /** |
| - * @param {!WebInspector.BreakpointManager.Breakpoint} breakpoint |
| + * @param {!WebInspector.UILocation} uiLocation |
| * @param {!Event} event |
| */ |
| - _breakpointContextMenu(breakpoint, event) { |
| - var breakpoints = this._items.valuesArray(); |
| + _breakpointContextMenu(uiLocation, event) { |
| + var breakpoints = this._breakpointManager.findBreakpoints(uiLocation.uiSourceCode, uiLocation.lineNumber); |
| + |
| var contextMenu = new WebInspector.ContextMenu(event); |
| - contextMenu.appendItem(WebInspector.UIString.capitalize('Remove ^breakpoint'), breakpoint.remove.bind(breakpoint)); |
| - if (breakpoints.length > 1) { |
| - var removeAllTitle = WebInspector.UIString.capitalize('Remove ^all ^breakpoints'); |
| - contextMenu.appendItem( |
| - removeAllTitle, this._breakpointManager.removeAllBreakpoints.bind(this._breakpointManager)); |
| - } |
| + var removeEntryTitle = breakpoints.length > 1 ? WebInspector.UIString('Remove all breakpoints in line') |
| + : WebInspector.UIString('Remove breakpoint'); |
| + contextMenu.appendItem(removeEntryTitle, () => breakpoints.map(breakpoint => breakpoint.remove())); |
| contextMenu.appendSeparator(); |
| this._appendBreakpointActiveItem(contextMenu); |
| - function enabledBreakpointCount(breakpoints) { |
| - var count = 0; |
| - for (var i = 0; i < breakpoints.length; ++i) { |
| - if (breakpoints[i].checkbox.checked) |
| - count++; |
| - } |
| - return count; |
| - } |
| - if (breakpoints.length > 1) { |
| - var enableBreakpointCount = enabledBreakpointCount(breakpoints); |
| - var enableTitle = WebInspector.UIString.capitalize('Enable ^all ^breakpoints'); |
| - var disableTitle = WebInspector.UIString.capitalize('Disable ^all ^breakpoints'); |
| - |
| - contextMenu.appendSeparator(); |
| - |
| + contextMenu.appendSeparator(); |
| + if (breakpoints.some(breakpoint => !breakpoint.enabled())) { |
| + var enableTitle = WebInspector.UIString('Enable all breakpoints'); |
| contextMenu.appendItem( |
| - enableTitle, this._breakpointManager.toggleAllBreakpoints.bind(this._breakpointManager, true), |
| - !(enableBreakpointCount !== breakpoints.length)); |
| + enableTitle, this._breakpointManager.toggleAllBreakpoints.bind(this._breakpointManager, true)); |
| + } |
| + if (breakpoints.some(breakpoint => breakpoint.enabled())) { |
| + var disableTitle = WebInspector.UIString('Disable all breakpoints'); |
| contextMenu.appendItem( |
| - disableTitle, this._breakpointManager.toggleAllBreakpoints.bind(this._breakpointManager, false), |
| - !(enableBreakpointCount > 1)); |
| + disableTitle, this._breakpointManager.toggleAllBreakpoints.bind(this._breakpointManager, false)); |
| } |
| - |
| + var removeAllTitle = WebInspector.UIString('Remove all breakpoints'); |
| + contextMenu.appendItem( |
| + removeAllTitle, this._breakpointManager.removeAllBreakpoints.bind(this._breakpointManager)); |
| contextMenu.show(); |
| } |
| - _addListElement(element, beforeElement) { |
| - if (beforeElement) |
| - this._listElement.insertBefore(element, beforeElement); |
| - else { |
| - if (!this._listElement.firstChild) { |
| - this.element.removeChild(this.emptyElement); |
| - this.element.appendChild(this._listElement); |
| - } |
| - this._listElement.appendChild(element); |
| - } |
| - } |
| - |
| - _removeListElement(element) { |
| - this._listElement.removeChild(element); |
| - if (!this._listElement.firstChild) { |
| - this.element.removeChild(this._listElement); |
| - this.element.appendChild(this.emptyElement); |
| - } |
| - } |
| - |
| - _compare(x, y) { |
| - if (x !== y) |
| - return x < y ? -1 : 1; |
| - return 0; |
| + /** |
| + * @override |
| + * @param {?Object} object |
| + */ |
| + flavorChanged(object) { |
| + this._scheduleUpdate(); |
| } |
| - _compareBreakpoints(b1, b2) { |
| - return this._compare(b1.uiSourceCode.url(), b2.uiSourceCode.url()) || this._compare(b1.lineNumber, b2.lineNumber); |
| + didUpdateForTest() { |
| } |
| - reset() { |
| - this._listElement.removeChildren(); |
| - if (this._listElement.parentElement) { |
| - this.element.removeChild(this._listElement); |
| - this.element.appendChild(this.emptyElement); |
| - } |
| - this._items.clear(); |
| + /** |
| + * @param {!WebInspector.UISourceCode} uiSourceCode |
| + * @param {number} lineNumber |
| + * @param {number} columnNumber |
| + */ |
| + didReceiveBreakpointLineForTest(uiSourceCode, lineNumber, columnNumber) { |
| } |
| }; |