Merge inbound to mozilla-central r=merge a=merge
authorMargareta Eliza Balazs <ebalazs@mozilla.com>
Mon, 27 Nov 2017 23:57:42 +0200
changeset 703941 f5f03ee9e6abf77964f8dc1b9d69c6ccd3f655fd
parent 703940 5441160857a6bb70b5b29a7b00d647fc88951b7a (current diff)
parent 703826 cec895f7cedad28bea953572480acefd7bec4d08 (diff)
child 703943 4b1257fe22a289d683e9da955caac42eefb76866
child 703956 0aefb01b1053f31626e91166f913cd20f29e83d9
child 703970 6ee41cc42faee3c41177577d06cf2c188d7940e8
child 703990 e34f7a1be4d692275c3031477b16d90ee129a2e3
child 703992 c3c96d9cc0d43b98142e5a3bb0f8b20312b33555
child 704008 70621dfd321f14a5898b2711a5d7216d2d5bf0e0
child 704012 ffbc7e38c55f55407456e2c23e7beb82f49e9d59
child 704021 a827f2ec7e3eeeb2f971b56610e050d118bf41d8
child 704022 a54cc4af5d0bbbac61d37350ed86bae9e78f28a6
child 704023 c8131e01f66d9fefbbc286712acd0f665dc2f878
child 704024 f478906b229c0999e7318ca4a48d9dd363d66ee9
child 704025 e0cf2015f8b2170212214a02fbaf6b2a3d23ed7d
child 704055 906f28fc37c88172bbcc6b829bc06d39123db9c7
child 704057 f2c610bd6fda84fdb5ee1663dc1a12a921ec6b9c
child 704060 26b8aeb3c9059f7053618819ce5647ff05e15be7
child 704061 2981bbf3bc9d62af74af9047a61abc282286392c
child 704062 f52b3b8816c56ccdb822b60331476a2b284dcefc
child 704063 a7169d616da36597854bf37616228eb72636f2b7
child 704064 310f085c9a5c60c4098d9b5e66335fb4e6eeb255
child 704070 767a95a3aad7aaa2c8637ba5acd4f92d8c484aa0
child 704086 1a529c3baa1a2a97d2131dba6cb9759cabdce1ba
child 704099 efb956d3561ddb3f2828611659a979b3943e6cec
child 704122 b2a7e0fb987e6469959a7a86294099885d4c77aa
child 704123 0ce8fa5d6c074db203626d5866138822fb5ac499
child 704124 fa8d0afdd8e85ef40db466b9f3668348a6999314
child 704127 6161979dc68badaa649b553a270806307c35fcd2
child 704129 6f670ae391b6d053cec6d9cfaa25d94a418e8a47
child 704133 bdc429c9c37a3c706f2da45751e14f17cfb85043
child 704153 530991098a471c3efe8d301b1fca843972fa4483
child 704196 cad15fae970f4d13784f6d3acb586ee14bb4c2c3
child 704223 894f12ac2fa15e22254262bea8991fc3ba86f66e
child 704224 09c567bcda8ed068909413e76e1f675391306c15
child 704225 033cf97fbedf26f8df9a44d3ad250746caa6b41d
child 704227 4860f05a3d7b979aa7429216d5abff020f8405cc
child 704229 5b9fc214848b86be2ddc2789cf2bfe15dc92d35d
child 704230 9b06fb69f41a9bb97ec84d7b9a29be8fd3166256
child 704234 7f87e113d705bf249e324ad4f287945ae479744c
child 704240 0d911716d118add20cccee08ca5b6bd3d9bd523e
child 704282 f220f5af0e46e02ebcc71db7e5c196772d9fde28
child 704288 eaf1a2d73bbd258fa46ccefed1aa5795ffe4928b
child 704358 0adcc723495d9f52e3eacdfc5421da9469629225
child 704677 6fe965cae8d7f560777195eac67fe25c40e738b1
child 704738 2a4f5bb668b3df9776e7f932eb1254704c4b00ae
child 704816 c2d0f3a9dfa79751ce1216a9b1b5b5c7bb6f07be
child 704826 f6275cd13151c4617a02b22db5e48c2eef5d3676
child 704831 d2a4d160d26e6f6996ed1cb3a673576236b8ef23
child 704936 3b8d74fb8c85498c50b042542b3c14f6801dcaf6
child 704985 0a91505c31257e24b6502a3db806c09bd163274a
child 705047 5adb5f80d0055eac99dc294cfd4b64af0f8b8957
child 705050 f4c5575ab20d7f83b0c1b726d30fe1e4c532f089
child 705173 98d946c36410538c1230a65bee5c07fbe4add389
child 705244 8f5042c87c95eb75e8f70f2dfa2daa30695eede4
child 705247 409380f1bed540b917902b729fd0cb981530e241
child 705253 dec777ac4cac4f65ff310fd918f03fd496a84bcc
child 705449 d9a636855cae20a7efeb03cf6b4aaa9e9a8dc319
child 705468 ebf9eb5d702d197f06b52c7ea2742a37f4d4cf06
child 705531 2700cc60f955db0ed41ed39d961383f7e0c3637b
child 705609 57a25ac34fbd7cf8884f3241976efe58569d53e7
child 705621 6faa6c924e46031b74f54e84013e3ec2b265fa63
child 705624 029c3a560ae88bde9c74d52d3ee9b895648e55a7
child 705630 fe18d6ce06a6ddef3b9886b37b843f3370152429
child 705637 284e60f9b95e885fa1da7bb00e52df98a2f71847
child 706052 6ff371a42715acde74622e1065d239de93089055
child 706068 4046c1286946485952eff3ee458f660676872c7a
child 706764 b9ed9353be3055a6d19b7f3280dbfab473a3c97c
child 706765 207a78f5ae392e4f0fc20f04e5e967c630420b69
child 706767 473f686eaaa479ea3a482f505980b4373783db16
child 709454 9bea9278255329e6f57a9927f6d18e9b000a9303
child 710982 b617cc38dbe0a9cf02abafb3a8f25938b5e798d1
child 711709 2ea052e6bf3946bf8e3d3958998199229c76d8f2
child 712938 58b9c403b7b7248d347185f3ccf01c0f766c60f8
child 717569 60b09bc4e356e5db372de7e293aebda47292455e
child 717570 b64d5ebb470f01991cddea66090a6c8c978ead4f
child 718323 25e4f779c94632623e7130bd7cbbb4b34be1ca29
child 718897 6b034c07d98f9ca9ac26de62548c4d6a4ab8d703
push id91002
push userbmo:psnyde2@uic.edu
push dateMon, 27 Nov 2017 22:40:16 +0000
reviewersmerge, merge
milestone59.0a1
Merge inbound to mozilla-central r=merge a=merge
browser/themes/shared/customizableui/panelUI.inc.css
layout/style/ServoStyleSet.cpp
testing/web-platform/meta/MANIFEST.json
--- a/accessible/tests/mochitest/tree/a11y.ini
+++ b/accessible/tests/mochitest/tree/a11y.ini
@@ -40,13 +40,14 @@ skip-if = buildapp == "mulet"
 [test_invalidationlist.html]
 [test_list.html]
 [test_map.html]
 [test_media.html]
 skip-if = buildapp == "mulet"
 [test_select.html]
 [test_tabbox.xul]
 [test_tabbrowser.xul]
+skip-if = os == 'linux' && debug # Bug 1389365
 [test_table.html]
 [test_tree.xul]
 [test_txtcntr.html]
 [test_txtctrl.html]
 [test_txtctrl.xul]
--- a/browser/extensions/screenshots/test/browser/browser.ini
+++ b/browser/extensions/screenshots/test/browser/browser.ini
@@ -1,5 +1,6 @@
 [DEFAULT]
 support-files =
   head.js
 
 [browser_screenshots_ui_check.js]
+skip-if = os == 'win' && debug # Bug 1394967
\ No newline at end of file
--- 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/build/moz.configure/rust.configure
+++ b/build/moz.configure/rust.configure
@@ -1,18 +1,26 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
+
+@imports(_from='os.path', _import='expanduser')
+def add_rustup_path(what):
+    # rustup installs rustc/cargo into ~/.cargo/bin by default,
+    # so look there if the binaries aren't in $PATH.
+    return [what, os.path.join(expanduser('~/.cargo/bin'), what)]
+
+
 # Rust is required by `rust_compiler` below. We allow_missing here
 # to propagate failures to the better error message there.
-rustc = check_prog('RUSTC', ['rustc'], allow_missing=True)
-cargo = check_prog('CARGO', ['cargo'], allow_missing=True)
+rustc = check_prog('RUSTC', add_rustup_path('rustc'), allow_missing=True)
+cargo = check_prog('CARGO', add_rustup_path('cargo'), allow_missing=True)
 
 
 @depends_if(rustc)
 @checking('rustc version', lambda info: info.version)
 def rustc_info(rustc):
     out = check_cmd_output(rustc, '--version', '--verbose').splitlines()
     info = dict((s.strip() for s in line.split(':', 1)) for line in out[1:])
     return namespace(
--- 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/dom/canvas/ImageBitmap.cpp
+++ b/dom/canvas/ImageBitmap.cpp
@@ -8,16 +8,17 @@
 #include "mozilla/CheckedInt.h"
 #include "mozilla/dom/ImageBitmapBinding.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/StructuredCloneTags.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/dom/WorkerRunnable.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/Swizzle.h"
+#include "mozilla/Mutex.h"
 #include "ImageBitmapColorUtils.h"
 #include "ImageBitmapUtils.h"
 #include "ImageUtils.h"
 #include "imgTools.h"
 
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
 
@@ -1073,324 +1074,202 @@ AsyncFulfillImageBitmapPromise(Promise* 
     NS_DispatchToCurrentThread(task); // Actually, to the main-thread.
   } else {
     RefPtr<FulfillImageBitmapPromiseWorkerTask> task =
       new FulfillImageBitmapPromiseWorkerTask(aPromise, aImageBitmap);
     task->Dispatch(); // Actually, to the current worker-thread.
   }
 }
 
