From 130bed417fcded508dd401f811198481ae2defe0 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Mon, 12 Oct 2015 13:29:48 -0700 Subject: [PATCH] Bug 1201620 - Make SavedFrame stacks structured cloneable; r=sfink --- dom/indexedDB/ActorsParent.cpp | 21 ++- js/public/StructuredClone.h | 2 +- js/src/jsapi-tests/testStructuredClone.cpp | 148 ++++++++++++++++ js/src/jsapi.cpp | 1 - js/src/vm/SavedFrame.h | 18 ++ js/src/vm/SavedStacks-inl.h | 8 +- js/src/vm/SavedStacks.cpp | 123 +++++++++---- js/src/vm/SavedStacks.h | 1 + js/src/vm/StructuredClone.cpp | 196 ++++++++++++++++++++- 9 files changed, 466 insertions(+), 52 deletions(-) diff --git a/dom/indexedDB/ActorsParent.cpp b/dom/indexedDB/ActorsParent.cpp index c96a4f6501e..37e1ef49689 100644 --- a/dom/indexedDB/ActorsParent.cpp +++ b/dom/indexedDB/ActorsParent.cpp @@ -146,11 +146,11 @@ class VersionChangeTransaction; // If JS_STRUCTURED_CLONE_VERSION changes then we need to update our major // schema version. -static_assert(JS_STRUCTURED_CLONE_VERSION == 5, +static_assert(JS_STRUCTURED_CLONE_VERSION == 6, "Need to update the major schema version."); // Major schema version. Bump for almost everything. -const uint32_t kMajorSchemaVersion = 21; +const uint32_t kMajorSchemaVersion = 22; // Minor schema version. Should almost always be 0 (maybe bump on release // branches if we have to). @@ -4066,6 +4066,19 @@ UpgradeSchemaFrom20_0To21_0(mozIStorageConnection* aConnection) return NS_OK; } +nsresult +UpgradeSchemaFrom21_0To22_0(mozIStorageConnection* aConnection) +{ + // The only change between 21 and 22 was a different structured clone format, + // but it's backwards-compatible. + nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(22, 0)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + nsresult GetDatabaseFileURL(nsIFile* aDatabaseFile, PersistenceType aPersistenceType, @@ -4558,7 +4571,7 @@ CreateStorageConnection(nsIFile* aDBFile, } } else { // This logic needs to change next time we change the schema! - static_assert(kSQLiteSchemaVersion == int32_t((21 << 4) + 0), + static_assert(kSQLiteSchemaVersion == int32_t((22 << 4) + 0), "Upgrade function needed due to schema version increase."); while (schemaVersion != kSQLiteSchemaVersion) { @@ -4598,6 +4611,8 @@ CreateStorageConnection(nsIFile* aDBFile, rv = UpgradeSchemaFrom19_0To20_0(aFMDirectory, connection); } else if (schemaVersion == MakeSchemaVersion(20, 0)) { rv = UpgradeSchemaFrom20_0To21_0(connection); + } else if (schemaVersion == MakeSchemaVersion(21, 0)) { + rv = UpgradeSchemaFrom21_0To22_0(connection); } else { IDB_WARNING("Unable to open IndexedDB database, no upgrade path is " "available!"); diff --git a/js/public/StructuredClone.h b/js/public/StructuredClone.h index 324521b62d8..2930a6073a1 100644 --- a/js/public/StructuredClone.h +++ b/js/public/StructuredClone.h @@ -121,7 +121,7 @@ typedef void (*FreeTransferStructuredCloneOp)(uint32_t tag, JS::TransferableOwne // Increment this when anything at all changes in the serialization format. // (Note that this does not need to be bumped for Transferable-only changes, // since they are never saved to persistent storage.) -#define JS_STRUCTURED_CLONE_VERSION 5 +#define JS_STRUCTURED_CLONE_VERSION 6 struct JSStructuredCloneCallbacks { ReadStructuredCloneOp read; diff --git a/js/src/jsapi-tests/testStructuredClone.cpp b/js/src/jsapi-tests/testStructuredClone.cpp index 0cfa966d9d1..4f5d29a1931 100644 --- a/js/src/jsapi-tests/testStructuredClone.cpp +++ b/js/src/jsapi-tests/testStructuredClone.cpp @@ -78,3 +78,151 @@ BEGIN_TEST(testStructuredClone_string) return true; } END_TEST(testStructuredClone_string) + +struct StructuredCloneTestPrincipals final : public JSPrincipals { + uint32_t rank; + + explicit StructuredCloneTestPrincipals(uint32_t rank, int32_t rc = 1) : rank(rank) { + this->refcount = rc; + } + + bool write(JSContext* cx, JSStructuredCloneWriter* writer) override { + return JS_WriteUint32Pair(writer, rank, 0); + } + + static bool read(JSContext* cx, JSStructuredCloneReader *reader, JSPrincipals** outPrincipals) { + uint32_t rank; + uint32_t unused; + if (!JS_ReadUint32Pair(reader, &rank, &unused)) + return false; + + *outPrincipals = new StructuredCloneTestPrincipals(rank); + return !!*outPrincipals; + } + + static void destroy(JSPrincipals* p) { + auto p1 = static_cast(p); + delete p1; + } + + static uint32_t getRank(JSPrincipals* p) { + if (!p) + return 0; + return static_cast(p)->rank; + } + + static bool subsumes(JSPrincipals* a, JSPrincipals* b) { + return getRank(a) > getRank(b); + } + + static JSSecurityCallbacks securityCallbacks; + + static StructuredCloneTestPrincipals testPrincipals; +}; + +JSSecurityCallbacks StructuredCloneTestPrincipals::securityCallbacks = { + nullptr, // contentSecurityPolicyAllows + subsumes +}; + +BEGIN_TEST(testStructuredClone_SavedFrame) +{ + JS_SetSecurityCallbacks(rt, &StructuredCloneTestPrincipals::securityCallbacks); + JS_InitDestroyPrincipalsCallback(rt, StructuredCloneTestPrincipals::destroy); + JS_InitReadPrincipalsCallback(rt, StructuredCloneTestPrincipals::read); + + auto testPrincipals = new StructuredCloneTestPrincipals(42, 0); + CHECK(testPrincipals); + + auto DONE = (JSPrincipals*) 0xDEADBEEF; + + struct { + const char* name; + JSPrincipals* principals; + } principalsToTest[] = { + { "IsSystem", &js::ReconstructedSavedFramePrincipals::IsSystem }, + { "IsNotSystem", &js::ReconstructedSavedFramePrincipals::IsNotSystem }, + { "testPrincipals", testPrincipals }, + { "nullptr principals", nullptr }, + { "DONE", DONE } + }; + + const char* FILENAME = "filename.js"; + + for (auto* pp = principalsToTest; pp->principals != DONE; pp++) { + fprintf(stderr, "Testing with principals '%s'\n", pp->name); + + JS::RootedObject g(cx, JS_NewGlobalObject(cx, getGlobalClass(), pp->principals, + JS::FireOnNewGlobalHook)); + CHECK(g); + JSAutoCompartment ac(cx, g); + + CHECK(js::DefineTestingFunctions(cx, g, false, false)); + + JS::RootedValue srcVal(cx); + CHECK(evaluate("(function one() { \n" // 1 + " return (function two() { \n" // 2 + " return (function three() { \n" // 3 + " return saveStack(); \n" // 4 + " }()); \n" // 5 + " }()); \n" // 6 + "}()); \n", // 7 + FILENAME, + 1, + &srcVal)); + + CHECK(srcVal.isObject()); + JS::RootedObject srcObj(cx, &srcVal.toObject()); + + CHECK(srcObj->is()); + js::RootedSavedFrame srcFrame(cx, &srcObj->as()); + + CHECK(srcFrame->getPrincipals() == pp->principals); + + JS::RootedValue destVal(cx); + CHECK(JS_StructuredClone(cx, srcVal, &destVal, nullptr, nullptr)); + + CHECK(destVal.isObject()); + JS::RootedObject destObj(cx, &destVal.toObject()); + + CHECK(destObj->is()); + auto destFrame = &destObj->as(); + + size_t framesCopied = 0; + for (auto& f : *destFrame) { + framesCopied++; + + CHECK(&f != srcFrame); + + if (pp->principals == testPrincipals) { + // We shouldn't get a pointer to the same + // StructuredCloneTestPrincipals instance since we should have + // serialized and then deserialized it into a new instance. + CHECK(f.getPrincipals() != pp->principals); + + // But it should certainly have the same rank. + CHECK(StructuredCloneTestPrincipals::getRank(f.getPrincipals()) == + StructuredCloneTestPrincipals::getRank(pp->principals)); + } else { + // For our singleton principals, we should always get the same + // pointer back. + CHECK(js::ReconstructedSavedFramePrincipals::is(pp->principals) || + pp->principals == nullptr); + CHECK(f.getPrincipals() == pp->principals); + } + + CHECK(EqualStrings(f.getSource(), srcFrame->getSource())); + CHECK(f.getLine() == srcFrame->getLine()); + CHECK(f.getColumn() == srcFrame->getColumn()); + CHECK(EqualStrings(f.getFunctionDisplayName(), srcFrame->getFunctionDisplayName())); + + srcFrame = srcFrame->getParent(); + } + + // Four function frames + one global frame. + CHECK(framesCopied == 4); + } + + return true; +} +END_TEST(testStructuredClone_SavedFrame) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 7bfc910310f..152364d064c 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -6438,4 +6438,3 @@ JS::ResetTimeZone() icu::TimeZone::recreateDefault(); #endif } - diff --git a/js/src/vm/SavedFrame.h b/js/src/vm/SavedFrame.h index df050453e34..b1b22d299b0 100644 --- a/js/src/vm/SavedFrame.h +++ b/js/src/vm/SavedFrame.h @@ -7,12 +7,15 @@ #ifndef vm_SavedFrame_h #define vm_SavedFrame_h +#include "jswrapper.h" + #include "js/UbiNode.h" namespace js { class SavedFrame : public NativeObject { friend class SavedStacks; + friend struct ::JSStructuredCloneReader; public: static const Class class_; @@ -120,6 +123,12 @@ class SavedFrame : public NativeObject { !obj.as().getReservedSlot(JSSLOT_SOURCE).isNull(); } + static bool isSavedFrameOrWrapperAndNotProto(JSObject& obj) { + auto unwrapped = CheckedUnwrap(&obj); + MOZ_ASSERT(unwrapped); + return isSavedFrameAndNotProto(*unwrapped); + } + struct Lookup; struct HashPolicy; @@ -142,8 +151,17 @@ class SavedFrame : public NativeObject { }; private: + static SavedFrame* create(JSContext* cx); static bool finishSavedFrameInit(JSContext* cx, HandleObject ctor, HandleObject proto); void initFromLookup(HandleLookup lookup); + void initSource(JSAtom* source); + void initLine(uint32_t line); + void initColumn(uint32_t column); + void initFunctionDisplayName(JSAtom* maybeName); + void initAsyncCause(JSAtom* maybeCause); + void initParent(SavedFrame* maybeParent); + void initPrincipalsAlreadyHeld(JSPrincipals* principals); + void initPrincipals(JSPrincipals* principals); enum { // The reserved slots in the SavedFrame class. diff --git a/js/src/vm/SavedStacks-inl.h b/js/src/vm/SavedStacks-inl.h index 768c228acad..dd05760b4ea 100644 --- a/js/src/vm/SavedStacks-inl.h +++ b/js/src/vm/SavedStacks-inl.h @@ -22,13 +22,7 @@ inline void js::AssertObjectIsSavedFrameOrWrapper(JSContext* cx, HandleObject stack) { -#ifdef DEBUG - if (stack) { - RootedObject savedFrameObj(cx, CheckedUnwrap(stack)); - MOZ_ASSERT(savedFrameObj); - MOZ_ASSERT(js::SavedFrame::isSavedFrameAndNotProto(*savedFrameObj)); - } -#endif + MOZ_ASSERT_IF(stack, js::SavedFrame::isSavedFrameOrWrapperAndNotProto(*stack)); } #endif // vm_SavedStacksInl_h diff --git a/js/src/vm/SavedStacks.cpp b/js/src/vm/SavedStacks.cpp index 8dbfe38771e..bde5636e92c 100644 --- a/js/src/vm/SavedStacks.cpp +++ b/js/src/vm/SavedStacks.cpp @@ -406,29 +406,91 @@ SavedFrame::getPrincipals() return static_cast(v.toPrivate()); } +void +SavedFrame::initSource(JSAtom* source) +{ + MOZ_ASSERT(source); + initReservedSlot(JSSLOT_SOURCE, StringValue(source)); +} + +void +SavedFrame::initLine(uint32_t line) +{ + initReservedSlot(JSSLOT_LINE, PrivateUint32Value(line)); +} + +void +SavedFrame::initColumn(uint32_t column) +{ + initReservedSlot(JSSLOT_COLUMN, PrivateUint32Value(column)); +} + +void +SavedFrame::initPrincipals(JSPrincipals* principals) +{ + if (principals) + JS_HoldPrincipals(principals); + initPrincipalsAlreadyHeld(principals); +} + +void +SavedFrame::initPrincipalsAlreadyHeld(JSPrincipals* principals) +{ + MOZ_ASSERT_IF(principals, principals->refcount > 0); + initReservedSlot(JSSLOT_PRINCIPALS, PrivateValue(principals)); +} + +void +SavedFrame::initFunctionDisplayName(JSAtom* maybeName) +{ + initReservedSlot(JSSLOT_FUNCTIONDISPLAYNAME, maybeName ? StringValue(maybeName) : NullValue()); +} + +void +SavedFrame::initAsyncCause(JSAtom* maybeCause) +{ + initReservedSlot(JSSLOT_ASYNCCAUSE, maybeCause ? StringValue(maybeCause) : NullValue()); +} + +void +SavedFrame::initParent(SavedFrame* maybeParent) +{ + initReservedSlot(JSSLOT_PARENT, ObjectOrNullValue(maybeParent)); +} + void SavedFrame::initFromLookup(SavedFrame::HandleLookup lookup) { - MOZ_ASSERT(lookup->source); - MOZ_ASSERT(getReservedSlot(JSSLOT_SOURCE).isUndefined()); - setReservedSlot(JSSLOT_SOURCE, StringValue(lookup->source)); + initSource(lookup->source); + initLine(lookup->line); + initColumn(lookup->column); + initFunctionDisplayName(lookup->functionDisplayName); + initAsyncCause(lookup->asyncCause); + initParent(lookup->parent); + initPrincipals(lookup->principals); +} - setReservedSlot(JSSLOT_LINE, PrivateUint32Value(lookup->line)); - setReservedSlot(JSSLOT_COLUMN, PrivateUint32Value(lookup->column)); - setReservedSlot(JSSLOT_FUNCTIONDISPLAYNAME, - lookup->functionDisplayName - ? StringValue(lookup->functionDisplayName) - : NullValue()); - setReservedSlot(JSSLOT_ASYNCCAUSE, - lookup->asyncCause - ? StringValue(lookup->asyncCause) - : NullValue()); - setReservedSlot(JSSLOT_PARENT, ObjectOrNullValue(lookup->parent)); +/* static */ SavedFrame* +SavedFrame::create(JSContext* cx) +{ + RootedGlobalObject global(cx, cx->global()); + assertSameCompartment(cx, global); - MOZ_ASSERT(getReservedSlot(JSSLOT_PRINCIPALS).isUndefined()); - if (lookup->principals) - JS_HoldPrincipals(lookup->principals); - setReservedSlot(JSSLOT_PRINCIPALS, PrivateValue(lookup->principals)); + // Ensure that we don't try to capture the stack again in the + // `SavedStacksMetadataCallback` for this new SavedFrame object, and + // accidentally cause O(n^2) behavior. + SavedStacks::AutoReentrancyGuard guard(cx->compartment()->savedStacks()); + + RootedNativeObject proto(cx, GlobalObject::getOrCreateSavedFramePrototype(cx, global)); + if (!proto) + return nullptr; + assertSameCompartment(cx, proto); + + RootedObject frameObj(cx, NewObjectWithGivenProto(cx, &SavedFrame::class_, proto)); + if (!frameObj) + return nullptr; + + return &frameObj->as(); } bool @@ -1222,30 +1284,15 @@ SavedStacks::getOrCreateSavedFrame(JSContext* cx, SavedFrame::HandleLookup looku SavedFrame* SavedStacks::createFrameFromLookup(JSContext* cx, SavedFrame::HandleLookup lookup) { - RootedGlobalObject global(cx, cx->global()); - assertSameCompartment(cx, global); - - // Ensure that we don't try to capture the stack again in the - // `SavedStacksMetadataCallback` for this new SavedFrame object, and - // accidentally cause O(n^2) behavior. - SavedStacks::AutoReentrancyGuard guard(*this); - - RootedNativeObject proto(cx, GlobalObject::getOrCreateSavedFramePrototype(cx, global)); - if (!proto) + RootedSavedFrame frame(cx, SavedFrame::create(cx)); + if (!frame) return nullptr; - assertSameCompartment(cx, proto); + frame->initFromLookup(lookup); - RootedObject frameObj(cx, NewObjectWithGivenProto(cx, &SavedFrame::class_, proto)); - if (!frameObj) + if (!FreezeObject(cx, frame)) return nullptr; - RootedSavedFrame f(cx, &frameObj->as()); - f->initFromLookup(lookup); - - if (!FreezeObject(cx, frameObj)) - return nullptr; - - return f.get(); + return frame; } /* diff --git a/js/src/vm/SavedStacks.h b/js/src/vm/SavedStacks.h index 06fdcd26d36..11ef1026ace 100644 --- a/js/src/vm/SavedStacks.h +++ b/js/src/vm/SavedStacks.h @@ -148,6 +148,7 @@ namespace js { // principals. class SavedStacks { + friend class SavedFrame; friend JSObject* SavedStacksMetadataCallback(JSContext* cx, JSObject* target); friend bool JS::ubi::ConstructSavedFrameStackSlow(JSContext* cx, JS::ubi::StackFrame& ubiFrame, diff --git a/js/src/vm/StructuredClone.cpp b/js/src/vm/StructuredClone.cpp index 4bcb548bd42..24b156a55d6 100644 --- a/js/src/vm/StructuredClone.cpp +++ b/js/src/vm/StructuredClone.cpp @@ -42,6 +42,7 @@ #include "builtin/MapObject.h" #include "js/Date.h" #include "js/TraceableHashTable.h" +#include "vm/SavedFrame.h" #include "vm/SharedArrayObject.h" #include "vm/TypedArrayObject.h" #include "vm/WrapperObject.h" @@ -92,6 +93,12 @@ enum StructuredDataType : uint32_t { SCTAG_END_OF_KEYS, SCTAG_SHARED_TYPED_ARRAY_OBJECT, SCTAG_DATA_VIEW_OBJECT, + SCTAG_SAVED_FRAME_OBJECT, + + SCTAG_JSPRINCIPALS, + SCTAG_NULL_JSPRINCIPALS, + SCTAG_RECONSTRUCTED_SAVED_FRAME_PRINCIPALS_IS_SYSTEM, + SCTAG_RECONSTRUCTED_SAVED_FRAME_PRINCIPALS_IS_NOT_SYSTEM, SCTAG_TYPED_ARRAY_V1_MIN = 0xFFFF0100, SCTAG_TYPED_ARRAY_V1_INT8 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Int8, @@ -243,6 +250,7 @@ struct JSStructuredCloneReader { bool readSharedTypedArray(uint32_t arrayType, uint32_t nelems, MutableHandleValue vp); bool readArrayBuffer(uint32_t nbytes, MutableHandleValue vp); bool readV1ArrayBuffer(uint32_t arrayType, uint32_t nelems, MutableHandleValue vp); + JSObject* readSavedFrame(uint32_t principalsTag); bool startRead(MutableHandleValue vp); SCInput& in; @@ -305,6 +313,7 @@ struct JSStructuredCloneWriter { bool traverseObject(HandleObject obj); bool traverseMap(HandleObject obj); bool traverseSet(HandleObject obj); + bool traverseSavedFrame(HandleObject obj); bool parseTransferable(); bool reportErrorTransferable(uint32_t errorId); @@ -327,6 +336,7 @@ struct JSStructuredCloneWriter { // For JSObject: Property IDs as value // For Map: Key followed by value // For Set: Key + // For SavedFrame: parent SavedFrame AutoValueVector entries; // The "memory" list described in the HTML5 internal structured cloning algorithm. @@ -1056,6 +1066,84 @@ JSStructuredCloneWriter::traverseSet(HandleObject obj) // // Notice how the end-of-children marker for key1 is sandwiched between the // value1 beginning and end. +bool +JSStructuredCloneWriter::traverseSavedFrame(HandleObject obj) +{ + RootedObject unwrapped(context(), js::CheckedUnwrap(obj)); + MOZ_ASSERT(unwrapped && unwrapped->is()); + + RootedSavedFrame savedFrame(context(), &unwrapped->as()); + + RootedObject parent(context(), savedFrame->getParent()); + if (!context()->compartment()->wrap(context(), &parent)) + return false; + + if (!objs.append(ObjectValue(*obj)) || + !entries.append(parent ? ObjectValue(*parent) : NullValue()) || + !counts.append(1)) + { + return false; + } + + checkStack(); + + // Write the SavedFrame tag and the SavedFrame's principals. + + if (savedFrame->getPrincipals() == &ReconstructedSavedFramePrincipals::IsSystem) { + if (!out.writePair(SCTAG_SAVED_FRAME_OBJECT, + SCTAG_RECONSTRUCTED_SAVED_FRAME_PRINCIPALS_IS_SYSTEM)) + { + return false; + }; + } else if (savedFrame->getPrincipals() == &ReconstructedSavedFramePrincipals::IsNotSystem) { + if (!out.writePair(SCTAG_SAVED_FRAME_OBJECT, + SCTAG_RECONSTRUCTED_SAVED_FRAME_PRINCIPALS_IS_NOT_SYSTEM)) + { + return false; + } + } else { + if (auto principals = savedFrame->getPrincipals()) { + if (!out.writePair(SCTAG_SAVED_FRAME_OBJECT, SCTAG_JSPRINCIPALS) || + !principals->write(context(), this)) + { + return false; + } + } else { + if (!out.writePair(SCTAG_SAVED_FRAME_OBJECT, SCTAG_NULL_JSPRINCIPALS)) + return false; + } + } + + // Write the SavedFrame's reserved slots, except for the parent, which is + // queued on objs for further traversal. + + RootedValue val(context()); + + val = StringValue(savedFrame->getSource()); + if (!startWrite(val)) + return false; + + val = NumberValue(savedFrame->getLine()); + if (!startWrite(val)) + return false; + + val = NumberValue(savedFrame->getColumn()); + if (!startWrite(val)) + return false; + + auto name = savedFrame->getFunctionDisplayName(); + val = name ? StringValue(name) : NullValue(); + if (!startWrite(val)) + return false; + + auto cause = savedFrame->getAsyncCause(); + val = cause ? StringValue(cause) : NullValue(); + if (!startWrite(val)) + return false; + + return true; +} + bool JSStructuredCloneWriter::startWrite(HandleValue v) { @@ -1130,6 +1218,8 @@ JSStructuredCloneWriter::startWrite(HandleValue v) return traverseMap(obj); } else if (cls == ESClass_Set) { return traverseSet(obj); + } else if (SavedFrame::isSavedFrameOrWrapperAndNotProto(*obj)) { + return traverseSavedFrame(obj); } if (callbacks && callbacks->write) @@ -1287,7 +1377,7 @@ JSStructuredCloneWriter::write(HandleValue v) if (!startWrite(key) || !startWrite(val)) return false; - } else if (cls == ESClass_Set) { + } else if (cls == ESClass_Set || SavedFrame::isSavedFrameOrWrapperAndNotProto(*obj)) { if (!startWrite(key)) return false; } else { @@ -1796,6 +1886,14 @@ JSStructuredCloneReader::startRead(MutableHandleValue vp) break; } + case SCTAG_SAVED_FRAME_OBJECT: { + auto obj = readSavedFrame(data); + if (!obj || !objs.append(ObjectValue(*obj))) + return false; + vp.setObject(*obj); + break; + } + default: { if (tag <= SCTAG_FLOAT_MAX) { double d = ReinterpretPairAsDouble(tag, data); @@ -1913,6 +2011,83 @@ JSStructuredCloneReader::readTransferMap() return true; } +JSObject* +JSStructuredCloneReader::readSavedFrame(uint32_t principalsTag) +{ + RootedSavedFrame savedFrame(context(), SavedFrame::create(context())); + if (!savedFrame) + return nullptr; + + JSPrincipals* principals; + if (principalsTag == SCTAG_JSPRINCIPALS) { + if (!context()->runtime()->readPrincipals) { + JS_ReportErrorNumber(context(), GetErrorMessage, nullptr, + JSMSG_SC_UNSUPPORTED_TYPE); + return nullptr; + } + + if (!context()->runtime()->readPrincipals(context(), this, &principals)) + return nullptr; + } else if (principalsTag == SCTAG_RECONSTRUCTED_SAVED_FRAME_PRINCIPALS_IS_SYSTEM) { + principals = &ReconstructedSavedFramePrincipals::IsSystem; + principals->refcount++; + } else if (principalsTag == SCTAG_RECONSTRUCTED_SAVED_FRAME_PRINCIPALS_IS_NOT_SYSTEM) { + principals = &ReconstructedSavedFramePrincipals::IsNotSystem; + principals->refcount++; + } else if (principalsTag == SCTAG_NULL_JSPRINCIPALS) { + principals = nullptr; + } else { + JS_ReportErrorNumber(context(), GetErrorMessage, nullptr, + JSMSG_SC_BAD_SERIALIZED_DATA, "bad SavedFrame principals"); + return nullptr; + } + savedFrame->initPrincipalsAlreadyHeld(principals); + + RootedValue source(context()); + if (!startRead(&source) || !source.isString()) + return nullptr; + auto atomSource = AtomizeString(context(), source.toString()); + if (!atomSource) + return nullptr; + savedFrame->initSource(atomSource); + + RootedValue lineVal(context()); + uint32_t line; + if (!startRead(&lineVal) || !lineVal.isNumber() || !ToUint32(context(), lineVal, &line)) + return nullptr; + savedFrame->initLine(line); + + RootedValue columnVal(context()); + uint32_t column; + if (!startRead(&columnVal) || !columnVal.isNumber() || !ToUint32(context(), columnVal, &column)) + return nullptr; + savedFrame->initColumn(column); + + RootedValue name(context()); + if (!startRead(&name) || !(name.isString() || name.isNull())) + return nullptr; + JSAtom* atomName = nullptr; + if (name.isString()) { + atomName = AtomizeString(context(), name.toString()); + if (!atomName) + return nullptr; + } + savedFrame->initFunctionDisplayName(atomName); + + RootedValue cause(context()); + if (!startRead(&cause) || !(cause.isString() || cause.isNull())) + return nullptr; + JSAtom* atomCause = nullptr; + if (cause.isString()) { + atomCause = AtomizeString(context(), cause.toString()); + if (!atomCause) + return nullptr; + } + savedFrame->initAsyncCause(atomCause); + + return savedFrame; +} + // Perform the whole recursive reading procedure. bool JSStructuredCloneReader::read(MutableHandleValue vp) @@ -1961,7 +2136,9 @@ JSStructuredCloneReader::read(MutableHandleValue vp) if (!startRead(&key)) return false; - if (key.isNull() && !(obj->is() || obj->is())) { + if (key.isNull() && + !(obj->is() || obj->is() || obj->is())) + { // Backwards compatibility: Null formerly indicated the end of // object properties. objs.popBack(); @@ -1976,6 +2153,21 @@ JSStructuredCloneReader::read(MutableHandleValue vp) continue; } + // SavedFrame object: there is one following value, the parent + // SavedFrame, which is either null or another SavedFrame object. + if (obj->is()) { + SavedFrame* parentFrame; + if (key.isNull()) + parentFrame = nullptr; + else if (key.isObject() && key.toObject().is()) + parentFrame = &key.toObject().as(); + else + return false; + + obj->as().initParent(parentFrame); + continue; + } + // Everything else uses a series of key,value,key,value,... Value // objects. RootedValue val(context());