Merge services-central with mozilla-central
authorPhilipp von Weitershausen <philipp@weitershausen.de>
Wed, 28 Sep 2011 16:45:43 -0700
changeset 77757 cb4b93331e4f0052a35f305a959aaa23460c3a88
parent 77749 80b55a615ac9a8e222f8c4e95270ba914234919b (current diff)
parent 77756 f030249274c140f6d112deba7fcecee8277e8a89 (diff)
child 77798 9672c3995eea2e7552d23359a5a7378e202d2f9e
push id3
push userfelipc@gmail.com
push dateFri, 30 Sep 2011 20:09:13 +0000
milestone10.0a1
Merge services-central with mozilla-central
--- a/browser/base/content/syncAddDevice.js
+++ b/browser/base/content/syncAddDevice.js
@@ -56,16 +56,20 @@ let gSyncAddDevice = {
     this.pin3.setAttribute("maxlength", PIN_PART_LENGTH);
 
     this.nextFocusEl = {pin1: this.pin2,
                         pin2: this.pin3,
                         pin3: this.wizard.getButton("next")};
 
     this.throbber = document.getElementById("add-device-throbber");
     this.errorRow = document.getElementById("errorRow");
+
+    // Kick off a sync. That way the server will have the most recent data from
+    // this computer and it will show up immediately on the new device.
+    Weave.Utils.nextTick(Weave.Service.sync, Weave.Service);
   },
 
   onPageShow: function onPageShow() {
     this.wizard.getButton("back").hidden = true;
 
     switch (this.wizard.pageIndex) {
       case ADD_DEVICE_PAGE:
         this.onTextBoxInput();
--- a/services/sync/modules/policies.js
+++ b/services/sync/modules/policies.js
@@ -201,18 +201,23 @@ let SyncScheduler = {
         this._log.trace("Engine " + data + " applied " + numItems + " items.");
         if (numItems)
           this.hasIncomingItems = true;
         break;
       case "weave:service:setup-complete":
          Svc.Idle.addIdleObserver(this, Svc.Prefs.get("scheduler.idleTime"));
          break;
       case "weave:service:start-over":
-         Svc.Idle.removeIdleObserver(this, Svc.Prefs.get("scheduler.idleTime"));
          SyncScheduler.setDefaults();
+         try {
+           Svc.Idle.removeIdleObserver(this, Svc.Prefs.get("scheduler.idleTime"));
+         } catch (ex if (ex.result == Cr.NS_ERROR_FAILURE)) {
+           // In all likelihood we didn't have an idle observer registered yet.
+           // It's all good.
+         }
          break;
       case "idle":
         this._log.trace("We're idle.");
         this.idle = true;
         // Adjust the interval for future syncs. This won't actually have any
         // effect until the next pending sync (which will happen soon since we
         // were just active.)
         this.adjustSyncInterval();
--- a/services/sync/modules/resource.js
+++ b/services/sync/modules/resource.js
@@ -523,31 +523,71 @@ ChannelListener.prototype = {
     this._data = '';
     this.delayAbort();
   },
 
   onStopRequest: function Channel_onStopRequest(channel, context, status) {
     // Clear the abort timer now that the channel is done.
     this.abortTimer.clear();
 
-    let success = Components.isSuccessCode(status);
+    /**
+     * Shim to help investigate Bug 672878.
+     * We seem to be seeing a situation in which onStopRequest is being called
+     * with a success code, but no mResponseHead yet set (a failure condition).
+     * One possibility, according to bzbarsky, is that the channel status and
+     * the status argument don't match up. This block will log that situation.
+     *
+     * Fetching channel.status should not throw, but if it does requestStatus
+     * will be NS_ERROR_UNEXPECTED, which will cause this method to report
+     * failure.
+     */
+    let requestStatus = Cr.NS_ERROR_UNEXPECTED;
+    let statusSuccess = Components.isSuccessCode(status);
+    try {
+      // From nsIRequest.
+      requestStatus = channel.status;
+      this._log.trace("Request status is " + requestStatus);
+    } catch (ex) {
+      this._log.warn("Got exception " + Utils.exceptionStr(ex) +
+                     " fetching channel.status.");
+    }
+    if (statusSuccess && (status != requestStatus)) {
+      this._log.error("Request status " + requestStatus +
+                      " does not match status arg " + status);
+      try {
+        channel.responseStatus;
+      } catch (ex) {
+        this._log.error("... and we got " + Utils.exceptionStr(ex) +
+                        " retrieving responseStatus.");
+      }
+    }
+
+    let requestStatusSuccess = Components.isSuccessCode(requestStatus);
+
     let uri = channel && channel.URI && channel.URI.spec || "<unknown>";
-    this._log.trace("Channel for " + channel.requestMethod + " " +
-                    uri + ": isSuccessCode(" + status + ")? " +
-                    success);
+    this._log.trace("Channel for " + channel.requestMethod + " " + uri + ": " +
+                    "isSuccessCode(" + status + ")? " + statusSuccess + ", " +
+                    "isSuccessCode(" + requestStatus + ")? " +
+                    requestStatusSuccess);
 
-    if (this._data == '')
+    if (this._data == '') {
       this._data = null;
+    }
 
-    // Throw the failure code and stop execution.  Use Components.Exception()
+    // Check both the nsIRequest status and the status we were provided.
+    // If either is unsuccessful, don't take the success branch!
+    //
+    // Pass back the failure code and stop execution. Use Components.Exception()
     // instead of Error() so the exception is QI-able and can be passed across
     // XPCOM borders while preserving the status code.
-    if (!success) {
-      let message = Components.Exception("", status).name;
-      let error = Components.Exception(message, status);
+    if (!statusSuccess || !requestStatusSuccess) {
+      // Pick the code that caused us to fail.
+      let code    = statusSuccess ? requestStatus : status;
+      let message = Components.Exception("", code).name;
+      let error   = Components.Exception(message, code);
       this._onComplete(error);
       return;
     }
 
     this._log.trace("Channel: flags = " + channel.loadFlags +
                     ", URI = " + uri +
                     ", HTTP success? " + channel.requestSucceeded);
     this._onComplete(null, this._data);
--- a/services/sync/modules/rest.js
+++ b/services/sync/modules/rest.js
@@ -401,24 +401,60 @@ RESTRequest.prototype = {
 
     // We don't want to do anything for a request that's already been aborted.
     if (this.status == this.ABORTED) {
       this._log.trace("Not proceeding with onStopRequest, request was aborted.");
       return;
     }
     this.status = this.COMPLETED;
 
+    /**
+     * Shim to help investigate Bug 672878.
+     * We seem to be seeing a situation in which onStopRequest is being called
+     * with a success code, but no mResponseHead yet set (a failure condition).
+     * One possibility, according to bzbarsky, is that the channel status and
+     * the status argument don't match up. This block will log that situation.
+     *
+     * Fetching channel.status should not throw, but if it does requestStatus
+     * will be NS_ERROR_UNEXPECTED, which will cause this method to report
+     * failure.
+     *
+     * This code parallels nearly identical shimming in resource.js.
+     */
+    let requestStatus = Cr.NS_ERROR_UNEXPECTED;
+    let statusSuccess = Components.isSuccessCode(statusCode);
+    try {
+      // From nsIRequest.
+      requestStatus = channel.status;
+      this._log.trace("Request status is " + requestStatus);
+    } catch (ex) {
+      this._log.warn("Got exception " + Utils.exceptionStr(ex) +
+                     " fetching channel.status.");
+    }
+    if (statusSuccess && (statusCode != requestStatus)) {
+      this._log.error("Request status " + requestStatus +
+                      " does not match status arg " + statusCode);
+      try {
+        channel.responseStatus;
+      } catch (ex) {
+        this._log.error("... and we got " + Utils.exceptionStr(ex) +
+                        " retrieving responseStatus.");
+      }
+    }
+
+    let requestStatusSuccess = Components.isSuccessCode(requestStatus);
+
     let uri = channel && channel.URI && channel.URI.spec || "<unknown>";
     this._log.trace("Channel for " + channel.requestMethod + " " + uri +
                     " returned status code " + statusCode);
 
     // Throw the failure code and stop execution.  Use Components.Exception()
     // instead of Error() so the exception is QI-able and can be passed across
     // XPCOM borders while preserving the status code.
-    if (!Components.isSuccessCode(statusCode)) {
+    if (!statusSuccess || !requestStatusSuccess) {
       let message = Components.Exception("", statusCode).name;
       let error = Components.Exception(message, statusCode);
       this.onComplete(error);
       this.onComplete = this.onProgress = null;
       return;
     }
 
     this._log.debug(this.method + " " + uri + " " + this.response.status);
--- a/toolkit/components/places/tests/head_common.js
+++ b/toolkit/components/places/tests/head_common.js
@@ -128,40 +128,56 @@ function DBConn() {
       Services.obs.removeObserver(arguments.callee, aTopic);
       gDBConn.asyncClose();
     }, "profile-before-change", false);
   }
 
   return gDBConn.connectionReady ? gDBConn : null;
 };
 
+/**
+ * Reads data from the provided inputstream.
+ *
+ * @return an array of bytes.
+ */ 
+function readInputStreamData(aStream) {
+  let bistream = Cc["@mozilla.org/binaryinputstream;1"].
+                 createInstance(Ci.nsIBinaryInputStream);
+  try {
+    bistream.setInputStream(aStream);
+    let expectedData = [];
+    let avail;
+    while (avail = bistream.available()) {
+      expectedData = expectedData.concat(bistream.readByteArray(avail));
+    }
+    return expectedData;
+  } finally {
+    bistream.close();
+  }
+}
 
 /**
- * Reads the data from the specified nsIFile, and returns an array of bytes.
+ * Reads the data from the specified nsIFile.
  *
  * @param aFile
  *        The nsIFile to read from.
+ * @return an array of bytes.
  */
 function readFileData(aFile) {
   let inputStream = Cc["@mozilla.org/network/file-input-stream;1"].
                     createInstance(Ci.nsIFileInputStream);
   // init the stream as RD_ONLY, -1 == default permissions.
   inputStream.init(aFile, 0x01, -1, null);
-  let size = inputStream.available();
 
-  // use a binary input stream to grab the bytes.
-  let bis = Cc["@mozilla.org/binaryinputstream;1"].
-            createInstance(Ci.nsIBinaryInputStream);
-  bis.setInputStream(inputStream);
-
-  let bytes = bis.readByteArray(size);
-
-  if (size != bytes.length)
-      throw "Didn't read expected number of bytes";
-
+  // Check the returned size versus the expected size.
+  let size  = inputStream.available();
+  let bytes = readInputStreamData(inputStream);
+  if (size != bytes.length) {
+    throw "Didn't read expected number of bytes";
+  }
   return bytes;
 }
 
 
 /**
  * Compares two arrays, and returns true if they are equal.
  *
  * @param aArray1
--- a/toolkit/components/places/tests/unit/test_favicons.js
+++ b/toolkit/components/places/tests/unit/test_favicons.js
@@ -1,447 +1,350 @@
-/*
- * Tests for nsIFaviconService
- */
+/* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */
 
-/*
- * dumpToFile()
- *
- * For test development, dumps the specified array to a file.
- * Call |dumpToFile(outData);| in a test to file to a file.
- */
-function dumpToFile(aData) {
-  const path = "/tmp";
+// Tests for nsIFaviconService.
 
-  var outputFile = Cc["@mozilla.org/file/local;1"].
-                   createInstance(Ci.nsILocalFile);
-  outputFile.initWithPath(path);
-  outputFile.append("testdump.png");
-
-  var outputStream = Cc["@mozilla.org/network/file-output-stream;1"].
-                     createInstance(Ci.nsIFileOutputStream);
-  // WR_ONLY|CREAT|TRUNC
-  outputStream.init(outputFile, 0x02 | 0x08 | 0x20, 0644, null);
+// The pixel values we get in Windows are sometimes +/- 1 value compared to
+// other platforms, so we need to compare against a different set of reference
+// images.
+let isWindows = ("@mozilla.org/windows-registry-key;1" in Cc);
 
-  var bos = Cc["@mozilla.org/binaryoutputstream;1"].
-            createInstance(Ci.nsIBinaryOutputStream);
-  bos.setOutputStream(outputStream);
-
-  bos.writeByteArray(aData, aData.length);
-
-  outputStream.close();
-}
-
-/*
+/**
  * setAndGetFaviconData()
  *
  * Calls setFaviconData() with the specified image data,
- * and then retrieves it with getFaviconData(). Returns
- * and array of bytes and mimetype.
+ * and then retrieves it with getFaviconData().
+ *
+ * @return an array: [bytes, MIME type].
  */
 function setAndGetFaviconData(aFilename, aData, aMimeType) {
-  var iconURI = uri("http://places.test/" + aFilename);
+  let iconsvc = PlacesUtils.favicons;
+  let iconURI = NetUtil.newURI("http://places.test/" + aFilename);
   try {
-    iconsvc.setFaviconData(iconURI,
-                           aData, aData.length, aMimeType,
+    iconsvc.setFaviconData(iconURI, aData, aData.length, aMimeType,
                            Number.MAX_VALUE);
   } catch (ex) {}
-  var dataURL = iconsvc.getFaviconDataAsDataURL(iconURI);
+  let dataURL = iconsvc.getFaviconDataAsDataURL(iconURI);
   try {
     iconsvc.setFaviconDataFromDataURL(iconURI, dataURL, Number.MAX_VALUE);
   } catch (ex) {}
-  var mimeTypeOutparam = {};
+  let mimeTypeOutparam = {};
 
-  var outData = iconsvc.getFaviconData(iconURI, mimeTypeOutparam);
+  let outData = iconsvc.getFaviconData(iconURI, mimeTypeOutparam);
 
   return [outData, mimeTypeOutparam.value];
 }
 
-
-// Get favicon service
-try {
-  var iconsvc = PlacesUtils.favicons;
+/**
+ * Retrieve and read a file, verifying the length of the returned data.
+ *
+ * @return the array of bytes read from the file.
+ */
+function readFileOfLength(name, expected) {
+  let file = do_get_file(name);
+  let data = readFileData(file);
+  do_check_eq(data.length, expected);
+  return data;
+}
 
-  // Ugh, this is an ugly hack. The pixel values we get in Windows are sometimes
-  // +/- 1 value compared to other platforms, so we need to compare against a
-  // different set of reference images. nsIXULRuntime.OS doesn't seem to be
-  // available in xpcshell, so we'll use this as a kludgy way to figure out if
-  // we're running on Windows.
-  var isWindows = ("@mozilla.org/windows-registry-key;1" in Cc);
-} catch(ex) {
-  do_throw("Could not get favicon service\n");
+/**
+ * Load a favicon file, verifying expected length.
+ *
+ * @return the loaded data to allow composing more complex checks.
+ */
+function setAndGetFaviconDataFromName(iconName, inMimeType, expectedLength) {
+  let inData = readFileOfLength(iconName, expectedLength);
+  let [outData, outMimeType] = setAndGetFaviconData(iconName, inData, inMimeType);
+  return [inData, outData, outMimeType];
 }
 
-// Get history services
-try {
-  var histsvc = Cc["@mozilla.org/browser/nav-history-service;1"].
-                getService(Ci.nsINavHistoryService);
-  var bhist = histsvc.QueryInterface(Ci.nsIBrowserHistory);
-} catch(ex) {
-  do_throw("Could not get history services\n");
+/**
+ * Load a non-oversized favicon file, verifying expected contents and length.
+ */
+function check_icon_data(iconName, inMimeType, expectedLength) {
+  let [inData, outData, outMimeType] =
+    setAndGetFaviconDataFromName(iconName, inMimeType, expectedLength);
+
+  // Ensure input and output are identical.
+  do_check_eq(inMimeType, outMimeType);
+  do_check_true(compareArrays(inData, outData));
 }
 
-function checkArrays(a, b) {
-  do_check_true(compareArrays(a, b));
+/**
+ * Load an oversized favicon file, verifying expected contents and lengths
+ * against a translated PNG file.
+ * If skipContent is true, the expected and output data are not compared.
+ */
+function check_oversized_icon_data(iconName, inMimeType, expectedLength, skipContent) {
+  let [inData, outData, outMimeType] =
+    setAndGetFaviconDataFromName(iconName, inMimeType, expectedLength);
+
+  // Read in the expected output.
+  let expectedFile = do_get_file("expected-" + iconName + ".png");
+  let expectedData = readFileData(expectedFile);
+
+  // Compare thet expected data to the actual data.
+  do_check_eq("image/png", outMimeType);
+  if (!skipContent) {
+    do_check_true(compareArrays(expectedData, outData));
+  }
+}
+
+/*
+ * Done with utilities! On to the tests.
+ */
+function run_test() {
+  run_next_test();
 }
 
-function run_test() {
-try {
-
-/* ========== 1 ========== */
-var testnum = 1;
-var testdesc = "test storing a normal 16x16 icon";
-
-// 16x16 png, 286 bytes.
-var iconName = "favicon-normal16.png";
-var inMimeType = "image/png";
-var iconFile = do_get_file(iconName);
-
-var inData = readFileData(iconFile);
-do_check_eq(inData.length, 286);
-
-var [outData, outMimeType] = setAndGetFaviconData(iconName, inData, inMimeType);
-
-// Ensure input and output are identical
-do_check_eq(inMimeType, outMimeType);
-checkArrays(inData, outData);
-                    
-
-/* ========== 2 ========== */
-testnum++;
-testdesc = "test storing a normal 32x32 icon";
-
-// 32x32 png, 344 bytes.
-iconName = "favicon-normal32.png";
-inMimeType = "image/png";
-iconFile = do_get_file(iconName);
+add_test(function test_storing_a_normal_16x16_icon() {
+  // 16x16 png, 286 bytes.
+  let iconName   = "favicon-normal16.png";
+  let inMimeType = "image/png";
+  check_icon_data(iconName, inMimeType, 286);
+  run_next_test();
+});
 
-inData = readFileData(iconFile);
-do_check_eq(inData.length, 344);
-
-[outData, outMimeType] = setAndGetFaviconData(iconName, inData, inMimeType);
-
-// Ensure input and output are identical
-do_check_eq(inMimeType, outMimeType);
-checkArrays(inData, outData);
-
-
-/* ========== 3 ========== */
-testnum++;
-testdesc = "test storing an oversize 16x16 icon ";
-
-//  in: 16x16 ico, 1406 bytes.
-// out: 16x16 png 
-iconName = "favicon-big16.ico";
-inMimeType = "image/x-icon";
-iconFile = do_get_file(iconName);
-
-inData = readFileData(iconFile);
-do_check_eq(inData.length, 1406);
-
-[outData, outMimeType] = setAndGetFaviconData(iconName, inData, inMimeType);
-
-// Read in the expected output.
-var expectedFile = do_get_file("expected-" + iconName + ".png");
-var expectedData = readFileData(expectedFile);
-
-// Compare thet expected data to the actual data.
-do_check_eq("image/png", outMimeType);
-checkArrays(expectedData, outData);
+add_test(function test_storing_a_normal_32x32_icon() {
+  // 32x32 png, 344 bytes.
+  let iconName   = "favicon-normal32.png";
+  let inMimeType = "image/png";
+  check_icon_data(iconName, inMimeType, 344);
+  run_next_test();
+});
 
-/* ========== 4 ========== */
-testnum++;
-testdesc = "test storing an oversize 4x4 icon ";
-
-//  in: 4x4 jpg, 4751 bytes.
-// out: 16x16 png 
-iconName = "favicon-big4.jpg";
-inMimeType = "image/jpeg";
-iconFile = do_get_file(iconName);
-
-inData = readFileData(iconFile);
-do_check_eq(inData.length, 4751);
-
-[outData, outMimeType] = setAndGetFaviconData(iconName, inData, inMimeType);
-
-// Read in the expected output.
-var expectedFile = do_get_file("expected-" + iconName + ".png");
-var expectedData = readFileData(expectedFile);
-
-// Compare thet expected data to the actual data.
-do_check_eq("image/png", outMimeType);
-checkArrays(expectedData, outData);
-
-
-/* ========== 5 ========== */
-testnum++;
-testdesc = "test storing an oversize 32x32 icon ";
+add_test(function test_storing_an_oversize_16x16_icon() {
+  //  in: 16x16 ico, 1406 bytes.
+  // out: 16x16 png
+  let iconName   = "favicon-big16.ico";
+  let inMimeType = "image/x-icon";
+  check_oversized_icon_data(iconName, inMimeType, 1406);
+  run_next_test();
+});
 
-//  in: 32x32 jpg, 3494 bytes.
-// out: 16x16 png 
-iconName = "favicon-big32.jpg";
-inMimeType = "image/jpeg";
-iconFile = do_get_file(iconName);
-
-inData = readFileData(iconFile);
-do_check_eq(inData.length, 3494);
-
-[outData, outMimeType] = setAndGetFaviconData(iconName, inData, inMimeType);
-
-// Read in the expected output.
-var expectedFile = do_get_file("expected-" + iconName + ".png");
-var expectedData = readFileData(expectedFile);
+add_test(function test_storing_an_oversize_4x4_icon() {
+  //  in: 4x4 jpg, 4751 bytes.
+  // out: 16x16 png
+  let iconName   = "favicon-big4.jpg";
+  let inMimeType = "image/jpeg";
+  check_oversized_icon_data(iconName, inMimeType, 4751);
+  run_next_test();
+});
 
-// Compare thet expected data to the actual data.
-do_check_eq("image/png", outMimeType);
-// Disabled on Windows due to problems with pixels varying slightly.
-if (!isWindows)
-  checkArrays(expectedData, outData);
-
-
-/* ========== 6 ========== */
-testnum++;
-testdesc = "test storing an oversize 48x48 icon ";
-
-//  in: 48x48 ico, 56646 bytes.
-// (howstuffworks.com icon, contains 13 icons with sizes from 16x16 to
-// 48x48 in varying depths)
-// out: 16x16 png 
-iconName = "favicon-big48.ico";
-inMimeType = "image/x-icon";
-iconFile = do_get_file(iconName);
-
-inData = readFileData(iconFile);
-do_check_eq(inData.length, 56646);
+add_test(function test_storing_an_oversize_32x32_icon() {
+  //  in: 32x32 jpg, 3494 bytes.
+  // out: 16x16 png
+  let iconName   = "favicon-big32.jpg";
+  let inMimeType = "image/jpeg";
 
-[outData, outMimeType] = setAndGetFaviconData(iconName, inData, inMimeType);
-
-// Read in the expected output.
-var expectedFile = do_get_file("expected-" + iconName + ".png");
-var expectedData = readFileData(expectedFile);
-
-// Compare thet expected data to the actual data.
-do_check_eq("image/png", outMimeType);
-checkArrays(expectedData, outData);
-
-/* ========== 7 ========== */
-testnum++;
-testdesc = "test storing an oversize 64x64 icon ";
-
-//  in: 64x64 png, 10698 bytes.
-// out: 16x16 png 
-iconName = "favicon-big64.png";
-inMimeType = "image/png";
-iconFile = do_get_file(iconName);
-
-inData = readFileData(iconFile);
-do_check_eq(inData.length, 10698);
-
-[outData, outMimeType] = setAndGetFaviconData(iconName, inData, inMimeType);
-
-// Read in the expected output.
-var expectedFile = do_get_file("expected-" + iconName + ".png");
-var expectedData = readFileData(expectedFile);
-
-// Compare thet expected data to the actual data.
-do_check_eq("image/png", outMimeType);
-checkArrays(expectedData, outData);
+  // Content check disabled on Windows due to problems with pixels varying
+  // slightly.
+  check_oversized_icon_data(iconName, inMimeType, 3494, isWindows);
+  run_next_test();
+});
 
-/* ========== 8 ========== */
-testnum++;
-testdesc = "test scaling an oversize 160x3 icon ";
-
-//  in: 160x3 jpg, 5095 bytes.
-// out: 16x16 png 
-iconName = "favicon-scale160x3.jpg";
-inMimeType = "image/jpeg";
-iconFile = do_get_file(iconName);
-
-inData = readFileData(iconFile);
-do_check_eq(inData.length, 5095);
-
-[outData, outMimeType] = setAndGetFaviconData(iconName, inData, inMimeType);
-
-// Read in the expected output.
-var expectedFile = do_get_file("expected-" + iconName + ".png");
-var expectedData = readFileData(expectedFile);
-
-// Compare thet expected data to the actual data.
-do_check_eq("image/png", outMimeType);
-checkArrays(expectedData, outData);
-
-/* ========== 9 ========== */
-testnum++;
-testdesc = "test scaling an oversize 3x160 icon ";
-
-//  in: 3x160 jpg, 5059 bytes.
-// out: 16x16 png 
-iconName = "favicon-scale3x160.jpg";
-inMimeType = "image/jpeg";
-iconFile = do_get_file(iconName);
+add_test(function test_storing_an_oversize_48x48_icon() {
+  //  in: 48x48 ico, 56646 bytes.
+  // (howstuffworks.com icon, contains 13 icons with sizes from 16x16 to
+  // 48x48 in varying depths)
+  // out: 16x16 png
+  let iconName   = "favicon-big48.ico";
+  let inMimeType = "image/x-icon";
+  check_oversized_icon_data(iconName, inMimeType, 56646);
+  run_next_test();
+});
 
-inData = readFileData(iconFile);
-do_check_eq(inData.length, 5059);
-
-[outData, outMimeType] = setAndGetFaviconData(iconName, inData, inMimeType);
-
-// Read in the expected output.
-var expectedFile = do_get_file("expected-" + iconName + ".png");
-var expectedData = readFileData(expectedFile);
-
-// Compare thet expected data to the actual data.
-do_check_eq("image/png", outMimeType);
-checkArrays(expectedData, outData);
-
-
-/* ========== 10 ========== */
-testnum++;
-testdesc = "test set and get favicon ";
-
-// 32x32 png, 344 bytes.
-var icon1Name = "favicon-normal32.png";
-var icon1MimeType = "image/png";
-var icon1File = do_get_file(icon1Name);
-var icon1Data = readFileData(icon1File);
-do_check_eq(icon1Data.length, 344);
-var icon1URI = uri("file:///./" + icon1Name);
+add_test(function test_storing_an_oversize_64x64_icon() {
+  //  in: 64x64 png, 10698 bytes.
+  // out: 16x16 png
+  let iconName   = "favicon-big64.png";
+  let inMimeType = "image/png";
+  check_oversized_icon_data(iconName, inMimeType, 10698);
+  run_next_test();
+});
 
-// 16x16 png, 286 bytes.
-var icon2Name = "favicon-normal16.png";
-var icon2MimeType = "image/png";
-var icon2File = do_get_file(icon2Name);
-var icon2Data = readFileData(icon2File);
-do_check_eq(icon2Data.length, 286);
-var icon2URI = uri("file:///./" + icon2Name);
-
-var page1URI = uri("http://foo.bar/");
-var page2URI = uri("http://bar.foo/");
-var page3URI = uri("http://foo.bar.moz/");
-
-// add visits to the db
-histsvc.addVisit(page1URI, Date.now() * 1000, null,
-                 histsvc.TRANSITION_TYPED, false, 0);
-histsvc.addVisit(page2URI, Date.now() * 1000, null,
-                 histsvc.TRANSITION_TYPED, false, 0);
-histsvc.addVisit(page3URI, Date.now() * 1000, null,
-                 histsvc.TRANSITION_TYPED, false, 0);
+add_test(function test_scaling_an_oversize_160x3_icon() {
+  //  in: 160x3 jpg, 5095 bytes.
+  // out: 16x16 png
+  let iconName   = "favicon-scale160x3.jpg";
+  let inMimeType = "image/jpeg";
+  check_oversized_icon_data(iconName, inMimeType, 5095);
+  run_next_test();
+});
 
-// set first page icon
-try {
-  iconsvc.setFaviconData(icon1URI, icon1Data, icon1Data.length,
-                         icon1MimeType, Number.MAX_VALUE);
-} catch (ex) {}
-iconsvc.setFaviconUrlForPage(page1URI, icon1URI);
-do_check_guid_for_uri(page1URI);
-var savedIcon1URI = iconsvc.getFaviconForPage(page1URI);
-
-// Test getFaviconForPage().
-do_test_pending();
-iconsvc.getFaviconURLForPage(page1URI, {
-    onFaviconDataAvailable: function(aURI, aDataLen, aData, aMimeType) {
-      do_check_true(aURI.equals(savedIcon1URI));
-      do_check_eq(aDataLen, 0);
-      do_check_eq(aData.length, 0);
-      do_check_eq(aMimeType, "");
-  },
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIFaviconDataCallback])
+add_test(function test_scaling_an_oversize_3x160_icon() {
+  //  in: 3x160 jpg, 5059 bytes.
+  // out: 16x16 png
+  let iconName = "favicon-scale3x160.jpg";
+  let inMimeType = "image/jpeg";
+  check_oversized_icon_data(iconName, inMimeType, 5059);
+  run_next_test();
 });
 
-iconsvc.getFaviconDataForPage(page1URI, {
-    onFaviconDataAvailable: function(aURI, aDataLen, aData, aMimeType) {
-      do_check_true(aURI.equals(savedIcon1URI));
-      do_check_eq(icon1MimeType, out1MimeType.value);
-      checkArrays(icon1Data, aData);
-      do_check_eq(aDataLen, aData.length);
-      do_test_finished();
+/*
+ * The following few tests are asynchronous but share state. We bundle that
+ * state into two arrays: `icons` and `pages`.
+ * The tests are in four parts:
+ *
+ * 1. setup, where we add some history visits, and set an icon for one of them;
+ * 2. getFaviconURLForPage, where we test synchronous retrieval;
+ * 3. second_and_third, where we add icons for the remaining two pages, and test
+ *    them synchronously;
+ * 4. getFaviconDataForPage, which tests asynchronous retrieval.
+ */
+let icons = [
+  {
+    name: "favicon-normal32.png",
+    mime: "image/png",
+    data: null,
+    uri:  NetUtil.newURI("file:///./favicon-normal32.png")
   },
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIFaviconDataCallback])
+  {
+    name: "favicon-normal16.png",
+    mime: "image/png",
+    data: null,
+    uri:  NetUtil.newURI("file:///./favicon-normal16.png")
+  }
+];
+
+let pages = [
+  NetUtil.newURI("http://foo.bar/"),
+  NetUtil.newURI("http://bar.foo/"),
+  NetUtil.newURI("http://foo.bar.moz/")
+];
+
+add_test(function test_set_and_get_favicon_setup() {
+  do_log_info("Setup code for set/get favicon.");
+  let [icon0, icon1] = icons;
+
+  // 32x32 png, 344 bytes.
+  icon0.data = readFileOfLength(icon0.name, 344);
+
+  // 16x16 png, 286 bytes.
+  icon1.data = readFileOfLength(icon1.name, 286);
+
+  // Add visits to the DB.
+  for each (let uri in pages) {
+    PlacesUtils.history.addVisit(uri, Date.now() * 1000, null,
+                                 PlacesUtils.history.TRANSITION_TYPED,
+                                 false, 0);
+  }
+
+  // Set first page icon.
+  try {
+    PlacesUtils.favicons.setFaviconData(icon0.uri, icon0.data, icon0.data.length,
+                                        icon0.mime, Number.MAX_VALUE);
+  } catch (ex) {
+    do_throw("Failure setting first page icon: " + ex);
+  }
+  PlacesUtils.favicons.setFaviconUrlForPage(pages[0], icon0.uri);
+  do_check_guid_for_uri(pages[0]);
+
+  let favicon = PlacesUtils.favicons.getFaviconForPage(pages[0]);
+  do_check_true(icon0.uri.equals(favicon));
+
+  run_next_test();
+});
+
+add_test(function test_set_and_get_favicon_getFaviconURLForPage() {
+  let [icon0] = icons;
+  PlacesUtils.favicons.getFaviconURLForPage(pages[0], {
+      onFaviconDataAvailable: function(aURI, aDataLen, aData, aMimeType) {
+        do_check_true(icon0.uri.equals(aURI));
+        do_check_eq(aDataLen, 0);
+        do_check_eq(aData.length, 0);
+        do_check_eq(aMimeType, "");
+        run_next_test();
+    },
+    QueryInterface: XPCOMUtils.generateQI([Ci.nsIFaviconDataCallback])
+  });
 });
 
-// set second page icon
-try {
-  iconsvc.setFaviconData(icon2URI, icon2Data, icon2Data.length,
-                         icon2MimeType, Number.MAX_VALUE);
-} catch (ex) {}
-iconsvc.setFaviconUrlForPage(page2URI, icon2URI);
-do_check_guid_for_uri(page2URI);
-var savedIcon2URI = iconsvc.getFaviconForPage(page2URI);
-
-// set third page icon as the same as first page one
-try {
-  iconsvc.setFaviconData(icon1URI, icon1Data, icon1Data.length,
-                         icon1MimeType, Number.MAX_VALUE);
-} catch (ex) {}
-iconsvc.setFaviconUrlForPage(page3URI, icon1URI);
-do_check_guid_for_uri(page3URI);
-var savedIcon3URI = iconsvc.getFaviconForPage(page3URI);
+add_test(function test_set_and_get_favicon_second_and_third() {
+  let [icon0, icon1] = icons;
+  try {
+    PlacesUtils.favicons.setFaviconData(icon1.uri, icon1.data, icon1.data.length,
+                                        icon1.mime, Number.MAX_VALUE);
+  } catch (ex) {
+    do_throw("Failure setting second page icon: " + ex);
+  }
+  PlacesUtils.favicons.setFaviconUrlForPage(pages[1], icon1.uri);
+  do_check_guid_for_uri(pages[1]);
+  do_check_true(icon1.uri.equals(PlacesUtils.favicons.getFaviconForPage(pages[1])));
 
-// check first page icon
-var out1MimeType = {};
-var out1Data = iconsvc.getFaviconData(savedIcon1URI, out1MimeType);
-do_check_eq(icon1MimeType, out1MimeType.value);
-checkArrays(icon1Data, out1Data);
+  // Set third page icon as the same as first page one.
+  try {
+    PlacesUtils.favicons.setFaviconData(icon0.uri, icon0.data, icon0.data.length,
+                                        icon0.mime, Number.MAX_VALUE);
+  } catch (ex) {
+    do_throw("Failure setting third page icon: " + ex);
+  }
+  PlacesUtils.favicons.setFaviconUrlForPage(pages[2], icon0.uri);
+  do_check_guid_for_uri(pages[2]);
+  let page3favicon = PlacesUtils.favicons.getFaviconForPage(pages[2]);
+  do_check_true(icon0.uri.equals(page3favicon));
 
-// check second page icon
-var out2MimeType = {};
-var out2Data = iconsvc.getFaviconData(savedIcon2URI, out2MimeType);
-do_check_eq(icon2MimeType, out2MimeType.value);
-checkArrays(icon2Data, out2Data);
+  // Check first page icon.
+  let out1MimeType = {};
+  let out1Data = PlacesUtils.favicons.getFaviconData(icon0.uri, out1MimeType);
+  do_check_eq(icon0.mime, out1MimeType.value);
+  do_check_true(compareArrays(icon0.data, out1Data));
 
-// check third page icon
-var out3MimeType = {};
-var out3Data = iconsvc.getFaviconData(savedIcon3URI, out3MimeType);
-do_check_eq(icon1MimeType, out3MimeType.value);
-checkArrays(icon1Data, out3Data);
+  // Check second page icon.
+  let out2MimeType = {};
+  let out2Data = PlacesUtils.favicons.getFaviconData(icon1.uri, out2MimeType);
+  do_check_eq(icon1.mime, out2MimeType.value);
+  do_check_true(compareArrays(icon1.data, out2Data));
 
-
-/* ========== 11 ========== */
-testnum++;
-testdesc = "test favicon links ";
+  // Check third page icon.
+  let out3MimeType = {};
+  let out3Data = PlacesUtils.favicons.getFaviconData(page3favicon, out3MimeType);
+  do_check_eq(icon0.mime, out3MimeType.value);
+  do_check_true(compareArrays(icon0.data, out3Data));
+  run_next_test();
+});
 
-var pageURI = uri("http://foo.bar/");
-var faviconURI = uri("file:///./favicon-normal32.png");
-do_check_eq(iconsvc.getFaviconImageForPage(pageURI).spec,
-            iconsvc.getFaviconLinkForIcon(faviconURI).spec);
-
-
-/* ========== 12 ========== */
-testnum++;
-testdesc = "test failed favicon cache ";
+add_test(function test_set_and_get_favicon_getFaviconDataForPage() {
+  let [icon0] = icons;
+  PlacesUtils.favicons.getFaviconDataForPage(pages[0], {
+      onFaviconDataAvailable: function(aURI, aDataLen, aData, aMimeType) {
+        do_check_true(aURI.equals(icon0.uri));
+        do_check_eq(icon0.mime, icon0.mime);
+        do_check_true(compareArrays(icon0.data, aData));
+        do_check_eq(aDataLen, aData.length);
+        run_next_test();
+      },
+      QueryInterface: XPCOMUtils.generateQI([Ci.nsIFaviconDataCallback])
+    });
+});
 
-// 32x32 png, 344 bytes.
-iconName = "favicon-normal32.png";
-faviconURI = uri("file:///./" + iconName);
-
-iconsvc.addFailedFavicon(faviconURI);
-do_check_true(iconsvc.isFailedFavicon(faviconURI));
-iconsvc.removeFailedFavicon(faviconURI);
-do_check_false(iconsvc.isFailedFavicon(faviconURI));
-
-
-/* ========== 13 ========== */
-testnum++;
-testdesc = "test getFaviconData on the default favicon ";
+add_test(function test_favicon_links() {
+  let pageURI = NetUtil.newURI("http://foo.bar/");
+  let faviconURI = NetUtil.newURI("file:///./favicon-normal32.png");
+  do_check_eq(PlacesUtils.favicons.getFaviconImageForPage(pageURI).spec,
+              PlacesUtils.favicons.getFaviconLinkForIcon(faviconURI).spec);
+  run_next_test();
+});
 
-outMimeType = {};
-outData = iconsvc.getFaviconData(iconsvc.defaultFavicon, outMimeType);
-do_check_eq(outMimeType.value, "image/png");
+add_test(function test_failed_favicon_cache() {
+  // 32x32 png, 344 bytes.
+  let iconName = "favicon-normal32.png";
+  let faviconURI = NetUtil.newURI("file:///./" + iconName);
+
+  PlacesUtils.favicons.addFailedFavicon(faviconURI);
+  do_check_true(PlacesUtils.favicons.isFailedFavicon(faviconURI));
+  PlacesUtils.favicons.removeFailedFavicon(faviconURI);
+  do_check_false(PlacesUtils.favicons.isFailedFavicon(faviconURI));
+  run_next_test();
+});
 
-// Read in the icon and compare it to what the API returned above.
-var istream = NetUtil.newChannel(iconsvc.defaultFavicon).open();
-var bistream = Cc["@mozilla.org/binaryinputstream;1"].
-               createInstance(Ci.nsIBinaryInputStream);
-bistream.setInputStream(istream);
-expectedData = [];
-var avail;
-while (avail = bistream.available()) {
-  expectedData = expectedData.concat(bistream.readByteArray(avail));
-}
-bistream.close();
-checkArrays(outData, expectedData);
+add_test(function test_getFaviconData_on_the_default_favicon() {
+  let icon = PlacesUtils.favicons.defaultFavicon;
+  let outMimeType = {};
+  let outData = PlacesUtils.favicons.getFaviconData(icon, outMimeType);
+  do_check_eq(outMimeType.value, "image/png");
 
-
-/* ========== end ========== */
-
-} catch (e) {
-    throw "FAILED in test #" + testnum + " -- " + testdesc + ": " + e;
-}
-};
+  // Read in the icon and compare it to what the API returned above.
+  let istream = NetUtil.newChannel(PlacesUtils.favicons.defaultFavicon).open();
+  let expectedData = readInputStreamData(istream);
+  do_check_true(compareArrays(outData, expectedData));
+  run_next_test();
+});