Bug 1058237 - Ctrl-Tab previews should use the PageThumbs API. r=ttaubert
authorDão Gottwald <dao@mozilla.com>
Wed, 03 Sep 2014 19:14:01 +0200
changeset 203431 e4d0c551410ea750bb84599ac4b6eefc6dfa9cc5
parent 203430 8a5f6d1f48828680b069abbda59f5d4d0055a2df
child 203432 bfef88becbba6972b2a5c1f92afac2ab4a9f0d4c
child 203477 0df611ce4a5bcd0fef854cf511e8ebb5f7482bab
push id48665
push userryanvm@gmail.com
push dateWed, 03 Sep 2014 20:40:15 +0000
treeherdermozilla-inbound@0da762e6868a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersttaubert
bugs1058237
milestone35.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
Bug 1058237 - Ctrl-Tab previews should use the PageThumbs API. r=ttaubert
browser/base/content/browser-tabPreviews.js
browser/base/content/browser-thumbnails.js
browser/base/content/test/general/browser_tabopen_reflows.js
toolkit/components/thumbnails/PageThumbs.jsm
toolkit/components/thumbnails/content/backgroundPageThumbsContent.js
--- a/browser/base/content/browser-tabPreviews.js
+++ b/browser/base/content/browser-tabPreviews.js
@@ -5,35 +5,29 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 #endif
  */
 
 /**
  * Tab previews utility, produces thumbnails
  */
 var tabPreviews = {
-  aspectRatio: 0.5625, // 16:9
-
-  get width() {
-    delete this.width;
-    return this.width = Math.ceil(screen.availWidth / 5.75);
-  },
-
-  get height() {
-    delete this.height;
-    return this.height = Math.round(this.width * this.aspectRatio);
-  },
-
   init: function tabPreviews_init() {
     if (this._selectedTab)
       return;
     this._selectedTab = gBrowser.selectedTab;
 
     gBrowser.tabContainer.addEventListener("TabSelect", this, false);
     gBrowser.tabContainer.addEventListener("SSTabRestored", this, false);
+
+    let screenManager = Cc["@mozilla.org/gfx/screenmanager;1"]
+                          .getService(Ci.nsIScreenManager);
+    let left = {}, top = {}, width = {}, height = {};
+    screenManager.primaryScreen.GetRectDisplayPix(left, top, width, height);
+    this.aspectRatio = height.value / width.value;
   },
 
   get: function tabPreviews_get(aTab) {
     let uri = aTab.linkedBrowser.currentURI.spec;
 
     if (aTab.__thumbnail_lastURI &&
         aTab.__thumbnail_lastURI != uri) {
       aTab.__thumbnail = null;
@@ -47,41 +41,45 @@ var tabPreviews = {
       let img = new Image;
       img.src = PageThumbs.getThumbnailURL(uri);
       return img;
     }
 
     return this.capture(aTab, !aTab.hasAttribute("busy"));
   },
 
-  capture: function tabPreviews_capture(aTab, aStore) {
-    var thumbnail = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
-    thumbnail.mozOpaque = true;
-    thumbnail.height = this.height;
-    thumbnail.width = this.width;
-
-    // drawWindow doesn't yet work with e10s (bug 698371)
-    if (gMultiProcessBrowser)
-      return thumbnail;
+  capture: function tabPreviews_capture(aTab, aShouldCache) {
+    let browser = aTab.linkedBrowser;
+    let uri = browser.currentURI.spec;
 
-    var ctx = thumbnail.getContext("2d");
-    var win = aTab.linkedBrowser.contentWindow;
-    var snippetWidth = win.innerWidth * .6;
-    var scale = this.width / snippetWidth;
-    ctx.scale(scale, scale);
-    ctx.drawWindow(win, win.scrollX, win.scrollY,
-                   snippetWidth, snippetWidth * this.aspectRatio, "rgb(255,255,255)");
+    // 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)) {
+      let img = new Image;
 
-    if (aStore &&
-        aTab.linkedBrowser /* bug 795608: the tab may got removed while drawing the thumbnail */) {
-      aTab.__thumbnail = thumbnail;
-      aTab.__thumbnail_lastURI = aTab.linkedBrowser.currentURI.spec;
+      PageThumbs.captureAndStore(browser, function () {
+        img.src = PageThumbs.getThumbnailURL(uri);
+      });
+
+      aTab.__thumbnail = img;
+      aTab.__thumbnail_lastURI = uri;
+      return img;
     }
 
-    return thumbnail;
+    let canvas = PageThumbs.createCanvas(window);
+
+    if (aShouldCache) {
+      aTab.__thumbnail = canvas;
+      aTab.__thumbnail_lastURI = uri;
+    }
+
+    PageThumbs.captureToCanvas(aTab.linkedBrowser.contentWindow, canvas);
+    return canvas;
   },
 
   handleEvent: function tabPreviews_handleEvent(event) {
     switch (event.type) {
       case "TabSelect":
         if (this._selectedTab &&
             this._selectedTab.parentNode &&
             !this._pendingUpdate) {
@@ -177,18 +175,17 @@ var ctrlTab = {
   },
   _selectedIndex: 0,
   get selected () this._selectedIndex < 0 ?
                     document.activeElement :
                     this.previews.item(this._selectedIndex),
   get isOpen   () this.panel.state == "open" || this.panel.state == "showing" || this._timer,
   get tabCount () this.tabList.length,
   get tabPreviewCount () Math.min(this.previews.length - 1, this.tabCount),
-  get canvasWidth () Math.min(tabPreviews.width,
-                              Math.ceil(screen.availWidth * .85 / this.tabPreviewCount)),
+  get canvasWidth () Math.ceil(screen.availWidth * .85 / this.tabPreviewCount),
   get canvasHeight () Math.round(this.canvasWidth * tabPreviews.aspectRatio),
 
   get tabList () {
     return this._recentlyUsedTabs;
   },
 
   init: function ctrlTab_init() {
     if (!this._recentlyUsedTabs) {
@@ -500,16 +497,25 @@ var ctrlTab = {
         break;
       case "popupshowing":
         if (event.target.id == "menu_viewPopup")
           document.getElementById("menu_showAllTabs").hidden = !allTabs.canOpen;
         break;
     }
   },
 
+  filterForThumbnailExpiration: function (aCallback) {
+    let urls = [];
+    let previewCount = this.tabPreviewCount;
+    for (let i = 0; i < previewCount; i++)
+      urls.push(this.tabList[i].linkedBrowser.currentURI.spec);
+
+    aCallback(urls);
+  },
+
   _initRecentlyUsedTabs: function () {
     this._recentlyUsedTabs =
       Array.filter(gBrowser.tabs, tab => !tab.closing)
            .sort((tab1, tab2) => tab2.lastAccessed - tab1.lastAccessed);
   },
 
   _init: function ctrlTab__init(enable) {
     var toggleEventListener = enable ? "addEventListener" : "removeEventListener";
@@ -520,16 +526,21 @@ var ctrlTab = {
     tabContainer[toggleEventListener]("TabOpen", this, false);
     tabContainer[toggleEventListener]("TabAttrModified", this, false);
     tabContainer[toggleEventListener]("TabSelect", this, false);
     tabContainer[toggleEventListener]("TabClose", this, false);
 
     document[toggleEventListener]("keypress", this, false);
     gBrowser.mTabBox.handleCtrlTab = !enable;
 
+    if (enable)
+      PageThumbs.addExpirationFilter(this);
+    else
+      PageThumbs.removeExpirationFilter(this);
+
     // If we're not running, hide the "Show All Tabs" menu item,
     // as Shift+Ctrl+Tab will be handled by the tab bar.
     document.getElementById("menu_showAllTabs").hidden = !enable;
     document.getElementById("menu_viewPopup")[toggleEventListener]("popupshowing", this);
 
     // Also disable the <key> to ensure Shift+Ctrl+Tab never triggers
     // Show All Tabs.
     var key_showAllTabs = document.getElementById("key_showAllTabs");
--- a/browser/base/content/browser-thumbnails.js
+++ b/browser/base/content/browser-thumbnails.js
@@ -98,17 +98,19 @@ let gBrowserThumbnails = {
   onStateChange: function Thumbnails_onStateChange(aBrowser, aWebProgress,
                                                    aRequest, aStateFlags, aStatus) {
     if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
         aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK)
       this._delayedCapture(aBrowser);
   },
 
   _capture: function Thumbnails_capture(aBrowser) {
-    if (this._shouldCapture(aBrowser))
+    // Only capture about:newtab top sites.
+    if (this._topSiteURLs.indexOf(aBrowser.currentURI.spec) >= 0 &&
+        this._shouldCapture(aBrowser))
       PageThumbs.captureAndStoreIfStale(aBrowser);
   },
 
   _delayedCapture: function Thumbnails_delayedCapture(aBrowser) {
     if (this._timeouts.has(aBrowser))
       clearTimeout(this._timeouts.get(aBrowser));
     else
       aBrowser.addEventListener("scroll", this, true);
@@ -116,25 +118,22 @@ let gBrowserThumbnails = {
     let timeout = setTimeout(function () {
       this._clearTimeout(aBrowser);
       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) {
     // Capture only if it's the currently selected tab.
     if (aBrowser != gBrowser.selectedBrowser)
       return false;
 
-    // Only capture about:newtab top sites.
-    if (this._topSiteURLs.indexOf(aBrowser.currentURI.spec) < 0)
-      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.
--- a/browser/base/content/test/general/browser_tabopen_reflows.js
+++ b/browser/base/content/test/general/browser_tabopen_reflows.js
@@ -44,24 +44,16 @@ const EXPECTED_REFLOWS = [
     "TabItems_link@chrome://browser/content/tabview.js|" +
     "TabItems_init/this._eventListeners.open@chrome://browser/content/tabview.js|",
 
   // SessionStore.getWindowDimensions()
   "ssi_getWindowDimension@resource:///modules/sessionstore/SessionStore.jsm|" +
     "ssi_updateWindowFeatures/<@resource:///modules/sessionstore/SessionStore.jsm|" +
     "ssi_updateWindowFeatures@resource:///modules/sessionstore/SessionStore.jsm|" +
     "ssi_collectWindowData@resource:///modules/sessionstore/SessionStore.jsm|",
-
-  // tabPreviews.capture()
-  "tabPreviews_capture@chrome://browser/content/browser.js|" +
-    "tabPreviews_handleEvent/<@chrome://browser/content/browser.js|",
-
-  // tabPreviews.capture()
-  "tabPreviews_capture@chrome://browser/content/browser.js|" +
-    "@chrome://browser/content/browser.js|"
 ];
 
 const PREF_PRELOAD = "browser.newtab.preload";
 const PREF_NEWTAB_DIRECTORYSOURCE = "browser.newtabpage.directory.source";
 
 /*
  * This test ensures that there are no unexpected
  * uninterruptible reflows when opening new tabs.
--- a/toolkit/components/thumbnails/PageThumbs.jsm
+++ b/toolkit/components/thumbnails/PageThumbs.jsm
@@ -174,17 +174,17 @@ this.PageThumbs = {
    *                  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();
+    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);
@@ -198,17 +198,17 @@ this.PageThumbs = {
    * @return {Promise}
    * @resolve {Blob} The thumbnail, as a Blob.
    */
   captureToBlob: function PageThumbs_captureToBlob(aWindow) {
     if (!this._prefEnabled()) {
       return null;
     }
 
-    let canvas = this._createCanvas();
+    let canvas = this.createCanvas();
     this.captureToCanvas(aWindow, canvas);
 
     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) {
@@ -413,17 +413,17 @@ this.PageThumbs = {
   },
 
   /**
    * 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) {
+  createCanvas: function PageThumbs_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;
--- a/toolkit/components/thumbnails/content/backgroundPageThumbsContent.js
+++ b/toolkit/components/thumbnails/content/backgroundPageThumbsContent.js
@@ -122,17 +122,17 @@ 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 canvas = PageThumbs.createCanvas(content);
     let canvasDrawDate = new Date();
     PageThumbs._captureToCanvas(content, canvas);
     capture.canvasDrawTime = new Date() - canvasDrawDate;
 
     canvas.toBlob(blob => {
       capture.imageBlob = blob;
       // Load about:blank to finish the capture and wait for onStateChange.
       this._loadAboutBlank();