Web Inspector: Elements: Styles: add icons for various CSS rule types
[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 WI.HeapAllocationsTimelineView = class HeapAllocationsTimelineView extends WI.TimelineView
27 {
28     constructor(timeline, extraArguments)
29     {
30         super(timeline, extraArguments);
31
32         console.assert(timeline.type === WI.TimelineRecord.Type.HeapAllocations, timeline);
33
34         this.element.classList.add("heap-allocations");
35
36         let columns = {
37             name: {
38                 title: WI.UIString("Name"),
39                 width: "150px",
40                 icon: true,
41             },
42             timestamp: {
43                 title: WI.UIString("Time"),
44                 width: "80px",
45                 sortable: true,
46                 aligned: "right",
47             },
48             size: {
49                 title: WI.UIString("Size"),
50                 width: "80px",
51                 sortable: true,
52                 aligned: "right",
53             },
54             liveSize: {
55                 title: WI.UIString("Live Size"),
56                 width: "80px",
57                 sortable: true,
58                 aligned: "right",
59             },
60         };
61
62         this._importButtonNavigationItem = new WI.ButtonNavigationItem("import", WI.UIString("Import"), "Images/Import.svg", 15, 15);
63         this._importButtonNavigationItem.tooltip = WI.UIString("Import");
64         this._importButtonNavigationItem.buttonStyle = WI.ButtonNavigationItem.Style.ImageAndText;
65         this._importButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._importButtonNavigationItemClicked, this);
66
67         let snapshotTooltip = WI.UIString("Take snapshot");
68         this._takeHeapSnapshotButtonItem = new WI.ButtonNavigationItem("take-snapshot", snapshotTooltip, "Images/Camera.svg", 16, 16);
69         this._takeHeapSnapshotButtonItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._takeHeapSnapshotClicked, this);
70
71         let defaultToolTip = WI.UIString("Compare snapshots");
72         let activatedToolTip = WI.UIString("Cancel comparison");
73         this._compareHeapSnapshotsButtonItem = new WI.ActivateButtonNavigationItem("compare-heap-snapshots", defaultToolTip, activatedToolTip, "Images/Compare.svg", 16, 16);
74         this._compareHeapSnapshotsButtonItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._compareHeapSnapshotsClicked, this);
75         this._compareHeapSnapshotsButtonItem.enabled = false;
76         this._compareHeapSnapshotsButtonItem.activated = false;
77
78         this._compareHeapSnapshotHelpTextItem = new WI.TextNavigationItem("compare-heap-snapshot-help-text", "");
79
80         this._selectingComparisonHeapSnapshots = false;
81         this._baselineDataGridNode = null;
82         this._baselineHeapSnapshotTimelineRecord = null;
83         this._heapSnapshotDiff = null;
84
85         this._snapshotListScrollTop = 0;
86         this._showingSnapshotList = true;
87
88         this._snapshotListPathComponent = new WI.HierarchicalPathComponent(WI.UIString("Snapshot List"), "snapshot-list-icon", "snapshot-list", false, false);
89         this._snapshotListPathComponent.addEventListener(WI.HierarchicalPathComponent.Event.Clicked, this._snapshotListPathComponentClicked, this);
90
91         this._dataGrid = new WI.TimelineDataGrid(columns);
92         this._dataGrid.sortColumnIdentifier = "timestamp";
93         this._dataGrid.sortOrder = WI.DataGrid.SortOrder.Ascending;
94         this._dataGrid.createSettings("heap-allocations-timeline-view");
95         this._dataGrid.addEventListener(WI.DataGrid.Event.SelectedNodeChanged, this._dataGridNodeSelected, this);
96         this.setupDataGrid(this._dataGrid);
97         this.addSubview(this._dataGrid);
98
99         this._contentViewContainer = new WI.ContentViewContainer;
100         this._contentViewContainer.addEventListener(WI.ContentViewContainer.Event.CurrentContentViewDidChange, this._currentContentViewDidChange, this);
101         WI.ContentView.addEventListener(WI.ContentView.Event.SelectionPathComponentsDidChange, this._contentViewSelectionPathComponentDidChange, this);
102
103         this._pendingRecords = [];
104
105         timeline.addEventListener(WI.Timeline.Event.RecordAdded, this._heapAllocationsTimelineRecordAdded, this);
106
107         WI.HeapSnapshotProxy.addEventListener(WI.HeapSnapshotProxy.Event.Invalidated, this._heapSnapshotInvalidated, this);
108         WI.HeapSnapshotWorkerProxy.singleton().addEventListener("HeapSnapshot.CollectionEvent", this._heapSnapshotCollectionEvent, this);
109     }
110
111     // Public
112
113     get scrollableElements()
114     {
115         if (this._showingSnapshotList)
116             return [this._dataGrid.scrollContainer];
117         if (this._contentViewContainer.currentContentView)
118             return this._contentViewContainer.currentContentView.scrollableElements;
119         return [];
120     }
121
122     showHeapSnapshotList()
123     {
124         if (this._showingSnapshotList)
125             return;
126
127         this._showingSnapshotList = true;
128         this._heapSnapshotDiff = null;
129         this._cancelSelectComparisonHeapSnapshots();
130
131         this._contentViewContainer.hidden();
132         this.removeSubview(this._contentViewContainer);
133         this.addSubview(this._dataGrid);
134
135         this._dataGrid.scrollContainer.scrollTop = this._snapshotListScrollTop;
136
137         this.dispatchEventToListeners(WI.ContentView.Event.SelectionPathComponentsDidChange);
138         this.dispatchEventToListeners(WI.ContentView.Event.NavigationItemsDidChange);
139     }
140
141     showHeapSnapshotTimelineRecord(heapSnapshotTimelineRecord)
142     {
143         if (this._showingSnapshotList) {
144             this._snapshotListScrollTop = this._dataGrid.scrollContainer.scrollTop;
145             this.removeSubview(this._dataGrid);
146             this.addSubview(this._contentViewContainer);
147             this._contentViewContainer.shown();
148         }
149
150         this._showingSnapshotList = false;
151         this._heapSnapshotDiff = null;
152         this._cancelSelectComparisonHeapSnapshots();
153
154         for (let dataGridNode of this._dataGrid.children) {
155             if (dataGridNode.record === heapSnapshotTimelineRecord) {
156                 dataGridNode.hidden = false;
157                 dataGridNode.select();
158                 break;
159             }
160         }
161
162         let shouldTriggerContentViewUpdate = this._contentViewContainer.currentContentView && this._contentViewContainer.currentContentView.representedObject === heapSnapshotTimelineRecord.heapSnapshot;
163
164         this._contentViewContainer.showContentViewForRepresentedObject(heapSnapshotTimelineRecord.heapSnapshot);
165
166         if (shouldTriggerContentViewUpdate)
167             this._currentContentViewDidChange();
168     }
169
170     showHeapSnapshotDiff(heapSnapshotDiff)
171     {
172         if (this._showingSnapshotList) {
173             this.removeSubview(this._dataGrid);
174             this.addSubview(this._contentViewContainer);
175             this._contentViewContainer.shown();
176         }
177
178         this._showingSnapshotList = false;
179         this._heapSnapshotDiff = heapSnapshotDiff;
180
181         this._contentViewContainer.showContentViewForRepresentedObject(heapSnapshotDiff);
182     }
183
184     // Protected
185
186     get navigationItems()
187     {
188         if (this._showingSnapshotList) {
189             let items = [this._importButtonNavigationItem, this._takeHeapSnapshotButtonItem, this._compareHeapSnapshotsButtonItem];
190             if (this._selectingComparisonHeapSnapshots)
191                 items.push(this._compareHeapSnapshotHelpTextItem);
192             return items;
193         }
194
195         if (this._contentViewContainer.currentContentView)
196             return this._contentViewContainer.currentContentView.navigationItems;
197
198         return [];
199     }
200
201     get selectionPathComponents()
202     {
203         let components = [this._snapshotListPathComponent];
204
205         if (this._showingSnapshotList)
206             return components;
207
208         if (this._heapSnapshotDiff) {
209             let firstSnapshotIdentifier = this._heapSnapshotDiff.snapshot1.identifier;
210             let secondSnapshotIdentifier = this._heapSnapshotDiff.snapshot2.identifier;
211             let diffComponent = new WI.HierarchicalPathComponent(WI.UIString("Snapshot Comparison (%d and %d)").format(firstSnapshotIdentifier, secondSnapshotIdentifier), "snapshot-diff-icon", "snapshot-diff");
212             components.push(diffComponent);
213         } else if (this._dataGrid.selectedNode) {
214             let heapSnapshotPathComponent = new WI.HeapAllocationsTimelineDataGridNodePathComponent(this._dataGrid.selectedNode);
215             heapSnapshotPathComponent.addEventListener(WI.HierarchicalPathComponent.Event.SiblingWasSelected, this._snapshotPathComponentSelected, this);
216             components.push(heapSnapshotPathComponent);
217         }
218
219         if (this._contentViewContainer.currentContentView)
220             components = components.concat(this._contentViewContainer.currentContentView.selectionPathComponents);
221
222         return components;
223     }
224
225     selectRecord(record)
226     {
227         if (record)
228             this.showHeapSnapshotTimelineRecord(record);
229         else
230             this.showHeapSnapshotList();
231     }
232
233     shown()
234     {
235         super.shown();
236
237         this._dataGrid.shown();
238
239         if (!this._showingSnapshotList)
240             this._contentViewContainer.shown();
241     }
242
243     hidden()
244     {
245         super.hidden();
246
247         this._dataGrid.hidden();
248
249         if (!this._showingSnapshotList)
250             this._contentViewContainer.hidden();
251     }
252
253     closed()
254     {
255         console.assert(this.representedObject instanceof WI.Timeline);
256         this.representedObject.removeEventListener(null, null, this);
257
258         this._dataGrid.closed();
259
260         this._contentViewContainer.closeAllContentViews();
261
262         WI.ContentView.removeEventListener(null, null, this);
263         WI.HeapSnapshotProxy.removeEventListener(null, null, this);
264         WI.HeapSnapshotWorkerProxy.singleton().removeEventListener("HeapSnapshot.CollectionEvent", this._heapSnapshotCollectionEvent, this);
265     }
266
267     layout()
268     {
269         super.layout();
270
271         if (!this._pendingRecords.length)
272             return;
273
274         for (let heapAllocationsTimelineRecord of this._pendingRecords) {
275             this._dataGrid.addRowInSortOrder(new WI.HeapAllocationsTimelineDataGridNode(heapAllocationsTimelineRecord, {
276                 graphDataSource: this,
277                 heapAllocationsView: this,
278             }));
279         }
280
281         this._pendingRecords = [];
282         this._updateCompareHeapSnapshotButton();
283     }
284
285     reset()
286     {
287         super.reset();
288
289         this._dataGrid.reset();
290
291         this.showHeapSnapshotList();
292         this._pendingRecords = [];
293         this._updateCompareHeapSnapshotButton();
294     }
295
296     updateFilter(filters)
297     {
298         if (this._showingSnapshotList) {
299             this._dataGrid.filterText = filters ? filters.text : "";
300             return;
301         }
302
303         console.assert(this._contentViewContainer.currentContentView);
304         this._contentViewContainer.currentContentView.updateFilter(filters);
305     }
306
307     // Private
308
309     _heapAllocationsTimelineRecordAdded(event)
310     {
311         this._pendingRecords.push(event.data.record);
312
313         this.needsLayout();
314     }
315
316     _heapSnapshotCollectionEvent(event)
317     {
318         function updateHeapSnapshotForEvent(heapSnapshot) {
319             if (heapSnapshot.invalid)
320                 return;
321             heapSnapshot.updateForCollectionEvent(event);
322         }
323
324         for (let node of this._dataGrid.children)
325             updateHeapSnapshotForEvent(node.record.heapSnapshot);
326         for (let record of this._pendingRecords)
327             updateHeapSnapshotForEvent(record.heapSnapshot);
328         if (this._heapSnapshotDiff)
329             updateHeapSnapshotForEvent(this._heapSnapshotDiff);
330     }
331
332     _snapshotListPathComponentClicked(event)
333     {
334         this.showHeapSnapshotList();
335     }
336
337     _snapshotPathComponentSelected(event)
338     {
339         console.assert(event.data.pathComponent.representedObject instanceof WI.HeapAllocationsTimelineRecord);
340         this.showHeapSnapshotTimelineRecord(event.data.pathComponent.representedObject);
341     }
342
343     _currentContentViewDidChange(event)
344     {
345         this.dispatchEventToListeners(WI.ContentView.Event.SelectionPathComponentsDidChange);
346         this.dispatchEventToListeners(WI.ContentView.Event.NavigationItemsDidChange);
347     }
348
349     _contentViewSelectionPathComponentDidChange(event)
350     {
351         if (event.target !== this._contentViewContainer.currentContentView)
352             return;
353         this.dispatchEventToListeners(WI.ContentView.Event.SelectionPathComponentsDidChange);
354     }
355
356     _heapSnapshotInvalidated(event)
357     {
358         let heapSnapshot = event.target;
359
360         if (this._baselineHeapSnapshotTimelineRecord) {
361             if (heapSnapshot === this._baselineHeapSnapshotTimelineRecord.heapSnapshot)
362                 this._cancelSelectComparisonHeapSnapshots();
363         }
364
365         if (this._heapSnapshotDiff) {
366             if (heapSnapshot === this._heapSnapshotDiff.snapshot1 || heapSnapshot === this._heapSnapshotDiff.snapshot2)
367                 this.showHeapSnapshotList();
368         } else if (this._dataGrid.selectedNode) {
369             if (heapSnapshot === this._dataGrid.selectedNode.record.heapSnapshot)
370                 this.showHeapSnapshotList();
371         }
372
373         this._updateCompareHeapSnapshotButton();
374     }
375
376     _updateCompareHeapSnapshotButton()
377     {
378         let hasAtLeastTwoValidSnapshots = false;
379
380         let count = 0;
381         for (let node of this._dataGrid.children) {
382             if (node.revealed && !node.hidden && !node.record.heapSnapshot.invalid) {
383                 count++;
384                 if (count === 2) {
385                     hasAtLeastTwoValidSnapshots = true;
386                     break;
387                 }
388             }
389         }
390
391         this._compareHeapSnapshotsButtonItem.enabled = hasAtLeastTwoValidSnapshots;
392     }
393
394     _importButtonNavigationItemClicked()
395     {
396         WI.FileUtilities.importText((result) => {
397             let snapshotStringData = result.text;
398             let workerProxy = WI.HeapSnapshotWorkerProxy.singleton();
399             workerProxy.createImportedSnapshot(snapshotStringData, result.filename, ({objectId, snapshot: serializedSnapshot}) => {
400                 let snapshot = WI.HeapSnapshotProxy.deserialize(objectId, serializedSnapshot);
401                 snapshot.snapshotStringData = snapshotStringData;
402                 const timestamp = NaN;
403                 WI.timelineManager.heapSnapshotAdded(timestamp, snapshot);
404                 this.dispatchEventToListeners(WI.TimelineView.Event.NeedsEntireSelectedRange);
405             });
406         });
407     }
408
409     _takeHeapSnapshotClicked()
410     {
411         HeapAgent.snapshot((error, timestamp, snapshotStringData) => {
412             let workerProxy = WI.HeapSnapshotWorkerProxy.singleton();
413             workerProxy.createSnapshot(snapshotStringData, ({objectId, snapshot: serializedSnapshot}) => {
414                 let snapshot = WI.HeapSnapshotProxy.deserialize(objectId, serializedSnapshot);
415                 snapshot.snapshotStringData = snapshotStringData;
416                 WI.timelineManager.heapSnapshotAdded(timestamp, snapshot);
417                 this.dispatchEventToListeners(WI.TimelineView.Event.NeedsEntireSelectedRange);
418             });
419         });
420     }
421
422     _cancelSelectComparisonHeapSnapshots()
423     {
424         if (!this._selectingComparisonHeapSnapshots)
425             return;
426
427         if (this._baselineDataGridNode)
428             this._baselineDataGridNode.clearBaseline();
429
430         this._selectingComparisonHeapSnapshots = false;
431         this._baselineDataGridNode = null;
432         this._baselineHeapSnapshotTimelineRecord = null;
433
434         this._compareHeapSnapshotsButtonItem.activated = false;
435
436         this.dispatchEventToListeners(WI.ContentView.Event.NavigationItemsDidChange);
437     }
438
439     _compareHeapSnapshotsClicked(event)
440     {
441         let newActivated = !this._compareHeapSnapshotsButtonItem.activated;
442         this._compareHeapSnapshotsButtonItem.activated = newActivated;
443
444         if (!newActivated) {
445             this._cancelSelectComparisonHeapSnapshots();
446             return;
447         }
448
449         if (this._dataGrid.selectedNode)
450             this._dataGrid.selectedNode.deselect();
451
452         this._selectingComparisonHeapSnapshots = true;
453         this._baselineHeapSnapshotTimelineRecord = null;
454         this._compareHeapSnapshotHelpTextItem.text = WI.UIString("Select baseline snapshot");
455
456         this.dispatchEventToListeners(WI.ContentView.Event.NavigationItemsDidChange);
457     }
458
459     _dataGridNodeSelected(event)
460     {
461         if (!this._selectingComparisonHeapSnapshots)
462             return;
463
464         let dataGridNode = this._dataGrid.selectedNode;
465         if (!dataGridNode)
466             return;
467
468         let heapAllocationsTimelineRecord = dataGridNode.record;
469
470         // Cancel the selection if the heap snapshot is invalid, or was already selected as the baseline.
471         if (heapAllocationsTimelineRecord.heapSnapshot.invalid || this._baselineHeapSnapshotTimelineRecord === heapAllocationsTimelineRecord) {
472             this._dataGrid.selectedNode.deselect();
473             return;
474         }
475
476         // Selected Baseline.
477         if (!this._baselineHeapSnapshotTimelineRecord) {
478             this._baselineDataGridNode = dataGridNode;
479             this._baselineDataGridNode.markAsBaseline();
480             this._baselineHeapSnapshotTimelineRecord = heapAllocationsTimelineRecord;
481             this._dataGrid.selectedNode.deselect();
482             this._compareHeapSnapshotHelpTextItem.text = WI.UIString("Select comparison snapshot");
483             this.dispatchEventToListeners(WI.ContentView.Event.NavigationItemsDidChange);
484             return;
485         }
486
487         // Selected Comparison.
488         let snapshot1 = this._baselineHeapSnapshotTimelineRecord.heapSnapshot;
489         let snapshot2 = heapAllocationsTimelineRecord.heapSnapshot;
490         if (snapshot1.identifier > snapshot2.identifier)
491             [snapshot1, snapshot2] = [snapshot2, snapshot1];
492
493         let workerProxy = WI.HeapSnapshotWorkerProxy.singleton();
494         workerProxy.createSnapshotDiff(snapshot1.proxyObjectId, snapshot2.proxyObjectId, ({objectId, snapshotDiff: serializedSnapshotDiff}) => {
495             let diff = WI.HeapSnapshotDiffProxy.deserialize(objectId, serializedSnapshotDiff);
496             this.showHeapSnapshotDiff(diff);
497         });
498
499         this._baselineDataGridNode.clearBaseline();
500         this._baselineDataGridNode = null;
501         this._selectingComparisonHeapSnapshots = false;
502         this._compareHeapSnapshotsButtonItem.activated = false;
503     }
504 };