1/*
2 * Copyright (C) 2013 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 */
36WebInspector.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
67WebInspector.SettingsScreen.Tabs = {
68    General: "general",
69    Overrides: "overrides",
70    Workspace: "workspace",
71    Experiments: "experiments",
72    Shortcuts: "shortcuts"
73}
74
75WebInspector.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 */
130WebInspector.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
141WebInspector.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    /**
248     * @param {string} label
249     * @param {WebInspector.Setting} setting
250     * @param {boolean} numeric
251     * @param {number=} maxLength
252     * @param {string=} width
253     * @param {function(string):boolean=} validatorCallback
254     */
255    _createInputSetting: function(label, setting, numeric, maxLength, width, validatorCallback)
256    {
257        var fieldset = document.createElement("fieldset");
258        var p = fieldset.createChild("p");
259        var labelElement = p.createChild("label");
260        labelElement.textContent = label + " ";
261        var inputElement = labelElement.createChild("input");
262        inputElement.value = setting.get();
263        inputElement.type = "text";
264        if (numeric)
265            inputElement.className = "numeric";
266        if (maxLength)
267            inputElement.maxLength = maxLength;
268        if (width)
269            inputElement.style.width = width;
270
271        function onBlur()
272        {
273            if (validatorCallback && !validatorCallback(inputElement.value)) {
274                inputElement.value = setting.get();
275                return;
276            }
277            setting.set(numeric ? Number(inputElement.value) : inputElement.value);
278        }
279        inputElement.addEventListener("blur", onBlur, false);
280        return fieldset;
281    },
282
283    _createCustomSetting: function(name, element)
284    {
285        var p = document.createElement("p");
286        var fieldsetElement = document.createElement("fieldset");
287        fieldsetElement.createChild("label").textContent = name;
288        fieldsetElement.appendChild(element);
289        p.appendChild(fieldsetElement);
290        return p;
291    },
292
293    __proto__: WebInspector.View.prototype
294}
295
296/**
297 * @constructor
298 * @extends {WebInspector.SettingsTab}
299 */
300WebInspector.GenericSettingsTab = function()
301{
302    WebInspector.SettingsTab.call(this, WebInspector.UIString("General"));
303
304    var p = this._appendSection();
305    if (Preferences.exposeDisableCache)
306        p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Disable cache"), WebInspector.settings.cacheDisabled));
307    var disableJSElement = this._createCheckboxSetting(WebInspector.UIString("Disable JavaScript"), WebInspector.settings.javaScriptDisabled);
308    p.appendChild(disableJSElement);
309    WebInspector.settings.javaScriptDisabled.addChangeListener(this._javaScriptDisabledChanged, this);
310    this._disableJSCheckbox = disableJSElement.getElementsByTagName("input")[0];
311    this._updateScriptDisabledCheckbox();
312
313    p = this._appendSection(WebInspector.UIString("Appearance"));
314    p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Show toolbar icons"), WebInspector.settings.showToolbarIcons));
315    p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Split panels vertically when docked to right"), WebInspector.settings.splitVerticallyWhenDockedToRight));
316
317    p = this._appendSection(WebInspector.UIString("Elements"));
318    p.appendChild(this._createRadioSetting(WebInspector.UIString("Color format"), [
319        [ WebInspector.Color.Format.Original, WebInspector.UIString("As authored") ],
320        [ WebInspector.Color.Format.HEX, "HEX: #DAC0DE" ],
321        [ WebInspector.Color.Format.RGB, "RGB: rgb(128, 255, 255)" ],
322        [ WebInspector.Color.Format.HSL, "HSL: hsl(300, 80%, 90%)" ] ], WebInspector.settings.colorFormat));
323    p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Show user agent styles"), WebInspector.settings.showUserAgentStyles));
324    p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Word wrap"), WebInspector.settings.domWordWrap));
325    p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Show Shadow DOM"), WebInspector.settings.showShadowDOM));
326    p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Show rulers"), WebInspector.settings.showMetricsRulers));
327
328    p = this._appendSection(WebInspector.UIString("Rendering"));
329    p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Show paint rectangles"), WebInspector.settings.showPaintRects));
330    WebInspector.settings.showPaintRects.addChangeListener(this._showPaintRectsChanged, this);
331
332    if (Capabilities.canShowDebugBorders) {
333        p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Show composited layer borders"), WebInspector.settings.showDebugBorders));
334        WebInspector.settings.showDebugBorders.addChangeListener(this._showDebugBordersChanged, this);
335    }
336    if (Capabilities.canShowFPSCounter) {
337        p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Show FPS meter"), WebInspector.settings.showFPSCounter));
338        WebInspector.settings.showFPSCounter.addChangeListener(this._showFPSCounterChanged, this);
339    }
340    if (Capabilities.canContinuouslyPaint) {
341        p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Enable continuous page repainting"), WebInspector.settings.continuousPainting));
342        WebInspector.settings.continuousPainting.addChangeListener(this._continuousPaintingChanged, this);
343    }
344
345    p = this._appendSection(WebInspector.UIString("Sources"));
346    p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Search in content scripts"), WebInspector.settings.searchInContentScripts));
347    p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Enable source maps"), WebInspector.settings.sourceMapsEnabled));
348    if (WebInspector.experimentsSettings.isEnabled("sass"))
349        p.appendChild(this._createCSSAutoReloadControls());
350    var indentationElement = this._createSelectSetting(WebInspector.UIString("Indentation"), [
351            [ WebInspector.UIString("2 spaces"), WebInspector.TextUtils.Indent.TwoSpaces ],
352            [ WebInspector.UIString("4 spaces"), WebInspector.TextUtils.Indent.FourSpaces ],
353            [ WebInspector.UIString("8 spaces"), WebInspector.TextUtils.Indent.EightSpaces ],
354            [ WebInspector.UIString("Tab character"), WebInspector.TextUtils.Indent.TabCharacter ]
355        ], WebInspector.settings.textEditorIndent);
356    indentationElement.firstChild.className = "toplevel";
357    p.appendChild(indentationElement);
358
359    p = this._appendSection(WebInspector.UIString("Profiler"));
360    p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Show objects' hidden properties"), WebInspector.settings.showHeapSnapshotObjectsHiddenProperties));
361
362    p = this._appendSection(WebInspector.UIString("Timeline"));
363    var checkbox = this._createCheckboxSetting(WebInspector.UIString("Limit number of captured JS stack frames"), WebInspector.settings.timelineLimitStackFramesFlag);
364    p.appendChild(checkbox);
365    var fieldset = this._createInputSetting(WebInspector.UIString("Frames to capture"), WebInspector.settings.timelineStackFramesToCapture, true, 2, "2em");
366    fieldset.disabled = !WebInspector.settings.timelineLimitStackFramesFlag.get();
367    WebInspector.settings.timelineLimitStackFramesFlag.addChangeListener(this._timelineLimitStackFramesChanged.bind(this, fieldset));
368    checkbox.appendChild(fieldset);
369
370    if (Capabilities.timelineCanMonitorMainThread)
371        p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Show CPU activity on the ruler"), WebInspector.settings.showCpuOnTimelineRuler));
372
373    p = this._appendSection(WebInspector.UIString("Console"));
374    p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Log XMLHttpRequests"), WebInspector.settings.monitoringXHREnabled));
375    p.appendChild(this._createCheckboxSetting(WebInspector.UIString("Preserve log upon navigation"), WebInspector.settings.preserveConsoleLog));
376
377    if (WebInspector.extensionServer.hasExtensions()) {
378        var handlerSelector = new WebInspector.HandlerSelector(WebInspector.openAnchorLocationRegistry);
379        p = this._appendSection(WebInspector.UIString("Extensions"));
380        p.appendChild(this._createCustomSetting(WebInspector.UIString("Open links in"), handlerSelector.element));
381    }
382}
383
384WebInspector.GenericSettingsTab.prototype = {
385    _showPaintRectsChanged: function()
386    {
387        PageAgent.setShowPaintRects(WebInspector.settings.showPaintRects.get());
388    },
389
390    _showDebugBordersChanged: function()
391    {
392        PageAgent.setShowDebugBorders(WebInspector.settings.showDebugBorders.get());
393    },
394
395    _showFPSCounterChanged: function()
396    {
397        PageAgent.setShowFPSCounter(WebInspector.settings.showFPSCounter.get());
398    },
399
400    _continuousPaintingChanged: function()
401    {
402        PageAgent.setContinuousPaintingEnabled(WebInspector.settings.continuousPainting.get());
403    },
404
405    /**
406     * @param {HTMLFieldSetElement} fieldset
407     */
408    _timelineLimitStackFramesChanged: function(fieldset)
409    {
410        fieldset.disabled = !WebInspector.settings.timelineLimitStackFramesFlag.get();
411    },
412
413    _updateScriptDisabledCheckbox: function()
414    {
415        function executionStatusCallback(error, status)
416        {
417            if (error || !status)
418                return;
419
420            switch (status) {
421            case "forbidden":
422                this._disableJSCheckbox.checked = true;
423                this._disableJSCheckbox.disabled = true;
424                break;
425            case "disabled":
426                this._disableJSCheckbox.checked = true;
427                break;
428            default:
429                this._disableJSCheckbox.checked = false;
430                break;
431            }
432        }
433
434        PageAgent.getScriptExecutionStatus(executionStatusCallback.bind(this));
435    },
436
437    _javaScriptDisabledChanged: function()
438    {
439        // We need to manually update the checkbox state, since enabling JavaScript in the page can actually uncover the "forbidden" state.
440        PageAgent.setScriptExecutionDisabled(WebInspector.settings.javaScriptDisabled.get(), this._updateScriptDisabledCheckbox.bind(this));
441    },
442
443    _createCSSAutoReloadControls: function()
444    {
445        var fragment = document.createDocumentFragment();
446        var labelElement = fragment.createChild("label");
447        var checkboxElement = labelElement.createChild("input");
448        checkboxElement.type = "checkbox";
449        checkboxElement.checked = WebInspector.settings.cssReloadEnabled.get();
450        checkboxElement.addEventListener("click", checkboxClicked, false);
451        labelElement.appendChild(document.createTextNode(WebInspector.UIString("Auto-reload CSS upon Sass save")));
452
453        var fieldsetElement = this._createInputSetting(WebInspector.UIString("Timeout (ms)"), WebInspector.settings.cssReloadTimeout, true, 8, "60px", validateReloadTimeout);
454        fieldsetElement.disabled = !checkboxElement.checked;
455        fragment.appendChild(fieldsetElement);
456        return fragment;
457
458        function checkboxClicked()
459        {
460            var reloadEnabled = checkboxElement.checked;
461            WebInspector.settings.cssReloadEnabled.set(reloadEnabled);
462            fieldsetElement.disabled = !reloadEnabled;
463        }
464
465        function validateReloadTimeout(value)
466        {
467            return isFinite(value) && value > 0;
468        }
469    },
470
471    __proto__: WebInspector.SettingsTab.prototype
472}
473
474/**
475 * @constructor
476 * @extends {WebInspector.SettingsTab}
477 */
478WebInspector.OverridesSettingsTab = function()
479{
480    WebInspector.SettingsTab.call(this, WebInspector.UIString("Overrides"), "overrides-tab-content");
481    this._view = new WebInspector.OverridesView();
482    this.containerElement.parentElement.appendChild(this._view.containerElement);
483    this.containerElement.removeSelf();
484    this.containerElement = this._view.containerElement;
485}
486
487WebInspector.OverridesSettingsTab.prototype = {
488    __proto__: WebInspector.SettingsTab.prototype
489}
490
491/**
492 * @constructor
493 * @extends {WebInspector.SettingsTab}
494 */
495WebInspector.WorkspaceSettingsTab = function()
496{
497    WebInspector.SettingsTab.call(this, WebInspector.UIString("Workspace"), "workspace-tab-content");
498    this._reset();
499}
500
501WebInspector.WorkspaceSettingsTab.prototype = {
502    wasShown: function()
503    {
504        WebInspector.SettingsTab.prototype.wasShown.call(this);
505        this._reset();
506    },
507
508    _reset: function()
509    {
510        this.containerElement.removeChildren();
511        this._createFileSystemsEditor();
512        this._createFileMappingEditor();
513    },
514
515    _createFileSystemsEditor: function()
516    {
517        var p = this._appendSection(WebInspector.UIString("File systems"));
518        this._fileSystemsEditor = p.createChild("p", "file-systems-editor");
519
520        this._addFileSystemRowElement = this._fileSystemsEditor.createChild("div", "workspace-settings-row");
521        var addFileSystemButton = this._addFileSystemRowElement.createChild("input", "file-system-add-button");
522        addFileSystemButton.type = "button";
523        addFileSystemButton.value = WebInspector.UIString("Add file system");
524        addFileSystemButton.addEventListener("click", this._addFileSystemClicked.bind(this));
525
526        var fileSystemPaths = WebInspector.isolatedFileSystemManager.mapping().fileSystemPaths();
527        for (var i = 0; i < fileSystemPaths.length; ++i)
528            this._addFileSystemRow(fileSystemPaths[i]);
529
530        return this._fileSystemsEditor;
531    },
532
533    /**
534     * @return {Element}
535     */
536    _createShowTextInput: function(className, value)
537    {
538        var inputElement = document.createElement("input");
539        inputElement.addStyleClass(className);
540        inputElement.type = "text";
541        inputElement.value = value;
542        inputElement.title = value;
543        inputElement.disabled = true;
544        return inputElement;
545    },
546
547    /**
548     * @return {Element}
549     */
550    _createEditTextInput: function(className, placeHolder)
551    {
552        var inputElement = document.createElement("input");
553        inputElement.addStyleClass(className);
554        inputElement.type = "text";
555        inputElement.placeholder = placeHolder;
556        return inputElement;
557    },
558
559    /**
560     * @param {function(Event)} handler
561     * @return {Element}
562     */
563    _createRemoveButton: function(handler)
564    {
565        var removeButton = document.createElement("button");
566        removeButton.addStyleClass("button");
567        removeButton.addStyleClass("remove-button");
568        removeButton.value = WebInspector.UIString("Remove");
569        removeButton.addEventListener("click", handler, false);
570        return removeButton;
571    },
572
573    /**
574     * @param {function(Event)} handler
575     * @return {Element}
576     */
577    _createAddButton: function(handler)
578    {
579        var addButton = document.createElement("button");
580        addButton.addStyleClass("button");
581        addButton.addStyleClass("add-button");
582        addButton.value = WebInspector.UIString("Add");
583        addButton.addEventListener("click", handler, false);
584        return addButton;
585    },
586
587    /**
588     * @param {string} fileSystemPath
589     */
590    _addFileSystemRow: function(fileSystemPath)
591    {
592        var fileSystemRow = document.createElement("div");
593        fileSystemRow.addStyleClass("workspace-settings-row");
594        fileSystemRow.addStyleClass("file-system-row");
595        this._fileSystemsEditor.insertBefore(fileSystemRow, this._addFileSystemRowElement);
596
597        fileSystemRow.appendChild(this._createShowTextInput("file-system-path", fileSystemPath));
598        var removeFileSystemButton = this._createRemoveButton(removeFileSystemClicked.bind(this));
599        fileSystemRow.appendChild(removeFileSystemButton);
600
601        function removeFileSystemClicked()
602        {
603            removeFileSystemButton.disabled = true;
604            WebInspector.isolatedFileSystemManager.removeFileSystem(fileSystemPath, fileSystemRemoved.bind(this));
605        }
606
607        function fileSystemRemoved()
608        {
609            this._fileSystemsEditor.removeChild(fileSystemRow);
610            removeFileSystemButton.disabled = false;
611        }
612    },
613
614    _addFileSystemClicked: function()
615    {
616        WebInspector.isolatedFileSystemManager.addFileSystem(this._fileSystemAdded.bind(this));
617    },
618
619    /**
620     * @param {?string} fileSystemPath
621     */
622    _fileSystemAdded: function(fileSystemPath)
623    {
624        if (fileSystemPath)
625            this._addFileSystemRow(fileSystemPath);
626    },
627
628    _createFileMappingEditor: function()
629    {
630        var p = this._appendSection(WebInspector.UIString("Mappings"));
631        this._fileMappingEditor = p.createChild("p", "file-mappings-editor");
632
633        this._addMappingRowElement = this._fileMappingEditor.createChild("div", "workspace-settings-row");
634
635        this._urlInputElement = this._createEditTextInput("file-mapping-url", WebInspector.UIString("File mapping url"));
636        this._addMappingRowElement.appendChild(this._urlInputElement);
637        this._pathInputElement = this._createEditTextInput("file-mapping-path", WebInspector.UIString("File mapping path"));
638        this._addMappingRowElement.appendChild(this._pathInputElement);
639
640        this._addMappingRowElement.appendChild(this._createAddButton(this._addFileMappingClicked.bind(this)));
641
642        var mappingEntries = WebInspector.fileMapping.mappingEntries();
643        for (var i = 0; i < mappingEntries.length; ++i)
644            this._addMappingRow(mappingEntries[i]);
645
646        return this._fileMappingEditor;
647    },
648
649    /**
650     * @param {WebInspector.FileMapping.Entry} mappingEntry
651     */
652    _addMappingRow: function(mappingEntry)
653    {
654        var fileMappingRow = document.createElement("div");
655        fileMappingRow.addStyleClass("workspace-settings-row");
656        this._fileMappingEditor.insertBefore(fileMappingRow, this._addMappingRowElement);
657
658        fileMappingRow.appendChild(this._createShowTextInput("file-mapping-url", mappingEntry.urlPrefix));
659        fileMappingRow.appendChild(this._createShowTextInput("file-mapping-path", mappingEntry.pathPrefix));
660
661        fileMappingRow.appendChild(this._createRemoveButton(removeMappingClicked.bind(this)));
662
663        function removeMappingClicked()
664        {
665            var index = Array.prototype.slice.call(fileMappingRow.parentElement.childNodes).indexOf(fileMappingRow);
666            var mappingEntries = WebInspector.fileMapping.mappingEntries();
667            mappingEntries.splice(index, 1);
668            WebInspector.fileMapping.setMappingEntries(mappingEntries);
669            this._fileMappingEditor.removeChild(fileMappingRow);
670        }
671    },
672
673    _addFileMappingClicked: function()
674    {
675        var url = this._urlInputElement.value;
676        var path = this._pathInputElement.value;
677        if (!url || !path)
678            return;
679        var mappingEntries = WebInspector.fileMapping.mappingEntries();
680        if (url[url.length - 1] !== "/")
681            url += "/";
682        if (path[path.length - 1] !== "/")
683            path += "/";
684        var mappingEntry = new WebInspector.FileMapping.Entry(url, path);
685        mappingEntries.push(mappingEntry);
686        WebInspector.fileMapping.setMappingEntries(mappingEntries);
687        this._addMappingRow(mappingEntry);
688        this._urlInputElement.value = "";
689        this._pathInputElement.value = "";
690    },
691
692    __proto__: WebInspector.SettingsTab.prototype
693}
694
695/**
696 * @constructor
697 * @extends {WebInspector.SettingsTab}
698 */
699WebInspector.ExperimentsSettingsTab = function()
700{
701    WebInspector.SettingsTab.call(this, WebInspector.UIString("Experiments"), "experiments-tab-content");
702
703    var experiments = WebInspector.experimentsSettings.experiments;
704    if (experiments.length) {
705        var experimentsSection = this._appendSection();
706        experimentsSection.appendChild(this._createExperimentsWarningSubsection());
707        for (var i = 0; i < experiments.length; ++i)
708            experimentsSection.appendChild(this._createExperimentCheckbox(experiments[i]));
709    }
710}
711
712WebInspector.ExperimentsSettingsTab.prototype = {
713    /**
714     * @return {Element} element
715     */
716    _createExperimentsWarningSubsection: function()
717    {
718        var subsection = document.createElement("div");
719        var warning = subsection.createChild("span", "settings-experiments-warning-subsection-warning");
720        warning.textContent = WebInspector.UIString("WARNING:");
721        subsection.appendChild(document.createTextNode(" "));
722        var message = subsection.createChild("span", "settings-experiments-warning-subsection-message");
723        message.textContent = WebInspector.UIString("These experiments could be dangerous and may require restart.");
724        return subsection;
725    },
726
727    _createExperimentCheckbox: function(experiment)
728    {
729        var input = document.createElement("input");
730        input.type = "checkbox";
731        input.name = experiment.name;
732        input.checked = experiment.isEnabled();
733        function listener()
734        {
735            experiment.setEnabled(input.checked);
736        }
737        input.addEventListener("click", listener, false);
738
739        var p = document.createElement("p");
740        var label = document.createElement("label");
741        label.appendChild(input);
742        label.appendChild(document.createTextNode(WebInspector.UIString(experiment.title)));
743        p.appendChild(label);
744        return p;
745    },
746
747    __proto__: WebInspector.SettingsTab.prototype
748}
749
750/**
751 * @constructor
752 */
753WebInspector.SettingsController = function()
754{
755    this._statusBarButton = new WebInspector.StatusBarButton(WebInspector.UIString("Settings"), "settings-status-bar-item");
756    if (WebInspector.experimentsSettings.showOverridesInDrawer.isEnabled())
757        this._statusBarButton.element.addEventListener("mousedown", this._mouseDown.bind(this), false);
758    else
759        this._statusBarButton.element.addEventListener("mouseup", this._mouseUp.bind(this), false);
760
761    /** @type {?WebInspector.SettingsScreen} */
762    this._settingsScreen;
763}
764
765WebInspector.SettingsController.prototype =
766{
767    get statusBarItem()
768    {
769        return this._statusBarButton.element;
770    },
771
772    /**
773     * @param {Event} event
774     */
775    _mouseDown: function(event)
776    {
777        var contextMenu = new WebInspector.ContextMenu(event);
778        contextMenu.appendItem(WebInspector.UIString("Overrides"), showOverrides.bind(this));
779        contextMenu.appendItem(WebInspector.UIString("Settings"), showSettings.bind(this));
780
781        function showOverrides()
782        {
783            if (this._settingsScreenVisible)
784                this._hideSettingsScreen();
785            WebInspector.OverridesView.showInDrawer();
786        }
787
788        function showSettings()
789        {
790            if (!this._settingsScreenVisible)
791                this.showSettingsScreen();
792        }
793
794        contextMenu.showSoftMenu();
795    },
796
797    /**
798     * @param {Event} event
799     */
800    _mouseUp: function(event)
801    {
802        this.showSettingsScreen();
803    },
804
805    _onHideSettingsScreen: function()
806    {
807        delete this._settingsScreenVisible;
808    },
809
810    /**
811     * @param {string=} tabId
812     */
813    showSettingsScreen: function(tabId)
814    {
815        if (!this._settingsScreen)
816            this._settingsScreen = new WebInspector.SettingsScreen(this._onHideSettingsScreen.bind(this));
817
818        if (tabId)
819            this._settingsScreen.selectTab(tabId);
820
821        this._settingsScreen.showModal();
822        this._settingsScreenVisible = true;
823    },
824
825    _hideSettingsScreen: function()
826    {
827        if (this._settingsScreen)
828            this._settingsScreen.hide();
829    },
830
831    resize: function()
832    {
833        if (this._settingsScreen && this._settingsScreen.isShowing())
834            this._settingsScreen.doResize();
835    }
836}
837