|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- |
|
2 * vim: set ts=8 sw=2 et tw=80: |
|
3 * |
|
4 * This Source Code Form is subject to the terms of the Mozilla Public |
|
5 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
7 |
|
8 #include "nsStructuredCloneContainer.h" |
|
9 |
|
10 #include "nsCOMPtr.h" |
|
11 #include "nsIScriptContext.h" |
|
12 #include "nsIVariant.h" |
|
13 #include "nsIXPConnect.h" |
|
14 #include "nsServiceManagerUtils.h" |
|
15 #include "nsContentUtils.h" |
|
16 #include "jsapi.h" |
|
17 #include "js/StructuredClone.h" |
|
18 |
|
19 #include "mozilla/Base64.h" |
|
20 |
|
21 using namespace mozilla; |
|
22 |
|
23 NS_IMPL_ADDREF(nsStructuredCloneContainer) |
|
24 NS_IMPL_RELEASE(nsStructuredCloneContainer) |
|
25 |
|
26 NS_INTERFACE_MAP_BEGIN(nsStructuredCloneContainer) |
|
27 NS_INTERFACE_MAP_ENTRY(nsIStructuredCloneContainer) |
|
28 NS_INTERFACE_MAP_ENTRY(nsISupports) |
|
29 NS_INTERFACE_MAP_END |
|
30 |
|
31 nsStructuredCloneContainer::nsStructuredCloneContainer() |
|
32 : mData(nullptr), mSize(0), mVersion(0) |
|
33 { |
|
34 } |
|
35 |
|
36 nsStructuredCloneContainer::~nsStructuredCloneContainer() |
|
37 { |
|
38 free(mData); |
|
39 } |
|
40 |
|
41 nsresult |
|
42 nsStructuredCloneContainer::InitFromJSVal(JS::Handle<JS::Value> aData, |
|
43 JSContext* aCx) |
|
44 { |
|
45 NS_ENSURE_STATE(!mData); |
|
46 NS_ENSURE_ARG_POINTER(aCx); |
|
47 |
|
48 // Make sure that we serialize in the right context. |
|
49 MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext()); |
|
50 JS::Rooted<JS::Value> jsData(aCx, aData); |
|
51 bool success = JS_WrapValue(aCx, &jsData); |
|
52 NS_ENSURE_STATE(success); |
|
53 |
|
54 uint64_t* jsBytes = nullptr; |
|
55 success = JS_WriteStructuredClone(aCx, jsData, &jsBytes, &mSize, |
|
56 nullptr, nullptr, |
|
57 JS::UndefinedHandleValue); |
|
58 NS_ENSURE_STATE(success); |
|
59 NS_ENSURE_STATE(jsBytes); |
|
60 |
|
61 // Copy jsBytes into our own buffer. |
|
62 mData = (uint64_t*) malloc(mSize); |
|
63 if (!mData) { |
|
64 mSize = 0; |
|
65 mVersion = 0; |
|
66 |
|
67 JS_ClearStructuredClone(jsBytes, mSize, nullptr, nullptr); |
|
68 return NS_ERROR_FAILURE; |
|
69 } |
|
70 else { |
|
71 mVersion = JS_STRUCTURED_CLONE_VERSION; |
|
72 } |
|
73 |
|
74 memcpy(mData, jsBytes, mSize); |
|
75 |
|
76 JS_ClearStructuredClone(jsBytes, mSize, nullptr, nullptr); |
|
77 return NS_OK; |
|
78 } |
|
79 |
|
80 nsresult |
|
81 nsStructuredCloneContainer::InitFromBase64(const nsAString &aData, |
|
82 uint32_t aFormatVersion, |
|
83 JSContext *aCx) |
|
84 { |
|
85 NS_ENSURE_STATE(!mData); |
|
86 |
|
87 NS_ConvertUTF16toUTF8 data(aData); |
|
88 |
|
89 nsAutoCString binaryData; |
|
90 nsresult rv = Base64Decode(data, binaryData); |
|
91 NS_ENSURE_SUCCESS(rv, rv); |
|
92 |
|
93 // Copy the string's data into our own buffer. |
|
94 mData = (uint64_t*) malloc(binaryData.Length()); |
|
95 NS_ENSURE_STATE(mData); |
|
96 memcpy(mData, binaryData.get(), binaryData.Length()); |
|
97 |
|
98 mSize = binaryData.Length(); |
|
99 mVersion = aFormatVersion; |
|
100 return NS_OK; |
|
101 } |
|
102 |
|
103 |
|
104 nsresult |
|
105 nsStructuredCloneContainer::DeserializeToVariant(JSContext *aCx, |
|
106 nsIVariant **aData) |
|
107 { |
|
108 NS_ENSURE_STATE(mData); |
|
109 NS_ENSURE_ARG_POINTER(aData); |
|
110 *aData = nullptr; |
|
111 |
|
112 // Deserialize to a JS::Value. |
|
113 JS::Rooted<JS::Value> jsStateObj(aCx); |
|
114 bool hasTransferable = false; |
|
115 bool success = JS_ReadStructuredClone(aCx, mData, mSize, mVersion, |
|
116 &jsStateObj, nullptr, nullptr) && |
|
117 JS_StructuredCloneHasTransferables(mData, mSize, |
|
118 &hasTransferable); |
|
119 // We want to be sure that mData doesn't contain transferable objects |
|
120 MOZ_ASSERT(!hasTransferable); |
|
121 NS_ENSURE_STATE(success && !hasTransferable); |
|
122 |
|
123 // Now wrap the JS::Value as an nsIVariant. |
|
124 nsCOMPtr<nsIVariant> varStateObj; |
|
125 nsCOMPtr<nsIXPConnect> xpconnect = do_GetService(nsIXPConnect::GetCID()); |
|
126 NS_ENSURE_STATE(xpconnect); |
|
127 xpconnect->JSValToVariant(aCx, jsStateObj, getter_AddRefs(varStateObj)); |
|
128 NS_ENSURE_STATE(varStateObj); |
|
129 |
|
130 NS_ADDREF(*aData = varStateObj); |
|
131 return NS_OK; |
|
132 } |
|
133 |
|
134 nsresult |
|
135 nsStructuredCloneContainer::GetDataAsBase64(nsAString &aOut) |
|
136 { |
|
137 NS_ENSURE_STATE(mData); |
|
138 aOut.Truncate(); |
|
139 |
|
140 nsAutoCString binaryData(reinterpret_cast<char*>(mData), mSize); |
|
141 nsAutoCString base64Data; |
|
142 nsresult rv = Base64Encode(binaryData, base64Data); |
|
143 NS_ENSURE_SUCCESS(rv, rv); |
|
144 |
|
145 aOut.Assign(NS_ConvertASCIItoUTF16(base64Data)); |
|
146 return NS_OK; |
|
147 } |
|
148 |
|
149 nsresult |
|
150 nsStructuredCloneContainer::GetSerializedNBytes(uint64_t *aSize) |
|
151 { |
|
152 NS_ENSURE_STATE(mData); |
|
153 NS_ENSURE_ARG_POINTER(aSize); |
|
154 |
|
155 // mSize is a size_t, while aSize is a uint64_t. We rely on an implicit cast |
|
156 // here so that we'll get a compile error if a size_t-to-uint64_t cast is |
|
157 // narrowing. |
|
158 *aSize = mSize; |
|
159 |
|
160 return NS_OK; |
|
161 } |
|
162 |
|
163 nsresult |
|
164 nsStructuredCloneContainer::GetFormatVersion(uint32_t *aFormatVersion) |
|
165 { |
|
166 NS_ENSURE_STATE(mData); |
|
167 NS_ENSURE_ARG_POINTER(aFormatVersion); |
|
168 *aFormatVersion = mVersion; |
|
169 return NS_OK; |
|
170 } |