Merge inbound to mozilla-central r=merge a=merge
authorMargareta Eliza Balazs <ebalazs@mozilla.com>
Mon, 27 Nov 2017 23:57:42 +0200
changeset 393836 f5f03ee9e6abf77964f8dc1b9d69c6ccd3f655fd
parent 393809 5441160857a6bb70b5b29a7b00d647fc88951b7a (current diff)
parent 393835 cec895f7cedad28bea953572480acefd7bec4d08 (diff)
child 393856 530991098a471c3efe8d301b1fca843972fa4483
child 393909 4b1257fe22a289d683e9da955caac42eefb76866
push id32983
push userebalazs@mozilla.com
push dateMon, 27 Nov 2017 21:58:04 +0000
treeherdermozilla-central@f5f03ee9e6ab [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone59.0a1
first release with
nightly linux32
f5f03ee9e6ab / 59.0a1 / 20171127220446 / files
nightly linux64
f5f03ee9e6ab / 59.0a1 / 20171127220446 / files
nightly mac
f5f03ee9e6ab / 59.0a1 / 20171127220446 / files
nightly win32
f5f03ee9e6ab / 59.0a1 / 20171127220446 / files
nightly win64
f5f03ee9e6ab / 59.0a1 / 20171127220446 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..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);