blob: 120bcaf3848da23f7eb449be696eea1875b3a0b7 [file] [log] [blame]
/*
* Copyright (C) 2022 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 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. 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. OR 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 "FormDataConsumer.h"
#include "BlobLoader.h"
#include "FormData.h"
#include <wtf/WorkQueue.h>
namespace WebCore {
FormDataConsumer::FormDataConsumer(const FormData& formData, ScriptExecutionContext& context, Callback&& callback)
: m_formData(formData.copy())
, m_context(&context)
, m_callback(WTFMove(callback))
, m_fileQueue(WorkQueue::create("FormDataConsumer file queue"))
{
read();
}
FormDataConsumer::~FormDataConsumer()
{
}
void FormDataConsumer::read()
{
if (isCancelled())
return;
ASSERT(m_callback);
ASSERT(!m_blobLoader);
if (m_currentElementIndex >= m_formData->elements().size()) {
m_callback(Span<const uint8_t> { });
return;
}
switchOn(m_formData->elements()[m_currentElementIndex++].data, [this](const Vector<uint8_t>& content) {
consumeData(content);
}, [this](const FormDataElement::EncodedFileData& fileData) {
consumeFile(fileData.filename);
}, [this](const FormDataElement::EncodedBlobData& blobData) {
consumeBlob(blobData.url);
});
}
void FormDataConsumer::consumeData(const Vector<uint8_t>& content)
{
consume(content.span());
}
void FormDataConsumer::consumeFile(const String& filename)
{
m_fileQueue->dispatch([weakThis = WeakPtr { *this }, identifier = m_context->identifier(), path = filename.isolatedCopy()]() mutable {
ScriptExecutionContext::postTaskTo(identifier, [weakThis = WTFMove(weakThis), content = FileSystem::readEntireFile(path)](auto&) {
if (!weakThis)
return;
if (!content) {
weakThis->didFail(Exception { InvalidStateError, "Unable to read form data file"_s });
return;
}
weakThis->consume(*content);
});
});
}
void FormDataConsumer::consumeBlob(const URL& blobURL)
{
m_blobLoader = makeUnique<BlobLoader>([weakThis = WeakPtr { *this }](BlobLoader&) mutable {
if (!weakThis)
return;
auto loader = std::exchange(weakThis->m_blobLoader, { });
if (!loader)
return;
if (auto optionalErrorCode = loader->errorCode()) {
weakThis->didFail(Exception { InvalidStateError, "Failed to read form data blob"_s });
return;
}
if (auto data = loader->arrayBufferResult())
weakThis->consume(Span<const uint8_t> { static_cast<const uint8_t*>(data->data()), data->byteLength() });
});
m_blobLoader->start(blobURL, m_context.get(), FileReaderLoader::ReadAsArrayBuffer);
if (!m_blobLoader || !m_blobLoader->isLoading())
didFail(Exception { InvalidStateError, "Unable to read form data blob"_s });
}
void FormDataConsumer::consume(Span<const uint8_t> content)
{
if (!m_callback)
return;
m_callback(WTFMove(content));
if (!m_callback)
return;
read();
}
void FormDataConsumer::didFail(auto&& exception)
{
auto callback = std::exchange(m_callback, nullptr);
cancel();
if (callback)
callback(WTFMove(exception));
}
void FormDataConsumer::cancel()
{
m_callback = nullptr;
if (auto loader = std::exchange(m_blobLoader, { }))
loader->cancel();
m_context = nullptr;
}
} // namespace WebCore