Bug 448976 - turn the Session Restore prompt into an error page, r=dietrich

This commit is contained in:
Simon Bünzli 2008-10-11 22:17:31 +03:30
parent 6da03d8e12
commit 2243cc4a79
19 changed files with 830 additions and 85 deletions

View File

@ -695,6 +695,9 @@ pref("browser.sessionstore.postdata", 0);
pref("browser.sessionstore.privacy_level", 1);
// how many tabs can be reopened (per window)
pref("browser.sessionstore.max_tabs_undo", 10);
// 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);
// allow META refresh by default
pref("accessibility.blockautorefresh", false);

View File

@ -0,0 +1,323 @@
/* ***** 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 ***** */
const Cc = Components.classes;
const Ci = Components.interfaces;
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 = Cc["@mozilla.org/browser/sessionstartup;1"].getService(Ci.nsISessionStartup);
sessionData.value = ss.state;
if (!sessionData.value)
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" };
return {
label: entry.title || entry.url,
checked: true,
src: aTabData.attributes.image || null,
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() {
// 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();
// restore the session into a new window and close the current tab
var top = getBrowserWindow();
var selfBrowser = top.gBrowser.getBrowserForDocument(document);
var newWindow = top.openDialog(top.location, "_blank", "chrome,dialog=no,all");
newWindow.addEventListener("load", function() {
newWindow.removeEventListener("load", arguments.callee, true);
var ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
ss.setWindowState(newWindow, stateString, true);
var tabbrowser = top.gBrowser;
if (tabbrowser.tabContainer.childNodes.length == 1)
top.close();
else
tabbrowser.removeTab(getTabForBrowser(selfBrowser));
}, 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(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShellTreeItem).rootTreeItem
.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
}
function getTabForBrowser(aBrowser) {
return Array.filter(getBrowserWindow().gBrowser.tabContainer.childNodes,
function(aTab) aTab.linkedBrowser == aBrowser)[0];
}
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 = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.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 = Cc["@mozilla.org/preferences-service;1"].getService(Ci.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 = Cc["@mozilla.org/atom-service;1"].getService(Ci.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 = idx + 1; t < gTreeData.length && this.getLevel(t) > thisLevel; t++);
return thisLevel == this.getLevel(t);
},
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;
},
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) { }
};

View File

@ -0,0 +1,117 @@
<?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 % 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://browser/locale/aboutSessionRestore.dtd">
%restorepageDTD;
]>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>&restorepage.title;</title>
<link rel="stylesheet" href="chrome://global/skin/netError.css" type="text/css" media="all"/>
<link rel="stylesheet" href="chrome://browser/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://browser/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.titletext;</h1>
</div>
<!-- LONG CONTENT (the section most likely to require scrolling) -->
<div id="errorLongContent">
<!-- Short Description -->
<div id="errorShortDesc">
<p id="errorShortDescText">&restorepage.shortDesc;</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>

View File

@ -0,0 +1,3 @@
browser.jar:
* content/browser/aboutSessionRestore.xhtml (content/aboutSessionRestore.xhtml)
content/browser/aboutSessionRestore.js (content/aboutSessionRestore.js)

View File

@ -42,6 +42,7 @@ include $(DEPTH)/config/autoconf.mk
EXTRA_PP_COMPONENTS = \
nsSessionStore.js \
nsSessionStartup.js \
aboutSessionRestore.js \
$(NULL)
include $(topsrcdir)/config/rules.mk

View File

@ -0,0 +1,63 @@
/* ***** 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 ***** */
const Cc = Components.classes;
const Ci = Components.interfaces;
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("{7c65e6f0-7605-11dd-ad8b-0800200c9a66}"),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIAboutModule]),
getURIFlags: function(aURI) {
return Ci.nsIAboutModule.ALLOW_SCRIPT;
},
newChannel: function(aURI) {
let ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
let channel = ios.newChannel("chrome://browser/content/aboutSessionRestore.xhtml",
null, null);
channel.originalURI = aURI;
return channel;
}
};
function NSGetModule(compMgr, fileSpec)
XPCOMUtils.generateModule([AboutSessionRestore]);

View File

