241a98920c85f9bb9a3e67274db209b7980dafb5
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Views / HeapAllocationsTimelineView.js
1 /*
2  * Copyright (C) 2016 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 WebInspector.HeapAllocationsTimelineView = class HeapAllocationsTimelineView extends WebInspector.TimelineView
27 {
28     constructor(timeline, extraArguments)
29     {
30         super(timeline, extraArguments);
31
32         console.assert(timeline.type === WebInspector.TimelineRecord.Type.HeapAllocations, timeline);
33
34         this.element.classList.add("heap-allocations");
35
36         let columns = {
37             name: {
38                 title: WebInspector.UIString("Name"),
39                 width: "150px",
40             },
41             timestamp: {
42                 title: WebInspector.UIString("Time"),
43                 width: "80px",
44                 sortable: true,
45                 aligned: "right",
46             },
47             size: {
48                 title: WebInspector.UIString("Size"),
49                 width: "80px",
50                 sortable: true,
51                 aligned: "right",
52             },
53         };
54
55         let snapshotTooltip = WebInspector.UIString("Take snapshot");
56         this._takeHeapSnapshotButtonItem = new WebInspector.ButtonNavigationItem("take-snapshot", snapshotTooltip, "Images/Camera.svg", 16, 16);
57         this._takeHeapSnapshotButtonItem.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, this._takeHeapSnapshotClicked, this);
58
59         let defaultToolTip = WebInspector.UIString("Compare snapshots");
60         let activatedToolTip = WebInspector.UIString("Cancel comparison");
61         this._compareHeapSnapshotsButtonItem = new WebInspector.ActivateButtonNavigationItem("compare-heap-snapshots", defaultToolTip, activatedToolTip, "Images/Compare.svg", 16, 16);
62         this._compareHeapSnapshotsButtonItem.addEventListener(WebInspector.ButtonNavigationItem.Event.Clicked, this._compareHeapSnapshotsClicked, this);
63         this._compareHeapSnapshotsButtonItem.enabled = false;
64         this._compareHeapSnapshotsButtonItem.activated = false;
65
66         this._compareHeapSnapshotHelpTextItem = new WebInspector.TextNavigationItem("compare-heap-snapshot-help-text", "");
67
68         this._selectingComparisonHeapSnapshots = false;
69         this._baselineDataGridNode = null;
70         this._baselineHeapSnapshotTimelineRecord = null;
71         this._heapSnapshotDiff = null;
72
73         this._showingSnapshotList = true;
74
75         this._snapshotListPathComponent = new WebInspector.HierarchicalPathComponent(WebInspector.UIString("Snapshot List"), "snapshot-list-icon", "snapshot-list", false, false);
76         this._snapshotListPathComponent.addEventListener(WebInspector.HierarchicalPathComponent.Event.Clicked, this._snapshotListPathComponentClicked, this);
77
78         this._dataGrid = new WebInspector.TimelineDataGrid(null, columns);
79         this._dataGrid.sortColumnIdentifierSetting = new WebInspector.Setting("heap-allocations-timeline-view-sort", "timestamp");
80         this._dataGrid.sortOrderSetting = new WebInspector.Setting("heap-allocations-timeline-view-sort-order", WebInspector.DataGrid.SortOrder.Ascending);
81         this._dataGrid.addEventListener(WebInspector.DataGrid.Event.SelectedNodeChanged, this._dataGridNodeSelected, this);
82         this.addSubview(this._dataGrid);
83
84         this._contentViewContainer = new WebInspector.ContentViewContainer;
85         this._contentViewContainer.addEventListener(WebInspector.ContentViewContainer.Event.CurrentContentViewDidChange, this._currentContentViewDidChange, this);
86         WebInspector.ContentView.addEventListener(WebInspector.ContentView.Event.SelectionPathComponentsDidChange, this._contentViewSelectionPathComponentDidChange, this);
87
88         this._pendingRecords = [];
89
90         timeline.addEventListener(WebInspector.Timeline.Event.RecordAdded, this._heapAllocationsTimelineRecordAdded, this);
91     }
92
93     // Public
94
95     showHeapSnapshotList()
96     {
97         if (this._showingSnapshotList)
98             return;
99
100         this._showingSnapshotList = true;
101         this._heapSnapshotDiff = null;
102         this._cancelSelectComparisonHeapSnapshots();
103
104         this._contentViewContainer.hidden();
105         this.removeSubview(this._contentViewContainer);
106         this.addSubview(this._dataGrid);
107
108         this.dispatchEventToListeners(WebInspector.ContentView.Event.SelectionPathComponentsDidChange);
109         this.dispatchEventToListeners(WebInspector.ContentView.Event.NavigationItemsDidChange);
110     }
111
112     showHeapSnapshotTimelineRecord(heapSnapshotTimelineRecord)
113     {
114         if (this._showingSnapshotList) {
115             this.removeSubview(this._dataGrid);
116             this.addSubview(this._contentViewContainer);
117             this._contentViewContainer.shown();
118         }
119
120         this._showingSnapshotList = false;
121         this._heapSnapshotDiff = null;
122         this._cancelSelectComparisonHeapSnapshots();
123
124         for (let dataGridNode of this._dataGrid.children) {
125             if (dataGridNode.record === heapSnapshotTimelineRecord) {
126                 dataGridNode.select();
127                 break;
128             }
129         }
130
131         let shouldManuallyTriggerContentViewUpdate = this._contentViewContainer.currentContentView && this._contentViewContainer.currentContentView.representedObject === heapSnapshotTimelineRecord.heapSnapshot;
132
133         this._contentViewContainer.showContentViewForRepresentedObject(heapSnapshotTimelineRecord.heapSnapshot);
134
135         if (shouldManuallyTriggerContentViewUpdate)
136             this._currentContentViewDidChange();
137     }
138
139     showHeapSnapshotDiff(heapSnapshotDiff)
140     {
141         if (this._showingSnapshotList) {
142             this.removeSubview(this._dataGrid);
143             this.addSubview(this._contentViewContainer);
144             this._contentViewContainer.shown();
145         }
146
147         this._showingSnapshotList = false;
148         this._heapSnapshotDiff = heapSnapshotDiff;
149
150         this._contentViewContainer.showContentViewForRepresentedObject(heapSnapshotDiff);
151     }
152
153     // Protected
154
155     get navigationItems()
156     {
157         if (this._showingSnapshotList) {
158             let items = [this._takeHeapSnapshotButtonItem, this._compareHeapSnapshotsButtonItem];
159             if (this._selectingComparisonHeapSnapshots)
160                 items.push(this._compareHeapSnapshotHelpTextItem);
161             return items;
162         }
163
164         return this._contentViewContainer.currentContentView.navigationItems;
165     }
166
167     get selectionPathComponents()
168     {
169         let components = [this._snapshotListPathComponent];
170
171         if (this._showingSnapshotList)
172             return components;
173
174         if (this._heapSnapshotDiff) {
175             let firstSnapshotIdentifier = this._heapSnapshotDiff.snapshot1.identifier;
176             let secondSnapshotIdentifier = this._heapSnapshotDiff.snapshot2.identifier;
177             let diffComponent = new WebInspector.HierarchicalPathComponent(WebInspector.UIString("Snapshot Comparison (%d and %d)").format(firstSnapshotIdentifier, secondSnapshotIdentifier), "snapshot-diff-icon", "snapshot-diff");
178             components.push(diffComponent);
179         } else {
180             if (this._dataGrid.selectedNode) {
181                 let heapSnapshotPathComponent = new WebInspector.TimelineDataGridNodePathComponent(this._dataGrid.selectedNode);
182                 heapSnapshotPathComponent.addEventListener(WebInspector.HierarchicalPathComponent.Event.SiblingWasSelected, this._snapshotPathComponentSelected, this);
183                 components.push(heapSnapshotPathComponent);
184             }
185         }
186
187         return components.concat(this._contentViewContainer.currentContentView.selectionPathComponents);
188     }
189
190     userSelectedRecordFromOverview(timelineRecord)
191     {
192         this.showHeapSnapshotTimelineRecord(timelineRecord);
193     }
194
195     shown()
196     {
197         super.shown();
198
199         this._dataGrid.shown();
200
201         if (!this._showingSnapshotList)
202             this._contentViewContainer.shown();
203     }
204
205     hidden()
206     {
207         super.hidden();
208
209         this._dataGrid.hidden();
210
211         if (!this._showingSnapshotList)
212             this._contentViewContainer.hidden();
213     }
214
215     closed()
216     {
217         console.assert(this.representedObject instanceof WebInspector.Timeline);
218         this.representedObject.removeEventListener(null, null, this);
219
220         this._dataGrid.closed();
221
222         this._contentViewContainer.closeAllContentViews();
223     }
224
225     layout()
226     {
227         // Wait to show records until our zeroTime has been set.
228         if (this._pendingRecords.length && this.zeroTime) {
229             for (let heapAllocationsTimelineRecord of this._pendingRecords) {
230                 let dataGridNode = new WebInspector.HeapAllocationsTimelineDataGridNode(heapAllocationsTimelineRecord, this.zeroTime, this);
231                 this._dataGrid.addRowInSortOrder(null, dataGridNode);
232             }
233
234             this._pendingRecords = [];
235             this._updateCompareHeapSnapshotButton();
236         }
237     }
238
239     reset()
240     {
241         super.reset();
242
243         this._dataGrid.reset();
244
245         this.showHeapSnapshotList();
246         this._pendingRecords = [];
247         this._updateCompareHeapSnapshotButton();
248     }
249
250     // Private
251
252     _heapAllocationsTimelineRecordAdded(event)
253     {
254         this._pendingRecords.push(event.data.record);
255
256         this.needsLayout();
257     }
258
259     _snapshotListPathComponentClicked(event)
260     {
261         this.showHeapSnapshotList();
262     }
263
264     _snapshotPathComponentSelected(event)
265     {
266         console.assert(event.data.pathComponent.representedObject instanceof WebInspector.HeapAllocationsTimelineRecord);
267         this.showHeapSnapshotTimelineRecord(event.data.pathComponent.representedObject);
268     }
269
270     _currentContentViewDidChange(event)
271     {
272         this.dispatchEventToListeners(WebInspector.ContentView.Event.SelectionPathComponentsDidChange);
273         this.dispatchEventToListeners(WebInspector.ContentView.Event.NavigationItemsDidChange);
274     }
275
276     _contentViewSelectionPathComponentDidChange(event)
277     {
278         if (event.target !== this._contentViewContainer.currentContentView)
279             return;
280         this.dispatchEventToListeners(WebInspector.ContentView.Event.SelectionPathComponentsDidChange);
281     }
282
283     _updateCompareHeapSnapshotButton()
284     {
285         let hasAtLeastTwoSnapshots = false;
286
287         let count = 0;
288         for (let node of this._dataGrid.children) {
289             if (node.revealed && !node.hidden) {
290                 count++;
291                 if (count === 2) {
292                     hasAtLeastTwoSnapshots = true;
293                     break;
294                 }
295             }
296         }
297
298         this._compareHeapSnapshotsButtonItem.enabled = hasAtLeastTwoSnapshots;
299     }
300
301     _takeHeapSnapshotClicked()
302     {
303         HeapAgent.snapshot(function(error, timestamp, snapshotStringData) {
304             let workerProxy = WebInspector.HeapSnapshotWorkerProxy.singleton();
305             workerProxy.createSnapshot(snapshotStringData, ({objectId, snapshot: serializedSnapshot}) => {
306                 let snapshot = WebInspector.HeapSnapshotProxy.deserialize(objectId, serializedSnapshot);
307                 WebInspector.timelineManager.heapSnapshotAdded(timestamp, snapshot);
308             });
309         });
310     }
311
312     _cancelSelectComparisonHeapSnapshots()
313     {
314         if (!this._selectingComparisonHeapSnapshots)
315             return;
316
317         if (this._baselineDataGridNode)
318             this._baselineDataGridNode.clearBaseline();
319
320         this._selectingComparisonHeapSnapshots = false;
321         this._baselineDataGridNode = null;
322         this._baselineHeapSnapshotTimelineRecord = null;
323
324         this._compareHeapSnapshotsButtonItem.activated = false;
325
326         this.dispatchEventToListeners(WebInspector.ContentView.Event.NavigationItemsDidChange);
327     }
328
329     _compareHeapSnapshotsClicked(event)
330     {
331         let newActivated = !this._compareHeapSnapshotsButtonItem.activated;
332         this._compareHeapSnapshotsButtonItem.activated = newActivated;
333
334         if (!newActivated) {
335             this._cancelSelectComparisonHeapSnapshots();
336             return;
337         }
338
339         if (this._dataGrid.selectedNode)
340             this._dataGrid.selectedNode.deselect();
341
342         this._selectingComparisonHeapSnapshots = true;
343         this._baselineHeapSnapshotTimelineRecord = null;
344         this._compareHeapSnapshotHelpTextItem.text = WebInspector.UIString("Select baseline snapshot");
345
346         this.dispatchEventToListeners(WebInspector.ContentView.Event.NavigationItemsDidChange);
347     }
348
349     _dataGridNodeSelected(event)
350     {
351         if (!this._selectingComparisonHeapSnapshots)
352             return;
353
354         let dataGridNode = this._dataGrid.selectedNode;
355         if (!dataGridNode)
356             return;
357
358         let heapAllocationsTimelineRecord = dataGridNode.record;
359         if (this._baselineHeapSnapshotTimelineRecord === heapAllocationsTimelineRecord) {
360             this._dataGrid.selectedNode.deselect();
361             return;
362         }
363
364         // Selected Baseline.
365         if (!this._baselineHeapSnapshotTimelineRecord) {
366             this._baselineDataGridNode = dataGridNode;
367             this._baselineDataGridNode.markAsBaseline();
368             this._baselineHeapSnapshotTimelineRecord = heapAllocationsTimelineRecord;
369             this._dataGrid.selectedNode.deselect();
370             this._compareHeapSnapshotHelpTextItem.text = WebInspector.UIString("Select comparison snapshot");
371             this.dispatchEventToListeners(WebInspector.ContentView.Event.NavigationItemsDidChange);
372             return;
373         }
374
375         // Selected Comparison.
376         let snapshot1 = this._baselineHeapSnapshotTimelineRecord.heapSnapshot;
377         let snapshot2 = heapAllocationsTimelineRecord.heapSnapshot;
378         let workerProxy = WebInspector.HeapSnapshotWorkerProxy.singleton();
379         workerProxy.createSnapshotDiff(snapshot1.proxyObjectId, snapshot2.proxyObjectId, ({objectId, snapshotDiff: serializedSnapshotDiff}) => {
380             let diff = WebInspector.HeapSnapshotDiffProxy.deserialize(objectId, serializedSnapshotDiff);
381             this.showHeapSnapshotDiff(diff);
382         });
383
384         this._baselineDataGridNode.clearBaseline();
385         this._selectingComparisonHeapSnapshots = false;
386         this._compareHeapSnapshotsButtonItem.activated = false;
387     }
388 };