Bug 459550 - Port Bug 448976 (turn the Session Restore prompt into an error page) to SeaMonkey. r+sr=Neil, a=wanted2+.

This commit is contained in:
Misak Khachatryan 2009-02-13 19:51:08 +01:00
Родитель c8f621edc5
Коммит 1158298990
18 изменённых файлов: 943 добавлений и 202 удалений

Просмотреть файл

@ -196,7 +196,6 @@ pref("browser.tabs.opentabfor.middleclick", false);
pref("browser.tabs.opentabfor.urlbar", false);
pref("browser.tabs.tooltippreview.enable", true);
pref("browser.tabs.tooltippreview.width", 300);
pref("browser.tabs.undoclose.depth", 3);
pref("browser.tabs.autoHide", true);
pref("browser.tabs.forceHide", false);
pref("browser.tabs.warnOnClose", true);
@ -321,6 +320,11 @@ pref("browser.sessionstore.postdata", 0);
// on which sites to save text data, POSTDATA and cookies
// 0 = everywhere, 1 = unencrypted sites, 2 = nowhere
pref("browser.sessionstore.privacy_level", 1);
// number of crashes that can occur before the about:sessionrestore page is displayed
// (this pref has no effect if more than 6 hours have passed since the last crash)
pref("browser.sessionstore.max_resumed_crashes", 1);
// how many tabs can be reopened (per window)
pref("browser.sessionstore.max_tabs_undo", 10);
pref("shell.checkDefaultClient", true);
// We want to check if we are the default client for browser and mail. See

Просмотреть файл

@ -744,7 +744,7 @@
var undoItem = document.getAnonymousElementByAttribute(this, "tbattr", "tabbrowser-undoclosetab");
undoItem.setAttribute("disabled", this.savedBrowsers.length == 0);
undoItem.hidden = this.mPrefs.getIntPref("browser.tabs.undoclose.depth") <= 0;
undoItem.hidden = this.mPrefs.getIntPref("browser.sessionstore.max_tabs_undo") <= 0;
]]>
</body>
</method>
@ -1425,7 +1425,7 @@
// Save the tab for undo.
// Even though we navigate to about:blank, it costs more RAM than
// really closing the tab. The pref controls how far you can undo
var maxUndoDepth = this.mPrefs.getIntPref("browser.tabs.undoclose.depth");
var maxUndoDepth = this.mPrefs.getIntPref("browser.sessionstore.max_tabs_undo");
var oldSH = oldBrowser.webNavigation.sessionHistory;
var inOnLoad = oldBrowser.docShell.isExecutingOnLoadHandler;
if (maxUndoDepth <= 0 || oldSH.count == 0 || aDisableUndo || inOnLoad) {

Просмотреть файл

@ -0,0 +1,328 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the nsSessionStore component.
*
* The Initial Developer of the Original Code is
* Simon Bünzli <zeniko@gmail.com>
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
var gStateObject;
var gTreeData;
// Page initialization
window.onload = function() {
// the crashed session state is kept inside a textbox so that SessionStore picks it up
// (for when the tab is closed or the session crashes right again)
var sessionData = document.getElementById("sessionData");
if (!sessionData.value) {
var ss = Components.classes["@mozilla.org/suite/sessionstartup;1"].getService(Components.interfaces.nsISessionStartup);
sessionData.value = ss.state;
if (!sessionData.value) {
document.getElementById("errorTryAgain").disabled = true;
return;
}
}
// make sure the data is tracked to be restored in case of a subsequent crash
var event = document.createEvent("UIEvents");
event.initUIEvent("input", true, true, window, 0);
sessionData.dispatchEvent(event);
var s = new Components.utils.Sandbox("about:blank");
gStateObject = Components.utils.evalInSandbox("(" + sessionData.value + ")", s);
initTreeView();
document.getElementById("errorTryAgain").focus();
};
function initTreeView() {
var tabList = document.getElementById("tabList");
var winLabel = tabList.getAttribute("_window_label");
gTreeData = [];
gStateObject.windows.forEach(function(aWinData, aIx) {
var winState = {
label: winLabel.replace("%S", (aIx + 1)),
open: true,
checked: true,
ix: aIx
};
winState.tabs = aWinData.tabs.map(function(aTabData) {
var entry = aTabData.entries[aTabData.index - 1] || { url: "about:blank" };
var iconURL = aTabData.attributes && aTabData.attributes.image || null;
// don't initiate a connection just to fetch a favicon (see bug 462863)
if (/^https?:/.test(iconURL))
iconURL = "moz-anno:favicon:" + iconURL;
return {
label: entry.title || entry.url,
checked: true,
src: iconURL,
parent: winState
};
});
gTreeData.push(winState);
for each (var tab in winState.tabs)
gTreeData.push(tab);
}, this);
tabList.view = treeView;
tabList.view.selection.select(0);
}
// User actions
function restoreSession() {
document.getElementById("errorTryAgain").disabled = true;
// remove all unselected tabs from the state before restoring it
var ix = gStateObject.windows.length - 1;
for (var t = gTreeData.length - 1; t >= 0; t--) {
if (treeView.isContainer(t)) {
if (gTreeData[t].checked === 0)
// this window will be restored partially
gStateObject.windows[ix].tabs =
gStateObject.windows[ix].tabs.filter(function(aTabData, aIx)
gTreeData[t].tabs[aIx].checked);
else if (!gTreeData[t].checked)
// this window won't be restored at all
gStateObject.windows.splice(ix, 1);
ix--;
}
}
var stateString = gStateObject.toSource();
var ss = Components.classes["@mozilla.org/suite/sessionstore;1"].getService(Components.interfaces.nsISessionStore);
var top = getBrowserWindow();
// if there's only this page open, reuse the window for restoring the session
if (top.gBrowser.tabContainer.childNodes.length == 1) {
ss.setWindowState(top, stateString, true);
return;
}
// restore the session into a new window and close the current tab
var newWindow = top.openDialog(top.location, "_blank", "chrome,dialog=no,all", "about:blank");
var tab = top.gBrowser.selectedTab;
newWindow.addEventListener("load", function() {
newWindow.removeEventListener("load", arguments.callee, true);
ss.setWindowState(newWindow, stateString, true);
top.gBrowser.removeTab(tab);
}, true);
}
function startNewSession() {
getBrowserWindow().BrowserHome();
}
function onListClick(aEvent) {
// don't react to right-clicks
if (aEvent.button == 2)
return;
var row = {}, col = {};
treeView.treeBox.getCellAt(aEvent.clientX, aEvent.clientY, row, col, {});
if (col.value) {
// restore this specific tab in the same window for middle-clicking
// or Ctrl+clicking on a tab's title
if ((aEvent.button == 1 || aEvent.ctrlKey) && col.value.id == "title" &&
!treeView.isContainer(row.value))
restoreSingleTab(row.value, aEvent.shiftKey);
else if (col.value.id == "restore")
toggleRowChecked(row.value);
}
}
function onListKeyDown(aEvent) {
switch (aEvent.keyCode)
{
case KeyEvent.DOM_VK_SPACE:
toggleRowChecked(document.getElementById("tabList").currentIndex);
break;
case KeyEvent.DOM_VK_RETURN:
var ix = document.getElementById("tabList").currentIndex;
if (aEvent.ctrlKey && !treeView.isContainer(ix))
restoreSingleTab(ix, aEvent.shiftKey);
break;
case KeyEvent.DOM_VK_UP:
case KeyEvent.DOM_VK_DOWN:
case KeyEvent.DOM_VK_PAGE_UP:
case KeyEvent.DOM_VK_PAGE_DOWN:
case KeyEvent.DOM_VK_HOME:
case KeyEvent.DOM_VK_END:
aEvent.preventDefault(); // else the page scrolls unwantedly
break;
}
}
// Helper functions
function getBrowserWindow() {
return window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIWebNavigation)
.QueryInterface(Components.interfaces.nsIDocShellTreeItem).rootTreeItem
.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIDOMWindow);
}
function toggleRowChecked(aIx) {
var item = gTreeData[aIx];
item.checked = !item.checked;
treeView.treeBox.invalidateRow(aIx);
function isChecked(aItem) aItem.checked;
if (treeView.isContainer(aIx)) {
// (un)check all tabs of this window as well
for each (var tab in item.tabs) {
tab.checked = item.checked;
treeView.treeBox.invalidateRow(gTreeData.indexOf(tab));
}
}
else {
// update the window's checkmark as well (0 means "partially checked")
item.parent.checked = item.parent.tabs.every(isChecked) ? true :
item.parent.tabs.some(isChecked) ? 0 : false;
treeView.treeBox.invalidateRow(gTreeData.indexOf(item.parent));
}
document.getElementById("errorTryAgain").disabled = !gTreeData.some(isChecked);
}
function restoreSingleTab(aIx, aShifted) {
var tabbrowser = getBrowserWindow().gBrowser;
var newTab = tabbrowser.addTab();
var item = gTreeData[aIx];
var ss = Components.classes["@mozilla.org/suite/sessionstore;1"].getService(Components.interfaces.nsISessionStore);
var tabState = gStateObject.windows[item.parent.ix]
.tabs[aIx - gTreeData.indexOf(item.parent) - 1];
ss.setTabState(newTab, tabState.toSource());
// respect the preference as to whether to select the tab (the Shift key inverses)
var prefBranch = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch);
if (prefBranch.getBoolPref("browser.tabs.loadInBackground") != !aShifted)
tabbrowser.selectedTab = newTab;
}
// Tree controller
var treeView = {
_atoms: {},
_getAtom: function(aName)
{
if (!this._atoms[aName]) {
var as = Components.classes["@mozilla.org/atom-service;1"].getService(Components.interfaces.nsIAtomService);
this._atoms[aName] = as.getAtom(aName);
}
return this._atoms[aName];
},
treeBox: null,
selection: null,
get rowCount() { return gTreeData.length; },
setTree: function(treeBox) { this.treeBox = treeBox; },
getCellText: function(idx, column) { return gTreeData[idx].label; },
isContainer: function(idx) { return "open" in gTreeData[idx]; },
getCellValue: function(idx, column){ return gTreeData[idx].checked; },
isContainerOpen: function(idx) { return gTreeData[idx].open; },
isContainerEmpty: function(idx) { return false; },
isSeparator: function(idx) { return false; },
isSorted: function() { return false; },
isEditable: function(idx, column) { return false; },
getLevel: function(idx) { return this.isContainer(idx) ? 0 : 1; },
getParentIndex: function(idx) {
if (!this.isContainer(idx))
for (var t = idx - 1; t >= 0 ; t--)
if (this.isContainer(t))
return t;
return -1;
},
hasNextSibling: function(idx, after) {
var thisLevel = this.getLevel(idx);
for (var t = after + 1; t < gTreeData.length; t++)
if (this.getLevel(t) <= thisLevel)
return this.getLevel(t) == thisLevel;
return false;
},
toggleOpenState: function(idx) {
if (!this.isContainer(idx))
return;
var item = gTreeData[idx];
if (item.open) {
// remove this window's tab rows from the view
var thisLevel = this.getLevel(idx);
for (var t = idx + 1; t < gTreeData.length && this.getLevel(t) > thisLevel; t++);
var deletecount = t - idx - 1;
gTreeData.splice(idx + 1, deletecount);
this.treeBox.rowCountChanged(idx + 1, -deletecount);
}
else {
// add this window's tab rows to the view
var toinsert = gTreeData[idx].tabs;
for (var i = 0; i < toinsert.length; i++)
gTreeData.splice(idx + i + 1, 0, toinsert[i]);
this.treeBox.rowCountChanged(idx + 1, toinsert.length);
}
item.open = !item.open;
this.treeBox.invalidateRow(idx);
},
getCellProperties: function(idx, column, prop) {
if (column.id == "restore" && this.isContainer(idx) && gTreeData[idx].checked === 0)
prop.AppendElement(this._getAtom("partial"));
if (column.id == "title")
prop.AppendElement(this._getAtom(this.getImageSrc(idx, column) ? "icon" : "noicon"));
},
getRowProperties: function(idx, prop) {
var winState = gTreeData[idx].parent || gTreeData[idx];
if (winState.ix % 2 != 0)
prop.AppendElement(this._getAtom("alternate"));
},
getImageSrc: function(idx, column) {
if (column.id == "title")
return gTreeData[idx].src || null;
return null;
},
getProgressMode : function(idx, column) { },
cycleHeader: function(column) { },
cycleCell: function(idx, column) { },
selectionChanged: function() { },
performAction: function(action) { },
performActionOnCell: function(action, index, column) { },
getColumnProperties: function(column, prop) { }
};

