1/*
2 * Copyright (C) 2014 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
26WebInspector.NetworkTimelineView = function(recording)
27{
28    WebInspector.TimelineView.call(this);
29
30    this.navigationSidebarTreeOutline.onselect = this._treeElementSelected.bind(this);
31    this.navigationSidebarTreeOutline.ondeselect = this._treeElementDeselected.bind(this);
32    this.navigationSidebarTreeOutline.element.classList.add(WebInspector.NavigationSidebarPanel.HideDisclosureButtonsStyleClassName);
33    this.navigationSidebarTreeOutline.element.classList.add(WebInspector.NetworkTimelineView.TreeOutlineStyleClassName);
34
35    var columns = {domain: {}, type: {}, method: {}, scheme: {}, statusCode: {}, cached: {}, size: {}, transferSize: {}, requestSent: {}, latency: {}, duration: {}};
36
37    columns.domain.title = WebInspector.UIString("Domain");
38    columns.domain.width = "10%";
39
40    columns.type.title = WebInspector.UIString("Type");
41    columns.type.width = "8%";
42    columns.type.scopeBar = WebInspector.TimelineDataGrid.createColumnScopeBar("network", WebInspector.Resource.Type);
43
44    columns.method.title = WebInspector.UIString("Method");
45    columns.method.width = "6%";
46
47    columns.scheme.title = WebInspector.UIString("Scheme");
48    columns.scheme.width = "6%";
49
50    columns.statusCode.title = WebInspector.UIString("Status");
51    columns.statusCode.width = "6%";
52
53    columns.cached.title = WebInspector.UIString("Cached");
54    columns.cached.width = "6%";
55
56    columns.size.title = WebInspector.UIString("Size");
57    columns.size.width = "8%";
58    columns.size.aligned = "right";
59
60    columns.transferSize.title = WebInspector.UIString("Transfered");
61    columns.transferSize.width = "8%";
62    columns.transferSize.aligned = "right";
63
64    columns.requestSent.title = WebInspector.UIString("Start Time");
65    columns.requestSent.width = "9%";
66    columns.requestSent.aligned = "right";
67
68    columns.latency.title = WebInspector.UIString("Latency");
69    columns.latency.width = "9%";
70    columns.latency.aligned = "right";
71
72    columns.duration.title = WebInspector.UIString("Duration");
73    columns.duration.width = "9%";
74    columns.duration.aligned = "right";
75
76    for (var column in columns)
77        columns[column].sortable = true;
78
79    this._dataGrid = new WebInspector.TimelineDataGrid(this.navigationSidebarTreeOutline, columns);
80    this._dataGrid.addEventListener(WebInspector.TimelineDataGrid.Event.FiltersDidChange, this._dataGridFiltersDidChange, this);
81    this._dataGrid.addEventListener(WebInspector.DataGrid.Event.SelectedNodeChanged, this._dataGridNodeSelected, this);
82    this._dataGrid.sortColumnIdentifier = "requestSent";
83    this._dataGrid.sortOrder = WebInspector.DataGrid.SortOrder.Ascending;
84
85    this.element.classList.add(WebInspector.NetworkTimelineView.StyleClassName);
86    this.element.appendChild(this._dataGrid.element);
87
88    var networkTimeline = recording.timelines.get(WebInspector.TimelineRecord.Type.Network);
89    networkTimeline.addEventListener(WebInspector.Timeline.Event.RecordAdded, this._networkTimelineRecordAdded, this);
90
91    this._pendingRecords = [];
92};
93
94WebInspector.NetworkTimelineView.StyleClassName = "network";
95WebInspector.NetworkTimelineView.TreeOutlineStyleClassName = "network";
96
97WebInspector.NetworkTimelineView.prototype = {
98    constructor: WebInspector.NetworkTimelineView,
99    __proto__: WebInspector.TimelineView.prototype,
100
101    // Public
102
103    get navigationSidebarTreeOutlineLabel()
104    {
105        return WebInspector.UIString("Resources");
106    },
107
108    shown: function()
109    {
110        WebInspector.TimelineView.prototype.shown.call(this);
111
112        this._dataGrid.shown();
113    },
114
115    hidden: function()
116    {
117        this._dataGrid.hidden();
118
119        WebInspector.TimelineView.prototype.hidden.call(this);
120    },
121
122    updateLayout: function()
123    {
124        WebInspector.TimelineView.prototype.updateLayout.call(this);
125
126        this._dataGrid.updateLayout();
127
128        this._processPendingRecords();
129    },
130
131    matchTreeElementAgainstCustomFilters: function(treeElement)
132    {
133        return this._dataGrid.treeElementMatchesActiveScopeFilters(treeElement);
134    },
135
136    reset: function()
137    {
138        WebInspector.TimelineView.prototype.reset.call(this);
139
140        this._dataGrid.reset();
141    },
142
143    // Protected
144
145    treeElementPathComponentSelected: function(event)
146    {
147        var dataGridNode = this._dataGrid.dataGridNodeForTreeElement(event.data.pathComponent.generalTreeElement);
148        if (!dataGridNode)
149            return;
150        dataGridNode.revealAndSelect();
151    },
152
153    // Private
154
155    _processPendingRecords: function()
156    {
157        if (!this._pendingRecords.length)
158            return;
159
160        for (var resourceTimelineRecord of this._pendingRecords) {
161            // Skip the record if it already exists in the tree.
162            var treeElement = this.navigationSidebarTreeOutline.findTreeElement(resourceTimelineRecord.resource);
163            if (treeElement)
164                continue;
165
166            treeElement = new WebInspector.ResourceTreeElement(resourceTimelineRecord.resource);
167            var dataGridNode = new WebInspector.ResourceTimelineDataGridNode(resourceTimelineRecord, false, this);
168
169            this._dataGrid.addRowInSortOrder(treeElement, dataGridNode);
170        }
171
172        this._pendingRecords = [];
173    },
174
175    _networkTimelineRecordAdded: function(event)
176    {
177        var resourceTimelineRecord = event.data.record;
178        console.assert(resourceTimelineRecord instanceof WebInspector.ResourceTimelineRecord);
179
180        this._pendingRecords.push(resourceTimelineRecord);
181
182        this.needsLayout();
183    },
184
185    _dataGridFiltersDidChange: function(event)
186    {
187        WebInspector.timelineSidebarPanel.updateFilter();
188    },
189
190    _dataGridNodeSelected: function(event)
191    {
192        this.dispatchEventToListeners(WebInspector.TimelineView.Event.SelectionPathComponentsDidChange);
193    },
194
195    _treeElementDeselected: function(treeElement)
196    {
197        if (treeElement.status)
198            treeElement.status = "";
199    },
200
201    _treeElementSelected: function(treeElement, selectedByUser)
202    {
203        if (this._dataGrid.shouldIgnoreSelectionEvent())
204            return;
205
206        if (!WebInspector.timelineSidebarPanel.canShowDifferentContentView())
207            return;
208
209        if (treeElement instanceof WebInspector.FolderTreeElement)
210            return;
211
212        if (treeElement instanceof WebInspector.ResourceTreeElement || treeElement instanceof WebInspector.ScriptTreeElement) {
213            WebInspector.resourceSidebarPanel.showSourceCode(treeElement.representedObject);
214            this._updateTreeElementWithCloseButton(treeElement);
215            return;
216        }
217
218        console.error("Unknown tree element selected.");
219    },
220
221    _updateTreeElementWithCloseButton: function(treeElement)
222    {
223        if (this._closeStatusButton) {
224            treeElement.status = this._closeStatusButton.element;
225            return;
226        }
227
228        wrappedSVGDocument(platformImagePath("Close.svg"), null, WebInspector.UIString("Close resource view"), function(element) {
229            this._closeStatusButton = new WebInspector.TreeElementStatusButton(element);
230            this._closeStatusButton.addEventListener(WebInspector.TreeElementStatusButton.Event.Clicked, this._closeStatusButtonClicked, this);
231            if (treeElement === this.navigationSidebarTreeOutline.selectedTreeElement)
232                this._updateTreeElementWithCloseButton(treeElement);
233        }.bind(this));
234    },
235
236    _closeStatusButtonClicked: function(event)
237    {
238        this.navigationSidebarTreeOutline.selectedTreeElement.deselect();
239        WebInspector.timelineSidebarPanel.showTimelineView(WebInspector.TimelineRecord.Type.Network);
240    }
241};
242