-
-
Save Noitidart/e0d3c21ab38822fbfd17 to your computer and use it in GitHub Desktop.
// naming gist |
Cu.import('resource://gre/modules/Services.jsm'); | |
Cu.import('resource://gre/modules/devtools/Console.jsm'); | |
//start pref stuff | |
const {interfaces: Ci, utils: Cu, classes: Cc} = Components; | |
const self = { | |
name: 'Bootstrap Preferences Skeleton', | |
id: 'Bootstrap-Preferences-Skeleton@jetpack', //because pref listener inits before startup and in startup is where aData.self.id becomes available | |
path: {}, | |
aData: 0, | |
}; | |
//start pref stuff | |
const myPrefBranch = 'extensions.' + self.name + '@jetpack.'; | |
var myPrefListener; | |
//needs ES5, i dont know what min browser version of FF starts support for ES5 | |
/** | |
* if want to change value of preference dont do prefs.holdTime.value = blah, instead must do `prefs.holdTime.setval(500)` | |
* because this will then properly set the pref on the branch then it will do the onChange properly with oldVal being correct | |
* NOTE: this fucntion prefSetval is not to be used directly, its only here as a contructor | |
*/ | |
PrefListener.prototype.prefSetval = function(pass_pref_name, pass_branch_name) { | |
//console.log('this outside', this); | |
var passBranchObj = this.watchBranches[pass_branch_name]; | |
var passPrefObj = passBranchObj.prefNames[pass_pref_name]; | |
var func = function(updateTo, iHave__on_PrefOnObj_Change__butOnNextChangeSkipExecute) { | |
var pref_name = pass_pref_name; | |
var branch_name = pass_branch_name; | |
var branchObj = passBranchObj; //this.watchBranches[branch_name]; | |
var prefObj = passPrefObj; //branchObj.prefNames[pref_name]; | |
//console.info('in prefSetval', 'this:', this, 'branchObj', branchObj, 'prefObj', prefObj, 'pref_name', pass_pref_name); | |
if (iHave__on_PrefOnObj_Change__butOnNextChangeSkipExecute) { | |
var curValOnTree = branchObj._branchLive['get' + typeStr_from_typeLong(prefObj.type) + 'Pref'](pref_name); | |
if (curValOnTree == updateTo) { | |
console.warn('setval called said to mark it for skipOnChange, however updateTo and curValOnTree are same so on_PrefOnTree_Change will not call after running this updateTo so will not mark for skip'); | |
} else { | |
prefObj.iHave__on_PrefOnObj_Change__butOnNextChangeSkipExecute = new Date().getTime(); | |
} | |
} | |
branchObj._branchLive['set' + typeStr_from_typeLong(prefObj.type) + 'Pref'](pref_name, updateTo); | |
console.log('set doooone'); | |
}; | |
return func; | |
} | |
function typeStr_from_typeLong(typeLong) { | |
switch (typeLong) { | |
case Ci.nsIPrefBranch.PREF_STRING: | |
return 'Char'; | |
case Ci.nsIPrefBranch.PREF_INT: | |
return 'Int'; | |
case Ci.nsIPrefBranch.PREF_BOOL: | |
return 'Bool'; | |
case Ci.nsIPrefBranch.PREF_INVALID: | |
//probably pref does not exist | |
throw new Error('typeLong is PREF_INVALID so probably pref DOES NOT EXIST'); | |
default: | |
throw new Error('unrecognized typeLong:', typeLong); | |
} | |
} | |
///pref listener generic stuff NO NEED TO EDIT | |
/** | |
* @constructor | |
* | |
* @param {string} branch_name | |
* @param {Function} callback must have the following arguments: | |
* branch, pref_leaf_name | |
*/ | |
//note: a weakness with this api i made for prefs, is that, if upgrading/downgrading and in installing rev a pref is no longer in use, the old pref will stay in the about:config system. prefs are only deleted when addon is uninstalled note: as of 080314 though i think i have a solution for this, watch the info/warn dump and if it holds true than edit it in | |
//note: good thing about this overhaul of the pref skeleton is that i can have this skeleton pasted in, and if no prefs being watched it doesnt do anything funky | |
function PrefListener() { | |
//is an array | |
// Keeping a reference to the observed preference branch or it will get garbage collected. | |
Object.keys(this.watchBranches).forEach(function(branch_name) { | |
this.watchBranches[branch_name]._branchLive = Services.prefs.getBranch(branch_name); | |
this.watchBranches[branch_name]._branchDefault = Services.prefs.getDefaultBranch(branch_name); | |
//this.watchBranches[branch_name]._branchLive.QueryInterface(Ci.nsIPrefBranch2); //do not need this anymore as i dont support FF3.x | |
}.bind(this)); | |
} | |
//start - edit in here your prefs to watch | |
PrefListener.prototype.watchBranches = { | |
/* | |
// start - demo | |
'branch.name': { //for own branch we handle this outside of this object as we key to `myPrefBranch` for others branch like set to `'gecko.handlerService.schemes.mailto'` | |
ownType: 0, //0-full, 1-none, 2-partial //defines whether all prefs on this branch are owned (full,0), you own none of them so just watching whole, or partial which is mix | |
prefNames: { //this is an object of the prefs that i add into this branch, meaning i set the defaults on them. my prefs meaning that they belong to this addon and should be removed when this addon is uninstalled | |
//each key here must match the exact name the pref is saved in the about:config database (without the prefix) | |
//note: if i include a default key on the pref then it is a pref that i make on this branch | |
someNameOfPref: { //this pref gets created if not found in this branch in about:config, a defaultBranch value is set for it too, this pref is also deleted on uninstall of the addon. createdPrefs are denoted by supplying a `default` and `type` key | |
owned: true, //set this to true as we create this, if set to false it expects that the pref was made and we just want to watch | |
default: 300, //if owned is true must have default, else if its false, then cannot have default | |
value: undefined, //start value at undefined | |
type: Ci.nsIPrefBranch.PREF_STRING, //should call thi skey typeLong but whatever //Ci.nsIPrefBranch.PREF_BOOL or Ci.nsIPrefBranch.PREF_STRING or Ci.nsIPrefBranch.PREF_INT | |
//json: null, //if want to use json type must be string //NOT SUPPORTED IN V2.0 //12/8/14 | |
on_PrefOnObj_Change: function(oldVal, newVal, refObj) { } //on change means on change of the object prefs.blah.value within. NOT on change of the pref in about:config. likewise onPreChange means before chanigng the perfs.blah.value, this is because if users changes pref from about:config, newVal is always obtained by doing a getIntVal etc //refObj holds | |
} | |
}, | |
unknownNameOnChange: function(oldVal, newVal, refObj) { //really this just is unspecifiedNameOnChange | |
//this onChange function is called for prefs not found in the the prefNames object. if the pref_name change exists in the prefNames object and it doesnt have an onChange, then no onChange is called for that. So again this unknownNameOnChange is only called for if pref_name does not exist in prefNames obj | |
} | |
}, | |
'gecko.handlerService.schemes.mailto': { | |
ownType: 1, //1 means none, but if i create any prefs on this (by setting nams in prefNames to owned:true) then i should set this to partial | |
prefNames: { | |
nameOfPreSpecifiedPref: { | |
owned: false, //set to false we're just watching, set to true, it gets created | |
} | |
}, | |
// unknownNameOnChange: function(oldVal, newVal, refObj) {} //this is not required | |
} | |
// end - demo | |
*/ | |
} | |
PrefListener.prototype.watchBranches[myPrefBranch] = { //have to do it this way because in the watchBranches obj i can't do { myPrefBranch: {...} } | |
ownType: 0, //0-full, 1-none, 2-partial | |
prefNames: { | |
'notifications': { //name of your pref | |
owned: true, | |
default: true, | |
value: undefined, | |
type: Ci.nsIPrefBranch.PREF_BOOL, | |
on_PrefOnObj_Change: writePrefToIni | |
} | |
}, | |
on_UnknownPrefNameOnObj_Change: function(oldVal, newVal, refObj) { | |
console.warn('on_UnknownPrefNameOnObj_Change', 'oldVal:', oldVal, 'newVal:', newVal, 'refObj:', refObj); | |
} | |
}; | |
//end - edit in here your prefs to watch | |
PrefListener.prototype.observe = function(subject, topic, data) { | |
//console.log('incoming PrefListener observe :: ', 'topic:', topic, 'data:', data, 'subject:', subject); | |
//console.info('compare subject to this._branchLive[extensions.MailtoWebmails@jetpack.]', this.watchBranches[subject.root]._branchLive); | |
if (topic == 'nsPref:changed') { | |
var branch_name = subject.root; | |
var pref_name = data; | |
this.on_PrefOnTree_Change(branch_name, pref_name); | |
} else { | |
console.warn('topic is something totally unexpected it is:', topic); | |
} | |
}; | |
/** | |
* @param {boolean=} trigger if true triggers the registered function | |
* on registration, that is, when this method is called. | |
*/ | |
PrefListener.prototype.register = function(aReason, exec__on_PrefOnObj_Change__onRegister) { | |
var branchesOnObj = Object.keys(this.watchBranches); | |
for (var i=0; i<branchesOnObj.length; i++) { | |
var branch_name = branchesOnObj[i]; | |
var branchObj = this.watchBranches[branch_name]; | |
if (branchObj.ownType == 0) { | |
var unusedPrefNamesOnTree = branchObj._branchLive.getChildList('', {}); | |
} | |
var prefNamesOnObj = Object.keys(this.watchBranches[branch_name].prefNames); | |
for (var j=0; j<prefNamesOnObj.length; j++) { | |
var pref_name_on_obj = prefNamesOnObj[j]; | |
var prefObj = branchObj.prefNames[pref_name_on_obj]; | |
if (prefObj.owned) { | |
prefObj.setval = this.prefSetval(pref_name_on_obj, branch_name); | |
if (aReason == ADDON_INSTALL) { | |
prefObj.value = prefObj.default; | |
} else { | |
console.log('not install so fetching value of owned pref, as it should exist, may need to catch error here and on error set to default'); | |
console.info('aReason == ', aReason); | |
try { | |
prefObj.value = branchObj._branchLive['get' + typeStr_from_typeLong(prefObj.type) + 'Pref'](pref_name_on_obj); | |
} catch(ex) { | |
//console.warn('excpetion occured when trying to fetch value, startup is not install so it should exist, however it probably doesnt so weird, so setting it to default, CAN GET HERE IF say have v1.2 installed and prefs were introduced in v1.3, so on update it can get here. ex:', ex); //this may happen if prefs were deleted somehow even though not uninstalled | |
console.warn('pref is missing, aReason == ', aReason); //expected if startup and pref value was default value on shutdown. or if upgrade/downgrade to new version which has prefs that were not there in previous version. | |
prefObj.value = prefObj.default; | |
var prefMissing = true; | |
} | |
} | |
if (prefMissing || [ADDON_INSTALL, ADDON_UPGRADE, ADDON_DOWNGRADE].indexOf(aReason) > -1) { | |
if (prefMissing) { | |
console.error('setting on default branch because prefMissing is true, aReason btw is ', aReason); | |
} else { | |
console.error('setting on default branch because aReason == ', aReason); | |
} | |
branchObj._branchDefault['set' + typeStr_from_typeLong(prefObj.type) + 'Pref'](pref_name_on_obj, prefObj.default); | |
} else { | |
console.error('NOT setting on default branch because aReason == ', aReason); | |
} | |
if (branchObj.ownType == 0) { | |
var indexOfPrefName_ON_unusedPrefNamesOnTree = unusedPrefNamesOnTree.indexOf(pref_name_on_obj); | |
if (indexOfPrefName_ON_unusedPrefNamesOnTree > -1) { | |
unusedPrefNamesOnTree.splice(indexOfPrefName_ON_unusedPrefNamesOnTree, 1); | |
} | |
} | |
} else { | |
prefObj.type = branchObj._branchLive.getPrefType(pref_name_on_obj); //use _branchLive in case it doesnt have default value //and its got to have _branchLive value as it is NOT owned UNLESS dev messed ownership up | |
prefObj.default = branchObj._branchDefault['get' + typeStr_from_typeLong(prefObj.type) + 'Pref'](pref_name_on_obj); | |
prefObj.value = branchObj._branchLive['get' + typeStr_from_typeLong(prefObj.type) + 'Pref'](pref_name_on_obj); | |
prefObj.setval = this.prefSetval(pref_name_on_obj, branch_name); | |
} | |
} | |
branchObj._branchLive.addObserver('', this, false); | |
for (var j=0; j<unusedPrefNamesOnTree.length; j++) { | |
var pref_name_in_arr = unusedPrefNamesOnTree[j]; | |
/* | |
if (!this._branchDefault) { | |
this._branchDefault = Services.prefs.getDefaultBranch(null); | |
} | |
this._branchDefault.deleteBranch(branch_name + pref_name); //delete default value | |
branchObj._branchLive.clearUserPref(pref_name_in_arr); //delete live value | |
*/ | |
Services.prefs.deleteBranch(branch_name + pref_name_in_arr); //deletes the default and live value so pref_name is gone from tree | |
} | |
} | |
if (exec__on_PrefOnObj_Change__onRegister) { //for robustness this must not be a per branch or a per pref property but on the whole watchBranches | |
for (var i=0; i<branchesOnObj.length; i++) { | |
var branch_name = branchesOnObj[i]; | |
var branchObj = this.watchBranches[branch_name]; | |
var prefNamesOnObj = Object.keys(this.watchBranches[branch_name].prefNames); | |
for (var j=0; j<prefNamesOnObj.length; j++) { | |
var pref_name_on_obj = prefNamesOnObj[j]; | |
var prefObj = branchObj.prefNames[pref_name_on_obj]; | |
if (prefObj.on_PrefOnObj_Change) { | |
var oldVal = undefined; //because this is what value on obj was before i set it to something | |
var newVal = prefObj.value; | |
var refObj = { | |
branch_name: branch_name, | |
pref_name: pref_name_on_obj, | |
prefObj: prefObj, | |
branchObj: branchObj | |
}; | |
prefObj.on_PrefOnObj_Change(oldVal, newVal, refObj); | |
} | |
} | |
} | |
} | |
}; | |
PrefListener.prototype.unregister = function() { | |
var branchesOnObj = Object.keys(this.watchBranches); | |
for (var i=0; i<branchesOnObj.length; i++) { | |
var branch_name = branchesOnObj[i]; | |
var branchObj = this.watchBranches[branch_name]; | |
branchObj._branchLive.removeObserver('', this); | |
console.log('removed observer from branch_name', branch_name); | |
} | |
}; | |
PrefListener.prototype.uninstall = function(aReason) { | |
console.log('in PrefListener.uninstall proc'); | |
if (aReason == ADDON_UNINSTALL) { | |
var branchesOnObj = Object.keys(this.watchBranches); | |
for (var i=0; i<branchesOnObj.length; i++) { | |
var branch_name = branchesOnObj[i]; | |
var branchObj = this.watchBranches[branch_name]; | |
if (branchObj.ownType == 0) { | |
Services.prefs.deleteBranch(branch_name); | |
} else { | |
var prefNamesOnObj = Object.keys(this.watchBranches[branch_name].prefNames); | |
for (var j=0; j<prefNamesOnObj.length; j++) { | |
var pref_name_on_obj = prefNamesOnObj[j]; | |
var prefObj = branchObj.prefNames[pref_name_on_obj]; | |
if (prefObj.owned) { | |
Services.prefs.deleteBranch(branch_name + pref_name_on_obj); | |
} | |
} | |
} | |
} | |
} else { | |
console.log('not real uninstall so quitting preflistener.uninstall proc'); | |
} | |
}; | |
PrefListener.prototype.on_PrefOnTree_Change = function (branch_name, pref_name_on_tree) { | |
console.log('on_PrefOnTree_Change', 'pref_name_on_tree:', pref_name_on_tree, 'branch_name:', branch_name); | |
var branchObj = this.watchBranches[branch_name]; | |
var refObj = { | |
branch_name: branch_name, | |
pref_name: pref_name_on_tree, | |
branchObj: branchObj | |
}; | |
if (pref_name_on_tree in branchObj.prefNames) { | |
var prefObj = branchObj.prefNames[pref_name_on_tree]; | |
var oldVal = prefObj.value; | |
try { | |
var newVal = branchObj._branchLive['get' + typeStr_from_typeLong(prefObj.type) + 'Pref'](pref_name_on_tree); | |
} catch (ex) { | |
console.info('probably deleted', 'newVal exception:', ex); | |
} | |
refObj.prefObj = prefObj; | |
if (prefObj.iHave__on_PrefOnObj_Change__butOnNextChangeSkipExecute) { | |
var msAgo_markedForSkip = new Date().getTime() - prefObj.iHave__on_PrefOnObj_Change__butOnNextChangeSkipExecute; | |
console.log('skipping this onChange as 2nd arg told to skip it, it was marked for skip this many ms ago:', msAgo_markedForSkip); | |
delete prefObj.iHave__on_PrefOnObj_Change__butOnNextChangeSkipExecute | |
} else { | |
if (prefObj.on_PrefOnObj_Change) { | |
prefObj.on_PrefOnObj_Change(oldVal, newVal, refObj); | |
} else { | |
//do nothing | |
} | |
} | |
prefObj.value = newVal; | |
console.log('prefObj value updated, prefObj:', prefObj); | |
} else { | |
if (branchObj.on_UnknownPrefNameOnObj_Change) { | |
var oldVal = null; //i actually dont know if it existed before | |
refObj.type = branchObj._branchLive.getPrefType(pref_name_on_tree); | |
console.info('refObj.type:', refObj.type); | |
if (refObj.type == 0) { | |
console.info('unknownNameOnObj pref probably deleted'); | |
newVal = null; | |
} | |
var newVal = branchObj._branchLive['get' + typeStr_from_typeLong(refObj.type) + 'Pref'](pref_name_on_tree); | |
refObj.setval = function(updateTo) { | |
branchObj._branchLive['set' + typeStr_from_typeLong(refObj.type) + 'Pref'](pref_name_on_tree, updateTo); | |
} | |
branchObj.on_UnknownPrefNameOnObj_Change(oldVal, newVal, refObj); | |
} else { | |
//do nothing | |
} | |
} | |
console.log('DONE on_PrefOnTree_Change'); | |
}; | |
////end pref listener stuff | |
//end pref stuff | |
function startup(aData, aReason) { | |
console.log('startup reason = ', aReason); | |
self.aData = aData; //must go first, because functions in loadIntoWindow use self.aData | |
//start pref stuff more | |
myPrefListener = new PrefListener(); //init | |
console.info('myPrefListener', myPrefListener); | |
myPrefListener.register(aReason, false); | |
//end pref stuff more | |
//windowListener.register(); | |
} | |
function shutdown(aData, aReason) { | |
console.log('shutdown reason = ', aReason); | |
if (aReason == APP_SHUTDOWN) return; | |
//windowListener.unregister(); | |
//start pref stuff more | |
myPrefListener.unregister(); | |
//end pref stuff more | |
} | |
function install(aData, aReason) { | |
//must have arguments of aData and aReason otherwise the uninstall function doesn't trigger | |
} | |
function uninstall(aData, aReason) { | |
console.info('UNINSTALLING reason = ', aReason); | |
//start pref stuff more | |
if (!myPrefListener) { | |
//lets not register observer/listener lets just "install" it which populates branches | |
console.log('in uninstall had to init (soft install) myPrefListener') | |
myPrefListener = new PrefListener(); //this pouplates this.watchBranches[branch_name] so we can access .branchLive and .branchDefault IT WILL NOT register the perf observer/listener so no overhead there | |
} | |
myPrefListener.uninstall(aReason); //deletes owned branches AND owned prefs on UNowned branches, this is optional, you can choose to leave your preferences on the users computer | |
//end pref stuff more | |
} |
<?xml version="1.0" encoding="utf-8"?> | |
<!-- This Source Code Form is subject to the terms of the Mozilla Public | |
- License, v. 2.0. If a copy of the MPL was not distributed with this | |
- file, You can obtain one at http://mozilla.org/MPL/2.0/. --> | |
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://www.mozilla.org/2004/em-rdf#"> | |
<Description about="urn:mozilla:install-manifest"> | |
<em:id>Bootstrap-Preferences-Skeleton@jetpack</em:id> | |
<em:version>initial</em:version> | |
<em:type>2</em:type> | |
<em:bootstrap>true</em:bootstrap> | |
<em:unpack>false</em:unpack> | |
<!-- Firefox --> | |
<em:targetApplication> | |
<Description> | |
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> | |
<em:minVersion>7.0</em:minVersion> | |
<em:maxVersion>27.0</em:maxVersion> | |
</Description> | |
</em:targetApplication> | |
<!-- Front End MetaData --> | |
<em:name>Bootstrap Preferences Skeleton</em:name> | |
<em:description>Shows the bare minimum I use for my addons with preferences. Utilizes inline options interface.</em:description> | |
<em:creator>Noitidart</em:creator> | |
<em:contributor>jmiller29 for Idea</em:contributor> | |
<em:contributor>Isaac Grant for Icon</em:contributor> | |
<em:contributor>therube for SeaMonkey Compatability</em:contributor> | |
</Description> | |
</RDF> |
<?xml version="1.0" ?> | |
<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> | |
<setting pref="extensions.Bootstrap-Preferences-Skeleton@jetpack.dummy" title="Dummy" type="integer"> | |
This is a dummy pref of integer type. | |
</setting> | |
<setting pref="extensions.Bootstrap-Preferences-Skeleton@jetpack.otherPref" title="Other Preference Example" type="string"> | |
This is an example of string type. | |
</setting> | |
</vbox> |
I'm thining about an argument aReason
for prefCallback
. For reasons being registering (but this is easy to detect as if its registering prefs[n].value === null
), another reason being pref changed, another reason being forced.
This is what I was working on but I decided against adding it, at least for now:
the reason i decided against it, is because there is oldVal
and newVal
in the onChange
so we can test there what reason is. If there is no change (meaning oldVal === newVal
), obviously it was forced. If oldVal === null
then it is obvioulsy registering/initializing. Outstanding q: HOW TO IDENT ON UNINSTALL ITS DELETING PREF?
for example:
onChange: function(oldVal, newVal, refObj) {
var msga = '?';
if (oldVal === null) {
msga = 'register/init';
} else if (oldVal == newVal) {
msga = 'probably a programmatic force';
} else if (oldVal != newVal) {
msga = 'really chaning';
}
Services.prompt.alert(null, 'prefChange - ' + refObj.name, msga);
BUT anyways this is the aReason thing I was working on, i was thinking of passing it as an argument. It was work in progress when i decided to quit as its likely not needed. (was working in throbber restored rev ~21)
//start pref stuff
const prefPrefix = 'extensions.ThrobberRestored.'; //cannot put this in startup and cannot use self.aData.id
var prefs = { //each key here must match the exact name the pref is saved in the about:config database (without the prefix)
customImgIdle: {
default: '',
value: null,
type: 'Char',
onChange: function(oldVal, newVal, refObj) {
Services.prompt.alert(null, "pref change", "pref change function triggering on - " + refObj.name);
if (oldVal && oldVal != '') {
myServices.sss.unregisterSheet(cssUri_CustomImgIdle, myServices.sss.USER_SHEET);
}
newVal = newVal.trim();
if (newVal == '') {
cssUri_CustomImgIdle = '';
} else {
var normalized = OS.Path.normalize(newVal);
var file = new FileUtils.File(normalized);
var fileuri = Services.io.newFileURI(file).spec;
console.log('fileuri', fileuri);
//var newuri = Services.io.newURI(newVal, null, null);
//var newValRep = 'file:///' + newuri.spec.replace(/\\/g, '/');
var css = '#navigator-throbber:not([loading]) { list-style-image: url("' + fileuri + '") !important; }';
var newURIParam = {
aURL: 'data:text/css,' + encodeURIComponent(css),
aOriginCharset: null,
aBaseURI: null
};
cssUri_CustomImgIdle = Services.io.newURI(newURIParam.aURL, newURIParam.aOriginCharset, newURIParam.aBaseURI);
myServices.sss.loadAndRegisterSheet(cssUri_CustomImgIdle, myServices.sss.USER_SHEET); //running this last as i think its syncronus
}
}
},
customImgLoading: {
default: '',
value: null,
type: 'Char',
onChange: function(oldVal, newVal, refObj) {
Services.prompt.alert(null, "pref change", "pref change function triggering on - " + refObj.name);
if (oldVal && oldVal != '') {
myServices.sss.unregisterSheet(cssUri_CustomImgLoading, myServices.sss.USER_SHEET);
}
newVal = newVal.trim();
if (newVal == '') {
cssUri_CustomImgLoading = '';
} else {
var normalized = OS.Path.normalize(newVal);
var file = new FileUtils.File(normalized);
var fileuri = Services.io.newFileURI(file).spec;
console.log('fileuri', fileuri);
//var newuri = Services.io.newURI(newVal, null, null);
//var newValRep = 'file:///' + newuri.spec.replace(/\\/g, '/');
var css = '#navigator-throbber[loading] { list-style-image: url("' + fileuri + '") !important; }';
var newURIParam = {
aURL: 'data:text/css,' + encodeURIComponent(css),
aOriginCharset: null,
aBaseURI: null
};
cssUri_CustomImgLoading = Services.io.newURI(newURIParam.aURL, newURIParam.aOriginCharset, newURIParam.aBaseURI);
myServices.sss.loadAndRegisterSheet(cssUri_CustomImgLoading, myServices.sss.USER_SHEET); //running this last as i think its syncronus
}
}
}
}
/**
* if want to change value of preference dont do prefs.holdTime.value = blah, instead must do `prefs.holdTime.setval(500)`
* because this will then properly set the pref on the branch then it will do the onChange properly with oldVal being correct
* NOTE: this fucntion prefSetval is not to be used directly, its only here as a contructor
*/
function prefSetval(name) {
return function(updateTo) {
console.log('in prefSetval');
console.info('this = ', this);
if ('json' in this) {
//updateTo must be an object
if (Object.prototype.toString.call(updateTo) != '[object Object]') {
console.warn('EXCEPTION: prefs[name] is json but updateTo supplied is not an object');
return;
}
var stringify = JSON.stringify(updateTo); //uneval(updateTo);
myPrefListener._branch['set' + this.type + 'Pref'](name, stringify);
//prefs[name].value = {};
//for (var p in updateTo) {
// prefs[name].value[p] = updateTo[p];
//}
} else {
//prefs[name].value = updateTo;
myPrefListener._branch['set' + this.type + 'Pref'](name, updateTo);
}
};
}
///pref listener generic stuff NO NEED TO EDIT
/**
* @constructor
*
* @param {string} branch_name
* @param {Function} callback must have the following arguments:
* branch, pref_leaf_name
*/
function PrefListener(branch_name, callback) {
// Keeping a reference to the observed preference branch or it will get garbage collected.
this._branch = Services.prefs.getBranch(branch_name);
this._defaultBranch = Services.prefs.getDefaultBranch(branch_name);
this._branch.QueryInterface(Ci.nsIPrefBranch2);
this._callback = callback;
}
PrefListener.prototype.observe = function(subject, topic, data) {
console.log('incomcing PrefListener observe', 'topic=', topic, 'data=', data, 'subject=', subject);
if (topic == 'nsPref:changed')
this._callback(this._branch, data);
};
/**
* @param {boolean=} trigger if true triggers the registered function
* on registration, that is, when this method is called.
*/
PrefListener.prototype.register = function(setDefaults, trigger) {
//adds the observer to all prefs and gives it the seval function
for (var p in prefs) {
prefs[p].setval = new prefSetval(p);
}
console.log('added setval');
if (setDefaults) {
this.setDefaults();
console.log('finished set defaults');
}
//should add observer after setting defaults otherwise it triggers the callbacks
this._branch.addObserver('', this, false);
console.log('added observer');
if (trigger) {
console.log('trigger callbacks');
this.forceCallbacks({aReason:});
console.log('finished all callbacks');
}
};
PrefListener.prototype.forceCallbacks = function(argObj) {
console.log('forcing pref callbacks');
let that = this;
this._branch.getChildList('', {}).
forEach(function (pref_leaf_name)
{ that._callback(that._branch, pref_leaf_name); });
};
PrefListener.prototype.setDefaults = function() {
//sets defaults on the prefs in prefs obj
console.log('doing setDefaults');
for (var p in prefs) {
console.log('will now set default on ', p);
console.log('will set it to "' + prefs[p].default + '"');
this._defaultBranch['set' + prefs[p].type + 'Pref'](p, prefs[p].default);
console.log('fined setting default on ', p);
}
console.log('set defaults done');
};
PrefListener.prototype.unregister = function() {
if (this._branch)
this._branch.removeObserver('', this);
};
var myPrefListener = new PrefListener(prefPrefix, function (branch, name, aReason) {
/* aReason - reason for calling this callback
* 0 - registering
* 1 - pref changed
* 2 - i as the programmer programatticaly called forceCallbacks, i should set this in the argObj
*/
//extensions.myextension[name] was changed
console.log('callback start for pref: ', name);
if (!(name in prefs)) {
console.warn('name is not in prefs so return name = ', name);
//added this because apparently some pref named prefPreix + '.sdk.console.logLevel' gets created when testing with builder
//ALSO gets here if say upgraded, and in this version this pref is not used (same with downgraded)
return;
}
var refObj = {name: name, aReason: aReason}; //passed to onPreChange and onChange
var oldVal = 'json' in prefs[name] ? prefs[name].json : prefs[name].value;
try {
var newVal = myPrefListener._branch['get' + prefs[name].type + 'Pref'](name);
} catch (ex) {
console.warn('exception when getting newVal (likely the pref was removed): ' + ex);
var newVal = null; //note: if ex thrown then pref was removed (likely probably)
}
console.log('oldVal == ', oldVal);
console.log('newVal == ', newVal);
prefs[name].value = newVal === null ? prefs[name].default : newVal;
if ('json' in prefs[name]) {
refObj.oldValStr = oldVal;
oldVal = JSON.parse(oldVal); //function(){ return eval('(' + oldVal + ')') }();
refObj.newValStr = prefs[name].value;
prefs[name].json = prefs[name].value;
prefs[name].value = JSON.parse(prefs[name].value); //function(){ return eval('(' + prefs[name].value + ')') }();
}
if (prefs[name].onChange) {
prefs[name].onChange(oldVal, prefs[name].value, refObj);
}
console.log('myPrefCallback done');
});
////end pref listener stuff
//end pref stuff
see initial.rev5 of MailtoWebservices repo here i use it to monitor some default prefs, which means i have not set a default value. so my solution was just check if default key exists, if it doesnt than dont set default on it.
also had to change this part:
prefs[name].value = newVal === null ? prefs[name].default : newVal;
to:
if ('default' in prefs[name]) {
prefs[name].value = newVal === null ? prefs[name].default : newVal;
} else {
prefs[name].value = null;
}
PrefSkel 2.0
Needed Features
- Create and monitor multiple branches
- On uninstall option to delete branches
- If new pref gets created after
register
it should get added in and should useunknownOnChange
- Watch unowned branches
- Add single pref to
prefNames
to get ability to addonChange
or add the single pref and omit theonChange
to make it ignoreunknownOnChange
- During run time, after registered this listener, if new pref gets created on the branch, it should get added and should use
unknownOnChange
- Any single pref that is not in
prefNames
should useunknownOnChange
- Add single pref to
- Watch single prefs on unowned branches (gets ability to set the value to)
- On uninstall option to
clearUserPref
on these watched values
- On uninstall option to
- DO NOT ALLOW
- Creating single prefs on unowned branches (this is problematic for an add-on like DevPrefs)
Is option.uxl intended? i thought that xul would be the right file ending.
README
Note This template and readme is in progress, I plan to write in detail how to use this. Basically though, all you need to edit is
prefPrefix
and theprefs
object.A lot of people found it very helpful and useful so I thought I would share it. They liked the
onChange
function capability that supplies it with the old and new value of the preference. They also liked how you can just doprefs.prefName.setval('blah')
because it properly triggers theonChange
and especially because they don't need to call so many different functions such assetCharPref
orsetIntPref
orsetBoolPref
.Note The
onChange
function is triggered even by the register function, but onChange the oldVal will benull
so this is how to detect if it's a startup thing.Rev1
Rev2
Rev3
Rev4
Rev5
windowListener
register and unregister functions, commented outuninstall
triggers even iffunction install() {}
is not coded, so no need to even pass arguments and leave body blank, but i didnt edit this out yet of gist aboveRev6
Rev7