2 * Copyright (C) 2009 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
34 WebInspector.Settings = function()
36 this._eventSupport = new WebInspector.Object();
37 this._registry = /** @type {!Object.<string, !WebInspector.Setting>} */ ({});
39 this.colorFormat = this.createSetting("colorFormat", "original");
40 this.consoleHistory = this.createSetting("consoleHistory", []);
41 this.domWordWrap = this.createSetting("domWordWrap", true);
42 this.eventListenersFilter = this.createSetting("eventListenersFilter", "all");
43 this.lastViewedScriptFile = this.createSetting("lastViewedScriptFile", "application");
44 this.monitoringXHREnabled = this.createSetting("monitoringXHREnabled", false);
45 this.preserveConsoleLog = this.createSetting("preserveConsoleLog", false);
46 this.consoleTimestampsEnabled = this.createSetting("consoleTimestampsEnabled", false);
47 this.resourcesLargeRows = this.createSetting("resourcesLargeRows", true);
48 this.resourcesSortOptions = this.createSetting("resourcesSortOptions", {timeOption: "responseTime", sizeOption: "transferSize"});
49 this.resourceViewTab = this.createSetting("resourceViewTab", "preview");
50 this.showInheritedComputedStyleProperties = this.createSetting("showInheritedComputedStyleProperties", false);
51 this.showUserAgentStyles = this.createSetting("showUserAgentStyles", true);
52 this.watchExpressions = this.createSetting("watchExpressions", []);
53 this.breakpoints = this.createSetting("breakpoints", []);
54 this.eventListenerBreakpoints = this.createSetting("eventListenerBreakpoints", []);
55 this.domBreakpoints = this.createSetting("domBreakpoints", []);
56 this.xhrBreakpoints = this.createSetting("xhrBreakpoints", []);
57 this.jsSourceMapsEnabled = this.createSetting("sourceMapsEnabled", true);
58 this.cssSourceMapsEnabled = this.createSetting("cssSourceMapsEnabled", true);
59 this.cacheDisabled = this.createSetting("cacheDisabled", false);
60 this.showUAShadowDOM = this.createSetting("showUAShadowDOM", false);
61 this.savedURLs = this.createSetting("savedURLs", {});
62 this.javaScriptDisabled = this.createSetting("javaScriptDisabled", false);
63 this.showAdvancedHeapSnapshotProperties = this.createSetting("showAdvancedHeapSnapshotProperties", false);
64 this.recordAllocationStacks = this.createSetting("recordAllocationStacks", false);
65 this.highResolutionCpuProfiling = this.createSetting("highResolutionCpuProfiling", false);
66 this.searchInContentScripts = this.createSetting("searchInContentScripts", false);
67 this.textEditorIndent = this.createSetting("textEditorIndent", " ");
68 this.textEditorAutoDetectIndent = this.createSetting("textEditorAutoIndentIndent", true);
69 this.textEditorAutocompletion = this.createSetting("textEditorAutocompletion", true);
70 this.textEditorBracketMatching = this.createSetting("textEditorBracketMatching", true);
71 this.cssReloadEnabled = this.createSetting("cssReloadEnabled", false);
72 this.timelineLiveUpdate = this.createSetting("timelineLiveUpdate", true);
73 this.showMetricsRulers = this.createSetting("showMetricsRulers", false);
74 this.workerInspectorWidth = this.createSetting("workerInspectorWidth", 600);
75 this.workerInspectorHeight = this.createSetting("workerInspectorHeight", 600);
76 this.messageURLFilters = this.createSetting("messageURLFilters", {});
77 this.networkHideDataURL = this.createSetting("networkHideDataURL", false);
78 this.networkResourceTypeFilters = this.createSetting("networkResourceTypeFilters", {});
79 this.messageLevelFilters = this.createSetting("messageLevelFilters", {});
80 this.splitVerticallyWhenDockedToRight = this.createSetting("splitVerticallyWhenDockedToRight", true);
81 this.visiblePanels = this.createSetting("visiblePanels", {});
82 this.shortcutPanelSwitch = this.createSetting("shortcutPanelSwitch", false);
83 this.showWhitespacesInEditor = this.createSetting("showWhitespacesInEditor", false);
84 this.skipStackFramesPattern = this.createRegExpSetting("skipStackFramesPattern", "");
85 this.pauseOnExceptionEnabled = this.createSetting("pauseOnExceptionEnabled", false);
86 this.pauseOnCaughtException = this.createSetting("pauseOnCaughtException", false);
87 this.enableAsyncStackTraces = this.createSetting("enableAsyncStackTraces", false);
88 this.showMediaQueryInspector = this.createSetting("showMediaQueryInspector", false);
89 this.disableOverridesWarning = this.createSetting("disableOverridesWarning", false);
92 this.showPaintRects = this.createSetting("showPaintRects", false);
93 this.showDebugBorders = this.createSetting("showDebugBorders", false);
94 this.showFPSCounter = this.createSetting("showFPSCounter", false);
95 this.continuousPainting = this.createSetting("continuousPainting", false);
96 this.showScrollBottleneckRects = this.createSetting("showScrollBottleneckRects", false);
99 WebInspector.Settings.prototype = {
101 * @param {string} key
102 * @param {*} defaultValue
103 * @return {!WebInspector.Setting}
105 createSetting: function(key, defaultValue)
107 if (!this._registry[key])
108 this._registry[key] = new WebInspector.Setting(key, defaultValue, this._eventSupport, window.localStorage);
109 return this._registry[key];
113 * @param {string} key
114 * @param {string} defaultValue
115 * @param {string=} regexFlags
116 * @return {!WebInspector.Setting}
118 createRegExpSetting: function(key, defaultValue, regexFlags)
120 if (!this._registry[key])
121 this._registry[key] = new WebInspector.RegExpSetting(key, defaultValue, this._eventSupport, window.localStorage, regexFlags);
122 return this._registry[key];
128 * @param {string} name
129 * @param {V} defaultValue
130 * @param {!WebInspector.Object} eventSupport
131 * @param {?Storage} storage
134 WebInspector.Setting = function(name, defaultValue, eventSupport, storage)
137 this._defaultValue = defaultValue;
138 this._eventSupport = eventSupport;
139 this._storage = storage;
142 WebInspector.Setting.prototype = {
144 * @param {function(!WebInspector.Event)} listener
145 * @param {!Object=} thisObject
147 addChangeListener: function(listener, thisObject)
149 this._eventSupport.addEventListener(this._name, listener, thisObject);
153 * @param {function(!WebInspector.Event)} listener
154 * @param {!Object=} thisObject
156 removeChangeListener: function(listener, thisObject)
158 this._eventSupport.removeEventListener(this._name, listener, thisObject);
171 if (typeof this._value !== "undefined")
174 this._value = this._defaultValue;
175 if (this._storage && this._name in this._storage) {
177 this._value = JSON.parse(this._storage[this._name]);
179 delete this._storage[this._name];
193 this._storage[this._name] = JSON.stringify(value);
195 console.error("Error saving setting with name:" + this._name);
198 this._eventSupport.dispatchEventToListeners(this._name, value);
204 * @extends {WebInspector.Setting}
205 * @param {string} name
206 * @param {string} defaultValue
207 * @param {!WebInspector.Object} eventSupport
208 * @param {?Storage} storage
209 * @param {string=} regexFlags
211 WebInspector.RegExpSetting = function(name, defaultValue, eventSupport, storage, regexFlags)
213 WebInspector.Setting.call(this, name, defaultValue ? [{ pattern: defaultValue }] : [], eventSupport, storage);
214 this._regexFlags = regexFlags;
217 WebInspector.RegExpSetting.prototype = {
225 var items = this.getAsArray();
226 for (var i = 0; i < items.length; ++i) {
228 if (item.pattern && !item.disabled)
229 result.push(item.pattern);
231 return result.join("|");
235 * @return {!Array.<{pattern: string, disabled: (boolean|undefined)}>}
237 getAsArray: function()
239 return WebInspector.Setting.prototype.get.call(this);
244 * @param {string} value
248 this.setAsArray([{ pattern: value }]);
252 * @param {!Array.<{pattern: string, disabled: (boolean|undefined)}>} value
254 setAsArray: function(value)
257 WebInspector.Setting.prototype.set.call(this, value);
265 if (typeof this._regex !== "undefined")
269 var pattern = this.get();
271 this._regex = new RegExp(pattern, this._regexFlags || "");
277 __proto__: WebInspector.Setting.prototype
282 * @param {boolean} experimentsEnabled
284 WebInspector.ExperimentsSettings = function(experimentsEnabled)
286 this._experimentsEnabled = experimentsEnabled;
287 this._setting = WebInspector.settings.createSetting("experiments", {});
288 this._experiments = [];
289 this._enabledForTest = {};
291 // Add currently running experiments here.
292 this.applyCustomStylesheet = this._createExperiment("applyCustomStylesheet", "Allow custom UI themes");
293 this.canvasInspection = this._createExperiment("canvasInspection ", "Canvas inspection");
294 this.devicesPanel = this._createExperiment("devicesPanel", "Devices panel");
295 this.disableAgentsWhenProfile = this._createExperiment("disableAgentsWhenProfile", "Disable other agents and UI when profiler is active", true);
296 this.dockToLeft = this._createExperiment("dockToLeft", "Dock to left", true);
297 this.documentation = this._createExperiment("documentation", "Documentation for JS and CSS", true);
298 this.fileSystemInspection = this._createExperiment("fileSystemInspection", "FileSystem inspection");
299 this.gpuTimeline = this._createExperiment("gpuTimeline", "GPU data on timeline", true);
300 this.layersPanel = this._createExperiment("layersPanel", "Layers panel");
301 this.timelineOnTraceEvents = this._createExperiment("timelineOnTraceEvents", "Timeline on trace events");
302 this.paintProfiler = this._createExperiment("paintProfiler", "Paint profiler");
303 this.timelinePowerProfiler = this._createExperiment("timelinePowerProfiler", "Timeline power profiler");
304 this.timelineJSCPUProfile = this._createExperiment("timelineJSCPUProfile", "Timeline with JS sampling");
306 this._cleanUpSetting();
309 WebInspector.ExperimentsSettings.prototype = {
311 * @return {!Array.<!WebInspector.Experiment>}
315 return this._experiments.slice();
321 get experimentsEnabled()
323 return this._experimentsEnabled;
327 * @param {string} experimentName
328 * @param {string} experimentTitle
329 * @param {boolean=} hidden
330 * @return {!WebInspector.Experiment}
332 _createExperiment: function(experimentName, experimentTitle, hidden)
334 var experiment = new WebInspector.Experiment(this, experimentName, experimentTitle, !!hidden);
335 this._experiments.push(experiment);
340 * @param {string} experimentName
343 isEnabled: function(experimentName)
345 if (this._enabledForTest[experimentName])
348 if (!this.experimentsEnabled)
351 var experimentsSetting = this._setting.get();
352 return experimentsSetting[experimentName];
356 * @param {string} experimentName
357 * @param {boolean} enabled
359 setEnabled: function(experimentName, enabled)
361 var experimentsSetting = this._setting.get();
362 experimentsSetting[experimentName] = enabled;
363 this._setting.set(experimentsSetting);
367 * @param {string} experimentName
369 _enableForTest: function(experimentName)
371 this._enabledForTest[experimentName] = true;
374 _cleanUpSetting: function()
376 var experimentsSetting = this._setting.get();
377 var cleanedUpExperimentSetting = {};
378 for (var i = 0; i < this._experiments.length; ++i) {
379 var experimentName = this._experiments[i].name;
380 if (experimentsSetting[experimentName])
381 cleanedUpExperimentSetting[experimentName] = true;
383 this._setting.set(cleanedUpExperimentSetting);
389 * @param {!WebInspector.ExperimentsSettings} experimentsSettings
390 * @param {string} name
391 * @param {string} title
392 * @param {boolean} hidden
394 WebInspector.Experiment = function(experimentsSettings, name, title, hidden)
398 this._hidden = hidden;
399 this._experimentsSettings = experimentsSettings;
402 WebInspector.Experiment.prototype = {
430 isEnabled: function()
432 return this._experimentsSettings.isEnabled(this._name);
436 * @param {boolean} enabled
438 setEnabled: function(enabled)
440 this._experimentsSettings.setEnabled(this._name, enabled);
443 enableForTest: function()
445 this._experimentsSettings._enableForTest(this._name);
452 WebInspector.VersionController = function()
456 WebInspector.VersionController.currentVersion = 9;
458 WebInspector.VersionController.prototype = {
459 updateVersion: function()
461 var versionSetting = WebInspector.settings.createSetting("inspectorVersion", 0);
462 var currentVersion = WebInspector.VersionController.currentVersion;
463 var oldVersion = versionSetting.get();
464 var methodsToRun = this._methodsToRunToUpdateVersion(oldVersion, currentVersion);
465 for (var i = 0; i < methodsToRun.length; ++i)
466 this[methodsToRun[i]].call(this);
467 versionSetting.set(currentVersion);
471 * @param {number} oldVersion
472 * @param {number} currentVersion
474 _methodsToRunToUpdateVersion: function(oldVersion, currentVersion)
477 for (var i = oldVersion; i < currentVersion; ++i)
478 result.push("_updateVersionFrom" + i + "To" + (i + 1));
482 _updateVersionFrom0To1: function()
484 this._clearBreakpointsWhenTooMany(WebInspector.settings.breakpoints, 500000);
487 _updateVersionFrom1To2: function()
489 var versionSetting = WebInspector.settings.createSetting("previouslyViewedFiles", []);
490 versionSetting.set([]);
493 _updateVersionFrom2To3: function()
495 var fileSystemMappingSetting = WebInspector.settings.createSetting("fileSystemMapping", {});
496 fileSystemMappingSetting.set({});
497 if (window.localStorage)
498 delete window.localStorage["fileMappingEntries"];
501 _updateVersionFrom3To4: function()
503 var advancedMode = WebInspector.settings.createSetting("showHeaSnapshotObjectsHiddenProperties", false).get();
504 WebInspector.settings.showAdvancedHeapSnapshotProperties.set(advancedMode);
507 _updateVersionFrom4To5: function()
509 if (!window.localStorage)
512 "FileSystemViewSidebarWidth": "fileSystemViewSplitViewState",
513 "canvasProfileViewReplaySplitLocation": "canvasProfileViewReplaySplitViewState",
514 "canvasProfileViewSplitLocation": "canvasProfileViewSplitViewState",
515 "elementsSidebarWidth": "elementsPanelSplitViewState",
516 "StylesPaneSplitRatio": "stylesPaneSplitViewState",
517 "heapSnapshotRetainersViewSize": "heapSnapshotSplitViewState",
518 "InspectorView.splitView": "InspectorView.splitViewState",
519 "InspectorView.screencastSplitView": "InspectorView.screencastSplitViewState",
520 "Inspector.drawerSplitView": "Inspector.drawerSplitViewState",
521 "layerDetailsSplitView": "layerDetailsSplitViewState",
522 "networkSidebarWidth": "networkPanelSplitViewState",
523 "sourcesSidebarWidth": "sourcesPanelSplitViewState",
524 "scriptsPanelNavigatorSidebarWidth": "sourcesPanelNavigatorSplitViewState",
525 "sourcesPanelSplitSidebarRatio": "sourcesPanelDebuggerSidebarSplitViewState",
526 "timeline-details": "timelinePanelDetailsSplitViewState",
527 "timeline-split": "timelinePanelRecorsSplitViewState",
528 "timeline-view": "timelinePanelTimelineStackSplitViewState",
529 "auditsSidebarWidth": "auditsPanelSplitViewState",
530 "layersSidebarWidth": "layersPanelSplitViewState",
531 "profilesSidebarWidth": "profilesPanelSplitViewState",
532 "resourcesSidebarWidth": "resourcesPanelSplitViewState"
534 for (var oldName in settingNames) {
535 var newName = settingNames[oldName];
536 var oldNameH = oldName + "H";
539 var oldSetting = WebInspector.settings.createSetting(oldName, undefined).get();
541 newValue = newValue || {};
542 newValue.vertical = {};
543 newValue.vertical.size = oldSetting;
544 delete window.localStorage[oldName];
546 var oldSettingH = WebInspector.settings.createSetting(oldNameH, undefined).get();
548 newValue = newValue || {};
549 newValue.horizontal = {};
550 newValue.horizontal.size = oldSettingH;
551 delete window.localStorage[oldNameH];
553 var newSetting = WebInspector.settings.createSetting(newName, {});
555 newSetting.set(newValue);
559 _updateVersionFrom5To6: function()
561 if (!window.localStorage)
565 "debuggerSidebarHidden": "sourcesPanelSplitViewState",
566 "navigatorHidden": "sourcesPanelNavigatorSplitViewState",
567 "WebInspector.Drawer.showOnLoad": "Inspector.drawerSplitViewState"
570 for (var oldName in settingNames) {
571 var newName = settingNames[oldName];
573 var oldSetting = WebInspector.settings.createSetting(oldName, undefined).get();
574 var invert = "WebInspector.Drawer.showOnLoad" === oldName;
575 var hidden = !!oldSetting !== invert;
576 delete window.localStorage[oldName];
577 var showMode = hidden ? "OnlyMain" : "Both";
579 var newSetting = WebInspector.settings.createSetting(newName, null);
580 var newValue = newSetting.get() || {};
581 newValue.vertical = newValue.vertical || {};
582 newValue.vertical.showMode = showMode;
583 newValue.horizontal = newValue.horizontal || {};
584 newValue.horizontal.showMode = showMode;
585 newSetting.set(newValue);
589 _updateVersionFrom6To7: function()
591 if (!window.localStorage)
595 "sourcesPanelNavigatorSplitViewState": "sourcesPanelNavigatorSplitViewState",
596 "elementsPanelSplitViewState": "elementsPanelSplitViewState",
597 "canvasProfileViewReplaySplitViewState": "canvasProfileViewReplaySplitViewState",
598 "stylesPaneSplitViewState": "stylesPaneSplitViewState",
599 "sourcesPanelDebuggerSidebarSplitViewState": "sourcesPanelDebuggerSidebarSplitViewState"
602 for (var name in settingNames) {
603 if (!(name in window.localStorage))
605 var setting = WebInspector.settings.createSetting(name, undefined);
606 var value = setting.get();
609 // Zero out saved percentage sizes, and they will be restored to defaults.
610 if (value.vertical && value.vertical.size && value.vertical.size < 1)
611 value.vertical.size = 0;
612 if (value.horizontal && value.horizontal.size && value.horizontal.size < 1)
613 value.horizontal.size = 0;
618 _updateVersionFrom7To8: function()
620 var settingName = "deviceMetrics";
621 if (!window.localStorage || !(settingName in window.localStorage))
623 var setting = WebInspector.settings.createSetting(settingName, undefined);
624 var value = setting.get();
628 var components = value.split("x");
629 if (components.length >= 3) {
630 var width = parseInt(components[0], 10);
631 var height = parseInt(components[1], 10);
632 var deviceScaleFactor = parseFloat(components[2]);
633 if (deviceScaleFactor) {
634 components[0] = "" + Math.round(width / deviceScaleFactor);
635 components[1] = "" + Math.round(height / deviceScaleFactor);
638 value = components.join("x");
642 _updateVersionFrom8To9: function()
644 if (!window.localStorage)
648 "skipStackFramesPattern",
649 "workspaceFolderExcludePattern"
652 for (var i = 0; i < settingNames.length; ++i) {
653 var settingName = settingNames[i];
654 if (!(settingName in window.localStorage))
657 var value = JSON.parse(window.localStorage[settingName]);
660 if (typeof value === "string")
662 for (var j = 0; j < value.length; ++j) {
663 if (typeof value[j] === "string")
664 value[j] = { pattern: value[j] };
666 window.localStorage[settingName] = JSON.stringify(value);
673 * @param {!WebInspector.Setting} breakpointsSetting
674 * @param {number} maxBreakpointsCount
676 _clearBreakpointsWhenTooMany: function(breakpointsSetting, maxBreakpointsCount)
678 // If there are too many breakpoints in a storage, it is likely due to a recent bug that caused
679 // periodical breakpoints duplication leading to inspector slowness.
680 if (breakpointsSetting.get().length > maxBreakpointsCount)
681 breakpointsSetting.set([]);
686 * @type {!WebInspector.Settings}
688 WebInspector.settings;
691 * @type {!WebInspector.ExperimentsSettings}
693 WebInspector.experimentsSettings;
695 // These methods are added for backwards compatibility with Devtools CodeSchool extension.
701 WebInspector.PauseOnExceptionStateSetting = function()
703 WebInspector.settings.pauseOnExceptionEnabled.addChangeListener(this._enabledChanged, this);
704 WebInspector.settings.pauseOnCaughtException.addChangeListener(this._pauseOnCaughtChanged, this);
705 this._name = "pauseOnExceptionStateString";
706 this._eventSupport = new WebInspector.Object();
707 this._value = this._calculateValue();
710 WebInspector.PauseOnExceptionStateSetting.prototype = {
712 * @param {function(!WebInspector.Event)} listener
713 * @param {!Object=} thisObject
715 addChangeListener: function(listener, thisObject)
717 this._eventSupport.addEventListener(this._name, listener, thisObject);
721 * @param {function(!WebInspector.Event)} listener
722 * @param {!Object=} thisObject
724 removeChangeListener: function(listener, thisObject)
726 this._eventSupport.removeEventListener(this._name, listener, thisObject);
740 _calculateValue: function()
742 if (!WebInspector.settings.pauseOnExceptionEnabled.get())
744 // The correct code here would be
745 // return WebInspector.settings.pauseOnCaughtException.get() ? "all" : "uncaught";
746 // But the CodeSchool DevTools relies on the fact that we used to enable pausing on ALL extensions by default, so we trick it here.
750 _enabledChanged: function(event)
752 this._fireChangedIfNeeded();
755 _pauseOnCaughtChanged: function(event)
757 this._fireChangedIfNeeded();
760 _fireChangedIfNeeded: function()
762 var newValue = this._calculateValue();
763 if (newValue === this._value)
765 this._value = newValue;
766 this._eventSupport.dispatchEventToListeners(this._name, this._value);