blob: eabd0f7a499139dec76dcc35975d9e4c71b8c8ec [file] [log] [blame]
Blink Reformat4c46d092018-04-07 15:32:371/*
2 * Copyright (C) 2011 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31/**
32 * @implements {UI.ContextFlavorListener}
Jack Lynch8a344762019-12-12 03:17:3833 * @implements {UI.ListDelegate<!SDK.DOMDebuggerModel.DOMBreakpoint>}
Blink Reformat4c46d092018-04-07 15:32:3734 */
Paul Lewis7f7a9202019-11-26 16:10:5635export class DOMBreakpointsSidebarPane extends UI.VBox {
Blink Reformat4c46d092018-04-07 15:32:3736 constructor() {
37 super(true);
38 this.registerRequiredCSS('browser_debugger/domBreakpointsSidebarPane.css');
39
Blink Reformat4c46d092018-04-07 15:32:3740 this._emptyElement = this.contentElement.createChild('div', 'gray-info-message');
41 this._emptyElement.textContent = Common.UIString('No breakpoints');
Jack Lynch8a344762019-12-12 03:17:3842 /** @type {!UI.ListModel.<!SDK.DOMDebuggerModel.DOMBreakpoint>} */
43 this._breakpoints = new UI.ListModel();
44 /** @type {!UI.ListControl.<!SDK.DOMDebuggerModel.DOMBreakpoint>} */
45 this._list = new UI.ListControl(this._breakpoints, this, UI.ListMode.NonViewport);
46 this.contentElement.appendChild(this._list.element);
47 this._list.element.classList.add('breakpoint-list', 'hidden');
48 UI.ARIAUtils.markAsList(this._list.element);
49 UI.ARIAUtils.setAccessibleName(this._list.element, ls`DOM Breakpoints list`);
50 this._emptyElement.tabIndex = -1;
Blink Reformat4c46d092018-04-07 15:32:3751
Blink Reformat4c46d092018-04-07 15:32:3752 SDK.targetManager.addModelListener(
53 SDK.DOMDebuggerModel, SDK.DOMDebuggerModel.Events.DOMBreakpointAdded, this._breakpointAdded, this);
54 SDK.targetManager.addModelListener(
55 SDK.DOMDebuggerModel, SDK.DOMDebuggerModel.Events.DOMBreakpointToggled, this._breakpointToggled, this);
56 SDK.targetManager.addModelListener(
57 SDK.DOMDebuggerModel, SDK.DOMDebuggerModel.Events.DOMBreakpointsRemoved, this._breakpointsRemoved, this);
58
59 for (const domDebuggerModel of SDK.targetManager.models(SDK.DOMDebuggerModel)) {
60 domDebuggerModel.retrieveDOMBreakpoints();
Tim van der Lippe1d6e57a2019-09-30 11:55:3461 for (const breakpoint of domDebuggerModel.domBreakpoints()) {
Blink Reformat4c46d092018-04-07 15:32:3762 this._addBreakpoint(breakpoint);
Tim van der Lippe1d6e57a2019-09-30 11:55:3463 }
Blink Reformat4c46d092018-04-07 15:32:3764 }
65
Jack Lynch8a344762019-12-12 03:17:3866 this._highlightedBreakpoint = null;
Blink Reformat4c46d092018-04-07 15:32:3767 this._update();
68 }
69
70 /**
Jack Lynch8a344762019-12-12 03:17:3871 * @override
72 * @param {!SDK.DOMDebuggerModel.DOMBreakpoint} item
73 * @return {!Element}
74 */
75 createElementForItem(item) {
76 const element = createElementWithClass('div', 'breakpoint-entry');
77 element.addEventListener('contextmenu', this._contextMenu.bind(this, item), true);
78 UI.ARIAUtils.markAsListitem(element);
79 element.tabIndex = this._list.selectedItem() === item ? 0 : -1;
80
81 const checkboxLabel = UI.CheckboxLabel.create(/* title */ '', item.enabled);
82 const checkboxElement = checkboxLabel.checkboxElement;
83 checkboxElement.addEventListener('click', this._checkboxClicked.bind(this, item), false);
84 checkboxElement.tabIndex = -1;
85 UI.ARIAUtils.markAsHidden(checkboxLabel);
86 element.appendChild(checkboxLabel);
87
88 const labelElement = createElementWithClass('div', 'dom-breakpoint');
89 element.appendChild(labelElement);
90 element.addEventListener('keydown', event => {
91 if (event.key === ' ') {
92 checkboxElement.click();
93 event.consume(true);
94 }
95 });
96
97 const description = createElement('div');
Paul Lewis2a4f9fe2020-01-09 15:19:4198 const breakpointTypeLabel = BreakpointTypeLabels.get(item.type);
Jack Lynch8a344762019-12-12 03:17:3899 description.textContent = breakpointTypeLabel;
100 const linkifiedNode = createElementWithClass('monospace');
101 linkifiedNode.style.display = 'block';
102 labelElement.appendChild(linkifiedNode);
103 Common.Linkifier.linkify(item.node, {preventKeyboardFocus: true}).then(linkified => {
104 linkifiedNode.appendChild(linkified);
105 UI.ARIAUtils.setAccessibleName(checkboxElement, ls`${breakpointTypeLabel}: ${linkified.deepTextContent()}`);
106 });
107
108 labelElement.appendChild(description);
109
110 const checkedStateText = item.enabled ? ls`checked` : ls`unchecked`;
111 if (item === this._highlightedBreakpoint) {
112 element.classList.add('breakpoint-hit');
113 UI.ARIAUtils.setDescription(element, ls`${checkedStateText} breakpoint hit`);
114 } else {
115 UI.ARIAUtils.setDescription(element, checkedStateText);
116 }
117
118
119 this._emptyElement.classList.add('hidden');
120 this._list.element.classList.remove('hidden');
121
122 return element;
123 }
124
125 /**
126 * @override
127 * @param {!SDK.DOMDebuggerModel.DOMBreakpoint} item
128 * @return {number}
129 */
130 heightForItem(item) {
131 return 0;
132 }
133
134 /**
135 * @override
136 * @param {!SDK.DOMDebuggerModel.DOMBreakpoint} item
137 * @return {boolean}
138 */
139 isItemSelectable(item) {
140 return true;
141 }
142
143 /**
144 * @override
145 * @param {?Element} fromElement
146 * @param {?Element} toElement
147 * @return {boolean}
148 */
149 updateSelectedItemARIA(fromElement, toElement) {
150 return true;
151 }
152
153 /**
154 * @override
155 * @param {?SDK.DOMDebuggerModel.DOMBreakpoint} from
156 * @param {?SDK.DOMDebuggerModel.DOMBreakpoint} to
157 * @param {?Element} fromElement
158 * @param {?Element} toElement
159 */
160 selectedItemChanged(from, to, fromElement, toElement) {
161 if (fromElement) {
162 fromElement.tabIndex = -1;
163 }
164
165 if (toElement) {
166 this.setDefaultFocusedElement(toElement);
167 toElement.tabIndex = 0;
168 if (this.hasFocus()) {
169 toElement.focus();
170 }
171 }
172 }
173
174 /**
Blink Reformat4c46d092018-04-07 15:32:37175 * @param {!Common.Event} event
176 */
177 _breakpointAdded(event) {
178 this._addBreakpoint(/** @type {!SDK.DOMDebuggerModel.DOMBreakpoint} */ (event.data));
179 }
180
181 /**
182 * @param {!Common.Event} event
183 */
184 _breakpointToggled(event) {
Jack Lynch8a344762019-12-12 03:17:38185 const hadFocus = this.hasFocus();
Blink Reformat4c46d092018-04-07 15:32:37186 const breakpoint = /** @type {!SDK.DOMDebuggerModel.DOMBreakpoint} */ (event.data);
Jack Lynch8a344762019-12-12 03:17:38187 this._list.refreshItem(breakpoint);
188 if (hadFocus) {
189 this.focus();
Tim van der Lippe1d6e57a2019-09-30 11:55:34190 }
Blink Reformat4c46d092018-04-07 15:32:37191 }
192
193 /**
194 * @param {!Common.Event} event
195 */
196 _breakpointsRemoved(event) {
Jack Lynch8a344762019-12-12 03:17:38197 const hadFocus = this.hasFocus();
Blink Reformat4c46d092018-04-07 15:32:37198 const breakpoints = /** @type {!Array<!SDK.DOMDebuggerModel.DOMBreakpoint>} */ (event.data);
Jack Lynch8a344762019-12-12 03:17:38199 let lastIndex = -1;
Blink Reformat4c46d092018-04-07 15:32:37200 for (const breakpoint of breakpoints) {
Jack Lynch8a344762019-12-12 03:17:38201 const index = this._breakpoints.indexOf(breakpoint);
202 if (index >= 0) {
203 this._breakpoints.remove(index);
204 lastIndex = index;
Blink Reformat4c46d092018-04-07 15:32:37205 }
206 }
Jack Lynch8a344762019-12-12 03:17:38207 if (this._breakpoints.length === 0) {
Blink Reformat4c46d092018-04-07 15:32:37208 this._emptyElement.classList.remove('hidden');
Jack Lynch8a344762019-12-12 03:17:38209 this.setDefaultFocusedElement(this._emptyElement);
210 this._list.element.classList.add('hidden');
211 } else if (lastIndex >= 0) {
212 const breakpointToSelect = this._breakpoints.at(lastIndex);
213 if (breakpointToSelect) {
214 this._list.selectItem(breakpointToSelect);
215 }
216 }
217 if (hadFocus) {
218 this.focus();
Blink Reformat4c46d092018-04-07 15:32:37219 }
220 }
221
222 /**
223 * @param {!SDK.DOMDebuggerModel.DOMBreakpoint} breakpoint
224 */
225 _addBreakpoint(breakpoint) {
Jack Lynch8a344762019-12-12 03:17:38226 this._breakpoints.insertWithComparator(breakpoint, (breakpointA, breakpointB) => {
227 if (breakpointA.type > breakpointB.type) {
228 return -1;
Tim van der Lippe1d6e57a2019-09-30 11:55:34229 }
Jack Lynch8a344762019-12-12 03:17:38230 if (breakpointA.type < breakpointB.type) {
231 return 1;
232 }
233 return 0;
234 });
235 if (!this.hasFocus()) {
236 this._list.selectItem(this._breakpoints.at(0));
Blink Reformat4c46d092018-04-07 15:32:37237 }
Blink Reformat4c46d092018-04-07 15:32:37238 }
239
240 /**
241 * @param {!SDK.DOMDebuggerModel.DOMBreakpoint} breakpoint
242 * @param {!Event} event
243 */
244 _contextMenu(breakpoint, event) {
245 const contextMenu = new UI.ContextMenu(event);
Jack Lynch8a344762019-12-12 03:17:38246 contextMenu.defaultSection().appendItem(
247 ls`Reveal DOM node in Elements panel`, Common.Revealer.reveal.bind(null, breakpoint.node));
Blink Reformat4c46d092018-04-07 15:32:37248 contextMenu.defaultSection().appendItem(Common.UIString('Remove breakpoint'), () => {
249 breakpoint.domDebuggerModel.removeDOMBreakpoint(breakpoint.node, breakpoint.type);
250 });
251 contextMenu.defaultSection().appendItem(Common.UIString('Remove all DOM breakpoints'), () => {
252 breakpoint.domDebuggerModel.removeAllDOMBreakpoints();
253 });
254 contextMenu.show();
255 }
256
257 /**
258 * @param {!SDK.DOMDebuggerModel.DOMBreakpoint} breakpoint
Jack Lynch8a344762019-12-12 03:17:38259 * @param {!Event} event
Blink Reformat4c46d092018-04-07 15:32:37260 */
Jack Lynch8a344762019-12-12 03:17:38261 _checkboxClicked(breakpoint, event) {
262 breakpoint.domDebuggerModel.toggleDOMBreakpoint(breakpoint, event.target.checked);
Blink Reformat4c46d092018-04-07 15:32:37263 }
264
265 /**
266 * @override
267 * @param {?Object} object
268 */
269 flavorChanged(object) {
270 this._update();
271 }
272
273 _update() {
274 const details = UI.context.flavor(SDK.DebuggerPausedDetails);
Jack Lynch8a344762019-12-12 03:17:38275 if (this._highlightedBreakpoint) {
276 const oldHighlightedBreakpoint = this._highlightedBreakpoint;
277 delete this._highlightedBreakpoint;
278 this._list.refreshItem(oldHighlightedBreakpoint);
279 }
Blink Reformat4c46d092018-04-07 15:32:37280 if (!details || !details.auxData || details.reason !== SDK.DebuggerModel.BreakReason.DOM) {
Blink Reformat4c46d092018-04-07 15:32:37281 return;
282 }
Jack Lynch8a344762019-12-12 03:17:38283
Blink Reformat4c46d092018-04-07 15:32:37284 const domDebuggerModel = details.debuggerModel.target().model(SDK.DOMDebuggerModel);
Tim van der Lippe1d6e57a2019-09-30 11:55:34285 if (!domDebuggerModel) {
Blink Reformat4c46d092018-04-07 15:32:37286 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34287 }
Blink Reformat4c46d092018-04-07 15:32:37288 const data = domDebuggerModel.resolveDOMBreakpointData(/** @type {!Object} */ (details.auxData));
Tim van der Lippe1d6e57a2019-09-30 11:55:34289 if (!data) {
Blink Reformat4c46d092018-04-07 15:32:37290 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34291 }
Blink Reformat4c46d092018-04-07 15:32:37292
Jack Lynch8a344762019-12-12 03:17:38293 for (const breakpoint of this._breakpoints) {
294 if (breakpoint.node === data.node && breakpoint.type === data.type) {
295 this._highlightedBreakpoint = breakpoint;
Tim van der Lippe1d6e57a2019-09-30 11:55:34296 }
Blink Reformat4c46d092018-04-07 15:32:37297 }
Jack Lynch8a344762019-12-12 03:17:38298 if (this._highlightedBreakpoint) {
299 this._list.refreshItem(this._highlightedBreakpoint);
Tim van der Lippe1d6e57a2019-09-30 11:55:34300 }
Blink Reformat4c46d092018-04-07 15:32:37301 UI.viewManager.showView('sources.domBreakpoints');
Blink Reformat4c46d092018-04-07 15:32:37302 }
Paul Lewis7f7a9202019-11-26 16:10:56303}
Blink Reformat4c46d092018-04-07 15:32:37304
Paul Lewis7f7a9202019-11-26 16:10:56305export const BreakpointTypeLabels = new Map([
Blink Reformat4c46d092018-04-07 15:32:37306 [SDK.DOMDebuggerModel.DOMBreakpoint.Type.SubtreeModified, Common.UIString('Subtree modified')],
307 [SDK.DOMDebuggerModel.DOMBreakpoint.Type.AttributeModified, Common.UIString('Attribute modified')],
308 [SDK.DOMDebuggerModel.DOMBreakpoint.Type.NodeRemoved, Common.UIString('Node removed')],
309]);
310
311/**
312 * @implements {UI.ContextMenu.Provider}
313 */
Paul Lewis7f7a9202019-11-26 16:10:56314export class ContextMenuProvider {
Blink Reformat4c46d092018-04-07 15:32:37315 /**
316 * @override
317 * @param {!Event} event
318 * @param {!UI.ContextMenu} contextMenu
319 * @param {!Object} object
320 */
321 appendApplicableItems(event, contextMenu, object) {
322 const node = /** @type {!SDK.DOMNode} */ (object);
Tim van der Lippe1d6e57a2019-09-30 11:55:34323 if (node.pseudoType()) {
Blink Reformat4c46d092018-04-07 15:32:37324 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34325 }
Blink Reformat4c46d092018-04-07 15:32:37326 const domDebuggerModel = node.domModel().target().model(SDK.DOMDebuggerModel);
Tim van der Lippe1d6e57a2019-09-30 11:55:34327 if (!domDebuggerModel) {
Blink Reformat4c46d092018-04-07 15:32:37328 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34329 }
Blink Reformat4c46d092018-04-07 15:32:37330
331 /**
332 * @param {!SDK.DOMDebuggerModel.DOMBreakpoint.Type} type
333 */
334 function toggleBreakpoint(type) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34335 if (domDebuggerModel.hasDOMBreakpoint(node, type)) {
Blink Reformat4c46d092018-04-07 15:32:37336 domDebuggerModel.removeDOMBreakpoint(node, type);
Tim van der Lippe1d6e57a2019-09-30 11:55:34337 } else {
Blink Reformat4c46d092018-04-07 15:32:37338 domDebuggerModel.setDOMBreakpoint(node, type);
Tim van der Lippe1d6e57a2019-09-30 11:55:34339 }
Blink Reformat4c46d092018-04-07 15:32:37340 }
341
342 const breakpointsMenu = contextMenu.debugSection().appendSubMenuItem(Common.UIString('Break on'));
343 for (const key in SDK.DOMDebuggerModel.DOMBreakpoint.Type) {
344 const type = SDK.DOMDebuggerModel.DOMBreakpoint.Type[key];
345 const label = Sources.DebuggerPausedMessage.BreakpointTypeNouns.get(type);
346 breakpointsMenu.defaultSection().appendCheckboxItem(
347 label, toggleBreakpoint.bind(null, type), domDebuggerModel.hasDOMBreakpoint(node, type));
348 }
349 }
Paul Lewis7f7a9202019-11-26 16:10:56350}