[Streams API] Support the start function parameter in ReadableStream constructor
[WebKit-https.git] / Source / WebCore / bindings / js / ReadableStreamJSSource.cpp
1 /*
2  * Copyright (C) 2015 Canon Inc.
3  * Copyright (C) 2015 Igalia S.L.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted, provided that the following conditions
7  * are required to be met:
8  *
9  * 1.  Redistributions of source code must retain the above copyright
10  *     notice, this list of conditions and the following disclaimer.
11  * 2.  Redistributions in binary form must reproduce the above copyright
12  *     notice, this list of conditions and the following disclaimer in the
13  *     documentation and/or other materials provided with the distribution.
14  * 3.  Neither the name of Canon Inc. nor the names of
15  *     its contributors may be used to endorse or promote products derived
16  *     from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY
19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR
22  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include "config.h"
31 #include "ReadableStreamJSSource.h"
32
33 #if ENABLE(STREAMS_API)
34
35 #include "DOMWrapperWorld.h"
36 #include "JSDOMPromise.h"
37 #include "JSReadableStream.h"
38 #include "NotImplemented.h"
39 #include "ScriptExecutionContext.h"
40 #include <runtime/Error.h>
41 #include <runtime/JSCJSValueInlines.h>
42 #include <runtime/JSString.h>
43 #include <runtime/StructureInlines.h>
44
45 using namespace JSC;
46
47 namespace WebCore {
48
49 void setInternalSlotToObject(ExecState* exec, JSValue objectValue, PrivateName& name, JSValue value)
50 {
51     JSObject* object = objectValue.toObject(exec);
52     PutPropertySlot propertySlot(objectValue);
53     object->put(object, exec, Identifier::fromUid(name), value, propertySlot);
54 }
55
56 JSValue getInternalSlotFromObject(ExecState* exec, JSValue objectValue, PrivateName& name)
57 {
58     JSObject* object = objectValue.toObject(exec);
59     PropertySlot propertySlot(objectValue);
60
61     Identifier propertyName = Identifier::fromUid(name);
62     if (!object->getOwnPropertySlot(object, exec, propertyName, propertySlot))
63         return JSValue();
64     return propertySlot.getValue(exec, propertyName);
65 }
66
67 static inline JSValue getPropertyFromObject(ExecState* exec, JSObject* object, const char* identifier)
68 {
69     return object->get(exec, Identifier::fromString(exec, identifier));
70 }
71
72 static inline void setPropertyToObject(ExecState* exec, JSValue objectValue, const char* name, JSValue value)
73 {
74     JSObject* object = objectValue.toObject(exec);
75     PutPropertySlot propertySlot(objectValue);
76     object->put(object, exec, Identifier::fromString(exec, name), value, propertySlot);
77 }
78
79 static inline JSValue callFunction(ExecState* exec, JSValue jsFunction, JSValue thisValue, const ArgList& arguments, JSValue* exception)
80 {
81     CallData callData;
82     CallType callType = getCallData(jsFunction, callData);
83     return call(exec, jsFunction, callType, callData, thisValue, arguments, exception);
84 }
85
86 Ref<ReadableStreamJSSource> ReadableStreamJSSource::create(JSC::ExecState* exec)
87 {
88     return adoptRef(*new ReadableStreamJSSource(exec));
89 }
90
91 ReadableStreamJSSource::ReadableStreamJSSource(JSC::ExecState* exec)
92 {
93     ASSERT_WITH_MESSAGE(!exec->argumentCount() || exec->argument(0).isObject(), "Caller of ReadableStreamJSSource constructor should ensure that passed argument if any is an object.");
94     JSObject* source =  exec->argumentCount() ? exec->argument(0).getObject() : JSFinalObject::create(exec->vm(), JSFinalObject::createStructure(exec->vm(), exec->callee()->globalObject(), jsNull(), 1));
95     m_source.set(exec->vm(), source);
96 }
97
98 static EncodedJSValue JSC_HOST_CALL notImplementedFunction(ExecState*)
99 {
100     notImplemented();
101     return JSValue::encode(jsUndefined());
102 }
103
104 static inline JSFunction* createReadableStreamEnqueueFunction(ExecState* exec)
105 {
106     return JSFunction::create(exec->vm(), exec->callee()->globalObject(), 1, String(), notImplementedFunction);
107 }
108
109 static inline JSFunction* createReadableStreamCloseFunction(ExecState* exec)
110 {
111     return JSFunction::create(exec->vm(), exec->callee()->globalObject(), 0, String(), notImplementedFunction);
112 }
113
114 static inline JSFunction* createReadableStreamErrorFunction(ExecState* exec)
115 {
116     return JSFunction::create(exec->vm(), exec->callee()->globalObject(), 1, String(), notImplementedFunction);
117 }
118
119 static void startReadableStreamAsync(ReadableStream& readableStream)
120 {
121     RefPtr<ReadableStream> stream = &readableStream;
122     stream->scriptExecutionContext()->postTask([stream](ScriptExecutionContext&) {
123         stream->start();
124     });
125 }
126
127 static inline JSObject* createReadableStreamController(JSC::ExecState* exec)
128 {
129     JSFunction* enqueueFunction = createReadableStreamEnqueueFunction(exec);
130     JSFunction* closeFunction = createReadableStreamCloseFunction(exec);
131     JSFunction* errorFunction = createReadableStreamErrorFunction(exec);
132
133     JSObject* controller =  JSFinalObject::create(exec->vm(), JSFinalObject::createStructure(exec->vm(), exec->callee()->globalObject(), jsNull(), 3));
134     setPropertyToObject(exec, controller, "enqueue", enqueueFunction);
135     setPropertyToObject(exec, controller, "close", closeFunction);
136     setPropertyToObject(exec, controller, "error", errorFunction);
137     return controller;
138 }
139
140 void ReadableStreamJSSource::start(JSC::ExecState* exec, JSReadableStream* readableStream)
141 {
142     JSLockHolder lock(exec);
143
144     m_controller.set(exec->vm(), createReadableStreamController(exec));
145
146     JSValue startFunction = getPropertyFromObject(exec, m_source.get(), "start");
147     if (!startFunction.isFunction()) {
148         if (!startFunction.isUndefined())
149             throwVMError(exec, createTypeError(exec, ASCIILiteral("ReadableStream constructor object start property should be a function.")));
150         else
151             startReadableStreamAsync(readableStream->impl());
152         return;
153     }
154
155     MarkedArgumentBuffer arguments;
156     arguments.append(m_controller.get());
157
158     JSValue exception;
159     callFunction(exec, startFunction, m_source.get(), arguments, &exception);
160
161     if (exception) {
162         throwVMError(exec, exception);
163         return;
164     }
165
166     // FIXME: Implement handling promise as result of calling start function.
167     startReadableStreamAsync(readableStream->impl());
168 }
169
170 Ref<ReadableJSStream> ReadableJSStream::create(ScriptExecutionContext& scriptExecutionContext, Ref<ReadableStreamJSSource>&& source)
171 {
172     auto readableStream = adoptRef(*new ReadableJSStream(scriptExecutionContext, WTF::move(source)));
173     return readableStream;
174 }
175
176 Ref<ReadableStreamReader> ReadableJSStream::createReader()
177 {
178     RefPtr<ReadableStreamReader> reader = ReadableJSStreamReader::create(*this);
179     return reader.releaseNonNull();
180 }
181
182 ReadableJSStream::ReadableJSStream(ScriptExecutionContext& scriptExecutionContext, Ref<ReadableStreamJSSource>&& source)
183     : ReadableStream(scriptExecutionContext, WTF::move(source))
184 {
185 }
186
187 Ref<ReadableJSStreamReader> ReadableJSStreamReader::create(ReadableJSStream& stream)
188 {
189     auto readableStreamReader = adoptRef(*new ReadableJSStreamReader(stream));
190     return readableStreamReader;
191 }
192
193 ReadableJSStreamReader::ReadableJSStreamReader(ReadableJSStream& readableStream)
194     : ReadableStreamReader(readableStream)
195 {
196 }
197
198 } // namespace WebCore
199
200 #endif