#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 "ReadableStreamSource.h"
#include <runtime/ArrayBufferView.h>
namespace WebCore {
FetchBody FetchBody::extract(ScriptExecutionContext& context, BindingDataType&& value, String& contentType)
{
if (WTF::holds_alternative<RefPtr<Blob>>(value)) {
Ref<const Blob> blob = WTF::get<RefPtr<Blob>>(value).releaseNonNull();
contentType = blob->type();
return FetchBody(WTFMove(blob));
}
if (WTF::holds_alternative<RefPtr<DOMFormData>>(value)) {
Ref<DOMFormData> domFormData = WTF::get<RefPtr<DOMFormData>>(value).releaseNonNull();
auto formData = FormData::createMultiPart(domFormData.get(), domFormData->encoding(), &static_cast<Document&>(context));
contentType = makeString("multipart/form-data; boundary=", formData->boundary().data());
return FetchBody(WTFMove(formData));
}
if (WTF::holds_alternative<RefPtr<URLSearchParams>>(value)) {
Ref<const URLSearchParams> params = WTF::get<RefPtr<URLSearchParams>>(value).releaseNonNull();
contentType = HTTPHeaderValues::formURLEncodedContentType();
return FetchBody(WTFMove(params));
}
if (WTF::holds_alternative<RefPtr<ArrayBuffer>>(value)) {
Ref<const ArrayBuffer> buffer = WTF::get<RefPtr<ArrayBuffer>>(value).releaseNonNull();
return FetchBody(WTFMove(buffer));
}
if (WTF::holds_alternative<RefPtr<ArrayBufferView>>(value)) {
Ref<const ArrayBufferView> buffer = WTF::get<RefPtr<ArrayBufferView>>(value).releaseNonNull();
return FetchBody(WTFMove(buffer));
}
ASSERT(WTF::holds_alternative<String>(value));
contentType = HTTPHeaderValues::textPlainContentType();
return FetchBody(WTFMove(WTF::get<String>(value)));
}
FetchBody FetchBody::readableStreamBody()
{
FetchBody body;
body.m_isReadableStream = true;
return body;
}
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, const String& contentType)
{
m_consumer.setType(type);
m_consumePromise = WTFMove(promise);
if (type == FetchBodyConsumer::Type::Blob)
m_consumer.setContentType(Blob::normalizedContentType(extractMIMETypeFromMediaType(contentType)));
}
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()) {
promise->reject();
return;
}
m_consumer.resolve(WTFMove(promise));
}
#if ENABLE(STREAMS_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)