blob: 055611f5f8aff5bdb277ba195db6c8714afa6280 [file] [log] [blame]
/*
* Copyright (C) 2019 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 "PreviewConverter.h"
#if ENABLE(PREVIEW_CONVERTER)
#include "PreviewConverterClient.h"
#include "PreviewConverterProvider.h"
#include <wtf/RunLoop.h>
#include <wtf/SetForScope.h>
namespace WebCore {
PreviewConverter::~PreviewConverter() = default;
bool PreviewConverter::supportsMIMEType(const String& mimeType)
{
if (mimeType.isNull())
return false;
if (equalLettersIgnoringASCIICase(mimeType, "text/html"_s) || equalLettersIgnoringASCIICase(mimeType, "text/plain"_s))
return false;
static NeverDestroyed<HashSet<String, ASCIICaseInsensitiveHash>> supportedMIMETypes = platformSupportedMIMETypes();
return supportedMIMETypes->contains(mimeType);
}
ResourceResponse PreviewConverter::previewResponse() const
{
auto response = platformPreviewResponse();
ASSERT(response.mimeType().length());
response.setIsQuickLook(true);
return response;
}
const ResourceError& PreviewConverter::previewError() const
{
return m_previewError;
}
const FragmentedSharedBuffer& PreviewConverter::previewData() const
{
return *m_previewData.get();
}
void PreviewConverter::updateMainResource()
{
if (m_isInClientCallback)
return;
if (m_state != State::Updating)
return;
auto provider = m_provider.get();
if (!provider) {
didFailUpdating();
return;
}
provider->provideMainResourceForPreviewConverter(*this, [this, protectedThis = Ref { *this }](auto&& buffer) {
appendFromBuffer(WTFMove(buffer));
});
}
void PreviewConverter::appendFromBuffer(const FragmentedSharedBuffer& buffer)
{
while (buffer.size() > m_lengthAppended) {
auto newData = buffer.getSomeData(m_lengthAppended);
platformAppend(newData);
m_lengthAppended += newData.size();
}
}
void PreviewConverter::finishUpdating()
{
if (m_isInClientCallback)
return;
if (m_state != State::Updating)
return;
platformFinishedAppending();
iterateClients([&](auto& client) {
client.previewConverterDidFinishUpdating(*this);
});
}
void PreviewConverter::failedUpdating()
{
if (m_isInClientCallback)
return;
if (m_state != State::Updating)
return;
m_state = State::FailedUpdating;
platformFailedAppending();
}
bool PreviewConverter::hasClient(PreviewConverterClient& client) const
{
return m_clients.contains(&client);
}
void PreviewConverter::addClient(PreviewConverterClient& client)
{
ASSERT(!hasClient(client));
m_clients.append(client);
didAddClient(client);
}
void PreviewConverter::removeClient(PreviewConverterClient& client)
{
m_clients.removeFirst(&client);
ASSERT(!hasClient(client));
}
static String& sharedPasswordForTesting()
{
static NeverDestroyed<String> passwordForTesting;
return passwordForTesting.get();
}
const String& PreviewConverter::passwordForTesting()
{
return sharedPasswordForTesting();
}
void PreviewConverter::setPasswordForTesting(const String& password)
{
sharedPasswordForTesting() = password;
}
template<typename T>
void PreviewConverter::iterateClients(T&& callback)
{
SetForScope isInClientCallback { m_isInClientCallback, true };
auto clientsCopy { m_clients };
auto protectedThis { Ref { *this } };
for (auto& client : clientsCopy) {
if (client && hasClient(*client))
callback(*client);
}
}
void PreviewConverter::didAddClient(PreviewConverterClient& client)
{
RunLoop::current().dispatch([this, protectedThis = Ref { *this }, weakClient = WeakPtr { client }]() {
if (auto client = weakClient.get())
replayToClient(*client);
});
}
void PreviewConverter::didFailConvertingWithError(const ResourceError& error)
{
m_previewError = error;
m_state = State::FailedConverting;
iterateClients([&](auto& client) {
client.previewConverterDidFailConverting(*this);
});
}
void PreviewConverter::didFailUpdating()
{
failedUpdating();
iterateClients([&](auto& client) {
client.previewConverterDidFailUpdating(*this);
});
}
void PreviewConverter::replayToClient(PreviewConverterClient& client)
{
if (!hasClient(client))
return;
SetForScope isInClientCallback { m_isInClientCallback, true };
auto protectedThis { Ref { *this } };
client.previewConverterDidStartUpdating(*this);
if (m_state == State::Updating || !hasClient(client))
return;
if (m_state == State::FailedUpdating) {
client.previewConverterDidFailUpdating(*this);
return;
}
ASSERT(m_state >= State::Converting);
client.previewConverterDidStartConverting(*this);
if (!m_previewData.isEmpty() && hasClient(client))
client.previewConverterDidReceiveData(*this, *m_previewData.get());
if (m_state == State::Converting || !hasClient(client))
return;
if (m_state == State::FailedConverting) {
ASSERT(!m_previewError.isNull());
client.previewConverterDidFailConverting(*this);
return;
}
ASSERT(m_state == State::FinishedConverting);
ASSERT(!m_previewData.isEmpty());
ASSERT(m_previewError.isNull());
client.previewConverterDidFinishConverting(*this);
}
void PreviewConverter::delegateDidReceiveData(const FragmentedSharedBuffer& data)
{
auto protectedThis { Ref { *this } };
if (m_state == State::Updating) {
m_provider = nullptr;
m_state = State::Converting;
iterateClients([&](auto& client) {
client.previewConverterDidStartConverting(*this);
});
}
ASSERT(m_state == State::Converting);
if (data.isEmpty())
return;
m_previewData.append(data);
iterateClients([&](auto& client) {
client.previewConverterDidReceiveData(*this, data);
});
}
void PreviewConverter::delegateDidFinishLoading()
{
ASSERT(m_state == State::Converting);
m_state = State::FinishedConverting;
iterateClients([&](auto& client) {
client.previewConverterDidFinishConverting(*this);
});
}
void PreviewConverter::delegateDidFailWithError(const ResourceError& error)
{
if (!isPlatformPasswordError(error)) {
didFailConvertingWithError(error);
return;
}
ASSERT(m_state == State::Updating);
auto provider = m_provider.get();
if (!provider) {
didFailConvertingWithError(error);
return;
}
provider->providePasswordForPreviewConverter(*this, [this, protectedThis = Ref { *this }](auto& password) mutable {
if (m_state != State::Updating)
return;
platformUnlockWithPassword(password);
m_lengthAppended = 0;
updateMainResource();
finishUpdating();
});
}
} // namespace WebCore
#endif // ENABLE(PREVIEW_CONVERTER)