-static already_AddRefed<SourceSurface>
-DecodeBlob(Blob& aBlob)
+class CreateImageBitmapFromBlobRunnable;
+class CreateImageBitmapFromBlobHolder;
+
+class CreateImageBitmapFromBlob final : public CancelableRunnable
+                                      , public imgIContainerCallback
 {
-  // Get the internal stream of the blob.
-  nsCOMPtr<nsIInputStream> stream;
-  ErrorResult error;
-  aBlob.Impl()->CreateInputStream(getter_AddRefs(stream), error);
-  if (NS_WARN_IF(error.Failed())) {
-    error.SuppressException();
-    return nullptr;
-  }
+  friend class CreateImageBitmapFromBlobRunnable;
 
-  // Get the MIME type string of the blob.
-  // The type will be checked in the DecodeImage() method.
-  nsAutoString mimeTypeUTF16;
-  aBlob.GetType(mimeTypeUTF16);
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_IMGICONTAINERCALLBACK
 
-  // Get the Component object.
-  nsCOMPtr<imgITools> imgtool = do_GetService(NS_IMGTOOLS_CID);
-  if (NS_WARN_IF(!imgtool)) {
-    return nullptr;
-  }
+  static already_AddRefed<CreateImageBitmapFromBlob>
+  Create(Promise* aPromise,
+         nsIGlobalObject* aGlobal,
+         Blob& aBlob,
+         const Maybe<IntRect>& aCropRect,
+         nsIEventTarget* aMainThreadEventTarget);
 
-  // Decode image.
-  NS_ConvertUTF16toUTF8 mimeTypeUTF8(mimeTypeUTF16); // NS_ConvertUTF16toUTF8 ---|> nsAutoCString
-  nsCOMPtr<imgIContainer> imgContainer;
-  nsresult rv = imgtool->DecodeImage(stream, mimeTypeUTF8, getter_AddRefs(imgContainer));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return nullptr;
-  }
+  NS_IMETHOD Run() override
+  {
+    MOZ_ASSERT(IsCurrentThread());
 
-  // Get the surface out.
-  uint32_t frameFlags = imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_WANT_DATA_SURFACE;
-  uint32_t whichFrame = imgIContainer::FRAME_FIRST;
-  RefPtr<SourceSurface> surface = imgContainer->GetFrame(whichFrame, frameFlags);
+    nsresult rv = StartDecodeAndCropBlob();
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      DecodeAndCropBlobCompletedMainThread(nullptr, rv);
+    }
 
-  if (NS_WARN_IF(!surface)) {
-    return nullptr;
+    return NS_OK;
   }
 
-  return surface.forget();
-}
-
-static already_AddRefed<layers::Image>
-DecodeAndCropBlob(Blob& aBlob, Maybe<IntRect>& aCropRect,
-                  /*Output*/ IntSize& sourceSize)
-{
-  // Decode the blob into a SourceSurface.
-  RefPtr<SourceSurface> surface = DecodeBlob(aBlob);
-
-  if (NS_WARN_IF(!surface)) {
-    return nullptr;
-  }
-
-  // Set the _sourceSize_ output parameter.
-  sourceSize = surface->GetSize();
-
-  // Crop the source surface if needed.
-  RefPtr<SourceSurface> croppedSurface = surface;
+  // Called by the WorkerHolder.
+  void WorkerShuttingDown();
 
-  if (aCropRect.isSome()) {
-    // The blob is just decoded into a RasterImage and not optimized yet, so the
-    // _surface_ we get is a DataSourceSurface which wraps the RasterImage's
-    // raw buffer.
-    //
-    // The _surface_ might already be optimized so that its type is not
-    // SurfaceType::DATA. However, we could keep using the generic cropping and
-    // copying since the decoded buffer is only used in this ImageBitmap so we
-    // should crop it to save memory usage.
-    //
-    // TODO: Bug1189632 is going to refactor this create-from-blob part to
-    //       decode the blob off the main thread. Re-check if we should do
-    //       cropping at this moment again there.
-    RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface();
-    croppedSurface = CropAndCopyDataSourceSurface(dataSurface, aCropRect.ref());
-    aCropRect->MoveTo(0, 0);
-  }
-
-  if (NS_WARN_IF(!croppedSurface)) {
-    return nullptr;
-  }
-
-  // Create an Image from the source surface.
-  RefPtr<layers::Image> image = CreateImageFromSurface(croppedSurface);
-
-  if (NS_WARN_IF(!image)) {
-    return nullptr;
-  }
-
-  return image.forget();
-}
-
-class CreateImageBitmapFromBlob
-{
-protected:
+private:
   CreateImageBitmapFromBlob(Promise* aPromise,
                             nsIGlobalObject* aGlobal,
-                            Blob& aBlob,
-                            const Maybe<IntRect>& aCropRect)
-  : mPromise(aPromise),
-    mGlobalObject(aGlobal),
-    mBlob(&aBlob),
-    mCropRect(aCropRect)
+                            already_AddRefed<nsIInputStream> aInputStream,
+                            const nsACString& aMimeType,
+                            const Maybe<IntRect>& aCropRect,
+                            nsIEventTarget* aMainThreadEventTarget)
+    : CancelableRunnable("dom::CreateImageBitmapFromBlob")
+    , mMutex("dom::CreateImageBitmapFromBlob::mMutex")
+    , mPromise(aPromise)
+    , mGlobalObject(aGlobal)
+    , mInputStream(Move(aInputStream))
+    , mMimeType(aMimeType)
+    , mCropRect(aCropRect)
+    , mOriginalCropRect(aCropRect)
+    , mMainThreadEventTarget(aMainThreadEventTarget)
+    , mThread(GetCurrentVirtualThread())
   {
   }
 
   virtual ~CreateImageBitmapFromBlob()
   {
   }
 
-  // Returns true on success, false on failure.
-  bool DoCreateImageBitmapFromBlob()
+  bool IsCurrentThread() const
   {
-    RefPtr<ImageBitmap> imageBitmap = CreateImageBitmap();
-
-    // handle errors while creating ImageBitmap
-    // (1) error occurs during reading of the object
-    // (2) the image data is not in a supported file format
-    // (3) the image data is corrupted
-    // All these three cases should reject the promise with "InvalidStateError"
-    // DOMException
-    if (!imageBitmap) {
-      return false;
-    }
-
-    if (imageBitmap && mCropRect.isSome()) {
-      ErrorResult rv;
-      imageBitmap->SetPictureRect(mCropRect.ref(), rv);
-
-      if (rv.Failed()) {
-        mPromise->MaybeReject(rv);
-        return false;
-      }
-    }
-
-    imageBitmap->mAllocatedImageData = true;
-
-    mPromise->MaybeResolve(imageBitmap);
-    return true;
+    return mThread == GetCurrentVirtualThread();
   }
 
-  // Will return null on failure.  In that case, mPromise will already
-  // be rejected with the right thing.
-  virtual already_AddRefed<ImageBitmap> CreateImageBitmap() = 0;
+  // Called on the owning thread.
+  nsresult StartDecodeAndCropBlob();
+
+  // Will be called when the decoding + cropping is completed on the
+  // main-thread. This could the not the owning thread!
+  void DecodeAndCropBlobCompletedMainThread(layers::Image* aImage,
+                                            nsresult aStatus);
+
+  // Will be called when the decoding + cropping is completed on the owning
+  // thread.
+  void DecodeAndCropBlobCompletedOwningThread(layers::Image* aImage,
+                                              nsresult aStatus);
+
+  // This is called on the main-thread only.
+  nsresult DecodeAndCropBlob();
+
+  Mutex mMutex;
 
+  // The access to this object is protected by mutex but is always nullified on
+  // the owning thread.
+  UniquePtr<CreateImageBitmapFromBlobHolder> mWorkerHolder;
+
+  // Touched only on the owning thread.
   RefPtr<Promise> mPromise;
+
+  // Touched only on the owning thread.
   nsCOMPtr<nsIGlobalObject> mGlobalObject;
-  RefPtr<mozilla::dom::Blob> mBlob;
+
+  nsCOMPtr<nsIInputStream> mInputStream;
+  nsCString mMimeType;
   Maybe<IntRect> mCropRect;
+  Maybe<IntRect> mOriginalCropRect;
+  IntSize mSourceSize;
+
+  nsCOMPtr<nsIEventTarget> mMainThreadEventTarget;
+  void* mThread;
 };
 
-class CreateImageBitmapFromBlobTask final : public Runnable,
-                                            public CreateImageBitmapFromBlob
+NS_IMPL_ISUPPORTS_INHERITED(CreateImageBitmapFromBlob, CancelableRunnable,
+                            imgIContainerCallback)
+
+class CreateImageBitmapFromBlobRunnable : public WorkerRunnable
 {
 public:
-  CreateImageBitmapFromBlobTask(Promise* aPromise,
-                                nsIGlobalObject* aGlobal,
-                                Blob& aBlob,
-                                const Maybe<IntRect>& aCropRect)
-    : Runnable("dom::CreateImageBitmapFromBlobTask")
-    , CreateImageBitmapFromBlob(aPromise, aGlobal, aBlob, aCropRect)
+  explicit CreateImageBitmapFromBlobRunnable(WorkerPrivate* aWorkerPrivate,
+                                             CreateImageBitmapFromBlob* aTask,
+                                             layers::Image* aImage,
+                                             nsresult aStatus)
+    : WorkerRunnable(aWorkerPrivate)
+    , mTask(aTask)
+    , mImage(aImage)
+    , mStatus(aStatus)
+  {}
+
+  bool
+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
   {
-  }
-
-  NS_IMETHOD Run() override
-  {
-    DoCreateImageBitmapFromBlob();
-    return NS_OK;
+    mTask->DecodeAndCropBlobCompletedOwningThread(mImage, mStatus);
+    return true;
   }
 
 private:
-  already_AddRefed<ImageBitmap> CreateImageBitmap() override
-  {
-    // _sourceSize_ is used to get the original size of the source image,
-    // before being cropped.
-    IntSize sourceSize;
-
-    // Keep the orignal cropping rectangle because the mCropRect might be
-    // modified in DecodeAndCropBlob().
-    Maybe<IntRect> originalCropRect = mCropRect;
-
-    RefPtr<layers::Image> data = DecodeAndCropBlob(*mBlob, mCropRect, sourceSize);
-
-    if (NS_WARN_IF(!data)) {
-      mPromise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
-      return nullptr;
-    }
-
-    // Create ImageBitmap object.
-    RefPtr<ImageBitmap> imageBitmap = new ImageBitmap(mGlobalObject, data);
-
-    // Set mIsCroppingAreaOutSideOfSourceImage.
-    imageBitmap->SetIsCroppingAreaOutSideOfSourceImage(sourceSize, originalCropRect);
-
-    return imageBitmap.forget();
-  }
+  RefPtr<CreateImageBitmapFromBlob> mTask;
+  RefPtr<layers::Image> mImage;
+  nsresult mStatus;
 };
 
