%PDF- <> %âãÏÓ endobj 2 0 obj <> endobj 3 0 obj <>/ExtGState<>/ProcSet[/PDF/Text/ImageB/ImageC/ImageI] >>/Annots[ 28 0 R 29 0 R] /MediaBox[ 0 0 595.5 842.25] /Contents 4 0 R/Group<>/Tabs/S>> endobj ºaâÚÎΞ-ÌE1ÍØÄ÷{òò2ÿ ÛÖ^ÔÀá TÎ{¦?§®¥kuµùÕ5sLOšuY>endobj 2 0 obj<>endobj 2 0 obj<>endobj 2 0 obj<>endobj 2 0 obj<> endobj 2 0 obj<>endobj 2 0 obj<>es 3 0 R>> endobj 2 0 obj<> ox[ 0.000000 0.000000 609.600000 935.600000]/Fi endobj 3 0 obj<> endobj 7 1 obj<>/ProcSet[/PDF/Text/ImageB/ImageC/ImageI]>>/Subtype/Form>> stream
'use strict'; // Test that objects created by the TextEncoderStream and TextDecoderStream APIs // are created in the correct realm. The tests work by creating an iframe for // each realm and then posting Javascript to them to be evaluated. Inputs and // outputs are passed around via global variables in each realm's scope. // Async setup is required before creating any tests, so require done() to be // called. setup({explicit_done: true}); function createRealm() { let iframe = document.createElement('iframe'); const scriptEndTag = '<' + '/script>'; iframe.srcdoc = `<!doctype html> <script> onmessage = event => { if (event.source !== window.parent) { throw new Error('unexpected message with source ' + event.source); } eval(event.data); }; ${scriptEndTag}`; iframe.style.display = 'none'; document.body.appendChild(iframe); let realmPromiseResolve; const realmPromise = new Promise(resolve => { realmPromiseResolve = resolve; }); iframe.onload = () => { realmPromiseResolve(iframe.contentWindow); }; return realmPromise; } async function createRealms() { // All realms are visible on the global object so they can access each other. // The realm that the constructor function comes from. window.constructorRealm = await createRealm(); // The realm in which the constructor object is called. window.constructedRealm = await createRealm(); // The realm in which reading happens. window.readRealm = await createRealm(); // The realm in which writing happens. window.writeRealm = await createRealm(); // The realm that provides the definitions of Readable and Writable methods. window.methodRealm = await createRealm(); await evalInRealmAndWait(methodRealm, ` window.ReadableStreamDefaultReader = new ReadableStream().getReader().constructor; window.WritableStreamDefaultWriter = new WritableStream().getWriter().constructor; `); window.readMethod = methodRealm.ReadableStreamDefaultReader.prototype.read; window.writeMethod = methodRealm.WritableStreamDefaultWriter.prototype.write; } // In order for values to be visible between realms, they need to be // global. To prevent interference between tests, variable names are generated // automatically. const id = (() => { let nextId = 0; return () => { return `realmsId${nextId++}`; }; })(); // Eval string "code" in the content of realm "realm". Evaluation happens // asynchronously, meaning it hasn't happened when the function returns. function evalInRealm(realm, code) { realm.postMessage(code, window.origin); } // Same as evalInRealm() but returns a Promise which will resolve when the // function has actually. async function evalInRealmAndWait(realm, code) { const resolve = id(); const waitOn = new Promise(r => { realm[resolve] = r; }); evalInRealm(realm, code); evalInRealm(realm, `${resolve}();`); await waitOn; } // The same as evalInRealmAndWait but returns the result of evaluating "code" as // an expression. async function evalInRealmAndReturn(realm, code) { const myId = id(); await evalInRealmAndWait(realm, `window.${myId} = ${code};`); return realm[myId]; } // Constructs an object in constructedRealm and copies it into readRealm and // writeRealm. Returns the id that can be used to access the object in those // realms. |what| can contain constructor arguments. async function constructAndStore(what) { const objId = id(); // Call |constructorRealm|'s constructor from inside |constructedRealm|. writeRealm[objId] = await evalInRealmAndReturn( constructedRealm, `new parent.constructorRealm.${what}`); readRealm[objId] = writeRealm[objId]; return objId; } // Calls read() on the readable side of the TransformStream stored in // readRealm[objId]. Locks the readable side as a side-effect. function readInReadRealm(objId) { return evalInRealmAndReturn(readRealm, ` parent.readMethod.call(window.${objId}.readable.getReader())`); } // Calls write() on the writable side of the TransformStream stored in // writeRealm[objId], passing |value|. Locks the writable side as a // side-effect. function writeInWriteRealm(objId, value) { const valueId = id(); writeRealm[valueId] = value; return evalInRealmAndReturn(writeRealm, ` parent.writeMethod.call(window.${objId}.writable.getWriter(), window.${valueId})`); } window.onload = () => { createRealms().then(() => { runGenericTests('TextEncoderStream'); runTextEncoderStreamTests(); runGenericTests('TextDecoderStream'); runTextDecoderStreamTests(); done(); }); }; function runGenericTests(classname) { promise_test(async () => { const obj = await evalInRealmAndReturn( constructedRealm, `new parent.constructorRealm.${classname}()`); assert_equals(obj.constructor, constructorRealm[classname], 'obj should be in constructor realm'); }, `a ${classname} object should be associated with the realm the ` + 'constructor came from'); promise_test(async () => { const objId = await constructAndStore(classname); const readableGetterId = id(); readRealm[readableGetterId] = Object.getOwnPropertyDescriptor( methodRealm[classname].prototype, 'readable').get; const writableGetterId = id(); writeRealm[writableGetterId] = Object.getOwnPropertyDescriptor( methodRealm[classname].prototype, 'writable').get; const readable = await evalInRealmAndReturn( readRealm, `${readableGetterId}.call(${objId})`); const writable = await evalInRealmAndReturn( writeRealm, `${writableGetterId}.call(${objId})`); assert_equals(readable.constructor, constructorRealm.ReadableStream, 'readable should be in constructor realm'); assert_equals(writable.constructor, constructorRealm.WritableStream, 'writable should be in constructor realm'); }, `${classname}'s readable and writable attributes should come from the ` + 'same realm as the constructor definition'); } function runTextEncoderStreamTests() { promise_test(async () => { const objId = await constructAndStore('TextEncoderStream'); const writePromise = writeInWriteRealm(objId, 'A'); const result = await readInReadRealm(objId); await writePromise; assert_equals(result.constructor, constructorRealm.Object, 'result should be in constructor realm'); assert_equals(result.value.constructor, constructorRealm.Uint8Array, 'chunk should be in constructor realm'); }, 'the output chunks when read is called after write should come from the ' + 'same realm as the constructor of TextEncoderStream'); promise_test(async () => { const objId = await constructAndStore('TextEncoderStream'); const chunkPromise = readInReadRealm(objId); writeInWriteRealm(objId, 'A'); // Now the read() should resolve. const result = await chunkPromise; assert_equals(result.constructor, constructorRealm.Object, 'result should be in constructor realm'); assert_equals(result.value.constructor, constructorRealm.Uint8Array, 'chunk should be in constructor realm'); }, 'the output chunks when write is called with a pending read should come ' + 'from the same realm as the constructor of TextEncoderStream'); // There is not absolute consensus regarding what realm exceptions should be // created in. Implementations may vary. The expectations in exception-related // tests may change in future once consensus is reached. promise_test(async t => { const objId = await constructAndStore('TextEncoderStream'); // Read first to relieve backpressure. const readPromise = readInReadRealm(objId); await promise_rejects_js(t, constructorRealm.TypeError, writeInWriteRealm(objId, { toString() { return {}; } }), 'write TypeError should come from constructor realm'); return promise_rejects_js(t, constructorRealm.TypeError, readPromise, 'read TypeError should come from constructor realm'); }, 'TypeError for unconvertable chunk should come from constructor realm ' + 'of TextEncoderStream'); } function runTextDecoderStreamTests() { promise_test(async () => { const objId = await constructAndStore('TextDecoderStream'); const writePromise = writeInWriteRealm(objId, new Uint8Array([65])); const result = await readInReadRealm(objId); await writePromise; assert_equals(result.constructor, constructorRealm.Object, 'result should be in constructor realm'); // A string is not an object, so doesn't have an associated realm. Accessing // string properties will create a transient object wrapper belonging to the // current realm. So checking the realm of result.value is not useful. }, 'the result object when read is called after write should come from the ' + 'same realm as the constructor of TextDecoderStream'); promise_test(async () => { const objId = await constructAndStore('TextDecoderStream'); const chunkPromise = readInReadRealm(objId); writeInWriteRealm(objId, new Uint8Array([65])); // Now the read() should resolve. const result = await chunkPromise; assert_equals(result.constructor, constructorRealm.Object, 'result should be in constructor realm'); // A string is not an object, so doesn't have an associated realm. Accessing // string properties will create a transient object wrapper belonging to the // current realm. So checking the realm of result.value is not useful. }, 'the result object when write is called with a pending ' + 'read should come from the same realm as the constructor of TextDecoderStream'); promise_test(async t => { const objId = await constructAndStore('TextDecoderStream'); // Read first to relieve backpressure. const readPromise = readInReadRealm(objId); await promise_rejects_js( t, constructorRealm.TypeError, writeInWriteRealm(objId, {}), 'write TypeError should come from constructor realm' ); return promise_rejects_js( t, constructorRealm.TypeError, readPromise, 'read TypeError should come from constructor realm' ); }, 'TypeError for chunk with the wrong type should come from constructor ' + 'realm of TextDecoderStream'); promise_test(async t => { const objId = await constructAndStore(`TextDecoderStream('utf-8', {fatal: true})`); // Read first to relieve backpressure. const readPromise = readInReadRealm(objId); await promise_rejects_js( t, constructorRealm.TypeError, writeInWriteRealm(objId, new Uint8Array([0xff])), 'write TypeError should come from constructor realm' ); return promise_rejects_js( t, constructorRealm.TypeError, readPromise, 'read TypeError should come from constructor realm' ); }, 'TypeError for invalid chunk should come from constructor realm ' + 'of TextDecoderStream'); promise_test(async t => { const objId = await constructAndStore(`TextDecoderStream('utf-8', {fatal: true})`); // Read first to relieve backpressure. readInReadRealm(objId); // Write an unfinished sequence of bytes. const incompleteBytesId = id(); writeRealm[incompleteBytesId] = new Uint8Array([0xf0]); return promise_rejects_js( t, constructorRealm.TypeError, // Can't use writeInWriteRealm() here because it doesn't make it possible // to reuse the writer. evalInRealmAndReturn(writeRealm, ` (() => { const writer = window.${objId}.writable.getWriter(); parent.writeMethod.call(writer, window.${incompleteBytesId}); return parent.methodRealm.WritableStreamDefaultWriter.prototype .close.call(writer); })(); `), 'close TypeError should come from constructor realm' ); }, 'TypeError for incomplete input should come from constructor realm ' + 'of TextDecoderStream'); }