diff options
Diffstat (limited to 'Source/WebCore/Modules/fetch')
33 files changed, 3533 insertions, 0 deletions
diff --git a/Source/WebCore/Modules/fetch/DOMWindowFetch.cpp b/Source/WebCore/Modules/fetch/DOMWindowFetch.cpp new file mode 100644 index 000000000..c59d3656a --- /dev/null +++ b/Source/WebCore/Modules/fetch/DOMWindowFetch.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2016 Canon Inc. + * Copyright (C) 2017 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "DOMWindowFetch.h" + +#if ENABLE(FETCH_API) + +#include "DOMWindow.h" +#include "Document.h" +#include "FetchResponse.h" + +namespace WebCore { + +void DOMWindowFetch::fetch(DOMWindow& window, FetchRequest& request, Ref<DeferredPromise>&& promise) +{ + auto* document = window.document(); + if (!document) + return; + FetchResponse::fetch(*document, request, WTFMove(promise)); +} + +} // namespace WebCore + +#endif // ENABLE(FETCH_API) diff --git a/Source/WebCore/Modules/fetch/DOMWindowFetch.h b/Source/WebCore/Modules/fetch/DOMWindowFetch.h new file mode 100644 index 000000000..b3d15757a --- /dev/null +++ b/Source/WebCore/Modules/fetch/DOMWindowFetch.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(FETCH_API) + +#include "JSDOMPromise.h" +#include <wtf/Forward.h> + +namespace WebCore { + +class DOMWindow; +class FetchRequest; + +class DOMWindowFetch { +public: + static void fetch(DOMWindow&, FetchRequest&, Ref<DeferredPromise>&&); +}; + +} // namespace WebCore + +#endif // ENABLE(FETCH_API) diff --git a/Source/WebCore/Modules/fetch/DOMWindowFetch.idl b/Source/WebCore/Modules/fetch/DOMWindowFetch.idl new file mode 100644 index 000000000..2c94ed393 --- /dev/null +++ b/Source/WebCore/Modules/fetch/DOMWindowFetch.idl @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +[ + Conditional=FETCH_API, + EnabledAtRuntime=FetchAPI, +] partial interface DOMWindow { + [JSBuiltin] Promise<Response> fetch(any input, optional RequestInit init); + [PrivateIdentifier, ImplementedAs=fetch] Promise<Response> fetchRequest(FetchRequest request); +}; diff --git a/Source/WebCore/Modules/fetch/DOMWindowFetch.js b/Source/WebCore/Modules/fetch/DOMWindowFetch.js new file mode 100644 index 000000000..ef6d075ea --- /dev/null +++ b/Source/WebCore/Modules/fetch/DOMWindowFetch.js @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2016 Apple Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// @conditional=ENABLE(FETCH_API) + +function fetch(input, init) +{ + "use strict"; + + try { + return @fetchRequest(new @Request(input, init)); + } catch (e) { + return @Promise.@reject(e); + } +} diff --git a/Source/WebCore/Modules/fetch/FetchBody.cpp b/Source/WebCore/Modules/fetch/FetchBody.cpp new file mode 100644 index 000000000..8eb878cf0 --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchBody.cpp @@ -0,0 +1,277 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "FetchBody.h" + +#if ENABLE(FETCH_API) + +#include "Document.h" +#include "FetchBodyOwner.h" +#include "FetchHeaders.h" +#include "FetchResponseSource.h" +#include "HTTPHeaderValues.h" +#include "HTTPParsers.h" +#include "JSBlob.h" +#include "JSDOMFormData.h" +#include "JSReadableStream.h" +#include "JSURLSearchParams.h" +#include "ReadableStreamSource.h" +#include <runtime/ArrayBufferView.h> + +namespace WebCore { + +std::optional<FetchBody> FetchBody::extract(ScriptExecutionContext& context, JSC::ExecState& state, JSC::JSValue value, String& contentType) +{ + JSC::VM& vm = state.vm(); + if (value.inherits(vm, JSBlob::info())) { + auto& blob = *JSBlob::toWrapped(vm, value); + contentType = blob.type(); + return FetchBody(blob); + } + if (value.inherits(vm, JSDOMFormData::info())) { + ASSERT(!context.isWorkerGlobalScope()); + auto& domFormData = *JSDOMFormData::toWrapped(vm, value); + auto formData = FormData::createMultiPart(domFormData, domFormData.encoding(), &static_cast<Document&>(context)); + contentType = makeString("multipart/form-data; boundary=", formData->boundary().data()); + return FetchBody(WTFMove(formData)); + } + if (value.isString()) { + contentType = HTTPHeaderValues::textPlainContentType(); + return FetchBody(String { asString(value)->value(&state) }); + } + if (value.inherits(vm, JSURLSearchParams::info())) { + contentType = HTTPHeaderValues::formURLEncodedContentType(); + return FetchBody(*JSURLSearchParams::toWrapped(vm, value)); + } + if (value.inherits(vm, JSReadableStream::info())) { + FetchBody body; + body.m_isReadableStream = true; + return WTFMove(body); + } + if (value.inherits(vm, JSC::JSArrayBuffer::info())) { + ArrayBuffer* data = toUnsharedArrayBuffer(vm, value); + ASSERT(data); + return FetchBody(*data); + } + if (value.inherits(vm, JSC::JSArrayBufferView::info())) + return FetchBody(toUnsharedArrayBufferView(vm, value).releaseConstNonNull()); + + return std::nullopt; +} + +void FetchBody::arrayBuffer(FetchBodyOwner& owner, Ref<DeferredPromise>&& promise) +{ + m_consumer.setType(FetchBodyConsumer::Type::ArrayBuffer); + consume(owner, WTFMove(promise)); +} + +void FetchBody::blob(FetchBodyOwner& owner, Ref<DeferredPromise>&& promise, const String& contentType) +{ + m_consumer.setType(FetchBodyConsumer::Type::Blob); + m_consumer.setContentType(Blob::normalizedContentType(extractMIMETypeFromMediaType(contentType))); + consume(owner, WTFMove(promise)); +} + +void FetchBody::json(FetchBodyOwner& owner, Ref<DeferredPromise>&& promise) +{ + if (isText()) { + fulfillPromiseWithJSON(WTFMove(promise), textBody()); + return; + } + m_consumer.setType(FetchBodyConsumer::Type::JSON); + consume(owner, WTFMove(promise)); +} + +void FetchBody::text(FetchBodyOwner& owner, Ref<DeferredPromise>&& promise) +{ + if (isText()) { + promise->resolve<IDLDOMString>(textBody()); + return; + } + m_consumer.setType(FetchBodyConsumer::Type::Text); + consume(owner, WTFMove(promise)); +} + +void FetchBody::consumeOnceLoadingFinished(FetchBodyConsumer::Type type, Ref<DeferredPromise>&& promise) +{ + m_consumer.setType(type); + m_consumePromise = WTFMove(promise); +} + +void FetchBody::consume(FetchBodyOwner& owner, Ref<DeferredPromise>&& promise) +{ + if (isArrayBuffer()) { + consumeArrayBuffer(WTFMove(promise)); + return; + } + if (isArrayBufferView()) { + consumeArrayBufferView(WTFMove(promise)); + return; + } + if (isText()) { + consumeText(WTFMove(promise), textBody()); + return; + } + if (isURLSearchParams()) { + consumeText(WTFMove(promise), urlSearchParamsBody().toString()); + return; + } + if (isBlob()) { + consumeBlob(owner, WTFMove(promise)); + return; + } + if (isFormData()) { + // FIXME: Support consuming FormData. + promise->reject(); + return; + } + m_consumer.resolve(WTFMove(promise)); +} + +#if ENABLE(READABLE_STREAM_API) +void FetchBody::consumeAsStream(FetchBodyOwner& owner, FetchResponseSource& source) +{ + bool closeStream = false; + if (isArrayBuffer()) { + closeStream = source.enqueue(ArrayBuffer::tryCreate(arrayBufferBody().data(), arrayBufferBody().byteLength())); + m_data = nullptr; + } else if (isArrayBufferView()) { + closeStream = source.enqueue(ArrayBuffer::tryCreate(arrayBufferViewBody().baseAddress(), arrayBufferViewBody().byteLength())); + m_data = nullptr; + } else if (isText()) { + auto data = UTF8Encoding().encode(textBody(), EntitiesForUnencodables); + closeStream = source.enqueue(ArrayBuffer::tryCreate(data.data(), data.length())); + m_data = nullptr; + } else if (isURLSearchParams()) { + auto data = UTF8Encoding().encode(urlSearchParamsBody().toString(), EntitiesForUnencodables); + closeStream = source.enqueue(ArrayBuffer::tryCreate(data.data(), data.length())); + m_data = nullptr; + } else if (isBlob()) { + owner.loadBlob(blobBody(), nullptr); + m_data = nullptr; + } else if (isFormData()) + source.error(ASCIILiteral("not implemented")); + else if (m_consumer.hasData()) + closeStream = source.enqueue(m_consumer.takeAsArrayBuffer()); + else + closeStream = true; + + if (closeStream) + source.close(); +} +#endif + +void FetchBody::consumeArrayBuffer(Ref<DeferredPromise>&& promise) +{ + m_consumer.resolveWithData(WTFMove(promise), static_cast<const uint8_t*>(arrayBufferBody().data()), arrayBufferBody().byteLength()); + m_data = nullptr; +} + +void FetchBody::consumeArrayBufferView(Ref<DeferredPromise>&& promise) +{ + m_consumer.resolveWithData(WTFMove(promise), static_cast<const uint8_t*>(arrayBufferViewBody().baseAddress()), arrayBufferViewBody().byteLength()); + m_data = nullptr; +} + +void FetchBody::consumeText(Ref<DeferredPromise>&& promise, const String& text) +{ + auto data = UTF8Encoding().encode(text, EntitiesForUnencodables); + m_consumer.resolveWithData(WTFMove(promise), reinterpret_cast<const uint8_t*>(data.data()), data.length()); + m_data = nullptr; +} + +void FetchBody::consumeBlob(FetchBodyOwner& owner, Ref<DeferredPromise>&& promise) +{ + m_consumePromise = WTFMove(promise); + owner.loadBlob(blobBody(), &m_consumer); + m_data = nullptr; +} + +void FetchBody::loadingFailed() +{ + if (m_consumePromise) { + m_consumePromise->reject(); + m_consumePromise = nullptr; + } +} + +void FetchBody::loadingSucceeded() +{ + if (m_consumePromise) + m_consumer.resolve(m_consumePromise.releaseNonNull()); +} + +RefPtr<FormData> FetchBody::bodyForInternalRequest(ScriptExecutionContext& context) const +{ + if (isText()) + return FormData::create(UTF8Encoding().encode(textBody(), EntitiesForUnencodables)); + if (isURLSearchParams()) + return FormData::create(UTF8Encoding().encode(urlSearchParamsBody().toString(), EntitiesForUnencodables)); + if (isBlob()) { + RefPtr<FormData> body = FormData::create(); + body->appendBlob(blobBody().url()); + return body; + } + if (isArrayBuffer()) + return FormData::create(arrayBufferBody().data(), arrayBufferBody().byteLength()); + if (isArrayBufferView()) + return FormData::create(arrayBufferViewBody().baseAddress(), arrayBufferViewBody().byteLength()); + if (isFormData()) { + ASSERT(!context.isWorkerGlobalScope()); + RefPtr<FormData> body = const_cast<FormData*>(&formDataBody()); + body->generateFiles(static_cast<Document*>(&context)); + return body; + } + ASSERT_NOT_REACHED(); + return nullptr; +} + +FetchBody FetchBody::clone() const +{ + ASSERT(!m_consumePromise); + FetchBody clone(m_consumer); + + if (isArrayBuffer()) + clone.m_data = arrayBufferBody(); + else if (isArrayBufferView()) + clone.m_data = arrayBufferViewBody(); + else if (isBlob()) + clone.m_data = blobBody(); + else if (isFormData()) + clone.m_data = const_cast<FormData&>(formDataBody()); + else if (isText()) + clone.m_data = textBody(); + else if (isURLSearchParams()) + clone.m_data = urlSearchParamsBody(); + return clone; +} + +} + +#endif // ENABLE(FETCH_API) diff --git a/Source/WebCore/Modules/fetch/FetchBody.h b/Source/WebCore/Modules/fetch/FetchBody.h new file mode 100644 index 000000000..d57e6a935 --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchBody.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(FETCH_API) + +#include "Blob.h" +#include "FetchBodyConsumer.h" +#include "FetchLoader.h" +#include "FormData.h" +#include "JSDOMPromise.h" +#include "URLSearchParams.h" +#include <wtf/Optional.h> +#include <wtf/Variant.h> + +namespace JSC { +class ExecState; +class JSValue; +}; + +namespace WebCore { + +class FetchBodyOwner; +class FetchResponseSource; +class ScriptExecutionContext; + +class FetchBody { +public: + void arrayBuffer(FetchBodyOwner&, Ref<DeferredPromise>&&); + void blob(FetchBodyOwner&, Ref<DeferredPromise>&&, const String&); + void json(FetchBodyOwner&, Ref<DeferredPromise>&&); + void text(FetchBodyOwner&, Ref<DeferredPromise>&&); + void formData(FetchBodyOwner&, Ref<DeferredPromise>&& promise) { promise.get().reject(0); } + +#if ENABLE(READABLE_STREAM_API) + void consumeAsStream(FetchBodyOwner&, FetchResponseSource&); +#endif + + bool isBlob() const { return WTF::holds_alternative<Ref<const Blob>>(m_data); } + bool isFormData() const { return WTF::holds_alternative<Ref<FormData>>(m_data); } + bool isArrayBuffer() const { return WTF::holds_alternative<Ref<const ArrayBuffer>>(m_data); } + bool isArrayBufferView() const { return WTF::holds_alternative<Ref<const ArrayBufferView>>(m_data); } + bool isURLSearchParams() const { return WTF::holds_alternative<Ref<const URLSearchParams>>(m_data); } + bool isText() const { return WTF::holds_alternative<String>(m_data); } + bool isReadableStream() const { return m_isReadableStream; } + + static std::optional<FetchBody> extract(ScriptExecutionContext&, JSC::ExecState&, JSC::JSValue, String&); + static FetchBody loadingBody() { return { }; } + + void loadingFailed(); + void loadingSucceeded(); + + RefPtr<FormData> bodyForInternalRequest(ScriptExecutionContext&) const; + + FetchBodyConsumer& consumer() { return m_consumer; } + + void consumeOnceLoadingFinished(FetchBodyConsumer::Type, Ref<DeferredPromise>&&); + void cleanConsumePromise() { m_consumePromise = nullptr; } + + FetchBody clone() const; + +private: + explicit FetchBody(Ref<const Blob>&& data) : m_data(WTFMove(data)) { } + explicit FetchBody(Ref<const ArrayBuffer>&& data) : m_data(WTFMove(data)) { } + explicit FetchBody(Ref<const ArrayBufferView>&& data) : m_data(WTFMove(data)) { } + explicit FetchBody(Ref<FormData>&& data) : m_data(WTFMove(data)) { } + explicit FetchBody(String&& data) : m_data(WTFMove(data)) { } + explicit FetchBody(Ref<const URLSearchParams>&& data) : m_data(WTFMove(data)) { } + explicit FetchBody(const FetchBodyConsumer& consumer) : m_consumer(consumer) { } + FetchBody() = default; + + void consume(FetchBodyOwner&, Ref<DeferredPromise>&&); + + void consumeArrayBuffer(Ref<DeferredPromise>&&); + void consumeArrayBufferView(Ref<DeferredPromise>&&); + void consumeText(Ref<DeferredPromise>&&, const String&); + void consumeBlob(FetchBodyOwner&, Ref<DeferredPromise>&&); + + const Blob& blobBody() const { return WTF::get<Ref<const Blob>>(m_data).get(); } + FormData& formDataBody() { return WTF::get<Ref<FormData>>(m_data).get(); } + const FormData& formDataBody() const { return WTF::get<Ref<FormData>>(m_data).get(); } + const ArrayBuffer& arrayBufferBody() const { return WTF::get<Ref<const ArrayBuffer>>(m_data).get(); } + const ArrayBufferView& arrayBufferViewBody() const { return WTF::get<Ref<const ArrayBufferView>>(m_data).get(); } + String& textBody() { return WTF::get<String>(m_data); } + const String& textBody() const { return WTF::get<String>(m_data); } + const URLSearchParams& urlSearchParamsBody() const { return WTF::get<Ref<const URLSearchParams>>(m_data).get(); } + + Variant<std::nullptr_t, Ref<const Blob>, Ref<FormData>, Ref<const ArrayBuffer>, Ref<const ArrayBufferView>, Ref<const URLSearchParams>, String> m_data { nullptr }; + + FetchBodyConsumer m_consumer { FetchBodyConsumer::Type::None }; + RefPtr<DeferredPromise> m_consumePromise; + + // FIXME: We probably want to keep the stream as a specific field in m_data when we will support stream data upload. + bool m_isReadableStream { false }; +}; + +} // namespace WebCore + +#endif // ENABLE(FETCH_API) diff --git a/Source/WebCore/Modules/fetch/FetchBody.idl b/Source/WebCore/Modules/fetch/FetchBody.idl new file mode 100644 index 000000000..5392c6f01 --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchBody.idl @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +[ + Conditional=FETCH_API, + EnabledAtRuntime=FetchAPI, + Exposed=(Window,Worker), + InterfaceName=Body, + NoInterfaceObject +] +interface FetchBody { + [ImplementedAs=isDisturbed] readonly attribute boolean bodyUsed; + [NewObject] Promise<ArrayBuffer> arrayBuffer(); + [NewObject] Promise<Blob> blob(); + // FIXME: Add support for form data consumption (https://bugs.webkit.org/show_bug.cgi?id=161190). + //[NewObject] Promise<DOMFormData> formData(); + [NewObject] Promise<any> json(); + [NewObject] Promise<USVString> text(); +}; diff --git a/Source/WebCore/Modules/fetch/FetchBodyConsumer.cpp b/Source/WebCore/Modules/fetch/FetchBodyConsumer.cpp new file mode 100644 index 000000000..e82a74a25 --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchBodyConsumer.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2016 Apple Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "FetchBodyConsumer.h" + +#if ENABLE(FETCH_API) + +#include "JSBlob.h" +#include "TextResourceDecoder.h" + +namespace WebCore { + +static inline Ref<Blob> blobFromData(const unsigned char* data, unsigned length, const String& contentType) +{ + Vector<uint8_t> value(length); + memcpy(value.data(), data, length); + return Blob::create(WTFMove(value), contentType); +} + +static inline bool shouldPrependBOM(const unsigned char* data, unsigned length) +{ + if (length < 3) + return true; + return data[0] != 0xef || data[1] != 0xbb || data[2] != 0xbf; +} + +static String textFromUTF8(const unsigned char* data, unsigned length) +{ + auto decoder = TextResourceDecoder::create("text/plain", "UTF-8"); + if (shouldPrependBOM(data, length)) + decoder->decode("\xef\xbb\xbf", 3); + return decoder->decodeAndFlush(reinterpret_cast<const char*>(data), length); +} + +void FetchBodyConsumer::resolveWithData(Ref<DeferredPromise>&& promise, const unsigned char* data, unsigned length) +{ + switch (m_type) { + case Type::ArrayBuffer: + fulfillPromiseWithArrayBuffer(WTFMove(promise), data, length); + return; + case Type::Blob: + promise->resolveWithNewlyCreated<IDLInterface<Blob>>(blobFromData(data, length, m_contentType).get()); + return; + case Type::JSON: + fulfillPromiseWithJSON(WTFMove(promise), textFromUTF8(data, length)); + return; + case Type::Text: + promise->resolve<IDLDOMString>(textFromUTF8(data, length)); + return; + case Type::None: + ASSERT_NOT_REACHED(); + return; + } +} + +void FetchBodyConsumer::resolve(Ref<DeferredPromise>&& promise) +{ + ASSERT(m_type != Type::None); + switch (m_type) { + case Type::ArrayBuffer: + fulfillPromiseWithArrayBuffer(WTFMove(promise), takeAsArrayBuffer().get()); + return; + case Type::Blob: + promise->resolveWithNewlyCreated<IDLInterface<Blob>>(takeAsBlob().get()); + return; + case Type::JSON: + fulfillPromiseWithJSON(WTFMove(promise), takeAsText()); + return; + case Type::Text: + promise->resolve<IDLDOMString>(takeAsText()); + return; + case Type::None: + ASSERT_NOT_REACHED(); + return; + } +} + +void FetchBodyConsumer::append(const char* data, unsigned length) +{ + if (!m_buffer) { + m_buffer = SharedBuffer::create(data, length); + return; + } + m_buffer->append(data, length); +} + +void FetchBodyConsumer::append(const unsigned char* data, unsigned length) +{ + append(reinterpret_cast<const char*>(data), length); +} + +RefPtr<SharedBuffer> FetchBodyConsumer::takeData() +{ + return WTFMove(m_buffer); +} + +RefPtr<JSC::ArrayBuffer> FetchBodyConsumer::takeAsArrayBuffer() +{ + if (!m_buffer) + return ArrayBuffer::tryCreate(nullptr, 0); + + auto arrayBuffer = m_buffer->createArrayBuffer(); + m_buffer = nullptr; + return arrayBuffer; +} + +Ref<Blob> FetchBodyConsumer::takeAsBlob() +{ + if (!m_buffer) + return Blob::create(Vector<uint8_t>(), m_contentType); + + // FIXME: We should try to move m_buffer to Blob without doing extra copy. + return blobFromData(reinterpret_cast<const unsigned char*>(m_buffer->data()), m_buffer->size(), m_contentType); +} + +String FetchBodyConsumer::takeAsText() +{ + // FIXME: We could probably text decode on the fly as soon as m_type is set to JSON or Text. + if (!m_buffer) + return String(); + + auto text = textFromUTF8(reinterpret_cast<const unsigned char*>(m_buffer->data()), m_buffer->size()); + m_buffer = nullptr; + return text; +} + +} // namespace WebCore + +#endif // ENABLE(FETCH_API) diff --git a/Source/WebCore/Modules/fetch/FetchBodyConsumer.h b/Source/WebCore/Modules/fetch/FetchBodyConsumer.h new file mode 100644 index 000000000..4a0fb39f1 --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchBodyConsumer.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2016 Apple Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(FETCH_API) + +#include "JSDOMPromise.h" +#include "SharedBuffer.h" + +namespace WebCore { + +class Blob; + +class FetchBodyConsumer { +public: + // Type is used in FetchResponse.js and should be kept synchronized with it. + enum class Type { None, ArrayBuffer, Blob, JSON, Text }; + + FetchBodyConsumer(Type type) : m_type(type) { } + + void append(const char* data, unsigned); + void append(const unsigned char* data, unsigned); + + RefPtr<SharedBuffer> takeData(); + RefPtr<JSC::ArrayBuffer> takeAsArrayBuffer(); + Ref<Blob> takeAsBlob(); + String takeAsText(); + + void setContentType(const String& contentType) { m_contentType = contentType; } + void setType(Type type) { m_type = type; } + + void clean() { m_buffer = nullptr; } + + void resolve(Ref<DeferredPromise>&&); + void resolveWithData(Ref<DeferredPromise>&&, const unsigned char*, unsigned); + + bool hasData() const { return !!m_buffer; } + +private: + Type m_type; + String m_contentType; + RefPtr<SharedBuffer> m_buffer; +}; + +} // namespace WebCore + +#endif // ENABLE(FETCH_API) diff --git a/Source/WebCore/Modules/fetch/FetchBodyOwner.cpp b/Source/WebCore/Modules/fetch/FetchBodyOwner.cpp new file mode 100644 index 000000000..380c570e9 --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchBodyOwner.cpp @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "FetchBodyOwner.h" + +#if ENABLE(FETCH_API) + +#include "FetchLoader.h" +#include "HTTPParsers.h" +#include "JSBlob.h" +#include "ResourceResponse.h" + +namespace WebCore { + +FetchBodyOwner::FetchBodyOwner(ScriptExecutionContext& context, std::optional<FetchBody>&& body, Ref<FetchHeaders>&& headers) + : ActiveDOMObject(&context) + , m_body(WTFMove(body)) + , m_headers(WTFMove(headers)) +{ + suspendIfNeeded(); +} + +void FetchBodyOwner::stop() +{ + if (m_body) + m_body->cleanConsumePromise(); + + if (m_blobLoader) { + bool isUniqueReference = hasOneRef(); + if (m_blobLoader->loader) + m_blobLoader->loader->stop(); + // After that point, 'this' may be destroyed, since unsetPendingActivity should have been called. + ASSERT_UNUSED(isUniqueReference, isUniqueReference || !m_blobLoader); + } +} + +bool FetchBodyOwner::isDisturbedOrLocked() const +{ + if (m_isDisturbed) + return true; + +#if ENABLE(READABLE_STREAM_API) + if (m_readableStreamSource && m_readableStreamSource->isReadableStreamLocked()) + return true; +#endif + + return false; +} + +void FetchBodyOwner::arrayBuffer(Ref<DeferredPromise>&& promise) +{ + if (isBodyNull()) { + fulfillPromiseWithArrayBuffer(WTFMove(promise), nullptr, 0); + return; + } + if (isDisturbedOrLocked()) { + promise->reject(TypeError); + return; + } + m_isDisturbed = true; + m_body->arrayBuffer(*this, WTFMove(promise)); +} + +void FetchBodyOwner::blob(Ref<DeferredPromise>&& promise) +{ + if (isBodyNull()) { + promise->resolve<IDLInterface<Blob>>(Blob::create({ }, Blob::normalizedContentType(extractMIMETypeFromMediaType(m_contentType))).get()); + return; + } + if (isDisturbedOrLocked()) { + promise->reject(TypeError); + return; + } + m_isDisturbed = true; + m_body->blob(*this, WTFMove(promise), m_contentType); +} + +void FetchBodyOwner::cloneBody(const FetchBodyOwner& owner) +{ + m_contentType = owner.m_contentType; + if (owner.isBodyNull()) + return; + m_body = owner.m_body->clone(); +} + +void FetchBodyOwner::extractBody(ScriptExecutionContext& context, JSC::ExecState& state, JSC::JSValue value) +{ + m_body = FetchBody::extract(context, state, value, m_contentType); +} + +void FetchBodyOwner::updateContentType() +{ + String contentType = m_headers->fastGet(HTTPHeaderName::ContentType); + if (!contentType.isNull()) { + m_contentType = WTFMove(contentType); + return; + } + if (!m_contentType.isNull()) + m_headers->fastSet(HTTPHeaderName::ContentType, m_contentType); +} + +void FetchBodyOwner::consumeOnceLoadingFinished(FetchBodyConsumer::Type type, Ref<DeferredPromise>&& promise) +{ + if (isDisturbedOrLocked()) { + promise->reject(TypeError); + return; + } + m_isDisturbed = true; + m_body->consumeOnceLoadingFinished(type, WTFMove(promise)); +} + +void FetchBodyOwner::formData(Ref<DeferredPromise>&& promise) +{ + if (isBodyNull()) { + promise->reject(); + return; + } + if (isDisturbedOrLocked()) { + promise->reject(TypeError); + return; + } + m_isDisturbed = true; + m_body->formData(*this, WTFMove(promise)); +} + +void FetchBodyOwner::json(Ref<DeferredPromise>&& promise) +{ + if (isBodyNull()) { + promise->reject(SYNTAX_ERR); + return; + } + if (isDisturbedOrLocked()) { + promise->reject(TypeError); + return; + } + m_isDisturbed = true; + m_body->json(*this, WTFMove(promise)); +} + +void FetchBodyOwner::text(Ref<DeferredPromise>&& promise) +{ + if (isBodyNull()) { + promise->resolve<IDLDOMString>({ }); + return; + } + if (isDisturbedOrLocked()) { + promise->reject(TypeError); + return; + } + m_isDisturbed = true; + m_body->text(*this, WTFMove(promise)); +} + +void FetchBodyOwner::loadBlob(const Blob& blob, FetchBodyConsumer* consumer) +{ + // Can only be called once for a body instance. + ASSERT(isDisturbed()); + ASSERT(!m_blobLoader); + ASSERT(!isBodyNull()); + + if (!scriptExecutionContext()) { + m_body->loadingFailed(); + return; + } + + m_blobLoader.emplace(*this); + m_blobLoader->loader = std::make_unique<FetchLoader>(*m_blobLoader, consumer); + + m_blobLoader->loader->start(*scriptExecutionContext(), blob); + if (!m_blobLoader->loader->isStarted()) { + m_body->loadingFailed(); + m_blobLoader = std::nullopt; + return; + } + setPendingActivity(this); +} + +void FetchBodyOwner::finishBlobLoading() +{ + ASSERT(m_blobLoader); + + m_blobLoader = std::nullopt; + unsetPendingActivity(this); +} + +void FetchBodyOwner::blobLoadingSucceeded() +{ + ASSERT(!isBodyNull()); +#if ENABLE(READABLE_STREAM_API) + if (m_readableStreamSource) { + m_readableStreamSource->close(); + m_readableStreamSource = nullptr; + } +#endif + m_body->loadingSucceeded(); + finishBlobLoading(); +} + +void FetchBodyOwner::blobLoadingFailed() +{ + ASSERT(!isBodyNull()); +#if ENABLE(READABLE_STREAM_API) + if (m_readableStreamSource) { + if (!m_readableStreamSource->isCancelling()) + m_readableStreamSource->error(ASCIILiteral("Blob loading failed")); + m_readableStreamSource = nullptr; + } else +#endif + m_body->loadingFailed(); + + finishBlobLoading(); +} + +void FetchBodyOwner::blobChunk(const char* data, size_t size) +{ + ASSERT(data); +#if ENABLE(READABLE_STREAM_API) + ASSERT(m_readableStreamSource); + if (!m_readableStreamSource->enqueue(ArrayBuffer::tryCreate(data, size))) + stop(); +#else + UNUSED_PARAM(data); + UNUSED_PARAM(size); +#endif +} + +FetchBodyOwner::BlobLoader::BlobLoader(FetchBodyOwner& owner) + : owner(owner) +{ +} + +void FetchBodyOwner::BlobLoader::didReceiveResponse(const ResourceResponse& response) +{ + if (response.httpStatusCode() != 200) + didFail(); +} + +void FetchBodyOwner::BlobLoader::didFail() +{ + // didFail might be called within FetchLoader::start call. + if (loader->isStarted()) + owner.blobLoadingFailed(); +} + +} // namespace WebCore + +#endif // ENABLE(FETCH_API) diff --git a/Source/WebCore/Modules/fetch/FetchBodyOwner.h b/Source/WebCore/Modules/fetch/FetchBodyOwner.h new file mode 100644 index 000000000..88e588f1c --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchBodyOwner.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(FETCH_API) + +#include "ActiveDOMObject.h" +#include "FetchBody.h" +#include "FetchHeaders.h" +#include "FetchLoaderClient.h" +#include "FetchResponseSource.h" + +namespace WebCore { + +class FetchLoader; + +class FetchBodyOwner : public RefCounted<FetchBodyOwner>, public ActiveDOMObject { +public: + FetchBodyOwner(ScriptExecutionContext&, std::optional<FetchBody>&&, Ref<FetchHeaders>&&); + + // Exposed Body API + bool isDisturbed() const { return m_isDisturbed; }; + + void arrayBuffer(Ref<DeferredPromise>&&); + void blob(Ref<DeferredPromise>&&); + void formData(Ref<DeferredPromise>&&); + void json(Ref<DeferredPromise>&&); + void text(Ref<DeferredPromise>&&); + + bool isDisturbedOrLocked() const; + + void loadBlob(const Blob&, FetchBodyConsumer*); + + bool isActive() const { return !!m_blobLoader; } + +protected: + const FetchBody& body() const { return *m_body; } + FetchBody& body() { return *m_body; } + bool isBodyNull() const { return !m_body; } + void cloneBody(const FetchBodyOwner&); + + void extractBody(ScriptExecutionContext&, JSC::ExecState&, JSC::JSValue); + void updateContentType(); + void consumeOnceLoadingFinished(FetchBodyConsumer::Type, Ref<DeferredPromise>&&); + + // ActiveDOMObject API + void stop() override; + + void setDisturbed() { m_isDisturbed = true; } + +private: + // Blob loading routines + void blobChunk(const char*, size_t); + void blobLoadingSucceeded(); + void blobLoadingFailed(); + void finishBlobLoading(); + + struct BlobLoader final : FetchLoaderClient { + BlobLoader(FetchBodyOwner&); + + // FetchLoaderClient API + void didReceiveResponse(const ResourceResponse&) final; + void didReceiveData(const char* data, size_t size) final { owner.blobChunk(data, size); } + void didFail() final; + void didSucceed() final { owner.blobLoadingSucceeded(); } + + FetchBodyOwner& owner; + std::unique_ptr<FetchLoader> loader; + }; + +protected: + std::optional<FetchBody> m_body; + String m_contentType; + bool m_isDisturbed { false }; +#if ENABLE(READABLE_STREAM_API) + RefPtr<FetchResponseSource> m_readableStreamSource; +#endif + Ref<FetchHeaders> m_headers; + +private: + std::optional<BlobLoader> m_blobLoader; +}; + +} // namespace WebCore + +#endif // ENABLE(FETCH_API) diff --git a/Source/WebCore/Modules/fetch/FetchHeaders.cpp b/Source/WebCore/Modules/fetch/FetchHeaders.cpp new file mode 100644 index 000000000..6e07674a9 --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchHeaders.cpp @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "FetchHeaders.h" + +#if ENABLE(FETCH_API) + +#include "ExceptionCode.h" +#include "HTTPParsers.h" + +namespace WebCore { + +static ExceptionOr<bool> canWriteHeader(const String& name, const String& value, FetchHeaders::Guard guard) +{ + if (!isValidHTTPToken(name) || !isValidHTTPHeaderValue(value)) + return Exception { TypeError }; + if (guard == FetchHeaders::Guard::Immutable) + return Exception { TypeError }; + if (guard == FetchHeaders::Guard::Request && isForbiddenHeaderName(name)) + return false; + if (guard == FetchHeaders::Guard::RequestNoCors && !isSimpleHeader(name, value)) + return false; + if (guard == FetchHeaders::Guard::Response && isForbiddenResponseHeaderName(name)) + return false; + return true; +} + +ExceptionOr<void> FetchHeaders::append(const String& name, const String& value) +{ + String normalizedValue = stripLeadingAndTrailingHTTPSpaces(value); + auto canWriteResult = canWriteHeader(name, normalizedValue, m_guard); + if (canWriteResult.hasException()) + return canWriteResult.releaseException(); + if (!canWriteResult.releaseReturnValue()) + return { }; + m_headers.add(name, normalizedValue); + return { }; +} + +ExceptionOr<void> FetchHeaders::remove(const String& name) +{ + auto canWriteResult = canWriteHeader(name, { }, m_guard); + if (canWriteResult.hasException()) + return canWriteResult.releaseException(); + if (!canWriteResult.releaseReturnValue()) + return { }; + m_headers.remove(name); + return { }; +} + +ExceptionOr<String> FetchHeaders::get(const String& name) const +{ + if (!isValidHTTPToken(name)) + return Exception { TypeError }; + return m_headers.get(name); +} + +ExceptionOr<bool> FetchHeaders::has(const String& name) const +{ + if (!isValidHTTPToken(name)) + return Exception { TypeError }; + return m_headers.contains(name); +} + +ExceptionOr<void> FetchHeaders::set(const String& name, const String& value) +{ + String normalizedValue = stripLeadingAndTrailingHTTPSpaces(value); + auto canWriteResult = canWriteHeader(name, normalizedValue, m_guard); + if (canWriteResult.hasException()) + return canWriteResult.releaseException(); + if (!canWriteResult.releaseReturnValue()) + return { }; + m_headers.set(name, normalizedValue); + return { }; +} + +void FetchHeaders::fill(const FetchHeaders* headers) +{ + ASSERT(m_guard != Guard::Immutable); + if (!headers) + return; + filterAndFill(headers->m_headers, m_guard); +} + +void FetchHeaders::filterAndFill(const HTTPHeaderMap& headers, Guard guard) +{ + for (auto& header : headers) { + auto canWriteResult = canWriteHeader(header.key, header.value, guard); + if (canWriteResult.hasException()) + continue; + if (!canWriteResult.releaseReturnValue()) + continue; + if (header.keyAsHTTPHeaderName) + m_headers.add(header.keyAsHTTPHeaderName.value(), header.value); + else + m_headers.add(header.key, header.value); + } +} + +std::optional<WTF::KeyValuePair<String, String>> FetchHeaders::Iterator::next() +{ + while (m_currentIndex < m_keys.size()) { + auto key = m_keys[m_currentIndex++]; + auto value = m_headers->m_headers.get(key); + if (!value.isNull()) + return WTF::KeyValuePair<String, String> { WTFMove(key), WTFMove(value) }; + } + return std::nullopt; +} + +FetchHeaders::Iterator::Iterator(FetchHeaders& headers) + : m_headers(headers) +{ + m_keys.reserveInitialCapacity(headers.m_headers.size()); + for (auto& header : headers.m_headers) + m_keys.uncheckedAppend(header.key.convertToASCIILowercase()); + std::sort(m_keys.begin(), m_keys.end(), WTF::codePointCompareLessThan); +} + +} // namespace WebCore + +#endif // ENABLE(FETCH_API) diff --git a/Source/WebCore/Modules/fetch/FetchHeaders.h b/Source/WebCore/Modules/fetch/FetchHeaders.h new file mode 100644 index 000000000..7f64ed11a --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchHeaders.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(FETCH_API) + +#include "ExceptionOr.h" +#include "HTTPHeaderMap.h" +#include <wtf/HashTraits.h> + +namespace WebCore { + +class FetchHeaders : public RefCounted<FetchHeaders> { +public: + enum class Guard { + None, + Immutable, + Request, + RequestNoCors, + Response + }; + + static Ref<FetchHeaders> create(Guard guard = Guard::None) { return adoptRef(*new FetchHeaders { guard }); } + static Ref<FetchHeaders> create(const FetchHeaders& headers) { return adoptRef(*new FetchHeaders { headers }); } + + ExceptionOr<void> append(const String& name, const String& value); + ExceptionOr<void> remove(const String&); + ExceptionOr<String> get(const String&) const; + ExceptionOr<bool> has(const String&) const; + ExceptionOr<void> set(const String& name, const String& value); + + void fill(const FetchHeaders*); + void filterAndFill(const HTTPHeaderMap&, Guard); + + String fastGet(HTTPHeaderName name) const { return m_headers.get(name); } + void fastSet(HTTPHeaderName name, const String& value) { m_headers.set(name, value); } + + class Iterator { + public: + explicit Iterator(FetchHeaders&); + std::optional<WTF::KeyValuePair<String, String>> next(); + + private: + Ref<FetchHeaders> m_headers; + size_t m_currentIndex { 0 }; + Vector<String> m_keys; + }; + Iterator createIterator() { return Iterator { *this }; } + + const HTTPHeaderMap& internalHeaders() const { return m_headers; } + + void setGuard(Guard); + +private: + FetchHeaders(Guard guard) : m_guard(guard) { } + FetchHeaders(const FetchHeaders&); + + Guard m_guard; + HTTPHeaderMap m_headers; +}; + +inline FetchHeaders::FetchHeaders(const FetchHeaders& other) + : RefCounted() + , m_guard(other.m_guard) + , m_headers(other.m_headers) +{ +} + +inline void FetchHeaders::setGuard(Guard guard) +{ + ASSERT(!m_headers.size()); + m_guard = guard; +} + +} // namespace WebCore + +#endif // ENABLE(FETCH_API) diff --git a/Source/WebCore/Modules/fetch/FetchHeaders.idl b/Source/WebCore/Modules/fetch/FetchHeaders.idl new file mode 100644 index 000000000..b2016a479 --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchHeaders.idl @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +[ + Conditional=FETCH_API, + EnabledAtRuntime=FetchAPI, + Exposed=(Window,Worker), + ImplementationLacksVTable, + InterfaceName=Headers, + JSBuiltinConstructor, + PrivateIdentifier, + PublicIdentifier, +] interface FetchHeaders { + [MayThrowException] void append(DOMString name, DOMString value); + [MayThrowException, ImplementedAs=remove] void delete(DOMString name); + [MayThrowException] DOMString? get(DOMString name); + [MayThrowException] boolean has(DOMString name); + [MayThrowException] void set(DOMString name, DOMString value); + + iterable<DOMString, DOMString>; + + [ImplementedAs=append, MayThrowException, PrivateIdentifier] void appendFromJS(DOMString name, DOMString value); + [ImplementedAs=fill, PrivateIdentifier] void fillFromJS(FetchHeaders? headers); +}; diff --git a/Source/WebCore/Modules/fetch/FetchHeaders.js b/Source/WebCore/Modules/fetch/FetchHeaders.js new file mode 100644 index 000000000..e8c2d7598 --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchHeaders.js @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CANON INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// @conditional=ENABLE(FETCH_API) + +function initializeFetchHeaders(headersInit) +{ + "use strict"; + + if (headersInit === @undefined) + return this; + + if (!@isObject(headersInit)) + @throwTypeError("headersInit must be an object"); + + @fillFetchHeaders(this, headersInit); + + return this; +} diff --git a/Source/WebCore/Modules/fetch/FetchInternals.js b/Source/WebCore/Modules/fetch/FetchInternals.js new file mode 100644 index 000000000..3b91cd3bd --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchInternals.js @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2016 Apple Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// @conditional=ENABLE(FETCH_API) +// @internal + +function fillFetchHeaders(headers, headersInit) +{ + "use strict"; + + if (headersInit === @undefined) + return; + + if (headersInit instanceof @Headers) { + @Headers.prototype.@fillFromJS.@call(headers, headersInit); + return; + } + + if (@isArray(headersInit)) { + for (let i = 0; i < headersInit.length; i++) { + let header = headersInit[i]; + if (header.length !== 2) + @throwTypeError("headersInit sequence items should contain two values"); + @Headers.prototype.@appendFromJS.@call(headers, header[0], header[1]); + } + return this; + } + + let propertyNames = @Object.@getOwnPropertyNames(headersInit); + for (let i = 0; i < propertyNames.length; ++i) { + let name = propertyNames[i]; + @Headers.prototype.@appendFromJS.@call(headers, name, headersInit[name]); + } +} + +function consumeStream(response, type) +{ + @assert(response instanceof @Response); + @assert(response.@body instanceof @ReadableStream); + + if (@isReadableStreamDisturbed(response.@body)) + return @Promise.@reject(new @TypeError("Cannot consume a disturbed Response body ReadableStream")); + + try { + let reader = new @ReadableStreamDefaultReader(response.@body); + + @Response.prototype.@startConsumingStream.@call(response, type); + let pull = (result) => { + if (result.done) + return @Response.prototype.@finishConsumingStream.@call(response); + @Response.prototype.@consumeChunk.@call(response, result.value); + return @Promise.prototype.@then.@call(@readableStreamDefaultReaderRead(reader), pull); + } + return @Promise.prototype.@then.@call(@readableStreamDefaultReaderRead(reader), pull); + } catch (e) { + return @Promise.@reject(e); + } +} diff --git a/Source/WebCore/Modules/fetch/FetchLoader.cpp b/Source/WebCore/Modules/fetch/FetchLoader.cpp new file mode 100644 index 000000000..6e2fd5c81 --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchLoader.cpp @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "FetchLoader.h" + +#if ENABLE(FETCH_API) + +#include "BlobURL.h" +#include "CachedResourceRequestInitiators.h" +#include "ContentSecurityPolicy.h" +#include "FetchBody.h" +#include "FetchLoaderClient.h" +#include "FetchRequest.h" +#include "ResourceRequest.h" +#include "ScriptExecutionContext.h" +#include "SecurityOrigin.h" +#include "SharedBuffer.h" +#include "TextResourceDecoder.h" +#include "ThreadableBlobRegistry.h" + +namespace WebCore { + +void FetchLoader::start(ScriptExecutionContext& context, const Blob& blob) +{ + auto urlForReading = BlobURL::createPublicURL(context.securityOrigin()); + if (urlForReading.isEmpty()) { + m_client.didFail(); + return; + } + + ThreadableBlobRegistry::registerBlobURL(context.securityOrigin(), urlForReading, blob.url()); + + ResourceRequest request(urlForReading); + request.setInitiatorIdentifier(context.resourceRequestIdentifier()); + request.setHTTPMethod("GET"); + + ThreadableLoaderOptions options; + options.sendLoadCallbacks = SendCallbacks; + options.dataBufferingPolicy = DoNotBufferData; + options.preflightPolicy = ConsiderPreflight; + options.credentials = FetchOptions::Credentials::Include; + options.mode = FetchOptions::Mode::SameOrigin; + options.contentSecurityPolicyEnforcement = ContentSecurityPolicyEnforcement::DoNotEnforce; + + m_loader = ThreadableLoader::create(context, *this, WTFMove(request), options); + m_isStarted = m_loader; +} + +void FetchLoader::start(ScriptExecutionContext& context, const FetchRequest& request) +{ + ThreadableLoaderOptions options(request.fetchOptions(), ConsiderPreflight, + context.shouldBypassMainWorldContentSecurityPolicy() ? ContentSecurityPolicyEnforcement::DoNotEnforce : ContentSecurityPolicyEnforcement::EnforceConnectSrcDirective, + String(cachedResourceRequestInitiators().fetch), + ResponseFilteringPolicy::Enable); + options.sendLoadCallbacks = SendCallbacks; + options.dataBufferingPolicy = DoNotBufferData; + options.sameOriginDataURLFlag = SameOriginDataURLFlag::Set; + + ResourceRequest fetchRequest = request.internalRequest(); + + ASSERT(context.contentSecurityPolicy()); + auto& contentSecurityPolicy = *context.contentSecurityPolicy(); + + contentSecurityPolicy.upgradeInsecureRequestIfNeeded(fetchRequest, ContentSecurityPolicy::InsecureRequestType::Load); + + if (!context.shouldBypassMainWorldContentSecurityPolicy() && !contentSecurityPolicy.allowConnectToSource(fetchRequest.url())) { + m_client.didFail(); + return; + } + + String referrer = request.internalRequestReferrer(); + if (referrer == "no-referrer") { + options.referrerPolicy = FetchOptions::ReferrerPolicy::NoReferrer; + referrer = String(); + } else + referrer = (referrer == "client") ? context.url().strippedForUseAsReferrer() : URL(context.url(), referrer).strippedForUseAsReferrer(); + + m_loader = ThreadableLoader::create(context, *this, WTFMove(fetchRequest), options, WTFMove(referrer)); + m_isStarted = m_loader; +} + +FetchLoader::FetchLoader(FetchLoaderClient& client, FetchBodyConsumer* consumer) + : m_client(client) + , m_consumer(consumer) +{ +} + +void FetchLoader::stop() +{ + if (m_consumer) + m_consumer->clean(); + if (m_loader) + m_loader->cancel(); +} + +RefPtr<SharedBuffer> FetchLoader::startStreaming() +{ + ASSERT(m_consumer); + auto firstChunk = m_consumer->takeData(); + m_consumer = nullptr; + return firstChunk; +} + +void FetchLoader::didReceiveResponse(unsigned long, const ResourceResponse& response) +{ + m_client.didReceiveResponse(response); +} + +void FetchLoader::didReceiveData(const char* value, int size) +{ + if (!m_consumer) { + m_client.didReceiveData(value, size); + return; + } + m_consumer->append(value, size); +} + +void FetchLoader::didFinishLoading(unsigned long, double) +{ + m_client.didSucceed(); +} + +void FetchLoader::didFail(const ResourceError&) +{ + m_client.didFail(); +} + +} // namespace WebCore + +#endif // ENABLE(FETCH_API) diff --git a/Source/WebCore/Modules/fetch/FetchLoader.h b/Source/WebCore/Modules/fetch/FetchLoader.h new file mode 100644 index 000000000..44c1adbce --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchLoader.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(FETCH_API) + +#include "FetchBodyConsumer.h" +#include "ThreadableLoader.h" +#include "ThreadableLoaderClient.h" + +namespace WebCore { + +class Blob; +class FetchLoaderClient; +class FetchRequest; +class ScriptExecutionContext; + +class FetchLoader final : public ThreadableLoaderClient { +public: + FetchLoader(FetchLoaderClient&, FetchBodyConsumer*); + + RefPtr<SharedBuffer> startStreaming(); + + void start(ScriptExecutionContext&, const FetchRequest&); + void start(ScriptExecutionContext&, const Blob&); + void stop(); + + bool isStarted() const { return m_isStarted; } + +private: + // ThreadableLoaderClient API. + void didReceiveResponse(unsigned long, const ResourceResponse&) final; + void didReceiveData(const char*, int) final; + void didFinishLoading(unsigned long, double) final; + void didFail(const ResourceError&) final; + +private: + FetchLoaderClient& m_client; + RefPtr<ThreadableLoader> m_loader; + FetchBodyConsumer* m_consumer; + bool m_isStarted { false }; +}; + +} // namespace WebCore + +#endif // ENABLE(FETCH_API) diff --git a/Source/WebCore/Modules/fetch/FetchLoaderClient.h b/Source/WebCore/Modules/fetch/FetchLoaderClient.h new file mode 100644 index 000000000..295b16b10 --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchLoaderClient.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(FETCH_API) + +#include <wtf/Forward.h> + +namespace WebCore { + +class ResourceResponse; + +class FetchLoaderClient { +public: + virtual ~FetchLoaderClient() { } + + virtual void didReceiveResponse(const ResourceResponse&) { } + + virtual void didReceiveData(const char*, size_t) { } + + virtual void didSucceed() = 0; + virtual void didFail() = 0; +}; + +} // namespace WebCore + +#endif // ENABLE(FETCH_API) diff --git a/Source/WebCore/Modules/fetch/FetchRequest.cpp b/Source/WebCore/Modules/fetch/FetchRequest.cpp new file mode 100644 index 000000000..b879a7bcc --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchRequest.cpp @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "FetchRequest.h" + +#if ENABLE(FETCH_API) + +#include "ExceptionCode.h" +#include "HTTPParsers.h" +#include "ScriptExecutionContext.h" +#include "SecurityOrigin.h" + +namespace WebCore { + +static std::optional<Exception> setMethod(ResourceRequest& request, const String& initMethod) +{ + if (!isValidHTTPToken(initMethod)) + return Exception { TypeError, ASCIILiteral("Method is not a valid HTTP token.") }; + + String method = initMethod.convertToASCIIUppercase(); + if (method == "CONNECT" || method == "TRACE" || method == "TRACK") + return Exception { TypeError, ASCIILiteral("Method is forbidden.") }; + + request.setHTTPMethod((method == "DELETE" || method == "GET" || method == "HEAD" || method == "OPTIONS" || method == "POST" || method == "PUT") ? method : initMethod); + + return std::nullopt; +} + +static std::optional<Exception> setReferrer(FetchRequest::InternalRequest& request, ScriptExecutionContext& context, const String& referrer) +{ + if (referrer.isEmpty()) { + request.referrer = ASCIILiteral("no-referrer"); + return std::nullopt; + } + // FIXME: Tighten the URL parsing algorithm according https://url.spec.whatwg.org/#concept-url-parser. + URL referrerURL = context.completeURL(referrer); + if (!referrerURL.isValid()) + return Exception { TypeError, ASCIILiteral("Referrer is not a valid URL.") }; + + if (referrerURL.protocolIs("about") && referrerURL.path() == "client") { + request.referrer = ASCIILiteral("client"); + return std::nullopt; + } + + if (!(context.securityOrigin() && context.securityOrigin()->canRequest(referrerURL))) + return Exception { TypeError, ASCIILiteral("Referrer is not same-origin.") }; + + request.referrer = referrerURL.string(); + return std::nullopt; +} + +static std::optional<Exception> buildOptions(FetchRequest::InternalRequest& request, ScriptExecutionContext& context, const FetchRequest::Init& init) +{ + if (!init.window.isUndefinedOrNull()) + return Exception { TypeError, ASCIILiteral("Window can only be null.") }; + + if (!init.referrer.isNull()) { + if (auto exception = setReferrer(request, context, init.referrer)) + return exception; + } + + if (init.referrerPolicy) + request.options.referrerPolicy = init.referrerPolicy.value(); + + if (init.mode) + request.options.mode = init.mode.value(); + if (request.options.mode == FetchOptions::Mode::Navigate) + return Exception { TypeError, ASCIILiteral("Request constructor does not accept navigate fetch mode.") }; + + if (init.credentials) + request.options.credentials = init.credentials.value(); + + if (init.cache) + request.options.cache = init.cache.value(); + if (request.options.cache == FetchOptions::Cache::OnlyIfCached && request.options.mode != FetchOptions::Mode::SameOrigin) + return Exception { TypeError, ASCIILiteral("only-if-cached cache option requires fetch mode to be same-origin.") }; + + if (init.redirect) + request.options.redirect = init.redirect.value(); + + if (!init.integrity.isNull()) + request.integrity = init.integrity; + + if (!init.method.isNull()) { + if (auto exception = setMethod(request.request, init.method)) + return exception; + } + + return std::nullopt; +} + +static bool methodCanHaveBody(const FetchRequest::InternalRequest& internalRequest) +{ + return internalRequest.request.httpMethod() != "GET" && internalRequest.request.httpMethod() != "HEAD"; +} + +ExceptionOr<FetchHeaders&> FetchRequest::initializeOptions(const Init& init) +{ + ASSERT(scriptExecutionContext()); + + auto exception = buildOptions(m_internalRequest, *scriptExecutionContext(), init); + if (exception) + return WTFMove(exception.value()); + + if (m_internalRequest.options.mode == FetchOptions::Mode::NoCors) { + const String& method = m_internalRequest.request.httpMethod(); + if (method != "GET" && method != "POST" && method != "HEAD") + return Exception { TypeError, ASCIILiteral("Method must be GET, POST or HEAD in no-cors mode.") }; + if (!m_internalRequest.integrity.isEmpty()) + return Exception { TypeError, ASCIILiteral("There cannot be an integrity in no-cors mode.") }; + m_headers->setGuard(FetchHeaders::Guard::RequestNoCors); + } + return m_headers.get(); +} + +ExceptionOr<FetchHeaders&> FetchRequest::initializeWith(const String& url, const Init& init) +{ + ASSERT(scriptExecutionContext()); + // FIXME: Tighten the URL parsing algorithm according https://url.spec.whatwg.org/#concept-url-parser. + URL requestURL = scriptExecutionContext()->completeURL(url); + if (!requestURL.isValid() || !requestURL.user().isEmpty() || !requestURL.pass().isEmpty()) + return Exception { TypeError, ASCIILiteral("URL is not valid or contains user credentials.") }; + + m_internalRequest.options.mode = Mode::Cors; + m_internalRequest.options.credentials = Credentials::Omit; + m_internalRequest.referrer = ASCIILiteral("client"); + m_internalRequest.request.setURL(requestURL); + m_internalRequest.request.setRequester(ResourceRequest::Requester::Fetch); + m_internalRequest.request.setInitiatorIdentifier(scriptExecutionContext()->resourceRequestIdentifier()); + + return initializeOptions(init); +} + +ExceptionOr<FetchHeaders&> FetchRequest::initializeWith(FetchRequest& input, const Init& init) +{ + if (input.isDisturbedOrLocked()) + return Exception {TypeError, ASCIILiteral("Request input is disturbed or locked.") }; + + m_internalRequest = input.m_internalRequest; + + return initializeOptions(init); +} + +ExceptionOr<void> FetchRequest::setBody(JSC::ExecState& execState, JSC::JSValue body, FetchRequest* request) +{ + if (!body.isNull()) { + if (!methodCanHaveBody(m_internalRequest)) + return Exception { TypeError }; + ASSERT(scriptExecutionContext()); + extractBody(*scriptExecutionContext(), execState, body); + if (isBodyNull()) + return Exception { TypeError }; + } else if (request && !request->isBodyNull()) { + if (!methodCanHaveBody(m_internalRequest)) + return Exception { TypeError }; + m_body = WTFMove(request->m_body); + request->setDisturbed(); + } + updateContentType(); + return { }; +} + +String FetchRequest::referrer() const +{ + if (m_internalRequest.referrer == "no-referrer") + return String(); + if (m_internalRequest.referrer == "client") + return ASCIILiteral("about:client"); + return m_internalRequest.referrer; +} + +const String& FetchRequest::url() const +{ + if (m_requestURL.isNull()) + m_requestURL = m_internalRequest.request.url().serialize(); + return m_requestURL; +} + +ResourceRequest FetchRequest::internalRequest() const +{ + ASSERT(scriptExecutionContext()); + + ResourceRequest request = m_internalRequest.request; + request.setHTTPHeaderFields(m_headers->internalHeaders()); + + if (!isBodyNull()) + request.setHTTPBody(body().bodyForInternalRequest(*scriptExecutionContext())); + + return request; +} + +ExceptionOr<Ref<FetchRequest>> FetchRequest::clone(ScriptExecutionContext& context) +{ + if (isDisturbedOrLocked()) + return Exception { TypeError }; + + auto clone = adoptRef(*new FetchRequest(context, std::nullopt, FetchHeaders::create(m_headers.get()), FetchRequest::InternalRequest(m_internalRequest))); + clone->cloneBody(*this); + return WTFMove(clone); +} + +const char* FetchRequest::activeDOMObjectName() const +{ + return "Request"; +} + +bool FetchRequest::canSuspendForDocumentSuspension() const +{ + // FIXME: We can probably do the same strategy as XHR. + return !isActive(); +} + +} // namespace WebCore + +#endif // ENABLE(FETCH_API) diff --git a/Source/WebCore/Modules/fetch/FetchRequest.h b/Source/WebCore/Modules/fetch/FetchRequest.h new file mode 100644 index 000000000..47fbf78c3 --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchRequest.h @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(FETCH_API) + +#include "ExceptionOr.h" +#include "FetchBodyOwner.h" +#include "FetchOptions.h" +#include "ResourceRequest.h" +#include <wtf/Optional.h> + +namespace WebCore { + +class Blob; +class ScriptExecutionContext; +class URLSearchParams; + +class FetchRequest final : public FetchBodyOwner { +public: + static Ref<FetchRequest> create(ScriptExecutionContext& context) { return adoptRef(*new FetchRequest(context, std::nullopt, FetchHeaders::create(FetchHeaders::Guard::Request), { })); } + + using Cache = FetchOptions::Cache; + using Credentials = FetchOptions::Credentials; + using Destination = FetchOptions::Destination; + using Mode = FetchOptions::Mode; + using Redirect = FetchOptions::Redirect; + using ReferrerPolicy = FetchOptions::ReferrerPolicy; + using Type = FetchOptions::Type; + + struct Init { + String method; + String referrer; + std::optional<ReferrerPolicy> referrerPolicy; + std::optional<Mode> mode; + std::optional<Credentials> credentials; + std::optional<Cache> cache; + std::optional<Redirect> redirect; + String integrity; + JSC::JSValue window; + }; + + ExceptionOr<FetchHeaders&> initializeWith(FetchRequest&, const Init&); + ExceptionOr<FetchHeaders&> initializeWith(const String&, const Init&); + ExceptionOr<void> setBody(JSC::ExecState&, JSC::JSValue, FetchRequest*); + + const String& method() const { return m_internalRequest.request.httpMethod(); } + const String& url() const; + FetchHeaders& headers() { return m_headers.get(); } + + Type type() const; + Destination destination() const; + String referrer() const; + ReferrerPolicy referrerPolicy() const; + Mode mode() const; + Credentials credentials() const; + Cache cache() const; + Redirect redirect() const; + + const String& integrity() const { return m_internalRequest.integrity; } + + ExceptionOr<Ref<FetchRequest>> clone(ScriptExecutionContext&); + + struct InternalRequest { + ResourceRequest request; + FetchOptions options; + String referrer; + String integrity; + }; + + const FetchOptions& fetchOptions() const { return m_internalRequest.options; } + ResourceRequest internalRequest() const; + bool isBodyReadableStream() const { return !isBodyNull() && body().isReadableStream(); } + + const String& internalRequestReferrer() const { return m_internalRequest.referrer; } + +private: + FetchRequest(ScriptExecutionContext&, std::optional<FetchBody>&&, Ref<FetchHeaders>&&, InternalRequest&&); + + ExceptionOr<FetchHeaders&> initializeOptions(const Init&); + + const char* activeDOMObjectName() const final; + bool canSuspendForDocumentSuspension() const final; + + InternalRequest m_internalRequest; + mutable String m_requestURL; +}; + +inline FetchRequest::FetchRequest(ScriptExecutionContext& context, std::optional<FetchBody>&& body, Ref<FetchHeaders>&& headers, InternalRequest&& internalRequest) + : FetchBodyOwner(context, WTFMove(body), WTFMove(headers)) + , m_internalRequest(WTFMove(internalRequest)) +{ +} + +inline auto FetchRequest::cache() const -> Cache +{ + return m_internalRequest.options.cache; +} + +inline auto FetchRequest::credentials() const -> Credentials +{ + return m_internalRequest.options.credentials; +} + +inline auto FetchRequest::destination() const -> Destination +{ + return m_internalRequest.options.destination; +} + +inline auto FetchRequest::mode() const -> Mode +{ + return m_internalRequest.options.mode; +} + +inline auto FetchRequest::redirect() const -> Redirect +{ + return m_internalRequest.options.redirect; +} + +inline auto FetchRequest::referrerPolicy() const -> ReferrerPolicy +{ + return m_internalRequest.options.referrerPolicy; +} + +inline auto FetchRequest::type() const -> Type +{ + return m_internalRequest.options.type; +} + +} // namespace WebCore + +#endif // ENABLE(FETCH_API) diff --git a/Source/WebCore/Modules/fetch/FetchRequest.idl b/Source/WebCore/Modules/fetch/FetchRequest.idl new file mode 100644 index 000000000..d9d7960f9 --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchRequest.idl @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +enum RequestType { "", "audio", "font", "image", "script", "style", "track", "video" }; +enum RequestDestination { "", "document", "sharedworker", "subresource", "unknown", "worker" }; +enum RequestMode { "navigate", "same-origin", "no-cors", "cors" }; +enum RequestCredentials { "omit", "same-origin", "include" }; +enum RequestCache { "default", "no-store", "reload", "no-cache", "force-cache", "only-if-cached" }; +enum RequestRedirect { "follow", "error", "manual" }; +enum ReferrerPolicy { "", "no-referrer", "no-referrer-when-downgrade", "origin", "origin-when-cross-origin", "unsafe-url" }; + +dictionary RequestInit { + ByteString method; + // FIXME: Should add: HeadersInit headers; + // FIXME: Should add: BodyInit? body; + USVString referrer; + ReferrerPolicy referrerPolicy; + RequestMode mode; + RequestCredentials credentials; + RequestCache cache; + RequestRedirect redirect; + DOMString integrity; + // FIXME: Should add: boolean keepalive; + any window; // can only be set to null +}; + +[ + ActiveDOMObject, + Conditional=FETCH_API, + Constructor(any input, optional RequestInit init), + EnabledAtRuntime=FetchAPI, + Exposed=(Window,Worker), + InterfaceName=Request, + JSBuiltinConstructor, + PrivateIdentifier, + PublicIdentifier, +] interface FetchRequest { + readonly attribute ByteString method; + readonly attribute DOMString url; + readonly attribute FetchHeaders headers; + + readonly attribute RequestType type; + readonly attribute RequestDestination destination; + readonly attribute USVString referrer; + readonly attribute ReferrerPolicy referrerPolicy; + readonly attribute RequestMode mode; + readonly attribute RequestCredentials credentials; + readonly attribute RequestCache cache; + readonly attribute RequestRedirect redirect; + readonly attribute DOMString integrity; + + [CallWith=ScriptExecutionContext, MayThrowException, NewObject] FetchRequest clone(); + + [MayThrowException, NewObject, PrivateIdentifier] FetchHeaders initializeWith(FetchRequest input, RequestInit init); + [MayThrowException, NewObject, PrivateIdentifier] FetchHeaders initializeWith(DOMString input, RequestInit init); + [CallWith=ScriptState, MayThrowException, PrivateIdentifier] void setBody(any body, FetchRequest? request); +}; + +FetchRequest implements FetchBody; diff --git a/Source/WebCore/Modules/fetch/FetchRequest.js b/Source/WebCore/Modules/fetch/FetchRequest.js new file mode 100644 index 000000000..533a33b52 --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchRequest.js @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2016 Apple Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// @conditional=ENABLE(FETCH_API) + +function initializeFetchRequest(input, init) +{ + "use strict"; + + if (init === @undefined) + init = { }; + else if (!@isObject(init)) + @throwTypeError("Request init must be an object"); + + let headers = this.@initializeWith(input, init); + @assert(headers instanceof @Headers); + + let inputIsRequest = input instanceof @Request; + if ("headers" in init) + @fillFetchHeaders(headers, init.headers) + else if (inputIsRequest) + @fillFetchHeaders(headers, input.headers) + + let hasInitBody = init.body !== @undefined && init.body !== null; + this.@setBody(hasInitBody ? init.body : null, inputIsRequest ? input : null); + + return this; +} diff --git a/Source/WebCore/Modules/fetch/FetchResponse.cpp b/Source/WebCore/Modules/fetch/FetchResponse.cpp new file mode 100644 index 000000000..40cec5b91 --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchResponse.cpp @@ -0,0 +1,365 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "FetchResponse.h" + +#if ENABLE(FETCH_API) + +#include "ExceptionCode.h" +#include "FetchRequest.h" +#include "HTTPParsers.h" +#include "JSBlob.h" +#include "JSFetchResponse.h" +#include "ScriptExecutionContext.h" + +namespace WebCore { + +static inline bool isRedirectStatus(int status) +{ + return status == 301 || status == 302 || status == 303 || status == 307 || status == 308; +} + +Ref<FetchResponse> FetchResponse::error(ScriptExecutionContext& context) +{ + auto response = adoptRef(*new FetchResponse(context, { }, FetchHeaders::create(FetchHeaders::Guard::Immutable), { })); + response->m_response.setType(Type::Error); + return response; +} + +ExceptionOr<Ref<FetchResponse>> FetchResponse::redirect(ScriptExecutionContext& context, const String& url, int status) +{ + // FIXME: Tighten the URL parsing algorithm according https://url.spec.whatwg.org/#concept-url-parser. + URL requestURL = context.completeURL(url); + if (!requestURL.isValid() || !requestURL.user().isEmpty() || !requestURL.pass().isEmpty()) + return Exception { TypeError }; + if (!isRedirectStatus(status)) + return Exception { RangeError }; + auto redirectResponse = adoptRef(*new FetchResponse(context, { }, FetchHeaders::create(FetchHeaders::Guard::Immutable), { })); + redirectResponse->m_response.setHTTPStatusCode(status); + redirectResponse->m_headers->fastSet(HTTPHeaderName::Location, requestURL.string()); + return WTFMove(redirectResponse); +} + +ExceptionOr<void> FetchResponse::setStatus(int status, const String& statusText) +{ + if (!isValidReasonPhrase(statusText)) + return Exception { TypeError }; + m_response.setHTTPStatusCode(status); + m_response.setHTTPStatusText(statusText); + return { }; +} + +void FetchResponse::initializeWith(JSC::ExecState& execState, JSC::JSValue body) +{ + ASSERT(scriptExecutionContext()); + extractBody(*scriptExecutionContext(), execState, body); + updateContentType(); +} + +FetchResponse::FetchResponse(ScriptExecutionContext& context, std::optional<FetchBody>&& body, Ref<FetchHeaders>&& headers, ResourceResponse&& response) + : FetchBodyOwner(context, WTFMove(body), WTFMove(headers)) + , m_response(WTFMove(response)) +{ +} + +Ref<FetchResponse> FetchResponse::cloneForJS() +{ + ASSERT(scriptExecutionContext()); + ASSERT(!isDisturbedOrLocked()); + + auto clone = adoptRef(*new FetchResponse(*scriptExecutionContext(), std::nullopt, FetchHeaders::create(headers()), ResourceResponse(m_response))); + clone->cloneBody(*this); + return clone; +} + +void FetchResponse::fetch(ScriptExecutionContext& context, FetchRequest& request, FetchPromise&& promise) +{ + if (request.isBodyReadableStream()) { + promise.reject(TypeError, "ReadableStream uploading is not supported"); + return; + } + auto response = adoptRef(*new FetchResponse(context, FetchBody::loadingBody(), FetchHeaders::create(FetchHeaders::Guard::Immutable), { })); + + response->m_bodyLoader.emplace(response.get(), WTFMove(promise)); + if (!response->m_bodyLoader->start(context, request)) + response->m_bodyLoader = std::nullopt; +} + +const String& FetchResponse::url() const +{ + if (m_responseURL.isNull()) + m_responseURL = m_response.url().serialize(true); + return m_responseURL; +} + +void FetchResponse::BodyLoader::didSucceed() +{ + ASSERT(m_response.hasPendingActivity()); + m_response.m_body->loadingSucceeded(); + +#if ENABLE(READABLE_STREAM_API) + if (m_response.m_readableStreamSource && !m_response.body().consumer().hasData()) + m_response.closeStream(); +#endif + + if (m_loader->isStarted()) { + Ref<FetchResponse> protector(m_response); + m_response.m_bodyLoader = std::nullopt; + } +} + +void FetchResponse::BodyLoader::didFail() +{ + ASSERT(m_response.hasPendingActivity()); + if (m_promise) + std::exchange(m_promise, std::nullopt)->reject(TypeError); + +#if ENABLE(READABLE_STREAM_API) + if (m_response.m_readableStreamSource) { + if (!m_response.m_readableStreamSource->isCancelling()) + m_response.m_readableStreamSource->error(ASCIILiteral("Loading failed")); + m_response.m_readableStreamSource = nullptr; + } +#endif + + // Check whether didFail is called as part of FetchLoader::start. + if (m_loader->isStarted()) { + Ref<FetchResponse> protector(m_response); + m_response.m_bodyLoader = std::nullopt; + } +} + +FetchResponse::BodyLoader::BodyLoader(FetchResponse& response, FetchPromise&& promise) + : m_response(response) + , m_promise(WTFMove(promise)) +{ + m_response.setPendingActivity(&m_response); +} + +FetchResponse::BodyLoader::~BodyLoader() +{ + m_response.unsetPendingActivity(&m_response); +} + +void FetchResponse::BodyLoader::didReceiveResponse(const ResourceResponse& resourceResponse) +{ + ASSERT(m_promise); + + m_response.m_response = resourceResponse; + m_response.m_headers->filterAndFill(resourceResponse.httpHeaderFields(), FetchHeaders::Guard::Response); + + std::exchange(m_promise, std::nullopt)->resolve(m_response); +} + +void FetchResponse::BodyLoader::didReceiveData(const char* data, size_t size) +{ +#if ENABLE(READABLE_STREAM_API) + ASSERT(m_response.m_readableStreamSource); + auto& source = *m_response.m_readableStreamSource; + + if (!source.isPulling()) { + m_response.body().consumer().append(data, size); + return; + } + + if (m_response.body().consumer().hasData() && !source.enqueue(m_response.body().consumer().takeAsArrayBuffer())) { + stop(); + return; + } + if (!source.enqueue(ArrayBuffer::tryCreate(data, size))) { + stop(); + return; + } + source.resolvePullPromise(); +#else + UNUSED_PARAM(data); + UNUSED_PARAM(size); +#endif +} + +bool FetchResponse::BodyLoader::start(ScriptExecutionContext& context, const FetchRequest& request) +{ + m_loader = std::make_unique<FetchLoader>(*this, &m_response.m_body->consumer()); + m_loader->start(context, request); + return m_loader->isStarted(); +} + +void FetchResponse::BodyLoader::stop() +{ + m_promise = std::nullopt; + if (m_loader) + m_loader->stop(); +} + +void FetchResponse::consume(unsigned type, Ref<DeferredPromise>&& wrapper) +{ + ASSERT(type <= static_cast<unsigned>(FetchBodyConsumer::Type::Text)); + auto consumerType = static_cast<FetchBodyConsumer::Type>(type); + + if (isLoading()) { + consumeOnceLoadingFinished(consumerType, WTFMove(wrapper)); + return; + } + + switch (consumerType) { + case FetchBodyConsumer::Type::ArrayBuffer: + arrayBuffer(WTFMove(wrapper)); + return; + case FetchBodyConsumer::Type::Blob: + blob(WTFMove(wrapper)); + return; + case FetchBodyConsumer::Type::JSON: + json(WTFMove(wrapper)); + return; + case FetchBodyConsumer::Type::Text: + text(WTFMove(wrapper)); + return; + case FetchBodyConsumer::Type::None: + ASSERT_NOT_REACHED(); + return; + } +} + +#if ENABLE(READABLE_STREAM_API) +void FetchResponse::startConsumingStream(unsigned type) +{ + m_isDisturbed = true; + m_consumer.setType(static_cast<FetchBodyConsumer::Type>(type)); +} + +void FetchResponse::consumeChunk(Ref<JSC::Uint8Array>&& chunk) +{ + m_consumer.append(chunk->data(), chunk->byteLength()); +} + +void FetchResponse::finishConsumingStream(Ref<DeferredPromise>&& promise) +{ + m_consumer.resolve(WTFMove(promise)); +} + +void FetchResponse::consumeBodyAsStream() +{ + ASSERT(m_readableStreamSource); + m_isDisturbed = true; + if (!isLoading()) { + body().consumeAsStream(*this, *m_readableStreamSource); + if (!m_readableStreamSource->isPulling()) + m_readableStreamSource = nullptr; + return; + } + + ASSERT(m_bodyLoader); + + RefPtr<SharedBuffer> data = m_bodyLoader->startStreaming(); + if (data) { + if (!m_readableStreamSource->enqueue(data->createArrayBuffer())) { + stop(); + return; + } + m_readableStreamSource->resolvePullPromise(); + } +} + +void FetchResponse::closeStream() +{ + ASSERT(m_readableStreamSource); + m_readableStreamSource->close(); + m_readableStreamSource = nullptr; +} + +void FetchResponse::feedStream() +{ + ASSERT(m_readableStreamSource); + bool shouldCloseStream = !m_bodyLoader; + + if (body().consumer().hasData()) { + if (!m_readableStreamSource->enqueue(body().consumer().takeAsArrayBuffer())) { + stop(); + return; + } + if (!shouldCloseStream) { + m_readableStreamSource->resolvePullPromise(); + return; + } + } else if (!shouldCloseStream) + return; + + closeStream(); +} + +ReadableStreamSource* FetchResponse::createReadableStreamSource() +{ + ASSERT(!m_readableStreamSource); + ASSERT(!m_isDisturbed); + + if (isBodyNull()) + return nullptr; + + m_readableStreamSource = adoptRef(*new FetchResponseSource(*this)); + return m_readableStreamSource.get(); +} + +RefPtr<SharedBuffer> FetchResponse::BodyLoader::startStreaming() +{ + ASSERT(m_loader); + return m_loader->startStreaming(); +} + +void FetchResponse::cancel() +{ + m_isDisturbed = true; + stop(); +} + +#endif + +void FetchResponse::stop() +{ + RefPtr<FetchResponse> protectedThis(this); + FetchBodyOwner::stop(); + if (m_bodyLoader) { + m_bodyLoader->stop(); + m_bodyLoader = std::nullopt; + } +} + +const char* FetchResponse::activeDOMObjectName() const +{ + return "Response"; +} + +bool FetchResponse::canSuspendForDocumentSuspension() const +{ + // FIXME: We can probably do the same strategy as XHR. + return !isActive(); +} + +} // namespace WebCore + +#endif // ENABLE(FETCH_API) diff --git a/Source/WebCore/Modules/fetch/FetchResponse.h b/Source/WebCore/Modules/fetch/FetchResponse.h new file mode 100644 index 000000000..b4d84f9e7 --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchResponse.h @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(FETCH_API) + +#include "FetchBodyOwner.h" +#include "ResourceResponse.h" +#include <runtime/TypedArrays.h> + +namespace JSC { +class ExecState; +class JSValue; +}; + +namespace WebCore { + +class FetchRequest; +class ReadableStreamSource; + +class FetchResponse final : public FetchBodyOwner { +public: + using Type = ResourceResponse::Type; + + static Ref<FetchResponse> create(ScriptExecutionContext& context) { return adoptRef(*new FetchResponse(context, std::nullopt, FetchHeaders::create(FetchHeaders::Guard::Response), ResourceResponse())); } + static Ref<FetchResponse> error(ScriptExecutionContext&); + static ExceptionOr<Ref<FetchResponse>> redirect(ScriptExecutionContext&, const String& url, int status); + + using FetchPromise = DOMPromise<IDLInterface<FetchResponse>>; + static void fetch(ScriptExecutionContext&, FetchRequest&, FetchPromise&&); + + void consume(unsigned, Ref<DeferredPromise>&&); +#if ENABLE(READABLE_STREAM_API) + void startConsumingStream(unsigned); + void consumeChunk(Ref<JSC::Uint8Array>&&); + void finishConsumingStream(Ref<DeferredPromise>&&); +#endif + + ExceptionOr<void> setStatus(int status, const String& statusText); + void initializeWith(JSC::ExecState&, JSC::JSValue); + + Type type() const { return m_response.type(); } + const String& url() const; + bool redirected() const { return m_response.isRedirected(); } + int status() const { return m_response.httpStatusCode(); } + bool ok() const { return m_response.isSuccessful(); } + const String& statusText() const { return m_response.httpStatusText(); } + + FetchHeaders& headers() { return m_headers; } + Ref<FetchResponse> cloneForJS(); + +#if ENABLE(READABLE_STREAM_API) + ReadableStreamSource* createReadableStreamSource(); + void consumeBodyAsStream(); + void feedStream(); + void cancel(); +#endif + + bool isLoading() const { return !!m_bodyLoader; } + +private: + FetchResponse(ScriptExecutionContext&, std::optional<FetchBody>&&, Ref<FetchHeaders>&&, ResourceResponse&&); + + static void startFetching(ScriptExecutionContext&, const FetchRequest&, FetchPromise&&); + + void stop() final; + const char* activeDOMObjectName() const final; + bool canSuspendForDocumentSuspension() const final; + +#if ENABLE(READABLE_STREAM_API) + void closeStream(); +#endif + + class BodyLoader final : public FetchLoaderClient { + public: + BodyLoader(FetchResponse&, FetchPromise&&); + ~BodyLoader(); + + bool start(ScriptExecutionContext&, const FetchRequest&); + void stop(); + +#if ENABLE(READABLE_STREAM_API) + RefPtr<SharedBuffer> startStreaming(); +#endif + + private: + // FetchLoaderClient API + void didSucceed() final; + void didFail() final; + void didReceiveResponse(const ResourceResponse&) final; + void didReceiveData(const char*, size_t) final; + + FetchResponse& m_response; + std::optional<FetchPromise> m_promise; + std::unique_ptr<FetchLoader> m_loader; + }; + + ResourceResponse m_response; + std::optional<BodyLoader> m_bodyLoader; + mutable String m_responseURL; + + FetchBodyConsumer m_consumer { FetchBodyConsumer::Type::ArrayBuffer }; +}; + +} // namespace WebCore + +#endif // ENABLE(FETCH_API) diff --git a/Source/WebCore/Modules/fetch/FetchResponse.idl b/Source/WebCore/Modules/fetch/FetchResponse.idl new file mode 100644 index 000000000..03fe2010f --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchResponse.idl @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +enum ResponseType { "basic", "cors", "default", "error", "opaque", "opaqueredirect" }; + +[ + ActiveDOMObject, + Conditional=FETCH_API, + ConstructorCallWith=ScriptExecutionContext, + EnabledAtRuntime=FetchAPI, + Exposed=(Window,Worker), + InterfaceName=Response, + JSBuiltinConstructor, + PrivateIdentifier, + PublicIdentifier, +] interface FetchResponse { + [CallWith=ScriptExecutionContext, NewObject] static FetchResponse error(); + [CallWith=ScriptExecutionContext, MayThrowException, NewObject] static FetchResponse redirect(DOMString url, optional unsigned short status = 302); + + readonly attribute ResponseType type; + + readonly attribute DOMString url; + readonly attribute boolean redirected; + readonly attribute unsigned short status; + readonly attribute boolean ok; + readonly attribute DOMString statusText; + readonly attribute FetchHeaders headers; // FIXME: Add support for SameObject keyword; use it here. + [JSBuiltin] readonly attribute ReadableStream? body; + + // Copy of FetchBody IDL as we want to implement some of these as built-ins. + [JSBuiltin] readonly attribute boolean bodyUsed; + [JSBuiltin] Promise<ArrayBuffer> arrayBuffer(); + [JSBuiltin] Promise<Blob> blob(); + [JSBuiltin] Promise<Blob> formData(); + [JSBuiltin] Promise<any> json(); + [JSBuiltin] Promise<USVString> text(); + + [JSBuiltin] FetchResponse clone(); + + [NewObject, PrivateIdentifier] FetchResponse cloneForJS(); + + [Conditional=READABLE_STREAM_API, PrivateIdentifier] void startConsumingStream(unsigned short type); + [Conditional=READABLE_STREAM_API, PrivateIdentifier] void consumeChunk(Uint8Array chunk); + [Conditional=READABLE_STREAM_API, PrivateIdentifier] Promise<any> finishConsumingStream(); + + [PrivateIdentifier] Promise<any> consume(unsigned short type); + [PrivateIdentifier] boolean isLoading(); + [MayThrowException, PrivateIdentifier] void setStatus(unsigned short status, DOMString statusText); + [CallWith=ScriptState, PrivateIdentifier] void initializeWith(any body); + [NewObject, PrivateIdentifier] ReadableStreamSource createReadableStreamSource(); + [PrivateIdentifier] boolean isDisturbed(); +}; diff --git a/Source/WebCore/Modules/fetch/FetchResponse.js b/Source/WebCore/Modules/fetch/FetchResponse.js new file mode 100644 index 000000000..6f53924f1 --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchResponse.js @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CANON INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// @conditional=ENABLE(FETCH_API) + +function initializeFetchResponse(body, init) +{ + "use strict"; + + if (init === @undefined) + init = { }; + else if (!@isObject(init)) + @throwTypeError("Response init must be an object"); + + let status = (init.status !== @undefined) ? @toNumber(init.status) : 200; + if (status < 200 || status > 599) + @throwRangeError("Status must be between 200 and 599"); + + let statusText = (init.statusText !== @undefined) ? init.statusText : "OK"; + + this.@setStatus(status, statusText); + + if (init.headers !== @undefined) + @fillFetchHeaders(this.headers, init.headers); + + if (body !== @undefined && body !== null) { + if (status === 101 || status === 204 || status === 205 || status === 304) + @throwTypeError("Response cannot have a body with the given status"); + + // FIXME: Use @isReadableStream once it is no longer guarded by READABLE_STREAM_API guard. + let isBodyReadableStream = (@isObject(body) && !!body.@readableStreamController); + if (isBodyReadableStream) + this.@body = body; + + this.@initializeWith(body); + } + + return this; +} + +function bodyUsed() +{ + if (!(this instanceof @Response)) + throw @makeGetterTypeError("Response", "bodyUsed"); + + if (this.@body) + return @isReadableStreamDisturbed(this.@body); + + return @Response.prototype.@isDisturbed.@call(this); +} + +function body() +{ + if (!(this instanceof @Response)) + throw @makeGetterTypeError("Response", "body"); + + if (!this.@body) { + if (@Response.prototype.@isDisturbed.@call(this)) { + this.@body = new @ReadableStream(); + // Get reader to lock it. + new @ReadableStreamDefaultReader(this.@body); + } else { + var source = @Response.prototype.@createReadableStreamSource.@call(this); + this.@body = source ? new @ReadableStream(source) : null; + } + } + return this.@body; +} + +function clone() +{ + if (!(this instanceof @Response)) + throw @makeThisTypeError("Response", "clone"); + + if (@Response.prototype.@isDisturbed.@call(this) || (this.@body && @isReadableStreamLocked(this.@body))) + @throwTypeError("Cannot clone a disturbed Response"); + + var cloned = @Response.prototype.@cloneForJS.@call(this); + + // Let's create @body if response body is loading to provide data to both clones. + if (@Response.prototype.@isLoading.@call(this) && !this.@body) { + var source = @Response.prototype.@createReadableStreamSource.@call(this); + @assert(!!source); + this.@body = new @ReadableStream(source); + } + + if (this.@body) { + var teedReadableStreams = @readableStreamTee(this.@body, true); + this.@body = teedReadableStreams[0]; + cloned.@body = teedReadableStreams[1]; + } + return cloned; +} + +// consume and consumeStream single parameter should be kept in sync with FetchBodyConsumer::Type. +function arrayBuffer() +{ + if (!(this instanceof @Response)) + return @Promise.@reject(@makeThisTypeError("Response", "arrayBuffer")); + + const arrayBufferConsumerType = 1; + if (!this.@body) + return @Response.prototype.@consume.@call(this, arrayBufferConsumerType); + + return @consumeStream(this, arrayBufferConsumerType); +} + +function blob() +{ + if (!(this instanceof @Response)) + return @Promise.@reject(@makeThisTypeError("Response", "blob")); + + const blobConsumerType = 2; + if (!this.@body) + return @Response.prototype.@consume.@call(this, blobConsumerType); + + return @consumeStream(this, blobConsumerType); +} + +function formData() +{ + if (!(this instanceof @Response)) + return @Promise.@reject(@makeThisTypeError("Response", "formData")); + + return @Promise.@reject("Not implemented"); +} + +function json() +{ + if (!(this instanceof @Response)) + return @Promise.@reject(@makeThisTypeError("Response", "json")); + + const jsonConsumerType = 3; + if (!this.@body) + return @Response.prototype.@consume.@call(this, jsonConsumerType); + + return @consumeStream(this, jsonConsumerType); +} + +function text() +{ + if (!(this instanceof @Response)) + return @Promise.@reject(@makeThisTypeError("Response", "text")); + + const textConsumerType = 4; + if (!this.@body) + return @Response.prototype.@consume.@call(this, textConsumerType); + + return @consumeStream(this, textConsumerType); +} diff --git a/Source/WebCore/Modules/fetch/FetchResponseSource.cpp b/Source/WebCore/Modules/fetch/FetchResponseSource.cpp new file mode 100644 index 000000000..5534ecd58 --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchResponseSource.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "FetchResponseSource.h" + +#if ENABLE(FETCH_API) && ENABLE(READABLE_STREAM_API) + +#include "FetchResponse.h" + +namespace WebCore { + +FetchResponseSource::FetchResponseSource(FetchResponse& response) + : m_response(response) +{ +} + +bool FetchResponseSource::isReadableStreamLocked() const +{ + return controller().isControlledReadableStreamLocked(); +} + +void FetchResponseSource::setActive() +{ + m_response.setPendingActivity(&m_response); +} + +void FetchResponseSource::setInactive() +{ + m_response.unsetPendingActivity(&m_response); +} + +void FetchResponseSource::doStart() +{ + m_response.consumeBodyAsStream(); +} + +void FetchResponseSource::doPull() +{ + m_response.feedStream(); +} + +void FetchResponseSource::doCancel() +{ + m_isCancelling = true; + m_response.cancel(); +} + +void FetchResponseSource::close() +{ + controller().close(); + clean(); +} +void FetchResponseSource::error(const String& value) +{ + controller().error(value); + clean(); +} + +} // namespace WebCore + +#endif // ENABLE(FETCH_API) && ENABLE(READABLE_STREAM_API) diff --git a/Source/WebCore/Modules/fetch/FetchResponseSource.h b/Source/WebCore/Modules/fetch/FetchResponseSource.h new file mode 100644 index 000000000..ba4424ec3 --- /dev/null +++ b/Source/WebCore/Modules/fetch/FetchResponseSource.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(FETCH_API) && ENABLE(READABLE_STREAM_API) + +#include "ReadableStreamSource.h" + +namespace JSC { +class ArrayBuffer; +}; + +namespace WebCore { + +class FetchResponse; + +class FetchResponseSource final : public ReadableStreamSource { +public: + FetchResponseSource(FetchResponse&); + + bool enqueue(RefPtr<JSC::ArrayBuffer>&& chunk) { return controller().enqueue(WTFMove(chunk)); } + void close(); + void error(const String&); + + bool isCancelling() const { return m_isCancelling; } + bool isReadableStreamLocked() const; + + void resolvePullPromise() { pullFinished(); } + +private: + void doStart() final; + void doPull() final; + void doCancel() final; + void setActive() final; + void setInactive() final; + + FetchResponse& m_response; + bool m_isCancelling { false }; +}; + +} // namespace WebCore + +#endif // ENABLE(FETCH_API) && ENABLE(READABLE_STREAM_API) diff --git a/Source/WebCore/Modules/fetch/WorkerGlobalScopeFetch.cpp b/Source/WebCore/Modules/fetch/WorkerGlobalScopeFetch.cpp new file mode 100644 index 000000000..1a30d5687 --- /dev/null +++ b/Source/WebCore/Modules/fetch/WorkerGlobalScopeFetch.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "WorkerGlobalScopeFetch.h" + +#if ENABLE(FETCH_API) + +#include "FetchResponse.h" +#include "WorkerGlobalScope.h" + +namespace WebCore { + +void WorkerGlobalScopeFetch::fetch(WorkerGlobalScope& scope, FetchRequest& request, Ref<DeferredPromise>&& promise) +{ + FetchResponse::fetch(scope, request, WTFMove(promise)); +} + +} // namespace WebCore + +#endif // ENABLE(FETCH_API) diff --git a/Source/WebCore/Modules/fetch/WorkerGlobalScopeFetch.h b/Source/WebCore/Modules/fetch/WorkerGlobalScopeFetch.h new file mode 100644 index 000000000..5bbb28b88 --- /dev/null +++ b/Source/WebCore/Modules/fetch/WorkerGlobalScopeFetch.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#if ENABLE(FETCH_API) + +#include "JSDOMPromise.h" + +namespace WebCore { + +class FetchRequest; +class WorkerGlobalScope; + +class WorkerGlobalScopeFetch { +public: + static void fetch(WorkerGlobalScope&, FetchRequest&, Ref<DeferredPromise>&&); +}; + +} // namespace WebCore + +#endif // ENABLE(FETCH_API) diff --git a/Source/WebCore/Modules/fetch/WorkerGlobalScopeFetch.idl b/Source/WebCore/Modules/fetch/WorkerGlobalScopeFetch.idl new file mode 100644 index 000000000..c53e72de9 --- /dev/null +++ b/Source/WebCore/Modules/fetch/WorkerGlobalScopeFetch.idl @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2016 Canon Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions + * are required to be met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Canon Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +[ + Conditional=FETCH_API, + EnabledAtRuntime=FetchAPI, +] partial interface WorkerGlobalScope { + [JSBuiltin] Promise<Response> fetch(any input, optional RequestInit init); + [PrivateIdentifier, ImplementedAs=fetch] Promise<Response> fetchRequest(FetchRequest request); +}; diff --git a/Source/WebCore/Modules/fetch/WorkerGlobalScopeFetch.js b/Source/WebCore/Modules/fetch/WorkerGlobalScopeFetch.js new file mode 100644 index 000000000..ef6d075ea --- /dev/null +++ b/Source/WebCore/Modules/fetch/WorkerGlobalScopeFetch.js @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2016 Apple Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// @conditional=ENABLE(FETCH_API) + +function fetch(input, init) +{ + "use strict"; + + try { + return @fetchRequest(new @Request(input, init)); + } catch (e) { + return @Promise.@reject(e); + } +} |