merge m-c to fx-team; a=desktop-only
authorTim Taubert <tim.taubert@gmx.de>
Thu, 19 Apr 2012 10:48:59 +0200
changeset 95228 a9b543de6b67b00bb67d41981451bd5e9f04b4b6
parent 95223 da53be6847941ee5a7017e8eaa79eea029547a0f (current diff)
parent 95227 c2ee16305bc6289def44a42abcc9b136a7493ed3 (diff)
child 95267 ee38392142edd0298275dba2273ffc2713c92bdb
child 95285 fe5bf2fca92ebb79204b1d218262ed67735149d1
push id886
push userlsblakk@mozilla.com
push dateMon, 04 Jun 2012 19:57:52 +0000
treeherdermozilla-beta@bbd8d5efd6d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdesktop-only
milestone14.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge m-c to fx-team; a=desktop-only
browser/components/tabview/storagePolicy.js
browser/components/tabview/test/browser_tabview_bug597248.js
browser/components/tabview/test/browser_tabview_bug627288.js
browser/components/tabview/test/browser_tabview_bug677310.js
browser/components/tabview/test/browser_tabview_thumbnail_storage.js
browser/components/tabview/thumbnailStorage.js
--- a/browser/components/tabview/content.js
+++ b/browser/components/tabview/content.js
@@ -1,43 +1,11 @@
-/* ***** 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 content.js.
- *
- * The Initial Developer of the Original Code is the Mozilla Foundation.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Tim Taubert <ttaubert@mozilla.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 ***** */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 let Cu = Components.utils;
 
 Cu.import("resource:///modules/tabview/utils.jsm");
 
 // Bug 671101 - directly using webProgress in this context
@@ -48,54 +16,54 @@ Cu.import("resource:///modules/tabview/u
 });
 
 // ----------
 // WindowEventHandler
 //
 // Handles events dispatched by the content window.
 let WindowEventHandler = {
   // ----------
-  // Function: onDOMContentLoaded
-  // Sends an asynchronous message when the "onDOMContentLoaded" event for the
-  // current page is fired.
-  onDOMContentLoaded: function WEH_onDOMContentLoaded(event) {
-    sendAsyncMessage("Panorama:DOMContentLoaded");
-  },
-
-  // ----------
   // Function: onDOMWillOpenModalDialog
   // Sends a synchronous message when the "onDOMWillOpenModalDialog" event
   // is fired right before a modal dialog will be opened by the current page.
   onDOMWillOpenModalDialog: function WEH_onDOMWillOpenModalDialog(event) {
     // (event.isTrusted == true) when the event is generated by a user action
     // and does not originate from a script.
     if (!event.isTrusted)
       return;
 
     // we're intentionally sending a synchronous message to handle this event
     // as quick as possible, switch the selected tab and hide the tabview
     // before the modal dialog is shown
     sendSyncMessage("Panorama:DOMWillOpenModalDialog");
+  },
+
+  // ----------
+  // Function: onMozAfterPaint
+  // Sends an asynchronous message when the "onMozAfterPaint" event
+  // is fired.
+  onMozAfterPaint: function WEH_onMozAfterPaint(event) {
+    sendAsyncMessage("Panorama:MozAfterPaint");
   }
 };
 
 // add event listeners
-addEventListener("DOMContentLoaded", WindowEventHandler.onDOMContentLoaded, false);
 addEventListener("DOMWillOpenModalDialog", WindowEventHandler.onDOMWillOpenModalDialog, false);
