Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / WebKit / Source / devtools / front_end / profiler / HeapSnapshotView.js
blob033d22bd9159483bfa962315365b38c6f0324b6b
1 /*
2 * Copyright (C) 2011 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
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.
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.
31 /**
32 * @constructor
33 * @implements {WebInspector.ProfileType.DataDisplayDelegate}
34 * @implements {WebInspector.Searchable}
35 * @extends {WebInspector.VBox}
36 * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
37 * @param {!WebInspector.HeapProfileHeader} profile
39 WebInspector.HeapSnapshotView = function(dataDisplayDelegate, profile)
41 WebInspector.VBox.call(this);
43 this.element.classList.add("heap-snapshot-view");
45 profile.profileType().addEventListener(WebInspector.HeapSnapshotProfileType.SnapshotReceived, this._onReceiveSnapshot, this);
46 profile.profileType().addEventListener(WebInspector.ProfileType.Events.RemoveProfileHeader, this._onProfileHeaderRemoved, this);
48 var isHeapTimeline = profile.profileType().id === WebInspector.TrackingHeapSnapshotProfileType.TypeId;
49 if (isHeapTimeline) {
50 this._trackingOverviewGrid = new WebInspector.HeapTrackingOverviewGrid(profile);
51 this._trackingOverviewGrid.addEventListener(WebInspector.HeapTrackingOverviewGrid.IdsRangeChanged, this._onIdsRangeChanged.bind(this));
54 this._parentDataDisplayDelegate = dataDisplayDelegate;
56 this._searchableView = new WebInspector.SearchableView(this);
57 this._searchableView.show(this.element);
59 this._splitWidget = new WebInspector.SplitWidget(false, true, "heapSnapshotSplitViewState", 200, 200);
60 this._splitWidget.show(this._searchableView.element);
62 this._containmentWidget = new WebInspector.VBox();
63 this._containmentWidget.setMinimumSize(50, 25);
64 this._containmentDataGrid = new WebInspector.HeapSnapshotContainmentDataGrid(this);
65 this._containmentDataGrid.show(this._containmentWidget.element);
66 this._containmentDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._selectionChanged, this);
68 this._statisticsView = new WebInspector.HeapSnapshotStatisticsView();
70 this._constructorsWidget = new WebInspector.VBox();
71 this._constructorsWidget.setMinimumSize(50, 25);
73 this._constructorsDataGrid = new WebInspector.HeapSnapshotConstructorsDataGrid(this);
74 this._constructorsDataGrid.show(this._constructorsWidget.element);
75 this._constructorsDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._selectionChanged, this);
77 this._diffWidget = new WebInspector.VBox();
78 this._diffWidget.setMinimumSize(50, 25);
80 this._diffDataGrid = new WebInspector.HeapSnapshotDiffDataGrid(this);
81 this._diffDataGrid.show(this._diffWidget.element);
82 this._diffDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._selectionChanged, this);
84 if (isHeapTimeline && WebInspector.moduleSetting("recordAllocationStacks").get()) {
85 this._allocationWidget = new WebInspector.VBox();
86 this._allocationWidget.setMinimumSize(50, 25);
87 this._allocationDataGrid = new WebInspector.AllocationDataGrid(profile.target() , this);
88 this._allocationDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._onSelectAllocationNode, this);
89 this._allocationDataGrid.show(this._allocationWidget.element);
91 this._allocationStackView = new WebInspector.HeapAllocationStackView(profile.target());
92 this._allocationStackView.setMinimumSize(50, 25);
94 this._tabbedPane = new WebInspector.TabbedPane();
95 this._tabbedPane.headerElement().classList.add("heap-object-details-header");
98 this._retainmentWidget = new WebInspector.VBox();
99 this._retainmentWidget.setMinimumSize(50, 21);
100 this._retainmentWidget.element.classList.add("retaining-paths-view");
102 var splitWidgetResizer;
103 if (this._allocationStackView) {
104 this._tabbedPane = new WebInspector.TabbedPane();
105 this._tabbedPane.headerElement().classList.add("heap-object-details-header");
107 this._tabbedPane.appendTab("retainers", WebInspector.UIString("Retainers"), this._retainmentWidget);
108 this._tabbedPane.appendTab("allocation-stack", WebInspector.UIString("Allocation stack"), this._allocationStackView);
110 splitWidgetResizer = this._tabbedPane.headerElement();
111 this._objectDetailsView = this._tabbedPane;
112 } else {
113 var retainmentViewHeader = createElementWithClass("div", "heap-snapshot-view-resizer");
114 var retainingPathsTitleDiv = retainmentViewHeader.createChild("div", "title");
115 var retainingPathsTitle = retainingPathsTitleDiv.createChild("span");
116 retainingPathsTitle.textContent = WebInspector.UIString("Retainers");
117 this._retainmentWidget.element.appendChild(retainmentViewHeader);
119 splitWidgetResizer = retainmentViewHeader;
120 this._objectDetailsView = this._retainmentWidget;
122 this._splitWidget.hideDefaultResizer();
123 this._splitWidget.installResizer(splitWidgetResizer);
125 this._retainmentDataGrid = new WebInspector.HeapSnapshotRetainmentDataGrid(this);
126 this._retainmentDataGrid.show(this._retainmentWidget.element);
127 this._retainmentDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._inspectedObjectChanged, this);
128 this._retainmentDataGrid.reset();
130 this._perspectives = [];
131 this._perspectives.push(new WebInspector.HeapSnapshotView.SummaryPerspective());
132 if (profile.profileType() !== WebInspector.ProfileTypeRegistry.instance.trackingHeapSnapshotProfileType)
133 this._perspectives.push(new WebInspector.HeapSnapshotView.ComparisonPerspective());
134 this._perspectives.push(new WebInspector.HeapSnapshotView.ContainmentPerspective());
135 if (this._allocationWidget)
136 this._perspectives.push(new WebInspector.HeapSnapshotView.AllocationPerspective());
137 this._perspectives.push(new WebInspector.HeapSnapshotView.StatisticsPerspective());
139 this._perspectiveSelect = new WebInspector.ToolbarComboBox(this._onSelectedPerspectiveChanged.bind(this));
140 for (var i = 0; i < this._perspectives.length; ++i)
141 this._perspectiveSelect.createOption(this._perspectives[i].title());
143 this._profile = profile;
145 this._baseSelect = new WebInspector.ToolbarComboBox(this._changeBase.bind(this));
146 this._baseSelect.setVisible(false);
147 this._updateBaseOptions();
149 this._filterSelect = new WebInspector.ToolbarComboBox(this._changeFilter.bind(this));
150 this._filterSelect.setVisible(false);
151 this._updateFilterOptions();
153 this._classNameFilter = new WebInspector.ToolbarInput("Class filter");
154 this._classNameFilter.setVisible(false);
155 this._constructorsDataGrid.setNameFilter(this._classNameFilter);
156 this._diffDataGrid.setNameFilter(this._classNameFilter);
158 this._selectedSizeText = new WebInspector.ToolbarText("");
160 this._popoverHelper = new WebInspector.ObjectPopoverHelper(this.element, this._getHoverAnchor.bind(this), this._resolveObjectForPopover.bind(this), undefined, true);
162 this._currentPerspectiveIndex = 0;
163 this._currentPerspective = this._perspectives[0];
164 this._currentPerspective.activate(this);
165 this._dataGrid = this._currentPerspective.masterGrid(this);
167 this._refreshView();
168 this._searchThrottler = new WebInspector.Throttler(0);
172 * @constructor
173 * @param {string} title
175 WebInspector.HeapSnapshotView.Perspective = function(title)
177 this._title = title;
180 WebInspector.HeapSnapshotView.Perspective.prototype = {
182 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
184 activate: function(heapSnapshotView) { },
187 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
189 deactivate: function(heapSnapshotView)
191 heapSnapshotView._baseSelect.setVisible(false);
192 heapSnapshotView._filterSelect.setVisible(false);
193 heapSnapshotView._classNameFilter.setVisible(false);
194 if (heapSnapshotView._trackingOverviewGrid)
195 heapSnapshotView._trackingOverviewGrid.detach();
196 if (heapSnapshotView._allocationWidget)
197 heapSnapshotView._allocationWidget.detach();
198 if (heapSnapshotView._statisticsView)
199 heapSnapshotView._statisticsView.detach();
201 heapSnapshotView._splitWidget.detach();
202 heapSnapshotView._splitWidget.detachChildWidgets();
206 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
207 * @return {?WebInspector.DataGrid}
209 masterGrid: function(heapSnapshotView)
211 return null;
215 * @return {string}
217 title: function()
219 return this._title;
223 * @return {boolean}
225 supportsSearch: function()
227 return false;
232 * @constructor
233 * @extends {WebInspector.HeapSnapshotView.Perspective}
235 WebInspector.HeapSnapshotView.SummaryPerspective = function()
237 WebInspector.HeapSnapshotView.Perspective.call(this, WebInspector.UIString("Summary"));
240 WebInspector.HeapSnapshotView.SummaryPerspective.prototype = {
242 * @override
243 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
245 activate: function(heapSnapshotView)
247 heapSnapshotView._splitWidget.setMainWidget(heapSnapshotView._constructorsWidget);
248 heapSnapshotView._splitWidget.setSidebarWidget(heapSnapshotView._objectDetailsView);
249 heapSnapshotView._splitWidget.show(heapSnapshotView._searchableView.element);
250 heapSnapshotView._filterSelect.setVisible(true);
251 heapSnapshotView._classNameFilter.setVisible(true);
252 if (heapSnapshotView._trackingOverviewGrid) {
253 heapSnapshotView._trackingOverviewGrid.show(heapSnapshotView._searchableView.element, heapSnapshotView._splitWidget.element);
254 heapSnapshotView._trackingOverviewGrid.update();
255 heapSnapshotView._trackingOverviewGrid._updateGrid();
260 * @override
261 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
262 * @return {?WebInspector.DataGrid}
264 masterGrid: function(heapSnapshotView)
266 return heapSnapshotView._constructorsDataGrid;
270 * @override
271 * @return {boolean}
273 supportsSearch: function()
275 return true;
278 __proto__: WebInspector.HeapSnapshotView.Perspective.prototype
282 * @constructor
283 * @extends {WebInspector.HeapSnapshotView.Perspective}
285 WebInspector.HeapSnapshotView.ComparisonPerspective = function()
287 WebInspector.HeapSnapshotView.Perspective.call(this, WebInspector.UIString("Comparison"));
290 WebInspector.HeapSnapshotView.ComparisonPerspective.prototype = {
292 * @override
293 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
295 activate: function(heapSnapshotView)
297 heapSnapshotView._splitWidget.setMainWidget(heapSnapshotView._diffWidget);
298 heapSnapshotView._splitWidget.setSidebarWidget(heapSnapshotView._objectDetailsView);
299 heapSnapshotView._splitWidget.show(heapSnapshotView._searchableView.element);
300 heapSnapshotView._baseSelect.setVisible(true);
301 heapSnapshotView._classNameFilter.setVisible(true);
305 * @override
306 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
307 * @return {?WebInspector.DataGrid}
309 masterGrid: function(heapSnapshotView)
311 return heapSnapshotView._diffDataGrid;
315 * @override
316 * @return {boolean}
318 supportsSearch: function()
320 return true;
323 __proto__: WebInspector.HeapSnapshotView.Perspective.prototype
327 * @constructor
328 * @extends {WebInspector.HeapSnapshotView.Perspective}
330 WebInspector.HeapSnapshotView.ContainmentPerspective = function()
332 WebInspector.HeapSnapshotView.Perspective.call(this, WebInspector.UIString("Containment"));
335 WebInspector.HeapSnapshotView.ContainmentPerspective.prototype = {
337 * @override
338 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
340 activate: function(heapSnapshotView)
342 heapSnapshotView._splitWidget.setMainWidget(heapSnapshotView._containmentWidget);
343 heapSnapshotView._splitWidget.setSidebarWidget(heapSnapshotView._objectDetailsView);
344 heapSnapshotView._splitWidget.show(heapSnapshotView._searchableView.element);
348 * @override
349 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
350 * @return {?WebInspector.DataGrid}
352 masterGrid: function(heapSnapshotView)
354 return heapSnapshotView._containmentDataGrid;
356 __proto__: WebInspector.HeapSnapshotView.Perspective.prototype
360 * @constructor
361 * @extends {WebInspector.HeapSnapshotView.Perspective}
363 WebInspector.HeapSnapshotView.AllocationPerspective = function()
365 WebInspector.HeapSnapshotView.Perspective.call(this, WebInspector.UIString("Allocation"));
366 this._allocationSplitWidget = new WebInspector.SplitWidget(false, true, "heapSnapshotAllocationSplitViewState", 200, 200);
367 this._allocationSplitWidget.setSidebarWidget(new WebInspector.VBox());
371 WebInspector.HeapSnapshotView.AllocationPerspective.prototype = {
373 * @override
374 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
376 activate: function(heapSnapshotView)
378 this._allocationSplitWidget.setMainWidget(heapSnapshotView._allocationWidget);
379 heapSnapshotView._splitWidget.setMainWidget(heapSnapshotView._constructorsWidget);
380 heapSnapshotView._splitWidget.setSidebarWidget(heapSnapshotView._objectDetailsView);
382 var allocatedObjectsView = new WebInspector.VBox();
383 var resizer = createElementWithClass("div", "heap-snapshot-view-resizer");
384 var title = resizer.createChild("div", "title").createChild("span");
385 title.textContent = WebInspector.UIString("Live objects");
386 this._allocationSplitWidget.hideDefaultResizer();
387 this._allocationSplitWidget.installResizer(resizer);
388 allocatedObjectsView.element.appendChild(resizer);
389 heapSnapshotView._splitWidget.show(allocatedObjectsView.element);
390 this._allocationSplitWidget.setSidebarWidget(allocatedObjectsView);
392 this._allocationSplitWidget.show(heapSnapshotView._searchableView.element);
394 heapSnapshotView._constructorsDataGrid.clear();
395 var selectedNode = heapSnapshotView._allocationDataGrid.selectedNode;
396 if (selectedNode)
397 heapSnapshotView._constructorsDataGrid.setAllocationNodeId(selectedNode.allocationNodeId());
401 * @override
402 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
404 deactivate: function(heapSnapshotView)
406 this._allocationSplitWidget.detach();
407 WebInspector.HeapSnapshotView.Perspective.prototype.deactivate.call(this, heapSnapshotView);
411 * @override
412 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
413 * @return {?WebInspector.DataGrid}
415 masterGrid: function(heapSnapshotView)
417 return heapSnapshotView._allocationDataGrid;
420 __proto__: WebInspector.HeapSnapshotView.Perspective.prototype
424 * @constructor
425 * @extends {WebInspector.HeapSnapshotView.Perspective}
427 WebInspector.HeapSnapshotView.StatisticsPerspective = function()
429 WebInspector.HeapSnapshotView.Perspective.call(this, WebInspector.UIString("Statistics"));
432 WebInspector.HeapSnapshotView.StatisticsPerspective.prototype = {
434 * @override
435 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
437 activate: function(heapSnapshotView)
439 heapSnapshotView._statisticsView.show(heapSnapshotView._searchableView.element);
443 * @override
444 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
445 * @return {?WebInspector.DataGrid}
447 masterGrid: function(heapSnapshotView)
449 return null;
452 __proto__: WebInspector.HeapSnapshotView.Perspective.prototype
456 WebInspector.HeapSnapshotView.prototype = {
458 * @return {!WebInspector.SearchableView}
460 searchableView: function()
462 return this._searchableView;
466 * @override
467 * @param {?WebInspector.ProfileHeader} profile
468 * @return {?WebInspector.Widget}
470 showProfile: function(profile)
472 return this._parentDataDisplayDelegate.showProfile(profile);
476 * @override
477 * @param {!HeapProfilerAgent.HeapSnapshotObjectId} snapshotObjectId
478 * @param {string} perspectiveName
480 showObject: function(snapshotObjectId, perspectiveName)
482 if (snapshotObjectId <= this._profile.maxJSObjectId)
483 this.selectLiveObject(perspectiveName, snapshotObjectId);
484 else
485 this._parentDataDisplayDelegate.showObject(snapshotObjectId, perspectiveName);
488 _refreshView: function()
490 this._profile._loadPromise.then(profileCallback.bind(this));
494 * @param {!WebInspector.HeapSnapshotProxy} heapSnapshotProxy
495 * @this {WebInspector.HeapSnapshotView}
497 function profileCallback(heapSnapshotProxy)
499 heapSnapshotProxy.getStatistics().then(this._gotStatistics.bind(this));
500 if (this._profile.profileType().id === WebInspector.TrackingHeapSnapshotProfileType.TypeId && this._profile.fromFile())
501 heapSnapshotProxy.getSamples().then(didGetSamples.bind(this, heapSnapshotProxy));
502 else
503 setSnapshotProxy.call(this, heapSnapshotProxy);
507 * @param {!WebInspector.HeapSnapshotProxy} heapSnapshotProxy
508 * @param {?WebInspector.HeapSnapshotCommon.Samples} samples
509 * @this {WebInspector.HeapSnapshotView}
511 function didGetSamples(heapSnapshotProxy, samples)
513 setSnapshotProxy.call(this, heapSnapshotProxy);
514 this._trackingOverviewGrid._setSamples(samples);
518 * @param {!WebInspector.HeapSnapshotProxy} heapSnapshotProxy
519 * @this {WebInspector.HeapSnapshotView}
521 function setSnapshotProxy(heapSnapshotProxy)
523 var list = this._profiles();
524 var profileIndex = list.indexOf(this._profile);
525 this._baseSelect.setSelectedIndex(Math.max(0, profileIndex - 1));
526 this._dataGrid.setDataSource(heapSnapshotProxy);
532 * @param {!WebInspector.HeapSnapshotCommon.Statistics} statistics
534 _gotStatistics: function(statistics)
536 this._statisticsView.setTotal(statistics.total);
537 this._statisticsView.addRecord(statistics.code, WebInspector.UIString("Code"), "#f77");
538 this._statisticsView.addRecord(statistics.strings, WebInspector.UIString("Strings"), "#5e5");
539 this._statisticsView.addRecord(statistics.jsArrays, WebInspector.UIString("JS Arrays"), "#7af");
540 this._statisticsView.addRecord(statistics.native, WebInspector.UIString("Typed Arrays"), "#fc5");
541 this._statisticsView.addRecord(statistics.system, WebInspector.UIString("System Objects"), "#98f");
542 this._statisticsView.addRecord(statistics.total, WebInspector.UIString("Total"));
545 _onIdsRangeChanged: function(event)
547 var minId = event.data.minId;
548 var maxId = event.data.maxId;
549 this._selectedSizeText.setText(WebInspector.UIString("Selected size: %s", Number.bytesToString(event.data.size)));
550 if (this._constructorsDataGrid.snapshot)
551 this._constructorsDataGrid.setSelectionRange(minId, maxId);
555 * @return {!Array.<!WebInspector.ToolbarItem>}
557 toolbarItems: function()
559 var result = [this._perspectiveSelect, this._classNameFilter];
560 if (this._profile.profileType() !== WebInspector.ProfileTypeRegistry.instance.trackingHeapSnapshotProfileType)
561 result.push(this._baseSelect, this._filterSelect);
562 result.push(this._selectedSizeText);
563 return result;
566 wasShown: function()
568 this._profile._loadPromise.then(this._profile._wasShown.bind(this._profile));
571 willHide: function()
573 this._currentSearchResultIndex = -1;
574 this._popoverHelper.hidePopover();
575 if (this.helpPopover && this.helpPopover.isShowing())
576 this.helpPopover.hide();
580 * @override
581 * @return {boolean}
583 supportsCaseSensitiveSearch: function()
585 return true;
589 * @override
590 * @return {boolean}
592 supportsRegexSearch: function()
594 return false;
598 * @override
600 searchCanceled: function()
602 this._currentSearchResultIndex = -1;
603 this._searchResults = [];
607 * @param {?WebInspector.HeapSnapshotGridNode} node
609 _selectRevealedNode: function(node)
611 if (node)
612 node.select();
616 * @override
617 * @param {!WebInspector.SearchableView.SearchConfig} searchConfig
618 * @param {boolean} shouldJump
619 * @param {boolean=} jumpBackwards
621 performSearch: function(searchConfig, shouldJump, jumpBackwards)
623 var nextQuery = new WebInspector.HeapSnapshotCommon.SearchConfig(
624 searchConfig.query.trim(),
625 searchConfig.caseSensitive,
626 searchConfig.isRegex,
627 shouldJump,
628 jumpBackwards || false
631 this._searchThrottler.schedule(this._performSearch.bind(this, nextQuery));
635 * @param {!WebInspector.HeapSnapshotCommon.SearchConfig} nextQuery
636 * @return {!Promise<?>}
638 _performSearch: function(nextQuery)
640 // Call searchCanceled since it will reset everything we need before doing a new search.
641 this.searchCanceled();
643 if (!this._currentPerspective.supportsSearch())
644 return Promise.resolve();
646 this.currentQuery = nextQuery;
647 var query = nextQuery.query.trim();
649 if (!query)
650 return Promise.resolve();
652 if (query.charAt(0) === "@") {
653 var snapshotNodeId = parseInt(query.substring(1), 10);
654 if (isNaN(snapshotNodeId))
655 return Promise.resolve();
656 return this._dataGrid.revealObjectByHeapSnapshotId(String(snapshotNodeId)).then(this._selectRevealedNode.bind(this));
660 * @param {!Array<number>} entryIds
661 * @return {!Promise<?>}
662 * @this {WebInspector.HeapSnapshotView}
664 function didSearch(entryIds)
666 this._searchResults = entryIds;
667 this._searchableView.updateSearchMatchesCount(this._searchResults.length);
668 if (this._searchResults.length)
669 this._currentSearchResultIndex = nextQuery.jumpBackwards ? this._searchResults.length - 1 : 0;
670 return this._jumpToSearchResult(this._currentSearchResultIndex);
673 return this._profile._snapshotProxy.search(this.currentQuery, this._dataGrid.nodeFilter()).then(didSearch.bind(this));
677 * @override
679 jumpToNextSearchResult: function()
681 if (!this._searchResults.length)
682 return;
683 this._currentSearchResultIndex = (this._currentSearchResultIndex + 1) % this._searchResults.length;
684 this._searchThrottler.schedule(this._jumpToSearchResult.bind(this, this._currentSearchResultIndex));
688 * @override
690 jumpToPreviousSearchResult: function()
692 if (!this._searchResults.length)
693 return;
694 this._currentSearchResultIndex = (this._currentSearchResultIndex + this._searchResults.length - 1) % this._searchResults.length;
695 this._searchThrottler.schedule(this._jumpToSearchResult.bind(this, this._currentSearchResultIndex));
699 * @param {number} searchResultIndex
700 * @return {!Promise<undefined>}
702 _jumpToSearchResult: function(searchResultIndex)
704 this._searchableView.updateCurrentMatchIndex(searchResultIndex);
705 return this._dataGrid.revealObjectByHeapSnapshotId(String(this._searchResults[searchResultIndex])).then(this._selectRevealedNode.bind(this));
708 refreshVisibleData: function()
710 if (!this._dataGrid)
711 return;
712 var child = this._dataGrid.rootNode().children[0];
713 while (child) {
714 child.refresh();
715 child = child.traverseNextNode(false, null, true);
719 _changeBase: function()
721 if (this._baseProfile === this._profiles()[this._baseSelect.selectedIndex()])
722 return;
724 this._baseProfile = this._profiles()[this._baseSelect.selectedIndex()];
725 var dataGrid = /** @type {!WebInspector.HeapSnapshotDiffDataGrid} */ (this._dataGrid);
726 // Change set base data source only if main data source is already set.
727 if (dataGrid.snapshot)
728 this._baseProfile._loadPromise.then(dataGrid.setBaseDataSource.bind(dataGrid));
730 if (!this.currentQuery || !this._searchResults)
731 return;
733 // The current search needs to be performed again. First negate out previous match
734 // count by calling the search finished callback with a negative number of matches.
735 // Then perform the search again with the same query and callback.
736 this.performSearch(this.currentQuery, false);
739 _changeFilter: function()
741 var profileIndex = this._filterSelect.selectedIndex() - 1;
742 this._dataGrid.filterSelectIndexChanged(this._profiles(), profileIndex);
744 if (!this.currentQuery || !this._searchResults)
745 return;
747 // The current search needs to be performed again. First negate out previous match
748 // count by calling the search finished callback with a negative number of matches.
749 // Then perform the search again with the same query and callback.
750 this.performSearch(this.currentQuery, false);
754 * @return {!Array.<!WebInspector.ProfileHeader>}
756 _profiles: function()
758 return this._profile.profileType().getProfiles();
762 * @param {!WebInspector.ContextMenu} contextMenu
763 * @param {!Event} event
765 populateContextMenu: function(contextMenu, event)
767 if (this._dataGrid)
768 this._dataGrid.populateContextMenu(contextMenu, event);
771 _selectionChanged: function(event)
773 var selectedNode = event.target.selectedNode;
774 this._setSelectedNodeForDetailsView(selectedNode);
775 this._inspectedObjectChanged(event);
778 _onSelectAllocationNode: function(event)
780 var selectedNode = event.target.selectedNode;
781 this._constructorsDataGrid.setAllocationNodeId(selectedNode.allocationNodeId());
782 this._setSelectedNodeForDetailsView(null);
785 _inspectedObjectChanged: function(event)
787 var selectedNode = event.target.selectedNode;
788 var target = this._profile.target();
789 if (target && selectedNode instanceof WebInspector.HeapSnapshotGenericObjectNode)
790 target.heapProfilerAgent().addInspectedHeapObject(String(selectedNode.snapshotNodeId));
794 * @param {?WebInspector.HeapSnapshotGridNode} nodeItem
796 _setSelectedNodeForDetailsView: function(nodeItem)
798 var dataSource = nodeItem && nodeItem.retainersDataSource();
799 if (dataSource) {
800 this._retainmentDataGrid.setDataSource(dataSource.snapshot, dataSource.snapshotNodeIndex);
801 if (this._allocationStackView)
802 this._allocationStackView.setAllocatedObject(dataSource.snapshot, dataSource.snapshotNodeIndex);
803 } else {
804 if (this._allocationStackView)
805 this._allocationStackView.clear();
806 this._retainmentDataGrid.reset();
811 * @param {string} perspectiveTitle
812 * @param {function()} callback
814 _changePerspectiveAndWait: function(perspectiveTitle, callback)
816 var perspectiveIndex = null;
817 for (var i = 0; i < this._perspectives.length; ++i) {
818 if (this._perspectives[i].title() === perspectiveTitle) {
819 perspectiveIndex = i;
820 break;
823 if (this._currentPerspectiveIndex === perspectiveIndex || perspectiveIndex === null) {
824 setTimeout(callback, 0);
825 return;
829 * @this {WebInspector.HeapSnapshotView}
831 function dataGridContentShown(event)
833 var dataGrid = event.data;
834 dataGrid.removeEventListener(WebInspector.HeapSnapshotSortableDataGrid.Events.ContentShown, dataGridContentShown, this);
835 if (dataGrid === this._dataGrid)
836 callback();
838 this._perspectives[perspectiveIndex].masterGrid(this).addEventListener(WebInspector.HeapSnapshotSortableDataGrid.Events.ContentShown, dataGridContentShown, this);
840 this._perspectiveSelect.setSelectedIndex(perspectiveIndex);
841 this._changePerspective(perspectiveIndex);
844 _updateDataSourceAndView: function()
846 var dataGrid = this._dataGrid;
847 if (!dataGrid || dataGrid.snapshot)
848 return;
850 this._profile._loadPromise.then(didLoadSnapshot.bind(this));
853 * @this {WebInspector.HeapSnapshotView}
855 function didLoadSnapshot(snapshotProxy)
857 if (this._dataGrid !== dataGrid)
858 return;
859 if (dataGrid.snapshot !== snapshotProxy)
860 dataGrid.setDataSource(snapshotProxy);
861 if (dataGrid === this._diffDataGrid) {
862 if (!this._baseProfile)
863 this._baseProfile = this._profiles()[this._baseSelect.selectedIndex()];
864 this._baseProfile._loadPromise.then(didLoadBaseSnaphot.bind(this));
869 * @this {WebInspector.HeapSnapshotView}
871 function didLoadBaseSnaphot(baseSnapshotProxy)
873 if (this._diffDataGrid.baseSnapshot !== baseSnapshotProxy)
874 this._diffDataGrid.setBaseDataSource(baseSnapshotProxy);
878 _onSelectedPerspectiveChanged: function(event)
880 this._changePerspective(event.target.selectedIndex);
884 * @param {number} selectedIndex
886 _changePerspective: function(selectedIndex)
888 if (selectedIndex === this._currentPerspectiveIndex)
889 return;
891 this._currentPerspectiveIndex = selectedIndex;
893 this._currentPerspective.deactivate(this);
894 var perspective = this._perspectives[selectedIndex];
895 this._currentPerspective = perspective;
896 this._dataGrid = perspective.masterGrid(this);
897 perspective.activate(this);
899 this.refreshVisibleData();
900 if (this._dataGrid)
901 this._dataGrid.updateWidths();
903 this._updateDataSourceAndView();
905 if (!this.currentQuery || !this._searchResults)
906 return;
908 // The current search needs to be performed again. First negate out previous match
909 // count by calling the search finished callback with a negative number of matches.
910 // Then perform the search again the with same query and callback.
911 this.performSearch(this.currentQuery, false);
915 * @param {string} perspectiveName
916 * @param {!HeapProfilerAgent.HeapSnapshotObjectId} snapshotObjectId
918 selectLiveObject: function(perspectiveName, snapshotObjectId)
920 this._changePerspectiveAndWait(perspectiveName, didChangePerspective.bind(this));
923 * @this {WebInspector.HeapSnapshotView}
925 function didChangePerspective()
927 this._dataGrid.revealObjectByHeapSnapshotId(snapshotObjectId, didRevealObject);
931 * @param {?WebInspector.HeapSnapshotGridNode} node
933 function didRevealObject(node)
935 if (node)
936 node.select();
937 else
938 WebInspector.console.error("Cannot find corresponding heap snapshot node");
942 _getHoverAnchor: function(target)
944 var span = target.enclosingNodeOrSelfWithNodeName("span");
945 if (!span)
946 return;
947 var row = target.enclosingNodeOrSelfWithNodeName("tr");
948 if (!row)
949 return;
950 span.node = row._dataGridNode;
951 return span;
954 _resolveObjectForPopover: function(element, showCallback, objectGroupName)
956 if (!this._profile.target())
957 return;
958 if (!element.node)
959 return;
960 element.node.queryObjectContent(this._profile.target(), showCallback, objectGroupName);
963 _updateBaseOptions: function()
965 var list = this._profiles();
966 // We're assuming that snapshots can only be added.
967 if (this._baseSelect.size() === list.length)
968 return;
970 for (var i = this._baseSelect.size(), n = list.length; i < n; ++i) {
971 var title = list[i].title;
972 this._baseSelect.createOption(title);
976 _updateFilterOptions: function()
978 var list = this._profiles();
979 // We're assuming that snapshots can only be added.
980 if (this._filterSelect.size() - 1 === list.length)
981 return;
983 if (!this._filterSelect.size())
984 this._filterSelect.createOption(WebInspector.UIString("All objects"));
986 for (var i = this._filterSelect.size() - 1, n = list.length; i < n; ++i) {
987 var title = list[i].title;
988 if (!i)
989 title = WebInspector.UIString("Objects allocated before %s", title);
990 else
991 title = WebInspector.UIString("Objects allocated between %s and %s", list[i - 1].title, title);
992 this._filterSelect.createOption(title);
996 _updateControls: function()
998 this._updateBaseOptions();
999 this._updateFilterOptions();
1003 * @param {!WebInspector.Event} event
1005 _onReceiveSnapshot: function(event)
1007 this._updateControls();
1011 * @param {!WebInspector.Event} event
1013 _onProfileHeaderRemoved: function(event)
1015 var profile = event.data;
1016 if (this._profile === profile) {
1017 this.detach();
1018 this._profile.profileType().removeEventListener(WebInspector.HeapSnapshotProfileType.SnapshotReceived, this._onReceiveSnapshot, this);
1019 this._profile.profileType().removeEventListener(WebInspector.ProfileType.Events.RemoveProfileHeader, this._onProfileHeaderRemoved, this);
1020 this.dispose();
1021 } else {
1022 this._updateControls();
1026 dispose: function()
1028 if (this._allocationStackView) {
1029 this._allocationStackView.clear();
1030 this._allocationDataGrid.dispose();
1032 if (this._trackingOverviewGrid)
1033 this._trackingOverviewGrid.dispose();
1036 __proto__: WebInspector.VBox.prototype
1040 * @constructor
1041 * @extends {WebInspector.ProfileType}
1042 * @implements {WebInspector.TargetManager.Observer}
1043 * @param {string=} id
1044 * @param {string=} title
1046 WebInspector.HeapSnapshotProfileType = function(id, title)
1048 WebInspector.ProfileType.call(this, id || WebInspector.HeapSnapshotProfileType.TypeId, title || WebInspector.UIString("Take Heap Snapshot"));
1049 WebInspector.targetManager.observeTargets(this);
1050 WebInspector.targetManager.addModelListener(WebInspector.HeapProfilerModel, WebInspector.HeapProfilerModel.Events.ResetProfiles, this._resetProfiles, this);
1051 WebInspector.targetManager.addModelListener(WebInspector.HeapProfilerModel, WebInspector.HeapProfilerModel.Events.AddHeapSnapshotChunk, this._addHeapSnapshotChunk, this);
1052 WebInspector.targetManager.addModelListener(WebInspector.HeapProfilerModel, WebInspector.HeapProfilerModel.Events.ReportHeapSnapshotProgress, this._reportHeapSnapshotProgress, this);
1055 WebInspector.HeapSnapshotProfileType.TypeId = "HEAP";
1056 WebInspector.HeapSnapshotProfileType.SnapshotReceived = "SnapshotReceived";
1058 WebInspector.HeapSnapshotProfileType.prototype = {
1060 * @override
1061 * @param {!WebInspector.Target} target
1063 targetAdded: function(target)
1065 target.heapProfilerModel.enable();
1069 * @override
1070 * @param {!WebInspector.Target} target
1072 targetRemoved: function(target)
1077 * @override
1078 * @return {string}
1080 fileExtension: function()
1082 return ".heapsnapshot";
1085 get buttonTooltip()
1087 return WebInspector.UIString("Take heap snapshot.");
1091 * @override
1092 * @return {boolean}
1094 isInstantProfile: function()
1096 return true;
1100 * @override
1101 * @return {boolean}
1103 buttonClicked: function()
1105 this._takeHeapSnapshot(function() {});
1106 WebInspector.userMetrics.ProfilesHeapProfileTaken.record();
1107 return false;
1110 get treeItemTitle()
1112 return WebInspector.UIString("HEAP SNAPSHOTS");
1115 get description()
1117 return WebInspector.UIString("Heap snapshot profiles show memory distribution among your page's JavaScript objects and related DOM nodes.");
1121 * @override
1122 * @param {string} title
1123 * @return {!WebInspector.ProfileHeader}
1125 createProfileLoadedFromFile: function(title)
1127 return new WebInspector.HeapProfileHeader(null, this, title);
1130 _takeHeapSnapshot: function(callback)
1132 if (this.profileBeingRecorded())
1133 return;
1134 var target = /** @type {!WebInspector.Target} */ (WebInspector.context.flavor(WebInspector.Target));
1135 var profile = new WebInspector.HeapProfileHeader(target, this);
1136 this.setProfileBeingRecorded(profile);
1137 this.addProfile(profile);
1138 profile.updateStatus(WebInspector.UIString("Snapshotting\u2026"));
1141 * @param {?string} error
1142 * @this {WebInspector.HeapSnapshotProfileType}
1144 function didTakeHeapSnapshot(error)
1146 var profile = this._profileBeingRecorded;
1147 profile.title = WebInspector.UIString("Snapshot %d", profile.uid);
1148 profile._finishLoad();
1149 this.setProfileBeingRecorded(null);
1150 this.dispatchEventToListeners(WebInspector.ProfileType.Events.ProfileComplete, profile);
1151 callback();
1153 target.heapProfilerAgent().takeHeapSnapshot(true, didTakeHeapSnapshot.bind(this));
1157 * @param {!WebInspector.Event} event
1159 _addHeapSnapshotChunk: function(event)
1161 if (!this.profileBeingRecorded())
1162 return;
1163 var chunk = /** @type {string} */(event.data);
1164 this.profileBeingRecorded().transferChunk(chunk);
1168 * @param {!WebInspector.Event} event
1170 _reportHeapSnapshotProgress: function(event)
1172 var profile = this.profileBeingRecorded();
1173 if (!profile)
1174 return;
1175 var data = /** @type {{done: number, total: number, finished: boolean}} */ (event.data);
1176 profile.updateStatus(WebInspector.UIString("%.0f%%", (data.done / data.total) * 100), true);
1177 if (data.finished)
1178 profile._prepareToLoad();
1181 _resetProfiles: function()
1183 this._reset();
1186 _snapshotReceived: function(profile)
1188 if (this._profileBeingRecorded === profile)
1189 this.setProfileBeingRecorded(null);
1190 this.dispatchEventToListeners(WebInspector.HeapSnapshotProfileType.SnapshotReceived, profile);
1193 __proto__: WebInspector.ProfileType.prototype
1198 * @constructor
1199 * @extends {WebInspector.HeapSnapshotProfileType}
1201 WebInspector.TrackingHeapSnapshotProfileType = function()
1203 WebInspector.HeapSnapshotProfileType.call(this, WebInspector.TrackingHeapSnapshotProfileType.TypeId, WebInspector.UIString("Record Heap Allocations"));
1206 WebInspector.TrackingHeapSnapshotProfileType.TypeId = "HEAP-RECORD";
1208 WebInspector.TrackingHeapSnapshotProfileType.HeapStatsUpdate = "HeapStatsUpdate";
1209 WebInspector.TrackingHeapSnapshotProfileType.TrackingStarted = "TrackingStarted";
1210 WebInspector.TrackingHeapSnapshotProfileType.TrackingStopped = "TrackingStopped";
1214 * @constructor
1216 WebInspector.TrackingHeapSnapshotProfileType.Samples = function()
1218 /** @type {!Array.<number>} */
1219 this.sizes = [];
1220 /** @type {!Array.<number>} */
1221 this.ids = [];
1222 /** @type {!Array.<number>} */
1223 this.timestamps = [];
1224 /** @type {!Array.<number>} */
1225 this.max = [];
1226 /** @type {number} */
1227 this.totalTime = 30000;
1230 WebInspector.TrackingHeapSnapshotProfileType.prototype = {
1233 * @override
1234 * @param {!WebInspector.Target} target
1236 targetAdded: function(target)
1238 WebInspector.HeapSnapshotProfileType.prototype.targetAdded.call(this, target);
1239 target.heapProfilerModel.addEventListener(WebInspector.HeapProfilerModel.Events.HeapStatsUpdate, this._heapStatsUpdate, this);
1240 target.heapProfilerModel.addEventListener(WebInspector.HeapProfilerModel.Events.LastSeenObjectId, this._lastSeenObjectId, this);
1244 * @override
1245 * @param {!WebInspector.Target} target
1247 targetRemoved: function(target)
1249 WebInspector.HeapSnapshotProfileType.prototype.targetRemoved.call(this, target);
1250 target.heapProfilerModel.removeEventListener(WebInspector.HeapProfilerModel.Events.HeapStatsUpdate, this._heapStatsUpdate, this);
1251 target.heapProfilerModel.removeEventListener(WebInspector.HeapProfilerModel.Events.LastSeenObjectId, this._lastSeenObjectId, this);
1255 * @param {!WebInspector.Event} event
1257 _heapStatsUpdate: function(event)
1259 if (!this._profileSamples)
1260 return;
1261 var samples = /** @type {!Array.<number>} */ (event.data);
1262 var index;
1263 for (var i = 0; i < samples.length; i += 3) {
1264 index = samples[i];
1265 var size = samples[i+2];
1266 this._profileSamples.sizes[index] = size;
1267 if (!this._profileSamples.max[index])
1268 this._profileSamples.max[index] = size;
1273 * @param {!WebInspector.Event} event
1275 _lastSeenObjectId: function(event)
1277 var profileSamples = this._profileSamples;
1278 if (!profileSamples)
1279 return;
1280 var data = /** @type {{lastSeenObjectId: number, timestamp: number}} */ (event.data);
1281 var currentIndex = Math.max(profileSamples.ids.length, profileSamples.max.length - 1);
1282 profileSamples.ids[currentIndex] = data.lastSeenObjectId;
1283 if (!profileSamples.max[currentIndex]) {
1284 profileSamples.max[currentIndex] = 0;
1285 profileSamples.sizes[currentIndex] = 0;
1287 profileSamples.timestamps[currentIndex] = data.timestamp;
1288 if (profileSamples.totalTime < data.timestamp - profileSamples.timestamps[0])
1289 profileSamples.totalTime *= 2;
1290 this.dispatchEventToListeners(WebInspector.TrackingHeapSnapshotProfileType.HeapStatsUpdate, this._profileSamples);
1291 this._profileBeingRecorded.updateStatus(null, true);
1295 * @override
1296 * @return {boolean}
1298 hasTemporaryView: function()
1300 return true;
1303 get buttonTooltip()
1305 return this._recording ? WebInspector.UIString("Stop recording heap profile.") : WebInspector.UIString("Start recording heap profile.");
1309 * @override
1310 * @return {boolean}
1312 isInstantProfile: function()
1314 return false;
1318 * @override
1319 * @return {boolean}
1321 buttonClicked: function()
1323 return this._toggleRecording();
1326 _startRecordingProfile: function()
1328 if (this.profileBeingRecorded())
1329 return;
1330 this._addNewProfile();
1331 var recordAllocationStacks = WebInspector.moduleSetting("recordAllocationStacks").get();
1332 this.profileBeingRecorded().target().heapProfilerAgent().startTrackingHeapObjects(recordAllocationStacks);
1335 _addNewProfile: function()
1337 var target = WebInspector.context.flavor(WebInspector.Target);
1338 this.setProfileBeingRecorded(new WebInspector.HeapProfileHeader(target, this, undefined));
1339 this._profileSamples = new WebInspector.TrackingHeapSnapshotProfileType.Samples();
1340 this._profileBeingRecorded._profileSamples = this._profileSamples;
1341 this._recording = true;
1342 this.addProfile(this._profileBeingRecorded);
1343 this._profileBeingRecorded.updateStatus(WebInspector.UIString("Recording\u2026"));
1344 this.dispatchEventToListeners(WebInspector.TrackingHeapSnapshotProfileType.TrackingStarted);
1347 _stopRecordingProfile: function()
1349 this._profileBeingRecorded.updateStatus(WebInspector.UIString("Snapshotting\u2026"));
1351 * @param {?string} error
1352 * @this {WebInspector.HeapSnapshotProfileType}
1354 function didTakeHeapSnapshot(error)
1356 var profile = this.profileBeingRecorded();
1357 if (!profile)
1358 return;
1359 profile._finishLoad();
1360 this._profileSamples = null;
1361 this.setProfileBeingRecorded(null);
1362 this.dispatchEventToListeners(WebInspector.ProfileType.Events.ProfileComplete, profile);
1365 this._profileBeingRecorded.target().heapProfilerAgent().stopTrackingHeapObjects(true, didTakeHeapSnapshot.bind(this));
1366 this._recording = false;
1367 this.dispatchEventToListeners(WebInspector.TrackingHeapSnapshotProfileType.TrackingStopped);
1370 _toggleRecording: function()
1372 if (this._recording)
1373 this._stopRecordingProfile();
1374 else
1375 this._startRecordingProfile();
1376 return this._recording;
1380 * @override
1381 * @return {string}
1383 fileExtension: function()
1385 return ".heaptimeline";
1388 get treeItemTitle()
1390 return WebInspector.UIString("HEAP TIMELINES");
1393 get description()
1395 return WebInspector.UIString("Record JavaScript object allocations over time. Use this profile type to isolate memory leaks.");
1399 * @override
1401 _resetProfiles: function()
1403 var wasRecording = this._recording;
1404 // Clear current profile to avoid stopping backend.
1405 this.setProfileBeingRecorded(null);
1406 WebInspector.HeapSnapshotProfileType.prototype._resetProfiles.call(this);
1407 this._profileSamples = null;
1408 if (wasRecording)
1409 this._addNewProfile();
1413 * @override
1415 profileBeingRecordedRemoved: function()
1417 this._stopRecordingProfile();
1418 this._profileSamples = null;
1421 __proto__: WebInspector.HeapSnapshotProfileType.prototype
1425 * @constructor
1426 * @extends {WebInspector.ProfileHeader}
1427 * @param {?WebInspector.Target} target
1428 * @param {!WebInspector.HeapSnapshotProfileType} type
1429 * @param {string=} title
1431 WebInspector.HeapProfileHeader = function(target, type, title)
1433 WebInspector.ProfileHeader.call(this, target, type, title || WebInspector.UIString("Snapshot %d", type.nextProfileUid()));
1434 this.maxJSObjectId = -1;
1436 * @type {?WebInspector.HeapSnapshotWorkerProxy}
1438 this._workerProxy = null;
1440 * @type {?WebInspector.OutputStream}
1442 this._receiver = null;
1444 * @type {?WebInspector.HeapSnapshotProxy}
1446 this._snapshotProxy = null;
1448 * @type {!Promise.<!WebInspector.HeapSnapshotProxy>}
1450 this._loadPromise = new Promise(loadResolver.bind(this));
1451 this._totalNumberOfChunks = 0;
1452 this._bufferedWriter = null;
1455 * @param {function(!WebInspector.HeapSnapshotProxy)} fulfill
1456 * @this {WebInspector.HeapProfileHeader}
1458 function loadResolver(fulfill)
1460 this._fulfillLoad = fulfill;
1464 WebInspector.HeapProfileHeader.prototype = {
1466 * @override
1467 * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
1468 * @return {!WebInspector.ProfileSidebarTreeElement}
1470 createSidebarTreeElement: function(dataDisplayDelegate)
1472 return new WebInspector.ProfileSidebarTreeElement(dataDisplayDelegate, this, "heap-snapshot-sidebar-tree-item");
1476 * @override
1477 * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
1478 * @return {!WebInspector.HeapSnapshotView}
1480 createView: function(dataDisplayDelegate)
1482 return new WebInspector.HeapSnapshotView(dataDisplayDelegate, this);
1485 _prepareToLoad: function()
1487 console.assert(!this._receiver, "Already loading");
1488 this._setupWorker();
1489 this.updateStatus(WebInspector.UIString("Loading\u2026"), true);
1492 _finishLoad: function()
1494 if (!this._wasDisposed)
1495 this._receiver.close();
1496 if (this._bufferedWriter) {
1497 this._bufferedWriter.finishWriting(this._didWriteToTempFile.bind(this));
1498 this._bufferedWriter = null;
1502 _didWriteToTempFile: function(tempFile)
1504 if (this._wasDisposed) {
1505 if (tempFile)
1506 tempFile.remove();
1507 return;
1509 this._tempFile = tempFile;
1510 if (!tempFile)
1511 this._failedToCreateTempFile = true;
1512 if (this._onTempFileReady) {
1513 this._onTempFileReady();
1514 this._onTempFileReady = null;
1518 _setupWorker: function()
1521 * @this {WebInspector.HeapProfileHeader}
1523 function setProfileWait(event)
1525 this.updateStatus(null, event.data);
1527 console.assert(!this._workerProxy, "HeapSnapshotWorkerProxy already exists");
1528 this._workerProxy = new WebInspector.HeapSnapshotWorkerProxy(this._handleWorkerEvent.bind(this));
1529 this._workerProxy.addEventListener("wait", setProfileWait, this);
1530 this._receiver = this._workerProxy.createLoader(this.uid, this._snapshotReceived.bind(this));
1534 * @param {string} eventName
1535 * @param {*} data
1537 _handleWorkerEvent: function(eventName, data)
1539 if (WebInspector.HeapSnapshotProgressEvent.BrokenSnapshot === eventName) {
1540 var error = /** @type {string} */ (data);
1541 WebInspector.console.error(error);
1542 return;
1545 if (WebInspector.HeapSnapshotProgressEvent.Update !== eventName)
1546 return;
1547 var subtitle = /** @type {string} */ (data);
1548 this.updateStatus(subtitle);
1552 * @override
1554 dispose: function()
1556 if (this._workerProxy)
1557 this._workerProxy.dispose();
1558 this.removeTempFile();
1559 this._wasDisposed = true;
1562 _didCompleteSnapshotTransfer: function()
1564 if (!this._snapshotProxy)
1565 return;
1566 this.updateStatus(Number.bytesToString(this._snapshotProxy.totalSize), false);
1570 * @param {string} chunk
1572 transferChunk: function(chunk)
1574 if (!this._bufferedWriter)
1575 this._bufferedWriter = new WebInspector.DeferredTempFile("heap-profiler", String(this.uid));
1576 this._bufferedWriter.write([chunk]);
1578 ++this._totalNumberOfChunks;
1579 this._receiver.write(chunk);
1582 _snapshotReceived: function(snapshotProxy)
1584 if (this._wasDisposed)
1585 return;
1586 this._receiver = null;
1587 this._snapshotProxy = snapshotProxy;
1588 this.maxJSObjectId = snapshotProxy.maxJSObjectId();
1589 this._didCompleteSnapshotTransfer();
1590 this._workerProxy.startCheckingForLongRunningCalls();
1591 this.notifySnapshotReceived();
1594 notifySnapshotReceived: function()
1596 this._fulfillLoad(this._snapshotProxy);
1597 this._profileType._snapshotReceived(this);
1598 if (this.canSaveToFile())
1599 this.dispatchEventToListeners(WebInspector.ProfileHeader.Events.ProfileReceived);
1602 // Hook point for tests.
1603 _wasShown: function()
1608 * @override
1609 * @return {boolean}
1611 canSaveToFile: function()
1613 return !this.fromFile() && !!this._snapshotProxy;
1617 * @override
1619 saveToFile: function()
1621 var fileOutputStream = new WebInspector.FileOutputStream();
1624 * @param {boolean} accepted
1625 * @this {WebInspector.HeapProfileHeader}
1627 function onOpen(accepted)
1629 if (!accepted)
1630 return;
1631 if (this._failedToCreateTempFile) {
1632 WebInspector.console.error("Failed to open temp file with heap snapshot");
1633 fileOutputStream.close();
1634 } else if (this._tempFile) {
1635 var delegate = new WebInspector.SaveSnapshotOutputStreamDelegate(this);
1636 this._tempFile.writeToOutputSteam(fileOutputStream, delegate);
1637 } else {
1638 this._onTempFileReady = onOpen.bind(this, accepted);
1639 this._updateSaveProgress(0, 1);
1642 this._fileName = this._fileName || "Heap-" + new Date().toISO8601Compact() + this._profileType.fileExtension();
1643 fileOutputStream.open(this._fileName, onOpen.bind(this));
1646 _updateSaveProgress: function(value, total)
1648 var percentValue = ((total ? (value / total) : 0) * 100).toFixed(0);
1649 this.updateStatus(WebInspector.UIString("Saving\u2026 %d%%", percentValue));
1653 * @override
1654 * @param {!File} file
1656 loadFromFile: function(file)
1658 this.updateStatus(WebInspector.UIString("Loading\u2026"), true);
1659 this._setupWorker();
1660 var delegate = new WebInspector.HeapSnapshotLoadFromFileDelegate(this);
1661 var fileReader = this._createFileReader(file, delegate);
1662 fileReader.start(this._receiver);
1665 _createFileReader: function(file, delegate)
1667 return new WebInspector.ChunkedFileReader(file, 10000000, delegate);
1670 __proto__: WebInspector.ProfileHeader.prototype
1674 * @constructor
1675 * @implements {WebInspector.OutputStreamDelegate}
1677 WebInspector.HeapSnapshotLoadFromFileDelegate = function(snapshotHeader)
1679 this._snapshotHeader = snapshotHeader;
1682 WebInspector.HeapSnapshotLoadFromFileDelegate.prototype = {
1684 * @override
1686 onTransferStarted: function()
1691 * @override
1692 * @param {!WebInspector.ChunkedReader} reader
1694 onChunkTransferred: function(reader)
1699 * @override
1701 onTransferFinished: function()
1706 * @override
1707 * @param {!WebInspector.ChunkedReader} reader
1708 * @param {!Event} e
1710 onError: function(reader, e)
1712 var subtitle;
1713 switch(e.target.error.code) {
1714 case e.target.error.NOT_FOUND_ERR:
1715 subtitle = WebInspector.UIString("'%s' not found.", reader.fileName());
1716 break;
1717 case e.target.error.NOT_READABLE_ERR:
1718 subtitle = WebInspector.UIString("'%s' is not readable", reader.fileName());
1719 break;
1720 case e.target.error.ABORT_ERR:
1721 return;
1722 default:
1723 subtitle = WebInspector.UIString("'%s' error %d", reader.fileName(), e.target.error.code);
1725 this._snapshotHeader.updateStatus(subtitle);
1730 * @constructor
1731 * @implements {WebInspector.OutputStreamDelegate}
1732 * @param {!WebInspector.HeapProfileHeader} profileHeader
1734 WebInspector.SaveSnapshotOutputStreamDelegate = function(profileHeader)
1736 this._profileHeader = profileHeader;
1739 WebInspector.SaveSnapshotOutputStreamDelegate.prototype = {
1741 * @override
1743 onTransferStarted: function()
1745 this._profileHeader._updateSaveProgress(0, 1);
1749 * @override
1751 onTransferFinished: function()
1753 this._profileHeader._didCompleteSnapshotTransfer();
1757 * @override
1758 * @param {!WebInspector.ChunkedReader} reader
1760 onChunkTransferred: function(reader)
1762 this._profileHeader._updateSaveProgress(reader.loadedSize(), reader.fileSize());
1766 * @override
1767 * @param {!WebInspector.ChunkedReader} reader
1768 * @param {!Event} event
1770 onError: function(reader, event)
1772 WebInspector.console.error("Failed to read heap snapshot from temp file: " + /** @type {!ErrorEvent} */ (event).message);
1773 this.onTransferFinished();
1778 * @constructor
1779 * @extends {WebInspector.VBox}
1780 * @param {!WebInspector.HeapProfileHeader} heapProfileHeader
1782 WebInspector.HeapTrackingOverviewGrid = function(heapProfileHeader)
1784 WebInspector.VBox.call(this);
1785 this.element.id = "heap-recording-view";
1786 this.element.classList.add("heap-tracking-overview");
1788 this._overviewContainer = this.element.createChild("div", "heap-overview-container");
1789 this._overviewGrid = new WebInspector.OverviewGrid("heap-recording");
1790 this._overviewGrid.element.classList.add("fill");
1792 this._overviewCanvas = this._overviewContainer.createChild("canvas", "heap-recording-overview-canvas");
1793 this._overviewContainer.appendChild(this._overviewGrid.element);
1794 this._overviewCalculator = new WebInspector.HeapTrackingOverviewGrid.OverviewCalculator();
1795 this._overviewGrid.addEventListener(WebInspector.OverviewGrid.Events.WindowChanged, this._onWindowChanged, this);
1797 this._profileSamples = heapProfileHeader.fromFile() ? new WebInspector.TrackingHeapSnapshotProfileType.Samples() : heapProfileHeader._profileSamples;
1798 this._profileType = heapProfileHeader.profileType();
1799 if (!heapProfileHeader.fromFile() && heapProfileHeader.profileType().profileBeingRecorded() === heapProfileHeader) {
1800 this._profileType.addEventListener(WebInspector.TrackingHeapSnapshotProfileType.HeapStatsUpdate, this._onHeapStatsUpdate, this);
1801 this._profileType.addEventListener(WebInspector.TrackingHeapSnapshotProfileType.TrackingStopped, this._onStopTracking, this);
1803 this._windowLeft = 0.0;
1804 this._windowRight = 1.0;
1805 this._overviewGrid.setWindow(this._windowLeft, this._windowRight);
1806 this._yScale = new WebInspector.HeapTrackingOverviewGrid.SmoothScale();
1807 this._xScale = new WebInspector.HeapTrackingOverviewGrid.SmoothScale();
1810 WebInspector.HeapTrackingOverviewGrid.IdsRangeChanged = "IdsRangeChanged";
1812 WebInspector.HeapTrackingOverviewGrid.prototype = {
1813 dispose: function()
1815 this._onStopTracking();
1818 _onStopTracking: function()
1820 this._profileType.removeEventListener(WebInspector.TrackingHeapSnapshotProfileType.HeapStatsUpdate, this._onHeapStatsUpdate, this);
1821 this._profileType.removeEventListener(WebInspector.TrackingHeapSnapshotProfileType.TrackingStopped, this._onStopTracking, this);
1824 _onHeapStatsUpdate: function(event)
1826 this._profileSamples = event.data;
1827 this._scheduleUpdate();
1831 * @param {?WebInspector.HeapSnapshotCommon.Samples} samples
1833 _setSamples: function(samples)
1835 if (!samples)
1836 return;
1837 console.assert(!this._profileSamples.timestamps.length, "Should only call this method when loading from file.");
1838 console.assert(samples.timestamps.length);
1839 this._profileSamples = new WebInspector.TrackingHeapSnapshotProfileType.Samples();
1840 this._profileSamples.sizes = samples.sizes;
1841 this._profileSamples.ids = samples.lastAssignedIds;
1842 this._profileSamples.timestamps = samples.timestamps;
1843 this._profileSamples.max = samples.sizes;
1844 this._profileSamples.totalTime = /** @type{number} */(samples.timestamps.peekLast());
1845 this.update();
1849 * @param {number} width
1850 * @param {number} height
1852 _drawOverviewCanvas: function(width, height)
1854 if (!this._profileSamples)
1855 return;
1856 var profileSamples = this._profileSamples;
1857 var sizes = profileSamples.sizes;
1858 var topSizes = profileSamples.max;
1859 var timestamps = profileSamples.timestamps;
1860 var startTime = timestamps[0];
1861 var endTime = timestamps[timestamps.length - 1];
1863 var scaleFactor = this._xScale.nextScale(width / profileSamples.totalTime);
1864 var maxSize = 0;
1866 * @param {!Array.<number>} sizes
1867 * @param {function(number, number):void} callback
1869 function aggregateAndCall(sizes, callback)
1871 var size = 0;
1872 var currentX = 0;
1873 for (var i = 1; i < timestamps.length; ++i) {
1874 var x = Math.floor((timestamps[i] - startTime) * scaleFactor);
1875 if (x !== currentX) {
1876 if (size)
1877 callback(currentX, size);
1878 size = 0;
1879 currentX = x;
1881 size += sizes[i];
1883 callback(currentX, size);
1887 * @param {number} x
1888 * @param {number} size
1890 function maxSizeCallback(x, size)
1892 maxSize = Math.max(maxSize, size);
1895 aggregateAndCall(sizes, maxSizeCallback);
1897 var yScaleFactor = this._yScale.nextScale(maxSize ? height / (maxSize * 1.1) : 0.0);
1899 this._overviewCanvas.width = width * window.devicePixelRatio;
1900 this._overviewCanvas.height = height * window.devicePixelRatio;
1901 this._overviewCanvas.style.width = width + "px";
1902 this._overviewCanvas.style.height = height + "px";
1904 var context = this._overviewCanvas.getContext("2d");
1905 context.scale(window.devicePixelRatio, window.devicePixelRatio);
1907 context.beginPath();
1908 context.lineWidth = 2;
1909 context.strokeStyle = "rgba(192, 192, 192, 0.6)";
1910 var currentX = (endTime - startTime) * scaleFactor;
1911 context.moveTo(currentX, height - 1);
1912 context.lineTo(currentX, 0);
1913 context.stroke();
1914 context.closePath();
1916 var gridY;
1917 var gridValue;
1918 var gridLabelHeight = 14;
1919 if (yScaleFactor) {
1920 const maxGridValue = (height - gridLabelHeight) / yScaleFactor;
1921 // The round value calculation is a bit tricky, because
1922 // it has a form k*10^n*1024^m, where k=[1,5], n=[0..3], m is an integer,
1923 // e.g. a round value 10KB is 10240 bytes.
1924 gridValue = Math.pow(1024, Math.floor(Math.log(maxGridValue) / Math.log(1024)));
1925 gridValue *= Math.pow(10, Math.floor(Math.log(maxGridValue / gridValue) / Math.LN10));
1926 if (gridValue * 5 <= maxGridValue)
1927 gridValue *= 5;
1928 gridY = Math.round(height - gridValue * yScaleFactor - 0.5) + 0.5;
1929 context.beginPath();
1930 context.lineWidth = 1;
1931 context.strokeStyle = "rgba(0, 0, 0, 0.2)";
1932 context.moveTo(0, gridY);
1933 context.lineTo(width, gridY);
1934 context.stroke();
1935 context.closePath();
1939 * @param {number} x
1940 * @param {number} size
1942 function drawBarCallback(x, size)
1944 context.moveTo(x, height - 1);
1945 context.lineTo(x, Math.round(height - size * yScaleFactor - 1));
1948 context.beginPath();
1949 context.lineWidth = 2;
1950 context.strokeStyle = "rgba(192, 192, 192, 0.6)";
1951 aggregateAndCall(topSizes, drawBarCallback);
1952 context.stroke();
1953 context.closePath();
1955 context.beginPath();
1956 context.lineWidth = 2;
1957 context.strokeStyle = "rgba(0, 0, 192, 0.8)";
1958 aggregateAndCall(sizes, drawBarCallback);
1959 context.stroke();
1960 context.closePath();
1962 if (gridValue) {
1963 var label = Number.bytesToString(gridValue);
1964 var labelPadding = 4;
1965 var labelX = 0;
1966 var labelY = gridY - 0.5;
1967 var labelWidth = 2 * labelPadding + context.measureText(label).width;
1968 context.beginPath();
1969 context.textBaseline = "bottom";
1970 context.font = "10px " + window.getComputedStyle(this.element, null).getPropertyValue("font-family");
1971 context.fillStyle = "rgba(255, 255, 255, 0.75)";
1972 context.fillRect(labelX, labelY - gridLabelHeight, labelWidth, gridLabelHeight);
1973 context.fillStyle = "rgb(64, 64, 64)";
1974 context.fillText(label, labelX + labelPadding, labelY);
1975 context.fill();
1976 context.closePath();
1980 onResize: function()
1982 this._updateOverviewCanvas = true;
1983 this._scheduleUpdate();
1986 _onWindowChanged: function()
1988 if (!this._updateGridTimerId)
1989 this._updateGridTimerId = setTimeout(this._updateGrid.bind(this), 10);
1992 _scheduleUpdate: function()
1994 if (this._updateTimerId)
1995 return;
1996 this._updateTimerId = setTimeout(this.update.bind(this), 10);
1999 _updateBoundaries: function()
2001 this._windowLeft = this._overviewGrid.windowLeft();
2002 this._windowRight = this._overviewGrid.windowRight();
2003 this._windowWidth = this._windowRight - this._windowLeft;
2006 update: function()
2008 this._updateTimerId = null;
2009 if (!this.isShowing())
2010 return;
2011 this._updateBoundaries();
2012 this._overviewCalculator._updateBoundaries(this);
2013 this._overviewGrid.updateDividers(this._overviewCalculator);
2014 this._drawOverviewCanvas(this._overviewContainer.clientWidth, this._overviewContainer.clientHeight - 20);
2017 _updateGrid: function()
2019 this._updateGridTimerId = 0;
2020 this._updateBoundaries();
2021 var ids = this._profileSamples.ids;
2022 var timestamps = this._profileSamples.timestamps;
2023 var sizes = this._profileSamples.sizes;
2024 var startTime = timestamps[0];
2025 var totalTime = this._profileSamples.totalTime;
2026 var timeLeft = startTime + totalTime * this._windowLeft;
2027 var timeRight = startTime + totalTime * this._windowRight;
2028 var minId = 0;
2029 var maxId = ids[ids.length - 1] + 1;
2030 var size = 0;
2031 for (var i = 0; i < timestamps.length; ++i) {
2032 if (!timestamps[i])
2033 continue;
2034 if (timestamps[i] > timeRight)
2035 break;
2036 maxId = ids[i];
2037 if (timestamps[i] < timeLeft) {
2038 minId = ids[i];
2039 continue;
2041 size += sizes[i];
2044 this.dispatchEventToListeners(WebInspector.HeapTrackingOverviewGrid.IdsRangeChanged, {minId: minId, maxId: maxId, size: size});
2047 __proto__: WebInspector.VBox.prototype
2052 * @constructor
2054 WebInspector.HeapTrackingOverviewGrid.SmoothScale = function()
2056 this._lastUpdate = 0;
2057 this._currentScale = 0.0;
2060 WebInspector.HeapTrackingOverviewGrid.SmoothScale.prototype = {
2062 * @param {number} target
2063 * @return {number}
2065 nextScale: function(target) {
2066 target = target || this._currentScale;
2067 if (this._currentScale) {
2068 var now = Date.now();
2069 var timeDeltaMs = now - this._lastUpdate;
2070 this._lastUpdate = now;
2071 var maxChangePerSec = 20;
2072 var maxChangePerDelta = Math.pow(maxChangePerSec, timeDeltaMs / 1000);
2073 var scaleChange = target / this._currentScale;
2074 this._currentScale *= Number.constrain(scaleChange, 1 / maxChangePerDelta, maxChangePerDelta);
2075 } else {
2076 this._currentScale = target;
2078 return this._currentScale;
2084 * @constructor
2085 * @implements {WebInspector.TimelineGrid.Calculator}
2087 WebInspector.HeapTrackingOverviewGrid.OverviewCalculator = function()
2091 WebInspector.HeapTrackingOverviewGrid.OverviewCalculator.prototype = {
2093 * @override
2094 * @return {number}
2096 paddingLeft: function()
2098 return 0;
2102 * @param {!WebInspector.HeapTrackingOverviewGrid} chart
2104 _updateBoundaries: function(chart)
2106 this._minimumBoundaries = 0;
2107 this._maximumBoundaries = chart._profileSamples.totalTime;
2108 this._xScaleFactor = chart._overviewContainer.clientWidth / this._maximumBoundaries;
2112 * @override
2113 * @param {number} time
2114 * @return {number}
2116 computePosition: function(time)
2118 return (time - this._minimumBoundaries) * this._xScaleFactor;
2122 * @override
2123 * @param {number} value
2124 * @param {number=} precision
2125 * @return {string}
2127 formatTime: function(value, precision)
2129 return Number.secondsToString(value / 1000, !!precision);
2133 * @override
2134 * @return {number}
2136 maximumBoundary: function()
2138 return this._maximumBoundaries;
2142 * @override
2143 * @return {number}
2145 minimumBoundary: function()
2147 return this._minimumBoundaries;
2151 * @override
2152 * @return {number}
2154 zeroTime: function()
2156 return this._minimumBoundaries;
2160 * @override
2161 * @return {number}
2163 boundarySpan: function()
2165 return this._maximumBoundaries - this._minimumBoundaries;
2171 * @constructor
2172 * @extends {WebInspector.VBox}
2174 WebInspector.HeapSnapshotStatisticsView = function()
2176 WebInspector.VBox.call(this);
2177 this.setMinimumSize(50, 25);
2178 this._pieChart = new WebInspector.PieChart(150, WebInspector.HeapSnapshotStatisticsView._valueFormatter, true);
2179 this._pieChart.element.classList.add("heap-snapshot-stats-pie-chart");
2180 this.element.appendChild(this._pieChart.element);
2181 this._labels = this.element.createChild("div", "heap-snapshot-stats-legend");
2185 * @param {number} value
2186 * @return {string}
2188 WebInspector.HeapSnapshotStatisticsView._valueFormatter = function(value)
2190 return WebInspector.UIString("%s KB", Number.withThousandsSeparator(Math.round(value / 1024)));
2193 WebInspector.HeapSnapshotStatisticsView.prototype = {
2195 * @param {number} value
2197 setTotal: function(value)
2199 this._pieChart.setTotal(value);
2203 * @param {number} value
2204 * @param {string} name
2205 * @param {string=} color
2207 addRecord: function(value, name, color)
2209 if (color)
2210 this._pieChart.addSlice(value, color);
2212 var node = this._labels.createChild("div");
2213 var swatchDiv = node.createChild("div", "heap-snapshot-stats-swatch");
2214 var nameDiv = node.createChild("div", "heap-snapshot-stats-name");
2215 var sizeDiv = node.createChild("div", "heap-snapshot-stats-size");
2216 if (color)
2217 swatchDiv.style.backgroundColor = color;
2218 else
2219 swatchDiv.classList.add("heap-snapshot-stats-empty-swatch");
2220 nameDiv.textContent = name;
2221 sizeDiv.textContent = WebInspector.HeapSnapshotStatisticsView._valueFormatter(value);
2224 __proto__: WebInspector.VBox.prototype
2228 * @constructor
2229 * @extends {WebInspector.Widget}
2230 * @param {?WebInspector.Target} target
2232 WebInspector.HeapAllocationStackView = function(target)
2234 WebInspector.Widget.call(this);
2235 this._target = target;;
2236 this._linkifier = new WebInspector.Linkifier();
2239 WebInspector.HeapAllocationStackView.prototype = {
2241 * @param {!WebInspector.HeapSnapshotProxy} snapshot
2242 * @param {number} snapshotNodeIndex
2244 setAllocatedObject: function(snapshot, snapshotNodeIndex)
2246 this.clear();
2247 snapshot.allocationStack(snapshotNodeIndex, this._didReceiveAllocationStack.bind(this));
2250 clear: function()
2252 this.element.removeChildren();
2253 this._linkifier.reset();
2257 * @param {?Array.<!WebInspector.HeapSnapshotCommon.AllocationStackFrame>} frames
2259 _didReceiveAllocationStack: function(frames)
2261 if (!frames) {
2262 var stackDiv = this.element.createChild("div", "no-heap-allocation-stack");
2263 stackDiv.createTextChild(WebInspector.UIString("Stack was not recorded for this object because it had been allocated before this profile recording started."));
2264 return;
2267 var stackDiv = this.element.createChild("div", "heap-allocation-stack");
2268 for (var i = 0; i < frames.length; i++) {
2269 var frame = frames[i];
2270 var frameDiv = stackDiv.createChild("div", "stack-frame");
2271 var name = frameDiv.createChild("div");
2272 name.textContent = WebInspector.beautifyFunctionName(frame.functionName);
2273 if (frame.scriptId) {
2274 var urlElement = this._linkifier.linkifyScriptLocation(this._target, String(frame.scriptId), frame.scriptName, frame.line - 1, frame.column - 1);
2275 frameDiv.appendChild(urlElement);
2280 __proto__: WebInspector.Widget.prototype