1<!doctype html> 2<meta charset="utf-8"> 3<title>Ensure Stream objects are created in expected globals. </title> 4 5<script src="/resources/testharness.js"></script> 6<script src="/resources/testharnessreport.js"></script> 7 8<body></body> 9<script> 10// These tests are loosely derived from Gecko's readable-stream-globals.js, 11// which is a test case designed around the JS Streams implementation. 12// 13// Unlike in JS Streams, where function calls switch realms and change 14// the resulting global of the resulting objects, in WebIDL streams, 15// the global of an object is (currently underspecified, but) intended 16// to be the "Relevant Global" of the 'this' object. 17// 18// See: 19// https://html.spec.whatwg.org/multipage/webappapis.html#relevant 20// https://github.com/whatwg/streams/issues/1213 21"use strict" 22 23const iframe = document.createElement("iframe") 24document.body.append(iframe) 25 26const otherGlobal = iframe.contentWindow; 27const OtherReadableStream = otherGlobal.ReadableStream 28const OtherReadableStreamDefaultReader = otherGlobal.ReadableStreamDefaultReader; 29const OtherReadableStreamDefaultController = otherGlobal.ReadableStreamDefaultController; 30 31promise_test(async () => { 32 33 // Controllers 34 let controller; 35 let otherController; 36 37 // Get Stream Prototypes and controllers. 38 let streamController; 39 let stream = new ReadableStream({start(c) { streamController = c; }}); 40 41 const callReaderThisGlobal = OtherReadableStream.prototype.getReader.call(stream); 42 const newReaderOtherGlobal = new OtherReadableStreamDefaultReader(new ReadableStream()); 43 44 // Relevant Global Checking. 45 assert_equals(callReaderThisGlobal instanceof ReadableStreamDefaultReader, true, "reader was created in this global (.call)"); 46 assert_equals(newReaderOtherGlobal instanceof ReadableStreamDefaultReader, false, "reader was created in other global (new)"); 47 48 assert_equals(callReaderThisGlobal instanceof OtherReadableStreamDefaultReader, false, "reader isn't coming from other global (.call)" ); 49 assert_equals(newReaderOtherGlobal instanceof OtherReadableStreamDefaultReader, true, "reader isn't coming from other global (new)"); 50 51 assert_equals(otherController instanceof ReadableStreamDefaultController, false, "otherController should come from other gloal") 52 53 54 const request = callReaderThisGlobal.read(); 55 assert_equals(request instanceof Promise, true, "Promise comes from this global"); 56 57 streamController.close(); 58 const requestResult = await request; 59 assert_equals(requestResult instanceof Object, true, "returned object comes from this global"); 60}, "Stream objects created in expected globals") 61 62promise_test(async () => { 63 const stream = new ReadableStream(); 64 const otherReader = new OtherReadableStreamDefaultReader(stream); 65 const cancelPromise = ReadableStreamDefaultReader.prototype.cancel.call(otherReader); 66 assert_equals(cancelPromise instanceof Promise, true, "Cancel promise comes from the same global as the stream"); 67 assert_equals(await cancelPromise, undefined, "Cancel promise resolves to undefined"); 68}, "Cancel promise is created in same global as stream") 69 70// Refresh the streams and controllers. 71function getFreshInstances() { 72 let controller; 73 let otherController; 74 let stream = new ReadableStream({ 75 start(c) { 76 controller = c; 77 } 78 }); 79 80 new OtherReadableStream({ 81 start(c) { 82 otherController = c; 83 } 84 }); 85 86 return {stream, controller, otherController} 87} 88 89 90promise_test(async () => { 91 // Test closed promise on reader from another global (connected to a this-global stream) 92 const {stream, controller, otherController} = getFreshInstances(); 93 94 const otherReader = new OtherReadableStreamDefaultReader(stream); 95 const closedPromise = otherReader.closed; 96 assert_equals(closedPromise instanceof otherGlobal.Promise, true, "Closed promise in other global."); 97}, "Closed Promise in correct global"); 98 99promise_test(async () => { 100 const {stream, controller, otherController} = getFreshInstances(); 101 102 const otherReader = OtherReadableStream.prototype.getReader.call(stream); 103 assert_equals(otherReader instanceof ReadableStreamDefaultReader, true, "Reader comes from this global") 104 const request = otherReader.read(); 105 assert_equals(request instanceof Promise, true, "Promise still comes from stream's realm (this realm)"); 106 otherController.close.call(controller); 107 assert_equals((await request) instanceof otherGlobal.Object, true, "Object comes from other realm"); 108}, "Reader objects in correct global"); 109 110 111promise_test(async () => { 112 const {stream, controller, otherController} = getFreshInstances(); 113 assert_equals(controller.desiredSize, 1, "Desired size is expected"); 114 Object.defineProperty(controller, "desiredSize", 115 Object.getOwnPropertyDescriptor(OtherReadableStreamDefaultController.prototype, "desiredSize")); 116 assert_equals(controller.desiredSize, 1, "Grafting getter from other prototype still returns desired size"); 117}, "Desired size can be grafted from one prototype to another"); 118 119promise_test(async () => { 120 const {stream, controller, otherController} = getFreshInstances(); 121 122 // Make sure the controller close method returns the correct TypeError 123 const enqueuedError = { name: "enqueuedError" }; 124 controller.error(enqueuedError); 125 126 assert_throws_js(TypeError, () => controller.close(), "Current Global controller"); 127 assert_throws_js(otherGlobal.TypeError, () => otherController.close.call(controller), "Other global controller"); 128}, "Closing errored stream throws object in appropriate global") 129 130promise_test(async () => { 131 const {otherController} = getFreshInstances(); 132 // We can enqueue chunks from multiple globals 133 const chunk = { name: "chunk" }; 134 135 let controller; 136 const stream = new ReadableStream({ start(c) { controller = c; } }, { size() {return 1} }); 137 otherController.enqueue.call(controller, chunk); 138 otherController.enqueue.call(controller, new otherGlobal.Uint8Array(10)); 139 controller.enqueue(new otherGlobal.Uint8Array(10)); 140}, "Can enqueue chunks from multiple globals") 141 142promise_test(async () => { 143 const {stream, controller, otherController} = getFreshInstances(); 144 const chunk = { name: "chunk" }; 145 146 // We get the correct type errors out of a closed stream. 147 controller.close(); 148 assert_throws_js(TypeError, () => controller.enqueue(new otherGlobal.Uint8Array(10))); 149 assert_throws_js(otherGlobal.TypeError, () => otherController.enqueue.call(controller, chunk)); 150 assert_throws_js(otherGlobal.TypeError, () => otherController.enqueue.call(controller, new otherGlobal.Uint8Array(10))); 151}, "Correct errors and globals for closed streams"); 152 153 154promise_test(async () => { 155 const {stream, controller, otherController} = getFreshInstances(); 156 // Branches out of tee are in the correct global 157 158 const [branch1, branch2] = otherGlobal.ReadableStream.prototype.tee.call(stream); 159 assert_equals(branch1 instanceof ReadableStream, true, "Branch created in this global (as stream is in this global)"); 160 assert_equals(branch2 instanceof ReadableStream, true, "Branch created in this global (as stream is in this global)"); 161}, "Tee Branches in correct global"); 162</script> 163