Bug 705911 - [Page Thumbnails] Save screenshots of redirecting sites; r=dao
authorTim Taubert <tim.taubert@gmx.de>
Fri, 23 Mar 2012 19:12:43 +0100
changeset 93442 4173214920c3dab17c187375eb53b73e95eea3c6
parent 93441 5ff4f574298e76cb6dd5633acec5c16a7b7edfc8
child 93443 8bc3c696e0a7cdf4ad64e27c69b919fea253eeb1
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)
reviewersdao
bugs705911
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
Bug 705911 - [Page Thumbnails] Save screenshots of redirecting sites; r=dao
browser/components/thumbnails/PageThumbs.jsm
browser/components/thumbnails/test/Makefile.in
browser/components/thumbnails/test/background_red.html
browser/components/thumbnails/test/background_red_redirect.sjs
browser/components/thumbnails/test/browser_thumbnails_redirect.js
browser/components/thumbnails/test/head.js
--- a/browser/components/thumbnails/PageThumbs.jsm
+++ b/browser/components/thumbnails/PageThumbs.jsm
@@ -105,23 +105,40 @@ let PageThumbs = {
 
   /**
    * 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) {
     let url = aBrowser.currentURI.spec;
+    let channel = aBrowser.docShell.currentDocumentChannel;
+    let originalURL = channel.originalURI.spec;
+
     this.capture(aBrowser.contentWindow, function (aInputStream) {
       let telemetryStoreTime = new Date();
 
       function finish(aSuccessful) {
         if (aSuccessful) {
           Services.telemetry.getHistogramById("FX_THUMBNAILS_STORE_TIME_MS")
             .add(new Date() - telemetryStoreTime);
+
+          // We've been redirected. Create a copy of the current thumbnail for
+          // the redirect source. We need to do this because:
+          //
+          // 1) Users can drag any kind of links onto the newtab page. If those
+          //    links redirect to a different URL then we want to be able to
+          //    provide thumbnails for both of them.
+          //
+          // 2) The newtab page should actually display redirect targets, only.
+          //    Because of bug 559175 this information can get lost when using
+          //    Sync and therefore also redirect sources appear on the newtab
+          //    page. We also want thumbnails for those.
+          if (url != originalURL)
+            PageThumbsCache._copy(url, originalURL);
         }
 
         if (aCallback)
           aCallback(aSuccessful);
       }
 
       // Get a writeable cache entry.
       PageThumbsCache.getWriteEntry(url, function (aEntry) {
@@ -202,16 +219,64 @@ let PageThumbsCache = {
    * @param aCallback The callback that is called when the cache entry is ready.
    */
   getWriteEntry: function Cache_getWriteEntry(aKey, aCallback) {
     // Try to open the desired cache entry.
     this._openCacheEntry(aKey, Ci.nsICache.ACCESS_WRITE, aCallback);
   },
 
   /**
+   * Copies an existing cache entry's data to a new cache entry.
+   * @param aSourceKey The key that contains the data to copy.
+   * @param aTargetKey The key that will be the copy of aSourceKey's data.
+   */
+  _copy: function Cache_copy(aSourceKey, aTargetKey) {
+    let sourceEntry, targetEntry, waitingCount = 2;
+
+    function finish() {
+      if (sourceEntry)
+        sourceEntry.close();
+
+      if (targetEntry)
+        targetEntry.close();
+    }
+
+    function copyDataWhenReady() {
+      if (--waitingCount > 0)
+        return;
+
+      if (!sourceEntry || !targetEntry) {
+        finish();
+        return;
+      }
+
+      let inputStream = sourceEntry.openInputStream(0);
+      let outputStream = targetEntry.openOutputStream(0);
+
+      // Copy the image data to a new entry.
+      NetUtil.asyncCopy(inputStream, outputStream, function (aResult) {
+        if (Components.isSuccessCode(aResult))
+          targetEntry.markValid();
+
+        finish();
+      });
+    }
+
+    this.getReadEntry(aSourceKey, function (aSourceEntry) {
+      sourceEntry = aSourceEntry;
+      copyDataWhenReady();
+    });
+
+    this.getWriteEntry(aTargetKey, function (aTargetEntry) {
+      targetEntry = aTargetEntry;
+      copyDataWhenReady();
+    });
+  },
+
+  /**
    * Opens the cache entry identified by the given key.
    * @param aKey The key identifying the desired cache entry.
    * @param aAccess The desired access mode (see nsICache.ACCESS_* constants).
    * @param aCallback The function to be called when the cache entry was opened.
    */
   _openCacheEntry: function Cache_openCacheEntry(aKey, aAccess, aCallback) {
     function onCacheEntryAvailable(aEntry, aAccessGranted, aStatus) {
       let validAccess = aAccess == aAccessGranted;
--- a/browser/components/thumbnails/test/Makefile.in
+++ b/browser/components/thumbnails/test/Makefile.in
@@ -8,14 +8,17 @@ srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir  = browser/components/thumbnails/test
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _BROWSER_FILES = \
 	browser_thumbnails_capture.js \
+	browser_thumbnails_redirect.js \
 	browser_thumbnails_bug726727.js \
 	head.js \
+	background_red.html \
+	background_red_redirect.sjs \
 	$(NULL)
 
 libs::	$(_BROWSER_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/browser/components/thumbnails/test/background_red.html
@@ -0,0 +1,3 @@
+<html>
+  <body bgcolor=ff0000></body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/components/thumbnails/test/background_red_redirect.sjs
@@ -0,0 +1,10 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function handleRequest(aRequest, aResponse) {
+ // Set HTTP Status.
+ aResponse.setStatusLine(aRequest.httpVersion, 301, "Moved Permanently");
+
+ // Set redirect URI.
+ aResponse.setHeader("Location", "http://mochi.test:8888/browser/browser/components/thumbnails/test/background_red.html");
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/thumbnails/test/browser_thumbnails_redirect.js
@@ -0,0 +1,51 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const URL = "http://mochi.test:8888/browser/browser/components/thumbnails/" +
+            "test/background_red_redirect.sjs";
+
+let cacheService = Cc["@mozilla.org/network/cache-service;1"]
+                   .getService(Ci.nsICacheService);
+
+/**
+ * These tests ensure that we save and provide thumbnails for redirecting sites.
+ */
+function runTests() {
+  // Kick off history by loading a tab first or the test fails in single mode.
+  yield addTab(URL);
+  gBrowser.removeTab(gBrowser.selectedTab);
+
+  // Create a tab, redirecting to a page with a red background.
+  yield addTab(URL);
+  yield captureAndCheckColor(255, 0, 0, "we have a red thumbnail");
+
+  // Wait until the referrer's thumbnail's cache entry has been written.
+  yield whenCacheEntryExists(URL);
+  yield checkThumbnailColor(URL, 255, 0, 0, "referrer has a red thumbnail");
+}
+
+function whenCacheEntryExists(aKey) {
+  let callback = next;
+
+  checkCacheEntryExists(aKey, function (aExists) {
+    if (!aExists)
+      callback = function () whenCacheEntryExists(aKey);
+
+    executeSoon(callback);
+  });
+}
+
+function checkCacheEntryExists(aKey, aCallback) {
+  PageThumbsCache.getReadEntry(aKey, function (aEntry) {
+    let inputStream = aEntry && aEntry.openInputStream(0);
+    let exists = inputStream && inputStream.available();
+
+    if (inputStream)
+      inputStream.close();
+
+    if (aEntry)
+      aEntry.close();
+
+    aCallback(exists);
+  });
+}
--- a/browser/components/thumbnails/test/head.js
+++ b/browser/components/thumbnails/test/head.js
@@ -93,36 +93,48 @@ function whenLoaded(aElement, aCallback)
  * @param aBlue The blue component's intensity.
  * @param aMessage The info message to print when comparing the pixel color.
  */
 function captureAndCheckColor(aRed, aGreen, aBlue, aMessage) {
   let browser = gBrowser.selectedBrowser;
 
   // Capture the screenshot.
   PageThumbs.captureAndStore(browser, function () {
-    let width = 100, height = 100;
-    let thumb = PageThumbs.getThumbnailURL(browser.currentURI.spec, width, height);
+    checkThumbnailColor(browser.currentURI.spec, aRed, aGreen, aBlue, aMessage);
+  });
+}
 
-    getXULDocument(function (aDocument) {
-      let htmlns = "http://www.w3.org/1999/xhtml";
-      let img = aDocument.createElementNS(htmlns, "img");
-      img.setAttribute("src", thumb);
+/**
+ * Retrieve a thumbnail from the cache and compare its pixel color values.
+ * @param aURL The URL of the thumbnail's page.
+ * @param aRed The red component's intensity.
+ * @param aGreen The green component's intensity.
+ * @param aBlue The blue component's intensity.
+ * @param aMessage The info message to print when comparing the pixel color.
+ */
+function checkThumbnailColor(aURL, aRed, aGreen, aBlue, aMessage) {
+  let width = 100, height = 100;
+  let thumb = PageThumbs.getThumbnailURL(aURL, width, height);
 
-      whenLoaded(img, function () {
-        let canvas = aDocument.createElementNS(htmlns, "canvas");
-        canvas.setAttribute("width", width);
-        canvas.setAttribute("height", height);
+  getXULDocument(function (aDocument) {
+    let htmlns = "http://www.w3.org/1999/xhtml";
+    let img = aDocument.createElementNS(htmlns, "img");
+    img.setAttribute("src", thumb);
 
-        // Draw the image to a canvas and compare the pixel color values.
-        let ctx = canvas.getContext("2d");
-        ctx.drawImage(img, 0, 0, width, height);
-        checkCanvasColor(ctx, aRed, aGreen, aBlue, aMessage);
+    whenLoaded(img, function () {
+      let canvas = aDocument.createElementNS(htmlns, "canvas");
+      canvas.setAttribute("width", width);
+      canvas.setAttribute("height", height);
 
-        next();
-      });
+      // Draw the image to a canvas and compare the pixel color values.
+      let ctx = canvas.getContext("2d");
+      ctx.drawImage(img, 0, 0, width, height);
+      checkCanvasColor(ctx, aRed, aGreen, aBlue, aMessage);
+
+      next();
     });
   });
 }
 
 /**
  * Passes a XUL document (created if necessary) to the given callback.
  * @param aCallback The function to be called when the XUL document has been
  *                  created. The first argument will be the document.