[Streams API] Support the start function parameter in ReadableStream constructor
authoryouenn.fablet@crf.canon.fr <youenn.fablet@crf.canon.fr@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 9 Apr 2015 06:48:16 +0000 (06:48 +0000)
committeryouenn.fablet@crf.canon.fr <youenn.fablet@crf.canon.fr@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 9 Apr 2015 06:48:16 +0000 (06:48 +0000)
https://bugs.webkit.org/show_bug.cgi?id=141160

Source/WebCore:

Reviewed by Benjamin Poulain.

Stores the JS source object in ReadableStreamJSSource and calls its "start" function.
This function takes a controller object that has three JS functions as members: close, enqueue and error.
This controller is stored in ReadableStreamJSSource as it will be reused as "pull" parameter.
All three JS functions do not currently do anything.

Test: streams/readablestream-start.html

* Modules/streams/ReadableStream.cpp:
(WebCore::ReadableStream::start): Place holder for step 11 of https://streams.spec.whatwg.org/#rs-constructor.
* Modules/streams/ReadableStream.h:
* bindings/js/JSReadableStreamCustom.cpp:
(WebCore::constructJSReadableStream):
* bindings/js/ReadableStreamJSSource.cpp:
(WebCore::getPropertyFromObject): Helper function to get a public property from an object.
(WebCore::setPropertyToObject): Helper function to set a public property to an object.
(WebCore::callFunction): Helper function to call a JS function from C++.
(WebCore::ReadableStreamJSSource::ReadableStreamJSSource):
(WebCore::notImplementedFunction):
(WebCore::createReadableStreamEnqueueFunction): Creates the JS function for enqueue.
(WebCore::createReadableStreamCloseFunction): Creates the JS function for close.
(WebCore::createReadableStreamErrorFunction): Creates the JS function for error.
(WebCore::startReadableStreamAsync): Equivalent of promise resolution for start.
(WebCore::ReadableStreamJSSource::start): Calls the "start" function of the JS source with all three JS functions (enqueue, close, error) as parameters.
* bindings/js/ReadableStreamJSSource.h:

LayoutTests:

Reviewed by Benjamin Poulain.

Added tests to check that start JS function is called with the right parameters and can throw errors.

* streams/readablestream-start-expected.txt: Added.
* streams/readablestream-start.html: Added.

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

LayoutTests/ChangeLog
LayoutTests/streams/readablestream-constructor.html
LayoutTests/streams/readablestream-start-expected.txt [new file with mode: 0644]
LayoutTests/streams/readablestream-start.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/Modules/streams/ReadableStream.cpp
Source/WebCore/Modules/streams/ReadableStream.h
Source/WebCore/bindings/js/JSReadableStreamCustom.cpp
Source/WebCore/bindings/js/ReadableStreamJSSource.cpp
Source/WebCore/bindings/js/ReadableStreamJSSource.h

index 4aba51077e833b419edd14a3a994b34c7842e13c..4d13ed79eb0c39a52d0034cc6efd4fae3b861c99 100644 (file)
@@ -1,3 +1,15 @@
+2015-04-04 Xabier Rodriguez Calvar <calvaris@igalia.com> and Youenn Fablet <youenn.fablet@crf.canon.fr>
+
+        [Streams API] Support the start function parameter in ReadableStream constructor
+        https://bugs.webkit.org/show_bug.cgi?id=141160
+
+        Reviewed by Benjamin Poulain.
+
+        Added tests to check that start JS function is called with the right parameters and can throw errors.
+
+        * streams/readablestream-start-expected.txt: Added.
+        * streams/readablestream-start.html: Added.
+
 2015-04-08  Brent Fulgham  <bfulgham@apple.com>
 
         [Win] Layout Test inspector/console/console-api.html is failing
