StructuredCloneHolder.cpp (65607B)
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/StructuredCloneHolder.h" 8 9 #include <new> 10 11 #include "ErrorList.h" 12 #include "MainThreadUtils.h" 13 #include "js/CallArgs.h" 14 #include "js/Value.h" 15 #include "js/WasmModule.h" 16 #include "js/Wrapper.h" 17 #include "jsapi.h" 18 #include "mozilla/AlreadyAddRefed.h" 19 #include "mozilla/AutoRestore.h" 20 #include "mozilla/ErrorResult.h" 21 #include "mozilla/OwningNonNull.h" 22 #include "mozilla/RefPtr.h" 23 #include "mozilla/ScopeExit.h" 24 #include "mozilla/StaticPrefs_dom.h" 25 #ifdef MOZ_WEBRTC 26 # include "mozilla/StaticPrefs_media.h" 27 #endif 28 #include "mozilla/dom/AudioData.h" 29 #include "mozilla/dom/BindingDeclarations.h" 30 #include "mozilla/dom/BindingUtils.h" 31 #include "mozilla/dom/Blob.h" 32 #include "mozilla/dom/BlobBinding.h" 33 #include "mozilla/dom/BlobImpl.h" 34 #include "mozilla/dom/BrowsingContext.h" 35 #include "mozilla/dom/ClonedErrorHolder.h" 36 #include "mozilla/dom/ClonedErrorHolderBinding.h" 37 #include "mozilla/dom/DOMJSClass.h" 38 #include "mozilla/dom/DOMTypes.h" 39 #include "mozilla/dom/Directory.h" 40 #include "mozilla/dom/DirectoryBinding.h" 41 #include "mozilla/dom/DocGroup.h" 42 #include "mozilla/dom/EncodedAudioChunk.h" 43 #include "mozilla/dom/EncodedAudioChunkBinding.h" 44 #include "mozilla/dom/EncodedVideoChunk.h" 45 #include "mozilla/dom/EncodedVideoChunkBinding.h" 46 #include "mozilla/dom/File.h" 47 #include "mozilla/dom/FileList.h" 48 #include "mozilla/dom/FileListBinding.h" 49 #include "mozilla/dom/FormData.h" 50 #include "mozilla/dom/FormDataBinding.h" 51 #include "mozilla/dom/ImageBitmap.h" 52 #include "mozilla/dom/ImageBitmapBinding.h" 53 #include "mozilla/dom/JSExecutionManager.h" 54 #include "mozilla/dom/MessagePort.h" 55 #include "mozilla/dom/MessagePortBinding.h" 56 #include "mozilla/dom/OffscreenCanvas.h" 57 #include "mozilla/dom/OffscreenCanvasBinding.h" 58 #ifdef MOZ_WEBRTC 59 # include "mozilla/dom/RTCEncodedAudioFrame.h" 60 # include "mozilla/dom/RTCEncodedAudioFrameBinding.h" 61 # include "mozilla/dom/RTCEncodedVideoFrame.h" 62 # include "mozilla/dom/RTCEncodedVideoFrameBinding.h" 63 #endif 64 #include "mozilla/dom/ReadableStream.h" 65 #include "mozilla/dom/ReadableStreamBinding.h" 66 #include "mozilla/dom/ScriptSettings.h" 67 #include "mozilla/dom/StructuredCloneBlob.h" 68 #include "mozilla/dom/StructuredCloneHolderBinding.h" 69 #include "mozilla/dom/StructuredCloneTags.h" 70 #include "mozilla/dom/ToJSValue.h" 71 #include "mozilla/dom/TransformStream.h" 72 #include "mozilla/dom/TransformStreamBinding.h" 73 #include "mozilla/dom/VideoFrame.h" 74 #include "mozilla/dom/VideoFrameBinding.h" 75 #include "mozilla/dom/WebIDLSerializable.h" 76 #include "mozilla/dom/WorkerCommon.h" 77 #include "mozilla/dom/WorkerPrivate.h" 78 #include "mozilla/dom/WritableStream.h" 79 #include "mozilla/dom/WritableStreamBinding.h" 80 #include "mozilla/fallible.h" 81 #include "mozilla/gfx/2D.h" 82 #include "nsContentUtils.h" 83 #include "nsDebug.h" 84 #include "nsError.h" 85 #include "nsID.h" 86 #include "nsIEventTarget.h" 87 #include "nsIFile.h" 88 #include "nsIGlobalObject.h" 89 #include "nsIInputStream.h" 90 #include "nsIPrincipal.h" 91 #include "nsISupports.h" 92 #include "nsJSPrincipals.h" 93 #include "nsPIDOMWindow.h" 94 #include "nsString.h" 95 #include "nsThreadUtils.h" 96 #include "nsXPCOM.h" 97 #include "xpcpublic.h" 98 99 #ifdef MOZ_WEBRTC 100 # include "mozilla/dom/RTCDataChannel.h" 101 #endif 102 103 using namespace mozilla::ipc; 104 105 namespace mozilla::dom { 106 107 namespace { 108 109 JSObject* StructuredCloneCallbacksRead( 110 JSContext* aCx, JSStructuredCloneReader* aReader, 111 const JS::CloneDataPolicy& aCloneDataPolicy, uint32_t aTag, uint32_t aIndex, 112 void* aClosure) { 113 StructuredCloneHolderBase* holder = 114 static_cast<StructuredCloneHolderBase*>(aClosure); 115 MOZ_ASSERT(holder); 116 return holder->CustomReadHandler(aCx, aReader, aCloneDataPolicy, aTag, 117 aIndex); 118 } 119 120 bool StructuredCloneCallbacksWrite(JSContext* aCx, 121 JSStructuredCloneWriter* aWriter, 122 JS::Handle<JSObject*> aObj, 123 bool* aSameProcessScopeRequired, 124 void* aClosure) { 125 StructuredCloneHolderBase* holder = 126 static_cast<StructuredCloneHolderBase*>(aClosure); 127 MOZ_ASSERT(holder); 128 return holder->CustomWriteHandler(aCx, aWriter, aObj, 129 aSameProcessScopeRequired); 130 } 131 132 bool StructuredCloneCallbacksReadTransfer( 133 JSContext* aCx, JSStructuredCloneReader* aReader, 134 const JS::CloneDataPolicy& aCloneDataPolicy, uint32_t aTag, void* aContent, 135 uint64_t aExtraData, void* aClosure, 136 JS::MutableHandle<JSObject*> aReturnObject) { 137 StructuredCloneHolderBase* holder = 138 static_cast<StructuredCloneHolderBase*>(aClosure); 139 MOZ_ASSERT(holder); 140 return holder->CustomReadTransferHandler(aCx, aReader, aCloneDataPolicy, aTag, 141 aContent, aExtraData, aReturnObject); 142 } 143 144 bool StructuredCloneCallbacksWriteTransfer( 145 JSContext* aCx, JS::Handle<JSObject*> aObj, void* aClosure, 146 // Output: 147 uint32_t* aTag, JS::TransferableOwnership* aOwnership, void** aContent, 148 uint64_t* aExtraData) { 149 StructuredCloneHolderBase* holder = 150 static_cast<StructuredCloneHolderBase*>(aClosure); 151 MOZ_ASSERT(holder); 152 return holder->CustomWriteTransferHandler(aCx, aObj, aTag, aOwnership, 153 aContent, aExtraData); 154 } 155 156 void StructuredCloneCallbacksFreeTransfer(uint32_t aTag, 157 JS::TransferableOwnership aOwnership, 158 void* aContent, uint64_t aExtraData, 159 void* aClosure) { 160 StructuredCloneHolderBase* holder = 161 static_cast<StructuredCloneHolderBase*>(aClosure); 162 MOZ_ASSERT(holder); 163 return holder->CustomFreeTransferHandler(aTag, aOwnership, aContent, 164 aExtraData); 165 } 166 167 bool StructuredCloneCallbacksCanTransfer(JSContext* aCx, 168 JS::Handle<JSObject*> aObject, 169 bool* aSameProcessScopeRequired, 170 void* aClosure) { 171 StructuredCloneHolderBase* holder = 172 static_cast<StructuredCloneHolderBase*>(aClosure); 173 MOZ_ASSERT(holder); 174 return holder->CustomCanTransferHandler(aCx, aObject, 175 aSameProcessScopeRequired); 176 } 177 178 bool StructuredCloneCallbacksSharedArrayBuffer(JSContext* cx, bool aReceiving, 179 void* aClosure) { 180 if (!StaticPrefs::dom_workers_serialized_sab_access()) { 181 return true; 182 } 183 184 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 185 186 if (workerPrivate) { 187 workerPrivate->SetExecutionManager( 188 JSExecutionManager::GetSABSerializationManager()); 189 } else if (NS_IsMainThread()) { 190 nsIGlobalObject* global = GetCurrentGlobal(); 191 192 nsPIDOMWindowInner* innerWindow = nullptr; 193 if (global) { 194 innerWindow = global->GetAsInnerWindow(); 195 } 196 197 DocGroup* docGroup = nullptr; 198 if (innerWindow) { 199 docGroup = innerWindow->GetDocGroup(); 200 } 201 202 if (docGroup) { 203 docGroup->SetExecutionManager( 204 JSExecutionManager::GetSABSerializationManager()); 205 } 206 } 207 return true; 208 } 209 210 void StructuredCloneCallbacksError(JSContext* aCx, uint32_t aErrorId, 211 void* aClosure, const char* aErrorMessage) { 212 NS_WARNING("Failed to clone data."); 213 StructuredCloneHolderBase* holder = 214 static_cast<StructuredCloneHolderBase*>(aClosure); 215 MOZ_ASSERT(holder); 216 return holder->SetErrorMessage(aErrorMessage); 217 } 218 219 void AssertTagValues() { 220 static_assert(SCTAG_DOM_IMAGEDATA == 0xffff8007 && 221 SCTAG_DOM_DOMPOINT == 0xffff8008 && 222 SCTAG_DOM_DOMPOINTREADONLY == 0xffff8009 && 223 SCTAG_DOM_CRYPTOKEY == 0xffff800a && 224 SCTAG_DOM_NULL_PRINCIPAL == 0xffff800b && 225 SCTAG_DOM_SYSTEM_PRINCIPAL == 0xffff800c && 226 SCTAG_DOM_CONTENT_PRINCIPAL == 0xffff800d && 227 SCTAG_DOM_DOMQUAD == 0xffff800e && 228 SCTAG_DOM_RTCCERTIFICATE == 0xffff800f && 229 SCTAG_DOM_DOMRECT == 0xffff8010 && 230 SCTAG_DOM_DOMRECTREADONLY == 0xffff8011 && 231 SCTAG_DOM_EXPANDED_PRINCIPAL == 0xffff8012 && 232 SCTAG_DOM_DOMMATRIX == 0xffff8013 && 233 SCTAG_DOM_URLSEARCHPARAMS == 0xffff8014 && 234 SCTAG_DOM_DOMMATRIXREADONLY == 0xffff8015 && 235 SCTAG_DOM_STRUCTUREDCLONETESTER == 0xffff8018 && 236 SCTAG_DOM_FILESYSTEMHANDLE == 0xffff8019 && 237 SCTAG_DOM_FILESYSTEMFILEHANDLE == 0xffff801a && 238 SCTAG_DOM_FILESYSTEMDIRECTORYHANDLE == 0xffff801b, 239 "Something has changed the sctag values. This is wrong!"); 240 } 241 242 } // anonymous namespace 243 244 const JSStructuredCloneCallbacks StructuredCloneHolder::sCallbacks = { 245 StructuredCloneCallbacksRead, 246 StructuredCloneCallbacksWrite, 247 StructuredCloneCallbacksError, 248 StructuredCloneCallbacksReadTransfer, 249 StructuredCloneCallbacksWriteTransfer, 250 StructuredCloneCallbacksFreeTransfer, 251 StructuredCloneCallbacksCanTransfer, 252 StructuredCloneCallbacksSharedArrayBuffer, 253 }; 254 255 // StructuredCloneHolderBase class 256 257 StructuredCloneHolderBase::StructuredCloneHolderBase( 258 StructuredCloneScope aScope) 259 : mStructuredCloneScope(aScope) 260 #ifdef DEBUG 261 , 262 mClearCalled(false) 263 #endif 264 { 265 } 266 267 StructuredCloneHolderBase::~StructuredCloneHolderBase() { 268 #ifdef DEBUG 269 MOZ_ASSERT(mClearCalled); 270 #endif 271 } 272 273 void StructuredCloneHolderBase::Clear() { 274 #ifdef DEBUG 275 mClearCalled = true; 276 #endif 277 278 mBuffer = nullptr; 279 } 280 281 bool StructuredCloneHolderBase::Write(JSContext* aCx, 282 JS::Handle<JS::Value> aValue) { 283 return Write(aCx, aValue, JS::UndefinedHandleValue, JS::CloneDataPolicy()); 284 } 285 286 bool StructuredCloneHolderBase::Write( 287 JSContext* aCx, JS::Handle<JS::Value> aValue, 288 JS::Handle<JS::Value> aTransfer, 289 const JS::CloneDataPolicy& aCloneDataPolicy) { 290 MOZ_ASSERT(!mBuffer, "Double Write is not allowed"); 291 MOZ_ASSERT(!mClearCalled, "This method cannot be called after Clear."); 292 293 mBuffer = MakeUnique<JSAutoStructuredCloneBuffer>( 294 mStructuredCloneScope, &StructuredCloneHolder::sCallbacks, this); 295 296 if (!mBuffer->write(aCx, aValue, aTransfer, aCloneDataPolicy, 297 &StructuredCloneHolder::sCallbacks, this)) { 298 mBuffer = nullptr; 299 return false; 300 } 301 302 // Let's update our scope to the final one. The new one could be more 303 // restrictive of the current one. 304 MOZ_ASSERT(mStructuredCloneScope >= mBuffer->scope()); 305 mStructuredCloneScope = mBuffer->scope(); 306 return true; 307 } 308 309 bool StructuredCloneHolderBase::Read(JSContext* aCx, 310 JS::MutableHandle<JS::Value> aValue) { 311 return Read(aCx, aValue, JS::CloneDataPolicy()); 312 } 313 314 bool StructuredCloneHolderBase::Read( 315 JSContext* aCx, JS::MutableHandle<JS::Value> aValue, 316 const JS::CloneDataPolicy& aCloneDataPolicy) { 317 MOZ_ASSERT(mBuffer, "Read() without Write() is not allowed."); 318 MOZ_ASSERT(!mClearCalled, "This method cannot be called after Clear."); 319 320 bool ok = mBuffer->read(aCx, aValue, aCloneDataPolicy, 321 &StructuredCloneHolder::sCallbacks, this); 322 return ok; 323 } 324 325 bool StructuredCloneHolderBase::CustomReadTransferHandler( 326 JSContext* aCx, JSStructuredCloneReader* aReader, 327 const JS::CloneDataPolicy& aCloneDataPolicy, uint32_t aTag, void* aContent, 328 uint64_t aExtraData, JS::MutableHandle<JSObject*> aReturnObject) { 329 MOZ_CRASH("Nothing to read."); 330 return false; 331 } 332 333 bool StructuredCloneHolderBase::CustomWriteTransferHandler( 334 JSContext* aCx, JS::Handle<JSObject*> aObj, uint32_t* aTag, 335 JS::TransferableOwnership* aOwnership, void** aContent, 336 uint64_t* aExtraData) { 337 // No transfers are supported by default. 338 return false; 339 } 340 341 void StructuredCloneHolderBase::CustomFreeTransferHandler( 342 uint32_t aTag, JS::TransferableOwnership aOwnership, void* aContent, 343 uint64_t aExtraData) { 344 MOZ_CRASH("Nothing to free."); 345 } 346 347 bool StructuredCloneHolderBase::CustomCanTransferHandler( 348 JSContext* aCx, JS::Handle<JSObject*> aObj, 349 bool* aSameProcessScopeRequired) { 350 return false; 351 } 352 353 // StructuredCloneHolder class 354 355 StructuredCloneHolder::StructuredCloneHolder( 356 CloningSupport aSupportsCloning, TransferringSupport aSupportsTransferring, 357 StructuredCloneScope aScope) 358 : StructuredCloneHolderBase(aScope), 359 mSupportsCloning(aSupportsCloning == CloningSupported), 360 mSupportsTransferring(aSupportsTransferring == TransferringSupported), 361 mGlobal(nullptr) 362 #ifdef DEBUG 363 , 364 mCreationEventTarget(GetCurrentSerialEventTarget()) 365 #endif 366 { 367 } 368 369 StructuredCloneHolder::~StructuredCloneHolder() { 370 Clear(); 371 MOZ_ASSERT(mTransferredPorts.IsEmpty()); 372 } 373 374 void StructuredCloneHolder::Write(JSContext* aCx, JS::Handle<JS::Value> aValue, 375 ErrorResult& aRv) { 376 Write(aCx, aValue, JS::UndefinedHandleValue, JS::CloneDataPolicy(), aRv); 377 } 378 379 void StructuredCloneHolder::Write(JSContext* aCx, JS::Handle<JS::Value> aValue, 380 JS::Handle<JS::Value> aTransfer, 381 const JS::CloneDataPolicy& aCloneDataPolicy, 382 ErrorResult& aRv) { 383 if (!StructuredCloneHolderBase::Write(aCx, aValue, aTransfer, 384 aCloneDataPolicy)) { 385 aRv.ThrowDataCloneError(mErrorMessage); 386 return; 387 } 388 } 389 390 void StructuredCloneHolder::Read(nsIGlobalObject* aGlobal, JSContext* aCx, 391 JS::MutableHandle<JS::Value> aValue, 392 ErrorResult& aRv) { 393 return Read(aGlobal, aCx, aValue, JS::CloneDataPolicy(), aRv); 394 } 395 396 void StructuredCloneHolder::Read(nsIGlobalObject* aGlobal, JSContext* aCx, 397 JS::MutableHandle<JS::Value> aValue, 398 const JS::CloneDataPolicy& aCloneDataPolicy, 399 ErrorResult& aRv) { 400 MOZ_ASSERT(aGlobal); 401 402 mozilla::AutoRestore<nsIGlobalObject*> guard(mGlobal); 403 auto errorMessageGuard = MakeScopeExit([&] { mErrorMessage.Truncate(); }); 404 mGlobal = aGlobal; 405 406 if (!StructuredCloneHolderBase::Read(aCx, aValue, aCloneDataPolicy)) { 407 mTransferredPorts.Clear(); 408 JS_ClearPendingException(aCx); 409 aRv.ThrowDataCloneError(mErrorMessage); 410 return; 411 } 412 413 // If we are transferring something, we cannot call 'Read()' more than once. 414 if (mSupportsTransferring) { 415 #define STMT(_member) (_member).Clear() 416 CLONED_DATA_MEMBERS 417 #undef STMT 418 419 Clear(); 420 } 421 } 422 423 void StructuredCloneHolder::ReadFromBuffer( 424 nsIGlobalObject* aGlobal, JSContext* aCx, JSStructuredCloneData& aBuffer, 425 JS::MutableHandle<JS::Value> aValue, 426 const JS::CloneDataPolicy& aCloneDataPolicy, ErrorResult& aRv) { 427 ReadFromBuffer(aGlobal, aCx, aBuffer, JS_STRUCTURED_CLONE_VERSION, aValue, 428 aCloneDataPolicy, aRv); 429 } 430 431 void StructuredCloneHolder::ReadFromBuffer( 432 nsIGlobalObject* aGlobal, JSContext* aCx, JSStructuredCloneData& aBuffer, 433 uint32_t aAlgorithmVersion, JS::MutableHandle<JS::Value> aValue, 434 const JS::CloneDataPolicy& aCloneDataPolicy, ErrorResult& aRv) { 435 MOZ_ASSERT(!mBuffer, "ReadFromBuffer() must be called without a Write()."); 436 437 mozilla::AutoRestore<nsIGlobalObject*> guard(mGlobal); 438 auto errorMessageGuard = MakeScopeExit([&] { mErrorMessage.Truncate(); }); 439 mGlobal = aGlobal; 440 441 if (!JS_ReadStructuredClone(aCx, aBuffer, aAlgorithmVersion, CloneScope(), 442 aValue, aCloneDataPolicy, &sCallbacks, this)) { 443 JS_ClearPendingException(aCx); 444 aRv.ThrowDataCloneError(mErrorMessage); 445 return; 446 } 447 } 448 449 static bool CheckExposedGlobals(JSContext* aCx, nsIGlobalObject* aGlobal, 450 uint16_t aExposedGlobals) { 451 JS::Rooted<JSObject*> global(aCx, aGlobal->GetGlobalJSObject()); 452 453 // Sandboxes aren't really DOM globals (though they do set the 454 // JSCLASS_DOM_GLOBAL flag), and so we can't simply do the exposure check. 455 // Some sandboxes do have a DOM global as their prototype, so using the 456 // prototype to check for exposure will at least make it work for those 457 // specific cases. 458 { 459 JSObject* proto = xpc::SandboxPrototypeOrNull(aCx, global); 460 if (proto) { 461 global = proto; 462 } 463 } 464 465 if (!IsGlobalInExposureSet(aCx, global, aExposedGlobals)) { 466 ErrorResult error; 467 error.ThrowDataCloneError("Interface is not exposed."); 468 MOZ_ALWAYS_TRUE(error.MaybeSetPendingException(aCx)); 469 return false; 470 } 471 return true; 472 } 473 474 /* static */ 475 JSObject* StructuredCloneHolder::ReadFullySerializableObjects( 476 JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag, 477 bool aIsForIndexedDB) { 478 AssertTagValues(); 479 480 nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx); 481 if (!global) { 482 return nullptr; 483 } 484 485 Maybe<std::pair<uint16_t, WebIDLDeserializer>> deserializer = 486 LookupDeserializer(StructuredCloneTags(aTag)); 487 if (deserializer.isSome()) { 488 uint16_t exposedGlobals; 489 WebIDLDeserializer deserialize; 490 std::tie(exposedGlobals, deserialize) = deserializer.ref(); 491 492 // https://html.spec.whatwg.org/multipage/structured-data.html#structureddeserialize 493 // 494 // 22. Otherwise: 495 // 496 // 1. Let interfaceName be serialized.[[Type]]. 497 // 2. If the interface identified by interfaceName is not exposed in 498 // targetRealm, then throw a "DataCloneError" DOMException. 499 // 500 // The special-casing for IndexedDB is because it uses a sandbox to 501 // deserialize, which means we don't actually have access to exposure 502 // information. 503 if (!aIsForIndexedDB && !CheckExposedGlobals(aCx, global, exposedGlobals)) { 504 return nullptr; 505 } 506 507 return deserialize(aCx, global, aReader); 508 } 509 510 if (aTag == SCTAG_DOM_NULL_PRINCIPAL || aTag == SCTAG_DOM_SYSTEM_PRINCIPAL || 511 aTag == SCTAG_DOM_CONTENT_PRINCIPAL || 512 aTag == SCTAG_DOM_EXPANDED_PRINCIPAL) { 513 JSPrincipals* prin; 514 if (!nsJSPrincipals::ReadKnownPrincipalType(aCx, aReader, aTag, &prin)) { 515 return nullptr; 516 } 517 518 JS::Rooted<JS::Value> result(aCx); 519 { 520 // nsJSPrincipals::ReadKnownPrincipalType addrefs for us, but because of 521 // the casting between JSPrincipals* and nsIPrincipal* we can't use 522 // getter_AddRefs above and have to already_AddRefed here. 523 nsCOMPtr<nsIPrincipal> principal = 524 already_AddRefed<nsIPrincipal>(nsJSPrincipals::get(prin)); 525 526 nsresult rv = nsContentUtils::WrapNative( 527 aCx, principal, &NS_GET_IID(nsIPrincipal), &result); 528 if (NS_FAILED(rv)) { 529 xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR); 530 return nullptr; 531 } 532 } 533 return result.toObjectOrNull(); 534 } 535 536 // Don't know what this is. Bail. 537 xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR); 538 return nullptr; 539 } 540 541 /* static */ 542 bool StructuredCloneHolder::WriteFullySerializableObjects( 543 JSContext* aCx, JSStructuredCloneWriter* aWriter, 544 JS::Handle<JSObject*> aObj) { 545 AssertTagValues(); 546 547 // Window and Location are not serializable, so it's OK to just do a static 548 // unwrap here. 549 JS::Rooted<JSObject*> obj(aCx, js::CheckedUnwrapStatic(aObj)); 550 if (!obj) { 551 return xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR); 552 } 553 554 const DOMJSClass* domClass = GetDOMClass(obj); 555 if (domClass && domClass->mSerializer) { 556 return domClass->mSerializer(aCx, aWriter, obj); 557 } 558 559 if (NS_IsMainThread() && xpc::IsReflector(obj, aCx)) { 560 // We only care about principals, so ReflectorToISupportsStatic is fine. 561 nsCOMPtr<nsISupports> base = xpc::ReflectorToISupportsStatic(obj); 562 nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(base); 563 if (principal) { 564 auto nsjsprincipals = nsJSPrincipals::get(principal); 565 return nsjsprincipals->write(aCx, aWriter); 566 } 567 } 568 569 // Don't know what this is 570 ErrorResult rv; 571 const char* className = JS::GetClass(obj)->name; 572 rv.ThrowDataCloneError(nsDependentCString(className) + 573 " object could not be cloned."_ns); 574 MOZ_ALWAYS_TRUE(rv.MaybeSetPendingException(aCx)); 575 return false; 576 } 577 578 template <typename char_type> 579 static bool ReadTString(JSStructuredCloneReader* aReader, 580 nsTString<char_type>& aString) { 581 uint32_t length, zero; 582 if (!JS_ReadUint32Pair(aReader, &length, &zero)) { 583 return false; 584 } 585 586 if (NS_WARN_IF(!aString.SetLength(length, fallible))) { 587 return false; 588 } 589 size_t charSize = sizeof(char_type); 590 return JS_ReadBytes(aReader, (void*)aString.BeginWriting(), 591 length * charSize); 592 } 593 594 template <typename char_type> 595 static bool WriteTString(JSStructuredCloneWriter* aWriter, 596 const nsTSubstring<char_type>& aString) { 597 size_t charSize = sizeof(char_type); 598 return JS_WriteUint32Pair(aWriter, aString.Length(), 0) && 599 JS_WriteBytes(aWriter, aString.BeginReading(), 600 aString.Length() * charSize); 601 } 602 603 /* static */ 604 bool StructuredCloneHolder::ReadString(JSStructuredCloneReader* aReader, 605 nsString& aString) { 606 return ReadTString(aReader, aString); 607 } 608 609 /* static */ 610 bool StructuredCloneHolder::WriteString(JSStructuredCloneWriter* aWriter, 611 const nsAString& aString) { 612 return WriteTString(aWriter, aString); 613 } 614 615 /* static */ 616 bool StructuredCloneHolder::ReadCString(JSStructuredCloneReader* aReader, 617 nsCString& aString) { 618 return ReadTString(aReader, aString); 619 } 620 621 /* static */ 622 bool StructuredCloneHolder::WriteCString(JSStructuredCloneWriter* aWriter, 623 const nsACString& aString) { 624 return WriteTString(aWriter, aString); 625 } 626 627 namespace { 628 629 JSObject* ReadBlob(JSContext* aCx, uint32_t aIndex, 630 StructuredCloneHolder* aHolder) { 631 MOZ_ASSERT(aHolder); 632 #ifdef FUZZING 633 if (aIndex >= aHolder->BlobImpls().Length()) { 634 return nullptr; 635 } 636 #endif 637 MOZ_ASSERT(aIndex < aHolder->BlobImpls().Length()); 638 JS::Rooted<JS::Value> val(aCx); 639 { 640 // RefPtr<File> and RefPtr<BlobImpl> need to go out of scope before 641 // toObject() is called because the static analysis thinks releasing XPCOM 642 // objects can GC (because in some cases it can!), and a return statement 643 // with a JSObject* type means that JSObject* is on the stack as a raw 644 // pointer while destructors are running. 645 RefPtr<BlobImpl> blobImpl = aHolder->BlobImpls()[aIndex]; 646 647 RefPtr<Blob> blob = Blob::Create(aHolder->GlobalDuringRead(), blobImpl); 648 if (NS_WARN_IF(!blob)) { 649 return nullptr; 650 } 651 652 if (!ToJSValue(aCx, blob, &val)) { 653 return nullptr; 654 } 655 } 656 657 return &val.toObject(); 658 } 659 660 bool WriteBlob(JSStructuredCloneWriter* aWriter, Blob* aBlob, 661 StructuredCloneHolder* aHolder) { 662 MOZ_ASSERT(aWriter); 663 MOZ_ASSERT(aBlob); 664 MOZ_ASSERT(aHolder); 665 666 RefPtr<BlobImpl> blobImpl = aBlob->Impl(); 667 668 // We store the position of the blobImpl in the array as index. 669 if (JS_WriteUint32Pair(aWriter, SCTAG_DOM_BLOB, 670 aHolder->BlobImpls().Length())) { 671 aHolder->BlobImpls().AppendElement(blobImpl); 672 return true; 673 } 674 675 return false; 676 } 677 678 // A directory is serialized as: 679 // - pair of ints: SCTAG_DOM_DIRECTORY, path length 680 // - path as string 681 bool WriteDirectory(JSStructuredCloneWriter* aWriter, Directory* aDirectory) { 682 MOZ_ASSERT(aWriter); 683 MOZ_ASSERT(aDirectory); 684 685 nsAutoString path; 686 aDirectory->GetFullRealPath(path); 687 688 size_t charSize = sizeof(nsString::char_type); 689 return JS_WriteUint32Pair(aWriter, SCTAG_DOM_DIRECTORY, path.Length()) && 690 JS_WriteBytes(aWriter, path.get(), path.Length() * charSize); 691 } 692 693 already_AddRefed<Directory> ReadDirectoryInternal( 694 JSStructuredCloneReader* aReader, uint32_t aPathLength, 695 StructuredCloneHolder* aHolder) { 696 MOZ_ASSERT(aReader); 697 MOZ_ASSERT(aHolder); 698 699 nsAutoString path; 700 if (NS_WARN_IF(!path.SetLength(aPathLength, fallible))) { 701 return nullptr; 702 } 703 size_t charSize = sizeof(nsString::char_type); 704 if (!JS_ReadBytes(aReader, (void*)path.BeginWriting(), 705 aPathLength * charSize)) { 706 return nullptr; 707 } 708 709 nsCOMPtr<nsIFile> file; 710 nsresult rv = NS_NewLocalFile(path, getter_AddRefs(file)); 711 if (NS_WARN_IF(NS_FAILED(rv))) { 712 return nullptr; 713 } 714 715 RefPtr<Directory> directory = 716 Directory::Create(aHolder->GlobalDuringRead(), file); 717 return directory.forget(); 718 } 719 720 JSObject* ReadDirectory(JSContext* aCx, JSStructuredCloneReader* aReader, 721 uint32_t aPathLength, StructuredCloneHolder* aHolder) { 722 MOZ_ASSERT(aCx); 723 MOZ_ASSERT(aReader); 724 MOZ_ASSERT(aHolder); 725 726 // RefPtr<Directory> needs to go out of scope before toObject() is 727 // called because the static analysis thinks dereferencing XPCOM objects 728 // can GC (because in some cases it can!), and a return statement with a 729 // JSObject* type means that JSObject* is on the stack as a raw pointer 730 // while destructors are running. 731 JS::Rooted<JS::Value> val(aCx); 732 { 733 RefPtr<Directory> directory = 734 ReadDirectoryInternal(aReader, aPathLength, aHolder); 735 if (!directory) { 736 return nullptr; 737 } 738 739 if (!ToJSValue(aCx, directory, &val)) { 740 return nullptr; 741 } 742 } 743 744 return &val.toObject(); 745 } 746 747 // Read the WriteFileList for the format. 748 JSObject* ReadFileList(JSContext* aCx, JSStructuredCloneReader* aReader, 749 uint32_t aCount, StructuredCloneHolder* aHolder) { 750 MOZ_ASSERT(aCx); 751 MOZ_ASSERT(aReader); 752 753 JS::Rooted<JS::Value> val(aCx); 754 { 755 RefPtr<FileList> fileList = new FileList(aHolder->GlobalDuringRead()); 756 757 uint32_t zero, index; 758 // |index| is the index of the first blobImpl. 759 if (!JS_ReadUint32Pair(aReader, &zero, &index) || zero != 0) { 760 return nullptr; 761 } 762 763 // |aCount| is the number of BlobImpls to use from the |index|. 764 for (uint32_t i = 0; i < aCount; ++i) { 765 uint32_t pos = index + i; 766 #ifdef FUZZING 767 if (pos >= aHolder->BlobImpls().Length()) { 768 return nullptr; 769 } 770 #endif 771 MOZ_ASSERT(pos < aHolder->BlobImpls().Length()); 772 773 RefPtr<BlobImpl> blobImpl = aHolder->BlobImpls()[pos]; 774 MOZ_ASSERT(blobImpl->IsFile()); 775 776 RefPtr<File> file = File::Create(aHolder->GlobalDuringRead(), blobImpl); 777 if (NS_WARN_IF(!file)) { 778 return nullptr; 779 } 780 781 if (!fileList->Append(file)) { 782 return nullptr; 783 } 784 } 785 786 if (!ToJSValue(aCx, fileList, &val)) { 787 return nullptr; 788 } 789 } 790 791 return &val.toObject(); 792 } 793 794 // The format of the FileList serialization is: 795 // - pair of ints: SCTAG_DOM_FILELIST, Length of the FileList 796 // - pair of ints: 0, The offset of the BlobImpl array 797 bool WriteFileList(JSStructuredCloneWriter* aWriter, FileList* aFileList, 798 StructuredCloneHolder* aHolder) { 799 MOZ_ASSERT(aWriter); 800 MOZ_ASSERT(aFileList); 801 MOZ_ASSERT(aHolder); 802 803 // A FileList is serialized writing the X number of elements and the offset 804 // from mBlobImplArray. The Read will take X elements from mBlobImplArray 805 // starting from the offset. 806 if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_FILELIST, aFileList->Length()) || 807 !JS_WriteUint32Pair(aWriter, 0, aHolder->BlobImpls().Length())) { 808 return false; 809 } 810 811 nsTArray<RefPtr<BlobImpl>> blobImpls; 812 813 for (uint32_t i = 0; i < aFileList->Length(); ++i) { 814 RefPtr<BlobImpl> blobImpl = aFileList->Item(i)->Impl(); 815 blobImpls.AppendElement(blobImpl); 816 } 817 818 aHolder->BlobImpls().AppendElements(blobImpls); 819 return true; 820 } 821 822 // Read the WriteFormData for the format. 823 JSObject* ReadFormData(JSContext* aCx, JSStructuredCloneReader* aReader, 824 uint32_t aCount, StructuredCloneHolder* aHolder) { 825 MOZ_ASSERT(aCx); 826 MOZ_ASSERT(aReader); 827 MOZ_ASSERT(aHolder); 828 829 // See the serialization of the FormData for the format. 830 JS::Rooted<JS::Value> val(aCx); 831 { 832 RefPtr<FormData> formData = new FormData(aHolder->GlobalDuringRead()); 833 834 Optional<nsAString> thirdArg; 835 for (uint32_t i = 0; i < aCount; ++i) { 836 nsAutoString name; 837 if (!StructuredCloneHolder::ReadString(aReader, name)) { 838 return nullptr; 839 } 840 841 uint32_t tag, indexOrLengthOfString; 842 if (!JS_ReadUint32Pair(aReader, &tag, &indexOrLengthOfString)) { 843 return nullptr; 844 } 845 846 if (tag == SCTAG_DOM_BLOB) { 847 #ifdef FUZZING 848 if (indexOrLengthOfString >= aHolder->BlobImpls().Length()) { 849 return nullptr; 850 } 851 #endif 852 MOZ_ASSERT(indexOrLengthOfString < aHolder->BlobImpls().Length()); 853 854 RefPtr<BlobImpl> blobImpl = aHolder->BlobImpls()[indexOrLengthOfString]; 855 856 RefPtr<Blob> blob = Blob::Create(aHolder->GlobalDuringRead(), blobImpl); 857 if (NS_WARN_IF(!blob)) { 858 return nullptr; 859 } 860 861 ErrorResult rv; 862 formData->Append(name, *blob, thirdArg, rv); 863 if (NS_WARN_IF(rv.Failed())) { 864 rv.SuppressException(); 865 return nullptr; 866 } 867 868 } else if (tag == SCTAG_DOM_DIRECTORY) { 869 RefPtr<Directory> directory = 870 ReadDirectoryInternal(aReader, indexOrLengthOfString, aHolder); 871 if (!directory) { 872 return nullptr; 873 } 874 875 formData->Append(name, directory); 876 877 } else { 878 if (NS_WARN_IF(tag != 0)) { 879 return nullptr; 880 } 881 882 nsAutoString value; 883 if (NS_WARN_IF(!value.SetLength(indexOrLengthOfString, fallible))) { 884 return nullptr; 885 } 886 size_t charSize = sizeof(nsString::char_type); 887 if (!JS_ReadBytes(aReader, (void*)value.BeginWriting(), 888 indexOrLengthOfString * charSize)) { 889 return nullptr; 890 } 891 892 ErrorResult rv; 893 formData->Append(name, value, rv); 894 if (NS_WARN_IF(rv.Failed())) { 895 rv.SuppressException(); 896 return nullptr; 897 } 898 } 899 } 900 901 if (!ToJSValue(aCx, formData, &val)) { 902 return nullptr; 903 } 904 } 905 906 return &val.toObject(); 907 } 908 909 // The format of the FormData serialization is: 910 // - pair of ints: SCTAG_DOM_FORMDATA, Length of the FormData elements 911 // - for each Element element: 912 // - name string 913 // - if it's a blob: 914 // - pair of ints: SCTAG_DOM_BLOB, index of the BlobImpl in the array 915 // mBlobImplArray. 916 // - if it's a directory (See WriteDirectory): 917 // - pair of ints: SCTAG_DOM_DIRECTORY, path length 918 // - path as string 919 // - else: 920 // - pair of ints: 0, string length 921 // - value string 922 bool WriteFormData(JSStructuredCloneWriter* aWriter, FormData* aFormData, 923 StructuredCloneHolder* aHolder) { 924 MOZ_ASSERT(aWriter); 925 MOZ_ASSERT(aFormData); 926 MOZ_ASSERT(aHolder); 927 928 if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_FORMDATA, aFormData->Length())) { 929 return false; 930 } 931 932 auto write = [aWriter, aHolder]( 933 const nsString& aName, 934 const OwningBlobOrDirectoryOrUSVString& aValue) { 935 if (!StructuredCloneHolder::WriteString(aWriter, aName)) { 936 return false; 937 } 938 939 if (aValue.IsBlob()) { 940 if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_BLOB, 941 aHolder->BlobImpls().Length())) { 942 return false; 943 } 944 945 RefPtr<BlobImpl> blobImpl = aValue.GetAsBlob()->Impl(); 946 947 aHolder->BlobImpls().AppendElement(blobImpl); 948 return true; 949 } 950 951 if (aValue.IsDirectory()) { 952 Directory* directory = aValue.GetAsDirectory(); 953 return WriteDirectory(aWriter, directory); 954 } 955 956 const size_t charSize = sizeof(nsString::char_type); 957 return JS_WriteUint32Pair(aWriter, 0, aValue.GetAsUSVString().Length()) && 958 JS_WriteBytes(aWriter, aValue.GetAsUSVString().get(), 959 aValue.GetAsUSVString().Length() * charSize); 960 }; 961 return aFormData->ForEach(write); 962 } 963 964 JSObject* ReadWasmModule(JSContext* aCx, uint32_t aIndex, 965 StructuredCloneHolder* aHolder) { 966 MOZ_ASSERT(aHolder); 967 MOZ_ASSERT(aHolder->CloneScope() == 968 StructuredCloneHolder::StructuredCloneScope::SameProcess); 969 #ifdef FUZZING 970 if (aIndex >= aHolder->WasmModules().Length()) { 971 return nullptr; 972 } 973 #endif 974 MOZ_ASSERT(aIndex < aHolder->WasmModules().Length()); 975 976 return aHolder->WasmModules()[aIndex]->createObject(aCx); 977 } 978 979 bool WriteWasmModule(JSStructuredCloneWriter* aWriter, 980 JS::WasmModule* aWasmModule, 981 StructuredCloneHolder* aHolder) { 982 MOZ_ASSERT(aWriter); 983 MOZ_ASSERT(aWasmModule); 984 MOZ_ASSERT(aHolder); 985 MOZ_ASSERT(aHolder->CloneScope() == 986 StructuredCloneHolder::StructuredCloneScope::SameProcess); 987 988 // We store the position of the wasmModule in the array as index. 989 if (JS_WriteUint32Pair(aWriter, SCTAG_DOM_WASM_MODULE, 990 aHolder->WasmModules().Length())) { 991 aHolder->WasmModules().AppendElement(aWasmModule); 992 return true; 993 } 994 995 return false; 996 } 997 998 JSObject* ReadInputStream(JSContext* aCx, uint32_t aIndex, 999 StructuredCloneHolder* aHolder) { 1000 MOZ_ASSERT(aHolder); 1001 #ifdef FUZZING 1002 if (aIndex >= aHolder->InputStreams().Length()) { 1003 return nullptr; 1004 } 1005 #endif 1006 MOZ_ASSERT(aIndex < aHolder->InputStreams().Length()); 1007 JS::Rooted<JS::Value> result(aCx); 1008 { 1009 nsCOMPtr<nsIInputStream> inputStream = aHolder->InputStreams()[aIndex]; 1010 1011 nsresult rv = nsContentUtils::WrapNative( 1012 aCx, inputStream, &NS_GET_IID(nsIInputStream), &result); 1013 if (NS_FAILED(rv)) { 1014 return nullptr; 1015 } 1016 } 1017 1018 return &result.toObject(); 1019 } 1020 1021 bool WriteInputStream(JSStructuredCloneWriter* aWriter, 1022 nsIInputStream* aInputStream, 1023 StructuredCloneHolder* aHolder) { 1024 MOZ_ASSERT(aWriter); 1025 MOZ_ASSERT(aInputStream); 1026 MOZ_ASSERT(aHolder); 1027 1028 // We store the position of the inputStream in the array as index. 1029 if (JS_WriteUint32Pair(aWriter, SCTAG_DOM_INPUTSTREAM, 1030 aHolder->InputStreams().Length())) { 1031 aHolder->InputStreams().AppendElement(aInputStream); 1032 return true; 1033 } 1034 1035 return false; 1036 } 1037 1038 } // anonymous namespace 1039 1040 static const uint16_t sWindowOrWorker = 1041 GlobalNames::DedicatedWorkerGlobalScope | 1042 GlobalNames::ServiceWorkerGlobalScope | 1043 GlobalNames::SharedWorkerGlobalScope | GlobalNames::Window | 1044 GlobalNames::WorkerDebuggerGlobalScope; 1045 1046 JSObject* StructuredCloneHolder::CustomReadHandler( 1047 JSContext* aCx, JSStructuredCloneReader* aReader, 1048 const JS::CloneDataPolicy& aCloneDataPolicy, uint32_t aTag, 1049 uint32_t aIndex) { 1050 MOZ_ASSERT(mSupportsCloning); 1051 1052 if (aTag == SCTAG_DOM_BLOB) { 1053 if (!CheckExposedGlobals(aCx, mGlobal, sWindowOrWorker)) { 1054 return nullptr; 1055 } 1056 return ReadBlob(aCx, aIndex, this); 1057 } 1058 1059 if (aTag == SCTAG_DOM_DIRECTORY) { 1060 if (!CheckExposedGlobals(aCx, mGlobal, sWindowOrWorker)) { 1061 return nullptr; 1062 } 1063 return ReadDirectory(aCx, aReader, aIndex, this); 1064 } 1065 1066 if (aTag == SCTAG_DOM_FILELIST) { 1067 if (!CheckExposedGlobals(aCx, mGlobal, sWindowOrWorker)) { 1068 return nullptr; 1069 } 1070 return ReadFileList(aCx, aReader, aIndex, this); 1071 } 1072 1073 if (aTag == SCTAG_DOM_FORMDATA) { 1074 if (!CheckExposedGlobals(aCx, mGlobal, sWindowOrWorker)) { 1075 return nullptr; 1076 } 1077 return ReadFormData(aCx, aReader, aIndex, this); 1078 } 1079 1080 if (aTag == SCTAG_DOM_IMAGEBITMAP && 1081 CloneScope() == StructuredCloneScope::SameProcess) { 1082 if (!CheckExposedGlobals(aCx, mGlobal, sWindowOrWorker)) { 1083 return nullptr; 1084 } 1085 // Get the current global object. 1086 // This can be null. 1087 JS::Rooted<JSObject*> result(aCx); 1088 { 1089 // aIndex is the index of the cloned image. 1090 result = ImageBitmap::ReadStructuredClone(aCx, aReader, mGlobal, 1091 GetSurfaces(), aIndex); 1092 } 1093 return result; 1094 } 1095 1096 if (aTag == SCTAG_DOM_STRUCTURED_CLONE_HOLDER) { 1097 return StructuredCloneBlob::ReadStructuredClone(aCx, aReader, this); 1098 } 1099 1100 if (aTag == SCTAG_DOM_WASM_MODULE && 1101 CloneScope() == StructuredCloneScope::SameProcess && 1102 aCloneDataPolicy.areIntraClusterClonableSharedObjectsAllowed()) { 1103 return ReadWasmModule(aCx, aIndex, this); 1104 } 1105 1106 if (aTag == SCTAG_DOM_INPUTSTREAM) { 1107 return ReadInputStream(aCx, aIndex, this); 1108 } 1109 1110 if (aTag == SCTAG_DOM_BROWSING_CONTEXT) { 1111 if (!CheckExposedGlobals(aCx, mGlobal, GlobalNames::Window)) { 1112 return nullptr; 1113 } 1114 return BrowsingContext::ReadStructuredClone(aCx, aReader, this); 1115 } 1116 1117 if (aTag == SCTAG_DOM_CLONED_ERROR_OBJECT) { 1118 if (!CheckExposedGlobals(aCx, mGlobal, sWindowOrWorker)) { 1119 return nullptr; 1120 } 1121 return ClonedErrorHolder::ReadStructuredClone(aCx, aReader, this); 1122 } 1123 1124 if (VideoFrame::PrefEnabled(aCx) && aTag == SCTAG_DOM_VIDEOFRAME && 1125 CloneScope() == StructuredCloneScope::SameProcess && 1126 aCloneDataPolicy.areIntraClusterClonableSharedObjectsAllowed()) { 1127 JS::Rooted<JSObject*> global(aCx, mGlobal->GetGlobalJSObject()); 1128 if (VideoFrame_Binding::ConstructorEnabled(aCx, global)) { 1129 return VideoFrame::ReadStructuredClone(aCx, mGlobal, aReader, 1130 VideoFrames()[aIndex]); 1131 } 1132 } 1133 1134 if (StaticPrefs::dom_media_webcodecs_enabled() && 1135 aTag == SCTAG_DOM_ENCODEDVIDEOCHUNK && 1136 CloneScope() == StructuredCloneScope::SameProcess && 1137 aCloneDataPolicy.areIntraClusterClonableSharedObjectsAllowed()) { 1138 JS::Rooted<JSObject*> global(aCx, mGlobal->GetGlobalJSObject()); 1139 if (EncodedVideoChunk_Binding::ConstructorEnabled(aCx, global)) { 1140 return EncodedVideoChunk::ReadStructuredClone( 1141 aCx, mGlobal, aReader, EncodedVideoChunks()[aIndex]); 1142 } 1143 } 1144 1145 if (StaticPrefs::dom_media_webcodecs_enabled() && 1146 aTag == SCTAG_DOM_AUDIODATA && 1147 CloneScope() == StructuredCloneScope::SameProcess && 1148 aCloneDataPolicy.areIntraClusterClonableSharedObjectsAllowed()) { 1149 JS::Rooted<JSObject*> global(aCx, mGlobal->GetGlobalJSObject()); 1150 if (AudioData_Binding::ConstructorEnabled(aCx, global)) { 1151 return AudioData::ReadStructuredClone(aCx, mGlobal, aReader, 1152 AudioData()[aIndex]); 1153 } 1154 } 1155 1156 if (StaticPrefs::dom_media_webcodecs_enabled() && 1157 aTag == SCTAG_DOM_ENCODEDAUDIOCHUNK && 1158 CloneScope() == StructuredCloneScope::SameProcess && 1159 aCloneDataPolicy.areIntraClusterClonableSharedObjectsAllowed()) { 1160 JS::Rooted<JSObject*> global(aCx, mGlobal->GetGlobalJSObject()); 1161 if (EncodedAudioChunk_Binding::ConstructorEnabled(aCx, global)) { 1162 return EncodedAudioChunk::ReadStructuredClone( 1163 aCx, mGlobal, aReader, EncodedAudioChunks()[aIndex]); 1164 } 1165 } 1166 1167 #ifdef MOZ_WEBRTC 1168 if (StaticPrefs::media_peerconnection_enabled() && 1169 aTag == SCTAG_DOM_RTCENCODEDVIDEOFRAME && 1170 CloneScope() == StructuredCloneScope::SameProcess && 1171 aCloneDataPolicy.areIntraClusterClonableSharedObjectsAllowed()) { 1172 JS::Rooted<JSObject*> global(aCx, mGlobal->GetGlobalJSObject()); 1173 if (RTCEncodedVideoFrame_Binding::ConstructorEnabled(aCx, global)) { 1174 return RTCEncodedVideoFrame::ReadStructuredClone( 1175 aCx, mGlobal, aReader, RtcEncodedVideoFrames()[aIndex]); 1176 } 1177 } 1178 1179 if (StaticPrefs::media_peerconnection_enabled() && 1180 aTag == SCTAG_DOM_RTCENCODEDAUDIOFRAME && 1181 CloneScope() == StructuredCloneScope::SameProcess && 1182 aCloneDataPolicy.areIntraClusterClonableSharedObjectsAllowed()) { 1183 JS::Rooted<JSObject*> global(aCx, mGlobal->GetGlobalJSObject()); 1184 if (RTCEncodedAudioFrame_Binding::ConstructorEnabled(aCx, global)) { 1185 return RTCEncodedAudioFrame::ReadStructuredClone( 1186 aCx, mGlobal, aReader, RtcEncodedAudioFrames()[aIndex]); 1187 } 1188 } 1189 #endif 1190 1191 return ReadFullySerializableObjects(aCx, aReader, aTag, false); 1192 } 1193 1194 bool StructuredCloneHolder::CustomWriteHandler( 1195 JSContext* aCx, JSStructuredCloneWriter* aWriter, 1196 JS::Handle<JSObject*> aObj, bool* aSameProcessScopeRequired) { 1197 if (!mSupportsCloning) { 1198 return false; 1199 } 1200 1201 JS::Rooted<JSObject*> obj(aCx, aObj); 1202 1203 // See if this is a File/Blob object. 1204 { 1205 Blob* blob = nullptr; 1206 if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, &obj, blob))) { 1207 return WriteBlob(aWriter, blob, this); 1208 } 1209 } 1210 1211 // See if this is a Directory object. 1212 { 1213 Directory* directory = nullptr; 1214 if (NS_SUCCEEDED(UNWRAP_OBJECT(Directory, &obj, directory))) { 1215 return WriteDirectory(aWriter, directory); 1216 } 1217 } 1218 1219 // See if this is a FileList object. 1220 { 1221 FileList* fileList = nullptr; 1222 if (NS_SUCCEEDED(UNWRAP_OBJECT(FileList, &obj, fileList))) { 1223 return WriteFileList(aWriter, fileList, this); 1224 } 1225 } 1226 1227 // See if this is a FormData object. 1228 { 1229 FormData* formData = nullptr; 1230 if (NS_SUCCEEDED(UNWRAP_OBJECT(FormData, &obj, formData))) { 1231 return WriteFormData(aWriter, formData, this); 1232 } 1233 } 1234 1235 // See if this is an ImageBitmap object. 1236 { 1237 ImageBitmap* imageBitmap = nullptr; 1238 if (NS_SUCCEEDED(UNWRAP_OBJECT(ImageBitmap, &obj, imageBitmap))) { 1239 SameProcessScopeRequired(aSameProcessScopeRequired); 1240 1241 if (CloneScope() == StructuredCloneScope::SameProcess) { 1242 ErrorResult rv; 1243 ImageBitmap::WriteStructuredClone(aWriter, GetSurfaces(), imageBitmap, 1244 rv); 1245 return !rv.MaybeSetPendingException(aCx); 1246 } 1247 return false; 1248 } 1249 } 1250 1251 // See if this is a StructuredCloneBlob object. 1252 { 1253 StructuredCloneBlob* holder = nullptr; 1254 if (NS_SUCCEEDED(UNWRAP_OBJECT(StructuredCloneHolder, &obj, holder))) { 1255 return holder->WriteStructuredClone(aCx, aWriter, this); 1256 } 1257 } 1258 1259 // See if this is a BrowsingContext object. 1260 { 1261 BrowsingContext* holder = nullptr; 1262 if (NS_SUCCEEDED(UNWRAP_OBJECT(BrowsingContext, &obj, holder))) { 1263 return holder->WriteStructuredClone(aCx, aWriter, this); 1264 } 1265 } 1266 1267 // See if this is a ClonedErrorHolder object. 1268 { 1269 ClonedErrorHolder* holder = nullptr; 1270 if (NS_SUCCEEDED(UNWRAP_OBJECT(ClonedErrorHolder, &obj, holder))) { 1271 return holder->WriteStructuredClone(aCx, aWriter, this); 1272 } 1273 } 1274 1275 // See if this is a WasmModule. 1276 if (JS::IsWasmModuleObject(obj)) { 1277 SameProcessScopeRequired(aSameProcessScopeRequired); 1278 if (CloneScope() == StructuredCloneScope::SameProcess) { 1279 RefPtr<JS::WasmModule> module = JS::GetWasmModule(obj); 1280 MOZ_ASSERT(module); 1281 1282 return WriteWasmModule(aWriter, module, this); 1283 } 1284 return false; 1285 } 1286 1287 // See if this is a VideoFrame object. 1288 if (VideoFrame::PrefEnabled(aCx)) { 1289 VideoFrame* videoFrame = nullptr; 1290 if (NS_SUCCEEDED(UNWRAP_OBJECT(VideoFrame, &obj, videoFrame))) { 1291 SameProcessScopeRequired(aSameProcessScopeRequired); 1292 return CloneScope() == StructuredCloneScope::SameProcess 1293 ? videoFrame->WriteStructuredClone(aWriter, this) 1294 : false; 1295 } 1296 } 1297 1298 // See if this is a EncodedVideoChunk object. 1299 if (StaticPrefs::dom_media_webcodecs_enabled()) { 1300 EncodedVideoChunk* encodedVideoChunk = nullptr; 1301 if (NS_SUCCEEDED( 1302 UNWRAP_OBJECT(EncodedVideoChunk, &obj, encodedVideoChunk))) { 1303 SameProcessScopeRequired(aSameProcessScopeRequired); 1304 return CloneScope() == StructuredCloneScope::SameProcess 1305 ? encodedVideoChunk->WriteStructuredClone(aWriter, this) 1306 : false; 1307 } 1308 } 1309 1310 // See if this is an AudioData object. 1311 if (StaticPrefs::dom_media_webcodecs_enabled()) { 1312 mozilla::dom::AudioData* audioData = nullptr; 1313 if (NS_SUCCEEDED(UNWRAP_OBJECT(AudioData, &obj, audioData))) { 1314 SameProcessScopeRequired(aSameProcessScopeRequired); 1315 return CloneScope() == StructuredCloneScope::SameProcess 1316 ? audioData->WriteStructuredClone(aWriter, this) 1317 : false; 1318 } 1319 } 1320 1321 // See if this is a EncodedAudioChunk object. 1322 if (StaticPrefs::dom_media_webcodecs_enabled()) { 1323 EncodedAudioChunk* encodedAudioChunk = nullptr; 1324 if (NS_SUCCEEDED( 1325 UNWRAP_OBJECT(EncodedAudioChunk, &obj, encodedAudioChunk))) { 1326 SameProcessScopeRequired(aSameProcessScopeRequired); 1327 return CloneScope() == StructuredCloneScope::SameProcess 1328 ? encodedAudioChunk->WriteStructuredClone(aWriter, this) 1329 : false; 1330 } 1331 } 1332 1333 #ifdef MOZ_WEBRTC 1334 // See if this is an RTCEncodedVideoFrame object. 1335 if (StaticPrefs::media_peerconnection_enabled()) { 1336 RTCEncodedVideoFrame* rtcFrame = nullptr; 1337 if (NS_SUCCEEDED(UNWRAP_OBJECT(RTCEncodedVideoFrame, &obj, rtcFrame))) { 1338 SameProcessScopeRequired(aSameProcessScopeRequired); 1339 return CloneScope() == StructuredCloneScope::SameProcess 1340 ? rtcFrame->WriteStructuredClone(aWriter, this) 1341 : false; 1342 } 1343 } 1344 1345 // See if this is an RTCEncodedAudioFrame object. 1346 if (StaticPrefs::media_peerconnection_enabled()) { 1347 RTCEncodedAudioFrame* rtcFrame = nullptr; 1348 if (NS_SUCCEEDED(UNWRAP_OBJECT(RTCEncodedAudioFrame, &obj, rtcFrame))) { 1349 SameProcessScopeRequired(aSameProcessScopeRequired); 1350 return CloneScope() == StructuredCloneScope::SameProcess 1351 ? rtcFrame->WriteStructuredClone(aWriter, this) 1352 : false; 1353 } 1354 } 1355 #endif 1356 { 1357 // We only care about streams, so ReflectorToISupportsStatic is fine. 1358 nsCOMPtr<nsISupports> base = xpc::ReflectorToISupportsStatic(aObj); 1359 nsCOMPtr<nsIInputStream> inputStream = do_QueryInterface(base); 1360 if (inputStream) { 1361 return WriteInputStream(aWriter, inputStream, this); 1362 } 1363 } 1364 1365 return WriteFullySerializableObjects(aCx, aWriter, aObj); 1366 } 1367 1368 already_AddRefed<MessagePort> StructuredCloneHolder::ReceiveMessagePort( 1369 uint64_t aIndex) { 1370 if (NS_WARN_IF(aIndex >= mPortIdentifiers.Length())) { 1371 return nullptr; 1372 } 1373 UniqueMessagePortId portId(mPortIdentifiers[aIndex]); 1374 1375 ErrorResult rv; 1376 RefPtr<MessagePort> port = MessagePort::Create(mGlobal, portId, rv); 1377 if (NS_WARN_IF(rv.Failed())) { 1378 rv.SuppressException(); 1379 return nullptr; 1380 } 1381 1382 return port.forget(); 1383 } 1384 1385 // TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230) 1386 MOZ_CAN_RUN_SCRIPT_BOUNDARY bool 1387 StructuredCloneHolder::CustomReadTransferHandler( 1388 JSContext* aCx, JSStructuredCloneReader* aReader, 1389 const JS::CloneDataPolicy& aCloneDataPolicy, uint32_t aTag, void* aContent, 1390 uint64_t aExtraData, JS::MutableHandle<JSObject*> aReturnObject) { 1391 MOZ_ASSERT(mSupportsTransferring); 1392 1393 if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) { 1394 if (!CheckExposedGlobals( 1395 aCx, mGlobal, 1396 sWindowOrWorker | GlobalNames::AudioWorkletGlobalScope)) { 1397 return false; 1398 } 1399 #ifdef FUZZING 1400 if (aExtraData >= mPortIdentifiers.Length()) { 1401 return false; 1402 } 1403 #endif 1404 RefPtr<MessagePort> port = ReceiveMessagePort(aExtraData); 1405 if (!port) { 1406 return false; 1407 } 1408 mTransferredPorts.AppendElement(port); 1409 1410 JS::Rooted<JS::Value> value(aCx); 1411 if (!GetOrCreateDOMReflector(aCx, port, &value)) { 1412 JS_ClearPendingException(aCx); 1413 return false; 1414 } 1415 1416 aReturnObject.set(&value.toObject()); 1417 return true; 1418 } 1419 1420 if (aTag == SCTAG_DOM_CANVAS && 1421 CloneScope() == StructuredCloneScope::SameProcess) { 1422 if (!CheckExposedGlobals(aCx, mGlobal, sWindowOrWorker)) { 1423 return false; 1424 } 1425 MOZ_ASSERT(aContent); 1426 OffscreenCanvasCloneData* data = 1427 static_cast<OffscreenCanvasCloneData*>(aContent); 1428 RefPtr<OffscreenCanvas> canvas = 1429 OffscreenCanvas::CreateFromCloneData(mGlobal, data); 1430 1431 JS::Rooted<JS::Value> value(aCx); 1432 if (!GetOrCreateDOMReflector(aCx, canvas, &value)) { 1433 JS_ClearPendingException(aCx); 1434 return false; 1435 } 1436 1437 delete data; 1438 aReturnObject.set(&value.toObject()); 1439 return true; 1440 } 1441 1442 if (aTag == SCTAG_DOM_IMAGEBITMAP && 1443 CloneScope() == StructuredCloneScope::SameProcess) { 1444 if (!CheckExposedGlobals(aCx, mGlobal, sWindowOrWorker)) { 1445 return false; 1446 } 1447 MOZ_ASSERT(aContent); 1448 ImageBitmapCloneData* data = static_cast<ImageBitmapCloneData*>(aContent); 1449 RefPtr<ImageBitmap> bitmap = 1450 ImageBitmap::CreateFromCloneData(mGlobal, data); 1451 1452 JS::Rooted<JS::Value> value(aCx); 1453 if (!GetOrCreateDOMReflector(aCx, bitmap, &value)) { 1454 JS_ClearPendingException(aCx); 1455 return false; 1456 } 1457 1458 delete data; 1459 aReturnObject.set(&value.toObject()); 1460 return true; 1461 } 1462 1463 if (aTag == SCTAG_DOM_READABLESTREAM) { 1464 #ifdef FUZZING 1465 if (aExtraData >= mPortIdentifiers.Length()) { 1466 return false; 1467 } 1468 #endif 1469 RefPtr<MessagePort> port = ReceiveMessagePort(aExtraData); 1470 if (!port) { 1471 return false; 1472 } 1473 nsCOMPtr<nsIGlobalObject> global = mGlobal; 1474 return ReadableStream::ReceiveTransfer(aCx, global, *port, aReturnObject); 1475 } 1476 1477 if (aTag == SCTAG_DOM_WRITABLESTREAM) { 1478 #ifdef FUZZING 1479 if (aExtraData >= mPortIdentifiers.Length()) { 1480 return false; 1481 } 1482 #endif 1483 RefPtr<MessagePort> port = ReceiveMessagePort(aExtraData); 1484 if (!port) { 1485 return false; 1486 } 1487 nsCOMPtr<nsIGlobalObject> global = mGlobal; 1488 return WritableStream::ReceiveTransfer(aCx, global, *port, aReturnObject); 1489 } 1490 1491 if (aTag == SCTAG_DOM_TRANSFORMSTREAM) { 1492 #ifdef FUZZING 1493 if (aExtraData + 1 >= mPortIdentifiers.Length()) { 1494 return false; 1495 } 1496 #endif 1497 RefPtr<MessagePort> port1 = ReceiveMessagePort(aExtraData); 1498 RefPtr<MessagePort> port2 = ReceiveMessagePort(aExtraData + 1); 1499 if (!port1 || !port2) { 1500 return false; 1501 } 1502 nsCOMPtr<nsIGlobalObject> global = mGlobal; 1503 return TransformStream::ReceiveTransfer(aCx, global, *port1, *port2, 1504 aReturnObject); 1505 } 1506 1507 if (VideoFrame::PrefEnabled(aCx) && aTag == SCTAG_DOM_VIDEOFRAME && 1508 CloneScope() == StructuredCloneScope::SameProcess && 1509 aCloneDataPolicy.areIntraClusterClonableSharedObjectsAllowed()) { 1510 MOZ_ASSERT(aContent); 1511 1512 JS::Rooted<JSObject*> globalObj(aCx, mGlobal->GetGlobalJSObject()); 1513 // aContent will be released in CustomFreeTransferHandler. 1514 if (!VideoFrame_Binding::ConstructorEnabled(aCx, globalObj)) { 1515 return false; 1516 } 1517 1518 VideoFrame::TransferredData* data = 1519 static_cast<VideoFrame::TransferredData*>(aContent); 1520 nsCOMPtr<nsIGlobalObject> global = mGlobal; 1521 RefPtr<VideoFrame> frame = VideoFrame::FromTransferred(global.get(), data); 1522 // aContent will be released in CustomFreeTransferHandler if frame is null. 1523 if (!frame) { 1524 return false; 1525 } 1526 1527 JS::Rooted<JS::Value> value(aCx); 1528 if (!GetOrCreateDOMReflector(aCx, frame, &value)) { 1529 JS_ClearPendingException(aCx); 1530 return false; 1531 } 1532 delete data; 1533 aContent = nullptr; 1534 aReturnObject.set(&value.toObject()); 1535 return true; 1536 } 1537 1538 if (StaticPrefs::dom_media_webcodecs_enabled() && 1539 aTag == SCTAG_DOM_AUDIODATA && 1540 CloneScope() == StructuredCloneScope::SameProcess && 1541 aCloneDataPolicy.areIntraClusterClonableSharedObjectsAllowed()) { 1542 MOZ_ASSERT(aContent); 1543 1544 JS::Rooted<JSObject*> globalObj(aCx, mGlobal->GetGlobalJSObject()); 1545 // aContent will be released in CustomFreeTransferHandler. 1546 if (!AudioData_Binding::ConstructorEnabled(aCx, globalObj)) { 1547 return false; 1548 } 1549 1550 AudioData::TransferredData* data = 1551 static_cast<AudioData::TransferredData*>(aContent); 1552 nsCOMPtr<nsIGlobalObject> global = mGlobal; 1553 RefPtr<mozilla::dom::AudioData> audioData = 1554 AudioData::FromTransferred(global.get(), data); 1555 // aContent will be released in CustomFreeTransferHandler if frame is null. 1556 if (!audioData) { 1557 return false; 1558 } 1559 1560 JS::Rooted<JS::Value> value(aCx); 1561 if (!GetOrCreateDOMReflector(aCx, audioData, &value)) { 1562 JS_ClearPendingException(aCx); 1563 return false; 1564 } 1565 delete data; 1566 aContent = nullptr; 1567 aReturnObject.set(&value.toObject()); 1568 return true; 1569 } 1570 1571 #ifdef MOZ_WEBRTC 1572 if (aTag == SCTAG_DOM_RTCDATACHANNEL && 1573 CloneScope() == StructuredCloneScope::SameProcess) { 1574 if (!CheckExposedGlobals(aCx, mGlobal, 1575 GlobalNames::DedicatedWorkerGlobalScope)) { 1576 return false; 1577 } 1578 MOZ_ASSERT(aContent); 1579 1580 // This DataHolder was created over in CustomWriteTransferHandler 1581 RTCDataChannel::DataHolder* dataHolder = 1582 static_cast<RTCDataChannel::DataHolder*>(aContent); 1583 aContent = nullptr; 1584 1585 RefPtr<RTCDataChannel> channel = new RTCDataChannel(mGlobal, *dataHolder); 1586 1587 // dataHolder will be released in CustomFreeTransferHandler if we return 1588 // false. Ordinarily, I would prefer taking ownership in here, but 1589 // CustomFreeTransferHandler is called in situations other than failure 1590 // here, and in those situations it *does* need to handle the cleanup. 1591 if (!channel) { 1592 // This should only happen on OOM 1593 return false; 1594 } 1595 channel->Init(); 1596 1597 JS::Rooted<JS::Value> value(aCx); 1598 if (!GetOrCreateDOMReflector(aCx, channel, &value)) { 1599 JS_ClearPendingException(aCx); 1600 return false; 1601 } 1602 1603 delete dataHolder; 1604 aReturnObject.set(&value.toObject()); 1605 return true; 1606 } 1607 #endif 1608 1609 return false; 1610 } 1611 1612 // TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230) 1613 MOZ_CAN_RUN_SCRIPT_BOUNDARY bool 1614 StructuredCloneHolder::CustomWriteTransferHandler( 1615 JSContext* aCx, JS::Handle<JSObject*> aObj, uint32_t* aTag, 1616 JS::TransferableOwnership* aOwnership, void** aContent, 1617 uint64_t* aExtraData) { 1618 if (!mSupportsTransferring) { 1619 return false; 1620 } 1621 1622 JS::Rooted<JSObject*> obj(aCx, aObj); 1623 1624 { 1625 MessagePort* port = nullptr; 1626 nsresult rv = UNWRAP_OBJECT(MessagePort, &obj, port); 1627 if (NS_SUCCEEDED(rv)) { 1628 if (!port->CanBeCloned()) { 1629 return false; 1630 } 1631 1632 UniqueMessagePortId identifier; 1633 port->CloneAndDisentangle(identifier); 1634 1635 // We use aExtraData to store the index of this new port identifier. 1636 *aExtraData = mPortIdentifiers.Length(); 1637 mPortIdentifiers.AppendElement(identifier.release()); 1638 1639 *aTag = SCTAG_DOM_MAP_MESSAGEPORT; 1640 *aContent = nullptr; 1641 *aOwnership = JS::SCTAG_TMO_CUSTOM; 1642 1643 return true; 1644 } 1645 1646 if (CloneScope() == StructuredCloneScope::SameProcess) { 1647 OffscreenCanvas* canvas = nullptr; 1648 rv = UNWRAP_OBJECT(OffscreenCanvas, &obj, canvas); 1649 if (NS_SUCCEEDED(rv)) { 1650 MOZ_ASSERT(canvas); 1651 1652 UniquePtr<OffscreenCanvasCloneData> clonedCanvas = 1653 canvas->ToCloneData(aCx); 1654 if (!clonedCanvas) { 1655 return false; 1656 } 1657 1658 *aExtraData = 0; 1659 *aTag = SCTAG_DOM_CANVAS; 1660 *aContent = clonedCanvas.release(); 1661 MOZ_ASSERT(*aContent); 1662 *aOwnership = JS::SCTAG_TMO_CUSTOM; 1663 1664 return true; 1665 } 1666 1667 ImageBitmap* bitmap = nullptr; 1668 rv = UNWRAP_OBJECT(ImageBitmap, &obj, bitmap); 1669 if (NS_SUCCEEDED(rv)) { 1670 MOZ_ASSERT(bitmap); 1671 MOZ_ASSERT(!bitmap->IsWriteOnly()); 1672 1673 *aExtraData = 0; 1674 *aTag = SCTAG_DOM_IMAGEBITMAP; 1675 1676 UniquePtr<ImageBitmapCloneData> clonedBitmap = bitmap->ToCloneData(); 1677 if (!clonedBitmap) { 1678 return false; 1679 } 1680 1681 *aContent = clonedBitmap.release(); 1682 MOZ_ASSERT(*aContent); 1683 *aOwnership = JS::SCTAG_TMO_CUSTOM; 1684 1685 bitmap->Close(); 1686 1687 return true; 1688 } 1689 1690 if (VideoFrame::PrefEnabled(aCx)) { 1691 VideoFrame* videoFrame = nullptr; 1692 rv = UNWRAP_OBJECT(VideoFrame, &obj, videoFrame); 1693 if (NS_SUCCEEDED(rv)) { 1694 MOZ_ASSERT(videoFrame); 1695 1696 *aExtraData = 0; 1697 *aTag = SCTAG_DOM_VIDEOFRAME; 1698 *aContent = nullptr; 1699 1700 UniquePtr<VideoFrame::TransferredData> data = videoFrame->Transfer(); 1701 if (!data) { 1702 return false; 1703 } 1704 *aContent = data.release(); 1705 MOZ_ASSERT(*aContent); 1706 *aOwnership = JS::SCTAG_TMO_CUSTOM; 1707 return true; 1708 } 1709 } 1710 if (StaticPrefs::dom_media_webcodecs_enabled()) { 1711 mozilla::dom::AudioData* audioData = nullptr; 1712 rv = UNWRAP_OBJECT(AudioData, &obj, audioData); 1713 if (NS_SUCCEEDED(rv)) { 1714 MOZ_ASSERT(audioData); 1715 1716 *aExtraData = 0; 1717 *aTag = SCTAG_DOM_AUDIODATA; 1718 *aContent = nullptr; 1719 1720 UniquePtr<AudioData::TransferredData> data = audioData->Transfer(); 1721 if (!data) { 1722 return false; 1723 } 1724 *aContent = data.release(); 1725 MOZ_ASSERT(*aContent); 1726 *aOwnership = JS::SCTAG_TMO_CUSTOM; 1727 return true; 1728 } 1729 } 1730 1731 #ifdef MOZ_WEBRTC 1732 { 1733 mozilla::dom::RTCDataChannel* channel = nullptr; 1734 rv = UNWRAP_OBJECT(RTCDataChannel, &obj, channel); 1735 if (NS_SUCCEEDED(rv)) { 1736 MOZ_ASSERT(channel); 1737 // We check above that CloneScope() == SameProcess 1738 1739 UniquePtr<RTCDataChannel::DataHolder> dataHolder = 1740 channel->Transfer(); 1741 if (!dataHolder) { 1742 // RTCDataChannel.[[IsTransferable]] is false, apparently 1743 return false; 1744 } 1745 1746 *aExtraData = 0; 1747 *aTag = SCTAG_DOM_RTCDATACHANNEL; 1748 1749 // Transfer ownership out (JS::SCTAG_TMO_CUSTOM signals this) 1750 // This will be processed by CustomReadTransferHandler, or freed by 1751 // CustomFreeTransferHandler if there's some error. 1752 *aContent = dataHolder.release(); 1753 *aOwnership = JS::SCTAG_TMO_CUSTOM; 1754 1755 return true; 1756 } 1757 } 1758 #endif 1759 } 1760 1761 { 1762 RefPtr<ReadableStream> stream; 1763 rv = UNWRAP_OBJECT(ReadableStream, &obj, stream); 1764 if (NS_SUCCEEDED(rv)) { 1765 MOZ_ASSERT(stream); 1766 1767 *aTag = SCTAG_DOM_READABLESTREAM; 1768 *aContent = nullptr; 1769 1770 UniqueMessagePortId id; 1771 if (!stream->Transfer(aCx, id)) { 1772 return false; 1773 } 1774 *aExtraData = mPortIdentifiers.Length(); 1775 mPortIdentifiers.AppendElement(id.release()); 1776 *aOwnership = JS::SCTAG_TMO_CUSTOM; 1777 return true; 1778 } 1779 } 1780 1781 { 1782 RefPtr<WritableStream> stream; 1783 rv = UNWRAP_OBJECT(WritableStream, &obj, stream); 1784 if (NS_SUCCEEDED(rv)) { 1785 MOZ_ASSERT(stream); 1786 1787 *aTag = SCTAG_DOM_WRITABLESTREAM; 1788 *aContent = nullptr; 1789 1790 UniqueMessagePortId id; 1791 if (!stream->Transfer(aCx, id)) { 1792 return false; 1793 } 1794 *aExtraData = mPortIdentifiers.Length(); 1795 mPortIdentifiers.AppendElement(id.release()); 1796 *aOwnership = JS::SCTAG_TMO_CUSTOM; 1797 return true; 1798 } 1799 } 1800 1801 { 1802 RefPtr<TransformStream> stream; 1803 rv = UNWRAP_OBJECT(TransformStream, &obj, stream); 1804 if (NS_SUCCEEDED(rv)) { 1805 MOZ_ASSERT(stream); 1806 1807 *aTag = SCTAG_DOM_TRANSFORMSTREAM; 1808 *aContent = nullptr; 1809 1810 UniqueMessagePortId id1; 1811 UniqueMessagePortId id2; 1812 if (!stream->Transfer(aCx, id1, id2)) { 1813 return false; 1814 } 1815 *aExtraData = mPortIdentifiers.Length(); 1816 mPortIdentifiers.AppendElement(id1.release()); 1817 mPortIdentifiers.AppendElement(id2.release()); 1818 *aOwnership = JS::SCTAG_TMO_CUSTOM; 1819 return true; 1820 } 1821 } 1822 } 1823 1824 return false; 1825 } 1826 1827 void StructuredCloneHolder::CustomFreeTransferHandler( 1828 uint32_t aTag, JS::TransferableOwnership aOwnership, void* aContent, 1829 uint64_t aExtraData) { 1830 MOZ_ASSERT(mSupportsTransferring); 1831 1832 if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) { 1833 MOZ_ASSERT(!aContent); 1834 #ifdef FUZZING 1835 if (aExtraData >= mPortIdentifiers.Length()) { 1836 return; 1837 } 1838 #endif 1839 MOZ_ASSERT(aExtraData < mPortIdentifiers.Length()); 1840 MessagePort::ForceClose(mPortIdentifiers[aExtraData]); 1841 return; 1842 } 1843 1844 if (aTag == SCTAG_DOM_CANVAS && 1845 CloneScope() == StructuredCloneScope::SameProcess) { 1846 MOZ_ASSERT(aContent); 1847 OffscreenCanvasCloneData* data = 1848 static_cast<OffscreenCanvasCloneData*>(aContent); 1849 delete data; 1850 return; 1851 } 1852 1853 if (aTag == SCTAG_DOM_IMAGEBITMAP && 1854 CloneScope() == StructuredCloneScope::SameProcess) { 1855 MOZ_ASSERT(aContent); 1856 ImageBitmapCloneData* data = static_cast<ImageBitmapCloneData*>(aContent); 1857 delete data; 1858 return; 1859 } 1860 1861 if (aTag == SCTAG_DOM_READABLESTREAM || aTag == SCTAG_DOM_WRITABLESTREAM) { 1862 MOZ_ASSERT(!aContent); 1863 #ifdef FUZZING 1864 if (aExtraData >= mPortIdentifiers.Length()) { 1865 return; 1866 } 1867 #endif 1868 MOZ_ASSERT(aExtraData < mPortIdentifiers.Length()); 1869 MessagePort::ForceClose(mPortIdentifiers[aExtraData]); 1870 return; 1871 } 1872 1873 if (aTag == SCTAG_DOM_TRANSFORMSTREAM) { 1874 MOZ_ASSERT(!aContent); 1875 #ifdef FUZZING 1876 if (aExtraData + 1 >= mPortIdentifiers.Length()) { 1877 return; 1878 } 1879 #endif 1880 MOZ_ASSERT(aExtraData + 1 < mPortIdentifiers.Length()); 1881 MessagePort::ForceClose(mPortIdentifiers[aExtraData]); 1882 MessagePort::ForceClose(mPortIdentifiers[aExtraData + 1]); 1883 return; 1884 } 1885 1886 if (aTag == SCTAG_DOM_VIDEOFRAME && 1887 CloneScope() == StructuredCloneScope::SameProcess) { 1888 if (aContent) { 1889 VideoFrame::TransferredData* data = 1890 static_cast<VideoFrame::TransferredData*>(aContent); 1891 delete data; 1892 } 1893 return; 1894 } 1895 if (aTag == SCTAG_DOM_AUDIODATA && 1896 CloneScope() == StructuredCloneScope::SameProcess) { 1897 if (aContent) { 1898 AudioData::TransferredData* data = 1899 static_cast<AudioData::TransferredData*>(aContent); 1900 delete data; 1901 } 1902 return; 1903 } 1904 #ifdef MOZ_WEBRTC 1905 if (aTag == SCTAG_DOM_RTCDATACHANNEL && 1906 CloneScope() == StructuredCloneScope::SameProcess) { 1907 if (aContent) { 1908 RTCDataChannel::DataHolder* dataHolder = 1909 static_cast<RTCDataChannel::DataHolder*>(aContent); 1910 delete dataHolder; 1911 } 1912 return; 1913 } 1914 #endif 1915 } 1916 1917 bool StructuredCloneHolder::CustomCanTransferHandler( 1918 JSContext* aCx, JS::Handle<JSObject*> aObj, 1919 bool* aSameProcessScopeRequired) { 1920 if (!mSupportsTransferring) { 1921 return false; 1922 } 1923 1924 JS::Rooted<JSObject*> obj(aCx, aObj); 1925 1926 { 1927 MessagePort* port = nullptr; 1928 nsresult rv = UNWRAP_OBJECT(MessagePort, &obj, port); 1929 if (NS_SUCCEEDED(rv)) { 1930 return true; 1931 } 1932 } 1933 1934 { 1935 OffscreenCanvas* canvas = nullptr; 1936 nsresult rv = UNWRAP_OBJECT(OffscreenCanvas, &obj, canvas); 1937 if (NS_SUCCEEDED(rv)) { 1938 SameProcessScopeRequired(aSameProcessScopeRequired); 1939 return CloneScope() == StructuredCloneScope::SameProcess; 1940 } 1941 } 1942 1943 { 1944 ImageBitmap* bitmap = nullptr; 1945 nsresult rv = UNWRAP_OBJECT(ImageBitmap, &obj, bitmap); 1946 if (NS_SUCCEEDED(rv)) { 1947 if (bitmap->IsWriteOnly()) { 1948 return false; 1949 } 1950 1951 SameProcessScopeRequired(aSameProcessScopeRequired); 1952 return CloneScope() == StructuredCloneScope::SameProcess; 1953 } 1954 } 1955 1956 { 1957 ReadableStream* stream = nullptr; 1958 nsresult rv = UNWRAP_OBJECT(ReadableStream, &obj, stream); 1959 if (NS_SUCCEEDED(rv)) { 1960 // https://streams.spec.whatwg.org/#ref-for-transfer-steps 1961 // Step 1: If ! IsReadableStreamLocked(value) is true, throw a 1962 // "DataCloneError" DOMException. 1963 return !stream->Locked(); 1964 } 1965 } 1966 1967 { 1968 WritableStream* stream = nullptr; 1969 nsresult rv = UNWRAP_OBJECT(WritableStream, &obj, stream); 1970 if (NS_SUCCEEDED(rv)) { 1971 // https://streams.spec.whatwg.org/#ref-for-transfer-steps①1972 // Step 1: If ! IsWritableStreamLocked(value) is true, throw a 1973 // "DataCloneError" DOMException. 1974 return !stream->Locked(); 1975 } 1976 } 1977 1978 { 1979 TransformStream* stream = nullptr; 1980 nsresult rv = UNWRAP_OBJECT(TransformStream, &obj, stream); 1981 if (NS_SUCCEEDED(rv)) { 1982 // https://streams.spec.whatwg.org/#ref-for-transfer-steps② 1983 // Step 3 + 4: If ! Is{Readable,Writable}StreamLocked(value) is true, 1984 // throw a "DataCloneError" DOMException. 1985 return !stream->Readable()->Locked() && !stream->Writable()->Locked(); 1986 } 1987 } 1988 1989 if (VideoFrame::PrefEnabled(aCx)) { 1990 VideoFrame* videoframe = nullptr; 1991 nsresult rv = UNWRAP_OBJECT(VideoFrame, &obj, videoframe); 1992 if (NS_SUCCEEDED(rv)) { 1993 SameProcessScopeRequired(aSameProcessScopeRequired); 1994 return CloneScope() == StructuredCloneScope::SameProcess; 1995 } 1996 } 1997 1998 if (StaticPrefs::dom_media_webcodecs_enabled()) { 1999 mozilla::dom::AudioData* audioData = nullptr; 2000 nsresult rv = UNWRAP_OBJECT(AudioData, &obj, audioData); 2001 if (NS_SUCCEEDED(rv)) { 2002 SameProcessScopeRequired(aSameProcessScopeRequired); 2003 return CloneScope() == StructuredCloneScope::SameProcess; 2004 } 2005 } 2006 2007 #ifdef MOZ_WEBRTC 2008 { 2009 mozilla::dom::RTCDataChannel* channel = nullptr; 2010 nsresult rv = UNWRAP_OBJECT(RTCDataChannel, &obj, channel); 2011 if (NS_SUCCEEDED(rv)) { 2012 SameProcessScopeRequired(aSameProcessScopeRequired); 2013 return CloneScope() == StructuredCloneScope::SameProcess; 2014 } 2015 } 2016 #endif 2017 2018 return false; 2019 } 2020 2021 bool StructuredCloneHolder::TakeTransferredPortsAsSequence( 2022 Sequence<OwningNonNull<mozilla::dom::MessagePort>>& aPorts) { 2023 nsTArray<RefPtr<MessagePort>> ports = TakeTransferredPorts(); 2024 2025 aPorts.Clear(); 2026 for (uint32_t i = 0, len = ports.Length(); i < len; ++i) { 2027 if (!aPorts.AppendElement(ports[i].forget(), fallible)) { 2028 return false; 2029 } 2030 } 2031 2032 return true; 2033 } 2034 2035 void StructuredCloneHolder::SameProcessScopeRequired( 2036 bool* aSameProcessScopeRequired) { 2037 MOZ_ASSERT(aSameProcessScopeRequired); 2038 if (mStructuredCloneScope == StructuredCloneScope::UnknownDestination) { 2039 mStructuredCloneScope = StructuredCloneScope::SameProcess; 2040 *aSameProcessScopeRequired = true; 2041 } 2042 } 2043 2044 } // namespace mozilla::dom