Web Inspector: Styles Redesign: turn on CSS spreadsheet editor by default
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Views / SettingsTabContentView.js
1 /*
2  * Copyright (C) 2015-2017 Apple Inc. All rights reserved.
3  * Copyright (C) 2016 Devin Rousso <webkit@devinrousso.com>. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
15  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
18  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
24  * THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 WI.SettingsTabContentView = class SettingsTabContentView extends WI.TabContentView
28 {
29     constructor(identifier)
30     {
31         let tabBarItem = new WI.PinnedTabBarItem("Images/Gear.svg", WI.UIString("Open Settings"));
32
33         super(identifier || "settings", "settings", tabBarItem);
34
35         // Ensures that the Settings tab is displayable from a pinned tab bar item.
36         tabBarItem.representedObject = this;
37
38         this._selectedSettingsView = null;
39         this._settingsViews = [];
40     }
41
42     static tabInfo()
43     {
44         return {
45             image: "Images/Gear.svg",
46             title: WI.UIString("Settings"),
47         };
48     }
49
50     static isEphemeral()
51     {
52         return true;
53     }
54
55     static shouldSaveTab()
56     {
57         return false;
58     }
59
60     // Public
61
62     get type() { return WI.SettingsTabContentView.Type; }
63
64     get supportsSplitContentBrowser()
65     {
66         return false;
67     }
68
69     get selectedSettingsView()
70     {
71         return this._selectedSettingsView;
72     }
73
74     set selectedSettingsView(settingsView)
75     {
76         if (this._selectedSettingsView === settingsView)
77             return;
78
79         if (this._selectedSettingsView)
80             this.replaceSubview(this._selectedSettingsView, settingsView);
81         else
82             this.addSubview(settingsView);
83
84         this._selectedSettingsView = settingsView;
85         this._selectedSettingsView.updateLayout();
86
87         let navigationItem = this._navigationBar.findNavigationItem(settingsView.identifier);
88         console.assert(navigationItem, "Missing navigation item for settings view.", settingsView);
89         if (!navigationItem)
90             return;
91
92         this._navigationBar.selectedNavigationItem = navigationItem;
93     }
94
95     addSettingsView(settingsView)
96     {
97         if (this._settingsViews.includes(settingsView)) {
98             console.assert(false, "SettingsView already exists.", settingsView);
99             return;
100         }
101
102         this._settingsViews.push(settingsView);
103         this._navigationBar.addNavigationItem(new WI.RadioButtonNavigationItem(settingsView.identifier, settingsView.displayName));
104
105         this._updateNavigationBarVisibility();
106     }
107
108     setSettingsViewVisible(settingsView, visible)
109     {
110         let navigationItem = this._navigationBar.findNavigationItem(settingsView.identifier);
111         console.assert(navigationItem, "Missing NavigationItem for identifier: " + settingsView.identifier);
112         if (!navigationItem)
113             return;
114
115         if (navigationItem.hidden === !visible)
116             return;
117
118         navigationItem.hidden = !visible;
119         settingsView.element.classList.toggle("hidden", !visible);
120
121         this._updateNavigationBarVisibility();
122
123         if (!this.selectedSettingsView) {
124             if (visible)
125                 this.selectedSettingsView = settingsView;
126             return;
127         }
128
129         if (this.selectedSettingsView !== settingsView)
130             return;
131
132         let index = this._settingsViews.indexOf(settingsView);
133         console.assert(index !== -1, "SettingsView not found.", settingsView);
134         if (index === -1)
135             return;
136
137         let previousIndex = index;
138         while (--previousIndex >= 0) {
139             let previousNavigationItem = this._navigationBar.navigationItems[previousIndex];
140             console.assert(previousNavigationItem);
141             if (!previousNavigationItem || previousNavigationItem.hidden)
142                 continue;
143
144             this.selectedSettingsView = this._settingsViews[previousIndex];
145             return;
146         }
147
148         let nextIndex = index;
149         while (++nextIndex < this._settingsViews.length) {
150             let nextNavigationItem = this._navigationBar.navigationItems[nextIndex];
151             console.assert(nextNavigationItem);
152             if (!nextNavigationItem || nextNavigationItem.hidden)
153                 continue;
154
155             this.selectedSettingsView = this._settingsViews[nextIndex];
156             return;
157         }
158     }
159
160     // Protected
161
162     initialLayout()
163     {
164         this._navigationBar = new WI.NavigationBar;
165         this._navigationBar.addEventListener(WI.NavigationBar.Event.NavigationItemSelected, this._navigationItemSelected, this);
166         this.addSubview(this._navigationBar);
167
168         this._createGeneralSettingsView();
169         this._createExperimentalSettingsView();
170
171         WI.notifications.addEventListener(WI.Notification.DebugUIEnabledDidChange, this._updateDebugSettingsViewVisibility, this);
172         this._updateDebugSettingsViewVisibility();
173
174         this.selectedSettingsView = this._settingsViews[0];
175     }
176
177     // Private
178
179     _createGeneralSettingsView()
180     {
181         let generalSettingsView = new WI.SettingsView("general", WI.UIString("General"));
182
183         const indentValues = [WI.UIString("Tabs"), WI.UIString("Spaces")];
184
185         let indentEditor = generalSettingsView.addGroupWithCustomSetting(WI.UIString("Prefer indent using:"), WI.SettingEditor.Type.Select, {values: indentValues});
186         indentEditor.value = indentValues[WI.settings.indentWithTabs.value ? 0 : 1];
187         indentEditor.addEventListener(WI.SettingEditor.Event.ValueDidChange, () => {
188             WI.settings.indentWithTabs.value = indentEditor.value === indentValues[0];
189         });
190
191         const widthLabel = WI.UIString("spaces");
192         const widthOptions = {min: 1};
193
194         generalSettingsView.addSetting(WI.UIString("Tab width:"), WI.settings.tabSize, widthLabel, widthOptions);
195         generalSettingsView.addSetting(WI.UIString("Indent width:"), WI.settings.indentUnit, widthLabel, widthOptions);
196
197         generalSettingsView.addSetting(WI.UIString("Line wrapping:"), WI.settings.enableLineWrapping, WI.UIString("Wrap lines to editor width"));
198
199         let showGroup = generalSettingsView.addGroup(WI.UIString("Show:"));
200         showGroup.addSetting(WI.settings.showWhitespaceCharacters, WI.UIString("Whitespace characters"));
201         showGroup.addSetting(WI.settings.showInvalidCharacters, WI.UIString("Invalid characters"));
202
203         generalSettingsView.addSeparator();
204
205         generalSettingsView.addSetting(WI.UIString("Network:"), WI.settings.clearNetworkOnNavigate, WI.UIString("Clear when page loads"));
206
207         generalSettingsView.addSeparator();
208
209         generalSettingsView.addSetting(WI.UIString("Console:"), WI.settings.clearLogOnNavigate, WI.UIString("Clear when page loads"));
210
211         generalSettingsView.addSeparator();
212
213         generalSettingsView.addSetting(WI.UIString("Debugger:"), WI.settings.showScopeChainOnPause, WI.UIString("Show Scope Chain on pause"));
214
215         generalSettingsView.addSeparator();
216
217         const zoomLevels = [0.6, 0.8, 1, 1.2, 1.4, 1.6, 1.8, 2, 2.2, 2.4];
218         const zoomValues = zoomLevels.map((level) => [level, Number.percentageString(level, 0)]);
219
220         let zoomEditor = generalSettingsView.addGroupWithCustomSetting(WI.UIString("Zoom:"), WI.SettingEditor.Type.Select, {values: zoomValues});
221         zoomEditor.value = WI.getZoomFactor();
222         zoomEditor.addEventListener(WI.SettingEditor.Event.ValueDidChange, () => { WI.setZoomFactor(zoomEditor.value); });
223         WI.settings.zoomFactor.addEventListener(WI.Setting.Event.Changed, () => { zoomEditor.value = WI.getZoomFactor().maxDecimals(2); });
224
225         if (WI.LogManager.supportsLogChannels()) {
226             const logLevels = [
227                 [WI.LoggingChannel.Level.Off, WI.UIString("Off")],
228                 [WI.LoggingChannel.Level.Log, WI.UIString("Log")],
229                 [WI.LoggingChannel.Level.Error, WI.UIString("Error")],
230                 [WI.LoggingChannel.Level.Warning, WI.UIString("Warning")],
231                 [WI.LoggingChannel.Level.Info, WI.UIString("Info")],
232                 [WI.LoggingChannel.Level.Debug, WI.UIString("Debug")],
233             ];
234             const editorLabels = {
235                 media: WI.UIString("Media Logging:"),
236                 webrtc: WI.UIString("WebRTC Logging:"),
237             };
238
239             let channels = WI.logManager.customLoggingChannels;
240             for (let channel of channels) {
241                 let logEditor = generalSettingsView.addGroupWithCustomSetting(editorLabels[channel.source], WI.SettingEditor.Type.Select, {values: logLevels});
242                 logEditor.value = channel.level;
243                 logEditor.addEventListener(WI.SettingEditor.Event.ValueDidChange, () => { ConsoleAgent.setLoggingChannelLevel(channel.source, logEditor.value); });
244             }
245         }
246
247         this.addSettingsView(generalSettingsView);
248     }
249
250     _createExperimentalSettingsView()
251     {
252         if (!(window.CanvasAgent || window.CSSAgent || window.NetworkAgent || window.LayerTreeAgent))
253             return;
254
255         let experimentalSettingsView = new WI.SettingsView("experimental", WI.UIString("Experimental"));
256
257         if (window.CSSAgent) {
258             experimentalSettingsView.addSetting(WI.UIString("Styles Panel:"), WI.settings.experimentalLegacyStyleEditor, WI.UIString("Legacy Style Editor"));
259             experimentalSettingsView.addSeparator();
260         }
261
262         if (window.LayerTreeAgent) {
263             experimentalSettingsView.addSetting(WI.UIString("Layers:"), WI.settings.experimentalEnableLayersTab, WI.UIString("Enable Layers Tab"));
264             experimentalSettingsView.addSeparator();
265         }
266
267         let reloadInspectorButton = document.createElement("button");
268         reloadInspectorButton.textContent = WI.UIString("Reload Web Inspector");
269         reloadInspectorButton.addEventListener("click", () => { window.location.reload(); });
270
271         let reloadInspectorContainerElement = experimentalSettingsView.addCenteredContainer(reloadInspectorButton, WI.UIString("for changes to take effect"));
272         reloadInspectorContainerElement.classList.add("hidden");
273
274         function listenForChange(setting) {
275             let initialValue = setting.value;
276             setting.addEventListener(WI.Setting.Event.Changed, () => {
277                 reloadInspectorContainerElement.classList.toggle("hidden", initialValue === setting.value);
278             });
279         }
280
281         listenForChange(WI.settings.experimentalLegacyStyleEditor);
282         listenForChange(WI.settings.experimentalEnableLayersTab);
283
284         this.addSettingsView(experimentalSettingsView);
285     }
286
287     _createDebugSettingsView()
288     {
289         if (this._debugSettingsView)
290             return;
291
292         // These settings are only ever shown in engineering builds, so the strings are unlocalized.
293
294         this._debugSettingsView = new WI.SettingsView("debug", WI.unlocalizedString("Debug"));
295
296         let protocolMessagesGroup = this._debugSettingsView.addGroup(WI.unlocalizedString("Protocol Logging:"));
297
298         let autoLogProtocolMessagesEditor = protocolMessagesGroup.addSetting(WI.settings.autoLogProtocolMessages, WI.unlocalizedString("Messages"));
299         WI.settings.autoLogProtocolMessages.addEventListener(WI.Setting.Event.Changed, () => {
300             autoLogProtocolMessagesEditor.value = InspectorBackend.dumpInspectorProtocolMessages;
301         });
302
303         protocolMessagesGroup.addSetting(WI.settings.autoLogTimeStats, WI.unlocalizedString("Time Stats"));
304
305         this._debugSettingsView.addSeparator();
306
307         this._debugSettingsView.addSetting(WI.unlocalizedString("Layout Flashing:"), WI.settings.enableLayoutFlashing, WI.unlocalizedString("Draw borders when a view performs a layout"));
308
309         this._debugSettingsView.addSeparator();
310
311         this._debugSettingsView.addSetting(WI.unlocalizedString("Uncaught Exception Reporter:"), WI.settings.enableUncaughtExceptionReporter, WI.unlocalizedString("Enabled"));
312
313         this._debugSettingsView.addSeparator();
314
315         const layoutDirectionValues = [
316             [WI.LayoutDirection.System, WI.unlocalizedString("System Default")],
317             [WI.LayoutDirection.LTR, WI.unlocalizedString("Left to Right (LTR)")],
318             [WI.LayoutDirection.RTL, WI.unlocalizedString("Right to Left (RTL)")],
319         ];
320
321         let layoutDirectionEditor = this._debugSettingsView.addGroupWithCustomSetting(WI.unlocalizedString("Layout Direction:"), WI.SettingEditor.Type.Select, {values: layoutDirectionValues});
322         layoutDirectionEditor.value = WI.settings.layoutDirection.value;
323         layoutDirectionEditor.addEventListener(WI.SettingEditor.Event.ValueDidChange, () => { WI.setLayoutDirection(layoutDirectionEditor.value); });
324
325         this.addSettingsView(this._debugSettingsView);
326     }
327
328     _updateNavigationBarVisibility()
329     {
330         let visibleItems = 0;
331         for (let item of this._navigationBar.navigationItems) {
332             if (!item.hidden && ++visibleItems > 1) {
333                 this._navigationBar.element.classList.remove("invisible");
334                 return;
335             }
336         }
337
338         this._navigationBar.element.classList.add("invisible");
339     }
340
341     _navigationItemSelected(event)
342     {
343         let navigationItem = event.target.selectedNavigationItem;
344         if (!navigationItem)
345             return;
346
347         let settingsView = this._settingsViews.find((view) => view.identifier === navigationItem.identifier);
348         console.assert(settingsView, "Missing SettingsView for identifier " + navigationItem.identifier);
349         if (!settingsView)
350             return;
351
352         this.selectedSettingsView = settingsView;
353     }
354
355     _updateDebugSettingsViewVisibility()
356     {
357         // Only create the Debug view if the debug UI is enabled.
358         if (WI.isDebugUIEnabled())
359             this._createDebugSettingsView();
360
361         if (!this._debugSettingsView)
362             return;
363
364         this.setSettingsViewVisible(this._debugSettingsView, WI.isDebugUIEnabled());
365
366         this.needsLayout();
367     }
368 };
369
370 WI.SettingsTabContentView.Type = "settings";