-class CreateImageBitmapFromBlobWorkerTask final : public WorkerSameThreadRunnable,
-                                                  public CreateImageBitmapFromBlob
+// This class keeps the worker alive and it informs CreateImageBitmapFromBlob
+// when it goes away.
+class CreateImageBitmapFromBlobHolder final : public WorkerHolder
 {
-  // This is a synchronous task.
-  class DecodeBlobInMainThreadSyncTask final : public WorkerMainThreadRunnable
-  {
-  public:
-    DecodeBlobInMainThreadSyncTask(WorkerPrivate* aWorkerPrivate,
-                                   Blob& aBlob,
-                                   Maybe<IntRect>& aCropRect,
-                                   layers::Image** aImage,
-                                   IntSize& aSourceSize)
-    : WorkerMainThreadRunnable(aWorkerPrivate,
-                               NS_LITERAL_CSTRING("ImageBitmap :: Create Image from Blob"))
-    , mBlob(aBlob)
-    , mCropRect(aCropRect)
-    , mImage(aImage)
-    , mSourceSize(aSourceSize)
-    {
-    }
-
-    bool MainThreadRun() override
-    {
-      RefPtr<layers::Image> image = DecodeAndCropBlob(mBlob, mCropRect, mSourceSize);
+public:
+  CreateImageBitmapFromBlobHolder(WorkerPrivate* aWorkerPrivate,
+                                  CreateImageBitmapFromBlob* aTask)
+    : WorkerHolder("CreateImageBitmapFromBlobHolder")
+    , mWorkerPrivate(aWorkerPrivate)
+    , mTask(aTask)
+    , mNotified(false)
+  {}
 
-      if (NS_WARN_IF(!image)) {
-        return true;
-      }
-
-      image.forget(mImage);
-
-      return true;
+  bool Notify(Status aStatus) override
+  {
+    if (!mNotified) {
+      mNotified = true;
+      mTask->WorkerShuttingDown();
     }
-
-  private:
-    Blob& mBlob;
-    Maybe<IntRect>& mCropRect;
-    layers::Image** mImage;
-    IntSize mSourceSize;
-  };
-
-public:
-  CreateImageBitmapFromBlobWorkerTask(Promise* aPromise,
-                                  nsIGlobalObject* aGlobal,
-                                  mozilla::dom::Blob& aBlob,
-                                  const Maybe<IntRect>& aCropRect)
-  : WorkerSameThreadRunnable(GetCurrentThreadWorkerPrivate()),
-    CreateImageBitmapFromBlob(aPromise, aGlobal, aBlob, aCropRect)
-  {
+    return true;
   }
 
-  bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
+  WorkerPrivate* GetWorkerPrivate() const
   {
-    return DoCreateImageBitmapFromBlob();
+    return mWorkerPrivate;
   }
 
 private:
-  already_AddRefed<ImageBitmap> CreateImageBitmap() override
-  {
-    // _sourceSize_ is used to get the original size of the source image,
-    // before being cropped.
-    IntSize sourceSize;
-
-    // Keep the orignal cropping rectangle because the mCropRect might be
-    // modified in DecodeAndCropBlob().
-    Maybe<IntRect> originalCropRect = mCropRect;
-
-    RefPtr<layers::Image> data;
-
-    ErrorResult rv;
-    RefPtr<DecodeBlobInMainThreadSyncTask> task =
-      new DecodeBlobInMainThreadSyncTask(mWorkerPrivate, *mBlob, mCropRect,
-                                         getter_AddRefs(data), sourceSize);
-    task->Dispatch(Terminating, rv); // This is a synchronous call.
-
-    // In case the worker is terminating, this rejection can be handled.
-    if (NS_WARN_IF(rv.Failed())) {
-      mPromise->MaybeReject(rv);
-      return nullptr;
-    }
-
-    if (NS_WARN_IF(!data)) {
-      mPromise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
-      return nullptr;
-    }
-
-    // Create ImageBitmap object.
-    RefPtr<ImageBitmap> imageBitmap = new ImageBitmap(mGlobalObject, data);
-
-    // Set mIsCroppingAreaOutSideOfSourceImage.
-    imageBitmap->SetIsCroppingAreaOutSideOfSourceImage(sourceSize, originalCropRect);
-
-    return imageBitmap.forget();
-  }
-
+  WorkerPrivate* mWorkerPrivate;
+  RefPtr<CreateImageBitmapFromBlob> mTask;
+  bool mNotified;
 };
 
 static void
 AsyncCreateImageBitmapFromBlob(Promise* aPromise, nsIGlobalObject* aGlobal,
                                Blob& aBlob, const Maybe<IntRect>& aCropRect)
 {
+  // Let's identify the main-thread event target.
+  nsCOMPtr<nsIEventTarget> mainThreadEventTarget;
   if (NS_IsMainThread()) {
-    nsCOMPtr<nsIRunnable> task =
-      new CreateImageBitmapFromBlobTask(aPromise, aGlobal, aBlob, aCropRect);
-    NS_DispatchToCurrentThread(task); // Actually, to the main-thread.
+     mainThreadEventTarget = aGlobal->EventTargetFor(TaskCategory::Other);
   } else {
-    RefPtr<CreateImageBitmapFromBlobWorkerTask> task =
-      new CreateImageBitmapFromBlobWorkerTask(aPromise, aGlobal, aBlob, aCropRect);
-    task->Dispatch(); // Actually, to the current worker-thread.
+    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+    MOZ_ASSERT(workerPrivate);
+    mainThreadEventTarget = workerPrivate->MainThreadEventTarget();
   }
+
+  RefPtr<CreateImageBitmapFromBlob> task =
+    CreateImageBitmapFromBlob::Create(aPromise, aGlobal, aBlob, aCropRect,
+                                      mainThreadEventTarget);
+  if (NS_WARN_IF(!task)) {
+    aPromise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return;
+  }
+
+  NS_DispatchToCurrentThread(task);
 }
 
 /* static */ already_AddRefed<Promise>
 ImageBitmap::Create(nsIGlobalObject* aGlobal, const ImageBitmapSource& aSrc,
                     const Maybe<gfx::IntRect>& aCropRect, ErrorResult& aRv)
 {
   MOZ_ASSERT(aGlobal);
 
@@ -2138,10 +2017,250 @@ ImageBitmap::GetAllocatedSize() const
 }
 
 size_t
 BindingJSObjectMallocBytes(ImageBitmap* aBitmap)
 {
   return aBitmap->GetAllocatedSize();
 }
 
+/* static */ already_AddRefed<CreateImageBitmapFromBlob>
+CreateImageBitmapFromBlob::Create(Promise* aPromise,
+                                  nsIGlobalObject* aGlobal,
+                                  Blob& aBlob,
+                                  const Maybe<IntRect>& aCropRect,
+                                  nsIEventTarget* aMainThreadEventTarget)
+{
+  // Get the internal stream of the blob.
+  nsCOMPtr<nsIInputStream> stream;
+  ErrorResult error;
+  aBlob.Impl()->CreateInputStream(getter_AddRefs(stream), error);
+  if (NS_WARN_IF(error.Failed())) {
+    return nullptr;
+  }
+
+  // Get the MIME type string of the blob.
+  // The type will be checked in the DecodeImageAsync() method.
+  nsAutoString mimeTypeUTF16;
+  aBlob.Impl()->GetType(mimeTypeUTF16);
+  NS_ConvertUTF16toUTF8 mimeType(mimeTypeUTF16);
+
+  RefPtr<CreateImageBitmapFromBlob> task =
+    new CreateImageBitmapFromBlob(aPromise, aGlobal, stream.forget(), mimeType,
+                                  aCropRect, aMainThreadEventTarget);
+
+  // Nothing to do for the main-thread.
+  if (NS_IsMainThread()) {
+    return task.forget();
+  }
+
+  // Let's use a WorkerHolder to keep the worker alive if this is not the
+  // main-thread.
+  WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+  MOZ_ASSERT(workerPrivate);
+
+  UniquePtr<CreateImageBitmapFromBlobHolder> holder(
+    new CreateImageBitmapFromBlobHolder(workerPrivate, task));
+
+  if (!holder->HoldWorker(workerPrivate, Terminating)) {
+    return nullptr;
+  }
+
+  task->mWorkerHolder = Move(holder);
+  return task.forget();
+}
+
+nsresult
+CreateImageBitmapFromBlob::StartDecodeAndCropBlob()
+{
+  MOZ_ASSERT(IsCurrentThread());
+
+  // Workers.
+  if (!NS_IsMainThread()) {
+    RefPtr<CreateImageBitmapFromBlob> self = this;
+    nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
+      "CreateImageBitmapFromBlob::DecodeAndCropBlob",
+      [self]() {
+        nsresult rv = self->DecodeAndCropBlob();
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          self->DecodeAndCropBlobCompletedMainThread(nullptr, rv);
+        }
+      });
+
+    return mMainThreadEventTarget->Dispatch(r.forget());
+  }
+
+  // Main-thread.
+  return DecodeAndCropBlob();
+}
+
+nsresult
+CreateImageBitmapFromBlob::DecodeAndCropBlob()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  // Get the Component object.
+  nsCOMPtr<imgITools> imgtool = do_GetService(NS_IMGTOOLS_CID);
+  if (NS_WARN_IF(!imgtool)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // Decode image.
+  nsCOMPtr<imgIContainer> imgContainer;
+  nsresult rv = imgtool->DecodeImageAsync(mInputStream, mMimeType, this,
+                                          mMainThreadEventTarget);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+CreateImageBitmapFromBlob::OnImageReady(imgIContainer* aImgContainer,
+                                        nsresult aStatus)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (NS_FAILED(aStatus)) {
+    DecodeAndCropBlobCompletedMainThread(nullptr, aStatus);
+    return NS_OK;
+  }
+
+  MOZ_ASSERT(aImgContainer);
+
+  // Get the surface out.
+  uint32_t frameFlags = imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_WANT_DATA_SURFACE;
+  uint32_t whichFrame = imgIContainer::FRAME_FIRST;
+  RefPtr<SourceSurface> surface = aImgContainer->GetFrame(whichFrame, frameFlags);
+
+  if (NS_WARN_IF(!surface)) {
+    DecodeAndCropBlobCompletedMainThread(nullptr, NS_ERROR_FAILURE);
+    return NS_OK;
+  }
+
+  // Store the sourceSize value for the DecodeAndCropBlobCompletedMainThread call.
+  mSourceSize = surface->GetSize();
+
+  // Crop the source surface if needed.
+  RefPtr<SourceSurface> croppedSurface = surface;
+
+  if (mCropRect.isSome()) {
+    // The blob is just decoded into a RasterImage and not optimized yet, so the
+    // _surface_ we get is a DataSourceSurface which wraps the RasterImage's
+    // raw buffer.
+    //
+    // The _surface_ might already be optimized so that its type is not
+    // SurfaceType::DATA. However, we could keep using the generic cropping and
+    // copying since the decoded buffer is only used in this ImageBitmap so we
+    // should crop it to save memory usage.
+    //
+    // TODO: Bug1189632 is going to refactor this create-from-blob part to
+    //       decode the blob off the main thread. Re-check if we should do
+    //       cropping at this moment again there.
+    RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface();
+    croppedSurface = CropAndCopyDataSourceSurface(dataSurface, mCropRect.ref());
+    mCropRect->MoveTo(0, 0);
+  }
+
+  if (NS_WARN_IF(!croppedSurface)) {
+    DecodeAndCropBlobCompletedMainThread(nullptr, NS_ERROR_FAILURE);
+    return NS_OK;
+  }
+
+  // Create an Image from the source surface.
+  RefPtr<layers::Image> image = CreateImageFromSurface(croppedSurface);
+
+  if (NS_WARN_IF(!image)) {
+    DecodeAndCropBlobCompletedMainThread(nullptr, NS_ERROR_FAILURE);
+    return NS_OK;
+  }
+
+  DecodeAndCropBlobCompletedMainThread(image, NS_OK);
+  return NS_OK;
+}
+
+void
+CreateImageBitmapFromBlob::DecodeAndCropBlobCompletedMainThread(layers::Image* aImage,
+                                                                nsresult aStatus)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!IsCurrentThread()) {
+    MutexAutoLock lock(mMutex);
+
+    if (!mWorkerHolder) {
+      // The worker is already gone.
+      return;
+    }
+
+    RefPtr<CreateImageBitmapFromBlobRunnable> r =
+      new CreateImageBitmapFromBlobRunnable(mWorkerHolder->GetWorkerPrivate(),
+                                            this, aImage, aStatus);
+    r->Dispatch();
+    return;
+  }
+
+  DecodeAndCropBlobCompletedOwningThread(aImage, aStatus);
+}
+
+void
+CreateImageBitmapFromBlob::DecodeAndCropBlobCompletedOwningThread(layers::Image* aImage,
+                                                                  nsresult aStatus)
+{
+  MOZ_ASSERT(IsCurrentThread());
+
+  if (!mPromise) {
+    // The worker is going to be released soon. No needs to continue.
+    return;
+  }
+
+  // Let's release what has to be released on the owning thread.
+  auto raii = MakeScopeExit([&] {
+    // Doing this we also release the worker.
+    mWorkerHolder = nullptr;
+
+    mPromise = nullptr;
+    mGlobalObject = nullptr;
+  });
+
+  if (NS_WARN_IF(NS_FAILED(aStatus))) {
+    mPromise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return;
+  }
+
+  // Create ImageBitmap object.
+  RefPtr<ImageBitmap> imageBitmap = new ImageBitmap(mGlobalObject, aImage);
+
+  // Set mIsCroppingAreaOutSideOfSourceImage.
+  imageBitmap->SetIsCroppingAreaOutSideOfSourceImage(mSourceSize,
+                                                     mOriginalCropRect);
+
+  if (mCropRect.isSome()) {
+    ErrorResult rv;
+    imageBitmap->SetPictureRect(mCropRect.ref(), rv);
+
+    if (rv.Failed()) {
+      mPromise->MaybeReject(rv);
+      return;
+    }
+  }
+
+  imageBitmap->mAllocatedImageData = true;
+
+  mPromise->MaybeResolve(imageBitmap);
+}
+
+void
+CreateImageBitmapFromBlob::WorkerShuttingDown()
+{
+  MOZ_ASSERT(IsCurrentThread());
+
+  MutexAutoLock lock(mMutex);
+
+  // Let's release all the non-thread-safe objects now.
+  mWorkerHolder = nullptr;
+  mPromise = nullptr;
+  mGlobalObject = nullptr;
+}
+
 } // namespace dom
 } // namespace mozilla