Просмотреть файл

@ -0,0 +1,119 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is the nsSessionStore component.
#
# The Initial Developer of the Original Code is
# Simon Bünzli <zeniko@gmail.com>
# Portions created by the Initial Developer are Copyright (C) 2008
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
-->
<!DOCTYPE html [
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
%brandDTD;
<!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
%htmlDTD;
<!ENTITY % netErrorDTD SYSTEM "chrome://global/locale/netError.dtd">
%netErrorDTD;
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
%globalDTD;
<!ENTITY % restorepageDTD SYSTEM "chrome://communicator/locale/aboutSessionRestore.dtd">
%restorepageDTD;
]>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>&restorepage.tabtitle;</title>
<link rel="stylesheet" href="chrome://global/skin/netError.css" type="text/css" media="all"/>
<link rel="stylesheet" href="chrome://communicator/skin/aboutSessionRestore.css" type="text/css" media="all"/>
<link rel="icon" type="image/png" href="chrome://global/skin/icons/question-16.png"/>
<script type="application/javascript" src="chrome://communicator/content/aboutSessionRestore.js"/>
</head>
<body dir="&locale.dir;">
<!-- PAGE CONTAINER (for styling purposes only) -->
<div id="errorPageContainer">
<!-- Error Title -->
<div id="errorTitle">
<h1 id="errorTitleText">&restorepage.pagetitle;</h1>
</div>
<!-- LONG CONTENT (the section most likely to require scrolling) -->
<div id="errorLongContent">
<!-- Short Description -->
<div id="errorShortDesc">
<p id="errorShortDescText">&restorepage.issueDesc;</p>
</div>
<!-- Long Description (Note: See netError.dtd for used XHTML tags) -->
<div id="errorLongDesc">
<p>&restorepage.remedies;</p>
<ul>
<li>&restorepage.dueToChrome;</li>
<li>&restorepage.dueToContent;</li>
</ul>
</div>
<!-- Short Description -->
<div id="errorTrailerDesc">
<tree xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
id="tabList" flex="1" seltype="single" hidecolumnpicker="true"
onclick="onListClick(event);" onkeydown="onListKeyDown(event);"
_window_label="&restorepage.windowLabel;">
<treecols>
<treecol id="restore" type="checkbox" label="&restorepage.restoreHeader;"/>
<splitter class="tree-splitter"/>
<treecol primary="true" id="title" label="&restorepage.listHeader;" flex="1"/>
</treecols>
<treechildren flex="1"/>
</tree>
</div>
</div>
<!-- Buttons -->
<hbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="buttons">
<button id="errorTryAgain" label="&restorepage.restoreButton;"
accesskey="&restorepage.restore.access;"
oncommand="restoreSession();"/>
<button id="errorCancel" label="&restorepage.cancelButton;"
accesskey="&restorepage.cancel.access;"
oncommand="startNewSession();"/>
</hbox>
<!-- holds the session data for when the tab is closed -->
<input type="text" id="sessionData" style="display: none;"/>
</div>
</body>
</html>

