233 lines
7.4 KiB
C++
233 lines
7.4 KiB
C++
/* 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)
|