diff options
Diffstat (limited to 'js/src/jsapi-tests/testStructuredClone.cpp')
-rw-r--r-- | js/src/jsapi-tests/testStructuredClone.cpp | 232 |
1 files changed, 232 insertions, 0 deletions
diff --git a/js/src/jsapi-tests/testStructuredClone.cpp b/js/src/jsapi-tests/testStructuredClone.cpp new file mode 100644 index 000000000..a01afccad --- /dev/null +++ b/js/src/jsapi-tests/testStructuredClone.cpp @@ -0,0 +1,232 @@ +/* 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/. */ + +#include "builtin/TestingFunctions.h" +#include "js/StructuredClone.h" + +#include "jsapi-tests/tests.h" + +using namespace js; + +BEGIN_TEST(testStructuredClone_object) +{ + JS::RootedObject g1(cx, createGlobal()); + JS::RootedObject g2(cx, createGlobal()); + CHECK(g1); + CHECK(g2); + + JS::RootedValue v1(cx); + + { + JSAutoCompartment ac(cx, g1); + JS::RootedValue prop(cx, JS::Int32Value(1337)); + + JS::RootedObject obj(cx, JS_NewPlainObject(cx)); + v1 = JS::ObjectOrNullValue(obj); + CHECK(v1.isObject()); + CHECK(JS_SetProperty(cx, obj, "prop", prop)); + } + + { + JSAutoCompartment ac(cx, g2); + JS::RootedValue v2(cx); + + CHECK(JS_StructuredClone(cx, v1, &v2, nullptr, nullptr)); + CHECK(v2.isObject()); + JS::RootedObject obj(cx, &v2.toObject()); + + JS::RootedValue prop(cx); + CHECK(JS_GetProperty(cx, obj, "prop", &prop)); + CHECK(prop.isInt32()); + CHECK(&v1.toObject() != obj); + CHECK_EQUAL(prop.toInt32(), 1337); + } + + return true; +} +END_TEST(testStructuredClone_object) + +BEGIN_TEST(testStructuredClone_string) +{ + JS::RootedObject g1(cx, createGlobal()); + JS::RootedObject g2(cx, createGlobal()); + CHECK(g1); + CHECK(g2); + + JS::RootedValue v1(cx); + + { + JSAutoCompartment ac(cx, g1); + JS::RootedValue prop(cx, JS::Int32Value(1337)); + + v1 = JS::StringValue(JS_NewStringCopyZ(cx, "Hello World!")); + CHECK(v1.isString()); + CHECK(v1.toString()); + } + + { + JSAutoCompartment ac(cx, g2); + JS::RootedValue v2(cx); + + CHECK(JS_StructuredClone(cx, v1, &v2, nullptr, nullptr)); + CHECK(v2.isString()); + CHECK(v2.toString()); + + JS::RootedValue expected(cx, JS::StringValue( + JS_NewStringCopyZ(cx, "Hello World!"))); + CHECK_SAME(v2, expected); + } + + 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<StructuredCloneTestPrincipals*>(p); + delete p1; + } + + static uint32_t getRank(JSPrincipals* p) { + if (!p) + return 0; + return static_cast<StructuredCloneTestPrincipals*>(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(cx, &StructuredCloneTestPrincipals::securityCallbacks); + JS_InitDestroyPrincipalsCallback(cx, StructuredCloneTestPrincipals::destroy); + JS_InitReadPrincipalsCallback(cx, 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::CompartmentOptions options; + JS::RootedObject g(cx, JS_NewGlobalObject(cx, getGlobalClass(), pp->principals, + JS::FireOnNewGlobalHook, options)); + 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::SavedFrame>()); + js::RootedSavedFrame srcFrame(cx, &srcObj->as<js::SavedFrame>()); + + 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<js::SavedFrame>()); + auto destFrame = &destObj->as<js::SavedFrame>(); + + 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) |