[Streams API] Shield streams against user replacing the Promise constructor
authorcalvaris@igalia.com <calvaris@igalia.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 4 Nov 2015 11:15:51 +0000 (11:15 +0000)
committercalvaris@igalia.com <calvaris@igalia.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 4 Nov 2015 11:15:51 +0000 (11:15 +0000)
https://bugs.webkit.org/show_bug.cgi?id=150887

Reviewed by Youenn Fablet.

Source/WebCore:

With this rework, we shield the Streams implementation against the user doing something like "Promise =
function() { /* do garbage */ };".

Test: streams/streams-promises.html.

* Modules/streams/ReadableStream.js:
(initializeReadableStream):
(cancel):
* Modules/streams/ReadableStreamInternals.js:
(privateInitializeReadableStreamReader):
(cancelReadableStream):
(readFromReadableStreamReader):
* Modules/streams/ReadableStreamReader.js:
(cancel):
(read):
(closed):
* Modules/streams/StreamInternals.js:
(promiseInvokeOrNoop):
(promiseInvokeOrFallbackOrNoop):
* Modules/streams/WritableStream.js:
(initializeWritableStream):
(abort):
(close):
(write):
(closed):
(ready):
* Modules/streams/WritableStreamInternals.js:
(syncWritableStreamStateWithQueue):

LayoutTests:

* streams/streams-promises-expected.txt: Added.
* streams/streams-promises.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@192021 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/streams/streams-promises-expected.txt [new file with mode: 0644]
LayoutTests/streams/streams-promises.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/Modules/streams/ReadableStream.js
Source/WebCore/Modules/streams/ReadableStreamInternals.js
Source/WebCore/Modules/streams/ReadableStreamReader.js
Source/WebCore/Modules/streams/StreamInternals.js
Source/WebCore/Modules/streams/WritableStream.js
Source/WebCore/Modules/streams/WritableStreamInternals.js

index f96ca17..9dcd501 100644 (file)
@@ -1,3 +1,13 @@
+2015-11-04  Xabier Rodriguez Calvar  <calvaris@igalia.com>
+
+        [Streams API] Shield streams against user replacing the Promise constructor
+        https://bugs.webkit.org/show_bug.cgi?id=150887
+
+        Reviewed by Youenn Fablet.
+
+        * streams/streams-promises-expected.txt: Added.
+        * streams/streams-promises.html: Added.
+
 2015-11-04  Antoine Quint  <graouts@apple.com>
 
         SVG: hit testing region for <text> elements is incorrect
diff --git a/LayoutTests/streams/streams-promises-expected.txt b/LayoutTests/streams/streams-promises-expected.txt
new file mode 100644 (file)
index 0000000..d5dc4b9
--- /dev/null
@@ -0,0 +1,3 @@
+
+PASS Streams can be built even if Promise constructor is replaced 
+
diff --git a/LayoutTests/streams/streams-promises.html b/LayoutTests/streams/streams-promises.html
new file mode 100644 (file)
index 0000000..7394c9b
--- /dev/null
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<script src='../resources/testharness.js'></script>
+<script src='../resources/testharnessreport.js'></script>
+<script>
+test(function() {
+    Promise = function() { throw new Error("nasty things"); };
+
+    const rs = new ReadableStream(); // Does not throw.
+    const ws = new WritableStream(); // Does not throw.
+}, 'Streams can be built even if Promise constructor is replaced');
+</script>
index bbbf0fe..0c47e0b 100644 (file)
@@ -1,3 +1,39 @@
+2015-11-04  Xabier Rodriguez Calvar  <calvaris@igalia.com>
+
+        [Streams API] Shield streams against user replacing the Promise constructor
+        https://bugs.webkit.org/show_bug.cgi?id=150887
+
+        Reviewed by Youenn Fablet.
+
+        With this rework, we shield the Streams implementation against the user doing something like "Promise =
+        function() { /* do garbage */ };".
+
+        Test: streams/streams-promises.html.
+
+        * Modules/streams/ReadableStream.js:
+        (initializeReadableStream):
+        (cancel):
+        * Modules/streams/ReadableStreamInternals.js:
+        (privateInitializeReadableStreamReader):
+        (cancelReadableStream):
+        (readFromReadableStreamReader):
+        * Modules/streams/ReadableStreamReader.js:
+        (cancel):
+        (read):
+        (closed):
+        * Modules/streams/StreamInternals.js:
+        (promiseInvokeOrNoop):
+        (promiseInvokeOrFallbackOrNoop):
+        * Modules/streams/WritableStream.js:
+        (initializeWritableStream):
+        (abort):
+        (close):
+        (write):
+        (closed):
+        (ready):
+        * Modules/streams/WritableStreamInternals.js:
+        (syncWritableStreamStateWithQueue):
+
 2015-11-04  Antoine Quint  <graouts@apple.com>
 
         SVG: hit testing region for <text> elements is incorrect
