Bug 1189389 - nsIStructuredCloneContainer should use StructuredCloneHelper, r=smaug
authorAndrea Marchesini <amarchesini@mozilla.com>
Wed, 26 Aug 2015 13:17:23 +0100
changeset 259376 f7893bbf4cb3809fe88a78587e9e23a7a400ecd4
parent 259375 0a6629904b68ff9aca323231cde87643ecbc82b3
child 259377 35603f958952f410eb19666286ed1ba22f0b42fa
push id29277
push userryanvm@gmail.com
push dateWed, 26 Aug 2015 18:32:23 +0000
treeherdermozilla-central@fea87cbeaa6b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1189389
milestone43.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 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