diff --git a/streams/transferable/readable-stream.html b/streams/transferable/readable-stream.html
new file mode 100644
index 00000000000000..59b57ce6723c10
--- /dev/null
+++ b/streams/transferable/readable-stream.html
@@ -0,0 +1,255 @@
+
+
+
+
+
+
+
+
diff --git a/streams/transferable/reason.html b/streams/transferable/reason.html
new file mode 100644
index 00000000000000..4251aa85b816bb
--- /dev/null
+++ b/streams/transferable/reason.html
@@ -0,0 +1,132 @@
+
+
+
+
+
+
diff --git a/streams/transferable/resources/echo-iframe.html b/streams/transferable/resources/echo-iframe.html
new file mode 100644
index 00000000000000..68f68503439fdb
--- /dev/null
+++ b/streams/transferable/resources/echo-iframe.html
@@ -0,0 +1,7 @@
+
+
+
diff --git a/streams/transferable/resources/echo-worker.js b/streams/transferable/resources/echo-worker.js
new file mode 100644
index 00000000000000..806c2371083399
--- /dev/null
+++ b/streams/transferable/resources/echo-worker.js
@@ -0,0 +1,2 @@
+// A worker that just transfers back any message that is sent to it.
+onmessage = evt => postMessage(evt.data, [evt.data]);
diff --git a/streams/transferable/resources/helpers.js b/streams/transferable/resources/helpers.js
new file mode 100644
index 00000000000000..05d481f277a387
--- /dev/null
+++ b/streams/transferable/resources/helpers.js
@@ -0,0 +1,121 @@
+'use strict';
+
+// Create a ReadableStream that will pass the tests in
+// testTransferredReadableStream(), below.
+function createOriginalReadableStream() {
+ return new ReadableStream({
+ start(controller) {
+ controller.enqueue('a');
+ controller.close();
+ }
+ });
+}
+
+// Common tests to roughly determine that |rs| is a correctly transferred
+// version of a stream created by createOriginalReadableStream().
+function testTransferredReadableStream(rs) {
+ assert_equals(rs.constructor, ReadableStream,
+ 'rs should be a ReadableStream in this realm');
+ assert_true(rs instanceof ReadableStream,
+ 'instanceof check should pass');
+
+ // Perform a brand-check on |rs| in the process of calling getReader().
+ const reader = ReadableStream.prototype.getReader.call(rs);
+
+ return reader.read().then(({value, done}) => {
+ assert_false(done, 'done should be false');
+ assert_equals(value, 'a', 'value should be "a"');
+ return reader.read();
+ }).then(({done}) => {
+ assert_true(done, 'done should be true');
+ });
+}
+
+function testMessage(msg) {
+ assert_array_equals(msg.ports, [], 'there should be no ports in the event');
+ return testTransferredReadableStream(msg.data);
+}
+
+function testMessageEvent(target) {
+ return new Promise((resolve, reject) => {
+ target.addEventListener('message', ev => {
+ try {
+ resolve(testMessage(ev));
+ } catch (e) {
+ reject(e);
+ }
+ }, {once: true});
+ });
+}
+
+function testMessageEventOrErrorMessage(target) {
+ return new Promise((resolve, reject) => {
+ target.addEventListener('message', ev => {
+ if (typeof ev.data === 'string') {
+ // Assume it's an error message and reject with it.
+ reject(ev.data);
+ return;
+ }
+
+ try {
+ resolve(testMessage(ev));
+ } catch (e) {
+ reject(e);
+ }
+ }, {once: true});
+ });
+}
+
+function checkTestResults(target) {
+ return new Promise((resolve, reject) => {
+ target.onmessage = msg => {
+ // testharness.js sends us objects which we need to ignore.
+ if (typeof msg.data !== 'string')
+ return;
+
+ if (msg.data === 'OK') {
+ resolve();
+ } else {
+ reject(msg.data);
+ }
+ };
+ });
+}
+
+// These tests assume that a transferred ReadableStream will behave the same
+// regardless of how it was transferred. This enables us to simply transfer the
+// stream to ourselves.
+function createTransferredReadableStream(underlyingSource) {
+ const original = new ReadableStream(underlyingSource);
+ const promise = new Promise((resolve, reject) => {
+ addEventListener('message', msg => {
+ const rs = msg.data;
+ if (rs instanceof ReadableStream) {
+ resolve(rs);
+ } else {
+ reject(new Error(`what is this thing: "${rs}"?`));
+ }
+ }, {once: true});
+ });
+ postMessage(original, '*', [original]);
+ return promise;
+}
+
+function recordingTransferredReadableStream(underlyingSource, strategy) {
+ const original = recordingReadableStream(underlyingSource, strategy);
+ const promise = new Promise((resolve, reject) => {
+ addEventListener('message', msg => {
+ const rs = msg.data;
+ if (rs instanceof ReadableStream) {
+ rs.events = original.events;
+ rs.eventsWithoutPulls = original.eventsWithoutPulls;
+ rs.controller = original.controller;
+ resolve(rs);
+ } else {
+ reject(new Error(`what is this thing: "${rs}"?`));
+ }
+ }, {once: true});
+ });
+ postMessage(original, '*', [original]);
+ return promise;
+}
diff --git a/streams/transferable/resources/receiving-shared-worker.js b/streams/transferable/resources/receiving-shared-worker.js
new file mode 100644
index 00000000000000..84f779c3db6e13
--- /dev/null
+++ b/streams/transferable/resources/receiving-shared-worker.js
@@ -0,0 +1,11 @@
+'use strict';
+importScripts('/resources/testharness.js', 'helpers.js');
+
+onconnect = evt => {
+ const port = evt.source;
+ const promise = testMessageEvent(port);
+ port.start();
+ promise
+ .then(() => port.postMessage('OK'))
+ .catch(err => port.postMessage(`BAD: ${err}`));
+};
diff --git a/streams/transferable/resources/receiving-worker.js b/streams/transferable/resources/receiving-worker.js
new file mode 100644
index 00000000000000..4ebb9c5f8fcec2
--- /dev/null
+++ b/streams/transferable/resources/receiving-worker.js
@@ -0,0 +1,7 @@
+'use strict';
+importScripts('/resources/testharness.js', 'helpers.js');
+
+const promise = testMessageEvent(self);
+promise
+ .then(() => postMessage('OK'))
+ .catch(err => postMessage(`BAD: ${err}`));
diff --git a/streams/transferable/resources/sending-shared-worker.js b/streams/transferable/resources/sending-shared-worker.js
new file mode 100644
index 00000000000000..e579077894d5b9
--- /dev/null
+++ b/streams/transferable/resources/sending-shared-worker.js
@@ -0,0 +1,12 @@
+'use strict';
+importScripts('helpers.js');
+
+onconnect = msg => {
+ const port = msg.source;
+ const orig = createOriginalReadableStream();
+ try {
+ port.postMessage(orig, [orig]);
+ } catch (e) {
+ port.postMessage(e.message);
+ }
+};
diff --git a/streams/transferable/resources/sending-worker.js b/streams/transferable/resources/sending-worker.js
new file mode 100644
index 00000000000000..0b79733f74d97b
--- /dev/null
+++ b/streams/transferable/resources/sending-worker.js
@@ -0,0 +1,5 @@
+'use strict';
+importScripts('helpers.js');
+
+const orig = createOriginalReadableStream();
+postMessage(orig, [orig]);
diff --git a/streams/transferable/resources/service-worker-iframe.html b/streams/transferable/resources/service-worker-iframe.html
new file mode 100644
index 00000000000000..348d067c926f58
--- /dev/null
+++ b/streams/transferable/resources/service-worker-iframe.html
@@ -0,0 +1,39 @@
+
+
+
+
+
diff --git a/streams/transferable/resources/service-worker.js b/streams/transferable/resources/service-worker.js
new file mode 100644
index 00000000000000..af76b6c11b4ed1
--- /dev/null
+++ b/streams/transferable/resources/service-worker.js
@@ -0,0 +1,30 @@
+'use strict';
+importScripts('/resources/testharness.js', 'helpers.js');
+
+onmessage = msg => {
+ const client = msg.source;
+ if (msg.data === 'SEND') {
+ sendingTest(client);
+ } else {
+ receivingTest(msg, client);
+ }
+};
+
+function sendingTest(client) {
+ const orig = createOriginalReadableStream();
+ try {
+ client.postMessage(orig, [orig]);
+ } catch (e) {
+ client.postMessage(e.message);
+ }
+}
+
+function receivingTest(msg, client) {
+ try {
+ msg.waitUntil(testMessage(msg)
+ .then(() => client.postMessage('OK'))
+ .catch(e => client.postMessage(`BAD: ${e}`)));
+ } catch (e) {
+ client.postMessage(`BAD: ${e}`);
+ }
+}
diff --git a/streams/transferable/service-worker.https.html b/streams/transferable/service-worker.https.html
new file mode 100644
index 00000000000000..2ca7f19c910f76
--- /dev/null
+++ b/streams/transferable/service-worker.https.html
@@ -0,0 +1,28 @@
+
+
+
+
+
+
diff --git a/streams/transferable/shared-worker.html b/streams/transferable/shared-worker.html
new file mode 100644
index 00000000000000..cd0415402d5018
--- /dev/null
+++ b/streams/transferable/shared-worker.html
@@ -0,0 +1,25 @@
+
+
+
+
+
+
diff --git a/streams/transferable/transform-stream.html b/streams/transferable/transform-stream.html
new file mode 100644
index 00000000000000..fbfbfe8fc1347a
--- /dev/null
+++ b/streams/transferable/transform-stream.html
@@ -0,0 +1,104 @@
+
+
+
+
+
+
diff --git a/streams/transferable/window.html b/streams/transferable/window.html
new file mode 100644
index 00000000000000..beaf548fe641c5
--- /dev/null
+++ b/streams/transferable/window.html
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
diff --git a/streams/transferable/worker.html b/streams/transferable/worker.html
new file mode 100644
index 00000000000000..c5dc9fc62f8cf2
--- /dev/null
+++ b/streams/transferable/worker.html
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
diff --git a/streams/transferable/writable-stream.html b/streams/transferable/writable-stream.html
new file mode 100644
index 00000000000000..adc6f457c27e87
--- /dev/null
+++ b/streams/transferable/writable-stream.html
@@ -0,0 +1,136 @@
+
+
+
+
+
+
+
+