Bug 1420223 - createImageBitmap must work with nsIAsyncInputStream - part 4 - ImageBitmap must use imgITools::decodeImageBuffer, r=aosmond
authorAndrea Marchesini <amarchesini@mozilla.com>
Mon, 27 Nov 2017 17:05:57 +0100
changeset 445580 e60f002f07d0d40843758d40557acfc3ac6c51f3
parent 445579 e257e633c87d416693404d80c080c92c91ce981d
child 445581 a4938644dfc4d94b677a18ea2a07325ea07a5bca
push id8527
push userCallek@gmail.com
push dateThu, 11 Jan 2018 21:05:50 +0000
treeherdermozilla-beta@95342d212a7a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersaosmond
bugs1420223
milestone59.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 1420223 - createImageBitmap must work with nsIAsyncInputStream - part 4 - ImageBitmap must use imgITools::decodeImageBuffer, r=aosmond
browser/modules/WindowsPreviewPerTab.jsm
devtools/client/inspector/test/browser_inspector_menu-01-sensitivity.js
devtools/shared/gcli/commands/screenshot.js
image/imgITools.idl
image/imgTools.cpp
image/test/unit/test_imgtools.js
toolkit/components/extensions/ext-clipboard.js
toolkit/components/places/nsFaviconService.cpp
toolkit/components/search/nsSearchService.js
toolkit/mozapps/extensions/internal/LightweightThemeImageOptimizer.jsm
widget/windows/WinUtils.cpp
--- a/browser/modules/WindowsPreviewPerTab.jsm
+++ b/browser/modules/WindowsPreviewPerTab.jsm
@@ -81,19 +81,37 @@ function _imageFromURI(uri, privateMode,
     channel.QueryInterface(Ci.nsIPrivateBrowsingChannel);
     channel.setPrivate(privateMode);
   } catch (e) {
     // Ignore channels which do not support nsIPrivateBrowsingChannel
   }
   NetUtil.asyncFetch(channel, function(inputStream, resultCode) {
     if (!Components.isSuccessCode(resultCode))
       return;
+
+    const decodeCallback = {
+      onImageReady(image, status) {
+        if (!image) {
+          // We failed, so use the default favicon (only if this wasn't the
+          // default favicon).
+          let defaultURI = PlacesUtils.favicons.defaultFavicon;
+          if (!defaultURI.equals(uri)) {
+            _imageFromURI(defaultURI, privateMode, callback);
+            return;
+          }
+        }
+
+        callback(image);
+      }
+    };
+
     try {
-      let out_img = imgTools.decodeImage(inputStream, channel.contentType);
-      callback(out_img);
+      let threadManager = Cc["@mozilla.org/thread-manager;1"].getService();
+      imgTools.decodeImage(inputStream, channel.contentType, decodeCallback,
+                           threadManager.currentThread);
     } catch (e) {
       // We failed, so use the default favicon (only if this wasn't the default
       // favicon).
       let defaultURI = PlacesUtils.favicons.defaultFavicon;
       if (!defaultURI.equals(uri))
         _imageFromURI(defaultURI, privateMode, callback);
     }
   });