index a36a68b..785a55d 100644 (file)
@@ -56,7 +56,7 @@ function initializeReadableStream(underlyingSource, strategy)
 
     var result = @invokeOrNoop(underlyingSource, "start", [this.@controller]);
     var _this = this;
-    Promise.resolve(result).then(function() {
+    @Promise.resolve(result).then(function() {
         _this.@started = true;
         @requestReadableStreamPull(_this);
     }, function(error) {
@@ -72,10 +72,10 @@ function cancel(reason)
     "use strict";
 
     if (!@isReadableStream(this))
-        return Promise.reject(new @TypeError("Function should be called on a ReadableStream"));
+        return @Promise.reject(new @TypeError("Function should be called on a ReadableStream"));
 
     if (@isReadableStreamLocked(this))
-        return Promise.reject(new @TypeError("ReadableStream is locked"));
+        return @Promise.reject(new @TypeError("ReadableStream is locked"));
 
     return @cancelReadableStream(this, reason);
 }
index c1309a9..f0f738b 100644 (file)
@@ -42,19 +42,19 @@ function privateInitializeReadableStreamReader(stream)
         this.@ownerReadableStream = stream;
         this.@storedError = undefined;
         stream.@reader = this;
-        this.@closedPromiseCapability = @newPromiseCapability(Promise);
+        this.@closedPromiseCapability = @newPromiseCapability(@Promise);
         return this;
     }
     if (stream.@state === @streamClosed) {
         this.@ownerReadableStream = null;
         this.@storedError = undefined;
-        this.@closedPromiseCapability = { @promise: Promise.resolve(undefined) };
+        this.@closedPromiseCapability = { @promise: @Promise.resolve(undefined) };
         return this;
     }
     // FIXME: ASSERT(stream.@state === @streamErrored);
     this.@ownerReadableStream = null;
     this.@storedError = stream.@storedError;
-    this.@closedPromiseCapability = { @promise: Promise.reject(stream.@storedError) };
+    this.@closedPromiseCapability = { @promise: @Promise.reject(stream.@storedError) };
 
     return this;
 }
@@ -275,9 +275,9 @@ function cancelReadableStream(stream, reason)
     "use strict";
 
     if (stream.@state === @streamClosed)
-        return Promise.resolve();
+        return @Promise.resolve();
     if (stream.@state === @streamErrored)
-        return Promise.reject(stream.@storedError);
+        return @Promise.reject(stream.@storedError);
     stream.@queue = @newQueue();
     @finishClosingReadableStream(stream);
     return @promiseInvokeOrNoop(stream.@underlyingSource, "cancel", [reason]).then(function() { });
@@ -354,9 +354,9 @@ function readFromReadableStreamReader(reader)
     "use strict";
 
     if (reader.@state === @streamClosed)
-        return Promise.resolve({value: undefined, done: true});
+        return @Promise.resolve({value: undefined, done: true});
     if (reader.@state === @streamErrored)
-        return Promise.reject(reader.@storedError);
+        return @Promise.reject(reader.@storedError);
     // FIXME: ASSERT(!!reader.@ownerReadableStream);
     // FIXME: ASSERT(reader.@ownerReadableStream.@state === @streamReadable);
     var stream = reader.@ownerReadableStream;
@@ -366,9 +366,9 @@ function readFromReadableStreamReader(reader)
             @requestReadableStreamPull(stream);
         else if (!stream.@queue.content.length)
             @finishClosingReadableStream(stream);
-        return Promise.resolve({value: chunk, done: false});
+        return @Promise.resolve({value: chunk, done: false});
     }