Просмотреть файл

@ -46,6 +46,8 @@ comm.jar:
% overlay chrome://messenger-smime/content/msgReadSecurityInfo.xul chrome://communicator/content/helpMessengerOverlay.xul
% content communicator-platform %content/communicator-platform/ platform xpcnativewrappers=yes
% content communicator-region %content/communicator-region/ xpcnativewrappers=yes
content/communicator/aboutSessionRestore.js
content/communicator/aboutSessionRestore.xhtml
content/communicator/askViewZoom.xul
content/communicator/askViewZoom.js
content/communicator/browserBindings.xul

Просмотреть файл

@ -46,6 +46,7 @@ MODULE = suitecommon
EXTRA_COMPONENTS = \
nsAboutCertError.js \
nsAboutSessionRestore.js \
nsSessionStartup.js \
nsSessionStore.js \
nsSuiteGlue.js \

Просмотреть файл

@ -0,0 +1,61 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the nsSessionStore component.
*
* The Initial Developer of the Original Code is
* Simon Bünzli <zeniko@gmail.com>
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
function AboutSessionRestore() { }
AboutSessionRestore.prototype = {
classDescription: "about:sessionrestore",
contractID: "@mozilla.org/network/protocol/about;1?what=sessionrestore",
classID: Components.ID("{a03c813e-abe8-41de-8d0c-5aa85f877696}"),
QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsIAboutModule]),
getURIFlags: function(aURI) {
return Components.interfaces.nsIAboutModule.ALLOW_SCRIPT;
},
newChannel: function(aURI) {
let ios = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
let channel = ios.newChannel("chrome://communicator/content/aboutSessionRestore.xhtml",
null, null);
channel.originalURI = aURI;
return channel;
}
};
function NSGetModule(compMgr, fileSpec) {
return XPCOMUtils.generateModule([AboutSessionRestore]);
}

Просмотреть файл

