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

Side by Side Diff: Source/devtools/front_end/profiler/CPUProfileView.js

Issue 696703003: DevTools: implement search for CPUProfiler FlameChart view (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: comments addressed Created 6 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 | Annotate | Revision Log
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2008 Apple Inc. All Rights Reserved. 2 * Copyright (C) 2008 Apple Inc. All Rights Reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions 5 * modification, are permitted provided that the following conditions
6 * are met: 6 * are met:
7 * 1. Redistributions of source code must retain the above copyright 7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer. 8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright 9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the 10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution. 11 * documentation and/or other materials provided with the distribution.
12 * 12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */ 24 */
25 25
26 26
27 /** 27 /**
28 * @constructor 28 * @constructor
29 * @implements {WebInspector.Searchable}
29 * @extends {WebInspector.VBox} 30 * @extends {WebInspector.VBox}
30 * @param {!WebInspector.CPUProfileHeader} profileHeader 31 * @param {!WebInspector.CPUProfileHeader} profileHeader
31 */ 32 */
32 WebInspector.CPUProfileView = function(profileHeader) 33 WebInspector.CPUProfileView = function(profileHeader)
33 { 34 {
34 WebInspector.VBox.call(this); 35 WebInspector.VBox.call(this);
35 this.element.classList.add("cpu-profile-view"); 36 this.element.classList.add("cpu-profile-view");
36 37
38 this._searchableView = new WebInspector.SearchableView(this);
39 this._searchableView.show(this.element);
40
37 this._viewType = WebInspector.settings.createSetting("cpuProfilerView", WebI nspector.CPUProfileView._TypeHeavy); 41 this._viewType = WebInspector.settings.createSetting("cpuProfilerView", WebI nspector.CPUProfileView._TypeHeavy);
38 42
39 var columns = []; 43 var columns = [];
40 columns.push({id: "self", title: WebInspector.UIString("Self"), width: "120p x", sort: WebInspector.DataGrid.Order.Descending, sortable: true}); 44 columns.push({id: "self", title: WebInspector.UIString("Self"), width: "120p x", sort: WebInspector.DataGrid.Order.Descending, sortable: true});
41 columns.push({id: "total", title: WebInspector.UIString("Total"), width: "12 0px", sortable: true}); 45 columns.push({id: "total", title: WebInspector.UIString("Total"), width: "12 0px", sortable: true});
42 columns.push({id: "function", title: WebInspector.UIString("Function"), disc losure: true, sortable: true}); 46 columns.push({id: "function", title: WebInspector.UIString("Function"), disc losure: true, sortable: true});
43 47
44 this.dataGrid = new WebInspector.DataGrid(columns); 48 this.dataGrid = new WebInspector.DataGrid(columns);
45 this.dataGrid.addEventListener(WebInspector.DataGrid.Events.SortingChanged, this._sortProfile, this); 49 this.dataGrid.addEventListener(WebInspector.DataGrid.Events.SortingChanged, this._sortProfile, this);
46 this.dataGrid.show(this.element);
47 50
48 this.viewSelectComboBox = new WebInspector.StatusBarComboBox(this._changeVie w.bind(this)); 51 this.viewSelectComboBox = new WebInspector.StatusBarComboBox(this._changeVie w.bind(this));
49 52
50 var options = {}; 53 var options = {};
51 options[WebInspector.CPUProfileView._TypeFlame] = this.viewSelectComboBox.cr eateOption(WebInspector.UIString("Chart"), "", WebInspector.CPUProfileView._Type Flame); 54 options[WebInspector.CPUProfileView._TypeFlame] = this.viewSelectComboBox.cr eateOption(WebInspector.UIString("Chart"), "", WebInspector.CPUProfileView._Type Flame);
52 options[WebInspector.CPUProfileView._TypeHeavy] = this.viewSelectComboBox.cr eateOption(WebInspector.UIString("Heavy (Bottom Up)"), "", WebInspector.CPUProfi leView._TypeHeavy); 55 options[WebInspector.CPUProfileView._TypeHeavy] = this.viewSelectComboBox.cr eateOption(WebInspector.UIString("Heavy (Bottom Up)"), "", WebInspector.CPUProfi leView._TypeHeavy);
53 options[WebInspector.CPUProfileView._TypeTree] = this.viewSelectComboBox.cre ateOption(WebInspector.UIString("Tree (Top Down)"), "", WebInspector.CPUProfileV iew._TypeTree); 56 options[WebInspector.CPUProfileView._TypeTree] = this.viewSelectComboBox.cre ateOption(WebInspector.UIString("Tree (Top Down)"), "", WebInspector.CPUProfileV iew._TypeTree);
54 57
55 var optionName = this._viewType.get() || WebInspector.CPUProfileView._TypeFl ame; 58 var optionName = this._viewType.get() || WebInspector.CPUProfileView._TypeFl ame;
56 var option = options[optionName] || options[WebInspector.CPUProfileView._Typ eFlame]; 59 var option = options[optionName] || options[WebInspector.CPUProfileView._Typ eFlame];
(...skipping 23 matching lines...) Expand all
80 83
81 this._changeView(); 84 this._changeView();
82 if (this._flameChart) 85 if (this._flameChart)
83 this._flameChart.update(); 86 this._flameChart.update();
84 } 87 }
85 88
86 WebInspector.CPUProfileView._TypeFlame = "Flame"; 89 WebInspector.CPUProfileView._TypeFlame = "Flame";
87 WebInspector.CPUProfileView._TypeTree = "Tree"; 90 WebInspector.CPUProfileView._TypeTree = "Tree";
88 WebInspector.CPUProfileView._TypeHeavy = "Heavy"; 91 WebInspector.CPUProfileView._TypeHeavy = "Heavy";
89 92
93 /**
94 * @interface
95 */
96 WebInspector.CPUProfileView.Searchable = function()
97 {
98 }
99
100 WebInspector.CPUProfileView.Searchable.prototype = {
101 jumpToNextSearchResult: function() {},
102 jumpToPreviousSearchResult: function() {},
103 searchCanceled: function() {},
104 /**
105 * @param {!WebInspector.SearchableView.SearchConfig} searchConfig
106 * @param {boolean} shouldJump
107 * @param {boolean=} jumpBackwards
108 * @return {number}
109 */
110 performSearch: function(searchConfig, shouldJump, jumpBackwards) {},
111 /**
112 * @return {number}
113 */
114 currentSearchResultIndex: function() {}
115 }
116
90 WebInspector.CPUProfileView.prototype = { 117 WebInspector.CPUProfileView.prototype = {
91 focus: function() 118 focus: function()
92 { 119 {
93 if (this._flameChart) 120 if (this._flameChart)
94 this._flameChart.focus(); 121 this._flameChart.focus();
95 else 122 else
96 WebInspector.View.prototype.focus.call(this); 123 WebInspector.View.prototype.focus.call(this);
97 }, 124 },
98 125
99 /** 126 /**
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
163 190
164 refreshVisibleData: function() 191 refreshVisibleData: function()
165 { 192 {
166 var child = this.dataGrid.rootNode().children[0]; 193 var child = this.dataGrid.rootNode().children[0];
167 while (child) { 194 while (child) {
168 child.refresh(); 195 child.refresh();
169 child = child.traverseNextNode(false, null, true); 196 child = child.traverseNextNode(false, null, true);
170 } 197 }
171 }, 198 },
172 199
200 /**
201 * @return {!WebInspector.SearchableView}
202 */
203 searchableView: function()
204 {
205 return this._searchableView;
206 },
207
208 /**
209 * @return {boolean}
210 */
211 supportsCaseSensitiveSearch: function()
212 {
213 return true;
214 },
215
216 /**
217 * @return {boolean}
218 */
219 supportsRegexSearch: function()
220 {
221 return false;
222 },
223
173 searchCanceled: function() 224 searchCanceled: function()
174 { 225 {
175 if (this._searchResults) { 226 this._searchableElement.searchCanceled();
176 for (var i = 0; i < this._searchResults.length; ++i) {
177 var profileNode = this._searchResults[i].profileNode;
178
179 delete profileNode._searchMatchedSelfColumn;
180 delete profileNode._searchMatchedTotalColumn;
181 delete profileNode._searchMatchedFunctionColumn;
182
183 profileNode.refresh();
184 }
185 }
186
187 delete this._searchFinishedCallback;
188 this._currentSearchResultIndex = -1;
189 this._searchResults = [];
190 }, 227 },
191 228
192 performSearch: function(query, finishedCallback) 229 /**
230 * @param {!WebInspector.SearchableView.SearchConfig} searchConfig
231 * @param {boolean} shouldJump
232 * @param {boolean=} jumpBackwards
233 */
234 performSearch: function(searchConfig, shouldJump, jumpBackwards)
193 { 235 {
194 // Call searchCanceled since it will reset everything we need before doi ng a new search. 236 var matchesCount = this._searchableElement.performSearch(searchConfig, s houldJump, jumpBackwards);
195 this.searchCanceled(); 237 this._searchableView.updateSearchMatchesCount(matchesCount);
196 238 this._searchableView.updateCurrentMatchIndex(this._searchableElement.cur rentSearchResultIndex());
197 query = query.trim();
198
199 if (!query.length)
200 return;
201
202 this._searchFinishedCallback = finishedCallback;
203
204 var greaterThan = (query.startsWith(">"));
205 var lessThan = (query.startsWith("<"));
206 var equalTo = (query.startsWith("=") || ((greaterThan || lessThan) && qu ery.indexOf("=") === 1));
207 var percentUnits = (query.lastIndexOf("%") === (query.length - 1));
208 var millisecondsUnits = (query.length > 2 && query.lastIndexOf("ms") === (query.length - 2));
209 var secondsUnits = (!millisecondsUnits && query.lastIndexOf("s") === (qu ery.length - 1));
210
211 var queryNumber = parseFloat(query);
212 if (greaterThan || lessThan || equalTo) {
213 if (equalTo && (greaterThan || lessThan))
214 queryNumber = parseFloat(query.substring(2));
215 else
216 queryNumber = parseFloat(query.substring(1));
217 }
218
219 var queryNumberMilliseconds = (secondsUnits ? (queryNumber * 1000) : que ryNumber);
220
221 // Make equalTo implicitly true if it wasn't specified there is no other operator.
222 if (!isNaN(queryNumber) && !(greaterThan || lessThan))
223 equalTo = true;
224
225 var matcher = createPlainTextSearchRegex(query, "i");
226
227 function matchesQuery(/*ProfileDataGridNode*/ profileDataGridNode)
228 {
229 delete profileDataGridNode._searchMatchedSelfColumn;
230 delete profileDataGridNode._searchMatchedTotalColumn;
231 delete profileDataGridNode._searchMatchedFunctionColumn;
232
233 if (percentUnits) {
234 if (lessThan) {
235 if (profileDataGridNode.selfPercent < queryNumber)
236 profileDataGridNode._searchMatchedSelfColumn = true;
237 if (profileDataGridNode.totalPercent < queryNumber)
238 profileDataGridNode._searchMatchedTotalColumn = true;
239 } else if (greaterThan) {
240 if (profileDataGridNode.selfPercent > queryNumber)
241 profileDataGridNode._searchMatchedSelfColumn = true;
242 if (profileDataGridNode.totalPercent > queryNumber)
243 profileDataGridNode._searchMatchedTotalColumn = true;
244 }
245
246 if (equalTo) {
247 if (profileDataGridNode.selfPercent == queryNumber)
248 profileDataGridNode._searchMatchedSelfColumn = true;
249 if (profileDataGridNode.totalPercent == queryNumber)
250 profileDataGridNode._searchMatchedTotalColumn = true;
251 }
252 } else if (millisecondsUnits || secondsUnits) {
253 if (lessThan) {
254 if (profileDataGridNode.selfTime < queryNumberMilliseconds)
255 profileDataGridNode._searchMatchedSelfColumn = true;
256 if (profileDataGridNode.totalTime < queryNumberMilliseconds)
257 profileDataGridNode._searchMatchedTotalColumn = true;
258 } else if (greaterThan) {
259 if (profileDataGridNode.selfTime > queryNumberMilliseconds)
260 profileDataGridNode._searchMatchedSelfColumn = true;
261 if (profileDataGridNode.totalTime > queryNumberMilliseconds)
262 profileDataGridNode._searchMatchedTotalColumn = true;
263 }
264
265 if (equalTo) {
266 if (profileDataGridNode.selfTime == queryNumberMilliseconds)
267 profileDataGridNode._searchMatchedSelfColumn = true;
268 if (profileDataGridNode.totalTime == queryNumberMilliseconds )
269 profileDataGridNode._searchMatchedTotalColumn = true;
270 }
271 }
272
273 if (profileDataGridNode.functionName.match(matcher) || (profileDataG ridNode.url && profileDataGridNode.url.match(matcher)))
274 profileDataGridNode._searchMatchedFunctionColumn = true;
275
276 if (profileDataGridNode._searchMatchedSelfColumn ||
277 profileDataGridNode._searchMatchedTotalColumn ||
278 profileDataGridNode._searchMatchedFunctionColumn)
279 {
280 profileDataGridNode.refresh();
281 return true;
282 }
283
284 return false;
285 }
286
287 var current = this.profileDataGridTree.children[0];
288
289 while (current) {
290 if (matchesQuery(current)) {
291 this._searchResults.push({ profileNode: current });
292 }
293
294 current = current.traverseNextNode(false, null, false);
295 }
296
297 finishedCallback(this, this._searchResults.length);
298 },
299
300 jumpToFirstSearchResult: function()
301 {
302 if (!this._searchResults || !this._searchResults.length)
303 return;
304 this._currentSearchResultIndex = 0;
305 this._jumpToSearchResult(this._currentSearchResultIndex);
306 },
307
308 jumpToLastSearchResult: function()
309 {
310 if (!this._searchResults || !this._searchResults.length)
311 return;
312 this._currentSearchResultIndex = (this._searchResults.length - 1);
313 this._jumpToSearchResult(this._currentSearchResultIndex);
314 }, 239 },
315 240
316 jumpToNextSearchResult: function() 241 jumpToNextSearchResult: function()
317 { 242 {
318 if (!this._searchResults || !this._searchResults.length) 243 this._searchableElement.jumpToNextSearchResult();
319 return; 244 this._searchableView.updateCurrentMatchIndex(this._searchableElement.cur rentSearchResultIndex());
320 if (++this._currentSearchResultIndex >= this._searchResults.length)
321 this._currentSearchResultIndex = 0;
322 this._jumpToSearchResult(this._currentSearchResultIndex);
323 }, 245 },
324 246
325 jumpToPreviousSearchResult: function() 247 jumpToPreviousSearchResult: function()
326 { 248 {
327 if (!this._searchResults || !this._searchResults.length) 249 this._searchableElement.jumpToPreviousSearchResult();
328 return; 250 this._searchableView.updateCurrentMatchIndex(this._searchableElement.cur rentSearchResultIndex());
329 if (--this._currentSearchResultIndex < 0)
330 this._currentSearchResultIndex = (this._searchResults.length - 1);
331 this._jumpToSearchResult(this._currentSearchResultIndex);
332 },
333
334 /**
335 * @return {number}
336 */
337 currentSearchResultIndex: function() {
338 return this._currentSearchResultIndex;
339 },
340
341 _jumpToSearchResult: function(index)
342 {
343 var searchResult = this._searchResults[index];
344 if (!searchResult)
345 return;
346
347 var profileNode = searchResult.profileNode;
348 profileNode.revealAndSelect();
349 }, 251 },
350 252
351 _ensureFlameChartCreated: function() 253 _ensureFlameChartCreated: function()
352 { 254 {
353 if (this._flameChart) 255 if (this._flameChart)
354 return; 256 return;
355 this._dataProvider = new WebInspector.CPUFlameChartDataProvider(this.pro file, this._profileHeader.target()); 257 this._dataProvider = new WebInspector.CPUFlameChartDataProvider(this.pro file, this._profileHeader.target());
356 this._flameChart = new WebInspector.CPUProfileFlameChart(this._dataProvi der); 258 this._flameChart = new WebInspector.CPUProfileFlameChart(this._dataProvi der);
357 this._flameChart.addEventListener(WebInspector.FlameChart.Events.EntrySe lected, this._onEntrySelected.bind(this)); 259 this._flameChart.addEventListener(WebInspector.FlameChart.Events.EntrySe lected, this._onEntrySelected.bind(this));
358 }, 260 },
(...skipping 13 matching lines...) Expand all
372 return; 274 return;
373 var location = /** @type {!WebInspector.DebuggerModel.Location} */ (scri pt.target().debuggerModel.createRawLocation(script, node.lineNumber, 0)); 275 var location = /** @type {!WebInspector.DebuggerModel.Location} */ (scri pt.target().debuggerModel.createRawLocation(script, node.lineNumber, 0));
374 WebInspector.Revealer.reveal(WebInspector.debuggerWorkspaceBinding.rawLo cationToUILocation(location)); 276 WebInspector.Revealer.reveal(WebInspector.debuggerWorkspaceBinding.rawLo cationToUILocation(location));
375 }, 277 },
376 278
377 _changeView: function() 279 _changeView: function()
378 { 280 {
379 if (!this.profile) 281 if (!this.profile)
380 return; 282 return;
381 283
382 switch (this.viewSelectComboBox.selectedOption().value) { 284 this._searchableView.closeSearch();
285
286 if (this._visibleView)
287 this._visibleView.detach();
288
289 this._viewType.set(this.viewSelectComboBox.selectedOption().value);
290 switch (this._viewType.get()) {
383 case WebInspector.CPUProfileView._TypeFlame: 291 case WebInspector.CPUProfileView._TypeFlame:
384 this._ensureFlameChartCreated(); 292 this._ensureFlameChartCreated();
385 this.dataGrid.detach();
386 this._flameChart.show(this.element);
387 this._viewType.set(WebInspector.CPUProfileView._TypeFlame);
388 this._statusBarButtonsElement.classList.toggle("hidden", true); 293 this._statusBarButtonsElement.classList.toggle("hidden", true);
389 return; 294 this._visibleView = this._flameChart;
295 this._searchableElement = this._flameChart;
296 break;
390 case WebInspector.CPUProfileView._TypeTree: 297 case WebInspector.CPUProfileView._TypeTree:
391 this.profileDataGridTree = this._getTopDownProfileDataGridTree(); 298 this.profileDataGridTree = this._getTopDownProfileDataGridTree();
392 this._sortProfile(); 299 this._sortProfile();
393 this._viewType.set(WebInspector.CPUProfileView._TypeTree); 300 this._visibleView = this.dataGrid;
301 this._searchableElement = this.profileDataGridTree;
394 break; 302 break;
395 case WebInspector.CPUProfileView._TypeHeavy: 303 case WebInspector.CPUProfileView._TypeHeavy:
396 this.profileDataGridTree = this._getBottomUpProfileDataGridTree(); 304 this.profileDataGridTree = this._getBottomUpProfileDataGridTree();
397 this._sortProfile(); 305 this._sortProfile();
398 this._viewType.set(WebInspector.CPUProfileView._TypeHeavy); 306 this._visibleView = this.dataGrid;
307 this._searchableElement = this.profileDataGridTree;
399 break; 308 break;
400 } 309 }
401 310
402 this._statusBarButtonsElement.classList.toggle("hidden", false); 311 this._statusBarButtonsElement.classList.toggle("hidden", this._viewType. get() === WebInspector.CPUProfileView._TypeFlame);
403 312 this._visibleView.show(this._searchableView.element);
404 if (this._flameChart)
405 this._flameChart.detach();
406 this.dataGrid.show(this.element);
407
408 if (!this.currentQuery || !this._searchFinishedCallback || !this._search Results)
409 return;
410
411 // The current search needs to be performed again. First negate out prev ious match
412 // count by calling the search finished callback with a negative number of matches.
413 // Then perform the search again the with same query and callback.
414 this._searchFinishedCallback(this, -this._searchResults.length);
415 this.performSearch(this.currentQuery, this._searchFinishedCallback);
416 }, 313 },
417 314
418 _focusClicked: function(event) 315 _focusClicked: function(event)
419 { 316 {
420 if (!this.dataGrid.selectedNode) 317 if (!this.dataGrid.selectedNode)
421 return; 318 return;
422 319
423 this.resetButton.visible = true; 320 this.resetButton.visible = true;
424 this.profileDataGridTree.focus(this.dataGrid.selectedNode); 321 this.profileDataGridTree.focus(this.dataGrid.selectedNode);
425 this.refresh(); 322 this.refresh();
(...skipping 454 matching lines...) Expand 10 before | Expand all | Expand 10 after
880 _notifyTempFileReady: function() 777 _notifyTempFileReady: function()
881 { 778 {
882 if (this._onTempFileReady) { 779 if (this._onTempFileReady) {
883 this._onTempFileReady(); 780 this._onTempFileReady();
884 this._onTempFileReady = null; 781 this._onTempFileReady = null;
885 } 782 }
886 }, 783 },
887 784
888 __proto__: WebInspector.ProfileHeader.prototype 785 __proto__: WebInspector.ProfileHeader.prototype
889 } 786 }
OLDNEW
« no previous file with comments | « Source/devtools/front_end/profiler/CPUProfileFlameChart.js ('k') | Source/devtools/front_end/profiler/HeapSnapshotView.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698