summaryrefslogtreecommitdiff
path: root/Source/WebCore/Modules/fetch
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/Modules/fetch')
-rw-r--r--Source/WebCore/Modules/fetch/DOMWindowFetch.cpp51
-rw-r--r--Source/WebCore/Modules/fetch/DOMWindowFetch.h48
-rw-r--r--Source/WebCore/Modules/fetch/DOMWindowFetch.idl35
-rw-r--r--Source/WebCore/Modules/fetch/DOMWindowFetch.js37
-rw-r--r--Source/WebCore/Modules/fetch/FetchBody.cpp277
-rw-r--r--Source/WebCore/Modules/fetch/FetchBody.h125
-rw-r--r--Source/WebCore/Modules/fetch/FetchBody.idl44
-rw-r--r--Source/WebCore/Modules/fetch/FetchBodyConsumer.cpp155
-rw-r--r--Source/WebCore/Modules/fetch/FetchBodyConsumer.h73
-rw-r--r--Source/WebCore/Modules/fetch/FetchBodyOwner.cpp273
-rw-r--r--Source/WebCore/Modules/fetch/FetchBodyOwner.h112
-rw-r--r--Source/WebCore/Modules/fetch/FetchHeaders.cpp148
-rw-r--r--Source/WebCore/Modules/fetch/FetchHeaders.h103
-rw-r--r--Source/WebCore/Modules/fetch/FetchHeaders.idl49
-rw-r--r--Source/WebCore/Modules/fetch/FetchHeaders.js41
-rw-r--r--Source/WebCore/Modules/fetch/FetchInternals.js80
-rw-r--r--Source/WebCore/Modules/fetch/FetchLoader.cpp156
-rw-r--r--Source/WebCore/Modules/fetch/FetchLoader.h72
-rw-r--r--Source/WebCore/Modules/fetch/FetchLoaderClient.h53
-rw-r--r--Source/WebCore/Modules/fetch/FetchRequest.cpp241
-rw-r--r--Source/WebCore/Modules/fetch/FetchRequest.h158
-rw-r--r--Source/WebCore/Modules/fetch/FetchRequest.idl84
-rw-r--r--Source/WebCore/Modules/fetch/FetchRequest.js50
-rw-r--r--Source/WebCore/Modules/fetch/FetchResponse.cpp365
-rw-r--r--Source/WebCore/Modules/fetch/FetchResponse.h133
-rw-r--r--Source/WebCore/Modules/fetch/FetchResponse.idl77
-rw-r--r--Source/WebCore/Modules/fetch/FetchResponse.js172
-rw-r--r--Source/WebCore/Modules/fetch/FetchResponseSource.cpp87
-rw-r--r--Source/WebCore/Modules/fetch/FetchResponseSource.h69
-rw-r--r--Source/WebCore/Modules/fetch/WorkerGlobalScopeFetch.cpp46
-rw-r--r--Source/WebCore/Modules/fetch/WorkerGlobalScopeFetch.h47
-rw-r--r--Source/WebCore/Modules/fetch/WorkerGlobalScopeFetch.idl35
-rw-r--r--Source/WebCore/Modules/fetch/WorkerGlobalScopeFetch.js37
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);
+ }
+}