@ -95,8 +95,8 @@ SessionStartup.prototype = {
* Initialize the component
*/
init: function sss_init() {
this._prefBranch = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefService).getBranch("browser.");
let prefBranch = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefService).getBranch("browser.");
// get file references
var dirService = Cc["@mozilla.org/file/directory_service;1"].
@ -104,11 +104,11 @@ SessionStartup.prototype = {
let sessionFile = dirService.get("ProfD", Ci.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;
@ -129,7 +129,7 @@ SessionStartup.prototype = {
initialState.session.state == STATE_RUNNING_STR;
// set the startup type
if (lastSessionCrashed && resumeFromCrash && this._doRecoverSession())
if (lastSessionCrashed && resumeFromCrash)
this._sessionType = Ci.nsISessionStartup.RECOVER_SESSION;
else if (!lastSessionCrashed && doResumeSession)
this._sessionType = Ci.nsISessionStartup.RESUME_SESSION;
@ -235,74 +235,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 = Cc["@mozilla.org/embedcomp/dialogparam;1"].
createInstance(Ci.nsIDialogParamBlock);
// default to recovering
params.SetInt(0, 0);
Cc["@mozilla.org/embedcomp/window-watcher;1"].
getService(Ci.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
const brandShortName = this._getStringBundle("chrome://branding/locale/brand.properties")
.GetStringFromName("brandShortName");
// create prompt strings
var ssStringBundle = this._getStringBundle("chrome://browser/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 = Cc["@mozilla.org/embedcomp/prompt-service;1"].
getService(Ci.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) {
return Cc["@mozilla.org/intl/stringbundle;1"].
getService(Ci.nsIStringBundleService).createBundle(aURI);
},
/* ........ Storage API .............. */
/**

View File

@ -122,8 +122,9 @@ SessionStoreService.prototype = {
Ci.nsIObserver,
Ci.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,
@ -150,6 +151,9 @@ SessionStoreService.prototype = {
// not-"dirty" windows usually don't need to have their data updated
_dirtyWindows: {},
// counts the number of crashes since the last clean start
_recentCrashes: 0,
/* ........ Global Event Handlers .............. */
/**
@ -218,6 +222,23 @@ SessionStoreService.prototype = {
this._writeFile(this._sessionFileBackup, iniString);
}
catch (ex) { } // nothing else we can do here
this._recentCrashes = (this._initialState.session &&
this._initialState.session.recentCrashes || 0) + 1;
const SIX_HOURS_IN_MS = 6 * 60 * 60 * 1000;
let max_resumed_crashes =
this._prefBranch.getIntPref("sessionstore.max_resumed_crashes");
let sessionAge = this._initialState.session &&
this._initialState.session.lastUpdate &&
(Date.now() - this._initialState.session.lastUpdate);
let needsRestorePage = max_resumed_crashes != -1 &&
(this._recentCrashes > max_resumed_crashes ||
sessionAge && sessionAge >= SIX_HOURS_IN_MS);
if (needsRestorePage)
// replace the crashed session with a restore-page-only session
this._initialState =
{ windows: [{ tabs: [{ entries: [{ url: "about:sessionrestore"}] }] }] };
}
// make sure that at least the first window doesn't have anything hidden
@ -1208,7 +1229,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)
@ -2102,7 +2124,12 @@ 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;
var stateString = Cc["@mozilla.org/supports-string;1"].
createInstance(Ci.nsISupportsString);

View File

@ -226,6 +226,7 @@ bin/components/extensions.xpt
bin/components/update.xpt
bin/components/nsSessionStartup.js
bin/components/nsSessionStore.js
bin/components/aboutSessionRestore.js
bin/components/sessionstore.xpt
bin/components/nsURLFormatter.js
bin/components/urlformatter.xpt

View File

@ -233,6 +233,7 @@ bin\components\extensions.xpt
bin\components\update.xpt
bin\components\nsSessionStartup.js
bin\components\nsSessionStore.js
bin\components\aboutSessionRestore.js
bin\components\sessionstore.xpt
bin\components\nsURLFormatter.js
bin\components\urlformatter.xpt

View File

@ -0,0 +1,16 @@
<!ENTITY restorepage.title "Restore Previous Session">
<!ENTITY restorepage.titletext "Your Last &brandShortName; Session Closed Unexpectedly">
<!ENTITY restorepage.shortDesc "You can restore the tabs and windows from your previous session, or start a new session if they are no longer needed. Our apologies for the inconvenience.">
<!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 crash:">
<!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: %S will be replaced with a number. -->
<!ENTITY restorepage.windowLabel "Window #&#37;S">

View File

@ -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

View File

@ -4,6 +4,7 @@
% locale browser @AB_CD@ %locale/browser/
locale/browser/aboutDialog.dtd (%chrome/browser/aboutDialog.dtd)
locale/browser/aboutRobots.dtd (%chrome/browser/aboutRobots.dtd)
locale/browser/aboutSessionRestore.dtd (%chrome/browser/aboutSessionRestore.dtd)
locale/browser/credits.dtd (%chrome/browser/credits.dtd)
* locale/browser/browser.dtd (%chrome/browser/browser.dtd)
locale/browser/baseMenuOverlay.dtd (%chrome/browser/baseMenuOverlay.dtd)

View File

@ -0,0 +1,86 @@
%if 0
/* ***** 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 ***** */
%endif
#errorPageContainer {
background-image: url("chrome://global/skin/icons/question-48.png");
}
#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://mozapps/skin/places/defaultFavicon.png");
}
treechildren::-moz-tree-image(container, noicon) {
list-style-image: url("chrome://global/skin/icons/folder-item.png");
-moz-image-region: rect(0px, 32px, 16px, 16px);
}
treechildren::-moz-tree-image(container, open, noicon) {
-moz-image-region: rect(16px, 32px, 32px, 16px);
}
treechildren::-moz-tree-image(checked) {
list-style-image: url("chrome://global/skin/checkbox/cbox-check.gif");
}
treechildren::-moz-tree-image(partial) {
list-style-image: url("chrome://global/skin/checkbox/cbox-check-dis.gif");
}
/* XXXzeniko will need an appropriate color here (or none?) */
treechildren::-moz-tree-row(alternate) {
background-color: #EEEEEE;
}
treechildren::-moz-tree-row(alternate, selected) {
background-color: Highlight;
}
#buttons {
width: 100%;
}
#buttons > button {
margin-top: 2em;
}

View File

@ -1,5 +1,6 @@
classic.jar:
% skin browser classic/1.0 %skin/classic/browser/
* skin/classic/browser/aboutSessionRestore.css (aboutSessionRestore.css)
* skin/classic/browser/browser.css (browser.css)
skin/classic/browser/browser.xml
* skin/classic/browser/engineManager.css (engineManager.css)

View File

@ -0,0 +1,88 @@
%if 0
/* ***** 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 ***** */
%endif
#errorPageContainer {
background-image: url("chrome://global/skin/icons/question-48.png");
}
#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://mozapps/skin/places/defaultFavicon.png");
}
treechildren::-moz-tree-image(container, noicon) {
list-style-image: url("chrome://global/skin/icons/folder-item.png");
-moz-image-region: rect(0px, 32px, 16px, 16px);
}
treechildren::-moz-tree-image(container, open, noicon) {
-moz-image-region: rect(16px, 32px, 32px, 16px);
}
treechildren::-moz-tree-image(checked) {
list-style-image: url("chrome://global/skin/checkbox/cbox-check.gif");
}
treechildren::-moz-tree-image(partial) {
list-style-image: url("chrome://global/skin/checkbox/cbox-check-dis.gif");
}
treechildren::-moz-tree-row(odd) {
background-color: -moz-field;
}
treechildren::-moz-tree-row(alternate) {
background-color: -moz-oddtreerow;
}
treechildren::-moz-tree-row(alternate, selected) {
background-color: -moz-mac-secondaryhighlight;
}
#buttons {
width: 100%;
}
#buttons > button {
margin-top: 2em;
}

