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: 25,
     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     canScreencast: false
     47 }
     48 
     49 /**
     50  * @constructor
     51  */
     52 WebInspector.Settings = function()
     53 {
     54     this._eventSupport = new WebInspector.Object();
     55     this._registry = /** @type {!Object.<string, !WebInspector.Setting>} */ ({});
     56 
     57     this.colorFormat = this.createSetting("colorFormat", "original");
     58     this.consoleHistory = this.createSetting("consoleHistory", []);
     59     this.domWordWrap = this.createSetting("domWordWrap", true);
     60     this.eventListenersFilter = this.createSetting("eventListenersFilter", "all");
     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.overrideUserAgent = this.createSetting("overrideUserAgent", false);
     78     this.userAgent = this.createSetting("userAgent", "");
     79     this.overrideDeviceMetrics = this.createSetting("overrideDeviceMetrics", false);
     80     this.deviceMetrics = this.createSetting("deviceMetrics", "");
     81     this.deviceFitWindow = this.createSetting("deviceFitWindow", true);
     82     this.emulateViewport = this.createSetting("emulateViewport", 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.highResolutionCpuProfiling = this.createSetting("highResolutionCpuProfiling", false);
     94     this.searchInContentScripts = this.createSetting("searchInContentScripts", false);
     95     this.textEditorIndent = this.createSetting("textEditorIndent", "    ");
     96     this.textEditorAutoDetectIndent = this.createSetting("textEditorAutoIndentIndent", true);
     97     this.textEditorAutocompletion = this.createSetting("textEditorAutocompletion", true);
     98     this.textEditorBracketMatching = this.createSetting("textEditorBracketMatching", true);
     99     this.lastDockState = this.createSetting("lastDockState", "");
    100     this.cssReloadEnabled = this.createSetting("cssReloadEnabled", false);
    101     this.timelineCaptureStacks = this.createSetting("timelineCaptureStacks", true);
    102     this.showMetricsRulers = this.createSetting("showMetricsRulers", false);
    103     this.overrideCSSMedia = this.createSetting("overrideCSSMedia", false);
    104     this.emulatedCSSMedia = this.createSetting("emulatedCSSMedia", "print");
    105     this.workerInspectorWidth = this.createSetting("workerInspectorWidth", 600);
    106     this.workerInspectorHeight = this.createSetting("workerInspectorHeight", 600);
    107     this.messageURLFilters = this.createSetting("messageURLFilters", {});
    108     this.networkHideDataURL = this.createSetting("networkHideDataURL", false);
    109     this.messageLevelFilters = this.createSetting("messageLevelFilters", {});
    110     this.splitVerticallyWhenDockedToRight = this.createSetting("splitVerticallyWhenDockedToRight", true);
    111     this.visiblePanels = this.createSetting("visiblePanels", {});
    112     this.shortcutPanelSwitch = this.createSetting("shortcutPanelSwitch", false);
    113     this.showWhitespacesInEditor = this.createSetting("showWhitespacesInEditor", false);
    114     this.skipStackFramesSwitch = this.createSetting("skipStackFramesSwitch", false);
    115     this.skipStackFramesPattern = this.createSetting("skipStackFramesPattern", "");
    116     this.screencastEnabled = this.createSetting("screencastEnabled", false);
    117     this.screencastSidebarWidth = this.createSetting("screencastSidebarWidth", 300);
    118     this.showEmulationViewInDrawer = this.createSetting("showEmulationViewInDrawer", true);
    119     this.showRenderingViewInDrawer = this.createSetting("showRenderingViewInDrawer", true);
    120     this.enableAsyncStackTraces = this.createSetting("enableAsyncStackTraces", false);
    121 }
    122 
    123 WebInspector.Settings.prototype = {
    124     /**
    125      * @param {string} key
    126      * @param {*} defaultValue
    127      * @return {!WebInspector.Setting}
    128      */
    129     createSetting: function(key, defaultValue)
    130     {
    131         if (!this._registry[key])
    132             this._registry[key] = new WebInspector.Setting(key, defaultValue, this._eventSupport, window.localStorage);
    133         return this._registry[key];
    134     },
    135 
    136     /**
    137      * @param {string} key
    138      * @param {*} defaultValue
    139      * @param {function(*, function(string, ...))} setterCallback
    140      * @return {!WebInspector.Setting}
    141      */
    142     createBackendSetting: function(key, defaultValue, setterCallback)
    143     {
    144         if (!this._registry[key])
    145             this._registry[key] = new WebInspector.BackendSetting(key, defaultValue, this._eventSupport, window.localStorage, setterCallback);
    146         return this._registry[key];
    147     }
    148 }
    149 
    150 /**
    151  * @constructor
    152  * @param {string} name
    153  * @param {*} defaultValue
    154  * @param {!WebInspector.Object} eventSupport
    155  * @param {?Storage} storage
    156  */
    157 WebInspector.Setting = function(name, defaultValue, eventSupport, storage)
    158 {
    159     this._name = name;
    160     this._defaultValue = defaultValue;
    161     this._eventSupport = eventSupport;
    162     this._storage = storage;
    163 }
    164 
    165 WebInspector.Setting.prototype = {
    166     /**
    167      * @param {function(!WebInspector.Event)} listener
    168      * @param {!Object=} thisObject
    169      */
    170     addChangeListener: function(listener, thisObject)
    171     {
    172         this._eventSupport.addEventListener(this._name, listener, thisObject);
    173     },
    174 
    175     /**
    176      * @param {function(!WebInspector.Event)} listener
    177      * @param {!Object=} thisObject
    178      */
    179     removeChangeListener: function(listener, thisObject)
    180     {
    181         this._eventSupport.removeEventListener(this._name, listener, thisObject);
    182     },
    183 
    184     get name()
    185     {
    186         return this._name;
    187     },
    188 
    189     get: function()
    190     {
    191         if (typeof this._value !== "undefined")
    192             return this._value;
    193 
    194         this._value = this._defaultValue;
    195         if (this._storage && this._name in this._storage) {
    196             try {
    197                 this._value = JSON.parse(this._storage[this._name]);
    198             } catch(e) {
    199                 delete this._storage[this._name];
    200             }
    201         }
    202         return this._value;
    203     },
    204 
    205     set: function(value)
    206     {
    207         this._value = value;
    208         if (this._storage) {
    209             try {
    210                 this._storage[this._name] = JSON.stringify(value);
    211             } catch(e) {
    212                 console.error("Error saving setting with name:" + this._name);
    213             }
    214         }
    215         this._eventSupport.dispatchEventToListeners(this._name, value);
    216     }
    217 }
    218 
    219 /**
    220  * @constructor
    221  * @extends {WebInspector.Setting}
    222  * @param {string} name
    223  * @param {*} defaultValue
    224  * @param {!WebInspector.Object} eventSupport
    225  * @param {?Storage} storage
    226  * @param {function(*,function(string, ...))} setterCallback
    227  */
    228 WebInspector.BackendSetting = function(name, defaultValue, eventSupport, storage, setterCallback)
    229 {
    230     WebInspector.Setting.call(this, name, defaultValue, eventSupport, storage);
    231     this._setterCallback = setterCallback;
    232     var currentValue = this.get();
    233     if (currentValue !== defaultValue)
    234         this.set(currentValue);
    235 }
    236 
    237 WebInspector.BackendSetting.prototype = {
    238     set: function(value)
    239     {
    240         /**
    241          * @param {?Protocol.Error} error
    242          * @this {WebInspector.BackendSetting}
    243          */
    244         function callback(error)
    245         {
    246             if (error) {
    247                 WebInspector.log("Error applying setting " + this._name + ": " + error);
    248                 this._eventSupport.dispatchEventToListeners(this._name, this._value);
    249                 return;
    250             }
    251             WebInspector.Setting.prototype.set.call(this, value);
    252         }
    253         this._setterCallback(value, callback.bind(this));
    254     },
    255 
    256     __proto__: WebInspector.Setting.prototype
    257 };
    258 
    259 /**
    260  * @constructor
    261  */
    262 WebInspector.ExperimentsSettings = function()
    263 {
    264     this._setting = WebInspector.settings.createSetting("experiments", {});
    265     this._experiments = [];
    266     this._enabledForTest = {};
    267 
    268     // Add currently running experiments here.
    269     this.asyncStackTraces = this._createExperiment("asyncStackTraces", "Enable support for async stack traces");
    270     this.fileSystemInspection = this._createExperiment("fileSystemInspection", "FileSystem inspection");
    271     this.canvasInspection = this._createExperiment("canvasInspection ", "Canvas inspection");
    272     this.cssRegions = this._createExperiment("cssRegions", "CSS Regions Support");
    273     this.frameworksDebuggingSupport = this._createExperiment("frameworksDebuggingSupport", "Enable frameworks debugging support");
    274     this.layersPanel = this._createExperiment("layersPanel", "Show Layers panel");
    275     this.stepIntoSelection = this._createExperiment("stepIntoSelection", "Show step-in candidates while debugging.");
    276     this.doNotOpenDrawerOnEsc = this._createExperiment("doNotOpenDrawerWithEsc", "Do not open drawer on Esc");
    277     this.showEditorInDrawer = this._createExperiment("showEditorInDrawer", "Show editor in drawer");
    278     this.gpuTimeline = this._createExperiment("gpuTimeline", "Show GPU data on timeline");
    279     this.applyCustomStylesheet = this._createExperiment("applyCustomStylesheet", "Allow custom UI themes");
    280 
    281     this._cleanUpSetting();
    282 }
    283 
    284 WebInspector.ExperimentsSettings.prototype = {
    285     /**
    286      * @return {!Array.<!WebInspector.Experiment>}
    287      */
    288     get experiments()
    289     {
    290         return this._experiments.slice();
    291     },
    292 
    293     /**
    294      * @return {boolean}
    295      */
    296     get experimentsEnabled()
    297     {
    298         return Preferences.experimentsEnabled || ("experiments" in WebInspector.queryParamsObject);
    299     },
    300 
    301     /**
    302      * @param {string} experimentName
    303      * @param {string} experimentTitle
    304      * @return {!WebInspector.Experiment}
    305      */
    306     _createExperiment: function(experimentName, experimentTitle)
    307     {
    308         var experiment = new WebInspector.Experiment(this, experimentName, experimentTitle);
    309         this._experiments.push(experiment);
    310         return experiment;
    311     },
    312 
    313     /**
    314      * @param {string} experimentName
    315      * @return {boolean}
    316      */
    317     isEnabled: function(experimentName)
    318     {
    319         if (this._enabledForTest[experimentName])
    320             return true;
    321 
    322         if (!this.experimentsEnabled)
    323             return false;
    324 
    325         var experimentsSetting = this._setting.get();
    326         return experimentsSetting[experimentName];
    327     },
    328 
    329     /**
    330      * @param {string} experimentName
    331      * @param {boolean} enabled
    332      */
    333     setEnabled: function(experimentName, enabled)
    334     {
    335         var experimentsSetting = this._setting.get();
    336         experimentsSetting[experimentName] = enabled;
    337         this._setting.set(experimentsSetting);
    338     },
    339 
    340     /**
    341      * @param {string} experimentName
    342      */
    343     _enableForTest: function(experimentName)
    344     {
    345         this._enabledForTest[experimentName] = true;
    346     },
    347 
    348     _cleanUpSetting: function()
    349     {
    350         var experimentsSetting = this._setting.get();
    351         var cleanedUpExperimentSetting = {};
    352         for (var i = 0; i < this._experiments.length; ++i) {
    353             var experimentName = this._experiments[i].name;
    354             if (experimentsSetting[experimentName])
    355                 cleanedUpExperimentSetting[experimentName] = true;
    356         }
    357         this._setting.set(cleanedUpExperimentSetting);
    358     }
    359 }
    360 
    361 /**
    362  * @constructor
    363  * @param {!WebInspector.ExperimentsSettings} experimentsSettings
    364  * @param {string} name
    365  * @param {string} title
    366  */
    367 WebInspector.Experiment = function(experimentsSettings, name, title)
    368 {
    369     this._name = name;
    370     this._title = title;
    371     this._experimentsSettings = experimentsSettings;
    372 }
    373 
    374 WebInspector.Experiment.prototype = {
    375     /**
    376      * @return {string}
    377      */
    378     get name()
    379     {
    380         return this._name;
    381     },
    382 
    383     /**
    384      * @return {string}
    385      */
    386     get title()
    387     {
    388         return this._title;
    389     },
    390 
    391     /**
    392      * @return {boolean}
    393      */
    394     isEnabled: function()
    395     {
    396         return this._experimentsSettings.isEnabled(this._name);
    397     },
    398 
    399     /**
    400      * @param {boolean} enabled
    401      */
    402     setEnabled: function(enabled)
    403     {
    404         return this._experimentsSettings.setEnabled(this._name, enabled);
    405     },
    406 
    407     enableForTest: function()
    408     {
    409         this._experimentsSettings._enableForTest(this._name);
    410     }
    411 }
    412 
    413 /**
    414  * @constructor
    415  */
    416 WebInspector.VersionController = function()
    417 {
    418 }
    419 
    420 WebInspector.VersionController.currentVersion = 4;
    421 
    422 WebInspector.VersionController.prototype = {
    423     updateVersion: function()
    424     {
    425         var versionSetting = WebInspector.settings.createSetting("inspectorVersion", 0);
    426         var currentVersion = WebInspector.VersionController.currentVersion;
    427         var oldVersion = versionSetting.get();
    428         var methodsToRun = this._methodsToRunToUpdateVersion(oldVersion, currentVersion);
    429         for (var i = 0; i < methodsToRun.length; ++i)
    430             this[methodsToRun[i]].call(this);
    431         versionSetting.set(currentVersion);
    432     },
    433 
    434     /**
    435      * @param {number} oldVersion
    436      * @param {number} currentVersion
    437      */
    438     _methodsToRunToUpdateVersion: function(oldVersion, currentVersion)
    439     {
    440         var result = [];
    441         for (var i = oldVersion; i < currentVersion; ++i)
    442             result.push("_updateVersionFrom" + i + "To" + (i + 1));
    443         return result;
    444     },
    445 
    446     _updateVersionFrom0To1: function()
    447     {
    448         this._clearBreakpointsWhenTooMany(WebInspector.settings.breakpoints, 500000);
    449     },
    450 
    451     _updateVersionFrom1To2: function()
    452     {
    453         var versionSetting = WebInspector.settings.createSetting("previouslyViewedFiles", []);
    454         versionSetting.set([]);
    455     },
    456 
    457     _updateVersionFrom2To3: function()
    458     {
    459         var fileSystemMappingSetting = WebInspector.settings.createSetting("fileSystemMapping", {});
    460         fileSystemMappingSetting.set({});
    461         if (window.localStorage)
    462             delete window.localStorage["fileMappingEntries"];
    463     },
    464 
    465     _updateVersionFrom3To4: function()
    466     {
    467         var advancedMode = WebInspector.settings.createSetting("showHeaSnapshotObjectsHiddenProperties", false).get();
    468         WebInspector.settings.showAdvancedHeapSnapshotProperties.set(advancedMode);
    469     },
    470 
    471     /**
    472      * @param {!WebInspector.Setting} breakpointsSetting
    473      * @param {number} maxBreakpointsCount
    474      */
    475     _clearBreakpointsWhenTooMany: function(breakpointsSetting, maxBreakpointsCount)
    476     {
    477         // If there are too many breakpoints in a storage, it is likely due to a recent bug that caused
    478         // periodical breakpoints duplication leading to inspector slowness.
    479         if (breakpointsSetting.get().length > maxBreakpointsCount)
    480             breakpointsSetting.set([]);
    481     }
    482 }
    483 
    484 WebInspector.settings = new WebInspector.Settings();
    485 WebInspector.experimentsSettings = new WebInspector.ExperimentsSettings();
    486