Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(161)

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/sources/JavaScriptBreakpointsSidebarPane.js

Issue 2491983002: [DevTools] reworked JavaScriptBreakpointsSidebarPane.js (Closed)
Patch Set: rebased + better tests Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2015 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 /** 4 /**
5 * @implements {UI.ContextFlavorListener} 5 * @implements {UI.ContextFlavorListener}
6 * @unrestricted 6 * @unrestricted
7 */ 7 */
8 Sources.JavaScriptBreakpointsSidebarPane = class extends UI.VBox { 8 Sources.JavaScriptBreakpointsSidebarPane = class extends UI.ThrottledWidget {
9 constructor() { 9 constructor() {
10 super(); 10 super(true);
11 this.registerRequiredCSS('components/breakpointsList.css'); 11 this.registerRequiredCSS('components/breakpointsList.css');
12 12
13 this._breakpointManager = Bindings.breakpointManager; 13 this._breakpointManager = Bindings.breakpointManager;
14 14 this._breakpointManager.addEventListener(
15 this._listElement = createElementWithClass('ol', 'breakpoint-list'); 15 Bindings.BreakpointManager.Events.BreakpointAdded, this.update, this);
16 16 this._breakpointManager.addEventListener(
17 this.emptyElement = this.element.createChild('div', 'gray-info-message'); 17 Bindings.BreakpointManager.Events.BreakpointRemoved, this.update, this);
18 this.emptyElement.textContent = Common.UIString('No Breakpoints'); 18 this._breakpointManager.addEventListener(
19 19 Bindings.BreakpointManager.Events.BreakpointsActiveStateChanged, this.up date, this);
20 this._items = new Map(); 20
21 21 /** @type {?Element} */
22 this._listElement = null;
23 this.update();
24 }
25
26 /**
27 * @override
28 * @return {!Promise<?>}
29 */
30 doUpdate() {
22 var breakpointLocations = this._breakpointManager.allBreakpointLocations(); 31 var breakpointLocations = this._breakpointManager.allBreakpointLocations();
23 for (var i = 0; i < breakpointLocations.length; ++i) 32 if (!breakpointLocations.length) {
24 this._addBreakpoint(breakpointLocations[i].breakpoint, breakpointLocations [i].uiLocation); 33 this._listElement = null;
25 34 this.contentElement.removeChildren();
26 this._breakpointManager.addEventListener( 35 var emptyElement = this.contentElement.createChild('div', 'gray-info-messa ge');
27 Bindings.BreakpointManager.Events.BreakpointAdded, this._breakpointAdded , this); 36 emptyElement.textContent = Common.UIString('No Breakpoints');
28 this._breakpointManager.addEventListener( 37 this.contentElement.appendChild(emptyElement);
29 Bindings.BreakpointManager.Events.BreakpointRemoved, this._breakpointRem oved, this); 38 this.didUpdateForTest();
lushnikov 2016/11/14 18:56:15 nit: let's make all ..ForTest() private while we a
kozy 2016/11/14 19:45:26 Done.
30 39 return Promise.resolve();
31 this.emptyElement.addEventListener('contextmenu', this._emptyElementContextM enu.bind(this), true); 40 }
32 this._breakpointManager.addEventListener( 41
33 Bindings.BreakpointManager.Events.BreakpointsActiveStateChanged, this._b reakpointsActiveStateChanged, this); 42 if (!this._listElement) {
34 this._breakpointsActiveStateChanged(); 43 this.contentElement.removeChildren();
35 this._update(); 44 this._listElement = this.contentElement.createChild('div');
36 } 45 this.contentElement.appendChild(this._listElement);
37 46 }
38 _emptyElementContextMenu(event) { 47
39 var contextMenu = new UI.ContextMenu(event); 48 breakpointLocations.sort((item1, item2) => item1.uiLocation.compareTo(item2. uiLocation));
40 this._appendBreakpointActiveItem(contextMenu); 49
41 contextMenu.show(); 50 /** @type {!Multimap<string, !{breakpoint: !Bindings.BreakpointManager.Break point, uiLocation: !Workspace.UILocation}>} */
42 } 51 var locationForEntry = new Multimap();
43 52 for (var breakpointLocation of breakpointLocations) {
44 /** 53 var uiLocation = breakpointLocation.uiLocation;
45 * @param {!UI.ContextMenu} contextMenu 54 var entryDescriptor = uiLocation.uiSourceCode.url() + ':' + uiLocation.lin eNumber;
46 */ 55 locationForEntry.set(entryDescriptor, breakpointLocation);
47 _appendBreakpointActiveItem(contextMenu) { 56 }
48 var breakpointActive = this._breakpointManager.breakpointsActive(); 57
49 var breakpointActiveTitle = breakpointActive ? Common.UIString.capitalize('D eactivate ^breakpoints') : 58 var details = UI.context.flavor(SDK.DebuggerPausedDetails);
50 Common.UIString.capitalize('A ctivate ^breakpoints'); 59 var selectedUILocation = details && details.callFrames.length ?
51 contextMenu.appendItem( 60 Bindings.debuggerWorkspaceBinding.rawLocationToUILocation(details.callFr ames[0].location()) :
52 breakpointActiveTitle, 61 null;
53 this._breakpointManager.setBreakpointsActive.bind(this._breakpointManage r, !breakpointActive)); 62
54 } 63 var shouldShowView = false;
55 64 var entry = this._listElement.firstChild;
56 /** 65 var promises = [];
57 * @param {!Common.Event} event 66 for (var descriptor of locationForEntry.keysArray()) {
lushnikov 2016/11/14 18:56:15 let's use valuesArray()
kozy 2016/11/14 19:45:25 Multimap.valuesArray returns flatten array of valu
58 */ 67 if (!entry) {
59 _breakpointAdded(event) { 68 entry = this._listElement.createChild('div', 'breakpoint-entry');
60 this._breakpointRemoved(event); 69 entry.addEventListener('contextmenu', this._breakpointContextMenu.bind(t his), true);
61 70 entry.addEventListener('click', this._revealLocation.bind(this), false);
62 var breakpoint = /** @type {!Bindings.BreakpointManager.Breakpoint} */ (even t.data.breakpoint); 71 var checkboxLabel = createCheckboxLabel('');
63 var uiLocation = /** @type {!Workspace.UILocation} */ (event.data.uiLocation ); 72 checkboxLabel.addEventListener('click', this._breakpointCheckboxClicked. bind(this), false);
64 this._addBreakpoint(breakpoint, uiLocation); 73 entry.appendChild(checkboxLabel);
65 } 74 entry[Sources.JavaScriptBreakpointsSidebarPane._checkboxLabelSymbol] = c heckboxLabel;
66 75 var snippetElement = entry.createChild('div', 'source-text monospace');
67 /** 76 entry[Sources.JavaScriptBreakpointsSidebarPane._snippetElementSymbol] = snippetElement;
68 * @param {!Bindings.BreakpointManager.Breakpoint} breakpoint 77 }
78
79 var locations = Array.from(locationForEntry.get(descriptor));
80 var uiLocation = locations[0].uiLocation;
81 var isSelected = !!selectedUILocation && locations.some(location => locati on.uiLocation.id() === selectedUILocation.id());
82 var hasEnabled = locations.some(location => location.breakpoint.enabled()) ;
83 var hasDisabled = locations.some(location => !location.breakpoint.enabled( ));
84 promises.push(this._resetEntry(/** @type {!Element}*/(entry), uiLocation, isSelected, hasEnabled, hasDisabled));
85
86 if (isSelected)
87 shouldShowView = true;
88 entry = entry.nextSibling;
89 }
90 var entriesToRemove = this._listElement.children.length - locationForEntry.s ize;
lushnikov 2016/11/14 18:56:15 while (entry) { var next = entry.nextSibling;
kozy 2016/11/14 19:45:25 Done.
91 while (entriesToRemove--)
92 this._listElement.removeChild(this._listElement.lastChild);
93 if (shouldShowView)
94 UI.viewManager.showView('sources.jsBreakpoints');
95 this._listElement.classList.toggle('breakpoints-list-deactivated', !this._br eakpointManager.breakpointsActive());
96 Promise.all(promises).then(() => this.didUpdateForTest());
97 return Promise.resolve();
98 }
99
100 /**
101 * @param {!Element} element
69 * @param {!Workspace.UILocation} uiLocation 102 * @param {!Workspace.UILocation} uiLocation
70 */ 103 * @param {boolean} isSelected
71 _addBreakpoint(breakpoint, uiLocation) { 104 * @param {boolean} hasEnabled
72 var element = createElementWithClass('li', 'cursor-pointer'); 105 * @param {boolean} hasDisabled
73 element.addEventListener('contextmenu', this._breakpointContextMenu.bind(thi s, breakpoint), true); 106 * @return {!Promise<undefined>}
74 element.addEventListener('click', this._breakpointClicked.bind(this, uiLocat ion), false); 107 */
75 108 _resetEntry(element, uiLocation, isSelected, hasEnabled, hasDisabled) {
76 var checkboxLabel = createCheckboxLabel(uiLocation.linkText(), breakpoint.en abled()); 109 element[Sources.JavaScriptBreakpointsSidebarPane._locationSymbol] = uiLocati on;
77 element.appendChild(checkboxLabel); 110 element.classList.toggle('breakpoint-hit', isSelected);
78 checkboxLabel.addEventListener('click', this._breakpointCheckboxClicked.bind (this, breakpoint), false); 111
79 112 var checkboxLabel = element[Sources.JavaScriptBreakpointsSidebarPane._checkb oxLabelSymbol];
80 var snippetElement = element.createChild('div', 'source-text monospace'); 113 checkboxLabel.textElement.textContent = uiLocation.linkText();
114 checkboxLabel.checkboxElement.checked = hasEnabled;
115 checkboxLabel.checkboxElement.indeterminate = hasEnabled && hasDisabled;
116
117 var snippetElement = element[Sources.JavaScriptBreakpointsSidebarPane._snipp etElementSymbol];
118 return uiLocation.uiSourceCode.requestContent().then(fillSnippetElement.bind (this, snippetElement));
81 119
82 /** 120 /**
121 * @param {!Element} snippetElement
83 * @param {?string} content 122 * @param {?string} content
84 * @this {Sources.JavaScriptBreakpointsSidebarPane} 123 * @this {Sources.JavaScriptBreakpointsSidebarPane}
85 */ 124 */
86 function didRequestContent(content) { 125 function fillSnippetElement(snippetElement, content) {
87 var lineNumber = uiLocation.lineNumber; 126 var lineNumber = uiLocation.lineNumber;
88 var columnNumber = uiLocation.columnNumber;
89 var text = new Common.Text(content || ''); 127 var text = new Common.Text(content || '');
90 if (lineNumber < text.lineCount()) { 128 if (lineNumber < text.lineCount()) {
91 var lineText = text.lineAt(lineNumber); 129 var lineText = text.lineAt(lineNumber);
92 var maxSnippetLength = 200; 130 var maxSnippetLength = 200;
93 var snippetStartIndex = columnNumber > 100 ? columnNumber : 0; 131 snippetElement.textContent = lineText.trimEnd(maxSnippetLength);
94 snippetElement.textContent = lineText.substr(snippetStartIndex).trimEnd( maxSnippetLength);
95 } 132 }
96 this.didReceiveBreakpointLineForTest(uiLocation.uiSourceCode, lineNumber, columnNumber); 133 this.didReceiveBreakpointLineForTest(uiLocation.uiSourceCode, lineNumber, 0);
97 } 134 }
98 135 }
99 uiLocation.uiSourceCode.requestContent().then(didRequestContent.bind(this)); 136
100 137 /**
101 element._data = uiLocation; 138 * @param {!Event} event
lushnikov 2016/11/14 18:56:15 @return
kozy 2016/11/14 19:45:25 Done.
102 var currentElement = this._listElement.firstChild; 139 */
103 while (currentElement) { 140 _locationFromEvent(event) {
lushnikov 2016/11/14 18:56:15 "_uiLocationFromEvent" (Alex)
kozy 2016/11/14 19:45:26 Done.
104 if (currentElement._data && this._compareBreakpoints(currentElement._data, element._data) > 0) 141 var node = event.target.enclosingNodeOrSelfWithClass('breakpoint-entry');
105 break; 142 if (!node)
106 currentElement = currentElement.nextSibling; 143 return null;
107 } 144 return node[Sources.JavaScriptBreakpointsSidebarPane._locationSymbol] || nul l;
108 this._addListElement(element, currentElement); 145 }
109 146
110 var breakpointItem = {element: element, checkbox: checkboxLabel.checkboxElem ent}; 147 /**
111 this._items.set(breakpoint, breakpointItem); 148 * @param {!Event} event
149 */
150 _breakpointCheckboxClicked(event) {
151 var uiLocation = this._locationFromEvent(event);
152 if (!uiLocation)
153 return;
154
155 var breakpoints = this._breakpointManager.findBreakpoints(uiLocation.uiSourc eCode, uiLocation.lineNumber);
156 var newState = event.target.checkboxElement.checked;
157 for (var breakpoint of breakpoints)
158 breakpoint.setEnabled(newState);
159 event.consume();
160 }
161
162 /**
163 * @param {!Event} event
164 */
165 _revealLocation(event) {
166 var uiLocation = this._locationFromEvent(event);
167 if (uiLocation)
168 Common.Revealer.reveal(uiLocation);
169 }
170
171 /**
172 * @param {!Event} event
173 */
174 _breakpointContextMenu(event) {
175 var uiLocation = this._locationFromEvent(event);
176 if (!uiLocation)
177 return;
178
179 var breakpoints = this._breakpointManager.findBreakpoints(uiLocation.uiSourc eCode, uiLocation.lineNumber);
180
181 var contextMenu = new UI.ContextMenu(event);
182 var removeEntryTitle = breakpoints.length > 1 ? Common.UIString('Remove all breakpoints in line')
183 : Common.UIString('Remove brea kpoint');
184 contextMenu.appendItem(removeEntryTitle, () => breakpoints.map(breakpoint => breakpoint.remove()));
185
186 contextMenu.appendSeparator();
187 var breakpointActive = this._breakpointManager.breakpointsActive();
188 var breakpointActiveTitle = breakpointActive ? Common.UIString('Deactivate b reakpoints') :
189 Common.UIString('Activate bre akpoints');
190 contextMenu.appendItem(
191 breakpointActiveTitle,
192 this._breakpointManager.setBreakpointsActive.bind(this._breakpointManage r, !breakpointActive));
193
194 contextMenu.appendSeparator();
195 if (breakpoints.some(breakpoint => !breakpoint.enabled())) {
196 var enableTitle = Common.UIString('Enable all breakpoints');
197 contextMenu.appendItem(
198 enableTitle, this._breakpointManager.toggleAllBreakpoints.bind(this._b reakpointManager, true));
199 }
200 if (breakpoints.some(breakpoint => breakpoint.enabled())) {
201 var disableTitle = Common.UIString('Disable all breakpoints');
202 contextMenu.appendItem(
203 disableTitle, this._breakpointManager.toggleAllBreakpoints.bind(this._ breakpointManager, false));
204 }
205 var removeAllTitle = Common.UIString('Remove all breakpoints');
206 contextMenu.appendItem(
207 removeAllTitle, this._breakpointManager.removeAllBreakpoints.bind(this._ breakpointManager));
208 contextMenu.show();
209 }
210
211 /**
212 * @override
213 * @param {?Object} object
214 */
215 flavorChanged(object) {
216 this.update();
217 }
218
219 didUpdateForTest() {
lushnikov 2016/11/14 18:56:15 nit: _didUpdateForTest
kozy 2016/11/14 19:45:25 Done.
112 } 220 }
113 221
114 /** 222 /**
115 * @param {!Workspace.UISourceCode} uiSourceCode 223 * @param {!Workspace.UISourceCode} uiSourceCode
116 * @param {number} lineNumber 224 * @param {number} lineNumber
117 * @param {number} columnNumber 225 * @param {number} columnNumber
118 */ 226 */
119 didReceiveBreakpointLineForTest(uiSourceCode, lineNumber, columnNumber) { 227 didReceiveBreakpointLineForTest(uiSourceCode, lineNumber, columnNumber) {
120 } 228 }
121
122 /**
123 * @param {!Common.Event} event
124 */
125 _breakpointRemoved(event) {
126 var breakpoint = /** @type {!Bindings.BreakpointManager.Breakpoint} */ (even t.data.breakpoint);
127 var breakpointItem = this._items.get(breakpoint);
128 if (!breakpointItem)
129 return;
130 this._items.remove(breakpoint);
131 this._removeListElement(breakpointItem.element);
132 }
133
134 /**
135 * @override
136 * @param {?Object} object
137 */
138 flavorChanged(object) {
139 this._update();
140 }
141
142 _update() {
143 var details = UI.context.flavor(SDK.DebuggerPausedDetails);
144 var uiLocation = details && details.callFrames.length ?
145 Bindings.debuggerWorkspaceBinding.rawLocationToUILocation(details.callFr ames[0].location()) :
146 null;
147 var breakpoint = uiLocation ?
148 this._breakpointManager.findBreakpoint(
149 uiLocation.uiSourceCode, uiLocation.lineNumber, uiLocation.columnNum ber) :
150 null;
151 var breakpointItem = this._items.get(breakpoint);
152 if (!breakpointItem) {
153 if (this._highlightedBreakpointItem) {
154 this._highlightedBreakpointItem.element.classList.remove('breakpoint-hit ');
155 delete this._highlightedBreakpointItem;
156 }
157 return;
158 }
159
160 breakpointItem.element.classList.add('breakpoint-hit');
161 this._highlightedBreakpointItem = breakpointItem;
162 UI.viewManager.showView('sources.jsBreakpoints');
163 }
164
165 _breakpointsActiveStateChanged() {
166 this._listElement.classList.toggle('breakpoints-list-deactivated', !this._br eakpointManager.breakpointsActive());
167 }
168
169 /**
170 * @param {!Workspace.UILocation} uiLocation
171 */
172 _breakpointClicked(uiLocation) {
173 Common.Revealer.reveal(uiLocation);
174 }
175
176 /**
177 * @param {!Bindings.BreakpointManager.Breakpoint} breakpoint
178 * @param {!Event} event
179 */
180 _breakpointCheckboxClicked(breakpoint, event) {
181 // Breakpoint element has it's own click handler.
182 event.consume();
183 breakpoint.setEnabled(event.target.checkboxElement.checked);
184 }
185
186 /**
187 * @param {!Bindings.BreakpointManager.Breakpoint} breakpoint
188 * @param {!Event} event
189 */
190 _breakpointContextMenu(breakpoint, event) {
191 var breakpoints = this._items.valuesArray();
192 var contextMenu = new UI.ContextMenu(event);
193 contextMenu.appendItem(Common.UIString.capitalize('Remove ^breakpoint'), bre akpoint.remove.bind(breakpoint));
194 if (breakpoints.length > 1) {
195 var removeAllTitle = Common.UIString.capitalize('Remove ^all ^breakpoints' );
196 contextMenu.appendItem(
197 removeAllTitle, this._breakpointManager.removeAllBreakpoints.bind(this ._breakpointManager));
198 }
199
200 contextMenu.appendSeparator();
201 this._appendBreakpointActiveItem(contextMenu);
202
203 function enabledBreakpointCount(breakpoints) {
204 var count = 0;
205 for (var i = 0; i < breakpoints.length; ++i) {
206 if (breakpoints[i].checkbox.checked)
207 count++;
208 }
209 return count;
210 }
211 if (breakpoints.length > 1) {
212 var enableBreakpointCount = enabledBreakpointCount(breakpoints);
213 var enableTitle = Common.UIString.capitalize('Enable ^all ^breakpoints');
214 var disableTitle = Common.UIString.capitalize('Disable ^all ^breakpoints') ;
215
216 contextMenu.appendSeparator();
217
218 contextMenu.appendItem(
219 enableTitle, this._breakpointManager.toggleAllBreakpoints.bind(this._b reakpointManager, true),
220 !(enableBreakpointCount !== breakpoints.length));
221 contextMenu.appendItem(
222 disableTitle, this._breakpointManager.toggleAllBreakpoints.bind(this._ breakpointManager, false),
223 !(enableBreakpointCount > 1));
224 }
225
226 contextMenu.show();
227 }
228
229 _addListElement(element, beforeElement) {
230 if (beforeElement)
231 this._listElement.insertBefore(element, beforeElement);
232 else {
233 if (!this._listElement.firstChild) {
234 this.element.removeChild(this.emptyElement);
235 this.element.appendChild(this._listElement);
236 }
237 this._listElement.appendChild(element);
238 }
239 }
240
241 _removeListElement(element) {
242 this._listElement.removeChild(element);
243 if (!this._listElement.firstChild) {
244 this.element.removeChild(this._listElement);
245 this.element.appendChild(this.emptyElement);
246 }
247 }
248
249 _compare(x, y) {
250 if (x !== y)
251 return x < y ? -1 : 1;
252 return 0;
253 }
254
255 _compareBreakpoints(b1, b2) {
256 return this._compare(b1.uiSourceCode.url(), b2.uiSourceCode.url()) || this._ compare(b1.lineNumber, b2.lineNumber);
257 }
258
259 reset() {
260 this._listElement.removeChildren();
261 if (this._listElement.parentElement) {
262 this.element.removeChild(this._listElement);
263 this.element.appendChild(this.emptyElement);
264 }
265 this._items.clear();
266 }
267 }; 229 };
230
231 Sources.JavaScriptBreakpointsSidebarPane._locationSymbol = Symbol('location');
232 Sources.JavaScriptBreakpointsSidebarPane._checkboxLabelSymbol = Symbol('checkbox -labal');
233 Sources.JavaScriptBreakpointsSidebarPane._snippetElementSymbol = Symbol('snippet -element');
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698