View File

@ -1,5 +1,6 @@
classic.jar:
% skin browser classic/1.0 %skin/classic/browser/
* skin/classic/browser/aboutSessionRestore.css (aboutSessionRestore.css)
skin/classic/browser/bookmark_toolbar_background.png
skin/classic/browser/bookmark-open-left.png
skin/classic/browser/bookmark-open-mid.png

View File

@ -0,0 +1,86 @@
%if 0
/* ***** 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 ***** */
%endif
#errorPageContainer {
background-image: url("chrome://global/skin/icons/question-48.png");
}
#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://mozapps/skin/places/defaultFavicon.png");
}
treechildren::-moz-tree-image(container, noicon) {
list-style-image: url("chrome://global/skin/icons/folder-item.png");
-moz-image-region: rect(0px, 32px, 16px, 16px);
}
treechildren::-moz-tree-image(container, open, noicon) {
-moz-image-region: rect(16px, 32px, 32px, 16px);
}
treechildren::-moz-tree-image(checked) {
list-style-image: url("chrome://global/skin/checkbox/cbox-check.gif");
}
treechildren::-moz-tree-image(partial) {
list-style-image: url("chrome://global/skin/checkbox/cbox-check-dis.gif");
}
/* XXXzeniko will need an appropriate color here (or none?) */
treechildren::-moz-tree-row(alternate) {
background-color: #EEEEEE;
}
treechildren::-moz-tree-row(alternate, selected) {
background-color: Highlight;
}
#buttons {
width: 100%;
}
#buttons > button {
margin-top: 2em;
}

View File

@ -3,6 +3,7 @@ classic.jar:
% skin browser classic/1.0 %skin/classic/browser/ os!=WINNT
# NOTE: If you add a new file here, you'll need to add it to the aero
# section at the bottom of this file
* skin/classic/browser/aboutSessionRestore.css (aboutSessionRestore.css)
* skin/classic/browser/browser.css (browser.css)
skin/classic/browser/browser.xml
* skin/classic/browser/engineManager.css (engineManager.css)
@ -91,6 +92,7 @@ classic.jar:
#ifdef XP_WIN
classic.jar:
% skin browser classic/1.0 %skin/classic/aero/browser/ os=WINNT osversion>=6
* skin/classic/aero/browser/aboutSessionRestore.css (aboutSessionRestore.css)
* skin/classic/aero/browser/browser.css (browser-aero.css)
skin/classic/aero/browser/browser.xml
* skin/classic/aero/browser/engineManager.css (engineManager.css)