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
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
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.
33 * @extends {WebInspector.View}
34 * @param {!WebInspector.HeapProfileHeader} profile
36 WebInspector.HeapSnapshotView = function(profile)
38 WebInspector.View.call(this);
40 this.element.classList.add("heap-snapshot-view");
42 profile.profileType().addEventListener(WebInspector.HeapSnapshotProfileType.SnapshotReceived, this._onReceivSnapshot, this);
43 profile.profileType().addEventListener(WebInspector.ProfileType.Events.RemoveProfileHeader, this._onProfileHeaderRemoved, this);
45 if (profile._profileType.id === WebInspector.TrackingHeapSnapshotProfileType.TypeId) {
46 this._trackingOverviewGrid = new WebInspector.HeapTrackingOverviewGrid(profile);
47 this._trackingOverviewGrid.addEventListener(WebInspector.HeapTrackingOverviewGrid.IdsRangeChanged, this._onIdsRangeChanged.bind(this));
48 this._trackingOverviewGrid.show(this.element);
51 this.viewsContainer = new WebInspector.SplitView(false, true, "heapSnapshotRetainersViewSize", 200, 200);
52 this.viewsContainer.show(this.element);
53 this.viewsContainer.setMainElementConstraints(50, 50);
54 this.viewsContainer.setSidebarElementConstraints(70, 70);
56 this.containmentView = new WebInspector.View();
57 this.containmentDataGrid = new WebInspector.HeapSnapshotContainmentDataGrid();
58 this.containmentDataGrid.element.addEventListener("mousedown", this._mouseDownInContentsGrid.bind(this), true);
59 this.containmentDataGrid.show(this.containmentView.element);
60 this.containmentDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._selectionChanged, this);
62 this.constructorsView = new WebInspector.View();
64 this.constructorsDataGrid = new WebInspector.HeapSnapshotConstructorsDataGrid();
65 this.constructorsDataGrid.element.addEventListener("mousedown", this._mouseDownInContentsGrid.bind(this), true);
66 this.constructorsDataGrid.show(this.constructorsView.element);
67 this.constructorsDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._selectionChanged, this);
69 this.dataGrid = /** @type {!WebInspector.HeapSnapshotSortableDataGrid} */ (this.constructorsDataGrid);
70 this.currentView = this.constructorsView;
71 this.currentView.show(this.viewsContainer.mainElement());
73 this.diffView = new WebInspector.View();
75 this.diffDataGrid = new WebInspector.HeapSnapshotDiffDataGrid();
76 this.diffDataGrid.show(this.diffView.element);
77 this.diffDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._selectionChanged, this);
79 this.dominatorView = new WebInspector.View();
80 this.dominatorDataGrid = new WebInspector.HeapSnapshotDominatorsDataGrid();
81 this.dominatorDataGrid.element.addEventListener("mousedown", this._mouseDownInContentsGrid.bind(this), true);
82 this.dominatorDataGrid.show(this.dominatorView.element);
83 this.dominatorDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._selectionChanged, this);
85 if (WebInspector.experimentsSettings.allocationProfiler.isEnabled() && profile.profileType() === WebInspector.ProfileTypeRegistry.instance.trackingHeapSnapshotProfileType) {
86 this.allocationView = new WebInspector.View();
87 this.allocationDataGrid = new WebInspector.AllocationDataGrid();
88 this.allocationDataGrid.element.addEventListener("mousedown", this._mouseDownInContentsGrid.bind(this), true);
89 this.allocationDataGrid.show(this.allocationView.element);
90 this.allocationDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._selectionChanged, this);
93 this.retainmentViewHeader = document.createElementWithClass("div", "retainers-view-header");
94 var retainingPathsTitleDiv = this.retainmentViewHeader.createChild("div", "title");
95 var retainingPathsTitle = retainingPathsTitleDiv.createChild("span");
96 retainingPathsTitle.textContent = WebInspector.UIString("Object's retaining tree");
97 this.viewsContainer.hideDefaultResizer();
98 this.viewsContainer.installResizer(this.retainmentViewHeader);
100 this.retainmentView = new WebInspector.View();
101 this.retainmentView.element.classList.add("retaining-paths-view");
102 this.retainmentView.element.appendChild(this.retainmentViewHeader);
103 this.retainmentDataGrid = new WebInspector.HeapSnapshotRetainmentDataGrid();
104 this.retainmentDataGrid.show(this.retainmentView.element);
105 this.retainmentDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._inspectedObjectChanged, this);
106 this.retainmentView.show(this.viewsContainer.sidebarElement());
107 this.retainmentDataGrid.reset();
109 this.viewSelect = new WebInspector.StatusBarComboBox(this._onSelectedViewChanged.bind(this));
111 this.views = [{title: WebInspector.UIString("Summary"), view: this.constructorsView, grid: this.constructorsDataGrid}];
113 if (profile.profileType() !== WebInspector.ProfileTypeRegistry.instance.trackingHeapSnapshotProfileType)
114 this.views.push({title: WebInspector.UIString("Comparison"), view: this.diffView, grid: this.diffDataGrid});
115 this.views.push({title: WebInspector.UIString("Containment"), view: this.containmentView, grid: this.containmentDataGrid});
116 if (WebInspector.settings.showAdvancedHeapSnapshotProperties.get())
117 this.views.push({title: WebInspector.UIString("Dominators"), view: this.dominatorView, grid: this.dominatorDataGrid});
118 if (this.allocationView)
119 this.views.push({title: WebInspector.UIString("Allocation"), view: this.allocationView, grid: this.allocationDataGrid});
120 this.views.current = 0;
121 for (var i = 0; i < this.views.length; ++i)
122 this.viewSelect.createOption(WebInspector.UIString(this.views[i].title));
124 this._profile = profile;
126 this.baseSelect = new WebInspector.StatusBarComboBox(this._changeBase.bind(this));
127 this._updateBaseOptions();
129 this._filterSelect = new WebInspector.StatusBarComboBox(this._changeFilter.bind(this));
130 this._updateFilterOptions();
132 this._classNameFilter = new WebInspector.StatusBarInput("Class filter");
133 this._classNameFilter.setOnChangeHandler(this._onClassFilterChanged.bind(this));
135 this.selectedSizeText = new WebInspector.StatusBarText("");
137 this._popoverHelper = new WebInspector.ObjectPopoverHelper(this.element, this._getHoverAnchor.bind(this), this._resolveObjectForPopover.bind(this), undefined, true);
139 this._updateSelectorsVisibility();
143 WebInspector.HeapSnapshotView.prototype = {
144 _refreshView: function()
146 this._profile.load(profileCallback.bind(this));
149 * @this {WebInspector.HeapSnapshotView}
151 function profileCallback(heapSnapshotProxy)
153 var list = this._profiles();
154 var profileIndex = list.indexOf(this._profile);
155 this.baseSelect.setSelectedIndex(Math.max(0, profileIndex - 1));
156 this.dataGrid.setDataSource(heapSnapshotProxy);
157 if (this._trackingOverviewGrid)
158 this._trackingOverviewGrid._updateGrid();
162 _onIdsRangeChanged: function(event)
164 var minId = event.data.minId;
165 var maxId = event.data.maxId;
166 this.selectedSizeText.setText(WebInspector.UIString("Selected size: %s", Number.bytesToString(event.data.size)));
167 if (this.constructorsDataGrid.snapshot)
168 this.constructorsDataGrid.setSelectionRange(minId, maxId);
173 var result = [this.viewSelect.element, this._classNameFilter.element];
174 if (this._profile.profileType() !== WebInspector.ProfileTypeRegistry.instance.trackingHeapSnapshotProfileType)
175 result.push(this.baseSelect.element, this._filterSelect.element);
176 result.push(this.selectedSizeText.element);
182 // FIXME: load base and current snapshots in parallel
183 this._profile.load(profileCallback.bind(this));
186 * @this {WebInspector.HeapSnapshotView}
188 function profileCallback() {
189 this._profile._wasShown();
190 if (this._baseProfile)
191 this._baseProfile.load(function() { });
197 this._currentSearchResultIndex = -1;
198 this._popoverHelper.hidePopover();
199 if (this.helpPopover && this.helpPopover.isShowing())
200 this.helpPopover.hide();
203 searchCanceled: function()
205 if (this._searchResults) {
206 for (var i = 0; i < this._searchResults.length; ++i) {
207 var node = this._searchResults[i].node;
208 delete node._searchMatched;
213 delete this._searchFinishedCallback;
214 this._currentSearchResultIndex = -1;
215 this._searchResults = [];
219 * @param {string} query
220 * @param {function(!WebInspector.View, number)} finishedCallback
222 performSearch: function(query, finishedCallback)
224 // Call searchCanceled since it will reset everything we need before doing a new search.
225 this.searchCanceled();
227 query = query.trim();
231 if (this.currentView !== this.constructorsView && this.currentView !== this.diffView)
235 * @param {boolean} found
236 * @this {WebInspector.HeapSnapshotView}
238 function didHighlight(found)
240 finishedCallback(this, found ? 1 : 0);
243 if (query.charAt(0) === "@") {
244 var snapshotNodeId = parseInt(query.substring(1), 10);
245 if (!isNaN(snapshotNodeId))
246 this.dataGrid.highlightObjectByHeapSnapshotId(String(snapshotNodeId), didHighlight.bind(this));
248 finishedCallback(this, 0);
252 this._searchFinishedCallback = finishedCallback;
253 var nameRegExp = createPlainTextSearchRegex(query, "i");
255 function matchesByName(gridNode) {
256 return ("_name" in gridNode) && nameRegExp.test(gridNode._name);
259 function matchesQuery(gridNode)
261 delete gridNode._searchMatched;
262 if (matchesByName(gridNode)) {
263 gridNode._searchMatched = true;
270 var current = this.dataGrid.rootNode().children[0];
274 // Restrict to type nodes and instances.
278 if (matchesQuery(current))
279 this._searchResults.push({ node: current });
280 current = current.traverseNextNode(false, null, (depth >= maxDepth), info);
281 depth += info.depthChange;
284 finishedCallback(this, this._searchResults.length);
287 jumpToFirstSearchResult: function()
289 if (!this._searchResults || !this._searchResults.length)
291 this._currentSearchResultIndex = 0;
292 this._jumpToSearchResult(this._currentSearchResultIndex);
295 jumpToLastSearchResult: function()
297 if (!this._searchResults || !this._searchResults.length)
299 this._currentSearchResultIndex = (this._searchResults.length - 1);
300 this._jumpToSearchResult(this._currentSearchResultIndex);
303 jumpToNextSearchResult: function()
305 if (!this._searchResults || !this._searchResults.length)
307 if (++this._currentSearchResultIndex >= this._searchResults.length)
308 this._currentSearchResultIndex = 0;
309 this._jumpToSearchResult(this._currentSearchResultIndex);
312 jumpToPreviousSearchResult: function()
314 if (!this._searchResults || !this._searchResults.length)
316 if (--this._currentSearchResultIndex < 0)
317 this._currentSearchResultIndex = (this._searchResults.length - 1);
318 this._jumpToSearchResult(this._currentSearchResultIndex);
324 showingFirstSearchResult: function()
326 return (this._currentSearchResultIndex === 0);
332 showingLastSearchResult: function()
334 return (this._searchResults && this._currentSearchResultIndex === (this._searchResults.length - 1));
340 currentSearchResultIndex: function() {
341 return this._currentSearchResultIndex;
344 _jumpToSearchResult: function(index)
346 var searchResult = this._searchResults[index];
350 var node = searchResult.node;
351 node.revealAndSelect();
354 refreshVisibleData: function()
356 var child = this.dataGrid.rootNode().children[0];
359 child = child.traverseNextNode(false, null, true);
363 _changeBase: function()
365 if (this._baseProfile=== this._profiles()[this.baseSelect.selectedIndex()])
368 this._baseProfile = this._profiles()[this.baseSelect.selectedIndex()];
369 var dataGrid = /** @type {!WebInspector.HeapSnapshotDiffDataGrid} */ (this.dataGrid);
370 // Change set base data source only if main data source is already set.
371 if (dataGrid.snapshot)
372 this._baseProfile.load(dataGrid.setBaseDataSource.bind(dataGrid));
374 if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults)
377 // The current search needs to be performed again. First negate out previous match
378 // count by calling the search finished callback with a negative number of matches.
379 // Then perform the search again with the same query and callback.
380 this._searchFinishedCallback(this, -this._searchResults.length);
381 this.performSearch(this.currentQuery, this._searchFinishedCallback);
384 _changeFilter: function()
386 var profileIndex = this._filterSelect.selectedIndex() - 1;
387 this.dataGrid.filterSelectIndexChanged(this._profiles(), profileIndex);
389 WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, {
390 action: WebInspector.UserMetrics.UserActionNames.HeapSnapshotFilterChanged,
391 label: this._filterSelect.selectedOption().label
394 if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults)
397 // The current search needs to be performed again. First negate out previous match
398 // count by calling the search finished callback with a negative number of matches.
399 // Then perform the search again with the same query and callback.
400 this._searchFinishedCallback(this, -this._searchResults.length);
401 this.performSearch(this.currentQuery, this._searchFinishedCallback);
405 * @param {string} value
407 _onClassFilterChanged: function(value)
409 this.dataGrid.changeNameFilter(value);
413 * @return {!Array.<!WebInspector.ProfileHeader>}
415 _profiles: function()
417 return this._profile.profileType().getProfiles();
421 * @param {!WebInspector.ContextMenu} contextMenu
422 * @param {?Event} event
424 populateContextMenu: function(contextMenu, event)
426 this.dataGrid.populateContextMenu(contextMenu, event);
429 _selectionChanged: function(event)
431 var selectedNode = event.target.selectedNode;
432 this._setRetainmentDataGridSource(selectedNode);
433 this._inspectedObjectChanged(event);
436 _inspectedObjectChanged: function(event)
438 var selectedNode = event.target.selectedNode;
439 if (!this._profile.fromFile() && selectedNode instanceof WebInspector.HeapSnapshotGenericObjectNode)
440 ConsoleAgent.addInspectedHeapObject(selectedNode.snapshotNodeId);
443 _setRetainmentDataGridSource: function(nodeItem)
445 if (nodeItem && nodeItem.snapshotNodeIndex)
446 this.retainmentDataGrid.setDataSource(nodeItem.isDeletedNode ? nodeItem.dataGrid.baseSnapshot : nodeItem.dataGrid.snapshot, nodeItem.snapshotNodeIndex);
448 this.retainmentDataGrid.reset();
451 _mouseDownInContentsGrid: function(event)
453 if (event.detail < 2)
456 var cell = event.target.enclosingNodeOrSelfWithNodeName("td");
457 if (!cell || (!cell.classList.contains("count-column") && !cell.classList.contains("shallowSize-column") && !cell.classList.contains("retainedSize-column")))
463 changeView: function(viewTitle, callback)
465 var viewIndex = null;
466 for (var i = 0; i < this.views.length; ++i) {
467 if (this.views[i].title === viewTitle) {
472 if (this.views.current === viewIndex || viewIndex == null) {
473 setTimeout(callback, 0);
478 * @this {WebInspector.HeapSnapshotView}
480 function dataGridContentShown(event)
482 var dataGrid = event.data;
483 dataGrid.removeEventListener(WebInspector.HeapSnapshotSortableDataGrid.Events.ContentShown, dataGridContentShown, this);
484 if (dataGrid === this.dataGrid)
487 this.views[viewIndex].grid.addEventListener(WebInspector.HeapSnapshotSortableDataGrid.Events.ContentShown, dataGridContentShown, this);
489 this.viewSelect.setSelectedIndex(viewIndex);
490 this._changeView(viewIndex);
493 _updateDataSourceAndView: function()
495 var dataGrid = this.dataGrid;
496 if (dataGrid.snapshot)
499 this._profile.load(didLoadSnapshot.bind(this));
502 * @this {WebInspector.HeapSnapshotView}
504 function didLoadSnapshot(snapshotProxy)
506 if (this.dataGrid !== dataGrid)
508 if (dataGrid.snapshot !== snapshotProxy)
509 dataGrid.setDataSource(snapshotProxy);
510 if (dataGrid === this.diffDataGrid) {
511 if (!this._baseProfile)
512 this._baseProfile = this._profiles()[this.baseSelect.selectedIndex()];
513 this._baseProfile.load(didLoadBaseSnaphot.bind(this));
518 * @this {WebInspector.HeapSnapshotView}
520 function didLoadBaseSnaphot(baseSnapshotProxy)
522 if (this.diffDataGrid.baseSnapshot !== baseSnapshotProxy)
523 this.diffDataGrid.setBaseDataSource(baseSnapshotProxy);
527 _onSelectedViewChanged: function(event)
529 this._changeView(event.target.selectedIndex);
532 _updateSelectorsVisibility: function()
534 this.baseSelect.visible = (this.currentView === this.diffView);
535 this._filterSelect.visible = (this.currentView === this.constructorsView);
536 this._classNameFilter.visible = (this.currentView === this.constructorsView || this.currentView === this.diffView);
538 if (this._trackingOverviewGrid) {
539 this._trackingOverviewGrid.element.classList.toggle("hidden", this.currentView !== this.constructorsView);
540 if (this.currentView === this.constructorsView)
541 this._trackingOverviewGrid.update();
545 _changeView: function(selectedIndex)
547 if (selectedIndex === this.views.current)
550 this.views.current = selectedIndex;
551 this.currentView.detach();
552 var view = this.views[this.views.current];
553 this.currentView = view.view;
554 this.dataGrid = view.grid;
555 this.currentView.show(this.viewsContainer.mainElement());
556 this.refreshVisibleData();
557 this.dataGrid.updateWidths();
559 this._updateSelectorsVisibility();
561 this._updateDataSourceAndView();
563 if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults)
566 // The current search needs to be performed again. First negate out previous match
567 // count by calling the search finished callback with a negative number of matches.
568 // Then perform the search again the with same query and callback.
569 this._searchFinishedCallback(this, -this._searchResults.length);
570 this.performSearch(this.currentQuery, this._searchFinishedCallback);
573 _getHoverAnchor: function(target)
575 var span = target.enclosingNodeOrSelfWithNodeName("span");
578 var row = target.enclosingNodeOrSelfWithNodeName("tr");
581 span.node = row._dataGridNode;
585 _resolveObjectForPopover: function(element, showCallback, objectGroupName)
587 if (this._profile.fromFile())
589 element.node.queryObjectContent(showCallback, objectGroupName);
592 _updateBaseOptions: function()
594 var list = this._profiles();
595 // We're assuming that snapshots can only be added.
596 if (this.baseSelect.size() === list.length)
599 for (var i = this.baseSelect.size(), n = list.length; i < n; ++i) {
600 var title = list[i].title;
601 this.baseSelect.createOption(title);
605 _updateFilterOptions: function()
607 var list = this._profiles();
608 // We're assuming that snapshots can only be added.
609 if (this._filterSelect.size() - 1 === list.length)
612 if (!this._filterSelect.size())
613 this._filterSelect.createOption(WebInspector.UIString("All objects"));
615 for (var i = this._filterSelect.size() - 1, n = list.length; i < n; ++i) {
616 var title = list[i].title;
618 title = WebInspector.UIString("Objects allocated before %s", title);
620 title = WebInspector.UIString("Objects allocated between %s and %s", list[i - 1].title, title);
621 this._filterSelect.createOption(title);
625 _updateControls: function()
627 this._updateBaseOptions();
628 this._updateFilterOptions();
632 * @param {!WebInspector.Event} event
634 _onReceivSnapshot: function(event)
636 this._updateControls();
640 * @param {!WebInspector.Event} event
642 _onProfileHeaderRemoved: function(event)
644 var profile = event.data;
645 if (this._profile === profile) {
647 this._profile.profileType().removeEventListener(WebInspector.ProfileType.Events.AddProfileHeader, this._onReceivSnapshot, this);
648 this._profile.profileType().removeEventListener(WebInspector.ProfileType.Events.RemoveProfileHeader, this._onProfileHeaderRemoved, this);
650 this._updateControls();
654 __proto__: WebInspector.View.prototype
659 * @implements {HeapProfilerAgent.Dispatcher}
661 WebInspector.HeapProfilerDispatcher = function()
663 this._dispatchers = [];
664 InspectorBackend.registerHeapProfilerDispatcher(this);
667 WebInspector.HeapProfilerDispatcher.prototype = {
669 * @param {!HeapProfilerAgent.Dispatcher} dispatcher
671 register: function(dispatcher)
673 this._dispatchers.push(dispatcher);
676 _genericCaller: function(eventName)
678 var args = Array.prototype.slice.call(arguments.callee.caller.arguments);
679 for (var i = 0; i < this._dispatchers.length; ++i)
680 this._dispatchers[i][eventName].apply(this._dispatchers[i], args);
685 * @param {!Array.<number>} samples
687 heapStatsUpdate: function(samples)
689 this._genericCaller("heapStatsUpdate");
694 * @param {number} lastSeenObjectId
695 * @param {number} timestamp
697 lastSeenObjectId: function(lastSeenObjectId, timestamp)
699 this._genericCaller("lastSeenObjectId");
704 * @param {string} chunk
706 addHeapSnapshotChunk: function(chunk)
708 this._genericCaller("addHeapSnapshotChunk");
713 * @param {number} done
714 * @param {number} total
715 * @param {boolean=} finished
717 reportHeapSnapshotProgress: function(done, total, finished)
719 this._genericCaller("reportHeapSnapshotProgress");
725 resetProfiles: function()
727 this._genericCaller("resetProfiles");
731 WebInspector.HeapProfilerDispatcher._dispatcher = new WebInspector.HeapProfilerDispatcher();
735 * @extends {WebInspector.ProfileType}
736 * @implements {HeapProfilerAgent.Dispatcher}
737 * @param {string=} id
738 * @param {string=} title
740 WebInspector.HeapSnapshotProfileType = function(id, title)
742 WebInspector.ProfileType.call(this, id || WebInspector.HeapSnapshotProfileType.TypeId, title || WebInspector.UIString("Take Heap Snapshot"));
743 WebInspector.HeapProfilerDispatcher._dispatcher.register(this);
746 WebInspector.HeapSnapshotProfileType.TypeId = "HEAP";
747 WebInspector.HeapSnapshotProfileType.SnapshotReceived = "SnapshotReceived";
749 WebInspector.HeapSnapshotProfileType.prototype = {
754 fileExtension: function()
756 return ".heapsnapshot";
761 return WebInspector.UIString("Take heap snapshot.");
768 isInstantProfile: function()
777 buttonClicked: function()
779 this._takeHeapSnapshot(function() {});
780 WebInspector.userMetrics.ProfilesHeapProfileTaken.record();
786 * @param {!Array.<number>} samples
788 heapStatsUpdate: function(samples)
794 * @param {number} lastSeenObjectId
795 * @param {number} timestamp
797 lastSeenObjectId: function(lastSeenObjectId, timestamp)
803 return WebInspector.UIString("HEAP SNAPSHOTS");
808 return WebInspector.UIString("Heap snapshot profiles show memory distribution among your page's JavaScript objects and related DOM nodes.");
813 * @param {!string} title
814 * @return {!WebInspector.ProfileHeader}
816 createProfileLoadedFromFile: function(title)
818 return new WebInspector.HeapProfileHeader(this, title);
821 _takeHeapSnapshot: function(callback)
823 if (this.profileBeingRecorded())
825 this._profileBeingRecorded = new WebInspector.HeapProfileHeader(this);
826 this.addProfile(this._profileBeingRecorded);
827 this._profileBeingRecorded.updateStatus(WebInspector.UIString("Snapshotting\u2026"));
830 * @param {?string} error
831 * @this {WebInspector.HeapSnapshotProfileType}
833 function didTakeHeapSnapshot(error)
835 var profile = this._profileBeingRecorded;
836 profile.title = WebInspector.UIString("Snapshot %d", profile.uid);
837 profile._finishLoad();
838 this._profileBeingRecorded = null;
839 WebInspector.panels.profiles.showProfile(profile);
842 HeapProfilerAgent.takeHeapSnapshot(true, didTakeHeapSnapshot.bind(this));
847 * @param {string} chunk
849 addHeapSnapshotChunk: function(chunk)
851 if (!this.profileBeingRecorded())
853 this.profileBeingRecorded().transferChunk(chunk);
858 * @param {number} done
859 * @param {number} total
860 * @param {boolean=} finished
862 reportHeapSnapshotProgress: function(done, total, finished)
864 var profile = this.profileBeingRecorded();
867 profile.updateStatus(WebInspector.UIString("%.0f%", (done / total) * 100), true);
869 profile._prepareToLoad();
875 resetProfiles: function()
880 _snapshotReceived: function(profile)
882 if (this._profileBeingRecorded === profile)
883 this._profileBeingRecorded = null;
884 this.dispatchEventToListeners(WebInspector.HeapSnapshotProfileType.SnapshotReceived, profile);
887 __proto__: WebInspector.ProfileType.prototype
893 * @extends {WebInspector.HeapSnapshotProfileType}
895 WebInspector.TrackingHeapSnapshotProfileType = function()
897 WebInspector.HeapSnapshotProfileType.call(this, WebInspector.TrackingHeapSnapshotProfileType.TypeId, WebInspector.UIString("Record Heap Allocations"));
900 WebInspector.TrackingHeapSnapshotProfileType.TypeId = "HEAP-RECORD";
902 WebInspector.TrackingHeapSnapshotProfileType.HeapStatsUpdate = "HeapStatsUpdate";
903 WebInspector.TrackingHeapSnapshotProfileType.TrackingStarted = "TrackingStarted";
904 WebInspector.TrackingHeapSnapshotProfileType.TrackingStopped = "TrackingStopped";
906 WebInspector.TrackingHeapSnapshotProfileType.prototype = {
910 * @param {!Array.<number>} samples
912 heapStatsUpdate: function(samples)
914 if (!this._profileSamples)
917 for (var i = 0; i < samples.length; i += 3) {
919 var count = samples[i+1];
920 var size = samples[i+2];
921 this._profileSamples.sizes[index] = size;
922 if (!this._profileSamples.max[index] || size > this._profileSamples.max[index])
923 this._profileSamples.max[index] = size;
925 this._lastUpdatedIndex = index;
930 * @param {number} lastSeenObjectId
931 * @param {number} timestamp
933 lastSeenObjectId: function(lastSeenObjectId, timestamp)
935 var profileSamples = this._profileSamples;
938 var currentIndex = Math.max(profileSamples.ids.length, profileSamples.max.length - 1);
939 profileSamples.ids[currentIndex] = lastSeenObjectId;
940 if (!profileSamples.max[currentIndex]) {
941 profileSamples.max[currentIndex] = 0;
942 profileSamples.sizes[currentIndex] = 0;
944 profileSamples.timestamps[currentIndex] = timestamp;
945 if (profileSamples.totalTime < timestamp - profileSamples.timestamps[0])
946 profileSamples.totalTime *= 2;
947 this.dispatchEventToListeners(WebInspector.TrackingHeapSnapshotProfileType.HeapStatsUpdate, this._profileSamples);
948 this._profileBeingRecorded.updateStatus(null, true);
955 hasTemporaryView: function()
962 return this._recording ? WebInspector.UIString("Stop recording heap profile.") : WebInspector.UIString("Start recording heap profile.");
969 isInstantProfile: function()
978 buttonClicked: function()
980 return this._toggleRecording();
983 _startRecordingProfile: function()
985 if (this.profileBeingRecorded())
987 this._profileBeingRecorded = new WebInspector.HeapProfileHeader(this);
988 this._lastSeenIndex = -1;
989 this._profileSamples = {
996 this._profileBeingRecorded._profileSamples = this._profileSamples;
997 this._recording = true;
998 this.addProfile(this._profileBeingRecorded);
999 this._profileBeingRecorded.updateStatus(WebInspector.UIString("Recording\u2026"));
1000 HeapProfilerAgent.startTrackingHeapObjects(WebInspector.experimentsSettings.allocationProfiler.isEnabled());
1001 this.dispatchEventToListeners(WebInspector.TrackingHeapSnapshotProfileType.TrackingStarted);
1004 _stopRecordingProfile: function()
1006 this._profileBeingRecorded.updateStatus(WebInspector.UIString("Snapshotting\u2026"));
1008 * @param {?string} error
1009 * @this {WebInspector.HeapSnapshotProfileType}
1011 function didTakeHeapSnapshot(error)
1013 var profile = this._profileBeingRecorded;
1016 profile._finishLoad();
1017 this._profileSamples = null;
1018 this._profileBeingRecorded = null;
1019 WebInspector.panels.profiles.showProfile(profile);
1022 HeapProfilerAgent.stopTrackingHeapObjects(true, didTakeHeapSnapshot.bind(this));
1023 this._recording = false;
1024 this.dispatchEventToListeners(WebInspector.TrackingHeapSnapshotProfileType.TrackingStopped);
1027 _toggleRecording: function()
1029 if (this._recording)
1030 this._stopRecordingProfile();
1032 this._startRecordingProfile();
1033 return this._recording;
1038 return WebInspector.UIString("HEAP TIMELINES");
1043 return WebInspector.UIString("Record JavaScript object allocations over time. Use this profile type to isolate memory leaks.");
1048 WebInspector.HeapSnapshotProfileType.prototype._reset.call(this);
1049 if (this._recording)
1050 this._stopRecordingProfile();
1051 this._profileSamples = null;
1052 this._lastSeenIndex = -1;
1058 profileBeingRecordedRemoved: function()
1060 this._stopRecordingProfile();
1061 this._profileSamples = null;
1064 __proto__: WebInspector.HeapSnapshotProfileType.prototype
1069 * @extends {WebInspector.ProfileHeader}
1070 * @param {!WebInspector.HeapSnapshotProfileType} type
1071 * @param {string=} title
1073 WebInspector.HeapProfileHeader = function(type, title)
1075 WebInspector.ProfileHeader.call(this, type, title || WebInspector.UIString("Snapshot %d", type._nextProfileUid));
1076 this.maxJSObjectId = -1;
1078 * @type {?WebInspector.HeapSnapshotWorkerProxy}
1080 this._workerProxy = null;
1082 * @type {?WebInspector.OutputStream}
1084 this._receiver = null;
1086 * @type {?WebInspector.HeapSnapshotProxy}
1088 this._snapshotProxy = null;
1090 * @type {?Array.<function(!WebInspector.HeapSnapshotProxy)>}
1092 this._loadCallbacks = [];
1093 this._totalNumberOfChunks = 0;
1094 this._bufferedWriter = null;
1097 WebInspector.HeapProfileHeader.prototype = {
1100 * @return {!WebInspector.ProfileSidebarTreeElement}
1102 createSidebarTreeElement: function()
1104 return new WebInspector.ProfileSidebarTreeElement(this, "heap-snapshot-sidebar-tree-item");
1109 * @return {!WebInspector.HeapSnapshotView}
1111 createView: function()
1113 return new WebInspector.HeapSnapshotView(this);
1118 * @param {function(!WebInspector.HeapSnapshotProxy):void} callback
1120 load: function(callback)
1122 if (this.uid === -1)
1124 if (this._snapshotProxy) {
1125 callback(this._snapshotProxy);
1128 this._loadCallbacks.push(callback);
1131 _prepareToLoad: function()
1133 console.assert(!this._receiver, "Already loading");
1134 this._setupWorker();
1135 this.updateStatus(WebInspector.UIString("Loading\u2026"), true);
1138 _finishLoad: function()
1140 this._receiver.close(function() {});
1141 if (this._bufferedWriter) {
1142 this._bufferedWriter.close(this._didWriteToTempFile.bind(this));
1143 this._bufferedWriter = null;
1147 _didWriteToTempFile: function(tempFile)
1149 this._tempFile = tempFile;
1151 this._failedToCreateTempFile = true;
1152 if (this._onTempFileReady) {
1153 this._onTempFileReady();
1154 this._onTempFileReady = null;
1158 _setupWorker: function()
1161 * @this {WebInspector.HeapProfileHeader}
1163 function setProfileWait(event)
1165 this.updateStatus(null, event.data);
1167 console.assert(!this._workerProxy, "HeapSnapshotWorkerProxy already exists");
1168 this._workerProxy = new WebInspector.HeapSnapshotWorkerProxy(this._handleWorkerEvent.bind(this));
1169 this._workerProxy.addEventListener("wait", setProfileWait, this);
1170 this._receiver = this._workerProxy.createLoader(this.uid, this._snapshotReceived.bind(this));
1174 * @param {string} eventName
1177 _handleWorkerEvent: function(eventName, data)
1179 if (WebInspector.HeapSnapshotProgressEvent.Update !== eventName)
1181 var subtitle = /** @type {string} */ (data);
1182 this.updateStatus(subtitle);
1190 if (this._workerProxy)
1191 this._workerProxy.dispose();
1192 this.removeTempFile();
1193 this._wasDisposed = true;
1196 _didCompleteSnapshotTransfer: function()
1198 if (!this._snapshotProxy)
1200 this.updateStatus(Number.bytesToString(this._snapshotProxy.totalSize), false);
1204 * @param {string} chunk
1206 transferChunk: function(chunk)
1208 if (!this._bufferedWriter)
1209 this._bufferedWriter = new WebInspector.BufferedTempFileWriter("heap-profiler", this.uid);
1210 this._bufferedWriter.write(chunk);
1212 ++this._totalNumberOfChunks;
1213 this._receiver.write(chunk, function() {});
1216 _snapshotReceived: function(snapshotProxy)
1218 if (this._wasDisposed)
1220 this._receiver = null;
1221 this._snapshotProxy = snapshotProxy;
1222 this.maxJSObjectId = snapshotProxy.maxJSObjectId();
1223 this._didCompleteSnapshotTransfer();
1224 this._workerProxy.startCheckingForLongRunningCalls();
1225 this.notifySnapshotReceived();
1228 notifySnapshotReceived: function()
1230 for (var i = 0; i < this._loadCallbacks.length; i++)
1231 this._loadCallbacks[i](this._snapshotProxy);
1232 this._loadCallbacks = null;
1233 this._profileType._snapshotReceived(this);
1234 if (this.canSaveToFile())
1235 this.dispatchEventToListeners(WebInspector.ProfileHeader.Events.ProfileReceived);
1238 // Hook point for tests.
1239 _wasShown: function()
1247 canSaveToFile: function()
1249 return !this.fromFile() && this._snapshotProxy;
1255 saveToFile: function()
1257 var fileOutputStream = new WebInspector.FileOutputStream();
1260 * @param {boolean} accepted
1261 * @this {WebInspector.HeapProfileHeader}
1263 function onOpen(accepted)
1267 if (this._failedToCreateTempFile) {
1268 WebInspector.log("Failed to open temp file with heap snapshot",
1269 WebInspector.ConsoleMessage.MessageLevel.Error);
1270 fileOutputStream.close();
1271 } else if (this._tempFile) {
1272 var delegate = new WebInspector.SaveSnapshotOutputStreamDelegate(this);
1273 this._tempFile.writeToOutputSteam(fileOutputStream, delegate);
1275 this._onTempFileReady = onOpen.bind(this, accepted);
1276 this._updateSaveProgress(0, 1);
1279 this._fileName = this._fileName || "Heap-" + new Date().toISO8601Compact() + this._profileType.fileExtension();
1280 fileOutputStream.open(this._fileName, onOpen.bind(this));
1283 _updateSaveProgress: function(value, total)
1285 var percentValue = ((total ? (value / total) : 0) * 100).toFixed(0);
1286 this.updateStatus(WebInspector.UIString("Saving\u2026 %d\%", percentValue));
1291 * @param {!File} file
1293 loadFromFile: function(file)
1295 this.updateStatus(WebInspector.UIString("Loading\u2026"), true);
1296 this._setupWorker();
1297 var delegate = new WebInspector.HeapSnapshotLoadFromFileDelegate(this);
1298 var fileReader = this._createFileReader(file, delegate);
1299 fileReader.start(this._receiver);
1302 _createFileReader: function(file, delegate)
1304 return new WebInspector.ChunkedFileReader(file, 10000000, delegate);
1307 __proto__: WebInspector.ProfileHeader.prototype
1312 * @implements {WebInspector.OutputStreamDelegate}
1314 WebInspector.HeapSnapshotLoadFromFileDelegate = function(snapshotHeader)
1316 this._snapshotHeader = snapshotHeader;
1319 WebInspector.HeapSnapshotLoadFromFileDelegate.prototype = {
1320 onTransferStarted: function()
1325 * @param {!WebInspector.ChunkedReader} reader
1327 onChunkTransferred: function(reader)
1331 onTransferFinished: function()
1336 * @param {!WebInspector.ChunkedReader} reader
1338 onError: function (reader, e)
1341 switch(e.target.error.code) {
1342 case e.target.error.NOT_FOUND_ERR:
1343 subtitle = WebInspector.UIString("'%s' not found.", reader.fileName());
1345 case e.target.error.NOT_READABLE_ERR:
1346 subtitle = WebInspector.UIString("'%s' is not readable", reader.fileName());
1348 case e.target.error.ABORT_ERR:
1351 subtitle = WebInspector.UIString("'%s' error %d", reader.fileName(), e.target.error.code);
1353 this._snapshotHeader.updateStatus(subtitle);
1359 * @implements {WebInspector.OutputStreamDelegate}
1360 * @param {!WebInspector.HeapProfileHeader} profileHeader
1362 WebInspector.SaveSnapshotOutputStreamDelegate = function(profileHeader)
1364 this._profileHeader = profileHeader;
1367 WebInspector.SaveSnapshotOutputStreamDelegate.prototype = {
1368 onTransferStarted: function()
1370 this._profileHeader._updateSaveProgress(0, 1);
1373 onTransferFinished: function()
1375 this._profileHeader._didCompleteSnapshotTransfer();
1379 * @param {!WebInspector.ChunkedReader} reader
1381 onChunkTransferred: function(reader)
1383 this._profileHeader._updateSaveProgress(reader.loadedSize(), reader.fileSize());
1387 * @param {!WebInspector.ChunkedReader} reader
1389 onError: function(reader, event)
1391 WebInspector.log("Failed to read heap snapshot from temp file: " + event.message,
1392 WebInspector.ConsoleMessage.MessageLevel.Error);
1393 this.onTransferFinished();
1399 * @extends {WebInspector.View}
1400 * @param {!WebInspector.HeapProfileHeader} heapProfileHeader
1402 WebInspector.HeapTrackingOverviewGrid = function(heapProfileHeader)
1404 WebInspector.View.call(this);
1405 this.registerRequiredCSS("flameChart.css");
1406 this.element.id = "heap-recording-view";
1407 this.element.classList.add("heap-tracking-overview");
1409 this._overviewContainer = this.element.createChild("div", "overview-container");
1410 this._overviewGrid = new WebInspector.OverviewGrid("heap-recording");
1411 this._overviewGrid.element.classList.add("fill");
1413 this._overviewCanvas = this._overviewContainer.createChild("canvas", "heap-recording-overview-canvas");
1414 this._overviewContainer.appendChild(this._overviewGrid.element);
1415 this._overviewCalculator = new WebInspector.HeapTrackingOverviewGrid.OverviewCalculator();
1416 this._overviewGrid.addEventListener(WebInspector.OverviewGrid.Events.WindowChanged, this._onWindowChanged, this);
1418 this._profileSamples = heapProfileHeader._profileSamples;
1419 if (heapProfileHeader.profileType().profileBeingRecorded() === heapProfileHeader) {
1420 this._profileType = heapProfileHeader._profileType;
1421 this._profileType.addEventListener(WebInspector.TrackingHeapSnapshotProfileType.HeapStatsUpdate, this._onHeapStatsUpdate, this);
1422 this._profileType.addEventListener(WebInspector.TrackingHeapSnapshotProfileType.TrackingStopped, this._onStopTracking, this);
1424 var timestamps = this._profileSamples.timestamps;
1425 var totalTime = this._profileSamples.totalTime;
1426 this._windowLeft = 0.0;
1427 this._windowRight = totalTime && timestamps.length ? (timestamps[timestamps.length - 1] - timestamps[0]) / totalTime : 1.0;
1428 this._overviewGrid.setWindow(this._windowLeft, this._windowRight);
1429 this._yScale = new WebInspector.HeapTrackingOverviewGrid.SmoothScale();
1430 this._xScale = new WebInspector.HeapTrackingOverviewGrid.SmoothScale();
1433 WebInspector.HeapTrackingOverviewGrid.IdsRangeChanged = "IdsRangeChanged";
1435 WebInspector.HeapTrackingOverviewGrid.prototype = {
1436 _onStopTracking: function(event)
1438 this._profileType.removeEventListener(WebInspector.TrackingHeapSnapshotProfileType.HeapStatsUpdate, this._onHeapStatsUpdate, this);
1439 this._profileType.removeEventListener(WebInspector.TrackingHeapSnapshotProfileType.TrackingStopped, this._onStopTracking, this);
1442 _onHeapStatsUpdate: function(event)
1444 this._profileSamples = event.data;
1445 this._scheduleUpdate();
1449 * @param {number} width
1450 * @param {number} height
1452 _drawOverviewCanvas: function(width, height)
1454 if (!this._profileSamples)
1456 var profileSamples = this._profileSamples;
1457 var sizes = profileSamples.sizes;
1458 var topSizes = profileSamples.max;
1459 var timestamps = profileSamples.timestamps;
1460 var startTime = timestamps[0];
1461 var endTime = timestamps[timestamps.length - 1];
1463 var scaleFactor = this._xScale.nextScale(width / profileSamples.totalTime);
1466 * @param {!Array.<number>} sizes
1467 * @param {function(number, number):void} callback
1469 function aggregateAndCall(sizes, callback)
1473 for (var i = 1; i < timestamps.length; ++i) {
1474 var x = Math.floor((timestamps[i] - startTime) * scaleFactor);
1475 if (x !== currentX) {
1477 callback(currentX, size);
1483 callback(currentX, size);
1488 * @param {number} size
1490 function maxSizeCallback(x, size)
1492 maxSize = Math.max(maxSize, size);
1495 aggregateAndCall(sizes, maxSizeCallback);
1497 var yScaleFactor = this._yScale.nextScale(maxSize ? height / (maxSize * 1.1) : 0.0);
1499 this._overviewCanvas.width = width * window.devicePixelRatio;
1500 this._overviewCanvas.height = height * window.devicePixelRatio;
1501 this._overviewCanvas.style.width = width + "px";
1502 this._overviewCanvas.style.height = height + "px";
1504 var context = this._overviewCanvas.getContext("2d");
1505 context.scale(window.devicePixelRatio, window.devicePixelRatio);
1507 context.beginPath();
1508 context.lineWidth = 2;
1509 context.strokeStyle = "rgba(192, 192, 192, 0.6)";
1510 var currentX = (endTime - startTime) * scaleFactor;
1511 context.moveTo(currentX, height - 1);
1512 context.lineTo(currentX, 0);
1514 context.closePath();
1518 var gridLabelHeight = 14;
1520 const maxGridValue = (height - gridLabelHeight) / yScaleFactor;
1521 // The round value calculation is a bit tricky, because
1522 // it has a form k*10^n*1024^m, where k=[1,5], n=[0..3], m is an integer,
1523 // e.g. a round value 10KB is 10240 bytes.
1524 gridValue = Math.pow(1024, Math.floor(Math.log(maxGridValue) / Math.log(1024)));
1525 gridValue *= Math.pow(10, Math.floor(Math.log(maxGridValue / gridValue) / Math.LN10));
1526 if (gridValue * 5 <= maxGridValue)
1528 gridY = Math.round(height - gridValue * yScaleFactor - 0.5) + 0.5;
1529 context.beginPath();
1530 context.lineWidth = 1;
1531 context.strokeStyle = "rgba(0, 0, 0, 0.2)";
1532 context.moveTo(0, gridY);
1533 context.lineTo(width, gridY);
1535 context.closePath();
1540 * @param {number} size
1542 function drawBarCallback(x, size)
1544 context.moveTo(x, height - 1);
1545 context.lineTo(x, Math.round(height - size * yScaleFactor - 1));
1548 context.beginPath();
1549 context.lineWidth = 2;
1550 context.strokeStyle = "rgba(192, 192, 192, 0.6)";
1551 aggregateAndCall(topSizes, drawBarCallback);
1553 context.closePath();
1555 context.beginPath();
1556 context.lineWidth = 2;
1557 context.strokeStyle = "rgba(0, 0, 192, 0.8)";
1558 aggregateAndCall(sizes, drawBarCallback);
1560 context.closePath();
1563 var label = Number.bytesToString(gridValue);
1564 var labelPadding = 4;
1566 var labelY = gridY - 0.5;
1567 var labelWidth = 2 * labelPadding + context.measureText(label).width;
1568 context.beginPath();
1569 context.textBaseline = "bottom";
1570 context.font = "10px " + window.getComputedStyle(this.element, null).getPropertyValue("font-family");
1571 context.fillStyle = "rgba(255, 255, 255, 0.75)";
1572 context.fillRect(labelX, labelY - gridLabelHeight, labelWidth, gridLabelHeight);
1573 context.fillStyle = "rgb(64, 64, 64)";
1574 context.fillText(label, labelX + labelPadding, labelY);
1576 context.closePath();
1580 onResize: function()
1582 this._updateOverviewCanvas = true;
1583 this._scheduleUpdate();
1586 _onWindowChanged: function()
1588 if (!this._updateGridTimerId)
1589 this._updateGridTimerId = setTimeout(this._updateGrid.bind(this), 10);
1592 _scheduleUpdate: function()
1594 if (this._updateTimerId)
1596 this._updateTimerId = setTimeout(this.update.bind(this), 10);
1599 _updateBoundaries: function()
1601 this._windowLeft = this._overviewGrid.windowLeft();
1602 this._windowRight = this._overviewGrid.windowRight();
1603 this._windowWidth = this._windowRight - this._windowLeft;
1608 this._updateTimerId = null;
1609 if (!this.isShowing())
1611 this._updateBoundaries();
1612 this._overviewCalculator._updateBoundaries(this);
1613 this._overviewGrid.updateDividers(this._overviewCalculator);
1614 this._drawOverviewCanvas(this._overviewContainer.clientWidth, this._overviewContainer.clientHeight - 20);
1617 _updateGrid: function()
1619 this._updateGridTimerId = 0;
1620 this._updateBoundaries();
1621 var ids = this._profileSamples.ids;
1622 var timestamps = this._profileSamples.timestamps;
1623 var sizes = this._profileSamples.sizes;
1624 var startTime = timestamps[0];
1625 var totalTime = this._profileSamples.totalTime;
1626 var timeLeft = startTime + totalTime * this._windowLeft;
1627 var timeRight = startTime + totalTime * this._windowRight;
1629 var maxId = ids[ids.length - 1] + 1;
1631 for (var i = 0; i < timestamps.length; ++i) {
1634 if (timestamps[i] > timeRight)
1637 if (timestamps[i] < timeLeft) {
1644 this.dispatchEventToListeners(WebInspector.HeapTrackingOverviewGrid.IdsRangeChanged, {minId: minId, maxId: maxId, size: size});
1647 __proto__: WebInspector.View.prototype
1654 WebInspector.HeapTrackingOverviewGrid.SmoothScale = function()
1656 this._lastUpdate = 0;
1657 this._currentScale = 0.0;
1660 WebInspector.HeapTrackingOverviewGrid.SmoothScale.prototype = {
1662 * @param {number} target
1665 nextScale: function(target) {
1666 target = target || this._currentScale;
1667 if (this._currentScale) {
1668 var now = Date.now();
1669 var timeDeltaMs = now - this._lastUpdate;
1670 this._lastUpdate = now;
1671 var maxChangePerSec = 20;
1672 var maxChangePerDelta = Math.pow(maxChangePerSec, timeDeltaMs / 1000);
1673 var scaleChange = target / this._currentScale;
1674 this._currentScale *= Number.constrain(scaleChange, 1 / maxChangePerDelta, maxChangePerDelta);
1676 this._currentScale = target;
1677 return this._currentScale;
1684 * @implements {WebInspector.TimelineGrid.Calculator}
1686 WebInspector.HeapTrackingOverviewGrid.OverviewCalculator = function()
1690 WebInspector.HeapTrackingOverviewGrid.OverviewCalculator.prototype = {
1692 * @param {!WebInspector.HeapTrackingOverviewGrid} chart
1694 _updateBoundaries: function(chart)
1696 this._minimumBoundaries = 0;
1697 this._maximumBoundaries = chart._profileSamples.totalTime;
1698 this._xScaleFactor = chart._overviewContainer.clientWidth / this._maximumBoundaries;
1702 * @param {number} time
1705 computePosition: function(time)
1707 return (time - this._minimumBoundaries) * this._xScaleFactor;
1711 * @param {number} value
1712 * @param {boolean=} hires
1715 formatTime: function(value, hires)
1717 return Number.secondsToString((value + this._minimumBoundaries) / 1000, hires);
1723 maximumBoundary: function()
1725 return this._maximumBoundaries;
1731 minimumBoundary: function()
1733 return this._minimumBoundaries;
1739 zeroTime: function()
1741 return this._minimumBoundaries;
1747 boundarySpan: function()
1749 return this._maximumBoundaries - this._minimumBoundaries;