d7232206bc4c637cb3f42001cd2e532de3f92e6b
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Views / AuditNavigationSidebarPanel.js
1 /*
2  * Copyright (C) 2018 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.AuditNavigationSidebarPanel = class AuditNavigationSidebarPanel extends WI.NavigationSidebarPanel
27 {
28     constructor()
29     {
30         super("audit", WI.UIString("Audits"));
31     }
32
33     // Public
34
35     showDefaultContentView()
36     {
37         let contentView = new WI.ContentView;
38
39         let contentPlaceholder = WI.createMessageTextView(WI.UIString("No audit selected"));
40         contentView.element.appendChild(contentPlaceholder);
41
42         let importNavigationItem = new WI.ButtonNavigationItem("import-audit", WI.UIString("Import"), "Images/Import.svg", 15, 15);
43         importNavigationItem.buttonStyle = WI.ButtonNavigationItem.Style.ImageAndText;
44         importNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleImportButtonNavigationItemClicked, this);
45
46         let importHelpElement = WI.createNavigationItemHelp(WI.UIString("Press %s to import a test or result file"), importNavigationItem);
47         contentPlaceholder.appendChild(importHelpElement);
48
49         this.contentBrowser.showContentView(contentView);
50     }
51
52     // Protected
53
54     initialLayout()
55     {
56         super.initialLayout();
57
58         this.contentTreeOutline.allowsRepeatSelection = false;
59
60         let navigationBar = new WI.NavigationBar;
61
62         this._startStopButtonNavigationItem = new WI.ToggleButtonNavigationItem("audit-start-stop", WI.UIString("Start"), WI.UIString("Stop"), "Images/AuditStart.svg", "Images/AuditStop.svg", 13, 13);
63         this._startStopButtonNavigationItem.buttonStyle = WI.ButtonNavigationItem.Style.ImageAndText;
64         this._updateStartStopButtonNavigationItemState();
65         this._startStopButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleStartStopButtonNavigationItemClicked, this);
66         navigationBar.addNavigationItem(this._startStopButtonNavigationItem);
67
68         navigationBar.addNavigationItem(new WI.DividerNavigationItem);
69
70         let importButtonNavigationItem = new WI.ButtonNavigationItem("audit-import", WI.UIString("Import"), "Images/Import.svg", 15, 15);
71         importButtonNavigationItem.buttonStyle = WI.ButtonNavigationItem.Style.ImageAndText;
72         importButtonNavigationItem.visibilityPriority = WI.NavigationItem.VisibilityPriority.Low;
73         importButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleImportButtonNavigationItemClicked, this);
74         navigationBar.addNavigationItem(importButtonNavigationItem);
75
76         this.addSubview(navigationBar);
77
78         for (let test of WI.auditManager.tests)
79             this._addTest(test);
80
81         WI.auditManager.results.forEach((result, i) => {
82             this._addResult(result, i);
83         });
84
85         WI.auditManager.addEventListener(WI.AuditManager.Event.TestAdded, this._handleAuditTestAdded, this);
86         WI.auditManager.addEventListener(WI.AuditManager.Event.TestCompleted, this._handleAuditTestCompleted, this);
87         WI.auditManager.addEventListener(WI.AuditManager.Event.TestRemoved, this._handleAuditTestRemoved, this);
88         WI.auditManager.addEventListener(WI.AuditManager.Event.TestScheduled, this._handleAuditTestScheduled, this);
89
90         this.contentTreeOutline.addEventListener(WI.TreeOutline.Event.SelectionDidChange, this._treeSelectionDidChange, this);
91     }
92
93     closed()
94     {
95         super.closed();
96
97         WI.auditManager.removeEventListener(null, null, this);
98     }
99
100     updateFilter()
101     {
102         super.updateFilter();
103
104         if (!this.hasActiveFilters)
105             this._updateNoAuditsPlaceholder();
106     }
107
108     // Private
109
110     _addTest(test)
111     {
112         this.element.classList.add("has-tests");
113
114         this._updateStartStopButtonNavigationItemState();
115
116         let treeElement = new WI.AuditTreeElement(test);
117
118         if (this._resultsFolderTreeElement) {
119             this.contentTreeOutline.insertChild(treeElement, this.contentTreeOutline.children.indexOf(this._resultsFolderTreeElement));
120             this._resultsFolderTreeElement.hidden = !this._resultsFolderTreeElement.children.length;
121         } else
122             this.contentTreeOutline.appendChild(treeElement);
123
124         this.hideEmptyContentPlaceholder();
125     }
126
127     _addResult(result, index)
128     {
129         this.element.classList.add("has-results");
130
131         this._updateStartStopButtonNavigationItemState();
132
133         if (!this._resultsFolderTreeElement) {
134             this._resultsFolderTreeElement = new WI.FolderTreeElement(WI.UIString("Results"));
135             this.contentTreeOutline.appendChild(this._resultsFolderTreeElement);
136         }
137
138         this._resultsFolderTreeElement.expand();
139
140         let resultFolderTreeElement = new WI.FolderTreeElement(WI.UIString("Run %d").format(index + 1));
141         if (result instanceof WI.AuditTestResultBase) {
142             resultFolderTreeElement.subtitle = WI.UIString("Imported");
143             result = [result];
144         }
145         this._resultsFolderTreeElement.appendChild(resultFolderTreeElement);
146
147         for (let resultItem of result)
148             resultFolderTreeElement.appendChild(new WI.AuditTreeElement(resultItem));
149     }
150
151     _updateStartStopButtonNavigationItemState()
152     {
153         this._startStopButtonNavigationItem.toggled = WI.auditManager.runningState !== WI.AuditManager.RunningState.Inactive;
154         this._startStopButtonNavigationItem.enabled = WI.auditManager.tests.length && WI.auditManager.runningState !== WI.AuditManager.RunningState.Stopping;
155     }
156
157     _updateNoAuditsPlaceholder()
158     {
159         if (WI.auditManager.tests.length)
160             return;
161
162         let contentPlaceholder = WI.createMessageTextView(WI.UIString("No Audits"));
163
164         let defaultButtonElement = contentPlaceholder.appendChild(document.createElement("button"));
165         defaultButtonElement.textContent = WI.UIString("Add Default Audits");
166         defaultButtonElement.addEventListener("click", () => {
167             WI.auditManager.addDefaultTestsIfNeeded();
168         });
169
170         contentPlaceholder = this.showEmptyContentPlaceholder(contentPlaceholder);
171
172         if (WI.auditManager.results.length) {
173             console.assert(this.contentTreeOutline.children[0] === this._resultsFolderTreeElement);
174
175             // Move the placeholder to be the first element in the content area, where it will
176             // be styled such that only the button is visible.
177             this.contentView.element.insertBefore(contentPlaceholder, this.contentView.element.firstChild);
178         }
179     }
180
181     _handleAuditTestAdded(event)
182     {
183         this._addTest(event.data.test);
184     }
185
186     _handleAuditTestCompleted(event)
187     {
188         let {result, index} = event.data;
189         this._addResult(result, index);
190     }
191
192     _handleAuditTestRemoved(event)
193     {
194         let {test} = event.data;
195         let treeElement = this.treeElementForRepresentedObject(test);
196         this.contentTreeOutline.removeChild(treeElement);
197
198         this.element.classList.toggle("has-tests", !!WI.auditManager.tests.length);
199
200         this._updateStartStopButtonNavigationItemState();
201         this._updateNoAuditsPlaceholder();
202     }
203
204     _handleAuditTestScheduled(event)
205     {
206         this._updateStartStopButtonNavigationItemState();
207     }
208
209     _treeSelectionDidChange(event)
210     {
211         if (!this.selected)
212             return;
213
214         let treeElement = this.contentTreeOutline.selectedTreeElement;
215         if (!treeElement || treeElement instanceof WI.FolderTreeElement) {
216             this.showDefaultContentView();
217             return;
218         }
219
220         let representedObject = treeElement.representedObject;
221         if (representedObject instanceof WI.AuditTestCase || representedObject instanceof WI.AuditTestGroup
222             || representedObject instanceof WI.AuditTestCaseResult || representedObject instanceof WI.AuditTestGroupResult) {
223             WI.showRepresentedObject(representedObject);
224             return;
225         }
226
227         console.error("Unknown tree element", treeElement);
228     }
229
230     _handleStartStopButtonNavigationItemClicked(event)
231     {
232         if (WI.auditManager.runningState === WI.AuditManager.RunningState.Inactive)
233             WI.auditManager.start();
234         else if (WI.auditManager.runningState === WI.AuditManager.RunningState.Active)
235             WI.auditManager.stop();
236
237         this._updateStartStopButtonNavigationItemState();
238     }
239
240     _handleImportButtonNavigationItemClicked(event)
241     {
242         WI.FileUtilities.importJSON((result) => WI.auditManager.processJSON(result));
243     }
244 };