Bug 1036214 - Tests. r=bz, a=sledru
☠☠ backed out by 947dd9a0f12b ☠ ☠
authorBobby Holley <bobbyholley@gmail.com>
Tue, 19 Aug 2014 18:12:15 -0700
changeset 217604 ea25dee8ad3fc5eec2349390779cc365197d1157
parent 217603 57cdea2c224d33d72cd3e2c5501f9cbf4db8dcb3
child 217605 2479de9ab541f7b13c9a3f86136e48680423481a
push id515
push userraliiev@mozilla.com
push dateMon, 06 Oct 2014 12:51:51 +0000
treeherdermozilla-release@267c7a481bef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz, sledru
bugs1036214, 923904
milestone33.0a2
Bug 1036214 - Tests. r=bz, a=sledru This whole test setup comes from bug 923904, so some of the things it's testing need to be rejiggered (like the checkGlobal stuff).
dom/bindings/test/TestInterfaceJS.js
dom/bindings/test/mochitest.ini
dom/bindings/test/test_bug1036214.html
dom/bindings/test/test_bug923904.html
dom/webidl/TestInterfaceJS.webidl
dom/webidl/TestInterfaceJSDictionaries.webidl
dom/webidl/moz.build
--- a/dom/bindings/test/TestInterfaceJS.js
+++ b/dom/bindings/test/TestInterfaceJS.js
@@ -4,48 +4,46 @@
 
 const Cu = Components.utils;
 const Ci = Components.interfaces;
 
 "use strict";
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
-var gGlobal = this;
-function checkGlobal(obj) {
-  if (Object(obj) === obj && Cu.getGlobalForObject(obj) != gGlobal) {
-    // This message may not make it to the caller in a useful form, so dump
-    // as well.
-    var msg = "TestInterfaceJS received an object from a different scope!";
-    dump(msg + "\n");
-    throw new Error(msg);
-  }
-}
-
 function TestInterfaceJS(anyArg, objectArg) {}
 
 TestInterfaceJS.prototype = {
   classID: Components.ID("{2ac4e026-cf25-47d5-b067-78d553c3cad8}"),
   contractID: "@mozilla.org/dom/test-interface-js;1",
   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
 
-  __init: function (anyArg, objectArg) {
+  __init: function (anyArg, objectArg, dictionaryArg) {
     this._anyAttr = undefined;
     this._objectAttr = null;
     this._anyArg = anyArg;
     this._objectArg = objectArg;
-    checkGlobal(anyArg);
-    checkGlobal(objectArg);
+    this._dictionaryArg = dictionaryArg;
   },
 
   get anyArg() { return this._anyArg; },
   get objectArg() { return this._objectArg; },
+  get dictionaryArg() { return this._dictionaryArg; },
   get anyAttr() { return this._anyAttr; },
-  set anyAttr(val) { checkGlobal(val); this._anyAttr = val; },
+  set anyAttr(val) { this._anyAttr = val; },
   get objectAttr() { return this._objectAttr; },
-  set objectAttr(val) { checkGlobal(val); this._objectAttr = val; },
-  pingPongAny: function(any) { checkGlobal(any); return any; },
-  pingPongObject: function(obj) { checkGlobal(obj); return obj; },
+  set objectAttr(val) { this._objectAttr = val; },
+  get dictionaryAttr() { return this._dictionaryAttr; },
+  set dictionaryAttr(val) { this._dictionaryAttr = val; },
+  pingPongAny: function(any) { return any; },
+  pingPongObject: function(obj) { return obj; },
+  pingPongObjectOrString: function(objectOrString) { return objectOrString; },
+  pingPongDictionary: function(dict) { return dict; },
+  pingPongDictionaryOrLong: function(dictOrLong) { return dictOrLong.anyMember || dictOrLong; },
+  pingPongMap: function(map) { return JSON.stringify(map); },
+  objectSequenceLength: function(seq) { return seq.length; },
+  anySequenceLength: function(seq) { return seq.length; },
+
 
   getCallerPrincipal: function() { return Cu.getWebIDLCallerPrincipal().origin; }
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TestInterfaceJS])
--- a/dom/bindings/test/mochitest.ini
+++ b/dom/bindings/test/mochitest.ini
@@ -11,20 +11,18 @@ support-files =
 [test_bug560072.html]
 [test_bug707564.html]
 [test_bug742191.html]
 [test_bug759621.html]
 [test_bug773326.html]
 [test_bug788369.html]
 [test_bug852846.html]
 [test_bug862092.html]
