/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * 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/. */
// Let's update our scope to the final one. The new one could be more // restrictive of the current one.
MOZ_ASSERT(mStructuredCloneScope >= mBuffer->scope());
mStructuredCloneScope = mBuffer->scope(); returntrue;
}
bool StructuredCloneHolderBase::Read(
JSContext* aCx, JS::MutableHandle<JS::Value> aValue, const JS::CloneDataPolicy& aCloneDataPolicy) {
MOZ_ASSERT(mBuffer, "Read() without Write() is not allowed.");
MOZ_ASSERT(!mClearCalled, "This method cannot be called after Clear.");
if (!StructuredCloneHolderBase::Read(aCx, aValue, aCloneDataPolicy)) {
mTransferredPorts.Clear();
JS_ClearPendingException(aCx);
aRv.ThrowDataCloneError(mErrorMessage); return;
}
// If we are tranferring something, we cannot call 'Read()' more than once. if (mSupportsTransferring) {
mBlobImplArray.Clear();
mWasmModuleArray.Clear();
mClonedSurfaces.Clear();
mInputStreamArray.Clear();
mVideoFrames.Clear();
mEncodedAudioChunks.Clear();
mEncodedVideoChunks.Clear();
Clear();
}
}
// Sandboxes aren't really DOM globals (though they do set the // JSCLASS_DOM_GLOBAL flag), and so we can't simply do the exposure check. // Some sandboxes do have a DOM global as their prototype, so using the // prototype to check for exposure will at least make it work for those // specific cases.
{
JSObject* proto = xpc::SandboxPrototypeOrNull(aCx, global); if (proto) {
global = proto;
}
}
if (!IsGlobalInExposureSet(aCx, global, aExposedGlobals)) {
ErrorResult error;
error.ThrowDataCloneError("Interface is not exposed.");
MOZ_ALWAYS_TRUE(error.MaybeSetPendingException(aCx)); returnfalse;
} returntrue;
}
// https://html.spec.whatwg.org/multipage/structured-data.html#structureddeserialize // // 22. Otherwise: // // 1. Let interfaceName be serialized.[[Type]]. // 2. If the interface identified by interfaceName is not exposed in // targetRealm, then throw a "DataCloneError" DOMException. // // The special-casing for IndexedDB is because it uses a sandbox to // deserialize, which means we don't actually have access to exposure // information. if (!aIsForIndexedDB && !CheckExposedGlobals(aCx, global, exposedGlobals)) { return nullptr;
}
JS::Rooted<JS::Value> result(aCx);
{ // nsJSPrincipals::ReadKnownPrincipalType addrefs for us, but because of // the casting between JSPrincipals* and nsIPrincipal* we can't use // getter_AddRefs above and have to already_AddRefed here.
nsCOMPtr<nsIPrincipal> principal =
already_AddRefed<nsIPrincipal>(nsJSPrincipals::get(prin));
// Window and Location are not serializable, so it's OK to just do a static // unwrap here.
JS::Rooted<JSObject*> obj(aCx, js::CheckedUnwrapStatic(aObj)); if (!obj) { return xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
}
if (NS_IsMainThread() && xpc::IsReflector(obj, aCx)) { // We only care about principals, so ReflectorToISupportsStatic is fine.
nsCOMPtr<nsISupports> base = xpc::ReflectorToISupportsStatic(obj);
nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(base); if (principal) { auto nsjsprincipals = nsJSPrincipals::get(principal); return nsjsprincipals->write(aCx, aWriter);
}
}
// Don't know what this is
ErrorResult rv; constchar* className = JS::GetClass(obj)->name;
rv.ThrowDataCloneError(nsDependentCString(className) + " object could not be cloned."_ns);
MOZ_ALWAYS_TRUE(rv.MaybeSetPendingException(aCx)); returnfalse;
}
JSObject* ReadBlob(JSContext* aCx, uint32_t aIndex,
StructuredCloneHolder* aHolder) {
MOZ_ASSERT(aHolder); #ifdef FUZZING if (aIndex >= aHolder->BlobImpls().Length()) { return nullptr;
} #endif
MOZ_ASSERT(aIndex < aHolder->BlobImpls().Length());
JS::Rooted<JS::Value> val(aCx);
{ // RefPtr<File> and RefPtr<BlobImpl> need to go out of scope before // toObject() is called because the static analysis thinks releasing XPCOM // objects can GC (because in some cases it can!), and a return statement // with a JSObject* type means that JSObject* is on the stack as a raw // pointer while destructors are running.
RefPtr<BlobImpl> blobImpl = aHolder->BlobImpls()[aIndex];
// We store the position of the blobImpl in the array as index. if (JS_WriteUint32Pair(aWriter, SCTAG_DOM_BLOB,
aHolder->BlobImpls().Length())) {
aHolder->BlobImpls().AppendElement(blobImpl); returntrue;
}
returnfalse;
}
// A directory is serialized as: // - pair of ints: SCTAG_DOM_DIRECTORY, path length // - path as string bool WriteDirectory(JSStructuredCloneWriter* aWriter, Directory* aDirectory) {
MOZ_ASSERT(aWriter);
MOZ_ASSERT(aDirectory);
// RefPtr<Directory> needs to go out of scope before toObject() is // called because the static analysis thinks dereferencing XPCOM objects // can GC (because in some cases it can!), and a return statement with a // JSObject* type means that JSObject* is on the stack as a raw pointer // while destructors are running.
JS::Rooted<JS::Value> val(aCx);
{
RefPtr<Directory> directory =
ReadDirectoryInternal(aReader, aPathLength, aHolder); if (!directory) { return nullptr;
}
if (!ToJSValue(aCx, directory, &val)) { return nullptr;
}
}
return &val.toObject();
}
// Read the WriteFileList for the format.
JSObject* ReadFileList(JSContext* aCx, JSStructuredCloneReader* aReader,
uint32_t aCount, StructuredCloneHolder* aHolder) {
MOZ_ASSERT(aCx);
MOZ_ASSERT(aReader);
JS::Rooted<JS::Value> val(aCx);
{
RefPtr<FileList> fileList = new FileList(aHolder->GlobalDuringRead());
uint32_t zero, index; // |index| is the index of the first blobImpl. if (!JS_ReadUint32Pair(aReader, &zero, &index) || zero != 0) { return nullptr;
}
// |aCount| is the number of BlobImpls to use from the |index|. for (uint32_t i = 0; i < aCount; ++i) {
uint32_t pos = index + i; #ifdef FUZZING if (pos >= aHolder->BlobImpls().Length()) { return nullptr;
} #endif
MOZ_ASSERT(pos < aHolder->BlobImpls().Length());
if (!fileList->Append(file)) { return nullptr;
}
}
if (!ToJSValue(aCx, fileList, &val)) { return nullptr;
}
}
return &val.toObject();
}
// The format of the FileList serialization is: // - pair of ints: SCTAG_DOM_FILELIST, Length of the FileList // - pair of ints: 0, The offset of the BlobImpl array bool WriteFileList(JSStructuredCloneWriter* aWriter, FileList* aFileList,
StructuredCloneHolder* aHolder) {
MOZ_ASSERT(aWriter);
MOZ_ASSERT(aFileList);
MOZ_ASSERT(aHolder);
// A FileList is serialized writing the X number of elements and the offset // from mBlobImplArray. The Read will take X elements from mBlobImplArray // starting from the offset. if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_FILELIST, aFileList->Length()) ||
!JS_WriteUint32Pair(aWriter, 0, aHolder->BlobImpls().Length())) { returnfalse;
}
nsTArray<RefPtr<BlobImpl>> blobImpls;
for (uint32_t i = 0; i < aFileList->Length(); ++i) {
RefPtr<BlobImpl> blobImpl = aFileList->Item(i)->Impl();
blobImpls.AppendElement(blobImpl);
}
// Read the WriteFormData for the format.
JSObject* ReadFormData(JSContext* aCx, JSStructuredCloneReader* aReader,
uint32_t aCount, StructuredCloneHolder* aHolder) {
MOZ_ASSERT(aCx);
MOZ_ASSERT(aReader);
MOZ_ASSERT(aHolder);
// See the serialization of the FormData for the format.
JS::Rooted<JS::Value> val(aCx);
{
RefPtr<FormData> formData = new FormData(aHolder->GlobalDuringRead());
Optional<nsAString> thirdArg; for (uint32_t i = 0; i < aCount; ++i) {
nsAutoString name; if (!StructuredCloneHolder::ReadString(aReader, name)) { return nullptr;
}
if (!ToJSValue(aCx, formData, &val)) { return nullptr;
}
}
return &val.toObject();
}
// The format of the FormData serialization is: // - pair of ints: SCTAG_DOM_FORMDATA, Length of the FormData elements // - for each Element element: // - name string // - if it's a blob: // - pair of ints: SCTAG_DOM_BLOB, index of the BlobImpl in the array // mBlobImplArray. // - if it's a directory (See WriteDirectory): // - pair of ints: SCTAG_DOM_DIRECTORY, path length // - path as string // - else: // - pair of ints: 0, string length // - value string bool WriteFormData(JSStructuredCloneWriter* aWriter, FormData* aFormData,
StructuredCloneHolder* aHolder) {
MOZ_ASSERT(aWriter);
MOZ_ASSERT(aFormData);
MOZ_ASSERT(aHolder);
if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_FORMDATA, aFormData->Length())) { returnfalse;
}
auto write = [aWriter, aHolder]( const nsString& aName, const OwningBlobOrDirectoryOrUSVString& aValue) { if (!StructuredCloneHolder::WriteString(aWriter, aName)) { returnfalse;
}
if (aValue.IsBlob()) { if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_BLOB,
aHolder->BlobImpls().Length())) { returnfalse;
}
// We store the position of the wasmModule in the array as index. if (JS_WriteUint32Pair(aWriter, SCTAG_DOM_WASM_MODULE,
aHolder->WasmModules().Length())) {
aHolder->WasmModules().AppendElement(aWasmModule); returntrue;
}
// We store the position of the inputStream in the array as index. if (JS_WriteUint32Pair(aWriter, SCTAG_DOM_INPUTSTREAM,
aHolder->InputStreams().Length())) {
aHolder->InputStreams().AppendElement(aInputStream); returntrue;
}
if (aTag == SCTAG_DOM_BLOB) { if (!CheckExposedGlobals(aCx, mGlobal, sWindowOrWorker)) { return nullptr;
} return ReadBlob(aCx, aIndex, this);
}
if (aTag == SCTAG_DOM_DIRECTORY) { if (!CheckExposedGlobals(aCx, mGlobal, sWindowOrWorker)) { return nullptr;
} return ReadDirectory(aCx, aReader, aIndex, this);
}
if (aTag == SCTAG_DOM_FILELIST) { if (!CheckExposedGlobals(aCx, mGlobal, sWindowOrWorker)) { return nullptr;
} return ReadFileList(aCx, aReader, aIndex, this);
}
if (aTag == SCTAG_DOM_FORMDATA) { if (!CheckExposedGlobals(aCx, mGlobal, sWindowOrWorker)) { return nullptr;
} return ReadFormData(aCx, aReader, aIndex, this);
}
if (aTag == SCTAG_DOM_IMAGEBITMAP &&
CloneScope() == StructuredCloneScope::SameProcess) { if (!CheckExposedGlobals(aCx, mGlobal, sWindowOrWorker)) { return nullptr;
} // Get the current global object. // This can be null.
JS::Rooted<JSObject*> result(aCx);
{ // aIndex is the index of the cloned image.
result = ImageBitmap::ReadStructuredClone(aCx, aReader, mGlobal,
GetSurfaces(), aIndex);
} return result;
}
if (aTag == SCTAG_DOM_STRUCTURED_CLONE_HOLDER) { return StructuredCloneBlob::ReadStructuredClone(aCx, aReader, this);
}
// See if this is a File/Blob object.
{
Blob* blob = nullptr; if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, &obj, blob))) { return WriteBlob(aWriter, blob, this);
}
}
// See if this is a Directory object.
{
Directory* directory = nullptr; if (NS_SUCCEEDED(UNWRAP_OBJECT(Directory, &obj, directory))) { return WriteDirectory(aWriter, directory);
}
}
// See if this is a FileList object.
{
FileList* fileList = nullptr; if (NS_SUCCEEDED(UNWRAP_OBJECT(FileList, &obj, fileList))) { return WriteFileList(aWriter, fileList, this);
}
}
// See if this is a FormData object.
{
FormData* formData = nullptr; if (NS_SUCCEEDED(UNWRAP_OBJECT(FormData, &obj, formData))) { return WriteFormData(aWriter, formData, this);
}
}
// See if this is an ImageBitmap object.
{
ImageBitmap* imageBitmap = nullptr; if (NS_SUCCEEDED(UNWRAP_OBJECT(ImageBitmap, &obj, imageBitmap))) {
SameProcessScopeRequired(aSameProcessScopeRequired);
// See if this is a StructuredCloneBlob object.
{
StructuredCloneBlob* holder = nullptr; if (NS_SUCCEEDED(UNWRAP_OBJECT(StructuredCloneHolder, &obj, holder))) { return holder->WriteStructuredClone(aCx, aWriter, this);
}
}
// See if this is a BrowsingContext object.
{
BrowsingContext* holder = nullptr; if (NS_SUCCEEDED(UNWRAP_OBJECT(BrowsingContext, &obj, holder))) { return holder->WriteStructuredClone(aCx, aWriter, this);
}
}
// See if this is a ClonedErrorHolder object.
{
ClonedErrorHolder* holder = nullptr; if (NS_SUCCEEDED(UNWRAP_OBJECT(ClonedErrorHolder, &obj, holder))) { return holder->WriteStructuredClone(aCx, aWriter, this);
}
}
// See if this is a WasmModule. if (JS::IsWasmModuleObject(obj)) {
SameProcessScopeRequired(aSameProcessScopeRequired); if (CloneScope() == StructuredCloneScope::SameProcess) {
RefPtr<JS::WasmModule> module = JS::GetWasmModule(obj);
MOZ_ASSERT(module);
// See if this is a VideoFrame object. if (VideoFrame::PrefEnabled()) {
VideoFrame* videoFrame = nullptr; if (NS_SUCCEEDED(UNWRAP_OBJECT(VideoFrame, &obj, videoFrame))) {
SameProcessScopeRequired(aSameProcessScopeRequired); return CloneScope() == StructuredCloneScope::SameProcess
? videoFrame->WriteStructuredClone(aWriter, this)
: false;
}
}
// See if this is a EncodedVideoChunk object. if (StaticPrefs::dom_media_webcodecs_enabled()) {
EncodedVideoChunk* encodedVideoChunk = nullptr; if (NS_SUCCEEDED(
UNWRAP_OBJECT(EncodedVideoChunk, &obj, encodedVideoChunk))) {
SameProcessScopeRequired(aSameProcessScopeRequired); return CloneScope() == StructuredCloneScope::SameProcess
? encodedVideoChunk->WriteStructuredClone(aWriter, this)
: false;
}
}
// See if this is an AudioData object. if (StaticPrefs::dom_media_webcodecs_enabled()) {
mozilla::dom::AudioData* audioData = nullptr; if (NS_SUCCEEDED(UNWRAP_OBJECT(AudioData, &obj, audioData))) {
SameProcessScopeRequired(aSameProcessScopeRequired); return CloneScope() == StructuredCloneScope::SameProcess
? audioData->WriteStructuredClone(aWriter, this)
: false;
}
}
// See if this is a EncodedAudioChunk object. if (StaticPrefs::dom_media_webcodecs_enabled()) {
EncodedAudioChunk* encodedAudioChunk = nullptr; if (NS_SUCCEEDED(
UNWRAP_OBJECT(EncodedAudioChunk, &obj, encodedAudioChunk))) {
SameProcessScopeRequired(aSameProcessScopeRequired); return CloneScope() == StructuredCloneScope::SameProcess
? encodedAudioChunk->WriteStructuredClone(aWriter, this)
: false;
}
}
{ // We only care about streams, so ReflectorToISupportsStatic is fine.
nsCOMPtr<nsISupports> base = xpc::ReflectorToISupportsStatic(aObj);
nsCOMPtr<nsIInputStream> inputStream = do_QueryInterface(base); if (inputStream) { return WriteInputStream(aWriter, inputStream, this);
}
}
JS::Rooted<JSObject*> globalObj(aCx, mGlobal->GetGlobalJSObject()); // aContent will be released in CustomFreeTransferHandler. if (!VideoFrame_Binding::ConstructorEnabled(aCx, globalObj)) { returnfalse;
}
VideoFrame::TransferredData* data = static_cast<VideoFrame::TransferredData*>(aContent);
nsCOMPtr<nsIGlobalObject> global = mGlobal;
RefPtr<VideoFrame> frame = VideoFrame::FromTransferred(global.get(), data); // aContent will be released in CustomFreeTransferHandler if frame is null. if (!frame) { returnfalse;
}
JS::Rooted<JSObject*> globalObj(aCx, mGlobal->GetGlobalJSObject()); // aContent will be released in CustomFreeTransferHandler. if (!AudioData_Binding::ConstructorEnabled(aCx, globalObj)) { returnfalse;
}
AudioData::TransferredData* data = static_cast<AudioData::TransferredData*>(aContent);
nsCOMPtr<nsIGlobalObject> global = mGlobal;
RefPtr<mozilla::dom::AudioData> audioData =
AudioData::FromTransferred(global.get(), data); // aContent will be released in CustomFreeTransferHandler if frame is null. if (!audioData) { returnfalse;
}
// We use aExtraData to store the index of this new port identifier.
*aExtraData = mPortIdentifiers.Length();
mPortIdentifiers.AppendElement(identifier.release());
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.