[or-cvs] [torbutton/master 12/31] Rewrite Session Store blocking support using new APIs.

mikeperry at torproject.org mikeperry at torproject.org
Tue Jan 4 07:02:19 UTC 2011


Author: Mike Perry <mikeperry-git at fscked.org>
Date: Tue, 14 Dec 2010 00:02:56 -0800
Subject: Rewrite Session Store blocking support using new APIs.
Commit: 67bd1d97c6401e51eabf06185af7a0f908eb4cd6

We now filter the JSON data written to the store instead of reimplementing the
component used to create it. This is slightly less fragile than what we used
to do, but perhaps not by much. If the structure of the session store changes
substantially, we'll break. From the comments, it does look like they try to
keep some level of compatibility with previous formats..
---
 src/chrome.manifest                  |    4 +
 src/chrome/content/torbutton.js      |   34 +-
 src/components/nsSessionStore35.diff |  108 --
 src/components/nsSessionStore35.js   | 2976 --------------------------------
 src/components/nsSessionStore36.diff |  109 --
 src/components/nsSessionStore36.js   | 3120 ----------------------------------
 src/components/tbSessionStore.js     |  187 ++
 7 files changed, 220 insertions(+), 6318 deletions(-)
 delete mode 100644 src/components/nsSessionStore35.diff
 delete mode 100644 src/components/nsSessionStore35.js
 delete mode 100644 src/components/nsSessionStore36.diff
 delete mode 100644 src/components/nsSessionStore36.js
 create mode 100644 src/components/tbSessionStore.js

diff --git a/src/chrome.manifest b/src/chrome.manifest
index 05cdd74..bad7e56 100644
--- a/src/chrome.manifest
+++ b/src/chrome.manifest
@@ -66,6 +66,9 @@ contract @mozilla.org/mime;1 {3da0269f-fc29-4e9e-a678-c3b1cafcf13f}
 contract @mozilla.org/uriloader/external-protocol-service;1 {3da0269f-fc29-4e9e-a678-c3b1cafcf13f}
 contract @mozilla.org/uriloader/external-helper-app-service;1 {3da0269f-fc29-4e9e-a678-c3b1cafcf13f}
 
+component {aef08952-b003-4697-b935-a392367e214f} components/tbSessionStore.js
+contract @torproject.org/torbutton-ss-blocker;1 {aef08952-b003-4697-b935-a392367e214f}
+
 component {06322def-6fde-4c06-aef6-47ae8e799629} components/crash-observer.js
 contract @torproject.org/crash-observer;1 {06322def-6fde-4c06-aef6-47ae8e799629}
 