-# When bug 923904 lands, this test can be turned on, but only for debug builds
-# where we have our test component. So this should become skip-if = debug == false.
-[test_bug923904.html]
-skip-if = true
+[test_bug1036214.html]
+skip-if = debug == false
 [test_bug1041646.html]
 [test_barewordGetsWindow.html]
 [test_callback_default_thisval.html]
 [test_cloneAndImportNode.html]
 [test_defineProperty.html]
 [test_enums.html]
 [test_exceptionThrowing.html]
 [test_exception_messages.html]
new file mode 100644
--- /dev/null
+++ b/dom/bindings/test/test_bug1036214.html
@@ -0,0 +1,123 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1036214
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1036214</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript">
+
+  /** Test for subsumes-checking |any| and |object| for js-implemented WebIDL. **/
+  SimpleTest.waitForExplicitFinish();
+  var xoObjects = [];
+  function setup() {
+    xoObjects.push(window[0]);
+    xoObjects.push(window[0].location);
+    xoObjects.push(SpecialPowers.unwrap(SpecialPowers.wrap(window[0]).document));
+    xoObjects.push(SpecialPowers);
+    xoObjects.push(SpecialPowers.wrap);
+    SpecialPowers.pushPrefEnv({set: [['dom.expose_test_interfaces', true]]}, go);
+  }
+
+  function checkThrows(f, msg) {
+    try {
+      f();
+      ok(false, "Should have thrown: " + msg);
+    } catch (e) {
+      ok(true, "Threw correctly: " + msg);
+      ok(/denied|insecure/.test(e), "Threw security exception: " + e);
+    }
+  }
+
+  function go() {
+
+    //
+    // Test the basics of the test interface.
+    //
+
+    var any = { a: 11 };
+    var obj = { b: 22, c: "str" };
+    var obj2 = { foo: "baz" };
+    var myDict = { anyMember: 42, objectMember: { answer: 42 }, objectOrStringMember: { answer: "anobject" },
+                   anySequenceMember: [{}, 1, "thirdinsequence"],
+                   innerDictionary: { innerObject: { answer: "rabbithole" } } };
+    var t = new TestInterfaceJS(any, obj, myDict);
+    is(Object.getPrototypeOf(t), TestInterfaceJS.prototype, "Prototype setup works correctly");
+    is(t.anyArg, any, "anyArg is correct");
+    is(t.objectArg, obj, "objectArg is correct");
+    is(t.dictionaryArg.anyMember, 42, "dictionaryArg looks correct");
+    is(t.dictionaryArg.objectMember.answer, 42, "dictionaryArg looks correct");
+    t.anyAttr = 2;
+    is(t.anyAttr, 2, "ping-pong any attribute works");
+    t.objAttr = obj2;
+    is(t.objAttr, obj2, "ping-pong object attribute works");
+    t.dictionaryAttr = myDict;
+    is(t.dictionaryAttr.anyMember, 42, "ping-pong dictionary attribute works");
+    is(t.dictionaryAttr.objectMember.answer, 42, "ping-pong dictionary attribute works");
+
+    is(any, t.pingPongAny(any), "ping-pong works with any");
+    is(obj, t.pingPongObject(obj), "ping-pong works with obj");
+    is(obj, t.pingPongObjectOrString(obj), "ping-pong works with obj or string");
+    is("foo", t.pingPongObjectOrString("foo"), "ping-pong works with obj or string");
+    is(t.pingPongDictionary(myDict).anyMember, 42, "ping pong works with dict");
+    is(t.pingPongDictionary(myDict).objectMember.answer, 42, "ping pong works with dict");
+    is(t.pingPongDictionary(myDict).objectOrStringMember.answer, "anobject", "ping pong works with dict");
+    is(t.pingPongDictionary(myDict).anySequenceMember[2], "thirdinsequence", "ping pong works with dict");
+    is(t.pingPongDictionary(myDict).innerDictionary.innerObject.answer, "rabbithole", "ping pong works with layered dicts");
+    is(t.pingPongDictionaryOrLong({anyMember: 42}), 42, "ping pong (dict or long) works with dict");
+    is(t.pingPongDictionaryOrLong(42), 42, "ping pong (dict or long) works with long");
+    ok(/canary/.test(t.pingPongMap({ someVal: 42, someOtherVal: "canary" })), "ping pong works with mozmap");
+    is(t.objectSequenceLength([{}, {}, {}]), 3, "ping pong works with object sequence");
+    is(t.anySequenceLength([42, 'string', {}, undefined]), 4, "ping pong works with any sequence");
+
+    //
+    // Test that we throw in the cross-origin cases.
+    //
+
+    xoObjects.forEach(function(xoObj) {
+      var blank = new TestInterfaceJS();
+      checkThrows(() => new TestInterfaceJS(xoObj, undefined), "any param for constructor");
+      checkThrows(() => new TestInterfaceJS(undefined, xoObj), "obj param for constructor");
+      checkThrows(() => new TestInterfaceJS(undefined, undefined, { anyMember: xoObj }), "any dict param for constructor");
+      checkThrows(() => new TestInterfaceJS(undefined, undefined, { objectMember: xoObj }), "object dict param for constructor");
+      checkThrows(() => new TestInterfaceJS(undefined, undefined, { objectOrStringMember: xoObj }), "union dict param for constructor");
+      checkThrows(() => new TestInterfaceJS(undefined, undefined, { anySequenceMember: [0, xoObj, 'hi' ] }), "sequence dict param for constructor");
+      checkThrows(() => new TestInterfaceJS(undefined, undefined, { innerDictionary: { innerObject: xoObj } }), "inner dict param for constructor");
+      checkThrows(() => t.anyAttr = xoObj, "anyAttr");
+      checkThrows(() => t.objectAttr = xoObj, "objAttr");
+      checkThrows(() => t.dictionaryAttr = { anyMember: xoObj }, "dictionaryAttr any");
+      checkThrows(() => t.dictionaryAttr = { objectMember: xoObj }, "dictionaryAttr object");
+      checkThrows(() => t.pingPongAny(xoObj), "pingpong any");
+      checkThrows(() => t.pingPongObject(xoObj), "pingpong obj");
+      checkThrows(() => t.pingPongObjectOrString(xoObj), "pingpong union");
+      checkThrows(() => t.pingPongDictionary({ anyMember: xoObj }), "dictionary pingpong any");
+      checkThrows(() => t.pingPongDictionary({ objectMember: xoObj }), "dictionary pingpong object");
+      checkThrows(() => t.pingPongDictionary({ anyMember: xoObj, objectMember: xoObj }), "dictionary pingpong both");
+      checkThrows(() => t.pingPongDictionary({ objectOrStringMember: xoObj }), "dictionary pingpong objectorstring");
+      checkThrows(() => t.pingPongDictionaryOrLong({ objectMember: xoObj }), "unionable dictionary");
+      checkThrows(() => t.pingPongDictionaryOrLong({ anyMember: xoObj }), "unionable dictionary");
+      checkThrows(() => t.pingPongMap({ someMember: 42, someOtherMember: {}, crossOriginMember: xoObj }), "mozmap");
+      checkThrows(() => t.objectSequenceLength([{}, {}, xoObj, {}]), "object sequence");
+      checkThrows(() => t.anySequenceLength([42, 'someString', xoObj, {}]), "any sequence");
+    });
+
+
+    SimpleTest.finish();
+  }
+
+  </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1036214">Mozilla Bug 1036214</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+<iframe id="ifr" onload="setup();" src="http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html"></iframe>
+</body>
+</html>
deleted file mode 100644
--- a/dom/bindings/test/test_bug923904.html
+++ /dev/null
@@ -1,66 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=923904
--->
-<head>
-  <meta charset="utf-8">
-  <title>Test for Bug 923904</title>
-  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-  <script type="application/javascript">
-
-  /** Test for cloning of |any| and |object| for JS-Implemented WebIDL. **/
-  SimpleTest.waitForExplicitFinish();
-  SpecialPowers.pushPrefEnv({set: [['dom.expose_test_interfaces', true]]}, go);
-
-  function go() {
-    var someAny = { a: 11 };
-    var someObj = { b: 22, c: "str" };
-    var t = new TestInterfaceJS(someAny, someObj);
-    is(Object.getPrototypeOf(t), TestInterfaceJS.prototype, "Prototype setup works correctly");
-    is(t.anyArg.toSource(), someAny.toSource(), "anyArg comes back looking like what we sent");
-    is(t.objectArg.toSource(), someObj.toSource(), "objectArg comes back looking like what we sent");
-    isnot(t.anyArg, t.anyArg, "get a new anyArg each time");
-    isnot(t.objectArg, t.objectArg, "get a new objectArg each time");
-
-    t.anyAttr = 2;
-    is(t.anyAttr, 2, "ping-pong works");
-    testObjectCloned(t, 'anyAttr');
-    testObjectCloned(t, 'objectAttr');
-
-    is(someAny.toSource(), t.pingPongAny(someAny).toSource(), "ping-pong works with any");
-    is(someObj.toSource(), t.pingPongObject(someObj).toSource(), "ping-pong works with obj");
-    isnot(someAny, t.pingPongAny(someAny), "Clone works for ping-pong any");
-    isnot(someObj, t.pingPongObject(someObj), "Clone works for ping-pong obj");
-
-    SimpleTest.finish();
-  }
-
-  function testObjectCloned(iface, propname) {
-    var obj = { prop: 42 };
-    iface[propname] = obj;
-    is(iface[propname].prop, 42, "objects come back as well");
-    is(iface[propname].__proto__, Object.prototype, "vanilla object");
-    isnot(iface[propname], obj, "Should not be the original object");
-    isnot(iface[propname], iface[propname], "Should get cloned each time");
-    try {
-      iface[propname] = { stringProp: "hi", reflectorProp: document };
-      ok(false, "Should throw when trying to clone reflector");
-    } catch (e) {
-      ok(/cloned/.test(e), "Should throw clone error: " + e);
-    }
-  }
-
-  </script>
-</head>
-<body>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=923904">Mozilla Bug 923904</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-
-</div>
-<pre id="test">
-</pre>
-</body>
-</html>
--- a/dom/webidl/TestInterfaceJS.webidl
+++ b/dom/webidl/TestInterfaceJS.webidl
@@ -1,20 +1,33 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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/.
  */
 
