1 /* See license.txt for terms of usage */ 2 3 define([ 4 "firebug/lib/events", 5 "firebug/lib/trace", 6 "firebug/lib/deprecated", 7 ], 8 function factoryOptions(Events, FBTrace, Deprecated) { 9 "use strict"; 10 11 // xxxFlorent: maybe parts of these methods should be in a seperate module 12 // ********************************************************************************************* // 13 // Constants 14 15 const Cc = Components.classes; 16 const Ci = Components.interfaces; 17 18 const nsIPrefBranch = Ci.nsIPrefBranch; 19 const PrefService = Cc["@mozilla.org/preferences-service;1"]; 20 21 const nsIPrefService = Ci.nsIPrefService; 22 const prefService = PrefService.getService(nsIPrefService); 23 const prefs = PrefService.getService(nsIPrefBranch); 24 25 const prefNames = // XXXjjb TODO distribute to modules 26 [ 27 // Global 28 "defaultPanelName", "throttleMessages", "textSize", "showInfoTips", 29 "commandEditor", "textWrapWidth", "framePosition", "showErrorCount", 30 "activateSameOrigin", "allPagesActivation", "hiddenPanels", 31 "panelTabMinWidth", "sourceLinkLabelWidth", "currentVersion", 32 "useDefaultLocale", "toolbarCustomizationDone", "addonBarOpened", 33 "showBreakNotification", "stringCropLength", "showFirstRunPage", 34 35 // Search 36 "searchCaseSensitive", "searchGlobal", "searchUseRegularExpression", 37 "netSearchHeaders", "netSearchParameters", "netSearchResponseBody", 38 39 // Console 40 "showJSErrors", "showJSWarnings", "showCSSErrors", "showXMLErrors", 41 "showChromeErrors", "showChromeMessages", 42 "showXMLHttpRequests", "showNetworkErrors", "tabularLogMaxHeight", 43 "consoleFilterTypes", "alwaysShowCommandLine", 44 45 // HTML 46 "showFullTextNodes", "showCommentNodes", 47 "showTextNodesWithWhitespace", "entityDisplay", 48 "highlightMutations", "expandMutations", "scrollToMutations", "shadeBoxModel", 49 "showQuickInfoBox", "displayedAttributeValueLimit", "multiHighlightLimit", 50 51 // CSS 52 "onlyShowAppliedStyles", 53 "showUserAgentCSS", 54 "expandShorthandProps", 55 "cssEditMode", 56 "colorDisplay", 57 58 // Computed 59 "computedStylesDisplay", 60 "showMozillaSpecificStyles", 61 62 // Script 63 "decompileEvals", "replaceTabs", "maxScriptLineLength", 64 65 // DOM 66 "showUserProps", "showUserFuncs", "showDOMProps", "showDOMFuncs", "showDOMConstants", 67 "ObjectShortIteratorMax", "showEnumerableProperties", "showOwnProperties", 68 "showInlineEventHandlers", 69 70 // Layout 71 "showRulers", 72 73 // Net 74 "netFilterCategory", "netDisplayedResponseLimit", 75 "netDisplayedPostBodyLimit", "netPhaseInterval", "sizePrecision", 76 "netParamNameLimit", "netShowPaintEvents", "netShowBFCacheResponses", 77 "netHtmlPreviewHeight", 78 79 // JSON Preview 80 "sortJsonPreview", 81 82 // Stack 83 "omitObjectPathStack", 84 85 "showStackTrace", // Console 86 "filterSystemURLs", // Stack 87 "breakOnErrors", "trackThrowCatch" // Script 88 ]; 89 90 var optionUpdateMap = {}; 91 92 var listeners = []; 93 94 var prefDomain = "extensions.firebug"; 95 96 // ********************************************************************************************* // 97 98 /** 99 * @name Options 100 * @lib Util to manage firefox preferences <br/> 101 * 102 * Panels send commands to request option change. 103 * Backend responds with events when the change is accepted. 104 */ 105 var Options = 106 /** @lends Options */ 107 { 108 // xxxFlorent: do we allow this syntax? (https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/get) 109 get prefDomain() 110 { 111 return prefDomain; 112 }, 113 114 // this function is less used than direct access to Options.prefDomain. 115 // So, this one is deprecated 116 getPrefDomain: Deprecated.deprecated("Use Options.prefDomain instead", function() 117 { 118 return prefDomain; 119 }), 120 121 initialize: function(_prefDomain) 122 { 123 prefDomain = _prefDomain; 124 125 if (FBTrace.DBG_INITIALIZE) 126 FBTrace.sysout("options.initialize with prefDomain " + this.prefDomain); 127 128 this.initializePrefs(); 129 }, 130 131 shutdown: function() 132 { 133 prefs.removeObserver(this.prefDomain, this, false); 134 }, 135 136 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 137 // Custom Listeners 138 139 addListener: function(listener) 140 { 141 listeners.push(listener); 142 }, 143 144 removeListener: function(listener) 145 { 146 for (var i=0; i<listeners.length; ++i) 147 if (listeners[i] == listener) 148 return listeners.splice(i, 1); 149 }, 150 151 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 152 // nsIPrefObserver 153 154 observe: function(subject, topic, data) 155 { 156 if (data.indexOf(Options.prefDomain) === -1) 157 return; 158 159 var name = data.substr(Options.prefDomain.length+1); // +1 for . 160 var value = this.get(name); 161 162 if (FBTrace.DBG_OPTIONS) 163 FBTrace.sysout("options.observe name = value: "+name+"= "+value+"\n"); 164 165 this.updatePref(name, value); 166 }, 167 168 updatePref: function(name, value) 169 { 170 // Prevent infinite recursion due to pref observer 171 if (optionUpdateMap.hasOwnProperty(name)) 172 return; 173 174 try 175 { 176 optionUpdateMap[name] = 1; 177 Firebug[name] = value; 178 179 Events.dispatch(listeners, "updateOption", [name, value]); 180 } 181 catch (err) 182 { 183 if (FBTrace.DBG_OPTIONS || FBTrace.DBG_ERRORS) 184 FBTrace.sysout("options.updatePref EXCEPTION:" + err, err); 185 } 186 finally 187 { 188 delete optionUpdateMap[name]; 189 } 190 191 if (FBTrace.DBG_OPTIONS) 192 FBTrace.sysout("options.updatePref EXIT: "+name+"="+value+"\n"); 193 }, 194 195 register: function(name, value) 196 { 197 var currentValue = this.getPref(this.prefDomain, name); 198 199 if (FBTrace.DBG_INITIALIZE) 200 FBTrace.sysout("registerPreference "+name+" -> "+value+" type "+typeof(value)+ 201 " with currentValue "+currentValue); 202 203 if (currentValue === undefined) 204 { 205 // https://developer.mozilla.org/en/Code_snippets/Preferences 206 // This is the reason why you should usually pass strings ending with a dot to 207 // getBranch(), like prefs.getBranch("accessibility."). 208 var defaultBranch = prefService.getDefaultBranch(this.prefDomain+"."); // 209 210 var type = this.getPreferenceTypeByExample(typeof(value)); 211 if (this.setPreference(name, value, type, defaultBranch)) 212 return true; 213 } 214 215 return false; 216 }, 217 218 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 219 // Options 220 // TODO support per context options eg break on error 221 222 initializePrefs: function() 223 { 224 for (var i = 0; i < prefNames.length; ++i) 225 Firebug[prefNames[i]] = this.getPref(this.prefDomain, prefNames[i]); 226 227 prefs.addObserver(this.prefDomain, this, false); 228 229 var basePrefNames = prefNames.length; 230 231 for (var i = basePrefNames; i < prefNames.length; ++i) 232 Firebug[prefNames[i]] = this.getPref(this.prefDomain, prefNames[i]); 233 234 if (FBTrace.DBG_OPTIONS) 235 { 236 for (var i = 0; i < prefNames.length; ++i) 237 { 238 FBTrace.sysout("options.initialize option "+this.prefDomain+"."+prefNames[i]+"="+ 239 Firebug[prefNames[i]]+"\n"); 240 } 241 } 242 }, 243 244 togglePref: function(name) 245 { 246 this.set(name, !this.get(name)); 247 }, 248 249 get: function(name) 250 { 251 return Options.getPref(this.prefDomain, name); 252 }, 253 254 getPref: function(prefDomain, name) 255 { 256 var prefName = prefDomain + "." + name; 257 258 var type = prefs.getPrefType(prefName); 259 260 var value; 261 if (type == nsIPrefBranch.PREF_STRING) 262 value = prefs.getCharPref(prefName); 263 else if (type == nsIPrefBranch.PREF_INT) 264 value = prefs.getIntPref(prefName); 265 else if (type == nsIPrefBranch.PREF_BOOL) 266 value = prefs.getBoolPref(prefName); 267 268 if (FBTrace.DBG_OPTIONS) 269 FBTrace.sysout("options.getPref "+prefName+" has type "+ 270 this.getPreferenceTypeName(type)+" and value "+value); 271 272 return value; 273 }, 274 275 set: function(name, value) 276 { 277 Options.setPref(Options.prefDomain, name, value); 278 }, 279 280 /** 281 * Set a preference value. 282 * 283 * @param prefDomain, e.g. "extensions.firebug" 284 * @param name Name of the preference (the part after prfDomain without dot) 285 * @param value New value for the preference. 286 * @param prefType optional pref type useful when adding a new preference. 287 */ 288 setPref: function(prefDomain, name, value, prefType) 289 { 290 var prefName = prefDomain + "." + name; 291 292 var type = this.getPreferenceTypeByExample((prefType ? prefType : typeof(value))); 293 if (!this.setPreference(prefName, value, type, prefs)) 294 return; 295 296 if (FBTrace.DBG_OPTIONS) 297 FBTrace.sysout("options.setPref type="+type+" name="+prefName+" value="+value); 298 }, 299 300 setPreference: function(prefName, value, type, prefBranch) 301 { 302 if (FBTrace.DBG_OPTIONS) 303 FBTrace.sysout("setPreference "+prefName, {prefName: prefName, value: value}); 304 305 if (type == nsIPrefBranch.PREF_STRING) 306 prefBranch.setCharPref(prefName, value); 307 else if (type == nsIPrefBranch.PREF_INT) 308 prefBranch.setIntPref(prefName, value); 309 else if (type == nsIPrefBranch.PREF_BOOL) 310 prefBranch.setBoolPref(prefName, value); 311 else if (type == nsIPrefBranch.PREF_INVALID) 312 { 313 FBTrace.sysout("options.setPref FAILS: Invalid preference "+prefName+" with type "+ 314 type+", check that it is listed in defaults/prefs.js"); 315 316 return false; 317 } 318 319 return true; 320 }, 321 322 getPreferenceTypeByExample: function(prefType) 323 { 324 if (prefType) 325 { 326 if (prefType === typeof("s")) 327 var type = nsIPrefBranch.PREF_STRING; 328 else if (prefType === typeof(1)) 329 var type = nsIPrefBranch.PREF_INT; 330 else if (prefType === typeof (true)) 331 var type = nsIPrefBranch.PREF_BOOL; 332 else 333 var type = nsIPrefBranch.PREF_INVALID; 334 } 335 else 336 { 337 var type = prefs.getPrefType(prefName); 338 } 339 340 return type; 341 }, 342 343 getPreferenceTypeName: function(prefType) 344 { 345 if (prefType == Ci.nsIPrefBranch.PREF_STRING) 346 return "string"; 347 else if (prefType == Ci.nsIPrefBranch.PREF_INT) 348 return "int"; 349 else if (prefType == Ci.nsIPrefBranch.PREF_BOOL) 350 return "boolean"; 351 }, 352 353 clear: function(name) 354 { 355 Options.clearPref(Options.prefDomain, name); 356 }, 357 358 clearPref: function(prefDomain, name) 359 { 360 var prefName = prefDomain + "." + name; 361 if (prefs.prefHasUserValue(prefName)) 362 prefs.clearUserPref(prefName); 363 }, 364 365 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 366 // Firebug UI text zoom 367 368 changeTextSize: function(amt) 369 { 370 var textSize = Options.get("textSize"); 371 var newTextSize = textSize + amt; 372 if ((newTextSize < 0 && Math.abs(newTextSize) < this.negativeZoomFactors.length) || 373 (newTextSize >= 0 && textSize+amt < this.positiveZoomFactors.length)) 374 { 375 this.setTextSize(textSize+amt); 376 } 377 }, 378 379 setTextSize: function(value) 380 { 381 var setValue = value; 382 if (value >= this.positiveZoomFactors.length) 383 setValue = this.positiveZoomFactors[this.positiveZoomFactors.length-1]; 384 else if (value < 0 && Math.abs(value) >= this.negativeZoomFactors.length) 385 setValue = this.negativeZoomFactors[this.negativeZoomFactors.length-1]; 386 this.set("textSize", setValue); 387 }, 388 389 positiveZoomFactors: [1, 1.1, 1.2, 1.3, 1.5, 2, 3], 390 negativeZoomFactors: [1, 0.95, 0.8, 0.7, 0.5], 391 392 getZoomByTextSize: function(value) 393 { 394 var zoom = value >= 0 ? this.positiveZoomFactors[value] : 395 this.negativeZoomFactors[Math.abs(value)]; 396 397 return zoom; 398 }, 399 400 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 401 402 /** 403 * Resets all Firebug options to default state. Note that every option 404 * starting with "extensions.firebug" is considered as a Firebug option. 405 * 406 * Options starting with DBG_ are intended for Firebug Tracing Console (FBTrace) 407 * and ignored (their state is not changed). 408 */ 409 resetAllOptions: function() 410 { 411 var preferences = prefs.getChildList("extensions.firebug", {}); 412 for (var i = 0; i < preferences.length; i++) 413 { 414 if (preferences[i].indexOf("DBG_") == -1) 415 { 416 if (FBTrace.DBG_OPTIONS) 417 FBTrace.sysout("Clearing option: " + i + ") " + preferences[i]); 418 419 if (prefs.prefHasUserValue(preferences[i])) // avoid exception 420 prefs.clearUserPref(preferences[i]); 421 } 422 else 423 { 424 if (FBTrace.DBG_OPTIONS) 425 FBTrace.sysout("Skipped clearing option: " + i + ") " + preferences[i]); 426 } 427 } 428 }, 429 430 forceSave: function() 431 { 432 prefs.savePrefFile(null); 433 } 434 }; 435 436 // ********************************************************************************************* // 437 // Registration 438 439 return Options; 440 441 // ********************************************************************************************* // 442 }); 443