Bug 909672 - Make cross-compartment cloning possible with JS_StructuredClone. r=jorendorff
authorMike Hordecki <mhordecki@mozilla.com>
Wed, 04 Sep 2013 13:20:51 -0400
changeset 158383 86114140b7973b8032684daf778efb7e3f32125f
parent 158382 02b05f0a72278ab527d5b12ee7a76ad2218ae1b0
child 158384 12da6e8f148d376da26f338b231d5e02eb10e124
push id2961
push userlsblakk@mozilla.com
push dateMon, 28 Oct 2013 21:59:28 +0000
treeherdermozilla-beta@73ef4f13486f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs909672
milestone26.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 909672 - Make cross-compartment cloning possible with JS_StructuredClone. r=jorendorff
js/src/jsapi-tests/moz.build
js/src/jsapi-tests/testStructuredClone.cpp
js/src/vm/StructuredClone.cpp
--- a/js/src/jsapi-tests/moz.build
+++ b/js/src/jsapi-tests/moz.build
@@ -55,15 +55,16 @@ CPP_SOURCES += [
     'testRegExp.cpp',
     'testResolveRecursion.cpp',
     'testSameValue.cpp',
     'testScriptInfo.cpp',
     'testScriptObject.cpp',
     'testSetProperty.cpp',
     'testSourcePolicy.cpp',
     'testStringBuffer.cpp',
+    'testStructuredClone.cpp',
     'testToIntWidth.cpp',
     'testTrap.cpp',
     'testTypedArrays.cpp',
     'testUTF8.cpp',
     'testXDR.cpp',
     'tests.cpp',
 ]
new file mode 100644
--- /dev/null
+++ b/js/src/jsapi-tests/testStructuredClone.cpp
@@ -0,0 +1,79 @@
+/* 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 "jsfriendapi.h"
+#include "js/StructuredClone.h"
+
+#include "jsapi-tests/tests.h"
+
+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));
+
+        v1 = JS::ObjectOrNullValue(JS_NewObject(cx, NULL, NULL, NULL));
+        CHECK(v1.isObject());
+        CHECK(JS_SetProperty(cx, &v1.toObject(), "prop", prop));
+    }
+
+    {
+        JSAutoCompartment ac(cx, g2);
+        JS::RootedValue v2(cx);
+
+        CHECK(JS_StructuredClone(cx, v1, v2.address(), NULL, NULL));
+        CHECK(v2.isObject());
+
+        JS::RootedValue prop(cx);
+        CHECK(JS_GetProperty(cx, &v2.toObject(), "prop", &prop));
+        CHECK(prop.isInt32());
+        CHECK(&v1.toObject() != &v2.toObject());
+        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.address(), NULL, NULL));
+        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)
--- a/js/src/vm/StructuredClone.cpp
+++ b/js/src/vm/StructuredClone.cpp
@@ -1483,25 +1483,45 @@ JS_StructuredCloneHasTransferables(const
 JS_PUBLIC_API(bool)
 JS_StructuredClone(JSContext *cx, JS::Value valueArg, JS::Value *vp,
                    const JSStructuredCloneCallbacks *optionalCallbacks,
                    void *closure)
 {
     RootedValue value(cx, valueArg);
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
-    assertSameCompartment(cx, value);
+
+    // Strings are associated with zones, not compartments,
+    // so we copy the string by wrapping it.
+    if (value.isString()) {
+      RootedString strValue(cx, value.toString());
+      if (!cx->compartment()->wrap(cx, strValue.address())) {
+        return false;
+      }
+      *vp = JS::StringValue(strValue);
+      return true;
+    }
 
     const JSStructuredCloneCallbacks *callbacks =
         optionalCallbacks ?
         optionalCallbacks :
         cx->runtime()->structuredCloneCallbacks;
+
     JSAutoStructuredCloneBuffer buf;
-    return buf.write(cx, value, callbacks, closure) &&
-           buf.read(cx, vp, callbacks, closure);
+    {
+        mozilla::Maybe<AutoCompartment> ac;
+        if (value.isObject()) {
+            ac.construct(cx, &value.toObject());
+        }
+
+        if (!buf.write(cx, value, callbacks, closure))
+            return false;
+    }
+
+    return buf.read(cx, vp, callbacks, closure);
 }
 
 void
 JSAutoStructuredCloneBuffer::clear()
 {
     if (data_) {
         ClearStructuredClone(data_, nbytes_);
         data_ = NULL;