Bug 1915045 Ensure decode tasks are scheduled on BufferingState::Enter() r=media...
[gecko.git] / js / xpconnect / tests / chrome / test_xrayToExplicitResourceManagement.html
blob643cd392375dee6263ce3e7f8b78cddecf0420b9
1 <!DOCTYPE html>
2 <html lang="en">
3 <!--
4 https://bugzilla.mozilla.org/show_bug.cgi?id=1929055
5 -->
7 <head>
8 <meta charset="UTF-8">
9 <title>Test for Bug 1929055</title>
10 <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
11 <link rel="stylesheet" type="text/css" href="chrome://global/skin" />
12 <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
13 <script>
14 var { AppConstants } = SpecialPowers.ChromeUtils.importESModule(
15 "resource://gre/modules/AppConstants.sys.mjs"
17 const isExplicitResourceManagementEnabled = AppConstants.ENABLE_EXPLICIT_RESOURCE_MANAGEMENT;
19 async function go() {
20 SimpleTest.waitForExplicitFinish();
22 let simpleConstructors = [
23 'DisposableStack',
24 'AsyncDisposableStack',
27 const errorObjectClasses = [
28 'SuppressedError',
31 simpleConstructors = simpleConstructors.concat(errorObjectClasses);
33 const iwin = document.getElementById('ifr').contentWindow;
35 if (!isExplicitResourceManagementEnabled) {
36 for (let c of simpleConstructors) {
37 is(iwin[c], undefined, "Constructors should not be exposed: " + c);
39 SimpleTest.finish();
40 return;
43 await SpecialPowers.pushPrefEnv({
44 set: [["javascript.options.experimental.explicit_resource_management", true]],
45 });
47 let global = Cu.getGlobalForObject.bind(Cu);
49 // Copied from js/xpconnect/tests/chrome/test_xrayToJS.xhtml
50 // ==== BEGIN ===
52 // Test constructors that can be instantiated with zero arguments, or with
53 // a fixed set of arguments provided using `...rest`.
54 for (let c of simpleConstructors) {
55 var args = [];
56 if (typeof c === 'object') {
57 args = c.args;
58 c = c.name;
60 ok(iwin[c], "Constructors appear: " + c);
61 is(iwin[c], Cu.unwaiveXrays(iwin.wrappedJSObject[c]),
62 "we end up with the appropriate constructor: " + c);
63 is(Cu.unwaiveXrays(Cu.waiveXrays(new iwin[c](...args)).constructor), iwin[c],
64 "constructor property is set up right: " + c);
65 let expectedProto = Cu.isOpaqueWrapper(new iwin[c](...args)) ?
66 iwin.Object.prototype : iwin[c].prototype;
67 is(Object.getPrototypeOf(new iwin[c](...args)), expectedProto,
68 "prototype is correct: " + c);
69 is(global(new iwin[c](...args)), iwin, "Got the right global: " + c);
72 var gPrototypeProperties = {};
73 var gConstructorProperties = {};
74 // Properties which cannot be invoked if callable without potentially
75 // rendering the object useless.
76 var gStatefulProperties = {};
78 function testProtoCallables(protoCallables, xray, xrayProto, localProto, callablesExcluded) {
79 // Handle undefined callablesExcluded.
80 let dontCall = callablesExcluded ?? [];
81 for (let name of protoCallables) {
82 info("Running tests for property: " + name);
83 // Test both methods and getter properties.
84 function lookupCallable(obj) {
85 let desc = null;
86 do {
87 desc = Object.getOwnPropertyDescriptor(obj, name);
88 if (desc) {
89 break;
91 obj = Object.getPrototypeOf(obj);
92 } while (obj);
93 return desc ? (desc.get || desc.value) : undefined;
95 ok(xrayProto.hasOwnProperty(name), `proto should have the property '${name}' as own`);
96 ok(!xray.hasOwnProperty(name), `instance should not have the property '${name}' as own`);
97 let method = lookupCallable(xrayProto);
98 is(typeof method, 'function', "Methods from Xrays are functions");
99 is(global(method), window, "Methods from Xrays are local");
100 ok(method instanceof Function, "instanceof works on methods from Xrays");
101 is(lookupCallable(xrayProto), method, "Holder caching works properly");
102 is(lookupCallable(xray), method, "Proto props resolve on the instance");
103 let local = lookupCallable(localProto);
104 is(method.length, local.length, "Function.length identical");
105 if (!method.length && !dontCall.includes(name)) {
106 is(method.call(xray) + "", local.call(xray) + "",
107 "Xray and local method results stringify identically");
109 // If invoking this method returns something non-Xrayable (opaque), the
110 // stringification is going to return [object Object].
111 // This happens for set[@@iterator] and other Iterator objects.
112 let callable = lookupCallable(xray.wrappedJSObject);
113 if (!Cu.isOpaqueWrapper(method.call(xray)) && callable) {
114 is(method.call(xray) + "",
115 callable.call(xray.wrappedJSObject) + "",
116 "Xray and waived method results stringify identically");
122 function testCtorCallables(ctorCallables, xrayCtor, localCtor) {
123 for (let name of ctorCallables) {
124 // Don't try to test Function.prototype, since that is in fact a callable
125 // but doesn't really do the things we expect callables to do here
126 // (e.g. it's in the wrong global, since it gets Xrayed itself).
127 if (name == "prototype" && localCtor.name == "Function") {
128 continue;
130 info(`Running tests for property: ${localCtor.name}.${name}`);
131 // Test both methods and getter properties.
132 function lookupCallable(obj) {
133 let desc = null;
134 do {
135 desc = Object.getOwnPropertyDescriptor(obj, name);
136 obj = Object.getPrototypeOf(obj);
137 } while (!desc);
138 return desc.get || desc.value;
141 ok(xrayCtor.hasOwnProperty(name), "ctor should have the property as own");
142 let method = lookupCallable(xrayCtor);
143 is(typeof method, 'function', "Methods from ctor Xrays are functions");
144 is(global(method), window, "Methods from ctor Xrays are local");
145 ok(method instanceof Function,
146 "instanceof works on methods from ctor Xrays");
147 is(lookupCallable(xrayCtor), method,
148 "Holder caching works properly on ctors");
149 let local = lookupCallable(localCtor);
150 is(method.length, local.length,
151 "Function.length identical for method from ctor");
152 // Don't try to do the return-value check on Date.now(), since there is
153 // absolutely no reason it should return the same value each time.
155 // Also don't try to do the return-value check on Regexp.lastMatch and
156 // Regexp["$&"] (which are aliases), because they get state off the global
157 // they live in, as far as I can tell, so testing them over Xrays will be
158 // wrong: on the Xray they will actaully get the lastMatch of _our_
159 // global, not the Xrayed one.
160 if (!method.length &&
161 !(localCtor.name == "Date" && name == "now") &&
162 !(localCtor.name == "RegExp" && (name == "lastMatch" || name == "$&"))) {
163 is(method.call(xrayCtor) + "", local.call(xrayCtor) + "",
164 "Xray and local method results stringify identically on constructors");
165 is(method.call(xrayCtor) + "",
166 lookupCallable(xrayCtor.wrappedJSObject).call(xrayCtor.wrappedJSObject) + "",
167 "Xray and waived method results stringify identically");
172 function filterOut(array, props) {
173 return array.filter(p => !props.includes(p));
176 function propertyIsGetter(obj, name) {
177 return !!Object.getOwnPropertyDescriptor(obj, name).get;
180 function constructorProps(arr) {
181 // Some props live on all constructors
182 return arr.concat(["prototype", "length", "name"]);
185 // Sort an array that may contain symbols as well as strings.
186 function sortProperties(arr) {
187 function sortKey(prop) {
188 return typeof prop + ":" + prop.toString();
190 arr.sort((a, b) => sortKey(a) < sortKey(b) ? -1 : +1);
193 function testXray(classname, xray, xray2, propsToSkip, ctorPropsToSkip = []) {
194 propsToSkip = propsToSkip || [];
195 let xrayProto = Object.getPrototypeOf(xray);
196 let localProto = window[classname].prototype;
197 let desiredProtoProps = Object.getOwnPropertyNames(localProto).sort();
199 is(desiredProtoProps.toSource(),
200 gPrototypeProperties[classname].filter(id => typeof id === "string").toSource(),
201 "A property on the " + classname +
202 " prototype has changed! You need a security audit from an XPConnect peer");
203 is(Object.getOwnPropertySymbols(localProto).map(uneval).sort().toSource(),
204 gPrototypeProperties[classname].filter(id => typeof id !== "string").map(uneval).sort().toSource(),
205 "A symbol-keyed property on the " + classname +
206 " prototype has been changed! You need a security audit from an XPConnect peer");
208 let protoProps = filterOut(desiredProtoProps, propsToSkip);
209 let protoCallables = protoProps.filter(name => propertyIsGetter(localProto, name, classname) ||
210 typeof localProto[name] == 'function' &&
211 name != 'constructor');
212 let callablesExcluded = gStatefulProperties[classname];
213 ok(!!protoCallables.length, "Need something to test");
214 is(xrayProto, iwin[classname].prototype, "Xray proto is correct");
215 is(xrayProto, xray.__proto__, "Proto accessors agree");
216 var protoProto = classname == "Object" ? null : iwin.Object.prototype;
217 is(Object.getPrototypeOf(xrayProto), protoProto, "proto proto is correct");
218 testProtoCallables(protoCallables, xray, xrayProto, localProto, callablesExcluded);
219 is(Object.getOwnPropertyNames(xrayProto).sort().toSource(),
220 protoProps.toSource(), "getOwnPropertyNames works");
221 is(Object.getOwnPropertySymbols(xrayProto).map(uneval).sort().toSource(),
222 gPrototypeProperties[classname].filter(id => typeof id !== "string" && !propsToSkip.includes(id))
223 .map(uneval).sort().toSource(),
224 "getOwnPropertySymbols works");
226 is(xrayProto.constructor, iwin[classname], "constructor property works");
228 xrayProto.expando = 42;
229 is(xray.expando, 42, "Xrayed instances see proto expandos");
230 is(xray2.expando, 42, "Xrayed instances see proto expandos");
232 // Now test constructors
233 let localCtor = window[classname];
234 let xrayCtor = xrayProto.constructor;
235 // We already checked that this is the same as iwin[classname]
237 let desiredCtorProps =
238 Object.getOwnPropertyNames(localCtor).sort();
239 is(desiredCtorProps.toSource(),
240 gConstructorProperties[classname].filter(id => typeof id === "string").toSource(),
241 "A property on the " + classname +
242 " constructor has changed! You need a security audit from an XPConnect peer");
243 let desiredCtorSymbols =
244 Object.getOwnPropertySymbols(localCtor).map(uneval).sort()
245 is(desiredCtorSymbols.toSource(),
246 gConstructorProperties[classname].filter(id => typeof id !== "string").map(uneval).sort().toSource(),
247 "A symbol-keyed property on the " + classname +
248 " constructor has been changed! You need a security audit from an XPConnect peer");
250 let ctorProps = filterOut(desiredCtorProps, ctorPropsToSkip);
251 let ctorSymbols = filterOut(desiredCtorSymbols, ctorPropsToSkip.map(uneval));
252 let ctorCallables = ctorProps.filter(name => propertyIsGetter(localCtor, name, classname) ||
253 typeof localCtor[name] == 'function');
254 testCtorCallables(ctorCallables, xrayCtor, localCtor);
255 is(Object.getOwnPropertyNames(xrayCtor).sort().toSource(),
256 ctorProps.toSource(), "getOwnPropertyNames works on Xrayed ctors");
257 is(Object.getOwnPropertySymbols(xrayCtor).map(uneval).sort().toSource(),
258 ctorSymbols.toSource(), "getOwnPropertySymbols works on Xrayed ctors");
261 // ==== END ===
263 gPrototypeProperties.DisposableStack = [
264 "adopt", "constructor", "defer", "dispose", "disposed", "move", "use",
265 Symbol.toStringTag, Symbol.dispose
267 gStatefulProperties.DisposableStack = ["dispose", Symbol.dispose, "move"];
268 gConstructorProperties.DisposableStack = constructorProps([]);
270 gPrototypeProperties.AsyncDisposableStack = [
271 "adopt", "constructor", "defer", "disposeAsync", "disposed", "move", "use",
272 Symbol.toStringTag, Symbol.asyncDispose
274 gStatefulProperties.AsyncDisposableStack = ["disposeAsync", Symbol.asyncDispose, "move"];
275 gConstructorProperties.AsyncDisposableStack = constructorProps([]);
277 // Sort all the lists so we don't need to mutate them later (or copy them
278 // again to sort them).
279 for (let c of Object.keys(gPrototypeProperties))
280 sortProperties(gPrototypeProperties[c]);
281 for (let c of Object.keys(gConstructorProperties))
282 sortProperties(gConstructorProperties[c]);
284 function testDisposableStack() {
285 testXray("DisposableStack", new iwin.DisposableStack(), new iwin.DisposableStack());
288 function testAsyncDisposableStack() {
289 testXray("AsyncDisposableStack", new iwin.AsyncDisposableStack(), new iwin.AsyncDisposableStack());
292 function testSuppressedError() {
293 const c = errorObjectClasses[0];
294 const args = ['error', 'suppressed', 'some message'];
295 var e = new iwin.SuppressedError(...args);
297 // Copied from js/xpconnect/tests/chrome/test_xrayToJS.xhtml
298 // ==== BEGIN ===
300 is(Object.getPrototypeOf(e).name, c, "Prototype has correct name");
301 is(Object.getPrototypeOf(Object.getPrototypeOf(e)), iwin.Error.prototype, "Dependent prototype set up correctly");
302 is(e.name, c, "Exception name inherited correctly");
304 function testProperty(name, criterion, goodReplacement, faultyReplacement) {
305 ok(criterion(e[name]), name + " property is correct: " + e[name]);
306 e.wrappedJSObject[name] = goodReplacement;
307 is(e[name], goodReplacement, name + " property ok after replacement: " + goodReplacement);
308 e.wrappedJSObject[name] = faultyReplacement;
309 is(e[name], name == 'message' ? "" : undefined, name + " property skipped after suspicious replacement");
311 testProperty('message', x => x == 'some message', 'some other message', 42);
312 testProperty('fileName', x => x == '', 'otherFilename.html', new iwin.Object());
313 testProperty('columnNumber', x => x == 1, 99, 99.5);
314 testProperty('lineNumber', x => x == 0, 50, 'foo');
316 // ==== END ===
318 e.wrappedJSObject.error = 42;
319 is(e.wrappedJSObject.error, 42, "errors is a plain data property");
320 is(e.error, 42, "error visible over Xrays");
322 e.wrappedJSObject.suppressed = 43;
323 is(e.wrappedJSObject.suppressed, 43, "suppressed is a plain data property");
324 is(e.suppressed, 43, "suppressed visible over Xrays");
327 testDisposableStack();
329 testAsyncDisposableStack();
331 testSuppressedError();
333 await SpecialPowers.popPrefEnv();
334 SimpleTest.finish();
336 </script>
337 </head>
339 <body>
340 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1929055">Mozilla Bug 1929055</a>
342 <iframe id="ifr" onload="go();" src="http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html" />
343 </body>
345 </html>