Bug 987111 - Tests. r=gabor
authorBobby Holley <bobbyholley@gmail.com>
Thu, 05 Jun 2014 22:32:39 -0700
changeset 206252 8501686e4923011ff02562e442051d9f36c0e214
parent 206251 9592f7abff55fdf582292cdc38991849d6c8d942
child 206253 63eb4a7d5a3c2ed0bd7254c89485d8d59e647637
push id3741
push userasasaki@mozilla.com
push dateMon, 21 Jul 2014 20:25:18 +0000
treeherdermozilla-beta@4d6f46f5af68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgabor
bugs987111
milestone32.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 987111 - Tests. r=gabor
js/xpconnect/tests/chrome/test_xrayToJS.xul
--- a/js/xpconnect/tests/chrome/test_xrayToJS.xul
+++ b/js/xpconnect/tests/chrome/test_xrayToJS.xul
@@ -18,16 +18,26 @@ https://bugzilla.mozilla.org/show_bug.cg
   <script type="application/javascript">
   <![CDATA[
 
   /** Test for ES constructors on Xrayed globals. **/
   SimpleTest.waitForExplicitFinish();
   const Cu = Components.utils;
   let global = Cu.getGlobalForObject.bind(Cu);
 
+  function checkThrows(f, rgxp, msg) {
+    try {
+      f();
+      ok(false, "Should have thrown: " + msg);
+    } catch (e) {
+      ok(true, "Threw as expected: " + msg);
+      ok(rgxp.test(e), "Message correct: " + e);
+    }
+  }
+
   simpleConstructors = ['Object', 'Function', 'Array', 'Boolean', 'Date', 'Number',
                         'String', 'RegExp', 'Error', 'InternalError', 'EvalError',
                         'RangeError', 'ReferenceError', 'SyntaxError', 'TypeError',
                         'URIError', 'ArrayBuffer', 'Int8Array', 'Uint8Array',
                         'Int16Array', 'Uint16Array', 'Int32Array', 'Uint32Array',
                         'Float32Array', 'Float64Array', 'Uint8ClampedArray',
                         'WeakMap', 'Map', 'Set'];
 
@@ -98,16 +108,18 @@ https://bugzilla.mozilla.org/show_bug.cg
     // Test eval.
     var toEval = "({a: 2, b: {foo: 'bar'}, f: function() { return window; }})";
     is(global(iwin.eval(toEval)), iwin, "eval creates objects in the correct global");
     is(iwin.eval(toEval).b.foo, 'bar', "eval-ed object looks right");
     is(Cu.waiveXrays(iwin.eval(toEval)).f(), Cu.waiveXrays(iwin), "evaled function works right");
 
     testDate();
 
+    testObject();
+
     // We could also test DataView and Iterator here for completeness, but it's
     // more trouble than it's worth.
 
 
     SimpleTest.finish();
   }
 
   // Maintain a static list of the properties that are available on each standard
@@ -123,16 +135,21 @@ https://bugzilla.mozilla.org/show_bug.cg
     "getUTCSeconds", "getMilliseconds", "getUTCMilliseconds", "setTime",
     "setYear", "setFullYear", "setUTCFullYear", "setMonth", "setUTCMonth",
     "setDate", "setUTCDate", "setHours", "setUTCHours", "setMinutes",
     "setUTCMinutes", "setSeconds", "setUTCSeconds", "setMilliseconds",
     "setUTCMilliseconds", "toUTCString", "toLocaleFormat", "toLocaleString",
     "toLocaleDateString", "toLocaleTimeString", "toDateString", "toTimeString",
     "toISOString", "toJSON", "toSource", "toString", "valueOf", "constructor",
     "toGMTString"];
+  gPrototypeProperties['Object'] = /* __proto__ is intentionally excluded here, because
+                                      the JS engine filters it out of getOwnPropertyNames */
+    ["constructor", "toSource", "toString", "toLocaleString", "valueOf", "watch",
+     "unwatch", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable",
+     "__defineGetter__", "__defineSetter__", "__lookupGetter__", "__lookupSetter__"];
 
   function filterOut(array, props) {
     return array.filter(p => props.indexOf(p) == -1);
   }
 
   function testXray(classname, xray, xray2, propsToSkip) {
     propsToSkip = propsToSkip || [];
     let xrayProto = Object.getPrototypeOf(xray);
@@ -143,17 +160,18 @@ https://bugzilla.mozilla.org/show_bug.cg
        " prototype has been added! You need a security audit from an XPConnect peer");
 
     let protoProps = filterOut(Object.getOwnPropertyNames(localProto), propsToSkip).sort();
     let protoMethods = protoProps.filter(name => typeof localProto[name] == 'function' &&
                                          name != 'constructor');
     ok(protoMethods.length > 0, "Need something to test");
     is(xrayProto, iwin[classname].prototype, "Xray proto is correct");
     is(xrayProto, xray.__proto__, "Proto accessors agree");
-    is(Object.getPrototypeOf(xrayProto), iwin.Object.prototype, "proto proto is correct");
+    var protoProto = classname == "Object" ? null : iwin.Object.prototype;
+    is(Object.getPrototypeOf(xrayProto), protoProto, "proto proto is correct");
     for (let name of protoMethods) {
       info("Running tests for property: " + name);
       ok(xrayProto.hasOwnProperty(name), "proto should have the property as own");
       ok(!xray.hasOwnProperty(name), "instance should not have the property as own");
       let method = xrayProto[name];
       is(typeof method, 'function', "Methods from Xrays are functions");
       is(global(method), window, "Methods from Xrays are local");
       ok(method instanceof Function, "instanceof works on methods from Xrays");
@@ -188,12 +206,97 @@ https://bugzilla.mozilla.org/show_bug.cg
     // Test the self-hosted toLocaleString.
     var d = new iwin.Date();
     isnot(d.toLocaleString, Cu.unwaiveXrays(d.wrappedJSObject.toLocaleString), "Different function identities");
     is(Cu.getGlobalForObject(d.toLocaleString), window, "Xray global is correct");
     is(Cu.getGlobalForObject(d.wrappedJSObject.toLocaleString), iwin, "Underlying global is correct");
     is(d.toLocaleString('de-DE'), d.wrappedJSObject.toLocaleString('de-DE'), "Results match");
   }
 
