Last active
August 29, 2015 13:57
-
-
Save yajd/9791029 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var me = Services.wm.getMostRecentWindow(null); | |
Cu.import('resource://gre/modules/osfile.jsm'); | |
var pathProfilesIni = OS.Path.join(OS.Constants.Path.userApplicationDataDir, 'profiles.ini'); | |
me.alert(pathProfilesIni); | |
//apparently theres for profiles made in default prof dir: | |
//localDir = C:\Users\ali57233\AppData\Local\Mozilla\Firefox\Profiles\czbm1ps9.rnd1 | |
//rootDir = C:\Users\ali57233\AppData\Roaming\Mozilla\Firefox\Profiles\czbm1ps9.rnd1 | |
//for custom prof dir localDir and rootDir are same | |
var ini = {}; | |
function readIni() { | |
let decoder = new TextDecoder(); // This decoder can be reused for several reads | |
let promise = OS.File.read(pathProfilesIni); // Read the complete file as an array | |
promise = promise.then( | |
function onSuccess(ArrayBuffer) { | |
var readStr = decoder.decode(ArrayBuffer); // Convert this array to a text | |
console.log(readStr); | |
ini = {}; | |
var patt = /\[(.*?)(\d*?)\](?:\s+?([\S]+)=([\S]+))(?:\s+?([\S]+)=([\S]+))?(?:\s+?([\S]+)=([\S]+))?(?:\s+?([\S]+)=([\S]+))?(?:\s+?([\S]+)=([\S]+))?/mg; | |
var blocks = []; | |
var match; | |
while (match = patt.exec(readStr)) { | |
console.log('MAAAAAAAAAAATCH', match); | |
var group = match[1]; | |
ini[group] = {}; | |
if (group == 'Profile') { | |
ini[group]['num'] = match[2]; | |
} | |
ini[group].props = {}; | |
for (var i = 3; i < match.length; i = i + 2) { | |
var prop = match[i]; | |
if (prop === undefined) { | |
break; | |
} | |
var propVal = match[i + 1] | |
ini[group].props[prop] = propVal; | |
} | |
if (group == 'Profile') { | |
//Object.defineProperty(ini, ini[group].props.Name, Object.getOwnPropertyDescriptor(ini[group], group)); | |
ini[ini[group].props.Name] = ini[group]; | |
delete ini[group]; | |
} | |
} | |
console.log('successfully read ini = ', ini); | |
updateProfToolkit(); | |
return ini; | |
}, | |
function onReject() { | |
console.error('Read ini failed'); | |
} | |
); | |
return promise; | |
} | |
var tps = Cc['@mozilla.org/toolkit/profile-service;1'].createInstance(Ci.nsIToolkitProfileService); //toolkitProfileService | |
//XPCOMUtils.defineLazyGetter(myServices, 'tps', function(){ return Cc['@mozilla.org/toolkit/profile-service;1'].createInstance(Ci.nsIToolkitProfileService) }); | |
function initProfToolkit() { | |
profToolkit = {}; | |
profToolkit.localPathDefault = OS.Path.dirname(OS.Constants.Path.localProfileDir); //will work as long as at least one profile is in the default profile folder //i havent tested when only custom profile | |
profToolkit.rootPathDefault = OS.Path.dirname(OS.Constants.Path.profileDir); | |
profToolkit.selectedProfile = {}; | |
profToolkit.selectedProfile.name = tps.selectedProfile.name; | |
} | |
function updateProfToolkit() { | |
if (!profToolkit.selectedProfile) { | |
initProfToolkit(); | |
} | |
var profileCount = 0; | |
var profileNames = []; | |
profToolkit.profiles = {}; | |
for (var p in ini) { | |
if ('num' in p) { | |
profileCount++; | |
profileNames.push(p); | |
} | |
} | |
profToolkit.profileCount = profileCount; | |
profToolkit.profileNames = profileNames; | |
} | |
function writeIni() { | |
var writeStr = []; | |
var profileI = -1; | |
for (var p in ini) { | |
if ('num' in ini[p]) { | |
//is profile | |
profileI++; //because we init profileI at -1 | |
var group = 'Profile' + profileI; | |
if (ini[p].num != profileI) { | |
console.log('profile I of profile changed from ' + ini[p].num + ' to ' + profileI + ' the object from ini read is =', ini[p]); | |
} | |
} else { | |
var group = p; | |
} | |
writeStr.push('[' + group + ']'); | |
for (var p2 in ini[p].props) { | |
writeStr.push(p2 + '=' + ini[p].props[p2]); | |
} | |
writeStr.push(''); | |
} | |
writeStr[writeStr.length - 1] = '\n'; //we want double new line at end of file | |
let encoder = new TextEncoder(); // This encoder can be reused for several writes | |
let BufferArray = encoder.encode(writeStr.join('\n')); // Convert the text to an array | |
let promise = OS.File.writeAtomic(pathProfilesIni, BufferArray, // Write the array atomically to "file.txt", using as temporary | |
{ | |
tmpPath: pathProfilesIni + '.profilist.tmp' | |
}); // buffer "file.txt.tmp". | |
promise.then( | |
function() {}, | |
function() { | |
console.error('writeIni failed'); | |
} | |
); | |
return promise; | |
} | |
/*start - salt generator from http://mxr.mozilla.org/mozilla-aurora/source/toolkit/profile/content/createProfileWizard.js?raw=1*/ | |
var kSaltTable = [ | |
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', | |
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', | |
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0' | |
]; | |
var kSaltString = ''; | |
for (var i = 0; i < 8; ++i) { | |
kSaltString += kSaltTable[Math.floor(Math.random() * kSaltTable.length)]; | |
} | |
function saltName(aName) { | |
return kSaltString + '.' + aName; | |
} | |
/*end - salt generator*/ | |
function createProfile(refreshIni, profName) { | |
//refreshIni is 0,1 or programmatically 2 | |
if (refreshIni == 1) { | |
var promise = readIni(); | |
promise.then( | |
function() { | |
createProfile(2, profName); | |
}, | |
function() { | |
console.error('Failed to refresh ini object from file during renameProfile'); | |
} | |
); | |
} else { | |
if (profName in ini) { | |
Services.prompt.alert(null, self.name + ' - ' + 'EXCEPTION', 'Cannot create profile with name "' + newName + '" because this name is already taken by another profile.'); | |
return; | |
} | |
//create folder in root dir (in it, one file "times.json" with contents: | |
/* | |
{ | |
"created": 1395861287491 | |
} | |
*/ | |
//todo: im curious i should ask at ask.m.o how come when profile is custom driectory, a folder in local path is not created, of course the one to the custom path will be root. b ut why not make a local folder? like is done for IsRelative createds? | |
//create folder in local dir if root dir is different (this one is empty) | |
//add to profiles ini | |
//check if profile exists first | |
var numProfiles = Object.keys(ini) - 1; | |
var dirName = saltName(profName); | |
ini[profName] = { | |
num: numProfiles, | |
props: { | |
Name: profName, | |
IsRelative: 1, | |
Path: 'Profiles/' + dirName | |
} | |
} | |
var rootPathDefaultDirName = OS.File.join(profToolkit.rootPathDefault, dirName); | |
var localPathDefaultDirName = OS.File.join(profToolkit.localPathDefault, dirName); | |
var promise = OS.File.makeDir(rootPathDefaultDirName); | |
promise.then( | |
function onSuc() { | |
console.log('successfully created root dir for profile ' + profName + ' the path is = ', rootPathDefaultDirName); | |
let encoder = new TextEncoder(); | |
let BufferArray = encoder.encode('{\n"created": ' + new Date().getTime() + '}\n'); | |
let promise3 = OS.File.writeAtomic(OS.Path.join(rootPathDefaultDirName, 'times.json'), BufferArray, | |
{ | |
tmpPath: OS.Path.join(rootPathDefaultDirName, 'times.json') + '.profilist.tmp' | |
} | |
); | |
promise3.then( | |
function() { | |
console.log('succesfully created times.json for profName of ' + profName + ' path is = ', OS.Path.join(rootPathDefaultDirName, 'times.json')); | |
}, | |
function() { | |
console.error('FAILED creating times.json for profName of ' + profName + ' failed times.json path is = ', OS.Path.join(rootPathDefaultDirName, 'times.json')); | |
} | |
); | |
return promise3; | |
}, | |
function onRej() { | |
console.error('FAILED to create root dir for profile ' + profName + ' the path is = ', rootPathDefaultDirName); | |
} | |
); | |
if (profToolkit.rootPathDefault != profToolkit.localPathDefault) { | |
var promise2 = OS.File.makeDir(localPathDefaultDirName); | |
promise2.then( | |
function onSuc() { | |
console.log('successfully created local dir for profile ' + profName + ' the path is = ', localPathDefaultDirName); | |
}, | |
function onRej() { | |
console.error('FAILED to create local dir for profile ' + profName + ' the path is = ', localPathDefaultDirName); | |
} | |
); | |
} | |
//see here: http://mxr.mozilla.org/mozilla-aurora/source/toolkit/profile/content/createProfileWizard.js | |
/* | |
29 var dirService = C["@mozilla.org/file/directory_service;1"].getService(I.nsIProperties); | |
30 gDefaultProfileParent = dirService.get("DefProfRt", I.nsIFile); | |
73 var defaultProfileDir = gDefaultProfileParent.clone(); | |
74 defaultProfileDir.append(saltName(document.getElementById("profileName").value)); | |
75 gProfileRoot = defaultProfileDir; | |
*/ | |
//see here for internal: http://mxr.mozilla.org/mozilla-aurora/source/toolkit/profile/content/profileSelection.js#139 | |
//actually see here for internal: http://mxr.mozilla.org/mozilla-central/source/toolkit/profile/nsToolkitProfileService.cpp#699 | |
} | |
} | |
function renameProfile(refreshIni, profName, newName) { | |
//refreshIni is 0,1 or programmatically 2 | |
if (refreshIni == 1) { | |
var promise = readIni(); | |
promise.then( | |
function() { | |
renameProfile(2, profName, newName); | |
}, | |
function() { | |
console.error('Failed to refresh ini object from file during renameProfile'); | |
} | |
); | |
} else { | |
//check if name is taken | |
if (profName in ini == false) { | |
Services.prompt.alert(null, self.name + ' - ' + 'EXCEPTION', 'Cannot find this profile name, "' + profName + '" so cannot delete it.'); | |
return; | |
} | |
if (newName in ini) { | |
Services.prompt.alert(null, self.name + ' - ' + 'EXCEPTION', 'Cannot rename to "' + newName + '" because this name is already taken by another profile.'); | |
return; | |
} | |
ini[profName].props.Name = newName; | |
var promise = writeIni(); | |
promise.then( | |
function onSuc() { | |
console.log('successfully edited name of ' + profName + ' to ' + newName + ' in Profiles.ini'); | |
}, | |
function onRej() { | |
console.error('FAILED to edit name of ' + profName + ' to ' + newName + ' in Profiles.ini'); | |
} | |
); | |
} | |
} | |
function deleteProfile(refreshIni, profName) { | |
//refreshIni is 0,1 or programmatically 2 | |
if (refreshIni == 1) { | |
var promise = readIni(); | |
promise.then( | |
function() { | |
deleteProfile(2, profName); | |
}, | |
function() { | |
console.error('Failed to refresh ini object from file on deleteProfile'); | |
} | |
); | |
} else { | |
//before deleting check if its default profile | |
if (profName in ini == false) { | |
Services.prompt.alert(null, self.name + ' - ' + 'EXCEPTION', 'Cannot find this profile name, "' + profName + '" so cannot delete it.'); | |
return; | |
} | |
//if (Object.keys(ini).length == 2) { | |
//Services.prompt.alert(null, self.name + ' - ' + 'EXCEPTION', 'Cannot delete this profile as it is the last profile remaining.'); | |
//return; | |
//} | |
//todo: figure out how to check if the profile is running, if it is dont delete but msg its open | |
if (ini[profName].props.IsRelative == '1') { | |
var PathRootDir = OS.Path.join(profToolkit.rootPathDefault, profToolkit.profiles[profName].rootDirName; | |
var PathLocalDir = OS.Path.join(profToolkit.localPathDefault, profToolkit.profiles[profName].localDirName; | |
var promise = OS.File.remove(PathRootDir); | |
promise.then( | |
function() { | |
console.log('successfully removed PathRootDir for profName of ' + profName, 'PathRootDir=', PathRootDir); | |
}, | |
function() { | |
console.warn('FAILED to remove PathRootDir for profName of ' + profName, 'PathRootDir=', PathRootDir); | |
} | |
); | |
var promise2 = OS.File.remove(PathLocalDir); | |
promise2.then( | |
function() { | |
console.info('successfully removed PathLocalDir for profName of ' + profName, 'PathLocalDir=', PathLocalDir); | |
}, | |
function() { | |
console.warn('FAILED to remove PathLocalDir for profName of ' + profName, 'PathLocalDir=', PathLocalDir); | |
} | |
); | |
} else { | |
var Path = ini[profName].props.Path; | |
var promise = OS.File.remove(Path); | |
promise.then( | |
function() { | |
console.log('successfully removed Path for profName of ' + profName, 'Path=', Path); | |
}, | |
function() { | |
console.warn('FAILED to remove Path for profName of ' + profName, 'Path=', Path); | |
} | |
); | |
} | |
delete ini[profName]; | |
var promise0 = writeIni(); | |
promise0.then( | |
function() { | |
console.log('successfully edited out profName of ' + profName + ' from Profiles.ini'); | |
}, | |
function() { | |
console.error('FAILED to edit out profName of ' + profName + ' from Profiles.ini'); | |
} | |
); | |
} | |
} | |
var promise = readIni(); | |
promise.then(function onSuc() { | |
me.alert('read done will now write') | |
var promise2 = writeIni(); | |
promise2.then(function onSuc() { | |
me.alert('write done') | |
}) | |
}) |
README
Rev2
- Added notes in comment for createProfile
Rev3
- Got rid of the vars of
onSuc
andonRej
, now just doing it in the function arguments, looks cleaner and simpler then before - All promises now have a
.then
with success and reject functions now
Rev4
- Trying to associate and populate with profToolkit (found a bunch of bugs with the old way of doing things thats currently in bootstrap.js of Profilist, like if loaded in custom profile but do
OS.Constants.Path.profileDir
it returns path to the root profile dir but the basename is that of the first profile folder in root (im not sure if first or default)
Rev5
- Initial but untested createProfile, deleteProfile, and renameProfile finished
- prefToolkit more complete I don't know if it's ready for port into bootstrap yet, I actually don't think it is
- todo: Should probably set TextEncoder and TextDecoder objects to lazy loader
Rev6
- Rather than recreate profToolkit exactly as it is in in bootsrap I have decided to change how bootstrap relies on profToolkit now that I have ini
- Made some updates
Notes on StartWithLastProfile (SWLP)
- On profile start of any profile FROM ProfileManager with option "Use the selected profile without asking at startup" is unchecked, the starting profile always gets set to default, if don't start from ProfileManager and when SWLP is 0 or 1
- I suspect if SWLP is 0, on start of exe it will open ProfileManager and if SWLP is 1 on start of exe it will open last profile marked as Default
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
might have to ignore profiles with name that start with webapp
http://mxr.mozilla.org/mozilla-aurora/source/mobile/android/base/GeckoProfile.java#568
556
557 if (!isDefaultSet && !mName.startsWith("webapp")) {
558 // only set as default if this is the first non-webapp
559 // profile we're creating
560 profileSection.setProperty("Default", 1);
561 }