@ -90,9 +90,9 @@ SessionStartup.prototype = {
* Initialize the component
*/
init: function sss_init() {
this._prefBranch = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefService)
.getBranch("browser.");
let prefBranch = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefService)
.getBranch("browser.");
// get file references
var dirService = Components.classes["@mozilla.org/file/directory_service;1"]
@ -100,11 +100,11 @@ SessionStartup.prototype = {
let sessionFile = dirService.get("ProfD", Components.interfaces.nsILocalFile);
sessionFile.append("sessionstore.js");
let doResumeSession = this._prefBranch.getBoolPref("sessionstore.resume_session_once") ||
this._prefBranch.getIntPref("startup.page") == 3;
let doResumeSession = prefBranch.getBoolPref("sessionstore.resume_session_once") ||
prefBranch.getIntPref("startup.page") == 3;
// only read the session file if config allows possibility of restoring
var resumeFromCrash = this._prefBranch.getBoolPref("sessionstore.resume_from_crash");
var resumeFromCrash = prefBranch.getBoolPref("sessionstore.resume_from_crash");
if ((!resumeFromCrash && !doResumeSession) || !sessionFile.exists())
return;
@ -130,7 +130,7 @@ SessionStartup.prototype = {
initialState.session.state == STATE_RUNNING_STR;
// set the startup type
if (lastSessionCrashed && resumeFromCrash && this._doRecoverSession())
if (lastSessionCrashed && resumeFromCrash)
this._sessionType = Components.interfaces.nsISessionStartup.RECOVER_SESSION;
else if (!lastSessionCrashed && doResumeSession)
this._sessionType = Components.interfaces.nsISessionStartup.RESUME_SESSION;
@ -201,78 +201,6 @@ SessionStartup.prototype = {
return this._sessionType;
},
/* ........ Auxiliary Functions .............. */
/**
* prompt user whether or not to restore the previous session,
* if the browser crashed
* @returns bool
*/
_doRecoverSession: function sss_doRecoverSession() {
// if the prompt fails, recover anyway
var recover = true;
// allow extensions to hook in a more elaborate restore prompt
// XXXzeniko drop this when we're using our own dialog instead of a standard prompt
var dialogURI = null;
try {
dialogURI = this._prefBranch.getCharPref("sessionstore.restore_prompt_uri");
}
catch (ex) { }
try {
if (dialogURI) { // extension provided dialog
var params = Components.classes["@mozilla.org/embedcomp/dialogparam;1"]
.createInstance(Components.interfaces.nsIDialogParamBlock);
// default to recovering
params.SetInt(0, 0);
Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
.getService(Components.interfaces.nsIWindowWatcher)
.openWindow(null, dialogURI, "_blank", "chrome,modal,centerscreen,titlebar", params);
recover = params.GetInt(0) == 0;
}
else { // basic prompt with no options
// get app name from branding properties
var brandStringBundle = this._getStringBundle("chrome://branding/locale/brand.properties");
var brandShortName = brandStringBundle.GetStringFromName("brandShortName");
// create prompt strings
var ssStringBundle = this._getStringBundle("chrome://communicator/locale/sessionstore.properties");
var restoreTitle = ssStringBundle.formatStringFromName("restoredTitle", [brandShortName], 1);
var restoreText = ssStringBundle.formatStringFromName("restoredMsg", [brandShortName], 1);
var okTitle = ssStringBundle.GetStringFromName("okTitle");
var cancelTitle = ssStringBundle.GetStringFromName("cancelTitle");
var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
.getService(Components.interfaces.nsIPromptService);
// set the buttons that will appear on the dialog
var flags = promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_0 +
promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_1 +
promptService.BUTTON_POS_0_DEFAULT;
var buttonChoice = promptService.confirmEx(null, restoreTitle, restoreText,
flags, okTitle, cancelTitle, null,
null, {});
recover = (buttonChoice == 0);
}
}
catch (ex) { dump(ex + "\n"); } // if the prompt fails, recover anyway
return recover;
},
/**
* Convenience method to get localized string bundles
* @param aURI
* @returns nsIStringBundle
*/
_getStringBundle: function sss_getStringBundle(aURI) {
var bundleService = Components.classes["@mozilla.org/intl/stringbundle;1"]
.getService(Components.interfaces.nsIStringBundleService);
var appLocale = Components.classes["@mozilla.org/intl/nslocaleservice;1"]
.getService(Components.interfaces.nsILocaleService).getApplicationLocale();
return bundleService.createBundle(aURI, appLocale);
},
/* ........ Storage API .............. */
/**

Просмотреть файл

@ -20,6 +20,7 @@
*
* Contributor(s):
* Dietrich Ayala <dietrich@mozilla.com>
* Ehsan Akhgari <ehsan.akhgari@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -93,8 +94,6 @@ const CAPABILITIES = [
"Subframes", "Plugins", "Javascript", "MetaRedirects", "Images"
];
// module for JSON conversion (needed for the nsISessionStore API)
Components.utils.import("resource://gre/modules/JSON.jsm");
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
function debug(aMsg) {
@ -117,8 +116,9 @@ SessionStoreService.prototype = {
Components.interfaces.nsIObserver,
Components.interfaces.nsISupportsWeakReference]),
// xul:tab attributes to (re)store (extensions might want to hook in here)
xulAttributes: [],
// xul:tab attributes to (re)store (extensions might want to hook in here);
// the favicon is always saved for the about:sessionrestore page
xulAttributes: ["image"],
// set default load state
_loadState: STATE_STOPPED,
@ -145,6 +145,12 @@ SessionStoreService.prototype = {
// not-"dirty" windows usually don't need to have their data updated
_dirtyWindows: {},
// collection of session states yet to be restored
_statesToRestore: {},
// counts the number of crashes since the last clean start
_recentCrashes: 0,
/* ........ Global Event Handlers .............. */
/**
@ -179,9 +185,9 @@ SessionStoreService.prototype = {
this._resume_from_crash = this._prefBranch.getBoolPref("sessionstore.resume_from_crash");
this._prefBranch.addObserver("sessionstore.resume_from_crash", this, true);
// observe prefs changes so we can modify stored data to match
this._prefBranch.addObserver("tabs.undoclose.depth", this, true);
this._prefBranch.addObserver("sessionstore.max_tabs_undo", this, true);
// this pref is only read at startup, so no need to observe it
this._sessionhistory_max_entries =
this._prefBranch.getIntPref("sessionhistory.max_entries");
// get file references
var dirService = Components.classes["@mozilla.org/file/directory_service;1"]
@ -211,6 +217,20 @@ SessionStoreService.prototype = {
let lastSessionCrashed =
this._initialState && this._initialState.session && this._initialState.session.state &&
this._initialState.session.state == STATE_RUNNING_STR;
// if last session crashed, backup the session
if (lastSessionCrashed) {
this._recentCrashes = (this._initialState.session &&
this._initialState.session.recentCrashes || 0) + 1;
if (this._needsRestorePage(this._initialState, this._recentCrashes)) {
// replace the crashed session with a restore-page-only session
let pageData = {
url: "about:sessionrestore",
formdata: { "#sessionData": iniString }
};
this._initialState = { windows: [{ tabs: [{ entries: [pageData] }] }] };
}
}
// make sure that at least the first window doesn't have anything hidden
if (this._initialState.windows[0])
delete this._initialState.windows[0].hidden;
@ -324,18 +344,6 @@ SessionStoreService.prototype = {
break;
case "nsPref:changed": // catch pref changes
switch (aData) {
// if the user decreases the max number of closed tabs they want
// preserved update our internal states to match that max
// misak: this shouldn't be needed, will remove in final patch
//case "tabs.undoclose.depth":
// var ix;
// for (ix in this._windows) {
// this._windows[ix]._closedTabs.splice(this._prefBranch.getIntPref("tabs.undoclose.depth"));
// }
// break;
case "sessionstore.max_tabs_undo":
//todo: we need some way to syncronise sessionstore.max_tabs_undo and tabs.undoclose.depth
break;
case "sessionstore.interval":
this._interval = this._prefBranch.getIntPref("sessionstore.interval");
// reset timer and save
@ -433,10 +441,6 @@ SessionStoreService.prototype = {
this._loadState = STATE_RUNNING;
this._lastSaveTime = Date.now();
// don't save during the first ten seconds
// (until most of the pages have been restored)
this.saveStateDelayed(aWindow, 10000);
// restore a crashed session resp. resume the last session if requested
if (this._initialState) {
// make sure that the restored tabs are first in the window
@ -444,14 +448,25 @@ SessionStoreService.prototype = {
this._restoreCount = this._initialState.windows ? this._initialState.windows.length : 0;
this.restoreWindow(aWindow, this._initialState, this._isCmdLineEmpty(aWindow));
delete this._initialState;
// mark ourselves as running
this.saveState(true);
}
else {
// Nothing to restore, notify observers things are complete.
var observerService = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
observerService.notifyObservers(null, NOTIFY_WINDOWS_RESTORED, "");
// the next delayed save request should execute immediately
this._lastSaveTime -= this._interval;
}
}
// this window was opened by _openWindowWithState
else if (!this._isWindowLoaded(aWindow)) {
let followUp = this._statesToRestore[aWindow.__SS_restoreID].windows.length == 1;
this.restoreWindow(aWindow, this._statesToRestore[aWindow.__SS_restoreID], true, followUp);
}
var tabbrowser = aWindow.getBrowser();
var tabpanels = tabbrowser.mPanelContainer;
@ -474,6 +489,16 @@ SessionStoreService.prototype = {
* Window reference
*/
onClose: function sss_onClose(aWindow) {
// this window was about to be restored - conserve its original data, if any
let isFullyLoaded = this._isWindowLoaded(aWindow);
if (!isFullyLoaded) {
if (!aWindow.__SSi)
aWindow.__SSi = "window" + Date.now();
this._window[aWindow.__SSi] = this._statesToRestore[aWindow.__SS_restoreID];
delete this._statesToRestore[aWindow.__SS_restoreID];
delete aWindow.__SS_restoreID;
}
// ignore windows not tracked by SessionStore
if (!aWindow.__SSi || !this._windows[aWindow.__SSi]) {
return;
@ -490,22 +515,23 @@ SessionStoreService.prototype = {
tabbrowser.removeEventListener("TabClose", this, true);
tabbrowser.removeEventListener("TabSelect", this, true);
let winData = this._windows[aWindow.__SSi];
windata._closedTabs = tabbrowser.savedBrowsers.map(function(e) { return e.tabData; });
if (this._loadState == STATE_RUNNING) { // window not closed during a regular shut-down
// update all window data for a last time
this._collectWindowData(aWindow);
// preserve this window's data (in case it was the last navigator:browser)
var winData = this._windows[aWindow.__SSi];
winData.title = aWindow.content.document.title;
winData._closedTabs = tabbrowser.savedBrowsers.map(function(e) { return e.tabData; });
// if this is a popup window, append it to what we've already got (cf. bug 368677)
if (!this._lastClosedWindows || !winData.isPopup)
this._lastClosedWindows = [winData];
else
this._lastClosedWindows.push(winData);
this._updateCookies(this._lastClosedWindows);
if (isFullyLoaded) {
winData.title = aWindow.content.document.title;
this._updateCookies(this._lastClosedWindows);
}
// clear this window from the list
delete this._windows[aWindow.__SSi];
@ -519,14 +545,7 @@ SessionStoreService.prototype = {
}
// cache the window state until the window is completely gone
aWindow.__SS_dyingCache = this._windows[aWindow.__SSi] || winData;
// reset the _tab property to avoid keeping the tab's XUL element alive
// longer than we need it
var tabCount = aWindow.__SS_dyingCache.tabs.length;
for (var t = 0; t < tabCount; t++) {
delete aWindow.__SS_dyingCache.tabs[t]._tab;
}
aWindow.__SS_dyingCache = winData;
delete aWindow.__SSi;
},
@ -591,7 +610,7 @@ SessionStoreService.prototype = {
event.initEvent("SSTabClosing", true, false);
aTab.dispatchEvent(event);
var maxTabsUndo = this._prefBranch.getIntPref("tabs.undoclose.depth");
var maxTabsUndo = this._prefBranch.getIntPref("browser.sessionstore.max_tabs_undo");
// don't update our internal state if we don't have to
if (maxTabsUndo == 0) {
return;
@ -601,10 +620,6 @@ SessionStoreService.prototype = {
var tabState = this._collectTabData(aTab);
this._updateTextAndScrollDataForTab(aWindow, aTab.linkedBrowser, tabState);
// reset the _tab property to avoid keeping the tab's XUL element alive
// longer than we need it
delete tabState._tab;
// store closed-tab data for undo
if (tabState.entries.length > 0) {
let tabTitle = aTab.label;
@ -695,9 +710,16 @@ SessionStoreService.prototype = {
},
setBrowserState: function sss_setBrowserState(aState) {
try {
var state = this._safeEval("(" + aState + ")");
}
catch (ex) { /* invalid state object - don't restore anything */ }
if (!state || !state.windows)
throw (Components.returnCode = Components.results.NS_ERROR_INVALID_ARG);
var window = this._getMostRecentBrowserWindow();
if (!window) {
this._openWindowWithState("(" + aState + ")");
this._openWindowWithState(state);
return;
}
@ -709,7 +731,7 @@ SessionStoreService.prototype = {
});
// restore to the given state
this.restoreWindow(window, "(" + aState + ")", true);
this.restoreWindow(window, state, true);
},
getWindowState: function sss_getWindowState(aWindow) {
@ -745,10 +767,8 @@ SessionStoreService.prototype = {
if (!tabState.entries || !aTab.ownerDocument || !aTab.ownerDocument.defaultView.__SSi)
throw (Components.returnCode = Components.results.NS_ERROR_INVALID_ARG);
tabState._tab = aTab;
var window = aTab.ownerDocument.defaultView;
this.restoreHistoryPrecursor(window, [tabState], 0, 0, 0);
this.restoreHistoryPrecursor(window, [aTab], [tabState], 0, 0, 0);
},
duplicateTab: function sss_duplicateTab(aWindow, aTab) {
@ -761,14 +781,12 @@ SessionStoreService.prototype = {
this._updateTextAndScrollDataForTab(sourceWindow, aTab.linkedBrowser, tabState, true);
var newTab = aWindow.getBrowser().addTab();
tabState._tab = newTab;
this.restoreHistoryPrecursor(aWindow, [tabState], 0, 0, 0);
this.restoreHistoryPrecursor(aWindow, [newTab], [tabState], 0, 0, 0);
return newTab;
},
getClosedTabCount: function sss_getClosedTabCount(aWindow) {
//misak: 2 lines uncommented
if (!aWindow.__SSi && aWindow.__SS_dyingCache)
return aWindow.__SS_dyingCache._closedTabs.length;
@ -776,8 +794,8 @@ SessionStoreService.prototype = {
// XXXzeniko shouldn't we throw here?
return 0; // not a browser window, or not otherwise tracked by SS.
return aWindow.getBrowser().savedBrowsers.length;
//return this._windows[aWindow.__SSi]._closedTabs.length;
let closedTabs = aWindow.getBrowser().savedBrowsers.map(function(e) { return e.tabData; });
return closedTabs.length;
},
getClosedTabData: function sss_getClosedTabDataAt(aWindow) {
@ -786,7 +804,8 @@ SessionStoreService.prototype = {
if (!aWindow.__SSi)
return this._toJSONString(aWindow.__SS_dyingCache._closedTabs);
return this._toJSONString(aWindow.getBrowser().savedBrowsers.map(function(e) { return e.tabData; }))
let closedTabs = aWindow.getBrowser().savedBrowsers.map(function(e) { return e.tabData; });
return this._toJSONString(closedTabs);
},
undoCloseTab: function sss_undoCloseTab(aWindow, aIndex) {
@ -804,9 +823,9 @@ SessionStoreService.prototype = {
let browser = aWindow.gBrowser;
// Seamonkey has it's own undoclosetab functionality
tab = browser.restoreTab(aIndex);
var newTab = browser.restoreTab(aIndex);
return tab;
return newTab;
},
getWindowValue: function sss_getWindowValue(aWindow, aKey) {
@ -903,7 +922,7 @@ SessionStoreService.prototype = {
if (!browser || !browser.currentURI)
// can happen when calling this function right after .addTab()
return tabData;
else if (browser.parentNode.__SS_data && browser.parentNode.__SS_data._tab)
else if (browser.parentNode.__SS_data && browser.parentNode.__SS_data._tabStillLoading)
// use the data to be restored when the tab hasn't been completely loaded
return browser.parentNode.__SS_data;
@ -913,8 +932,11 @@ SessionStoreService.prototype = {
}
catch (ex) { } // this could happen if we catch a tab during (de)initialization
// XXXzeniko anchor navigation doesn't reset __SS_data, so we could reuse
// data even when we shouldn't (e.g. Back, different anchor)
if (history && browser.parentNode.__SS_data &&
browser.parentNode.__SS_data.entries[history.index] && !aFullData) {
browser.parentNode.__SS_data.entries[history.index] &&
history.index < this._sessionhistory_max_entries - 1 && !aFullData) {
tabData = browser.parentNode.__SS_data;
tabData.index = history.index + 1;
}
@ -1141,7 +1163,8 @@ SessionStoreService.prototype = {
for (var i = 0; i < browsers.length; i++) {
try {
var tabData = this._windows[aWindow.__SSi].tabs[i];
if (browsers[i].parentNode.__SS_data && browsers[i].parentNode.__SS_data._tab)
if (browsers[i].parentNode.__SS_data &&
browsers[i].parentNode.__SS_data._tabStillLoading)
continue; // ignore incompletely initialized tabs
this._updateTextAndScrollDataForTab(aWindow, browsers[i], tabData);
}
@ -1209,7 +1232,8 @@ SessionStoreService.prototype = {
}
var isHTTPS = this._getURIFromString((aContent.parent || aContent).
document.location.href).schemeIs("https");
if (aFullData || this._checkPrivacyLevel(isHTTPS)) {
if (aFullData || this._checkPrivacyLevel(isHTTPS) ||
aContent.top.document.location.href == "about:sessionrestore") {
if (aFullData || aUpdateFormData) {
let formData = this._collectFormDataForFrame(aContent.document);
if (formData)
@ -1271,8 +1295,12 @@ SessionStoreService.prototype = {
let data = {};
do {
let id = node.id ? "#" + node.id : XPathHelper.generate(node);
if (node instanceof Components.interfaces.nsIDOMHTMLInputElement)
data[id] = node.type == "checkbox" || node.type == "radio" ? node.checked : node.value;
if (node instanceof Components.interfaces.nsIDOMHTMLInputElement) {
if (node.type != "file")
data[id] = node.type == "checkbox" || node.type == "radio" ? node.checked : node.value;
else
data[id] = { type: "file", value: node.value };
}
else if (node instanceof Components.interfaces.nsIDOMHTMLTextAreaElement)
data[id] = node.value;
else if (!node.multiple)
@ -1398,6 +1426,8 @@ SessionStoreService.prototype = {
if (this._loadState == STATE_RUNNING) {
// update the data for all windows with activities since the last save operation
this._forEachBrowserWindow(function(aWindow) {
if (!this._isWindowLoaded(aWindow)) // window data is still in _statesToRestore
return;
if (aUpdateAll || this._dirtyWindows[aWindow.__SSi] || aWindow == activeWindow) {
this._collectWindowData(aWindow);
}
@ -1419,6 +1449,16 @@ SessionStoreService.prototype = {
nonPopupCount++;
}
this._updateCookies(total);
// collect the data for all windows yet to be restored
for (ix in this._statesToRestore) {
for each (let winData in this._statesToRestore[ix].windows) {
total.push(winData);
if (!winData.isPopup)
nonPopupCount++;
}
}
// if no non-popup browser window remains open, return the state of the last closed window(s)
if (nonPopupCount == 0 && this._lastClosedWindows) {
// prepend the last non-popup browser window, so that if the user loads more tabs
@ -1439,6 +1479,9 @@ SessionStoreService.prototype = {
* @returns string
*/
_getWindowState: function sss_getWindowState(aWindow) {
if (!this._isWindowLoaded(aWindow))
return this._statesToRestore[aWindow.__SS_restoreID];
if (this._loadState == STATE_RUNNING) {
this._collectWindowData(aWindow);
}
@ -1451,6 +1494,9 @@ SessionStoreService.prototype = {
},
_collectWindowData: function sss_collectWindowData(aWindow) {
if (!this._isWindowLoaded(aWindow))
return;
// update the internal state data for this window
this._saveWindowHistory(aWindow);
this._updateTextAndScrollData(aWindow);
@ -1525,12 +1571,13 @@ SessionStoreService.prototype = {
var tabbrowser = aWindow.getBrowser();
var openTabCount = aOverwriteTabs ? tabbrowser.browsers.length : -1;
var newTabCount = winData.tabs.length;
let tabs = [];
for (var t = 0; t < newTabCount; t++) {
winData.tabs[t]._tab = t < openTabCount ? tabbrowser.mTabs[t] : tabbrowser.addTab();
tabs.push(t < openTabCount ? tabbrowser.mTabs[t] : tabbrowser.addTab());
// when resuming at startup: add additionally requested pages to the end
if (!aOverwriteTabs && root._firstTabs) {
tabbrowser.moveTabTo(winData.tabs[t]._tab, t);
tabbrowser.moveTabTo(tabs[t], t);
}
}
@ -1546,8 +1593,8 @@ SessionStoreService.prototype = {
this.restoreCookies(winData.cookies);
}
if (winData.extData) {
if (!this._windows[aWindow.__SSi].extData) {
this._windows[aWindow.__SSi].extData = {}
if (aOverwriteTabs || !this._windows[aWindow.__SSi].extData) {
this._windows[aWindow.__SSi].extData = {};
}
for (var key in winData.extData) {
this._windows[aWindow.__SSi].extData[key] = winData.extData[key];
@ -1569,18 +1616,20 @@ SessionStoreService.prototype = {
// }
//}
this.restoreHistoryPrecursor(aWindow, winData.tabs, (aOverwriteTabs ?
(parseInt(winData.selected) || 1) : 0), 0, 0);
this.restoreHistoryPrecursor(aWindow, tabs, winData.tabs,
(aOverwriteTabs ? (parseInt(winData.selected) || 1) : 0), 0, 0);
this._notifyIfAllWindowsRestored();
},
/**
* Manage history restoration for a window
* @param aWindow
* Window to restore the tabs into
* @param aTabs
* Array of tab data
* @param aCurrentTabs
* Array of tab references
* @param aTabData
* Array of tab data
* @param aSelectTab
* Index of selected tab
* @param aIx
@ -1588,21 +1637,22 @@ SessionStoreService.prototype = {
* @param aCount
* Counter for number of times delaying b/c browser or history aren't ready
*/
restoreHistoryPrecursor: function sss_restoreHistoryPrecursor(aWindow, aTabs, aSelectTab, aIx, aCount) {
restoreHistoryPrecursor:
function sss_restoreHistoryPrecursor(aWindow, aTabs, aTabData, aSelectTab, aIx, aCount) {
var tabbrowser = aWindow.getBrowser();
// make sure that all browsers and their histories are available
// - if one's not, resume this check in 100ms (repeat at most 10 times)
for (var t = aIx; t < aTabs.length; t++) {
try {
if (!tabbrowser.getBrowserForTab(aTabs[t]._tab).webNavigation.sessionHistory) {
if (!tabbrowser.getBrowserForTab(aTabs[t]).webNavigation.sessionHistory) {
throw new Error();
}
}
catch (ex) { // in case browser or history aren't ready yet
if (aCount < 10) {
var restoreHistoryFunc = function(self) {
self.restoreHistoryPrecursor(aWindow, aTabs, aSelectTab, aIx, aCount + 1);
self.restoreHistoryPrecursor(aWindow, aTabs, aTabData, aSelectTab, aIx, aCount + 1);
}
aWindow.setTimeout(restoreHistoryFunc, 100, this);
return;
@ -1612,10 +1662,11 @@ SessionStoreService.prototype = {
// mark the tabs as loading
for (t = 0; t < aTabs.length; t++) {
var tab = aTabs[t]._tab;
var tab = aTabs[t];
var browser = tabbrowser.getBrowserForTab(tab);
if (!aTabs[t].entries || aTabs[t].entries.length == 0) {
aTabData[t]._tabStillLoading = true;
if (!aTabData[t].entries || aTabData[t].entries.length == 0) {
// make sure to blank out this tab's content
// (just purging the tab's history won't be enough)
browser.contentDocument.location = "about:blank";
@ -1632,24 +1683,31 @@ SessionStoreService.prototype = {
// wall-paper fix for bug 439675: make sure that the URL to be loaded
// is always visible in the address bar
let activeIndex = (aTabs[t].index || aTabs[t].entries.length) - 1;
let activePageData = aTabs[t].entries[activeIndex] || null;
let activeIndex = (aTabData[t].index || aTabData[t].entries.length) - 1;
let activePageData = aTabData[t].entries[activeIndex] || null;
browser.userTypedValue = activePageData ? activePageData.url || null : null;
// keep the data around to prevent dataloss in case
// a tab gets closed before it's been properly restored
browser.parentNode.__SS_data = aTabs[t];
browser.parentNode.__SS_data = aTabData[t];
}
// make sure to restore the selected tab first (if any)
if (aSelectTab-- && aTabs[aSelectTab]) {
aTabs.unshift(aTabs.splice(aSelectTab, 1)[0]);
tabbrowser.selectedTab = aTabs[0]._tab;
aTabData.unshift(aTabData.splice(aSelectTab, 1)[0]);
tabbrowser.selectedTab = aTabs[0];
}
if (!this._isWindowLoaded(aWindow)) {
// from now on, the data will come from the actual window
delete this._statesToRestore[aWindow.__SS_restoreID];
delete aWindow.__SS_restoreID;
}
// helper hash for ensuring unique frame IDs
var idMap = { used: {} };
this.restoreHistory(aWindow, aTabs, idMap);
this.restoreHistory(aWindow, aTabs, aTabData, idMap);
},
/**
@ -1657,22 +1715,25 @@ SessionStoreService.prototype = {
* @param aWindow
* Window reference
* @param aTabs
* Array of tab references
* @param aTabData
* Array of tab data
* @param aIdMap
* Hash for ensuring unique frame IDs
*/
restoreHistory: function sss_restoreHistory(aWindow, aTabs, aIdMap) {
restoreHistory: function sss_restoreHistory(aWindow, aTabs, aTabData, aIdMap) {
var _this = this;
while (aTabs.length > 0 && (!aTabs[0]._tab || !aTabs[0]._tab.parentNode)) {
while (aTabs.length > 0 && (!aTabData[0]._tabStillLoading || !aTabs[0].parentNode)) {
aTabs.shift(); // this tab got removed before being completely restored
aTabData.shift();
}
if (aTabs.length == 0) {
return; // no more tabs to restore
}
var tabData = aTabs.shift();
var tab = aTabs.shift();
var tabData = aTabData.shift();
var tab = tabData._tab;
var browser = aWindow.getBrowser().getBrowserForTab(tab);
var history = browser.webNavigation.sessionHistory;
@ -1685,8 +1746,12 @@ SessionStoreService.prototype = {
tabData.entries = [];
}
if (tabData.extData) {
tab.__SS_extdata = tabData.extData;
tab.__SS_extdata = {};
for (let key in tabData.extData)
tab.__SS_extdata[key] = tabData.extData[key];
}
else
delete tab.__SS_extdata;
for (var i = 0; i < tabData.entries.length; i++) {
history.addEntry(this._deserializeHistoryEntry(tabData.entries[i], aIdMap), true);
@ -1743,7 +1808,7 @@ SessionStoreService.prototype = {
browser.addEventListener("load", browser.__SS_restore, true);
}
aWindow.setTimeout(function(){ _this.restoreHistory(aWindow, aTabs, aIdMap); }, 0);
aWindow.setTimeout(function(){ _this.restoreHistory(aWindow, aTabs, aTabData, aIdMap); }, 0);
},
/**
@ -1842,7 +1907,7 @@ SessionStoreService.prototype = {
* A tab's docshell (containing the sessionStorage)
*/
_deserializeSessionStorage: function sss_deserializeSessionStorage(aStorageData, aDocShell) {
let ioService = Cc["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
let ioService = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
for (let url in aStorageData) {
let uri = ioService.newURI(url, null, null);
let storage = aDocShell.getSessionStorageForURI(uri);
@ -1878,7 +1943,7 @@ SessionStoreService.prototype = {
RegExp.$1 == aPrefix && hasExpectedURL(aContent.document, aURL)) {
var document = aContent.document;
var node = RegExp.$2 ? document.getElementById(RegExp.$3) : document.getElementsByName(RegExp.$3)[0] || null;
if (node && "value" in node) {
if (node && "value" in node && node.type != "file") {
node.value = decodeURI(RegExp.$4);
var event = document.createEvent("UIEvents");
@ -1900,7 +1965,10 @@ SessionStoreService.prototype = {
continue;
let value = aData[key];
if (typeof value == "string") {
if (typeof value == "string" && node.type != "file") {
if (node.value == value)
continue; // don't dispatch an input event for no change
node.value = value;
let event = aDocument.createEvent("UIEvents");
@ -1913,6 +1981,8 @@ SessionStoreService.prototype = {
try {
node.selectedIndex = value;
} catch (ex) { /* throws for invalid indices */ }
else if (value && value.type && value.type == node.type)
node.value = value.value;
else if (value && typeof value.indexOf == "function" && node.options) {
Array.forEach(node.options, function(aOpt, aIx) {
aOpt.selected = value.indexOf(aIx) > -1;
@ -2031,11 +2101,20 @@ SessionStoreService.prototype = {
if (!isNaN(aLeft) && !isNaN(aTop) && (aLeft != win_("screenX") || aTop != win_("screenY"))) {
aWindow.moveTo(aLeft, aTop);
}
if (aSizeMode == "maximized" && win_("sizemode") != "maximized") {
aWindow.maximize();
}
else if (aSizeMode && aSizeMode != "maximized" && win_("sizemode") != "normal") {
aWindow.restore();
switch (aSizeMode)
{
case "maximized":
if (win_("sizemode") != "maximized")
aWindow.maximize();
break
case "minimized":
if (win_("sizemode") != "minimized")
aWindow.minimize();
break
default:
if (win_("sizemode") != "normal")
aWindow.restore();
break
}
var sidebar = aWindow.document.getElementById("sidebar-box");
if (sidebar.getAttribute("sidebarcommand") != aSidebar) {
@ -2044,7 +2123,7 @@ SessionStoreService.prototype = {
// since resizing/moving a window brings it to the foreground,
// we might want to re-focus the last focused window
if (this.windowToFocus) {
this.windowToFocus.focus();
this.windowToFocus.content.focus();
}
},
@ -2124,11 +2203,24 @@ SessionStoreService.prototype = {
return;
var oState = this._getCurrentState(aUpdateAll);
oState.session = { state: ((this._loadState == STATE_RUNNING) ? STATE_RUNNING_STR : STATE_STOPPED_STR) };
oState.session = {
state: this._loadState == STATE_RUNNING ? STATE_RUNNING_STR : STATE_STOPPED_STR,
lastUpdate: Date.now()
};
if (this._recentCrashes)
oState.session.recentCrashes = this._recentCrashes;
this._saveStateObject(oState);
},
/**
* write a state object to disk
*/
_saveStateObject: function sss_saveStateObject(aStateObj) {
var stateString = Components.classes["@mozilla.org/supports-string;1"]
.createInstance(Components.interfaces.nsISupportsString);
stateString.data = oState.toSource();
// parentheses are for backwards compatibility with older sessionstore files
stateString.data = "(" + this._toJSONString(aStateObj) + ")";
var observerService = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
@ -2207,13 +2299,10 @@ SessionStoreService.prototype = {
.openWindow(null, this._prefBranch.getCharPref("chromeURL"), "_blank",
"chrome,dialog=no,all", argString);
window.__SS_state = aState;
var _this = this;
window.addEventListener("load", function(aEvent) {
aEvent.currentTarget.removeEventListener("load", arguments.callee, true);
_this.restoreWindow(aEvent.currentTarget, aEvent.currentTarget.__SS_state, true, true);
delete aEvent.currentTarget.__SS_state;
}, true);
do {
var ID = "window" + Math.random();
} while (ID in this._statesToRestore);
this._statesToRestore[(window.__SS_restoreID = ID)] = aState;
return window;
},
@ -2342,6 +2431,37 @@ SessionStoreService.prototype = {
}
},
/**
* @param aState is a session state
* @param aRecentCrashes is the number of consecutive crashes
* @returns whether a restore page will be needed for the session state
*/
_needsRestorePage: function sss_needsRestorePage(aState, aRecentCrashes) {
const SIX_HOURS_IN_MS = 6 * 60 * 60 * 1000;
// don't wrap a single about:sessionrestore page
let winData = aState.windows || null;
if (winData && winData.length == 1 && winData[0].tabs &&
winData[0].tabs.length == 1 && winData[0].tabs[0].entries &&
winData[0].tabs[0].entries.length == 1 &&
winData[0].tabs[0].entries[0].url == "about:sessionrestore")
return false;
// don't automatically restore in Safe Mode
let XRE = Components.classes["@mozilla.org/xre/app-info;1"].getService(Components.interfaces.nsIXULRuntime);
if (XRE.inSafeMode)
return true;
let max_resumed_crashes =
this._prefBranch.getIntPref("sessionstore.max_resumed_crashes");
let sessionAge = aState.session && aState.session.lastUpdate &&
(Date.now() - aState.session.lastUpdate);
return max_resumed_crashes != -1 &&
(aRecentCrashes > max_resumed_crashes ||
sessionAge && sessionAge >= SIX_HOURS_IN_MS);
},
/**
* safe eval'ing
*/
@ -2353,20 +2473,15 @@ SessionStoreService.prototype = {
* Converts a JavaScript object into a JSON string
* (see http://www.json.org/ for more information).
*
* The inverse operation consists of eval("(" + JSON_string + ")");
* and should be provably safe.
* The inverse operation consists of JSON.parse(JSON_string).
*
* @param aJSObject is the object to be converted
* @return the object's JSON representation
* @returns the object's JSON representation
*/
_toJSONString: function sss_toJSONString(aJSObject) {
let str = JSONModule.toString(aJSObject, ["_tab", "_hosts", "_formDataSaved"] /* keys to drop */);
// sanity check - so that API consumers can just eval this string
if (!JSONModule.isMostlyHarmless(str))
throw new Error("JSON conversion failed unexpectedly!");
return str;
// XXXzeniko drop the following keys used only for internal bookkeeping:
// _tabStillLoading, _hosts, _formDataSaved
return JSON.stringify(aJSObject);
},
_notifyIfAllWindowsRestored: function sss_notifyIfAllWindowsRestored() {
@ -2381,6 +2496,16 @@ SessionStoreService.prototype = {
}
},
/**
* @param aWindow
* Window reference
* @returns whether this window's data is still cached in _statesToRestore
* because it's not fully loaded yet
*/
_isWindowLoaded: function sss_isWindowLoaded(aWindow) {
return !aWindow.__SS_restoreID;
},
/* ........ Storage API .............. */
/**

Просмотреть файл

@ -242,6 +242,7 @@ bin/components/libzipwriter.so
bin/components/nsSessionStartup.js
bin/components/nsSessionStore.js
bin/components/sessionstore.xpt
bin/components/aboutSessionRestore.js
; JavaScript components
bin/components/FeedProcessor.js

Просмотреть файл

@ -246,6 +246,7 @@ bin\sqlite3.dll
bin\components\nsSessionStartup.js
bin\components\nsSessionStore.js
bin\components\sessionstore.xpt
bin\components\aboutSessionRestore.js
; JavaScript components
bin\components\FeedProcessor.js

Просмотреть файл

@ -0,0 +1,18 @@
<!ENTITY restorepage.tabtitle "Restore Session">
<!ENTITY restorepage.pagetitle "Would you like to restore your session?">
<!-- LOCALIZATION NOTE: If "closed unexpectedly" sounds too awkward in the translation,
you may translate "crash" instead (even though it's IT-speak) -->
<!ENTITY restorepage.issueDesc "Your previous &brandShortName; session closed unexpectedly. We sincerely apologize for the inconvenience. You can restore the tabs and windows from your previous session, or start a new session if they are no longer needed.">
<!ENTITY restorepage.remedies "If &brandShortName; closes repeatedly:">
<!ENTITY restorepage.dueToChrome "Try disabling any recently added extensions in the Add-ons Manager.">
<!ENTITY restorepage.dueToContent "Try restoring your session without any Web pages you suspect might be causing the problem:">
<!ENTITY restorepage.restoreButton "Restore Previous Session">
<!ENTITY restorepage.restore.access "R">
<!ENTITY restorepage.cancelButton "Start New Session">
<!ENTITY restorepage.cancel.access "S">
<!ENTITY restorepage.restoreHeader "Restore">
<!ENTITY restorepage.listHeader "Windows and Tabs">
<!-- LOCALIZATION NOTE: &#37;S will be replaced with a number. -->
<!ENTITY restorepage.windowLabel "Window &#37;S">

Просмотреть файл

@ -1,7 +0,0 @@
restoredTitle= %S - Restore Previous Session
restoredMsg=Your last %S session closed unexpectedly. You can restore the tabs and windows from your previous session, or start a new session if you think the problem was related to a page you were viewing.
# Localization note: It is recommended that okTitle be longer than cancelTitle
# so that hitting the more prominent button doesn't lead to dataloss (see bug 346264).
okTitle=Restore Previous Session
cancelTitle=Start New Session

Просмотреть файл

@ -29,7 +29,6 @@
locale/@AB_CD@/communicator/openLocation.properties (%chrome/common/openLocation.properties)
locale/@AB_CD@/communicator/passwordManager.dtd (%chrome/common/passwordManager.dtd)
locale/@AB_CD@/communicator/printPreview.dtd (%chrome/common/printPreview.dtd)
locale/@AB_CD@/communicator/sessionstore.properties (%chrome/common/sessionstore.properties)
locale/@AB_CD@/communicator/shellservice.properties (%chrome/common/shellservice.properties)
locale/@AB_CD@/communicator/sanitize.dtd (%chrome/common/sanitize.dtd)
locale/@AB_CD@/communicator/tasksOverlay.dtd (%chrome/common/tasksOverlay.dtd)
@ -38,6 +37,7 @@
locale/@AB_CD@/communicator/utilityOverlay.properties (%chrome/common/utilityOverlay.properties)
locale/@AB_CD@/communicator/viewZoomOverlay.dtd (%chrome/common/viewZoomOverlay.dtd)
locale/@AB_CD@/communicator/viewZoomOverlay.properties (%chrome/common/viewZoomOverlay.properties)
locale/@AB_CD@/communicator/aboutSessionRestore.dtd (%chrome/common/aboutSessionRestore.dtd)
locale/@AB_CD@/communicator/bookmarks/addBookmark.dtd (%chrome/common/bookmarks/addBookmark.dtd)
locale/@AB_CD@/communicator/bookmarks/bm-props.dtd (%chrome/common/bookmarks/bm-props.dtd)
locale/@AB_CD@/communicator/bookmarks/bookmarks.dtd (%chrome/common/bookmarks/bookmarks.dtd)

Просмотреть файл

@ -0,0 +1,85 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the nsSessionStore component.
*
* The Initial Developer of the Original Code is
* Simon Bünzli <zeniko@gmail.com>
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Michael Ventnor <m.ventnor@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#tabList {
width: 100%;
height: 12em;
}
treechildren::-moz-tree-image(icon),
treechildren::-moz-tree-image(noicon) {
padding-right: 2px;
margin: 0px 2px;
width: 16px;
height: 16px;
}
treechildren::-moz-tree-image(noicon) {
list-style-image: url("chrome://communicator/skin/bookmarks/bookmark-item.png");
}
treechildren::-moz-tree-image(container, noicon) {
list-style-image: url("chrome://communicator/skin/bookmarks/bookmark-folder-closed.png");
}
treechildren::-moz-tree-image(container, noicon, open) {
list-style-image: url("chrome://communicator/skin/bookmarks/bookmark-folder-open.png");
}
treechildren::-moz-tree-checkbox(checked) {
list-style-image: url("chrome://global/skin/checkbox/cbox-check.gif");
}
treechildren::-moz-tree-checkbox(partial) {
list-style-image: url("chrome://global/skin/checkbox/cbox-check-dis.gif");
}
treechildren::-moz-tree-row(alternate) {
background-color: -moz-oddtreerow;
}
treechildren::-moz-tree-row(alternate, selected) {
background-color: Highlight;
}
#buttons {
-moz-margin-start: 80px;
}
#buttons > button {
margin-top: 2em;
-moz-margin-start: 5px;
}

Просмотреть файл

@ -21,6 +21,7 @@ classic.jar:
skin/classic/communicator/preferences.css (communicator/preferences.css)
skin/classic/communicator/prefpanels.css (communicator/prefpanels.css)
skin/classic/communicator/smileys.css (communicator/smileys.css)
skin/classic/communicator/aboutSessionRestore.css (communicator/aboutSessionRestore.css)
skin/classic/communicator/bookmarks/bookmark-folder-button.gif (communicator/bookmarks/bookmark-folder-button.gif)
skin/classic/communicator/bookmarks/bookmark-folder-closed.png (communicator/bookmarks/bookmark-folder-closed.png)
skin/classic/communicator/bookmarks/bookmark-folder-dis.png (communicator/bookmarks/bookmark-folder-dis.png)

Просмотреть файл

@ -0,0 +1,73 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the nsSessionStore component.
*
* The Initial Developer of the Original Code is
* Simon Bünzli <zeniko@gmail.com>
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Michael Ventnor <m.ventnor@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#tabList {
width: 100%;
height: 12em;
}
treechildren::-moz-tree-image(icon),
treechildren::-moz-tree-image(noicon) {
padding-right: 2px;
margin: 0px 2px;
width: 16px;
height: 16px;
}
treechildren::-moz-tree-image(noicon) {
list-style-image: url("chrome://communicator/skin/bookmarks/bookmark-item.png");
}
treechildren::-moz-tree-image(container, noicon) {
list-style-image: url("chrome://communicator/skin/bookmarks/bookmark-folder-closed.gif");
}
treechildren::-moz-tree-image(container, noicon, open) {
list-style-image: url("chrome://communicator/skin/bookmarks/bookmark-folder-open.gif");
}
treechildren::-moz-tree-checkbox {
list-style-image: url("chrome://global/skin/checkbox/cbox.gif");
}
treechildren::-moz-tree-checkbox(checked) {
list-style-image: url("chrome://global/skin/checkbox/cbox-check.gif");
}
treechildren::-moz-tree-checkbox(partial) {
list-style-image: url("chrome://global/skin/checkbox/cbox-check-dis.gif");
}

Просмотреть файл

@ -18,6 +18,7 @@ modern.jar:
skin/modern/communicator/helpOverlay.css (communicator/helpOverlay.css)
skin/modern/communicator/smileys.css (communicator/smileys.css)
skin/modern/communicator/communicator.css (communicator/communicator.css)
skin/modern/communicator/aboutSessionRestore.css (communicator/aboutSessionRestore.css)
skin/modern/communicator/bookmarks/bookmark-folder-closed.gif (communicator/bookmarks/bookmark-folder-closed.gif)
skin/modern/communicator/bookmarks/bookmark-folder-dis.gif (communicator/bookmarks/bookmark-folder-dis.gif)
skin/modern/communicator/bookmarks/bookmark-folder-open.gif (communicator/bookmarks/bookmark-folder-open.gif)