Home | History | Annotate | Download | only in front_end
      1 /*
      2  * Copyright (C) 2009 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 var Preferences = {
     33     maxInlineTextChildLength: 80,
     34     minConsoleHeight: 75,
     35     minSidebarWidth: 100,
     36     minSidebarHeight: 75,
     37     minElementsSidebarWidth: 200,
     38     minElementsSidebarHeight: 200,
     39     minScriptsSidebarWidth: 200,
     40     applicationTitle: "Developer Tools - %s",
     41     experimentsEnabled: false
     42 }
     43 
     44 var Capabilities = {
     45     canInspectWorkers: false
     46 }
     47 
     48 /**
     49  * @constructor
     50  */
     51 WebInspector.Settings = function()
     52 {
     53     this._eventSupport = new WebInspector.Object();
     54     this._registry = /** @type {!Object.<string, !WebInspector.Setting>} */ ({});
     55 
     56     this.colorFormat = this.createSetting("colorFormat", "original");
     57     this.consoleHistory = this.createSetting("consoleHistory", []);
     58     this.domWordWrap = this.createSetting("domWordWrap", true);
     59     this.eventListenersFilter = this.createSetting("eventListenersFilter", "all");
     60     this.lastActivePanel = this.createSetting("lastActivePanel", "elements");
     61     this.lastViewedScriptFile = this.createSetting("lastViewedScriptFile", "application");
     62     this.monitoringXHREnabled = this.createSetting("monitoringXHREnabled", false);
     63     this.preserveConsoleLog = this.createSetting("preserveConsoleLog", false);
     64     this.resourcesLargeRows = this.createSetting("resourcesLargeRows", true);
     65     this.resourcesSortOptions = this.createSetting("resourcesSortOptions", {timeOption: "responseTime", sizeOption: "transferSize"});
     66     this.resourceViewTab = this.createSetting("resourceViewTab", "preview");
     67     this.showInheritedComputedStyleProperties = this.createSetting("showInheritedComputedStyleProperties", false);
     68     this.showUserAgentStyles = this.createSetting("showUserAgentStyles", true);
     69     this.watchExpressions = this.createSetting("watchExpressions", []);
     70     this.breakpoints = this.createSetting("breakpoints", []);
     71     this.eventListenerBreakpoints = this.createSetting("eventListenerBreakpoints", []);
     72     this.domBreakpoints = this.createSetting("domBreakpoints", []);
     73     this.xhrBreakpoints = this.createSetting("xhrBreakpoints", []);
     74     this.jsSourceMapsEnabled = this.createSetting("sourceMapsEnabled", true);
     75     this.cssSourceMapsEnabled = this.createSetting("cssSourceMapsEnabled", true);
     76     this.cacheDisabled = this.createSetting("cacheDisabled", false);
     77     this.enableOverridesOnStartup = this.createSetting("enableOverridesOnStartup", false);
     78     this.overrideUserAgent = this.createSetting("overrideUserAgent", false);
     79     this.userAgent = this.createSetting("userAgent", "");
     80     this.overrideDeviceMetrics = this.createSetting("overrideDeviceMetrics", false);
     81     this.deviceMetrics = this.createSetting("deviceMetrics", "");
     82     this.deviceFitWindow = this.createSetting("deviceFitWindow", false);
     83     this.emulateTouchEvents = this.createSetting("emulateTouchEvents", false);
     84     this.showShadowDOM = this.createSetting("showShadowDOM", false);
     85     this.zoomLevel = this.createSetting("zoomLevel", 0);
     86     this.savedURLs = this.createSetting("savedURLs", {});
     87     this.javaScriptDisabled = this.createSetting("javaScriptDisabled", false);
     88     this.overrideGeolocation = this.createSetting("overrideGeolocation", false);
     89     this.geolocationOverride = this.createSetting("geolocationOverride", "");
     90     this.overrideDeviceOrientation = this.createSetting("overrideDeviceOrientation", false);
     91     this.deviceOrientationOverride = this.createSetting("deviceOrientationOverride", "");
     92     this.showAdvancedHeapSnapshotProperties = this.createSetting("showAdvancedHeapSnapshotProperties", false);
     93     this.searchInContentScripts = this.createSetting("searchInContentScripts", false);
     94     this.textEditorIndent = this.createSetting("textEditorIndent", "    ");
     95     this.textEditorAutoDetectIndent = this.createSetting("textEditorAutoIndentIndent", true);
     96     this.lastDockState = this.createSetting("lastDockState", "");
     97     this.cssReloadEnabled = this.createSetting("cssReloadEnabled", false);
     98     this.showCpuOnTimelineRuler = this.createSetting("showCpuOnTimelineRuler", false);
     99     this.timelineStackFramesToCapture = this.createSetting("timelineStackFramesToCapture", 30);
    100     this.timelineLimitStackFramesFlag = this.createSetting("timelineLimitStackFramesFlag", false);
    101     this.showMetricsRulers = this.createSetting("showMetricsRulers", false);
    102     this.overrideCSSMedia = this.createSetting("overrideCSSMedia", false);
    103     this.emulatedCSSMedia = this.createSetting("emulatedCSSMedia", "print");
    104     this.workerInspectorWidth = this.createSetting("workerInspectorWidth", 600);
    105     this.workerInspectorHeight = this.createSetting("workerInspectorHeight", 600);
    106     this.messageURLFilters = this.createSetting("messageURLFilters", {});
    107     this.messageSourceFilters = this.createSetting("messageSourceFilters", {"CSS": true});
    108     this.messageLevelFilters = this.createSetting("messageLevelFilters", {});
    109     this.splitVerticallyWhenDockedToRight = this.createSetting("splitVerticallyWhenDockedToRight", true);
    110     this.visiblePanels = this.createSetting("visiblePanels", {});
    111     this.shortcutPanelSwitch = this.createSetting("shortcutPanelSwitch", false);
    112     this.portForwardings = this.createSetting("portForwardings", []);
    113     this.showWhitespacesInEditor = this.createSetting("showWhitespacesInEditor", false);
    114     this.skipStackFramesSwitch = this.createSetting("skipStackFramesSwitch", false);
    115     this.skipStackFramesPattern = this.createSetting("skipStackFramesPattern", "");
    116 }
    117 
    118 WebInspector.Settings.prototype = {
    119     /**
    120      * @param {string} key
    121      * @param {*} defaultValue
    122      * @return {!WebInspector.Setting}
    123      */
    124     createSetting: function(key, defaultValue)
    125     {
    126         if (!this._registry[key])
    127             this._registry[key] = new WebInspector.Setting(key, defaultValue, this._eventSupport, window.localStorage);
    128         return this._registry[key];
    129     },
    130 
    131     /**
    132      * @param {string} key
    133      * @param {*} defaultValue
    134      * @param {function(*, function(string, ...))} setterCallback
    135      * @return {!WebInspector.Setting}
    136      */
    137     createBackendSetting: function(key, defaultValue, setterCallback)
    138     {
    139         if (!this._registry[key])
    140             this._registry[key] = new WebInspector.BackendSetting(key, defaultValue, this._eventSupport, window.localStorage, setterCallback);
    141         return this._registry[key];
    142     }
    143 }
    144 
    145 /**
    146  * @constructor
    147  * @param {string} name
    148  * @param {*} defaultValue
    149  * @param {!WebInspector.Object} eventSupport
    150  * @param {?Storage} storage
    151  */
    152 WebInspector.Setting = function(name, defaultValue, eventSupport, storage)
    153 {
    154     this._name = name;
    155     this._defaultValue = defaultValue;
    156     this._eventSupport = eventSupport;
    157     this._storage = storage;
    158 }
    159 
    160 WebInspector.Setting.prototype = {
    161     addChangeListener: function(listener, thisObject)
    162     {
    163         this._eventSupport.addEventListener(this._name, listener, thisObject);
    164     },
    165 
    166     removeChangeListener: function(listener, thisObject)
    167     {
    168         this._eventSupport.removeEventListener(this._name, listener, thisObject);
    169     },
    170 
    171     get name()
    172     {
    173         return this._name;
    174     },
    175 
    176     get: function()
    177     {
    178         if (typeof this._value !== "undefined")
    179             return this._value;
    180 
    181         this._value = this._defaultValue;
    182         if (this._storage && this._name in this._storage) {
    183             try {
    184                 this._value = JSON.parse(this._storage[this._name]);
    185             } catch(e) {
    186                 delete this._storage[this._name];
    187             }
    188         }
    189         return this._value;
    190     },
    191 
    192     set: function(value)
    193     {
    194         this._value = value;
    195         if (this._storage) {
    196             try {
    197                 this._storage[this._name] = JSON.stringify(value);
    198             } catch(e) {
    199                 console.error("Error saving setting with name:" + this._name);
    200             }
    201         }
    202         this._eventSupport.dispatchEventToListeners(this._name, value);
    203     }
    204 }
    205 
    206 /**
    207  * @constructor
    208  * @extends {WebInspector.Setting}
    209  * @param {string} name
    210  * @param {*} defaultValue
    211  * @param {!WebInspector.Object} eventSupport
    212  * @param {?Storage} storage
    213  * @param {function(*,function(string, ...))} setterCallback
    214  */
    215 WebInspector.BackendSetting = function(name, defaultValue, eventSupport, storage, setterCallback)
    216 {
    217     WebInspector.Setting.call(this, name, defaultValue, eventSupport, storage);
    218     this._setterCallback = setterCallback;
    219     var currentValue = this.get();
    220     if (currentValue !== defaultValue) {
    221         this._value = defaultValue; // Make sure we're in sync with backend, in case setting fails.
    222         this.set(currentValue);
    223     }
    224 }
    225 
    226 WebInspector.BackendSetting.prototype = {
    227     set: function(value)
    228     {
    229         function callback(error)
    230         {
    231             if (error) {
    232                 WebInspector.log("Error applying setting " + this._name + ": " + error);
    233                 this._eventSupport.dispatchEventToListeners(this._name, this._value);
    234                 return;
    235             }
    236             WebInspector.Setting.prototype.set.call(this, value);
    237         }
    238         this._setterCallback(value, callback.bind(this));
    239     },
    240 
    241     __proto__: WebInspector.Setting.prototype
    242 };
    243 
    244 /**
    245  * @constructor
    246  */
    247 WebInspector.ExperimentsSettings = function()
    248 {
    249     this._setting = WebInspector.settings.createSetting("experiments", {});
    250     this._experiments = [];
    251     this._enabledForTest = {};
    252 
    253     // Add currently running experiments here.
    254     this.fileSystemInspection = this._createExperiment("fileSystemInspection", "FileSystem inspection");
    255     this.canvasInspection = this._createExperiment("canvasInspection ", "Canvas inspection");
    256     this.cssRegions = this._createExperiment("cssRegions", "CSS Regions Support");
    257     this.showOverridesInDrawer = this._createExperiment("showOverridesInDrawer", "Show Overrides in drawer");
    258     this.customizableToolbar = this._createExperiment("customizableToolbar", "Enable toolbar customization");
    259     this.tethering = this._createExperiment("tethering", "Enable port forwarding");
    260     this.drawerOverlay = this._createExperiment("drawerOverlay", "Open console as overlay");
    261     this.frameworksDebuggingSupport = this._createExperiment("frameworksDebuggingSupport", "Enable frameworks debugging support");
    262     this.refreshFileSystemsOnFocus = this._createExperiment("refreshFileSystemsOnFocus", "Refresh file system folders on window focus");
    263     this.scrollBeyondEndOfFile = this._createExperiment("scrollBeyondEndOfFile", "Support scrolling beyond end of file");
    264 
    265     this._cleanUpSetting();
    266 }
    267 
    268 WebInspector.ExperimentsSettings.prototype = {
    269     /**
    270      * @return {Array.<WebInspector.Experiment>}
    271      */
    272     get experiments()
    273     {
    274         return this._experiments.slice();
    275     },
    276 
    277     /**
    278      * @return {boolean}
    279      */
    280     get experimentsEnabled()
    281     {
    282         return Preferences.experimentsEnabled || ("experiments" in WebInspector.queryParamsObject);
    283     },
    284 
    285     /**
    286      * @param {string} experimentName
    287      * @param {string} experimentTitle
    288      * @return {WebInspector.Experiment}
    289      */
    290     _createExperiment: function(experimentName, experimentTitle)
    291     {
    292         var experiment = new WebInspector.Experiment(this, experimentName, experimentTitle);
    293         this._experiments.push(experiment);
    294         return experiment;
    295     },
    296 
    297     /**
    298      * @param {string} experimentName
    299      * @return {boolean}
    300      */
    301     isEnabled: function(experimentName)
    302     {
    303         if (this._enabledForTest[experimentName])
    304             return true;
    305 
    306         if (!this.experimentsEnabled)
    307             return false;
    308 
    309         var experimentsSetting = this._setting.get();
    310         return experimentsSetting[experimentName];
    311     },
    312 
    313     /**
    314      * @param {string} experimentName
    315      * @param {boolean} enabled
    316      */
    317     setEnabled: function(experimentName, enabled)
    318     {
    319         var experimentsSetting = this._setting.get();
    320         experimentsSetting[experimentName] = enabled;
    321         this._setting.set(experimentsSetting);
    322     },
    323 
    324     /**
    325      * @param {string} experimentName
    326      */
    327     _enableForTest: function(experimentName)
    328     {
    329         this._enabledForTest[experimentName] = true;
    330     },
    331 
    332     _cleanUpSetting: function()
    333     {
    334         var experimentsSetting = this._setting.get();
    335         var cleanedUpExperimentSetting = {};
    336         for (var i = 0; i < this._experiments.length; ++i) {
    337             var experimentName = this._experiments[i].name;
    338             if (experimentsSetting[experimentName])
    339                 cleanedUpExperimentSetting[experimentName] = true;
    340         }
    341         this._setting.set(cleanedUpExperimentSetting);
    342     }
    343 }
    344 
    345 /**
    346  * @constructor
    347  * @param {WebInspector.ExperimentsSettings} experimentsSettings
    348  * @param {string} name
    349  * @param {string} title
    350  */
    351 WebInspector.Experiment = function(experimentsSettings, name, title)
    352 {
    353     this._name = name;
    354     this._title = title;
    355     this._experimentsSettings = experimentsSettings;
    356 }
    357 
    358 WebInspector.Experiment.prototype = {
    359     /**
    360      * @return {string}
    361      */
    362     get name()
    363     {
    364         return this._name;
    365     },
    366 
    367     /**
    368      * @return {string}
    369      */
    370     get title()
    371     {
    372         return this._title;
    373     },
    374 
    375     /**
    376      * @return {boolean}
    377      */
    378     isEnabled: function()
    379     {
    380         return this._experimentsSettings.isEnabled(this._name);
    381     },
    382 
    383     /**
    384      * @param {boolean} enabled
    385      */
    386     setEnabled: function(enabled)
    387     {
    388         return this._experimentsSettings.setEnabled(this._name, enabled);
    389     },
    390 
    391     enableForTest: function()
    392     {
    393         this._experimentsSettings._enableForTest(this._name);
    394     }
    395 }
    396 
    397 /**
    398  * @constructor
    399  */
    400 WebInspector.VersionController = function()
    401 {
    402 }
    403 
    404 WebInspector.VersionController.currentVersion = 4;
    405 
    406 WebInspector.VersionController.prototype = {
    407     updateVersion: function()
    408     {
    409         var versionSetting = WebInspector.settings.createSetting("inspectorVersion", 0);
    410         var currentVersion = WebInspector.VersionController.currentVersion;
    411         var oldVersion = versionSetting.get();
    412         var methodsToRun = this._methodsToRunToUpdateVersion(oldVersion, currentVersion);
    413         for (var i = 0; i < methodsToRun.length; ++i)
    414             this[methodsToRun[i]].call(this);
    415         versionSetting.set(currentVersion);
    416     },
    417 
    418     /**
    419      * @param {number} oldVersion
    420      * @param {number} currentVersion
    421      */
    422     _methodsToRunToUpdateVersion: function(oldVersion, currentVersion)
    423     {
    424         var result = [];
    425         for (var i = oldVersion; i < currentVersion; ++i)
    426             result.push("_updateVersionFrom" + i + "To" + (i + 1));
    427         return result;
    428     },
    429 
    430     _updateVersionFrom0To1: function()
    431     {
    432         this._clearBreakpointsWhenTooMany(WebInspector.settings.breakpoints, 500000);
    433     },
    434 
    435     _updateVersionFrom1To2: function()
    436     {
    437         var versionSetting = WebInspector.settings.createSetting("previouslyViewedFiles", []);
    438         versionSetting.set([]);
    439     },
    440 
    441     _updateVersionFrom2To3: function()
    442     {
    443         var fileSystemMappingSetting = WebInspector.settings.createSetting("fileSystemMapping", {});
    444         fileSystemMappingSetting.set({});
    445         delete window.localStorage["fileMappingEntries"];
    446     },
    447 
    448     _updateVersionFrom3To4: function()
    449     {
    450         var advancedMode = WebInspector.settings.createSetting("showHeaSnapshotObjectsHiddenProperties", false).get();
    451         WebInspector.settings.showAdvancedHeapSnapshotProperties.set(advancedMode);
    452     },
    453 
    454     /**
    455      * @param {WebInspector.Setting} breakpointsSetting
    456      * @param {number} maxBreakpointsCount
    457      */
    458     _clearBreakpointsWhenTooMany: function(breakpointsSetting, maxBreakpointsCount)
    459     {
    460         // If there are too many breakpoints in a storage, it is likely due to a recent bug that caused
    461         // periodical breakpoints duplication leading to inspector slowness.
    462         if (breakpointsSetting.get().length > maxBreakpointsCount)
    463             breakpointsSetting.set([]);
    464     }
    465 }
    466 
    467 WebInspector.settings = new WebInspector.Settings();
    468 WebInspector.experimentsSettings = new WebInspector.ExperimentsSettings();
    469