Bug 1852740: add tests for the `fetchpriority` attribute in Link headers. r=necko...
[gecko.git] / js / src / jsapi-tests / testStructuredClone.cpp
blob3a16175207117adbf56a1904891ea786aa161fd0
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "builtin/TestingFunctions.h"
6 #include "js/ArrayBuffer.h" // JS::{IsArrayBufferObject,GetArrayBufferLengthAndData,NewExternalArrayBuffer}
7 #include "js/GlobalObject.h" // JS_NewGlobalObject
8 #include "js/PropertyAndElement.h" // JS_GetProperty, JS_SetProperty
9 #include "js/StructuredClone.h"
11 #include "jsapi-tests/tests.h"
13 using namespace js;
15 #ifdef DEBUG
16 // Skip test, since it will abort with an assert in buf->Init(7).
17 #else
18 BEGIN_TEST(testStructuredClone_invalidLength) {
19 auto buf = js::MakeUnique<JSStructuredCloneData>(
20 JS::StructuredCloneScope::DifferentProcess);
21 CHECK(buf);
22 CHECK(buf->Init(7));
23 RootedValue clone(cx);
24 JS::CloneDataPolicy policy;
25 CHECK(!JS_ReadStructuredClone(cx, *buf, JS_STRUCTURED_CLONE_VERSION,
26 JS::StructuredCloneScope::DifferentProcess,
27 &clone, policy, nullptr, nullptr));
28 return true;
30 END_TEST(testStructuredClone_invalidLength)
31 #endif
33 BEGIN_TEST(testStructuredClone_object) {
34 JS::RootedObject g1(cx, createGlobal());
35 JS::RootedObject g2(cx, createGlobal());
36 CHECK(g1);
37 CHECK(g2);
39 JS::RootedValue v1(cx);
42 JSAutoRealm ar(cx, g1);
43 JS::RootedValue prop(cx, JS::Int32Value(1337));
45 JS::RootedObject obj(cx, JS_NewPlainObject(cx));
46 v1 = JS::ObjectOrNullValue(obj);
47 CHECK(v1.isObject());
48 CHECK(JS_SetProperty(cx, obj, "prop", prop));
52 JSAutoRealm ar(cx, g2);
53 JS::RootedValue v2(cx);
55 CHECK(JS_StructuredClone(cx, v1, &v2, nullptr, nullptr));
56 CHECK(v2.isObject());
57 JS::RootedObject obj(cx, &v2.toObject());
59 JS::RootedValue prop(cx);
60 CHECK(JS_GetProperty(cx, obj, "prop", &prop));
61 CHECK(prop.isInt32());
62 CHECK(&v1.toObject() != obj);
63 CHECK_EQUAL(prop.toInt32(), 1337);
66 return true;
68 END_TEST(testStructuredClone_object)
70 BEGIN_TEST(testStructuredClone_string) {
71 JS::RootedObject g1(cx, createGlobal());
72 JS::RootedObject g2(cx, createGlobal());
73 CHECK(g1);
74 CHECK(g2);
76 JS::RootedValue v1(cx);
79 JSAutoRealm ar(cx, g1);
80 JS::RootedValue prop(cx, JS::Int32Value(1337));
82 v1 = JS::StringValue(JS_NewStringCopyZ(cx, "Hello World!"));
83 CHECK(v1.isString());
84 CHECK(v1.toString());
88 JSAutoRealm ar(cx, g2);
89 JS::RootedValue v2(cx);
91 CHECK(JS_StructuredClone(cx, v1, &v2, nullptr, nullptr));
92 CHECK(v2.isString());
93 CHECK(v2.toString());
95 JS::RootedValue expected(
96 cx, JS::StringValue(JS_NewStringCopyZ(cx, "Hello World!")));
97 CHECK_SAME(v2, expected);
100 return true;
102 END_TEST(testStructuredClone_string)
104 BEGIN_TEST(testStructuredClone_externalArrayBuffer) {
105 ExternalData data("One two three four");
106 auto dataPointer = data.pointer();
107 JS::RootedObject g1(cx, createGlobal());
108 JS::RootedObject g2(cx, createGlobal());
109 CHECK(g1);
110 CHECK(g2);
112 JS::RootedValue v1(cx);
115 JSAutoRealm ar(cx, g1);
117 JS::RootedObject obj(
118 cx, JS::NewExternalArrayBuffer(cx, data.len(), std::move(dataPointer)));
119 CHECK(!data.wasFreed());
121 v1 = JS::ObjectOrNullValue(obj);
122 CHECK(v1.isObject());
126 JSAutoRealm ar(cx, g2);
127 JS::RootedValue v2(cx);
129 CHECK(JS_StructuredClone(cx, v1, &v2, nullptr, nullptr));
130 CHECK(v2.isObject());
132 JS::RootedObject obj(cx, &v2.toObject());
133 CHECK(&v1.toObject() != obj);
135 size_t len;
136 bool isShared;
137 uint8_t* clonedData;
138 JS::GetArrayBufferLengthAndData(obj, &len, &isShared, &clonedData);
140 // The contents of the two array buffers should be equal, but not the
141 // same pointer.
142 CHECK_EQUAL(len, data.len());
143 CHECK(clonedData != data.contents());
144 CHECK(strcmp(reinterpret_cast<char*>(clonedData), data.asString()) == 0);
145 CHECK(!data.wasFreed());
148 // GC the array buffer before data goes out of scope
149 v1.setNull();
150 JS_GC(cx);
151 JS_GC(cx); // Trigger another to wait for background finalization to end
153 CHECK(data.wasFreed());
155 return true;
157 END_TEST(testStructuredClone_externalArrayBuffer)
159 BEGIN_TEST(testStructuredClone_externalArrayBufferDifferentThreadOrProcess) {
160 CHECK(testStructuredCloneCopy(JS::StructuredCloneScope::SameProcess));
161 CHECK(testStructuredCloneCopy(JS::StructuredCloneScope::DifferentProcess));
162 return true;
165 bool testStructuredCloneCopy(JS::StructuredCloneScope scope) {
166 ExternalData data("One two three four");
167 auto dataPointer = data.pointer();
168 JS::RootedObject buffer(
169 cx, JS::NewExternalArrayBuffer(cx, data.len(), std::move(dataPointer)));
170 CHECK(buffer);
171 CHECK(!data.wasFreed());
173 JS::RootedValue v1(cx, JS::ObjectValue(*buffer));
174 JS::RootedValue v2(cx);
175 CHECK(clone(scope, v1, &v2));
176 JS::RootedObject bufferOut(cx, v2.toObjectOrNull());
177 CHECK(bufferOut);
178 CHECK(JS::IsArrayBufferObject(bufferOut));
180 size_t len;
181 bool isShared;
182 uint8_t* clonedData;
183 JS::GetArrayBufferLengthAndData(bufferOut, &len, &isShared, &clonedData);
185 // Cloning should copy the data, so the contents of the two array buffers
186 // should be equal, but not the same pointer.
187 CHECK_EQUAL(len, data.len());
188 CHECK(clonedData != data.contents());
189 CHECK(strcmp(reinterpret_cast<char*>(clonedData), data.asString()) == 0);
190 CHECK(!data.wasFreed());
192 buffer = nullptr;
193 bufferOut = nullptr;
194 v1.setNull();
195 v2.setNull();
196 JS_GC(cx);
197 JS_GC(cx);
198 CHECK(data.wasFreed());
200 return true;
203 bool clone(JS::StructuredCloneScope scope, JS::HandleValue v1,
204 JS::MutableHandleValue v2) {
205 JSAutoStructuredCloneBuffer clonedBuffer(scope, nullptr, nullptr);
206 CHECK(clonedBuffer.write(cx, v1));
207 CHECK(clonedBuffer.read(cx, v2));
208 return true;
210 END_TEST(testStructuredClone_externalArrayBufferDifferentThreadOrProcess)
212 struct StructuredCloneTestPrincipals final : public JSPrincipals {
213 uint32_t rank;
215 explicit StructuredCloneTestPrincipals(uint32_t rank, int32_t rc = 1)
216 : rank(rank) {
217 this->refcount = rc;
220 bool write(JSContext* cx, JSStructuredCloneWriter* writer) override {
221 return JS_WriteUint32Pair(writer, rank, 0);
224 bool isSystemOrAddonPrincipal() override { return true; }
226 static bool read(JSContext* cx, JSStructuredCloneReader* reader,
227 JSPrincipals** outPrincipals) {
228 uint32_t rank;
229 uint32_t unused;
230 if (!JS_ReadUint32Pair(reader, &rank, &unused)) {
231 return false;
234 *outPrincipals = new StructuredCloneTestPrincipals(rank);
235 return !!*outPrincipals;
238 static void destroy(JSPrincipals* p) {
239 auto p1 = static_cast<StructuredCloneTestPrincipals*>(p);
240 delete p1;
243 static uint32_t getRank(JSPrincipals* p) {
244 if (!p) {
245 return 0;
247 return static_cast<StructuredCloneTestPrincipals*>(p)->rank;
250 static bool subsumes(JSPrincipals* a, JSPrincipals* b) {
251 return getRank(a) > getRank(b);
254 static JSSecurityCallbacks securityCallbacks;
256 static StructuredCloneTestPrincipals testPrincipals;
259 JSSecurityCallbacks StructuredCloneTestPrincipals::securityCallbacks = {
260 nullptr, // contentSecurityPolicyAllows
261 subsumes};
263 BEGIN_TEST(testStructuredClone_SavedFrame) {
264 JS_SetSecurityCallbacks(cx,
265 &StructuredCloneTestPrincipals::securityCallbacks);
266 JS_InitDestroyPrincipalsCallback(cx, StructuredCloneTestPrincipals::destroy);
267 JS_InitReadPrincipalsCallback(cx, StructuredCloneTestPrincipals::read);
269 auto testPrincipals = new StructuredCloneTestPrincipals(42, 0);
270 CHECK(testPrincipals);
272 auto DONE = (JSPrincipals*)0xDEADBEEF;
274 struct {
275 const char* name;
276 JSPrincipals* principals;
277 } principalsToTest[] = {
278 {"IsSystem", &js::ReconstructedSavedFramePrincipals::IsSystem},
279 {"IsNotSystem", &js::ReconstructedSavedFramePrincipals::IsNotSystem},
280 {"testPrincipals", testPrincipals},
281 {"nullptr principals", nullptr},
282 {"DONE", DONE}};
284 const char* FILENAME = "filename.js";
286 for (auto* pp = principalsToTest; pp->principals != DONE; pp++) {
287 fprintf(stderr, "Testing with principals '%s'\n", pp->name);
289 JS::RealmOptions options;
290 JS::RootedObject g(cx,
291 JS_NewGlobalObject(cx, getGlobalClass(), pp->principals,
292 JS::FireOnNewGlobalHook, options));
293 CHECK(g);
294 JSAutoRealm ar(cx, g);
296 CHECK(js::DefineTestingFunctions(cx, g, false, false));
298 JS::RootedValue srcVal(cx);
299 CHECK(
300 evaluate("(function one() { \n" // 1
301 " return (function two() { \n" // 2
302 " return (function three() { \n" // 3
303 " return saveStack(); \n" // 4
304 " }()); \n" // 5
305 " }()); \n" // 6
306 "}()); \n", // 7
307 FILENAME, 1, &srcVal));
309 CHECK(srcVal.isObject());
310 JS::RootedObject srcObj(cx, &srcVal.toObject());
312 CHECK(srcObj->is<js::SavedFrame>());
313 JS::Rooted<js::SavedFrame*> srcFrame(cx, &srcObj->as<js::SavedFrame>());
315 CHECK(srcFrame->getPrincipals() == pp->principals);
317 JS::RootedValue destVal(cx);
318 CHECK(JS_StructuredClone(cx, srcVal, &destVal, nullptr, nullptr));
320 CHECK(destVal.isObject());
321 JS::RootedObject destObj(cx, &destVal.toObject());
323 CHECK(destObj->is<js::SavedFrame>());
324 JS::Handle<js::SavedFrame*> destFrame = destObj.as<js::SavedFrame>();
326 size_t framesCopied = 0;
327 for (JS::Handle<js::SavedFrame*> f :
328 js::SavedFrame::RootedRange(cx, destFrame)) {
329 framesCopied++;
331 CHECK(f != srcFrame);
333 if (pp->principals == testPrincipals) {
334 // We shouldn't get a pointer to the same
335 // StructuredCloneTestPrincipals instance since we should have
336 // serialized and then deserialized it into a new instance.
337 CHECK(f->getPrincipals() != pp->principals);
339 // But it should certainly have the same rank.
340 CHECK(StructuredCloneTestPrincipals::getRank(f->getPrincipals()) ==
341 StructuredCloneTestPrincipals::getRank(pp->principals));
342 } else {
343 // For our singleton principals, we should always get the same
344 // pointer back.
345 CHECK(js::ReconstructedSavedFramePrincipals::is(pp->principals) ||
346 pp->principals == nullptr);
347 CHECK(f->getPrincipals() == pp->principals);
350 CHECK(EqualStrings(f->getSource(), srcFrame->getSource()));
351 CHECK(f->getLine() == srcFrame->getLine());
352 CHECK(f->getColumn() == srcFrame->getColumn());
353 CHECK(EqualStrings(f->getFunctionDisplayName(),
354 srcFrame->getFunctionDisplayName()));
356 srcFrame = srcFrame->getParent();
359 // Four function frames + one global frame.
360 CHECK(framesCopied == 4);
363 return true;
365 END_TEST(testStructuredClone_SavedFrame)