+dictionary TestInterfaceJSUnionableDictionary {
+  object objectMember;
+  any anyMember;
+};
+
 [JSImplementation="@mozilla.org/dom/test-interface-js;1",
  Pref="dom.expose_test_interfaces",
- Constructor(optional any anyArg, optional object objectArg)]
+ Constructor(optional any anyArg, optional object objectArg, optional TestInterfaceJSDictionary dictionaryArg)]
 interface TestInterfaceJS {
   readonly attribute any anyArg;
   readonly attribute object objectArg;
+  [Cached, Pure] readonly attribute TestInterfaceJSDictionary dictionaryArg;
   attribute any anyAttr;
   attribute object objectAttr;
+  [Cached, Pure] attribute TestInterfaceJSDictionary dictionaryAttr;
   any pingPongAny(any arg);
-  object pingPongObject(any obj);
+  object pingPongObject(object obj);
+  any pingPongObjectOrString((object or DOMString) objOrString);
+  TestInterfaceJSDictionary pingPongDictionary(optional TestInterfaceJSDictionary dict);
+  long pingPongDictionaryOrLong(optional (TestInterfaceJSUnionableDictionary or long) dictOrLong);
+  DOMString pingPongMap(MozMap<any> map);
+  long objectSequenceLength(sequence<object> seq);
+  long anySequenceLength(sequence<any> seq);
 
   // For testing bug 968335.
   DOMString getCallerPrincipal();
 };