-    var readPromiseCapability = @newPromiseCapability(Promise);
+    var readPromiseCapability = @newPromiseCapability(@Promise);
     reader.@readRequests.push(readPromiseCapability);
     @requestReadableStreamPull(stream);
     return readPromiseCapability.@promise;
index 815a226..c75adb2 100644 (file)
@@ -30,13 +30,13 @@ function cancel(reason)
     "use strict";
 
     if (!@isReadableStreamReader(this))
-        return Promise.reject(new @TypeError("Function should be called on a ReadableStreamReader"));
+        return @Promise.reject(new @TypeError("Function should be called on a ReadableStreamReader"));
 
     if (this.@state === @streamClosed)
-        return Promise.resolve();
+        return @Promise.resolve();
 
     if (this.@state === @streamErrored)
-        return Promise.reject(this.@storedError);
+        return @Promise.reject(this.@storedError);
 
     // FIXME: ASSERT(@isReadableStream(this.@ownerReadableStream));
     // FIXME: ASSERT(this.@ownerReadableStream.@state === @streamReadable);
@@ -48,7 +48,7 @@ function read()
     "use strict";
 
     if (!@isReadableStreamReader(this))
-        return Promise.reject(new @TypeError("Function should be called on a ReadableStreamReader"));
+        return @Promise.reject(new @TypeError("Function should be called on a ReadableStreamReader"));
 
     return @readFromReadableStreamReader(this);
 }
@@ -74,7 +74,7 @@ function closed()
     "use strict";
 
     if (!@isReadableStreamReader(this))
-        return Promise.reject(new @TypeError("Callee of closed is not a ReadableStreamReader"));
+        return @Promise.reject(new @TypeError("Callee of closed is not a ReadableStreamReader"));
 
     return this.@closedPromiseCapability.@promise;
 }
index 0bbff4e..0475c51 100644 (file)
@@ -44,12 +44,12 @@ function promiseInvokeOrNoop(object, key, args)
     try {
         var method = object[key];
         if (typeof method === "undefined")
-            return Promise.resolve();
+            return @Promise.resolve();
         var result = method.@apply(object, args);
-        return Promise.resolve(result);
+        return @Promise.resolve(result);
     }
     catch(error) {
-        return Promise.reject(error);
+        return @Promise.reject(error);
     }
 
 }
@@ -63,10 +63,10 @@ function promiseInvokeOrFallbackOrNoop(object, key1, args1, key2, args2)
         if (typeof method === "undefined")
             return @promiseInvokeOrNoop(object, key2, args2);
         const result = method.@apply(object, args1);
-        return Promise.resolve(result);
+        return @Promise.resolve(result);
     }
     catch(error) {
-        return Promise.reject(error);
+        return @Promise.reject(error);
     }
 }
 
index 9ae58d7..4767a9d 100644 (file)
@@ -42,8 +42,8 @@ function initializeWritableStream(underlyingSink, strategy)
         throw new @TypeError("WritableStream constructor takes an object as second argument, if any");
 
     this.@underlyingSink = underlyingSink;
-    this.@closedPromiseCapability = @newPromiseCapability(Promise);
-    this.@readyPromiseCapability = { @promise: Promise.resolve(undefined) };
+    this.@closedPromiseCapability = @newPromiseCapability(@Promise);
+    this.@readyPromiseCapability = { @promise: @Promise.resolve(undefined) };
     this.@queue = @newQueue();
     this.@state = @streamWritable;
     this.@started = false;
@@ -55,7 +55,7 @@ function initializeWritableStream(underlyingSink, strategy)
 
     var error = @errorWritableStream.bind(this);
     var startResult = @invokeOrNoop(underlyingSink, "start", [error]);