+  function testObject() {
+    testXray('Object', iwin.Object.create(new iwin.Object()), new iwin.Object(), []);
+
+    // Construct an object full of tricky things.
+    var trickyObject =
+      iwin.eval('new Object({ \
+                   primitiveProp: 42, objectProp: { foo: 2 }, \
+                   xoProp: top.location, hasOwnProperty: 10, \
+                   get getterProp() { return 2; }, \
+                   set setterProp(x) { }, \
+                   get getterSetterProp() { return 3; }, \
+                   set getterSetterProp(x) { }, \
+                   callableProp: function() { }, \
+                   nonXrayableProp: new WeakMap() })');
+
+    // Make sure it looks right under the hood.
+    is(trickyObject.wrappedJSObject.getterProp, 2, "Underlying object has getter");
+    is(Cu.unwaiveXrays(trickyObject.wrappedJSObject.xoProp), top.location, "Underlying object has xo property");
+
+    // Test getOwnPropertyNames.
+    is(Object.getOwnPropertyNames(trickyObject).sort().toSource(),
+       ['objectProp', 'primitiveProp'].toSource(), "getOwnPropertyNames should be filtered correctly");
+
+    // Test iteration and in-place modification. Beware of 'expando', which is the property
+    // we placed on the xray proto.
+    var propCount = 0;
+    for (let prop in trickyObject) {
+      if (prop == 'primitiveProp')
+        trickyObject[prop] = trickyObject[prop] - 10;
+      if (prop != 'expando')
+        trickyObject[prop] = trickyObject[prop];
+      ++propCount;
+    }
+    is(propCount, 3, "Should iterate the correct number of times");
+
+    // Test Object.keys.
+    is(Object.keys(trickyObject).sort().toSource(),
+       ['objectProp', 'primitiveProp'].toSource(), "Object.keys should be filtered correctly");
+
+    // Test getOwnPropertyDescriptor.
+    is(trickyObject.primitiveProp, 32, "primitive prop works");
+    is(trickyObject.objectProp.foo, 2, "object prop works");
+    is(typeof trickyObject.callableProp, 'undefined', "filtering works correctly");
+    is(Object.getOwnPropertyDescriptor(trickyObject, 'primitiveProp').value, 32, "getOwnPropertyDescriptor works");
+    is(Object.getOwnPropertyDescriptor(trickyObject, 'xoProp'), undefined, "filtering works with getOwnPropertyDescriptor");
+
+    // Test defineProperty.
+
+    trickyObject.primitiveSetByXray = 'fourty two';
+    is(trickyObject.primitiveSetByXray, 'fourty two', "Can set primitive correctly over Xray (ready via Xray)");
+    is(trickyObject.wrappedJSObject.primitiveSetByXray, 'fourty two', "Can set primitive correctly over Xray (ready via Waiver)");
+
+    var newContentObject = iwin.eval('new Object({prop: 99, get getterProp() { return 2; }})');
+    trickyObject.objectSetByXray = newContentObject;
+    is(trickyObject.objectSetByXray.prop, 99, "Can set object correctly over Xray (ready via Xray)");
+    is(trickyObject.wrappedJSObject.objectSetByXray.prop, 99, "Can set object correctly over Xray (ready via Waiver)");
+    checkThrows(function() { trickyObject.rejectedProp = {foo: 33}}, /cross-origin object/,
+                "Should reject privileged object property definition");
+
+    // Test JSON.stringify.
+    var jsonStr = JSON.stringify(newContentObject);
+    ok(/prop/.test(jsonStr), "JSON stringification should work: " + jsonStr);
+
+    // Test deletion.
+    delete newContentObject.prop;
+    ok(!newContentObject.hasOwnProperty('prop'), "Deletion should work");
+    ok(!newContentObject.wrappedJSObject.hasOwnProperty('prop'), "Deletion should forward");
+    delete newContentObject.getterProp;
+    ok(newContentObject.wrappedJSObject.hasOwnProperty('getterProp'), "Deletion be no-op for filtered property");
+
+    // We should be able to overwrite an existing accessor prop and convert it
+    // to a value prop.
+    is(trickyObject.wrappedJSObject.getterSetterProp, 3, "Underlying object has getter");
+    is(trickyObject.getterSetterProp, undefined, "Filtering properly over Xray");
+    trickyObject.getterSetterProp = 'redefined';
+    is(trickyObject.getterSetterProp, 'redefined', "Redefinition works");
+    is(trickyObject.wrappedJSObject.getterSetterProp, 'redefined', "Redefinition forwards");
+
+    checkThrows(function() { trickyObject.hasOwnProperty = 33; }, /shadow/,
+                "Should reject shadowing of pre-existing inherited properties over Xrays");
+
+    checkThrows(function() { Object.defineProperty(trickyObject, 'rejectedProp', { get: function() {}}); },
+                /accessor property/, "Should reject accessor property definition");
+  }
+
   ]]>
   </script>
   <iframe id="ifr" onload="go();" src="http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html" />
 </window>