summaryrefslogtreecommitdiffstats
path: root/js/src/jsapi-tests/testStructuredClone.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jsapi-tests/testStructuredClone.cpp')
-rw-r--r--js/src/jsapi-tests/testStructuredClone.cpp232
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)