Bug 448976 - turn the Session Restore prompt into an error page, r=dietrich
authorSimon Bünzli <zeniko@gmail.com>
Sat, 11 Oct 2008 22:17:31 +0330
changeset 20315 df62dc445ddccf154be2c3ec6990474d32269f47
parent 20314 1d719b91762901ab5ba99662bb8bff52c3a2cc67
child 20316 3069ac5e6241fd29edd8623dbd15008227eec002
push idunknown
push userunknown
push dateunknown
reviewersdietrich
bugs448976
milestone1.9.1b2pre
Bug 448976 - turn the Session Restore prompt into an error page, r=dietrich
browser/app/profile/firefox.js
browser/components/sessionstore/content/aboutSessionRestore.js
browser/components/sessionstore/content/aboutSessionRestore.xhtml
browser/components/sessionstore/jar.mn
browser/components/sessionstore/src/Makefile.in
browser/components/sessionstore/src/aboutSessionRestore.js
browser/components/sessionstore/src/nsSessionStartup.js
browser/components/sessionstore/src/nsSessionStore.js
browser/installer/unix/packages-static
browser/installer/windows/packages-static
browser/locales/en-US/chrome/browser/aboutSessionRestore.dtd
browser/locales/en-US/chrome/browser/sessionstore.properties
browser/locales/jar.mn
browser/themes/gnomestripe/browser/aboutSessionRestore.css
browser/themes/gnomestripe/browser/jar.mn
browser/themes/pinstripe/browser/aboutSessionRestore.css
browser/themes/pinstripe/browser/jar.mn
browser/themes/winstripe/browser/aboutSessionRestore.css
browser/themes/winstripe/browser/jar.mn
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -690,16 +690,19 @@ pref("browser.sessionstore.interval", 10
 // maximum amount of POSTDATA to be saved in bytes per history entry (-1 = all of it)
 // (NB: POSTDATA will be saved either entirely or not at all)
 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);
 // 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);
 
 // the (maximum) number of the recent visits to sample
 // when calculating frecency
 pref("places.frecency.numVisits", 10);
 
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/content/aboutSessionRestore.js
@@ -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) { }
+};
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/content/aboutSessionRestore.xhtml
@@ -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>
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/jar.mn
@@ -0,0 +1,3 @@
+browser.jar:
+*   content/browser/aboutSessionRestore.xhtml             (content/aboutSessionRestore.xhtml) 
+    content/browser/aboutSessionRestore.js                (content/aboutSessionRestore.js)
--- a/browser/components/sessionstore/src/Makefile.in
+++ b/browser/components/sessionstore/src/Makefile.in
@@ -37,11 +37,12 @@ topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH     = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 EXTRA_PP_COMPONENTS = \
 	nsSessionStore.js \
 	nsSessionStartup.js \
