6ef2a86df9947f33dd0e9ea048a864d58b9729c5
[WebKit-https.git] / Source / WebCore / inspector / front-end / SettingsScreen.js
1 /*
2  * Copyright (C) 2011 Google 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 are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 /**
32  * @constructor
33  * @param {!function()} onHide
34  * @extends {WebInspector.HelpScreen}
35  */
36 WebInspector.SettingsScreen = function(onHide)
37 {
38     WebInspector.HelpScreen.call(this);
39     this.element.id = "settings-screen";
40
41     /** @type {function()} */
42     this._onHide = onHide;
43
44     this._tabbedPane = new WebInspector.TabbedPane();
45     this._tabbedPane.element.addStyleClass("help-window-main");
46     var settingsLabelElement = document.createElement("div");
47     settingsLabelElement.className = "help-window-label";
48     settingsLabelElement.createTextChild(WebInspector.UIString("Settings"));
49     this._tabbedPane.element.insertBefore(settingsLabelElement, this._tabbedPane.element.firstChild);
50     this._tabbedPane.element.appendChild(this._createCloseButton());
51     this._tabbedPane.appendTab(WebInspector.SettingsScreen.Tabs.General, WebInspector.UIString("General"), new WebInspector.GenericSettingsTab());
52     if (!WebInspector.experimentsSettings.showOverridesInDrawer.isEnabled())
53         this._tabbedPane.appendTab(WebInspector.SettingsScreen.Tabs.Overrides, WebInspector.UIString("Overrides"), new WebInspector.OverridesSettingsTab());
54     if (WebInspector.experimentsSettings.fileSystemProject.isEnabled())
55         this._tabbedPane.appendTab(WebInspector.SettingsScreen.Tabs.Workspace, WebInspector.UIString("Workspace"), new WebInspector.WorkspaceSettingsTab());
56     if (WebInspector.experimentsSettings.experimentsEnabled)
57         this._tabbedPane.appendTab(WebInspector.SettingsScreen.Tabs.Experiments, WebInspector.UIString("Experiments"), new WebInspector.ExperimentsSettingsTab());
58     this._tabbedPane.appendTab(WebInspector.SettingsScreen.Tabs.Shortcuts, WebInspector.UIString("Shortcuts"), WebInspector.shortcutsScreen.createShortcutsTabView());
59     this._tabbedPane.shrinkableTabs = false;
60     this._tabbedPane.verticalTabLayout = true;
61
62     this._lastSelectedTabSetting = WebInspector.settings.createSetting("lastSelectedSettingsTab", WebInspector.SettingsScreen.Tabs.General);
63     this.selectTab(this._lastSelectedTabSetting.get());
64     this._tabbedPane.addEventListener(WebInspector.TabbedPane.EventTypes.TabSelected, this._tabSelected, this);
65 }
66
67 WebInspector.SettingsScreen.Tabs = {
68     General: "general",
69     Overrides: "overrides",
70     Workspace: "workspace",
71     Experiments: "experiments",
72     Shortcuts: "shortcuts"
73 }
74
75 WebInspector.SettingsScreen.prototype = {
76     /**
77      * @param {string} tabId
78      */
79     selectTab: function(tabId)
80     {
81         this._tabbedPane.selectTab(tabId);
82     },
83
84     /**
85      * @param {WebInspector.Event} event
86      */
87     _tabSelected: function(event)
88     {
89         this._lastSelectedTabSetting.set(this._tabbedPane.selectedTabId);
90     },
91
92     /**
93      * @override
94      */
95     wasShown: function()
96     {
97         this._tabbedPane.show(this.element);
98         WebInspector.HelpScreen.prototype.wasShown.call(this);
99     },
100
101     /**
102      * @override
103      */
104     isClosingKey: function(keyCode)
105     {
106         return [
107             WebInspector.KeyboardShortcut.Keys.Enter.code,
108             WebInspector.KeyboardShortcut.Keys.Esc.code,
109         ].indexOf(keyCode) >= 0;
110     },
111
112     /**
113      * @override
114      */
115     willHide: function()
116     {
117         this._onHide();
118         WebInspector.HelpScreen.prototype.willHide.call(this);
119     },
120
121     __proto__: WebInspector.HelpScreen.prototype
122 }
123
124 /**
125  * @constructor
126  * @extends {WebInspector.View}
127  * @param {string} name
128  * @param {string=} id
129  */
130 WebInspector.SettingsTab = function(name, id)
131 {
132     WebInspector.View.call(this);
133     this.element.className = "settings-tab-container";
134     if (id)
135         this.element.id = id;
136     var header = this.element.createChild("header");
137     header.createChild("h3").appendChild(document.createTextNode(name));
138     this.containerElement = this.element.createChild("div", "help-container-wrapper").createChild("div", "settings-tab help-content help-container");
139 }
140
141 WebInspector.SettingsTab.prototype = {
142     /**
143      *  @param {string=} name
144      *  @return {!Element}
145      */
146     _appendSection: function(name)
147     {
148         var block = this.containerElement.createChild("div", "help-block");
149         if (name)
150             block.createChild("div", "help-section-title").textContent = name;
151         return block;
152     },
153
154     /**
155      * @param {boolean=} omitParagraphElement
156      * @param {Element=} inputElement
157      */
158     _createCheckboxSetting: function(name, setting, omitParagraphElement, inputElement)
159     {
160         var input = inputElement || document.createElement("input");
161         input.type = "checkbox";
162         input.name = name;
163         input.checked = setting.get();
164
165         function listener()
166         {
167             setting.set(input.checked);
168         }
169         input.addEventListener("click", listener, false);
170
171         var label = document.createElement("label");
172         label.appendChild(input);
173         label.appendChild(document.createTextNode(name));
174         if (omitParagraphElement)
175             return label;
176
177         var p = document.createElement("p");
178         p.appendChild(label);
179         return p;
180     },
181
182     _createSelectSetting: function(name, options, setting)
183     {
184         var fieldsetElement = document.createElement("fieldset");
185         fieldsetElement.createChild("label").textContent = name;
186
187         var select = document.createElement("select");
188         var settingValue = setting.get();
189
190         for (var i = 0; i < options.length; ++i) {
191             var option = options[i];
192             select.add(new Option(option[0], option[1]));
193             if (settingValue === option[1])
194                 select.selectedIndex = i;
195         }
196
197         function changeListener(e)
198         {
199             setting.set(e.target.value);
200         }
201
202         select.addEventListener("change", changeListener, false);
203         fieldsetElement.appendChild(select);
204
205         var p = document.createElement("p");
206         p.appendChild(fieldsetElement);
207         return p;
208     },
209
210     _createRadioSetting: function(name, options, setting)
211     {
212         var pp = document.createElement("p");
213         var fieldsetElement = document.createElement("fieldset");
214         var legendElement = document.createElement("legend");
215         legendElement.textContent = name;
216         fieldsetElement.appendChild(legendElement);
217
218         function clickListener(e)
219         {
220             setting.set(e.target.value);
221         }
222
223         var settingValue = setting.get();
224         for (var i = 0; i < options.length; ++i) {
225             var p = document.createElement("p");
226             var label = document.createElement("label");
227             p.appendChild(label);
228
229             var input = document.createElement("input");
230             input.type = "radio";
231             input.name = setting.name;
232             input.value = options[i][0];
233             input.addEventListener("click", clickListener, false);
234             if (settingValue == input.value)
235                 input.checked = true;
236
237             label.appendChild(input);
238             label.appendChild(document.createTextNode(options[i][1]));
239
240             fieldsetElement.appendChild(p);
241         }
242
243         pp.appendChild(fieldsetElement);
244         return pp;
245     },
246
247     _createCustomSetting: function(name, element)
248     {
249         var p = document.createElement("p");
250         var fieldsetElement = document.createElement("fieldset");
251         fieldsetElement.createChild("label").textContent = name;
252         fieldsetElement.appendChild(element);
253         p.appendChild(fieldsetElement);
254         return p;
255     },
256
257     __proto__: WebInspector.View.prototype
258 }
259
260 /**
261  * @constructor
262  * @extends {WebInspector.SettingsTab}
263  */
264 WebInspector.GenericSettingsTab = function()
265 {
266     WebInspector.SettingsTab.call(this, WebInspector.UIString("General"));
267
268     var p = this._appendSection();
269     if (Preferences.exposeDisableCache)
270         p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Disable cache"), WebInspector.settings.cacheDisabled));
271     var disableJSElement = this._createCheckboxSetting(WebInspector.UIString("Disable JavaScript"), WebInspector.settings.javaScriptDisabled);
272     p.appendChild(disableJSElement);
273     WebInspector.settings.javaScriptDisabled.addChangeListener(this._javaScriptDisabledChanged, this);
274     this._disableJSCheckbox = disableJSElement.getElementsByTagName("input")[0];
275     this._updateScriptDisabledCheckbox();
276
277     p = this._appendSection(WebInspector.UIString("Appearance"));
278     p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Show toolbar icons"), WebInspector.settings.showToolbarIcons));
279
280     p = this._appendSection(WebInspector.UIString("Elements"));
281     p.appendChild(this._createRadioSetting(WebInspector.UIString("Color format"), [
282         [ WebInspector.Color.Format.Original, WebInspector.UIString("As authored") ],
283         [ WebInspector.Color.Format.HEX, "HEX: #DAC0DE" ],
284         [ WebInspector.Color.Format.RGB, "RGB: rgb(128, 255, 255)" ],
285         [ WebInspector.Color.Format.HSL, "HSL: hsl(300, 80%, 90%)" ] ], WebInspector.settings.colorFormat));
286     p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Show user agent styles"), WebInspector.settings.showUserAgentStyles));
287     p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Word wrap"), WebInspector.settings.domWordWrap));
288     p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Show Shadow DOM"), WebInspector.settings.showShadowDOM));
289     p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Show rulers"), WebInspector.settings.showMetricsRulers));
290
291     p = this._appendSection(WebInspector.UIString("Rendering"));
292     p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Show paint rectangles"), WebInspector.settings.showPaintRects));
293     WebInspector.settings.showPaintRects.addChangeListener(this._showPaintRectsChanged, this);
294
295     if (Capabilities.canShowDebugBorders) {
296         p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Show composited layer borders"), WebInspector.settings.showDebugBorders));
297         WebInspector.settings.showDebugBorders.addChangeListener(this._showDebugBordersChanged, this);
298     }
299     if (Capabilities.canShowFPSCounter) {
300         p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Show FPS meter"), WebInspector.settings.showFPSCounter));
301         WebInspector.settings.showFPSCounter.addChangeListener(this._showFPSCounterChanged, this);
302     }
303     if (Capabilities.canContinuouslyPaint) {
304         p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Enable continuous page repainting"), WebInspector.settings.continuousPainting));
305         WebInspector.settings.continuousPainting.addChangeListener(this._continuousPaintingChanged, this);
306     }
307
308     p = this._appendSection(WebInspector.UIString("Sources"));
309     p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Search in content scripts"), WebInspector.settings.searchInContentScripts));
310     p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Enable source maps"), WebInspector.settings.sourceMapsEnabled));
311     if (WebInspector.experimentsSettings.isEnabled("sass"))
312         p.appendChild(this._createCSSAutoReloadControls());
313     var indentationElement = this._createSelectSetting(WebInspector.UIString("Indentation"), [
314             [ WebInspector.UIString("2 spaces"), WebInspector.TextEditorModel.Indent.TwoSpaces ],
315             [ WebInspector.UIString("4 spaces"), WebInspector.TextEditorModel.Indent.FourSpaces ],
316             [ WebInspector.UIString("8 spaces"), WebInspector.TextEditorModel.Indent.EightSpaces ],
317             [ WebInspector.UIString("Tab character"), WebInspector.TextEditorModel.Indent.TabCharacter ]
318         ], WebInspector.settings.textEditorIndent);
319     indentationElement.firstChild.className = "toplevel";
320     p.appendChild(indentationElement);
321
322     p = this._appendSection(WebInspector.UIString("Profiler"));
323     p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Show objects' hidden properties"), WebInspector.settings.showHeapSnapshotObjectsHiddenProperties));
324     if (WebInspector.experimentsSettings.nativeMemorySnapshots.isEnabled())
325         p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Show uninstrumented native memory"), WebInspector.settings.showNativeSnapshotUninstrumentedSize));
326
327     if (Capabilities.timelineCanMonitorMainThread) {
328         p = this._appendSection(WebInspector.UIString("Timeline"));
329         p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Show CPU activity on the ruler"), WebInspector.settings.showCpuOnTimelineRuler));
330     }
331
332     p = this._appendSection(WebInspector.UIString("Console"));
333     p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Log XMLHttpRequests"), WebInspector.settings.monitoringXHREnabled));
334     p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Preserve log upon navigation"), WebInspector.settings.preserveConsoleLog));
335
336     if (WebInspector.extensionServer.hasExtensions()) {
337         var handlerSelector = new WebInspector.HandlerSelector(WebInspector.openAnchorLocationRegistry);
338         p = this._appendSection(WebInspector.UIString("Extensions"));
339         p.appendChild(this._createCustomSetting(WebInspector.UIString("Open links in"), handlerSelector.element));
340     }
341 }
342
343 WebInspector.GenericSettingsTab.prototype = {
344     _showPaintRectsChanged: function()
345     {
346         PageAgent.setShowPaintRects(WebInspector.settings.showPaintRects.get());
347     },
348
349     _showDebugBordersChanged: function()
350     {
351         PageAgent.setShowDebugBorders(WebInspector.settings.showDebugBorders.get());
352     },
353
354     _showFPSCounterChanged: function()
355     {
356         PageAgent.setShowFPSCounter(WebInspector.settings.showFPSCounter.get());
357     },
358
359     _continuousPaintingChanged: function()
360     {
361         PageAgent.setContinuousPaintingEnabled(WebInspector.settings.continuousPainting.get());
362     },
363
364     _updateScriptDisabledCheckbox: function()
365     {
366         function executionStatusCallback(error, status)
367         {
368             if (error || !status)
369                 return;
370
371             switch (status) {
372             case "forbidden":
373                 this._disableJSCheckbox.checked = true;
374                 this._disableJSCheckbox.disabled = true;
375                 break;
376             case "disabled":
377                 this._disableJSCheckbox.checked = true;
378                 break;
379             default:
380                 this._disableJSCheckbox.checked = false;
381                 break;
382             }
383         }
384
385         PageAgent.getScriptExecutionStatus(executionStatusCallback.bind(this));
386     },
387
388     _javaScriptDisabledChanged: function()
389     {
390         // We need to manually update the checkbox state, since enabling JavaScript in the page can actually uncover the "forbidden" state.
391         PageAgent.setScriptExecutionDisabled(WebInspector.settings.javaScriptDisabled.get(), this._updateScriptDisabledCheckbox.bind(this));
392     },
393
394     _createCSSAutoReloadControls: function()
395     {
396         var fragment = document.createDocumentFragment();
397         var labelElement = fragment.createChild("label");
398         var checkboxElement = labelElement.createChild("input");
399         checkboxElement.type = "checkbox";
400         checkboxElement.checked = WebInspector.settings.cssReloadEnabled.get();
401         checkboxElement.addEventListener("click", checkboxClicked, false);
402         labelElement.appendChild(document.createTextNode(WebInspector.UIString("Auto-reload CSS upon Sass save")));
403
404         var fieldsetElement = fragment.createChild("fieldset");
405         fieldsetElement.disabled = !checkboxElement.checked;
406         var p = fieldsetElement.createChild("p");
407         p.appendChild(document.createTextNode(WebInspector.UIString("Timeout (ms)")));
408         p.appendChild(document.createTextNode(" "));
409         var timeoutInput = p.createChild("input");
410         timeoutInput.value = WebInspector.settings.cssReloadTimeout.get();
411         timeoutInput.className = "numeric";
412         timeoutInput.style.width = "60px";
413         timeoutInput.maxLength = 8;
414         timeoutInput.addEventListener("blur", blurListener, false);
415         return fragment;
416
417         function checkboxClicked()
418         {
419             var reloadEnabled = checkboxElement.checked;
420             WebInspector.settings.cssReloadEnabled.set(reloadEnabled);
421             fieldsetElement.disabled = !reloadEnabled;
422         }
423
424         function blurListener()
425         {
426             var value = timeoutInput.value;
427             if (!isFinite(value) || value <= 0) {
428                 timeoutInput.value = WebInspector.settings.cssReloadTimeout.get();
429                 return;
430             }
431             WebInspector.settings.cssReloadTimeout.set(Number(value));
432         }
433     },
434
435     __proto__: WebInspector.SettingsTab.prototype
436 }
437
438 /**
439  * @constructor
440  * @extends {WebInspector.SettingsTab}
441  */
442 WebInspector.OverridesSettingsTab = function()
443 {
444     WebInspector.SettingsTab.call(this, WebInspector.UIString("Overrides"), "overrides-tab-content");
445     this._view = new WebInspector.OverridesView();
446     this.containerElement.parentElement.appendChild(this._view.containerElement);
447     this.containerElement.removeSelf();
448     this.containerElement = this._view.containerElement;
449 }
450
451 WebInspector.OverridesSettingsTab.prototype = {
452     __proto__: WebInspector.SettingsTab.prototype
453 }
454
455 /**
456  * @constructor
457  * @extends {WebInspector.SettingsTab}
458  */
459 WebInspector.WorkspaceSettingsTab = function()
460 {
461     WebInspector.SettingsTab.call(this, WebInspector.UIString("Workspace"), "workspace-tab-content");
462     this._createFileSystemsEditor();
463     this._createFileMappingEditor();
464 }
465
466 WebInspector.WorkspaceSettingsTab.prototype = {
467     _createFileSystemsEditor: function()
468     {
469         var p = this._appendSection(WebInspector.UIString("File systems"));
470         this._fileSystemsEditor = p.createChild("p", "file-systems-editor");
471
472         this._addFileSystemRowElement = this._fileSystemsEditor.createChild("div", "workspace-settings-row");
473         var addFileSystemButton = this._addFileSystemRowElement.createChild("input", "file-system-add-button");
474         addFileSystemButton.type = "button";
475         addFileSystemButton.value = WebInspector.UIString("Add file system");
476         addFileSystemButton.addEventListener("click", this._addFileSystemClicked.bind(this));
477
478         var fileSystemPaths = WebInspector.isolatedFileSystemModel.mapping().fileSystemPaths();
479         for (var i = 0; i < fileSystemPaths.length; ++i)
480             this._addFileSystemRow(fileSystemPaths[i]);
481
482         return this._fileSystemsEditor;
483     },
484
485     /**
486      * @return {Element}
487      */
488     _createShowTextInput: function(className, value)
489     {
490         var inputElement = document.createElement("input");
491         inputElement.addStyleClass(className);
492         inputElement.type = "text";
493         inputElement.value = value;
494         inputElement.title = value;
495         inputElement.disabled = true;
496         return inputElement;
497     },
498
499     /**
500      * @return {Element}
501      */
502     _createEditTextInput: function(className, placeHolder)
503     {
504         var inputElement = document.createElement("input");
505         inputElement.addStyleClass(className);
506         inputElement.type = "text";
507         inputElement.placeholder = placeHolder;
508         return inputElement;
509     },
510
511     /**
512      * @param {function(Event)} handler
513      * @return {Element}
514      */
515     _createRemoveButton: function(handler)
516     {
517         var removeButton = document.createElement("button");
518         removeButton.addStyleClass("button");
519         removeButton.addStyleClass("remove-button");
520         removeButton.value = WebInspector.UIString("Remove");
521         removeButton.addEventListener("click", handler, false);
522         return removeButton;
523     },
524
525     /**
526      * @param {function(Event)} handler
527      * @return {Element}
528      */
529     _createAddButton: function(handler)
530     {
531         var addButton = document.createElement("button");
532         addButton.addStyleClass("button");
533         addButton.addStyleClass("add-button");
534         addButton.value = WebInspector.UIString("Add");
535         addButton.addEventListener("click", handler, false);
536         return addButton;
537     },
538
539     /**
540      * @param {string} fileSystemPath
541      */
542     _addFileSystemRow: function(fileSystemPath)
543     {
544         var fileSystemRow = document.createElement("div");
545         fileSystemRow.addStyleClass("workspace-settings-row");
546         fileSystemRow.addStyleClass("file-system-row");
547         this._fileSystemsEditor.insertBefore(fileSystemRow, this._addFileSystemRowElement);
548
549         fileSystemRow.appendChild(this._createShowTextInput("file-system-path", fileSystemPath));
550         var removeFileSystemButton = this._createRemoveButton(removeFileSystemClicked.bind(this));
551         fileSystemRow.appendChild(removeFileSystemButton);
552
553         function removeFileSystemClicked()
554         {
555             removeFileSystemButton.disabled = true;
556             WebInspector.isolatedFileSystemModel.removeFileSystem(fileSystemPath, fileSystemRemoved.bind(this));
557         }
558         
559         function fileSystemRemoved()
560         {
561             this._fileSystemsEditor.removeChild(fileSystemRow);
562             removeFileSystemButton.disabled = false;
563         }
564     },
565
566     _addFileSystemClicked: function()
567     {
568         WebInspector.isolatedFileSystemModel.addFileSystem(this._fileSystemAdded.bind(this));
569     },
570
571     /**
572      * @param {?string} fileSystemPath
573      */
574     _fileSystemAdded: function(fileSystemPath)
575     {
576         if (fileSystemPath)
577             this._addFileSystemRow(fileSystemPath);
578     },
579
580     _createFileMappingEditor: function()
581     {
582         var p = this._appendSection(WebInspector.UIString("Mappings"));
583         this._fileMappingEditor = p.createChild("p", "file-mappings-editor");
584
585         this._addMappingRowElement = this._fileMappingEditor.createChild("div", "workspace-settings-row");
586
587         this._urlInputElement = this._createEditTextInput("file-mapping-url", WebInspector.UIString("File mapping url"));
588         this._addMappingRowElement.appendChild(this._urlInputElement);
589         this._pathInputElement = this._createEditTextInput("file-mapping-path", WebInspector.UIString("File mapping path"));
590         this._addMappingRowElement.appendChild(this._pathInputElement);
591
592         this._addMappingRowElement.appendChild(this._createAddButton(this._addFileMappingClicked.bind(this)));
593
594         var mappingEntries = WebInspector.fileMapping.mappingEntries();
595         for (var i = 0; i < mappingEntries.length; ++i)
596             this._addMappingRow(mappingEntries[i]);
597
598         return this._fileMappingEditor;
599     },
600
601     /**
602      * @param {WebInspector.FileMapping.Entry} mappingEntry
603      */
604     _addMappingRow: function(mappingEntry)
605     {
606         var fileMappingRow = document.createElement("div");
607         fileMappingRow.addStyleClass("workspace-settings-row");
608         this._fileMappingEditor.insertBefore(fileMappingRow, this._addMappingRowElement);
609
610         fileMappingRow.appendChild(this._createShowTextInput("file-mapping-url", mappingEntry.urlPrefix));
611         fileMappingRow.appendChild(this._createShowTextInput("file-mapping-path", mappingEntry.pathPrefix));
612
613         fileMappingRow.appendChild(this._createRemoveButton(removeMappingClicked.bind(this)));
614
615         function removeMappingClicked()
616         {
617             var index = Array.prototype.slice.call(fileMappingRow.parentElement.childNodes).indexOf(fileMappingRow);
618             var mappingEntries = WebInspector.fileMapping.mappingEntries();
619             mappingEntries.splice(index, 1);
620             WebInspector.fileMapping.setMappingEntries(mappingEntries);
621             this._fileMappingEditor.removeChild(fileMappingRow);
622         }
623     },
624
625     _addFileMappingClicked: function()
626     {
627         var url = this._urlInputElement.value;
628         var path = this._pathInputElement.value;
629         if (!url || !path)
630             return;
631         var mappingEntries = WebInspector.fileMapping.mappingEntries();
632         if (url[url.length - 1] !== "/")
633             url += "/";
634         if (path[path.length - 1] !== "/")
635             path += "/";
636         var mappingEntry = new WebInspector.FileMapping.Entry(url, path);
637         mappingEntries.push(mappingEntry);
638         WebInspector.fileMapping.setMappingEntries(mappingEntries);
639         this._addMappingRow(mappingEntry);
640         this._urlInputElement.value = "";
641         this._pathInputElement.value = "";
642     },
643
644     __proto__: WebInspector.SettingsTab.prototype
645 }
646
647 /**
648  * @constructor
649  * @extends {WebInspector.SettingsTab}
650  */
651 WebInspector.ExperimentsSettingsTab = function()
652 {
653     WebInspector.SettingsTab.call(this, WebInspector.UIString("Experiments"), "experiments-tab-content");
654
655     var experiments = WebInspector.experimentsSettings.experiments;
656     if (experiments.length) {
657         var experimentsSection = this._appendSection();
658         experimentsSection.appendChild(this._createExperimentsWarningSubsection());
659         for (var i = 0; i < experiments.length; ++i)
660             experimentsSection.appendChild(this._createExperimentCheckbox(experiments[i]));
661     }
662 }
663
664 WebInspector.ExperimentsSettingsTab.prototype = {
665     /**
666      * @return {Element} element
667      */
668     _createExperimentsWarningSubsection: function()
669     {
670         var subsection = document.createElement("div");
671         var warning = subsection.createChild("span", "settings-experiments-warning-subsection-warning");
672         warning.textContent = WebInspector.UIString("WARNING:");
673         subsection.appendChild(document.createTextNode(" "));
674         var message = subsection.createChild("span", "settings-experiments-warning-subsection-message");
675         message.textContent = WebInspector.UIString("These experiments could be dangerous and may require restart.");
676         return subsection;
677     },
678
679     _createExperimentCheckbox: function(experiment)
680     {
681         var input = document.createElement("input");
682         input.type = "checkbox";
683         input.name = experiment.name;
684         input.checked = experiment.isEnabled();
685         function listener()
686         {
687             experiment.setEnabled(input.checked);
688         }
689         input.addEventListener("click", listener, false);
690
691         var p = document.createElement("p");
692         var label = document.createElement("label");
693         label.appendChild(input);
694         label.appendChild(document.createTextNode(WebInspector.UIString(experiment.title)));
695         p.appendChild(label);
696         return p;
697     },
698
699     __proto__: WebInspector.SettingsTab.prototype
700 }
701
702 /**
703  * @constructor
704  */
705 WebInspector.SettingsController = function()
706 {
707     this._statusBarButton = new WebInspector.StatusBarButton(WebInspector.UIString("Settings"), "settings-status-bar-item");
708     if (WebInspector.experimentsSettings.showOverridesInDrawer.isEnabled())
709         this._statusBarButton.element.addEventListener("mousedown", this._mouseDown.bind(this), false);
710     else
711         this._statusBarButton.element.addEventListener("mouseup", this._mouseUp.bind(this), false);
712
713     /** @type {?WebInspector.SettingsScreen} */
714     this._settingsScreen;
715 }
716
717 WebInspector.SettingsController.prototype =
718 {
719     get statusBarItem()
720     {
721         return this._statusBarButton.element;
722     },
723
724     /**
725      * @param {Event} event
726      */
727     _mouseDown: function(event)
728     {
729         var contextMenu = new WebInspector.ContextMenu(event);
730         contextMenu.appendItem(WebInspector.UIString("Overrides"), showOverrides.bind(this));
731         contextMenu.appendItem(WebInspector.UIString("Settings"), showSettings.bind(this));
732
733         function showOverrides()
734         {
735             if (this._settingsScreenVisible)
736                 this._hideSettingsScreen();
737             WebInspector.OverridesView.showInDrawer();
738         }
739
740         function showSettings()
741         {
742             if (!this._settingsScreenVisible)
743                 this.showSettingsScreen();
744         }
745
746         contextMenu.showSoftMenu();
747     },
748
749     /**
750      * @param {Event} event
751      */
752     _mouseUp: function(event)
753     {
754         this.showSettingsScreen();
755     },
756
757     _onHideSettingsScreen: function()
758     {
759         delete this._settingsScreenVisible;
760     },
761
762     /**
763      * @param {string=} tabId
764      */
765     showSettingsScreen: function(tabId)
766     {
767         if (!this._settingsScreen)
768             this._settingsScreen = new WebInspector.SettingsScreen(this._onHideSettingsScreen.bind(this));
769
770         if (tabId)
771             this._settingsScreen.selectTab(tabId);
772
773         this._settingsScreen.showModal();
774         this._settingsScreenVisible = true;
775     },
776
777     _hideSettingsScreen: function()
778     {
779         if (this._settingsScreen)
780             this._settingsScreen.hide();
781     },
782
783     resize: function()
784     {
785         if (this._settingsScreen && this._settingsScreen.isShowing())
786             this._settingsScreen.doResize();
787     }
788 }