Bug 1915045 Ensure decode tasks are scheduled on BufferingState::Enter() r=media...
[gecko.git] / js / xpconnect / tests / chrome / test_xrayToJS.xhtml
blobe2cc76f6f952b1f12d62513b08c20b0a920734f0
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 src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
11 <!-- test results are displayed in the html:body -->
12 <body xmlns="http://www.w3.org/1999/xhtml">
13 <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=933681"
14 target="_blank">Mozilla Bug 933681</a>
15 </body>
17 <!-- test code goes here -->
18 <script type="application/javascript">
19 <![CDATA[
21 /** Test for ES constructors on Xrayed globals. **/
22 SimpleTest.waitForExplicitFinish();
23 let global = Cu.getGlobalForObject.bind(Cu);
25 function checkThrows(f, rgxp, msg) {
26 try {
27 f();
28 ok(false, "Should have thrown: " + msg);
29 } catch (e) {
30 ok(true, "Threw as expected: " + msg);
31 ok(rgxp.test(e), "Message correct: " + e);
35 var { AppConstants } = SpecialPowers.ChromeUtils.importESModule(
36 "resource://gre/modules/AppConstants.sys.mjs"
38 var isNightlyBuild = AppConstants.NIGHTLY_BUILD;
39 var isReleaseOrBeta = AppConstants.RELEASE_OR_BETA;
41 let typedArrayClasses = ['Uint8Array', 'Int8Array', 'Uint16Array', 'Int16Array',
42 'Uint32Array', 'Int32Array', 'Float32Array', 'Float64Array',
43 'Uint8ClampedArray'];
44 let errorObjectClasses = ['Error', 'InternalError', 'EvalError', 'RangeError', 'ReferenceError',
45 'SyntaxError', 'TypeError', 'URIError', 'AggregateError'];
47 // A simpleConstructors entry can either be the name of a constructor as a
48 // string, or an object containing the properties `name`, and `args`.
49 // In the former case, the constructor is invoked without any args; in the
50 // latter case, it is invoked with `args` as the arguments list.
51 let simpleConstructors = ['Object', 'Function', 'Array', 'Boolean', 'Date', 'Number',
52 'String', 'RegExp', 'ArrayBuffer', 'WeakMap', 'WeakSet', 'Map', 'Set',
53 {name: 'Promise', args: [function(){}]}];
55 // All TypedArray constructors can be called with zero arguments.
56 simpleConstructors = simpleConstructors.concat(typedArrayClasses);
58 // All Error constructors except AggregateError can be called with zero arguments.
59 simpleConstructors = simpleConstructors.concat(errorObjectClasses.filter(name => {
60 return name !== 'AggregateError';
61 }));
63 function go() {
64 window.iwin = document.getElementById('ifr').contentWindow;
65 /* global iwin */
67 // Test constructors that can be instantiated with zero arguments, or with
68 // a fixed set of arguments provided using `...rest`.
69 for (let c of simpleConstructors) {
70 var args = [];
71 if (typeof c === 'object') {
72 args = c.args;
73 c = c.name;
75 ok(iwin[c], "Constructors appear: " + c);
76 is(iwin[c], Cu.unwaiveXrays(iwin.wrappedJSObject[c]),
77 "we end up with the appropriate constructor: " + c);
78 is(Cu.unwaiveXrays(Cu.waiveXrays(new iwin[c](...args)).constructor), iwin[c],
79 "constructor property is set up right: " + c);
80 let expectedProto = Cu.isOpaqueWrapper(new iwin[c](...args)) ?
81 iwin.Object.prototype : iwin[c].prototype;
82 is(Object.getPrototypeOf(new iwin[c](...args)), expectedProto,
83 "prototype is correct: " + c);
84 is(global(new iwin[c](...args)), iwin, "Got the right global: " + c);
87 // Test Object in more detail.
88 var num = new iwin.Object(4);
89 is(Cu.waiveXrays(num).valueOf(), 4, "primitive object construction works");
90 is(global(num), iwin, "correct global for num");
91 var obj = new iwin.Object();
92 obj.foo = 2;
93 var withProto = Cu.unwaiveXrays(Cu.waiveXrays(iwin).Object.create(obj));
94 is(global(withProto), iwin, "correct global for withProto");
95 is(Cu.waiveXrays(withProto).foo, 2, "Inherits properly");
97 // Test Function.
98 var primitiveFun = new iwin.Function('return 2');
99 is(global(primitiveFun), iwin, "function construction works");
100 is(primitiveFun(), 2, "basic function works");
101 var doSetFoo = new iwin.Function('arg', 'arg.foo = 2;');
102 is(global(doSetFoo), iwin, "function with args works");
103 try {
104 doSetFoo({});
105 ok(false, "should have thrown while setting property on object");
106 } catch (e) {
107 ok(!!/denied/.test(e), "Threw correctly: " + e);
109 var factoryFun = new iwin.Function('return {foo: 32}');
110 is(global(factoryFun), iwin, "proper global for factoryFun");
111 is(factoryFun().foo, 32, "factoryFun invokable");
112 is(global(factoryFun()), iwin, "minted objects live in the content scope");
113 testXray('Function', factoryFun, new iwin.Function(), ['length', 'name']);
114 var echoThis = new iwin.Function('return this;');
115 echoThis.wrappedJSObject.bind = 42;
116 var boundEchoThis = echoThis.bind(document);
117 is(boundEchoThis(), document, "bind() works correctly over Xrays");
118 is(global(boundEchoThis), window, "bound functions live in the caller's scope");
119 ok(/return this/.test(echoThis.toSource()), 'toSource works: ' + echoThis.toSource());
120 ok(/return this/.test(echoThis.toString()), 'toString works: ' + echoThis.toString());
121 is(iwin.Function.prototype, Object.getPrototypeOf(echoThis), "Function.prototype works for standard classes");
122 is(echoThis.prototype, undefined, "Function.prototype not visible for non standard constructors");
123 iwin.eval('var foopyFunction = function namedFoopyFunction(a, b, c) {}');
124 var foopyFunction = Cu.unwaiveXrays(Cu.waiveXrays(iwin).foopyFunction);
125 ok(Cu.isXrayWrapper(foopyFunction), "Should be Xrays");
126 is(foopyFunction.name, "namedFoopyFunction", ".name works over Xrays");
127 is(foopyFunction.length, 3, ".length works over Xrays");
128 ok(Object.getOwnPropertyNames(foopyFunction).includes('length'), "Should list length");
129 ok(Object.getOwnPropertyNames(foopyFunction).includes('name'), "Should list name");
130 ok(!Object.getOwnPropertyNames(foopyFunction).includes('prototype'), "Should not list prototype");
131 ok(Object.getOwnPropertyNames(iwin.Array).includes('prototype'), "Should list prototype for standard constructor");
133 // Test BoundFunction.
134 iwin.eval('var boundFoopyFunction = foopyFunction.bind(null, 1)');
135 var boundFoopyFunction = Cu.unwaiveXrays(Cu.waiveXrays(iwin).boundFoopyFunction);
136 is(boundFoopyFunction.name, "bound namedFoopyFunction", "bound .name works over Xrays");
137 is(boundFoopyFunction.length, 2, "bound .length works over Xrays");
138 is(JSON.stringify(Object.getOwnPropertyNames(boundFoopyFunction).sort()), '["length","name"]', "Should list length and name");
139 // Mutate .name, it's just a data property.
140 iwin.eval('Object.defineProperty(boundFoopyFunction, "name", {value: "foobar", configurable: true, writable: true});');
141 is(boundFoopyFunction.name, "foobar", "mutated .name works over Xrays");
142 iwin.eval('boundFoopyFunction.name = 123;');
143 is(boundFoopyFunction.name, undefined, "only support string for .name");
144 iwin.eval('delete boundFoopyFunction.name');
145 is(boundFoopyFunction.name, undefined, "deleted .name works over Xrays");
146 // Mutate .length.
147 iwin.eval('Object.defineProperty(boundFoopyFunction, "length", {value: 456, configurable: true, writable: true});');
148 is(boundFoopyFunction.length, 456, "mutated .length works over Xrays");
149 iwin.eval('boundFoopyFunction.length = "bar";');
150 is(boundFoopyFunction.length, undefined, "only support number for .length");
152 // Test proxies.
153 var targetObject = new iwin.Object();
154 targetObject.foo = 9;
155 var forwardingProxy = new iwin.Proxy(targetObject, new iwin.Object());
156 is(global(forwardingProxy), iwin, "proxy global correct");
157 is(Cu.waiveXrays(forwardingProxy).foo, 9, "forwards correctly");
159 // Test AggregateError.
161 // AggregateError throws when called without an iterable object as its first argument.
162 let args = [new iwin.Array()];
164 ok(iwin.AggregateError, "AggregateError constructor is present");
165 is(iwin.AggregateError, Cu.unwaiveXrays(iwin.wrappedJSObject.AggregateError),
166 "we end up with the appropriate AggregateError constructor");
167 is(Cu.unwaiveXrays(Cu.waiveXrays(new iwin.AggregateError(...args)).constructor), iwin.AggregateError,
168 "AggregateError constructor property is set up right");
169 let expectedProto = Cu.isOpaqueWrapper(new iwin.AggregateError(...args)) ?
170 iwin.Object.prototype : iwin.AggregateError.prototype;
171 is(Object.getPrototypeOf(new iwin.AggregateError(...args)), expectedProto,
172 "AggregateError prototype is correct");
173 is(global(new iwin.AggregateError(...args)), iwin, "Got the right global for AggregateError");
176 // Test eval.
177 var toEval = "({a: 2, b: {foo: 'bar'}, f: function() { return window; }})";
178 is(global(iwin.eval(toEval)), iwin, "eval creates objects in the correct global");
179 is(iwin.eval(toEval).b.foo, 'bar', "eval-ed object looks right");
180 is(Cu.waiveXrays(iwin.eval(toEval)).f(), Cu.waiveXrays(iwin), "evaled function works right");
182 testDate();
184 testObject();
186 testArray();
188 testTypedArrays();
190 testErrorObjects();
192 testRegExp();
194 testPromise();
196 testArrayBuffer();
198 testMap();
200 testSet();
202 testWeakMap();
204 testWeakSet();
206 testProxy();
208 testDataView();
210 testNumber();
212 SimpleTest.finish();
215 // Maintain a static list of the properties that are available on each standard
216 // prototype, so that we make sure to audit any new ones to make sure they're
217 // Xray-safe.
219 // DO NOT CHANGE WTIHOUT REVIEW FROM AN XPCONNECT PEER.
220 var gPrototypeProperties = {};
221 var gConstructorProperties = {};
222 // Properties which cannot be invoked if callable without potentially
223 // rendering the object useless.
224 var gStatefulProperties = {};
225 function constructorProps(arr) {
226 // Some props live on all constructors
227 return arr.concat(["prototype", "length", "name"]);
229 gPrototypeProperties.Date =
230 ["getTime", "getTimezoneOffset", "getYear", "getFullYear", "getUTCFullYear",
231 "getMonth", "getUTCMonth", "getDate", "getUTCDate", "getDay", "getUTCDay",
232 "getHours", "getUTCHours", "getMinutes", "getUTCMinutes", "getSeconds",
233 "getUTCSeconds", "getMilliseconds", "getUTCMilliseconds", "setTime",
234 "setYear", "setFullYear", "setUTCFullYear", "setMonth", "setUTCMonth",
235 "setDate", "setUTCDate", "setHours", "setUTCHours", "setMinutes",
236 "setUTCMinutes", "setSeconds", "setUTCSeconds", "setMilliseconds",
237 "setUTCMilliseconds", "toUTCString", "toLocaleString",
238 "toLocaleDateString", "toLocaleTimeString", "toDateString", "toTimeString",
239 "toISOString", "toJSON", "toSource", "toString", "valueOf", "constructor",
240 "toGMTString", Symbol.toPrimitive];
241 gConstructorProperties.Date = constructorProps(["UTC", "parse", "now"]);
242 gPrototypeProperties.Object =
243 ["constructor", "toSource", "toString", "toLocaleString", "valueOf",
244 "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable",
245 "__defineGetter__", "__defineSetter__", "__lookupGetter__", "__lookupSetter__",
246 "__proto__"];
247 gConstructorProperties.Object =
248 constructorProps(["setPrototypeOf", "getOwnPropertyDescriptor", "getOwnPropertyDescriptors",
249 "keys", "is", "defineProperty", "defineProperties", "create",
250 "getOwnPropertyNames", "getOwnPropertySymbols",
251 "preventExtensions", "freeze", "fromEntries", "isFrozen", "seal",
252 "isSealed", "assign", "getPrototypeOf", "values",
253 "entries", "isExtensible", "hasOwn", "groupBy"]);
254 gPrototypeProperties.Array =
255 ["length", "toSource", "toString", "toLocaleString", "join", "reverse", "sort", "push",
256 "pop", "shift", "unshift", "splice", "concat", "slice", "lastIndexOf", "indexOf",
257 "includes", "forEach", "map", "reduce", "reduceRight", "filter", "some", "every", "find",
258 "findIndex", "copyWithin", "fill", Symbol.iterator, Symbol.unscopables, "entries", "keys",
259 "values", "constructor", "flat", "flatMap", "at", "findLast", "findLastIndex",
260 "toReversed", "toSorted", "toSpliced", "with"];
261 gConstructorProperties.Array =
262 constructorProps(["isArray", "from", "fromAsync", "of", Symbol.species]);
263 for (let c of typedArrayClasses) {
264 gPrototypeProperties[c] = ["constructor", "BYTES_PER_ELEMENT"];
265 gConstructorProperties[c] = constructorProps(["BYTES_PER_ELEMENT"]);
267 // There is no TypedArray constructor, looks like.
268 is(window.TypedArray, undefined, "If this ever changes, add to this test!");
269 for (let c of errorObjectClasses) {
270 gPrototypeProperties[c] = ["constructor", "name", "message", "stack"];
271 gConstructorProperties[c] = constructorProps([]);
274 if (typeof Error.isError === "function") {
275 gConstructorProperties.Error.push("isError");
278 // toString and toSource only live on the parent proto (Error.prototype).
279 gPrototypeProperties.Error.push('toString');
280 gPrototypeProperties.Error.push('toSource');
282 gPrototypeProperties.Function =
283 ["constructor", "toSource", "toString", "apply", "call", "bind",
284 "length", "name", "arguments", "caller", Symbol.hasInstance];
285 gConstructorProperties.Function = constructorProps([])
287 gPrototypeProperties.RegExp =
288 ["constructor", "toSource", "toString", "compile", "exec", "test",
289 Symbol.match, Symbol.matchAll, Symbol.replace, Symbol.search, Symbol.split,
290 "flags", "dotAll", "global", "hasIndices", "ignoreCase", "multiline", "source", "sticky",
291 "unicode", "unicodeSets"];
292 gConstructorProperties.RegExp =
293 constructorProps(["escape", "input", "lastMatch", "lastParen",
294 "leftContext", "rightContext", "$1", "$2", "$3", "$4",
295 "$5", "$6", "$7", "$8", "$9", "$_", "$&", "$+",
296 "$`", "$'", Symbol.species])
298 gPrototypeProperties.Promise =
299 ["constructor", "catch", "then", "finally", Symbol.toStringTag];
300 gConstructorProperties.Promise =
301 constructorProps(["resolve", "reject", "all", "allSettled", "any", "race", "try",
302 "withResolvers", Symbol.species]);
304 gPrototypeProperties.ArrayBuffer =
305 ["constructor", "byteLength", "detached", "slice", Symbol.toStringTag, "transfer", "transferToFixedLength", "maxByteLength", "resizable", "resize"];
306 gConstructorProperties.ArrayBuffer =
307 constructorProps(["isView", Symbol.species]);
308 gStatefulProperties.ArrayBuffer = ["transfer", "transferToFixedLength"]
310 gPrototypeProperties.SharedArrayBuffer = ["constructor", "slice", "byteLength", "detached", Symbol.toStringTag, "transfer", "transferToFixedLength", "maxByteLength", "growable", "grow"];
311 gConstructorProperties.SharedArrayBuffer = constructorProps([Symbol.species]);
312 gStatefulProperties.SharedArrayBuffer = ["transfer", "transferToFixedLength"]
314 gPrototypeProperties.Map =
315 ["constructor", "size", Symbol.toStringTag, "get", "has", "set", "delete",
316 "keys", "values", "clear", "forEach", "entries", Symbol.iterator];
317 gConstructorProperties.Map =
318 constructorProps(["groupBy", Symbol.species]);
320 gPrototypeProperties.Set =
321 [Symbol.toStringTag, Symbol.iterator, "add", "clear", "constructor", "delete",
322 "difference", "entries", "forEach", "has", "intersection", "isDisjointFrom",
323 "isSubsetOf", "isSupersetOf", "keys", "size", "symmetricDifference", "union",
324 "values"];
325 gConstructorProperties.Set =
326 constructorProps([Symbol.species]);
328 gPrototypeProperties.WeakMap =
329 ["constructor", Symbol.toStringTag, "get", "has", "set", "delete"];
330 gConstructorProperties.WeakMap =
331 constructorProps([]);
333 gPrototypeProperties.WeakSet =
334 ["constructor", Symbol.toStringTag, "has", "add", "delete"];
335 gConstructorProperties.WeakSet =
336 constructorProps([]);
338 gPrototypeProperties.DataView =
339 ["constructor", "buffer", "byteLength", "byteOffset", Symbol.toStringTag,
340 "getInt8", "getUint8", "getInt16", "getUint16",
341 "getInt32", "getUint32", "getFloat32", "getFloat64",
342 "setInt8", "setUint8", "setInt16", "setUint16",
343 "setInt32", "setUint32", "setFloat32", "setFloat64",
344 "getBigInt64", "getBigUint64", "setBigInt64", "setBigUint64",
345 "getFloat16", "setFloat16"];
346 gConstructorProperties.DataView = constructorProps([]);
348 // Sort an array that may contain symbols as well as strings.
349 function sortProperties(arr) {
350 function sortKey(prop) {
351 return typeof prop + ":" + prop.toString();
353 arr.sort((a, b) => sortKey(a) < sortKey(b) ? -1 : +1);
356 // Sort all the lists so we don't need to mutate them later (or copy them
357 // again to sort them).
358 for (let c of Object.keys(gPrototypeProperties))
359 sortProperties(gPrototypeProperties[c]);
360 for (let c of Object.keys(gConstructorProperties))
361 sortProperties(gConstructorProperties[c]);
363 function filterOut(array, props) {
364 return array.filter(p => !props.includes(p));
367 function isTypedArrayClass(classname) {
368 return typedArrayClasses.includes(classname);
371 function propertyIsGetter(obj, name) {
372 return !!Object.getOwnPropertyDescriptor(obj, name).get;
375 function testProtoCallables(protoCallables, xray, xrayProto, localProto, callablesExcluded) {
376 // Handle undefined callablesExcluded.
377 let dontCall = callablesExcluded ?? [];
378 for (let name of protoCallables) {
379 info("Running tests for property: " + name);
380 // Test both methods and getter properties.
381 function lookupCallable(obj) {
382 let desc = null;
383 do {
384 desc = Object.getOwnPropertyDescriptor(obj, name);
385 if (desc) {
386 break;
388 obj = Object.getPrototypeOf(obj);
389 } while (obj);
390 return desc ? (desc.get || desc.value) : undefined;
392 ok(xrayProto.hasOwnProperty(name), `proto should have the property '${name}' as own`);
393 ok(!xray.hasOwnProperty(name), `instance should not have the property '${name}' as own`);
394 let method = lookupCallable(xrayProto);
395 is(typeof method, 'function', "Methods from Xrays are functions");
396 is(global(method), window, "Methods from Xrays are local");
397 ok(method instanceof Function, "instanceof works on methods from Xrays");
398 is(lookupCallable(xrayProto), method, "Holder caching works properly");
399 is(lookupCallable(xray), method, "Proto props resolve on the instance");
400 let local = lookupCallable(localProto);
401 is(method.length, local.length, "Function.length identical");
402 if (!method.length && !dontCall.includes(name)) {
403 is(method.call(xray) + "", local.call(xray) + "",
404 "Xray and local method results stringify identically");
406 // If invoking this method returns something non-Xrayable (opaque), the
407 // stringification is going to return [object Object].
408 // This happens for set[@@iterator] and other Iterator objects.
409 let callable = lookupCallable(xray.wrappedJSObject);
410 if (!Cu.isOpaqueWrapper(method.call(xray)) && callable) {
411 is(method.call(xray) + "",
412 callable.call(xray.wrappedJSObject) + "",
413 "Xray and waived method results stringify identically");
419 function testCtorCallables(ctorCallables, xrayCtor, localCtor) {
420 for (let name of ctorCallables) {
421 // Don't try to test Function.prototype, since that is in fact a callable
422 // but doesn't really do the things we expect callables to do here
423 // (e.g. it's in the wrong global, since it gets Xrayed itself).
424 if (name == "prototype" && localCtor.name == "Function") {
425 continue;
427 info(`Running tests for property: ${localCtor.name}.${name}`);
428 // Test both methods and getter properties.
429 function lookupCallable(obj) {
430 let desc = null;
431 do {
432 desc = Object.getOwnPropertyDescriptor(obj, name);
433 obj = Object.getPrototypeOf(obj);
434 } while (!desc);
435 return desc.get || desc.value;
438 ok(xrayCtor.hasOwnProperty(name), "ctor should have the property as own");
439 let method = lookupCallable(xrayCtor);
440 is(typeof method, 'function', "Methods from ctor Xrays are functions");
441 is(global(method), window, "Methods from ctor Xrays are local");
442 ok(method instanceof Function,
443 "instanceof works on methods from ctor Xrays");
444 is(lookupCallable(xrayCtor), method,
445 "Holder caching works properly on ctors");
446 let local = lookupCallable(localCtor);
447 is(method.length, local.length,
448 "Function.length identical for method from ctor");
449 // Don't try to do the return-value check on Date.now(), since there is
450 // absolutely no reason it should return the same value each time.
452 // Also don't try to do the return-value check on Regexp.lastMatch and
453 // Regexp["$&"] (which are aliases), because they get state off the global
454 // they live in, as far as I can tell, so testing them over Xrays will be
455 // wrong: on the Xray they will actaully get the lastMatch of _our_
456 // global, not the Xrayed one.
457 if (!method.length &&
458 !(localCtor.name == "Date" && name == "now") &&
459 !(localCtor.name == "RegExp" && (name == "lastMatch" || name == "$&"))) {
460 is(method.call(xrayCtor) + "", local.call(xrayCtor) + "",
461 "Xray and local method results stringify identically on constructors");
462 is(method.call(xrayCtor) + "",
463 lookupCallable(xrayCtor.wrappedJSObject).call(xrayCtor.wrappedJSObject) + "",
464 "Xray and waived method results stringify identically");
469 function testXray(classname, xray, xray2, propsToSkip, ctorPropsToSkip = []) {
470 propsToSkip = propsToSkip || [];
471 let xrayProto = Object.getPrototypeOf(xray);
472 let localProto = window[classname].prototype;
473 let desiredProtoProps = Object.getOwnPropertyNames(localProto).sort();
475 is(desiredProtoProps.toSource(),
476 gPrototypeProperties[classname].filter(id => typeof id === "string").toSource(),
477 "A property on the " + classname +
478 " prototype has changed! You need a security audit from an XPConnect peer");
479 is(Object.getOwnPropertySymbols(localProto).map(uneval).sort().toSource(),
480 gPrototypeProperties[classname].filter(id => typeof id !== "string").map(uneval).sort().toSource(),
481 "A symbol-keyed property on the " + classname +
482 " prototype has been changed! You need a security audit from an XPConnect peer");
484 let protoProps = filterOut(desiredProtoProps, propsToSkip);
485 let protoCallables = protoProps.filter(name => propertyIsGetter(localProto, name, classname) ||
486 typeof localProto[name] == 'function' &&
487 name != 'constructor');
488 let callablesExcluded = gStatefulProperties[classname];
489 ok(!!protoCallables.length, "Need something to test");
490 is(xrayProto, iwin[classname].prototype, "Xray proto is correct");
491 is(xrayProto, xray.__proto__, "Proto accessors agree");
492 var protoProto = classname == "Object" ? null : iwin.Object.prototype;
493 is(Object.getPrototypeOf(xrayProto), protoProto, "proto proto is correct");
494 testProtoCallables(protoCallables, xray, xrayProto, localProto, callablesExcluded);
495 is(Object.getOwnPropertyNames(xrayProto).sort().toSource(),
496 protoProps.toSource(), "getOwnPropertyNames works");
497 is(Object.getOwnPropertySymbols(xrayProto).map(uneval).sort().toSource(),
498 gPrototypeProperties[classname].filter(id => typeof id !== "string" && !propsToSkip.includes(id))
499 .map(uneval).sort().toSource(),
500 "getOwnPropertySymbols works");
502 is(xrayProto.constructor, iwin[classname], "constructor property works");
504 xrayProto.expando = 42;
505 is(xray.expando, 42, "Xrayed instances see proto expandos");
506 is(xray2.expando, 42, "Xrayed instances see proto expandos");
508 // Now test constructors
509 let localCtor = window[classname];
510 let xrayCtor = xrayProto.constructor;
511 // We already checked that this is the same as iwin[classname]
513 let desiredCtorProps =
514 Object.getOwnPropertyNames(localCtor).sort();
515 is(desiredCtorProps.toSource(),
516 gConstructorProperties[classname].filter(id => typeof id === "string").toSource(),
517 "A property on the " + classname +
518 " constructor has changed! You need a security audit from an XPConnect peer");
519 let desiredCtorSymbols =
520 Object.getOwnPropertySymbols(localCtor).map(uneval).sort()
521 is(desiredCtorSymbols.toSource(),
522 gConstructorProperties[classname].filter(id => typeof id !== "string").map(uneval).sort().toSource(),
523 "A symbol-keyed property on the " + classname +
524 " constructor has been changed! You need a security audit from an XPConnect peer");
526 let ctorProps = filterOut(desiredCtorProps, ctorPropsToSkip);
527 let ctorSymbols = filterOut(desiredCtorSymbols, ctorPropsToSkip.map(uneval));
528 let ctorCallables = ctorProps.filter(name => propertyIsGetter(localCtor, name, classname) ||
529 typeof localCtor[name] == 'function');
530 testCtorCallables(ctorCallables, xrayCtor, localCtor);
531 is(Object.getOwnPropertyNames(xrayCtor).sort().toSource(),
532 ctorProps.toSource(), "getOwnPropertyNames works on Xrayed ctors");
533 is(Object.getOwnPropertySymbols(xrayCtor).map(uneval).sort().toSource(),
534 ctorSymbols.toSource(), "getOwnPropertySymbols works on Xrayed ctors");
537 // We will need arraysEqual and testArrayIterators both in this global scope
538 // and in sandboxes.
539 function arraysEqual(arr1, arr2, reason) {
540 is(arr1.length, arr2.length, `${reason}; lengths should be equal`)
541 for (var i = 0; i < arr1.length; ++i) {
542 if (Array.isArray(arr2[i])) {
543 arraysEqual(arr1[i], arr2[i], `${reason}; item at index ${i}`);
544 } else {
545 is(arr1[i], arr2[i], `${reason}; item at index ${i} should be equal`);
550 function testArrayIterators(arrayLike, equivalentArray, reason) {
551 arraysEqual([...arrayLike], equivalentArray, `${reason}; spread operator`);
552 arraysEqual([...arrayLike.entries()], [...equivalentArray.entries()],
553 `${reason}; entries`);
554 arraysEqual([...arrayLike.keys()], [...equivalentArray.keys()],
555 `${reason}; keys`);
556 if (arrayLike.values) {
557 arraysEqual([...arrayLike.values()], equivalentArray,
558 `${reason}; values`);
561 var forEachCopy = [];
562 arrayLike.forEach(function(arg) { forEachCopy.push(arg); });
563 arraysEqual(forEachCopy, equivalentArray, `${reason}; forEach copy`);
565 var everyCopy = [];
566 arrayLike.every(function(arg) { everyCopy.push(arg); return true; });
567 arraysEqual(everyCopy, equivalentArray, `${reason}; every() copy`);
569 var filterCopy = [];
570 var filterResult = arrayLike.filter(function(arg) {
571 filterCopy.push(arg);
572 return true;
574 arraysEqual(filterCopy, equivalentArray, `${reason}; filter copy`);
575 arraysEqual([...filterResult], equivalentArray, `${reason}; filter result`);
577 var findCopy = [];
578 arrayLike.find(function(arg) { findCopy.push(arg); return false; });
579 arraysEqual(findCopy, equivalentArray, `${reason}; find() copy`);
581 var findIndexCopy = [];
582 arrayLike.findIndex(function(arg) { findIndexCopy.push(arg); return false; });
583 arraysEqual(findIndexCopy, equivalentArray, `${reason}; findIndex() copy`);
585 var mapCopy = [];
586 var mapResult = arrayLike.map(function(arg) { mapCopy.push(arg); return arg});
587 arraysEqual(mapCopy, equivalentArray, `${reason}; map() copy`);
588 arraysEqual([...mapResult], equivalentArray, `${reason}; map() result`);
590 var reduceCopy = [];
591 arrayLike.reduce(function(_, arg) { reduceCopy.push(arg); }, 0);
592 arraysEqual(reduceCopy, equivalentArray, `${reason}; reduce() copy`);
594 var reduceRightCopy = [];
595 arrayLike.reduceRight(function(_, arg) { reduceRightCopy.unshift(arg); }, 0);
596 arraysEqual(reduceRightCopy, equivalentArray, `${reason}; reduceRight() copy`);
598 var someCopy = [];
599 arrayLike.some(function(arg) { someCopy.push(arg); return false; });
600 arraysEqual(someCopy, equivalentArray, `${reason}; some() copy`);
603 function testDate() {
604 // toGMTString is handled oddly in the engine. We don't bother to support
605 // it over Xrays.
606 let propsToSkip = ['toGMTString'];
608 testXray('Date', new iwin.Date(), new iwin.Date(), propsToSkip);
610 // Test the self-hosted toLocaleString.
611 var d = new iwin.Date();
612 isnot(d.toLocaleString, Cu.unwaiveXrays(d.wrappedJSObject.toLocaleString), "Different function identities");
613 is(Cu.getGlobalForObject(d.toLocaleString), window, "Xray global is correct");
614 is(Cu.getGlobalForObject(d.wrappedJSObject.toLocaleString), iwin, "Underlying global is correct");
615 is(d.toLocaleString('de-DE'), d.wrappedJSObject.toLocaleString('de-DE'), "Results match");
618 var uniqueSymbol;
620 function testObject() {
621 testXray('Object', Cu.unwaiveXrays(Cu.waiveXrays(iwin).Object.create(new iwin.Object())),
622 new iwin.Object(), []);
624 // Construct an object full of tricky things.
625 let symbolProps = '';
626 uniqueSymbol = iwin.eval('var uniqueSymbol = Symbol("uniqueSymbol"); uniqueSymbol');
627 symbolProps = `, [uniqueSymbol]: 43,
628 [Symbol.for("registrySymbolProp")]: 44`;
629 var trickyObject =
630 iwin.eval(`(function() {
631 var o = new Object({
632 primitiveProp: 42, objectProp: { foo: 2 },
633 xoProp: top, hasOwnProperty: 10,
634 get getterProp() { return 2; },
635 set setterProp(x) { },
636 get getterSetterProp() { return 3; },
637 set getterSetterProp(x) { },
638 callableProp: function() { },
639 nonXrayableProp: new Map()[Symbol.iterator]()
640 ${symbolProps}
642 Object.defineProperty(o, "nonConfigurableGetterSetterProp",
643 { get: function() { return 5; }, set: function() {} });
644 return o;
645 })()`);
646 testTrickyObject(trickyObject);
649 function testArray() {
650 // The |length| property is generally very weird, especially with respect
651 // to its behavior on the prototype. Array.prototype is actually an Array
652 // instance, and therefore has a vestigial .length. But we don't want to
653 // show that over Xrays, and generally want .length to just appear as an
654 // |own| data property. So we add it to the ignore list here, and check it
655 // separately.
657 // |Symbol.unscopables| should in principle be exposed, but it is
658 // inconvenient (as it's a data property, unsupported by ClassSpec) and
659 // low value.
660 let propsToSkip = ['length', Symbol.unscopables];
662 testXray('Array', new iwin.Array(20), new iwin.Array(), propsToSkip);
664 let symbolProps = '';
665 uniqueSymbol = iwin.eval('var uniqueSymbol = Symbol("uniqueSymbol"); uniqueSymbol');
666 symbolProps = `trickyArray[uniqueSymbol] = 43;
667 trickyArray[Symbol.for("registrySymbolProp")] = 44;`;
668 var trickyArray =
669 iwin.eval(`var trickyArray = [];
670 trickyArray.primitiveProp = 42;
671 trickyArray.objectProp = { foo: 2 };
672 trickyArray.xoProp = top;
673 trickyArray.hasOwnProperty = 10;
674 Object.defineProperty(trickyArray, 'getterProp', { get: function() { return 2; }});
675 Object.defineProperty(trickyArray, 'setterProp', { set: function(x) {}});
676 Object.defineProperty(trickyArray, 'getterSetterProp', { get: function() { return 3; }, set: function(x) {}, configurable: true});
677 Object.defineProperty(trickyArray, 'nonConfigurableGetterSetterProp', { get: function() { return 5; }, set: function(x) {}});
678 trickyArray.callableProp = function() {};
679 trickyArray.nonXrayableProp = new Map()[Symbol.iterator]();
680 ${symbolProps}
681 trickyArray;`);
683 // Test indexed access.
684 trickyArray.wrappedJSObject[9] = "some indexed property";
685 is(trickyArray[9], "some indexed property", "indexed properties work correctly over Xrays");
686 is(trickyArray.length, 10, "Length works correctly over Xrays");
687 checkThrows(function() { "use strict"; delete trickyArray.length; }, /config/, "Can't delete non-configurable 'length' property");
688 delete trickyArray[9];
689 is(trickyArray[9], undefined, "Delete works correctly over Xrays");
690 is(trickyArray.wrappedJSObject[9], undefined, "Delete works correctly over Xrays (viewed via waiver)");
691 is(trickyArray.length, 10, "length doesn't change");
692 trickyArray[11] = "some other indexed property";
693 is(trickyArray.length, 12, "length now changes");
694 is(trickyArray.wrappedJSObject[11], "some other indexed property");
695 trickyArray.length = 0;
696 is(trickyArray.length, 0, "Setting length works over Xray");
697 is(trickyArray[11], undefined, "Setting length truncates over Xray");
698 Object.defineProperty(trickyArray, 'length', { configurable: false, enumerable: false, writable: false, value: 0 });
699 trickyArray[1] = "hi";
700 is(trickyArray.length, 0, "Length remains non-writable");
701 is(trickyArray[1], undefined, "Frozen length forbids new properties");
702 is(trickyArray instanceof iwin.Array, true, "instanceof should work across xray wrappers.");
703 testTrickyObject(trickyArray);
705 testArrayIterators(new iwin.Array(1, 1, 2, 3, 5), [1, 1, 2, 3, 5]);
708 // Parts of this function are kind of specific to testing Object, but we factor
709 // it out so that we can re-use the trickyObject stuff on Arrays.
710 function testTrickyObject(trickyObject) {
712 // Make sure it looks right under the hood.
713 is(trickyObject.wrappedJSObject.getterProp, 2, "Underlying object has getter");
714 is(Cu.unwaiveXrays(trickyObject.wrappedJSObject.xoProp), top, "Underlying object has xo property");
716 // Test getOwnPropertyNames.
717 var expectedNames = ['objectProp', 'primitiveProp'];
718 if (trickyObject instanceof iwin.Array)
719 expectedNames.push('length');
720 is(Object.getOwnPropertyNames(trickyObject).sort().toSource(),
721 expectedNames.sort().toSource(), "getOwnPropertyNames should be filtered correctly");
722 var expectedSymbols = [Symbol.for("registrySymbolProp"), uniqueSymbol];
723 is(Object.getOwnPropertySymbols(trickyObject).map(uneval).sort().toSource(),
724 expectedSymbols.map(uneval).sort().toSource(),
725 "getOwnPropertySymbols should be filtered correctly");
727 // Test that cloning uses the Xray view.
728 var cloned = Cu.cloneInto(trickyObject, this);
729 is(Object.getOwnPropertyNames(cloned).sort().toSource(),
730 expectedNames.sort().toSource(), "structured clone should use the Xray view");
731 is(Object.getOwnPropertySymbols(cloned).map(uneval).sort().toSource(),
732 "[]", "structured cloning doesn't clone symbol-keyed properties yet");
734 // Test iteration and in-place modification. Beware of 'expando', which is the property
735 // we placed on the xray proto.
736 var propCount = 0;
737 for (let prop in trickyObject) {
738 if (prop == 'primitiveProp')
739 trickyObject[prop] = trickyObject[prop] - 10;
740 if (prop != 'expando') {
741 // eslint-disable-next-line no-self-assign
742 trickyObject[prop] = trickyObject[prop];
744 ++propCount;
746 is(propCount, 3, "Should iterate the correct number of times");
748 // Test Object.keys.
749 is(Object.keys(trickyObject).sort().toSource(),
750 ['objectProp', 'primitiveProp'].toSource(), "Object.keys should be filtered correctly");
752 // Test getOwnPropertyDescriptor.
753 is(trickyObject.primitiveProp, 32, "primitive prop works");
754 is(trickyObject.objectProp.foo, 2, "object prop works");
755 is(typeof trickyObject.callableProp, 'undefined', "filtering works correctly");
756 is(Object.getOwnPropertyDescriptor(trickyObject, 'primitiveProp').value, 32, "getOwnPropertyDescriptor works");
757 is(Object.getOwnPropertyDescriptor(trickyObject, 'xoProp'), undefined, "filtering works with getOwnPropertyDescriptor");
759 // Test defineProperty.
761 trickyObject.primitiveSetByXray = 'fourty two';
762 is(trickyObject.primitiveSetByXray, 'fourty two', "Can set primitive correctly over Xray (ready via Xray)");
763 is(trickyObject.wrappedJSObject.primitiveSetByXray, 'fourty two', "Can set primitive correctly over Xray (ready via Waiver)");
765 var newContentObject = iwin.eval('new Object({prop: 99, get getterProp() { return 2; }})');
766 trickyObject.objectSetByXray = newContentObject;
767 is(trickyObject.objectSetByXray.prop, 99, "Can set object correctly over Xray (ready via Xray)");
768 is(trickyObject.wrappedJSObject.objectSetByXray.prop, 99, "Can set object correctly over Xray (ready via Waiver)");
769 checkThrows(function() { trickyObject.rejectedProp = {foo: 33}}, /cross-origin object/,
770 "Should reject privileged object property definition");
772 // Test JSON.stringify.
773 var jsonStr = JSON.stringify(newContentObject);
774 ok(/prop/.test(jsonStr), "JSON stringification should work: " + jsonStr);
776 // Test deletion.
777 delete newContentObject.prop;
778 ok(!newContentObject.hasOwnProperty('prop'), "Deletion should work");
779 ok(!newContentObject.wrappedJSObject.hasOwnProperty('prop'), "Deletion should forward");
780 delete newContentObject.getterProp;
781 ok(newContentObject.wrappedJSObject.hasOwnProperty('getterProp'), "Deletion be no-op for filtered property");
783 // We should be able to overwrite an existing accessor prop and convert it
784 // to a value prop.
785 is(trickyObject.wrappedJSObject.getterSetterProp, 3, "Underlying object has getter");
786 is(trickyObject.getterSetterProp, undefined, "Filtering properly over Xray");
787 trickyObject.getterSetterProp = 'redefined';
788 is(trickyObject.getterSetterProp, 'redefined', "Redefinition works");
789 is(trickyObject.wrappedJSObject.getterSetterProp, 'redefined', "Redefinition forwards");
791 // We should NOT be able to overwrite an existing non-configurable accessor
792 // prop, though.
793 is(trickyObject.wrappedJSObject.nonConfigurableGetterSetterProp, 5,
794 "Underlying object has getter");
795 is(trickyObject.nonConfigurableGetterSetterProp, undefined,
796 "Filtering properly over Xray here too");
797 is((trickyObject.nonConfigurableGetterSetterProp = 'redefined'), 'redefined',
798 "Assigning to non-configurable prop should fail silently in non-strict mode");
799 checkThrows(function() {
800 "use strict";
801 trickyObject.nonConfigurableGetterSetterProp = 'redefined';
802 }, /config/, "Should throw when redefining non-configurable prop in strict mode");
803 is(trickyObject.nonConfigurableGetterSetterProp, undefined,
804 "Redefinition should have failed");
805 is(trickyObject.wrappedJSObject.nonConfigurableGetterSetterProp, 5,
806 "Redefinition really should have failed");
808 checkThrows(function() { trickyObject.hasOwnProperty = 33; }, /shadow/,
809 "Should reject shadowing of pre-existing inherited properties over Xrays");
811 checkThrows(function() { Object.defineProperty(trickyObject, 'rejectedProp', { get() { return undefined; }}); },
812 /accessor property/, "Should reject accessor property definition");
815 function testTypedArrays() {
816 // We don't invoke testXray with %TypedArray%, because that function isn't
817 // set up to deal with "anonymous" dependent classes (that is, classes not
818 // visible as a global property, which %TypedArray% is not), and fixing it
819 // up is more trouble than it's worth.
821 var typedArrayProto = Object.getPrototypeOf(Int8Array.prototype);
823 var desiredInheritedProps = Object.getOwnPropertyNames(typedArrayProto).sort();
824 var inheritedProps =
825 filterOut(desiredInheritedProps, ["BYTES_PER_ELEMENT", "constructor"]);
827 var inheritedCallables =
828 inheritedProps.filter(name => (propertyIsGetter(typedArrayProto, name) ||
829 typeof typedArrayProto[name] === "function") &&
830 name !== "constructor");
832 for (let c of typedArrayClasses) {
833 var t = new iwin[c](10);
834 checkThrows(function() { t[2]; }, /performant/, "direct property-wise reading of typed arrays forbidden over Xrays");
835 checkThrows(function() { t[2] = 3; }, /performant/, "direct property-wise writing of typed arrays forbidden over Xrays");
836 var wesb = new Cu.Sandbox([iwin], {isWebExtensionContentScript: true});
837 wesb.t = t;
838 wesb.eval('t[2] = 3');
839 is(wesb.eval('t.wrappedJSObject[2]'), 3, "direct property-wise writing of typed arrays allowed for WebExtension content scripts");
840 is(wesb.eval('t[2]'), 3, "direct property-wise reading and writing of typed arrays allowed for WebExtensions content scripts");
842 t.wrappedJSObject[2] = 3;
843 is(t.wrappedJSObject[2], 3, "accessing elements over waivers works");
844 t.wrappedJSObject.expando = 'hi';
845 is(t.wrappedJSObject.expando, 'hi', "access expandos over waivers works");
846 is(Cu.cloneInto(t, window)[2], 3, "cloneInto works");
847 is(Cu.cloneInto(t, window).expando, undefined, "cloneInto does not copy expandos");
848 is(Object.getOwnPropertyNames(t).sort().toSource(),
849 '["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]',
850 "Only indexed properties visible over Xrays");
851 Object.defineProperty(t.wrappedJSObject, 'length', {value: 42});
852 is(t.wrappedJSObject.length, 42, "Set tricky expando")
853 is(t.length, 10, "Length accessor works over Xrays")
854 is(t.byteLength, t.length * window[c].prototype.BYTES_PER_ELEMENT, "byteLength accessor works over Xrays")
856 // Can create TypedArray from content ArrayBuffer
857 var buffer = new iwin.ArrayBuffer(8);
858 new window[c](buffer);
860 var xray = new iwin[c](0);
861 var xrayTypedArrayProto = Object.getPrototypeOf(Object.getPrototypeOf(xray));
862 testProtoCallables(inheritedCallables, new iwin[c](0), xrayTypedArrayProto, typedArrayProto);
864 // When testing iterators, make sure to do so from inside our web
865 // extension sandbox, since from chrome we can't poke their indices. Note
866 // that we have to actually recreate our functions that touch typed array
867 // indices inside the sandbox, not just export them, because otherwise
868 // they'll just run with our principal anyway.
870 // But we do want to export is(), since we want ours called.
871 wesb.eval(String(arraysEqual));
872 wesb.eval(String(testArrayIterators));
873 Cu.exportFunction(is, wesb,
874 { defineAs: "is" });
875 wesb.eval('testArrayIterators(t, [0, 0, 3, 0, 0, 0, 0, 0, 0, 0])');
879 function testErrorObjects() {
880 // We only invoke testXray with Error, because that function isn't set up
881 // to deal with dependent classes and fixing it up is more trouble than
882 // it's worth.
883 testXray('Error', new iwin.Error('some error message'), new iwin.Error());
885 // Make sure that the dependent classes have their prototypes set up correctly.
886 for (let c of errorObjectClasses.filter(x => x != "Error")) {
887 var args = ['some message'];
888 if (c === 'AggregateError') {
889 // AggregateError's first argument is the list of aggregated errors.
890 args.unshift(new iwin.Array('error 1', 'error 2'));
892 var e = new iwin[c](...args);
893 is(Object.getPrototypeOf(e).name, c, "Prototype has correct name");
894 is(Object.getPrototypeOf(Object.getPrototypeOf(e)), iwin.Error.prototype, "Dependent prototype set up correctly");
895 is(e.name, c, "Exception name inherited correctly");
897 function testProperty(name, criterion, goodReplacement, faultyReplacement) {
898 ok(criterion(e[name]), name + " property is correct: " + e[name]);
899 e.wrappedJSObject[name] = goodReplacement;
900 is(e[name], goodReplacement, name + " property ok after replacement: " + goodReplacement);
901 e.wrappedJSObject[name] = faultyReplacement;
902 is(e[name], name == 'message' ? "" : undefined, name + " property skipped after suspicious replacement");
904 testProperty('message', x => x == 'some message', 'some other message', 42);
905 testProperty('fileName', x => x == '', 'otherFilename.html', new iwin.Object());
906 testProperty('columnNumber', x => x == 1, 99, 99.5);
907 testProperty('lineNumber', x => x == 0, 50, 'foo');
909 if (c === 'AggregateError') {
910 let {errors} = e;
911 is(errors.length, 2, "errors property has the correct length");
912 is(errors[0], 'error 1', "errors[0] has the correct value");
913 is(errors[1], 'error 2', "errors[1] has the correct value");
915 e.wrappedJSObject.errors = 42;
916 is(e.wrappedJSObject.errors, 42, "errors is a plain data property");
917 is(e.errors, 42, "visible over Xrays");
920 // Note - an Exception newed via Xrays is going to have an empty stack given the
921 // current semantics and implementation. This tests the current behavior, but that
922 // may change in bug 1036527 or similar.
924 // Furthermore, xrays should always return an error's original stack, and
925 // not overwrite it.
926 var stack = e.stack;
927 ok(/^\s*$/.test(stack), "stack property should be correct");
928 e.wrappedJSObject.stack = "not a stack";
929 is(e.stack, stack, "Xrays should never get an overwritten stack property.");
931 // Test the .cause property is correctly handled, too.
932 if (isNightlyBuild) {
933 let cause = 'error cause';
934 let options = new iwin.Object();
935 options.cause = cause;
936 args.push(options);
938 let e = new iwin[c](...args);
939 is(e.cause, cause);
941 e.wrappedJSObject.cause = 42;
942 is(e.wrappedJSObject.cause, 42, "cause is a plain data property");
943 is(e.cause, 42, "visible over Xrays");
948 function testRegExp() {
949 // RegExp statics are very weird, and in particular RegExp has static
950 // properties that have to do with the last regexp execution in the global.
951 // Xraying those makes no sense, so we just skip constructor properties for
952 // RegExp xrays.
953 // RegExp[@@species] is affected by above skip, but we don't fix it until
954 // compelling use-case appears, as supporting RegExp[@@species] while
955 // skipping other static properties makes things complicated.
956 // Since RegExp.escape is a method, there's no obvious reason to skip it,
957 // but it would require some changes in the Xray code (would need to special
958 // case it in xpc::JSXrayTraits::resolveOwnProperty and
959 // xpc::JSXrayTraits::enumerateNames) that are not necessarily worth the effort
960 // since it is a static method with no state.
961 let ctorPropsToSkip = ["escape", "input", "lastMatch", "lastParen",
962 "leftContext", "rightContext", "$1", "$2", "$3",
963 "$4", "$5", "$6", "$7", "$8", "$9", "$_", "$&",
964 "$+", "$`", "$'", Symbol.species];
965 testXray('RegExp', new iwin.RegExp('foo'), new iwin.RegExp(), [],
966 ctorPropsToSkip);
968 // Test the self-hosted |flags| property, toString, and toSource.
969 for (var flags of ["", "g", "i", "m", "y", "gimy"]) {
970 var re = new iwin.RegExp("foo", flags);
971 is(re.flags, re.wrappedJSObject.flags, "Results match");
973 isnot(re.toString, Cu.unwaiveXrays(re.wrappedJSObject.toString), "Different function identities");
974 is(Cu.getGlobalForObject(re.toString), window, "Xray global is correct");
975 is(Cu.getGlobalForObject(re.wrappedJSObject.toString), iwin, "Underlying global is correct");
976 is(re.toString(), re.wrappedJSObject.toString(), "Results match");
978 isnot(re.toSource, Cu.unwaiveXrays(re.wrappedJSObject.toSource), "Different function identities");
979 is(Cu.getGlobalForObject(re.toSource), window, "Xray global is correct");
980 if (re.wrappedJSObject.toSource) {
981 is(Cu.getGlobalForObject(re.wrappedJSObject.toSource), iwin, "Underlying global is correct");
982 is(re.toSource(), re.wrappedJSObject.toSource(), "Results match");
985 // Test with modified flags accessors
986 iwin.eval(`
987 var props = ["global", "ignoreCase", "multiline", "sticky", "source", "unicode"];
988 var origDescs = {};
989 for (var prop of props) {
990 origDescs[prop] = Object.getOwnPropertyDescriptor(RegExp.prototype, prop);
991 Object.defineProperty(RegExp.prototype, prop, {
992 get: function() {
993 throw new Error("modified accessor is called");
998 try {
999 is(re.flags, flags, "Unmodified flags accessors are called");
1000 is(re.toString(), "/foo/" + flags, "Unmodified flags and source accessors are called");
1001 is(re.toSource(), "/foo/" + flags, "Unmodified flags and source accessors are called");
1002 } finally {
1003 iwin.eval(`
1004 for (var prop of props) {
1005 Object.defineProperty(RegExp.prototype, prop, origDescs[prop]);
1012 // Note: this is a small set of basic tests. More in-depth tests are located
1013 // in test_promise_xrays.html.
1014 function testPromise() {
1015 testXray('Promise', new iwin.Promise(function(){}), new iwin.Promise(function(){}));
1017 // Test catch and then.
1018 var pr = new iwin.Promise(function(){});
1019 isnot(pr.catch, Cu.unwaiveXrays(pr.wrappedJSObject.catch), "Different function identities");
1020 is(Cu.getGlobalForObject(pr.catch), window, "Xray global is correct");
1021 is(Cu.getGlobalForObject(pr.wrappedJSObject.catch), iwin, "Underlying global is correct");
1023 isnot(pr.then, Cu.unwaiveXrays(pr.wrappedJSObject.then), "Different function identities");
1024 is(Cu.getGlobalForObject(pr.then), window, "Xray global is correct");
1025 is(Cu.getGlobalForObject(pr.wrappedJSObject.then), iwin, "Underlying global is correct");
1028 function testArrayBuffer() {
1029 let constructors = ['ArrayBuffer'];
1031 for (const c of constructors) {
1032 testXray(c, new iwin[c](0), new iwin[c](12));
1034 var t = new iwin[c](12);
1035 is(t.byteLength, 12, `${c} byteLength is correct`);
1037 is(t.slice(4).byteLength, 8, `${c} byteLength is correct after slicing`);
1038 is(Cu.getGlobalForObject(t.slice(4)), iwin, "Slice results lives in the target compartment");
1039 is(Object.getPrototypeOf(t.slice(4)), iwin[c].prototype, "Slice results proto lives in target compartment")
1041 var i32Array = new Int32Array(t);
1042 // i32Array is going to be created in the buffer's target compartment,
1043 // but usually this is unobservable, because the proto is set to
1044 // the current compartment's prototype.
1045 // However Xrays ignore the object's proto and claim its proto is
1046 // the default proto for that class in the relevant compartment,
1047 // so see through this proto hack.
1048 todo_is(Object.getPrototypeOf(i32Array), Int32Array.prototype, "Int32Array has correct proto");
1049 is(i32Array.length, 3, `Int32Array created from Xray ${c} has the correct length`);
1050 is(i32Array.buffer, t, "Int32Array has the correct buffer that we passed in");
1052 i32Array = new iwin.Int32Array(t);
1053 is(Object.getPrototypeOf(i32Array), iwin.Int32Array.prototype, "Xray Int32Array has correct proto");
1054 is(i32Array.length, 3, `Xray Int32Array created from Xray ${c} has the correct length`);
1055 is(i32Array.buffer, t, "Xray Int32Array has the correct buffer that we passed in");
1057 t = (new iwin.Int32Array(2)).buffer;
1058 is(t.byteLength, 8, `Can access ${c} returned by buffer property`);
1062 function testMap() {
1063 testXray('Map', new iwin.Map(), new iwin.Map());
1065 var t = iwin.eval(`new Map([[1, "a"], [null, "b"]])`);
1066 is(t.size, 2, "Map size is correct");
1067 is(t.get(1), "a", "Key 1 has the correct value");
1068 is(t.get(null), "b", "Key null has the correct value");
1069 is(t.has(1), true, "Has Key 1");
1070 is(t.set(3, 5).get(3), 5, "Correctly sets key");
1071 is(t.delete(null), true, "Key null can be deleted");
1073 let values = [];
1074 t.forEach((value) => values.push(value));
1075 is(values.toString(), "a,5", "forEach enumerates values correctly");
1077 t.clear();
1078 is(t.size, 0, "Map is empty after calling clear");
1081 function testSet() {
1082 testXray('Set', new iwin.Set(), new iwin.Set());
1084 var t = iwin.eval(`new Set([1, null])`);
1085 is(t.size, 2, "Set size is correct");
1086 is(t.has(1), true, "Contains 1");
1087 is(t.has(null), true, "Contains null");
1088 is(t.add(5).has(5), true, "Can add value to set");
1089 is(t.delete(null), true, "Value null can be deleted");
1091 let values = [];
1092 t.forEach(value => values.push(value));
1093 is(values.toString(), "1,5", "forEach enumerates values correctly");
1095 t.clear();
1096 is(t.size, 0, "Set is empty after calling clear");
1099 function testWeakMap() {
1100 testXray('WeakMap', new iwin.WeakMap(), new iwin.WeakMap());
1102 var key1 = iwin.eval(`var key1 = {}; key1`);
1103 var key2 = iwin.eval(`var key2 = []; key2`);
1104 var key3 = iwin.eval(`var key3 = /a/; key3`);
1105 var key4 = {};
1106 var key5 = [];
1107 var t = iwin.eval(`new WeakMap([[key1, "a"], [key2, "b"]])`);
1108 is(t.get(key1), "a", "key1 has the correct value");
1109 is(t.get(key2), "b", "key2 has the correct value");
1110 is(t.has(key1), true, "Has key1");
1111 is(t.has(key3), false, "Doesn't have key3");
1112 is(t.has(key5), false, "Doesn't have key5");
1113 is(t.set(key4, 5).get(key4), 5, "Correctly sets key");
1114 is(t.get(key1), "a", "key1 has the correct value after modification");
1115 is(t.get(key2), "b", "key2 has the correct value after modification");
1116 is(t.delete(key1), true, "key1 can be deleted");
1117 is(t.delete(key2), true, "key2 can be deleted");
1118 is(t.delete(key3), false, "key3 cannot be deleted");
1119 is(t.delete(key4), true, "key4 can be deleted");
1120 is(t.delete(key5), false, "key5 cannot be deleted");
1123 function testWeakSet() {
1124 testXray('WeakSet', new iwin.WeakSet(), new iwin.WeakSet());
1126 var key1 = iwin.eval(`var key1 = {}; key1`);
1127 var key2 = iwin.eval(`var key2 = []; key2`);
1128 var key3 = iwin.eval(`var key3 = /a/; key3`);
1129 var key4 = {};
1130 var key5 = [];
1131 var t = iwin.eval(`new WeakSet([key1, key2])`);
1132 is(t.has(key1), true, "Has key1");
1133 is(t.has(key2), true, "Has key2");
1134 is(t.has(key3), false, "Doesn't have key3");
1135 is(t.has(key5), false, "Doesn't have key5");
1136 is(t.add(key4, 5).has(key4), true, "Can add value to set");
1137 is(t.delete(key1), true, "key1 can be deleted");
1138 is(t.delete(key2), true, "key2 can be deleted");
1139 is(t.delete(key3), false, "key3 cannot be deleted");
1140 is(t.delete(key4), true, "key4 can be deleted");
1141 is(t.delete(key5), false, "key5 cannot be deleted");
1144 function testProxy() {
1145 let ProxyCtor = iwin.Proxy;
1146 is(Object.getOwnPropertyNames(ProxyCtor).sort().toSource(),
1147 ["length", "name"].sort().toSource(),
1148 "Xrayed Proxy constructor should not have any properties");
1149 is(ProxyCtor.prototype, undefined, "Proxy.prototype should not be set");
1150 // Proxy.revocable can safely be exposed, but it is not.
1151 // Until it is supported, check that the property is not set.
1152 is(ProxyCtor.revocable, undefined, "Proxy.reflect is not set");
1155 function testDataView() {
1156 testXray('DataView', new iwin.DataView(new iwin.ArrayBuffer(4)),
1157 new iwin.DataView(new iwin.ArrayBuffer(8)));
1159 const versions = [() => iwin.eval(`new DataView(new ArrayBuffer(8))`),
1160 () => new DataView(new iwin.ArrayBuffer(8))];
1162 for (const constructor of versions) {
1163 let t = constructor();
1164 is(t.byteLength, 8, `byteLength correct for "${constructor}"`);
1165 is(t.byteOffset, 0, `byteOffset correct for "${constructor}"`);
1166 is(t.buffer.byteLength, 8, `buffer works for "${constructor}"`);
1168 const get = ["getInt8", "getUint8", "getInt16", "getUint16",
1169 "getInt32", "getUint32", "getFloat32", "getFloat64"];
1171 const set = ["setInt8", "setUint8", "setInt16", "setUint16",
1172 "setInt32", "setUint32", "setFloat32", "setFloat64"];
1174 for (const f of get) {
1175 let x = t[f](0);
1176 is(x, 0, `${f} is 0 for "${constructor}"`);
1177 is(typeof x, 'number', `typeof ${f} is number for "${constructor}"`);
1180 for (const f of ["getBigInt64", "getBigUint64"]) {
1181 let x = t[f](0);
1182 is(x, BigInt(0), `${f} is 0n for "${constructor}"`);
1183 is(typeof x, 'bigint', `typeof ${f} is bigint for "${constructor}"`);
1186 for (let i = 0; i < set.length; i++) {
1187 t[set[i]](0, 13);
1188 is(t[get[i]](0), 13, `${get[i]}(0) afer ${set[i]}(0, 13) is 13 for "${constructor}"`);
1191 for (const k of ["BigInt64", "BigUint64"]) {
1192 t["set" + k](0, BigInt(13));
1193 is(t["get" + k](0), BigInt(13), `get${k}(0) afer set${k}(0, 13n) is 13n for "${constructor}"`);
1198 function testNumber() {
1199 // We don't actually support Xrays to Number yet. This is testing
1200 // that case. If we add such support, we might have to start
1201 // using a different non-Xrayed class here, if we can find one.
1202 let xrayCtor = iwin.Number;
1203 is(Object.getOwnPropertyNames(xrayCtor).sort().toSource(),
1204 Object.getOwnPropertyNames(function() {}).sort().toSource(),
1205 "We should not have any static properties on a non-Xrayable constructor");
1206 is(xrayCtor.noSuchProperty, undefined,
1207 "Where did our noSuchProperty property come from?");
1211 </script>
1212 <iframe id="ifr" onload="go();" src="http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html" />
1213 </window>