+	aboutSessionRestore.js \
 	$(NULL)
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/src/aboutSessionRestore.js
@@ -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]);
--- a/browser/components/sessionstore/src/nsSessionStartup.js
+++ b/browser/components/sessionstore/src/nsSessionStartup.js
@@ -90,30 +90,30 @@ SessionStartup.prototype = {
   _sessionType: Ci.nsISessionStartup.NO_SESSION,
 
 /* ........ Global Event Handlers .............. */
 
   /**
    * 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"].
                      getService(Ci.nsIProperties);
     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;
     
     // get string containing session state
     this._iniString = this._readStateFile(sessionFile);
     if (!this._iniString)
       return;
     
@@ -124,17 +124,17 @@ SessionStartup.prototype = {
     }
     catch (ex) { debug("The session file is invalid: " + ex); } 
     
     let lastSessionCrashed =
       initialState && initialState.session && initialState.session.state &&
       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;
     else
       this._iniString = null; // reset the state string
 
     if (this._sessionType != Ci.nsISessionStartup.NO_SESSION) {
       // wait for the first browser window to open
@@ -230,84 +230,16 @@ SessionStartup.prototype = {
 
   /**
    * Get the type of pending session store, if any.
    */
   get sessionType() {
     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 .............. */
 
   /**
    * Reads a session state file into a string and lets
    * observers modify the state before it's being used
    *
    * @param aFile is any nsIFile
    * @returns a session state string
--- a/browser/components/sessionstore/src/nsSessionStore.js
+++ b/browser/components/sessionstore/src/nsSessionStore.js
@@ -117,18 +117,19 @@ SessionStoreService.prototype = {
   classDescription: "Browser Session Store Service",
   contractID: "@mozilla.org/browser/sessionstore;1",
   classID: Components.ID("{5280606b-2510-4fe0-97ef-9b5a22eafe6b}"),
   QueryInterface: XPCOMUtils.generateQI([Ci.nsISessionStore,
                                          Ci.nsIDOMEventListener,
                                          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,
 
   // minimal interval between two save operations (in milliseconds)
   _interval: 10000,
 
   // when crash recovery is disabled, session data is not written to disk
@@ -145,16 +146,19 @@ SessionStoreService.prototype = {
 
   // in case the last closed window ain't a navigator:browser one
   // (also contains browser popup windows closed after the last non-popup one)
   _lastClosedWindows: null,
 
   // 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 .............. */
 
   /**
    * Initialize the component
    */
   init: function sss_init(aWindow) {
     if (!aWindow || this._loadState == STATE_RUNNING) {
       // make sure that all browser windows which try to initialize
@@ -213,16 +217,33 @@ SessionStoreService.prototype = {
         let lastSessionCrashed =
           this._initialState.session && this._initialState.session.state &&
           this._initialState.session.state == STATE_RUNNING_STR;
         if (lastSessionCrashed) {
           try {
             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
         delete this._initialState.windows[0].hidden;
       }
       catch (ex) { debug("The session file is invalid: " + ex); }
     }
 
@@ -1203,17 +1224,18 @@ SessionStoreService.prototype = {
                                                  aUpdateFormData, aFullData) {
     for (var i = 0; i < aContent.frames.length; i++) {
       if (aData.children && aData.children[i])
         this._updateTextAndScrollDataForFrame(aWindow, aContent.frames[i],
                                               aData.children[i], aUpdateFormData, aFullData);
     }
     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)
           aData.formdata = formData;
         else if (aData.formdata)
           delete aData.formdata;
       }
       
@@ -2097,17 +2119,22 @@ SessionStoreService.prototype = {
    *        Bool update all windows 
    */
   saveState: function sss_saveState(aUpdateAll) {
     // if crash recovery is disabled, only save session resuming information
     if (!this._resume_from_crash && this._loadState == STATE_RUNNING)
       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);
     stateString.data = oState.toSource();
     
     var observerService = Cc["@mozilla.org/observer-service;1"].
                           getService(Ci.nsIObserverService);
     observerService.notifyObservers(stateString, "sessionstore-state-write", "");
--- a/browser/installer/unix/packages-static
+++ b/browser/installer/unix/packages-static
@@ -221,16 +221,17 @@ bin/components/nsSidebar.js
 bin/components/nsExtensionManager.js
 bin/components/nsBlocklistService.js
 bin/components/nsUpdateService.js
 bin/components/pluginGlue.js
 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
 bin/components/libbrowserdirprovider.so
 bin/components/libbrowsercomps.so
 bin/components/txEXSLTRegExFunctions.js
 bin/components/nsLivemarkService.js
 bin/components/nsTaggingService.js
--- a/browser/installer/windows/packages-static
+++ b/browser/installer/windows/packages-static
@@ -228,16 +228,17 @@ bin\components\nsLoginManager.js
 bin\components\nsLoginManagerPrompter.js
 bin\components\storage-Legacy.js
 bin\components\storage-mozStorage.js
 bin\components\pluginGlue.js
 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
 bin\components\browserdirprovider.dll
 bin\components\brwsrcmp.dll
 bin\components\txEXSLTRegExFunctions.js
 bin\components\nsLivemarkService.js
 bin\components\nsTaggingService.js
new file mode 100644
--- /dev/null
+++ b/browser/locales/en-US/chrome/browser/aboutSessionRestore.dtd
@@ -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">
deleted file mode 100644
--- a/browser/locales/en-US/chrome/browser/sessionstore.properties
+++ /dev/null
@@ -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
--- a/browser/locales/jar.mn
+++ b/browser/locales/jar.mn
@@ -1,14 +1,15 @@
 #filter substitution
 
 @AB_CD@.jar:
 % 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)
     locale/browser/browser.properties              (%chrome/browser/browser.properties)
     locale/browser/metaData.dtd                    (%chrome/browser/metaData.dtd)
     locale/browser/metaData.properties             (%chrome/browser/metaData.properties)
     locale/browser/openLocation.dtd                (%chrome/browser/openLocation.dtd)
     locale/browser/openLocation.properties         (%chrome/browser/openLocation.properties)
new file mode 100644
--- /dev/null
+++ b/browser/themes/gnomestripe/browser/aboutSessionRestore.css
@@ -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;
+}
--- a/browser/themes/gnomestripe/browser/jar.mn
+++ b/browser/themes/gnomestripe/browser/jar.mn
@@ -1,10 +1,11 @@
 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)
   skin/classic/browser/Go-arrow.png
   skin/classic/browser/Go-arrow-rtl.png
   skin/classic/browser/identity.png
   skin/classic/browser/Info.png
   skin/classic/browser/monitor.png
new file mode 100644
--- /dev/null
+++ b/browser/themes/pinstripe/browser/aboutSessionRestore.css
@@ -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;
+}
--- a/browser/themes/pinstripe/browser/jar.mn
+++ b/browser/themes/pinstripe/browser/jar.mn
@@ -1,10 +1,11 @@
 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
   skin/classic/browser/bookmark-open-right.png
 * skin/classic/browser/browser.css                          (browser.css)
   skin/classic/browser/browser.xml
   skin/classic/browser/contextDialogBackground.png
 * skin/classic/browser/engineManager.css                    (engineManager.css)
new file mode 100644
--- /dev/null
+++ b/browser/themes/winstripe/browser/aboutSessionRestore.css
@@ -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;
+}
--- a/browser/themes/winstripe/browser/jar.mn
+++ b/browser/themes/winstripe/browser/jar.mn
@@ -1,13 +1,14 @@
 classic.jar:
 % skin browser classic/1.0 %skin/classic/browser/ os=WINNT osversion<6
 % 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)
         skin/classic/browser/Info.png                                (Info.png)
         skin/classic/browser/identity.png                            (identity.png)
         skin/classic/browser/KUI-background.png
         skin/classic/browser/pageInfo.css
         skin/classic/browser/pageInfo.png                            (pageInfo.png)
@@ -86,16 +87,17 @@ classic.jar:
         skin/classic/browser/tabbrowser/tab-hover-bkgnd.png                     (tabbrowser/tab-hover-bkgnd.png)
         skin/classic/browser/tabbrowser/tabstrip-bottom.png                     (tabbrowser/tabstrip-bottom.png)
         icon.png
         preview.png
 
 #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)
         skin/classic/aero/browser/Info.png                           (Info-aero.png)
         skin/classic/aero/browser/identity.png                       (identity-aero.png)
         skin/classic/aero/browser/KUI-background.png
         skin/classic/aero/browser/pageInfo.css
         skin/classic/aero/browser/pageInfo.png                       (pageInfo-aero.png)