blob: 277d50cb19b7534308b42729465ff57cb2f963ca [file] [log] [blame]
/* 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(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::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)