Web Inspector: show whitespace characters in DTE
[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("Show whitespace"), WebInspector.settings.showWhitespaceInEditor));
311     p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Enable source maps"), WebInspector.settings.sourceMapsEnabled));
312     if (WebInspector.experimentsSettings.isEnabled("sass"))
313         p.appendChild(this._createCSSAutoReloadControls());
314     var indentationElement = this._createSelectSetting(WebInspector.UIString("Indentation"), [
315             [ WebInspector.UIString("2 spaces"), WebInspector.TextEditorModel.Indent.TwoSpaces ],
316             [ WebInspector.UIString("4 spaces"), WebInspector.TextEditorModel.Indent.FourSpaces ],
317             [ WebInspector.UIString("8 spaces"), WebInspector.TextEditorModel.Indent.EightSpaces ],
318             [ WebInspector.UIString("Tab character"), WebInspector.TextEditorModel.Indent.TabCharacter ]
319         ], WebInspector.settings.textEditorIndent);
320     indentationElement.firstChild.className = "toplevel";
321     p.appendChild(indentationElement);
322
323     p = this._appendSection(WebInspector.UIString("Profiler"));
324     p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Show objects' hidden properties"), WebInspector.settings.showHeapSnapshotObjectsHiddenProperties));
325     if (WebInspector.experimentsSettings.nativeMemorySnapshots.isEnabled())
326         p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Show uninstrumented native memory"), WebInspector.settings.showNativeSnapshotUninstrumentedSize));
327
328     if (Capabilities.timelineCanMonitorMainThread) {
329         p = this._appendSection(WebInspector.UIString("Timeline"));
330         p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Show CPU activity on the ruler"), WebInspector.settings.showCpuOnTimelineRuler));
331     }
332
333     p = this._appendSection(WebInspector.UIString("Console"));
334     p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Log XMLHttpRequests"), WebInspector.settings.monitoringXHREnabled));
335     p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Preserve log upon navigation"), WebInspector.settings.preserveConsoleLog));
336
337     if (WebInspector.extensionServer.hasExtensions()) {
338         var handlerSelector = new WebInspector.HandlerSelector(WebInspector.openAnchorLocationRegistry);
339         p = this._appendSection(WebInspector.UIString("Extensions"));
340         p.appendChild(this._createCustomSetting(WebInspector.UIString("Open links in"), handlerSelector.element));
341     }
342 }
343
344 WebInspector.GenericSettingsTab.prototype = {
345     _showPaintRectsChanged: function()
346     {
347         PageAgent.setShowPaintRects(WebInspector.settings.showPaintRects.get());
348     },
349
350     _showDebugBordersChanged: function()
351     {
352         PageAgent.setShowDebugBorders(WebInspector.settings.showDebugBorders.get());
353     },
354
355     _showFPSCounterChanged: function()
356     {
357         PageAgent.setShowFPSCounter(WebInspector.settings.showFPSCounter.get());
358     },
359
360     _continuousPaintingChanged: function()
361     {
362         PageAgent.setContinuousPaintingEnabled(WebInspector.settings.continuousPainting.get());
363     },
364
365     _updateScriptDisabledCheckbox: function()
366     {
367         function executionStatusCallback(error, status)
368         {
369             if (error || !status)
370                 return;
371
372             switch (status) {
373             case "forbidden":
374                 this._disableJSCheckbox.checked = true;
375                 this._disableJSCheckbox.disabled = true;
376                 break;
377             case "disabled":
378                 this._disableJSCheckbox.checked = true;
379                 break;
380             default:
381                 this._disableJSCheckbox.checked = false;
382                 break;
383             }
384         }
385
386         PageAgent.getScriptExecutionStatus(executionStatusCallback.bind(this));
387     },
388
389     _javaScriptDisabledChanged: function()
390     {
391         // We need to manually update the checkbox state, since enabling JavaScript in the page can actually uncover the "forbidden" state.
392         PageAgent.setScriptExecutionDisabled(WebInspector.settings.javaScriptDisabled.get(), this._updateScriptDisabledCheckbox.bind(this));
393     },
394
395     _createCSSAutoReloadControls: function()
396     {
397         var fragment = document.createDocumentFragment();
398         var labelElement = fragment.createChild("label");
399         var checkboxElement = labelElement.createChild("input");
400         checkboxElement.type = "checkbox";
401         checkboxElement.checked = WebInspector.settings.cssReloadEnabled.get();
402         checkboxElement.addEventListener("click", checkboxClicked, false);
403         labelElement.appendChild(document.createTextNode(WebInspector.UIString("Auto-reload CSS upon Sass save")));
404
405         var fieldsetElement = fragment.createChild("fieldset");
406         fieldsetElement.disabled = !checkboxElement.checked;
407         var p = fieldsetElement.createChild("p");
408         p.appendChild(document.createTextNode(WebInspector.UIString("Timeout (ms)")));
409         p.appendChild(document.createTextNode(" "));
410         var timeoutInput = p.createChild("input");
411         timeoutInput.value = WebInspector.settings.cssReloadTimeout.get();
412         timeoutInput.className = "numeric";
413         timeoutInput.style.width = "60px";
414         timeoutInput.maxLength = 8;
415         timeoutInput.addEventListener("blur", blurListener, false);
416         return fragment;
417
418         function checkboxClicked()
419         {
420             var reloadEnabled = checkboxElement.checked;
421             WebInspector.settings.cssReloadEnabled.set(reloadEnabled);
422             fieldsetElement.disabled = !reloadEnabled;
423         }
424
425         function blurListener()
426         {
427             var value = timeoutInput.value;
428             if (!isFinite(value) || value <= 0) {
429                 timeoutInput.value = WebInspector.settings.cssReloadTimeout.get();
430                 return;
431             }
432             WebInspector.settings.cssReloadTimeout.set(Number(value));
433         }
434     },
435
436     __proto__: WebInspector.SettingsTab.prototype
437 }
438
439 /**
440  * @constructor
441  * @extends {WebInspector.SettingsTab}
442  */
443 WebInspector.OverridesSettingsTab = function()
444 {
445     WebInspector.SettingsTab.call(this, WebInspector.UIString("Overrides"), "overrides-tab-content");
446     this._view = new WebInspector.OverridesView();
447     this.containerElement.parentElement.appendChild(this._view.containerElement);
448     this.containerElement.removeSelf();
449     this.containerElement = this._view.containerElement;
450 }
451
452 WebInspector.OverridesSettingsTab.prototype = {
453     __proto__: WebInspector.SettingsTab.prototype
454 }
455
456 /**
457  * @constructor
458  * @extends {WebInspector.SettingsTab}
459  */
460 WebInspector.WorkspaceSettingsTab = function()
461 {
462     WebInspector.SettingsTab.call(this, WebInspector.UIString("Workspace"), "workspace-tab-content");
463     this._createFileSystemsEditor();
464     this._createFileMappingEditor();
465 }
466
467 WebInspector.WorkspaceSettingsTab.prototype = {
468     _createFileSystemsEditor: function()
469     {
470         var p = this._appendSection(WebInspector.UIString("File systems"));
471         this._fileSystemsEditor = p.createChild("p", "file-systems-editor");
472
473         this._addFileSystemRowElement = this._fileSystemsEditor.createChild("div", "workspace-settings-row");
474         var addFileSystemButton = this._addFileSystemRowElement.createChild("input", "file-system-add-button");
475         addFileSystemButton.type = "button";
476         addFileSystemButton.value = WebInspector.UIString("Add file system");
477         addFileSystemButton.addEventListener("click", this._addFileSystemClicked.bind(this));
478
479         var fileSystemPaths = WebInspector.isolatedFileSystemModel.mapping().fileSystemPaths();
480         for (var i = 0; i < fileSystemPaths.length; ++i)
481             this._addFileSystemRow(fileSystemPaths[i]);
482
483         return this._fileSystemsEditor;
484     },
485
486     /**
487      * @return {Element}
488      */
489     _createShowTextInput: function(className, value)
490     {
491         var inputElement = document.createElement("input");
492         inputElement.addStyleClass(className);
493         inputElement.type = "text";
494         inputElement.value = value;
495         inputElement.title = value;
496         inputElement.disabled = true;
497         return inputElement;
498     },
499
500     /**
501      * @return {Element}
502      */
503     _createEditTextInput: function(className, placeHolder)
504     {
505         var inputElement = document.createElement("input");
506         inputElement.addStyleClass(className);
507         inputElement.type = "text";
508         inputElement.placeholder = placeHolder;
509         return inputElement;
510     },
511
512     /**
513      * @param {function(Event)} handler
514      * @return {Element}
515      */
516     _createRemoveButton: function(handler)
517     {
518         var removeButton = document.createElement("button");
519         removeButton.addStyleClass("button");
520         removeButton.addStyleClass("remove-button");
521         removeButton.value = WebInspector.UIString("Remove");
522         removeButton.addEventListener("click", handler, false);
523         return removeButton;
524     },
525
526     /**
527      * @param {function(Event)} handler
528      * @return {Element}
529      */
530     _createAddButton: function(handler)
531     {
532         var addButton = document.createElement("button");
533         addButton.addStyleClass("button");
534         addButton.addStyleClass("add-button");
535         addButton.value = WebInspector.UIString("Add");
536         addButton.addEventListener("click", handler, false);
537         return addButton;
538     },
539
540     /**
541      * @param {string} fileSystemPath
542      */
543     _addFileSystemRow: function(fileSystemPath)
544     {
545         var fileSystemRow = document.createElement("div");
546         fileSystemRow.addStyleClass("workspace-settings-row");
547         fileSystemRow.addStyleClass("file-system-row");
548         this._fileSystemsEditor.insertBefore(fileSystemRow, this._addFileSystemRowElement);
549
550         fileSystemRow.appendChild(this._createShowTextInput("file-system-path", fileSystemPath));
551         var removeFileSystemButton = this._createRemoveButton(removeFileSystemClicked.bind(this));
552         fileSystemRow.appendChild(removeFileSystemButton);
553
554         function removeFileSystemClicked()
555         {
556             removeFileSystemButton.disabled = true;
557             WebInspector.isolatedFileSystemModel.removeFileSystem(fileSystemPath, fileSystemRemoved.bind(this));
558         }
559         
560         function fileSystemRemoved()
561         {
562             this._fileSystemsEditor.removeChild(fileSystemRow);
563             removeFileSystemButton.disabled = false;
564         }
565     },
566
567     _addFileSystemClicked: function()
568     {
569         WebInspector.isolatedFileSystemModel.addFileSystem(this._fileSystemAdded.bind(this));
570     },
571
572     /**
573      * @param {?string} fileSystemPath
574      */
575     _fileSystemAdded: function(fileSystemPath)
576     {
577         if (fileSystemPath)
578             this._addFileSystemRow(fileSystemPath);
579     },
580
581     _createFileMappingEditor: function()
582     {
583         var p = this._appendSection(WebInspector.UIString("Mappings"));
584         this._fileMappingEditor = p.createChild("p", "file-mappings-editor");
585
586         this._addMappingRowElement = this._fileMappingEditor.createChild("div", "workspace-settings-row");
587
588         this._urlInputElement = this._createEditTextInput("file-mapping-url", WebInspector.UIString("File mapping url"));
589         this._addMappingRowElement.appendChild(this._urlInputElement);
590         this._pathInputElement = this._createEditTextInput("file-mapping-path", WebInspector.UIString("File mapping path"));
591         this._addMappingRowElement.appendChild(this._pathInputElement);
592
593         this._addMappingRowElement.appendChild(this._createAddButton(this._addFileMappingClicked.bind(this)));
594
595         var mappingEntries = WebInspector.fileMapping.mappingEntries();
596         for (var i = 0; i < mappingEntries.length; ++i)
597             this._addMappingRow(mappingEntries[i]);
598
599         return this._fileMappingEditor;
600     },
601
602     /**
603      * @param {WebInspector.FileMapping.Entry} mappingEntry
604      */
605     _addMappingRow: function(mappingEntry)
606     {
607         var fileMappingRow = document.createElement("div");
608         fileMappingRow.addStyleClass("workspace-settings-row");
609         this._fileMappingEditor.insertBefore(fileMappingRow, this._addMappingRowElement);
610
611         fileMappingRow.appendChild(this._createShowTextInput("file-mapping-url", mappingEntry.urlPrefix));
612         fileMappingRow.appendChild(this._createShowTextInput("file-mapping-path", mappingEntry.pathPrefix));
613
614         fileMappingRow.appendChild(this._createRemoveButton(removeMappingClicked.bind(this)));
615
616         function removeMappingClicked()
617         {
618             var index = Array.prototype.slice.call(fileMappingRow.parentElement.childNodes).indexOf(fileMappingRow);
619             var mappingEntries = WebInspector.fileMapping.mappingEntries();
620             mappingEntries.splice(index, 1);
621             WebInspector.fileMapping.setMappingEntries(mappingEntries);
622             this._fileMappingEditor.removeChild(fileMappingRow);
623         }
624     },
625
626     _addFileMappingClicked: function()
627     {
628         var url = this._urlInputElement.value;
629         var path = this._pathInputElement.value;
630         if (!url || !path)
631             return;
632         var mappingEntries = WebInspector.fileMapping.mappingEntries();
633         if (url[url.length - 1] !== "/")
634             url += "/";
635         if (path[path.length - 1] !== "/")
636             path += "/";
637         var mappingEntry = new WebInspector.FileMapping.Entry(url, path);
638         mappingEntries.push(mappingEntry);
639         WebInspector.fileMapping.setMappingEntries(mappingEntries);
640         this._addMappingRow(mappingEntry);
641         this._urlInputElement.value = "";
642         this._pathInputElement.value = "";
643     },
644
645     __proto__: WebInspector.SettingsTab.prototype
646 }
647
648 /**
649  * @constructor
650  * @extends {WebInspector.SettingsTab}
651  */
652 WebInspector.ExperimentsSettingsTab = function()
653 {
654     WebInspector.SettingsTab.call(this, WebInspector.UIString("Experiments"), "experiments-tab-content");
655
656     var experiments = WebInspector.experimentsSettings.experiments;
657     if (experiments.length) {
658         var experimentsSection = this._appendSection();
659         experimentsSection.appendChild(this._createExperimentsWarningSubsection());
660         for (var i = 0; i < experiments.length; ++i)
661             experimentsSection.appendChild(this._createExperimentCheckbox(experiments[i]));
662     }
663 }
664
665 WebInspector.ExperimentsSettingsTab.prototype = {
666     /**
667      * @return {Element} element
668      */
669     _createExperimentsWarningSubsection: function()
670     {
671         var subsection = document.createElement("div");
672         var warning = subsection.createChild("span", "settings-experiments-warning-subsection-warning");
673         warning.textContent = WebInspector.UIString("WARNING:");
674         subsection.appendChild(document.createTextNode(" "));
675         var message = subsection.createChild("span", "settings-experiments-warning-subsection-message");
676         message.textContent = WebInspector.UIString("These experiments could be dangerous and may require restart.");
677         return subsection;
678     },
679
680     _createExperimentCheckbox: function(experiment)
681     {
682         var input = document.createElement("input");
683         input.type = "checkbox";
684         input.name = experiment.name;
685         input.checked = experiment.isEnabled();
686         function listener()
687         {
688             experiment.setEnabled(input.checked);
689         }
690         input.addEventListener("click", listener, false);
691
692         var p = document.createElement("p");
693         var label = document.createElement("label");
694         label.appendChild(input);
695         label.appendChild(document.createTextNode(WebInspector.UIString(experiment.title)));
696         p.appendChild(label);
697         return p;
698     },
699
700     __proto__: WebInspector.SettingsTab.prototype
701 }
702
703 /**
704  * @constructor
705  */
706 WebInspector.SettingsController = function()
707 {
708     this._statusBarButton = new WebInspector.StatusBarButton(WebInspector.UIString("Settings"), "settings-status-bar-item");
709     if (WebInspector.experimentsSettings.showOverridesInDrawer.isEnabled())
710         this._statusBarButton.element.addEventListener("mousedown", this._mouseDown.bind(this), false);
711     else
712         this._statusBarButton.element.addEventListener("mouseup", this._mouseUp.bind(this), false);
713
714     /** @type {?WebInspector.SettingsScreen} */
715     this._settingsScreen;
716 }
717
718 WebInspector.SettingsController.prototype =
719 {
720     get statusBarItem()
721     {
722         return this._statusBarButton.element;
723     },
724
725     /**
726      * @param {Event} event
727      */
728     _mouseDown: function(event)
729     {
730         var contextMenu = new WebInspector.ContextMenu(event);
731         contextMenu.appendItem(WebInspector.UIString("Overrides"), showOverrides.bind(this));
732         contextMenu.appendItem(WebInspector.UIString("Settings"), showSettings.bind(this));
733
734         function showOverrides()
735         {
736             if (this._settingsScreenVisible)
737                 this._hideSettingsScreen();
738             WebInspector.OverridesView.showInDrawer();
739         }
740
741         function showSettings()
742         {
743             if (!this._settingsScreenVisible)
744                 this.showSettingsScreen();
745         }
746
747         contextMenu.showSoftMenu();
748     },
749
750     /**
751      * @param {Event} event
752      */
753     _mouseUp: function(event)
754     {
755         this.showSettingsScreen();
756     },
757
758     _onHideSettingsScreen: function()
759     {
760         delete this._settingsScreenVisible;
761     },
762
763     /**
764      * @param {string=} tabId
765      */
766     showSettingsScreen: function(tabId)
767     {
768         if (!this._settingsScreen)
769             this._settingsScreen = new WebInspector.SettingsScreen(this._onHideSettingsScreen.bind(this));
770
771         if (tabId)
772             this._settingsScreen.selectTab(tabId);
773
774         this._settingsScreen.showModal();
775         this._settingsScreenVisible = true;
776     },
777
778     _hideSettingsScreen: function()
779     {
780         if (this._settingsScreen)
781             this._settingsScreen.hide();
782     },
783
784     resize: function()
785     {
786         if (this._settingsScreen && this._settingsScreen.isShowing())
787             this._settingsScreen.doResize();
788     }
789 }