RetroZilla/xpfe/global/resources/content/extensions/sessionmanager/sessionmanager.js

1278 lines
38 KiB
JavaScript
Raw Normal View History

2016-04-15 05:02:37 +02:00
/*const*/ var gSessionManager = {
mCrashRecoveryService: Components.classes["@zeniko/crashrecoveryservice;1"].getService(Components.interfaces.nsICrashRecoveryService),
mObserverService: Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService),
mPrefRoot: Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch2),
mWindowMediator: Components.classes["@mozilla.org/appshell/window-mediator;1"].getService(Components.interfaces.nsIWindowMediator),
mPromptService: Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService(Components.interfaces.nsIPromptService),
mProfileDirectory: Components.classes["@mozilla.org/file/directory_service;1"].getService(Components.interfaces.nsIProperties).get("ProfD", Components.interfaces.nsILocalFile),
mIOService: Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService),
mComponents: Components,
mObserving: ["crashrecovery:windowclosed", "browser:purge-session-history", "quit-application-granted", "quit-application"],
mPrefBranchName: "extensions.sessionmanager.",
mClosedWindowFile: "sessionmanager.dat",
mDataTabAttribute: "CR_closed_tabs",
mBackupSessionName: "backup.session",
mPromptSessionName: "?",
mSessionExt: ".session",
mClosedTabs: [],
mSessionCache: {},
mDataTab: null,
/* ........ Listeners / Observers.............. */
onLoad_proxy: function()
{
this.removeEventListener("load", gSessionManager.onLoad_proxy, false);
gSessionManager.onLoad();
},
onLoad: function(aDialog)
{
this.mBundle = document.getElementById("bundle_sessionmanager");
this.mTitle = this._string("sessionManager");
this.mEOL = this.getEOL();
this.mPrefBranch = this.mPrefRoot.QueryInterface(Components.interfaces.nsIPrefService).getBranch(this.mPrefBranchName).QueryInterface(Components.interfaces.nsIPrefBranch2);
if (aDialog || this.mFullyLoaded)
{
return;
}
if (this.getPref("_resume_session"))
{
this.setResumeCurrent(true);
this.delPref("_resume_session");
}
this.mObserving.forEach(function(aTopic) {
this.mObserverService.addObserver(this, aTopic, false);
}, this);
this.mPref_backup_session = this.getPref("backup_session", 1);
this.mPref_max_backup_keep = this.getPref("max_backup_keep", 0);
this.mPref_max_closed_undo = this.getPref("max_closed_undo", 10);
this.mPref_max_tabs_undo = this.getPref("max_tabs_undo", 10);
this.mPref_name_format = this.getPref("name_format", "%40t-%d");
this.mPref_overwrite = this.getPref("overwrite", false);
this.mPref_reload = this.getPref("reload", false);
this.mPref_resume_session = this.getPref("resume_session", "");
this.mPref_save_closed_tabs = this.getPref("save_closed_tabs", 0);
this.mPref_save_window_list = this.getPref("save_window_list", false);
this.mPref_session_list_order = this.getPref("session_list_order", 1);
this.mPref_submenus = this.getPref("submenus", false);
this.mPref__running = this.getPref("_running", false);
this.mPrefBranch.addObserver("", this, false);
getBrowser().addEventListener("CRTabRestoring", this.onTabRestoring_proxy, false);
gBrowser.addEventListener("CRTabRestored", this.onTabRestored_proxy, false);
gBrowser.addEventListener("SMTabClosing", this.onTabClosing_proxy, false);
gBrowser.addEventListener("load", this.onTabLoading_proxy, true);
this.recoverSession();
this.patchTabBrowser();
this.updateToolbarButton();
if (!this.mPref__running)
{
this.setPref("_running", true);
this.mPrefRoot.savePrefFile(null);
}
this.mFullyLoaded = true;
},
onUnload_proxy: function()
{
this.removeEventListener("unload", gSessionManager.onUnload_proxy, false);
gSessionManager.onUnload();
},
onUnload: function()
{
this.mObserving.forEach(function(aTopic) {
this.mObserverService.removeObserver(this, aTopic);
}, this);
this.mPrefBranch.removeObserver("", this);
gBrowser.removeEventListener("CRTabRestoring", this.onTabRestoring_proxy, false);
gBrowser.removeEventListener("CRTabRestored", this.onTabRestored_proxy, false);
gBrowser.removeEventListener("SMTabClosing", this.onTabClosing_proxy, false);
gBrowser.removeEventListener("load", this.onTabLoading_proxy, true);
if (this.mPref__running && this.getBrowserWindows().length == 0)
{
this.mObserverService.addObserver(this, "quit-application", false);
this.mObserverService.addObserver(this, "domwindowopened", false);
this._string_preserve_session = this._string("preserve_session");
this._string_backup_session = this._string("backup_session");
this._string_old_backup_session = this._string("old_backup_session");
this.setPref("_hibernating", true);
}
this.mBundle = null;
this.mSessions = null;
this.mClosedTabs = null;
this.mFullyLoaded = false;
},
observe: function(aSubject, aTopic, aData)
{
switch (aTopic)
{
case "crashrecovery:windowclosed":
if (aSubject == window)
{
this.appendClosedWindow(aData);
}
else if (this.mPref_max_closed_undo > 0)
{
this.updateToolbarButton(true);
}
break;
case "browser:purge-session-history":
this.clearUndoData("all");
this.delFile(this.getSessionDir(this.mBackupSessionName));
break;
case "nsPref:changed":
this["mPref_" + aData] = this.getPref(aData);
switch (aData)
{
case "max_closed_undo":
if (this.mPref_max_closed_undo == 0)
{
this.clearUndoData("window", true);
}
break;
case "max_tabs_undo":
if (this.mPref_max_tabs_undo == 0)
{
this.clearUndoData("tab", true);
}
break;
case "resume_session":
this.setResumeCurrent(this.mPref_resume_session == this.mBackupSessionName);
break;
case "save_closed_tabs":
if (!this.mPref_save_closed_tabs)
{
this.clearUndoData("datatab");
}
else if (this.mClosedTabs.length > 0)
{
this.persistClosedTabList();
}
break;
}
break;
case "quit-application-granted":
case "quit-application":
if (this.getPref("_running"))
{
this.delPref("_running");
this.delPref("_hibernating");
if (!this.mPref_save_window_list)
{
this.clearUndoData("window", true);
}
if (this.doResumeCurrent(true))
{
this.setPref("_resume_session_once", (this.mPref__resume_session_once = true))
}
this.backupCurrentSession();
}
break;
case "domwindowopened":
aSubject.__gSeMa = this;
aSubject.addEventListener("load", this.onDomWindowOpened_proxy, false);
break;
}
},
onDomWindowOpened_proxy: function()
{
this.removeEventListener("load", this.__gSeMa.onDomWindowOpened_proxy, false);
if (this.document.documentElement.getAttribute("windowtype") == "navigator:browser")
{
this.__gSeMa.mObserverService.removeObserver(this.__gSeMa, "quit-application");
this.__gSeMa.mObserverService.removeObserver(this.__gSeMa, "domwindowopened");
}
delete this.__gSeMa;
},
onTabRestoring_proxy: function(aEvent)
{
gSessionManager.onTabRestoring(aEvent.originalTarget);
},
onTabRestoring: function(aTab)
{
if (aTab.hasAttribute(this.mDataTabAttribute))
{
if (this.mDataTab && this.mDataTab != aTab)
{
this.clearUndoData("datatab");
}
this.mDataTab = aTab;
var serializedTabs = aTab.getAttribute(this.mDataTabAttribute);
this.mClosedTabs = serializedTabs.split("\f\f").map(function(aData) {
if (/^(\d+) (.*)\n([\s\S]*)/.test(aData))
{
return { name: RegExp.$2, pos: parseInt(RegExp.$1), state: RegExp.$3 };
}
return null;
}).filter(function(aTab) { return aTab != null; }).slice(0, this.mPref_max_tabs_undo);
this.updateToolbarButton((this.mClosedTabs.length > 0)?true:undefined);
if (!this.mPref_save_closed_tabs)
{
this.clearUndoData("datatab");
}
}
},
onTabRestored_proxy: function(aEvent)
{
var browser = this.getBrowserForTab(aEvent.originalTarget);
if (gSessionManager.mPref_reload && gSessionManager._allowReload && !browser.__CR_data && !gSessionManager.mIOService.offline)
{
var nsIWebNavigation = Components.interfaces.nsIWebNavigation;
var webNav = browser.webNavigation;
try
{
webNav = webNav.sessionHistory.QueryInterface(nsIWebNavigation);
}
catch (ex) { }
webNav.reload(nsIWebNavigation.LOAD_FLAGS_BYPASS_PROXY | nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE);
}
},
onTabClosing_proxy: function(aEvent)
{
gSessionManager.onTabClosing(aEvent.originalTarget);
},
onTabClosing: function(aTab)
{
if (this.mPref_max_tabs_undo == 0)
{
return;
}
if (this.mDataTab && this.mDataTab == aTab)
{
var dataTabData = aTab.getAttribute(this.mDataTabAttribute);
this.mDataTab = gBrowser.mTabs[(aTab.previousSibling)?0:1] || null;
if (this.mDataTab && dataTabData)
{
this.mDataTab.setAttribute(this.mDataTabAttribute, dataTabData);
dataTabData = null;
}
aTab.removeAttribute(this.mDataTabAttribute);
}
if (!this._ignoreRemovedTabs && !this.isCleanBrowser(gBrowser.getBrowserForTab(aTab)))
{
var state = this.getSessionState(null, true);
var position = (aTab._tPos != undefined)?aTab._tPos + 1:0;
var title = aTab.label;
if (!position || gBrowser.mTabs[position - 1] != aTab)
{
for (position = 1; (aTab = aTab.previousSibling); position++);
}
this.appendClosedTab(state, position, title, this.mDataTab || gBrowser.mTabs[(aTab.previousSibling)?0:1] || null);
}
else if (dataTabData)
{
this.mDataTab = aTab;
this.mDataTab.setAttribute(this.mDataTabAttribute, dataTabData);
}
},
onTabLoading_proxy: function(aEvent)
{
if (aEvent.originalTarget && aEvent.originalTarget.loadOverlay)
{
aEvent.originalTarget.loadOverlay("chrome://sessionmanager/content/zzz_closewindow_hack.xul", null);
}
},
onToolbarClick: function(aEvent, aButton)
{
if (aEvent.button == 1)
{
eval("var event = { shiftKey: true }; " + aButton.getAttribute("oncommand"));
}
else if (aEvent.button == 2 && aButton.getAttribute("disabled") != "true")
{
aButton.open = true;
}
},
/* ........ Menu Event Handlers .............. */
init: function(aPopup, aIsToolbar)
{
function get_(a_id) { return aPopup.getElementsByAttribute("_id", a_id)[0] || null; }
var separator = get_("separator");
var startSep = get_("start-separator");
for (var item = startSep.nextSibling; item != separator; item = startSep.nextSibling)
{
aPopup.removeChild(item);
}
this.mSessions = this.getSessions();
this.mSessions.forEach(function(aSession, aIx) {
var key = (aIx < 9)?aIx + 1:(aIx == 9)?"0":"";
var menuitem = document.createElement("menuitem");
menuitem.setAttribute("label", ((key)?key + ") ":"") + aSession.name);
menuitem.setAttribute("oncommand", 'gSessionManager.load("' + aSession.fileName + '", (event.shiftKey && event.ctrlKey)?"overwrite":(event.shiftKey)?"newwindow":(event.ctrlKey)?"append":"");');
menuitem.setAttribute("onclick", 'if (event.button == 1) gSessionManager.load("' + aSession.fileName + '", "newwindow");');
menuitem.setAttribute("accesskey", key);
aPopup.insertBefore(menuitem, separator);
});
separator.hidden = (this.mSessions.length == 0);
this.setDisabled(separator.nextSibling, separator.hidden);
this.setDisabled(separator.nextSibling.nextSibling, separator.hidden);
try
{
get_("resume").setAttribute("checked", this.doResumeCurrent());
get_("overwrite").setAttribute("checked", this.mPref_overwrite);
get_("reload").setAttribute("checked", this.mPref_reload);
}
catch (ex) { } // not available for Firefox
var undoMenu = get_("undo-menu");
while (aPopup.lastChild != undoMenu)
{
aPopup.removeChild(aPopup.lastChild);
}
var undoDisabled = (this.mPref_max_closed_undo == 0 && this.mPref_max_tabs_undo == 0);
var divertedMenu = aIsToolbar && document.getElementById("sessionmanager-undo");
var canUndo = !undoDisabled && !divertedMenu && this.initUndo(undoMenu.firstChild);
undoMenu.hidden = undoDisabled || divertedMenu || !this.mPref_submenus;
undoMenu.previousSibling.hidden = !canUndo && undoMenu.hidden;
this.setDisabled(undoMenu, !canUndo);
if (!this.mPref_submenus && canUndo)
{
for (item = undoMenu.firstChild.firstChild; item; item = item.nextSibling)
{
aPopup.appendChild(item.cloneNode(true));
}
}
// in case the popup belongs to a collapsed element
aPopup.style.visibility = "visible";
},
uninit: function(aPopup)
{
aPopup.style.visibility = "";
this.mSessions = null;
},
save: function(aName, aFileName, aOneWindow)
{
var values = { text: this.getFormattedName(content.document.title || "about:blank", new Date()) || (new Date()).toLocaleString() };
if (!aName)
{
if (!this.prompt(this._string("save2_session"), this._string("save_" + ((aOneWindow)?"window":"session") + "_ok"), values, this._string("save_" + ((aOneWindow)?"window":"session")), this._string("save_session_ok2")))
{
return;
}
aName = values.text;
aFileName = values.name;
}
if (aName)
{
var file = this.getSessionDir(aFileName || this.makeFileName(aName), !aFileName);
try
{
this.writeFile(file, this.getSessionState(aName, aOneWindow, this.mPref_save_closed_tabs < 2));
}
catch (ex)
{
this.ioError(ex);
}
}
},
saveWindow: function(aName, aFileName)
{
this.save(aName, aFileName, true);
},
load: function(aFileName, aMode)
{
var state = this.readSessionFile(this.getSessionDir(aFileName));
if (!state)
{
this.ioError();
return;
}
var newWindow = false;
var overwriteTabs = true;
var tabsToMove = null;
aMode = aMode || "default";
if (aMode == "startup")
{
overwriteTabs = this.isCmdLineEmpty();
tabsToMove = (!overwriteTabs)?Array.slice(gBrowser.mTabs):null;
}
else if (aMode == "append")
{
overwriteTabs = false;
}
else if (aMode == "newwindow" || (aMode != "overwrite" && !this.mPref_overwrite))
{
newWindow = true;
}
else
{
this.getBrowserWindows().forEach(function(aWindow) {
if (aWindow != window) { aWindow.close(); }
});
this.mObserverService.notifyObservers(window, "crashrecovery:windowclosed", this.getSessionState(null, true));
}
setTimeout(function() {
var tabcount = gBrowser.mTabs.length;
gSessionManager.restoreSession((!newWindow)?window:null, state, overwriteTabs, true);
if (tabsToMove)
{
var endPos = gBrowser.mTabs.length - 1;
tabsToMove.forEach(function(aTab) { gBrowser.moveTabTo(aTab, endPos); });
}
else if (!overwriteTabs && gBrowser.mTabs[tabcount])
{
setTimeout(function(aTab) {
gBrowser.selectedTab = aTab;
}, 100, gBrowser.mTabs[tabcount]);
}
}, 0);
},
rename: function()
{
var values = {};
if (!this.prompt(this._string("rename_session"), this._string("rename_session_ok"), values, this._string("rename2_session")))
{
return;
}
var file = this.getSessionDir(values.name);
var filename = this.makeFileName(values.text);
var newFile = (filename != file.leafName)?this.getSessionDir(filename, true):null;
try
{
this.writeFile(newFile || file, this.nameState(this.readSessionFile(file), values.text));
if (newFile)
{
if (this.mPref_resume_session == file.leafName && this.mPref_resume_session != this.mBackupSessionName)
{
this.setPref("resume_session", filename);
}
this.delFile(file);
}
}
catch (ex)
{
this.ioError(ex);
}
},
remove: function(aSession)
{
if (!aSession)
{
aSession = this.selectSession(this._string("remove_session"), this._string("remove_session_ok"), { multiSelect: true });
}
if (aSession)
{
aSession.split("\n").forEach(function(aFileName) {
this.delFile(this.getSessionDir(aFileName));
}, this);
}
},
openFolder: function()
{
try
{
this.getSessionDir().launch();
}
catch (ex)
{
this.ioError(ex);
}
},
openOptions: function()
{
openDialog("chrome://sessionmanager/content/options.xul", "_blank", "chrome,dialog,modal,titlebar,centerscreen");
},
toggleResume: function()
{
this.setResumeCurrent(!this.doResumeCurrent());
},
toggleReload: function()
{
this.setPref("reload", !this.mPref_reload);
},
toggleOverwrite: function()
{
this.setPref("overwrite", !this.mPref_overwrite);
},
/* ........ Undo Menu Event Handlers .............. */
initUndo: function(aPopup, aStandAlone)
{
function get_(a_id) { return aPopup.getElementsByAttribute("_id", a_id)[0] || null; }
var separator = get_("closed-separator");
var label = get_("windows");
for (var item = separator.previousSibling; item != label; item = separator.previousSibling)
{
aPopup.removeChild(item);
}
var closedWindows = this.getClosedWindows();
closedWindows.forEach(function(aWindow, aIx) {
var menuitem = document.createElement("menuitem");
menuitem.setAttribute("label", aWindow.name);
menuitem.setAttribute("oncommand", 'gSessionManager.undoCloseWindow(' + aIx + ', (event.shiftKey && event.ctrlKey)?"overwrite":(event.ctrlKey)?"append":"");');
aPopup.insertBefore(menuitem, separator);
});
label.hidden = (closedWindows.length == 0);
var listEnd = get_("end-separator");
for (item = separator.nextSibling.nextSibling; item != listEnd; item = separator.nextSibling.nextSibling)
{
aPopup.removeChild(item);
}
this.mClosedTabs.forEach(function(aTab, aIx) {
var menuitem = document.createElement("menuitem");
menuitem.setAttribute("label", aTab.name);
menuitem.setAttribute("oncommand", 'gSessionManager.undoCloseTab(' + aIx + ');');
aPopup.insertBefore(menuitem, listEnd);
});
separator.nextSibling.hidden = (this.mClosedTabs.length == 0);
separator.hidden = separator.nextSibling.hidden || label.hidden;
var showPopup = closedWindows.length + this.mClosedTabs.length > 0;
if (aStandAlone)
{
if (!showPopup)
{
this.updateToolbarButton(false);
setTimeout(function(aPopup) { aPopup.parentNode.open = false; }, 0, aPopup);
}
else
{
aPopup.style.visibility = "visible";
}
}
return showPopup;
},
uninitUndo: function(aPopup)
{
aPopup.style.visibility = "";
},
undoCloseWindow: function(aIx, aMode)
{
var closedWindows = this.getClosedWindows();
if (closedWindows[aIx || 0])
{
var state = closedWindows.splice(aIx || 0, 1)[0].state;
this.storeClosedWindows(closedWindows);
if (aMode == "overwrite")
{
this.mObserverService.notifyObservers(window, "crashrecovery:windowclosed", this.getSessionState(null, true));
}
else if (aMode == "append")
{
state = this.stripTabUndoData(state);
}
this.restoreSession((aMode == "overwrite" || aMode == "append")?window:null, state, aMode != "append");
}
},
undoCloseTab: function(aIx)
{
if (this.mClosedTabs[aIx || 0])
{
var tabData = this.mClosedTabs.splice(aIx || 0, 1)[0];
this.restoreSession(window, tabData.state);
if (this.mClosedTabs.length == 0)
{
this.updateToolbarButton();
}
var newTab = document.getAnonymousElementByAttribute(gBrowser, "linkedpanel", gBrowser.mPanelContainer.lastChild.id);
if (gBrowser.mTabs.length == 2 && !this.getPref("browser.tabs.autoHide", true, true) && this.isCleanBrowser(gBrowser.selectedBrowser))
{
gBrowser.removeTab(gBrowser.selectedTab);
}
if (tabData.pos && tabData.pos < gBrowser.mTabs.length)
{
gBrowser.moveTabTo(newTab, tabData.pos - 1);
}
gBrowser.selectedTab = newTab;
}
},
clearUndoList: function()
{
var max_tabs_undo = this.getPref("max_tabs_undo", 10);
this.setPref("max_tabs_undo", 0);
this.setPref("max_tabs_undo", max_tabs_undo);
this.clearUndoData("window");
},
/* ........ User Prompts .............. */
prompt: function(aSessionLabel, aAcceptLabel, aValues, aTextLabel, aAcceptExistingLabel)
{
var params = Components.classes["@mozilla.org/embedcomp/dialogparam;1"].createInstance(Components.interfaces.nsIDialogParamBlock);
aValues = aValues || {};
params.SetNumberStrings(7);
params.SetString(1, aSessionLabel);
params.SetString(2, aAcceptLabel);
params.SetString(3, aValues.name || "");
params.SetString(4, aTextLabel || "");
params.SetString(5, aAcceptExistingLabel || "");
params.SetString(6, aValues.text || "");
params.SetInt(1, ((aValues.addCurrentSession)?1:0) | ((aValues.multiSelect)?2:0) | ((aValues.ignorable)?4:0) | ((aValues.allowNamedReplace)?256:0));
this.openWindow("chrome://sessionmanager/content/session_prompt.xul", "chrome,modal,centerscreen,titlebar,resizable", params, (this.mFullyLoaded)?window:null);
aValues.name = params.GetString(3);
aValues.text = params.GetString(6);
aValues.ignore = params.GetInt(1);
return !params.GetInt(0);
},
selectSession: function(aSessionLabel, aAcceptLabel, aValues)
{
var values = aValues || {};
if (this.prompt(aSessionLabel, aAcceptLabel, values))
{
return values.name;
}
return null;
},
ioError: function(aException)
{
this.mPromptService.alert(window, this.mTitle, (this.mBundle)?this.mBundle.getFormattedString("io_error", [(aException)?aException.message:this._string("unknown_error")]):aException);
},
openWindow: function(aChromeURL, aFeatures, aArgument, aParent)
{
if (!aArgument || typeof aArgument == "string")
{
var argString = Components.classes["@mozilla.org/supports-string;1"].createInstance(Components.interfaces.nsISupportsString);
argString.data = aArgument || "";
aArgument = argString;
}
return Components.classes["@mozilla.org/embedcomp/window-watcher;1"].getService(Components.interfaces.nsIWindowWatcher).openWindow(aParent || null, aChromeURL, "_blank", aFeatures, aArgument);
},
clearUndoListPrompt: function()
{
if (this.mPromptService.confirm(window, this.mTitle, this._string("clear_list_prompt")))
{
this.clearUndoList();
}
},
/* ........ File Handling .............. */
getProfileFile: function(aFileName)
{
var file = this.mProfileDirectory.clone();
file.append(aFileName);
return file;
},
getSessionDir: function(aFileName, aUnique)
{
var dir = this.getProfileFile("sessions");
if (!dir.exists())
{
dir.create(this.mComponents.interfaces.nsIFile.DIRECTORY_TYPE, 0700);
}
if (aFileName)
{
dir.append(aFileName);
if (aUnique)
{
var postfix = 1, ext = "";
if (aFileName.slice(-this.mSessionExt.length) == this.mSessionExt)
{
aFileName = aFileName.slice(0, -this.mSessionExt.length);
ext = this.mSessionExt;
}
while (dir.exists())
{
dir = dir.parent;
dir.append(aFileName + "-" + (++postfix) + ext);
}
}
}
return dir.QueryInterface(this.mComponents.interfaces.nsILocalFile);
},
getSessions: function()
{
var sessions = [];
var filesEnum = this.getSessionDir().directoryEntries.QueryInterface(this.mComponents.interfaces.nsISimpleEnumerator);
while (filesEnum.hasMoreElements())
{
var file = filesEnum.getNext().QueryInterface(this.mComponents.interfaces.nsIFile);
var fileName = file.leafName;
var cached = this.mSessionCache[fileName] || null;
if (cached && cached.time == file.lastModifiedTime)
{
sessions.push({ fileName: fileName, name: cached.name, timestamp: cached.timestamp });
continue;
}
if (/^\[SessionManager\]\n(?:name=(.*)\n)?(?:timestamp=(\d+))?/m.test(this.readSessionFile(file)))
{
var timestamp = parseInt(RegExp.$2) || file.lastModifiedTime;
sessions.push({ fileName: fileName, name: RegExp.$1, timestamp: timestamp });
this.mSessionCache[fileName] = { name: RegExp.$1, timestamp: timestamp, time: file.lastModifiedTime };
}
}
if (!this.mPref_session_list_order)
{
this.mPref_session_list_order = this.getPref("session_list_order", 1);
}
switch (Math.abs(this.mPref_session_list_order))
{
case 1: // alphabetically
sessions = sessions.sort(function(a, b) { return a.name.toLowerCase().localeCompare(b.name.toLowerCase()); });
break;
case 2: // chronologically
sessions = sessions.sort(function(a, b) { return a.timestamp - b.timestamp; });
break;
}
return (this.mPref_session_list_order < 0)?sessions.reverse():sessions;
},
getClosedWindows: function()
{
var data = this.readFile(this.getProfileFile(this.mClosedWindowFile));
return (data)?data.split("\n\n").map(function(aEntry) {
var parts = aEntry.split("\n");
return { name: parts.shift(), state: parts.join("\n") };
}):[];
},
storeClosedWindows: function(aList)
{
this.writeFile(this.getProfileFile(this.mClosedWindowFile), aList.map(function(aEntry) {
return aEntry.name + "\n" + aEntry.state;
}).join("\n\n"));
this.updateToolbarButton(aList.length + this.mClosedTabs.length > 0);
},
appendClosedWindow: function(aState)
{
if (this.mPref_max_closed_undo == 0 || Array.every(gBrowser.browsers, this.isCleanBrowser))
{
return;
}
var name = content.document.title || ((gBrowser.currentURI.spec != "about:blank")?gBrowser.currentURI.spec:this._string("untitled_window"));
var windows = this.getClosedWindows();
aState = aState.replace(/^\n+|\n+$/g, "").replace(/\n{2,}/g, "\n");
windows.unshift({ name: name, state: aState });
this.storeClosedWindows(windows.slice(0, this.mPref_max_closed_undo));
},
clearUndoData: function(aType, aSilent)
{
if (aType == "window" || aType == "all")
{
this.delFile(this.getProfileFile(this.mClosedWindowFile), aSilent);
}
if (aType == "tab" || aType == "all")
{
this.mClosedTabs = [];
}
if ((aType == "tab" || aType == "datatab" || aType == "all") && this.mDataTab)
{
this.mDataTab.removeAttribute(this.mDataTabAttribute);
this.mDataTab = null;
}
this.updateToolbarButton((aType == "all")?false:undefined);
},
backupCurrentSession: function()
{
var backup = this.getPref("backup_session", 1);
if (backup == 2 && !this.mPref__resume_session_once)
{
backup = (this.mPromptService.confirmEx(null, this.mTitle, this._string_preserve_session || this._string("preserve_session"), this.mPromptService.BUTTON_TITLE_YES * this.mPromptService.BUTTON_POS_0 + this.mPromptService.BUTTON_TITLE_NO * this.mPromptService.BUTTON_POS_1, null, null, null, null, {}) == 1)?-1:1;
}
if (backup > 0)
{
this.keepOldBackups();
try
{
var state = this.getSessionState(this._string_backup_session || this._string("backup_session"));
this.writeFile(this.getSessionDir(this.mBackupSessionName), state);
}
catch (ex)
{
this.ioError(ex);
}
}
else
{
this.setPref("_resume_session", this.doResumeCurrent());
this.setResumeCurrent(false);
this.delFile(this.getProfileFile("crashrecovery.dat"), true);
this.delFile(this.getProfileFile("crashrecovery.bak"), true);
this.delFile(this.getSessionDir(this.mBackupSessionName), true);
this.keepOldBackups();
}
},
keepOldBackups: function()
{
var backup = this.getSessionDir(this.mBackupSessionName);
if (backup.exists() && this.mPref_max_backup_keep)
{
var oldBackup = this.getSessionDir(this.mBackupSessionName, true);
var name = this.getFormattedName("", new Date(), this._string_old_backup_session || this._string("old_backup_session"));
this.writeFile(oldBackup, this.nameState(this.readSessionFile(backup), name));
}
if (this.mPref_max_backup_keep != -1)
{
this.getSessions().filter(function(aSession) {
return /^backup-\d+\.session$/.test(aSession.fileName);
}).sort(function(a, b) {
return b.timestamp - a.timestamp;
}).slice(this.mPref_max_backup_keep).forEach(function(aSession) {
this.delFile(this.getSessionDir(aSession.fileName), true);
}, this);
}
},
readSessionFile: function(aFile)
{
var state = this.readFile(aFile);
if (/^-?\d+.*~~~~/.test(state)) // old file format
{
if ("@mozilla.org/extensions/manager;1" in Components.classes)
{
var GUID = "{1280606b-2510-4fe0-97ef-9b5a22eafe30}";
var component = Components.classes["@mozilla.org/extensions/manager;1"].getService(Components.interfaces.nsIExtensionManager).getInstallLocation(GUID).getItemFile(GUID, "components/crashrecovery.js");
}
else // SeaMonkey
{
component = Components.classes["@mozilla.org/file/directory_service;1"].getService(Components.interfaces.nsIProperties).get("ComsD", Components.interfaces.nsILocalFile);
component.append("crashrecovery.js");
}
var context = {};
Components.classes["@mozilla.org/moz/jssubscript-loader;1"].getService(Components.interfaces.mozIJSSubScriptLoader).loadSubScript(this.mIOService.getProtocolHandler("file").QueryInterface(Components.interfaces.nsIFileProtocolHandler).getURLSpecFromFile(component), context);
state = state.replace(/^(.*? ~~~~)( .*)?/, "$1");
var name = RegExp.$2.substr(1) || this._string("untitled_window");
state = context.encodeINI(context.CR_ini_decodeOldFormat(state));
state = this.nameState(state.replace(/\n\[/g, "\n$&") + "\n", name);
this.writeFile(aFile, state);
}
return state;
},
readFile: function(aFile)
{
try
{
var stream = this.mComponents.classes["@mozilla.org/network/file-input-stream;1"].createInstance(this.mComponents.interfaces.nsIFileInputStream);
stream.init(aFile, 0x01, 0, 0);
var cvstream = this.mComponents.classes["@mozilla.org/intl/converter-input-stream;1"].createInstance(this.mComponents.interfaces.nsIConverterInputStream);
cvstream.init(stream, "UTF-8", 1024, this.mComponents.interfaces.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
var content = "";
var data = {};
while (cvstream.readString(4096, data))
{
content += data.value;
}
cvstream.close();
return content.replace(/\r\n?/g, "\n");
}
catch (ex) { }
return null;
},
writeFile: function(aFile, aData)
{
var stream = this.mComponents.classes["@mozilla.org/network/file-output-stream;1"].createInstance(this.mComponents.interfaces.nsIFileOutputStream);
stream.init(aFile, 0x02 | 0x08 | 0x20, 0600, 0);
var cvstream = this.mComponents.classes["@mozilla.org/intl/converter-output-stream;1"].createInstance(this.mComponents.interfaces.nsIConverterOutputStream);
cvstream.init(stream, "UTF-8", 0, this.mComponents.interfaces.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
cvstream.writeString(aData.replace(/\n/g, this.mEOL));
cvstream.flush();
cvstream.close();
},
delFile: function(aFile, aSilent)
{
if (aFile.exists())
{
try
{
aFile.remove(false);
}
catch (ex)
{
if (!aSilent)
{
this.ioError(ex);
}
}
}
},
/* ........ Preference Access .............. */
getPref: function(aName, aDefault, aUseRootBranch)
{
var pb = (aUseRootBranch)?this.mPrefRoot:this.mPrefBranch;
switch (pb.getPrefType(aName))
{
case pb.PREF_STRING:
return pb.getCharPref(aName);
case pb.PREF_BOOL:
return pb.getBoolPref(aName);
case pb.PREF_INT:
return pb.getIntPref(aName);
default:
return aDefault;
}
},
setPref: function(aName, aValue, aUseRootBranch)
{
var pb = (aUseRootBranch)?this.mPrefRoot:this.mPrefBranch;
switch (typeof aValue)
{
case "boolean":
pb.setBoolPref(aName, aValue);
break;
case "number":
pb.setIntPref(aName, parseInt(aValue));
break;
default:
pb.setCharPref(aName, "" + aValue);
break;
}
},
delPref: function(aName, aUseRootBranch)
{
((aUseRootBranch)?this.mPrefRoot:this.mPrefBranch).deleteBranch(aName);
},
/* ........ Miscellaneous Enhancements .............. */
recoverSession: function()
{
var recovering = this.getPref("_recovering");
var recoverOnly = recovering || this.mPref__running && !this.getPref("_hibernating") || this.doResumeCurrent() || this.getPref("_resume_session_once");
if (recovering)
{
this.delPref("_recovering");
this.load(recovering, "startup");
}
else if (!recoverOnly && this.mPref_resume_session && this.getSessions().length > 0)
{
var values = { ignorable: true };
var session = (this.mPref_resume_session == this.mPromptSessionName)?this.selectSession(this._string("resume_session"), this._string("resume_session_ok"), values):this.mPref_resume_session;
if (session && this.getSessionDir(session).exists())
{
this.load(session, "startup");
}
if (values.ignore)
{
this.setPref("resume_session", session || "");
}
}
this.delPref("_hibernating");
this.delPref("_resume_session_once");
},
isCmdLineEmpty: function()
{
var homepage = null;
switch (this.getPref("browser.startup.page", 1, true))
{
case 0:
homepage = "about:blank";
break;
case 1:
try
{
homepage = this.mPrefRoot.getComplexValue("browser.startup.homepage", Components.interfaces.nsIPrefLocalizedString).data;
}
catch (ex)
{
homepage = this.getPref("browser.startup.homepage", "", true);
}
break;
}
if (window.arguments.length > 0 && window.arguments[0].split("\n")[0] == homepage)
{
window.arguments.shift();
}
return (window.arguments.length == 0);
},
patchTabBrowser: function()
{
eval("gBrowser.removeTab = " + gBrowser.removeTab.toString().replace(/\{/, '$& var $$SM_event = document.createEvent("Events"); $$SM_event.initEvent("SMTabClosing", true, false); ((aTab.localName != "tab")?this.mCurrentTab:aTab).dispatchEvent($$SM_event);'));
if (!gBrowser.undoRemoveTab) // tweak for Tab Clicking Options
{
gBrowser.undoRemoveTab = function() { gSessionManager.undoCloseTab(); };
}
},
updateToolbarButton: function(aEnable)
{
var button = (document)?document.getElementById("sessionmanager-undo"):null;
if (button)
{
this.setDisabled(button, (aEnable != undefined)?!aEnable:this.mClosedTabs.length == 0 && this.getClosedWindows().length == 0);
}
},
appendClosedTab: function(aState, aPosition, aTitle, aNewDataTab)
{
var oneTabRE = /\n\[Window(1\.Tab\d+)\]\n[\s\S]*?(?=\n\[Window(?!\1)|$)/;
var tabData = aState.substr(aState.indexOf("\n[Window1.Tab" + aPosition + "]")).match(oneTabRE)[0].replace(/^(\[Window1\.Tab)\d+/gm, "$11");
this.mClosedTabs.unshift({ name: aTitle, pos: aPosition, state: tabData });
this.mClosedTabs.splice(this.mPref_max_tabs_undo);
this.persistClosedTabList(aNewDataTab);
this.updateToolbarButton(true);
},
persistClosedTabList: function(aNewDataTab)
{
if (!this.mDataTab && this.mPref_save_closed_tabs)
{
this.mDataTab = aNewDataTab || gBrowser.selectedTab;
}
if (this.mDataTab)
{
var serializedTabs = this.mClosedTabs.map(function(aTabData) {
return aTabData.pos + " " + aTabData.name + "\n" + aTabData.state;
}).join("\f\f");
this.mDataTab.setAttribute(this.mDataTabAttribute, serializedTabs);
}
},
/* ........ Auxiliary Functions .............. */
getSessionState: function(aName, aOneWindow, aNoUndoData)
{
var state = (aOneWindow)?this.mCrashRecoveryService.getWindowState(window):this.mCrashRecoveryService.getCurrentState();
if (aNoUndoData)
{
state = this.stripTabUndoData(state);
}
return (aName != null)?this.nameState(("[SessionManager]\nname=" + (new Date()).toString() + "\ntimestamp=" + Date.now() + "\n" + state + "\n").replace(/\n\[/g, "\n$&"), aName || ""):state;
},
restoreSession: function(aWindow, aState, aReplaceTabs, aAllowReload)
{
if (!aWindow)
{
aWindow = this.openWindow(this.getPref("browser.chromeURL", null, true), "chrome,dialog=no,all");
aWindow.__SM_restore = function() {
this.removeEventListener("load", this.__SM_restore, true);
this.gSessionManager.restoreSession(this, aState, aReplaceTabs, aAllowReload);
delete this.__SM_restore;
};
aWindow.addEventListener("load", aWindow.__SM_restore, true);
return;
}
if (aReplaceTabs)
{
this.clearUndoData("tab");
if (aState.indexOf("\n[Window2]\n") == -1 && this.getBrowserWindows().length < 2)
{
aState = aState.replace(/^(\[Window1\]\n([^\[\s].*\n)*)hidden=.*\n/m, "$1");
}
}
this._allowReload = aAllowReload;
this._ignoreRemovedTabs = true;
this.mCrashRecoveryService.restoreWindow(aWindow || window, aState, aReplaceTabs || false);
this._ignoreRemovedTabs = false;
},
nameState: function(aState, aName)
{
if (!/^\[SessionManager\]/m.test(aState))
{
return "[SessionManager]\nname=" + aName + "\n\n" + aState;
}
return aState.replace(/^(\[SessionManager\])(?:\nname=.*)?/m, function($0, $1) { return $1 + "\nname=" + aName; });
},
stripTabUndoData: function(aState)
{
return aState.replace(new RegExp("^(xultab=)((?:\\S+ )*)" + this.mDataTabAttribute + "=\\S+( )?", "gm"), function($0, $1, $2, $3) { return $1 + ($2 + $3).slice(0, -1); });
},
getFormattedName: function(aTitle, aDate, aFormat)
{
function cut(aString, aLength)
{
return aString.replace(new RegExp("^(.{" + (aLength - 3) + "}).{4,}$"), "$1...");
}
function toISO8601(aDate)
{
return [aDate.getFullYear(), pad2(aDate.getMonth() + 1), pad2(aDate.getDate())].join("-");
}
function pad2(a) { return (a < 10)?"0" + a:a; }
return (aFormat || this.mPref_name_format).split("%%").map(function(aPiece) {
return aPiece.replace(/%(\d*)([tdm])/g, function($0, $1, $2) {
$0 = ($2 == "t")?aTitle:($2 == "d")?toISO8601(aDate):pad2(aDate.getHours()) + ":" + pad2(aDate.getMinutes());
return ($1)?cut($0, Math.max(parseInt($1), 3)):$0;
});
}).join("%");
},
makeFileName: function(aString)
{
return aString.replace(/[^\w ',;!()@&*+=~\x80-\xFE-]/g, "_").substr(0, 64) + this.mSessionExt;
},
getBrowserWindows: function()
{
var windowsEnum = this.mWindowMediator.getEnumerator("navigator:browser");
var windows = [];
while (windowsEnum.hasMoreElements())
{
windows.push(windowsEnum.getNext());
}
return windows;
},
doResumeCurrent: function(aOnce)
{
return this.getPref("extensions.crashrecovery.resume_session" + ((aOnce)?"_once":""), false, true);
},
setResumeCurrent: function(aValue)
{
this.setPref("extensions.crashrecovery.resume_session", aValue, true);
},
isCleanBrowser: function(aBrowser)
{
return aBrowser.sessionHistory.count < 2 && aBrowser.currentURI.spec == "about:blank";
},
setDisabled: function(aObj, aValue)
{
if (aValue)
{
aObj.setAttribute("disabled", "true");
}
else
{
aObj.removeAttribute("disabled");
}
},
getEOL: function()
{
return /win|os[\/_]?2/i.test(navigator.platform)?"\r\n":/mac/i.test(navigator.platform)?"\r":"\n";
},
_string: function(aName)
{
return this.mBundle.getString(aName);
}
};
window.addEventListener("load", gSessionManager.onLoad_proxy, false);
window.addEventListener("unload", gSessionManager.onUnload_proxy, false);
window.addEventListener("load", function() {
if (!window.SessionManager) // if Tab Mix Plus isn't installed
{
window.SessionManager = gSessionManager;
}
if (typeof tabBarScrollStatus == "function") // hack for some Tab Mix Plus startup issue
{
setTimeout(tabBarScrollStatus, 0);
}
}, false);