blob: 69e066cc3d12623f641966e7e4fc7f08d8846524 [file] [log] [blame]
// Copyright (c) 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @implements {UI.ContextFlavorListener}
* @unrestricted
*/
Sources.JavaScriptBreakpointsSidebarPane = class extends UI.VBox {
constructor() {
super();
this.registerRequiredCSS('components/breakpointsList.css');
this._breakpointManager = Bindings.breakpointManager;
this._listElement = createElementWithClass('ol', 'breakpoint-list');
this.emptyElement = this.element.createChild('div', 'gray-info-message');
this.emptyElement.textContent = Common.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(
Bindings.BreakpointManager.Events.BreakpointAdded, this._breakpointAdded, this);
this._breakpointManager.addEventListener(
Bindings.BreakpointManager.Events.BreakpointRemoved, this._breakpointRemoved, this);
this.emptyElement.addEventListener('contextmenu', this._emptyElementContextMenu.bind(this), true);
this._breakpointManager.addEventListener(
Bindings.BreakpointManager.Events.BreakpointsActiveStateChanged, this._breakpointsActiveStateChanged, this);
this._breakpointsActiveStateChanged();
this._update();
}
_emptyElementContextMenu(event) {
var contextMenu = new UI.ContextMenu(event);
this._appendBreakpointActiveItem(contextMenu);
contextMenu.show();
}
/**
* @param {!UI.ContextMenu} contextMenu
*/
_appendBreakpointActiveItem(contextMenu) {
var breakpointActive = this._breakpointManager.breakpointsActive();
var breakpointActiveTitle = breakpointActive ? Common.UIString.capitalize('Deactivate ^breakpoints') :
Common.UIString.capitalize('Activate ^breakpoints');
contextMenu.appendItem(
breakpointActiveTitle,
this._breakpointManager.setBreakpointsActive.bind(this._breakpointManager, !breakpointActive));
}
/**
* @param {!Common.Event} event
*/
_breakpointAdded(event) {
this._breakpointRemoved(event);
var breakpoint = /** @type {!Bindings.BreakpointManager.Breakpoint} */ (event.data.breakpoint);
var uiLocation = /** @type {!Workspace.UILocation} */ (event.data.uiLocation);
this._addBreakpoint(breakpoint, uiLocation);
}
/**
* @param {!Bindings.BreakpointManager.Breakpoint} breakpoint
* @param {!Workspace.UILocation} uiLocation
*/
_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());
element.appendChild(checkboxLabel);
checkboxLabel.addEventListener('click', this._breakpointCheckboxClicked.bind(this, breakpoint), false);
var snippetElement = element.createChild('div', 'source-text monospace');
/**
* @param {?string} content
* @this {Sources.JavaScriptBreakpointsSidebarPane}
*/
function didRequestContent(content) {
var lineNumber = uiLocation.lineNumber;
var columnNumber = uiLocation.columnNumber;
var text = new Common.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);
}
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._addListElement(element, currentElement);
var breakpointItem = {element: element, checkbox: checkboxLabel.checkboxElement};
this._items.set(breakpoint, breakpointItem);
}
/**
* @param {!Workspace.UISourceCode} uiSourceCode
* @param {number} lineNumber
* @param {number} columnNumber
*/
didReceiveBreakpointLineForTest(uiSourceCode, lineNumber, columnNumber) {
}
/**
* @param {!Common.Event} event
*/
_breakpointRemoved(event) {
var breakpoint = /** @type {!Bindings.BreakpointManager.Breakpoint} */ (event.data.breakpoint);
var breakpointItem = this._items.get(breakpoint);
if (!breakpointItem)
return;
this._items.remove(breakpoint);
this._removeListElement(breakpointItem.element);
}
/**
* @override
* @param {?Object} object
*/
flavorChanged(object) {
this._update();
}
_update() {
var details = UI.context.flavor(SDK.DebuggerPausedDetails);
var uiLocation = details && details.callFrames.length ?
Bindings.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;
UI.viewManager.showView('sources.jsBreakpoints');
}
_breakpointsActiveStateChanged() {
this._listElement.classList.toggle('breakpoints-list-deactivated', !this._breakpointManager.breakpointsActive());
}
/**
* @param {!Workspace.UILocation} uiLocation
*/
_breakpointClicked(uiLocation) {
Common.Revealer.reveal(uiLocation);
}
/**
* @param {!Bindings.BreakpointManager.Breakpoint} breakpoint
* @param {!Event} event
*/
_breakpointCheckboxClicked(breakpoint, event) {
// Breakpoint element has it's own click handler.
event.consume();
breakpoint.setEnabled(event.target.checkboxElement.checked);
}
/**
* @param {!Bindings.BreakpointManager.Breakpoint} breakpoint
* @param {!Event} event
*/
_breakpointContextMenu(breakpoint, event) {
var breakpoints = this._items.valuesArray();
var contextMenu = new UI.ContextMenu(event);
contextMenu.appendItem(Common.UIString.capitalize('Remove ^breakpoint'), breakpoint.remove.bind(breakpoint));
if (breakpoints.length > 1) {
var removeAllTitle = Common.UIString.capitalize('Remove ^all ^breakpoints');
contextMenu.appendItem(
removeAllTitle, this._breakpointManager.removeAllBreakpoints.bind(this._breakpointManager));
}
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 = Common.UIString.capitalize('Enable ^all ^breakpoints');
var disableTitle = Common.UIString.capitalize('Disable ^all ^breakpoints');
contextMenu.appendSeparator();
contextMenu.appendItem(
enableTitle, this._breakpointManager.toggleAllBreakpoints.bind(this._breakpointManager, true),
!(enableBreakpointCount !== breakpoints.length));
contextMenu.appendItem(
disableTitle, this._breakpointManager.toggleAllBreakpoints.bind(this._breakpointManager, false),
!(enableBreakpointCount > 1));
}
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;
}
_compareBreakpoints(b1, b2) {
return this._compare(b1.uiSourceCode.url(), b2.uiSourceCode.url()) || this._compare(b1.lineNumber, b2.lineNumber);
}
reset() {
this._listElement.removeChildren();
if (this._listElement.parentElement) {
this.element.removeChild(this._listElement);
this.element.appendChild(this.emptyElement);
}
this._items.clear();
}
};