2 * Copyright (C) 2015 Canon Inc.
3 * Copyright (C) 2015 Igalia S.L.
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:
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.
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.
31 #include "ReadableJSStream.h"
33 #if ENABLE(STREAMS_API)
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/Exception.h>
43 #include <runtime/JSCJSValueInlines.h>
44 #include <runtime/JSString.h>
45 #include <runtime/StructureInlines.h>
51 static inline JSValue getPropertyFromObject(ExecState& exec, JSObject* object, const char* identifier)
53 return object->get(&exec, Identifier::fromString(&exec, identifier));
56 static inline JSValue callFunction(ExecState& exec, JSValue jsFunction, JSValue thisValue, const ArgList& arguments)
59 CallType callType = getCallData(jsFunction, callData);
60 return call(&exec, jsFunction, callType, callData, thisValue, arguments);
63 JSValue ReadableJSStream::invoke(ExecState& exec, const char* propertyName)
65 JSValue function = getPropertyFromObject(exec, m_source.get(), propertyName);
66 if (exec.hadException())
69 if (!function.isFunction()) {
70 if (!function.isUndefined())
71 throwVMError(&exec, createTypeError(&exec, ASCIILiteral("ReadableStream trying to call a property that is not callable")));
75 MarkedArgumentBuffer arguments;
76 arguments.append(jsController(exec, globalObject()));
77 return callFunction(exec, function, m_source.get(), arguments);
80 JSDOMGlobalObject* ReadableJSStream::globalObject()
82 return jsDynamicCast<JSDOMGlobalObject*>(m_source->globalObject());
85 static void startReadableStreamAsync(ReadableStream& readableStream)
87 RefPtr<ReadableStream> stream = &readableStream;
88 stream->scriptExecutionContext()->postTask([stream](ScriptExecutionContext&) {
93 void ReadableJSStream::doStart(ExecState& exec)
95 JSLockHolder lock(&exec);
97 invoke(exec, "start");
99 if (exec.hadException())
102 // FIXME: Implement handling promise as result of calling start function.
103 startReadableStreamAsync(*this);
106 void ReadableJSStream::doPull()
108 ExecState& state = *globalObject()->globalExec();
109 JSLockHolder lock(&state);
111 invoke(state, "pull");
113 if (state.hadException()) {
114 storeException(state);
115 ASSERT(!state.hadException());
118 // FIXME: Implement handling promise as result of calling pull function.
121 RefPtr<ReadableJSStream> ReadableJSStream::create(ExecState& exec, ScriptExecutionContext& scriptExecutionContext)
124 JSValue value = exec.argument(0);
125 if (value.isObject())
126 jsSource = value.getObject();
127 else if (!value.isUndefined()) {
128 throwVMError(&exec, createTypeError(&exec, ASCIILiteral("ReadableStream constructor first argument, if any, should be an object")));
131 jsSource = JSFinalObject::create(exec.vm(), JSFinalObject::createStructure(exec.vm(), exec.callee()->globalObject(), jsNull(), 1));
133 RefPtr<ReadableJSStream> readableStream = adoptRef(*new ReadableJSStream(scriptExecutionContext, exec, jsSource));
134 readableStream->doStart(exec);
136 if (exec.hadException())
139 return readableStream;
142 ReadableJSStream::ReadableJSStream(ScriptExecutionContext& scriptExecutionContext, ExecState& exec, JSObject* source)
143 : ReadableStream(scriptExecutionContext)
145 m_source.set(exec.vm(), source);
148 JSValue ReadableJSStream::jsController(ExecState& exec, JSDOMGlobalObject* globalObject)
151 m_controller = std::make_unique<ReadableStreamController>(*this);
152 return toJS(&exec, globalObject, m_controller.get());
155 void ReadableJSStream::storeException(JSC::ExecState& state)
157 JSValue exception = state.exception()->value();
158 state.clearException();
159 storeError(state, exception);
162 void ReadableJSStream::storeError(JSC::ExecState& exec)
164 storeError(exec, exec.argumentCount() ? exec.argument(0) : createError(&exec, ASCIILiteral("Error function called.")));
167 void ReadableJSStream::storeError(JSC::ExecState& exec, JSValue error)
171 m_error.set(exec.vm(), error);
173 changeStateToErrored();
176 bool ReadableJSStream::hasValue() const
178 return m_chunkQueue.size();
181 JSValue ReadableJSStream::read()
185 return m_chunkQueue.takeFirst().get();
188 void ReadableJSStream::enqueue(ExecState& exec)
190 ASSERT(!isCloseRequested());
195 JSValue chunk = exec.argumentCount() ? exec.argument(0) : jsUndefined();
196 if (resolveReadCallback(chunk)) {
201 m_chunkQueue.append(JSC::Strong<JSC::Unknown>(exec.vm(), chunk));
202 // FIXME: Compute chunk size.
206 } // namespace WebCore