Bug 1189389 - nsIStructuredCloneContainer should use StructuredCloneHelper, r=smaug
authorAndrea Marchesini <amarchesini@mozilla.com>
Wed, 26 Aug 2015 13:17:23 +0100
changeset 288047 f7893bbf4cb3809fe88a78587e9e23a7a400ecd4
parent 288046 0a6629904b68ff9aca323231cde87643ecbc82b3
child 288048 35603f958952f410eb19666286ed1ba22f0b42fa
push id4792
push useryura.zenevich@gmail.com
push dateWed, 26 Aug 2015 20:00:52 +0000
reviewerssmaug
bugs1189389
milestone43.0a1
Bug 1189389 - nsIStructuredCloneContainer should use StructuredCloneHelper, r=smaug
dom/base/StructuredCloneHelper.cpp
dom/base/StructuredCloneHelper.h
dom/base/nsStructuredCloneContainer.cpp
dom/base/nsStructuredCloneContainer.h
--- a/dom/base/StructuredCloneHelper.cpp
+++ b/dom/base/StructuredCloneHelper.cpp
@@ -127,17 +127,16 @@ StructuredCloneHelperInternal::~Structur
   MOZ_ASSERT(mShutdownCalled);
 #endif
 }
 
 void
 StructuredCloneHelperInternal::Shutdown()
 {
 #ifdef DEBUG
-  MOZ_ASSERT(!mShutdownCalled, "Shutdown already called!");
   mShutdownCalled = true;
 #endif
 
   mBuffer = nullptr;
 }
 
 bool
 StructuredCloneHelperInternal::Write(JSContext* aCx,
@@ -165,17 +164,16 @@ StructuredCloneHelperInternal::Write(JSC
 bool
 StructuredCloneHelperInternal::Read(JSContext* aCx,
                                     JS::MutableHandle<JS::Value> aValue)
 {
   MOZ_ASSERT(mBuffer, "Read() without Write() is not allowed.");
   MOZ_ASSERT(!mShutdownCalled, "This method cannot be called after Shutdown.");
 
   bool ok = mBuffer->read(aCx, aValue, &gCallbacks, this);
-  mBuffer = nullptr;
   return ok;
 }
 
 bool
 StructuredCloneHelperInternal::ReadTransferCallback(JSContext* aCx,
                                                     JSStructuredCloneReader* aReader,
                                                     uint32_t aTag,
                                                     void* aContent,
@@ -249,36 +247,52 @@ StructuredCloneHelper::Read(nsISupports*
   mozilla::AutoRestore<nsISupports*> guard(mParent);
   mParent = aParent;
 
   if (!StructuredCloneHelperInternal::Read(aCx, aValue)) {
     JS_ClearPendingException(aCx);
     aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
   }
 
-  mBlobImplArray.Clear();
+  // If we are tranferring something, we cannot call 'Read()' more than once.
+  if (mSupportsTransferring) {
+    mBlobImplArray.Clear();
+    Shutdown();
+  }
 }
 
 void
 StructuredCloneHelper::ReadFromBuffer(nsISupports* aParent,
                                       JSContext* aCx,
                                       uint64_t* aBuffer,
                                       size_t aBufferLength,
                                       JS::MutableHandle<JS::Value> aValue,
                                       ErrorResult& aRv)
 {
+  ReadFromBuffer(aParent, aCx, aBuffer, aBufferLength,
+                 JS_STRUCTURED_CLONE_VERSION, aValue, aRv);
+}
+
+void
+StructuredCloneHelper::ReadFromBuffer(nsISupports* aParent,
+                                      JSContext* aCx,
+                                      uint64_t* aBuffer,
+                                      size_t aBufferLength,
+                                      uint32_t aAlgorithmVersion,
+                                      JS::MutableHandle<JS::Value> aValue,
+                                      ErrorResult& aRv)
+{
   MOZ_ASSERT(!mBuffer, "ReadFromBuffer() must be called without a Write().");
   MOZ_ASSERT(aBuffer);
 
   mozilla::AutoRestore<nsISupports*> guard(mParent);
   mParent = aParent;
 
-  if (!JS_ReadStructuredClone(aCx, aBuffer, aBufferLength,
-                              JS_STRUCTURED_CLONE_VERSION, aValue,
-                              &gCallbacks, this)) {
+  if (!JS_ReadStructuredClone(aCx, aBuffer, aBufferLength, aAlgorithmVersion,
+                              aValue, &gCallbacks, this)) {
     JS_ClearPendingException(aCx);
     aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
   }
 }
 
 void
 StructuredCloneHelper::MoveBufferDataToArray(FallibleTArray<uint8_t>& aArray,
                                              ErrorResult& aRv)
--- a/dom/base/StructuredCloneHelper.h
+++ b/dom/base/StructuredCloneHelper.h
@@ -155,20 +155,34 @@ public:
   // and/or the PortIdentifiers.
   void ReadFromBuffer(nsISupports* aParent,
                       JSContext* aCx,
                       uint64_t* aBuffer,
                       size_t aBufferLength,
                       JS::MutableHandle<JS::Value> aValue,
                       ErrorResult &aRv);
 
+  void ReadFromBuffer(nsISupports* aParent,
+                      JSContext* aCx,
+                      uint64_t* aBuffer,
+                      size_t aBufferLength,
+                      uint32_t aAlgorithmVersion,
+                      JS::MutableHandle<JS::Value> aValue,
+                      ErrorResult &aRv);
+
   // Use this method to free a buffer generated by MoveToBuffer().
   void FreeBuffer(uint64_t* aBuffer,
                   size_t aBufferLength);
 
+  bool HasClonedDOMObjects() const
+  {
+    return !mBlobImplArray.IsEmpty() ||
+           !mClonedImages.IsEmpty();
+  }
+
   nsTArray<nsRefPtr<BlobImpl>>& BlobImpls()
   {
     MOZ_ASSERT(mSupportsCloning, "Blobs cannot be taken/set if cloning is not supported.");
     return mBlobImplArray;
   }
 
   const nsTArray<nsRefPtr<MessagePortBase>>& GetTransferredPorts() const
   {
--- a/dom/base/nsStructuredCloneContainer.cpp
+++ b/dom/base/nsStructuredCloneContainer.cpp
@@ -8,170 +8,220 @@
 
 #include "nsCOMPtr.h"
 #include "nsIGlobalObject.h"
 #include "nsIVariant.h"
 #include "nsIXPConnect.h"
 #include "nsServiceManagerUtils.h"
 #include "nsContentUtils.h"
 #include "jsapi.h"
-#include "jsfriendapi.h"
-#include "js/StructuredClone.h"
 #include "xpcpublic.h"
 
 #include "mozilla/Base64.h"
+#include "mozilla/dom/StructuredCloneHelper.h"
 #include "mozilla/dom/ScriptSettings.h"
 
 using namespace mozilla;
+using namespace mozilla::dom;
 
 NS_IMPL_ADDREF(nsStructuredCloneContainer)
 NS_IMPL_RELEASE(nsStructuredCloneContainer)
 
 NS_INTERFACE_MAP_BEGIN(nsStructuredCloneContainer)
   NS_INTERFACE_MAP_ENTRY(nsIStructuredCloneContainer)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 nsStructuredCloneContainer::nsStructuredCloneContainer()
-  : mData(nullptr), mSize(0), mVersion(0)
+  : StructuredCloneHelper(CloningSupported, TransferringNotSupported)
+  , mState(eNotInitialized) , mData(nullptr), mSize(0), mVersion(0)
 {
 }
 
 nsStructuredCloneContainer::~nsStructuredCloneContainer()
 {
-  free(mData);
+  if (mData) {
+    free(mData);
+  }
 }
 
-nsresult
+NS_IMETHODIMP
 nsStructuredCloneContainer::InitFromJSVal(JS::Handle<JS::Value> aData,
                                           JSContext* aCx)
 {
-  NS_ENSURE_STATE(!mData);
-
-  uint64_t* jsBytes = nullptr;
-  bool success = JS_WriteStructuredClone(aCx, aData, &jsBytes, &mSize,
-                                           nullptr, nullptr,
-                                           JS::UndefinedHandleValue);
-  NS_ENSURE_STATE(success);
-  NS_ENSURE_STATE(jsBytes);
-
-  // Copy jsBytes into our own buffer.
-  mData = (uint64_t*) malloc(mSize);
-  if (!mData) {
-    mSize = 0;
-    mVersion = 0;
-
-    JS_ClearStructuredClone(jsBytes, mSize, nullptr, nullptr);
+  if (mState != eNotInitialized) {
     return NS_ERROR_FAILURE;
   }
-  else {
-    mVersion = JS_STRUCTURED_CLONE_VERSION;
+
+  ErrorResult rv;
+  Write(aCx, aData, rv);
+  if (NS_WARN_IF(rv.Failed())) {
+    return rv.StealNSResult();
   }
 
-  memcpy(mData, jsBytes, mSize);
-
-  JS_ClearStructuredClone(jsBytes, mSize, nullptr, nullptr);
+  mState = eInitializedFromJSVal;
   return NS_OK;
 }
 
-nsresult
+NS_IMETHODIMP
 nsStructuredCloneContainer::InitFromBase64(const nsAString &aData,
                                            uint32_t aFormatVersion,
-                                           JSContext *aCx)
+                                           JSContext* aCx)
 {
-  NS_ENSURE_STATE(!mData);
+  if (mState != eNotInitialized) {
+    return NS_ERROR_FAILURE;
+  }
 
   NS_ConvertUTF16toUTF8 data(aData);
 
   nsAutoCString binaryData;
   nsresult rv = Base64Decode(data, binaryData);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Copy the string's data into our own buffer.
   mData = (uint64_t*) malloc(binaryData.Length());
   NS_ENSURE_STATE(mData);
   memcpy(mData, binaryData.get(), binaryData.Length());
 
   mSize = binaryData.Length();
   mVersion = aFormatVersion;
+
+  mState = eInitializedFromBase64;
   return NS_OK;
 }
 
 nsresult
 nsStructuredCloneContainer::DeserializeToJsval(JSContext* aCx,
                                                JS::MutableHandle<JS::Value> aValue)
 {
   aValue.setNull();
   JS::Rooted<JS::Value> jsStateObj(aCx);
-  bool hasTransferable = false;
-  bool success = JS_ReadStructuredClone(aCx, mData, mSize, mVersion,
-                                        &jsStateObj, nullptr, nullptr) &&
-                 JS_StructuredCloneHasTransferables(mData, mSize,
-                                                    &hasTransferable);
-  // We want to be sure that mData doesn't contain transferable objects
-  MOZ_ASSERT(!hasTransferable);
-  NS_ENSURE_STATE(success && !hasTransferable);
+
+  if (mState == eInitializedFromJSVal) {
+    ErrorResult rv;
+    Read(nullptr, aCx, &jsStateObj, rv);
+    if (NS_WARN_IF(rv.Failed())) {
+      return rv.StealNSResult();
+    }
+  } else {
+    MOZ_ASSERT(mState == eInitializedFromBase64);
+    MOZ_ASSERT(mData);
+
+    ErrorResult rv;
+    ReadFromBuffer(nullptr, aCx, mData, mSize, mVersion, &jsStateObj, rv);
+    if (NS_WARN_IF(rv.Failed())) {
+      return rv.StealNSResult();
+    }
+  }
 
   aValue.set(jsStateObj);
   return NS_OK;
 }
 
-nsresult
-nsStructuredCloneContainer::DeserializeToVariant(JSContext *aCx,
-                                                 nsIVariant **aData)
+NS_IMETHODIMP
+nsStructuredCloneContainer::DeserializeToVariant(JSContext* aCx,
+                                                 nsIVariant** aData)
 {
-  NS_ENSURE_STATE(mData);
   NS_ENSURE_ARG_POINTER(aData);
   *aData = nullptr;
 
+  if (mState == eNotInitialized) {
+    return NS_ERROR_FAILURE;
+  }
+
   // Deserialize to a JS::Value.
   JS::Rooted<JS::Value> jsStateObj(aCx);
   nsresult rv = DeserializeToJsval(aCx, &jsStateObj);
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
 
   // Now wrap the JS::Value as an nsIVariant.
   nsCOMPtr<nsIVariant> varStateObj;
   nsCOMPtr<nsIXPConnect> xpconnect = do_GetService(nsIXPConnect::GetCID());
   NS_ENSURE_STATE(xpconnect);
   xpconnect->JSValToVariant(aCx, jsStateObj, getter_AddRefs(varStateObj));
   NS_ENSURE_STATE(varStateObj);
 
-  NS_ADDREF(*aData = varStateObj);
+  varStateObj.forget(aData);
   return NS_OK;
 }
 
-nsresult
+NS_IMETHODIMP
 nsStructuredCloneContainer::GetDataAsBase64(nsAString &aOut)
 {
-  NS_ENSURE_STATE(mData);
   aOut.Truncate();
 
-  nsAutoCString binaryData(reinterpret_cast<char*>(mData), mSize);
+  if (mState == eNotInitialized) {
+    return NS_ERROR_FAILURE;
+  }
+
+  uint64_t* data;
+  size_t size;
+
+  if (mState == eInitializedFromJSVal) {
+    if (HasClonedDOMObjects()) {
+      return NS_ERROR_FAILURE;
+    }
+
+    data = BufferData();
+    size = BufferSize();
+  } else {
+    MOZ_ASSERT(mState == eInitializedFromBase64);
+    MOZ_ASSERT(mData);
+
+    data = mData;
+    size = mSize;
+  }
+
+  nsAutoCString binaryData(reinterpret_cast<char*>(data), size);
   nsAutoCString base64Data;
   nsresult rv = Base64Encode(binaryData, base64Data);
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
 
-  aOut.Assign(NS_ConvertASCIItoUTF16(base64Data));
+  CopyASCIItoUTF16(base64Data, aOut);
   return NS_OK;
 }
 
-nsresult
-nsStructuredCloneContainer::GetSerializedNBytes(uint64_t *aSize)
+NS_IMETHODIMP
+nsStructuredCloneContainer::GetSerializedNBytes(uint64_t* aSize)
 {
-  NS_ENSURE_STATE(mData);
   NS_ENSURE_ARG_POINTER(aSize);
 
+  if (mState == eNotInitialized) {
+    return NS_ERROR_FAILURE;
+  }
+
+  if (mState == eInitializedFromJSVal) {
+    *aSize = BufferSize();
+    return NS_OK;
+  }
+
+  MOZ_ASSERT(mState == eInitializedFromBase64);
+
   // mSize is a size_t, while aSize is a uint64_t.  We rely on an implicit cast
   // here so that we'll get a compile error if a size_t-to-uint64_t cast is
   // narrowing.
   *aSize = mSize;
 
   return NS_OK;
 }
 
-nsresult
-nsStructuredCloneContainer::GetFormatVersion(uint32_t *aFormatVersion)
+NS_IMETHODIMP
+nsStructuredCloneContainer::GetFormatVersion(uint32_t* aFormatVersion)
 {
-  NS_ENSURE_STATE(mData);
   NS_ENSURE_ARG_POINTER(aFormatVersion);
+
+  if (mState == eNotInitialized) {
+    return NS_ERROR_FAILURE;
+  }
+
+  if (mState == eInitializedFromJSVal) {
+    *aFormatVersion = JS_STRUCTURED_CLONE_VERSION;
+    return NS_OK;
+  }
+
+  MOZ_ASSERT(mState == eInitializedFromBase64);
   *aFormatVersion = mVersion;
   return NS_OK;
 }
--- a/dom/base/nsStructuredCloneContainer.h
+++ b/dom/base/nsStructuredCloneContainer.h
@@ -4,38 +4,47 @@
  * 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/. */
 
 #ifndef nsStructuredCloneContainer_h__
 #define nsStructuredCloneContainer_h__
 
 #include "nsIStructuredCloneContainer.h"
 #include "mozilla/Attributes.h"
+#include "mozilla/dom/StructuredCloneHelper.h"
 
 #define NS_STRUCTUREDCLONECONTAINER_CONTRACTID \
   "@mozilla.org/docshell/structured-clone-container;1"
 #define NS_STRUCTUREDCLONECONTAINER_CID \
 { /* 38bd0634-0fd4-46f0-b85f-13ced889eeec */       \
   0x38bd0634,                                      \
   0x0fd4,                                          \
   0x46f0,                                          \
   {0xb8, 0x5f, 0x13, 0xce, 0xd8, 0x89, 0xee, 0xec} \
 }
 
-class nsStructuredCloneContainer final : public nsIStructuredCloneContainer
+class nsStructuredCloneContainer final
+  : public nsIStructuredCloneContainer
+  , public mozilla::dom::StructuredCloneHelper
 {
   public:
     nsStructuredCloneContainer();
 
     NS_DECL_ISUPPORTS
     NS_DECL_NSISTRUCTUREDCLONECONTAINER
 
   private:
     ~nsStructuredCloneContainer();
 
+    enum {
+      eNotInitialized = 0,
+      eInitializedFromJSVal,
+      eInitializedFromBase64,
+    } mState;
+
     uint64_t* mData;
 
     // This needs to be size_t rather than a PR-type so it matches the JS API.
     size_t mSize;
     uint32_t mVersion;
 };
 
 #endif