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