Web Inspector: REGRESSION: Audit: default audits aren't added when an existing audit...
[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         if (WI.auditManager.editing) {
40             let contentPlaceholder = WI.createMessageTextView(WI.UIString("Editing audits"));
41             contentPlaceholder.classList.add("finish-editing-audits-placeholder");
42             contentView.element.appendChild(contentPlaceholder);
43
44             let finishEditingNavigationItem = new WI.ButtonNavigationItem("finish-editing-audits", WI.UIString("Done"));
45             finishEditingNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, (event) => {
46                 WI.auditManager.editing = false;
47             });
48
49             let importHelpElement = WI.createNavigationItemHelp(WI.UIString("Press %s to stop editing"), finishEditingNavigationItem);
50             contentPlaceholder.appendChild(importHelpElement);
51         } else {
52             let contentPlaceholder = WI.createMessageTextView(WI.UIString("No audit selected"));
53             contentView.element.appendChild(contentPlaceholder);
54
55             let importNavigationItem = new WI.ButtonNavigationItem("import-audit", WI.UIString("Import"), "Images/Import.svg", 15, 15);
56             importNavigationItem.buttonStyle = WI.ButtonNavigationItem.Style.ImageAndText;
57             importNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleImportButtonNavigationItemClicked, this);
58
59             let importHelpElement = WI.createNavigationItemHelp(WI.UIString("Press %s to import a test or result file"), importNavigationItem);
60             contentPlaceholder.appendChild(importHelpElement);
61         }
62
63         let versionContainer = contentView.element.appendChild(document.createElement("div"));
64         versionContainer.classList.add("audit-version");
65
66         let version = WI.AuditTestBase.Version;
67         if (InspectorBackend.domains.Audit)
68             version = Math.min(version, InspectorBackend.domains.Audit.VERSION);
69         versionContainer.textContent = WI.UIString("Audit version: %s").format(version);
70
71         this.contentBrowser.showContentView(contentView);
72     }
73
74     // Protected
75
76     initialLayout()
77     {
78         super.initialLayout();
79
80         this.contentTreeOutline.allowsRepeatSelection = false;
81
82         let controlsNavigationBar = new WI.NavigationBar;
83
84         this._startStopButtonNavigationItem = new WI.ToggleButtonNavigationItem("audit-start-stop", WI.UIString("Start"), WI.UIString("Stop"), "Images/AuditStart.svg", "Images/AuditStop.svg", 13, 13);
85         this._startStopButtonNavigationItem.buttonStyle = WI.ButtonNavigationItem.Style.ImageAndText;
86         this._updateStartStopButtonNavigationItemState();
87         this._startStopButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleStartStopButtonNavigationItemClicked, this);
88         controlsNavigationBar.addNavigationItem(this._startStopButtonNavigationItem);
89
90         controlsNavigationBar.addNavigationItem(new WI.DividerNavigationItem);
91
92         let importButtonNavigationItem = new WI.ButtonNavigationItem("audit-import", WI.UIString("Import"), "Images/Import.svg", 15, 15);
93         importButtonNavigationItem.buttonStyle = WI.ButtonNavigationItem.Style.ImageAndText;
94         importButtonNavigationItem.visibilityPriority = WI.NavigationItem.VisibilityPriority.Low;
95         importButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleImportButtonNavigationItemClicked, this);
96         controlsNavigationBar.addNavigationItem(importButtonNavigationItem);
97
98         this.addSubview(controlsNavigationBar);
99
100         let editNavigationbar = new WI.NavigationBar;
101
102         this._editButtonNavigationItem = new WI.ActivateButtonNavigationItem("edit-audits", WI.UIString("Edit"), WI.UIString("Done"));
103         this._editButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleEditButtonNavigationItemClicked, this);
104         editNavigationbar.addNavigationItem(this._editButtonNavigationItem);
105
106         this.contentView.addSubview(editNavigationbar);
107
108         for (let test of WI.auditManager.tests)
109             this._addTest(test);
110
111         WI.auditManager.results.forEach((result, i) => {
112             this._addResult(result, i);
113         });
114
115         WI.auditManager.addEventListener(WI.AuditManager.Event.EditingChanged, this._handleAuditManagerEditingChanged, this);
116         WI.auditManager.addEventListener(WI.AuditManager.Event.TestAdded, this._handleAuditTestAdded, this);
117         WI.auditManager.addEventListener(WI.AuditManager.Event.TestCompleted, this._handleAuditTestCompleted, this);
118         WI.auditManager.addEventListener(WI.AuditManager.Event.TestRemoved, this._handleAuditTestRemoved, this);
119         WI.auditManager.addEventListener(WI.AuditManager.Event.TestScheduled, this._handleAuditTestScheduled, this);
120
121         this.contentTreeOutline.addEventListener(WI.TreeOutline.Event.SelectionDidChange, this._treeSelectionDidChange, this);
122     }
123
124     closed()
125     {
126         super.closed();
127
128         WI.auditManager.removeEventListener(null, null, this);
129     }
130
131     updateFilter()
132     {
133         super.updateFilter();
134
135         if (!this.hasActiveFilters)
136             this._updateNoAuditsPlaceholder();
137     }
138
139     hasCustomFilters()
140     {
141         return true;
142     }
143
144     matchTreeElementAgainstCustomFilters(treeElement, flags)
145     {
146         if (WI.auditManager.editing) {
147             if (treeElement.representedObject instanceof WI.AuditTestResultBase || treeElement.hasAncestor(this._resultsFolderTreeElement) || treeElement === this._resultsFolderTreeElement)
148                 return false;
149         } else {
150             if (treeElement.representedObject instanceof WI.AuditTestBase && treeElement.representedObject.disabled)
151                 return false;
152         }
153
154         return super.matchTreeElementAgainstCustomFilters(treeElement, flags);
155     }
156
157     // Private
158
159     _addTest(test)
160     {
161         let treeElement = new WI.AuditTreeElement(test);
162
163         if (this._resultsFolderTreeElement) {
164             this.contentTreeOutline.insertChild(treeElement, this.contentTreeOutline.children.indexOf(this._resultsFolderTreeElement));
165             this._resultsFolderTreeElement.hidden = !this._resultsFolderTreeElement.children.length;
166         } else
167             this.contentTreeOutline.appendChild(treeElement);
168
169         this._updateStartStopButtonNavigationItemState();
170         this._updateEditButtonNavigationItemState();
171         this._updateNoAuditsPlaceholder();
172     }
173
174     _addResult(result, index)
175     {
176         this.element.classList.add("has-results");
177
178         if (!this._resultsFolderTreeElement) {
179             this._resultsFolderTreeElement = new WI.FolderTreeElement(WI.UIString("Results"));
180             this.contentTreeOutline.appendChild(this._resultsFolderTreeElement);
181         }
182
183         this._resultsFolderTreeElement.expand();
184
185         let resultFolderTreeElement = new WI.FolderTreeElement(WI.UIString("Run %d").format(index + 1));
186         if (result instanceof WI.AuditTestResultBase) {
187             resultFolderTreeElement.subtitle = WI.UIString("Imported");
188             result = [result];
189         }
190         this._resultsFolderTreeElement.appendChild(resultFolderTreeElement);
191
192         console.assert(this._resultsFolderTreeElement.children.length === WI.auditManager.results.length);
193
194         for (let resultItem of result)
195             resultFolderTreeElement.appendChild(new WI.AuditTreeElement(resultItem));
196
197         this._updateStartStopButtonNavigationItemState();
198         this._updateEditButtonNavigationItemState();
199     }
200
201     _updateStartStopButtonNavigationItemState()
202     {
203         this._startStopButtonNavigationItem.toggled = WI.auditManager.runningState === WI.AuditManager.RunningState.Active || WI.auditManager.runningState === WI.AuditManager.RunningState.Stopping;
204         this._startStopButtonNavigationItem.enabled = WI.auditManager.tests.some((test) => !test.disabled) && (WI.auditManager.runningState === WI.AuditManager.RunningState.Inactive || WI.auditManager.runningState === WI.AuditManager.RunningState.Active);
205     }
206
207      _updateEditButtonNavigationItemState()
208     {
209         this._editButtonNavigationItem.label = WI.auditManager.editing ? this._editButtonNavigationItem.activatedToolTip : this._editButtonNavigationItem.defaultToolTip;
210         this._editButtonNavigationItem.activated = WI.auditManager.editing;
211         this._editButtonNavigationItem.enabled = WI.auditManager.tests.length && (WI.auditManager.editing || WI.auditManager.runningState === WI.AuditManager.RunningState.Inactive);
212     }
213
214     _updateNoAuditsPlaceholder()
215     {
216         if (WI.auditManager.editing || WI.auditManager.tests.some((test) => !test.disabled)) {
217             if (!this.hasActiveFilters)
218                 this.hideEmptyContentPlaceholder();
219             return;
220         }
221
222         let contentPlaceholder = this.showEmptyContentPlaceholder(WI.UIString("No Enabled Audits"));
223         contentPlaceholder.classList.add("no-enabled-audits");
224
225         if (WI.auditManager.results.length) {
226             // Move the placeholder to be the first element in the content area, where it will
227             // be styled so that it doesn't obstruct the results elements.
228             this.contentView.element.insertBefore(contentPlaceholder, this.contentView.element.firstChild);
229         }
230     }
231
232     _handleAuditManagerEditingChanged(event)
233     {
234         if (WI.auditManager.editing) {
235             console.assert(!this._selectedTreeElementBeforeEditing);
236             this._selectedTreeElementBeforeEditing = this.contentTreeOutline.selectedTreeElement;
237             if (this._selectedTreeElementBeforeEditing)
238                 this._selectedTreeElementBeforeEditing.deselect();
239         } else if (this._selectedTreeElementBeforeEditing) {
240             if (!(this._selectedTreeElementBeforeEditing.representedObject instanceof WI.AuditTestBase) || !this._selectedTreeElementBeforeEditing.representedObject.disabled)
241                 this._selectedTreeElementBeforeEditing.select();
242             this._selectedTreeElementBeforeEditing = null;
243         }
244
245         if (!this.contentTreeOutline.selectedTreeElement)
246             this.showDefaultContentView();
247
248         this._updateStartStopButtonNavigationItemState();
249         this._updateEditButtonNavigationItemState();
250
251         this.updateFilter();
252     }
253
254     _handleAuditTestAdded(event)
255     {
256         this._addTest(event.data.test);
257     }
258
259     _handleAuditTestCompleted(event)
260     {
261         let {result, index} = event.data;
262         this._addResult(result, index);
263     }
264
265     _handleAuditTestRemoved(event)
266     {
267         let {test} = event.data;
268         let treeElement = this.treeElementForRepresentedObject(test);
269         this.contentTreeOutline.removeChild(treeElement);
270
271         this._updateStartStopButtonNavigationItemState();
272         this._updateEditButtonNavigationItemState();
273         this._updateNoAuditsPlaceholder();
274     }
275
276     _handleAuditTestScheduled(event)
277     {
278         this._updateStartStopButtonNavigationItemState();
279         this._updateEditButtonNavigationItemState();
280     }
281
282     _treeSelectionDidChange(event)
283     {
284         if (!this.selected)
285             return;
286
287         let treeElement = this.contentTreeOutline.selectedTreeElement;
288         if (!treeElement || treeElement instanceof WI.FolderTreeElement) {
289             this.showDefaultContentView();
290             return;
291         }
292
293         if (WI.auditManager.editing)
294             return;
295
296         let representedObject = treeElement.representedObject;
297         if (representedObject instanceof WI.AuditTestCase || representedObject instanceof WI.AuditTestGroup
298             || representedObject instanceof WI.AuditTestCaseResult || representedObject instanceof WI.AuditTestGroupResult) {
299             WI.showRepresentedObject(representedObject);
300             return;
301         }
302
303         console.error("Unknown tree element", treeElement);
304     }
305
306     _handleStartStopButtonNavigationItemClicked(event)
307     {
308         if (WI.auditManager.runningState === WI.AuditManager.RunningState.Inactive)
309             WI.auditManager.start();
310         else if (WI.auditManager.runningState === WI.AuditManager.RunningState.Active)
311             WI.auditManager.stop();
312
313         this._updateStartStopButtonNavigationItemState();
314     }
315
316     _handleImportButtonNavigationItemClicked(event)
317     {
318         WI.FileUtilities.importJSON((result) => WI.auditManager.processJSON(result));
319     }
320
321     _handleEditButtonNavigationItemClicked(event)
322     {
323         WI.auditManager.editing = !WI.auditManager.editing;
324     }
325 };