StructuredCloneBlob.cpp (7589B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "mozilla/dom/StructuredCloneBlob.h" 8 9 #include <cstddef> 10 #include <cstdint> 11 #include <new> 12 #include <utility> 13 14 #include "js/StructuredClone.h" 15 #include "js/Value.h" 16 #include "js/Wrapper.h" 17 #include "jsapi.h" 18 #include "mozilla/ErrorResult.h" 19 #include "mozilla/Maybe.h" 20 #include "mozilla/UniquePtr.h" 21 #include "mozilla/dom/BindingDeclarations.h" 22 #include "mozilla/dom/BlobImpl.h" 23 #include "mozilla/dom/StructuredCloneHolderBinding.h" 24 #include "mozilla/dom/StructuredCloneTags.h" 25 #include "nsPrintfCString.h" 26 #include "xpcpublic.h" 27 28 namespace mozilla::dom { 29 30 StructuredCloneBlob::StructuredCloneBlob() { 31 mHolder.emplace(Holder::CloningSupported, Holder::TransferringNotSupported, 32 Holder::StructuredCloneScope::DifferentProcess); 33 } 34 35 StructuredCloneBlob::~StructuredCloneBlob() { 36 UnregisterWeakMemoryReporter(this); 37 } 38 39 /* static */ 40 already_AddRefed<StructuredCloneBlob> StructuredCloneBlob::Constructor( 41 GlobalObject& aGlobal, const nsACString& aName, 42 const nsACString& aAnonymizedName, JS::Handle<JS::Value> aValue, 43 JS::Handle<JSObject*> aTargetGlobal, ErrorResult& aRv) { 44 JSContext* cx = aGlobal.Context(); 45 46 RefPtr<StructuredCloneBlob> holder = StructuredCloneBlob::Create(); 47 48 holder->mName = aName; 49 holder->mAnonymizedName = aAnonymizedName.IsVoid() ? aName : aAnonymizedName; 50 51 Maybe<JSAutoRealm> ar; 52 JS::Rooted<JS::Value> value(cx, aValue); 53 54 if (aTargetGlobal) { 55 // OK to unwrap if our caller (represented by cx's Realm) can do it. 56 JS::Rooted<JSObject*> targetGlobal( 57 cx, js::CheckedUnwrapDynamic(aTargetGlobal, cx)); 58 if (!targetGlobal) { 59 js::ReportAccessDenied(cx); 60 aRv.NoteJSContextException(cx); 61 return nullptr; 62 } 63 64 ar.emplace(cx, targetGlobal); 65 66 if (!JS_WrapValue(cx, &value)) { 67 aRv.NoteJSContextException(cx); 68 return nullptr; 69 } 70 } else if (value.isObject()) { 71 // OK to unwrap if our caller (represented by cx's Realm) can do it. 72 JS::Rooted<JSObject*> obj(cx, 73 js::CheckedUnwrapDynamic(&value.toObject(), cx)); 74 if (!obj) { 75 js::ReportAccessDenied(cx); 76 aRv.NoteJSContextException(cx); 77 return nullptr; 78 } 79 80 ar.emplace(cx, obj); 81 value = JS::ObjectValue(*obj); 82 } 83 84 holder->mHolder->Write(cx, value, aRv); 85 if (aRv.Failed()) { 86 return nullptr; 87 } 88 89 return holder.forget(); 90 } 91 92 void StructuredCloneBlob::Deserialize(JSContext* aCx, 93 JS::Handle<JSObject*> aTargetScope, 94 bool aKeepData, 95 JS::MutableHandle<JS::Value> aResult, 96 ErrorResult& aRv) { 97 // OK to unwrap if our caller (represented by aCx's Realm) can do it. 98 JS::Rooted<JSObject*> scope(aCx, js::CheckedUnwrapDynamic(aTargetScope, aCx)); 99 if (!scope) { 100 js::ReportAccessDenied(aCx); 101 aRv.NoteJSContextException(aCx); 102 return; 103 } 104 105 if (!mHolder.isSome()) { 106 aRv.Throw(NS_ERROR_NOT_INITIALIZED); 107 return; 108 } 109 110 { 111 JSAutoRealm ar(aCx, scope); 112 113 mHolder->Read(xpc::NativeGlobal(scope), aCx, aResult, aRv); 114 if (aRv.Failed()) { 115 return; 116 } 117 } 118 119 if (!aKeepData) { 120 mHolder.reset(); 121 } 122 123 if (!JS_WrapValue(aCx, aResult)) { 124 aResult.set(JS::UndefinedValue()); 125 aRv.NoteJSContextException(aCx); 126 } 127 } 128 129 /* static */ 130 JSObject* StructuredCloneBlob::ReadStructuredClone( 131 JSContext* aCx, JSStructuredCloneReader* aReader, 132 StructuredCloneHolder* aHolder) { 133 JS::Rooted<JSObject*> obj(aCx); 134 { 135 RefPtr<StructuredCloneBlob> holder = StructuredCloneBlob::Create(); 136 137 if (!StructuredCloneHolder::ReadCString(aReader, holder->mName)) { 138 return nullptr; 139 } 140 141 if (!StructuredCloneHolder::ReadCString(aReader, holder->mAnonymizedName)) { 142 return nullptr; 143 } 144 145 if (!holder->mHolder->ReadStructuredCloneInternal(aCx, aReader, aHolder) || 146 !holder->WrapObject(aCx, nullptr, &obj)) { 147 return nullptr; 148 } 149 } 150 return obj.get(); 151 } 152 153 bool StructuredCloneBlob::Holder::ReadStructuredCloneInternal( 154 JSContext* aCx, JSStructuredCloneReader* aReader, 155 StructuredCloneHolder* aHolder) { 156 uint32_t length; 157 uint32_t version; 158 if (!JS_ReadUint32Pair(aReader, &length, &version)) { 159 return false; 160 } 161 if (length % 8 != 0) { 162 return false; 163 } 164 165 uint32_t blobOffset; 166 uint32_t blobCount; 167 if (!JS_ReadUint32Pair(aReader, &blobOffset, &blobCount)) { 168 return false; 169 } 170 if (blobCount) { 171 #ifdef FUZZING 172 if (blobOffset >= aHolder->BlobImpls().Length()) { 173 return false; 174 } 175 #endif 176 BlobImpls().AppendElements(&aHolder->BlobImpls()[blobOffset], blobCount); 177 } 178 179 JSStructuredCloneData data(mStructuredCloneScope); 180 while (length) { 181 size_t size; 182 char* buffer = data.AllocateBytes(length, &size); 183 if (!buffer || !JS_ReadBytes(aReader, buffer, size)) { 184 return false; 185 } 186 length -= size; 187 } 188 189 mBuffer = MakeUnique<JSAutoStructuredCloneBuffer>( 190 mStructuredCloneScope, &StructuredCloneHolder::sCallbacks, this); 191 mBuffer->adopt(std::move(data), version, &StructuredCloneHolder::sCallbacks); 192 193 return true; 194 } 195 196 bool StructuredCloneBlob::WriteStructuredClone(JSContext* aCx, 197 JSStructuredCloneWriter* aWriter, 198 StructuredCloneHolder* aHolder) { 199 if (mHolder.isNothing()) { 200 return false; 201 } 202 203 if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_STRUCTURED_CLONE_HOLDER, 0) || 204 !StructuredCloneHolder::WriteCString(aWriter, mName) || 205 !StructuredCloneHolder::WriteCString(aWriter, mAnonymizedName)) { 206 return false; 207 } 208 209 return mHolder->WriteStructuredClone(aCx, aWriter, aHolder); 210 } 211 212 bool StructuredCloneBlob::Holder::WriteStructuredClone( 213 JSContext* aCx, JSStructuredCloneWriter* aWriter, 214 StructuredCloneHolder* aHolder) { 215 auto& data = mBuffer->data(); 216 if (!JS_WriteUint32Pair(aWriter, data.Size(), JS_STRUCTURED_CLONE_VERSION) || 217 !JS_WriteUint32Pair(aWriter, aHolder->BlobImpls().Length(), 218 BlobImpls().Length())) { 219 return false; 220 } 221 222 aHolder->BlobImpls().AppendElements(BlobImpls()); 223 224 return data.ForEachDataChunk([&](const char* aData, size_t aSize) { 225 return JS_WriteBytes(aWriter, aData, aSize); 226 }); 227 } 228 229 bool StructuredCloneBlob::WrapObject(JSContext* aCx, 230 JS::Handle<JSObject*> aGivenProto, 231 JS::MutableHandle<JSObject*> aResult) { 232 return StructuredCloneHolder_Binding::Wrap(aCx, this, aGivenProto, aResult); 233 } 234 235 NS_IMETHODIMP 236 StructuredCloneBlob::CollectReports(nsIHandleReportCallback* aHandleReport, 237 nsISupports* aData, bool aAnonymize) { 238 size_t size = MallocSizeOf(this); 239 if (mHolder.isSome()) { 240 size += mHolder->SizeOfExcludingThis(MallocSizeOf); 241 } 242 243 aHandleReport->Callback( 244 ""_ns, 245 nsPrintfCString("explicit/dom/structured-clone-holder/%s", 246 aAnonymize ? mAnonymizedName.get() : mName.get()), 247 KIND_HEAP, UNITS_BYTES, size, 248 "Memory used by StructuredCloneHolder DOM objects."_ns, aData); 249 250 return NS_OK; 251 } 252 253 NS_IMPL_ISUPPORTS(StructuredCloneBlob, nsIMemoryReporter) 254 255 } // namespace mozilla::dom