@@ -93,6 +96,7 @@ contract @mozilla.org/network/protocol;1?name=tors {a5a4bc50-5e8d-11de-8a39-0800
 category content-policy ContentPolicy @torproject.org/cssblocker;1
 category profile-after-change CookieJarSelector @torproject.org/cookie-jar-selector;1
 category profile-after-change RefSpoofer @torproject.org/torRefSpoofer;1
+category profile-after-change TBSessionBlocker @torproject.org/torbutton-ss-blocker;1
 
 
 
diff --git a/src/chrome/content/torbutton.js b/src/chrome/content/torbutton.js
index ebd218c..2095f35 100644
--- a/src/chrome/content/torbutton.js
+++ b/src/chrome/content/torbutton.js
@@ -2115,7 +2115,7 @@ function torbutton_check_js_tag(browser, tor_enabled, js_enabled) {
         // Defensive programming to tag this window here to 
         // an alternate tor state. It wil lmake this window totally
         // useless, but that is better than some undefined state
-        browser.__tb_tor_fetched = !tor_enabled;
+        torbutton_apply_tab_tag(browser, !tor_enabled);
     }
 
     /* Solution from: https://bugzilla.mozilla.org/show_bug.cgi?id=409737 */
@@ -2246,6 +2246,30 @@ tbHistoryListener.prototype = {
     OnHistoryReload: function(uri,flags) { return this.f1(); }
 };
 
+function torbutton_apply_tab_tag(browser, tag) {
+   if (typeof(browser["__tb_tor_fetched"]) == "undefined" ||
+           browser.__tb_tor_fetched != tag) {
+     // Only update the browser's session store tag if the tag has changed.
+     // This is an expensive operation.
+     var ss = Components.classes["@mozilla.org/browser/sessionstore;1"]
+                             .getService(Components.interfaces.nsISessionStore);
+
+     // http://stackoverflow.com/questions/3374056/firefox-gbrowser-getbrowserfortab-but-no-gbrowser-gettabforbrowser
+     var mTabs = gBrowser.mTabContainer.childNodes;
+     var tab = null;
+     for (var i=0; i<mTabs.length; i++) {
+         if (mTabs[i].linkedBrowser == browser) {
+             tab = mTabs[i];
+         }
+     }
+     if (tab)
+       ss.setTabValue(tab, "__tb_tor_fetched", tag.toString());
+     else
+       torbutton_log(5, "No tab found for session store tag.");
+   }
+   browser.__tb_tor_fetched = tag;
+}
+
 function torbutton_tag_new_browser(browser, tor_tag, no_plugins) {
     if (!tor_tag && no_plugins) {
         browser.docShell.allowPlugins = tor_tag;
@@ -2260,7 +2284,7 @@ function torbutton_tag_new_browser(browser, tor_tag, no_plugins) {
     // Only tag new windows
     if (typeof(browser.__tb_tor_fetched) == 'undefined') {
         torbutton_log(3, "Tagging new window: "+tor_tag);
-        browser.__tb_tor_fetched = !tor_tag;
+        torbutton_apply_tab_tag(browser, !tor_tag);
 
         // XXX: Do we need to remove this listener on tab close?
         // No, but we probably do need to remove it on window close!
@@ -2333,7 +2357,7 @@ function torbutton_conditional_set(state) {
                     }
                 }
             }
-            b.__tb_tor_fetched = state;
+            torbutton_apply_tab_tag(b, state);
         }
     }
 
@@ -3560,7 +3584,7 @@ function torbutton_update_tags(win, new_loc) {
             // Defensive programming to tag this window here to 
             // an alternate tor state. It wil lmake this window totally
             // useless, but that is better than some undefined state
-            browser.__tb_tor_fetched = tor_tag;
+            torbutton_apply_tab_tag(browser, tor_tag);
         }
         if(browser.__tb_tor_fetched != !tor_tag) {
             // Purge session history every time we fetch a new doc 
@@ -3591,7 +3615,7 @@ function torbutton_update_tags(win, new_loc) {
             }
         }
 
-        browser.__tb_tor_fetched = !tor_tag;
+        torbutton_apply_tab_tag(browser, !tor_tag);
         browser.docShell.allowPlugins = tor_tag || !kill_plugins;
 
         /* We want to disable allowDNSPrefetch on Tor-loaded tabs
diff --git a/src/components/nsSessionStore35.diff b/src/components/nsSessionStore35.diff
deleted file mode 100644
index 4bacd75..0000000
--- a/src/components/nsSessionStore35.diff
+++ /dev/null
@@ -1,108 +0,0 @@
---- ./nsSessionStore.js	2009-11-02 15:01:50.000000000 -0800
-+++ ./nsSessionStore35.js	2009-11-18 17:02:54.000000000 -0800
-@@ -106,7 +106,20 @@
-   "Subframes", "Plugins", "Javascript", "MetaRedirects", "Images"
- ];
- 
--Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-+var is_FF35 = false;
-+var appInfo = Components.classes["@mozilla.org/xre/app-info;1"]
-+  .getService(Components.interfaces.nsIXULAppInfo);
-+var versionChecker = Components.classes["@mozilla.org/xpcom/version-comparator;1"]
-+  .getService(Components.interfaces.nsIVersionComparator);
-+
-+if(versionChecker.compare(appInfo.version, "3.5a1") >= 0
-+        && versionChecker.compare(appInfo.version, "3.6a1") <= 0) {
-+    is_FF35 = true;
-+    Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-+} else {
-+    is_FF35 = false;
-+    var XPCOMUtils = { generateQI: function() { return null; } };
-+}
- 
- function debug(aMsg) {
-   aMsg = ("SessionStore: " + aMsg).replace(/\S{80}/g, "$&\n");
-@@ -117,6 +130,9 @@
- /* :::::::: The Service ::::::::::::::: */
- 
- function SessionStoreService() {
-+  this.logger = Components.classes["@torproject.org/torbutton-logger;1"]
-+      .getService(Components.interfaces.nsISupports).wrappedJSObject;
-+  this.logger.log(3, "Component Load: New SessionStoreService @mozilla.org/browser/sessionstore;1");
- }
- 
- SessionStoreService.prototype = {
-@@ -1064,9 +1080,26 @@
-     var tabbrowser = aWindow.getBrowser();
-     var tabs = tabbrowser.mTabs;
-     var tabsData = this._windows[aWindow.__SSi].tabs = [];
--    
--    for (var i = 0; i < tabs.length; i++)
--      tabsData.push(this._collectTabData(tabs[i]));
-+    var prefs = Components.classes["@mozilla.org/preferences-service;1"]
-+        .getService(Components.interfaces.nsIPrefBranch);
-+    var bypass_tor = prefs.getBoolPref("extensions.torbutton.notor_sessionstore");
-+    var bypass_nontor = prefs.getBoolPref("extensions.torbutton.nonontor_sessionstore");
-+  
-+    for (var i = 0; i < tabs.length; i++) {
-+      if(typeof(tabs[i].linkedBrowser.__tb_tor_fetched) != "undefined") {
-+          if(bypass_tor && tabs[i].linkedBrowser.__tb_tor_fetched) {
-+              this.logger.log(2, "Skipping saving tor tab: "+tabs[i].linkedBrowser.currentURI.spec);
-+              continue; 
-+          }
-+          if(bypass_nontor && !tabs[i].linkedBrowser.__tb_tor_fetched) {
-+              this.logger.log(2, "Skipping saving non-tor tab: "+tabs[i].linkedBrowser.currentURI.spec);
-+              continue; 
-+          }
-+      }
-+      this.logger.log(2, "Saving tab: "+tabs[i].linkedBrowser.currentURI.spec);
-+      var tabData = this._collectTabData(tabs[i]);
-+      if(tabData) tabsData.push(tabData);
-+    }
-     
-     this._windows[aWindow.__SSi].selected = tabbrowser.mTabBox.selectedIndex + 1;
-   },
-@@ -1102,6 +1135,7 @@
-         browser.parentNode.__SS_data.entries[history.index] &&
-         history.index < this._sessionhistory_max_entries - 1 && !aFullData) {
-       tabData = browser.parentNode.__SS_data;
-+      if(!tabData) return null;
-       tabData.index = history.index + 1;
-     }
-     else if (history && history.count > 0) {
-@@ -1336,7 +1370,8 @@
-     for (var i = 0; i < browsers.length; i++) {
-       try {
-         var tabData = this._windows[aWindow.__SSi].tabs[i];
--        if (browsers[i].parentNode.__SS_data &&
-+        if (!tabData || tabData.entries.length == 0 ||
-+            browsers[i].parentNode.__SS_data &&
-             browsers[i].parentNode.__SS_data._tabStillLoading)
-           continue; // ignore incompletely initialized tabs
-         this._updateTextAndScrollDataForTab(aWindow, browsers[i], tabData);
-@@ -2919,5 +2954,23 @@
-          (prevChar == "." || prevChar == "/");
- }
- 
--function NSGetModule(aComMgr, aFileSpec)
--  XPCOMUtils.generateModule([SessionStoreService]);
-+const NoModule = {
-+  getClassObject: function(aCompMgr, aCID, aIID) {
-+    Components.returnCode = Cr.NS_ERROR_NOT_REGISTERED;
-+    return null;
-+  },
-+  registerSelf: function(aCompMgr, aFileSpec, aLocation, aType) { return; },
-+  unregisterSelf: function(aCompMgr, aLocation, aType) { return; },
-+  canUnload: function(aCompMgr) { return true; }
-+};
-+
-+
-+function NSGetModule(aComMgr, aFileSpec) {
-+  var prefs = Components.classes["@mozilla.org/preferences-service;1"]
-+        .getService(Components.interfaces.nsIPrefBranch);
-+  if(is_FF35) {
-+      return XPCOMUtils.generateModule([SessionStoreService]);
-+  } else {
-+      return NoModule;
-+  }
-+}
diff --git a/src/components/nsSessionStore35.js b/src/components/nsSessionStore35.js
deleted file mode 100644
index 6bf41c2..0000000
--- a/src/components/nsSessionStore35.js
+++ /dev/null
@@ -1,2976 +0,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 at gmail.com>
- * Portions created by the Initial Developer are Copyright (C) 2006
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Dietrich Ayala <dietrich at mozilla.com>
- *   Ehsan Akhgari <ehsan.akhgari at gmail.com>
- *   Paul O’Shannessy <paul at oshannessy.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 ***** */
-
-/**
- * Session Storage and Restoration
- * 
- * Overview
- * This service keeps track of a user's session, storing the various bits
- * required to return the browser to its current state. The relevant data is 
- * stored in memory, and is periodically saved to disk in a file in the 
- * profile directory. The service is started at first window load, in
- * delayedStartup, and will restore the session from the data received from
- * the nsSessionStartup service.
- */
-
-/* :::::::: Constants and Helpers ::::::::::::::: */
-
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-const Cr = Components.results;
-const Cu = Components.utils;
-
-const STATE_STOPPED = 0;
-const STATE_RUNNING = 1;
-const STATE_QUITTING = -1;
-
-const STATE_STOPPED_STR = "stopped";
-const STATE_RUNNING_STR = "running";
-
-const PRIVACY_NONE = 0;
-const PRIVACY_ENCRYPTED = 1;
-const PRIVACY_FULL = 2;
-
-const NOTIFY_WINDOWS_RESTORED = "sessionstore-windows-restored";
-
-const INTERFACES = [Ci.nsISessionStore, Ci.nsIDOMEventListener,
-                    Ci.nsIObserver, Ci.nsISupportsWeakReference,
-                    Ci.nsISessionStore_MOZILLA_1_9_1, Ci.nsIClassInfo];
-
-// global notifications observed
-const OBSERVING = [
-  "domwindowopened", "domwindowclosed",
-  "quit-application-requested", "quit-application-granted",
-  "quit-application", "browser:purge-session-history",
-  "private-browsing", "browser:purge-domain-data",
-  "private-browsing-change-granted"
-];
-
-/*
-XUL Window properties to (re)store
-Restored in restoreDimensions()
-*/
-const WINDOW_ATTRIBUTES = ["width", "height", "screenX", "screenY", "sizemode"];
-
-/* 
-Hideable window features to (re)store
-Restored in restoreWindowFeatures()
-*/
-const WINDOW_HIDEABLE_FEATURES = [
-  "menubar", "toolbar", "locationbar", 
-  "personalbar", "statusbar", "scrollbars"
-];
-
-/*
-docShell capabilities to (re)store
-Restored in restoreHistory()
-eg: browser.docShell["allow" + aCapability] = false;
-*/
-const CAPABILITIES = [
-  "Subframes", "Plugins", "Javascript", "MetaRedirects", "Images"
-];
-
-var is_FF35 = false;
-var appInfo = Components.classes["@mozilla.org/xre/app-info;1"]
-  .getService(Components.interfaces.nsIXULAppInfo);
-var versionChecker = Components.classes["@mozilla.org/xpcom/version-comparator;1"]
-  .getService(Components.interfaces.nsIVersionComparator);
-
-if(versionChecker.compare(appInfo.version, "3.5a1") >= 0
-        && versionChecker.compare(appInfo.version, "3.6a1") <= 0) {
-    is_FF35 = true;
-    Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-} else {
-    is_FF35 = false;
-    var XPCOMUtils = { generateQI: function() { return null; } };
-}
-
-function debug(aMsg) {
-  aMsg = ("SessionStore: " + aMsg).replace(/\S{80}/g, "$&\n");
-  Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService)
-                                     .logStringMessage(aMsg);
-}
-
-/* :::::::: The Service ::::::::::::::: */
-
-function SessionStoreService() {
-  this.logger = Components.classes["@torproject.org/torbutton-logger;1"]
-      .getService(Components.interfaces.nsISupports).wrappedJSObject;
-  this.logger.log(3, "Component Load: New SessionStoreService @mozilla.org/browser/sessionstore;1");
-}
-
-SessionStoreService.prototype = {
-  classDescription: "Browser Session Store Service",
-  contractID: "@mozilla.org/browser/sessionstore;1",
-  classID: Components.ID("{5280606b-2510-4fe0-97ef-9b5a22eafe6b}"),
-  QueryInterface: XPCOMUtils.generateQI(INTERFACES),
-
-  // extra requirements for nsIClassInfo
-  getInterfaces: function sss_getInterfaces(aCountRef) {
-    aCountRef.value = INTERFACES.length;
-    return INTERFACES;
-  },
-  getHelperForLanguage: function sss_getHelperForLanguage (aLanguage) null,
-  implementationLanguage: Ci.nsIProgrammingLanguage.JAVASCRIPT,
-  flags: Ci.nsIClassInfo.SINGLETON,
-
-  // 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
-  _resume_from_crash: true,
-
-  // During the initial restore tracks the number of windows yet to be restored
-  _restoreCount: 0,
-
-  // time in milliseconds (Date.now()) when the session was last written to file
-  _lastSaveTime: 0, 
-
-  // states for all currently opened windows
-  _windows: {},
-
-  // states for all recently closed windows
-  _closedWindows: [],
-
-  // 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,
-
-  // whether we are in private browsing mode
-  _inPrivateBrowsing: false,
-
-  // whether we clearing history on shutdown
-  _clearingOnShutdown: false,
-
-/* ........ 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
-      // SessionStore are really tracked by it
-      if (aWindow && (!aWindow.__SSi || !this._windows[aWindow.__SSi]))
-        this.onLoad(aWindow);
-      return;
-    }
-
-    this._prefBranch = Cc["@mozilla.org/preferences-service;1"].
-                       getService(Ci.nsIPrefService).getBranch("browser.");
-    this._prefBranch.QueryInterface(Ci.nsIPrefBranch2);
-
-    var observerService = Cc["@mozilla.org/observer-service;1"].
-                          getService(Ci.nsIObserverService);
-
-    OBSERVING.forEach(function(aTopic) {
-      observerService.addObserver(this, aTopic, true);
-    }, this);
-
-    var pbs = Cc["@mozilla.org/privatebrowsing;1"].
-              getService(Ci.nsIPrivateBrowsingService);
-    this._inPrivateBrowsing = pbs.privateBrowsingEnabled;
-
-    // get interval from prefs - used often, so caching/observing instead of fetching on-demand
-    this._interval = this._prefBranch.getIntPref("sessionstore.interval");
-    this._prefBranch.addObserver("sessionstore.interval", this, true);
-    
-    // get crash recovery state from prefs and allow for proper reaction to state changes
-    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("sessionstore.max_tabs_undo", this, true);
-    this._prefBranch.addObserver("sessionstore.max_windows_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 = Cc["@mozilla.org/file/directory_service;1"].
-                     getService(Ci.nsIProperties);
-    this._sessionFile = dirService.get("ProfD", Ci.nsILocalFile);
-    this._sessionFileBackup = this._sessionFile.clone();
-    this._sessionFile.append("sessionstore.js");
-    this._sessionFileBackup.append("sessionstore.bak");
-
-    // get string containing session state
-    var iniString;
-    try {
-      var ss = Cc["@mozilla.org/browser/sessionstartup;1"].
-               getService(Ci.nsISessionStartup);
-      if (ss.doRestore())
-        iniString = ss.state;
-    }
-    catch(ex) { dump(ex + "\n"); } // no state to restore, which is ok
-
-    if (iniString) {
-      try {
-        // parse the session state into JS objects
-        this._initialState = this._safeEval("(" + iniString + ")");
-        
-        let lastSessionCrashed =
-          this._initialState.session && this._initialState.session.state &&
-          this._initialState.session.state == STATE_RUNNING_STR;
-        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
-        delete this._initialState.windows[0].hidden;
-      }
-      catch (ex) { debug("The session file is invalid: " + ex); }
-    }
-
-    // remove the session data files if crash recovery is disabled
-    if (!this._resume_from_crash)
-      this._clearDisk();
-    else { // create a backup if the session data file exists
-      try {
-        if (this._sessionFileBackup.exists())
-          this._sessionFileBackup.remove(false);
-        if (this._sessionFile.exists())
-          this._sessionFile.copyTo(null, this._sessionFileBackup.leafName);
-      }
-      catch (ex) { Cu.reportError(ex); } // file was write-locked?
-    }
-
-    // at this point, we've as good as resumed the session, so we can
-    // clear the resume_session_once flag, if it's set
-    if (this._loadState != STATE_QUITTING &&
-        this._prefBranch.getBoolPref("sessionstore.resume_session_once"))
-      this._prefBranch.setBoolPref("sessionstore.resume_session_once", false);
-    
-    // As this is called at delayedStartup, restoration must be initiated here
-    this.onLoad(aWindow);
-  },
-
-  /**
-   * Called on application shutdown, after notifications:
-   * quit-application-granted, quit-application
-   */
-  _uninit: function sss_uninit() {
-    if (this._doResumeSession()) { // save all data for session resuming 
-      this.saveState(true);
-    }
-    else { // discard all session related data 
-      this._clearDisk();
-    }
-    // Make sure to break our cycle with the save timer
-    if (this._saveTimer) {
-      this._saveTimer.cancel();
-      this._saveTimer = null;
-    }
-  },
-
-  /**
-   * Handle notifications
-   */
-  observe: function sss_observe(aSubject, aTopic, aData) {
-    // for event listeners
-    var _this = this;
-
-    switch (aTopic) {
-    case "domwindowopened": // catch new windows
-      aSubject.addEventListener("load", function(aEvent) {
-        aEvent.currentTarget.removeEventListener("load", arguments.callee, false);
-        _this.onLoad(aEvent.currentTarget);
-        }, false);
-      break;
-    case "domwindowclosed": // catch closed windows
-      this.onClose(aSubject);
-      break;
-    case "quit-application-requested":
-      // get a current snapshot of all windows
-      this._forEachBrowserWindow(function(aWindow) {
-        this._collectWindowData(aWindow);
-      });
-      this._dirtyWindows = [];
-      break;
-    case "quit-application-granted":
-      // freeze the data at what we've got (ignoring closing windows)
-      this._loadState = STATE_QUITTING;
-      break;
-    case "quit-application":
-      if (aData == "restart") {
-        this._prefBranch.setBoolPref("sessionstore.resume_session_once", true);
-        this._clearingOnShutdown = false;
-      }
-      this._loadState = STATE_QUITTING; // just to be sure
-      this._uninit();
-      break;
-    case "browser:purge-session-history": // catch sanitization 
-      let openWindows = {};
-      this._forEachBrowserWindow(function(aWindow) {
-        Array.forEach(aWindow.getBrowser().browsers, function(aBrowser) {
-          delete aBrowser.parentNode.__SS_data;
-        });
-        openWindows[aWindow.__SSi] = true;
-      });
-      // also clear all data about closed tabs and windows
-      for (let ix in this._windows) {
-        if (ix in openWindows)
-          this._windows[ix]._closedTabs = [];
-        else
-          delete this._windows[ix];
-      }
-      // also clear all data about closed windows
-      this._closedWindows = [];
-      this._clearDisk();
-      // give the tabbrowsers a chance to clear their histories first
-      var win = this._getMostRecentBrowserWindow();
-      if (win)
-        win.setTimeout(function() { _this.saveState(true); }, 0);
-      else if (this._loadState == STATE_RUNNING)
-        this.saveState(true);
-      // Delete the private browsing backed up state, if any
-      if ("_stateBackup" in this)
-        delete this._stateBackup;
-      if (this._loadState == STATE_QUITTING)
-        this._clearingOnShutdown = true;
-      break;
-    case "browser:purge-domain-data":
-      // does a session history entry contain a url for the given domain?
-      function containsDomain(aEntry) {
-        try {
-          if (this._getURIFromString(aEntry.url).host.hasRootDomain(aData))
-            return true;
-        }
-        catch (ex) { /* url had no host at all */ }
-        return aEntry.children && aEntry.children.some(containsDomain, this);
-      }
-      // remove all closed tabs containing a reference to the given domain
-      for (let ix in this._windows) {
-        let closedTabs = this._windows[ix]._closedTabs;
-        for (let i = closedTabs.length - 1; i >= 0; i--) {
-          if (closedTabs[i].state.entries.some(containsDomain, this))
-            closedTabs.splice(i, 1);
-        }
-      }
-      // remove all open & closed tabs containing a reference to the given
-      // domain in closed windows
-      for (let ix = this._closedWindows.length - 1; ix >= 0; ix--) {
-        let closedTabs = this._closedWindows[ix]._closedTabs;
-        let openTabs = this._closedWindows[ix].tabs;
-        let openTabCount = openTabs.length;
-        for (let i = closedTabs.length - 1; i >= 0; i--)
-          if (closedTabs[i].state.entries.some(containsDomain, this))
-            closedTabs.splice(i, 1);
-        for (let j = openTabs.length - 1; j >= 0; j--) {
-          if (openTabs[j].entries.some(containsDomain, this)) {
-            openTabs.splice(j, 1);
-            if (this._closedWindows[ix].selected > j)
-              this._closedWindows[ix].selected--;
-          }
-        }
-        if (openTabs.length == 0) {
-          this._closedWindows.splice(ix, 1);
-        }
-        else if (openTabs.length != openTabCount) {
-          // Adjust the window's title if we removed an open tab
-          let selectedTab = openTabs[this._closedWindows[ix].selected - 1];
-          // some duplication from restoreHistory - make sure we get the correct title
-          let activeIndex = (selectedTab.index || selectedTab.entries.length) - 1;
-          if (activeIndex >= selectedTab.entries.length)
-            activeIndex = selectedTab.entries.length - 1;
-          this._closedWindows[ix].title = selectedTab.entries[activeIndex].title;
-        }
-      }
-      if (this._loadState == STATE_RUNNING)
-        this.saveState(true);
-      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
-      case "sessionstore.max_tabs_undo":
-        for (let ix in this._windows) {
-          this._windows[ix]._closedTabs.splice(this._prefBranch.getIntPref("sessionstore.max_tabs_undo"));
-        }
-        break;
-      case "sessionstore.max_windows_undo":
-        this._capClosedWindows();
-        break;
-      case "sessionstore.interval":
-        this._interval = this._prefBranch.getIntPref("sessionstore.interval");
-        // reset timer and save
-        if (this._saveTimer) {
-          this._saveTimer.cancel();
-          this._saveTimer = null;
-        }
-        this.saveStateDelayed(null, -1);
-        break;
-      case "sessionstore.resume_from_crash":
-        this._resume_from_crash = this._prefBranch.getBoolPref("sessionstore.resume_from_crash");
-        // either create the file with crash recovery information or remove it
-        // (when _loadState is not STATE_RUNNING, that file is used for session resuming instead)
-        if (this._resume_from_crash)
-          this.saveState(true);
-        else if (this._loadState == STATE_RUNNING)
-          this._clearDisk();
-        break;
-      }
-      break;
-    case "timer-callback": // timer call back for delayed saving
-      this._saveTimer = null;
-      this.saveState();
-      break;
-    case "private-browsing":
-      switch (aData) {
-      case "enter":
-        this._inPrivateBrowsing = true;
-        break;
-      case "exit":
-        aSubject.QueryInterface(Ci.nsISupportsPRBool);
-        let quitting = aSubject.data;
-        if (quitting) {
-          // save the backed up state with session set to stopped,
-          // otherwise resuming next time would look like a crash
-          if ("_stateBackup" in this) {
-            var oState = this._stateBackup;
-            oState.session = { state: STATE_STOPPED_STR };
-
-            this._saveStateObject(oState);
-          }
-          // make sure to restore the non-private session upon resuming
-          this._prefBranch.setBoolPref("sessionstore.resume_session_once", true);
-        }
-        else
-          this._inPrivateBrowsing = false;
-        delete this._stateBackup;
-        break;
-      }
-      break;
-    case "private-browsing-change-granted":
-      if (aData == "enter") {
-        this.saveState(true);
-        this._stateBackup = this._safeEval(this._getCurrentState(true).toSource());
-      }
-      break;
-    }
-  },
-
-/* ........ Window Event Handlers .............. */
-
-  /**
-   * Implement nsIDOMEventListener for handling various window and tab events
-   */
-  handleEvent: function sss_handleEvent(aEvent) {
-    switch (aEvent.type) {
-      case "load":
-      case "pageshow":
-        this.onTabLoad(aEvent.currentTarget.ownerDocument.defaultView, aEvent.currentTarget, aEvent);
-        break;
-      case "change":
-      case "input":
-      case "DOMAutoComplete":
-        this.onTabInput(aEvent.currentTarget.ownerDocument.defaultView, aEvent.currentTarget);
-        break;
-      case "scroll":
-        this.onTabScroll(aEvent.currentTarget.ownerDocument.defaultView);
-        break;
-      case "TabOpen":
-      case "TabClose":
-        let target = aEvent.originalTarget;
-        let panelID = target.linkedPanel;
-        let ownerDoc = target.ownerDocument;
-        let bindingParent = ownerDoc.getBindingParent(target);
-        let tabpanel =
-          ownerDoc.getAnonymousElementByAttribute(bindingParent, "id",
-                                                  panelID);
-        if (aEvent.type == "TabOpen") {
-          this.onTabAdd(aEvent.currentTarget.ownerDocument.defaultView, tabpanel);
-        }
-        else {
-          // aEvent.detail determines if the tab was closed by moving to a different window
-          if (!aEvent.detail)
-            this.onTabClose(aEvent.currentTarget.ownerDocument.defaultView, aEvent.originalTarget);
-          this.onTabRemove(aEvent.currentTarget.ownerDocument.defaultView, tabpanel);
-        }
-        break;
-      case "TabSelect":
-        var tabpanels = aEvent.currentTarget.mPanelContainer;
-        this.onTabSelect(aEvent.currentTarget.ownerDocument.defaultView, tabpanels);
-        break;
-    }
-  },
-
-  /**
-   * If it's the first window load since app start...
-   * - determine if we're reloading after a crash or a forced-restart
-   * - restore window state
-   * - restart downloads
-   * Set up event listeners for this window's tabs
-   * @param aWindow
-   *        Window reference
-   */
-  onLoad: function sss_onLoad(aWindow) {
-    // return if window has already been initialized
-    if (aWindow && aWindow.__SSi && this._windows[aWindow.__SSi])
-      return;
-
-    // ignore non-browser windows and windows opened while shutting down
-    if (aWindow.document.documentElement.getAttribute("windowtype") != "navigator:browser" ||
-      this._loadState == STATE_QUITTING)
-      return;
-
-    // assign it a unique identifier (timestamp)
-    aWindow.__SSi = "window" + Date.now();
-
-    // and create its data object
-    this._windows[aWindow.__SSi] = { tabs: [], selected: 0, _closedTabs: [] };
-    if (!aWindow.toolbar.visible)
-      this._windows[aWindow.__SSi].isPopup = true;
-    
-    // perform additional initialization when the first window is loading
-    if (this._loadState == STATE_STOPPED) {
-      this._loadState = STATE_RUNNING;
-      this._lastSaveTime = Date.now();
-      
-      // 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
-        this._initialState._firstTabs = true;
-        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 = Cc["@mozilla.org/observer-service;1"].
-                              getService(Ci.nsIObserverService);
-        observerService.notifyObservers(null, NOTIFY_WINDOWS_RESTORED, "");
-        
-//@line 591 "/builds/slave/linux_build/build/browser/components/sessionstore/src/nsSessionStore.js"
-        // the next delayed save request should execute immediately
-        this._lastSaveTime -= this._interval;
-//@line 598 "/builds/slave/linux_build/build/browser/components/sessionstore/src/nsSessionStore.js"
-      }
-    }
-    // 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;
-    
-    // add tab change listeners to all already existing tabs
-    for (var i = 0; i < tabpanels.childNodes.length; i++) {
-      this.onTabAdd(aWindow, tabpanels.childNodes[i], true);
-    }
-    // notification of tab add/remove/selection
-    tabbrowser.addEventListener("TabOpen", this, true);
-    tabbrowser.addEventListener("TabClose", this, true);
-    tabbrowser.addEventListener("TabSelect", this, true);
-  },
-
-  /**
-   * On window close...
-   * - remove event listeners from tabs
-   * - save all window data
-   * @param aWindow
-   *        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;
-    }
-    
-    if (this.windowToFocus && this.windowToFocus == aWindow) {
-      delete this.windowToFocus;
-    }
-    
-    var tabbrowser = aWindow.getBrowser();
-    var tabpanels = tabbrowser.mPanelContainer;
-
-    tabbrowser.removeEventListener("TabOpen", this, true);
-    tabbrowser.removeEventListener("TabClose", this, true);
-    tabbrowser.removeEventListener("TabSelect", this, true);
-    
-    let winData = this._windows[aWindow.__SSi];
-    if (this._loadState == STATE_RUNNING) { // window not closed during a regular shut-down 
-      // update all window data for a last time
-      this._collectWindowData(aWindow);
-      
-      if (isFullyLoaded) {
-        winData.title = aWindow.content.document.title || tabbrowser.selectedTab.label;
-        winData.title = this._replaceLoadingTitle(winData.title, tabbrowser,
-                                                  tabbrowser.selectedTab);
-        this._updateCookies([winData]);
-      }
-      
-      // save the window if it has multiple tabs or a single tab with entries
-      if (winData.tabs.length > 1 ||
-          (winData.tabs.length == 1 && winData.tabs[0].entries.length > 0)) {
-        this._closedWindows.unshift(winData);
-        this._capClosedWindows();
-      }
-      
-      // clear this window from the list
-      delete this._windows[aWindow.__SSi];
-      
-      // save the state without this window to disk
-      this.saveStateDelayed();
-    }
-    
-    for (var i = 0; i < tabpanels.childNodes.length; i++) {
-      this.onTabRemove(aWindow, tabpanels.childNodes[i], true);
-    }
-    
-    // cache the window state until the window is completely gone
-    aWindow.__SS_dyingCache = winData;
-    
-    delete aWindow.__SSi;
-  },
-
-  /**
-   * set up listeners for a new tab
-   * @param aWindow
-   *        Window reference
-   * @param aPanel
-   *        TabPanel reference
-   * @param aNoNotification
-   *        bool Do not save state if we're updating an existing tab
-   */
-  onTabAdd: function sss_onTabAdd(aWindow, aPanel, aNoNotification) {
-    aPanel.addEventListener("load", this, true);
-    aPanel.addEventListener("pageshow", this, true);
-    aPanel.addEventListener("change", this, true);
-    aPanel.addEventListener("input", this, true);
-    aPanel.addEventListener("DOMAutoComplete", this, true);
-    aPanel.addEventListener("scroll", this, true);
-    
-    if (!aNoNotification) {
-      this.saveStateDelayed(aWindow);
-    }
-  },
-
-  /**
-   * remove listeners for a tab
-   * @param aWindow
-   *        Window reference
-   * @param aPanel
-   *        TabPanel reference
-   * @param aNoNotification
-   *        bool Do not save state if we're updating an existing tab
-   */
-  onTabRemove: function sss_onTabRemove(aWindow, aPanel, aNoNotification) {
-    aPanel.removeEventListener("load", this, true);
-    aPanel.removeEventListener("pageshow", this, true);
-    aPanel.removeEventListener("change", this, true);
-    aPanel.removeEventListener("input", this, true);
-    aPanel.removeEventListener("DOMAutoComplete", this, true);
-    aPanel.removeEventListener("scroll", this, true);
-    
-    delete aPanel.__SS_data;
-    
-    if (!aNoNotification) {
-      this.saveStateDelayed(aWindow);
-    }
-  },
-
-  /**
-   * When a tab closes, collect its properties
-   * @param aWindow
-   *        Window reference
-   * @param aTab
-   *        TabPanel reference
-   */
-  onTabClose: function sss_onTabClose(aWindow, aTab) {
-    // notify the tabbrowser that the tab state will be retrieved for the last time
-    // (so that extension authors can easily set data on soon-to-be-closed tabs)
-    var event = aWindow.document.createEvent("Events");
-    event.initEvent("SSTabClosing", true, false);
-    aTab.dispatchEvent(event);
-    
-    var maxTabsUndo = this._prefBranch.getIntPref("sessionstore.max_tabs_undo");
-    // don't update our internal state if we don't have to
-    if (maxTabsUndo == 0) {
-      return;
-    }
-    
-    // make sure that the tab related data is up-to-date
-    var tabState = this._collectTabData(aTab);
-    this._updateTextAndScrollDataForTab(aWindow, aTab.linkedBrowser, tabState);
-
-    // store closed-tab data for undo
-    if (tabState.entries.length > 0) {
-      let tabTitle = aTab.label;
-      let tabbrowser = aWindow.gBrowser;
-      tabTitle = this._replaceLoadingTitle(tabTitle, tabbrowser, aTab);
-      
-      this._windows[aWindow.__SSi]._closedTabs.unshift({
-        state: tabState,
-        title: tabTitle,
-        image: aTab.getAttribute("image"),
-        pos: aTab._tPos
-      });
-      var length = this._windows[aWindow.__SSi]._closedTabs.length;
-      if (length > maxTabsUndo)
-        this._windows[aWindow.__SSi]._closedTabs.splice(maxTabsUndo, length - maxTabsUndo);
-    }
-  },
-
-  /**
-   * When a tab loads, save state.
-   * @param aWindow
-   *        Window reference
-   * @param aPanel
-   *        TabPanel reference
-   * @param aEvent
-   *        Event obj
-   */
-  onTabLoad: function sss_onTabLoad(aWindow, aPanel, aEvent) { 
-    // react on "load" and solitary "pageshow" events (the first "pageshow"
-    // following "load" is too late for deleting the data caches)
-    if (aEvent.type != "load" && !aEvent.persisted) {
-      return;
-    }
-    
-    delete aPanel.__SS_data;
-    this.saveStateDelayed(aWindow);
-    
-    // attempt to update the current URL we send in a crash report
-    this._updateCrashReportURL(aWindow);
-  },
-
-  /**
-   * Called when a tabpanel sends the "input" notification 
-   * @param aWindow
-   *        Window reference
-   * @param aPanel
-   *        TabPanel reference
-   */
-  onTabInput: function sss_onTabInput(aWindow, aPanel) {
-    if (aPanel.__SS_data)
-      delete aPanel.__SS_data._formDataSaved;
-    
-    this.saveStateDelayed(aWindow, 3000);
-  },
-
-  /**
-   * Called when a tabpanel sends a "scroll" notification 
-   * @param aWindow
-   *        Window reference
-   */
-  onTabScroll: function sss_onTabScroll(aWindow) {
-    this.saveStateDelayed(aWindow, 3000);
-  },
-
-  /**
-   * When a tab is selected, save session data
-   * @param aWindow
-   *        Window reference
-   * @param aPanels
-   *        TabPanel reference
-   */
-  onTabSelect: function sss_onTabSelect(aWindow, aPanels) {
-    if (this._loadState == STATE_RUNNING) {
-      this._windows[aWindow.__SSi].selected = aPanels.selectedIndex;
-      this.saveStateDelayed(aWindow);
-
-      // attempt to update the current URL we send in a crash report
-      this._updateCrashReportURL(aWindow);
-    }
-  },
-
-/* ........ nsISessionStore API .............. */
-
-  getBrowserState: function sss_getBrowserState() {
-    return this._toJSONString(this._getCurrentState());
-  },
-
-  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 = Cr.NS_ERROR_INVALID_ARG);
-    
-    var window = this._getMostRecentBrowserWindow();
-    if (!window) {
-      this._openWindowWithState(state);
-      return;
-    }
-
-    // close all other browser windows
-    this._forEachBrowserWindow(function(aWindow) {
-      if (aWindow != window) {
-        aWindow.close();
-      }
-    });
-
-    // make sure closed window data isn't kept
-    this._closedWindows = [];
-
-    // restore to the given state
-    this.restoreWindow(window, state, true);
-  },
-
-  getWindowState: function sss_getWindowState(aWindow) {
-    if (!aWindow.__SSi && !aWindow.__SS_dyingCache)
-      throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
-    
-    if (!aWindow.__SSi)
-      return this._toJSONString({ windows: [aWindow.__SS_dyingCache] });
-    return this._toJSONString(this._getWindowState(aWindow));
-  },
-
-  setWindowState: function sss_setWindowState(aWindow, aState, aOverwrite) {
-    if (!aWindow.__SSi)
-      throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
-    
-    this.restoreWindow(aWindow, "(" + aState + ")", aOverwrite);
-  },
-
-  getTabState: function sss_getTabState(aTab) {
-    if (!aTab.ownerDocument || !aTab.ownerDocument.defaultView.__SSi)
-      throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
-    
-    var tabState = this._collectTabData(aTab);
-    
-    var window = aTab.ownerDocument.defaultView;
-    this._updateTextAndScrollDataForTab(window, aTab.linkedBrowser, tabState);
-    
-    return this._toJSONString(tabState);
-  },
-
-  setTabState: function sss_setTabState(aTab, aState) {
-    var tabState = this._safeEval("(" + aState + ")");
-    if (!tabState.entries || !aTab.ownerDocument || !aTab.ownerDocument.defaultView.__SSi)
-      throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
-    
-    var window = aTab.ownerDocument.defaultView;
-    this.restoreHistoryPrecursor(window, [aTab], [tabState], 0, 0, 0);
-  },
-
-  duplicateTab: function sss_duplicateTab(aWindow, aTab) {
-    if (!aTab.ownerDocument || !aTab.ownerDocument.defaultView.__SSi ||
-        !aWindow.getBrowser)
-      throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
-    
-    var tabState = this._collectTabData(aTab, true);
-    var sourceWindow = aTab.ownerDocument.defaultView;
-    this._updateTextAndScrollDataForTab(sourceWindow, aTab.linkedBrowser, tabState, true);
-    
-    var newTab = aWindow.getBrowser().addTab();
-    this.restoreHistoryPrecursor(aWindow, [newTab], [tabState], 0, 0, 0);
-    
-    return newTab;
-  },
-
-  getClosedTabCount: function sss_getClosedTabCount(aWindow) {
-    if (!aWindow.__SSi && aWindow.__SS_dyingCache)
-      return aWindow.__SS_dyingCache._closedTabs.length;
-    if (!aWindow.__SSi)
-      // XXXzeniko shouldn't we throw here?
-      return 0; // not a browser window, or not otherwise tracked by SS.
-    
-    return this._windows[aWindow.__SSi]._closedTabs.length;
-  },
-
-  getClosedTabData: function sss_getClosedTabDataAt(aWindow) {
-    if (!aWindow.__SSi && !aWindow.__SS_dyingCache)
-      throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
-    
-    if (!aWindow.__SSi)
-      return this._toJSONString(aWindow.__SS_dyingCache._closedTabs);
-    return this._toJSONString(this._windows[aWindow.__SSi]._closedTabs);
-  },
-
-  undoCloseTab: function sss_undoCloseTab(aWindow, aIndex) {
-    if (!aWindow.__SSi)
-      throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
-    
-    var closedTabs = this._windows[aWindow.__SSi]._closedTabs;
-
-    // default to the most-recently closed tab
-    aIndex = aIndex || 0;
-    if (!(aIndex in closedTabs))
-      throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
-    
-    // fetch the data of closed tab, while removing it from the array
-    let closedTab = closedTabs.splice(aIndex, 1).shift();
-    let closedTabState = closedTab.state;
-
-    // create a new tab
-    let browser = aWindow.gBrowser;
-    let tab = browser.addTab();
-      
-    // restore the tab's position
-    browser.moveTabTo(tab, closedTab.pos);
-
-    // restore tab content
-    this.restoreHistoryPrecursor(aWindow, [tab], [closedTabState], 1, 0, 0);
-
-    // focus the tab's content area
-    let content = browser.getBrowserForTab(tab).contentWindow;
-    aWindow.setTimeout(function() { content.focus(); }, 0);
-    
-    return tab;
-  },
-
-  getClosedWindowCount: function sss_getClosedWindowCount() {
-    return this._closedWindows.length;
-  },
-
-  getClosedWindowData: function sss_getClosedWindowData() {
-    return this._toJSONString(this._closedWindows);
-  },
-
-  undoCloseWindow: function sss_undoCloseWindow(aIndex) {
-    // default to the most-recently closed window
-    aIndex = aIndex || 0;
-
-    if (!aIndex in this._closedWindows)
-      throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
-
-    // reopen the window
-    let state = { windows: this._closedWindows.splice(aIndex, 1) };
-    let window = this._openWindowWithState(state);
-    this.windowToFocus = window;
-    return window;
-  },
-
-  getWindowValue: function sss_getWindowValue(aWindow, aKey) {
-    if (aWindow.__SSi) {
-      var data = this._windows[aWindow.__SSi].extData || {};
-      return data[aKey] || "";
-    }
-    if (aWindow.__SS_dyingCache) {
-      data = aWindow.__SS_dyingCache.extData || {};
-      return data[aKey] || "";
-    }
-    throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
-  },
-
-  setWindowValue: function sss_setWindowValue(aWindow, aKey, aStringValue) {
-    if (aWindow.__SSi) {
-      if (!this._windows[aWindow.__SSi].extData) {
-        this._windows[aWindow.__SSi].extData = {};
-      }
-      this._windows[aWindow.__SSi].extData[aKey] = aStringValue;
-      this.saveStateDelayed(aWindow);
-    }
-    else {
-      throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
-    }
-  },
-
-  deleteWindowValue: function sss_deleteWindowValue(aWindow, aKey) {
-    if (aWindow.__SSi && this._windows[aWindow.__SSi].extData &&
-        this._windows[aWindow.__SSi].extData[aKey])
-      delete this._windows[aWindow.__SSi].extData[aKey];
-    else
-      throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
-  },
-
-  getTabValue: function sss_getTabValue(aTab, aKey) {
-    var data = aTab.__SS_extdata || {};
-    return data[aKey] || "";
-  },
-
-  setTabValue: function sss_setTabValue(aTab, aKey, aStringValue) {
-    if (!aTab.__SS_extdata) {
-      aTab.__SS_extdata = {};
-    }
-    aTab.__SS_extdata[aKey] = aStringValue;
-    this.saveStateDelayed(aTab.ownerDocument.defaultView);
-  },
-
-  deleteTabValue: function sss_deleteTabValue(aTab, aKey) {
-    if (aTab.__SS_extdata && aTab.__SS_extdata[aKey])
-      delete aTab.__SS_extdata[aKey];
-    else
-      throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
-  },
-
-  persistTabAttribute: function sss_persistTabAttribute(aName) {
-    if (this.xulAttributes.indexOf(aName) != -1)
-      return; // this attribute is already being tracked
-    
-    this.xulAttributes.push(aName);
-    this.saveStateDelayed();
-  },
-
-/* ........ Saving Functionality .............. */
-
-  /**
-   * Store all session data for a window
-   * @param aWindow
-   *        Window reference
-   */
-  _saveWindowHistory: function sss_saveWindowHistory(aWindow) {
-    var tabbrowser = aWindow.getBrowser();
-    var tabs = tabbrowser.mTabs;
-    var tabsData = this._windows[aWindow.__SSi].tabs = [];
-    var prefs = Components.classes["@mozilla.org/preferences-service;1"]
-        .getService(Components.interfaces.nsIPrefBranch);
-    var bypass_tor = prefs.getBoolPref("extensions.torbutton.notor_sessionstore");
-    var bypass_nontor = prefs.getBoolPref("extensions.torbutton.nonontor_sessionstore");
-  
-    for (var i = 0; i < tabs.length; i++) {
-      if(typeof(tabs[i].linkedBrowser.__tb_tor_fetched) != "undefined") {
-          if(bypass_tor && tabs[i].linkedBrowser.__tb_tor_fetched) {
-              this.logger.log(2, "Skipping saving tor tab: "+tabs[i].linkedBrowser.currentURI.spec);
-              continue; 
-          }
-          if(bypass_nontor && !tabs[i].linkedBrowser.__tb_tor_fetched) {
-              this.logger.log(2, "Skipping saving non-tor tab: "+tabs[i].linkedBrowser.currentURI.spec);
-              continue; 
-          }
-      }
-      this.logger.log(2, "Saving tab: "+tabs[i].linkedBrowser.currentURI.spec);
-      var tabData = this._collectTabData(tabs[i]);
-      if(tabData) tabsData.push(tabData);
-    }
-    
-    this._windows[aWindow.__SSi].selected = tabbrowser.mTabBox.selectedIndex + 1;
-  },
-
-  /**
-   * Collect data related to a single tab
-   * @param aTab
-   *        tabbrowser tab
-   * @param aFullData
-   *        always return privacy sensitive data (use with care)
-   * @returns object
-   */
-  _collectTabData: function sss_collectTabData(aTab, aFullData) {
-    var tabData = { entries: [] };
-    var browser = aTab.linkedBrowser;
-    
-    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._tabStillLoading)
-      // use the data to be restored when the tab hasn't been completely loaded
-      return browser.parentNode.__SS_data;
-    
-    var history = null;
-    try {
-      history = browser.sessionHistory;
-    }
-    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] &&
-        history.index < this._sessionhistory_max_entries - 1 && !aFullData) {
-      tabData = browser.parentNode.__SS_data;
-      if(!tabData) return null;
-      tabData.index = history.index + 1;
-    }
-    else if (history && history.count > 0) {
-      for (var j = 0; j < history.count; j++)
-        tabData.entries.push(this._serializeHistoryEntry(history.getEntryAtIndex(j, false),
-                                                         aFullData));
-      tabData.index = history.index + 1;
-
-      // make sure not to cache privacy sensitive data which shouldn't get out
-      if (!aFullData)
-        browser.parentNode.__SS_data = tabData;
-    }
-    else if (browser.currentURI.spec != "about:blank" ||
-             browser.contentDocument.body.hasChildNodes()) {
-      tabData.entries[0] = { url: browser.currentURI.spec };
-      tabData.index = 1;
-    }
-    
-    var disallow = [];
-    for (var i = 0; i < CAPABILITIES.length; i++)
-      if (!browser.docShell["allow" + CAPABILITIES[i]])
-        disallow.push(CAPABILITIES[i]);
-    if (disallow.length > 0)
-      tabData.disallow = disallow.join(",");
-    else if (tabData.disallow)
-      delete tabData.disallow;
-    
-    if (this.xulAttributes.length > 0) {
-      tabData.attributes = {};
-      Array.forEach(aTab.attributes, function(aAttr) {
-        if (this.xulAttributes.indexOf(aAttr.name) > -1)
-          tabData.attributes[aAttr.name] = aAttr.value;
-      }, this);
-    }
-    
-    if (aTab.__SS_extdata)
-      tabData.extData = aTab.__SS_extdata;
-    else if (tabData.extData)
-      delete tabData.extData;
-    
-    if (history && browser.docShell instanceof Ci.nsIDocShell)
-      this._serializeSessionStorage(tabData, history, browser.docShell, aFullData);
-    
-    return tabData;
-  },
-
-  /**
-   * Get an object that is a serialized representation of a History entry
-   * Used for data storage
-   * @param aEntry
-   *        nsISHEntry instance
-   * @param aFullData
-   *        always return privacy sensitive data (use with care)
-   * @returns object
-   */
-  _serializeHistoryEntry: function sss_serializeHistoryEntry(aEntry, aFullData) {
-    var entry = { url: aEntry.URI.spec };
-    
-    if (aEntry.title && aEntry.title != entry.url) {
-      entry.title = aEntry.title;
-    }
-    if (aEntry.isSubFrame) {
-      entry.subframe = true;
-    }
-    if (!(aEntry instanceof Ci.nsISHEntry)) {
-      return entry;
-    }
-    
-    var cacheKey = aEntry.cacheKey;
-    if (cacheKey && cacheKey instanceof Ci.nsISupportsPRUint32 &&
-        cacheKey.data != 0) {
-      // XXXbz would be better to have cache keys implement
-      // nsISerializable or something.
-      entry.cacheKey = cacheKey.data;
-    }
-    entry.ID = aEntry.ID;
-    
-    if (aEntry.contentType)
-      entry.contentType = aEntry.contentType;
-    
-    var x = {}, y = {};
-    aEntry.getScrollPosition(x, y);
-    if (x.value != 0 || y.value != 0)
-      entry.scroll = x.value + "," + y.value;
-    
-    try {
-      var prefPostdata = this._prefBranch.getIntPref("sessionstore.postdata");
-      if (aEntry.postData && (aFullData ||
-            prefPostdata && this._checkPrivacyLevel(aEntry.URI.schemeIs("https")))) {
-        aEntry.postData.QueryInterface(Ci.nsISeekableStream).
-                        seek(Ci.nsISeekableStream.NS_SEEK_SET, 0);
-        var stream = Cc["@mozilla.org/binaryinputstream;1"].
-                     createInstance(Ci.nsIBinaryInputStream);
-        stream.setInputStream(aEntry.postData);
-        var postBytes = stream.readByteArray(stream.available());
-        var postdata = String.fromCharCode.apply(null, postBytes);
-        if (aFullData || prefPostdata == -1 ||
-            postdata.replace(/^(Content-.*\r\n)+(\r\n)*/, "").length <=
-              prefPostdata) {
-          // We can stop doing base64 encoding once our serialization into JSON
-          // is guaranteed to handle all chars in strings, including embedded
-          // nulls.
-          entry.postdata_b64 = btoa(postdata);
-        }
-      }
-    }
-    catch (ex) { debug(ex); } // POSTDATA is tricky - especially since some extensions don't get it right
-
-    if (aEntry.owner) {
-      // Not catching anything specific here, just possible errors
-      // from writeCompoundObject and the like.
-      try {
-        var binaryStream = Cc["@mozilla.org/binaryoutputstream;1"].
-                           createInstance(Ci.nsIObjectOutputStream);
-        var pipe = Cc["@mozilla.org/pipe;1"].createInstance(Ci.nsIPipe);
-        pipe.init(false, false, 0, 0xffffffff, null);
-        binaryStream.setOutputStream(pipe.outputStream);
-        binaryStream.writeCompoundObject(aEntry.owner, Ci.nsISupports, true);
-        binaryStream.close();
-
-        // Now we want to read the data from the pipe's input end and encode it.
-        var scriptableStream = Cc["@mozilla.org/binaryinputstream;1"].
-                               createInstance(Ci.nsIBinaryInputStream);
-        scriptableStream.setInputStream(pipe.inputStream);
-        var ownerBytes =
-          scriptableStream.readByteArray(scriptableStream.available());
-        // We can stop doing base64 encoding once our serialization into JSON
-        // is guaranteed to handle all chars in strings, including embedded
-        // nulls.
-        entry.owner_b64 = btoa(String.fromCharCode.apply(null, ownerBytes));
-      }
-      catch (ex) { debug(ex); }
-    }
-    
-    if (!(aEntry instanceof Ci.nsISHContainer)) {
-      return entry;
-    }
-    
-    if (aEntry.childCount > 0) {
-      entry.children = [];
-      for (var i = 0; i < aEntry.childCount; i++) {
-        var child = aEntry.GetChildAt(i);
-        if (child) {
-          entry.children.push(this._serializeHistoryEntry(child, aFullData));
-        }
-        else { // to maintain the correct frame order, insert a dummy entry 
-          entry.children.push({ url: "about:blank" });
-        }
-        // don't try to restore framesets containing wyciwyg URLs (cf. bug 424689 and bug 450595)
-        if (/^wyciwyg:\/\//.test(entry.children[i].url)) {
-          delete entry.children;
-          break;
-        }
-      }
-    }
-    
-    return entry;
-  },
-
-  /**
-   * Updates all sessionStorage "super cookies"
-   * @param aTabData
-   *        The data object for a specific tab
-   * @param aHistory
-   *        That tab's session history
-   * @param aDocShell
-   *        That tab's docshell (containing the sessionStorage)
-   * @param aFullData
-   *        always return privacy sensitive data (use with care)
-   */
-  _serializeSessionStorage:
-    function sss_serializeSessionStorage(aTabData, aHistory, aDocShell, aFullData) {
-    let storageData = {};
-    let hasContent = false;
-
-    aDocShell.QueryInterface(Ci.nsIDocShell_MOZILLA_1_9_1_SessionStorage);
-    for (let i = 0; i < aHistory.count; i++) {
-      let uri = aHistory.getEntryAtIndex(i, false).URI;
-      // sessionStorage is saved per origin (cf. nsDocShell::GetSessionStorageForURI)
-      let domain = uri.spec;
-      try {
-        if (uri.host)
-          domain = uri.prePath;
-      }
-      catch (ex) { /* this throws for host-less URIs (such as about: or jar:) */ }
-      if (storageData[domain] || !(aFullData || this._checkPrivacyLevel(uri.schemeIs("https"))))
-        continue;
-
-      let storage, storageItemCount = 0;
-      try {
-        var principal = Cc["@mozilla.org/scriptsecuritymanager;1"].
-                        getService(Ci.nsIScriptSecurityManager).
-                        getCodebasePrincipal(uri);
-
-        // Using getSessionStorageForPrincipal instead of getSessionStorageForURI
-        // just to be able to pass aCreate = false, that avoids creation of the
-        // sessionStorage object for the page earlier than the page really
-        // requires it. It was causing problems while accessing a storage when
-        // a page later changed its domain.
-        storage = aDocShell.getSessionStorageForPrincipal(principal, false);
-        if (storage)
-          storageItemCount = storage.length;
-      }
-      catch (ex) { /* sessionStorage might throw if it's turned off, see bug 458954 */ }
-      if (storageItemCount == 0)
-        continue;
-
-      let data = storageData[domain] = {};
-      for (let j = 0; j < storageItemCount; j++) {
-        try {
-          let key = storage.key(j);
-          let item = storage.getItem(key);
-          data[key] = item;
-        }
-        catch (ex) { /* XXXzeniko this currently throws for secured items (cf. bug 442048) */ }
-      }
-      hasContent = true;
-    }
-
-    if (hasContent)
-      aTabData.storage = storageData;
-  },
-
-  /**
-   * go through all tabs and store the current scroll positions
-   * and innerHTML content of WYSIWYG editors
-   * @param aWindow
-   *        Window reference
-   */
-  _updateTextAndScrollData: function sss_updateTextAndScrollData(aWindow) {
-    var browsers = aWindow.getBrowser().browsers;
-    for (var i = 0; i < browsers.length; i++) {
-      try {
-        var tabData = this._windows[aWindow.__SSi].tabs[i];
-        if (!tabData || tabData.entries.length == 0 ||
-            browsers[i].parentNode.__SS_data &&
-            browsers[i].parentNode.__SS_data._tabStillLoading)
-          continue; // ignore incompletely initialized tabs
-        this._updateTextAndScrollDataForTab(aWindow, browsers[i], tabData);
-      }
-      catch (ex) { debug(ex); } // get as much data as possible, ignore failures (might succeed the next time)
-    }
-  },
-
-  /**
-   * go through all frames and store the current scroll positions
-   * and innerHTML content of WYSIWYG editors
-   * @param aWindow
-   *        Window reference
-   * @param aBrowser
-   *        single browser reference
-   * @param aTabData
-   *        tabData object to add the information to
-   * @param aFullData
-   *        always return privacy sensitive data (use with care)
-   */
-  _updateTextAndScrollDataForTab:
-    function sss_updateTextAndScrollDataForTab(aWindow, aBrowser, aTabData, aFullData) {
-    var tabIndex = (aTabData.index || aTabData.entries.length) - 1;
-    // entry data needn't exist for tabs just initialized with an incomplete session state
-    if (!aTabData.entries[tabIndex])
-      return;
-    
-    let selectedPageStyle = aBrowser.markupDocumentViewer.authorStyleDisabled ? "_nostyle" :
-                            this._getSelectedPageStyle(aBrowser.contentWindow);
-    if (selectedPageStyle)
-      aTabData.pageStyle = selectedPageStyle;
-    else if (aTabData.pageStyle)
-      delete aTabData.pageStyle;
-    
-    this._updateTextAndScrollDataForFrame(aWindow, aBrowser.contentWindow,
-                                          aTabData.entries[tabIndex],
-                                          !aTabData._formDataSaved, aFullData);
-    aTabData._formDataSaved = true;
-    if (aBrowser.currentURI.spec == "about:config")
-      aTabData.entries[tabIndex].formdata = {
-        "#textbox": aBrowser.contentDocument.getElementById("textbox").wrappedJSObject.value
-      };
-  },
-
-  /**
-   * go through all subframes and store all form data, the current
-   * scroll positions and innerHTML content of WYSIWYG editors
-   * @param aWindow
-   *        Window reference
-   * @param aContent
-   *        frame reference
-   * @param aData
-   *        part of a tabData object to add the information to
-   * @param aUpdateFormData
-   *        update all form data for this tab
-   * @param aFullData
-   *        always return privacy sensitive data (use with care)
-   */
-  _updateTextAndScrollDataForFrame:
-    function sss_updateTextAndScrollDataForFrame(aWindow, aContent, aData,
-                                                 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) ||
-        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;
-      }
-      
-      // designMode is undefined e.g. for XUL documents (as about:config)
-      if ((aContent.document.designMode || "") == "on") {
-        if (aData.innerHTML === undefined && !aFullData) {
-          // we get no "input" events from iframes - listen for keypress here
-          let _this = this;
-          aContent.addEventListener("keypress", function(aEvent) {
-            _this.saveStateDelayed(aWindow, 3000);
-          }, true);
-        }
-        aData.innerHTML = aContent.document.body.innerHTML;
-      }
-    }
-
-    // get scroll position from nsIDOMWindowUtils, since it allows avoiding a
-    // flush of layout
-    let domWindowUtils = aContent.QueryInterface(Ci.nsIInterfaceRequestor)
-                                 .getInterface(Ci.nsIDOMWindowUtils);
-    let scrollX = {}, scrollY = {};
-    domWindowUtils.getScrollXY(false, scrollX, scrollY);
-    aData.scroll = scrollX.value + "," + scrollY.value;
-  },
-
-  /**
-   * determine the title of the currently enabled style sheet (if any)
-   * and recurse through the frameset if necessary
-   * @param   aContent is a frame reference
-   * @returns the title style sheet determined to be enabled (empty string if none)
-   */
-  _getSelectedPageStyle: function sss_getSelectedPageStyle(aContent) {
-    const forScreen = /(?:^|,)\s*(?:all|screen)\s*(?:,|$)/i;
-    for (let i = 0; i < aContent.document.styleSheets.length; i++) {
-      let ss = aContent.document.styleSheets[i];
-      let media = ss.media.mediaText;
-      if (!ss.disabled && ss.title && (!media || forScreen.test(media)))
-        return ss.title
-    }
-    for (let i = 0; i < aContent.frames.length; i++) {
-      let selectedPageStyle = this._getSelectedPageStyle(aContent.frames[i]);
-      if (selectedPageStyle)
-        return selectedPageStyle;
-    }
-    return "";
-  },
-
-  /**
-   * collect the state of all form elements
-   * @param aDocument
-   *        document reference
-   */
-  _collectFormDataForFrame: function sss_collectFormDataForFrame(aDocument) {
-    let formNodes = aDocument.evaluate(XPathHelper.restorableFormNodes, aDocument,
-                                       XPathHelper.resolveNS,
-                                       Ci.nsIDOMXPathResult.UNORDERED_NODE_ITERATOR_TYPE, null);
-    let node = formNodes.iterateNext();
-    if (!node)
-      return null;
-    
-    const MAX_GENERATED_XPATHS = 100;
-    let generatedCount = 0;
-    
-    let data = {};
-    do {
-      // Only generate a limited number of XPath expressions for perf reasons (cf. bug 477564)
-      if (!node.id && ++generatedCount > MAX_GENERATED_XPATHS)
-        continue;
-      
-      let id = node.id ? "#" + node.id : XPathHelper.generate(node);
-      if (node instanceof Ci.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 Ci.nsIDOMHTMLTextAreaElement)
-        data[id] = node.value;
-      else if (!node.multiple)
-        data[id] = node.selectedIndex;
-      else {
-        let options = Array.map(node.options, function(aOpt, aIx) aOpt.selected ? aIx : -1);
-        data[id] = options.filter(function(aIx) aIx >= 0);
-      }
-    } while ((node = formNodes.iterateNext()));
-    
-    return data;
-  },
-
-  /**
-   * store all hosts for a URL
-   * @param aWindow
-   *        Window reference
-   */
-  _updateCookieHosts: function sss_updateCookieHosts(aWindow) {
-    var hosts = this._windows[aWindow.__SSi]._hosts = {};
-    
-    // get all possible subdomain levels for a given URL
-    var _this = this;
-    function extractHosts(aEntry) {
-      if (/^https?:\/\/(?:[^@\/\s]+@)?([\w.-]+)/.test(aEntry.url) &&
-        !hosts[RegExp.$1] && _this._checkPrivacyLevel(_this._getURIFromString(aEntry.url).schemeIs("https"))) {
-        var host = RegExp.$1;
-        var ix;
-        for (ix = host.indexOf(".") + 1; ix; ix = host.indexOf(".", ix) + 1) {
-          hosts[host.substr(ix)] = true;
-        }
-        hosts[host] = true;
-      }
-      else if (/^file:\/\/([^\/]*)/.test(aEntry.url)) {
-        hosts[RegExp.$1] = true;
-      }
-      if (aEntry.children) {
-        aEntry.children.forEach(extractHosts);
-      }
-    }
-    
-    this._windows[aWindow.__SSi].tabs.forEach(function(aTabData) { aTabData.entries.forEach(extractHosts); });
-  },
-
-  /**
-   * Serialize cookie data
-   * @param aWindows
-   *        array of Window references
-   */
-  _updateCookies: function sss_updateCookies(aWindows) {
-    var cookiesEnum = Cc["@mozilla.org/cookiemanager;1"].
-                      getService(Ci.nsICookieManager).enumerator;
-    // collect the cookies per window
-    for (var i = 0; i < aWindows.length; i++)
-      aWindows[i].cookies = [];
-    
-    // MAX_EXPIRY should be 2^63-1, but JavaScript can't handle that precision
-    var MAX_EXPIRY = Math.pow(2, 62);
-    while (cookiesEnum.hasMoreElements()) {
-      var cookie = cookiesEnum.getNext().QueryInterface(Ci.nsICookie2);
-      if (cookie.isSession && this._checkPrivacyLevel(cookie.isSecure)) {
-        var jscookie = null;
-        aWindows.forEach(function(aWindow) {
-          if (aWindow._hosts && aWindow._hosts[cookie.rawHost]) {
-            // serialize the cookie when it's first needed
-            if (!jscookie) {
-              jscookie = { host: cookie.host, value: cookie.value };
-              // only add attributes with non-default values (saving a few bits)
-              if (cookie.path) jscookie.path = cookie.path;
-              if (cookie.name) jscookie.name = cookie.name;
-              if (cookie.isSecure) jscookie.secure = true;
-              if (cookie.isHttpOnly) jscookie.httponly = true;
-              if (cookie.expiry < MAX_EXPIRY) jscookie.expiry = cookie.expiry;
-            }
-            aWindow.cookies.push(jscookie);
-          }
-        });
-      }
-    }
-    
-    // don't include empty cookie sections
-    for (i = 0; i < aWindows.length; i++)
-      if (aWindows[i].cookies.length == 0)
-        delete aWindows[i].cookies;
-  },
-
-  /**
-   * Store window dimensions, visibility, sidebar
-   * @param aWindow
-   *        Window reference
-   */
-  _updateWindowFeatures: function sss_updateWindowFeatures(aWindow) {
-    var winData = this._windows[aWindow.__SSi];
-    
-    WINDOW_ATTRIBUTES.forEach(function(aAttr) {
-      winData[aAttr] = this._getWindowDimension(aWindow, aAttr);
-    }, this);
-    
-    var hidden = WINDOW_HIDEABLE_FEATURES.filter(function(aItem) {
-      return aWindow[aItem] && !aWindow[aItem].visible;
-    });
-    if (hidden.length != 0)
-      winData.hidden = hidden.join(",");
-    else if (winData.hidden)
-      delete winData.hidden;
-
-    var sidebar = aWindow.document.getElementById("sidebar-box").getAttribute("sidebarcommand");
-    if (sidebar)
-      winData.sidebar = sidebar;
-    else if (winData.sidebar)
-      delete winData.sidebar;
-  },
-
-  /**
-   * serialize session data as Ini-formatted string
-   * @param aUpdateAll
-   *        Bool update all windows 
-   * @returns string
-   */
-  _getCurrentState: function sss_getCurrentState(aUpdateAll) {
-    var activeWindow = this._getMostRecentBrowserWindow();
-    
-    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);
-        }
-        else { // always update the window features (whose change alone never triggers a save operation)
-          this._updateWindowFeatures(aWindow);
-        }
-      }, this);
-      this._dirtyWindows = [];
-    }
-    
-    // collect the data for all windows
-    var total = [], windows = [];
-    var nonPopupCount = 0;
-    var ix;
-    for (ix in this._windows) {
-      total.push(this._windows[ix]);
-      windows.push(ix);
-      if (!this._windows[ix].isPopup)
-        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++;
-      }
-    }
-
-    // shallow copy this._closedWindows to preserve current state
-    let lastClosedWindowsCopy = this._closedWindows.slice();
-
-//@line 1657 "/builds/slave/linux_build/build/browser/components/sessionstore/src/nsSessionStore.js"
-    // if no non-popup browser window remains open, return the state of the last closed window(s)
-    if (nonPopupCount == 0 && lastClosedWindowsCopy.length > 0) {
-      // prepend the last non-popup browser window, so that if the user loads more tabs
-      // at startup we don't accidentally add them to a popup window
-      do {
-        total.unshift(lastClosedWindowsCopy.shift())
-      } while (total[0].isPopup)
-    }
-//@line 1666 "/builds/slave/linux_build/build/browser/components/sessionstore/src/nsSessionStore.js"
-
-    if (activeWindow) {
-      this.activeWindowSSiCache = activeWindow.__SSi || "";
-    }
-    ix = this.activeWindowSSiCache ? windows.indexOf(this.activeWindowSSiCache) : -1;
-
-    return { windows: total, selectedWindow: ix + 1, _closedWindows: lastClosedWindowsCopy };
-  },
-
-  /**
-   * serialize session data for a window 
-   * @param aWindow
-   *        Window reference
-   * @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);
-    }
-    
-    var total = [this._windows[aWindow.__SSi]];
-    this._updateCookies(total);
-    
-    return { windows: total };
-  },
-
-  _collectWindowData: function sss_collectWindowData(aWindow) {
-    if (!this._isWindowLoaded(aWindow))
-      return;
-    
-    // update the internal state data for this window
-    this._saveWindowHistory(aWindow);
-    this._updateTextAndScrollData(aWindow);
-    this._updateCookieHosts(aWindow);
-    this._updateWindowFeatures(aWindow);
-    
-    this._dirtyWindows[aWindow.__SSi] = false;
-  },
-
-/* ........ Restoring Functionality .............. */
-
-  /**
-   * restore features to a single window
-   * @param aWindow
-   *        Window reference
-   * @param aState
-   *        JS object or its eval'able source
-   * @param aOverwriteTabs
-   *        bool overwrite existing tabs w/ new ones
-   * @param aFollowUp
-   *        bool this isn't the restoration of the first window
-   */
-  restoreWindow: function sss_restoreWindow(aWindow, aState, aOverwriteTabs, aFollowUp) {
-    if (!aFollowUp) {
-      this.windowToFocus = aWindow;
-    }
-    // initialize window if necessary
-    if (aWindow && (!aWindow.__SSi || !this._windows[aWindow.__SSi]))
-      this.onLoad(aWindow);
-
-    try {
-      var root = typeof aState == "string" ? this._safeEval(aState) : aState;
-      if (!root.windows[0]) {
-        this._notifyIfAllWindowsRestored();
-        return; // nothing to restore
-      }
-    }
-    catch (ex) { // invalid state object - don't restore anything 
-      debug(ex);
-      this._notifyIfAllWindowsRestored();
-      return;
-    }
-
-    if (root._closedWindows)
-      this._closedWindows = root._closedWindows;
-
-    var winData;
-    if (!aState.selectedWindow) {
-      aState.selectedWindow = 0;
-    }
-    // open new windows for all further window entries of a multi-window session
-    // (unless they don't contain any tab data)
-    for (var w = 1; w < root.windows.length; w++) {
-      winData = root.windows[w];
-      if (winData && winData.tabs && winData.tabs[0]) {
-        var window = this._openWindowWithState({ windows: [winData] });
-        if (w == aState.selectedWindow - 1) {
-          this.windowToFocus = window;
-        }
-      }
-    }
-    winData = root.windows[0];
-    if (!winData.tabs) {
-      winData.tabs = [];
-    }
-    // don't restore a single blank tab when we've had an external
-    // URL passed in for loading at startup (cf. bug 357419)
-    else if (root._firstTabs && !aOverwriteTabs && winData.tabs.length == 1 &&
-             (!winData.tabs[0].entries || winData.tabs[0].entries.length == 0)) {
-      winData.tabs = [];
-    }
-    
-    var tabbrowser = aWindow.gBrowser;
-    var openTabCount = aOverwriteTabs ? tabbrowser.browsers.length : -1;
-    var newTabCount = winData.tabs.length;
-    var tabs = [];
-
-    // disable smooth scrolling while adding, moving, removing and selecting tabs
-    var tabstrip = tabbrowser.tabContainer.mTabstrip;
-    var smoothScroll = tabstrip.smoothScroll;
-    tabstrip.smoothScroll = false;
-    
-    // make sure that the selected tab won't be closed in order to
-    // prevent unnecessary flickering
-    if (aOverwriteTabs && tabbrowser.selectedTab._tPos >= newTabCount)
-      tabbrowser.moveTabTo(tabbrowser.selectedTab, newTabCount - 1);
-    
-    for (var t = 0; t < newTabCount; t++) {
-      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(tabs[t], t);
-      }
-    }
-
-    // when overwriting tabs, remove all superflous ones
-    if (aOverwriteTabs && newTabCount < openTabCount) {
-      Array.slice(tabbrowser.mTabs, newTabCount, openTabCount)
-           .forEach(tabbrowser.removeTab, tabbrowser);
-    }
-    
-    if (aOverwriteTabs) {
-      this.restoreWindowFeatures(aWindow, winData);
-    }
-    if (winData.cookies) {
-      this.restoreCookies(winData.cookies);
-    }
-    if (winData.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];
-      }
-    }
-    if (winData._closedTabs && (root._firstTabs || aOverwriteTabs)) {
-      this._windows[aWindow.__SSi]._closedTabs = winData._closedTabs;
-    }
-    
-    this.restoreHistoryPrecursor(aWindow, tabs, winData.tabs,
-      (aOverwriteTabs ? (parseInt(winData.selected) || 1) : 0), 0, 0);
-
-    // set smoothScroll back to the original value
-    tabstrip.smoothScroll = smoothScroll;
-
-    this._notifyIfAllWindowsRestored();
-  },
-
-  /**
-   * Manage history restoration for a window
-   * @param aWindow
-   *        Window to restore the tabs into
-   * @param aTabs
-   *        Array of tab references
-   * @param aTabData
-   *        Array of tab data
-   * @param aSelectTab
-   *        Index of selected tab
-   * @param aIx
-   *        Index of the next tab to check readyness for
-   * @param aCount
-   *        Counter for number of times delaying b/c browser or history aren't ready
-   */
-  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]).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, aTabData, aSelectTab, aIx, aCount + 1);
-          }
-          aWindow.setTimeout(restoreHistoryFunc, 100, this);
-          return;
-        }
-      }
-    }
-    
-    // mark the tabs as loading
-    for (t = 0; t < aTabs.length; t++) {
-      var tab = aTabs[t];
-      var browser = tabbrowser.getBrowserForTab(tab);
-      
-      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";
-        continue;
-      }
-      
-      browser.stop(); // in case about:blank isn't done yet
-      
-      tab.setAttribute("busy", "true");
-      tabbrowser.updateIcon(tab);
-      tabbrowser.setTabTitleLoading(tab);
-      
-      // wall-paper fix for bug 439675: make sure that the URL to be loaded
-      // is always visible in the address bar
-      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 = aTabData[t];
-    }
-    
-    if (aTabs.length > 0) {
-      // Determine if we can optimize & load visible tabs first
-      let tabScrollBoxObject = tabbrowser.tabContainer.mTabstrip.scrollBoxObject;
-      let tabBoxObject = aTabs[0].boxObject;
-      let maxVisibleTabs = Math.ceil(tabScrollBoxObject.width / tabBoxObject.width);
-
-      // make sure we restore visible tabs first, if there are enough
-      if (maxVisibleTabs < aTabs.length && aSelectTab > 1) {
-        let firstVisibleTab = 0;
-        if (aTabs.length - maxVisibleTabs > aSelectTab) {
-          // aSelectTab is leftmost since we scroll to it when possible
-          firstVisibleTab = aSelectTab - 1;
-        } else {
-          // aSelectTab is rightmost or no more room to scroll right
-          firstVisibleTab = aTabs.length - maxVisibleTabs;
-        }
-        aTabs = aTabs.splice(firstVisibleTab, maxVisibleTabs).concat(aTabs);
-        aTabData = aTabData.splice(firstVisibleTab, maxVisibleTabs).concat(aTabData);
-        aSelectTab -= firstVisibleTab;
-      }
-
-      // make sure to restore the selected tab first (if any)
-      if (aSelectTab-- && aTabs[aSelectTab]) {
-        aTabs.unshift(aTabs.splice(aSelectTab, 1)[0]);
-        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, aTabData, idMap);
-  },
-
-  /**
-   * Restory history for a window
-   * @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, aTabData, aIdMap) {
-    var _this = this;
-    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 tab = aTabs.shift();
-    var tabData = aTabData.shift();
-
-    var browser = aWindow.getBrowser().getBrowserForTab(tab);
-    var history = browser.webNavigation.sessionHistory;
-    
-    if (history.count > 0) {
-      history.PurgeHistory(history.count);
-    }
-    history.QueryInterface(Ci.nsISHistoryInternal);
-    
-    if (!tabData.entries) {
-      tabData.entries = [];
-    }
-    if (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);
-    }
-    
-    // make sure to reset the capabilities and attributes, in case this tab gets reused
-    var disallow = (tabData.disallow)?tabData.disallow.split(","):[];
-    CAPABILITIES.forEach(function(aCapability) {
-      browser.docShell["allow" + aCapability] = disallow.indexOf(aCapability) == -1;
-    });
-    Array.filter(tab.attributes, function(aAttr) {
-      return (_this.xulAttributes.indexOf(aAttr.name) > -1);
-    }).forEach(tab.removeAttribute, tab);
-    if (tabData.xultab) {
-      // restore attributes from the legacy Firefox 2.0/3.0 format
-      tabData.xultab.split(" ").forEach(function(aAttr) {
-        if (/^([^\s=]+)=(.*)/.test(aAttr)) {
-          tab.setAttribute(RegExp.$1, decodeURI(RegExp.$2));
-        }
-      });
-    }
-    for (let name in tabData.attributes)
-      tab.setAttribute(name, tabData.attributes[name]);
-    
-    if (tabData.storage && browser.docShell instanceof Ci.nsIDocShell)
-      this._deserializeSessionStorage(tabData.storage, browser.docShell);
-    
-    // notify the tabbrowser that the tab chrome has been restored
-    var event = aWindow.document.createEvent("Events");
-    event.initEvent("SSTabRestoring", true, false);
-    tab.dispatchEvent(event);
-    
-    let activeIndex = (tabData.index || tabData.entries.length) - 1;
-    if (activeIndex >= tabData.entries.length)
-      activeIndex = tabData.entries.length - 1;
-    try {
-      if (activeIndex >= 0)
-        browser.webNavigation.gotoIndex(activeIndex);
-    }
-    catch (ex) {
-      // ignore page load errors
-      tab.removeAttribute("busy");
-    }
-    
-    if (tabData.entries.length > 0) {
-      // restore those aspects of the currently active documents
-      // which are not preserved in the plain history entries
-      // (mainly scroll state and text data)
-      browser.__SS_restore_data = tabData.entries[activeIndex] || {};
-      browser.__SS_restore_text = tabData.text || "";
-      browser.__SS_restore_pageStyle = tabData.pageStyle || "";
-      browser.__SS_restore_tab = tab;
-      browser.__SS_restore = this.restoreDocument_proxy;
-      browser.addEventListener("load", browser.__SS_restore, true);
-    }
-    
-    aWindow.setTimeout(function(){ _this.restoreHistory(aWindow, aTabs, aTabData, aIdMap); }, 0);
-  },
-
-  /**
-   * expands serialized history data into a session-history-entry instance
-   * @param aEntry
-   *        Object containing serialized history data for a URL
-   * @param aIdMap
-   *        Hash for ensuring unique frame IDs
-   * @returns nsISHEntry
-   */
-  _deserializeHistoryEntry: function sss_deserializeHistoryEntry(aEntry, aIdMap) {
-    var shEntry = Cc["@mozilla.org/browser/session-history-entry;1"].
-                  createInstance(Ci.nsISHEntry);
-    
-    var ioService = Cc["@mozilla.org/network/io-service;1"].
-                    getService(Ci.nsIIOService);
-    shEntry.setURI(ioService.newURI(aEntry.url, null, null));
-    shEntry.setTitle(aEntry.title || aEntry.url);
-    if (aEntry.subframe)
-      shEntry.setIsSubFrame(aEntry.subframe || false);
-    shEntry.loadType = Ci.nsIDocShellLoadInfo.loadHistory;
-    if (aEntry.contentType)
-      shEntry.contentType = aEntry.contentType;
-    
-    if (aEntry.cacheKey) {
-      var cacheKey = Cc["@mozilla.org/supports-PRUint32;1"].
-                     createInstance(Ci.nsISupportsPRUint32);
-      cacheKey.data = aEntry.cacheKey;
-      shEntry.cacheKey = cacheKey;
-    }
-
-    if (aEntry.ID) {
-      // get a new unique ID for this frame (since the one from the last
-      // start might already be in use)
-      var id = aIdMap[aEntry.ID] || 0;
-      if (!id) {
-        for (id = Date.now(); id in aIdMap.used; id++);
-        aIdMap[aEntry.ID] = id;
-        aIdMap.used[id] = true;
-      }
-      shEntry.ID = id;
-    }
-    
-    if (aEntry.scroll) {
-      var scrollPos = (aEntry.scroll || "0,0").split(",");
-      scrollPos = [parseInt(scrollPos[0]) || 0, parseInt(scrollPos[1]) || 0];
-      shEntry.setScrollPosition(scrollPos[0], scrollPos[1]);
-    }
-
-    var postdata;
-    if (aEntry.postdata_b64) {  // Firefox 3
-      postdata = atob(aEntry.postdata_b64);
-    } else if (aEntry.postdata) { // Firefox 2
-      postdata = aEntry.postdata;
-    }
-
-    if (postdata) {
-      var stream = Cc["@mozilla.org/io/string-input-stream;1"].
-                   createInstance(Ci.nsIStringInputStream);
-      stream.setData(postdata, postdata.length);
-      shEntry.postData = stream;
-    }
-
-    if (aEntry.owner_b64) {  // Firefox 3
-      var ownerInput = Cc["@mozilla.org/io/string-input-stream;1"].
-                       createInstance(Ci.nsIStringInputStream);
-      var binaryData = atob(aEntry.owner_b64);
-      ownerInput.setData(binaryData, binaryData.length);
-      var binaryStream = Cc["@mozilla.org/binaryinputstream;1"].
-                         createInstance(Ci.nsIObjectInputStream);
-      binaryStream.setInputStream(ownerInput);
-      try { // Catch possible deserialization exceptions
-        shEntry.owner = binaryStream.readObject(true);
-      } catch (ex) { debug(ex); }
-    } else if (aEntry.ownerURI) { // Firefox 2
-      var uriObj = ioService.newURI(aEntry.ownerURI, null, null);
-      shEntry.owner = Cc["@mozilla.org/scriptsecuritymanager;1"].
-                      getService(Ci.nsIScriptSecurityManager).
-                      getCodebasePrincipal(uriObj);
-    }
-    
-    if (aEntry.children && shEntry instanceof Ci.nsISHContainer) {
-      for (var i = 0; i < aEntry.children.length; i++) {
-        shEntry.AddChild(this._deserializeHistoryEntry(aEntry.children[i], aIdMap), i);
-      }
-    }
-    
-    return shEntry;
-  },
-
-  /**
-   * restores all sessionStorage "super cookies"
-   * @param aStorageData
-   *        Storage data to be restored
-   * @param aDocShell
-   *        A tab's docshell (containing the sessionStorage)
-   */
-  _deserializeSessionStorage: function sss_deserializeSessionStorage(aStorageData, aDocShell) {
-    let ioService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
-    for (let url in aStorageData) {
-      let uri = ioService.newURI(url, null, null);
-      let docShell_191 = aDocShell.QueryInterface(Ci.nsIDocShell_MOZILLA_1_9_1_SessionStorage);
-      let storage = docShell_191.getSessionStorageForURI(uri);
-      for (let key in aStorageData[url]) {
-        try {
-          storage.setItem(key, aStorageData[url][key]);
-        }
-        catch (ex) { Cu.reportError(ex); } // throws e.g. for URIs that can't have sessionStorage
-      }
-    }
-  },
-
-  /**
-   * Restore properties to a loaded document
-   */
-  restoreDocument_proxy: function sss_restoreDocument_proxy(aEvent) {
-    // wait for the top frame to be loaded completely
-    if (!aEvent || !aEvent.originalTarget || !aEvent.originalTarget.defaultView || aEvent.originalTarget.defaultView != aEvent.originalTarget.defaultView.top) {
-      return;
-    }
-    
-    // always call this before injecting content into a document!
-    function hasExpectedURL(aDocument, aURL)
-      !aURL || aURL.replace(/#.*/, "") == aDocument.location.href.replace(/#.*/, "");
-    
-    // restore text data saved by Firefox 2.0/3.0
-    var textArray = this.__SS_restore_text ? this.__SS_restore_text.split(" ") : [];
-    function restoreTextData(aContent, aPrefix, aURL) {
-      textArray.forEach(function(aEntry) {
-        if (/^((?:\d+\|)*)(#?)([^\s=]+)=(.*)$/.test(aEntry) &&
-            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 && node.type != "file") {
-            node.value = decodeURI(RegExp.$4);
-            
-            var event = document.createEvent("UIEvents");
-            event.initUIEvent("input", true, true, aContent, 0);
-            node.dispatchEvent(event);
-          }
-        }
-      });
-    }
-    
-    function restoreFormData(aDocument, aData, aURL) {
-      for (let key in aData) {
-        if (!hasExpectedURL(aDocument, aURL))
-          return;
-        
-        let node = key.charAt(0) == "#" ? aDocument.getElementById(key.slice(1)) :
-                                          XPathHelper.resolve(aDocument, key);
-        if (!node)
-          continue;
-        
-        let value = aData[key];
-        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");
-          event.initUIEvent("input", true, true, aDocument.defaultView, 0);
-          node.dispatchEvent(event);
-        }
-        else if (typeof value == "boolean")
-          node.checked = value;
-        else if (typeof value == "number")
-          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;
-          });
-        }
-        // NB: dispatching "change" events might have unintended side-effects
-      }
-    }
-    
-    let selectedPageStyle = this.__SS_restore_pageStyle;
-    let window = this.ownerDocument.defaultView;
-    function restoreTextDataAndScrolling(aContent, aData, aPrefix) {
-      if (aData.formdata)
-        restoreFormData(aContent.document, aData.formdata, aData.url);
-      else
-        restoreTextData(aContent, aPrefix, aData.url);
-      if (aData.innerHTML) {
-        window.setTimeout(function() {
-          if (aContent.document.designMode == "on" &&
-              hasExpectedURL(aContent.document, aData.url)) {
-            aContent.document.body.innerHTML = aData.innerHTML;
-          }
-        }, 0);
-      }
-      if (aData.scroll && /(\d+),(\d+)/.test(aData.scroll)) {
-        aContent.scrollTo(RegExp.$1, RegExp.$2);
-      }
-      Array.forEach(aContent.document.styleSheets, function(aSS) {
-        aSS.disabled = aSS.title && aSS.title != selectedPageStyle;
-      });
-      for (var i = 0; i < aContent.frames.length; i++) {
-        if (aData.children && aData.children[i] &&
-          hasExpectedURL(aContent.document, aData.url)) {
-          restoreTextDataAndScrolling(aContent.frames[i], aData.children[i], aPrefix + i + "|");
-        }
-      }
-    }
-    
-    // don't restore text data and scrolling state if the user has navigated
-    // away before the loading completed (except for in-page navigation)
-    if (hasExpectedURL(aEvent.originalTarget, this.__SS_restore_data.url)) {
-      var content = aEvent.originalTarget.defaultView;
-      if (this.currentURI.spec == "about:config") {
-        // unwrap the document for about:config because otherwise the properties
-        // of the XBL bindings - as the textbox - aren't accessible (see bug 350718)
-        content = content.wrappedJSObject;
-      }
-      restoreTextDataAndScrolling(content, this.__SS_restore_data, "");
-      this.markupDocumentViewer.authorStyleDisabled = selectedPageStyle == "_nostyle";
-      
-      // notify the tabbrowser that this document has been completely restored
-      var event = this.ownerDocument.createEvent("Events");
-      event.initEvent("SSTabRestored", true, false);
-      this.__SS_restore_tab.dispatchEvent(event);
-    }
-    
-    this.removeEventListener("load", this.__SS_restore, true);
-    delete this.__SS_restore_data;
-    delete this.__SS_restore_text;
-    delete this.__SS_restore_pageStyle;
-    delete this.__SS_restore_tab;
-    delete this.__SS_restore;
-  },
-
-  /**
-   * Restore visibility and dimension features to a window
-   * @param aWindow
-   *        Window reference
-   * @param aWinData
-   *        Object containing session data for the window
-   */
-  restoreWindowFeatures: function sss_restoreWindowFeatures(aWindow, aWinData) {
-    var hidden = (aWinData.hidden)?aWinData.hidden.split(","):[];
-    WINDOW_HIDEABLE_FEATURES.forEach(function(aItem) {
-      aWindow[aItem].visible = hidden.indexOf(aItem) == -1;
-    });
-    
-    if (aWinData.isPopup) {
-      this._windows[aWindow.__SSi].isPopup = true;
-      if (aWindow.gURLBar) {
-        aWindow.gURLBar.readOnly = true;
-        aWindow.gURLBar.setAttribute("enablehistory", "false");
-      }
-    }
-    else {
-      delete this._windows[aWindow.__SSi].isPopup;
-      if (aWindow.gURLBar) {
-        aWindow.gURLBar.readOnly = false;
-        aWindow.gURLBar.setAttribute("enablehistory", "true");
-      }
-    }
-
-    var _this = this;
-    aWindow.setTimeout(function() {
-      _this.restoreDimensions.apply(_this, [aWindow, aWinData.width || 0, 
-        aWinData.height || 0, "screenX" in aWinData ? aWinData.screenX : NaN,
-        "screenY" in aWinData ? aWinData.screenY : NaN,
-        aWinData.sizemode || "", aWinData.sidebar || ""]);
-    }, 0);
-  },
-
-  /**
-   * Restore a window's dimensions
-   * @param aWidth
-   *        Window width
-   * @param aHeight
-   *        Window height
-   * @param aLeft
-   *        Window left
-   * @param aTop
-   *        Window top
-   * @param aSizeMode
-   *        Window size mode (eg: maximized)
-   * @param aSidebar
-   *        Sidebar command
-   */
-  restoreDimensions: function sss_restoreDimensions(aWindow, aWidth, aHeight, aLeft, aTop, aSizeMode, aSidebar) {
-    var win = aWindow;
-    var _this = this;
-    function win_(aName) { return _this._getWindowDimension(win, aName); }
-    
-    // only modify those aspects which aren't correct yet
-    if (aWidth && aHeight && (aWidth != win_("width") || aHeight != win_("height"))) {
-      aWindow.resizeTo(aWidth, aHeight);
-    }
-    if (!isNaN(aLeft) && !isNaN(aTop) && (aLeft != win_("screenX") || aTop != win_("screenY"))) {
-      aWindow.moveTo(aLeft, aTop);
-    }
-    if (aSizeMode && win_("sizemode") != aSizeMode)
-    {
-      switch (aSizeMode)
-      {
-      case "maximized":
-        aWindow.maximize();
-        break;
-      case "minimized":
-        aWindow.minimize();
-        break;
-      case "normal":
-        aWindow.restore();
-        break;
-      }
-    }
-    var sidebar = aWindow.document.getElementById("sidebar-box");
-    if (sidebar.getAttribute("sidebarcommand") != aSidebar) {
-      aWindow.toggleSidebar(aSidebar);
-    }
-    // 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.content.focus();
-    }
-  },
-
-  /**
-   * Restores cookies (accepting both Firefox 2.0 and current format)
-   * @param aCookies
-   *        Array of cookie objects
-   */
-  restoreCookies: function sss_restoreCookies(aCookies) {
-    if (aCookies.count && aCookies.domain1) {
-      // convert to the new cookie serialization format
-      var converted = [];
-      for (var i = 1; i <= aCookies.count; i++) {
-        // for simplicity we only accept the format we produced ourselves
-        var parsed = aCookies["value" + i].match(/^([^=;]+)=([^;]*);(?:domain=[^;]+;)?(?:path=([^;]*);)?(secure;)?(httponly;)?/);
-        if (parsed && /^https?:\/\/([^\/]+)/.test(aCookies["domain" + i]))
-          converted.push({
-            host: RegExp.$1, path: parsed[3], name: parsed[1], value: parsed[2],
-            secure: parsed[4], httponly: parsed[5]
-          });
-      }
-      aCookies = converted;
-    }
-    
-    var cookieManager = Cc["@mozilla.org/cookiemanager;1"].
-                        getService(Ci.nsICookieManager2);
-    // MAX_EXPIRY should be 2^63-1, but JavaScript can't handle that precision
-    var MAX_EXPIRY = Math.pow(2, 62);
-    for (i = 0; i < aCookies.length; i++) {
-      var cookie = aCookies[i];
-      try {
-        cookieManager.add(cookie.host, cookie.path || "", cookie.name || "", cookie.value, !!cookie.secure, !!cookie.httponly, true, "expiry" in cookie ? cookie.expiry : MAX_EXPIRY);
-      }
-      catch (ex) { Cu.reportError(ex); } // don't let a single cookie stop recovering
-    }
-  },
-
-/* ........ Disk Access .............. */
-
-  /**
-   * save state delayed by N ms
-   * marks window as dirty (i.e. data update can't be skipped)
-   * @param aWindow
-   *        Window reference
-   * @param aDelay
-   *        Milliseconds to delay
-   */
-  saveStateDelayed: function sss_saveStateDelayed(aWindow, aDelay) {
-    if (aWindow) {
-      this._dirtyWindows[aWindow.__SSi] = true;
-    }
-
-    if (!this._saveTimer && this._resume_from_crash &&
-        !this._inPrivateBrowsing) {
-      // interval until the next disk operation is allowed
-      var minimalDelay = this._lastSaveTime + this._interval - Date.now();
-      
-      // if we have to wait, set a timer, otherwise saveState directly
-      aDelay = Math.max(minimalDelay, aDelay || 2000);
-      if (aDelay > 0) {
-        this._saveTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-        this._saveTimer.init(this, aDelay, Ci.nsITimer.TYPE_ONE_SHOT);
-      }
-      else {
-        this.saveState();
-      }
-    }
-  },
-
-  /**
-   * save state to disk
-   * @param aUpdateAll
-   *        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;
-
-    // if we're in private browsing mode, do nothing
-    if (this._inPrivateBrowsing)
-      return;
-
-    var oState = this._getCurrentState(aUpdateAll);
-    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 = Cc["@mozilla.org/supports-string;1"].
-                        createInstance(Ci.nsISupportsString);
-    // parentheses are for backwards compatibility with Firefox 2.0 and 3.0
-    stateString.data = "(" + this._toJSONString(aStateObj) + ")";
-
-    var observerService = Cc["@mozilla.org/observer-service;1"].
-                          getService(Ci.nsIObserverService);
-    observerService.notifyObservers(stateString, "sessionstore-state-write", "");
-
-    // don't touch the file if an observer has deleted all state data
-    if (stateString.data)
-      this._writeFile(this._sessionFile, stateString.data);
-
-    this._lastSaveTime = Date.now();
-  },
-
-  /**
-   * delete session datafile and backup
-   */
-  _clearDisk: function sss_clearDisk() {
-    if (this._sessionFile.exists()) {
-      try {
-        this._sessionFile.remove(false);
-      }
-      catch (ex) { dump(ex + '\n'); } // couldn't remove the file - what now?
-    }
-    if (this._sessionFileBackup.exists()) {
-      try {
-        this._sessionFileBackup.remove(false);
-      }
-      catch (ex) { dump(ex + '\n'); } // couldn't remove the file - what now?
-    }
-  },
-
-/* ........ Auxiliary Functions .............. */
-
-  /**
-   * call a callback for all currently opened browser windows
-   * (might miss the most recent one)
-   * @param aFunc
-   *        Callback each window is passed to
-   */
-  _forEachBrowserWindow: function sss_forEachBrowserWindow(aFunc) {
-    var windowMediator = Cc["@mozilla.org/appshell/window-mediator;1"].
-                         getService(Ci.nsIWindowMediator);
-    var windowsEnum = windowMediator.getEnumerator("navigator:browser");
-    
-    while (windowsEnum.hasMoreElements()) {
-      var window = windowsEnum.getNext();
-      if (window.__SSi) {
-        aFunc.call(this, window);
-      }
-    }
-  },
-
-  /**
-   * Returns most recent window
-   * @returns Window reference
-   */
-  _getMostRecentBrowserWindow: function sss_getMostRecentBrowserWindow() {
-    var windowMediator = Cc["@mozilla.org/appshell/window-mediator;1"].
-                         getService(Ci.nsIWindowMediator);
-    return windowMediator.getMostRecentWindow("navigator:browser");
-  },
-
-  /**
-   * open a new browser window for a given session state
-   * called when restoring a multi-window session
-   * @param aState
-   *        Object containing session data
-   */
-  _openWindowWithState: function sss_openWindowWithState(aState) {
-    var argString = Cc["@mozilla.org/supports-string;1"].
-                    createInstance(Ci.nsISupportsString);
-    argString.data = "";
-
-    //XXXzeniko shouldn't it be possible to set the window's dimensions here (as feature)?
-    var window = Cc["@mozilla.org/embedcomp/window-watcher;1"].
-                 getService(Ci.nsIWindowWatcher).
-                 openWindow(null, this._prefBranch.getCharPref("chromeURL"), "_blank",
-                            "chrome,dialog=no,all", argString);
-    
-    do {
-      var ID = "window" + Math.random();
-    } while (ID in this._statesToRestore);
-    this._statesToRestore[(window.__SS_restoreID = ID)] = aState;
-    
-    return window;
-  },
-
-  /**
-   * Whether or not to resume session, if not recovering from a crash.
-   * @returns bool
-   */
-  _doResumeSession: function sss_doResumeSession() {
-    if (this._clearingOnShutdown)
-      return false;
-
-    return this._prefBranch.getIntPref("startup.page") == 3 ||
-           this._prefBranch.getBoolPref("sessionstore.resume_session_once");
-  },
-
-  /**
-   * whether the user wants to load any other page at startup
-   * (except the homepage) - needed for determining whether to overwrite the current tabs
-   * C.f.: nsBrowserContentHandler's defaultArgs implementation.
-   * @returns bool
-   */
-  _isCmdLineEmpty: function sss_isCmdLineEmpty(aWindow) {
-    var defaultArgs = Cc["@mozilla.org/browser/clh;1"].
-                      getService(Ci.nsIBrowserHandler).defaultArgs;
-    if (aWindow.arguments && aWindow.arguments[0] &&
-        aWindow.arguments[0] == defaultArgs)
-      aWindow.arguments[0] = null;
-
-    return !aWindow.arguments || !aWindow.arguments[0];
-  },
-
-  /**
-   * don't save sensitive data if the user doesn't want to
-   * (distinguishes between encrypted and non-encrypted sites)
-   * @param aIsHTTPS
-   *        Bool is encrypted
-   * @returns bool
-   */
-  _checkPrivacyLevel: function sss_checkPrivacyLevel(aIsHTTPS) {
-    return this._prefBranch.getIntPref("sessionstore.privacy_level") < (aIsHTTPS ? PRIVACY_ENCRYPTED : PRIVACY_FULL);
-  },
-
-  /**
-   * on popup windows, the XULWindow's attributes seem not to be set correctly
-   * we use thus JSDOMWindow attributes for sizemode and normal window attributes
-   * (and hope for reasonable values when maximized/minimized - since then
-   * outerWidth/outerHeight aren't the dimensions of the restored window)
-   * @param aWindow
-   *        Window reference
-   * @param aAttribute
-   *        String sizemode | width | height | other window attribute
-   * @returns string
-   */
-  _getWindowDimension: function sss_getWindowDimension(aWindow, aAttribute) {
-    if (aAttribute == "sizemode") {
-      switch (aWindow.windowState) {
-      case aWindow.STATE_MAXIMIZED:
-        return "maximized";
-      case aWindow.STATE_MINIMIZED:
-        return "minimized";
-      default:
-        return "normal";
-      }
-    }
-    
-    var dimension;
-    switch (aAttribute) {
-    case "width":
-      dimension = aWindow.outerWidth;
-      break;
-    case "height":
-      dimension = aWindow.outerHeight;
-      break;
-    default:
-      dimension = aAttribute in aWindow ? aWindow[aAttribute] : "";
-      break;
-    }
-    
-    if (aWindow.windowState == aWindow.STATE_NORMAL) {
-      return dimension;
-    }
-    return aWindow.document.documentElement.getAttribute(aAttribute) || dimension;
-  },
-
-  /**
-   * Get nsIURI from string
-   * @param string
-   * @returns nsIURI
-   */
-  _getURIFromString: function sss_getURIFromString(aString) {
-    var ioService = Cc["@mozilla.org/network/io-service;1"].
-                    getService(Ci.nsIIOService);
-    return ioService.newURI(aString, null, null);
-  },
-
-  /**
-   * Annotate a breakpad crash report with the currently selected tab's URL.
-   */
-  _updateCrashReportURL: function sss_updateCrashReportURL(aWindow) {
-    if (!Ci.nsICrashReporter) {
-      // if breakpad isn't built, don't bother next time at all
-      this._updateCrashReportURL = function(aWindow) {};
-      return;
-    }
-    try {
-      var currentURI = aWindow.getBrowser().currentURI.clone();
-      // if the current URI contains a username/password, remove it
-      try { 
-        currentURI.userPass = ""; 
-      } 
-      catch (ex) { } // ignore failures on about: URIs
-
-      var cr = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsICrashReporter);
-      cr.annotateCrashReport("URL", currentURI.spec);
-    }
-    catch (ex) {
-      // don't make noise when crashreporter is built but not enabled
-      if (ex.result != Components.results.NS_ERROR_NOT_INITIALIZED)
-        debug(ex);
-    }
-  },
-
-  /**
-   * @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 display the page when there's nothing to restore
-    let winData = aState.windows || null;
-    if (!winData || winData.length == 0)
-      return false;
-    
-    // don't wrap a single about:sessionrestore page
-    if (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 = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.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
-   */
-  _safeEval: function sss_safeEval(aStr) {
-    return Cu.evalInSandbox(aStr, new Cu.Sandbox("about:blank"));
-  },
-
-  /**
-   * Converts a JavaScript object into a JSON string
-   * (see http://www.json.org/ for more information).
-   *
-   * The inverse operation consists of JSON.parse(JSON_string).
-   *
-   * @param aJSObject is the object to be converted
-   * @returns the object's JSON representation
-   */
-  _toJSONString: function sss_toJSONString(aJSObject) {
-    // XXXzeniko drop the following keys used only for internal bookkeeping:
-    //           _tabStillLoading, _hosts, _formDataSaved
-    let jsonString = JSON.stringify(aJSObject);
-    
-    if (/[\u2028\u2029]/.test(jsonString)) {
-      // work-around for bug 485563 until we can use JSON.parse
-      // instead of evalInSandbox everywhere
-      jsonString = jsonString.replace(/[\u2028\u2029]/g,
-                                      function($0) "\\u" + $0.charCodeAt(0).toString(16));
-    }
-    
-    return jsonString;
-  },
-
-  _notifyIfAllWindowsRestored: function sss_notifyIfAllWindowsRestored() {
-    if (this._restoreCount) {
-      this._restoreCount--;
-      if (this._restoreCount == 0) {
-        // This was the last window restored at startup, notify observers.
-        var observerService = Cc["@mozilla.org/observer-service;1"].
-                              getService(Ci.nsIObserverService);
-        observerService.notifyObservers(null, NOTIFY_WINDOWS_RESTORED, "");
-      }
-    }
-  },
-
-  /**
-   * @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;
-  },
-
-  /**
-   * Replace "Loading..." with the tab label (with minimal side-effects)
-   * @param aString is the string the title is stored in
-   * @param aTabbrowser is a tabbrowser object, containing aTab
-   * @param aTab is the tab whose title we're updating & using
-   *
-   * @returns aString that has been updated with the new title
-   */
-  _replaceLoadingTitle : function sss_replaceLoadingTitle(aString, aTabbrowser, aTab) {
-    if (aString == aTabbrowser.mStringBundle.getString("tabs.loading")) {
-      aTabbrowser.setTabTitle(aTab);
-      [aString, aTab.label] = [aTab.label, aString];
-    }
-    return aString;
-  },
-
-  /**
-   * Resize this._closedWindows to the value of the pref, except in the case
-   * where we don't have any non-popup windows on Windows and Linux. Then we must
-   * resize such that we have at least one non-popup window.
-   */
-  _capClosedWindows : function sss_capClosedWindows() {
-    let maxWindowsUndo = this._prefBranch.getIntPref("sessionstore.max_windows_undo");
-    if (this._closedWindows.length <= maxWindowsUndo)
-      return;
-    let spliceTo = maxWindowsUndo;
-//@line 2780 "/builds/slave/linux_build/build/browser/components/sessionstore/src/nsSessionStore.js"
-    let normalWindowIndex = 0;
-    // try to find a non-popup window in this._closedWindows
-    while (normalWindowIndex < this._closedWindows.length &&
-           !!this._closedWindows[normalWindowIndex].isPopup)
-      normalWindowIndex++;
-    if (normalWindowIndex >= maxWindowsUndo)
-      spliceTo = normalWindowIndex + 1;
-//@line 2788 "/builds/slave/linux_build/build/browser/components/sessionstore/src/nsSessionStore.js"
-    this._closedWindows.splice(spliceTo);
-  },
-
-/* ........ Storage API .............. */
-
-  /**
-   * write file to disk
-   * @param aFile
-   *        nsIFile
-   * @param aData
-   *        String data
-   */
-  _writeFile: function sss_writeFile(aFile, aData) {
-    // init stream
-    var stream = Cc["@mozilla.org/network/safe-file-output-stream;1"].
-                 createInstance(Ci.nsIFileOutputStream);
-    stream.init(aFile, 0x02 | 0x08 | 0x20, 0600, 0);
-
-    // convert to UTF-8
-    var converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
-                    createInstance(Ci.nsIScriptableUnicodeConverter);
-    converter.charset = "UTF-8";
-    var convertedData = converter.ConvertFromUnicode(aData);
-    convertedData += converter.Finish();
-
-    // write and close stream
-    stream.write(convertedData, convertedData.length);
-    if (stream instanceof Ci.nsISafeOutputStream) {
-      stream.finish();
-    } else {
-      stream.close();
-    }
-  }
-};
-
-let XPathHelper = {
-  // these two hashes should be kept in sync
-  namespaceURIs:     { "xhtml": "http://www.w3.org/1999/xhtml" },
-  namespacePrefixes: { "http://www.w3.org/1999/xhtml": "xhtml" },
-
-  /**
-   * Generates an approximate XPath query to an (X)HTML node
-   */
-  generate: function sss_xph_generate(aNode) {
-    // have we reached the document node already?
-    if (!aNode.parentNode)
-      return "";
-    
-    let prefix = this.namespacePrefixes[aNode.namespaceURI] || null;
-    let tag = (prefix ? prefix + ":" : "") + this.escapeName(aNode.localName);
-    
-    // stop once we've found a tag with an ID
-    if (aNode.id)
-      return "//" + tag + "[@id=" + this.quoteArgument(aNode.id) + "]";
-    
-    // count the number of previous sibling nodes of the same tag
-    // (and possible also the same name)
-    let count = 0;
-    let nName = aNode.name || null;
-    for (let n = aNode; (n = n.previousSibling); )
-      if (n.localName == aNode.localName && n.namespaceURI == aNode.namespaceURI &&
-          (!nName || n.name == nName))
-        count++;
-    
-    // recurse until hitting either the document node or an ID'd node
-    return this.generate(aNode.parentNode) + "/" + tag +
-           (nName ? "[@name=" + this.quoteArgument(nName) + "]" : "") +
-           (count ? "[" + (count + 1) + "]" : "");
-  },
-
-  /**
-   * Resolves an XPath query generated by XPathHelper.generate
-   */
-  resolve: function sss_xph_resolve(aDocument, aQuery) {
-    let xptype = Ci.nsIDOMXPathResult.FIRST_ORDERED_NODE_TYPE;
-    return aDocument.evaluate(aQuery, aDocument, this.resolveNS, xptype, null).singleNodeValue;
-  },
-
-  /**
-   * Namespace resolver for the above XPath resolver
-   */
-  resolveNS: function sss_xph_resolveNS(aPrefix) {
-    return XPathHelper.namespaceURIs[aPrefix] || null;
-  },
-
-  /**
-   * @returns valid XPath for the given node (usually just the local name itself)
-   */
-  escapeName: function sss_xph_escapeName(aName) {
-    // we can't just use the node's local name, if it contains
-    // special characters (cf. bug 485482)
-    return /^\w+$/.test(aName) ? aName :
-           "*[local-name()=" + this.quoteArgument(aName) + "]";
-  },
-
-  /**
-   * @returns a properly quoted string to insert into an XPath query
-   */
-  quoteArgument: function sss_xph_quoteArgument(aArg) {
-    return !/'/.test(aArg) ? "'" + aArg + "'" :
-           !/"/.test(aArg) ? '"' + aArg + '"' :
-           "concat('" + aArg.replace(/'+/g, "',\"$&\",'") + "')";
-  },
-
-  /**
-   * @returns an XPath query to all savable form field nodes
-   */
-  get restorableFormNodes() {
-    // for a comprehensive list of all available <INPUT> types see
-    // http://mxr.mozilla.org/mozilla-central/search?string=kInputTypeTable
-    let ignoreTypes = ["password", "hidden", "button", "image", "submit", "reset"];
-    // XXXzeniko work-around until lower-case has been implemented (bug 398389)
-    let toLowerCase = '"ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz"';
-    let ignore = "not(translate(@type, " + toLowerCase + ")='" +
-      ignoreTypes.join("' or translate(@type, " + toLowerCase + ")='") + "')";
-    let formNodesXPath = "//textarea|//select|//xhtml:textarea|//xhtml:select|" +
-      "//input[" + ignore + "]|//xhtml:input[" + ignore + "]";
-    
-    delete this.restorableFormNodes;
-    return (this.restorableFormNodes = formNodesXPath);
-  }
-};
-
-// see nsPrivateBrowsingService.js
-String.prototype.hasRootDomain = function hasRootDomain(aDomain)
-{
-  let index = this.indexOf(aDomain);
-  if (index == -1)
-    return false;
-
-  if (this == aDomain)
-    return true;
-
-  let prevChar = this[index - 1];
-  return (index == (this.length - aDomain.length)) &&
-         (prevChar == "." || prevChar == "/");
-}
-
-const NoModule = {
-  getClassObject: function(aCompMgr, aCID, aIID) {
-    Components.returnCode = Cr.NS_ERROR_NOT_REGISTERED;
-    return null;
-  },
-  registerSelf: function(aCompMgr, aFileSpec, aLocation, aType) { return; },
-  unregisterSelf: function(aCompMgr, aLocation, aType) { return; },
-  canUnload: function(aCompMgr) { return true; }
-};
-
-
-function NSGetModule(aComMgr, aFileSpec) {
-  var prefs = Components.classes["@mozilla.org/preferences-service;1"]
-        .getService(Components.interfaces.nsIPrefBranch);
-  if(is_FF35) {
-      return XPCOMUtils.generateModule([SessionStoreService]);
-  } else {
-      return NoModule;
-  }
-}
diff --git a/src/components/nsSessionStore36.diff b/src/components/nsSessionStore36.diff
deleted file mode 100644
index 7181457..0000000
--- a/src/components/nsSessionStore36.diff
+++ /dev/null
@@ -1,109 +0,0 @@
---- nsSessionStore36.js.orig	2009-12-13 11:46:41.000000000 -0800
-+++ nsSessionStore36.js	2009-12-13 11:46:18.000000000 -0800
-@@ -112,7 +112,20 @@
- 
- //@line 116 "/builds/moz2_slave/linux_build/build/browser/components/sessionstore/src/nsSessionStore.js"
- 
--Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-+var is_FF36 = false;
-+var appInfo = Components.classes["@mozilla.org/xre/app-info;1"]
-+  .getService(Components.interfaces.nsIXULAppInfo);
-+var versionChecker = Components.classes["@mozilla.org/xpcom/version-comparator;1"]
-+  .getService(Components.interfaces.nsIVersionComparator);
-+
-+if(versionChecker.compare(appInfo.version, "3.6a1") >= 0
-+        && versionChecker.compare(appInfo.version, "3.7a1") <= 0) {
-+    is_FF36 = true;
-+    Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-+} else {
-+    is_FF36 = false;
-+    var XPCOMUtils = { generateQI: function() { return null; } };
-+}
- 
- function debug(aMsg) {
-   aMsg = ("SessionStore: " + aMsg).replace(/\S{80}/g, "$&\n");
-@@ -129,6 +142,9 @@
- /* :::::::: The Service ::::::::::::::: */
- 
- function SessionStoreService() {
-+  this.logger = Components.classes["@torproject.org/torbutton-logger;1"]
-+      .getService(Components.interfaces.nsISupports).wrappedJSObject;
-+  this.logger.log(3, "Component Load: New SessionStoreService @mozilla.org/browser/sessionstore;1");
- }
- 
- SessionStoreService.prototype = {
-@@ -1151,9 +1167,26 @@
-     var tabbrowser = aWindow.getBrowser();
-     var tabs = tabbrowser.mTabs;
-     var tabsData = this._windows[aWindow.__SSi].tabs = [];
--    
--    for (var i = 0; i < tabs.length; i++)
--      tabsData.push(this._collectTabData(tabs[i]));
-+    var prefs = Components.classes["@mozilla.org/preferences-service;1"]
-+        .getService(Components.interfaces.nsIPrefBranch);
-+    var bypass_tor = prefs.getBoolPref("extensions.torbutton.notor_sessionstore");
-+    var bypass_nontor = prefs.getBoolPref("extensions.torbutton.nonontor_sessionstore");
-+  
-+    for (var i = 0; i < tabs.length; i++) {
-+      if(typeof(tabs[i].linkedBrowser.__tb_tor_fetched) != "undefined") {
-+          if(bypass_tor && tabs[i].linkedBrowser.__tb_tor_fetched) {
-+              this.logger.log(2, "Skipping saving tor tab: "+tabs[i].linkedBrowser.currentURI.spec);
-+              continue; 
-+          }
-+          if(bypass_nontor && !tabs[i].linkedBrowser.__tb_tor_fetched) {
-+              this.logger.log(2, "Skipping saving non-tor tab: "+tabs[i].linkedBrowser.currentURI.spec);
-+              continue; 
-+          }
-+      }
-+      this.logger.log(2, "Saving tab: "+tabs[i].linkedBrowser.currentURI.spec);
-+      var tabData = this._collectTabData(tabs[i]);
-+      if(tabData) tabsData.push(tabData);
-+    }
-     
-     this._windows[aWindow.__SSi].selected = tabbrowser.mTabBox.selectedIndex + 1;
-   },
-@@ -1189,6 +1222,7 @@
-         browser.__SS_data.entries[history.index] &&
-         history.index < this._sessionhistory_max_entries - 1 && !aFullData) {
-       tabData = browser.__SS_data;
-+      if(!tabData) return null;
-       tabData.index = history.index + 1;
-     }
-     else if (history && history.count > 0) {
-@@ -1434,8 +1468,8 @@
-     for (var i = 0; i < browsers.length; i++) {
-       try {
-         var tabData = this._windows[aWindow.__SSi].tabs[i];
--        if (browsers[i].__SS_data &&
--            browsers[i].__SS_data._tabStillLoading)
-+        if (!tabData || tabData.entries.length == 0 ||
-+            browsers[i].__SS_data && browsers[i].__SS_data._tabStillLoading)
-           continue; // ignore incompletely initialized tabs
-         this._updateTextAndScrollDataForTab(aWindow, browsers[i], tabData);
-       }
-@@ -3064,5 +3098,23 @@
-          (prevChar == "." || prevChar == "/");
- }
- 
--function NSGetModule(aComMgr, aFileSpec)
--  XPCOMUtils.generateModule([SessionStoreService]);
-+const NoModule = {
-+  getClassObject: function(aCompMgr, aCID, aIID) {
-+    Components.returnCode = Cr.NS_ERROR_NOT_REGISTERED;
-+    return null;
-+  },
-+  registerSelf: function(aCompMgr, aFileSpec, aLocation, aType) { return; },
-+  unregisterSelf: function(aCompMgr, aLocation, aType) { return; },
-+  canUnload: function(aCompMgr) { return true; }
-+};
-+
-+
-+function NSGetModule(aComMgr, aFileSpec) {
-+  var prefs = Components.classes["@mozilla.org/preferences-service;1"]
-+        .getService(Components.interfaces.nsIPrefBranch);
-+  if(is_FF36) {
-+      return XPCOMUtils.generateModule([SessionStoreService]);
-+  } else {
-+      return NoModule;
-+  }
-+}
diff --git a/src/components/nsSessionStore36.js b/src/components/nsSessionStore36.js
deleted file mode 100644
index e721c9d..0000000
--- a/src/components/nsSessionStore36.js
+++ /dev/null
@@ -1,3120 +0,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 at gmail.com>
- * Portions created by the Initial Developer are Copyright (C) 2006
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Dietrich Ayala <dietrich at mozilla.com>
- *   Ehsan Akhgari <ehsan.akhgari at gmail.com>
- *   Michael Kraft <morac99-firefox at yahoo.com>
- *   Paul O’Shannessy <paul at oshannessy.com>
- *   Nils Maier <maierman at web.de>
- *
- * 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 ***** */
-
-/**
- * Session Storage and Restoration
- * 
- * Overview
- * This service keeps track of a user's session, storing the various bits
- * required to return the browser to its current state. The relevant data is 
- * stored in memory, and is periodically saved to disk in a file in the 
- * profile directory. The service is started at first window load, in
- * delayedStartup, and will restore the session from the data received from
- * the nsSessionStartup service.
- */
-
-/* :::::::: Constants and Helpers ::::::::::::::: */
-
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-const Cr = Components.results;
-const Cu = Components.utils;
-
-const STATE_STOPPED = 0;
-const STATE_RUNNING = 1;
-const STATE_QUITTING = -1;
-
-const STATE_STOPPED_STR = "stopped";
-const STATE_RUNNING_STR = "running";
-
-const PRIVACY_NONE = 0;
-const PRIVACY_ENCRYPTED = 1;
-const PRIVACY_FULL = 2;
-
-const NOTIFY_WINDOWS_RESTORED = "sessionstore-windows-restored";
-const NOTIFY_BROWSER_STATE_RESTORED = "sessionstore-browser-state-restored";
-
-// global notifications observed
-const OBSERVING = [
-  "domwindowopened", "domwindowclosed",
-  "quit-application-requested", "quit-application-granted",
-  "browser-lastwindow-close-granted",
-  "quit-application", "browser:purge-session-history",
-  "private-browsing", "browser:purge-domain-data",
-  "private-browsing-change-granted"
-];
-
-/*
-XUL Window properties to (re)store
-Restored in restoreDimensions()
-*/
-const WINDOW_ATTRIBUTES = ["width", "height", "screenX", "screenY", "sizemode"];
-
-/* 
-Hideable window features to (re)store
-Restored in restoreWindowFeatures()
-*/
-const WINDOW_HIDEABLE_FEATURES = [
-  "menubar", "toolbar", "locationbar", 
-  "personalbar", "statusbar", "scrollbars"
-];
-
-/*
-docShell capabilities to (re)store
-Restored in restoreHistory()
-eg: browser.docShell["allow" + aCapability] = false;
-
-XXX keep these in sync with all the attributes starting
-    with "allow" in /docshell/base/nsIDocShell.idl
-*/
-const CAPABILITIES = [
-  "Subframes", "Plugins", "Javascript", "MetaRedirects", "Images",
-  "DNSPrefetch", "Auth"
-];
-
-//@line 116 "/builds/moz2_slave/linux_build/build/browser/components/sessionstore/src/nsSessionStore.js"
-
-var is_FF36 = false;
-var appInfo = Components.classes["@mozilla.org/xre/app-info;1"]
-  .getService(Components.interfaces.nsIXULAppInfo);
-var versionChecker = Components.classes["@mozilla.org/xpcom/version-comparator;1"]
-  .getService(Components.interfaces.nsIVersionComparator);
-
-if(versionChecker.compare(appInfo.version, "3.6a1") >= 0
-        && versionChecker.compare(appInfo.version, "3.7a1") <= 0) {
-    is_FF36 = true;
-    Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-} else {
-    is_FF36 = false;
-    var XPCOMUtils = { generateQI: function() { return null; } };
-}
-
-function debug(aMsg) {
-  aMsg = ("SessionStore: " + aMsg).replace(/\S{80}/g, "$&\n");
-  Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService)
-                                     .logStringMessage(aMsg);
-}
-
-__defineGetter__("NetUtil", function() {
-  delete this.NetUtil;
-  Cu.import("resource://gre/modules/NetUtil.jsm");
-  return NetUtil;
-});
-
-/* :::::::: The Service ::::::::::::::: */
-
-function SessionStoreService() {
-  this.logger = Components.classes["@torproject.org/torbutton-logger;1"]
-      .getService(Components.interfaces.nsISupports).wrappedJSObject;
-  this.logger.log(3, "Component Load: New SessionStoreService @mozilla.org/browser/sessionstore;1");
-}
-
-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);
-  // 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
-  _resume_from_crash: true,
-
-  // During the initial restore and setBrowserState calls tracks the number of
-  // windows yet to be restored
-  _restoreCount: 0,
-
-  // whether a setBrowserState call is in progress
-  _browserSetState: false,
-
-  // time in milliseconds (Date.now()) when the session was last written to file
-  _lastSaveTime: 0, 
-
-  // states for all currently opened windows
-  _windows: {},
-
-  // states for all recently closed windows
-  _closedWindows: [],
-
-  // 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,
-
-  // whether we are in private browsing mode
-  _inPrivateBrowsing: false,
-
-  // whether we clearing history on shutdown
-  _clearingOnShutdown: false,
-
-  // List of windows that are being closed during setBrowserState.
-  _closingWindows: [],
-
-//@line 193 "/builds/moz2_slave/linux_build/build/browser/components/sessionstore/src/nsSessionStore.js"
-  // whether the last window was closed and should be restored
-  _restoreLastWindow: false,
-//@line 196 "/builds/moz2_slave/linux_build/build/browser/components/sessionstore/src/nsSessionStore.js"
-
-/* ........ 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
-      // SessionStore are really tracked by it
-      if (aWindow && (!aWindow.__SSi || !this._windows[aWindow.__SSi]))
-        this.onLoad(aWindow);
-      return;
-    }
-
-    this._prefBranch = Cc["@mozilla.org/preferences-service;1"].
-                       getService(Ci.nsIPrefService).getBranch("browser.");
-    this._prefBranch.QueryInterface(Ci.nsIPrefBranch2);
-
-    this._observerService = Cc["@mozilla.org/observer-service;1"].
-                            getService(Ci.nsIObserverService);
-
-    OBSERVING.forEach(function(aTopic) {
-      this._observerService.addObserver(this, aTopic, true);
-    }, this);
-
-    var pbs = Cc["@mozilla.org/privatebrowsing;1"].
-              getService(Ci.nsIPrivateBrowsingService);
-    this._inPrivateBrowsing = pbs.privateBrowsingEnabled;
-
-    // get interval from prefs - used often, so caching/observing instead of fetching on-demand
-    this._interval = this._prefBranch.getIntPref("sessionstore.interval");
-    this._prefBranch.addObserver("sessionstore.interval", this, true);
-    
-    // get crash recovery state from prefs and allow for proper reaction to state changes
-    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("sessionstore.max_tabs_undo", this, true);
-    this._prefBranch.addObserver("sessionstore.max_windows_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 = Cc["@mozilla.org/file/directory_service;1"].
-                     getService(Ci.nsIProperties);
-    this._sessionFile = dirService.get("ProfD", Ci.nsILocalFile);
-    this._sessionFileBackup = this._sessionFile.clone();
-    this._sessionFile.append("sessionstore.js");
-    this._sessionFileBackup.append("sessionstore.bak");
-
-    // get string containing session state
-    var iniString;
-    try {
-      var ss = Cc["@mozilla.org/browser/sessionstartup;1"].
-               getService(Ci.nsISessionStartup);
-      if (ss.doRestore())
-        iniString = ss.state;
-    }
-    catch(ex) { dump(ex + "\n"); } // no state to restore, which is ok
-
-    if (iniString) {
-      try {
-        // parse the session state into JS objects
-        this._initialState = this._safeEval("(" + iniString + ")");
-        
-        let lastSessionCrashed =
-          this._initialState.session && this._initialState.session.state &&
-          this._initialState.session.state == STATE_RUNNING_STR;
-        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
-        delete this._initialState.windows[0].hidden;
-        // Since nothing is hidden in the first window, it cannot be a popup
-        delete this._initialState.windows[0].isPopup;
-      }
-      catch (ex) { debug("The session file is invalid: " + ex); }
-    }
-
-    // remove the session data files if crash recovery is disabled
-    if (!this._resume_from_crash)
-      this._clearDisk();
-    else { // create a backup if the session data file exists
-      try {
-        if (this._sessionFileBackup.exists())
-          this._sessionFileBackup.remove(false);
-        if (this._sessionFile.exists())
-          this._sessionFile.copyTo(null, this._sessionFileBackup.leafName);
-      }
-      catch (ex) { Cu.reportError(ex); } // file was write-locked?
-    }
-
-    // at this point, we've as good as resumed the session, so we can
-    // clear the resume_session_once flag, if it's set
-    if (this._loadState != STATE_QUITTING &&
-        this._prefBranch.getBoolPref("sessionstore.resume_session_once"))
-      this._prefBranch.setBoolPref("sessionstore.resume_session_once", false);
-    
-    // As this is called at delayedStartup, restoration must be initiated here
-    this.onLoad(aWindow);
-  },
-
-  /**
-   * Called on application shutdown, after notifications:
-   * quit-application-granted, quit-application
-   */
-  _uninit: function sss_uninit() {
-    if (this._doResumeSession()) { // save all data for session resuming 
-      this.saveState(true);
-    }
-    else { // discard all session related data 
-      this._clearDisk();
-    }
-    // Make sure to break our cycle with the save timer
-    if (this._saveTimer) {
-      this._saveTimer.cancel();
-      this._saveTimer = null;
-    }
-  },
-
-  /**
-   * Handle notifications
-   */
-  observe: function sss_observe(aSubject, aTopic, aData) {
-    // for event listeners
-    var _this = this;
-
-    switch (aTopic) {
-    case "domwindowopened": // catch new windows
-      aSubject.addEventListener("load", function(aEvent) {
-        aEvent.currentTarget.removeEventListener("load", arguments.callee, false);
-        _this.onLoad(aEvent.currentTarget);
-        }, false);
-      break;
-    case "domwindowclosed": // catch closed windows
-      if (this._closingWindows.length > 0) {
-        let index = this._closingWindows.indexOf(aSubject);
-        if (index != -1) {
-          this._closingWindows.splice(index, 1);
-          if (this._closingWindows.length == 0)
-            this._sendRestoreCompletedNotifications(true);
-        }
-      }
-      this.onClose(aSubject);
-      break;
-    case "quit-application-requested":
-      // get a current snapshot of all windows
-      this._forEachBrowserWindow(function(aWindow) {
-        this._collectWindowData(aWindow);
-      });
-      this._dirtyWindows = [];
-      break;
-    case "quit-application-granted":
-      // freeze the data at what we've got (ignoring closing windows)
-      this._loadState = STATE_QUITTING;
-      break;
-//@line 368 "/builds/moz2_slave/linux_build/build/browser/components/sessionstore/src/nsSessionStore.js"
-    case "browser-lastwindow-close-granted":
-      // last browser window is quitting.
-      // remember to restore the last window when another browser window is openend
-      // do not account for pref(resume_session_once) at this point, as it might be
-      // set by another observer getting this notice after us
-      this._restoreLastWindow = true;
-      break;
-//@line 376 "/builds/moz2_slave/linux_build/build/browser/components/sessionstore/src/nsSessionStore.js"
-    case "quit-application":
-      if (aData == "restart") {
-        this._prefBranch.setBoolPref("sessionstore.resume_session_once", true);
-        this._clearingOnShutdown = false;
-      }
-      this._loadState = STATE_QUITTING; // just to be sure
-      this._uninit();
-      break;
-    case "browser:purge-session-history": // catch sanitization 
-      let openWindows = {};
-      this._forEachBrowserWindow(function(aWindow) {
-        Array.forEach(aWindow.gBrowser.browsers, function(aBrowser) {
-          delete aBrowser.__SS_data;
-        });
-        openWindows[aWindow.__SSi] = true;
-      });
-      // also clear all data about closed tabs and windows
-      for (let ix in this._windows) {
-        if (ix in openWindows)
-          this._windows[ix]._closedTabs = [];
-        else
-          delete this._windows[ix];
-      }
-      // also clear all data about closed windows
-      this._closedWindows = [];
-      this._clearDisk();
-      // give the tabbrowsers a chance to clear their histories first
-      var win = this._getMostRecentBrowserWindow();
-      if (win)
-        win.setTimeout(function() { _this.saveState(true); }, 0);
-      else if (this._loadState == STATE_RUNNING)
-        this.saveState(true);
-      // Delete the private browsing backed up state, if any
-      if ("_stateBackup" in this)
-        delete this._stateBackup;
-      if (this._loadState == STATE_QUITTING)
-        this._clearingOnShutdown = true;
-      break;
-    case "browser:purge-domain-data":
-      // does a session history entry contain a url for the given domain?
-      function containsDomain(aEntry) {
-        try {
-          if (this._getURIFromString(aEntry.url).host.hasRootDomain(aData))
-            return true;
-        }
-        catch (ex) { /* url had no host at all */ }
-        return aEntry.children && aEntry.children.some(containsDomain, this);
-      }
-      // remove all closed tabs containing a reference to the given domain
-      for (let ix in this._windows) {
-        let closedTabs = this._windows[ix]._closedTabs;
-        for (let i = closedTabs.length - 1; i >= 0; i--) {
-          if (closedTabs[i].state.entries.some(containsDomain, this))
-            closedTabs.splice(i, 1);
-        }
-      }
-      // remove all open & closed tabs containing a reference to the given
-      // domain in closed windows
-      for (let ix = this._closedWindows.length - 1; ix >= 0; ix--) {
-        let closedTabs = this._closedWindows[ix]._closedTabs;
-        let openTabs = this._closedWindows[ix].tabs;
-        let openTabCount = openTabs.length;
-        for (let i = closedTabs.length - 1; i >= 0; i--)
-          if (closedTabs[i].state.entries.some(containsDomain, this))
-            closedTabs.splice(i, 1);
-        for (let j = openTabs.length - 1; j >= 0; j--) {
-          if (openTabs[j].entries.some(containsDomain, this)) {
-            openTabs.splice(j, 1);
-            if (this._closedWindows[ix].selected > j)
-              this._closedWindows[ix].selected--;
-          }
-        }
-        if (openTabs.length == 0) {
-          this._closedWindows.splice(ix, 1);
-        }
-        else if (openTabs.length != openTabCount) {
-          // Adjust the window's title if we removed an open tab
-          let selectedTab = openTabs[this._closedWindows[ix].selected - 1];
-          // some duplication from restoreHistory - make sure we get the correct title
-          let activeIndex = (selectedTab.index || selectedTab.entries.length) - 1;
-          if (activeIndex >= selectedTab.entries.length)
-            activeIndex = selectedTab.entries.length - 1;
-          this._closedWindows[ix].title = selectedTab.entries[activeIndex].title;
-        }
-      }
-      if (this._loadState == STATE_RUNNING)
-        this.saveState(true);
-      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
-      case "sessionstore.max_tabs_undo":
-        for (let ix in this._windows) {
-          this._windows[ix]._closedTabs.splice(this._prefBranch.getIntPref("sessionstore.max_tabs_undo"));
-        }
-        break;
-      case "sessionstore.max_windows_undo":
-        this._capClosedWindows();
-        break;
-      case "sessionstore.interval":
-        this._interval = this._prefBranch.getIntPref("sessionstore.interval");
-        // reset timer and save
-        if (this._saveTimer) {
-          this._saveTimer.cancel();
-          this._saveTimer = null;
-        }
-        this.saveStateDelayed(null, -1);
-        break;
-      case "sessionstore.resume_from_crash":
-        this._resume_from_crash = this._prefBranch.getBoolPref("sessionstore.resume_from_crash");
-        // either create the file with crash recovery information or remove it
-        // (when _loadState is not STATE_RUNNING, that file is used for session resuming instead)
-        if (this._resume_from_crash)
-          this.saveState(true);
-        else if (this._loadState == STATE_RUNNING)
-          this._clearDisk();
-        break;
-      }
-      break;
-    case "timer-callback": // timer call back for delayed saving
-      this._saveTimer = null;
-      this.saveState();
-      break;
-    case "private-browsing":
-      switch (aData) {
-      case "enter":
-        this._inPrivateBrowsing = true;
-        break;
-      case "exit":
-        aSubject.QueryInterface(Ci.nsISupportsPRBool);
-        let quitting = aSubject.data;
-        if (quitting) {
-          // save the backed up state with session set to stopped,
-          // otherwise resuming next time would look like a crash
-          if ("_stateBackup" in this) {
-            var oState = this._stateBackup;
-            oState.session = { state: STATE_STOPPED_STR };
-
-            this._saveStateObject(oState);
-          }
-          // make sure to restore the non-private session upon resuming
-          this._prefBranch.setBoolPref("sessionstore.resume_session_once", true);
-        }
-        else
-          this._inPrivateBrowsing = false;
-        delete this._stateBackup;
-        break;
-      }
-      break;
-    case "private-browsing-change-granted":
-      if (aData == "enter") {
-        this.saveState(true);
-        this._stateBackup = this._safeEval(this._getCurrentState(true).toSource());
-      }
-      break;
-    }
-  },
-
-/* ........ Window Event Handlers .............. */
-
-  /**
-   * Implement nsIDOMEventListener for handling various window and tab events
-   */
-  handleEvent: function sss_handleEvent(aEvent) {
-    var win = aEvent.currentTarget.ownerDocument.defaultView;
-    switch (aEvent.type) {
-      case "load":
-      case "pageshow":
-        this.onTabLoad(win, aEvent.currentTarget, aEvent);
-        break;
-      case "change":
-      case "input":
-      case "DOMAutoComplete":
-        this.onTabInput(win, aEvent.currentTarget);
-        break;
-      case "scroll":
-        this.onTabScroll(win);
-        break;
-      case "TabOpen":
-      case "TabClose":
-        let browser = aEvent.originalTarget.linkedBrowser;
-        if (aEvent.type == "TabOpen") {
-          this.onTabAdd(win, browser);
-        }
-        else {
-          // aEvent.detail determines if the tab was closed by moving to a different window
-          if (!aEvent.detail)
-            this.onTabClose(win, aEvent.originalTarget);
-          this.onTabRemove(win, browser);
-        }
-        break;
-      case "TabSelect":
-        this.onTabSelect(win);
-        break;
-    }
-  },
-
-  /**
-   * If it's the first window load since app start...
-   * - determine if we're reloading after a crash or a forced-restart
-   * - restore window state
-   * - restart downloads
-   * Set up event listeners for this window's tabs
-   * @param aWindow
-   *        Window reference
-   */
-  onLoad: function sss_onLoad(aWindow) {
-    // return if window has already been initialized
-    if (aWindow && aWindow.__SSi && this._windows[aWindow.__SSi])
-      return;
-
-    // ignore non-browser windows and windows opened while shutting down
-    if (aWindow.document.documentElement.getAttribute("windowtype") != "navigator:browser" ||
-      this._loadState == STATE_QUITTING)
-      return;
-
-    // assign it a unique identifier (timestamp)
-    aWindow.__SSi = "window" + Date.now();
-
-    // and create its data object
-    this._windows[aWindow.__SSi] = { tabs: [], selected: 0, _closedTabs: [] };
-    if (!aWindow.toolbar.visible)
-      this._windows[aWindow.__SSi].isPopup = true;
-    
-    // perform additional initialization when the first window is loading
-    if (this._loadState == STATE_STOPPED) {
-      this._loadState = STATE_RUNNING;
-      this._lastSaveTime = Date.now();
-      
-      // 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
-        this._initialState._firstTabs = true;
-        this._restoreCount = this._initialState.windows ? this._initialState.windows.length : 0;
-        this.restoreWindow(aWindow, this._initialState, this._isCmdLineEmpty(aWindow));
-        delete this._initialState;
-        
-        // _loadState changed from "stopped" to "running"
-        // force a save operation so that crashes happening during startup are correctly counted
-        this.saveState(true);
-      }
-      else {
-        // Nothing to restore, notify observers things are complete.
-        this._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);
-    }
-//@line 632 "/builds/moz2_slave/linux_build/build/browser/components/sessionstore/src/nsSessionStore.js"
-    else if (this._restoreLastWindow && aWindow.toolbar.visible &&
-             this._closedWindows.length && this._doResumeSession() &&
-             !this._inPrivateBrowsing) {
-
-      // default to the most-recently closed window
-      // don't use popup windows
-      let state = null;
-      this._closedWindows = this._closedWindows.filter(function(aWinState) {
-        if (!state && !aWinState.isPopup) {
-          state = aWinState;
-          return false;
-        }
-        return true;
-      });
-      if (state) {
-        delete state.hidden;
-        state = { windows: [state] };
-        this._restoreCount = 1;
-        this.restoreWindow(aWindow, state, this._isCmdLineEmpty(aWindow));
-      }
-      // we actually restored the session just now.
-      this._prefBranch.setBoolPref("sessionstore.resume_session_once", false);
-    }
-    if (this._restoreLastWindow && aWindow.toolbar.visible) {
-      // always reset (if not a popup window)
-      // we don't want to restore a window directly after, for example,
-      // undoCloseWindow was executed.
-      this._restoreLastWindow = false;
-    }
-//@line 662 "/builds/moz2_slave/linux_build/build/browser/components/sessionstore/src/nsSessionStore.js"
-
-    var tabbrowser = aWindow.gBrowser;
-    
-    // add tab change listeners to all already existing tabs
-    for (let i = 0; i < tabbrowser.browsers.length; i++) {
-      this.onTabAdd(aWindow, tabbrowser.browsers[i], true);
-    }
-    // notification of tab add/remove/selection
-    tabbrowser.addEventListener("TabOpen", this, true);
-    tabbrowser.addEventListener("TabClose", this, true);
-    tabbrowser.addEventListener("TabSelect", this, true);
-  },
-
-  /**
-   * On window close...
-   * - remove event listeners from tabs
-   * - save all window data
-   * @param aWindow
-   *        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;
-    }
-    
-    if (this.windowToFocus && this.windowToFocus == aWindow) {
-      delete this.windowToFocus;
-    }
-    
-    var tabbrowser = aWindow.gBrowser;
-
-    tabbrowser.removeEventListener("TabOpen", this, true);
-    tabbrowser.removeEventListener("TabClose", this, true);
-    tabbrowser.removeEventListener("TabSelect", this, true);
-    
-    let winData = this._windows[aWindow.__SSi];
-    if (this._loadState == STATE_RUNNING) { // window not closed during a regular shut-down 
-      // update all window data for a last time
-      this._collectWindowData(aWindow);
-      
-      if (isFullyLoaded) {
-        winData.title = aWindow.content.document.title || tabbrowser.selectedTab.label;
-        winData.title = this._replaceLoadingTitle(winData.title, tabbrowser,
-                                                  tabbrowser.selectedTab);
-        this._updateCookies([winData]);
-      }
-      
-      // save the window if it has multiple tabs or a single tab with entries
-      if (winData.tabs.length > 1 ||
-          (winData.tabs.length == 1 && winData.tabs[0].entries.length > 0)) {
-        this._closedWindows.unshift(winData);
-        this._capClosedWindows();
-      }
-      
-      // clear this window from the list
-      delete this._windows[aWindow.__SSi];
-      
-      // save the state without this window to disk
-      this.saveStateDelayed();
-    }
-    
-    for (let i = 0; i < tabbrowser.browsers.length; i++) {
-      this.onTabRemove(aWindow, tabbrowser.browsers[i], true);
-    }
-    
-    // cache the window state until the window is completely gone
-    aWindow.__SS_dyingCache = winData;
-    
-    delete aWindow.__SSi;
-  },
-
-  /**
-   * set up listeners for a new tab
-   * @param aWindow
-   *        Window reference
-   * @param aBrowser
-   *        Browser reference
-   * @param aNoNotification
-   *        bool Do not save state if we're updating an existing tab
-   */
-  onTabAdd: function sss_onTabAdd(aWindow, aBrowser, aNoNotification) {
-    aBrowser.addEventListener("load", this, true);
-    aBrowser.addEventListener("pageshow", this, true);
-    aBrowser.addEventListener("change", this, true);
-    aBrowser.addEventListener("input", this, true);
-    aBrowser.addEventListener("DOMAutoComplete", this, true);
-    aBrowser.addEventListener("scroll", this, true);
-    
-    if (!aNoNotification) {
-      this.saveStateDelayed(aWindow);
-    }
-
-    this._updateCrashReportURL(aWindow);
-  },
-
-  /**
-   * remove listeners for a tab
-   * @param aWindow
-   *        Window reference
-   * @param aBrowser
-   *        Browser reference
-   * @param aNoNotification
-   *        bool Do not save state if we're updating an existing tab
-   */
-  onTabRemove: function sss_onTabRemove(aWindow, aBrowser, aNoNotification) {
-    aBrowser.removeEventListener("load", this, true);
-    aBrowser.removeEventListener("pageshow", this, true);
-    aBrowser.removeEventListener("change", this, true);
-    aBrowser.removeEventListener("input", this, true);
-    aBrowser.removeEventListener("DOMAutoComplete", this, true);
-    aBrowser.removeEventListener("scroll", this, true);
-    
-    delete aBrowser.__SS_data;
-    
-    if (!aNoNotification) {
-      this.saveStateDelayed(aWindow);
-    }
-  },
-
-  /**
-   * When a tab closes, collect its properties
-   * @param aWindow
-   *        Window reference
-   * @param aTab
-   *        Tab reference
-   */
-  onTabClose: function sss_onTabClose(aWindow, aTab) {
-    // notify the tabbrowser that the tab state will be retrieved for the last time
-    // (so that extension authors can easily set data on soon-to-be-closed tabs)
-    var event = aWindow.document.createEvent("Events");
-    event.initEvent("SSTabClosing", true, false);
-    aTab.dispatchEvent(event);
-    
-    var maxTabsUndo = this._prefBranch.getIntPref("sessionstore.max_tabs_undo");
-    // don't update our internal state if we don't have to
-    if (maxTabsUndo == 0) {
-      return;
-    }
-    
-    // make sure that the tab related data is up-to-date
-    var tabState = this._collectTabData(aTab);
-    this._updateTextAndScrollDataForTab(aWindow, aTab.linkedBrowser, tabState);
-
-    // store closed-tab data for undo
-    if (tabState.entries.length > 0) {
-      let tabTitle = aTab.label;
-      let tabbrowser = aWindow.gBrowser;
-      tabTitle = this._replaceLoadingTitle(tabTitle, tabbrowser, aTab);
-      
-      this._windows[aWindow.__SSi]._closedTabs.unshift({
-        state: tabState,
-        title: tabTitle,
-        image: aTab.getAttribute("image"),
-        pos: aTab._tPos
-      });
-      var length = this._windows[aWindow.__SSi]._closedTabs.length;
-      if (length > maxTabsUndo)
-        this._windows[aWindow.__SSi]._closedTabs.splice(maxTabsUndo, length - maxTabsUndo);
-    }
-  },
-
-  /**
-   * When a tab loads, save state.
-   * @param aWindow
-   *        Window reference
-   * @param aBrowser
-   *        Browser reference
-   * @param aEvent
-   *        Event obj
-   */
-  onTabLoad: function sss_onTabLoad(aWindow, aBrowser, aEvent) { 
-    // react on "load" and solitary "pageshow" events (the first "pageshow"
-    // following "load" is too late for deleting the data caches)
-    if (aEvent.type != "load" && !aEvent.persisted) {
-      return;
-    }
-    
-    delete aBrowser.__SS_data;
-    this.saveStateDelayed(aWindow);
-    
-    // attempt to update the current URL we send in a crash report
-    this._updateCrashReportURL(aWindow);
-  },
-
-  /**
-   * Called when a browser sends the "input" notification 
-   * @param aWindow
-   *        Window reference
-   * @param aBrowser
-   *        Browser reference
-   */
-  onTabInput: function sss_onTabInput(aWindow, aBrowser) {
-    if (aBrowser.__SS_data)
-      delete aBrowser.__SS_data._formDataSaved;
-    
-    this.saveStateDelayed(aWindow, 3000);
-  },
-
-  /**
-   * Called when a browser sends a "scroll" notification 
-   * @param aWindow
-   *        Window reference
-   */
-  onTabScroll: function sss_onTabScroll(aWindow) {
-    this.saveStateDelayed(aWindow, 3000);
-  },
-
-  /**
-   * When a tab is selected, save session data
-   * @param aWindow
-   *        Window reference
-   */
-  onTabSelect: function sss_onTabSelect(aWindow) {
-    if (this._loadState == STATE_RUNNING) {
-      this._windows[aWindow.__SSi].selected = aWindow.gBrowser.tabContainer.selectedIndex;
-      this.saveStateDelayed(aWindow);
-
-      // attempt to update the current URL we send in a crash report
-      this._updateCrashReportURL(aWindow);
-    }
-  },
-
-/* ........ nsISessionStore API .............. */
-
-  getBrowserState: function sss_getBrowserState() {
-    return this._toJSONString(this._getCurrentState());
-  },
-
-  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 = Cr.NS_ERROR_INVALID_ARG);
-
-    this._browserSetState = true;
-
-    var window = this._getMostRecentBrowserWindow();
-    if (!window) {
-      this._restoreCount = 1;
-      this._openWindowWithState(state);
-      return;
-    }
-
-    // make sure closed window data isn't kept
-    this._closedWindows = [];
-
-    // determine how many windows are meant to be restored
-    this._restoreCount = state.windows ? state.windows.length : 0;
-
-    var self = this;
-    // close all other browser windows
-    this._forEachBrowserWindow(function(aWindow) {
-      if (aWindow != window) {
-        self._closingWindows.push(aWindow);
-        aWindow.close();
-      }
-    });
-
-    // restore to the given state
-    this.restoreWindow(window, state, true);
-  },
-
-  getWindowState: function sss_getWindowState(aWindow) {
-    if (!aWindow.__SSi && !aWindow.__SS_dyingCache)
-      throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
-    
-    if (!aWindow.__SSi)
-      return this._toJSONString({ windows: [aWindow.__SS_dyingCache] });
-    return this._toJSONString(this._getWindowState(aWindow));
-  },
-
-  setWindowState: function sss_setWindowState(aWindow, aState, aOverwrite) {
-    if (!aWindow.__SSi)
-      throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
-    
-    this.restoreWindow(aWindow, "(" + aState + ")", aOverwrite);
-  },
-
-  getTabState: function sss_getTabState(aTab) {
-    if (!aTab.ownerDocument || !aTab.ownerDocument.defaultView.__SSi)
-      throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
-    
-    var tabState = this._collectTabData(aTab);
-    
-    var window = aTab.ownerDocument.defaultView;
-    this._updateTextAndScrollDataForTab(window, aTab.linkedBrowser, tabState);
-    
-    return this._toJSONString(tabState);
-  },
-
-  setTabState: function sss_setTabState(aTab, aState) {
-    var tabState = this._safeEval("(" + aState + ")");
-    if (!tabState.entries || !aTab.ownerDocument || !aTab.ownerDocument.defaultView.__SSi)
-      throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
-    
-    var window = aTab.ownerDocument.defaultView;
-    this.restoreHistoryPrecursor(window, [aTab], [tabState], 0, 0, 0);
-  },
-
-  duplicateTab: function sss_duplicateTab(aWindow, aTab) {
-    if (!aTab.ownerDocument || !aTab.ownerDocument.defaultView.__SSi ||
-        !aWindow.getBrowser)
-      throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
-    
-    var tabState = this._collectTabData(aTab, true);
-    var sourceWindow = aTab.ownerDocument.defaultView;
-    this._updateTextAndScrollDataForTab(sourceWindow, aTab.linkedBrowser, tabState, true);
-    
-    var newTab = aWindow.getBrowser().addTab();
-    this.restoreHistoryPrecursor(aWindow, [newTab], [tabState], 0, 0, 0);
-    
-    return newTab;
-  },
-
-  getClosedTabCount: function sss_getClosedTabCount(aWindow) {
-    if (!aWindow.__SSi && aWindow.__SS_dyingCache)
-      return aWindow.__SS_dyingCache._closedTabs.length;
-    if (!aWindow.__SSi)
-      // XXXzeniko shouldn't we throw here?
-      return 0; // not a browser window, or not otherwise tracked by SS.
-    
-    return this._windows[aWindow.__SSi]._closedTabs.length;
-  },
-
-  getClosedTabData: function sss_getClosedTabDataAt(aWindow) {
-    if (!aWindow.__SSi && !aWindow.__SS_dyingCache)
-      throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
-    
-    if (!aWindow.__SSi)
-      return this._toJSONString(aWindow.__SS_dyingCache._closedTabs);
-    return this._toJSONString(this._windows[aWindow.__SSi]._closedTabs);
-  },
-
-  undoCloseTab: function sss_undoCloseTab(aWindow, aIndex) {
-    if (!aWindow.__SSi)
-      throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
-    
-    var closedTabs = this._windows[aWindow.__SSi]._closedTabs;
-
-    // default to the most-recently closed tab
-    aIndex = aIndex || 0;
-    if (!(aIndex in closedTabs))
-      throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
-    
-    // fetch the data of closed tab, while removing it from the array
-    let closedTab = closedTabs.splice(aIndex, 1).shift();
-    let closedTabState = closedTab.state;
-
-    // create a new tab
-    let browser = aWindow.gBrowser;
-    let tab = browser.addTab();
-      
-    // restore the tab's position
-    browser.moveTabTo(tab, closedTab.pos);
-
-    // restore tab content
-    this.restoreHistoryPrecursor(aWindow, [tab], [closedTabState], 1, 0, 0);
-
-    // focus the tab's content area
-    let content = browser.getBrowserForTab(tab).contentWindow;
-    aWindow.setTimeout(function() { content.focus(); }, 0);
-    
-    return tab;
-  },
-
-  forgetClosedTab: function sss_forgetClosedTab(aWindow, aIndex) {
-    if (!aWindow.__SSi)
-      throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
-    
-    var closedTabs = this._windows[aWindow.__SSi]._closedTabs;
-
-    // default to the most-recently closed tab
-    aIndex = aIndex || 0;
-    if (!(aIndex in closedTabs))
-      throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
-    
-    // remove closed tab from the array
-    closedTabs.splice(aIndex, 1);
-  },
-
-  getClosedWindowCount: function sss_getClosedWindowCount() {
-    return this._closedWindows.length;
-  },
-
-  getClosedWindowData: function sss_getClosedWindowData() {
-    return this._toJSONString(this._closedWindows);
-  },
-
-  undoCloseWindow: function sss_undoCloseWindow(aIndex) {
-    if (!(aIndex in this._closedWindows))
-      throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
-
-    // reopen the window
-    let state = { windows: this._closedWindows.splice(aIndex, 1) };
-    let window = this._openWindowWithState(state);
-    this.windowToFocus = window;
-    return window;
-  },
-
-  forgetClosedWindow: function sss_forgetClosedWindow(aIndex) {
-    // default to the most-recently closed window
-    aIndex = aIndex || 0;
-    if (!(aIndex in this._closedWindows))
-      throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
-    
-    // remove closed window from the array
-    this._closedWindows.splice(aIndex, 1);
-  },
-
-  getWindowValue: function sss_getWindowValue(aWindow, aKey) {
-    if (aWindow.__SSi) {
-      var data = this._windows[aWindow.__SSi].extData || {};
-      return data[aKey] || "";
-    }
-    if (aWindow.__SS_dyingCache) {
-      data = aWindow.__SS_dyingCache.extData || {};
-      return data[aKey] || "";
-    }
-    throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
-  },
-
-  setWindowValue: function sss_setWindowValue(aWindow, aKey, aStringValue) {
-    if (aWindow.__SSi) {
-      if (!this._windows[aWindow.__SSi].extData) {
-        this._windows[aWindow.__SSi].extData = {};
-      }
-      this._windows[aWindow.__SSi].extData[aKey] = aStringValue;
-      this.saveStateDelayed(aWindow);
-    }
-    else {
-      throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
-    }
-  },
-
-  deleteWindowValue: function sss_deleteWindowValue(aWindow, aKey) {
-    if (aWindow.__SSi && this._windows[aWindow.__SSi].extData &&
-        this._windows[aWindow.__SSi].extData[aKey])
-      delete this._windows[aWindow.__SSi].extData[aKey];
-    else
-      throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
-  },
-
-  getTabValue: function sss_getTabValue(aTab, aKey) {
-    var data = aTab.__SS_extdata || {};
-    return data[aKey] || "";
-  },
-
-  setTabValue: function sss_setTabValue(aTab, aKey, aStringValue) {
-    if (!aTab.__SS_extdata) {
-      aTab.__SS_extdata = {};
-    }
-    aTab.__SS_extdata[aKey] = aStringValue;
-    this.saveStateDelayed(aTab.ownerDocument.defaultView);
-  },
-
-  deleteTabValue: function sss_deleteTabValue(aTab, aKey) {
-    if (aTab.__SS_extdata && aTab.__SS_extdata[aKey])
-      delete aTab.__SS_extdata[aKey];
-    else
-      throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
-  },
-
-  persistTabAttribute: function sss_persistTabAttribute(aName) {
-    if (this.xulAttributes.indexOf(aName) != -1)
-      return; // this attribute is already being tracked
-    
-    this.xulAttributes.push(aName);
-    this.saveStateDelayed();
-  },
-
-/* ........ Saving Functionality .............. */
-
-  /**
-   * Store all session data for a window
-   * @param aWindow
-   *        Window reference
-   */
-  _saveWindowHistory: function sss_saveWindowHistory(aWindow) {
-    var tabbrowser = aWindow.getBrowser();
-    var tabs = tabbrowser.mTabs;
-    var tabsData = this._windows[aWindow.__SSi].tabs = [];
-    var prefs = Components.classes["@mozilla.org/preferences-service;1"]
-        .getService(Components.interfaces.nsIPrefBranch);
-    var bypass_tor = prefs.getBoolPref("extensions.torbutton.notor_sessionstore");
-    var bypass_nontor = prefs.getBoolPref("extensions.torbutton.nonontor_sessionstore");
-  
-    for (var i = 0; i < tabs.length; i++) {
-      if(typeof(tabs[i].linkedBrowser.__tb_tor_fetched) != "undefined") {
-          if(bypass_tor && tabs[i].linkedBrowser.__tb_tor_fetched) {
-              this.logger.log(2, "Skipping saving tor tab: "+tabs[i].linkedBrowser.currentURI.spec);
-              continue; 
-          }
-          if(bypass_nontor && !tabs[i].linkedBrowser.__tb_tor_fetched) {
-              this.logger.log(2, "Skipping saving non-tor tab: "+tabs[i].linkedBrowser.currentURI.spec);
-              continue; 
-          }
-      }
-      this.logger.log(2, "Saving tab: "+tabs[i].linkedBrowser.currentURI.spec);
-      var tabData = this._collectTabData(tabs[i]);
-      if(tabData) tabsData.push(tabData);
-    }
-    
-    this._windows[aWindow.__SSi].selected = tabbrowser.mTabBox.selectedIndex + 1;
-  },
-
-  /**
-   * Collect data related to a single tab
-   * @param aTab
-   *        tabbrowser tab
-   * @param aFullData
-   *        always return privacy sensitive data (use with care)
-   * @returns object
-   */
-  _collectTabData: function sss_collectTabData(aTab, aFullData) {
-    var tabData = { entries: [] };
-    var browser = aTab.linkedBrowser;
-    
-    if (!browser || !browser.currentURI)
-      // can happen when calling this function right after .addTab()
-      return tabData;
-    else if (browser.__SS_data && browser.__SS_data._tabStillLoading)
-      // use the data to be restored when the tab hasn't been completely loaded
-      return browser.__SS_data;
-    
-    var history = null;
-    try {
-      history = browser.sessionHistory;
-    }
-    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.__SS_data &&
-        browser.__SS_data.entries[history.index] &&
-        history.index < this._sessionhistory_max_entries - 1 && !aFullData) {
-      tabData = browser.__SS_data;
-      if(!tabData) return null;
-      tabData.index = history.index + 1;
-    }
-    else if (history && history.count > 0) {
-      for (var j = 0; j < history.count; j++)
-        tabData.entries.push(this._serializeHistoryEntry(history.getEntryAtIndex(j, false),
-                                                         aFullData));
-      tabData.index = history.index + 1;
-
-      // make sure not to cache privacy sensitive data which shouldn't get out
-      if (!aFullData)
-        browser.__SS_data = tabData;
-    }
-    else if (browser.currentURI.spec != "about:blank" ||
-             browser.contentDocument.body.hasChildNodes()) {
-      tabData.entries[0] = { url: browser.currentURI.spec };
-      tabData.index = 1;
-    }
-
-    // If there is a userTypedValue set, then either the user has typed something
-    // in the URL bar, or a new tab was opened with a URI to load. userTypedClear
-    // is used to indicate whether the tab was in some sort of loading state with
-    // userTypedValue.
-    if (browser.userTypedValue) {
-      tabData.userTypedValue = browser.userTypedValue;
-      tabData.userTypedClear = browser.userTypedClear;
-    }
-
-    var disallow = [];
-    for (var i = 0; i < CAPABILITIES.length; i++)
-      if (!browser.docShell["allow" + CAPABILITIES[i]])
-        disallow.push(CAPABILITIES[i]);
-    if (disallow.length > 0)
-      tabData.disallow = disallow.join(",");
-    else if (tabData.disallow)
-      delete tabData.disallow;
-    
-    if (this.xulAttributes.length > 0) {
-      tabData.attributes = {};
-      Array.forEach(aTab.attributes, function(aAttr) {
-        if (this.xulAttributes.indexOf(aAttr.name) > -1)
-          tabData.attributes[aAttr.name] = aAttr.value;
-      }, this);
-    }
-    
-    if (aTab.__SS_extdata)
-      tabData.extData = aTab.__SS_extdata;
-    else if (tabData.extData)
-      delete tabData.extData;
-    
-    if (history && browser.docShell instanceof Ci.nsIDocShell)
-      this._serializeSessionStorage(tabData, history, browser.docShell, aFullData);
-    
-    return tabData;
-  },
-
-  /**
-   * Get an object that is a serialized representation of a History entry
-   * Used for data storage
-   * @param aEntry
-   *        nsISHEntry instance
-   * @param aFullData
-   *        always return privacy sensitive data (use with care)
-   * @returns object
-   */
-  _serializeHistoryEntry: function sss_serializeHistoryEntry(aEntry, aFullData) {
-    var entry = { url: aEntry.URI.spec };
-    
-    if (aEntry.title && aEntry.title != entry.url) {
-      entry.title = aEntry.title;
-    }
-    if (aEntry.isSubFrame) {
-      entry.subframe = true;
-    }
-    if (!(aEntry instanceof Ci.nsISHEntry)) {
-      return entry;
-    }
-    
-    var cacheKey = aEntry.cacheKey;
-    if (cacheKey && cacheKey instanceof Ci.nsISupportsPRUint32 &&
-        cacheKey.data != 0) {
-      // XXXbz would be better to have cache keys implement
-      // nsISerializable or something.
-      entry.cacheKey = cacheKey.data;
-    }
-    entry.ID = aEntry.ID;
-    
-    if (aEntry.referrerURI)
-      entry.referrer = aEntry.referrerURI.spec;
-
-    if (aEntry.contentType)
-      entry.contentType = aEntry.contentType;
-    
-    var x = {}, y = {};
-    aEntry.getScrollPosition(x, y);
-    if (x.value != 0 || y.value != 0)
-      entry.scroll = x.value + "," + y.value;
-    
-    try {
-      var prefPostdata = this._prefBranch.getIntPref("sessionstore.postdata");
-      if (aEntry.postData && (aFullData ||
-            prefPostdata && this._checkPrivacyLevel(aEntry.URI.schemeIs("https")))) {
-        aEntry.postData.QueryInterface(Ci.nsISeekableStream).
-                        seek(Ci.nsISeekableStream.NS_SEEK_SET, 0);
-        var stream = Cc["@mozilla.org/binaryinputstream;1"].
-                     createInstance(Ci.nsIBinaryInputStream);
-        stream.setInputStream(aEntry.postData);
-        var postBytes = stream.readByteArray(stream.available());
-        var postdata = String.fromCharCode.apply(null, postBytes);
-        if (aFullData || prefPostdata == -1 ||
-            postdata.replace(/^(Content-.*\r\n)+(\r\n)*/, "").length <=
-              prefPostdata) {
-          // We can stop doing base64 encoding once our serialization into JSON
-          // is guaranteed to handle all chars in strings, including embedded
-          // nulls.
-          entry.postdata_b64 = btoa(postdata);
-        }
-      }
-    }
-    catch (ex) { debug(ex); } // POSTDATA is tricky - especially since some extensions don't get it right
-
-    if (aEntry.owner) {
-      // Not catching anything specific here, just possible errors
-      // from writeCompoundObject and the like.
-      try {
-        var binaryStream = Cc["@mozilla.org/binaryoutputstream;1"].
-                           createInstance(Ci.nsIObjectOutputStream);
-        var pipe = Cc["@mozilla.org/pipe;1"].createInstance(Ci.nsIPipe);
-        pipe.init(false, false, 0, 0xffffffff, null);
-        binaryStream.setOutputStream(pipe.outputStream);
-        binaryStream.writeCompoundObject(aEntry.owner, Ci.nsISupports, true);
-        binaryStream.close();
-
-        // Now we want to read the data from the pipe's input end and encode it.
-        var scriptableStream = Cc["@mozilla.org/binaryinputstream;1"].
-                               createInstance(Ci.nsIBinaryInputStream);
-        scriptableStream.setInputStream(pipe.inputStream);
-        var ownerBytes =
-          scriptableStream.readByteArray(scriptableStream.available());
-        // We can stop doing base64 encoding once our serialization into JSON
-        // is guaranteed to handle all chars in strings, including embedded
-        // nulls.
-        entry.owner_b64 = btoa(String.fromCharCode.apply(null, ownerBytes));
-      }
-      catch (ex) { debug(ex); }
-    }
-    
-    if (!(aEntry instanceof Ci.nsISHContainer)) {
-      return entry;
-    }
-    
-    if (aEntry.childCount > 0) {
-      entry.children = [];
-      for (var i = 0; i < aEntry.childCount; i++) {
-        var child = aEntry.GetChildAt(i);
-        if (child) {
-          entry.children.push(this._serializeHistoryEntry(child, aFullData));
-        }
-        else { // to maintain the correct frame order, insert a dummy entry 
-          entry.children.push({ url: "about:blank" });
-        }
-        // don't try to restore framesets containing wyciwyg URLs (cf. bug 424689 and bug 450595)
-        if (/^wyciwyg:\/\//.test(entry.children[i].url)) {
-          delete entry.children;
-          break;
-        }
-      }
-    }
-    
-    return entry;
-  },
-
-  /**
-   * Updates all sessionStorage "super cookies"
-   * @param aTabData
-   *        The data object for a specific tab
-   * @param aHistory
-   *        That tab's session history
-   * @param aDocShell
-   *        That tab's docshell (containing the sessionStorage)
-   * @param aFullData
-   *        always return privacy sensitive data (use with care)
-   */
-  _serializeSessionStorage:
-    function sss_serializeSessionStorage(aTabData, aHistory, aDocShell, aFullData) {
-    let storageData = {};
-    let hasContent = false;
-
-    for (let i = 0; i < aHistory.count; i++) {
-      let uri = aHistory.getEntryAtIndex(i, false).URI;
-      // sessionStorage is saved per origin (cf. nsDocShell::GetSessionStorageForURI)
-      let domain = uri.spec;
-      try {
-        if (uri.host)
-          domain = uri.prePath;
-      }
-      catch (ex) { /* this throws for host-less URIs (such as about: or jar:) */ }
-      if (storageData[domain] || !(aFullData || this._checkPrivacyLevel(uri.schemeIs("https"))))
-        continue;
-
-      let storage, storageItemCount = 0;
-      try {
-        var principal = Cc["@mozilla.org/scriptsecuritymanager;1"].
-                        getService(Ci.nsIScriptSecurityManager).
-                        getCodebasePrincipal(uri);
-
-        // Using getSessionStorageForPrincipal instead of getSessionStorageForURI
-        // just to be able to pass aCreate = false, that avoids creation of the
-        // sessionStorage object for the page earlier than the page really
-        // requires it. It was causing problems while accessing a storage when
-        // a page later changed its domain.
-        storage = aDocShell.getSessionStorageForPrincipal(principal, false);
-        if (storage)
-          storageItemCount = storage.length;
-      }
-      catch (ex) { /* sessionStorage might throw if it's turned off, see bug 458954 */ }
-      if (storageItemCount == 0)
-        continue;
-
-      let data = storageData[domain] = {};
-      for (let j = 0; j < storageItemCount; j++) {
-        try {
-          let key = storage.key(j);
-          let item = storage.getItem(key);
-          data[key] = item;
-        }
-        catch (ex) { /* XXXzeniko this currently throws for secured items (cf. bug 442048) */ }
-      }
-      hasContent = true;
-    }
-
-    if (hasContent)
-      aTabData.storage = storageData;
-  },
-
-  /**
-   * go through all tabs and store the current scroll positions
-   * and innerHTML content of WYSIWYG editors
-   * @param aWindow
-   *        Window reference
-   */
-  _updateTextAndScrollData: function sss_updateTextAndScrollData(aWindow) {
-    var browsers = aWindow.getBrowser().browsers;
-    for (var i = 0; i < browsers.length; i++) {
-      try {
-        var tabData = this._windows[aWindow.__SSi].tabs[i];
-        if (!tabData || tabData.entries.length == 0 ||
-            browsers[i].__SS_data && browsers[i].__SS_data._tabStillLoading)
-          continue; // ignore incompletely initialized tabs
-        this._updateTextAndScrollDataForTab(aWindow, browsers[i], tabData);
-      }
-      catch (ex) { debug(ex); } // get as much data as possible, ignore failures (might succeed the next time)
-    }
-  },
-
-  /**
-   * go through all frames and store the current scroll positions
-   * and innerHTML content of WYSIWYG editors
-   * @param aWindow
-   *        Window reference
-   * @param aBrowser
-   *        single browser reference
-   * @param aTabData
-   *        tabData object to add the information to
-   * @param aFullData
-   *        always return privacy sensitive data (use with care)
-   */
-  _updateTextAndScrollDataForTab:
-    function sss_updateTextAndScrollDataForTab(aWindow, aBrowser, aTabData, aFullData) {
-    var tabIndex = (aTabData.index || aTabData.entries.length) - 1;
-    // entry data needn't exist for tabs just initialized with an incomplete session state
-    if (!aTabData.entries[tabIndex])
-      return;
-    
-    let selectedPageStyle = aBrowser.markupDocumentViewer.authorStyleDisabled ? "_nostyle" :
-                            this._getSelectedPageStyle(aBrowser.contentWindow);
-    if (selectedPageStyle)
-      aTabData.pageStyle = selectedPageStyle;
-    else if (aTabData.pageStyle)
-      delete aTabData.pageStyle;
-    
-    this._updateTextAndScrollDataForFrame(aWindow, aBrowser.contentWindow,
-                                          aTabData.entries[tabIndex],
-                                          !aTabData._formDataSaved, aFullData);
-    aTabData._formDataSaved = true;
-    if (aBrowser.currentURI.spec == "about:config")
-      aTabData.entries[tabIndex].formdata = {
-        "#textbox": aBrowser.contentDocument.getElementById("textbox").wrappedJSObject.value
-      };
-  },
-
-  /**
-   * go through all subframes and store all form data, the current
-   * scroll positions and innerHTML content of WYSIWYG editors
-   * @param aWindow
-   *        Window reference
-   * @param aContent
-   *        frame reference
-   * @param aData
-   *        part of a tabData object to add the information to
-   * @param aUpdateFormData
-   *        update all form data for this tab
-   * @param aFullData
-   *        always return privacy sensitive data (use with care)
-   */
-  _updateTextAndScrollDataForFrame:
-    function sss_updateTextAndScrollDataForFrame(aWindow, aContent, aData,
-                                                 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) ||
-        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;
-      }
-      
-      // designMode is undefined e.g. for XUL documents (as about:config)
-      if ((aContent.document.designMode || "") == "on") {
-        if (aData.innerHTML === undefined && !aFullData) {
-          // we get no "input" events from iframes - listen for keypress here
-          let _this = this;
-          aContent.addEventListener("keypress", function(aEvent) {
-            _this.saveStateDelayed(aWindow, 3000);
-          }, true);
-        }
-        aData.innerHTML = aContent.document.body.innerHTML;
-      }
-    }
-
-    // get scroll position from nsIDOMWindowUtils, since it allows avoiding a
-    // flush of layout
-    let domWindowUtils = aContent.QueryInterface(Ci.nsIInterfaceRequestor)
-                                 .getInterface(Ci.nsIDOMWindowUtils);
-    let scrollX = {}, scrollY = {};
-    domWindowUtils.getScrollXY(false, scrollX, scrollY);
-    aData.scroll = scrollX.value + "," + scrollY.value;
-  },
-
-  /**
-   * determine the title of the currently enabled style sheet (if any)
-   * and recurse through the frameset if necessary
-   * @param   aContent is a frame reference
-   * @returns the title style sheet determined to be enabled (empty string if none)
-   */
-  _getSelectedPageStyle: function sss_getSelectedPageStyle(aContent) {
-    const forScreen = /(?:^|,)\s*(?:all|screen)\s*(?:,|$)/i;
-    for (let i = 0; i < aContent.document.styleSheets.length; i++) {
-      let ss = aContent.document.styleSheets[i];
-      let media = ss.media.mediaText;
-      if (!ss.disabled && ss.title && (!media || forScreen.test(media)))
-        return ss.title
-    }
-    for (let i = 0; i < aContent.frames.length; i++) {
-      let selectedPageStyle = this._getSelectedPageStyle(aContent.frames[i]);
-      if (selectedPageStyle)
-        return selectedPageStyle;
-    }
-    return "";
-  },
-
-  /**
-   * collect the state of all form elements
-   * @param aDocument
-   *        document reference
-   */
-  _collectFormDataForFrame: function sss_collectFormDataForFrame(aDocument) {
-    let formNodes = aDocument.evaluate(XPathHelper.restorableFormNodes, aDocument,
-                                       XPathHelper.resolveNS,
-                                       Ci.nsIDOMXPathResult.UNORDERED_NODE_ITERATOR_TYPE, null);
-    let node = formNodes.iterateNext();
-    if (!node)
-      return null;
-    
-    const MAX_GENERATED_XPATHS = 100;
-    let generatedCount = 0;
-    
-    let data = {};
-    do {
-      // Only generate a limited number of XPath expressions for perf reasons (cf. bug 477564)
-      if (!node.id && ++generatedCount > MAX_GENERATED_XPATHS)
-        continue;
-      
-      let id = node.id ? "#" + node.id : XPathHelper.generate(node);
-      if (node instanceof Ci.nsIDOMHTMLInputElement) {
-        if (node.type != "file")
-          data[id] = node.type == "checkbox" || node.type == "radio" ? node.checked : node.value;
-        else
-          data[id] = { type: "file", fileList: node.mozGetFileNameArray({}) };
-      }
-      else if (node instanceof Ci.nsIDOMHTMLTextAreaElement)
-        data[id] = node.value;
-      else if (!node.multiple)
-        data[id] = node.selectedIndex;
-      else {
-        let options = Array.map(node.options, function(aOpt, aIx) aOpt.selected ? aIx : -1);
-        data[id] = options.filter(function(aIx) aIx >= 0);
-      }
-    } while ((node = formNodes.iterateNext()));
-    
-    return data;
-  },
-
-  /**
-   * store all hosts for a URL
-   * @param aWindow
-   *        Window reference
-   */
-  _updateCookieHosts: function sss_updateCookieHosts(aWindow) {
-    var hosts = this._windows[aWindow.__SSi]._hosts = {};
-    
-    // get the domain for each URL
-    function extractHosts(aEntry) {
-      if (/^https?:\/\/(?:[^@\/\s]+@)?([\w.-]+)/.test(aEntry.url)) {
-        if (!hosts[RegExp.$1] && _this._checkPrivacyLevel(_this._getURIFromString(aEntry.url).schemeIs("https"))) {
-          hosts[RegExp.$1] = true;
-        }
-      }
-      else if (/^file:\/\/([^\/]*)/.test(aEntry.url)) {
-        hosts[RegExp.$1] = true;
-      }
-      if (aEntry.children) {
-        aEntry.children.forEach(extractHosts);
-      }
-    }
-
-    var _this = this;
-    this._windows[aWindow.__SSi].tabs.forEach(function(aTabData) { aTabData.entries.forEach(extractHosts); });
-  },
-
-  /**
-   * Serialize cookie data
-   * @param aWindows
-   *        array of Window references
-   */
-  _updateCookies: function sss_updateCookies(aWindows) {
-    function addCookieToHash(aHash, aHost, aPath, aName, aCookie) {
-      // lazily build up a 3-dimensional hash, with
-      // aHost, aPath, and aName as keys
-      if (!aHash[aHost])
-        aHash[aHost] = {};
-      if (!aHash[aHost][aPath])
-        aHash[aHost][aPath] = {};
-      aHash[aHost][aPath][aName] = aCookie;
-    }
-
-    var cm = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager2);
-    // collect the cookies per window
-    for (var i = 0; i < aWindows.length; i++)
-      aWindows[i].cookies = [];
-
-    var jscookies = {};
-    var _this = this;
-    // MAX_EXPIRY should be 2^63-1, but JavaScript can't handle that precision
-    var MAX_EXPIRY = Math.pow(2, 62);
-    aWindows.forEach(function(aWindow) {
-      for (var host in aWindow._hosts) {
-        var list = cm.getCookiesFromHost(host);
-        while (list.hasMoreElements()) {
-          var cookie = list.getNext().QueryInterface(Ci.nsICookie2);
-          if (cookie.isSession && _this._checkPrivacyLevel(cookie.isSecure)) {
-            // use the cookie's host, path, and name as keys into a hash,
-            // to make sure we serialize each cookie only once
-            if (!(cookie.host in jscookies &&
-                  cookie.path in jscookies[cookie.host] &&
-                  cookie.name in jscookies[cookie.host][cookie.path])) {
-              var jscookie = { "host": cookie.host, "value": cookie.value };
-              // only add attributes with non-default values (saving a few bits)
-              if (cookie.path) jscookie.path = cookie.path;
-              if (cookie.name) jscookie.name = cookie.name;
-              if (cookie.isSecure) jscookie.secure = true;
-              if (cookie.isHttpOnly) jscookie.httponly = true;
-              if (cookie.expiry < MAX_EXPIRY) jscookie.expiry = cookie.expiry;
-
-              addCookieToHash(jscookies, cookie.host, cookie.path, cookie.name, jscookie);
-            }
-            aWindow.cookies.push(jscookies[cookie.host][cookie.path][cookie.name]);
-          }
-        }
-      }
-    });
-
-    // don't include empty cookie sections
-    for (i = 0; i < aWindows.length; i++)
-      if (aWindows[i].cookies.length == 0)
-        delete aWindows[i].cookies;
-  },
-
-  /**
-   * Store window dimensions, visibility, sidebar
-   * @param aWindow
-   *        Window reference
-   */
-  _updateWindowFeatures: function sss_updateWindowFeatures(aWindow) {
-    var winData = this._windows[aWindow.__SSi];
-    
-    WINDOW_ATTRIBUTES.forEach(function(aAttr) {
-      winData[aAttr] = this._getWindowDimension(aWindow, aAttr);
-    }, this);
-    
-    var hidden = WINDOW_HIDEABLE_FEATURES.filter(function(aItem) {
-      return aWindow[aItem] && !aWindow[aItem].visible;
-    });
-    if (hidden.length != 0)
-      winData.hidden = hidden.join(",");
-    else if (winData.hidden)
-      delete winData.hidden;
-
-    var sidebar = aWindow.document.getElementById("sidebar-box").getAttribute("sidebarcommand");
-    if (sidebar)
-      winData.sidebar = sidebar;
-    else if (winData.sidebar)
-      delete winData.sidebar;
-  },
-
-  /**
-   * serialize session data as Ini-formatted string
-   * @param aUpdateAll
-   *        Bool update all windows 
-   * @returns string
-   */
-  _getCurrentState: function sss_getCurrentState(aUpdateAll) {
-    var activeWindow = this._getMostRecentBrowserWindow();
-    
-    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);
-        }
-        else { // always update the window features (whose change alone never triggers a save operation)
-          this._updateWindowFeatures(aWindow);
-        }
-      }, this);
-      this._dirtyWindows = [];
-    }
-    
-    // collect the data for all windows
-    var total = [], windows = [];
-    var nonPopupCount = 0;
-    var ix;
-    for (ix in this._windows) {
-      total.push(this._windows[ix]);
-      windows.push(ix);
-      if (!this._windows[ix].isPopup)
-        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++;
-      }
-    }
-
-    // shallow copy this._closedWindows to preserve current state
-    let lastClosedWindowsCopy = this._closedWindows.slice();
-
-//@line 1765 "/builds/moz2_slave/linux_build/build/browser/components/sessionstore/src/nsSessionStore.js"
-    // if no non-popup browser window remains open, return the state of the last closed window(s)
-    if (nonPopupCount == 0 && lastClosedWindowsCopy.length > 0) {
-      // prepend the last non-popup browser window, so that if the user loads more tabs
-      // at startup we don't accidentally add them to a popup window
-      do {
-        total.unshift(lastClosedWindowsCopy.shift())
-      } while (total[0].isPopup)
-    }
-//@line 1774 "/builds/moz2_slave/linux_build/build/browser/components/sessionstore/src/nsSessionStore.js"
-
-    if (activeWindow) {
-      this.activeWindowSSiCache = activeWindow.__SSi || "";
-    }
-    ix = this.activeWindowSSiCache ? windows.indexOf(this.activeWindowSSiCache) : -1;
-
-    return { windows: total, selectedWindow: ix + 1, _closedWindows: lastClosedWindowsCopy };
-  },
-
-  /**
-   * serialize session data for a window 
-   * @param aWindow
-   *        Window reference
-   * @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);
-    }
-    
-    var total = [this._windows[aWindow.__SSi]];
-    this._updateCookies(total);
-    
-    return { windows: total };
-  },
-
-  _collectWindowData: function sss_collectWindowData(aWindow) {
-    if (!this._isWindowLoaded(aWindow))
-      return;
-    
-    // update the internal state data for this window
-    this._saveWindowHistory(aWindow);
-    this._updateTextAndScrollData(aWindow);
-    this._updateCookieHosts(aWindow);
-    this._updateWindowFeatures(aWindow);
-    
-    this._dirtyWindows[aWindow.__SSi] = false;
-  },
-
-/* ........ Restoring Functionality .............. */
-
-  /**
-   * restore features to a single window
-   * @param aWindow
-   *        Window reference
-   * @param aState
-   *        JS object or its eval'able source
-   * @param aOverwriteTabs
-   *        bool overwrite existing tabs w/ new ones
-   * @param aFollowUp
-   *        bool this isn't the restoration of the first window
-   */
-  restoreWindow: function sss_restoreWindow(aWindow, aState, aOverwriteTabs, aFollowUp) {
-    if (!aFollowUp) {
-      this.windowToFocus = aWindow;
-    }
-    // initialize window if necessary
-    if (aWindow && (!aWindow.__SSi || !this._windows[aWindow.__SSi]))
-      this.onLoad(aWindow);
-
-    try {
-      var root = typeof aState == "string" ? this._safeEval(aState) : aState;
-      if (!root.windows[0]) {
-        this._sendRestoreCompletedNotifications();
-        return; // nothing to restore
-      }
-    }
-    catch (ex) { // invalid state object - don't restore anything 
-      debug(ex);
-      this._sendRestoreCompletedNotifications();
-      return;
-    }
-
-    if (root._closedWindows)
-      this._closedWindows = root._closedWindows;
-
-    var winData;
-    if (!aState.selectedWindow) {
-      aState.selectedWindow = 0;
-    }
-    // open new windows for all further window entries of a multi-window session
-    // (unless they don't contain any tab data)
-    for (var w = 1; w < root.windows.length; w++) {
-      winData = root.windows[w];
-      if (winData && winData.tabs && winData.tabs[0]) {
-        var window = this._openWindowWithState({ windows: [winData] });
-        if (w == aState.selectedWindow - 1) {
-          this.windowToFocus = window;
-        }
-      }
-    }
-    winData = root.windows[0];
-    if (!winData.tabs) {
-      winData.tabs = [];
-    }
-    // don't restore a single blank tab when we've had an external
-    // URL passed in for loading at startup (cf. bug 357419)
-    else if (root._firstTabs && !aOverwriteTabs && winData.tabs.length == 1 &&
-             (!winData.tabs[0].entries || winData.tabs[0].entries.length == 0)) {
-      winData.tabs = [];
-    }
-    
-    var tabbrowser = aWindow.gBrowser;
-    var openTabCount = aOverwriteTabs ? tabbrowser.browsers.length : -1;
-    var newTabCount = winData.tabs.length;
-    var tabs = [];
-
-    // disable smooth scrolling while adding, moving, removing and selecting tabs
-    var tabstrip = tabbrowser.tabContainer.mTabstrip;
-    var smoothScroll = tabstrip.smoothScroll;
-    tabstrip.smoothScroll = false;
-    
-    // make sure that the selected tab won't be closed in order to
-    // prevent unnecessary flickering
-    if (aOverwriteTabs && tabbrowser.selectedTab._tPos >= newTabCount)
-      tabbrowser.moveTabTo(tabbrowser.selectedTab, newTabCount - 1);
-    
-    for (var t = 0; t < newTabCount; t++) {
-      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(tabs[t], t);
-      }
-    }
-
-    // when overwriting tabs, remove all superflous ones
-    if (aOverwriteTabs && newTabCount < openTabCount) {
-      Array.slice(tabbrowser.mTabs, newTabCount, openTabCount)
-           .forEach(tabbrowser.removeTab, tabbrowser);
-    }
-    
-    if (aOverwriteTabs) {
-      this.restoreWindowFeatures(aWindow, winData);
-      delete this._windows[aWindow.__SSi].extData;
-    }
-    if (winData.cookies) {
-      this.restoreCookies(winData.cookies);
-    }
-    if (winData.extData) {
-      if (!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];
-      }
-    }
-    if (aOverwriteTabs || root._firstTabs) {
-      this._windows[aWindow.__SSi]._closedTabs = winData._closedTabs || [];
-    }
-    
-    this.restoreHistoryPrecursor(aWindow, tabs, winData.tabs,
-      (aOverwriteTabs ? (parseInt(winData.selected) || 1) : 0), 0, 0);
-
-    // set smoothScroll back to the original value
-    tabstrip.smoothScroll = smoothScroll;
-
-    this._sendRestoreCompletedNotifications();
-  },
-
-  /**
-   * Manage history restoration for a window
-   * @param aWindow
-   *        Window to restore the tabs into
-   * @param aTabs
-   *        Array of tab references
-   * @param aTabData
-   *        Array of tab data
-   * @param aSelectTab
-   *        Index of selected tab
-   * @param aIx
-   *        Index of the next tab to check readyness for
-   * @param aCount
-   *        Counter for number of times delaying b/c browser or history aren't ready
-   */
-  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]).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, aTabData, aSelectTab, aIx, aCount + 1);
-          }
-          aWindow.setTimeout(restoreHistoryFunc, 100, this);
-          return;
-        }
-      }
-    }
-    
-    // mark the tabs as loading
-    for (t = 0; t < aTabs.length; t++) {
-      var tab = aTabs[t];
-      var browser = tabbrowser.getBrowserForTab(tab);
-      
-      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";
-        continue;
-      }
-      
-      browser.stop(); // in case about:blank isn't done yet
-      
-      tab.setAttribute("busy", "true");
-      tabbrowser.updateIcon(tab);
-      tabbrowser.setTabTitleLoading(tab);
-      
-      // wall-paper fix for bug 439675: make sure that the URL to be loaded
-      // is always visible in the address bar
-      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.__SS_data = aTabData[t];
-    }
-    
-    if (aTabs.length > 0) {
-      // Determine if we can optimize & load visible tabs first
-      let maxVisibleTabs = Math.ceil(tabbrowser.tabContainer.mTabstrip.scrollClientSize /
-                                     aTabs[0].clientWidth);
-
-      // make sure we restore visible tabs first, if there are enough
-      if (maxVisibleTabs < aTabs.length && aSelectTab > 1) {
-        let firstVisibleTab = 0;
-        if (aTabs.length - maxVisibleTabs > aSelectTab) {
-          // aSelectTab is leftmost since we scroll to it when possible
-          firstVisibleTab = aSelectTab - 1;
-        } else {
-          // aSelectTab is rightmost or no more room to scroll right
-          firstVisibleTab = aTabs.length - maxVisibleTabs;
-        }
-        aTabs = aTabs.splice(firstVisibleTab, maxVisibleTabs).concat(aTabs);
-        aTabData = aTabData.splice(firstVisibleTab, maxVisibleTabs).concat(aTabData);
-        aSelectTab -= firstVisibleTab;
-      }
-
-      // make sure to restore the selected tab first (if any)
-      if (aSelectTab-- && aTabs[aSelectTab]) {
-        aTabs.unshift(aTabs.splice(aSelectTab, 1)[0]);
-        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, aTabData, idMap);
-  },
-
-  /**
-   * Restory history for a window
-   * @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, aTabData, aIdMap) {
-    var _this = this;
-    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 tab = aTabs.shift();
-    var tabData = aTabData.shift();
-
-    var browser = aWindow.getBrowser().getBrowserForTab(tab);
-    var history = browser.webNavigation.sessionHistory;
-    
-    if (history.count > 0) {
-      history.PurgeHistory(history.count);
-    }
-    history.QueryInterface(Ci.nsISHistoryInternal);
-    
-    if (!tabData.entries) {
-      tabData.entries = [];
-    }
-    if (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++) {
-      //XXXzpao Wallpaper patch for bug 514751
-      if (!tabData.entries[i].url)
-        continue;
-      history.addEntry(this._deserializeHistoryEntry(tabData.entries[i], aIdMap), true);
-    }
-    
-    // make sure to reset the capabilities and attributes, in case this tab gets reused
-    var disallow = (tabData.disallow)?tabData.disallow.split(","):[];
-    CAPABILITIES.forEach(function(aCapability) {
-      browser.docShell["allow" + aCapability] = disallow.indexOf(aCapability) == -1;
-    });
-    Array.filter(tab.attributes, function(aAttr) {
-      return (_this.xulAttributes.indexOf(aAttr.name) > -1);
-    }).forEach(tab.removeAttribute, tab);
-    if (tabData.xultab) {
-      // restore attributes from the legacy Firefox 2.0/3.0 format
-      tabData.xultab.split(" ").forEach(function(aAttr) {
-        if (/^([^\s=]+)=(.*)/.test(aAttr)) {
-          tab.setAttribute(RegExp.$1, decodeURI(RegExp.$2));
-        }
-      });
-    }
-    for (let name in tabData.attributes)
-      tab.setAttribute(name, tabData.attributes[name]);
-    
-    if (tabData.storage && browser.docShell instanceof Ci.nsIDocShell)
-      this._deserializeSessionStorage(tabData.storage, browser.docShell);
-    
-    // notify the tabbrowser that the tab chrome has been restored
-    var event = aWindow.document.createEvent("Events");
-    event.initEvent("SSTabRestoring", true, false);
-    tab.dispatchEvent(event);
-    
-    let activeIndex = (tabData.index || tabData.entries.length) - 1;
-    if (activeIndex >= tabData.entries.length)
-      activeIndex = tabData.entries.length - 1;
-    try {
-      if (activeIndex >= 0)
-        browser.webNavigation.gotoIndex(activeIndex);
-    }
-    catch (ex) {
-      // ignore page load errors
-      tab.removeAttribute("busy");
-    }
-    
-    if (tabData.entries.length > 0) {
-      // restore those aspects of the currently active documents
-      // which are not preserved in the plain history entries
-      // (mainly scroll state and text data)
-      browser.__SS_restore_data = tabData.entries[activeIndex] || {};
-      browser.__SS_restore_text = tabData.text || "";
-      browser.__SS_restore_pageStyle = tabData.pageStyle || "";
-      browser.__SS_restore_tab = tab;
-      browser.__SS_restore = this.restoreDocument_proxy;
-      browser.addEventListener("load", browser.__SS_restore, true);
-    }
-
-    // Handle userTypedValue. Setting userTypedValue seems to update gURLbar
-    // as needed. Calling loadURI will cancel form filling in restoreDocument_proxy
-    if (tabData.userTypedValue) {
-      browser.userTypedValue = tabData.userTypedValue;
-      if (tabData.userTypedClear)
-        browser.loadURI(tabData.userTypedValue, null, null, true);
-    }
-
-    aWindow.setTimeout(function(){ _this.restoreHistory(aWindow, aTabs, aTabData, aIdMap); }, 0);
-  },
-
-  /**
-   * expands serialized history data into a session-history-entry instance
-   * @param aEntry
-   *        Object containing serialized history data for a URL
-   * @param aIdMap
-   *        Hash for ensuring unique frame IDs
-   * @returns nsISHEntry
-   */
-  _deserializeHistoryEntry: function sss_deserializeHistoryEntry(aEntry, aIdMap) {
-    var shEntry = Cc["@mozilla.org/browser/session-history-entry;1"].
-                  createInstance(Ci.nsISHEntry);
-    
-    var ioService = Cc["@mozilla.org/network/io-service;1"].
-                    getService(Ci.nsIIOService);
-    shEntry.setURI(ioService.newURI(aEntry.url, null, null));
-    shEntry.setTitle(aEntry.title || aEntry.url);
-    if (aEntry.subframe)
-      shEntry.setIsSubFrame(aEntry.subframe || false);
-    shEntry.loadType = Ci.nsIDocShellLoadInfo.loadHistory;
-    if (aEntry.contentType)
-      shEntry.contentType = aEntry.contentType;
-    if (aEntry.referrer) 
-      shEntry.referrerURI = ioService.newURI(aEntry.referrer, null, null);
-    
-    if (aEntry.cacheKey) {
-      var cacheKey = Cc["@mozilla.org/supports-PRUint32;1"].
-                     createInstance(Ci.nsISupportsPRUint32);
-      cacheKey.data = aEntry.cacheKey;
-      shEntry.cacheKey = cacheKey;
-    }
-
-    if (aEntry.ID) {
-      // get a new unique ID for this frame (since the one from the last
-      // start might already be in use)
-      var id = aIdMap[aEntry.ID] || 0;
-      if (!id) {
-        for (id = Date.now(); id in aIdMap.used; id++);
-        aIdMap[aEntry.ID] = id;
-        aIdMap.used[id] = true;
-      }
-      shEntry.ID = id;
-    }
-    
-    if (aEntry.scroll) {
-      var scrollPos = (aEntry.scroll || "0,0").split(",");
-      scrollPos = [parseInt(scrollPos[0]) || 0, parseInt(scrollPos[1]) || 0];
-      shEntry.setScrollPosition(scrollPos[0], scrollPos[1]);
-    }
-
-    var postdata;
-    if (aEntry.postdata_b64) {  // Firefox 3
-      postdata = atob(aEntry.postdata_b64);
-    } else if (aEntry.postdata) { // Firefox 2
-      postdata = aEntry.postdata;
-    }
-
-    if (postdata) {
-      var stream = Cc["@mozilla.org/io/string-input-stream;1"].
-                   createInstance(Ci.nsIStringInputStream);
-      stream.setData(postdata, postdata.length);
-      shEntry.postData = stream;
-    }
-
-    if (aEntry.owner_b64) {  // Firefox 3
-      var ownerInput = Cc["@mozilla.org/io/string-input-stream;1"].
-                       createInstance(Ci.nsIStringInputStream);
-      var binaryData = atob(aEntry.owner_b64);
-      ownerInput.setData(binaryData, binaryData.length);
-      var binaryStream = Cc["@mozilla.org/binaryinputstream;1"].
-                         createInstance(Ci.nsIObjectInputStream);
-      binaryStream.setInputStream(ownerInput);
-      try { // Catch possible deserialization exceptions
-        shEntry.owner = binaryStream.readObject(true);
-      } catch (ex) { debug(ex); }
-    } else if (aEntry.ownerURI) { // Firefox 2
-      var uriObj = ioService.newURI(aEntry.ownerURI, null, null);
-      shEntry.owner = Cc["@mozilla.org/scriptsecuritymanager;1"].
-                      getService(Ci.nsIScriptSecurityManager).
-                      getCodebasePrincipal(uriObj);
-    }
-    
-    if (aEntry.children && shEntry instanceof Ci.nsISHContainer) {
-      for (var i = 0; i < aEntry.children.length; i++) {
-        //XXXzpao Wallpaper patch for bug 514751
-        if (!aEntry.children[i].url)
-          continue;
-        shEntry.AddChild(this._deserializeHistoryEntry(aEntry.children[i], aIdMap), i);
-      }
-    }
-    
-    return shEntry;
-  },
-
-  /**
-   * restores all sessionStorage "super cookies"
-   * @param aStorageData
-   *        Storage data to be restored
-   * @param aDocShell
-   *        A tab's docshell (containing the sessionStorage)
-   */
-  _deserializeSessionStorage: function sss_deserializeSessionStorage(aStorageData, aDocShell) {
-    let ioService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
-    for (let url in aStorageData) {
-      let uri = ioService.newURI(url, null, null);
-      let storage = aDocShell.getSessionStorageForURI(uri);
-      for (let key in aStorageData[url]) {
-        try {
-          storage.setItem(key, aStorageData[url][key]);
-        }
-        catch (ex) { Cu.reportError(ex); } // throws e.g. for URIs that can't have sessionStorage
-      }
-    }
-  },
-
-  /**
-   * Restore properties to a loaded document
-   */
-  restoreDocument_proxy: function sss_restoreDocument_proxy(aEvent) {
-    // wait for the top frame to be loaded completely
-    if (!aEvent || !aEvent.originalTarget || !aEvent.originalTarget.defaultView || aEvent.originalTarget.defaultView != aEvent.originalTarget.defaultView.top) {
-      return;
-    }
-    
-    // always call this before injecting content into a document!
-    function hasExpectedURL(aDocument, aURL)
-      !aURL || aURL.replace(/#.*/, "") == aDocument.location.href.replace(/#.*/, "");
-    
-    // restore text data saved by Firefox 2.0/3.0
-    var textArray = this.__SS_restore_text ? this.__SS_restore_text.split(" ") : [];
-    function restoreTextData(aContent, aPrefix, aURL) {
-      textArray.forEach(function(aEntry) {
-        if (/^((?:\d+\|)*)(#?)([^\s=]+)=(.*)$/.test(aEntry) &&
-            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 && node.type != "file") {
-            node.value = decodeURI(RegExp.$4);
-            
-            var event = document.createEvent("UIEvents");
-            event.initUIEvent("input", true, true, aContent, 0);
-            node.dispatchEvent(event);
-          }
-        }
-      });
-    }
-    
-    function restoreFormData(aDocument, aData, aURL) {
-      for (let key in aData) {
-        if (!hasExpectedURL(aDocument, aURL))
-          return;
-        
-        let node = key.charAt(0) == "#" ? aDocument.getElementById(key.slice(1)) :
-                                          XPathHelper.resolve(aDocument, key);
-        if (!node)
-          continue;
-        
-        let value = aData[key];
-        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");
-          event.initUIEvent("input", true, true, aDocument.defaultView, 0);
-          node.dispatchEvent(event);
-        }
-        else if (typeof value == "boolean")
-          node.checked = value;
-        else if (typeof value == "number")
-          try {
-            node.selectedIndex = value;
-          } catch (ex) { /* throws for invalid indices */ }
-        else if (value && value.fileList && value.type == "file" && node.type == "file")
-          node.mozSetFileNameArray(value.fileList, value.fileList.length);
-        else if (value && typeof value.indexOf == "function" && node.options) {
-          Array.forEach(node.options, function(aOpt, aIx) {
-            aOpt.selected = value.indexOf(aIx) > -1;
-          });
-        }
-        // NB: dispatching "change" events might have unintended side-effects
-      }
-    }
-    
-    let selectedPageStyle = this.__SS_restore_pageStyle;
-    let window = this.ownerDocument.defaultView;
-    function restoreTextDataAndScrolling(aContent, aData, aPrefix) {
-      if (aData.formdata)
-        restoreFormData(aContent.document, aData.formdata, aData.url);
-      else
-        restoreTextData(aContent, aPrefix, aData.url);
-      if (aData.innerHTML) {
-        window.setTimeout(function() {
-          if (aContent.document.designMode == "on" &&
-              hasExpectedURL(aContent.document, aData.url)) {
-            aContent.document.body.innerHTML = aData.innerHTML;
-          }
-        }, 0);
-      }
-      if (aData.scroll && /(\d+),(\d+)/.test(aData.scroll)) {
-        aContent.scrollTo(RegExp.$1, RegExp.$2);
-      }
-      Array.forEach(aContent.document.styleSheets, function(aSS) {
-        aSS.disabled = aSS.title && aSS.title != selectedPageStyle;
-      });
-      for (var i = 0; i < aContent.frames.length; i++) {
-        if (aData.children && aData.children[i] &&
-          hasExpectedURL(aContent.document, aData.url)) {
-          restoreTextDataAndScrolling(aContent.frames[i], aData.children[i], aPrefix + i + "|");
-        }
-      }
-    }
-    
-    // don't restore text data and scrolling state if the user has navigated
-    // away before the loading completed (except for in-page navigation)
-    if (hasExpectedURL(aEvent.originalTarget, this.__SS_restore_data.url)) {
-      var content = aEvent.originalTarget.defaultView;
-      if (this.currentURI.spec == "about:config") {
-        // unwrap the document for about:config because otherwise the properties
-        // of the XBL bindings - as the textbox - aren't accessible (see bug 350718)
-        content = content.wrappedJSObject;
-      }
-      restoreTextDataAndScrolling(content, this.__SS_restore_data, "");
-      this.markupDocumentViewer.authorStyleDisabled = selectedPageStyle == "_nostyle";
-      
-      // notify the tabbrowser that this document has been completely restored
-      var event = this.ownerDocument.createEvent("Events");
-      event.initEvent("SSTabRestored", true, false);
-      this.__SS_restore_tab.dispatchEvent(event);
-    }
-    
-    this.removeEventListener("load", this.__SS_restore, true);
-    delete this.__SS_restore_data;
-    delete this.__SS_restore_text;
-    delete this.__SS_restore_pageStyle;
-    delete this.__SS_restore_tab;
-    delete this.__SS_restore;
-  },
-
-  /**
-   * Restore visibility and dimension features to a window
-   * @param aWindow
-   *        Window reference
-   * @param aWinData
-   *        Object containing session data for the window
-   */
-  restoreWindowFeatures: function sss_restoreWindowFeatures(aWindow, aWinData) {
-    var hidden = (aWinData.hidden)?aWinData.hidden.split(","):[];
-    WINDOW_HIDEABLE_FEATURES.forEach(function(aItem) {
-      aWindow[aItem].visible = hidden.indexOf(aItem) == -1;
-    });
-    
-    if (aWinData.isPopup) {
-      this._windows[aWindow.__SSi].isPopup = true;
-      if (aWindow.gURLBar) {
-        aWindow.gURLBar.readOnly = true;
-        aWindow.gURLBar.setAttribute("enablehistory", "false");
-      }
-    }
-    else {
-      delete this._windows[aWindow.__SSi].isPopup;
-      if (aWindow.gURLBar) {
-        aWindow.gURLBar.readOnly = false;
-        aWindow.gURLBar.setAttribute("enablehistory", "true");
-      }
-    }
-
-    var _this = this;
-    aWindow.setTimeout(function() {
-      _this.restoreDimensions.apply(_this, [aWindow, aWinData.width || 0, 
-        aWinData.height || 0, "screenX" in aWinData ? aWinData.screenX : NaN,
-        "screenY" in aWinData ? aWinData.screenY : NaN,
-        aWinData.sizemode || "", aWinData.sidebar || ""]);
-    }, 0);
-  },
-
-  /**
-   * Restore a window's dimensions
-   * @param aWidth
-   *        Window width
-   * @param aHeight
-   *        Window height
-   * @param aLeft
-   *        Window left
-   * @param aTop
-   *        Window top
-   * @param aSizeMode
-   *        Window size mode (eg: maximized)
-   * @param aSidebar
-   *        Sidebar command
-   */
-  restoreDimensions: function sss_restoreDimensions(aWindow, aWidth, aHeight, aLeft, aTop, aSizeMode, aSidebar) {
-    var win = aWindow;
-    var _this = this;
-    function win_(aName) { return _this._getWindowDimension(win, aName); }
-    
-    // only modify those aspects which aren't correct yet
-    if (aWidth && aHeight && (aWidth != win_("width") || aHeight != win_("height"))) {
-      aWindow.resizeTo(aWidth, aHeight);
-    }
-    if (!isNaN(aLeft) && !isNaN(aTop) && (aLeft != win_("screenX") || aTop != win_("screenY"))) {
-      aWindow.moveTo(aLeft, aTop);
-    }
-    if (aSizeMode && win_("sizemode") != aSizeMode)
-    {
-      switch (aSizeMode)
-      {
-      case "maximized":
-        aWindow.maximize();
-        break;
-      case "minimized":
-        aWindow.minimize();
-        break;
-      case "normal":
-        aWindow.restore();
-        break;
-      }
-    }
-    var sidebar = aWindow.document.getElementById("sidebar-box");
-    if (sidebar.getAttribute("sidebarcommand") != aSidebar) {
-      aWindow.toggleSidebar(aSidebar);
-    }
-    // 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.content) {
-      this.windowToFocus.content.focus();
-    }
-  },
-
-  /**
-   * Restores cookies (accepting both Firefox 2.0 and current format)
-   * @param aCookies
-   *        Array of cookie objects
-   */
-  restoreCookies: function sss_restoreCookies(aCookies) {
-    if (aCookies.count && aCookies.domain1) {
-      // convert to the new cookie serialization format
-      var converted = [];
-      for (var i = 1; i <= aCookies.count; i++) {
-        // for simplicity we only accept the format we produced ourselves
-        var parsed = aCookies["value" + i].match(/^([^=;]+)=([^;]*);(?:domain=[^;]+;)?(?:path=([^;]*);)?(secure;)?(httponly;)?/);
-        if (parsed && /^https?:\/\/([^\/]+)/.test(aCookies["domain" + i]))
-          converted.push({
-            host: RegExp.$1, path: parsed[3], name: parsed[1], value: parsed[2],
-            secure: parsed[4], httponly: parsed[5]
-          });
-      }
-      aCookies = converted;
-    }
-    
-    var cookieManager = Cc["@mozilla.org/cookiemanager;1"].
-                        getService(Ci.nsICookieManager2);
-    // MAX_EXPIRY should be 2^63-1, but JavaScript can't handle that precision
-    var MAX_EXPIRY = Math.pow(2, 62);
-    for (i = 0; i < aCookies.length; i++) {
-      var cookie = aCookies[i];
-      try {
-        cookieManager.add(cookie.host, cookie.path || "", cookie.name || "", cookie.value, !!cookie.secure, !!cookie.httponly, true, "expiry" in cookie ? cookie.expiry : MAX_EXPIRY);
-      }
-      catch (ex) { Cu.reportError(ex); } // don't let a single cookie stop recovering
-    }
-  },
-
-/* ........ Disk Access .............. */
-
-  /**
-   * save state delayed by N ms
-   * marks window as dirty (i.e. data update can't be skipped)
-   * @param aWindow
-   *        Window reference
-   * @param aDelay
-   *        Milliseconds to delay
-   */
-  saveStateDelayed: function sss_saveStateDelayed(aWindow, aDelay) {
-    if (aWindow) {
-      this._dirtyWindows[aWindow.__SSi] = true;
-    }
-
-    if (!this._saveTimer && this._resume_from_crash &&
-        !this._inPrivateBrowsing) {
-      // interval until the next disk operation is allowed
-      var minimalDelay = this._lastSaveTime + this._interval - Date.now();
-      
-      // if we have to wait, set a timer, otherwise saveState directly
-      aDelay = Math.max(minimalDelay, aDelay || 2000);
-      if (aDelay > 0) {
-        this._saveTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-        this._saveTimer.init(this, aDelay, Ci.nsITimer.TYPE_ONE_SHOT);
-      }
-      else {
-        this.saveState();
-      }
-    }
-  },
-
-  /**
-   * save state to disk
-   * @param aUpdateAll
-   *        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;
-
-    // if we're in private browsing mode, do nothing
-    if (this._inPrivateBrowsing)
-      return;
-
-    var oState = this._getCurrentState(aUpdateAll);
-    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 = Cc["@mozilla.org/supports-string;1"].
-                        createInstance(Ci.nsISupportsString);
-    // parentheses are for backwards compatibility with Firefox 2.0 and 3.0
-    stateString.data = "(" + this._toJSONString(aStateObj) + ")";
-
-    this._observerService.notifyObservers(stateString,
-                                          "sessionstore-state-write", "");
-
-    // don't touch the file if an observer has deleted all state data
-    if (stateString.data)
-      this._writeFile(this._sessionFile, stateString.data);
-
-    this._lastSaveTime = Date.now();
-  },
-
-  /**
-   * delete session datafile and backup
-   */
-  _clearDisk: function sss_clearDisk() {
-    if (this._sessionFile.exists()) {
-      try {
-        this._sessionFile.remove(false);
-      }
-      catch (ex) { dump(ex + '\n'); } // couldn't remove the file - what now?
-    }
-    if (this._sessionFileBackup.exists()) {
-      try {
-        this._sessionFileBackup.remove(false);
-      }
-      catch (ex) { dump(ex + '\n'); } // couldn't remove the file - what now?
-    }
-  },
-
-/* ........ Auxiliary Functions .............. */
-
-  /**
-   * call a callback for all currently opened browser windows
-   * (might miss the most recent one)
-   * @param aFunc
-   *        Callback each window is passed to
-   */
-  _forEachBrowserWindow: function sss_forEachBrowserWindow(aFunc) {
-    var windowMediator = Cc["@mozilla.org/appshell/window-mediator;1"].
-                         getService(Ci.nsIWindowMediator);
-    var windowsEnum = windowMediator.getEnumerator("navigator:browser");
-    
-    while (windowsEnum.hasMoreElements()) {
-      var window = windowsEnum.getNext();
-      if (window.__SSi && !window.closed) {
-        aFunc.call(this, window);
-      }
-    }
-  },
-
-  /**
-   * Returns most recent window
-   * @returns Window reference
-   */
-  _getMostRecentBrowserWindow: function sss_getMostRecentBrowserWindow() {
-    var wm = Cc["@mozilla.org/appshell/window-mediator;1"].
-             getService(Ci.nsIWindowMediator);
-
-    var win = wm.getMostRecentWindow("navigator:browser");
-    if (!win)
-      return null;
-    if (!win.closed)
-      return win;
-
-//@line 2648 "/builds/moz2_slave/linux_build/build/browser/components/sessionstore/src/nsSessionStore.js"
-    win = null;
-    var windowsEnum = wm.getEnumerator("navigator:browser");
-    // this is oldest to newest, so this gets a bit ugly
-    while (windowsEnum.hasMoreElements()) {
-      let nextWin = windowsEnum.getNext();
-      if (!nextWin.closed)
-        win = nextWin;
-    }
-    return win;
-//@line 2666 "/builds/moz2_slave/linux_build/build/browser/components/sessionstore/src/nsSessionStore.js"
-  },
-
-  /**
-   * open a new browser window for a given session state
-   * called when restoring a multi-window session
-   * @param aState
-   *        Object containing session data
-   */
-  _openWindowWithState: function sss_openWindowWithState(aState) {
-    var argString = Cc["@mozilla.org/supports-string;1"].
-                    createInstance(Ci.nsISupportsString);
-    argString.data = "";
-
-    //XXXzeniko shouldn't it be possible to set the window's dimensions here (as feature)?
-    var window = Cc["@mozilla.org/embedcomp/window-watcher;1"].
-                 getService(Ci.nsIWindowWatcher).
-                 openWindow(null, this._prefBranch.getCharPref("chromeURL"), "_blank",
-                            "chrome,dialog=no,all", argString);
-    
-    do {
-      var ID = "window" + Math.random();
-    } while (ID in this._statesToRestore);
-    this._statesToRestore[(window.__SS_restoreID = ID)] = aState;
-    
-    return window;
-  },
-
-  /**
-   * Whether or not to resume session, if not recovering from a crash.
-   * @returns bool
-   */
-  _doResumeSession: function sss_doResumeSession() {
-    if (this._clearingOnShutdown)
-      return false;
-
-    return this._prefBranch.getIntPref("startup.page") == 3 ||
-           this._prefBranch.getBoolPref("sessionstore.resume_session_once");
-  },
-
-  /**
-   * whether the user wants to load any other page at startup
-   * (except the homepage) - needed for determining whether to overwrite the current tabs
-   * C.f.: nsBrowserContentHandler's defaultArgs implementation.
-   * @returns bool
-   */
-  _isCmdLineEmpty: function sss_isCmdLineEmpty(aWindow) {
-    var defaultArgs = Cc["@mozilla.org/browser/clh;1"].
-                      getService(Ci.nsIBrowserHandler).defaultArgs;
-    if (aWindow.arguments && aWindow.arguments[0] &&
-        aWindow.arguments[0] == defaultArgs)
-      aWindow.arguments[0] = null;
-
-    return !aWindow.arguments || !aWindow.arguments[0];
-  },
-
-  /**
-   * don't save sensitive data if the user doesn't want to
-   * (distinguishes between encrypted and non-encrypted sites)
-   * @param aIsHTTPS
-   *        Bool is encrypted
-   * @returns bool
-   */
-  _checkPrivacyLevel: function sss_checkPrivacyLevel(aIsHTTPS) {
-    return this._prefBranch.getIntPref("sessionstore.privacy_level") < (aIsHTTPS ? PRIVACY_ENCRYPTED : PRIVACY_FULL);
-  },
-
-  /**
-   * on popup windows, the XULWindow's attributes seem not to be set correctly
-   * we use thus JSDOMWindow attributes for sizemode and normal window attributes
-   * (and hope for reasonable values when maximized/minimized - since then
-   * outerWidth/outerHeight aren't the dimensions of the restored window)
-   * @param aWindow
-   *        Window reference
-   * @param aAttribute
-   *        String sizemode | width | height | other window attribute
-   * @returns string
-   */
-  _getWindowDimension: function sss_getWindowDimension(aWindow, aAttribute) {
-    if (aAttribute == "sizemode") {
-      switch (aWindow.windowState) {
-      case aWindow.STATE_FULLSCREEN:
-      case aWindow.STATE_MAXIMIZED:
-        return "maximized";
-      case aWindow.STATE_MINIMIZED:
-        return "minimized";
-      default:
-        return "normal";
-      }
-    }
-    
-    var dimension;
-    switch (aAttribute) {
-    case "width":
-      dimension = aWindow.outerWidth;
-      break;
-    case "height":
-      dimension = aWindow.outerHeight;
-      break;
-    default:
-      dimension = aAttribute in aWindow ? aWindow[aAttribute] : "";
-      break;
-    }
-    
-    if (aWindow.windowState == aWindow.STATE_NORMAL) {
-      return dimension;
-    }
-    return aWindow.document.documentElement.getAttribute(aAttribute) || dimension;
-  },
-
-  /**
-   * Get nsIURI from string
-   * @param string
-   * @returns nsIURI
-   */
-  _getURIFromString: function sss_getURIFromString(aString) {
-    var ioService = Cc["@mozilla.org/network/io-service;1"].
-                    getService(Ci.nsIIOService);
-    return ioService.newURI(aString, null, null);
-  },
-
-  /**
-   * Annotate a breakpad crash report with the currently selected tab's URL.
-   */
-  _updateCrashReportURL: function sss_updateCrashReportURL(aWindow) {
-    if (!Ci.nsICrashReporter) {
-      // if breakpad isn't built, don't bother next time at all
-      this._updateCrashReportURL = function(aWindow) {};
-      return;
-    }
-    try {
-      var currentURI = aWindow.getBrowser().currentURI.clone();
-      // if the current URI contains a username/password, remove it
-      try { 
-        currentURI.userPass = ""; 
-      } 
-      catch (ex) { } // ignore failures on about: URIs
-
-      var cr = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsICrashReporter);
-      cr.annotateCrashReport("URL", currentURI.spec);
-    }
-    catch (ex) {
-      // don't make noise when crashreporter is built but not enabled
-      if (ex.result != Components.results.NS_ERROR_NOT_INITIALIZED)
-        debug(ex);
-    }
-  },
-
-  /**
-   * @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 display the page when there's nothing to restore
-    let winData = aState.windows || null;
-    if (!winData || winData.length == 0)
-      return false;
-    
-    // don't wrap a single about:sessionrestore page
-    if (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 = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.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
-   */
-  _safeEval: function sss_safeEval(aStr) {
-    return Cu.evalInSandbox(aStr, new Cu.Sandbox("about:blank"));
-  },
-
-  /**
-   * Converts a JavaScript object into a JSON string
-   * (see http://www.json.org/ for more information).
-   *
-   * The inverse operation consists of JSON.parse(JSON_string).
-   *
-   * @param aJSObject is the object to be converted
-   * @returns the object's JSON representation
-   */
-  _toJSONString: function sss_toJSONString(aJSObject) {
-    // XXXzeniko drop the following keys used only for internal bookkeeping:
-    //           _tabStillLoading, _hosts, _formDataSaved
-    let jsonString = JSON.stringify(aJSObject);
-    
-    if (/[\u2028\u2029]/.test(jsonString)) {
-      // work-around for bug 485563 until we can use JSON.parse
-      // instead of evalInSandbox everywhere
-      jsonString = jsonString.replace(/[\u2028\u2029]/g,
-                                      function($0) "\\u" + $0.charCodeAt(0).toString(16));
-    }
-    
-    return jsonString;
-  },
-
-  _sendRestoreCompletedNotifications:
-  function sss_sendRestoreCompletedNotifications(aOnWindowClose) {
-    if (this._restoreCount && !aOnWindowClose)
-      this._restoreCount--;
-
-    if (this._restoreCount == 0 && this._closingWindows.length == 0) {
-      // This was the last window restored at startup, notify observers.
-      this._observerService.notifyObservers(null,
-        this._browserSetState ? NOTIFY_BROWSER_STATE_RESTORED : NOTIFY_WINDOWS_RESTORED,
-        "");
-      this._browserSetState = false;
-    }
-  },
-
-  /**
-   * @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;
-  },
-
-  /**
-   * Replace "Loading..." with the tab label (with minimal side-effects)
-   * @param aString is the string the title is stored in
-   * @param aTabbrowser is a tabbrowser object, containing aTab
-   * @param aTab is the tab whose title we're updating & using
-   *
-   * @returns aString that has been updated with the new title
-   */
-  _replaceLoadingTitle : function sss_replaceLoadingTitle(aString, aTabbrowser, aTab) {
-    if (aString == aTabbrowser.mStringBundle.getString("tabs.loading")) {
-      aTabbrowser.setTabTitle(aTab);
-      [aString, aTab.label] = [aTab.label, aString];
-    }
-    return aString;
-  },
-
-  /**
-   * Resize this._closedWindows to the value of the pref, except in the case
-   * where we don't have any non-popup windows on Windows and Linux. Then we must
-   * resize such that we have at least one non-popup window.
-   */
-  _capClosedWindows : function sss_capClosedWindows() {
-    let maxWindowsUndo = this._prefBranch.getIntPref("sessionstore.max_windows_undo");
-    if (this._closedWindows.length <= maxWindowsUndo)
-      return;
-    let spliceTo = maxWindowsUndo;
-//@line 2930 "/builds/moz2_slave/linux_build/build/browser/components/sessionstore/src/nsSessionStore.js"
-    let normalWindowIndex = 0;
-    // try to find a non-popup window in this._closedWindows
-    while (normalWindowIndex < this._closedWindows.length &&
-           !!this._closedWindows[normalWindowIndex].isPopup)
-      normalWindowIndex++;
-    if (normalWindowIndex >= maxWindowsUndo)
-      spliceTo = normalWindowIndex + 1;
-//@line 2938 "/builds/moz2_slave/linux_build/build/browser/components/sessionstore/src/nsSessionStore.js"
-    this._closedWindows.splice(spliceTo);
-  },
-
-/* ........ Storage API .............. */
-
-  /**
-   * write file to disk
-   * @param aFile
-   *        nsIFile
-   * @param aData
-   *        String data
-   */
-  _writeFile: function sss_writeFile(aFile, aData) {
-    // Initialize the file output stream.
-    var ostream = Cc["@mozilla.org/network/safe-file-output-stream;1"].
-                  createInstance(Ci.nsIFileOutputStream);
-    ostream.init(aFile, 0x02 | 0x08 | 0x20, 0600, 0);
-
-    // Obtain a converter to convert our data to a UTF-8 encoded input stream.
-    var converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
-                    createInstance(Ci.nsIScriptableUnicodeConverter);
-    converter.charset = "UTF-8";
-
-    // Asynchronously copy the data to the file.
-    var istream = converter.convertToInputStream(aData);
-    var self = this;
-    NetUtil.asyncCopy(istream, ostream, function(rc) {
-      if (Components.isSuccessCode(rc)) {
-        self._observerService.notifyObservers(null,
-                                              "sessionstore-state-write-complete",
-                                              "");
-      }
-    });
-  }
-};
-
-let XPathHelper = {
-  // these two hashes should be kept in sync
-  namespaceURIs:     { "xhtml": "http://www.w3.org/1999/xhtml" },
-  namespacePrefixes: { "http://www.w3.org/1999/xhtml": "xhtml" },
-
-  /**
-   * Generates an approximate XPath query to an (X)HTML node
-   */
-  generate: function sss_xph_generate(aNode) {
-    // have we reached the document node already?
-    if (!aNode.parentNode)
-      return "";
-    
-    let prefix = this.namespacePrefixes[aNode.namespaceURI] || null;
-    let tag = (prefix ? prefix + ":" : "") + this.escapeName(aNode.localName);
-    
-    // stop once we've found a tag with an ID
-    if (aNode.id)
-      return "//" + tag + "[@id=" + this.quoteArgument(aNode.id) + "]";
-    
-    // count the number of previous sibling nodes of the same tag
-    // (and possible also the same name)
-    let count = 0;
-    let nName = aNode.name || null;
-    for (let n = aNode; (n = n.previousSibling); )
-      if (n.localName == aNode.localName && n.namespaceURI == aNode.namespaceURI &&
-          (!nName || n.name == nName))
-        count++;
-    
-    // recurse until hitting either the document node or an ID'd node
-    return this.generate(aNode.parentNode) + "/" + tag +
-           (nName ? "[@name=" + this.quoteArgument(nName) + "]" : "") +
-           (count ? "[" + (count + 1) + "]" : "");
-  },
-
-  /**
-   * Resolves an XPath query generated by XPathHelper.generate
-   */
-  resolve: function sss_xph_resolve(aDocument, aQuery) {
-    let xptype = Ci.nsIDOMXPathResult.FIRST_ORDERED_NODE_TYPE;
-    return aDocument.evaluate(aQuery, aDocument, this.resolveNS, xptype, null).singleNodeValue;
-  },
-
-  /**
-   * Namespace resolver for the above XPath resolver
-   */
-  resolveNS: function sss_xph_resolveNS(aPrefix) {
-    return XPathHelper.namespaceURIs[aPrefix] || null;
-  },
-
-  /**
-   * @returns valid XPath for the given node (usually just the local name itself)
-   */
-  escapeName: function sss_xph_escapeName(aName) {
-    // we can't just use the node's local name, if it contains
-    // special characters (cf. bug 485482)
-    return /^\w+$/.test(aName) ? aName :
-           "*[local-name()=" + this.quoteArgument(aName) + "]";
-  },
-
-  /**
-   * @returns a properly quoted string to insert into an XPath query
-   */
-  quoteArgument: function sss_xph_quoteArgument(aArg) {
-    return !/'/.test(aArg) ? "'" + aArg + "'" :
-           !/"/.test(aArg) ? '"' + aArg + '"' :
-           "concat('" + aArg.replace(/'+/g, "',\"$&\",'") + "')";
-  },
-
-  /**
-   * @returns an XPath query to all savable form field nodes
-   */
-  get restorableFormNodes() {
-    // for a comprehensive list of all available <INPUT> types see
-    // http://mxr.mozilla.org/mozilla-central/search?string=kInputTypeTable
-    let ignoreTypes = ["password", "hidden", "button", "image", "submit", "reset"];
-    // XXXzeniko work-around until lower-case has been implemented (bug 398389)
-    let toLowerCase = '"ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz"';
-    let ignore = "not(translate(@type, " + toLowerCase + ")='" +
-      ignoreTypes.join("' or translate(@type, " + toLowerCase + ")='") + "')";
-    let formNodesXPath = "//textarea|//select|//xhtml:textarea|//xhtml:select|" +
-      "//input[" + ignore + "]|//xhtml:input[" + ignore + "]";
-    
-    delete this.restorableFormNodes;
-    return (this.restorableFormNodes = formNodesXPath);
-  }
-};
-
-// see nsPrivateBrowsingService.js
-String.prototype.hasRootDomain = function hasRootDomain(aDomain)
-{
-  let index = this.indexOf(aDomain);
-  if (index == -1)
-    return false;
-
-  if (this == aDomain)
-    return true;
-
-  let prevChar = this[index - 1];
-  return (index == (this.length - aDomain.length)) &&
-         (prevChar == "." || prevChar == "/");
-}
-
-const NoModule = {
-  getClassObject: function(aCompMgr, aCID, aIID) {
-    Components.returnCode = Cr.NS_ERROR_NOT_REGISTERED;
-    return null;
-  },
-  registerSelf: function(aCompMgr, aFileSpec, aLocation, aType) { return; },
-  unregisterSelf: function(aCompMgr, aLocation, aType) { return; },
-  canUnload: function(aCompMgr) { return true; }
-};
-
-
-function NSGetModule(aComMgr, aFileSpec) {
-  var prefs = Components.classes["@mozilla.org/preferences-service;1"]
-        .getService(Components.interfaces.nsIPrefBranch);
-  if(is_FF36) {
-      return XPCOMUtils.generateModule([SessionStoreService]);
-  } else {
-      return NoModule;
-  }
-}
diff --git a/src/components/tbSessionStore.js b/src/components/tbSessionStore.js
new file mode 100644
index 0000000..e8cafe5
--- /dev/null
+++ b/src/components/tbSessionStore.js
@@ -0,0 +1,187 @@
+/*************************************************************************
+ * Torbutton Session Store Control
+ *
+ * Uses the new Firefox 3.5+ session store APIs to prevent writing
+ * of tor-loaded tabs to disk.
+ *
+ *************************************************************************/
+
+// Module specific constants
+const kMODULE_NAME = "Torbutton Session Store Blocker";
+const kMODULE_CONTRACTID = "@torproject.org/torbutton-ss-blocker;1";
+const kMODULE_CID = Components.ID("aef08952-b003-4697-b935-a392367e214f");
+
+const Cr = Components.results;
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+function TBSessionBlocker() {
+    this.logger = Components.classes["@torproject.org/torbutton-logger;1"]
+        .getService(Components.interfaces.nsISupports).wrappedJSObject;
+    this.logger.log(3, "Torbutton Session Store Blocker initialized");
+
+    var obsSvc = Components.classes["@mozilla.org/observer-service;1"]
+        .getService(Ci.nsIObserverService);
+    obsSvc.addObserver(this, "sessionstore-state-write", false);
+    this.prefs = Components.classes["@mozilla.org/preferences-service;1"]
+        .getService(Components.interfaces.nsIPrefBranch);
+    this.wrappedJSObject = this;
+}
+
+TBSessionBlocker.prototype =
+{
+  QueryInterface: function(iid) {
+    if (!iid.equals(Ci.nsIClassInfo) &&
+        !iid.equals(Ci.nsIObserver) &&
+        !iid.equals(Ci.nsISupports)) {
+      Components.returnCode = Cr.NS_ERROR_NO_INTERFACE;
+      return null;
+    }
+    return this;
+  },
+
+  wrappedJSObject: null,  // Initialized by constructor
+
+  // make this an nsIClassInfo object
+  flags: Ci.nsIClassInfo.DOM_OBJECT,
+
+  // method of nsIClassInfo
+  classDescription: kMODULE_NAME,
+  classID: kMODULE_CID,
+  contractID: kMODULE_CONTRACTID,
+
+  // Hack to get us registered early enough to observe the session store.
+  _xpcom_categories: [{category:"profile-after-change"}],
+
+  // method of nsIClassInfo
+  getInterfaces: function(count) {
+    var interfaceList = [Ci.nsIClassInfo];
+    count.value = interfaceList.length;
+    return interfaceList;
+  },
+
+  // method of nsIClassInfo
+  getHelperForLanguage: function(count) { return null; },
+
+  _walkObj: function(soFar, obj) {
+    for (let m in obj) {
+      this.logger.log(3, soFar+"."+m);
+      if (obj[m] != obj)
+        this._walkObj(soFar+"."+m, obj[m]);
+    }
+  },
+
+  // observer interface implementation
+  // topic:   what event occurred
+  // subject: what nsIPrefBranch we're observing
+  // data:    which pref has been changed (relative to subject)
+  observe: function(subject, topic, data)
+  {
+      if (topic != "sessionstore-state-write") return;
+      this.logger.log(3, "Got Session Store observe: "+topic);
+      subject = subject.QueryInterface(Ci.nsISupportsString);
+
+      var state = this._safeJSONparse(subject);
+      if (!"windows" in state) {
+        this.logger.log(4, "Got a session store write but with no windows?");
+        return;
+      }
+      var bypass_tor = this.prefs.getBoolPref("extensions.torbutton.notor_sessionstore");
+      var bypass_nontor = this.prefs.getBoolPref("extensions.torbutton.nonontor_sessionstore");
+      // XXX: It appears that if we filter out everything, firefox quits after restoring the
+      // blank store
+      for (let w in state.windows) {
+         // FIXME: Can we just kill state.windows.0._hosts and cookies?
+         // There seems to be no reason to store either of these in the session store..
+         state.windows[w]._hosts = {};
+         if (state.windows[w].cookies)
+           delete state.windows[w].cookies;
+
+         if (!"tabs" in state.windows[w]) {
+           this.logger.log(4, "Got a session store write for a window with no tabs?");
+           continue;
+         }
+         // Prune state.windows.0.tabs.0.extData.__tb_tor_fetched.0
+         for (let t = state.windows[w].tabs.length - 1; t >= 0; t--) {
+           if ("extData" in state.windows[w].tabs[t]
+               && "__tb_tor_fetched" in state.windows[w].tabs[t].extData) {
+             if((bypass_tor
+                 && state.windows[w].tabs[t].extData.__tb_tor_fetched == "true") ||
+                (bypass_nontor
+                 && state.windows[w].tabs[t].extData.__tb_tor_fetched == "false")) {
+               this.logger.log(3,
+                    "Blocking session store save of tab from opposite tor state:"
+                     +bypass_tor+", "+state.windows[w].tabs[t].extData.__tb_tor_fetched+", "
+                     +bypass_nontor+", "+state.windows[w].tabs[t].extData.__tb_tor_fetched);
+               state.windows[w].tabs.splice(t,1);
+             }
+           } else {
+             this.logger.log(4, "Got a tab with no extension data?");
+           }
+         }
+         if (!"_closedTabs" in state.windows[w]) {
+           this.logger.log(3, "Window has no closed tabs");
+           continue;
+         }
+         // Prune state.windows.0._closedTabs.0.state.extData.__tb_tor_fetched.1
+         for (let t = state.windows[w]._closedTabs.length - 1; t >= 0; t--) {
+           if ("extData" in state.windows[w]._closedTabs[t].state
+                   && "__tb_tor_fetched" in state.windows[w]._closedTabs[t].state.extData) {
+             if((bypass_tor
+                 && state.windows[w]._closedTabs[t].state.extData.__tb_tor_fetched == "true") ||
+                (bypass_nontor
+                 && state.windows[w]._closedTabs[t].state.extData.__tb_tor_fetched == "false")) {
+               this.logger.log(3, "Blocking session store save of closed tab from opposite tor state.");
+               state.windows[w]._closedTabs.splice(t,1);
+             }
+           } else {
+             this.logger.log(4, "Got a tab with no extension data?");
+           }
+         }
+      }
+      subject.data = "("+this._toJSONString(state)+")";
+      this.logger.log(3, "Filtered Session Store JSON: "+subject);
+
+      // XXX: This is all debugging and should be removed
+      var state = this._safeJSONparse(subject);
+      this.logger.log(3, "Parsed Session Store: "+state);
+      this._walkObj("state", state);
+  },
+
+  _safeJSONparse: function(aStr) {
+    return Cu.evalInSandbox(aStr, new Cu.Sandbox("about:blank"));
+  },
+
+  /**
+   * Converts a JavaScript object into a JSON string
+   * (see http://www.json.org/ for more information).
+   *
+   * This method is hacked due to bug 485563...
+   */
+  _toJSONString: function(aJSObject) {
+    // XXXzeniko drop the following keys used only for internal bookkeeping:
+    //           _tabStillLoading, _hosts, _formDataSaved
+    let jsonString = JSON.stringify(aJSObject);
+
+    if (/[\u2028\u2029]/.test(jsonString)) {
+      // work-around for bug 485563 until we can use JSON.parse
+      // instead of evalInSandbox everywhere
+      jsonString = jsonString.replace(/[\u2028\u2029]/g,
+                                      function($0) "\\u" + $0.charCodeAt(0).toString(16));
+    }
+
+    return jsonString;
+  }
+};
+
+/**
+* XPCOMUtils.generateNSGetFactory was introduced in Mozilla 2 (Firefox 4).
+* XPCOMUtils.generateNSGetModule is for Mozilla 1.9.2 (Firefox 3.6).
+*/
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+// XXX: This won't work for FF3... We need to not register ourselves here..
+if (XPCOMUtils.generateNSGetFactory)
+  var NSGetFactory = XPCOMUtils.generateNSGetFactory([TBSessionBlocker]);
+else
+  var NSGetModule = XPCOMUtils.generateNSGetModule([TBSessionBlocker]);
-- 
1.7.1




More information about the tor-commits mailing list