[Streams API] ReadableStream constructor start function should be able to error the...
[WebKit-https.git] / Source / WebCore / bindings / js / ReadableJSStream.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 "ReadableJSStream.h"
32
33 #if ENABLE(STREAMS_API)
34
35 #include "DOMWrapperWorld.h"
36 #include "JSDOMPromise.h"
37 #include "JSReadableStream.h"
38 #include "JSReadableStreamController.h"
39 #include "NotImplemented.h"
40 #include "ScriptExecutionContext.h"
41 #include <runtime/Error.h>
42 #include <runtime/JSCJSValueInlines.h>
43 #include <runtime/JSString.h>
44 #include <runtime/StructureInlines.h>
45
46 using namespace JSC;
47
48 namespace WebCore {
49
50 void setInternalSlotToObject(ExecState* exec, JSValue objectValue, PrivateName& name, JSValue value)
51 {
52     JSObject* object = objectValue.toObject(exec);
53     PutPropertySlot propertySlot(objectValue);
54     object->put(object, exec, Identifier::fromUid(name), value, propertySlot);
55 }
56
57 JSValue getInternalSlotFromObject(ExecState* exec, JSValue objectValue, PrivateName& name)
58 {
59     JSObject* object = objectValue.toObject(exec);
60     PropertySlot propertySlot(objectValue);
61
62     Identifier propertyName = Identifier::fromUid(name);
63     if (!object->getOwnPropertySlot(object, exec, propertyName, propertySlot))
64         return JSValue();
65     return propertySlot.getValue(exec, propertyName);
66 }
67
68 static inline JSValue getPropertyFromObject(ExecState* exec, JSObject* object, const char* identifier)
69 {
70     return object->get(exec, Identifier::fromString(exec, identifier));
71 }
72
73 static inline JSValue callFunction(ExecState* exec, JSValue jsFunction, JSValue thisValue, const ArgList& arguments, JSValue* exception)
74 {
75     CallData callData;
76     CallType callType = getCallData(jsFunction, callData);
77     return call(exec, jsFunction, callType, callData, thisValue, arguments, exception);
78 }
79
80 Ref<ReadableJSStream::Source> ReadableJSStream::Source::create(ExecState& exec)
81 {
82     return adoptRef(*new Source(exec));
83 }
84
85 ReadableJSStream::Source::Source(ExecState& exec)
86 {
87     ASSERT_WITH_MESSAGE(!exec.argumentCount() || exec.argument(0).isObject(), "Caller of ReadableJSStream::Source constructor should ensure that passed argument if any is an object.");
88     JSObject* source =  exec.argumentCount() ? exec.argument(0).getObject() : JSFinalObject::create(exec.vm(), JSFinalObject::createStructure(exec.vm(), exec.callee()->globalObject(), jsNull(), 1));
89     m_source.set(exec.vm(), source);
90 }
91
92 JSDOMGlobalObject* ReadableJSStream::Source::globalObject()
93 {
94     return jsDynamicCast<JSDOMGlobalObject*>(m_source->globalObject());
95 }
96
97 static void startReadableStreamAsync(ReadableStream& readableStream)
98 {
99     RefPtr<ReadableStream> stream = &readableStream;
100     stream->scriptExecutionContext()->postTask([stream](ScriptExecutionContext&) {
101         stream->start();
102     });
103 }
104
105 void ReadableJSStream::Source::start(ExecState& exec, ReadableJSStream& readableStream)
106 {
107     JSLockHolder lock(&exec);
108
109     JSValue startFunction = getPropertyFromObject(&exec, m_source.get(), "start");
110     if (!startFunction.isFunction()) {
111         if (!startFunction.isUndefined())
112             throwVMError(&exec, createTypeError(&exec, ASCIILiteral("ReadableStream constructor object start property should be a function.")));
113         else
114             startReadableStreamAsync(readableStream);
115         return;
116     }
117
118     MarkedArgumentBuffer arguments;
119     arguments.append(readableStream.jsController(exec, globalObject()));
120
121     JSValue exception;
122     callFunction(&exec, startFunction, m_source.get(), arguments, &exception);
123
124     if (exception) {
125         throwVMError(&exec, exception);
126         return;
127     }
128
129     // FIXME: Implement handling promise as result of calling start function.
130     startReadableStreamAsync(readableStream);
131 }
132
133 Ref<ReadableJSStream> ReadableJSStream::create(ExecState& exec, ScriptExecutionContext& scriptExecutionContext)
134 {
135     Ref<ReadableJSStream::Source> source = ReadableJSStream::Source::create(exec);
136     Ref<ReadableJSStream> readableStream = adoptRef(*new ReadableJSStream(scriptExecutionContext, WTF::move(source)));
137
138     static_cast<ReadableJSStream::Source&>(readableStream->source()).start(exec, readableStream.get());
139     return readableStream;
140 }
141
142 Ref<ReadableStreamReader> ReadableJSStream::createReader()
143 {
144     Ref<Reader> reader = Reader::create(*this);
145     if (m_error)
146         reader->storeError(*jsSource().globalObject()->globalExec(), m_error.get());
147     return static_reference_cast<ReadableStreamReader>(reader);
148 }
149
150 ReadableJSStream::ReadableJSStream(ScriptExecutionContext& scriptExecutionContext, Ref<ReadableJSStream::Source>&& source)
151     : ReadableStream(scriptExecutionContext, WTF::move(source))
152 {
153 }
154
155 ReadableJSStream::Source& ReadableJSStream::jsSource()
156 {
157     return static_cast<ReadableJSStream::Source&>(source());
158 }
159
160 JSValue ReadableJSStream::jsController(ExecState& exec, JSDOMGlobalObject* globalObject)
161 {
162     if (!m_controller)
163         m_controller = std::make_unique<ReadableStreamController>(*this);
164     return toJS(&exec, globalObject, m_controller.get());
165 }
166
167 void ReadableJSStream::storeError(JSC::ExecState& exec)
168 {
169     if (m_error)
170         return;
171     JSValue error = exec.argumentCount() ? exec.argument(0) : createError(&exec, ASCIILiteral("Error function called."));
172     m_error.set(exec.vm(), error);
173
174     if (auto reader = static_cast<Reader*>(this->reader()))
175         reader->storeError(exec, error);
176
177     changeStateToErrored();
178 }
179
180 Ref<ReadableJSStream::Reader> ReadableJSStream::Reader::create(ReadableJSStream& stream)
181 {
182     return adoptRef(*new Reader(stream));
183 }
184
185 ReadableJSStream::Reader::Reader(ReadableJSStream& readableStream)
186     : ReadableStreamReader(readableStream)
187 {
188 }
189
190 void ReadableJSStream::Reader::storeError(JSC::ExecState& exec, JSValue error)
191 {
192     m_error.set(exec.vm(), error);
193 }
194
195 } // namespace WebCore
196
197 #endif