Bug 1044102 - Part 3 - Support StructuredClone. r=baku
authorKaku Kuo <tkuo@mozilla.com>
Thu, 30 Jul 2015 20:50:00 +0200
changeset 255591 f0af677fae4fd177a69edae17fcc9eb388492612
parent 255590 075fd774a827e561b8b0ef92dc7adaa946f39a66
child 255592 5afab84bd62f6736c6050971d2e13695c6480840
push id63095
push usercbook@mozilla.com
push dateFri, 31 Jul 2015 09:43:14 +0000
treeherdermozilla-inbound@5afab84bd62f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku
bugs1044102
milestone42.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1044102 - Part 3 - Support StructuredClone. r=baku
dom/base/StructuredCloneHelper.cpp
dom/base/StructuredCloneHelper.h
dom/base/StructuredCloneTags.h
dom/canvas/ImageBitmap.cpp
dom/canvas/ImageBitmap.h
dom/workers/ServiceWorkerClient.cpp
dom/workers/WorkerPrivate.cpp
dom/workers/WorkerStructuredClone.h
dom/workers/XMLHttpRequest.cpp
--- a/dom/base/StructuredCloneHelper.cpp
+++ b/dom/base/StructuredCloneHelper.cpp
@@ -1,18 +1,21 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "StructuredCloneHelper.h"
 
+#include "ImageContainer.h"
 #include "mozilla/dom/BlobBinding.h"
 #include "mozilla/dom/FileListBinding.h"
+#include "mozilla/dom/ImageBitmap.h"
+#include "mozilla/dom/ImageBitmapBinding.h"
 #include "mozilla/dom/StructuredCloneTags.h"
 
 namespace mozilla {
 namespace dom {
 
 namespace {
 
 JSObject*
@@ -358,16 +361,25 @@ StructuredCloneHelper::ReadCallback(JSCo
       if (!ToJSValue(aCx, fileList, &val)) {
         return nullptr;
       }
     }
 
     return &val.toObject();
   }
 
+  if (aTag == SCTAG_DOM_IMAGEBITMAP) {
+     // Get the current global object.
+     // This can be null.
+     nsCOMPtr<nsIGlobalObject> parent = do_QueryInterface(mParent);
+     // aIndex is the index of the cloned image.
+     return ImageBitmap::ReadStructuredClone(aCx, aReader,
+                                             parent, GetImages(), aIndex);
+   }
+
   return NS_DOMReadStructuredClone(aCx, aReader, aTag, aIndex, nullptr);
 }
 
 bool
 StructuredCloneHelper::WriteCallback(JSContext* aCx,
                                      JSStructuredCloneWriter* aWriter,
                                      JS::Handle<JSObject*> aObj)
 {
@@ -406,16 +418,26 @@ StructuredCloneHelper::WriteCallback(JSC
       for (uint32_t i = 0; i < fileList->Length(); ++i) {
         mBlobImplArray.AppendElement(fileList->Item(i)->Impl());
       }
 
       return true;
     }
   }
 
+  // See if this is an ImageBitmap object.
+  {
+    ImageBitmap* imageBitmap = nullptr;
+    if (NS_SUCCEEDED(UNWRAP_OBJECT(ImageBitmap, aObj, imageBitmap))) {
+      return ImageBitmap::WriteStructuredClone(aWriter,
+                                               GetImages(),
+                                               imageBitmap);
+    }
+  }
+
   return NS_DOMWriteStructuredClone(aCx, aWriter, aObj, nullptr);
 }
 
 bool
 StructuredCloneHelper::ReadTransferCallback(JSContext* aCx,
                                             JSStructuredCloneReader* aReader,
                                             uint32_t aTag,
                                             void* aContent,
@@ -449,17 +471,16 @@ StructuredCloneHelper::ReadTransferCallb
 
     aReturnObject.set(&value.toObject());
     return true;
   }
 
   return false;
 }
 