new file mode 100644
--- /dev/null
+++ b/dom/webidl/TestInterfaceJSDictionaries.webidl
@@ -0,0 +1,27 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ */
+
+//
+// These dictionaries are in a separate WebIDL file to avoid circular include
+// problems. One of the dictionary includes a union as a member, so that
+// dictionary's header needs to include UnionTypes.h. But the API in
+// TestInterfaceJS also declares a union of dictionaries, so _that_
+// dictionary's header needs to be included _by_ UnionTypes.h. The solution
+// is to separate those two dictionaries into separate header files.
+//
+
+dictionary TestInterfaceJSDictionary2 {
+  object innerObject;
+};
+
+dictionary TestInterfaceJSDictionary {
+  TestInterfaceJSDictionary2 innerDictionary;
+  object objectMember;
+  any anyMember;
+  (object or DOMString) objectOrStringMember;
+  sequence<any> anySequenceMember;
+};
+
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -555,17 +555,17 @@ WEBIDL_FILES += [
     'StyleRuleChangeEvent.webidl',
     'StyleSheetApplicableStateChangeEvent.webidl',
     'StyleSheetChangeEvent.webidl',
 ]
 
 # We only expose our prefable test interfaces in debug builds, just to be on
 # the safe side.
 if CONFIG['MOZ_DEBUG']:
-    WEBIDL_FILES += ['TestInterfaceJS.webidl']
+    WEBIDL_FILES += ['TestInterfaceJS.webidl', 'TestInterfaceJSDictionaries.webidl']
 
 if CONFIG['MOZ_B2G_BT']:
     if CONFIG['MOZ_B2G_BT_API_V2']:
         WEBIDL_FILES += [
             'BluetoothAdapter2.webidl',
             'BluetoothClassOfDevice.webidl',
             'BluetoothDevice2.webidl',
             'BluetoothDiscoveryHandle.webidl',