index 44a5096d466615fc329e0431e53006da7a32cc97..bb00545c3abda632db93980c2de2fae9e2f6b148 100644 (file)
@@ -7,6 +7,13 @@ test(function() {
     assert_throws(new TypeError(), function() {
         new ReadableStream('potato');
     });
+    var x
+    assert_throws(new TypeError(), function() {
+        new ReadableStream(x);
+    });
+    assert_throws(new TypeError(), function() {
+        new ReadableStream(null);
+    });
 }, 'ReadableStream constructor should get an object as argument');
 
 test(function() {
diff --git a/LayoutTests/streams/readablestream-start-expected.txt b/LayoutTests/streams/readablestream-start-expected.txt
new file mode 100644 (file)
index 0000000..5a2d9b3
--- /dev/null
@@ -0,0 +1,7 @@
+
+PASS ReadableStream start should be called with the proper parameters 
+PASS ReadableStream start controller parameter should be updatable 
+PASS ReadableStream should be able to call start method within prototype chain of its source 
+PASS ReadableStream start should be able to throw 
+PASS ReadableStream constructor should get a function as start argument 
+
diff --git a/LayoutTests/streams/readablestream-start.html b/LayoutTests/streams/readablestream-start.html
new file mode 100644 (file)
index 0000000..c8e6298
--- /dev/null
@@ -0,0 +1,90 @@
+<!DOCTYPE html>
+<script src='../resources/testharness.js'></script>
+<script src='../resources/testharnessreport.js'></script>
+<script>
+
+test(function()
+{
+    var isStartCalled = false;
+    var source =
+    { start:
+        function(controller) {
+            assert_equals(this, source);
+
+            assert_array_equals(Object.getOwnPropertyNames(controller), ['enqueue', 'close', 'error']);
+
+            enqueue = controller.enqueue;
+            close = controller.close;
+            error = controller.error;
+
+            assert_equals(typeof enqueue, 'function');
+            assert_equals(typeof close, 'function');
+            assert_equals(typeof error, 'function');
+
+            assert_array_equals(Object.getOwnPropertyNames(enqueue), ['name', 'length']);
+            assert_array_equals(Object.getOwnPropertyNames(close), ['name', 'length']);
+            assert_array_equals(Object.getOwnPropertyNames(error), ['name', 'length']);
+
+            assert_equals(enqueue.name, '');
+            assert_equals(close.name, '');
+            assert_equals(error.name, '');
+
+            assert_equals(enqueue.length, 1);
+            assert_equals(close.length, 0);
+            assert_equals(error.length, 1);
+
+            isStartCalled = true;
+        }
+    };
+    var rs = new ReadableStream(source);
+    assert_true(isStartCalled);
+}, 'ReadableStream start should be called with the proper parameters');
+
+test(function()
+{
+    var isStartCalled = false;
+    var source =
+    { start:
+        function(controller) {
+            assert_array_equals(Object.getOwnPropertyNames(controller), ['enqueue', 'close', 'error']);
+            controller.test = "";
+            assert_array_equals(Object.getOwnPropertyNames(controller), ['enqueue', 'close', 'error', 'test']);
+
+            isStartCalled = true;
+        }
+    };
+    var rs = new ReadableStream(source);
+    assert_true(isStartCalled);
+}, 'ReadableStream start controller parameter should be updatable');
+
+test(function()
+{
+    var isStartCalled = false;
+    SimpleStreamSource = function() { };
+    SimpleStreamSource.prototype.start = function() { isStartCalled = true; };
+    SimpleStreamSource.prototype.constructor = SimpleStreamSource;
+
+    var rs = new ReadableStream(new SimpleStreamSource());
+    assert_true(isStartCalled);
+}, 'ReadableStream should be able to call start method within prototype chain of its source');
+
+test(function()
+{
+    mashedError = new Error('potato');
+    assert_throws(mashedError, function() {
+        var rs = new ReadableStream(
+        { start:
+            function() {
+                throw mashedError;
+            }
+        });
+    });
+}, 'ReadableStream start should be able to throw');
+
+test(function() {
+    assert_throws(new TypeError(), function() {
+        new ReadableStream({ start: 'potato'});
+    });
+}, 'ReadableStream constructor should get a function as start argument');
+
+</script>
index 6419592dd552f356760f75c8caf605b774ef82b2..269a653f7e6686d62899175052a5fdad69f20434 100644 (file)
@@ -1,3 +1,35 @@
+2015-04-08  Xabier Rodriguez Calvar  <calvaris@igalia.com> and Youenn Fablet  <youenn.fablet@crf.canon.fr>
+
+        [Streams API] Support the start function parameter in ReadableStream constructor
+        https://bugs.webkit.org/show_bug.cgi?id=141160
+
+        Reviewed by Benjamin Poulain.
+
+        Stores the JS source object in ReadableStreamJSSource and calls its "start" function.
+        This function takes a controller object that has three JS functions as members: close, enqueue and error.
+        This controller is stored in ReadableStreamJSSource as it will be reused as "pull" parameter.
+        All three JS functions do not currently do anything.
+
+        Test: streams/readablestream-start.html
+
+        * Modules/streams/ReadableStream.cpp:
+        (WebCore::ReadableStream::start): Place holder for step 11 of https://streams.spec.whatwg.org/#rs-constructor.
+        * Modules/streams/ReadableStream.h:
+        * bindings/js/JSReadableStreamCustom.cpp:
+        (WebCore::constructJSReadableStream):
+        * bindings/js/ReadableStreamJSSource.cpp:
+        (WebCore::getPropertyFromObject): Helper function to get a public property from an object.
+        (WebCore::setPropertyToObject): Helper function to set a public property to an object.
+        (WebCore::callFunction): Helper function to call a JS function from C++.
+        (WebCore::ReadableStreamJSSource::ReadableStreamJSSource):
+        (WebCore::notImplementedFunction):
+        (WebCore::createReadableStreamEnqueueFunction): Creates the JS function for enqueue.
+        (WebCore::createReadableStreamCloseFunction): Creates the JS function for close.
+        (WebCore::createReadableStreamErrorFunction): Creates the JS function for error.
+        (WebCore::startReadableStreamAsync): Equivalent of promise resolution for start.
+        (WebCore::ReadableStreamJSSource::start): Calls the "start" function of the JS source with all three JS functions (enqueue, close, error) as parameters.
+        * bindings/js/ReadableStreamJSSource.h:
+
 2015-04-08  Brent Fulgham  <bfulgham@apple.com>
 
         [Mac] Unreviewed test fix after r182584
index 403c9c11392847656ba45c5b22269fbb738b7603..0f5fe0301a6615e649e5966dba993c144e9ba230 100644 (file)
@@ -58,6 +58,11 @@ ReadableStream::~ReadableStream()
 #endif
 }
 
+void ReadableStream::start()
+{
+    notImplemented();
+}
+
 const char* ReadableStream::activeDOMObjectName() const
 {
     return "ReadableStream";
index cf0c4685cf666a878c16820e089e59396775ff5a..4e9269318491cbbbb0e28c1eabc9b79bd61d88d4 100644 (file)
@@ -67,6 +67,8 @@ public:
 
     State internalState() { return m_state; }
 
+    void start();
+
 protected:
     ReadableStream(ScriptExecutionContext&, Ref<ReadableStreamSource>&&);
 
index 581e4363fd180b37ae19829699b1f2de40269a72..d8b0681163f714c1a2b63e65f169ea9da08e65d4 100644 (file)
@@ -87,7 +87,7 @@ EncodedJSValue JSC_HOST_CALL constructJSReadableStream(ExecState* exec)
     JSGlobalObject* globalObject = exec->callee()->globalObject();
     JSReadableStream* jsReadableStream = JSReadableStream::create(JSReadableStream::createStructure(vm, globalObject, JSReadableStream::createPrototype(vm, globalObject)), jsCast<JSDOMGlobalObject*>(globalObject), readableStream.releaseNonNull());
 
-    source->start(exec);
+    source->start(exec, jsReadableStream);
 
     return JSValue::encode(jsReadableStream);
 }
index c49486cdaa40b3a71552f40a863841c498e3fcb3..6f9d7ba79fe419c5e522d4af3ad977b83f5d9110 100644 (file)
  */
 
 #include "config.h"
+#include "ReadableStreamJSSource.h"
 
 #if ENABLE(STREAMS_API)
-#include "ReadableStreamJSSource.h"
 
 #include "DOMWrapperWorld.h"
 #include "JSDOMPromise.h"
 #include "JSReadableStream.h"
 #include "NotImplemented.h"
+#include "ScriptExecutionContext.h"
 #include <runtime/Error.h>
 #include <runtime/JSCJSValueInlines.h>
 #include <runtime/JSString.h>
@@ -63,6 +64,25 @@ JSValue getInternalSlotFromObject(ExecState* exec, JSValue objectValue, PrivateN
     return propertySlot.getValue(exec, propertyName);
 }
 
+static inline JSValue getPropertyFromObject(ExecState* exec, JSObject* object, const char* identifier)
+{
+    return object->get(exec, Identifier::fromString(exec, identifier));
+}
+
+static inline void setPropertyToObject(ExecState* exec, JSValue objectValue, const char* name, JSValue value)
+{
+    JSObject* object = objectValue.toObject(exec);
+    PutPropertySlot propertySlot(objectValue);
+    object->put(object, exec, Identifier::fromString(exec, name), value, propertySlot);
+}
+
+static inline JSValue callFunction(ExecState* exec, JSValue jsFunction, JSValue thisValue, const ArgList& arguments, JSValue* exception)
+{
+    CallData callData;
+    CallType callType = getCallData(jsFunction, callData);
+    return call(exec, jsFunction, callType, callData, thisValue, arguments, exception);
+}
+
 Ref<ReadableStreamJSSource> ReadableStreamJSSource::create(JSC::ExecState* exec)
 {
     return adoptRef(*new ReadableStreamJSSource(exec));
@@ -70,15 +90,81 @@ Ref<ReadableStreamJSSource> ReadableStreamJSSource::create(JSC::ExecState* exec)
 
 ReadableStreamJSSource::ReadableStreamJSSource(JSC::ExecState* exec)
 {
-    if (exec->argumentCount()) {
-        ASSERT_WITH_MESSAGE(exec->argument(0).isObject(), "Caller of ReadableStreamJSSource constructor should ensure that passed argument is an object.");
-        // FIXME: Implement parameters support;
-    }
+    ASSERT_WITH_MESSAGE(!exec->argumentCount() || exec->argument(0).isObject(), "Caller of ReadableStreamJSSource constructor should ensure that passed argument if any is an object.");
+    JSObject* source =  exec->argumentCount() ? exec->argument(0).getObject() : JSFinalObject::create(exec->vm(), JSFinalObject::createStructure(exec->vm(), exec->callee()->globalObject(), jsNull(), 1));
+    m_source.set(exec->vm(), source);
 }
 
-void ReadableStreamJSSource::start(JSC::ExecState*)
+static EncodedJSValue JSC_HOST_CALL notImplementedFunction(ExecState*)
 {
     notImplemented();
+    return JSValue::encode(jsUndefined());
+}
+
+static inline JSFunction* createReadableStreamEnqueueFunction(ExecState* exec)
+{
+    return JSFunction::create(exec->vm(), exec->callee()->globalObject(), 1, String(), notImplementedFunction);
+}
+
+static inline JSFunction* createReadableStreamCloseFunction(ExecState* exec)
+{
+    return JSFunction::create(exec->vm(), exec->callee()->globalObject(), 0, String(), notImplementedFunction);
+}
+
+static inline JSFunction* createReadableStreamErrorFunction(ExecState* exec)
+{
+    return JSFunction::create(exec->vm(), exec->callee()->globalObject(), 1, String(), notImplementedFunction);
+}
+
+static void startReadableStreamAsync(ReadableStream& readableStream)
+{
+    RefPtr<ReadableStream> stream = &readableStream;
+    stream->scriptExecutionContext()->postTask([stream](ScriptExecutionContext&) {
+        stream->start();
+    });
+}
+
+static inline JSObject* createReadableStreamController(JSC::ExecState* exec)
+{
+    JSFunction* enqueueFunction = createReadableStreamEnqueueFunction(exec);
+    JSFunction* closeFunction = createReadableStreamCloseFunction(exec);
+    JSFunction* errorFunction = createReadableStreamErrorFunction(exec);
+
+    JSObject* controller =  JSFinalObject::create(exec->vm(), JSFinalObject::createStructure(exec->vm(), exec->callee()->globalObject(), jsNull(), 3));
+    setPropertyToObject(exec, controller, "enqueue", enqueueFunction);
+    setPropertyToObject(exec, controller, "close", closeFunction);
+    setPropertyToObject(exec, controller, "error", errorFunction);
+    return controller;
+}
+
+void ReadableStreamJSSource::start(JSC::ExecState* exec, JSReadableStream* readableStream)
+{
+    JSLockHolder lock(exec);
+
+    m_controller.set(exec->vm(), createReadableStreamController(exec));
+
+    JSValue startFunction = getPropertyFromObject(exec, m_source.get(), "start");
+    if (!startFunction.isFunction()) {
+        if (!startFunction.isUndefined())
+            throwVMError(exec, createTypeError(exec, ASCIILiteral("ReadableStream constructor object start property should be a function.")));
+        else
+            startReadableStreamAsync(readableStream->impl());
+        return;
+    }
+
+    MarkedArgumentBuffer arguments;
+    arguments.append(m_controller.get());
+
+    JSValue exception;
+    callFunction(exec, startFunction, m_source.get(), arguments, &exception);
+
+    if (exception) {
+        throwVMError(exec, exception);
+        return;
+    }
+
+    // FIXME: Implement handling promise as result of calling start function.
+    startReadableStreamAsync(readableStream->impl());
 }
 
 Ref<ReadableJSStream> ReadableJSStream::create(ScriptExecutionContext& scriptExecutionContext, Ref<ReadableStreamJSSource>&& source)
index e0ead0289523e18b5e02fcebf06c509d2dfabef0..478510ba9709dffba72a1a360c4ffba499cb9750 100644 (file)
@@ -50,10 +50,15 @@ public:
     static Ref<ReadableStreamJSSource> create(JSC::ExecState*);
     ~ReadableStreamJSSource() { }
 
-    void start(JSC::ExecState*);
+    void start(JSC::ExecState*, JSReadableStream*);
 
 private:
     ReadableStreamJSSource(JSC::ExecState*);
+
+    // Object passed to constructor.
+    JSC::Strong<JSC::JSObject> m_source;
+
+    JSC::Strong<JSC::JSObject> m_controller;
 };
 
 class ReadableJSStream: public ReadableStream {