new file mode 100644
index 0000000000000000000000000000000000000000..48c454d27c9c46574597f80fdd64ecd4d254577d
GIT binary patch
literal 361
zc$}S%Jr2S!427SY62(c{IxR3UaS;ZjN^lMi!+jW87&rt!3pWYQNSWZXWLZz|+0M88
zg6zlL2%-urE#xP*Fjb*+qtv!8IBRE?o%igRee9wSZD4Lv({>_BeC&r_2A!oWp^`MU
z)1U+n@-z8@<Z!^j&>B@DlPF)Y&{y$^Re3H`6{;?Yrf$c@oxa@<L_>PVBy4b|?sS}5
I{)F=V4K5EK<NyEw
--- a/dom/file/ipc/tests/mochitest.ini
+++ b/dom/file/ipc/tests/mochitest.ini
@@ -1,6 +1,8 @@
 [DEFAULT]
 support-files =
-  script_file.js
 
 [test_ipcBlob_fileReaderSync.html]
+support-files = script_file.js
 [test_ipcBlob_workers.html]
+[test_ipcBlob_createImageBitmap.html]
+support-files = green.jpg
new file mode 100644
--- /dev/null
+++ b/dom/file/ipc/tests/test_ipcBlob_createImageBitmap.html
@@ -0,0 +1,84 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test IPCBlob and CreateImageBitmap</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<script type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+function test_mainThread() {
+  let bc = new BroadcastChannel('testMainThread');
+  bc.onmessage = e => {
+    createImageBitmap(e.data).then(image => {
+      ok(image.height, "this image has a valid size.");
+    }, () => {
+      ok(false, "error creating the image!");
+    }).then(next);
+  }
+
+  fetch('green.jpg').then(r => r.blob()).then(blob => {
+    let bc = new BroadcastChannel('testMainThread');
+    bc.postMessage(blob);
+  });
+}
+
+function test_worker() {
+  function workerScript() {
+    function ok(a, msg) { postMessage({ type: 'test', status: !!a, msg }); };
+    function finish() { postMessage({ type: 'finish' }); };
+
+    let bc = new BroadcastChannel('testWorker');
+    bc.onmessage = e => {
+      createImageBitmap(e.data).then(image => {
+        ok(image.height, "this image has a valid size.");
+      }, () => {
+        ok(false, "error creating the image!");
+      }).then(finish);
+    }
+
+    fetch('http://mochi.test:8888/tests/dom/file/ipc/tests/green.jpg').then(r => r.blob()).then(blob => {
+      let bc = new BroadcastChannel('testWorker');
+      bc.postMessage(blob);
+    });
+  }
+  let workerUrl = URL.createObjectURL(new Blob(["(", workerScript.toSource(), ")()"]));
+  let worker = new Worker(workerUrl);
+
+  worker.onmessage = event => {
+    if (event.data.type == 'test') {
+      ok(event.data.status, event.data.msg);
+      return;
+    }
+
+    if (event.data.type == 'finish') {
+      next();
+    }
+  }
+}
+
+let tests = [
+  test_mainThread,
+  test_worker,
+];
+
+function next() {
+  if (!tests.length) {
+    SimpleTest.finish();
+    return;
+  }
+
+  let test = tests.shift();
+  test();
+}
+
+next();
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -82,17 +82,16 @@
 
 #ifdef MOZ_GECKO_PROFILER
 #include "ChildProfilerController.h"
 #endif
 
 #if defined(MOZ_CONTENT_SANDBOX)
 #include "mozilla/SandboxSettings.h"
 #if defined(XP_WIN)
-#define TARGET_SANDBOX_EXPORTS
 #include "mozilla/sandboxTarget.h"
 #elif defined(XP_LINUX)
 #include "mozilla/Sandbox.h"
 #include "mozilla/SandboxInfo.h"
 
 // Remove this include with Bug 1104619
 #include "CubebUtils.h"
 #elif defined(XP_MACOSX)
--- a/dom/media/CloneableWithRangeMediaResource.cpp
+++ b/dom/media/CloneableWithRangeMediaResource.cpp
@@ -39,17 +39,20 @@ public:
 
   nsresult
   Read(char* aBuffer, uint32_t aSize, uint32_t* aRead)
   {
     uint32_t done = 0;
     do {
       uint32_t read;
       nsresult rv = SyncRead(aBuffer + done, aSize - done, &read);
-      if (NS_WARN_IF(NS_FAILED(rv)) || read == 0) {
+      if (NS_SUCCEEDED(rv) && read == 0) {
+        break;
+      }
+      if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       done += read;
     } while(done != aSize);
 
     *aRead = done;
     return NS_OK;
   }
@@ -74,17 +77,17 @@ private:
 
   nsresult
   SyncRead(char* aBuffer, uint32_t aSize, uint32_t* aRead)
   {
     while (1) {
       nsresult rv = mStream->Read(aBuffer, aSize, aRead);
       // All good.
       if (rv == NS_BASE_STREAM_CLOSED || NS_SUCCEEDED(rv)) {
-        return rv;
+        return NS_OK;
       }
 
       // An error.
       if (NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK) {
         return rv;
       }
 
       // We need to proceed async.
--- a/dom/plugins/ipc/PluginProcessChild.cpp
+++ b/dom/plugins/ipc/PluginProcessChild.cpp
@@ -18,17 +18,16 @@
 // An undocumented CoreGraphics framework method, present in the same form
 // since at least OS X 10.5.
 extern "C" CGError CGSSetDebugOptions(int options);
 #endif
 
 #ifdef XP_WIN
 bool ShouldProtectPluginCurrentDirectory(char16ptr_t pluginFilePath);
 #if defined(MOZ_SANDBOX)
-#define TARGET_SANDBOX_EXPORTS
 #include "mozilla/sandboxTarget.h"
 #endif
 #endif
 
 using mozilla::ipc::IOThreadChild;
 
 #ifdef OS_WIN
 #include "nsSetDllDirectory.h"
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -248,36 +248,16 @@ GetWorkerPref(const nsACString& aPref,
     else {
       result = aDefault;
     }
   }
 
   return result;
 }
 
-// This fn creates a key for a SharedWorker that contains the name, script
-// spec, and the serialized origin attributes:
-// "name|scriptSpec^key1=val1&key2=val2&key3=val3"
-void
-GenerateSharedWorkerKey(const nsACString& aScriptSpec,
-                        const nsAString& aName,
-                        const OriginAttributes& aAttrs,
-                        nsCString& aKey)
-{
-  nsAutoCString suffix;
-  aAttrs.CreateSuffix(suffix);
-
-  aKey.Truncate();
-  aKey.SetCapacity(aName.Length() + aScriptSpec.Length() + suffix.Length() + 2);
-  aKey.Append(NS_ConvertUTF16toUTF8(aName));
-  aKey.Append('|');
-  aKey.Append(aScriptSpec);
-  aKey.Append(suffix);
-}
-
 void
 LoadContextOptions(const char* aPrefName, void* /* aClosure */)
 {
   AssertIsOnMainThread();
 
   RuntimeService* rts = RuntimeService::GetService();
   if (!rts) {
     // May be shutting down, just bail.
@@ -1597,26 +1577,33 @@ RuntimeService::RegisterWorker(WorkerPri
     else if (isServiceWorker) {
       domainInfo->mActiveServiceWorkers.AppendElement(aWorkerPrivate);
     }
     else {
       domainInfo->mActiveWorkers.AppendElement(aWorkerPrivate);
     }
 
     if (isSharedWorker) {
-      const nsString& sharedWorkerName(aWorkerPrivate->WorkerName());
-      nsAutoCString key;
-      GenerateSharedWorkerKey(sharedWorkerScriptSpec, sharedWorkerName,
-                              aWorkerPrivate->GetOriginAttributes(), key);
-      MOZ_ASSERT(!domainInfo->mSharedWorkerInfos.Get(key));
-
-      SharedWorkerInfo* sharedWorkerInfo =
+#ifdef DEBUG
+      for (const UniquePtr<SharedWorkerInfo>& data : domainInfo->mSharedWorkerInfos) {
+         if (data->mScriptSpec == sharedWorkerScriptSpec &&
+             data->mName == aWorkerPrivate->WorkerName() &&
+             // We want to be sure that the window's principal subsumes the
+             // SharedWorker's principal and vice versa.
+             data->mWorkerPrivate->GetPrincipal()->Subsumes(aWorkerPrivate->GetPrincipal()) &&
+             aWorkerPrivate->GetPrincipal()->Subsumes(data->mWorkerPrivate->GetPrincipal())) {
+           MOZ_CRASH("We should not instantiate a new SharedWorker!");
+         }
+      }
+#endif
+
+      UniquePtr<SharedWorkerInfo> sharedWorkerInfo(
         new SharedWorkerInfo(aWorkerPrivate, sharedWorkerScriptSpec,
-                             sharedWorkerName);
-      domainInfo->mSharedWorkerInfos.Put(key, sharedWorkerInfo);
+                             aWorkerPrivate->WorkerName()));
+      domainInfo->mSharedWorkerInfos.AppendElement(Move(sharedWorkerInfo));
     }
   }
 
   // From here on out we must call UnregisterWorker if something fails!
   if (parent) {
     if (!parent->AddChildWorker(aWorkerPrivate)) {
       UnregisterWorker(aWorkerPrivate);
       return false;
@@ -1666,28 +1653,21 @@ RuntimeService::RegisterWorker(WorkerPri
   }
   return true;
 }
 
 void
 RuntimeService::RemoveSharedWorker(WorkerDomainInfo* aDomainInfo,
                                    WorkerPrivate* aWorkerPrivate)
 {
-  for (auto iter = aDomainInfo->mSharedWorkerInfos.Iter();
-       !iter.Done();
-       iter.Next()) {
-    SharedWorkerInfo* data = iter.UserData();
+  for (uint32_t i = 0; i < aDomainInfo->mSharedWorkerInfos.Length(); ++i) {
+    const UniquePtr<SharedWorkerInfo>& data =
+      aDomainInfo->mSharedWorkerInfos[i];
     if (data->mWorkerPrivate == aWorkerPrivate) {
-#ifdef DEBUG
-      nsAutoCString key;
-      GenerateSharedWorkerKey(data->mScriptSpec, data->mName,
-                              aWorkerPrivate->GetOriginAttributes(), key);
-      MOZ_ASSERT(iter.Key() == key);
-#endif
-      iter.Remove();
+      aDomainInfo->mSharedWorkerInfos.RemoveElementAt(i);
       break;
     }
   }
 }
 
 void
 RuntimeService::UnregisterWorker(WorkerPrivate* aWorkerPrivate)
 {
@@ -2494,31 +2474,35 @@ RuntimeService::CreateSharedWorkerFromLo
   AssertIsOnMainThread();
   MOZ_ASSERT(aLoadInfo);
   MOZ_ASSERT(aLoadInfo->mResolvedScriptURI);
 
   RefPtr<WorkerPrivate> workerPrivate;
   {
     MutexAutoLock lock(mMutex);
 
-    WorkerDomainInfo* domainInfo;
-    SharedWorkerInfo* sharedWorkerInfo;
-
     nsCString scriptSpec;
     nsresult rv = aLoadInfo->mResolvedScriptURI->GetSpec(scriptSpec);
     NS_ENSURE_SUCCESS(rv, rv);
 
     MOZ_ASSERT(aLoadInfo->mPrincipal);
-    nsAutoCString key;
-    GenerateSharedWorkerKey(scriptSpec, aName,
-        aLoadInfo->mPrincipal->OriginAttributesRef(), key);
-
-    if (mDomainMap.Get(aLoadInfo->mDomain, &domainInfo) &&
-        domainInfo->mSharedWorkerInfos.Get(key, &sharedWorkerInfo)) {
-      workerPrivate = sharedWorkerInfo->mWorkerPrivate;
+
+    WorkerDomainInfo* domainInfo;
+    if (mDomainMap.Get(aLoadInfo->mDomain, &domainInfo)) {
+      for (const UniquePtr<SharedWorkerInfo>& data : domainInfo->mSharedWorkerInfos) {
+        if (data->mScriptSpec == scriptSpec &&
+            data->mName == aName &&
+            // We want to be sure that the window's principal subsumes the
+            // SharedWorker's principal and vice versa.
+            aLoadInfo->mPrincipal->Subsumes(data->mWorkerPrivate->GetPrincipal()) &&
+            data->mWorkerPrivate->GetPrincipal()->Subsumes(aLoadInfo->mPrincipal)) {
+          workerPrivate = data->mWorkerPrivate;
+          break;
+        }
+      }
     }
   }
 
   // Keep a reference to the window before spawning the worker. If the worker is
   // a Shared/Service worker and the worker script loads and executes before
   // the SharedWorker object itself is created before then WorkerScriptLoaded()
   // will reset the loadInfo's window.
   nsCOMPtr<nsPIDOMWindowInner> window = aLoadInfo->mWindow;
--- a/dom/workers/RuntimeService.h
+++ b/dom/workers/RuntimeService.h
@@ -40,17 +40,17 @@ class RuntimeService final : public nsIO
   };
 
   struct WorkerDomainInfo
   {
     nsCString mDomain;
     nsTArray<WorkerPrivate*> mActiveWorkers;
     nsTArray<WorkerPrivate*> mActiveServiceWorkers;
     nsTArray<WorkerPrivate*> mQueuedWorkers;
-    nsClassHashtable<nsCStringHashKey, SharedWorkerInfo> mSharedWorkerInfos;
+    nsTArray<UniquePtr<SharedWorkerInfo>> mSharedWorkerInfos;
     uint32_t mChildWorkerCount;
 
     WorkerDomainInfo()
     : mActiveWorkers(1), mChildWorkerCount(0)
     { }
 
     uint32_t
     ActiveWorkerCount() const
--- a/gfx/ipc/GPUProcessImpl.cpp
+++ b/gfx/ipc/GPUProcessImpl.cpp
@@ -3,17 +3,16 @@
 /* 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/. */
 #include "GPUProcessImpl.h"
 #include "mozilla/ipc/IOThreadChild.h"
 #include "nsXPCOM.h"
 
 #if defined(OS_WIN) && defined(MOZ_SANDBOX)
-#define TARGET_SANDBOX_EXPORTS
 #include "mozilla/sandboxTarget.h"
 #endif
 
 namespace mozilla {
 namespace gfx {
 
 using namespace ipc;
 
--- a/gfx/layers/client/ClientPaintedLayer.cpp
+++ b/gfx/layers/client/ClientPaintedLayer.cpp
@@ -61,23 +61,17 @@ ClientPaintedLayer::CanRecordLayer(Readb
     return false;
   }
 
   // Not supported yet
   if (aReadback && UsedForReadback()) {
     return false;
   }
 
-  // If we have mask layers, we have to render those first
-  // In this case, don't record for now.
-  if (GetMaskLayer()) {
-    return false;
-  }
-
-  return GetAncestorMaskLayerCount() == 0;
+  return true;
 }
 
 void
 ClientPaintedLayer::UpdateContentClient(PaintState& aState)
 {
   Mutated();
 
   AddToValidRegion(aState.mRegionToDraw);
--- a/image/imgITools.idl
+++ b/image/imgITools.idl
@@ -1,40 +1,69 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  *
  * 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/. */
 
 #include "nsISupports.idl"
 
+interface nsIEventTarget;
 interface nsIInputStream;
 interface imgIContainer;
 interface imgILoader;
 interface imgICache;
 interface nsIDOMDocument;
 interface imgIScriptedNotificationObserver;
 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 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.
      *
      * @param aStream
      *        An input stream for an encoded image file.
      * @param aMimeType
      *        Type of image in the stream.
+     * @param aCallback
+     *        The callback is executed when the imgContainer is fully created.
+     * @param aEventTarget
+     *        This eventTarget is used to execute aCallback
      */
-    imgIContainer decodeImage(in nsIInputStream aStream,
-                              in ACString aMimeType);
+    void decodeImageAsync(in nsIInputStream aStream,
+                          in ACString aMimeType,
+                          in imgIContainerCallback aCallback,
+                          in nsIEventTarget aEventTarget);
 
     /**
      * encodeImage
      * Caller provides an image container, and the mime type it should be
      * encoded to. We return an input stream for the encoded image data.
      *
      * @param aContainer
      *        An image container.
@@ -123,8 +152,18 @@ interface imgITools : nsISupports
      * Create a wrapper around a scripted notification observer (ordinarily
      * imgINotificationObserver cannot be implemented from scripts).
      *
      * @param aObserver The scripted observer to wrap
      */
     imgINotificationObserver
     createScriptedObserver(in imgIScriptedNotificationObserver aObserver);
 };
+
+/**
+ * This is a companion interface for nsIAsyncInputStream::asyncWait.
+ */
+[function, scriptable, uuid(f195772c-a4c0-47ae-80ca-211e001c67be)]
+interface imgIContainerCallback : nsISupports
+{
+    /* If the operation fails, aStatus will contain the error value */
+    void onImageReady(in imgIContainer aImage, in nsresult aStatus);
+};
--- a/image/imgTools.cpp
+++ b/image/imgTools.cpp
@@ -1,40 +1,174 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  *
  * 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/. */
 
 #include "imgTools.h"
 
+#include "DecodePool.h"
 #include "gfxUtils.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/Logging.h"
 #include "mozilla/RefPtr.h"
 #include "nsCOMPtr.h"
 #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"
 
 using namespace mozilla::gfx;
 
 namespace mozilla {
 namespace image {
+
+namespace {
+
+class ImageDecoderHelper final : public Runnable
+                               , public nsIInputStreamCallback
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+
+  ImageDecoderHelper(already_AddRefed<image::Image> aImage,
+                     already_AddRefed<nsIInputStream> aInputStream,
+                     nsIEventTarget* aEventTarget,
+                     imgIContainerCallback* aCallback,
+                     nsIEventTarget* aCallbackEventTarget)
+    : Runnable("ImageDecoderHelper")
+    , mImage(Move(aImage))
+    , mInputStream(Move(aInputStream))
+    , mEventTarget(aEventTarget)
+    , mCallback(aCallback)
+    , mCallbackEventTarget(aCallbackEventTarget)
+    , mStatus(NS_OK)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+  }
+
+  NS_IMETHOD
+  Run() override
+  {
+    // This runnable is dispatched on the Image thread when reading data, but
+    // at the end, it goes back to the main-thread in order to complete the
+    // operation.
+    if (NS_IsMainThread()) {
+      // Let the Image know we've sent all the data.
+      mImage->OnImageDataComplete(nullptr, nullptr, mStatus, true);
+
+      RefPtr<ProgressTracker> tracker = mImage->GetProgressTracker();
+      tracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE);
+
+      nsCOMPtr<imgIContainer> container;
+      if (NS_SUCCEEDED(mStatus)) {
+        container = do_QueryInterface(mImage);
+      }
+
+      mCallback->OnImageReady(container, mStatus);
+      return NS_OK;
+    }
+
+    uint64_t length;
+    nsresult rv = mInputStream->Available(&length);
+    if (rv == NS_BASE_STREAM_CLOSED) {
+      return OperationCompleted(NS_OK);
+    }
+
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return OperationCompleted(rv);
+    }
+
+    // Nothing else to read, but maybe we just need to wait.
+    if (length == 0) {
+      nsCOMPtr<nsIAsyncInputStream> asyncInputStream =
+        do_QueryInterface(mInputStream);
+      if (asyncInputStream) {
+        rv = asyncInputStream->AsyncWait(this, 0, 0, mEventTarget);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return OperationCompleted(rv);
+        }
+        return NS_OK;
+      }
+
+      // We really have nothing else to read.
+      if (length == 0) {
+        return OperationCompleted(NS_OK);
+      }
+    }
+
+    // Send the source data to the Image.
+    rv = mImage->OnImageDataAvailable(nullptr, nullptr, mInputStream, 0,
+                                      uint32_t(length));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return OperationCompleted(rv);
+    }
+
+    rv = mEventTarget->Dispatch(this, NS_DISPATCH_NORMAL);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return OperationCompleted(rv);
+    }
+
+    return NS_OK;
+  }
+
+  NS_IMETHOD
+  OnInputStreamReady(nsIAsyncInputStream* aAsyncInputStream) override
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+    return Run();
+  }
+
+  nsresult
+  OperationCompleted(nsresult aStatus)
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+
+    mStatus = aStatus;
+    mCallbackEventTarget->Dispatch(this, NS_DISPATCH_NORMAL);
+    return NS_OK;
+  }
+
+private:
+  ~ImageDecoderHelper()
+  {
+    NS_ReleaseOnMainThreadSystemGroup("ImageDecoderHelper::mImage",
+                                      mImage.forget());
+    NS_ReleaseOnMainThreadSystemGroup("ImageDecoderHelper::mCallback",
+                                      mCallback.forget());
+  }
+
+  RefPtr<image::Image> mImage;
+
+  nsCOMPtr<nsIInputStream> mInputStream;
+  nsCOMPtr<nsIEventTarget> mEventTarget;
+  nsCOMPtr<imgIContainerCallback> mCallback;
+  nsCOMPtr<nsIEventTarget> mCallbackEventTarget;
+
+  nsresult mStatus;
+};
+
+NS_IMPL_ISUPPORTS_INHERITED(ImageDecoderHelper, Runnable,
+                            nsIInputStreamCallback)
+
+} // anonymous
+
 /* ========== imgITools implementation ========== */
 
 
 
 NS_IMPL_ISUPPORTS(imgTools, imgITools)
 
 imgTools::imgTools()
 {
@@ -42,65 +176,103 @@ 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());
 
-  nsresult rv;
-
-  NS_ENSURE_ARG_POINTER(aInStr);
-
-  // Prepare the input stream.
-  nsCOMPtr<nsIInputStream> inStream = aInStr;
-  if (!NS_InputStreamIsBuffered(aInStr)) {
-    nsCOMPtr<nsIInputStream> bufStream;
-    rv = NS_NewBufferedInputStream(getter_AddRefs(bufStream),
-                                   inStream.forget(), 1024);
-    if (NS_SUCCEEDED(rv)) {
-      inStream = bufStream;
-    }
-  }
-
-  // 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;
   }
 
-  // Send the source data to 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.
-  NS_ADDREF(*aContainer = image.get());
+  image.forget(aContainer);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+imgTools::DecodeImageAsync(nsIInputStream* aInStr,
+                           const nsACString& aMimeType,
+                           imgIContainerCallback* aCallback,
+                           nsIEventTarget* aEventTarget)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  NS_ENSURE_ARG_POINTER(aInStr);
+  NS_ENSURE_ARG_POINTER(aCallback);
+  NS_ENSURE_ARG_POINTER(aEventTarget);
+
+  nsresult rv;
+
+  // Let's continuing the reading on a separate thread.
+  DecodePool* decodePool = DecodePool::Singleton();
+  MOZ_ASSERT(decodePool);
+
+  RefPtr<nsIEventTarget> target = decodePool->GetIOEventTarget();
+  NS_ENSURE_TRUE(target, NS_ERROR_FAILURE);
+
+  // Prepare the input stream.
+  nsCOMPtr<nsIInputStream> stream = aInStr;
+  if (!NS_InputStreamIsBuffered(aInStr)) {
+    nsCOMPtr<nsIInputStream> bufStream;
+    rv = NS_NewBufferedInputStream(getter_AddRefs(bufStream),
+                                   stream.forget(), 1024);
+    NS_ENSURE_SUCCESS(rv, rv);
+    stream = bufStream.forget();
+  }
+
+  // Create a new image container to hold the decoded data.
+  nsAutoCString mimeType(aMimeType);
+  RefPtr<image::Image> image = ImageFactory::CreateAnonymousImage(mimeType, 0);
+
+  // Already an error?
+  if (image->HasError()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  RefPtr<ImageDecoderHelper> helper =
+    new ImageDecoderHelper(image.forget(), stream.forget(), target, aCallback,
+                           aEventTarget);
+  rv = target->Dispatch(helper.forget(), NS_DISPATCH_NORMAL);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   return NS_OK;
 }
 
 /**
  * This takes a DataSourceSurface rather than a SourceSurface because some
  * of the callers have a DataSourceSurface and we don't want to call
  * GetDataSurface on such surfaces since that may incure a conversion to
  * SurfaceType::DATA which we don't need.
--- 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/ipc/glue/ProtocolUtils.cpp
+++ b/ipc/glue/ProtocolUtils.cpp
@@ -19,17 +19,16 @@
 #include "mozilla/ipc/MessageChannel.h"
 #include "mozilla/ipc/Transport.h"
 #include "mozilla/StaticMutex.h"
 #include "mozilla/SystemGroup.h"
 #include "mozilla/Unused.h"
 #include "nsPrintfCString.h"
 
 #if defined(MOZ_SANDBOX) && defined(XP_WIN)
-#define TARGET_SANDBOX_EXPORTS
 #include "mozilla/sandboxTarget.h"
 #endif
 
 #if defined(XP_WIN)
 #include "aclapi.h"
 #include "sddl.h"
 
 #include "mozilla/TypeTraits.h"
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -9310,16 +9310,22 @@ nsTextFrame::ReflowText(nsLineLayout& aL
       // Make sure this frame maps the trimmable whitespace.
       if (MOZ_UNLIKELY(offset > GetContentEnd())) {
         SetLength(offset - GetContentOffset(), &aLineLayout,
                   ALLOW_FRAME_CREATION_AND_DESTRUCTION);
       }
     }
   }
 
+  // If trimming whitespace left us with nothing to do, return early.
+  if (length == 0) {
+    ClearMetrics(aMetrics);
+    return;
+  }
+
   bool completedFirstLetter = false;
   // Layout dependent styles are a problem because we need to reconstruct
   // the gfxTextRun based on our layout.
   if (aLineLayout.GetInFirstLetter() || aLineLayout.GetInFirstLine()) {
     SetLength(maxContentLength, &aLineLayout,
               ALLOW_FRAME_CREATION_AND_DESTRUCTION);
 
     if (aLineLayout.GetInFirstLetter()) {
@@ -9798,22 +9804,31 @@ nsTextFrame::CanContinueTextRun() const
 {
   // We can continue a text run through a text frame
   return true;
 }
 
 nsTextFrame::TrimOutput
 nsTextFrame::TrimTrailingWhiteSpace(DrawTarget* aDrawTarget)
 {
+  MOZ_ASSERT(!HasAnyStateBits(NS_FRAME_IS_DIRTY | NS_FRAME_FIRST_REFLOW),
+             "frame should have been reflowed");
+
   TrimOutput result;
   result.mChanged = false;
   result.mDeltaWidth = 0;
 
   AddStateBits(TEXT_END_OF_LINE);
 
+  if (!GetTextRun(nsTextFrame::eInflated)) {
+    // If reflow didn't create a textrun, there must have been no content once
+    // leading whitespace was trimmed, so nothing more to do here.
+    return result;
+  }
+
   int32_t contentLength = GetContentLength();
   if (!contentLength)
     return result;
 
   gfxSkipCharsIterator start =
     EnsureTextRun(nsTextFrame::eInflated, aDrawTarget);
   NS_ENSURE_TRUE(mTextRun, result);
 
--- a/layout/style/ServoStyleSet.cpp
+++ b/layout/style/ServoStyleSet.cpp
@@ -1029,16 +1029,17 @@ void
 ServoStyleSet::RecordStyleSheetChange(
     ServoStyleSheet* aSheet,
     StyleSheet::ChangeType aChangeType)
 {
   switch (aChangeType) {
     case StyleSheet::ChangeType::RuleAdded:
     case StyleSheet::ChangeType::RuleRemoved:
     case StyleSheet::ChangeType::RuleChanged:
+    case StyleSheet::ChangeType::ReparsedFromInspector:
       // FIXME(emilio): We can presumably do better in a bunch of these.
       return MarkOriginsDirty(aSheet->GetOrigin());
     case StyleSheet::ChangeType::ApplicableStateChanged:
     case StyleSheet::ChangeType::Added:
     case StyleSheet::ChangeType::Removed:
       // Do nothing, we've already recorded the change in the
       // Append/Remove/Replace methods, etc, and will act consequently.
       return;
--- a/layout/style/ServoStyleSheet.cpp
+++ b/layout/style/ServoStyleSheet.cpp
@@ -4,19 +4,20 @@
  * 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/. */
 
 #include "mozilla/ServoStyleSheet.h"
 
 #include "mozilla/css/Rule.h"
 #include "mozilla/StyleBackendType.h"
 #include "mozilla/ServoBindings.h"
+#include "mozilla/ServoCSSRuleList.h"
 #include "mozilla/ServoImportRule.h"
 #include "mozilla/ServoMediaList.h"
-#include "mozilla/ServoCSSRuleList.h"
+#include "mozilla/ServoStyleSet.h"
 #include "mozilla/css/GroupRule.h"
 #include "mozilla/dom/CSSRuleList.h"
 #include "mozilla/dom/MediaList.h"
 #include "nsIStyleSheetLinkingElement.h"
 #include "Loader.h"
 
 
 #include "mozAutoDocUpdate.h"
@@ -337,16 +338,29 @@ ServoStyleSheet::ReparseSheet(const nsAS
       // Document observers could possibly detach document from this sheet.
       if (!mDocument) {
         // If detached, don't process any more rules.
         break;
       }
     }
   }
 
+  // FIXME(emilio): This is kind-of a hack for bug 1420713. As you may notice,
+  // there's nothing that triggers a style flush or anything similar (neither
+  // here or in the relevant Gecko path inside DidDirty).
+  //
+  // The tl;dr is: if we want to make sure scripted changes to sheets not
+  // associated with any document get properly reflected, we need to rejigger a
+  // fair amount of stuff. I'm probably doing that work as part of the shadow
+  // DOM stuff.
+  for (StyleSetHandle handle : mStyleSets) {
+    handle->AsServo()->RecordStyleSheetChange(
+      this, StyleSheet::ChangeType::ReparsedFromInspector);
+  }
+
   return NS_OK;
 }
 
 // nsICSSLoaderObserver implementation
 NS_IMETHODIMP
 ServoStyleSheet::StyleSheetLoaded(StyleSheet* aSheet,
                                   bool aWasAlternate,
                                   nsresult aStatus)
--- a/layout/style/StyleSheet.cpp
+++ b/layout/style/StyleSheet.cpp
@@ -866,16 +866,19 @@ StyleSheet::List(FILE* out, int32_t aInd
     child->List(out, aIndent + 1);
   }
 }
 #endif
 
 void
 StyleSheet::SetMedia(dom::MediaList* aMedia)
 {
+  if (aMedia) {
+    aMedia->SetStyleSheet(this);
+  }
   mMedia = aMedia;
 }
 
 void
 StyleSheet::DropMedia()
 {
   if (mMedia) {
     mMedia->SetStyleSheet(nullptr);
--- a/layout/style/StyleSheet.h
+++ b/layout/style/StyleSheet.h
@@ -72,16 +72,17 @@ public:
    */
   enum class ChangeType {
     Added,
     Removed,
     ApplicableStateChanged,
     RuleAdded,
     RuleRemoved,
     RuleChanged,
+    ReparsedFromInspector,
   };
 
   void SetOwningNode(nsINode* aOwningNode)
   {
     mOwningNode = aOwningNode;
   }
 
   css::SheetParsingMode ParsingMode() const { return mParsingMode; }
--- a/taskcluster/taskgraph/util/verify.py
+++ b/taskcluster/taskgraph/util/verify.py
@@ -138,18 +138,24 @@ def verify_dependency_tiers(task, taskgr
                                      .get('tier', sys.maxint)
     else:
         def printable_tier(tier):
             if tier == sys.maxint:
                 return 'unknown'
             return tier
 
         for task in taskgraph.tasks.itervalues():
+            # Buildbot bridge tasks cannot have tiers, so we cannot enforce
+            # this check for them
+            if task.task.get("workerType") == "buildbot-bridge":
+                continue
             tier = tiers[task.label]
             for d in task.dependencies.itervalues():
+                if taskgraph[d].task.get("workerType") == "buildbot-bridge":
+                    continue
                 if tier < tiers[d]:
                     raise Exception(
                         '{} (tier {}) cannot depend on {} (tier {})'
                         .format(task.label, printable_tier(tier),
                                 d, printable_tier(tiers[d])))
 
 
 @verifications.add('optimized_task_graph')
--- a/testing/config/tooltool-manifests/macosx64/hostutils.manifest
+++ b/testing/config/tooltool-manifests/macosx64/hostutils.manifest
@@ -1,10 +1,10 @@
 [
   {
-    "size": 121025970,
+    "size": 72112596,
     "visibility": "public",
-    "digest": "1374f461ad90fcdf1f6859df56379f000c7dbb0f8ea54c6652472c9e6f25052ec2386d00d6a41ed55579060e9300886674363f9689b1391e44518e500b9f2681",
+    "digest": "4d3fe61dd8ad9ca09c72e677e8d2d5b7409988de32d6feb9fc55cb0a320a4835b6e2f384a68d2409e89e7a0d2f622bf3ceb9b707e01ad8afe1270ad0d1c9973c",
     "algorithm": "sha512",
-    "filename": "host-utils-50.0a2.en-US.mac.tar.gz",
+    "filename": "host-utils-58.0a1.en-US.mac.tar.gz",
     "unpack": true
   }
 ]
--- a/testing/mozharness/scripts/android_emulator_unittest.py
+++ b/testing/mozharness/scripts/android_emulator_unittest.py
@@ -195,16 +195,25 @@ class AndroidEmulatorTest(BlobUploadMixi
         # constructed in start_emulator.
         env['LD_LIBRARY_PATH'] = self.abs_dirs['abs_work_dir']
 
         # Write a default ddms.cfg to avoid unwanted prompts
         avd_home_dir = self.abs_dirs['abs_avds_dir']
         with open(os.path.join(avd_home_dir, "ddms.cfg"), 'w') as f:
             f.write("pingOptIn=false\npingId=0\n")
 
+        # Delete emulator auth file, so it doesn't prompt
+        AUTH_FILE = os.path.join(os.path.expanduser('~'), '.emulator_console_auth_token')
+        if os.path.exists(AUTH_FILE):
+            try:
+                os.remove(AUTH_FILE)
+                self.info("deleted %s" % AUTH_FILE)
+            except:
+                self.warning("failed to remove %s" % AUTH_FILE)
+
         # Set environment variables to help emulator find the AVD.
         # In newer versions of the emulator, ANDROID_AVD_HOME should
         # point to the 'avd' directory.
         # For older versions of the emulator, ANDROID_SDK_HOME should
         # point to the directory containing the '.android' directory
         # containing the 'avd' directory.
         env['ANDROID_AVD_HOME'] = os.path.join(avd_home_dir, 'avd')
         env['ANDROID_SDK_HOME'] = os.path.abspath(os.path.join(avd_home_dir, '..'))
@@ -254,39 +263,39 @@ class AndroidEmulatorTest(BlobUploadMixi
         return status
 
     def _run_with_timeout(self, timeout, cmd, quiet=False):
         timeout_cmd = ['timeout', '%s' % timeout] + cmd
         return self._run_proc(timeout_cmd, quiet=quiet)
 
     def _run_proc(self, cmd, quiet=False):
         self.info('Running %s' % subprocess.list2cmdline(cmd))
-        p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
+        p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
         out, err = p.communicate()
         if out and not quiet:
             self.info('%s' % str(out.strip()))
         if err and not quiet:
             self.info('stderr: %s' % str(err.strip()))
-        return out
+        return out, err
 
     def _verify_adb(self):
         self.info('Verifying adb connectivity')
         self._run_with_timeout(180, [self.adb_path, 'wait-for-device'])
         return True
 
     def _verify_adb_device(self):
-        out = self._run_with_timeout(30, [self.adb_path, 'devices'])
+        out, _ = self._run_with_timeout(30, [self.adb_path, 'devices'])
         if (self.emulator['device_id'] in out) and ("device" in out):
             return True
         return False
 
     def _is_boot_completed(self):
         boot_cmd = [self.adb_path, '-s', self.emulator['device_id'],
                     'shell', 'getprop', 'sys.boot_completed']
-        out = self._run_with_timeout(30, boot_cmd)
+        out, _ = self._run_with_timeout(30, boot_cmd)
         if out.strip() == '1':
             return True
         return False
 
     def _verify_emulator(self):
         adb_ok = self._verify_adb()
         if not adb_ok:
             self.warning('Unable to communicate with adb')
@@ -322,31 +331,31 @@ class AndroidEmulatorTest(BlobUploadMixi
     def _install_fennec_apk(self):
         install_ok = False
         if int(self.sdk_level) >= 23:
             cmd = [self.adb_path, '-s', self.emulator['device_id'], 'install', '-r', '-g',
                    self.installer_path]
         else:
             cmd = [self.adb_path, '-s', self.emulator['device_id'], 'install', '-r',
                    self.installer_path]
-        out = self._run_with_timeout(300, cmd, True)
-        if 'Success' in out:
+        out, err = self._run_with_timeout(300, cmd, True)
+        if 'Success' in out or 'Success' in err:
             install_ok = True
         return install_ok
 
     def _install_robocop_apk(self):
         install_ok = False
         if int(self.sdk_level) >= 23:
             cmd = [self.adb_path, '-s', self.emulator['device_id'], 'install', '-r', '-g',
                    self.robocop_path]
         else:
             cmd = [self.adb_path, '-s', self.emulator['device_id'], 'install', '-r',
                    self.robocop_path]
-        out = self._run_with_timeout(300, cmd, True)
-        if 'Success' in out:
+        out, err = self._run_with_timeout(300, cmd, True)
+        if 'Success' in out or 'Success' in err:
             install_ok = True
         return install_ok
 
     def _dump_host_state(self):
         self._run_proc(['ps', '-ef'])
         self._run_proc(['netstat', '-a', '-p', '-n', '-t', '-u'])
 
     def _dump_emulator_log(self):
@@ -363,17 +372,17 @@ class AndroidEmulatorTest(BlobUploadMixi
         for line in out.splitlines():
             if process_name in line:
                 pid = int(line.split(None, 1)[0])
                 self.info("Killing pid %d." % pid)
                 os.kill(pid, signal.SIGKILL)
 
     def _restart_adbd(self):
         self._run_with_timeout(30, [self.adb_path, 'kill-server'])
-        self._run_with_timeout(30, [self.adb_path, 'start-server'])
+        self._run_with_timeout(30, [self.adb_path, 'root'])
 
     def _screenshot(self, prefix):
         """
            Save a screenshot of the entire screen to the blob upload directory.
         """
         dirs = self.query_abs_dirs()
         utility = os.path.join(self.xre_path, "screentopng")
         if not os.path.exists(utility):
@@ -609,43 +618,43 @@ class AndroidEmulatorTest(BlobUploadMixi
         sometimes. This is hopefully a temporary diagnostic.
         See bug 1321605.
         '''
         dir = self.query_abs_dirs()['abs_blob_upload_dir']
         perf_path = os.path.join(dir, "android-performance.log")
         with open(perf_path, "w") as f:
 
             f.write('\n\nHost /proc/cpuinfo:\n')
-            out = self._run_proc(['cat', '/proc/cpuinfo'], quiet=True)
+            out, _ = self._run_proc(['cat', '/proc/cpuinfo'], quiet=True)
             f.write(out)
 
             f.write('\n\nHost /proc/meminfo:\n')
-            out = self._run_proc(['cat', '/proc/meminfo'], quiet=True)
+            out, _ = self._run_proc(['cat', '/proc/meminfo'], quiet=True)
             f.write(out)
 
             f.write('\n\nHost process list:\n')
-            out = self._run_proc(['ps', '-ef'], quiet=True)
+            out, _ = self._run_proc(['ps', '-ef'], quiet=True)
             f.write(out)
 
             f.write('\n\nEmulator /proc/cpuinfo:\n')
             cmd = [self.adb_path, '-s', self.emulator['device_id'],
                    'shell', 'cat', '/proc/cpuinfo']
-            out = self._run_with_timeout(30, cmd, quiet=True)
+            out, _ = self._run_with_timeout(30, cmd, quiet=True)
             f.write(out)
 
             f.write('\n\nEmulator /proc/meminfo:\n')
             cmd = [self.adb_path, '-s', self.emulator['device_id'],
                    'shell', 'cat', '/proc/meminfo']
-            out = self._run_with_timeout(30, cmd, quiet=True)
+            out, _ = self._run_with_timeout(30, cmd, quiet=True)
             f.write(out)
 
             f.write('\n\nEmulator process list:\n')
             cmd = [self.adb_path, '-s', self.emulator['device_id'],
                    'shell', 'ps']
-            out = self._run_with_timeout(30, cmd, quiet=True)
+            out, _ = self._run_with_timeout(30, cmd, quiet=True)
             f.write(out)
 
     def verify_emulator(self):
         '''
         Check to see if the emulator can be contacted via adb.
         If any communication attempt fails, kill the emulator, re-launch, and re-check.
         '''
         self.mkdir_p(self.query_abs_dirs()['abs_blob_upload_dir'])
@@ -702,19 +711,19 @@ class AndroidEmulatorTest(BlobUploadMixi
             self.config["suite_definitions"][self.test_suite].get("install")
         if install_needed is False:
             self.info("Skipping apk installation for %s" % self.test_suite)
             return
 
         assert self.installer_path is not None, \
             "Either add installer_path to the config or use --installer-path."
 
-        self.sdk_level = self._run_with_timeout(30, [self.adb_path, '-s',
-                                                     self.emulator['device_id'],
-                                                'shell', 'getprop', 'ro.build.version.sdk'])
+        cmd = [self.adb_path, '-s', self.emulator['device_id'], 'shell',
+               'getprop', 'ro.build.version.sdk']
+        self.sdk_level, _ = self._run_with_timeout(30, cmd)
 
         # Install Fennec
         install_ok = self._retry(3, 30, self._install_fennec_apk, "Install app APK")
         if not install_ok:
             self.fatal('INFRA-ERROR: Failed to install %s on %s' %
                        (self.installer_path, self.emulator["name"]),
                        EXIT_STATUS_DICT[TBPL_RETRY])
 
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -155018,16 +155018,28 @@
       [
        "/css/cssom-view/scrollingElement-quirks-dynamic-002-ref.html",
        "=="
       ]
      ],
      {}
     ]
    ],
+   "css/cssom/medialist-dynamic-001.html": [
+    [
+     "/css/cssom/medialist-dynamic-001.html",
+     [
+      [
+       "/css/cssom/medialist-dynamic-001-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/cssom/selectorText-modification-restyle-001.html": [
     [
      "/css/cssom/selectorText-modification-restyle-001.html",
      [
       [
        "/css/cssom/selectorText-modification-restyle-001-ref.html",
        "=="
       ]
@@ -253158,16 +253170,21 @@
      {}
     ]
    ],
    "css/cssom/OWNERS": [
     [
      {}
     ]
    ],
+   "css/cssom/medialist-dynamic-001-ref.html": [
+    [
+     {}
+    ]
+   ],
    "css/cssom/selectorText-modification-restyle-001-ref.html": [
     [
      {}
     ]
    ],
    "css/cssom/stylesheet-same-origin.css": [
     [
      {}
@@ -511060,17 +511077,17 @@
    "b2261ec702116c211ab5ac6fbb53698dfe60a7be",
    "testharness"
   ],
   "css/cssom-view/offsetTopLeftInScrollableParent.html": [
    "82a6920db4d0be087e6ed621313ecf92e3e2ed68",
    "testharness"
   ],
   "css/cssom-view/overscrollBehavior-manual.html": [
-   "ca369af991f1e1c34116f2effe0d53a806ed9df5",
+   "65cb71ca6b606dc8b0b527c58902566539e771b2",
    "manual"
   ],
   "css/cssom-view/resources/elementsFromPoint.js": [
    "0c31158817d4d6f9e59df0d2ebe0e41c6ce41bb5",
    "support"
   ],
   "css/cssom-view/resources/iframe1.html": [
    "ec93f617bdc7b0055d96c7b00ab7832cca1c1af0",
@@ -511463,16 +511480,24 @@
   "css/cssom/insertRule-no-index.html": [
    "812f2b02d7694dd270b7a3e1ef205b99890ab216",
    "testharness"
   ],
   "css/cssom/interfaces.html": [
    "c1dfd96239986c9c57d7b07caebbd1fc9654e0b9",
    "testharness"
   ],
+  "css/cssom/medialist-dynamic-001-ref.html": [
+   "bdf98c994adcebff3a3434260dfe71e99c8441e1",
+   "support"
+  ],
+  "css/cssom/medialist-dynamic-001.html": [
+   "8c62d1e6b5791b68240551c0c9cd115f4d16a892",
+   "reftest"
+  ],
   "css/cssom/medialist-interfaces-001.html": [
    "dfaea262508d72d123006409174e3e21832a305f",
    "testharness"
   ],
   "css/cssom/medialist-interfaces-002.html": [
    "114fac94342afe2e7fe432a67c4b0bbf03d24bc4",
    "testharness"
   ],
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/medialist-dynamic-001-ref.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSS Test Reference</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+Should not be red.
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/medialist-dynamic-001.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSS Test: Dynamic changes to the stylesheet media attributes via CSSOM get reflected</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="match" href="medialist-dynamic-001-ref.html">
+<link rel="help" href="https://drafts.csswg.org/cssom/#dom-medialist-mediatext">
+<style media="all">* { color: red }</style>
+Should not be red.
+<script>
+  document.body.offsetTop;
+  document.styleSheets[0].media.mediaText = "not all";
+</script>
--- 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);