michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * vim: set ts=8 sw=2 et tw=80: michael@0: * michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsStructuredCloneContainer.h" michael@0: michael@0: #include "nsCOMPtr.h" michael@0: #include "nsIScriptContext.h" michael@0: #include "nsIVariant.h" michael@0: #include "nsIXPConnect.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: #include "nsContentUtils.h" michael@0: #include "jsapi.h" michael@0: #include "js/StructuredClone.h" michael@0: michael@0: #include "mozilla/Base64.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: NS_IMPL_ADDREF(nsStructuredCloneContainer) michael@0: NS_IMPL_RELEASE(nsStructuredCloneContainer) michael@0: michael@0: NS_INTERFACE_MAP_BEGIN(nsStructuredCloneContainer) michael@0: NS_INTERFACE_MAP_ENTRY(nsIStructuredCloneContainer) michael@0: NS_INTERFACE_MAP_ENTRY(nsISupports) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: nsStructuredCloneContainer::nsStructuredCloneContainer() michael@0: : mData(nullptr), mSize(0), mVersion(0) michael@0: { michael@0: } michael@0: michael@0: nsStructuredCloneContainer::~nsStructuredCloneContainer() michael@0: { michael@0: free(mData); michael@0: } michael@0: michael@0: nsresult michael@0: nsStructuredCloneContainer::InitFromJSVal(JS::Handle aData, michael@0: JSContext* aCx) michael@0: { michael@0: NS_ENSURE_STATE(!mData); michael@0: NS_ENSURE_ARG_POINTER(aCx); michael@0: michael@0: // Make sure that we serialize in the right context. michael@0: MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext()); michael@0: JS::Rooted jsData(aCx, aData); michael@0: bool success = JS_WrapValue(aCx, &jsData); michael@0: NS_ENSURE_STATE(success); michael@0: michael@0: uint64_t* jsBytes = nullptr; michael@0: success = JS_WriteStructuredClone(aCx, jsData, &jsBytes, &mSize, michael@0: nullptr, nullptr, michael@0: JS::UndefinedHandleValue); michael@0: NS_ENSURE_STATE(success); michael@0: NS_ENSURE_STATE(jsBytes); michael@0: michael@0: // Copy jsBytes into our own buffer. michael@0: mData = (uint64_t*) malloc(mSize); michael@0: if (!mData) { michael@0: mSize = 0; michael@0: mVersion = 0; michael@0: michael@0: JS_ClearStructuredClone(jsBytes, mSize, nullptr, nullptr); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: else { michael@0: mVersion = JS_STRUCTURED_CLONE_VERSION; michael@0: } michael@0: michael@0: memcpy(mData, jsBytes, mSize); michael@0: michael@0: JS_ClearStructuredClone(jsBytes, mSize, nullptr, nullptr); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsStructuredCloneContainer::InitFromBase64(const nsAString &aData, michael@0: uint32_t aFormatVersion, michael@0: JSContext *aCx) michael@0: { michael@0: NS_ENSURE_STATE(!mData); michael@0: michael@0: NS_ConvertUTF16toUTF8 data(aData); michael@0: michael@0: nsAutoCString binaryData; michael@0: nsresult rv = Base64Decode(data, binaryData); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Copy the string's data into our own buffer. michael@0: mData = (uint64_t*) malloc(binaryData.Length()); michael@0: NS_ENSURE_STATE(mData); michael@0: memcpy(mData, binaryData.get(), binaryData.Length()); michael@0: michael@0: mSize = binaryData.Length(); michael@0: mVersion = aFormatVersion; michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: nsStructuredCloneContainer::DeserializeToVariant(JSContext *aCx, michael@0: nsIVariant **aData) michael@0: { michael@0: NS_ENSURE_STATE(mData); michael@0: NS_ENSURE_ARG_POINTER(aData); michael@0: *aData = nullptr; michael@0: michael@0: // Deserialize to a JS::Value. michael@0: JS::Rooted jsStateObj(aCx); michael@0: bool hasTransferable = false; michael@0: bool success = JS_ReadStructuredClone(aCx, mData, mSize, mVersion, michael@0: &jsStateObj, nullptr, nullptr) && michael@0: JS_StructuredCloneHasTransferables(mData, mSize, michael@0: &hasTransferable); michael@0: // We want to be sure that mData doesn't contain transferable objects michael@0: MOZ_ASSERT(!hasTransferable); michael@0: NS_ENSURE_STATE(success && !hasTransferable); michael@0: michael@0: // Now wrap the JS::Value as an nsIVariant. michael@0: nsCOMPtr varStateObj; michael@0: nsCOMPtr xpconnect = do_GetService(nsIXPConnect::GetCID()); michael@0: NS_ENSURE_STATE(xpconnect); michael@0: xpconnect->JSValToVariant(aCx, jsStateObj, getter_AddRefs(varStateObj)); michael@0: NS_ENSURE_STATE(varStateObj); michael@0: michael@0: NS_ADDREF(*aData = varStateObj); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsStructuredCloneContainer::GetDataAsBase64(nsAString &aOut) michael@0: { michael@0: NS_ENSURE_STATE(mData); michael@0: aOut.Truncate(); michael@0: michael@0: nsAutoCString binaryData(reinterpret_cast(mData), mSize); michael@0: nsAutoCString base64Data; michael@0: nsresult rv = Base64Encode(binaryData, base64Data); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: aOut.Assign(NS_ConvertASCIItoUTF16(base64Data)); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsStructuredCloneContainer::GetSerializedNBytes(uint64_t *aSize) michael@0: { michael@0: NS_ENSURE_STATE(mData); michael@0: NS_ENSURE_ARG_POINTER(aSize); michael@0: michael@0: // mSize is a size_t, while aSize is a uint64_t. We rely on an implicit cast michael@0: // here so that we'll get a compile error if a size_t-to-uint64_t cast is michael@0: // narrowing. michael@0: *aSize = mSize; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsStructuredCloneContainer::GetFormatVersion(uint32_t *aFormatVersion) michael@0: { michael@0: NS_ENSURE_STATE(mData); michael@0: NS_ENSURE_ARG_POINTER(aFormatVersion); michael@0: *aFormatVersion = mVersion; michael@0: return NS_OK; michael@0: }