+addEventListener("MozAfterPaint", WindowEventHandler.onMozAfterPaint, false);
 
 // ----------
 // WindowMessageHandler
 //
 // Handles messages sent by the chrome process.
 let WindowMessageHandler = {
   // ----------
   // Function: isDocumentLoaded
   // Checks if the currently active document is loaded.
   isDocumentLoaded: function WMH_isDocumentLoaded(cx) {
-    let isLoaded = (content.document.readyState == "complete" &&
+    let isLoaded = (content.document.readyState != "uninitialized" &&
                     !webProgress.isLoadingDocument);
 
     sendAsyncMessage(cx.name, {isLoaded: isLoaded});
   },
 
   // ----------
   // Function: isImageDocument
   // Checks if the currently active document is an image document or not.
@@ -105,95 +73,8 @@ let WindowMessageHandler = {
     sendAsyncMessage(cx.name, {isImageDocument: isImageDocument});
   }
 };
 
 // add message listeners
 addMessageListener("Panorama:isDocumentLoaded", WindowMessageHandler.isDocumentLoaded);
 addMessageListener("Panorama:isImageDocument", WindowMessageHandler.isImageDocument);
 
-// ----------
-// WebProgressListener
-//
-// Observe the web progress of content pages loaded into this browser. When the
-// state of a page changes we check if we're still allowed to store page
-// information permanently.
-let WebProgressListener = {
-  // ----------
-  // Function: onStateChange
-  // Called by the webProgress when its state changes.
-  onStateChange: function WPL_onStateChange(webProgress, request, flag, status) {
-    // The browser just started loading (again). Explicitly grant storage
-    // because the browser might have been blocked before (e.g. when navigating
-    // from a https-page to a http-page).
-    if (flag & Ci.nsIWebProgressListener.STATE_START) {
-      // ensure the dom window is the top one
-      if (this._isTopWindow(webProgress))
-        sendAsyncMessage("Panorama:StoragePolicy:granted");
-    }
-
-    // The browser finished loading - check the cache control headers. Send
-    // a message if we're not allowed to store information about this page.
-    if (flag & Ci.nsIWebProgressListener.STATE_STOP) {
-      // ensure the dom window is the top one
-      if (this._isTopWindow(webProgress) &&
-          request && request instanceof Ci.nsIHttpChannel) {
-        request.QueryInterface(Ci.nsIHttpChannel);
-
-        let exclude = false;
-        let reason = "";
-
-        // Check if the "Cache-Control" header is "no-store". In this case we're
-        // not allowed to store information about the current page.
-        if (this._isNoStoreResponse(request)) {
-          exclude = true;
-          reason = "no-store";
-        }
-        // Otherwise we'll deny storage if we're currently viewing a https
-        // page without a "Cache-Control: public" header.
-        else if (request.URI.schemeIs("https")) {
-          let cacheControlHeader = this._getCacheControlHeader(request);
-          if (cacheControlHeader && !(/public/i).test(cacheControlHeader)) {
-            exclude = true;
-            reason = "https";
-          }
-        }
-
-        if (exclude)
-          sendAsyncMessage("Panorama:StoragePolicy:denied", {reason: reason});
-      }
-    }
-  },
-
-  // ----------
-  // Function: _isTopWindow
-  // Returns whether the DOMWindow associated with the webProgress is the
-  // top content window (and not an iframe or similar).
-  _isTopWindow: function WPL__isTopWindow(webProgress) {
-    // can throw if there's no associated DOMWindow
-    return !!Utils.attempt(function () webProgress.DOMWindow == content);
-  },
-
-  // ----------
-  // Function: _isNoStoreResponse
-  // Checks if the "Cache-Control" header is "no-store".
-  _isNoStoreResponse: function WPL__isNoStoreResponse(req) {
-    // can throw if called before the response has been received
-    return !!Utils.attempt(function () req.isNoStoreResponse());
-  },
-
-  // ----------
-  // Function: _getCacheControlHeader
-  // Returns the value of the "Cache-Control" header.
-  _getCacheControlHeader: function WPL__getCacheControlHeader(req) {
-    // can throw when the "Cache-Control" header doesn't exist
-    return Utils.attempt(function () req.getResponseHeader("Cache-Control"));
-  },
-
-  // ----------
-  // Implements progress listener interface.
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
-                                         Ci.nsISupportsWeakReference,
-                                         Ci.nsISupports])
-};
-
-// add web progress listener
-webProgress.addProgressListener(WebProgressListener, Ci.nsIWebProgress.NOTIFY_STATE_WINDOW);
deleted file mode 100644
--- a/browser/components/tabview/storagePolicy.js
+++ /dev/null
@@ -1,208 +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 storagePolicy.js.
- *
- * The Initial Developer of the Original Code is
- * the Mozilla Foundation.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- * Tim Taubert <ttaubert@mozilla.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 ***** */
-
-// **********
-// Title: storagePolicy.js
-
-// ##########
-// Class: StoragePolicy
-// Singleton for implementing a storage policy for sensitive data.
-let StoragePolicy = {
-  // Pref that controls whether we can store SSL content on disk
-  PREF_DISK_CACHE_SSL: "browser.cache.disk_cache_ssl",
-
-  // Used to keep track of disk_cache_ssl preference
-  _enablePersistentHttpsCaching: null,
-
-  // Used to keep track of browsers whose data we shouldn't store permanently
-  _deniedBrowsers: [],
-
-  // ----------
-  // Function: toString
-  // Prints [StoragePolicy] for debug use.
-  toString: function StoragePolicy_toString() {
-    return "[StoragePolicy]";
-  },
-
-  // ----------
-  // Function: init
-  // Initializes the StoragePolicy object.
-  init: function StoragePolicy_init() {
-    // store the preference value
-    this._enablePersistentHttpsCaching =
-      Services.prefs.getBoolPref(this.PREF_DISK_CACHE_SSL);
-
-    Services.prefs.addObserver(this.PREF_DISK_CACHE_SSL, this, false);
-
-    // tabs are already loaded before UI is initialized so cache-control
-    // values are unknown. We add browsers with https to the list for now.
-    if (!this._enablePersistentHttpsCaching)
-      Array.forEach(gBrowser.browsers, this._initializeBrowser.bind(this));
-
-    // make sure to remove tab browsers when tabs get closed
-    this._onTabClose = this._onTabClose.bind(this);
-    gBrowser.tabContainer.addEventListener("TabClose", this._onTabClose, false);
-
-    let mm = gWindow.messageManager;
-
-    // add message listeners for storage granted
-    this._onGranted = this._onGranted.bind(this);
-    mm.addMessageListener("Panorama:StoragePolicy:granted", this._onGranted);
-
-    // add message listeners for storage denied
-    this._onDenied = this._onDenied.bind(this);
-    mm.addMessageListener("Panorama:StoragePolicy:denied", this._onDenied);
-  },
-
-  // ----------
-  // Function: _initializeBrowser
-  // Initializes the given browser and checks if we need to add it to our
-  // internal exclusion list.
-  _initializeBrowser: function StoragePolicy__initializeBrowser(browser) {
-    let self = this;
-
-    function checkExclusion() {
-      if (browser.currentURI.schemeIs("https"))
-        self._deniedBrowsers.push(browser);
-    }
-
-    function waitForDocumentLoad() {
-      let mm = browser.messageManager;
-
-      mm.addMessageListener("Panorama:DOMContentLoaded", function onLoad(cx) {
-        mm.removeMessageListener(cx.name, onLoad);
-        checkExclusion(browser);
-      });
-    }
-
-    this._isDocumentLoaded(browser, function (isLoaded) {
-      if (isLoaded)
-        checkExclusion();
-      else
-        waitForDocumentLoad();
-    });
-  },
-
-  // ----------
-  // Function: _isDocumentLoaded
-  // Check if the given browser's document is loaded.
-  _isDocumentLoaded: function StoragePolicy__isDocumentLoaded(browser, callback) {
-    let mm = browser.messageManager;
-    let message = "Panorama:isDocumentLoaded";
-
-    mm.addMessageListener(message, function onMessage(cx) {
-      mm.removeMessageListener(cx.name, onMessage);
-      callback(cx.json.isLoaded);
-    });
-
-    mm.sendAsyncMessage(message);
-  },
-
-  // ----------
-  // Function: uninit
-  // Is called by UI.init() when the browser windows is closed.
-  uninit: function StoragePolicy_uninit() {
-    Services.prefs.removeObserver(this.PREF_DISK_CACHE_SSL, this);
-    gBrowser.removeTabsProgressListener(this);
-    gBrowser.tabContainer.removeEventListener("TabClose", this._onTabClose, false);
-
-    let mm = gWindow.messageManager;
-
-    // remove message listeners
-    mm.removeMessageListener("Panorama:StoragePolicy:granted", this._onGranted);
-    mm.removeMessageListener("Panorama:StoragePolicy:denied", this._onDenied);
-  },
-
-  // ----------
-  // Function: _onGranted
-  // Handle the 'granted' message and remove the given browser from the list
-  // of denied browsers.
-  _onGranted: function StoragePolicy__onGranted(cx) {
-    let index = this._deniedBrowsers.indexOf(cx.target);
-
-    if (index > -1)
-      this._deniedBrowsers.splice(index, 1);
-  },
-
-  // ----------
-  // Function: _onDenied
-  // Handle the 'denied' message and add the given browser to the list of denied
-  // browsers.
-  _onDenied: function StoragePolicy__onDenied(cx) {
-    // exclusion is optional because cache-control is not no-store or public and
-    // the protocol is https. don't exclude when persistent https caching is
-    // enabled.
-    if ("https" == cx.json.reason && this._enablePersistentHttpsCaching)
-      return;
-
-    let browser = cx.target;
-
-    if (this._deniedBrowsers.indexOf(browser) == -1)
-      this._deniedBrowsers.push(browser);
-  },
-
-  // ----------
-  // Function: _onTabClose
-  // Remove the browser from our internal exclusion list when a tab gets closed.
-  _onTabClose: function StoragePolicy__onTabClose(event) {
-    let browser = event.target.linkedBrowser;
-    let index = this._deniedBrowsers.indexOf(browser);
-
-    if (index > -1)
-      this._deniedBrowsers.splice(index, 1);
-  },
-
-  // ----------
-  // Function: canStoreThumbnailForTab
-  // Returns whether we're allowed to store the thumbnail of the given tab.
-  canStoreThumbnailForTab: function StoragePolicy_canStoreThumbnailForTab(tab) {
-    // deny saving thumbnails in private browsing mode
-    if (gPrivateBrowsing.privateBrowsingEnabled &&
-        UI._privateBrowsing.transitionMode != "enter")
-      return false;
-
-    return (this._deniedBrowsers.indexOf(tab.linkedBrowser) == -1);
-  },
-
-  // ----------
-  // Function: observe
-  // Observe pref changes.
-  observe: function StoragePolicy_observe(subject, topic, data) {
-    this._enablePersistentHttpsCaching =
-      Services.prefs.getBoolPref(this.PREF_DISK_CACHE_SSL);
-  }
-};
--- a/browser/components/tabview/tabitems.js
+++ b/browser/components/tabview/tabitems.js
@@ -63,36 +63,28 @@ function TabItem(tab, options) {
   // ___ set up div
   document.body.appendChild(TabItems.fragment().cloneNode(true));
   
   // The document fragment contains just one Node
   // As per DOM3 appendChild: it will then be the last child
   let div = document.body.lastChild;
   let $div = iQ(div);
 
-  this._cachedImageData = null;
-  this._thumbnailNeedsSaving = false;
+  this._showsCachedData = false;
   this.canvasSizeForced = false;
   this.$thumb = iQ('.thumb', $div);
   this.$fav   = iQ('.favicon', $div);
   this.$tabTitle = iQ('.tab-title', $div);
   this.$canvas = iQ('.thumb canvas', $div);
   this.$cachedThumb = iQ('img.cached-thumb', $div);
   this.$favImage = iQ('.favicon>img', $div);
   this.$close = iQ('.close', $div);
 
   this.tabCanvas = new TabCanvas(this.tab, this.$canvas[0]);
 
-  let self = this;
-
-  // when we paint onto the canvas make sure our thumbnail gets saved
-  this.tabCanvas.addSubscriber("painted", function () {
-    self._thumbnailNeedsSaving = true;
-  });
-
   this.defaultSize = new Point(TabItems.tabWidth, TabItems.tabHeight);
   this._hidden = false;
   this.isATabItem = true;
   this.keepProportional = true;
   this._hasBeenDrawn = false;
   this._reconnected = false;
   this.isDragging = false;
   this.isStacked = false;
@@ -118,16 +110,18 @@ function TabItem(tab, options) {
   // override dropOptions with custom tabitem methods
   this.dropOptions.drop = function(e) {
     let groupItem = drag.info.item.parent;
     groupItem.add(drag.info.$el);
   };
 
   this.draggable();
 
+  let self = this;
+
   // ___ more div setup
   $div.mousedown(function(e) {
     if (!Utils.isRightClick(e))
       self.lastMouseDownTarget = e.target;
   });
 
   $div.mouseup(function(e) {
     var same = (e.target == self.lastMouseDownTarget);
@@ -185,45 +179,42 @@ TabItem.prototype = Utils.extend(new Ite
     this.canvasSizeForced = false;
   },
 
   // ----------
   // Function: isShowingCachedData
   // Returns a boolean indicates whether the cached data is being displayed or
   // not. 
   isShowingCachedData: function TabItem_isShowingCachedData() {
-    return (this._cachedImageData != null);
+    return this._showsCachedData;
   },
 
   // ----------
   // Function: showCachedData
   // Shows the cached data i.e. image and title.  Note: this method should only
   // be called at browser startup with the cached data avaliable.
-  //
-  // Parameters:
-  //   imageData - the image data
-  showCachedData: function TabItem_showCachedData(imageData) {
-    this._cachedImageData = imageData;
-    this.$cachedThumb.attr("src", this._cachedImageData).show();
+  showCachedData: function TabItem_showCachedData() {
+    let {title, url} = this.getTabState();
+    let thumbnailURL = gPageThumbnails.getThumbnailURL(url);
+
+    this.$cachedThumb.attr("src", thumbnailURL).show();
     this.$canvas.css({opacity: 0});
 
-    let {title, url} = this.getTabState();
-    this.$tabTitle.text(title).attr("title", title ? title + "\n" + url : url);
-
-    this._sendToSubscribers("showingCachedData");
+    let tooltip = (title && title != url ? title + "\n" + url : url);
+    this.$tabTitle.text(title).attr("title", tooltip);
+    this._showsCachedData = true;
   },
 
   // ----------
   // Function: hideCachedData
   // Hides the cached data i.e. image and title and show the canvas.
   hideCachedData: function TabItem_hideCachedData() {
-    this.$cachedThumb.hide();
+    this.$cachedThumb.attr("src", "").hide();
     this.$canvas.css({opacity: 1.0});
-    if (this._cachedImageData)
-      this._cachedImageData = null;
+    this._showsCachedData = false;
   },
 
   // ----------
   // Function: getStorageData
   // Get data to be used for persistent storage of this object.
   getStorageData: function TabItem_getStorageData() {
     let data = {
       groupID: (this.parent ? this.parent.id : 0)
@@ -282,105 +273,16 @@ TabItem.prototype = Utils.extend(new Ite
     } else {
       url = this.tab.linkedBrowser.currentURI.spec;
     }
 
     return {title: title, url: url};
   },
 
   // ----------
-  // Function: loadThumbnail
-  // Loads the tabItems thumbnail.
-  loadThumbnail: function TabItem_loadThumbnail() {
-    let self = this;
-
-    function TabItem_loadThumbnail_callback(error, imageData) {
-      // we could have been unlinked while waiting for the thumbnail to load
-      if (!self.tab)
-        return;
-
-      if (error || !imageData) {
-        // paint the canvas to avoid leaving traces when dragging tab over it
-        self.tabCanvas.paint();
-        return;
-      }
-
-      self._sendToSubscribers("loadedCachedImageData");
-
-      // If we have a cached image, then show it if the loaded URL matches
-      // what the cache is from, OR the loaded URL is blank, which means
-      // that the page hasn't loaded yet.
-      let currentUrl = self.tab.linkedBrowser.currentURI.spec;
-      if (self.getTabState().url == currentUrl || currentUrl == "about:blank")
-        self.showCachedData(imageData);
-    }
-
-    ThumbnailStorage.loadThumbnail(this.getTabState().url, TabItem_loadThumbnail_callback);
-  },
-
-  // ----------
-  // Function: saveThumbnail
-  // Saves the tabItems thumbnail.
-  saveThumbnail: function TabItem_saveThumbnail(options) {
-    if (!this.tabCanvas)
-      return;
-
-    // nothing to do if the thumbnail hasn't changed
-    if (!this._thumbnailNeedsSaving)
-      return;
-
-    // check the storage policy to see if we're allowed to store the thumbnail
-    if (!StoragePolicy.canStoreThumbnailForTab(this.tab)) {
-      this._sendToSubscribers("deniedToSaveImageData");
-      return;
-    }
-
-    let url = this.tab.linkedBrowser.currentURI.spec;
-    let delayed = this._saveThumbnailDelayed;
-    let synchronously = (options && options.synchronously);
-
-    // is there a delayed save waiting?
-    if (delayed) {
-      // check if url has changed since last call to saveThumbnail
-      if (!synchronously && url == delayed.url)
-        return;
-
-      // url has changed in the meantime, clear the timeout
-      clearTimeout(delayed.timeout);
-    }
-
-    let self = this;
-
-    function callback(error) {
-      if (!error) {
-        self._thumbnailNeedsSaving = false;
-        self._sendToSubscribers("savedCachedImageData");
-      }
-    }
-
-    function doSaveThumbnail() {
-      self._saveThumbnailDelayed = null;
-
-      // we could have been unlinked in the meantime
-      if (!self.tabCanvas)
-        return;
-
-      let imageData = self.tabCanvas.toImageData();
-      ThumbnailStorage.saveThumbnail(url, imageData, callback, options);
-    }
-
-    if (synchronously) {
-      doSaveThumbnail();
-    } else {
-      let timeout = setTimeout(doSaveThumbnail, 2000);
-      this._saveThumbnailDelayed = {url: url, timeout: timeout};
-    }
-  },
-
-  // ----------
   // Function: _reconnect
   // Load the reciever's persistent data from storage. If there is none, 
   // treats it as a new tab. 
   //
   // Parameters:
   //   options - an object with additional parameters, see below
   //
   // Possible options:
@@ -389,17 +291,19 @@ TabItem.prototype = Utils.extend(new Ite
   _reconnect: function TabItem__reconnect(options) {
     Utils.assertThrow(!this._reconnected, "shouldn't already be reconnected");
     Utils.assertThrow(this.tab, "should have a xul:tab");
 
     let tabData = Storage.getTabData(this.tab);
     let groupItem;
 
     if (tabData && TabItems.storageSanity(tabData)) {
-      this.loadThumbnail();
+      // Show the cached data while we're waiting for the tabItem to be updated.
+      // If the tab isn't restored yet this acts as a placeholder until it is.
+      this.showCachedData();
 
       if (this.parent)
         this.parent.remove(this, {immediately: true});
 
       if (tabData.groupID)
         groupItem = GroupItems.groupItem(tabData.groupID);
       else
         groupItem = new GroupItem([], {immediately: true, bounds: tabData.bounds});
@@ -796,16 +700,42 @@ TabItem.prototype = Utils.extend(new Ite
 
     let xOrigin = (left - zoomLeft) / ((left - zoomLeft) + (zoomLeft + zoomWidth - right)) * 100;
     let yOrigin = (top - zoomTop) / ((top - zoomTop) + (zoomTop + zoomHeight - bottom)) * 100;
 
     return {
       transformOrigin: xOrigin + "% " + yOrigin + "%",
       transform: "scale(" + zoomScaleFactor + ")"
     };
+  },
+
+  // ----------
+  // Function: updateCanvas
+  // Updates the tabitem's canvas.
+  updateCanvas: function TabItem_updateCanvas() {
+    // ___ thumbnail
+    let $canvas = this.$canvas;
+    if (!this.canvasSizeForced) {
+      let w = $canvas.width();
+      let h = $canvas.height();
+      if (w != $canvas[0].width || h != $canvas[0].height) {
+        $canvas[0].width = w;
+        $canvas[0].height = h;
+      }
+    }
+
+    TabItems._lastUpdateTime = Date.now();
+    this._lastTabUpdateTime = TabItems._lastUpdateTime;
+
+    if (this.tabCanvas)
+      this.tabCanvas.paint();
+
+    // ___ cache
+    if (this.isShowingCachedData())
+      this.hideCachedData();
   }
 });
 
 // ##########
 // Class: TabItems
 // Singleton for managing <TabItem>s
 let TabItems = {
   minTabWidth: 40,
@@ -823,16 +753,17 @@ let TabItems = {
   _heartbeatTiming: 200, // milliseconds between calls
   _maxTimeForUpdating: 200, // milliseconds that consecutive updates can take
   _lastUpdateTime: Date.now(),
   _eventListeners: [],
   _pauseUpdateForTest: false,
   tempCanvas: null,
   _reconnectingPaused: false,
   tabItemPadding: {},
+  _mozAfterPaintHandler: null,
 
   // ----------
   // Function: toString
   // Prints [TabItems count=count] for debug use
   toString: function TabItems_toString() {
     return "[TabItems count=" + this.items.length + "]";
   },
 
@@ -854,16 +785,20 @@ let TabItems = {
     $canvas.appendTo(iQ("body"));
     $canvas.hide();
     this.tempCanvas = $canvas[0];
     // 150 pixels is an empirical size, below which FF's drawWindow()
     // algorithm breaks down
     this.tempCanvas.width = 150;
     this.tempCanvas.height = 112;
 
+    let mm = gWindow.messageManager;
+    this._mozAfterPaintHandler = this.onMozAfterPaint.bind(this);
+    mm.addMessageListener("Panorama:MozAfterPaint", this._mozAfterPaintHandler);
+
     // When a tab is opened, create the TabItem
     this._eventListeners.open = function (event) {
       let tab = event.target;
 
       if (!tab.pinned)
         self.link(tab);
     }
     // When a tab's content is loaded, show the canvas and hide the cached data
@@ -903,16 +838,19 @@ let TabItems = {
       self.link(tab, options);
       self.update(tab);
     });
   },
 
   // ----------
   // Function: uninit
   uninit: function TabItems_uninit() {
+    let mm = gWindow.messageManager;
+    mm.removeMessageListener("Panorama:MozAfterPaint", this._mozAfterPaintHandler);
+
     for (let name in this._eventListeners) {
       AllTabs.unregister(name, this._eventListeners[name]);
     }
     this.items.forEach(function(tabItem) {
       for (let x in tabItem) {
         if (typeof tabItem[x] == "object")
           tabItem[x] = null;
       }
@@ -941,30 +879,43 @@ let TabItems = {
             "<span class='tab-title'>&nbsp;</span>" +
             "<div class='close'></div>";
     this._fragment = document.createDocumentFragment();
     this._fragment.appendChild(div);
 
     return this._fragment;
   },
 
+  // Function: _isComplete
+  // Checks whether the xul:tab has fully loaded and calls a callback with a 
+  // boolean indicates whether the tab is loaded or not.
+  _isComplete: function TabItems__isComplete(tab, callback) {
+    Utils.assertThrow(tab, "tab");
+
+    let mm = tab.linkedBrowser.messageManager;
+    let message = "Panorama:isDocumentLoaded";
+
+    mm.addMessageListener(message, function onMessage(cx) {
+      mm.removeMessageListener(cx.name, onMessage);
+      callback(cx.json.isLoaded);
+    });
+    mm.sendAsyncMessage(message);
+  },
+
   // ----------
-  // Function: isComplete
-  // Return whether the xul:tab has fully loaded.
-  isComplete: function TabItems_isComplete(tab) {
-    // If our readyState is complete, but we're showing about:blank,
-    // and we're not loading about:blank, it means we haven't really
-    // started loading. This can happen to the first few tabs in a
-    // page.
-    Utils.assertThrow(tab, "tab");
-    return (
-      tab.linkedBrowser.contentDocument.readyState == 'complete' &&
-      !(tab.linkedBrowser.contentDocument.URL == 'about:blank' &&
-        tab._tabViewTabItem.getTabState().url != 'about:blank')
-    );
+  // Function: onMozAfterPaint
+  // Called when a web page is painted.
+  onMozAfterPaint: function TabItems_onMozAfterPaint(cx) {
+    let index = gBrowser.browsers.indexOf(cx.target);
+    if (index == -1)
+      return;
+
+    let tab = gBrowser.tabs[index];
+    if (!tab.pinned)
+      this.update(tab);
   },
 
   // ----------
   // Function: update
   // Takes in a xul:tab.
   update: function TabItems_update(tab) {
     try {
       Utils.assertThrow(tab, "tab");
@@ -1032,48 +983,36 @@ let TabItems = {
         $name.text(label);
 
       // ___ remove from waiting list now that we have no other
       // early returns
       this._tabsWaitingForUpdate.remove(tab);
 
       // ___ URL
       let tabUrl = tab.linkedBrowser.currentURI.spec;
-      tabItem.$container.attr("title", label + "\n" + tabUrl);
+      let tooltip = (label == tabUrl ? label : label + "\n" + tabUrl);
+      tabItem.$container.attr("title", tooltip);
 
       // ___ Make sure the tab is complete and ready for updating.
-      if (!this.isComplete(tab) && (!options || !options.force)) {
-        // If it's incomplete, stick it on the end of the queue
-        this._tabsWaitingForUpdate.push(tab);
-        return;
-      }
+      if (options && options.force) {
+        tabItem.updateCanvas();
+        tabItem._sendToSubscribers("updated");
+      } else {
+        this._isComplete(tab, function TabItems__update_isComplete(isComplete) {
+          if (!Utils.isValidXULTab(tab) || tab.pinned)
+            return;
 
-      // ___ thumbnail
-      let $canvas = tabItem.$canvas;
-      if (!tabItem.canvasSizeForced) {
-        let w = $canvas.width();
-        let h = $canvas.height();
-        if (w != tabItem.$canvas[0].width || h != tabItem.$canvas[0].height) {
-          tabItem.$canvas[0].width = w;
-          tabItem.$canvas[0].height = h;
-        }
+          if (isComplete) {
+            tabItem.updateCanvas();
+            tabItem._sendToSubscribers("updated");
+          } else {
+            this._tabsWaitingForUpdate.push(tab);
+          }
+        }.bind(this));
       }
-
-      this._lastUpdateTime = Date.now();
-      tabItem._lastTabUpdateTime = this._lastUpdateTime;
-
-      tabItem.tabCanvas.paint();
-      tabItem.saveThumbnail();
-
-      // ___ cache
-      if (tabItem.isShowingCachedData())
-        tabItem.hideCachedData();
-
-      // ___ notify subscribers that a full update has completed.
-      tabItem._sendToSubscribers("updated");
     } catch(e) {
       Utils.log(e);
     }
   },
 
   // ----------
   // Function: link
   // Takes in a xul:tab, creates a TabItem for it and adds it to the scene. 
@@ -1276,27 +1215,16 @@ let TabItems = {
     let tabItems = this.getItems();
 
     tabItems.forEach(function TabItems_saveAll_forEach(tabItem) {
       tabItem.save();
     });
   },
 
   // ----------
-  // Function: saveAllThumbnails
-  // Saves thumbnails of all open <TabItem>s.
-  saveAllThumbnails: function TabItems_saveAllThumbnails(options) {
-    let tabItems = this.getItems();
-
-    tabItems.forEach(function TabItems_saveAllThumbnails_forEach(tabItem) {
-      tabItem.saveThumbnail(options);
-    });
-  },
-
-  // ----------
   // Function: storageSanity
   // Checks the specified data (as returned by TabItem.getStorageData or loaded from storage)
   // and returns true if it looks valid.
   // TODO: this is a stub, please implement
   storageSanity: function TabItems_storageSanity(data) {
     return true;
   },
 
--- a/browser/components/tabview/tabview.js
+++ b/browser/components/tabview/tabview.js
@@ -20,26 +20,21 @@ XPCOMUtils.defineLazyGetter(this, "tabbr
 
 function tabviewString(name) tabviewBundle.GetStringFromName('tabview.' + name);
 function tabbrowserString(name) tabbrowserBundle.GetStringFromName(name);
 
 XPCOMUtils.defineLazyGetter(this, "gPrefBranch", function() {
   return Services.prefs.getBranch("browser.panorama.");
 });
 
-XPCOMUtils.defineLazyGetter(this, "gPrivateBrowsing", function() {
-  return Cc["@mozilla.org/privatebrowsing;1"].
-           getService(Ci.nsIPrivateBrowsingService);
-});
+XPCOMUtils.defineLazyServiceGetter(this, "gPrivateBrowsing",
+  "@mozilla.org/privatebrowsing;1", "nsIPrivateBrowsingService");
 
-XPCOMUtils.defineLazyGetter(this, "gNetUtil", function() {
-  var obj = {};
-  Cu.import("resource://gre/modules/NetUtil.jsm", obj);
-  return obj.NetUtil;
-});
+XPCOMUtils.defineLazyModuleGetter(this, "gPageThumbnails",
+  "resource:///modules/PageThumbs.jsm", "PageThumbs");
 
 var gWindow = window.parent;
 var gBrowser = gWindow.gBrowser;
 var gTabView = gWindow.TabView;
 var gTabViewDeck = gWindow.document.getElementById("tab-view-deck");
 var gBrowserPanel = gWindow.document.getElementById("browser-panel");
 var gTabViewFrame = gWindow.document.getElementById("tab-view");
 
@@ -66,18 +61,16 @@ let AllTabs = {
     gBrowser.tabContainer.removeEventListener(this._events[eventName], callback, false);
   }
 };
 
 # NB: Certain files need to evaluate before others
 
 #include iq.js
 #include storage.js
-#include storagePolicy.js
 #include items.js
 #include groupitems.js
 #include tabitems.js
 #include favicons.js
 #include drag.js
 #include trench.js
-#include thumbnailStorage.js
 #include search.js
 #include ui.js
--- a/browser/components/tabview/test/Makefile.in
+++ b/browser/components/tabview/test/Makefile.in
@@ -66,17 +66,16 @@ include $(topsrcdir)/config/rules.mk
                  browser_tabview_bug595521.js \
                  browser_tabview_bug595560.js \
                  browser_tabview_bug595601.js \
                  browser_tabview_bug595804.js \
                  browser_tabview_bug595930.js \
                  browser_tabview_bug595943.js \
                  browser_tabview_bug595965.js \
                  browser_tabview_bug596781.js \
-                 browser_tabview_bug597248.js \
                  browser_tabview_bug597360.js \
                  browser_tabview_bug597399.js \
                  browser_tabview_bug598375.js \
                  browser_tabview_bug598600.js \
                  browser_tabview_bug599048.js \
                  browser_tabview_bug599626.js \
                  browser_tabview_bug600645.js \
                  browser_tabview_bug600812.js \
@@ -110,17 +109,16 @@ include $(topsrcdir)/config/rules.mk
                  browser_tabview_bug625195.js \
                  browser_tabview_bug625269.js \
                  browser_tabview_bug625424.js \
                  browser_tabview_bug625955.js \
                  browser_tabview_bug626368.js \
                  browser_tabview_bug626455.js \
                  browser_tabview_bug626525.js \
                  browser_tabview_bug626791.js \
-                 browser_tabview_bug627288.js \
                  browser_tabview_bug627736.js \
                  browser_tabview_bug628061.js \
                  browser_tabview_bug628165.js \
                  browser_tabview_bug628270.js \
                  browser_tabview_bug628887.js \
                  browser_tabview_bug629189.js \
                  browser_tabview_bug629195.js \
                  browser_tabview_bug630102.js \
@@ -146,23 +144,23 @@ include $(topsrcdir)/config/rules.mk
                  browser_tabview_bug650573.js \
                  browser_tabview_bug651311.js \
                  browser_tabview_bug654295.js \
                  browser_tabview_bug654721.js \
                  browser_tabview_bug654941.js \
                  browser_tabview_bug655269.js \
                  browser_tabview_bug656778.js \
                  browser_tabview_bug656913.js \
+                 browser_tabview_bug659594.js \
                  browser_tabview_bug662266.js \
                  browser_tabview_bug663421.js \
                  browser_tabview_bug665502.js \
                  browser_tabview_bug669694.js \
                  browser_tabview_bug673196.js \
                  browser_tabview_bug673729.js \
-                 browser_tabview_bug677310.js \
                  browser_tabview_bug678374.js \
                  browser_tabview_bug679853.js \
                  browser_tabview_bug681599.js \
                  browser_tabview_bug685476.js \
                  browser_tabview_bug685692.js \
                  browser_tabview_bug686654.js \
                  browser_tabview_bug696602.js \
                  browser_tabview_bug697390.js \
@@ -183,18 +181,16 @@ include $(topsrcdir)/config/rules.mk
                  browser_tabview_group.js \
                  browser_tabview_launch.js \
                  browser_tabview_multiwindow_search.js \
                  browser_tabview_privatebrowsing.js \
                  browser_tabview_rtl.js \
                  browser_tabview_search.js \
                  browser_tabview_snapping.js \
                  browser_tabview_startup_transitions.js \
-                 browser_tabview_storage_policy.js \
-                 browser_tabview_thumbnail_storage.js \
                  browser_tabview_undo_group.js \
                  dummy_page.html \
                  head.js \
                  search1.html \
                  search2.html \
                  test_bug600645.html \
                  test_bug644097.html \
                  test_bug678374.html \
--- a/browser/components/tabview/test/browser_tabview_bug594958.js
+++ b/browser/components/tabview/test/browser_tabview_bug594958.js
@@ -49,22 +49,27 @@ function test() {
     if (!tests.length) {
       finishTest();
       return;
     }
 
     let test = tests.shift();
     let tab = win.gBrowser.tabs[0];
 
-    tab.linkedBrowser.addEventListener('load', function onLoad() {
-      tab.linkedBrowser.removeEventListener('load', onLoad, true);
-      checkUrl(test);
+    let browser = tab.linkedBrowser;
+    browser.addEventListener("load", function onLoad(event) {
+       browser.removeEventListener("load", onLoad, true);
+       
+       let tabItem = tab._tabViewTabItem;
+       tabItem.addSubscriber("updated", function onUpdated() {
+         tabItem.removeSubscriber("updated", onUpdated);
+         checkUrl(test);
+       });
     }, true);
-
-    tab.linkedBrowser.loadURI(test.url);
+    browser.loadURI(test.url);
   }
 
   let checkUrl = function (test) {
     let sizes = [[-400, 0], [800, -200], [-400, 200]];
 
     let nextSize = function () {
       if (!sizes.length) {
         next();
--- a/browser/components/tabview/test/browser_tabview_bug595020.js
+++ b/browser/components/tabview/test/browser_tabview_bug595020.js
@@ -1,13 +1,11 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
-let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
-
 let stateStartup = {windows:[
   {tabs:[{entries:[{url:"about:home"}]}], extData:{"tabview-last-session-group-name":"title"}}
 ]};
 
 function test() {
   let assertWindowTitle = function (win, title) {
     let browser = win.gBrowser.tabs[0].linkedBrowser;
     let winTitle = win.gBrowser.getWindowTitleForBrowser(browser);
deleted file mode 100644
--- a/browser/components/tabview/test/browser_tabview_bug597248.js
+++ /dev/null
@@ -1,146 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-let newTabOne;
-let newTabTwo;
-let newTabThree;
-let restoredNewTabTwoLoaded = false;
-let restoredNewTabThreeLoaded = false;
-let frameInitialized = false;
-
-function test() {
-  waitForExplicitFinish();
-  newWindowWithTabView(setupOne);
-}
-
-function setupOne(win) {
-  win.TabView.firstUseExperienced = true;
-
-  win.gBrowser.addTab("http://mochi.test:8888/browser/browser/components/tabview/test/search1.html");
-  win.gBrowser.addTab("http://mochi.test:8888/browser/browser/components/tabview/test/dummy_page.html");
-
-  afterAllTabsLoaded(function () setupTwo(win), win);
-}
-
-let restoredWin;
-
-function setupTwo(win) {
-  let contentWindow = win.TabView.getContentWindow();
-
-  let tabItems = contentWindow.TabItems.getItems();
-  is(tabItems.length, 3, "There should be 3 tab items before closing");
-
-  let numTabsToSave = tabItems.length;
-
-  // force all canvases to update, and hook in imageData save detection
-  tabItems.forEach(function(tabItem) {
-    // mark thumbnail as dirty
-    tabItem.tabCanvas.paint();
-
-    tabItem.addSubscriber("savedCachedImageData", function onSaved(item) {
-      item.removeSubscriber("savedCachedImageData", onSaved);
-
-      if (!--numTabsToSave)
-        restoreWindow();
-    });
-  });
-
-  // after the window is closed, restore it.
-  let restoreWindow = function() {
-    executeSoon(function() {
-      restoredWin = undoCloseWindow();
-      restoredWin.addEventListener("load", function onLoad(event) {
-        restoredWin.removeEventListener("load", onLoad, false);
-
-        registerCleanupFunction(function() restoredWin.close());
-        is(restoredWin.gBrowser.tabs.length, 3, "The total number of tabs is 3");
-
-        // setup tab variables and listen to the tabs load progress
-        newTabOne = restoredWin.gBrowser.tabs[0];
-        newTabTwo = restoredWin.gBrowser.tabs[1];
-        newTabThree = restoredWin.gBrowser.tabs[2];
-        restoredWin.gBrowser.addTabsProgressListener(gTabsProgressListener);
-
-        // execute code when the frame is initialized
-        let onTabViewFrameInitialized = function() {
-          restoredWin.removeEventListener(
-            "tabviewframeinitialized", onTabViewFrameInitialized, false);
-
-          let restoredContentWindow = restoredWin.TabView.getContentWindow();
-          // prevent TabItems._update being called before checking cached images
-          restoredContentWindow.TabItems._pauseUpdateForTest = true;
-
-          let nextStep = function() {
-            // since we are not sure whether the frame is initialized first or two tabs
-            // compete loading first so we need this.
-            if (restoredNewTabTwoLoaded && restoredNewTabThreeLoaded)
-              updateAndCheck();
-            else
-              frameInitialized = true;
-          }
-
-          let tabItems = restoredContentWindow.TabItems.getItems();
-          let count = tabItems.length;
-
-          tabItems.forEach(function(tabItem) {
-            tabItem.addSubscriber("showingCachedData", function onLoaded() {
-              tabItem.removeSubscriber("showingCachedData", onLoaded);
-              ok(tabItem.isShowingCachedData(),
-                "Tab item is showing cached data and is just connected. " +
-                tabItem.tab.linkedBrowser.currentURI.spec);
-              if (--count == 0)
-                nextStep();
-            });
-          });
-        }
-
-        restoredWin.addEventListener(
-          "tabviewframeinitialized", onTabViewFrameInitialized, false);
-      }, false);
-    });
-  };
-
-  win.close();
-}
-
-let gTabsProgressListener = {
-  onStateChange: function(browser, webProgress, request, stateFlags, status) {
-    // ensure about:blank doesn't trigger the code
-    if ((stateFlags & Ci.nsIWebProgressListener.STATE_STOP) &&
-        (stateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW) &&
-         browser.currentURI.spec != "about:blank") {
-      if (newTabTwo.linkedBrowser == browser)
-        restoredNewTabTwoLoaded = true;
-      else if (newTabThree.linkedBrowser == browser)
-        restoredNewTabThreeLoaded = true;
-
-      // since we are not sure whether the frame is initialized first or two tabs
-      // compete loading first so we need this.
-      if (restoredNewTabTwoLoaded && restoredNewTabThreeLoaded) {
-        restoredWin.gBrowser.removeTabsProgressListener(gTabsProgressListener);
-
-        if (frameInitialized)
-          updateAndCheck();
-      }
-    }
-  }
-};
-
-function updateAndCheck() {
-  // force all canvas to update
-  let contentWindow = restoredWin.TabView.getContentWindow();
-
-  contentWindow.TabItems._pauseUpdateForTest = false;
-
-  let tabItems = contentWindow.TabItems.getItems();
-  tabItems.forEach(function(tabItem) {
-    contentWindow.TabItems._update(tabItem.tab);
-    ok(!tabItem.isShowingCachedData(),
-      "Tab item is not showing cached data anymore. " +
-      tabItem.tab.linkedBrowser.currentURI.spec);
-  });
-
-  // clean up and finish
-  restoredWin.close();
-  finish();
-}
--- a/browser/components/tabview/test/browser_tabview_bug610242.js
+++ b/browser/components/tabview/test/browser_tabview_bug610242.js
@@ -62,39 +62,35 @@ function onTabViewWindowLoaded(win) {
     if (visible) {
       is(display, "block", label + " has favicon");
     } else {
       is(display, "none", label + " has no favicon");
     }
   }
 
   afterAllTabsLoaded(function() {
-    afterAllTabItemsUpdated(function() {
-      let children = group.getChildren();
-      let len = children.length;
-      let iconUpdateCounter = 0;
+    let children = group.getChildren();
+    let len = children.length;
+    let iconUpdateCounter = 0;
 
-      children.forEach(function(tabItem) {
-        tabItem.addSubscriber("iconUpdated", function onIconUpdated() {
-          // the tab is not loaded completely so ignore it.
-          if (tabItem.tab.linkedBrowser.currentURI.spec == "about:blank")
-            return;
-
-          tabItem.removeSubscriber("iconUpdated", onIconUpdated);
+    children.forEach(function(tabItem) {
+      tabItem.addSubscriber("iconUpdated", function onIconUpdated() {
+        tabItem.removeSubscriber("iconUpdated", onIconUpdated);
 
-          if (++iconUpdateCounter == len) {
-            check(datatext, "datatext", false);
-            check(datahtml, "datahtml", false);
-            check(mozilla, "about:mozilla", false);
-            check(robots, "about:robots", true);
-            check(html, "html", true);
-            check(png, "png", false);
-            check(svg, "svg", true);
+        if (++iconUpdateCounter == len) {
+          check(datatext, "datatext", false);
+          check(datahtml, "datahtml", false);
+          check(mozilla, "about:mozilla", false);
+          check(robots, "about:robots", true);
+          check(html, "html", true);
+          check(png, "png", false);
+          check(svg, "svg", true);
 
-            // Get rid of the group and its children
-            // The group close will trigger a finish().
-            closeGroupItem(group);
-          }
-        });
+          // Get rid of the group and its children
+          // The group close will trigger a finish().
+          closeGroupItem(group);
+        }
       });
-    }, win);
+    });
+
+    afterAllTabItemsUpdated(function () {}, win);
   }, win);
 }
deleted file mode 100644
--- a/browser/components/tabview/test/browser_tabview_bug627288.js
+++ /dev/null
@@ -1,93 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-function test() {
-  let cw;
-  let tab;
-
-  let testReconnectWithSameUrl = function () {
-    tab = gBrowser.loadOneTab('http://mochi.test:8888/', {inBackground: true});
-
-    afterAllTabsLoaded(function () {
-      let tabItem = tab._tabViewTabItem;
-      let data = tabItem.getStorageData(true);
-      gBrowser.removeTab(tab);
-
-      cw.TabItems.pauseReconnecting();
-      tab = gBrowser.loadOneTab('http://mochi.test:8888/', {inBackground: true});
-
-      afterAllTabsLoaded(function () {
-        tabItem = tab._tabViewTabItem;
-
-        tabItem.addSubscriber("savedCachedImageData", function onSaved() {
-          tabItem.removeSubscriber("savedCachedImageData", onSaved);
-
-          tabItem.addSubscriber("showingCachedData", function onLoaded() {
-            tabItem.removeSubscriber("showingCachedData", onLoaded);
-
-            ok(tabItem.isShowingCachedData(), 'tabItem shows cached data');
-            testChangeUrlAfterReconnect();
-          });
-
-          cw.TabItems.resumeReconnecting();
-        });
-
-        cw.Storage.saveTab(tab, data);
-        tabItem.saveThumbnail();
-      });
-    });
-  }
-
-  let testChangeUrlAfterReconnect = function () {
-    tab.linkedBrowser.loadURI('http://mochi.test:8888/browser/');
-
-    whenTabAttrModified(tab, function () {
-      cw.TabItems._update(tab);
-
-      let tabItem = tab._tabViewTabItem;
-      let currentLabel = tabItem.$tabTitle.text();
-
-      is(currentLabel, 'mochitest index /browser/', 'tab label is up-to-date');
-      testReconnectWithNewUrl();
-    });
-  }
-
-  let testReconnectWithNewUrl = function () {
-    let tabItem = tab._tabViewTabItem;
-    let data = tabItem.getStorageData(true);
-    gBrowser.removeTab(tab);
-
-    cw.TabItems.pauseReconnecting();
-    tab = gBrowser.loadOneTab('http://mochi.test:8888/', {inBackground: true});
-    cw.Storage.saveTab(tab, data);
-
-    whenTabAttrModified(tab, function () {
-      tabItem = tab._tabViewTabItem;
-      cw.TabItems.resumeReconnecting();
-      ok(!tabItem.isShowingCachedData(), 'tabItem does not show cached data');
-
-      gBrowser.removeTab(tab);
-      hideTabView(finish);
-    });
-  }
-
-  waitForExplicitFinish();
-
-  showTabView(function () {
-    cw = TabView.getContentWindow();
-    testReconnectWithSameUrl();
-  });
-}
-
-// ----------
-function whenTabAttrModified(tab, callback) {
-  let onModified = function (event) {
-    tab.removeEventListener(event.type, onModified, false);
-    // we need executeSoon here because the tabItem also listens for the
-    // TabAttrModified event. so this is to make sure the tabItem logic
-    // is executed before the test logic.
-    executeSoon(callback);
-  }
-
-  tab.addEventListener("TabAttrModified", onModified, false);
-}
--- a/browser/components/tabview/test/browser_tabview_bug655269.js
+++ b/browser/components/tabview/test/browser_tabview_bug655269.js
@@ -1,20 +1,21 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 function test() {
   waitForExplicitFinish();
 
   newWindowWithTabView(function (win) {
     let cw = win.TabView.getContentWindow();
-    let tabItem = win.gBrowser.tabs[0]._tabViewTabItem;
+
+    win.addEventListener("SSWindowClosing", function onClose() {
+      win.removeEventListener("SSWindowClosing", onClose);
 
-    tabItem.addSubscriber("savedCachedImageData", function onSaved() {
-      tabItem.removeSubscriber("savedCachedImageData", onSaved);
-
-      ok(cw.UI.isDOMWindowClosing, "dom window is closing");
-      waitForFocus(finish);
+      executeSoon(function () {
+        ok(cw.UI.isDOMWindowClosing, "dom window is closing");
+        waitForFocus(finish);
+      });
     });
 
     win.close();
   });
 }
new file mode 100644
--- /dev/null
+++ b/browser/components/tabview/test/browser_tabview_bug659594.js
@@ -0,0 +1,39 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function test() {
+  waitForExplicitFinish();
+
+  newWindowWithTabView(function(win) {
+    let numTabsToUpdate = 2;
+
+    showTabView(function() {
+      let contentWindow = win.TabView.getContentWindow();
+      let groupItem = contentWindow.GroupItems.groupItems[0];
+
+      groupItem.getChildren().forEach(function(tabItem) {
+        tabItem.addSubscriber("updated", function onUpdated() {
+          tabItem.removeSubscriber("updated", onUpdated);
+
+          if (--numTabsToUpdate == 0)
+            finish();
+        });
+        contentWindow.TabItems.update(tabItem.tab);
+      });
+    }, win);
+  }, function(win) {
+    BrowserOffline.toggleOfflineStatus();
+    ok(Services.io.offline, "It is now offline");
+
+    let originalTab = win.gBrowser.tabs[0];
+    originalTab.linkedBrowser.loadURI("http://www.example.com/foo");
+    win.gBrowser.addTab("http://www.example.com/bar");
+
+    registerCleanupFunction(function () {
+      if (Services.io.offline)
+        BrowserOffline.toggleOfflineStatus();
+      win.close();
+    });
+  });
+}
+
deleted file mode 100644
--- a/browser/components/tabview/test/browser_tabview_bug677310.js
+++ /dev/null
@@ -1,48 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-let pb = Cc["@mozilla.org/privatebrowsing;1"].
-         getService(Ci.nsIPrivateBrowsingService);
-
-function test() {
-  let thumbnailsSaved = false;
-
-  waitForExplicitFinish();
-
-  registerCleanupFunction(function () {
-    ok(thumbnailsSaved, "thumbs have been saved before entering pb mode");
-    pb.privateBrowsingEnabled = false;
-  });
-
-  afterAllTabsLoaded(function () {
-    showTabView(function () {
-      hideTabView(function () {
-        let numConditions = 2;
-
-        function check() {
-          if (--numConditions)
-            return;
-
-          togglePrivateBrowsing(finish);
-        }
-
-        let tabItem = gBrowser.tabs[0]._tabViewTabItem;
-
-        // save all thumbnails synchronously to cancel all delayed thumbnail
-        // saves that might be active
-        tabItem.saveThumbnail({synchronously: true});
-
-        // force a tabCanvas paint to flag the thumbnail as dirty
-        tabItem.tabCanvas.paint();
-
-        tabItem.addSubscriber("savedCachedImageData", function onSaved() {
-          tabItem.removeSubscriber("savedCachedImageData", onSaved);
-          thumbnailsSaved = true;
-          check();
-        });
-
-        togglePrivateBrowsing(check);
-      });
-    });
-  });
-}
--- a/browser/components/tabview/test/browser_tabview_expander.js
+++ b/browser/components/tabview/test/browser_tabview_expander.js
@@ -21,24 +21,23 @@ function onTabViewWindowLoaded(win) {
   });
 
   let expander = contentWindow.iQ(group.container).find(".stackExpander");
   ok("length" in expander && expander.length == 1, "The group has an expander.");
 
   // procreate!
   contentWindow.UI.setActive(group);
   for (var i=0; i<7; i++) {
-    win.gBrowser.loadOneTab('about:blank#' + i, {inBackground: true});
+    win.gBrowser.loadOneTab('http://example.com#' + i, {inBackground: true});
   }
   let children = group.getChildren();
   
   // Wait until they all update because, once updated, they will notice that they
   // don't have favicons and this will change their styling at some unknown time.
   afterAllTabItemsUpdated(function() {
-    
     ok(!group.shouldStack(group._children.length), "The group should not stack.");
     is(expander[0].style.display, "none", "The expander is hidden.");
     
     // now resize the group down.
     group.setSize(100, 100, true);
   
     ok(group.shouldStack(group._children.length), "The group should stack.");
     isnot(expander[0].style.display, "none", "The expander is now visible!");
deleted file mode 100644
--- a/browser/components/tabview/test/browser_tabview_thumbnail_storage.js
+++ /dev/null
@@ -1,161 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-let tests = [testRawSyncSave, testRawAsyncSave, testRawLoadError,
-             testAsyncSave, testSyncSave, testOverrideAsyncSave,
-             testSaveCleanThumbnail];
-
-function test() {
-  waitForExplicitFinish();
-  loadTabView(next);
-}
-
-function testRawSyncSave() {
-  let cw = TabView.getContentWindow();
-  let url = "http://example.com/sync-url";
-  let data = "thumbnail-data-sync";
-  let saved = false;
-
-  cw.ThumbnailStorage.saveThumbnail(url, data, function (error) {
-    ok(!error, "thumbnail entry was saved");
-    ok(!saved, "thumbnail was saved synchronously");
-
-    cw.ThumbnailStorage.loadThumbnail(url, function (error, imageData) {
-      ok(!error, "thumbnail entry was loaded");
-      is(imageData, data, "valid thumbnail data received");
-      next();
-    });
-  }, {synchronously: true});
-
-  saved = true;
-}
-
-function testRawAsyncSave() {
-  let cw = TabView.getContentWindow();
-  let url = "http://example.com/async-url";
-  let data = "thumbnail-data-async";
-  let saved = false;
-
-  cw.ThumbnailStorage.saveThumbnail(url, data, function (error) {
-    ok(!error, "thumbnail entry was saved");
-    ok(saved, "thumbnail was saved asynchronously");
-
-    cw.ThumbnailStorage.loadThumbnail(url, function (error, imageData) {
-      ok(!error, "thumbnail entry was loaded");
-      is(imageData, data, "valid thumbnail data received");
-      next();
-    });
-  });
-
-  saved = true;
-}
-
-function testRawLoadError() {
-  let cw = TabView.getContentWindow();
-
-  cw.ThumbnailStorage.loadThumbnail("non-existant-url", function (error, data) {
-    ok(error, "thumbnail entry failed to load");
-    is(null, data, "no thumbnail data received");
-    next();
-  });
-}
-
-function testSyncSave() {
-  let tabItem = gBrowser.tabs[0]._tabViewTabItem;
-
-  // set the thumbnail to dirty
-  tabItem.tabCanvas.paint();
-
-  let saved = false;
-
-  whenThumbnailSaved(tabItem, function () {
-    ok(!saved, "thumbnail was saved synchronously");
-    next();
-  });
-
-  tabItem.saveThumbnail({synchronously: true});
-  saved = true;
-}
-
-function testAsyncSave() {
-  let tabItem = gBrowser.tabs[0]._tabViewTabItem;
-
-  // set the thumbnail to dirty
-  tabItem.tabCanvas.paint();
-
-  let saved = false;
-
-  whenThumbnailSaved(tabItem, function () {
-    ok(saved, "thumbnail was saved asynchronously");
-    next();
-  });
-
-  tabItem.saveThumbnail();
-  saved = true;
-}
-
-function testOverrideAsyncSave() {
-  let tabItem = gBrowser.tabs[0]._tabViewTabItem;
-
-  // set the thumbnail to dirty
-  tabItem.tabCanvas.paint();
-
-  // initiate async save
-  tabItem.saveThumbnail();
-
-  let saveCount = 0;
-
-  whenThumbnailSaved(tabItem, function () {
-    saveCount = 1;
-  });
-
-  tabItem.saveThumbnail({synchronously: true});
-
-  is(saveCount, 1, "thumbnail got saved once");
-  next();
-}
-
-function testSaveCleanThumbnail() {
-  let tabItem = gBrowser.tabs[0]._tabViewTabItem;
-
-  // set the thumbnail to dirty
-  tabItem.tabCanvas.paint();
-
-  let saveCount = 0;
-
-  whenThumbnailSaved(tabItem, function () saveCount++);
-  tabItem.saveThumbnail({synchronously: true});
-  tabItem.saveThumbnail({synchronously: true});
-
-  is(saveCount, 1, "thumbnail got saved once, only");
-  next();
-}
-
-// ----------
-function whenThumbnailSaved(tabItem, callback) {
-  tabItem.addSubscriber("savedCachedImageData", function onSaved() {
-    tabItem.removeSubscriber("savedCachedImageData", onSaved);
-    callback();
-  });
-}
-
-// ----------
-function loadTabView(callback) {
-  afterAllTabsLoaded(function () {
-    showTabView(function () {
-      hideTabView(callback);
-    });
-  });
-}
-
-// ----------
-function next() {
-  let test = tests.shift();
-
-  if (test) {
-    info("* running " + test.name + "...");
-    test();
-  } else {
-    finish();
-  }
-}
--- a/browser/components/tabview/test/head.js
+++ b/browser/components/tabview/test/head.js
@@ -73,23 +73,33 @@ function closeGroupItem(groupItem, callb
 
   groupItem.closeAll();
 }
 
 // ----------
 function afterAllTabItemsUpdated(callback, win) {
   win = win || window;
   let tabItems = win.document.getElementById("tab-view").contentWindow.TabItems;
+  let counter = 0;
 
   for (let a = 0; a < win.gBrowser.tabs.length; a++) {
     let tabItem = win.gBrowser.tabs[a]._tabViewTabItem;
-    if (tabItem)
-      tabItems._update(win.gBrowser.tabs[a]);
+    if (tabItem) {
+      let tab = win.gBrowser.tabs[a];
+      counter++;
+      tabItem.addSubscriber("updated", function onUpdated() {
+        tabItem.removeSubscriber("updated", onUpdated);
+        if (--counter == 0)
+          callback();
+      });
+      tabItems.update(tab);
+    }
   }
-  callback();
+  if (counter == 0)
+    callback();
 }
 
 // ---------
 function newWindowWithTabView(shownCallback, loadCallback, width, height) {
   let winWidth = width || 800;
   let winHeight = height || 800;
   let win = window.openDialog(getBrowserURL(), "_blank",
                               "chrome,all,dialog=no,height=" + winHeight +
deleted file mode 100644
--- a/browser/components/tabview/thumbnailStorage.js
+++ /dev/null
@@ -1,266 +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 thumbnailStorage.js.
- *
- * The Initial Developer of the Original Code is
- * the Mozilla Foundation.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- * Raymond Lee <raymond@appcoast.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 ***** */
-
-// **********
-// Title: thumbnailStorage.js
-
-// ##########
-// Class: ThumbnailStorage
-// Singleton for persistent storage of thumbnail data.
-let ThumbnailStorage = {
-  CACHE_CLIENT_IDENTIFIER: "tabview-cache",
-  CACHE_PREFIX: "moz-panorama:",
-
-  // Holds the cache session reference
-  _cacheSession: null,
-
-  // Holds the string input stream reference
-  _stringInputStream: null,
-
-  // Holds the storage stream reference
-  _storageStream: null,
-
-  // ----------
-  // Function: toString
-  // Prints [ThumbnailStorage] for debug use.
-  toString: function ThumbnailStorage_toString() {
-    return "[ThumbnailStorage]";
-  },
-
-  // ----------
-  // Function: init
-  // Should be called when UI is initialized.
-  init: function ThumbnailStorage_init() {
-    // Create stream-based cache session for tabview
-    let cacheService = 
-      Cc["@mozilla.org/network/cache-service;1"].
-        getService(Ci.nsICacheService);
-    this._cacheSession = cacheService.createSession(
-      this.CACHE_CLIENT_IDENTIFIER, Ci.nsICache.STORE_ON_DISK, true);
-    this._stringInputStream = Components.Constructor(
-      "@mozilla.org/io/string-input-stream;1", "nsIStringInputStream",
-      "setData");
-    this._storageStream = Components.Constructor(
-      "@mozilla.org/storagestream;1", "nsIStorageStream", 
-      "init");
-  },
-
-  // ----------
-  // Function: _openCacheEntry
-  // Opens a cache entry for the given <url> and requests access <access>.
-  // Calls <successCallback>(entry) when the entry was successfully opened with
-  // requested access rights. Otherwise calls <errorCallback>().
-  //
-  // Parameters:
-  //   url - the url to use as the storage key
-  //   access - access flags, see Ci.nsICache.ACCESS_*
-  //   successCallback - the callback to be called on success
-  //   errorCallback - the callback to be called when an error occured
-  //   options - an object with additional parameters, see below
-  //
-  // Possible options:
-  //   synchronously - set to true to force sync mode
-  _openCacheEntry:
-    function ThumbnailStorage__openCacheEntry(url, access, successCallback,
-                                              errorCallback, options) {
-    Utils.assert(url, "invalid or missing argument <url>");
-    Utils.assert(access, "invalid or missing argument <access>");
-    Utils.assert(successCallback, "invalid or missing argument <successCallback>");
-    Utils.assert(errorCallback, "invalid or missing argument <errorCallback>");
-
-    function onCacheEntryAvailable(entry, accessGranted, status) {
-      if (entry && access == accessGranted && Components.isSuccessCode(status)) {
-        successCallback(entry);
-      } else {
-        if (entry)
-          entry.close();
-
-        errorCallback();
-      }
-    }
-
-    let key = this.CACHE_PREFIX + url;
-
-    if (options && options.synchronously) {
-      let entry = this._cacheSession.openCacheEntry(key, access, true);
-      let status = Cr.NS_OK;
-      onCacheEntryAvailable(entry, entry.accessGranted, status);
-    } else {
-      let listener = new CacheListener(onCacheEntryAvailable);
-      this._cacheSession.asyncOpenCacheEntry(key, access, listener);
-    }
-  },
-
-  // ----------
-  // Function: saveThumbnail
-  // Saves the given thumbnail in the cache.
-  //
-  // Parameters:
-  //   url - the url to use as the storage key
-  //   imageData - the image data to save for the given key
-  //   callback - the callback that is called when the operation is finished
-  //   options - an object with additional parameters, see below
-  //
-  // Possible options:
-  //   synchronously - set to true to force sync mode
-  saveThumbnail:
-    function ThumbnailStorage_saveThumbnail(url, imageData, callback, options) {
-    Utils.assert(url, "invalid or missing argument <url>");
-    Utils.assert(imageData, "invalid or missing argument <imageData>");
-    Utils.assert(callback, "invalid or missing argument <callback>");
-
-    let synchronously = (options && options.synchronously);
-    let self = this;
-
-    function onCacheEntryAvailable(entry) {
-      let outputStream = entry.openOutputStream(0);
-
-      function cleanup() {
-        outputStream.close();
-        entry.close();
-      }
-
-      // synchronous mode
-      if (synchronously) {
-        outputStream.write(imageData, imageData.length);
-        cleanup();
-        callback();
-        return;
-      }
-
-      // asynchronous mode
-      let inputStream = new self._stringInputStream(imageData, imageData.length);
-      gNetUtil.asyncCopy(inputStream, outputStream, function (result) {
-        cleanup();
-        inputStream.close();
-        callback(Components.isSuccessCode(result) ? "" : "failure");
-      });
-    }
-
-    function onCacheEntryUnavailable() {
-      callback("unavailable");
-    }
-
-    this._openCacheEntry(url, Ci.nsICache.ACCESS_WRITE, onCacheEntryAvailable,
-                         onCacheEntryUnavailable, options);
-  },
-
-  // ----------
-  // Function: loadThumbnail
-  // Loads a thumbnail from the cache.
-  //
-  // Parameters:
-  //   url - the url to use as the storage key
-  //   callback - the callback that is called when the operation is finished
-  loadThumbnail: function ThumbnailStorage_loadThumbnail(url, callback) {
-    Utils.assert(url, "invalid or missing argument <url>");
-    Utils.assert(callback, "invalid or missing argument <callback>");
-
-    let self = this;
-
-    function onCacheEntryAvailable(entry) {
-      let imageChunks = [];
-      let nativeInputStream = entry.openInputStream(0);
-
-      const CHUNK_SIZE = 0x10000; // 65k
-      const PR_UINT32_MAX = 0xFFFFFFFF;
-      let storageStream = new self._storageStream(CHUNK_SIZE, PR_UINT32_MAX, null);
-      let storageOutStream = storageStream.getOutputStream(0);
-
-      let cleanup = function () {
-        nativeInputStream.close();
-        storageStream.close();
-        storageOutStream.close();
-        entry.close();
-      }
-
-      gNetUtil.asyncCopy(nativeInputStream, storageOutStream, function (result) {
-        // cancel if parent window has already been closed
-        if (typeof UI == "undefined") {
-          cleanup();
-          return;
-        }
-
-        let imageData = null;
-        let isSuccess = Components.isSuccessCode(result);
-
-        if (isSuccess) {
-          let storageInStream = storageStream.newInputStream(0);
-          imageData = gNetUtil.readInputStreamToString(storageInStream,
-            storageInStream.available());
-          storageInStream.close();
-        }
-
-        cleanup();
-        callback(isSuccess ? "" : "failure", imageData);
-      });
-    }
-
-    function onCacheEntryUnavailable() {
-      callback("unavailable");
-    }
-
-    this._openCacheEntry(url, Ci.nsICache.ACCESS_READ, onCacheEntryAvailable,
-                         onCacheEntryUnavailable);
-  }
-}
-
-// ##########
-// Class: CacheListener
-// Generic CacheListener for feeding to asynchronous cache calls.
-// Calls <callback>(entry, access, status) when the requested cache entry
-// is available.
-function CacheListener(callback) {
-  Utils.assert(typeof callback == "function", "callback arg must be a function");
-  this.callback = callback;
-};
-
-CacheListener.prototype = {
-  // ----------
-  // Function: toString
-  // Prints [CacheListener] for debug use
-  toString: function CacheListener_toString() {
-    return "[CacheListener]";
-  },
-
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsICacheListener]),
-  onCacheEntryAvailable: function CacheListener_onCacheEntryAvailable(
-    entry, access, status) {
-    this.callback(entry, access, status);
-  }
-};
-
--- a/browser/components/tabview/ui.js
+++ b/browser/components/tabview/ui.js
@@ -153,25 +153,19 @@ let UI = {
   // Must be called after the object is created.
   init: function UI_init() {
     try {
       let self = this;
 
       // initialize the direction of the page
       this._initPageDirection();
 
-      // ___ thumbnail storage
-      ThumbnailStorage.init();
-
       // ___ storage
       Storage.init();
 
-      // ___ storage policy
-      StoragePolicy.init();
-
       if (Storage.readWindowBusyState(gWindow))
         this.storageBusy();
 
       let data = Storage.readUIData(gWindow);
       this._storageSanity(data);
       this._pageBounds = data.pageBounds;
 
       // ___ search
@@ -272,17 +266,16 @@ let UI = {
 
         // XXX bug #635975 - don't unlink the tab if the dom window is closing.
         self.isDOMWindowClosing = true;
 
         if (self.isTabViewVisible())
           GroupItems.removeHiddenGroups();
 
         TabItems.saveAll();
-        TabItems.saveAllThumbnails({synchronously: true});
 
         self._save();
       }, false);
 
       // ___ load frame script
       let frameScript = "chrome://browser/content/tabview-content.js";
       gWindow.messageManager.loadFrameScript(frameScript, true);
 
@@ -314,17 +307,16 @@ let UI = {
     });
     this._cleanupFunctions = [];
 
     // additional clean up
     TabItems.uninit();
     GroupItems.uninit();
     FavIcons.uninit();
     Storage.uninit();
-    StoragePolicy.uninit();
 
     this._removeTabActionHandlers();
     this._currentTab = null;
     this._pageBounds = null;
     this._reorderTabItemsOnShow = null;
     this._reorderTabsOnHide = null;
     this._frameInitialized = false;
   },
@@ -712,21 +704,16 @@ let UI = {
           self._privateBrowsing.wasInTabView = self.isTabViewVisible();
           if (self.isTabViewVisible())
             self.goToTab(gBrowser.selectedTab);
         }
       } else if (topic == "private-browsing-change-granted") {
         if (data == "enter" || data == "exit") {
           Search.hide();
           self._privateBrowsing.transitionMode = data;
-
-          // make sure to save all thumbnails that haven't been saved yet
-          // before we enter the private browsing mode
-          if (data == "enter")
-            TabItems.saveAllThumbnails({synchronously: true});
         }
       } else if (topic == "private-browsing-transition-complete") {
         // We use .transitionMode here, as aData is empty.
         if (self._privateBrowsing.transitionMode == "exit" &&
             self._privateBrowsing.wasInTabView)
           self.showTabView(false);
 
         self._privateBrowsing.transitionMode = "";