Backed out 4 changesets (bug 698371) for e10s bc3 bustage
authorWes Kocher <wkocher@mozilla.com>
Fri, 17 Oct 2014 18:33:34 -0700
changeset 210917 5e9facc28af97e0f98e616c6a0856b621180fc98
parent 210916 d8d5e2acfb7688fed3f70b9448ec74c90037cbde
child 210918 37e5f630049bf37946698295a681b9d6a9a7140e
push id9442
push userkwierso@gmail.com
push dateSat, 18 Oct 2014 01:33:44 +0000
treeherderfx-team@5e9facc28af9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs698371
milestone36.0a1
backs out6c6d420a3acc312920562e4fa8133d4f93fdfec2
81378dee5a62a3c03006823121fda71fc21b12d4
d004bfd7f706abd404ec50881dfc388b312258e8
17bbdeffa8a63c894023012e61cd85508059c398
Backed out 4 changesets (bug 698371) for e10s bc3 bustage Backed out changeset 6c6d420a3acc (bug 698371) Backed out changeset 81378dee5a62 (bug 698371) Backed out changeset d004bfd7f706 (bug 698371) Backed out changeset 17bbdeffa8a6 (bug 698371)
browser/base/content/browser-tabPreviews.js
browser/base/content/browser-thumbnails.js
browser/base/content/socialchat.xml
browser/base/content/tabbrowser.xml
browser/components/tabview/tabitems.js
toolkit/components/thumbnails/PageThumbUtils.jsm
toolkit/components/thumbnails/PageThumbs.jsm
toolkit/components/thumbnails/content/backgroundPageThumbsContent.js
toolkit/components/thumbnails/moz.build
toolkit/components/thumbnails/test/browser.ini
toolkit/components/thumbnails/test/head.js
toolkit/content/browser-child.js
--- a/browser/base/content/browser-tabPreviews.js
+++ b/browser/base/content/browser-tabPreviews.js
@@ -42,16 +42,20 @@ var tabPreviews = {
       img.src = PageThumbs.getThumbnailURL(uri);
       return img;
     }
 
     return this.capture(aTab, !aTab.hasAttribute("busy"));
   },
 
   capture: function tabPreviews_capture(aTab, aShouldCache) {
+    // Bug 863512 - Make page thumbnails work in electrolysis
+    if (gMultiProcessBrowser)
+      return new Image();
+
     let browser = aTab.linkedBrowser;
     let uri = browser.currentURI.spec;
 
     // FIXME: The gBrowserThumbnails._shouldCapture determines whether
     //        thumbnails should be written to disk. This should somehow be part
     //        of the PageThumbs API. (bug 1062414)
     if (aShouldCache &&
         gBrowserThumbnails._shouldCapture(browser)) {
@@ -68,17 +72,17 @@ var tabPreviews = {
 
     let canvas = PageThumbs.createCanvas(window);
 
     if (aShouldCache) {
       aTab.__thumbnail = canvas;
       aTab.__thumbnail_lastURI = uri;
     }
 
-    PageThumbs.captureToCanvas(browser, canvas);
+    PageThumbs.captureToCanvas(aTab.linkedBrowser.contentWindow, canvas);
     return canvas;
   },
 
   handleEvent: function tabPreviews_handleEvent(event) {
     switch (event.type) {
       case "TabSelect":
         if (this._selectedTab &&
             this._selectedTab.parentNode &&
--- a/browser/base/content/browser-thumbnails.js
+++ b/browser/base/content/browser-thumbnails.js
@@ -28,31 +28,39 @@ let gBrowserThumbnails = {
   _timeouts: null,
 
   /**
    * List of tab events we want to listen for.
    */
   _tabEvents: ["TabClose", "TabSelect"],
 
   init: function Thumbnails_init() {
+    // Bug 863512 - Make page thumbnails work in electrolysis
+    if (gMultiProcessBrowser)
+      return;
+
     PageThumbs.addExpirationFilter(this);
     gBrowser.addTabsProgressListener(this);
     Services.prefs.addObserver(this.PREF_DISK_CACHE_SSL, this, false);
 
     this._sslDiskCacheEnabled =
       Services.prefs.getBoolPref(this.PREF_DISK_CACHE_SSL);
 
     this._tabEvents.forEach(function (aEvent) {
       gBrowser.tabContainer.addEventListener(aEvent, this, false);
     }, this);
 
     this._timeouts = new WeakMap();
   },
 
   uninit: function Thumbnails_uninit() {
+    // Bug 863512 - Make page thumbnails work in electrolysis
+    if (gMultiProcessBrowser)
+      return;
+
     PageThumbs.removeExpirationFilter(this);
     gBrowser.removeTabsProgressListener(this);
     Services.prefs.removeObserver(this.PREF_DISK_CACHE_SSL, this);
 
     this._tabEvents.forEach(function (aEvent) {
       gBrowser.tabContainer.removeEventListener(aEvent, this, false);
     }, this);
   },
@@ -112,41 +120,41 @@ let gBrowserThumbnails = {
       this._capture(aBrowser);
     }.bind(this), this._captureDelayMS);
 
     this._timeouts.set(aBrowser, timeout);
   },
 
   // FIXME: This should be part of the PageThumbs API. (bug 1062414)
   _shouldCapture: function Thumbnails_shouldCapture(aBrowser) {
+    // Don't try to capture in e10s yet (because of bug 698371)
+    if (gMultiProcessBrowser)
+      return false;
+
     // Capture only if it's the currently selected tab.
     if (aBrowser != gBrowser.selectedBrowser)
       return false;
 
     // Don't capture in per-window private browsing mode.
     if (PrivateBrowsingUtils.isWindowPrivate(window))
       return false;
 
     let doc = aBrowser.contentDocument;
 
     // FIXME Bug 720575 - Don't capture thumbnails for SVG or XML documents as
     //       that currently regresses Talos SVG tests.
     if (doc instanceof SVGDocument || doc instanceof XMLDocument)
       return false;
 
-    // Don't take screenshots of about: pages.
-    if (aBrowser.currentURI.schemeIs("about"))
+    // There's no point in taking screenshot of loading pages.
+    if (aBrowser.docShell.busyFlags != Ci.nsIDocShell.BUSY_FLAGS_NONE)
       return false;
 
-    // FIXME e10s work around, we need channel information. bug 1073957
-    if (!aBrowser.docShell)
-      return true;
-
-    // There's no point in taking screenshot of loading pages.
-    if (aBrowser.docShell.busyFlags != Ci.nsIDocShell.BUSY_FLAGS_NONE)
+    // Don't take screenshots of about: pages.
+    if (aBrowser.currentURI.schemeIs("about"))
       return false;
 
     let channel = aBrowser.docShell.currentDocumentChannel;
 
     // No valid document channel. We shouldn't take a screenshot.
     if (!channel)
       return false;
 
--- a/browser/base/content/socialchat.xml
+++ b/browser/base/content/socialchat.xml
@@ -702,17 +702,17 @@
         // canvas size (in CSS pixels) to the window's backing resolution in order
         // to get a full-resolution drag image for use on HiDPI displays.
         let windowUtils = window.getInterface(Ci.nsIDOMWindowUtils);
         let scale = windowUtils.screenPixelsPerCSSPixel / windowUtils.fullZoom;
         let canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
         canvas.mozOpaque = true;
         canvas.width = 160 * scale;
         canvas.height = 90 * scale;
-        PageThumbs.captureToCanvas(chatbox, canvas);
+        PageThumbs.captureToCanvas(chatbox.contentWindow, canvas);
         dt.setDragImage(canvas, -16 * scale, -16 * scale);
 
         event.stopPropagation();
       ]]></handler>
 
       <handler event="dragend"><![CDATA[
         let dt = event.dataTransfer;
         let draggedChat = dt.mozGetDataAt("application/x-moz-chatbox", 0);
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -4525,17 +4525,17 @@
         let windowUtils = window.getInterface(Ci.nsIDOMWindowUtils);
         let scale = windowUtils.screenPixelsPerCSSPixel / windowUtils.fullZoom;
         let canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
         canvas.mozOpaque = true;
         canvas.width = 160 * scale;
         canvas.height = 90 * scale;
         if (!gMultiProcessBrowser) {
           // Bug 863512 - Make page thumbnails work in e10s
-          PageThumbs.captureToCanvas(browser, canvas);
+          PageThumbs.captureToCanvas(browser.contentWindow, canvas);
         }
         dt.setDragImage(canvas, -16 * scale, -16 * scale);
 
         // _dragData.offsetX/Y give the coordinates that the mouse should be
         // positioned relative to the corner of the new window created upon
         // dragend such that the mouse appears to have the same position
         // relative to the corner of the dragged tab.
         function clientX(ele) ele.getBoundingClientRect().left;
--- a/browser/components/tabview/tabitems.js
+++ b/browser/components/tabview/tabitems.js
@@ -1378,19 +1378,25 @@ TabCanvas.prototype = Utils.extend(new S
   // ----------
   // Function: paint
   paint: function TabCanvas_paint(evt) {
     var w = this.canvas.width;
     var h = this.canvas.height;
     if (!w || !h)
       return;
 
-    gPageThumbnails.captureToCanvas(this.tab.linkedBrowser, this.canvas, () => {
-      this._sendToSubscribers("painted");
-    });
+    if (!this.tab.linkedBrowser.contentWindow) {
+      Utils.log('no tab.linkedBrowser.contentWindow in TabCanvas.paint()');
+      return;
+    }
+
+    let win = this.tab.linkedBrowser.contentWindow;
+    gPageThumbnails.captureToCanvas(win, this.canvas);
+
+    this._sendToSubscribers("painted");
   },
 
   // ----------
   // Function: toImageData
   toImageData: function TabCanvas_toImageData() {
     return this.canvas.toDataURL("image/png");
   }
 });
deleted file mode 100644
--- a/toolkit/components/thumbnails/PageThumbUtils.jsm
+++ /dev/null
@@ -1,105 +0,0 @@
-/* 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/. */
-
-/*
- * Common thumbnailing routines used by various consumers, including
- * PageThumbs and backgroundPageThumbsContent.
- */
-
-this.EXPORTED_SYMBOLS = ["PageThumbUtils"];
-
-const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/Promise.jsm", this);
-
-this.PageThumbUtils = {
-  // The default background color for page thumbnails.
-  THUMBNAIL_BG_COLOR: "#fff",
-  // The namespace for thumbnail canvas elements.
-  HTML_NAMESPACE: "http://www.w3.org/1999/xhtml",
-
-  /**
-   * Creates a new canvas element in the context of aWindow, or if aWindow
-   * is undefined, in the context of hiddenDOMWindow.
-   *
-   * @param aWindow (optional) The document of this window will be used to
-   *  create the canvas.  If not given, the hidden window will be used.
-   * @return The newly created canvas.
-   */
-  createCanvas: function (aWindow) {
-    let doc = (aWindow || Services.appShell.hiddenDOMWindow).document;
-    let canvas = doc.createElementNS(this.HTML_NAMESPACE, "canvas");
-    canvas.mozOpaque = true;
-    canvas.mozImageSmoothingEnabled = true;
-    let [thumbnailWidth, thumbnailHeight] = this.getThumbnailSize();
-    canvas.width = thumbnailWidth;
-    canvas.height = thumbnailHeight;
-    return canvas;
-  },
-
-  /**
-   * Calculates a preferred initial thumbnail size based on current desktop
-   * dimensions. The resulting dims will generally be about 1/3 the
-   * size of the desktop. (jimm: why??)
-   *
-   * @return The calculated thumbnail size or a default if unable to calculate.
-   */
-  getThumbnailSize: function () {
-    if (!this._thumbnailWidth || !this._thumbnailHeight) {
-      let screenManager = Cc["@mozilla.org/gfx/screenmanager;1"]
-                            .getService(Ci.nsIScreenManager);
-      let left = {}, top = {}, width = {}, height = {};
-      screenManager.primaryScreen.GetRectDisplayPix(left, top, width, height);
-      this._thumbnailWidth = Math.round(width.value / 3);
-      this._thumbnailHeight = Math.round(height.value / 3);
-    }
-    return [this._thumbnailWidth, this._thumbnailHeight];
-  },
-
-  /**
-   * Determine a good thumbnail crop size and scale for a given content
-   * window.
-   *
-   * @param aWindow The content window.
-   * @param aCanvas The target canvas.
-   * @return An array containing width, height and scale.
-   */
-  determineCropSize: function (aWindow, aCanvas) {
-    if (Cu.isCrossProcessWrapper(aWindow)) {
-      throw new Error('Do not pass cpows here.');
-    }
-    let utils = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                       .getInterface(Ci.nsIDOMWindowUtils);
-    // aWindow may be a cpow, add exposed props security values.
-    let sbWidth = {}, sbHeight = {};
-
-    try {
-      utils.getScrollbarSize(false, sbWidth, sbHeight);
-    } catch (e) {
-      // This might fail if the window does not have a presShell.
-      Cu.reportError("Unable to get scrollbar size in determineCropSize.");
-      sbWidth.value = sbHeight.value = 0;
-    }
-
-    // Even in RTL mode, scrollbars are always on the right.
-    // So there's no need to determine a left offset.
-    let width = aWindow.innerWidth - sbWidth.value;
-    let height = aWindow.innerHeight - sbHeight.value;
-
-    let {width: thumbnailWidth, height: thumbnailHeight} = aCanvas;
-    let scale = Math.min(Math.max(thumbnailWidth / width, thumbnailHeight / height), 1);
-    let scaledWidth = width * scale;
-    let scaledHeight = height * scale;
-
-    if (scaledHeight > thumbnailHeight)
-      height -= Math.floor(Math.abs(scaledHeight - thumbnailHeight) * scale);
-
-    if (scaledWidth > thumbnailWidth)
-      width -= Math.floor(Math.abs(scaledWidth - thumbnailWidth) * scale);
-
-    return [width, height, scale];
-  }
-};
--- a/toolkit/components/thumbnails/PageThumbs.jsm
+++ b/toolkit/components/thumbnails/PageThumbs.jsm
@@ -5,34 +5,38 @@
 "use strict";
 
 this.EXPORTED_SYMBOLS = ["PageThumbs", "PageThumbsStorage"];
 
 const Cu = Components.utils;
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 
+const HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
 const PREF_STORAGE_VERSION = "browser.pagethumbnails.storage_version";
 const LATEST_STORAGE_VERSION = 3;
 
 const EXPIRATION_MIN_CHUNK_SIZE = 50;
 const EXPIRATION_INTERVAL_SECS = 3600;
 
-var gRemoteThumbId = 0;
-
 // If a request for a thumbnail comes in and we find one that is "stale"
 // (or don't find one at all) we automatically queue a request to generate a
 // new one.
 const MAX_THUMBNAIL_AGE_SECS = 172800; // 2 days == 60*60*24*2 == 172800 secs.
 
 /**
  * Name of the directory in the profile that contains the thumbnails.
  */
 const THUMBNAIL_DIRECTORY = "thumbnails";
 
+/**
+ * The default background color for page thumbnails.
+ */
+const THUMBNAIL_BG_COLOR = "#fff";
+
 Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
 Cu.import("resource://gre/modules/PromiseWorker.jsm", this);
 Cu.import("resource://gre/modules/Promise.jsm", this);
 Cu.import("resource://gre/modules/osfile.jsm", this);
 
 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
   "resource://gre/modules/NetUtil.jsm");
 
@@ -60,18 +64,16 @@ XPCOMUtils.defineLazyGetter(this, "gUnic
 });
 
 XPCOMUtils.defineLazyModuleGetter(this, "Task",
   "resource://gre/modules/Task.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Deprecated",
   "resource://gre/modules/Deprecated.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "AsyncShutdown",
   "resource://gre/modules/AsyncShutdown.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "PageThumbUtils",
-  "resource://gre/modules/PageThumbUtils.jsm");
 
 /**
  * Utilities for dealing with promises and Task.jsm
  */
 const TaskUtils = {
   /**
    * Read the bytes from a blob, asynchronously.
    *
@@ -161,195 +163,123 @@ this.PageThumbs = {
     * @param aUrl The web page's url.
     * @return The path of the thumbnail file.
     */
    getThumbnailPath: function PageThumbs_getThumbnailPath(aUrl) {
      return PageThumbsStorage.getFilePathForURL(aUrl);
    },
 
   /**
-   * Asynchronously returns a thumbnail as a blob for the given
-   * window.
+   * Captures a thumbnail for the given window.
+   * @param aWindow The DOM window to capture a thumbnail from.
+   * @param aCallback The function to be called when the thumbnail has been
+   *                  captured. The first argument will be the data stream
+   *                  containing the image data.
+   */
+  capture: function PageThumbs_capture(aWindow, aCallback) {
+    if (!this._prefEnabled()) {
+      return;
+    }
+
+    let canvas = this.createCanvas();
+    this.captureToCanvas(aWindow, canvas);
+
+    // Fetch the canvas data on the next event loop tick so that we allow
+    // some event processing in between drawing to the canvas and encoding
+    // its data. We want to block the UI as short as possible. See bug 744100.
+    Services.tm.currentThread.dispatch(function () {
+      canvas.mozFetchAsStream(aCallback, this.contentType);
+    }.bind(this), Ci.nsIThread.DISPATCH_NORMAL);
+  },
+
+
+  /**
+   * Captures a thumbnail for the given window.
    *
-   * @param aBrowser The <browser> to capture a thumbnail from.
+   * @param aWindow The DOM window to capture a thumbnail from.
    * @return {Promise}
    * @resolve {Blob} The thumbnail, as a Blob.
    */
-  captureToBlob: function PageThumbs_captureToBlob(aBrowser) {
+  captureToBlob: function PageThumbs_captureToBlob(aWindow) {
     if (!this._prefEnabled()) {
       return null;
     }
 
-    let deferred = Promise.defer();
+    let canvas = this.createCanvas();
+    this.captureToCanvas(aWindow, canvas);
 
-    let canvas = this.createCanvas();
-    this.captureToCanvas(aBrowser, canvas, () => {
-      canvas.toBlob(blob => {
-        deferred.resolve(blob, this.contentType);
-      });
+    let deferred = Promise.defer();
+    let type = this.contentType;
+    // Fetch the canvas data on the next event loop tick so that we allow
+    // some event processing in between drawing to the canvas and encoding
+    // its data. We want to block the UI as short as possible. See bug 744100.
+    canvas.toBlob(function asBlob(blob) {
+      deferred.resolve(blob, type);
     });
-
     return deferred.promise;
   },
 
   /**
    * Captures a thumbnail from a given window and draws it to the given canvas.
-   * Note, when dealing with remote content, this api draws into the passed
-   * canvas asynchronously. Pass aCallback to receive an async callback after
-   * canvas painting has completed.
-   * @param aBrowser The browser to capture a thumbnail from.
+   * @param aWindow The DOM window to capture a thumbnail from.
    * @param aCanvas The canvas to draw to.
-   * @param aCallback (optional) A callback invoked once the thumbnail has been
-   * rendered to aCanvas.
    */
-  captureToCanvas: function PageThumbs_captureToCanvas(aBrowser, aCanvas, aCallback) {
+  captureToCanvas: function PageThumbs_captureToCanvas(aWindow, aCanvas) {
     let telemetryCaptureTime = new Date();
-    this._captureToCanvas(aBrowser, aCanvas, function () {
-      Services.telemetry
-              .getHistogramById("FX_THUMBNAILS_CAPTURE_TIME_MS")
-              .add(new Date() - telemetryCaptureTime);
-      if (aCallback) {
-        aCallback(aCanvas);
-      }
-    });
+    this._captureToCanvas(aWindow, aCanvas);
+    let telemetry = Services.telemetry;
+    telemetry.getHistogramById("FX_THUMBNAILS_CAPTURE_TIME_MS")
+      .add(new Date() - telemetryCaptureTime);
   },
 
   // The background thumbnail service captures to canvas but doesn't want to
   // participate in this service's telemetry, which is why this method exists.
-  _captureToCanvas: function (aBrowser, aCanvas, aCallback) {
-    if (aBrowser.isRemoteBrowser) {
-      Task.spawn(function () {
-        let data =
-          yield this._captureRemoteThumbnail(aBrowser, aCanvas);
-        let canvas = data.thumbnail;
-        let ctx = canvas.getContext("2d");
-        let imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
-        aCanvas.getContext("2d").putImageData(imgData, 0, 0);
-        if (aCallback) {
-          aCallback(aCanvas);
-        }
-      }.bind(this));
-      return;
-    }
-
-    // Generate in-process content thumbnail
-    let [width, height, scale] =
-      PageThumbUtils.determineCropSize(aBrowser.contentWindow, aCanvas);
+  _captureToCanvas: function PageThumbs__captureToCanvas(aWindow, aCanvas) {
+    let [sw, sh, scale] = this._determineCropSize(aWindow, aCanvas);
     let ctx = aCanvas.getContext("2d");
 
     // Scale the canvas accordingly.
     ctx.save();
     ctx.scale(scale, scale);
 
     try {
       // Draw the window contents to the canvas.
-      ctx.drawWindow(aBrowser.contentWindow, 0, 0, width, height,
-                     PageThumbUtils.THUMBNAIL_BG_COLOR,
+      ctx.drawWindow(aWindow, 0, 0, sw, sh, THUMBNAIL_BG_COLOR,
                      ctx.DRAWWINDOW_DO_NOT_FLUSH);
     } catch (e) {
       // We couldn't draw to the canvas for some reason.
     }
+
     ctx.restore();
-
-    if (aCallback) {
-      aCallback(aCanvas);
-    }
   },
 
   /**
-   * Asynchrnously render an appropriately scaled thumbnail to canvas.
-   *
-   * @param aBrowser The browser to capture a thumbnail from.
-   * @param aCanvas The canvas to draw to.
-   * @return a promise
-   */
-  _captureRemoteThumbnail: function (aBrowser, aCanvas) {
-    let deferred = Promise.defer();
-
-    // The index we send with the request so we can identify the
-    // correct response.
-    let index = gRemoteThumbId++;
-
-    // Thumbnail request response handler
-    let mm = aBrowser.messageManager;
-
-    // Browser:Thumbnail:Response handler
-    let thumbFunc = function (aMsg) {
-      // Ignore events unrelated to our request
-      if (aMsg.data.id != index) {
-        return;
-      }
-
-      mm.removeMessageListener("Browser:Thumbnail:Response", thumbFunc);
-      let imageBlob = aMsg.data.thumbnail;
-      let doc = aBrowser.parentElement.ownerDocument;
-      let reader = Cc["@mozilla.org/files/filereader;1"].
-                   createInstance(Ci.nsIDOMFileReader);
-      reader.addEventListener("loadend", function() {
-        let image = doc.createElementNS(PageThumbUtils.HTML_NAMESPACE, "img");
-        image.onload = function () {
-          let thumbnail = doc.createElementNS(PageThumbUtils.HTML_NAMESPACE, "canvas");
-          thumbnail.width = image.naturalWidth;
-          thumbnail.height = image.naturalHeight;
-          let ctx = thumbnail.getContext("2d");
-          ctx.drawImage(image, 0, 0);
-          deferred.resolve({
-            thumbnail: thumbnail
-          });
-        }
-        image.src = reader.result;
-      });
-      // xxx wish there was a way to skip this encoding step
-      reader.readAsDataURL(imageBlob);
-    }
-
-    // Send a thumbnail request
-    mm.addMessageListener("Browser:Thumbnail:Response", thumbFunc);
-    mm.sendAsyncMessage("Browser:Thumbnail:Request", {
-      canvasWidth: aCanvas.width,
-      canvasHeight: aCanvas.height,
-      background: PageThumbUtils.THUMBNAIL_BG_COLOR,
-      id: index
-    });
-
-    return deferred.promise;
-  },
-
-  /**
    * Captures a thumbnail for the given browser and stores it to the cache.
    * @param aBrowser The browser to capture a thumbnail for.
    * @param aCallback The function to be called when finished (optional).
    */
   captureAndStore: function PageThumbs_captureAndStore(aBrowser, aCallback) {
     if (!this._prefEnabled()) {
       return;
     }
 
     let url = aBrowser.currentURI.spec;
-    let originalURL;
-    let channelError = false;
+    let channel = aBrowser.docShell.currentDocumentChannel;
+    let originalURL = channel.originalURI.spec;
 
-    if (!aBrowser.isRemoteBrowser) {
-      let channel = aBrowser.docShell.currentDocumentChannel;
-      originalURL = channel.originalURI.spec;
-      // see if this was an error response.
-      channelError = this._isChannelErrorResponse(channel);
-    } else {
-      // We need channel info (bug 1073957)
-      originalURL = url;
-    }
+    // see if this was an error response.
+    let wasError = this._isChannelErrorResponse(channel);
 
     Task.spawn((function task() {
       let isSuccess = true;
       try {
-        let blob = yield this.captureToBlob(aBrowser);
+        let blob = yield this.captureToBlob(aBrowser.contentWindow);
         let buffer = yield TaskUtils.readBlob(blob);
-        yield this._store(originalURL, url, buffer, channelError);
-      } catch (ex) {
-        Components.utils.reportError("Exception thrown during thumbnail capture: '" + ex + "'");
+        yield this._store(originalURL, url, buffer, wasError);
+      } catch (_) {
         isSuccess = false;
       }
       if (aCallback) {
         aCallback(isSuccess);
       }
     }).bind(this));
   },
 
@@ -440,23 +370,84 @@ this.PageThumbs = {
    * Unregister an expiration filter.
    * @param aFilter A filter that was previously passed to addExpirationFilter.
    */
   removeExpirationFilter: function PageThumbs_removeExpirationFilter(aFilter) {
     PageThumbsExpiration.removeFilter(aFilter);
   },
 
   /**
+   * Determines the crop size for a given content window.
+   * @param aWindow The content window.
+   * @param aCanvas The target canvas.
+   * @return An array containing width, height and scale.
+   */
+  _determineCropSize: function PageThumbs_determineCropSize(aWindow, aCanvas) {
+    let utils = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                       .getInterface(Ci.nsIDOMWindowUtils);
+    let sbWidth = {}, sbHeight = {};
+
+    try {
+      utils.getScrollbarSize(false, sbWidth, sbHeight);
+    } catch (e) {
+      // This might fail if the window does not have a presShell.
+      Cu.reportError("Unable to get scrollbar size in _determineCropSize.");
+      sbWidth.value = sbHeight.value = 0;
+    }
+
+    // Even in RTL mode, scrollbars are always on the right.
+    // So there's no need to determine a left offset.
+    let sw = aWindow.innerWidth - sbWidth.value;
+    let sh = aWindow.innerHeight - sbHeight.value;
+
+    let {width: thumbnailWidth, height: thumbnailHeight} = aCanvas;
+    let scale = Math.min(Math.max(thumbnailWidth / sw, thumbnailHeight / sh), 1);
+    let scaledWidth = sw * scale;
+    let scaledHeight = sh * scale;
+
+    if (scaledHeight > thumbnailHeight)
+      sh -= Math.floor(Math.abs(scaledHeight - thumbnailHeight) * scale);
+
+    if (scaledWidth > thumbnailWidth)
+      sw -= Math.floor(Math.abs(scaledWidth - thumbnailWidth) * scale);
+
+    return [sw, sh, scale];
+  },
+
+  /**
    * Creates a new hidden canvas element.
    * @param aWindow The document of this window will be used to create the
    *                canvas.  If not given, the hidden window will be used.
    * @return The newly created canvas.
    */
   createCanvas: function PageThumbs_createCanvas(aWindow) {
-    return PageThumbUtils.createCanvas(aWindow);
+    let doc = (aWindow || Services.appShell.hiddenDOMWindow).document;
+    let canvas = doc.createElementNS(HTML_NAMESPACE, "canvas");
+    canvas.mozOpaque = true;
+    canvas.mozImageSmoothingEnabled = true;
+    let [thumbnailWidth, thumbnailHeight] = this._getThumbnailSize();
+    canvas.width = thumbnailWidth;
+    canvas.height = thumbnailHeight;
+    return canvas;
+  },
+
+  /**
+   * Calculates the thumbnail size based on current desktop's dimensions.
+   * @return The calculated thumbnail size or a default if unable to calculate.
+   */
+  _getThumbnailSize: function PageThumbs_getThumbnailSize() {
+    if (!this._thumbnailWidth || !this._thumbnailHeight) {
+      let screenManager = Cc["@mozilla.org/gfx/screenmanager;1"]
+                            .getService(Ci.nsIScreenManager);
+      let left = {}, top = {}, width = {}, height = {};
+      screenManager.primaryScreen.GetRectDisplayPix(left, top, width, height);
+      this._thumbnailWidth = Math.round(width.value / 3);
+      this._thumbnailHeight = Math.round(height.value / 3);
+    }
+    return [this._thumbnailWidth, this._thumbnailHeight];
   },
 
   /**
    * Given a channel, returns true if it should be considered an "error
    * response", false otherwise.
    */
   _isChannelErrorResponse: function(channel) {
     // No valid document channel sounds like an error to me!
--- a/toolkit/components/thumbnails/content/backgroundPageThumbsContent.js
+++ b/toolkit/components/thumbnails/content/backgroundPageThumbsContent.js
@@ -1,17 +1,19 @@
 /* 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/. */
 
+(function () { // bug 673569 workaround :(
+
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 Cu.importGlobalProperties(['Blob']);
 
-Cu.import("resource://gre/modules/PageThumbUtils.jsm");
+Cu.import("resource://gre/modules/PageThumbs.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 const STATE_LOADING = 1;
 const STATE_CAPTURING = 2;
 const STATE_CANCELED = 3;
 
 const backgroundPageThumbsContent = {
@@ -42,17 +44,17 @@ const backgroundPageThumbsContent = {
       addProgressListener(this, Ci.nsIWebProgress.NOTIFY_STATE_WINDOW);
   },
 
   observe: function (subj, topic, data) {
     // Arrange to prevent (most) popup dialogs for this window - popups done
     // in the parent (eg, auth) aren't prevented, but alert() etc are.
     // disableDialogs only works on the current inner window, so it has
     // to be called every page load, but before scripts run.
-    if (content && subj == content.document) {
+    if (subj == content.document) {
       content.
         QueryInterface(Ci.nsIInterfaceRequestor).
         getInterface(Ci.nsIDOMWindowUtils).
         disableDialogs();
     }
   },
 
   get _webNav() {
@@ -122,29 +124,19 @@ const backgroundPageThumbsContent = {
     }
   },
 
   _captureCurrentPage: function () {
     let capture = this._currentCapture;
     capture.finalURL = this._webNav.currentURI.spec;
     capture.pageLoadTime = new Date() - capture.pageLoadStartDate;
 
+    let canvas = PageThumbs.createCanvas(content);
     let canvasDrawDate = new Date();
-
-    let canvas = PageThumbUtils.createCanvas(content);
-    let [sw, sh, scale] = PageThumbUtils.determineCropSize(content, canvas);
-
-    let ctx = canvas.getContext("2d");
-    ctx.save();
-    ctx.scale(scale, scale);
-    ctx.drawWindow(content, 0, 0, sw, sh,
-                   PageThumbUtils.THUMBNAIL_BG_COLOR,
-                   ctx.DRAWWINDOW_DO_NOT_FLUSH);
-    ctx.restore();
-
+    PageThumbs._captureToCanvas(content, canvas);
     capture.canvasDrawTime = new Date() - canvasDrawDate;
 
     canvas.toBlob(blob => {
       capture.imageBlob = new Blob([blob]);
       // Load about:blank to finish the capture and wait for onStateChange.
       this._loadAboutBlank();
     });
   },
@@ -187,8 +179,10 @@ const backgroundPageThumbsContent = {
   QueryInterface: XPCOMUtils.generateQI([
     Ci.nsIWebProgressListener,
     Ci.nsISupportsWeakReference,
     Ci.nsIObserver,
   ]),
 };
 
 backgroundPageThumbsContent.init();
+
+})();
--- a/toolkit/components/thumbnails/moz.build
+++ b/toolkit/components/thumbnails/moz.build
@@ -10,12 +10,11 @@ EXTRA_COMPONENTS += [
     'BrowserPageThumbs.manifest',
     'PageThumbsProtocol.js',
 ]
 
 EXTRA_JS_MODULES += [
     'BackgroundPageThumbs.jsm',
     'PageThumbs.jsm',
     'PageThumbsWorker.js',
-    'PageThumbUtils.jsm',
 ]
 
 JAR_MANIFESTS += ['jar.mn']
--- a/toolkit/components/thumbnails/test/browser.ini
+++ b/toolkit/components/thumbnails/test/browser.ini
@@ -1,46 +1,40 @@
 [DEFAULT]
+skip-if = e10s # Bug 863512 - thumbnails are disabled with e10s enabled.
 support-files =
   background_red.html
   background_red_redirect.sjs
   background_red_scroll.html
   head.js
   privacy_cache_control.sjs
   thumbnails_background.sjs
   thumbnails_crash_content_helper.js
   thumbnails_update.sjs
 
 [browser_thumbnails_bg_bad_url.js]
 [browser_thumbnails_bg_crash_during_capture.js]
-skip-if = buildapp == 'mulet' || !crashreporter || e10s # crashing the remote thumbnailer crashes the remote test tab
+skip-if = buildapp == 'mulet' || !crashreporter
 [browser_thumbnails_bg_crash_while_idle.js]
-skip-if = buildapp == 'mulet' || !crashreporter || e10s
+skip-if = buildapp == 'mulet' || !crashreporter
 [browser_thumbnails_bg_basic.js]
 [browser_thumbnails_bg_queueing.js]
 [browser_thumbnails_bg_timeout.js]
 [browser_thumbnails_bg_redirect.js]
 [browser_thumbnails_bg_destroy_browser.js]
 [browser_thumbnails_bg_no_cookies_sent.js]
-skip-if = e10s # e10s cookie problems
 [browser_thumbnails_bg_no_cookies_stored.js]
 [browser_thumbnails_bg_no_auth_prompt.js]
 [browser_thumbnails_bg_no_alert.js]
 [browser_thumbnails_bg_no_duplicates.js]
 [browser_thumbnails_bg_captureIfMissing.js]
 [browser_thumbnails_bug726727.js]
 skip-if = buildapp == 'mulet'
 [browser_thumbnails_bug727765.js]
-skip-if = e10s # tries to open crypto/local file from the child
 [browser_thumbnails_bug818225.js]
-skip-if = (e10s && os == 'linux') # load event issues. see original bug for follow up.
 [browser_thumbnails_capture.js]
-skip-if = e10s # tries to call drawWindow with a remote browser.
 [browser_thumbnails_expiration.js]
 [browser_thumbnails_privacy.js]
-skip-if = e10s # nsSSLStatus has null mServerCert, bug 820466
 [browser_thumbnails_redirect.js]
-skip-if = e10s # bug 1050869
 [browser_thumbnails_storage.js]
 [browser_thumbnails_storage_migrate3.js]
 skip-if = buildapp == 'mulet'
 [browser_thumbnails_update.js]
-skip-if = e10s # tries to open crypto/local file from the child
--- a/toolkit/components/thumbnails/test/head.js
+++ b/toolkit/components/thumbnails/test/head.js
@@ -132,17 +132,16 @@ function captureAndCheckColor(aRed, aGre
       next();
     });
   });
 }
 
 /**
  * For a given URL, loads the corresponding thumbnail
  * to a canvas and passes its image data to the callback.
- * Note, not compat with e10s!
  * @param aURL The url associated with the thumbnail.
  * @param aCallback The function to pass the image data to.
  */
 function retrieveImageDataForURL(aURL, aCallback) {
   let width = 100, height = 100;
   let thumb = PageThumbs.getThumbnailURL(aURL, width, height);
   // create a tab with a chrome:// URL so it can host the thumbnail image.
   // Note that we tried creating the element directly in the top-level chrome
--- a/toolkit/content/browser-child.js
+++ b/toolkit/content/browser-child.js
@@ -6,19 +6,16 @@ let Cc = Components.classes;
 let Ci = Components.interfaces;
 let Cu = Components.utils;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
 Cu.import("resource://gre/modules/RemoteAddonsChild.jsm");
 Cu.import("resource://gre/modules/Timer.jsm");
 
-XPCOMUtils.defineLazyModuleGetter(this, "PageThumbUtils",
-  "resource://gre/modules/PageThumbUtils.jsm");
-
 #ifdef MOZ_CRASHREPORTER
 XPCOMUtils.defineLazyServiceGetter(this, "CrashReporter",
                                    "@mozilla.org/xre/app-info;1",
                                    "nsICrashReporter");
 #endif
 
 let FocusSyncHandler = {
   init: function() {
@@ -366,47 +363,16 @@ addEventListener("ZoomChangeUsingMouseWh
   sendAsyncMessage("ZoomChangeUsingMouseWheel", {});
 }, false);
 
 addMessageListener("UpdateCharacterSet", function (aMessage) {
   docShell.charset = aMessage.data.value;
   docShell.gatherCharsetMenuTelemetry();
 });
 
-/**
- * Remote thumbnail request handler for PageThumbs thumbnails.
- */
-addMessageListener("Browser:Thumbnail:Request", function (aMessage) {
-  let thumbnail = content.document.createElementNS(PageThumbUtils.HTML_NAMESPACE,
-                                                   "canvas");
-  thumbnail.mozOpaque = true;
-  thumbnail.mozImageSmoothingEnabled = true;
-
-  thumbnail.width = aMessage.data.canvasWidth;
-  thumbnail.height = aMessage.data.canvasHeight;
-
-  let [width, height, scale] =
-    PageThumbUtils.determineCropSize(content, thumbnail);
-
-  let ctx = thumbnail.getContext("2d");
-  ctx.save();
-  ctx.scale(scale, scale);
-  ctx.drawWindow(content, 0, 0, width, height,
-                 aMessage.data.background,
-                 ctx.DRAWWINDOW_DO_NOT_FLUSH);
-  ctx.restore();
-
-  thumbnail.toBlob(function (aBlob) {
-    sendAsyncMessage("Browser:Thumbnail:Response", {
-      thumbnail: aBlob,
-      id: aMessage.data.id
-    });
-  });
-});
-
 // The AddonsChild needs to be rooted so that it stays alive as long as
 // the tab.
 let AddonsChild;
 if (Services.appinfo.browserTabsRemoteAutostart) {
   // Currently, the addon shims are only supported when autostarting
   // with remote tabs.
   AddonsChild = RemoteAddonsChild.init(this);