#include "config.h"
#include "DownloadProxy.h"
#include "APIData.h"
#include "APIDownloadClient.h"
#include "APIFrameInfo.h"
#include "AuthenticationChallengeProxy.h"
#include "DataReference.h"
#include "DownloadProxyMap.h"
#include "FrameInfoData.h"
#include "NetworkProcessMessages.h"
#include "NetworkProcessProxy.h"
#include "WebPageProxy.h"
#include "WebProcessMessages.h"
#include "WebProcessPool.h"
#include "WebProtectionSpace.h"
#include <WebCore/MIMETypeRegistry.h>
#include <WebCore/ResourceResponseBase.h>
#include <wtf/FileSystem.h>
#include <wtf/text/CString.h>
#include <wtf/text/WTFString.h>
namespace WebKit {
using namespace WebCore;
DownloadProxy::DownloadProxy(DownloadProxyMap& downloadProxyMap, WebsiteDataStore& dataStore, API::DownloadClient& client, const ResourceRequest& resourceRequest, const FrameInfoData& frameInfoData, WebPageProxy* originatingPage)
: m_downloadProxyMap(downloadProxyMap)
, m_dataStore(&dataStore)
, m_client(client)
, m_downloadID(DownloadID::generate())
, m_request(resourceRequest)
, m_originatingPage(makeWeakPtr(originatingPage))
, m_frameInfo(API::FrameInfo::create(FrameInfoData { frameInfoData }, originatingPage))
{
}
DownloadProxy::~DownloadProxy()
{
if (m_didStartCallback)
m_didStartCallback(nullptr);
}
static RefPtr<API::Data> createData(const IPC::DataReference& data)
{
if (data.isEmpty())
return nullptr;
return API::Data::create(data.data(), data.size());
}
void DownloadProxy::cancel(CompletionHandler<void(API::Data*)>&& completionHandler)
{
if (m_dataStore) {
m_dataStore->networkProcess().sendWithAsyncReply(Messages::NetworkProcess::CancelDownload(m_downloadID), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)] (const IPC::DataReference& resumeData) mutable {
m_legacyResumeData = createData(resumeData);
completionHandler(m_legacyResumeData.get());
m_downloadProxyMap.downloadFinished(*this);
});
} else
completionHandler(nullptr);
}
void DownloadProxy::invalidate()
{
ASSERT(m_dataStore);
m_dataStore = nullptr;
}
void DownloadProxy::processDidClose()
{
m_client->processDidCrash(*this);
}
WebPageProxy* DownloadProxy::originatingPage() const
{
return m_originatingPage.get();
}
#if PLATFORM(COCOA)
void DownloadProxy::publishProgress(const URL& URL)
{
if (!m_dataStore)
return;
SandboxExtension::Handle handle;
bool createdSandboxExtension = SandboxExtension::createHandle(URL.fileSystemPath(), SandboxExtension::Type::ReadWrite, handle);
ASSERT_UNUSED(createdSandboxExtension, createdSandboxExtension);
m_dataStore->networkProcess().send(Messages::NetworkProcess::PublishDownloadProgress(m_downloadID, URL, handle), 0);
}
#endif // PLATFORM(COCOA)
void DownloadProxy::didStart(const ResourceRequest& request, const String& suggestedFilename)
{
m_request = request;
m_suggestedFilename = suggestedFilename;
if (m_redirectChain.isEmpty() || m_redirectChain.last() != request.url())
m_redirectChain.append(request.url());
if (m_didStartCallback)
m_didStartCallback(this);
m_client->legacyDidStart(*this);
}
void DownloadProxy::didReceiveAuthenticationChallenge(AuthenticationChallenge&& authenticationChallenge, uint64_t challengeID)
{
if (!m_dataStore)
return;
auto authenticationChallengeProxy = AuthenticationChallengeProxy::create(WTFMove(authenticationChallenge), challengeID, makeRef(*m_dataStore->networkProcess().connection()), nullptr);
m_client->didReceiveAuthenticationChallenge(*this, authenticationChallengeProxy.get());
}
void DownloadProxy::willSendRequest(ResourceRequest&& proposedRequest, const ResourceResponse& redirectResponse)
{
m_client->willSendRequest(*this, WTFMove(proposedRequest), redirectResponse, [this, protectedThis = makeRef(*this)](ResourceRequest&& newRequest) {
m_redirectChain.append(newRequest.url());
if (!protectedThis->m_dataStore)
return;
auto& networkProcessProxy = protectedThis->m_dataStore->networkProcess();
networkProcessProxy.send(Messages::NetworkProcess::ContinueWillSendRequest(protectedThis->m_downloadID, newRequest), 0);
});
}
void DownloadProxy::didReceiveData(uint64_t bytesWritten, uint64_t totalBytesWritten, uint64_t totalBytesExpectedToWrite)
{
m_client->didReceiveData(*this, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
}
void DownloadProxy::decideDestinationWithSuggestedFilename(const WebCore::ResourceResponse& response, String&& suggestedFilename, CompletionHandler<void(String, SandboxExtension::Handle, AllowOverwrite)>&& completionHandler)
{
if (response.isAttachmentWithFilename() || (suggestedFilename.isEmpty() && m_suggestedFilename.isEmpty()))
suggestedFilename = response.suggestedFilename();
else if (!m_suggestedFilename.isEmpty())
suggestedFilename = m_suggestedFilename;
suggestedFilename = MIMETypeRegistry::appendFileExtensionIfNecessary(suggestedFilename, response.mimeType());
m_client->decideDestinationWithSuggestedFilename(*this, response, ResourceResponseBase::sanitizeSuggestedFilename(suggestedFilename), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)] (AllowOverwrite allowOverwrite, String destination) mutable {
SandboxExtension::Handle sandboxExtensionHandle;
if (!destination.isNull())
SandboxExtension::createHandle(destination, SandboxExtension::Type::ReadWrite, sandboxExtensionHandle);
setDestinationFilename(destination);
completionHandler(destination, WTFMove(sandboxExtensionHandle), allowOverwrite);
});
}
void DownloadProxy::didCreateDestination(const String& path)
{
m_client->didCreateDestination(*this, path);
}
void DownloadProxy::didFinish()
{
m_client->didFinish(*this);
m_downloadProxyMap.downloadFinished(*this);
}
void DownloadProxy::didFail(const ResourceError& error, const IPC::DataReference& resumeData)
{
m_legacyResumeData = createData(resumeData);
m_client->didFail(*this, error, m_legacyResumeData.get());
m_downloadProxyMap.downloadFinished(*this);
}
void DownloadProxy::setClient(Ref<API::DownloadClient>&& client)
{
m_client = WTFMove(client);
}
}