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