blob: 8cd1c84008e22d60e8ecb8f093e3a2a2012c9d31 [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}
* @implements {UI.ToolbarItem.ItemsProvider}
* @unrestricted
*/
export class XHRBreakpointsSidebarPane extends UI.VBox {
constructor() {
super(true);
this.registerRequiredCSS('browser_debugger/xhrBreakpointsSidebarPane.css');
this._listElement = this.contentElement.createChild('div', 'breakpoint-list hidden');
this._emptyElement = this.contentElement.createChild('div', 'gray-info-message');
this._emptyElement.textContent = Common.UIString('No breakpoints');
/** @type {!Map.<string, !Element>} */
this._breakpointElements = new Map();
this._addButton = new UI.ToolbarButton(Common.UIString('Add breakpoint'), 'largeicon-add');
this._addButton.addEventListener(UI.ToolbarButton.Events.Click, this._addButtonClicked.bind(this));
this._emptyElement.addEventListener('contextmenu', this._emptyElementContextMenu.bind(this), true);
this._restoreBreakpoints();
this._update();
}
/**
* @override
* @return {!Array<!UI.ToolbarItem>}
*/
toolbarItems() {
return [this._addButton];
}
_emptyElementContextMenu(event) {
const contextMenu = new UI.ContextMenu(event);
contextMenu.defaultSection().appendItem(Common.UIString('Add breakpoint'), this._addButtonClicked.bind(this));
contextMenu.show();
}
async _addButtonClicked() {
await UI.viewManager.showView('sources.xhrBreakpoints');
const inputElementContainer = createElementWithClass('p', 'breakpoint-condition');
inputElementContainer.textContent = Common.UIString('Break when URL contains:');
const inputElement = inputElementContainer.createChild('span', 'breakpoint-condition-input');
this._addListElement(inputElementContainer, /** @type {?Element} */ (this._listElement.firstChild));
/**
* @param {boolean} accept
* @param {!Element} e
* @param {string} text
* @this {BrowserDebugger.XHRBreakpointsSidebarPane}
*/
function finishEditing(accept, e, text) {
this._removeListElement(inputElementContainer);
if (accept) {
SDK.domDebuggerManager.addXHRBreakpoint(text, true);
this._setBreakpoint(text, true);
}
}
const config = new UI.InplaceEditor.Config(finishEditing.bind(this, true), finishEditing.bind(this, false));
UI.InplaceEditor.startEditing(inputElement, config);
}
/**
* @param {string} url
* @param {boolean} enabled
*/
_setBreakpoint(url, enabled) {
if (this._breakpointElements.has(url)) {
this._breakpointElements.get(url)._checkboxElement.checked = enabled;
return;
}
const element = createElementWithClass('div', 'breakpoint-entry');
element._url = url;
element.addEventListener('contextmenu', this._contextMenu.bind(this, url), true);
const title = url ? Common.UIString('URL contains "%s"', url) : Common.UIString('Any XHR or fetch');
const label = UI.CheckboxLabel.create(title, enabled);
element.appendChild(label);
label.checkboxElement.addEventListener('click', this._checkboxClicked.bind(this, url), false);
element._checkboxElement = label.checkboxElement;
label.classList.add('cursor-auto');
label.textElement.addEventListener('dblclick', this._labelClicked.bind(this, url), false);
let currentElement = /** @type {?Element} */ (this._listElement.firstChild);
while (currentElement) {
if (currentElement._url && currentElement._url < element._url) {
break;
}
currentElement = /** @type {?Element} */ (currentElement.nextSibling);
}
this._addListElement(element, currentElement);
this._breakpointElements.set(url, element);
}
/**
* @param {string} url
*/
_removeBreakpoint(url) {
const element = this._breakpointElements.get(url);
if (!element) {
return;
}
this._removeListElement(element);
this._breakpointElements.delete(url);
}
/**
* @param {!Element} element
* @param {?Node} beforeNode
*/
_addListElement(element, beforeNode) {
this._listElement.insertBefore(element, beforeNode);
this._emptyElement.classList.add('hidden');
this._listElement.classList.remove('hidden');
}
/**
* @param {!Element} element
*/
_removeListElement(element) {
this._listElement.removeChild(element);
if (!this._listElement.firstChild) {
this._emptyElement.classList.remove('hidden');
this._listElement.classList.add('hidden');
}
}
_contextMenu(url, event) {
const contextMenu = new UI.ContextMenu(event);
/**
* @this {BrowserDebugger.XHRBreakpointsSidebarPane}
*/
function removeBreakpoint() {
SDK.domDebuggerManager.removeXHRBreakpoint(url);
this._removeBreakpoint(url);
}
/**
* @this {BrowserDebugger.XHRBreakpointsSidebarPane}
*/
function removeAllBreakpoints() {
for (const url of this._breakpointElements.keys()) {
SDK.domDebuggerManager.removeXHRBreakpoint(url);
this._removeBreakpoint(url);
}
}
const removeAllTitle = Common.UIString('Remove all breakpoints');
contextMenu.defaultSection().appendItem(Common.UIString('Add breakpoint'), this._addButtonClicked.bind(this));
contextMenu.defaultSection().appendItem(Common.UIString('Remove breakpoint'), removeBreakpoint.bind(this));
contextMenu.defaultSection().appendItem(removeAllTitle, removeAllBreakpoints.bind(this));
contextMenu.show();
}
_checkboxClicked(url, event) {
SDK.domDebuggerManager.toggleXHRBreakpoint(url, event.target.checked);
}
_labelClicked(url) {
const element = this._breakpointElements.get(url) || null;
const inputElement = createElementWithClass('span', 'breakpoint-condition');
inputElement.textContent = url;
this._listElement.insertBefore(inputElement, element);
element.classList.add('hidden');
/**
* @param {boolean} accept
* @param {!Element} e
* @param {string} text
* @this {BrowserDebugger.XHRBreakpointsSidebarPane}
*/
function finishEditing(accept, e, text) {
this._removeListElement(inputElement);
if (accept) {
SDK.domDebuggerManager.removeXHRBreakpoint(url);
this._removeBreakpoint(url);
const enabled = element ? element._checkboxElement.checked : true;
SDK.domDebuggerManager.addXHRBreakpoint(text, enabled);
this._setBreakpoint(text, enabled);
} else {
element.classList.remove('hidden');
}
}
UI.InplaceEditor.startEditing(
inputElement, new UI.InplaceEditor.Config(finishEditing.bind(this, true), finishEditing.bind(this, false)));
}
/**
* @override
* @param {?Object} object
*/
flavorChanged(object) {
this._update();
}
_update() {
const details = UI.context.flavor(SDK.DebuggerPausedDetails);
if (!details || details.reason !== SDK.DebuggerModel.BreakReason.XHR) {
if (this._highlightedElement) {
this._highlightedElement.classList.remove('breakpoint-hit');
delete this._highlightedElement;
}
return;
}
const url = details.auxData['breakpointURL'];
const element = this._breakpointElements.get(url);
if (!element) {
return;
}
UI.viewManager.showView('sources.xhrBreakpoints');
element.classList.add('breakpoint-hit');
this._highlightedElement = element;
}
_restoreBreakpoints() {
const breakpoints = SDK.domDebuggerManager.xhrBreakpoints();
for (const url of breakpoints.keys()) {
this._setBreakpoint(url, breakpoints.get(url));
}
}
}
/* Legacy exported object */
self.BrowserDebugger = self.BrowserDebugger || {};
/* Legacy exported object */
BrowserDebugger = BrowserDebugger || {};
/**
* @constructor
*/
BrowserDebugger.XHRBreakpointsSidebarPane = XHRBreakpointsSidebarPane;