-
 bool
 StructuredCloneHelper::WriteTransferCallback(JSContext* aCx,
                                              JS::Handle<JSObject*> aObj,
                                              uint32_t* aTag,
                                              JS::TransferableOwnership* aOwnership,
                                              void** aContent,
                                              uint64_t* aExtraData)
 {
--- a/dom/base/StructuredCloneHelper.h
+++ b/dom/base/StructuredCloneHelper.h
@@ -7,16 +7,20 @@
 #define mozilla_dom_StructuredCloneHelper_h
 
 #include "js/StructuredClone.h"
 #include "nsAutoPtr.h"
 #include "nsISupports.h"
 #include "nsTArray.h"
 
 namespace mozilla {
+namespace layers {
+class Image;
+}
+
 namespace dom {
 
 class StructuredCloneHelperInternal
 {
 public:
   StructuredCloneHelperInternal();
   virtual ~StructuredCloneHelperInternal();
 
@@ -172,16 +176,21 @@ public:
   }
 
   nsTArray<MessagePortIdentifier>& PortIdentifiers()
   {
     MOZ_ASSERT(mSupportsTransferring);
     return mPortIdentifiers;
   }
 
+  nsTArray<nsRefPtr<layers::Image>>& GetImages()
+  {
+    return mClonedImages;
+  }
+
   // Custom Callbacks
 
   virtual JSObject* ReadCallback(JSContext* aCx,
                                  JSStructuredCloneReader* aReader,
                                  uint32_t aTag,
                                  uint32_t aIndex) override;
 
   virtual bool WriteCallback(JSContext* aCx,
@@ -209,16 +218,22 @@ public:
 private:
   bool mSupportsCloning;
   bool mSupportsTransferring;
 
   // Useful for the structured clone algorithm:
 
   nsTArray<nsRefPtr<BlobImpl>> mBlobImplArray;
 
+  // This is used for sharing the backend of ImageBitmaps.
+  // The layers::Image object must be thread-safely reference-counted.
+  // The layers::Image object will not be written ever via any ImageBitmap
+  // instance, so no race condition will occur.
+  nsTArray<nsRefPtr<layers::Image>> mClonedImages;
+
   // This raw pointer is set and unset into the ::Read(). It's always null
   // outside that method. For this reason it's a raw pointer.
   nsISupports* MOZ_NON_OWNING_REF mParent;
 
   // This hashtable contains the ports while doing write (transferring and
   // mapping transferred objects to the objects in the clone). It's an empty
   // array outside the 'Write()' method.
   nsTArray<nsRefPtr<MessagePortBase>> mTransferringPort;
--- a/dom/base/StructuredCloneTags.h
+++ b/dom/base/StructuredCloneTags.h
@@ -37,16 +37,17 @@ enum StructuredCloneTags {
   // This tag is for WebCrypto keys
   SCTAG_DOM_WEBCRYPTO_KEY,
 
   SCTAG_DOM_NULL_PRINCIPAL,
   SCTAG_DOM_SYSTEM_PRINCIPAL,
   SCTAG_DOM_CONTENT_PRINCIPAL,
 
   SCTAG_DOM_NFC_NDEF,
+  SCTAG_DOM_IMAGEBITMAP,
 
   SCTAG_DOM_RTC_CERTIFICATE,
 
   SCTAG_DOM_MAX
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/canvas/ImageBitmap.cpp
+++ b/dom/canvas/ImageBitmap.cpp
@@ -6,16 +6,17 @@
 
 #include "mozilla/dom/ImageBitmap.h"
 #include "mozilla/dom/ImageBitmapBinding.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/dom/WorkerRunnable.h"
 #include "mozilla/gfx/2D.h"
 #include "imgTools.h"
+#include "js/StructuredClone.h"
 #include "libyuv.h"
 #include "nsLayoutUtils.h"
 
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
 
 namespace mozilla {
 namespace dom {
@@ -1081,10 +1082,93 @@ ImageBitmap::Create(nsIGlobalObject* aGl
 
   if (!aRv.Failed()) {
     AsyncFulfillImageBitmapPromise(promise, imageBitmap);
   }
 
   return promise.forget();
 }
 
+/*static*/ JSObject*
+ImageBitmap::ReadStructuredClone(JSContext* aCx,
+                                 JSStructuredCloneReader* aReader,
+                                 nsIGlobalObject* aParent,
+                                 const nsTArray<nsRefPtr<layers::Image>>& aClonedImages,
+                                 uint32_t aIndex)
+{
+  MOZ_ASSERT(aCx);
+  MOZ_ASSERT(aReader);
+  // aParent might be null.
+
+  uint32_t picRectX_;
+  uint32_t picRectY_;
+  uint32_t picRectWidth_;
+  uint32_t picRectHeight_;
+
+  if (!JS_ReadUint32Pair(aReader, &picRectX_, &picRectY_) ||
+      !JS_ReadUint32Pair(aReader, &picRectWidth_, &picRectHeight_)) {
+    return nullptr;
+  }
+
+  int32_t picRectX = BitwiseCast<int32_t>(picRectX_);
+  int32_t picRectY = BitwiseCast<int32_t>(picRectY_);
+  int32_t picRectWidth = BitwiseCast<int32_t>(picRectWidth_);
+  int32_t picRectHeight = BitwiseCast<int32_t>(picRectHeight_);
+
+  // Create a new ImageBitmap.
+  MOZ_ASSERT(!aClonedImages.IsEmpty());
+  MOZ_ASSERT(aIndex < aClonedImages.Length());
+
+  // nsRefPtr<ImageBitmap> needs to go out of scope before toObjectOrNull() is
+  // called because the static analysis thinks dereferencing XPCOM objects
+  // can GC (because in some cases it can!), and a return statement with a
+  // JSObject* type means that JSObject* is on the stack as a raw pointer
+  // while destructors are running.
+  JS::Rooted<JS::Value> value(aCx);
+  {
+    nsRefPtr<ImageBitmap> imageBitmap =
+      new ImageBitmap(aParent, aClonedImages[aIndex]);
+
+    ErrorResult error;
+    imageBitmap->SetPictureRect(IntRect(picRectX, picRectY,
+                                        picRectWidth, picRectHeight), error);
+    if (NS_WARN_IF(error.Failed())) {
+      error.SuppressException();
+      return nullptr;
+    }
+
+    if (!GetOrCreateDOMReflector(aCx, imageBitmap, &value)) {
+      return nullptr;
+    }
+  }
+
+  return &(value.toObject());
+}
+
+/*static*/ bool
+ImageBitmap::WriteStructuredClone(JSStructuredCloneWriter* aWriter,
+                                  nsTArray<nsRefPtr<layers::Image>>& aClonedImages,
+                                  ImageBitmap* aImageBitmap)
+{
+  MOZ_ASSERT(aWriter);
+  MOZ_ASSERT(aImageBitmap);
+
+  const uint32_t picRectX = BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.x);
+  const uint32_t picRectY = BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.y);
+  const uint32_t picRectWidth = BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.width);
+  const uint32_t picRectHeight = BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.height);
+
+  // Indexing the cloned images and send the index to the receiver.
+  uint32_t index = aClonedImages.Length();
+
+  if (NS_WARN_IF(!JS_WriteUint32Pair(aWriter, SCTAG_DOM_IMAGEBITMAP, index)) ||
+      NS_WARN_IF(!JS_WriteUint32Pair(aWriter, picRectX, picRectY)) ||
+      NS_WARN_IF(!JS_WriteUint32Pair(aWriter, picRectWidth, picRectHeight))) {
+    return false;
+  }
+
+  aClonedImages.AppendElement(aImageBitmap->mData);
+
+  return true;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/canvas/ImageBitmap.h
+++ b/dom/canvas/ImageBitmap.h
@@ -10,38 +10,45 @@
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/ImageBitmapSource.h"
 #include "mozilla/dom/TypedArray.h"
 #include "mozilla/gfx/Rect.h"
 #include "mozilla/Maybe.h"
 #include "nsCycleCollectionParticipant.h"
 
 struct JSContext;
+struct JSStructuredCloneReader;
+struct JSStructuredCloneWriter;
 
 namespace mozilla {
 
 class ErrorResult;
 
 namespace gfx {
 class SourceSurface;
 }
 
 namespace layers {
 class Image;
 }
 
 namespace dom {
 
+namespace workers {
+class WorkerStructuredCloneClosure;
+}
+
 class CanvasRenderingContext2D;
 class File;
 class HTMLCanvasElement;
 class HTMLImageElement;
 class HTMLVideoElement;
 class ImageData;
 class Promise;
+class PostMessageEvent; // For StructuredClone between windows.
 class CreateImageBitmapFromBlob;
 class CreateImageBitmapFromBlobTask;
 class CreateImageBitmapFromBlobWorkerTask;
 
 /*
  * ImageBitmap is an opaque handler to several kinds of image-like objects from
  * HTMLImageElement, HTMLVideoElement, HTMLCanvasElement, ImageData to
  * CanvasRenderingContext2D and Image Blob.
@@ -80,16 +87,28 @@ public:
    */
   already_AddRefed<gfx::SourceSurface>
   PrepareForDrawTarget(gfx::DrawTarget* aTarget);
 
   static already_AddRefed<Promise>
   Create(nsIGlobalObject* aGlobal, const ImageBitmapSource& aSrc,
          const Maybe<gfx::IntRect>& aCropRect, ErrorResult& aRv);
 
+  static JSObject*
+  ReadStructuredClone(JSContext* aCx,
+                      JSStructuredCloneReader* aReader,
+                      nsIGlobalObject* aParent,
+                      const nsTArray<nsRefPtr<layers::Image>>& aClonedImages,
+                      uint32_t aIndex);
+
+  static bool
+  WriteStructuredClone(JSStructuredCloneWriter* aWriter,
+                       nsTArray<nsRefPtr<layers::Image>>& aClonedImages,
+                       ImageBitmap* aImageBitmap);
+
   friend CreateImageBitmapFromBlob;
   friend CreateImageBitmapFromBlobTask;
   friend CreateImageBitmapFromBlobWorkerTask;
 
 protected:
 
   ImageBitmap(nsIGlobalObject* aGlobal, layers::Image* aData);
 
--- a/dom/workers/ServiceWorkerClient.cpp
+++ b/dom/workers/ServiceWorkerClient.cpp
@@ -83,16 +83,17 @@ class ServiceWorkerClientPostMessageRunn
 public:
   ServiceWorkerClientPostMessageRunnable(uint64_t aWindowId,
                                          JSAutoStructuredCloneBuffer&& aData,
                                          WorkerStructuredCloneClosure& aClosure)
     : mWindowId(aWindowId),
       mBuffer(Move(aData))
   {
     mClosure.mClonedObjects.SwapElements(aClosure.mClonedObjects);
+    mClosure.mClonedImages.SwapElements(aClosure.mClonedImages);
     MOZ_ASSERT(aClosure.mMessagePorts.IsEmpty());
     mClosure.mMessagePortIdentifiers.SwapElements(aClosure.mMessagePortIdentifiers);
   }
 
   NS_IMETHOD
   Run()
   {
     AssertIsOnMainThread();
@@ -122,16 +123,17 @@ private:
   DispatchDOMEvent(JSContext* aCx, ServiceWorkerContainer* aTargetContainer)
   {
     AssertIsOnMainThread();
 
     // Release reference to objects that were AddRef'd for
     // cloning into worker when array goes out of scope.
     WorkerStructuredCloneClosure closure;
     closure.mClonedObjects.SwapElements(mClosure.mClonedObjects);
+    closure.mClonedImages.SwapElements(mClosure.mClonedImages);
     MOZ_ASSERT(mClosure.mMessagePorts.IsEmpty());
     closure.mMessagePortIdentifiers.SwapElements(mClosure.mMessagePortIdentifiers);
     closure.mParentWindow = do_QueryInterface(aTargetContainer->GetParentObject());
 
     JS::Rooted<JS::Value> messageData(aCx);
     if (!mBuffer.read(aCx, &messageData,
                       WorkerStructuredCloneCallbacks(true), &closure)) {
       xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -30,30 +30,33 @@
 #include "nsIURL.h"
 #include "nsIWeakReferenceUtils.h"
 #include "nsIWorkerDebugger.h"
 #include "nsIXPConnect.h"
 #include "nsPerformance.h"
 #include "nsPIDOMWindow.h"
 
 #include <algorithm>
+#include "ImageContainer.h"
 #include "jsfriendapi.h"
 #include "js/MemoryMetrics.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/ContentEvents.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/Likely.h"
 #include "mozilla/LoadContext.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/BlobBinding.h"
 #include "mozilla/dom/ErrorEvent.h"
 #include "mozilla/dom/ErrorEventBinding.h"
 #include "mozilla/dom/Exceptions.h"
 #include "mozilla/dom/FunctionBinding.h"
+#include "mozilla/dom/ImageBitmap.h"
+#include "mozilla/dom/ImageBitmapBinding.h"
 #include "mozilla/dom/ImageData.h"
 #include "mozilla/dom/ImageDataBinding.h"
 #include "mozilla/dom/MessageEvent.h"
 #include "mozilla/dom/MessageEventBinding.h"
 #include "mozilla/dom/MessagePort.h"
 #include "mozilla/dom/MessagePortBinding.h"
 #include "mozilla/dom/MessagePortList.h"
 #include "mozilla/dom/Promise.h"
@@ -629,16 +632,28 @@ struct WorkerStructuredCloneCallbacks
     // See if the object is a FormData.
     else if (aTag == DOMWORKER_SCTAG_FORMDATA) {
       JS::Rooted<JSObject*> formData(aCx);
       // aData is the entry count.
       ReadFormData(aCx, aReader, /* aIsMainThread */ false,  aData, &formData);
       return formData;
     }
 
+    // See if the object is an ImageBitmap.
+    if (aTag == SCTAG_DOM_IMAGEBITMAP) {
+      NS_ASSERTION(aClosure, "Null pointer!");
+
+      // Get the current global object.
+      auto* closure = static_cast<WorkerStructuredCloneClosure*>(aClosure);
+      nsCOMPtr<nsIGlobalObject> parent = do_QueryInterface(closure->mParentWindow);
+      // aData is the index of the cloned image.
+      return ImageBitmap::ReadStructuredClone(aCx, aReader, parent,
+                                              closure->mClonedImages, aData);
+    }
+
     Error(aCx, 0);
     return nullptr;
   }
 
   static bool
   Write(JSContext* aCx, JSStructuredCloneWriter* aWriter,
         JS::Handle<JSObject*> aObj, void* aClosure)
   {
@@ -672,16 +687,26 @@ struct WorkerStructuredCloneCallbacks
       nsFormData* formData = nullptr;
       if (NS_SUCCEEDED(UNWRAP_OBJECT(FormData, aObj, formData))) {
         if (WriteFormData(aCx, aWriter, formData, *closure)) {
           return true;
         }
       }
     }
 
+    // See if this is an ImageBitmap object.
+    {
+      ImageBitmap* imageBitmap = nullptr;
+      if (NS_SUCCEEDED(UNWRAP_OBJECT(ImageBitmap, aObj, imageBitmap))) {
+        return ImageBitmap::WriteStructuredClone(aWriter,
+                                                 closure->mClonedImages,
+                                                 imageBitmap);
+      }
+    }
+
     Error(aCx, 0);
     return false;
   }
 
   static void
   Error(JSContext* aCx, uint32_t /* aErrorId */)
   {
     Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
@@ -805,16 +830,28 @@ struct MainThreadWorkerStructuredCloneCa
     // See if the object is a FormData.
     else if (aTag == DOMWORKER_SCTAG_FORMDATA) {
       JS::Rooted<JSObject*> formData(aCx);
       // aData is the entry count.
       ReadFormData(aCx, aReader, /* aIsMainThread */ true,  aData, &formData);
       return formData;
     }
 
+    // See if the object is a ImageBitmap
+    if (aTag == SCTAG_DOM_IMAGEBITMAP) {
+      NS_ASSERTION(aClosure, "Null pointer!");
+
+      // Get the current global object.
+      auto* closure = static_cast<WorkerStructuredCloneClosure*>(aClosure);
+      nsCOMPtr<nsIGlobalObject> parent = do_QueryInterface(closure->mParentWindow);
+      // aData is the index of the cloned image.
+      return ImageBitmap::ReadStructuredClone(aCx, aReader, parent,
+                                              closure->mClonedImages, aData);
+    }
+
     JS_ClearPendingException(aCx);
     return NS_DOMReadStructuredClone(aCx, aReader, aTag, aData, nullptr);
   }
 
   static bool
   Write(JSContext* aCx, JSStructuredCloneWriter* aWriter,
         JS::Handle<JSObject*> aObj, void* aClosure)
   {
@@ -834,16 +871,26 @@ struct MainThreadWorkerStructuredCloneCa
         if (!blobImpl->MayBeClonedToOtherThreads()) {
           NS_WARNING("Not all the blob implementations can be sent between threads.");
         } else if (WriteBlobOrFile(aCx, aWriter, blobImpl, *closure)) {
           return true;
         }
       }
     }
 
+    // Handle imageBitmap cloning
+    {
+      ImageBitmap* imageBitmap = nullptr;
+      if (NS_SUCCEEDED(UNWRAP_OBJECT(ImageBitmap, aObj, imageBitmap))) {
+        return ImageBitmap::WriteStructuredClone(aWriter,
+                                                 closure->mClonedImages,
+                                                 imageBitmap);
+      }
+    }
+
     JS_ClearPendingException(aCx);
     return NS_DOMWriteStructuredClone(aCx, aWriter, aObj, nullptr);
   }
 
   static void
   Error(JSContext* aCx, uint32_t aErrorId)
   {
     AssertIsOnMainThread();
@@ -1281,16 +1328,17 @@ public:
   bool
   DispatchDOMEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
                    DOMEventTargetHelper* aTarget, bool aIsMainThread)
   {
     // Release reference to objects that were AddRef'd for
     // cloning into worker when array goes out of scope.
     WorkerStructuredCloneClosure closure;
     closure.mClonedObjects.SwapElements(mClosure.mClonedObjects);
+    closure.mClonedImages.SwapElements(mClosure.mClonedImages);
     MOZ_ASSERT(mClosure.mMessagePorts.IsEmpty());
     closure.mMessagePortIdentifiers.SwapElements(mClosure.mMessagePortIdentifiers);
 
     if (aIsMainThread) {
       closure.mParentWindow = do_QueryInterface(aTarget->GetParentObject());
     }
 
     JS::Rooted<JS::Value> messageData(aCx);
@@ -3578,16 +3626,17 @@ WorkerPrivateParent<Derived>::DispatchMe
   private:
     JSAutoStructuredCloneBuffer& mBuffer;
     const JSStructuredCloneCallbacks* mCallbacks;
     WorkerStructuredCloneClosure& mClosure;
   };
 
   WorkerStructuredCloneClosure closure;
   closure.mClonedObjects.SwapElements(aClosure.mClonedObjects);
+  closure.mClonedImages.SwapElements(aClosure.mClonedImages);
   MOZ_ASSERT(aClosure.mMessagePorts.IsEmpty());
   closure.mMessagePortIdentifiers.SwapElements(aClosure.mMessagePortIdentifiers);
 
   AutoCloneBufferCleaner bufferCleaner(buffer, callbacks, closure);
 
   SharedWorker* sharedWorker;
   if (!mSharedWorkers.Get(aMessagePortSerial, &sharedWorker)) {
     // SharedWorker has already been unregistered?
@@ -7531,14 +7580,15 @@ WorkerStructuredCloneClosure::WorkerStru
 WorkerStructuredCloneClosure::~WorkerStructuredCloneClosure()
 {}
 
 void
 WorkerStructuredCloneClosure::Clear()
 {
   mParentWindow = nullptr;
   mClonedObjects.Clear();
+  mClonedImages.Clear();
   mMessagePorts.Clear();
   mMessagePortIdentifiers.Clear();
   mTransferredPorts.Clear();
 }
 
 END_WORKERS_NAMESPACE
--- a/dom/workers/WorkerStructuredClone.h
+++ b/dom/workers/WorkerStructuredClone.h
@@ -7,16 +7,20 @@
 #define mozilla_dom_workers_WorkerStructuredClone_h
 
 #include "Workers.h"
 #include "mozilla/dom/PMessagePort.h"
 
 class nsPIDOMWindow;
 
 namespace mozilla {
+namespace layers {
+class Image;
+}
+
 namespace dom {
 
 class MessagePortBase;
 
 namespace workers {
 
 // This class is implemented in WorkerPrivate.cpp
 class WorkerStructuredCloneClosure final
@@ -31,16 +35,22 @@ public:
 
   void Clear();
 
   // This can be null if the MessagePort is created in a worker.
   nsCOMPtr<nsPIDOMWindow> mParentWindow;
 
   nsTArray<nsCOMPtr<nsISupports>> mClonedObjects;
 
+  // This is used for sharing the backend of ImageBitmaps.
+  // The layers::Image object must be thread-safely reference-counted.
+  // The layers::Image object will not be written ever via any ImageBitmap
+  // instance, so no race condition will occur.
+  nsTArray<nsRefPtr<layers::Image>> mClonedImages;
+
   // The transferred ports.
   nsTArray<nsRefPtr<MessagePortBase>> mMessagePorts;
 
   // Information for the transferring.
   nsTArray<MessagePortIdentifier> mMessagePortIdentifiers;
 
   // To avoid duplicates in the transferred ports.
   nsTArray<nsRefPtr<MessagePortBase>> mTransferredPorts;
--- a/dom/workers/XMLHttpRequest.cpp
+++ b/dom/workers/XMLHttpRequest.cpp
@@ -806,16 +806,17 @@ public:
                nsIEventTarget* aSyncLoopTarget, bool aHasUploadListeners)
   : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy)
   , mStringBody(aStringBody)
   , mBody(Move(aBody))
   , mSyncLoopTarget(aSyncLoopTarget)
   , mHasUploadListeners(aHasUploadListeners)
   {
     mClosure.mClonedObjects.SwapElements(aClosure.mClonedObjects);
+    mClosure.mClonedImages.SwapElements(aClosure.mClonedImages);
     MOZ_ASSERT(aClosure.mMessagePorts.IsEmpty());
     MOZ_ASSERT(aClosure.mMessagePortIdentifiers.IsEmpty());
   }
 
 private:
   ~SendRunnable()
   { }
 
@@ -1232,16 +1233,17 @@ EventRunnable::PreDispatch(JSContext* aC
             workers::ChromeWorkerStructuredCloneCallbacks(true) :
             workers::WorkerStructuredCloneCallbacks(true);
 
           WorkerStructuredCloneClosure closure;
 
           if (mResponseBuffer.write(aCx, response, transferable, callbacks,
                                     &closure)) {
             mResponseClosure.mClonedObjects.SwapElements(closure.mClonedObjects);
+            mResponseClosure.mClonedImages.SwapElements(closure.mClonedImages);
             MOZ_ASSERT(mResponseClosure.mMessagePorts.IsEmpty());
             MOZ_ASSERT(mResponseClosure.mMessagePortIdentifiers.IsEmpty());
           } else {
             NS_WARNING("Failed to clone response!");
             mResponseResult = NS_ERROR_DOM_DATA_CLONE_ERR;
             mProxy->mArrayBufferResponseWasTransferred = false;
           }
         }
@@ -1343,16 +1345,17 @@ EventRunnable::WorkerRun(JSContext* aCx,
 
         const JSStructuredCloneCallbacks* callbacks =
           aWorkerPrivate->IsChromeWorker() ?
           workers::ChromeWorkerStructuredCloneCallbacks(false) :
           workers::WorkerStructuredCloneCallbacks(false);
 
         WorkerStructuredCloneClosure closure;
         closure.mClonedObjects.SwapElements(mResponseClosure.mClonedObjects);
+        closure.mClonedImages.SwapElements(mResponseClosure.mClonedImages);
         MOZ_ASSERT(mResponseClosure.mMessagePorts.IsEmpty());
         MOZ_ASSERT(mResponseClosure.mMessagePortIdentifiers.IsEmpty());
 
         JS::Rooted<JS::Value> response(aCx);
         if (!responseBuffer.read(aCx, &response, callbacks, &closure)) {
           return false;
         }