test_xrayToJS.xul 46 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943
  1. <?xml version="1.0"?>
  2. <?xml-stylesheet type="text/css" href="chrome://global/skin"?>
  3. <?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
  4. <!--
  5. https://bugzilla.mozilla.org/show_bug.cgi?id=933681
  6. -->
  7. <window title="Mozilla Bug 933681"
  8. xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
  9. <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
  10. <!-- test results are displayed in the html:body -->
  11. <body xmlns="http://www.w3.org/1999/xhtml">
  12. <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=933681"
  13. target="_blank">Mozilla Bug 933681</a>
  14. </body>
  15. <!-- test code goes here -->
  16. <script type="application/javascript">
  17. <![CDATA[
  18. /** Test for ES constructors on Xrayed globals. **/
  19. SimpleTest.waitForExplicitFinish();
  20. const Cc = Components.classes;
  21. const Ci = Components.interfaces;
  22. const Cu = Components.utils;
  23. let global = Cu.getGlobalForObject.bind(Cu);
  24. function checkThrows(f, rgxp, msg) {
  25. try {
  26. f();
  27. ok(false, "Should have thrown: " + msg);
  28. } catch (e) {
  29. ok(true, "Threw as expected: " + msg);
  30. ok(rgxp.test(e), "Message correct: " + e);
  31. }
  32. }
  33. typedArrayClasses = ['Uint8Array', 'Int8Array', 'Uint16Array', 'Int16Array',
  34. 'Uint32Array', 'Int32Array', 'Float32Array', 'Float64Array',
  35. 'Uint8ClampedArray'];
  36. errorObjectClasses = ['Error', 'InternalError', 'EvalError', 'RangeError', 'ReferenceError',
  37. 'SyntaxError', 'TypeError', 'URIError'];
  38. // A simpleConstructors entry can either be the name of a constructor as a
  39. // string, or an object containing the properties `name`, and `args`.
  40. // In the former case, the constructor is invoked without any args; in the
  41. // latter case, it is invoked with `args` as the arguments list.
  42. simpleConstructors = ['Object', 'Function', 'Array', 'Boolean', 'Date', 'Number',
  43. 'String', 'RegExp', 'ArrayBuffer', 'WeakMap', 'Map', 'Set',
  44. {name: 'Promise', args: [function(){}]}].concat(typedArrayClasses)
  45. .concat(errorObjectClasses);
  46. function go() {
  47. window.iwin = document.getElementById('ifr').contentWindow;
  48. // Test constructors that can be instantiated with zero arguments, or with
  49. // a fixed set of arguments provided using `...rest`.
  50. for (var c of simpleConstructors) {
  51. var args = [];
  52. if (typeof c === 'object') {
  53. args = c.args;
  54. c = c.name;
  55. }
  56. ok(iwin[c], "Constructors appear: " + c);
  57. is(iwin[c], Cu.unwaiveXrays(iwin.wrappedJSObject[c]),
  58. "we end up with the appropriate constructor: " + c);
  59. is(Cu.unwaiveXrays(Cu.waiveXrays(new iwin[c](...args)).constructor), iwin[c],
  60. "constructor property is set up right: " + c);
  61. let expectedProto = /Opaque/.test(new iwin[c](...args)) ? iwin['Object'].prototype
  62. : iwin[c].prototype;
  63. is(Object.getPrototypeOf(new iwin[c](...args)), expectedProto,
  64. "prototype is correct: " + c);
  65. is(global(new iwin[c](...args)), iwin, "Got the right global: " + c);
  66. }
  67. // Test Object in more detail.
  68. var num = new iwin.Object(4);
  69. is(Cu.waiveXrays(num).valueOf(), 4, "primitive object construction works");
  70. is(global(num), iwin, "correct global for num");
  71. var obj = new iwin.Object();
  72. obj.foo = 2;
  73. var withProto = Cu.unwaiveXrays(Cu.waiveXrays(iwin).Object.create(obj));
  74. is(global(withProto), iwin, "correct global for withProto");
  75. is(Cu.waiveXrays(withProto).foo, 2, "Inherits properly");
  76. // Test Function.
  77. var primitiveFun = new iwin.Function('return 2');
  78. is(global(primitiveFun), iwin, "function construction works");
  79. is(primitiveFun(), 2, "basic function works");
  80. var doSetFoo = new iwin.Function('arg', 'arg.foo = 2;');
  81. is(global(doSetFoo), iwin, "function with args works");
  82. try {
  83. doSetFoo(new Object());
  84. ok(false, "should have thrown while setting property on object");
  85. } catch (e) {
  86. ok(!!/denied/.test(e), "Threw correctly: " + e);
  87. }
  88. var factoryFun = new iwin.Function('return {foo: 32}');
  89. is(global(factoryFun), iwin, "proper global for factoryFun");
  90. is(factoryFun().foo, 32, "factoryFun invokable");
  91. is(global(factoryFun()), iwin, "minted objects live in the content scope");
  92. testXray('Function', factoryFun, new iwin.Function(), ['length', 'name']);
  93. var echoThis = new iwin.Function('return this;');
  94. echoThis.wrappedJSObject.bind = 42;
  95. var boundEchoThis = echoThis.bind(document);
  96. is(boundEchoThis(), document, "bind() works correctly over Xrays");
  97. is(global(boundEchoThis), window, "bound functions live in the caller's scope");
  98. ok(/return this/.test(echoThis.toSource()), 'toSource works: ' + echoThis.toSource());
  99. ok(/return this/.test(echoThis.toString()), 'toString works: ' + echoThis.toString());
  100. is(iwin.Function.prototype, Object.getPrototypeOf(echoThis), "Function.prototype works for standard classes");
  101. is(echoThis.prototype, undefined, "Function.prototype not visible for non standard constructors");
  102. iwin.eval('var foopyFunction = function namedFoopyFunction(a, b, c) {}');
  103. var foopyFunction = Cu.unwaiveXrays(Cu.waiveXrays(iwin).foopyFunction);
  104. ok(Cu.isXrayWrapper(foopyFunction), "Should be Xrays");
  105. is(foopyFunction.name, "namedFoopyFunction", ".name works over Xrays");
  106. is(foopyFunction.length, 3, ".length works over Xrays");
  107. ok(Object.getOwnPropertyNames(foopyFunction).indexOf('length') >= 0, "Should list length");
  108. ok(Object.getOwnPropertyNames(foopyFunction).indexOf('name') >= 0, "Should list name");
  109. ok(Object.getOwnPropertyNames(foopyFunction).indexOf('prototype') == -1, "Should not list prototype");
  110. ok(Object.getOwnPropertyNames(iwin.Array).indexOf('prototype') >= 0, "Should list prototype for standard constructor");
  111. // Test proxies.
  112. var targetObject = new iwin.Object();
  113. targetObject.foo = 9;
  114. var forwardingProxy = new iwin.Proxy(targetObject, new iwin.Object());
  115. is(global(forwardingProxy), iwin, "proxy global correct");
  116. is(Cu.waiveXrays(forwardingProxy).foo, 9, "forwards correctly");
  117. // Test eval.
  118. var toEval = "({a: 2, b: {foo: 'bar'}, f: function() { return window; }})";
  119. is(global(iwin.eval(toEval)), iwin, "eval creates objects in the correct global");
  120. is(iwin.eval(toEval).b.foo, 'bar', "eval-ed object looks right");
  121. is(Cu.waiveXrays(iwin.eval(toEval)).f(), Cu.waiveXrays(iwin), "evaled function works right");
  122. testDate();
  123. testObject();
  124. testArray();
  125. testTypedArrays();
  126. testErrorObjects();
  127. testRegExp();
  128. testPromise();
  129. testArrayBuffer();
  130. // We could also test DataView and Iterator here for completeness, but it's
  131. // more trouble than it's worth.
  132. SimpleTest.finish();
  133. }
  134. // Maintain a static list of the properties that are available on each standard
  135. // prototype, so that we make sure to audit any new ones to make sure they're
  136. // Xray-safe.
  137. //
  138. // DO NOT CHANGE WTIHOUT REVIEW FROM AN XPCONNECT PEER.
  139. var version = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo).version;
  140. var isNightlyBuild = version.endsWith("a1");
  141. var isReleaseOrBeta = !version.includes("a");
  142. var gPrototypeProperties = {};
  143. var gConstructorProperties = {};
  144. function constructorProps(arr) {
  145. // Some props live on all constructors
  146. return arr.concat(["prototype", "length", "name"]);
  147. }
  148. gPrototypeProperties['Date'] =
  149. ["getTime", "getTimezoneOffset", "getYear", "getFullYear", "getUTCFullYear",
  150. "getMonth", "getUTCMonth", "getDate", "getUTCDate", "getDay", "getUTCDay",
  151. "getHours", "getUTCHours", "getMinutes", "getUTCMinutes", "getSeconds",
  152. "getUTCSeconds", "getMilliseconds", "getUTCMilliseconds", "setTime",
  153. "setYear", "setFullYear", "setUTCFullYear", "setMonth", "setUTCMonth",
  154. "setDate", "setUTCDate", "setHours", "setUTCHours", "setMinutes",
  155. "setUTCMinutes", "setSeconds", "setUTCSeconds", "setMilliseconds",
  156. "setUTCMilliseconds", "toUTCString", "toLocaleFormat", "toLocaleString",
  157. "toLocaleDateString", "toLocaleTimeString", "toDateString", "toTimeString",
  158. "toISOString", "toJSON", "toSource", "toString", "valueOf", "constructor",
  159. "toGMTString", Symbol.toPrimitive];
  160. gConstructorProperties['Date'] = constructorProps(["UTC", "parse", "now"]);
  161. gPrototypeProperties['Object'] =
  162. ["constructor", "toSource", "toString", "toLocaleString", "valueOf",
  163. "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable",
  164. "__defineGetter__", "__defineSetter__", "__lookupGetter__", "__lookupSetter__",
  165. "__proto__"];
  166. gConstructorProperties['Object'] =
  167. constructorProps(["setPrototypeOf", "getOwnPropertyDescriptor", "getOwnPropertyDescriptors",
  168. "keys", "is", "defineProperty", "defineProperties", "create",
  169. "getOwnPropertyNames", "getOwnPropertySymbols",
  170. "preventExtensions", "freeze", "fromEntries", "isFrozen", "seal",
  171. "isSealed", "assign", "getPrototypeOf", "values",
  172. "entries", "isExtensible"]);
  173. gPrototypeProperties['Array'] =
  174. ["length", "toSource", "toString", "toLocaleString", "join", "reverse", "sort", "push",
  175. "pop", "shift", "unshift", "splice", "concat", "slice", "lastIndexOf", "indexOf",
  176. "includes", "forEach", "map", "reduce", "reduceRight", "filter", "some", "every", "find",
  177. "findIndex", "copyWithin", "fill", Symbol.iterator, Symbol.unscopables, "entries", "keys",
  178. "values", "constructor", "flat", "flatMap"];
  179. if (isNightlyBuild) {
  180. // ...nothing now
  181. }
  182. gConstructorProperties['Array'] =
  183. constructorProps(["join", "reverse", "sort", "push", "pop", "shift",
  184. "unshift", "splice", "concat", "slice", "isArray",
  185. "lastIndexOf", "indexOf", "forEach", "map", "filter",
  186. "every", "some", "reduce", "reduceRight", "from", "of",
  187. Symbol.species]);
  188. for (var c of typedArrayClasses) {
  189. gPrototypeProperties[c] = ["constructor", "BYTES_PER_ELEMENT"];
  190. gConstructorProperties[c] = constructorProps(["BYTES_PER_ELEMENT"]);
  191. }
  192. gPrototypeProperties['TypedArray'] =
  193. ["length", "buffer", "byteLength", "byteOffset", Symbol.iterator, "subarray",
  194. "set", "copyWithin", "find", "findIndex", "forEach","indexOf", "lastIndexOf", "includes",
  195. "reverse", "join", "every", "some", "reduce", "reduceRight", "entries", "keys", "values",
  196. "slice", "map", "filter"];
  197. // There is no TypedArray constructor, looks like.
  198. is(window.TypedArray, undefined, "If this ever changes, add to this test!");
  199. for (var c of errorObjectClasses) {
  200. gPrototypeProperties[c] = ["constructor", "name", "message", "stack"];
  201. gConstructorProperties[c] = constructorProps([]);
  202. }
  203. // toString and toSource only live on the parent proto (Error.prototype).
  204. gPrototypeProperties['Error'].push('toString');
  205. gPrototypeProperties['Error'].push('toSource');
  206. gPrototypeProperties['Function'] =
  207. ["constructor", "toSource", "toString", "apply", "call", "bind",
  208. "isGenerator", "length", "name", "arguments", "caller", Symbol.hasInstance];
  209. gConstructorProperties['Function'] = constructorProps([])
  210. gPrototypeProperties['RegExp'] =
  211. ["constructor", "toSource", "toString", "compile", "exec", "test",
  212. Symbol.match, Symbol.replace, Symbol.search, Symbol.split,
  213. "flags", "global", "ignoreCase", "multiline", "source", "sticky", "unicode"];
  214. gConstructorProperties['RegExp'] =
  215. constructorProps(["input", "lastMatch", "lastParen",
  216. "leftContext", "rightContext", "$1", "$2", "$3", "$4",
  217. "$5", "$6", "$7", "$8", "$9", "$_", "$&", "$+",
  218. "$`", "$'", Symbol.species])
  219. gPrototypeProperties['Promise'] =
  220. ["constructor", "catch", "then", "finally", Symbol.toStringTag];
  221. gConstructorProperties['Promise'] =
  222. constructorProps(["resolve", "reject", "all", "race", Symbol.species]);
  223. gPrototypeProperties['ArrayBuffer'] =
  224. ["constructor", "byteLength", "slice", Symbol.toStringTag];
  225. gConstructorProperties['ArrayBuffer'] =
  226. constructorProps(["isView", "slice", Symbol.species]);
  227. if (!isReleaseOrBeta) {
  228. gPrototypeProperties['SharedArrayBuffer'] = ["constructor", "slice", "byteLength", Symbol.toStringTag];
  229. gConstructorProperties['SharedArrayBuffer'] = constructorProps([Symbol.species]);
  230. } else {
  231. is(typeof SharedArrayBuffer, "undefined", "Enable tests!");
  232. }
  233. // Sort an array that may contain symbols as well as strings.
  234. function sortProperties(arr) {
  235. function sortKey(prop) {
  236. return typeof prop + ":" + prop.toString();
  237. }
  238. arr.sort((a, b) => sortKey(a) < sortKey(b) ? -1 : +1);
  239. }
  240. // Sort all the lists so we don't need to mutate them later (or copy them
  241. // again to sort them).
  242. for (var c of Object.keys(gPrototypeProperties))
  243. sortProperties(gPrototypeProperties[c]);
  244. for (var c of Object.keys(gConstructorProperties))
  245. sortProperties(gConstructorProperties[c]);
  246. function filterOut(array, props) {
  247. return array.filter(p => props.indexOf(p) == -1);
  248. }
  249. function isTypedArrayClass(classname) {
  250. return typedArrayClasses.indexOf(classname) >= 0;
  251. }
  252. function propertyIsGetter(obj, name, classname) {
  253. return !!Object.getOwnPropertyDescriptor(obj, name).get;
  254. }
  255. function testProtoCallables(protoCallables, xray, xrayProto, localProto) {
  256. for (let name of protoCallables) {
  257. info("Running tests for property: " + name);
  258. // Test both methods and getter properties.
  259. function lookupCallable(obj) {
  260. let desc = null;
  261. do {
  262. desc = Object.getOwnPropertyDescriptor(obj, name);
  263. obj = Object.getPrototypeOf(obj);
  264. } while (!desc);
  265. return desc.get || desc.value;
  266. };
  267. ok(xrayProto.hasOwnProperty(name), "proto should have the property as own");
  268. ok(!xray.hasOwnProperty(name), "instance should not have the property as own");
  269. let method = lookupCallable(xrayProto);
  270. is(typeof method, 'function', "Methods from Xrays are functions");
  271. is(global(method), window, "Methods from Xrays are local");
  272. ok(method instanceof Function, "instanceof works on methods from Xrays");
  273. is(lookupCallable(xrayProto), method, "Holder caching works properly");
  274. is(lookupCallable(xray), method, "Proto props resolve on the instance");
  275. let local = lookupCallable(localProto);
  276. is(method.length, local.length, "Function.length identical");
  277. if (method.length == 0) {
  278. is(method.call(xray) + "", local.call(xray) + "",
  279. "Xray and local method results stringify identically");
  280. // If invoking this method returns something non-Xrayable, the
  281. // stringification is going to return [object Opaque].
  282. ok(!/Opaque/.test(method.call(xray)), "Method result is xrayable");
  283. is(method.call(xray) + "",
  284. lookupCallable(xray.wrappedJSObject).call(xray.wrappedJSObject) + "",
  285. "Xray and waived method results stringify identically");
  286. }
  287. }
  288. }
  289. function testCtorCallables(ctorCallables, xrayCtor, localCtor) {
  290. for (let name of ctorCallables) {
  291. // Don't try to test Function.prototype, since that is in fact a callable
  292. // but doesn't really do the things we expect callables to do here
  293. // (e.g. it's in the wrong global, since it gets Xrayed itself).
  294. if (name == "prototype" && localCtor.name == "Function") {
  295. continue;
  296. }
  297. info(`Running tests for property: ${localCtor.name}.${name}`);
  298. // Test both methods and getter properties.
  299. function lookupCallable(obj) {
  300. let desc = null;
  301. do {
  302. desc = Object.getOwnPropertyDescriptor(obj, name);
  303. obj = Object.getPrototypeOf(obj);
  304. } while (!desc);
  305. return desc.get || desc.value;
  306. };
  307. ok(xrayCtor.hasOwnProperty(name), "ctor should have the property as own");
  308. let method = lookupCallable(xrayCtor);
  309. is(typeof method, 'function', "Methods from ctor Xrays are functions");
  310. is(global(method), window, "Methods from ctor Xrays are local");
  311. ok(method instanceof Function,
  312. "instanceof works on methods from ctor Xrays");
  313. is(lookupCallable(xrayCtor), method,
  314. "Holder caching works properly on ctors");
  315. let local = lookupCallable(localCtor);
  316. is(method.length, local.length,
  317. "Function.length identical for method from ctor");
  318. // Don't try to do the return-value check on Date.now(), since there is
  319. // absolutely no reason it should return the same value each time.
  320. //
  321. // Also don't try to do the return-value check on Regexp.lastMatch and
  322. // Regexp["$&"] (which are aliases), because they get state off the global
  323. // they live in, as far as I can tell, so testing them over Xrays will be
  324. // wrong: on the Xray they will actaully get the lastMatch of _our_
  325. // global, not the Xrayed one.
  326. if (method.length == 0 &&
  327. !(localCtor.name == "Date" && name == "now") &&
  328. !(localCtor.name == "RegExp" && (name == "lastMatch" || name == "$&"))) {
  329. is(method.call(xrayCtor) + "", local.call(xrayCtor) + "",
  330. "Xray and local method results stringify identically on constructors");
  331. is(method.call(xrayCtor) + "",
  332. lookupCallable(xrayCtor.wrappedJSObject).call(xrayCtor.wrappedJSObject) + "",
  333. "Xray and waived method results stringify identically");
  334. }
  335. }
  336. }
  337. function testXray(classname, xray, xray2, propsToSkip, ctorPropsToSkip = []) {
  338. propsToSkip = propsToSkip || [];
  339. let xrayProto = Object.getPrototypeOf(xray);
  340. let localProto = window[classname].prototype;
  341. let desiredProtoProps = Object.getOwnPropertyNames(localProto).sort();
  342. is(desiredProtoProps.toSource(),
  343. gPrototypeProperties[classname].filter(id => typeof id === "string").toSource(),
  344. "A property on the " + classname +
  345. " prototype has changed! You need a security audit from an XPConnect peer");
  346. is(Object.getOwnPropertySymbols(localProto).map(uneval).sort().toSource(),
  347. gPrototypeProperties[classname].filter(id => typeof id !== "string").map(uneval).sort().toSource(),
  348. "A symbol-keyed property on the " + classname +
  349. " prototype has been changed! You need a security audit from an XPConnect peer");
  350. let protoProps = filterOut(desiredProtoProps, propsToSkip);
  351. let protoCallables = protoProps.filter(name => propertyIsGetter(localProto, name, classname) ||
  352. typeof localProto[name] == 'function' &&
  353. name != 'constructor');
  354. ok(protoCallables.length > 0, "Need something to test");
  355. is(xrayProto, iwin[classname].prototype, "Xray proto is correct");
  356. is(xrayProto, xray.__proto__, "Proto accessors agree");
  357. var protoProto = classname == "Object" ? null : iwin.Object.prototype;
  358. is(Object.getPrototypeOf(xrayProto), protoProto, "proto proto is correct");
  359. testProtoCallables(protoCallables, xray, xrayProto, localProto);
  360. is(Object.getOwnPropertyNames(xrayProto).sort().toSource(),
  361. protoProps.toSource(), "getOwnPropertyNames works");
  362. is(Object.getOwnPropertySymbols(xrayProto).map(uneval).sort().toSource(),
  363. gPrototypeProperties[classname].filter(id => typeof id !== "string" && !propsToSkip.includes(id))
  364. .map(uneval).sort().toSource(),
  365. "getOwnPropertySymbols works");
  366. is(xrayProto.constructor, iwin[classname], "constructor property works");
  367. xrayProto.expando = 42;
  368. is(xray.expando, 42, "Xrayed instances see proto expandos");
  369. is(xray2.expando, 42, "Xrayed instances see proto expandos");
  370. // Now test constructors
  371. localCtor = window[classname];
  372. xrayCtor = xrayProto.constructor;
  373. // We already checked that this is the same as iwin[classname]
  374. let desiredCtorProps =
  375. Object.getOwnPropertyNames(localCtor).sort();
  376. is(desiredCtorProps.toSource(),
  377. gConstructorProperties[classname].filter(id => typeof id === "string").toSource(),
  378. "A property on the " + classname +
  379. " constructor has changed! You need a security audit from an XPConnect peer");
  380. let desiredCtorSymbols =
  381. Object.getOwnPropertySymbols(localCtor).map(uneval).sort()
  382. is(desiredCtorSymbols.toSource(),
  383. gConstructorProperties[classname].filter(id => typeof id !== "string").map(uneval).sort().toSource(),
  384. "A symbol-keyed property on the " + classname +
  385. " constructor has been changed! You need a security audit from an XPConnect peer");
  386. let ctorProps = filterOut(desiredCtorProps, ctorPropsToSkip);
  387. let ctorSymbols = filterOut(desiredCtorSymbols, ctorPropsToSkip.map(uneval));
  388. let ctorCallables = ctorProps.filter(name => propertyIsGetter(localCtor, name, classname) ||
  389. typeof localCtor[name] == 'function');
  390. testCtorCallables(ctorCallables, xrayCtor, localCtor);
  391. is(Object.getOwnPropertyNames(xrayCtor).sort().toSource(),
  392. ctorProps.toSource(), "getOwnPropertyNames works on Xrayed ctors");
  393. is(Object.getOwnPropertySymbols(xrayCtor).map(uneval).sort().toSource(),
  394. ctorSymbols.toSource(), "getOwnPropertySymbols works on Xrayed ctors");
  395. }
  396. // We will need arraysEqual and testArrayIterators both in this global scope
  397. // and in sandboxes, so define them as strings up front.
  398. var arraysEqualSource = `function arraysEqual(arr1, arr2, reason) {
  399. is(arr1.length, arr2.length, \`\${reason}; lengths should be equal\`)
  400. for (var i = 0; i < arr1.length; ++i) {
  401. if (Array.isArray(arr2[i])) {
  402. arraysEqual(arr1[i], arr2[i], \`\${reason}; item at index \${i}\`);
  403. } else {
  404. is(arr1[i], arr2[i], \`\${reason}; item at index \${i} should be equal\`);
  405. }
  406. }
  407. }`;
  408. eval(arraysEqualSource);
  409. var testArrayIteratorsSource = `
  410. function testArrayIterators(arrayLike, equivalentArray, reason) {
  411. arraysEqual([...arrayLike], equivalentArray, \`\${reason}; spread operator\`);
  412. arraysEqual([...arrayLike.entries()], [...equivalentArray.entries()],
  413. \`\${reason}; entries\`);
  414. arraysEqual([...arrayLike.keys()], [...equivalentArray.keys()],
  415. \`\${reason}; keys\`);
  416. if (arrayLike.values) {
  417. arraysEqual([...arrayLike.values()], equivalentArray,
  418. \`\${reason}; values\`);
  419. }
  420. var forEachCopy = [];
  421. arrayLike.forEach(function(arg) { forEachCopy.push(arg); });
  422. arraysEqual(forEachCopy, equivalentArray, \`\${reason}; forEach copy\`);
  423. var everyCopy = [];
  424. arrayLike.every(function(arg) { everyCopy.push(arg); return true; });
  425. arraysEqual(everyCopy, equivalentArray, \`\${reason}; every() copy\`);
  426. var filterCopy = [];
  427. var filterResult = arrayLike.filter(function(arg) {
  428. filterCopy.push(arg);
  429. return true;
  430. });
  431. arraysEqual(filterCopy, equivalentArray, \`\${reason}; filter copy\`);
  432. arraysEqual([...filterResult], equivalentArray, \`\${reason}; filter result\`);
  433. var findCopy = [];
  434. arrayLike.find(function(arg) { findCopy.push(arg); return false; });
  435. arraysEqual(findCopy, equivalentArray, \`\${reason}; find() copy\`);
  436. var findIndexCopy = [];
  437. arrayLike.findIndex(function(arg) { findIndexCopy.push(arg); return false; });
  438. arraysEqual(findIndexCopy, equivalentArray, \`\${reason}; findIndex() copy\`);
  439. var mapCopy = [];
  440. var mapResult = arrayLike.map(function(arg) { mapCopy.push(arg); return arg});
  441. arraysEqual(mapCopy, equivalentArray, \`\${reason}; map() copy\`);
  442. arraysEqual([...mapResult], equivalentArray, \`\${reason}; map() result\`);
  443. var reduceCopy = [];
  444. arrayLike.reduce(function(_, arg) { reduceCopy.push(arg); }, 0);
  445. arraysEqual(reduceCopy, equivalentArray, \`\${reason}; reduce() copy\`);
  446. var reduceRightCopy = [];
  447. arrayLike.reduceRight(function(_, arg) { reduceRightCopy.unshift(arg); }, 0);
  448. arraysEqual(reduceRightCopy, equivalentArray, \`\${reason}; reduceRight() copy\`);
  449. var someCopy = [];
  450. arrayLike.some(function(arg) { someCopy.push(arg); return false; });
  451. arraysEqual(someCopy, equivalentArray, \`\${reason}; some() copy\`);
  452. }`;
  453. eval(testArrayIteratorsSource);
  454. function testDate() {
  455. // toGMTString is handled oddly in the engine. We don't bother to support
  456. // it over Xrays.
  457. let propsToSkip = ['toGMTString'];
  458. testXray('Date', new iwin.Date(), new iwin.Date(), propsToSkip);
  459. // Test the self-hosted toLocaleString.
  460. var d = new iwin.Date();
  461. isnot(d.toLocaleString, Cu.unwaiveXrays(d.wrappedJSObject.toLocaleString), "Different function identities");
  462. is(Cu.getGlobalForObject(d.toLocaleString), window, "Xray global is correct");
  463. is(Cu.getGlobalForObject(d.wrappedJSObject.toLocaleString), iwin, "Underlying global is correct");
  464. is(d.toLocaleString('de-DE'), d.wrappedJSObject.toLocaleString('de-DE'), "Results match");
  465. }
  466. var uniqueSymbol;
  467. function testObject() {
  468. testXray('Object', Cu.unwaiveXrays(Cu.waiveXrays(iwin).Object.create(new iwin.Object())),
  469. new iwin.Object(), []);
  470. // Construct an object full of tricky things.
  471. let symbolProps = '';
  472. uniqueSymbol = iwin.eval('var uniqueSymbol = Symbol("uniqueSymbol"); uniqueSymbol');
  473. symbolProps = `, [uniqueSymbol]: 43,
  474. [Symbol.for("registrySymbolProp")]: 44`;
  475. var trickyObject =
  476. iwin.eval(`(function() {
  477. var o = new Object({
  478. primitiveProp: 42, objectProp: { foo: 2 },
  479. xoProp: top, hasOwnProperty: 10,
  480. get getterProp() { return 2; },
  481. set setterProp(x) { },
  482. get getterSetterProp() { return 3; },
  483. set getterSetterProp(x) { },
  484. callableProp: function() { },
  485. nonXrayableProp: new WeakMap()
  486. ${symbolProps}
  487. });
  488. Object.defineProperty(o, "nonConfigurableGetterSetterProp",
  489. { get: function() { return 5; }, set: function() {} });
  490. return o;
  491. })()`);
  492. testTrickyObject(trickyObject);
  493. }
  494. function testArray() {
  495. // The |length| property is generally very weird, especially with respect
  496. // to its behavior on the prototype. Array.prototype is actually an Array
  497. // instance, and therefore has a vestigial .length. But we don't want to
  498. // show that over Xrays, and generally want .length to just appear as an
  499. // |own| data property. So we add it to the ignore list here, and check it
  500. // separately.
  501. //
  502. // |Symbol.unscopables| should in principle be exposed, but it is
  503. // inconvenient (as it's a data property, unsupported by ClassSpec) and
  504. // low value.
  505. let propsToSkip = ['length', Symbol.unscopables];
  506. testXray('Array', new iwin.Array(20), new iwin.Array(), propsToSkip);
  507. let symbolProps = '';
  508. uniqueSymbol = iwin.eval('var uniqueSymbol = Symbol("uniqueSymbol"); uniqueSymbol');
  509. symbolProps = `trickyArray[uniqueSymbol] = 43;
  510. trickyArray[Symbol.for("registrySymbolProp")] = 44;`;
  511. var trickyArray =
  512. iwin.eval(`var trickyArray = [];
  513. trickyArray.primitiveProp = 42;
  514. trickyArray.objectProp = { foo: 2 };
  515. trickyArray.xoProp = top;
  516. trickyArray.hasOwnProperty = 10;
  517. Object.defineProperty(trickyArray, 'getterProp', { get: function() { return 2; }});
  518. Object.defineProperty(trickyArray, 'setterProp', { set: function(x) {}});
  519. Object.defineProperty(trickyArray, 'getterSetterProp', { get: function() { return 3; }, set: function(x) {}, configurable: true});
  520. Object.defineProperty(trickyArray, 'nonConfigurableGetterSetterProp', { get: function() { return 5; }, set: function(x) {}});
  521. trickyArray.callableProp = function() {};
  522. trickyArray.nonXrayableProp = new WeakMap();
  523. ${symbolProps}
  524. trickyArray;`);
  525. // Test indexed access.
  526. trickyArray.wrappedJSObject[9] = "some indexed property";
  527. is(trickyArray[9], "some indexed property", "indexed properties work correctly over Xrays");
  528. is(trickyArray.length, 10, "Length works correctly over Xrays");
  529. checkThrows(function() { "use strict"; delete trickyArray.length; }, /config/, "Can't delete non-configurable 'length' property");
  530. delete trickyArray[9];
  531. is(trickyArray[9], undefined, "Delete works correctly over Xrays");
  532. is(trickyArray.wrappedJSObject[9], undefined, "Delete works correctly over Xrays (viewed via waiver)");
  533. is(trickyArray.length, 10, "length doesn't change");
  534. trickyArray[11] = "some other indexed property";
  535. is(trickyArray.length, 12, "length now changes");
  536. is(trickyArray.wrappedJSObject[11], "some other indexed property");
  537. trickyArray.length = 0;
  538. is(trickyArray.length, 0, "Setting length works over Xray");
  539. is(trickyArray[11], undefined, "Setting length truncates over Xray");
  540. Object.defineProperty(trickyArray, 'length', { configurable: false, enumerable: false, writable: false, value: 0 });
  541. trickyArray[1] = "hi";
  542. is(trickyArray.length, 0, "Length remains non-writable");
  543. is(trickyArray[1], undefined, "Frozen length forbids new properties");
  544. is(trickyArray instanceof iwin.Array, true, "instanceof should work across xray wrappers.");
  545. testTrickyObject(trickyArray);
  546. testArrayIterators(new iwin.Array(1, 1, 2, 3, 5), [1, 1, 2, 3, 5]);
  547. }
  548. // Parts of this function are kind of specific to testing Object, but we factor
  549. // it out so that we can re-use the trickyObject stuff on Arrays.
  550. function testTrickyObject(trickyObject) {
  551. // Make sure it looks right under the hood.
  552. is(trickyObject.wrappedJSObject.getterProp, 2, "Underlying object has getter");
  553. is(Cu.unwaiveXrays(trickyObject.wrappedJSObject.xoProp), top, "Underlying object has xo property");
  554. // Test getOwnPropertyNames.
  555. var expectedNames = ['objectProp', 'primitiveProp'];
  556. if (trickyObject instanceof iwin.Array)
  557. expectedNames.push('length');
  558. is(Object.getOwnPropertyNames(trickyObject).sort().toSource(),
  559. expectedNames.sort().toSource(), "getOwnPropertyNames should be filtered correctly");
  560. var expectedSymbols = [Symbol.for("registrySymbolProp"), uniqueSymbol];
  561. is(Object.getOwnPropertySymbols(trickyObject).map(uneval).sort().toSource(),
  562. expectedSymbols.map(uneval).sort().toSource(),
  563. "getOwnPropertySymbols should be filtered correctly");
  564. // Test that cloning uses the Xray view.
  565. var cloned = Cu.cloneInto(trickyObject, this);
  566. is(Object.getOwnPropertyNames(cloned).sort().toSource(),
  567. expectedNames.sort().toSource(), "structured clone should use the Xray view");
  568. is(Object.getOwnPropertySymbols(cloned).map(uneval).sort().toSource(),
  569. "[]", "structured cloning doesn't clone symbol-keyed properties yet");
  570. // Test iteration and in-place modification. Beware of 'expando', which is the property
  571. // we placed on the xray proto.
  572. var propCount = 0;
  573. for (let prop in trickyObject) {
  574. if (prop == 'primitiveProp')
  575. trickyObject[prop] = trickyObject[prop] - 10;
  576. if (prop != 'expando')
  577. trickyObject[prop] = trickyObject[prop];
  578. ++propCount;
  579. }
  580. is(propCount, 3, "Should iterate the correct number of times");
  581. // Test Object.keys.
  582. is(Object.keys(trickyObject).sort().toSource(),
  583. ['objectProp', 'primitiveProp'].toSource(), "Object.keys should be filtered correctly");
  584. // Test getOwnPropertyDescriptor.
  585. is(trickyObject.primitiveProp, 32, "primitive prop works");
  586. is(trickyObject.objectProp.foo, 2, "object prop works");
  587. is(typeof trickyObject.callableProp, 'undefined', "filtering works correctly");
  588. is(Object.getOwnPropertyDescriptor(trickyObject, 'primitiveProp').value, 32, "getOwnPropertyDescriptor works");
  589. is(Object.getOwnPropertyDescriptor(trickyObject, 'xoProp'), undefined, "filtering works with getOwnPropertyDescriptor");
  590. // Test defineProperty.
  591. trickyObject.primitiveSetByXray = 'fourty two';
  592. is(trickyObject.primitiveSetByXray, 'fourty two', "Can set primitive correctly over Xray (ready via Xray)");
  593. is(trickyObject.wrappedJSObject.primitiveSetByXray, 'fourty two', "Can set primitive correctly over Xray (ready via Waiver)");
  594. var newContentObject = iwin.eval('new Object({prop: 99, get getterProp() { return 2; }})');
  595. trickyObject.objectSetByXray = newContentObject;
  596. is(trickyObject.objectSetByXray.prop, 99, "Can set object correctly over Xray (ready via Xray)");
  597. is(trickyObject.wrappedJSObject.objectSetByXray.prop, 99, "Can set object correctly over Xray (ready via Waiver)");
  598. checkThrows(function() { trickyObject.rejectedProp = {foo: 33}}, /cross-origin object/,
  599. "Should reject privileged object property definition");
  600. // Test JSON.stringify.
  601. var jsonStr = JSON.stringify(newContentObject);
  602. ok(/prop/.test(jsonStr), "JSON stringification should work: " + jsonStr);
  603. // Test deletion.
  604. delete newContentObject.prop;
  605. ok(!newContentObject.hasOwnProperty('prop'), "Deletion should work");
  606. ok(!newContentObject.wrappedJSObject.hasOwnProperty('prop'), "Deletion should forward");
  607. delete newContentObject.getterProp;
  608. ok(newContentObject.wrappedJSObject.hasOwnProperty('getterProp'), "Deletion be no-op for filtered property");
  609. // We should be able to overwrite an existing accessor prop and convert it
  610. // to a value prop.
  611. is(trickyObject.wrappedJSObject.getterSetterProp, 3, "Underlying object has getter");
  612. is(trickyObject.getterSetterProp, undefined, "Filtering properly over Xray");
  613. trickyObject.getterSetterProp = 'redefined';
  614. is(trickyObject.getterSetterProp, 'redefined', "Redefinition works");
  615. is(trickyObject.wrappedJSObject.getterSetterProp, 'redefined', "Redefinition forwards");
  616. // We should NOT be able to overwrite an existing non-configurable accessor
  617. // prop, though.
  618. is(trickyObject.wrappedJSObject.nonConfigurableGetterSetterProp, 5,
  619. "Underlying object has getter");
  620. is(trickyObject.nonConfigurableGetterSetterProp, undefined,
  621. "Filtering properly over Xray here too");
  622. is((trickyObject.nonConfigurableGetterSetterProp = 'redefined'), 'redefined',
  623. "Assigning to non-configurable prop should fail silently in non-strict mode");
  624. checkThrows(function() {
  625. "use strict";
  626. trickyObject.nonConfigurableGetterSetterProp = 'redefined';
  627. }, /config/, "Should throw when redefining non-configurable prop in strict mode");
  628. is(trickyObject.nonConfigurableGetterSetterProp, undefined,
  629. "Redefinition should have failed");
  630. is(trickyObject.wrappedJSObject.nonConfigurableGetterSetterProp, 5,
  631. "Redefinition really should have failed");
  632. checkThrows(function() { trickyObject.hasOwnProperty = 33; }, /shadow/,
  633. "Should reject shadowing of pre-existing inherited properties over Xrays");
  634. checkThrows(function() { Object.defineProperty(trickyObject, 'rejectedProp', { get: function() {}}); },
  635. /accessor property/, "Should reject accessor property definition");
  636. }
  637. function testTypedArrays() {
  638. // We don't invoke testXray with %TypedArray%, because that function isn't
  639. // set up to deal with "anonymous" dependent classes (that is, classes not
  640. // visible as a global property, which %TypedArray% is not), and fixing it
  641. // up is more trouble than it's worth.
  642. var typedArrayProto = Object.getPrototypeOf(Int8Array.prototype);
  643. var desiredInheritedProps = Object.getOwnPropertyNames(typedArrayProto).sort();
  644. var inheritedProps =
  645. filterOut(desiredInheritedProps, ["BYTES_PER_ELEMENT", "constructor"]);
  646. var inheritedCallables =
  647. inheritedProps.filter(name => (propertyIsGetter(typedArrayProto, name) ||
  648. typeof typedArrayProto[name] === "function") &&
  649. name !== "constructor");
  650. for (var c of typedArrayClasses) {
  651. var t = new iwin[c](10);
  652. checkThrows(function() { t[2]; }, /performant/, "direct property-wise reading of typed arrays forbidden over Xrays");
  653. checkThrows(function() { t[2] = 3; }, /performant/, "direct property-wise writing of typed arrays forbidden over Xrays");
  654. var wesb = new Cu.Sandbox([iwin], {isWebExtensionContentScript: true});
  655. wesb.t = t;
  656. wesb.eval('t[2] = 3');
  657. is(wesb.eval('t.wrappedJSObject[2]'), 3, "direct property-wise writing of typed arrays allowed for WebExtension content scripts");
  658. is(wesb.eval('t[2]'), 3, "direct property-wise reading and writing of typed arrays allowed for WebExtensions content scripts");
  659. t.wrappedJSObject[2] = 3;
  660. is(t.wrappedJSObject[2], 3, "accessing elements over waivers works");
  661. t.wrappedJSObject.expando = 'hi';
  662. is(t.wrappedJSObject.expando, 'hi', "access expandos over waivers works");
  663. is(Cu.cloneInto(t, window)[2], 3, "cloneInto works");
  664. is(Cu.cloneInto(t, window).expando, undefined, "cloneInto does not copy expandos");
  665. is(Object.getOwnPropertyNames(t).sort().toSource(),
  666. '["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]',
  667. "Only indexed properties visible over Xrays");
  668. Object.defineProperty(t.wrappedJSObject, 'length', {value: 42});
  669. is(t.wrappedJSObject.length, 42, "Set tricky expando")
  670. is(t.length, 10, "Length accessor works over Xrays")
  671. is(t.byteLength, t.length * window[c].prototype.BYTES_PER_ELEMENT, "byteLength accessor works over Xrays")
  672. // Can create TypedArray from content ArrayBuffer
  673. var buffer = new iwin.ArrayBuffer(8);
  674. eval(`new ${c}(buffer);`);
  675. var xray = new iwin[c](0);
  676. var xrayTypedArrayProto = Object.getPrototypeOf(Object.getPrototypeOf(xray));
  677. testProtoCallables(inheritedCallables, new iwin[c](0), xrayTypedArrayProto, typedArrayProto);
  678. // When testing iterators, make sure to do so from inside our web
  679. // extension sandbox, since from chrome we can't poke their indices. Note
  680. // that we have to actually recreate our functions that touch typed array
  681. // indices inside the sandbox, not just export them, because otherwise
  682. // they'll just run with our principal anyway.
  683. //
  684. // But we do want to export is(), since we want ours called.
  685. wesb.eval(arraysEqualSource);
  686. wesb.eval(testArrayIteratorsSource);
  687. Cu.exportFunction(is, wesb,
  688. { defineAs: "is" });
  689. wesb.eval('testArrayIterators(t, [0, 0, 3, 0, 0, 0, 0, 0, 0, 0])');
  690. }
  691. }
  692. function testErrorObjects() {
  693. // We only invoke testXray with Error, because that function isn't set up
  694. // to deal with dependent classes and fixing it up is more trouble than
  695. // it's worth.
  696. testXray('Error', new iwin.Error('some error message'), new iwin.Error());
  697. // Make sure that the dependent classes have their prototypes set up correctly.
  698. for (let c of errorObjectClasses.filter(x => x != "Error")) {
  699. var e = new iwin[c]('some message');
  700. is(Object.getPrototypeOf(e).name, c, "Prototype has correct name");
  701. is(Object.getPrototypeOf(Object.getPrototypeOf(e)), iwin.Error.prototype, "Dependent prototype set up correctly");
  702. is(e.name, c, "Exception name inherited correctly");
  703. function testProperty(name, criterion, goodReplacement, faultyReplacement) {
  704. ok(criterion(e[name]), name + " property is correct: " + e[name]);
  705. e.wrappedJSObject[name] = goodReplacement;
  706. is(e[name], goodReplacement, name + " property ok after replacement: " + goodReplacement);
  707. e.wrappedJSObject[name] = faultyReplacement;
  708. is(e[name], name == 'message' ? "" : undefined, name + " property skipped after suspicious replacement");
  709. }
  710. testProperty('message', x => x == 'some message', 'some other message', 42);
  711. testProperty('fileName', x => x == '', 'otherFilename.html', new iwin.Object());
  712. testProperty('columnNumber', x => x == 1, 99, 99.5);
  713. testProperty('lineNumber', x => x == 0, 50, 'foo');
  714. // Note - an Exception newed via Xrays is going to have an empty stack given the
  715. // current semantics and implementation. This tests the current behavior, but that
  716. // may change in bug 1036527 or similar.
  717. //
  718. // Furthermore, xrays should always return an error's original stack, and
  719. // not overwrite it.
  720. var stack = e.stack;
  721. ok(/^\s*$/.test(stack), "stack property should be correct");
  722. e.wrappedJSObject.stack = "not a stack";
  723. is(e.stack, stack, "Xrays should never get an overwritten stack property.");
  724. }
  725. }
  726. function testRegExp() {
  727. // RegExp statics are very weird, and in particular RegExp has static
  728. // properties that have to do with the last regexp execution in the global.
  729. // Xraying those makes no sense, so we just skip constructor properties for
  730. // RegExp xrays.
  731. // RegExp[@@species] is affected by above skip, but we don't fix it until
  732. // compelling use-case appears, as supporting RegExp[@@species] while
  733. // skipping other static properties makes things complicated.
  734. let ctorPropsToSkip = ["input", "lastMatch", "lastParen",
  735. "leftContext", "rightContext", "$1", "$2", "$3",
  736. "$4", "$5", "$6", "$7", "$8", "$9", "$_", "$&",
  737. "$+", "$`", "$'", Symbol.species];
  738. testXray('RegExp', new iwin.RegExp('foo'), new iwin.RegExp(), [],
  739. ctorPropsToSkip);
  740. // Test the self-hosted |flags| property, toString, and toSource.
  741. for (var flags of ["", "g", "i", "m", "y", "gimy"]) {
  742. var re = new iwin.RegExp("foo", flags);
  743. is(re.flags, re.wrappedJSObject.flags, "Results match");
  744. isnot(re.toString, Cu.unwaiveXrays(re.wrappedJSObject.toString), "Different function identities");
  745. is(Cu.getGlobalForObject(re.toString), window, "Xray global is correct");
  746. is(Cu.getGlobalForObject(re.wrappedJSObject.toString), iwin, "Underlying global is correct");
  747. is(re.toString(), re.wrappedJSObject.toString(), "Results match");
  748. isnot(re.toSource, Cu.unwaiveXrays(re.wrappedJSObject.toSource), "Different function identities");
  749. is(Cu.getGlobalForObject(re.toSource), window, "Xray global is correct");
  750. is(Cu.getGlobalForObject(re.wrappedJSObject.toSource), iwin, "Underlying global is correct");
  751. is(re.toSource(), re.wrappedJSObject.toSource(), "Results match");
  752. // Test with modified flags accessors
  753. iwin.eval(`
  754. var props = ["global", "ignoreCase", "multiline", "sticky", "source", "unicode"];
  755. var origDescs = {};
  756. for (var prop of props) {
  757. origDescs[prop] = Object.getOwnPropertyDescriptor(RegExp.prototype, prop);
  758. Object.defineProperty(RegExp.prototype, prop, {
  759. get: function() {
  760. throw new Error("modified accessor is called");
  761. }
  762. });
  763. }
  764. `);
  765. try {
  766. is(re.flags, flags, "Unmodified flags accessors are called");
  767. is(re.toString(), "/foo/" + flags, "Unmodified flags and source accessors are called");
  768. is(re.toSource(), "/foo/" + flags, "Unmodified flags and source accessors are called");
  769. } finally {
  770. iwin.eval(`
  771. for (var prop of props) {
  772. Object.defineProperty(RegExp.prototype, prop, origDescs[prop]);
  773. }
  774. `);
  775. }
  776. }
  777. }
  778. // Note: this is a small set of basic tests. More in-depth tests are located
  779. // in test_promise_xrays.html.
  780. function testPromise() {
  781. testXray('Promise', new iwin.Promise(function(){}), new iwin.Promise(function(){}));
  782. // Test catch and then.
  783. var pr = new iwin.Promise(function(){});
  784. isnot(pr.catch, Cu.unwaiveXrays(pr.wrappedJSObject.catch), "Different function identities");
  785. is(Cu.getGlobalForObject(pr.catch), window, "Xray global is correct");
  786. is(Cu.getGlobalForObject(pr.wrappedJSObject.catch), iwin, "Underlying global is correct");
  787. isnot(pr.then, Cu.unwaiveXrays(pr.wrappedJSObject.then), "Different function identities");
  788. is(Cu.getGlobalForObject(pr.then), window, "Xray global is correct");
  789. is(Cu.getGlobalForObject(pr.wrappedJSObject.then), iwin, "Underlying global is correct");
  790. }
  791. function testArrayBuffer() {
  792. let constructors = ['ArrayBuffer'];
  793. if (!isReleaseOrBeta) {
  794. constructors.push('SharedArrayBuffer');
  795. }
  796. for (const c of constructors) {
  797. testXray(c, new iwin[c](0), new iwin[c](12));
  798. var t = new iwin[c](12);
  799. is(t.byteLength, 12, `${c} byteLength is correct`);
  800. is(t.slice(4).byteLength, 8, `${c} byteLength is correct after slicing`);
  801. is(Cu.getGlobalForObject(t.slice(4)), iwin, "Slice results lives in the target compartment");
  802. is(Object.getPrototypeOf(t.slice(4)), iwin[c].prototype, "Slice results proto lives in target compartment")
  803. // SharedArrayBuffer does not have static slice method
  804. if (c === 'ArrayBuffer') {
  805. is(ArrayBuffer.slice(t, 4).byteLength, 8, `${c}.slice (deprecated) works`);
  806. }
  807. var i32Array = new Int32Array(t);
  808. // i32Array is going to be created in the buffer's target compartment,
  809. // but usually this is unobservable, because the proto is set to
  810. // the current compartment's prototype.
  811. // However Xrays ignore the object's proto and claim its proto is
  812. // the default proto for that class in the relevant compartment,
  813. // so see through this proto hack.
  814. todo_is(Object.getPrototypeOf(i32Array), Int32Array.prototype, "Int32Array has correct proto");
  815. is(i32Array.length, 3, `Int32Array created from Xray ${c} has the correct length`);
  816. is(i32Array.buffer, t, "Int32Array has the correct buffer that we passed in");
  817. i32Array = new iwin.Int32Array(t);
  818. is(Object.getPrototypeOf(i32Array), iwin.Int32Array.prototype, "Xray Int32Array has correct proto");
  819. is(i32Array.length, 3, `Xray Int32Array created from Xray ${c} has the correct length`);
  820. is(i32Array.buffer, t, "Xray Int32Array has the correct buffer that we passed in");
  821. t = (new iwin.Int32Array(2)).buffer;
  822. is(t.byteLength, 8, `Can access ${c} returned by buffer property`);
  823. }
  824. }
  825. ]]>
  826. </script>
  827. <iframe id="ifr" onload="go();" src="http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html" />
  828. </window>