-    this.@startedPromise = Promise.resolve(startResult);
+    this.@startedPromise = @Promise.resolve(startResult);
     var _this = this;
     this.@startedPromise.then(function() {
         _this.@started = true;
@@ -70,13 +70,13 @@ function abort(reason)
     "use strict";
 
     if (!@isWritableStream(this))
-        return Promise.reject(new @TypeError("The WritableStream.abort method can only be used on instances of WritableStream"));
+        return @Promise.reject(new @TypeError("The WritableStream.abort method can only be used on instances of WritableStream"));
 
     if (this.@state === @streamClosed)
-        return Promise.resolve(undefined);
+        return @Promise.resolve(undefined);
 
     if (this.@state === @streamErrored)
-        return Promise.reject(this.@storedError);
+        return @Promise.reject(this.@storedError);
 
     @errorWritableStream.@apply(this, [reason]);
 
@@ -90,13 +90,13 @@ function close()
     "use strict";
 
     if (!@isWritableStream(this))
-        return Promise.reject(new @TypeError("The WritableStream.close method can only be used on instances of WritableStream"));
+        return @Promise.reject(new @TypeError("The WritableStream.close method can only be used on instances of WritableStream"));
 
     if (this.@state === @streamClosed || this.@state === @streamClosing)
-        return Promise.reject(new @TypeError("Cannot close a WritableString that is closed or closing"));
+        return @Promise.reject(new @TypeError("Cannot close a WritableString that is closed or closing"));
 
     if (this.@state === @streamErrored)
-        return Promise.reject(this.@storedError);
+        return @Promise.reject(this.@storedError);
 
     if (this.@state === @streamWaiting)
         this.@readyPromiseCapability.@resolve.@call(undefined, undefined);
@@ -113,13 +113,13 @@ function write(chunk)
     "use strict";
 
     if (!@isWritableStream(this))
-        return Promise.reject(new @TypeError("The WritableStream.close method can only be used on instances of WritableStream"));
+        return @Promise.reject(new @TypeError("The WritableStream.close method can only be used on instances of WritableStream"));
 
     if (this.@state === @streamClosed || this.@state === @streamClosing)
-        return Promise.reject(new @TypeError("Cannot write on a WritableString that is closed or closing"));
+        return @Promise.reject(new @TypeError("Cannot write on a WritableString that is closed or closing"));
 
     if (this.@state === @streamErrored)
-        return Promise.reject(this.@storedError);
+        return @Promise.reject(this.@storedError);
 
     // FIXME
     // assert(this.@state === @streamWritable || this.@state === @streamWaiting);
@@ -130,16 +130,16 @@ function write(chunk)
             chunkSize = this.@strategy.size.@call(undefined, chunk);
         } catch(e) {
             @errorWritableStream.@call(this, e);
-            return Promise.reject(e);
+            return @Promise.reject(e);
         }
     }
 
-    const promiseCapability = @newPromiseCapability(Promise);
+    const promiseCapability = @newPromiseCapability(@Promise);
     try {
         @enqueueValueWithSize(this.@queue, { promiseCapability: promiseCapability, chunk: chunk }, chunkSize);
     } catch (e) {
         @errorWritableStream.@call(this, e);
-        return Promise.reject(e);
+        return @Promise.reject(e);
     }
 
     @syncWritableStreamStateWithQueue(this);
@@ -153,7 +153,7 @@ function closed()
     "use strict";
 
     if (!@isWritableStream(this))
-        return Promise.reject(new @TypeError("The WritableStream.closed getter can only be used on instances of WritableStream"));
+        return @Promise.reject(new @TypeError("The WritableStream.closed getter can only be used on instances of WritableStream"));
 
     return this.@closedPromiseCapability.@promise;
 }
@@ -163,7 +163,7 @@ function ready()
     "use strict";
 
     if (!@isWritableStream(this))
-        return Promise.reject(new @TypeError("The WritableStream.ready getter can only be used on instances of WritableStream"));
+        return @Promise.reject(new @TypeError("The WritableStream.ready getter can only be used on instances of WritableStream"));
 
     return this.@readyPromiseCapability.@promise;
 }
index 5b88d75..3cdf92b 100644 (file)
@@ -47,7 +47,7 @@ function syncWritableStreamStateWithQueue(stream)
     const shouldApplyBackpressure = stream.@queue.size > stream.@strategy.highWaterMark;
     if (shouldApplyBackpressure && stream.@state === @streamWritable) {
         stream.@state = @streamWaiting;
-        stream.@readyPromiseCapability = @newPromiseCapability(Promise);
+        stream.@readyPromiseCapability = @newPromiseCapability(@Promise);
     }
     if (!shouldApplyBackpressure && stream.@state === @streamWaiting) {
         stream.@state = @streamWritable;