--- a/devtools/client/inspector/test/browser_inspector_menu-01-sensitivity.js
+++ b/devtools/client/inspector/test/browser_inspector_menu-01-sensitivity.js
@@ -303,23 +303,19 @@ function copyImageToClipboard(data) {
   let clipboardService = Cc["@mozilla.org/widget/clipboard;1"]
                               .getService(Ci.nsIClipboard);
   let imageTools = Cc["@mozilla.org/image/tools;1"]
                      .getService(Ci.imgITools);
 
   // Image data is stored as base64 in the test.
   let image = atob(data);
 
-  let input = Cc["@mozilla.org/io/string-input-stream;1"]
-                .createInstance(Ci.nsIStringInputStream);
-  input.setData(image, image.length);
-
   let imgPtr = Cc["@mozilla.org/supports-interface-pointer;1"]
                  .createInstance(Ci.nsISupportsInterfacePointer);
-  imgPtr.data = imageTools.decodeImage(input, "image/png");
+  imgPtr.data = imageTools.decodeImageBuffer(image, image.length, "image/png");
 
   let xferable = Cc["@mozilla.org/widget/transferable;1"]
                    .createInstance(Ci.nsITransferable);
   xferable.init(null);
   xferable.addDataFlavor("image/png");
   xferable.setTransferData("image/png", imgPtr, -1);
 
   clipboardService.setData(xferable, null, clipboardService.kGlobalClipboard);
--- a/devtools/shared/gcli/commands/screenshot.js
+++ b/devtools/shared/gcli/commands/screenshot.js
@@ -358,55 +358,74 @@ function getFilename(defaultName) {
 }
 
 /**
  * Save the image data to the clipboard. This returns a promise, so it can
  * be treated exactly like imgur / file processing, but it's really sync
  * for now.
  */
 function saveToClipboard(context, reply) {
-  try {
-    const channel = NetUtil.newChannel({
-      uri: reply.data,
-      loadUsingSystemPrincipal: true,
-      contentPolicyType: Ci.nsIContentPolicy.TYPE_INTERNAL_IMAGE
-    });
-    const input = channel.open2();
+  return new Promise(resolve => {
+    try {
+      const channel = NetUtil.newChannel({
+        uri: reply.data,
+        loadUsingSystemPrincipal: true,
+        contentPolicyType: Ci.nsIContentPolicy.TYPE_INTERNAL_IMAGE
+      });
+      const input = channel.open2();
+
+      const loadContext = context.environment.chromeWindow
+                                 .QueryInterface(Ci.nsIInterfaceRequestor)
+                                 .getInterface(Ci.nsIWebNavigation)
+                                 .QueryInterface(Ci.nsILoadContext);
 
-    const loadContext = context.environment.chromeWindow
-                               .QueryInterface(Ci.nsIInterfaceRequestor)
-                               .getInterface(Ci.nsIWebNavigation)
-                               .QueryInterface(Ci.nsILoadContext);
+      const callback = {
+        onImageReady(container, status) {
+          if (!container) {
+            console.error("imgTools.decodeImageAsync failed");
+            reply.destinations.push(l10n.lookup("screenshotErrorCopying"));
+            resolve();
+            return;
+          }
 
-    const imgTools = Cc["@mozilla.org/image/tools;1"]
-                        .getService(Ci.imgITools);
-
-    const container = imgTools.decodeImage(input, channel.contentType);
+          try {
+            const wrapped = Cc["@mozilla.org/supports-interface-pointer;1"]
+                              .createInstance(Ci.nsISupportsInterfacePointer);
+            wrapped.data = container;
 
-    const wrapped = Cc["@mozilla.org/supports-interface-pointer;1"]
-                      .createInstance(Ci.nsISupportsInterfacePointer);
-    wrapped.data = container;
+            const trans = Cc["@mozilla.org/widget/transferable;1"]
+                            .createInstance(Ci.nsITransferable);
+            trans.init(loadContext);
+            trans.addDataFlavor(channel.contentType);
+            trans.setTransferData(channel.contentType, wrapped, -1);
 
-    const trans = Cc["@mozilla.org/widget/transferable;1"]
-                    .createInstance(Ci.nsITransferable);
-    trans.init(loadContext);
-    trans.addDataFlavor(channel.contentType);
-    trans.setTransferData(channel.contentType, wrapped, -1);
+            const clip = Cc["@mozilla.org/widget/clipboard;1"]
+                            .getService(Ci.nsIClipboard);
+            clip.setData(trans, null, Ci.nsIClipboard.kGlobalClipboard);
 
-    const clip = Cc["@mozilla.org/widget/clipboard;1"]
-                    .getService(Ci.nsIClipboard);
-    clip.setData(trans, null, Ci.nsIClipboard.kGlobalClipboard);
+            reply.destinations.push(l10n.lookup("screenshotCopied"));
+          } catch (ex) {
+            console.error(ex);
+            reply.destinations.push(l10n.lookup("screenshotErrorCopying"));
+          }
+          resolve();
+        }
+      };
 
-    reply.destinations.push(l10n.lookup("screenshotCopied"));
-  } catch (ex) {
-    console.error(ex);
-    reply.destinations.push(l10n.lookup("screenshotErrorCopying"));
-  }
-
-  return Promise.resolve();
+      const threadManager = Cc["@mozilla.org/thread-manager;1"].getService();
+      const imgTools = Cc["@mozilla.org/image/tools;1"]
+                          .getService(Ci.imgITools);
+      imgTools.decodeImageAsync(input, channel.contentType, callback,
+                                threadManager.currentThread);
+    } catch (ex) {
+      console.error(ex);
+      reply.destinations.push(l10n.lookup("screenshotErrorCopying"));
+      resolve();
+    }
+  });
 }
 
 /**
  * Upload screenshot data to Imgur, returning a promise of a URL (as a string)
  */
 function uploadToImgur(reply) {
   return new Promise((resolve, reject) => {
     const xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
--- a/image/imgITools.idl
+++ b/image/imgITools.idl
@@ -16,27 +16,33 @@ interface imgIScriptedNotificationObserv
 interface imgINotificationObserver;
 interface imgIContainerCallback;
 
 [scriptable, builtinclass, uuid(4c2383a4-931c-484d-8c4a-973590f66e3f)]
 interface imgITools : nsISupports
 {
     /**
      * decodeImage
-     * Caller provides an input stream and mimetype. We read from the stream
-     * and decompress it (according to the specified mime type) and return
-     * the resulting imgIContainer.
+     * decodeImageBuffer
+     * Caller provides an buffer, a buffer size and a mimetype. We read from
+     * the stream and decompress it (according to the specified mime type) and
+     * return the resulting imgIContainer.
      *
      * @param aStream
      *        An input stream for an encoded image file.
+     * @param aBuffer
+     *        Data in memory.
+     * @param aSize
+     *        Buffer size.
      * @param aMimeType
      *        Type of image in the stream.
      */
-    imgIContainer decodeImage(in nsIInputStream aStream,
-                              in ACString aMimeType);
+    imgIContainer decodeImageBuffer(in string aBuffer,
+                                    in unsigned long aSize,
+                                    in ACString aMimeType);
 
     /**
      * decodeImageAsync
      * See decodeImage. The main difference between this method and decodeImage
      * is that here the operation is done async on a thread from the decode
      * pool. When the operation is completed, the callback is executed with the
      * result.
      *
--- a/image/imgTools.cpp
+++ b/image/imgTools.cpp
@@ -15,16 +15,17 @@
 #include "nsIDocument.h"
 #include "nsIDOMDocument.h"
 #include "nsError.h"
 #include "imgLoader.h"
 #include "imgICache.h"
 #include "imgIContainer.h"
 #include "imgIEncoder.h"
 #include "nsStreamUtils.h"
+#include "nsStringStream.h"
 #include "nsContentUtils.h"
 #include "nsProxyRelease.h"
 #include "ImageFactory.h"
 #include "Image.h"
 #include "ScriptedNotificationObserver.h"
 #include "imgIScriptedNotificationObserver.h"
 #include "gfxPlatform.h"
 
@@ -175,71 +176,45 @@ imgTools::imgTools()
 }
 
 imgTools::~imgTools()
 {
   /* destructor code */
 }
 
 NS_IMETHODIMP
-imgTools::DecodeImage(nsIInputStream* aInStr,
-                      const nsACString& aMimeType,
-                      imgIContainer** aContainer)
+imgTools::DecodeImageBuffer(const char* aBuffer, uint32_t aSize,
+                            const nsACString& aMimeType,
+                            imgIContainer** aContainer)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  NS_ENSURE_ARG_POINTER(aInStr);
-
-  // We are not able to decode an async inputStream! Please use
-  // DecodeImageAsync instead.
-  nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(aInStr);
-  if (NS_WARN_IF(asyncStream)) {
-    return NS_ERROR_FAILURE;
-  }
-
-  bool nonBlocking;
-  nsresult rv = aInStr->IsNonBlocking(&nonBlocking);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // We don't want to block the main-thread. Please use DecodeImageAsync
-  // instead.
-  if (NS_WARN_IF(!nonBlocking)) {
-    MOZ_ASSERT_UNREACHABLE("We don't want to block the main-thread. Please use DecodeImageAsync instead.");
-    return NS_ERROR_FAILURE;
-  }
-
-  // Prepare the input stream.
-  nsCOMPtr<nsIInputStream> inStream = aInStr;
-  if (!NS_InputStreamIsBuffered(aInStr)) {
-    nsCOMPtr<nsIInputStream> bufStream;
-    rv = NS_NewBufferedInputStream(getter_AddRefs(bufStream),
-                                   inStream.forget(), 1024);
-    NS_ENSURE_SUCCESS(rv, rv);
-    inStream = bufStream.forget();
-  }
-
-  // Figure out how much data we've been passed.
-  uint64_t length;
-  rv = inStream->Available(&length);
-  NS_ENSURE_SUCCESS(rv, rv);
-  NS_ENSURE_TRUE(length <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
+  NS_ENSURE_ARG_POINTER(aBuffer);
 
   // Create a new image container to hold the decoded data.
   nsAutoCString mimeType(aMimeType);
   RefPtr<image::Image> image =
-    ImageFactory::CreateAnonymousImage(mimeType, uint32_t(length));
+    ImageFactory::CreateAnonymousImage(mimeType, aSize);
   RefPtr<ProgressTracker> tracker = image->GetProgressTracker();
 
   if (image->HasError()) {
     return NS_ERROR_FAILURE;
   }
 
-  // If we have anything to read, let's inform the image.
-  rv = image->OnImageDataAvailable(nullptr, nullptr, inStream, 0,
-                                   uint32_t(length));
+  // Let's create a temporary inputStream.
+  nsCOMPtr<nsIInputStream> stream;
+  nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream),
+                                      aBuffer, aSize,
+                                      NS_ASSIGNMENT_DEPEND);
+  NS_ENSURE_SUCCESS(rv, rv);
+  MOZ_ASSERT(stream);
+  MOZ_ASSERT(NS_InputStreamIsBuffered(stream));
+
+  rv = image->OnImageDataAvailable(nullptr, nullptr, stream, 0,
+                                   aSize);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Let the Image know we've sent all the data.
   rv = image->OnImageDataComplete(nullptr, nullptr, NS_OK, true);
   tracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // All done.
--- a/image/test/unit/test_imgtools.js
+++ b/image/test/unit/test_imgtools.js
@@ -1,15 +1,17 @@
 /*
  * Tests for imgITools
  */
 
 var Ci = Components.interfaces;
 var Cc = Components.classes;
+var Cu = Components.utils;
 
+Cu.import("resource://gre/modules/NetUtil.jsm");
 
 /*
  * 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) {
@@ -140,17 +142,18 @@ testdesc = "test decoding a PNG";
 // 64x64 png, 8415 bytes.
 var imgName = "image1.png";
 var inMimeType = "image/png";
 var imgFile = do_get_file(imgName);
 
 var istream = getFileInputStream(imgFile);
 do_check_eq(istream.available(), 8415);
 
-var container = imgTools.decodeImage(istream, inMimeType);
+var buffer = NetUtil.readInputStreamToString(istream, istream.available());
+var container = imgTools.decodeImageBuffer(buffer, buffer.length, inMimeType);
 
 // It's not easy to look at the pixel values from JS, so just
 // check the container's size.
 do_check_eq(container.width,  64);
 do_check_eq(container.height, 64);
 
 
 /* ========== 2 ========== */
@@ -198,17 +201,18 @@ testdesc = "test decoding a JPEG";
 // 32x32 jpeg, 3494 bytes.
 imgName = "image2.jpg";
 inMimeType = "image/jpeg";
 imgFile = do_get_file(imgName);
 
 istream = getFileInputStream(imgFile);
 do_check_eq(istream.available(), 3494);
 
-container = imgTools.decodeImage(istream, inMimeType);
+buffer = NetUtil.readInputStreamToString(istream, istream.available());
+container = imgTools.decodeImageBuffer(buffer, buffer.length, inMimeType);
 
 // It's not easy to look at the pixel values from JS, so just
 // check the container's size.
 do_check_eq(container.width,  32);
 do_check_eq(container.height, 32);
 
 
 /* ========== 5 ========== */
@@ -260,17 +264,18 @@ testdesc = "test decoding a ICO";
 // 16x16 ico, 1406 bytes.
 imgName = "image3.ico";
 inMimeType = "image/x-icon";
 imgFile = do_get_file(imgName);
 
 istream = getFileInputStream(imgFile);
 do_check_eq(istream.available(), 1406);
 
-container = imgTools.decodeImage(istream, inMimeType);
+buffer = NetUtil.readInputStreamToString(istream, istream.available());
+container = imgTools.decodeImageBuffer(buffer, buffer.length, inMimeType);
 
 // It's not easy to look at the pixel values from JS, so just
 // check the container's size.
 do_check_eq(container.width,  16);
 do_check_eq(container.height, 16);
 
 
 /* ========== 8 ========== */
@@ -318,17 +323,18 @@ testdesc = "test decoding a GIF";
 // 32x32 gif, 1809 bytes.
 imgName = "image4.gif";
 inMimeType = "image/gif";
 imgFile = do_get_file(imgName);
 
 istream = getFileInputStream(imgFile);
 do_check_eq(istream.available(), 1809);
 
-container = imgTools.decodeImage(istream, inMimeType);
+buffer = NetUtil.readInputStreamToString(istream, istream.available());
+container = imgTools.decodeImageBuffer(buffer, buffer.length, inMimeType);
 
 // It's not easy to look at the pixel values from JS, so just
 // check the container's size.
 do_check_eq(container.width, 32);
 do_check_eq(container.height, 32);
 
 /* ========== 11 ========== */
 testnum++;
@@ -426,17 +432,18 @@ testdesc = "test cropping a JPG";
 // 32x32 jpeg, 3494 bytes.
 imgName = "image2.jpg";
 inMimeType = "image/jpeg";
 imgFile = do_get_file(imgName);
 
 istream = getFileInputStream(imgFile);
 do_check_eq(istream.available(), 3494);
 
-container = imgTools.decodeImage(istream, inMimeType);
+buffer = NetUtil.readInputStreamToString(istream, istream.available());
+container = imgTools.decodeImageBuffer(buffer, buffer.length, inMimeType);
 
 // It's not easy to look at the pixel values from JS, so just
 // check the container's size.
 do_check_eq(container.width,  32);
 do_check_eq(container.height, 32);
 
 // encode a cropped image
 istream = imgTools.encodeCroppedImage(container, "image/jpeg", 0, 0, 16, 16);
@@ -644,17 +651,19 @@ for(var i=0; i<testData.length; ++i) {
 
     var imgFile = do_get_file(dict["refImage"]);
     var istream = getFileInputStream(imgFile);
     var refBytes = streamToArray(istream);
 
     imgFile = do_get_file(dict["preImage"]);
     istream = getFileInputStream(imgFile);
 
-    var container = imgTools.decodeImage(istream, dict["preImageMimeType"]);
+    var buffer = NetUtil.readInputStreamToString(istream, istream.available());
+    var container = imgTools.decodeImageBuffer(buffer, buffer.length,
+                                               dict["preImageMimeType"]);
 
     istream = imgTools.encodeImage(container, dict["refImageMimeType"]);
 
     var sstream = Cc["@mozilla.org/storagestream;1"].
 	          createInstance(Ci.nsIStorageStream);
     sstream.init(4096, 4294967295, null);
     var ostream = sstream.getOutputStream(0);
     var bostream = Cc["@mozilla.org/network/buffered-output-stream;1"].
@@ -680,22 +689,24 @@ imgName = "bug413512.ico";
 inMimeType = "image/x-icon";
 imgFile = do_get_file(imgName);
 
 istream = getFileInputStream(imgFile);
 do_check_eq(istream.available(), 17759);
 var errsrc = "none";
 
 try {
-  container = imgTools.decodeImage(istream, inMimeType);
+  buffer = NetUtil.readInputStreamToString(istream, istream.available());
+  container = imgTools.decodeImageBuffer(buffer, buffer.length, inMimeType);
 
   // We expect to hit an error during encoding because the ICO header of the
-  // image is fine, but the actual resources are corrupt. Since decodeImage()
-  // only performs a metadata decode, it doesn't decode far enough to realize
-  // this, but we'll find out when we do a full decode during encodeImage().
+  // image is fine, but the actual resources are corrupt. Since
+  // decodeImageBuffer() only performs a metadata decode, it doesn't decode far
+  // enough to realize this, but we'll find out when we do a full decode during
+  // encodeImage().
   try {
       istream = imgTools.encodeImage(container, "image/png");
   } catch (e) {
       err = e;
       errsrc = "encode";
   }
 } catch (e) {
   err = e;
@@ -712,17 +723,18 @@ testdesc = "test correct ico hotspots (b
 
 imgName = "bug815359.ico";
 inMimeType = "image/x-icon";
 imgFile = do_get_file(imgName);
 
 istream = getFileInputStream(imgFile);
 do_check_eq(istream.available(), 4286);
 
-container = imgTools.decodeImage(istream, inMimeType);
+buffer = NetUtil.readInputStreamToString(istream, istream.available());
+container = imgTools.decodeImageBuffer(buffer, buffer.length, inMimeType);
 
 var props = container.QueryInterface(Ci.nsIProperties);
 
 do_check_eq(props.get("hotspotX", Ci.nsISupportsPRUint32).data, 10);
 do_check_eq(props.get("hotspotY", Ci.nsISupportsPRUint32).data, 9);
 
 
 /* ========== end ========== */
--- a/toolkit/components/extensions/ext-clipboard.js
+++ b/toolkit/components/extensions/ext-clipboard.js
@@ -1,37 +1,33 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
 XPCOMUtils.defineLazyServiceGetter(this, "imgTools",
                                    "@mozilla.org/image/tools;1", "imgITools");
 
-const ArrayBufferInputStream = Components.Constructor(
-    "@mozilla.org/io/arraybuffer-input-stream;1", "nsIArrayBufferInputStream");
 const SupportsInterfacePointer = Components.Constructor(
     "@mozilla.org/supports-interface-pointer;1", "nsISupportsInterfacePointer");
 const Transferable = Components.Constructor(
     "@mozilla.org/widget/transferable;1", "nsITransferable");
 
 this.clipboard = class extends ExtensionAPI {
   getAPI(context) {
     return {
       clipboard: {
         async setImageData(imageData, imageType) {
           if (AppConstants.platform == "android") {
             return Promise.reject({message: "Writing images to the clipboard is not supported on Android"});
           }
           let mimeType = `image/${imageType}`;
-          let input = new ArrayBufferInputStream();
-          input.setData(imageData, 0, imageData.byteLength);
-
           let container;
           try {
-            container = imgTools.decodeImage(input, mimeType);
+            let str = String.fromCharCode.apply(String, new Uint8Array(imageData));
+            container = imgTools.decodeImageBuffer(str, str.length, mimeType);
           } catch (e) {
             return Promise.reject({message: `Data is not a valid ${imageType} image`});
           }
 
           // Other applications can only access the copied image once the data
           // is exported via the platform-specific clipboard APIs:
           // nsClipboard::SelectionGetEvent (widget/gtk/nsClipboard.cpp)
           // nsClipboard::PasteDictFromTransferable (widget/cocoa/nsClipboard.mm)
--- a/toolkit/components/places/nsFaviconService.cpp
+++ b/toolkit/components/places/nsFaviconService.cpp
@@ -17,17 +17,16 @@
 
 #include "nsNavHistory.h"
 #include "nsPlacesMacros.h"
 #include "Helpers.h"
 
 #include "nsNetUtil.h"
 #include "nsReadableUtils.h"
 #include "nsStreamUtils.h"
-#include "nsStringStream.h"
 #include "plbase64.h"
 #include "nsIClassInfoImpl.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/LoadInfo.h"
 #include "mozilla/Preferences.h"
 #include "nsILoadInfo.h"
 #include "nsIContentPolicy.h"
 #include "nsContentUtils.h"
@@ -814,27 +813,22 @@ nsFaviconService::OptimizeIconSizes(Icon
       aIcon.payloads.Clear();
     }
     return NS_OK;
   }
 
   // Make space for the optimized payloads.
   aIcon.payloads.Clear();
 
-  nsCOMPtr<nsIInputStream> stream;
-  nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream),
-                                      payload.data.get(),
-                                      payload.data.Length(),
-                                      NS_ASSIGNMENT_DEPEND);
-  NS_ENSURE_SUCCESS(rv, rv);
-
   // decode image
   nsCOMPtr<imgIContainer> container;
-  rv = GetImgTools()->DecodeImage(stream, payload.mimeType,
-                                  getter_AddRefs(container));
+  nsresult rv = GetImgTools()->DecodeImageBuffer(payload.data.get(),
+                                                 payload.data.Length(),
+                                                 payload.mimeType,
+                                                 getter_AddRefs(container));
   NS_ENSURE_SUCCESS(rv, rv);
 
   // For ICO files, we must evaluate each of the frames we care about.
   nsTArray<FrameData> framesInfo;
   rv = GetFramesInfoForContainer(container, framesInfo);
   NS_ENSURE_SUCCESS(rv, rv);
 
   for (const auto& frameInfo : framesInfo) {
--- a/toolkit/components/search/nsSearchService.js
+++ b/toolkit/components/search/nsSearchService.js
@@ -27,19 +27,16 @@ XPCOMUtils.defineLazyModuleGetters(this,
 });
 
 XPCOMUtils.defineLazyServiceGetters(this, {
   gTextToSubURI: ["@mozilla.org/intl/texttosuburi;1", "nsITextToSubURI"],
   gEnvironment: ["@mozilla.org/process/environment;1", "nsIEnvironment"],
   gChromeReg: ["@mozilla.org/chrome/chrome-registry;1", "nsIChromeRegistry"],
 });
 
-const ArrayBufferInputStream = Components.Constructor(
-  "@mozilla.org/io/arraybuffer-input-stream;1",
-  "nsIArrayBufferInputStream", "setData");
 const BinaryInputStream = Components.Constructor(
   "@mozilla.org/binaryinputstream;1",
   "nsIBinaryInputStream", "setInputStream");
 
 Cu.importGlobalProperties(["XMLHttpRequest"]);
 
 // A text encoder to UTF8, used whenever we commit the cache to disk.
 XPCOMUtils.defineLazyGetter(this, "gEncoder",
@@ -352,20 +349,21 @@ loadListener.prototype = {
  * @param aByteArray Byte array containing the icon payload.
  * @param aContentType Mime type of the payload.
  * @param [optional] aSize desired icon size.
  * @throws if the icon cannot be rescaled or the rescaled icon is too big.
  */
 function rescaleIcon(aByteArray, aContentType, aSize = 32) {
   if (aContentType == "image/svg+xml")
     throw new Error("Cannot rescale SVG image");
-  let buffer = Uint8Array.from(aByteArray).buffer;
+
+  let str = String.fromCharCode.apply(String, new Uint8Array(aByteArray));
+
   let imgTools = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools);
-  let input = new ArrayBufferInputStream(buffer, 0, buffer.byteLength);
-  let container = imgTools.decodeImage(input, aContentType);
+  let container = imgTools.decodeImageBuffer(str, str.length, aContentType);
   let stream = imgTools.encodeScaledImage(container, "image/png", aSize, aSize);
   let size = stream.available();
   if (size > MAX_ICON_SIZE)
     throw new Error("Icon is too big");
   let bis = new BinaryInputStream(stream);
   return [bis.readByteArray(size), "image/png"];
 }
 
--- a/toolkit/mozapps/extensions/internal/LightweightThemeImageOptimizer.jsm
+++ b/toolkit/mozapps/extensions/internal/LightweightThemeImageOptimizer.jsm
@@ -1,16 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 this.EXPORTED_SYMBOLS = ["LightweightThemeImageOptimizer"];
 
+const Cc = Components.classes;
 const Cu = Components.utils;
 const Ci = Components.interfaces;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Services",
   "resource://gre/modules/Services.jsm");
 
@@ -93,24 +94,24 @@ var ImageCropper = {
     inProgress[aTargetFile.path] = true;
 
     function resetInProgress() {
       delete inProgress[aTargetFile.path];
     }
 
     ImageFile.read(aURI, function(aInputStream, aContentType) {
       if (aInputStream && aContentType) {
-        let image = ImageTools.decode(aInputStream, aContentType);
-        if (image && image.width && image.height) {
-          let stream = ImageTools.encode(image, aScreen, aOrigin, aContentType);
-          if (stream) {
-            ImageFile.write(aTargetFile, stream, resetInProgress);
-            return;
+        ImageTools.decode(aInputStream, aContentType, function(aImage) {
+          if (aImage && aImage.width && aImage.height) {
+            let stream = ImageTools.encode(aImage, aScreen, aOrigin, aContentType);
+            if (stream) {
+              ImageFile.write(aTargetFile, stream, resetInProgress);
+            }
           }
-        }
+        });
       }
 
       resetInProgress();
     });
   }
 };
 
 var ImageFile = {
@@ -145,24 +146,30 @@ var ImageFile = {
     });
   }
 };
 
 XPCOMUtils.defineLazyModuleGetter(ImageFile, "_netUtil",
   "resource://gre/modules/NetUtil.jsm", "NetUtil");
 
 var ImageTools = {
-  decode(aInputStream, aContentType) {
-    let outParam = null;
+  decode(aInputStream, aContentType, aCallback) {
+    let callback = {
+      onImageReady(aImage, aStatus) {
+        aCallback(aImage);
+      }
+    };
 
     try {
-      outParam = this._imgTools.decodeImage(aInputStream, aContentType);
-    } catch (e) {}
-
-    return outParam;
+      let threadManager = Cc["@mozilla.org/thread-manager;1"].getService();
+      this._imgTools.decodeImageAsync(aInputStream, aContentType, callback,
+                                      threadManager.currentThread);
+    } catch (e) {
+      aCallback(null);
+    }
   },
 
   encode(aImage, aScreen, aOrigin, aContentType) {
     let stream;
     let width = Math.min(aImage.width, aScreen.width);
     let height = Math.min(aImage.height, aScreen.height);
     let x = aOrigin == ORIGIN_TOP_RIGHT ? aImage.width - width : 0;
 
--- a/widget/windows/WinUtils.cpp
+++ b/widget/windows/WinUtils.cpp
@@ -27,17 +27,16 @@
 #include "nsContentUtils.h"
 
 #include "mozilla/Logging.h"
 
 #include "nsString.h"
 #include "nsDirectoryServiceUtils.h"
 #include "imgIContainer.h"
 #include "imgITools.h"
-#include "nsStringStream.h"
 #include "nsNetUtil.h"
 #include "nsIOutputStream.h"
 #include "nsNetCID.h"
 #include "prtime.h"
 #ifdef MOZ_PLACES
 #include "mozIAsyncFavicons.h"
 #endif
 #include "nsIIconURI.h"
@@ -1261,28 +1260,22 @@ AsyncFaviconDataReady::OnComplete(nsIURI
   nsCOMPtr<nsIFile> icoFile;
   nsresult rv = FaviconHelper::GetOutputIconPath(mNewURI, icoFile, mURLShortcut);
   NS_ENSURE_SUCCESS(rv, rv);
   
   nsAutoString path;
   rv = icoFile->GetPath(path);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  // Convert the obtained favicon data to an input stream
-  nsCOMPtr<nsIInputStream> stream;
-  rv = NS_NewByteInputStream(getter_AddRefs(stream),
-                             reinterpret_cast<const char*>(aData),
-                             aDataLen,
-                             NS_ASSIGNMENT_DEPEND);
-  NS_ENSURE_SUCCESS(rv, rv);
-
   // Decode the image from the format it was returned to us in (probably PNG)
   nsCOMPtr<imgIContainer> container;
   nsCOMPtr<imgITools> imgtool = do_CreateInstance("@mozilla.org/image/tools;1");
-  rv = imgtool->DecodeImage(stream, aMimeType, getter_AddRefs(container));
+  rv = imgtool->DecodeImageBuffer(reinterpret_cast<const char*>(aData),
+                                  aDataLen, aMimeType,
+                                  getter_AddRefs(container));
   NS_ENSURE_SUCCESS(rv, rv);
 
   RefPtr<SourceSurface> surface =
     container->GetFrame(imgIContainer::FRAME_FIRST,
                         imgIContainer::FLAG_SYNC_DECODE |
                         imgIContainer::FLAG_ASYNC_NOTIFY);
   NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE);