Add Optional to Forward.h.
[WebKit.git] / Source / WebCore / platform / network / BlobResourceHandle.cpp
index 2d8af3b..cc67ce6 100644 (file)
 
 #include "config.h"
 
-#if ENABLE(BLOB)
-
 #include "BlobResourceHandle.h"
 
 #include "AsyncFileStream.h"
-#include "BlobStorageData.h"
+#include "BlobData.h"
 #include "FileStream.h"
-#include "FileSystem.h"
+#include "HTTPHeaderNames.h"
 #include "HTTPParsers.h"
-#include "KURL.h"
+#include "ParsedContentRange.h"
 #include "ResourceError.h"
 #include "ResourceHandleClient.h"
 #include "ResourceRequest.h"
 #include "ResourceResponse.h"
 #include "SharedBuffer.h"
+#include <wtf/CompletionHandler.h>
+#include <wtf/FileSystem.h>
 #include <wtf/MainThread.h>
+#include <wtf/Ref.h>
+#include <wtf/URL.h>
 
 namespace WebCore {
 
 static const unsigned bufferSize = 512 * 1024;
-static const long long positionNotSpecified = -1;
 
 static const int httpOK = 200;
 static const int httpPartialContent = 206;
 static const int httpNotAllowed = 403;
-static const int httpNotFound = 404;
 static const int httpRequestedRangeNotSatisfiable = 416;
 static const int httpInternalError = 500;
 static const char* httpOKText = "OK";
 static const char* httpPartialContentText = "Partial Content";
 static const char* httpNotAllowedText = "Not Allowed";
-static const char* httpNotFoundText = "Not Found";
 static const char* httpRequestedRangeNotSatisfiableText = "Requested Range Not Satisfiable";
 static const char* httpInternalErrorText = "Internal Server Error";
 
 static const char* const webKitBlobResourceDomain = "WebKitBlobResource";
-enum {
-    notFoundError = 1,
-    securityError = 2,
-    rangeError = 3,
-    notReadableError = 4,
-    methodNotAllowed = 5
-};
 
 ///////////////////////////////////////////////////////////////////////////////
 // BlobResourceSynchronousLoader
@@ -83,10 +75,12 @@ class BlobResourceSynchronousLoader : public ResourceHandleClient {
 public:
     BlobResourceSynchronousLoader(ResourceError&, ResourceResponse&, Vector<char>&);
 
-    virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&) OVERRIDE;
-    virtual void didReceiveData(ResourceHandle*, const char*, int, int /*encodedDataLength*/) OVERRIDE;
-    virtual void didFinishLoading(ResourceHandle*, double /*finishTime*/) OVERRIDE;
-    virtual void didFail(ResourceHandle*, const ResourceError&) OVERRIDE;
+    void didReceiveResponseAsync(ResourceHandle*, ResourceResponse&&, CompletionHandler<void()>&&) final;
+    void didFail(ResourceHandle*, const ResourceError&) final;
+    void willSendRequestAsync(ResourceHandle*, ResourceRequest&&, ResourceResponse&&, CompletionHandler<void(ResourceRequest&&)>&&) final;
+#if USE(PROTECTION_SPACE_AUTH_CALLBACK)
+    void canAuthenticateAgainstProtectionSpaceAsync(ResourceHandle*, const ProtectionSpace&, CompletionHandler<void(bool)>&&) final;
+#endif
 
 private:
     ResourceError& m_error;
@@ -101,11 +95,26 @@ BlobResourceSynchronousLoader::BlobResourceSynchronousLoader(ResourceError& erro
 {
 }
 
-void BlobResourceSynchronousLoader::didReceiveResponse(ResourceHandle* handle, const ResourceResponse& response)
+void BlobResourceSynchronousLoader::willSendRequestAsync(ResourceHandle*, ResourceRequest&& request, ResourceResponse&&, CompletionHandler<void(ResourceRequest&&)>&& completionHandler)
+{
+    ASSERT_NOT_REACHED();
+    completionHandler(WTFMove(request));
+}
+
+#if USE(PROTECTION_SPACE_AUTH_CALLBACK)
+void BlobResourceSynchronousLoader::canAuthenticateAgainstProtectionSpaceAsync(ResourceHandle*, const ProtectionSpace&, CompletionHandler<void(bool)>&& completionHandler)
+{
+    ASSERT_NOT_REACHED();
+    completionHandler(false);
+}
+#endif
+
+void BlobResourceSynchronousLoader::didReceiveResponseAsync(ResourceHandle* handle, ResourceResponse&& response, CompletionHandler<void()>&& completionHandler)
 {
     // We cannot handle the size that is more than maximum integer.
     if (response.expectedContentLength() > INT_MAX) {
-        m_error = ResourceError(webKitBlobResourceDomain, notReadableError, response.url(), "File is too large");
+        m_error = ResourceError(webKitBlobResourceDomain, static_cast<int>(BlobResourceHandle::Error::NotReadableError), response.url(), "File is too large");
+        completionHandler();
         return;
     }
 
@@ -114,14 +123,7 @@ void BlobResourceSynchronousLoader::didReceiveResponse(ResourceHandle* handle, c
     // Read all the data.
     m_data.resize(static_cast<size_t>(response.expectedContentLength()));
     static_cast<BlobResourceHandle*>(handle)->readSync(m_data.data(), static_cast<int>(m_data.size()));
-}
-
-void BlobResourceSynchronousLoader::didReceiveData(ResourceHandle*, const char*, int, int)
-{
-}
-
-void BlobResourceSynchronousLoader::didFinishLoading(ResourceHandle*, double)
-{
+    completionHandler();
 }
 
 void BlobResourceSynchronousLoader::didFail(ResourceHandle*, const ResourceError& error)
@@ -134,19 +136,15 @@ void BlobResourceSynchronousLoader::didFail(ResourceHandle*, const ResourceError
 ///////////////////////////////////////////////////////////////////////////////
 // BlobResourceHandle
 
-PassRefPtr<BlobResourceHandle> BlobResourceHandle::createAsync(BlobStorageData* blobData, const ResourceRequest& request, ResourceHandleClient* client)
+Ref<BlobResourceHandle> BlobResourceHandle::createAsync(BlobData* blobData, const ResourceRequest& request, ResourceHandleClient* client)
 {
-    // FIXME: Should probably call didFail() instead of blocking the load without explanation.
-    if (!equalIgnoringCase(request.httpMethod(), "GET"))
-        return 0;
-
-    return adoptRef(new BlobResourceHandle(blobData, request, client, true));
+    return adoptRef(*new BlobResourceHandle(blobData, request, client, true));
 }
 
-void BlobResourceHandle::loadResourceSynchronously(BlobStorageData* blobData, const ResourceRequest& request, ResourceError& error, ResourceResponse& response, Vector<char>& data)
+void BlobResourceHandle::loadResourceSynchronously(BlobData* blobData, const ResourceRequest& request, ResourceError& error, ResourceResponse& response, Vector<char>& data)
 {
-    if (!equalIgnoringCase(request.httpMethod(), "GET")) {
-        error = ResourceError(webKitBlobResourceDomain, methodNotAllowed, response.url(), "Request method must be GET");
+    if (!equalLettersIgnoringASCIICase(request.httpMethod(), "get")) {
+        error = ResourceError(webKitBlobResourceDomain, static_cast<int>(Error::MethodNotAllowed), response.url(), "Request method must be GET");
         return;
     }
 
@@ -155,89 +153,65 @@ void BlobResourceHandle::loadResourceSynchronously(BlobStorageData* blobData, co
     handle->start();
 }
 
-BlobResourceHandle::BlobResourceHandle(PassRefPtr<BlobStorageData> blobData, const ResourceRequest& request, ResourceHandleClient* client, bool async)
-    : ResourceHandle(0, request, client, false, false)
-    , m_blobData(blobData)
-    , m_async(async)
-    , m_errorCode(0)
-    , m_aborted(false)
-    , m_rangeOffset(positionNotSpecified)
-    , m_rangeEnd(positionNotSpecified)
-    , m_rangeSuffixLength(positionNotSpecified)
-    , m_totalRemainingSize(0)
-    , m_currentItemReadSize(0)
-    , m_sizeItemCount(0)
-    , m_readItemCount(0)
-    , m_fileOpened(false)
+BlobResourceHandle::BlobResourceHandle(BlobData* blobData, const ResourceRequest& request, ResourceHandleClient* client, bool async)
+    : ResourceHandle { nullptr, request, client, false /* defersLoading */, false /* shouldContentSniff */, true /* shouldContentEncodingSniff */ }
+    , m_blobData { blobData }
+    , m_async { async }
 {
     if (m_async)
-        m_asyncStream = AsyncFileStream::create(this);
+        m_asyncStream = std::make_unique<AsyncFileStream>(*this);
     else
-        m_stream = FileStream::create();
+        m_stream = std::make_unique<FileStream>();
 }
 
-BlobResourceHandle::~BlobResourceHandle()
-{
-    if (m_async) {
-        if (m_asyncStream)
-            m_asyncStream->stop();
-    } else {
-        if (m_stream)
-            m_stream->stop();
-    }
-}
+BlobResourceHandle::~BlobResourceHandle() = default;
 
 void BlobResourceHandle::cancel()
 {
-    if (m_async) {
-        if (m_asyncStream) {
-            m_asyncStream->stop();
-            m_asyncStream = 0;
-        }
-    }
+    m_asyncStream = nullptr;
+    m_fileOpened = false;
 
     m_aborted = true;
 
     ResourceHandle::cancel();
 }
 
-void delayedStartBlobResourceHandle(void* context)
-{
-    RefPtr<BlobResourceHandle> handle = adoptRef(static_cast<BlobResourceHandle*>(context));
-    handle->doStart();
-}
-
 void BlobResourceHandle::start()
 {
-    if (m_async) {
-        // Keep BlobResourceHandle alive until delayedStartBlobResourceHandle runs.
-        ref();
-
-        // Finish this async call quickly and return.
-        callOnMainThread(delayedStartBlobResourceHandle, this);
+    if (!m_async) {
+        doStart();
         return;
     }
 
-    doStart();
+    // Finish this async call quickly and return.
+    callOnMainThread([protectedThis = makeRef(*this)]() mutable {
+        protectedThis->doStart();
+    });
 }
 
 void BlobResourceHandle::doStart()
 {
+    ASSERT(isMainThread());
+
     // Do not continue if the request is aborted or an error occurs.
-    if (m_aborted || m_errorCode)
+    if (erroredOrAborted())
+        return;
+
+    if (!equalLettersIgnoringASCIICase(firstRequest().httpMethod(), "get")) {
+        notifyFail(Error::MethodNotAllowed);
         return;
+    }
 
     // If the blob data is not found, fail now.
     if (!m_blobData) {
-        m_errorCode = notFoundError;
-        notifyResponse();
+        notifyFail(Error::NotFoundError);
         return;
     }
 
     // Parse the "Range" header we care about.
-    String range = firstRequest().httpHeaderField("Range");
+    String range = firstRequest().httpHeaderField(HTTPHeaderName::Range);
     if (!range.isEmpty() && !parseRange(range, m_rangeOffset, m_rangeEnd, m_rangeSuffixLength)) {
-        m_errorCode = rangeError;
+        m_errorCode = Error::RangeError;
         notifyResponse();
         return;
     }
@@ -245,8 +219,8 @@ void BlobResourceHandle::doStart()
     if (m_async)
         getSizeForNext();
     else {
-        RefPtr<BlobResourceHandle> protect(this); // getSizeForNext calls the client
-        for (size_t i = 0; i < m_blobData->items().size() && !m_aborted && !m_errorCode; ++i)
+        Ref<BlobResourceHandle> protectedThis(*this); // getSizeForNext calls the client
+        for (size_t i = 0; i < m_blobData->items().size() && !erroredOrAborted(); ++i)
             getSizeForNext();
         notifyResponse();
     }
@@ -254,30 +228,31 @@ void BlobResourceHandle::doStart()
 
 void BlobResourceHandle::getSizeForNext()
 {
+    ASSERT(isMainThread());
+
     // Do we finish validating and counting size for all items?
     if (m_sizeItemCount >= m_blobData->items().size()) {
         seek();
 
         // Start reading if in asynchronous mode.
         if (m_async) {
-            RefPtr<BlobResourceHandle> protect(this);
+            Ref<BlobResourceHandle> protectedThis(*this);
             notifyResponse();
-            m_buffer.resize(bufferSize);
-            readAsync();
         }
         return;
     }
 
     const BlobDataItem& item = m_blobData->items().at(m_sizeItemCount);
-    switch (item.type) {
-    case BlobDataItem::Data:
-        didGetSize(item.length);
+    switch (item.type()) {
+    case BlobDataItem::Type::Data:
+        didGetSize(item.length());
         break;
-    case BlobDataItem::File:
+    case BlobDataItem::Type::File:
+        // Files know their sizes, but asking the stream to verify that the file wasn't modified.
         if (m_async)
-            m_asyncStream->getSize(item.path, item.expectedModificationTime);
+            m_asyncStream->getSize(item.file()->path(), item.file()->expectedModificationTime());
         else
-            didGetSize(m_stream->getSize(item.path, item.expectedModificationTime));
+            didGetSize(m_stream->getSize(item.file()->path(), item.file()->expectedModificationTime()));
         break;
     default:
         ASSERT_NOT_REACHED();
@@ -286,26 +261,27 @@ void BlobResourceHandle::getSizeForNext()
 
 void BlobResourceHandle::didGetSize(long long size)
 {
+    ASSERT(isMainThread());
+
     // Do not continue if the request is aborted or an error occurs.
-    if (m_aborted || m_errorCode)
+    if (erroredOrAborted())
         return;
 
     // If the size is -1, it means the file has been moved or changed. Fail now.
     if (size == -1) {
-        m_errorCode = notFoundError;
-        notifyResponse();
+        notifyFail(Error::NotFoundError);
         return;
     }
 
     // The size passed back is the size of the whole file. If the underlying item is a sliced file, we need to use the slice length.
     const BlobDataItem& item = m_blobData->items().at(m_sizeItemCount);
-    if (item.type == BlobDataItem::File && item.length != BlobDataItem::toEndOfFile)
-        size = item.length;
+    size = item.length();
 
     // Cache the size.
     m_itemLengthList.append(size);
 
     // Count the size.
+    m_totalSize += size;
     m_totalRemainingSize += size;
     m_sizeItemCount++;
 
@@ -315,14 +291,16 @@ void BlobResourceHandle::didGetSize(long long size)
 
 void BlobResourceHandle::seek()
 {
+    ASSERT(isMainThread());
+
     // Convert from the suffix length to the range.
-    if (m_rangeSuffixLength != positionNotSpecified) {
+    if (m_rangeSuffixLength != kPositionNotSpecified) {
         m_rangeOffset = m_totalRemainingSize - m_rangeSuffixLength;
         m_rangeEnd = m_rangeOffset + m_rangeSuffixLength - 1;
     }
 
     // Bail out if the range is not provided.
-    if (m_rangeOffset == positionNotSpecified)
+    if (m_rangeOffset == kPositionNotSpecified)
         return;
 
     // Skip the initial items that are not in the range.
@@ -334,7 +312,7 @@ void BlobResourceHandle::seek()
     m_currentItemReadSize = offset;
 
     // Adjust the total remaining size in order not to go beyond the range.
-    if (m_rangeEnd != positionNotSpecified) {
+    if (m_rangeEnd != kPositionNotSpecified) {
         long long rangeSize = m_rangeEnd - m_rangeOffset + 1;
         if (m_totalRemainingSize > rangeSize)
             m_totalRemainingSize = rangeSize;
@@ -344,25 +322,27 @@ void BlobResourceHandle::seek()
 
 int BlobResourceHandle::readSync(char* buf, int length)
 {
+    ASSERT(isMainThread());
+
     ASSERT(!m_async);
-    RefPtr<BlobResourceHandle> protect(this);
+    Ref<BlobResourceHandle> protectedThis(*this);
 
     int offset = 0;
     int remaining = length;
     while (remaining) {
         // Do not continue if the request is aborted or an error occurs.
-        if (m_aborted || m_errorCode)
+        if (erroredOrAborted())
             break;
 
         // If there is no more remaining data to read, we are done.
         if (!m_totalRemainingSize || m_readItemCount >= m_blobData->items().size())
             break;
-        
+
         const BlobDataItem& item = m_blobData->items().at(m_readItemCount);
         int bytesRead = 0;
-        if (item.type == BlobDataItem::Data)
+        if (item.type() == BlobDataItem::Type::Data)
             bytesRead = readDataSync(item, buf + offset, remaining);
-        else if (item.type == BlobDataItem::File)
+        else if (item.type() == BlobDataItem::Type::File)
             bytesRead = readFileSync(item, buf + offset, remaining);
         else
             ASSERT_NOT_REACHED();
@@ -374,7 +354,7 @@ int BlobResourceHandle::readSync(char* buf, int length)
     }
 
     int result;
-    if (m_aborted || m_errorCode)
+    if (erroredOrAborted())
         result = -1;
     else
         result = length - remaining;
@@ -390,17 +370,19 @@ int BlobResourceHandle::readSync(char* buf, int length)
 
 int BlobResourceHandle::readDataSync(const BlobDataItem& item, char* buf, int length)
 {
+    ASSERT(isMainThread());
+
     ASSERT(!m_async);
 
-    long long remaining = item.length - m_currentItemReadSize;
+    long long remaining = item.length() - m_currentItemReadSize;
     int bytesToRead = (length > remaining) ? static_cast<int>(remaining) : length;
     if (bytesToRead > m_totalRemainingSize)
         bytesToRead = static_cast<int>(m_totalRemainingSize);
-    memcpy(buf, item.data->data() + item.offset + m_currentItemReadSize, bytesToRead);
+    memcpy(buf, item.data().data() + item.offset() + m_currentItemReadSize, bytesToRead);
     m_totalRemainingSize -= bytesToRead;
 
     m_currentItemReadSize += bytesToRead;
-    if (m_currentItemReadSize == item.length) {
+    if (m_currentItemReadSize == item.length()) {
         m_readItemCount++;
         m_currentItemReadSize = 0;
     }
@@ -410,16 +392,18 @@ int BlobResourceHandle::readDataSync(const BlobDataItem& item, char* buf, int le
 
 int BlobResourceHandle::readFileSync(const BlobDataItem& item, char* buf, int length)
 {
+    ASSERT(isMainThread());
+
     ASSERT(!m_async);
 
     if (!m_fileOpened) {
         long long bytesToRead = m_itemLengthList[m_readItemCount] - m_currentItemReadSize;
         if (bytesToRead > m_totalRemainingSize)
             bytesToRead = m_totalRemainingSize;
-        bool success = m_stream->openForRead(item.path, item.offset + m_currentItemReadSize, bytesToRead);
+        bool success = m_stream->openForRead(item.file()->path(), item.offset() + m_currentItemReadSize, bytesToRead);
         m_currentItemReadSize = 0;
         if (!success) {
-            m_errorCode = notReadableError;
+            m_errorCode = Error::NotReadableError;
             return 0;
         }
 
@@ -428,7 +412,7 @@ int BlobResourceHandle::readFileSync(const BlobDataItem& item, char* buf, int le
 
     int bytesRead = m_stream->read(buf, length);
     if (bytesRead < 0) {
-        m_errorCode = notReadableError;
+        m_errorCode = Error::NotReadableError;
         return 0;
     }
     if (!bytesRead) {
@@ -443,10 +427,10 @@ int BlobResourceHandle::readFileSync(const BlobDataItem& item, char* buf, int le
 
 void BlobResourceHandle::readAsync()
 {
-    ASSERT(m_async);
+    ASSERT(isMainThread());
 
     // Do not continue if the request is aborted or an error occurs.
-    if (m_aborted || m_errorCode)
+    if (erroredOrAborted())
         return;
 
     // If there is no more remaining data to read, we are done.
@@ -456,9 +440,9 @@ void BlobResourceHandle::readAsync()
     }
 
     const BlobDataItem& item = m_blobData->items().at(m_readItemCount);
-    if (item.type == BlobDataItem::Data)
+    if (item.type() == BlobDataItem::Type::Data)
         readDataAsync(item);
-    else if (item.type == BlobDataItem::File)
+    else if (item.type() == BlobDataItem::Type::File)
         readFileAsync(item);
     else
         ASSERT_NOT_REACHED();
@@ -466,19 +450,21 @@ void BlobResourceHandle::readAsync()
 
 void BlobResourceHandle::readDataAsync(const BlobDataItem& item)
 {
-    ASSERT(m_async);
-    RefPtr<BlobResourceHandle> protect(this);
+    ASSERT(isMainThread());
+    ASSERT(item.data().data());
+
+    Ref<BlobResourceHandle> protectedThis(*this);
 
-    long long bytesToRead = item.length - m_currentItemReadSize;
+    long long bytesToRead = item.length() - m_currentItemReadSize;
     if (bytesToRead > m_totalRemainingSize)
         bytesToRead = m_totalRemainingSize;
-    consumeData(item.data->data() + item.offset + m_currentItemReadSize, static_cast<int>(bytesToRead));
+    consumeData(reinterpret_cast<const char*>(item.data().data()->data()) + item.offset() + m_currentItemReadSize, static_cast<int>(bytesToRead));
     m_currentItemReadSize = 0;
 }
 
 void BlobResourceHandle::readFileAsync(const BlobDataItem& item)
 {
-    ASSERT(m_async);
+    ASSERT(isMainThread());
 
     if (m_fileOpened) {
         m_asyncStream->read(m_buffer.data(), m_buffer.size());
@@ -488,7 +474,7 @@ void BlobResourceHandle::readFileAsync(const BlobDataItem& item)
     long long bytesToRead = m_itemLengthList[m_readItemCount] - m_currentItemReadSize;
     if (bytesToRead > m_totalRemainingSize)
         bytesToRead = static_cast<int>(m_totalRemainingSize);
-    m_asyncStream->openForRead(item.path, item.offset + m_currentItemReadSize, bytesToRead);
+    m_asyncStream->openForRead(item.file()->path(), item.offset() + m_currentItemReadSize, bytesToRead);
     m_fileOpened = true;
     m_currentItemReadSize = 0;
 }
@@ -498,7 +484,7 @@ void BlobResourceHandle::didOpen(bool success)
     ASSERT(m_async);
 
     if (!success) {
-        failed(notReadableError);
+        failed(Error::NotReadableError);
         return;
     }
 
@@ -509,7 +495,7 @@ void BlobResourceHandle::didOpen(bool success)
 void BlobResourceHandle::didRead(int bytesRead)
 {
     if (bytesRead < 0) {
-        failed(notReadableError);
+        failed(Error::NotReadableError);
         return;
     }
 
@@ -519,7 +505,7 @@ void BlobResourceHandle::didRead(int bytesRead)
 void BlobResourceHandle::consumeData(const char* data, int bytesRead)
 {
     ASSERT(m_async);
-    RefPtr<BlobResourceHandle> protect(this);
+    Ref<BlobResourceHandle> protectedThis(*this);
 
     m_totalRemainingSize -= bytesRead;
 
@@ -546,10 +532,10 @@ void BlobResourceHandle::consumeData(const char* data, int bytesRead)
     readAsync();
 }
 
-void BlobResourceHandle::failed(int errorCode)
+void BlobResourceHandle::failed(Error errorCode)
 {
     ASSERT(m_async);
-    RefPtr<BlobResourceHandle> protect(this);
+    Ref<BlobResourceHandle> protectedThis(*this);
 
     // Notify the client.
     notifyFail(errorCode);
@@ -566,8 +552,8 @@ void BlobResourceHandle::notifyResponse()
     if (!client())
         return;
 
-    if (m_errorCode) {
-        RefPtr<BlobResourceHandle> protect(this);
+    if (m_errorCode != Error::NoError) {
+        Ref<BlobResourceHandle> protectedThis(*this);
         notifyResponseOnError();
         notifyFinish();
     } else
@@ -576,38 +562,39 @@ void BlobResourceHandle::notifyResponse()
 
 void BlobResourceHandle::notifyResponseOnSuccess()
 {
-    bool isRangeRequest = m_rangeOffset != positionNotSpecified;
-    ResourceResponse response(firstRequest().url(), m_blobData->contentType(), m_totalRemainingSize, String(), String());
-    response.setExpectedContentLength(m_totalRemainingSize);
+    ASSERT(isMainThread());
+
+    bool isRangeRequest = m_rangeOffset != kPositionNotSpecified;
+    ResourceResponse response(firstRequest().url(), m_blobData->contentType(), m_totalRemainingSize, String());
     response.setHTTPStatusCode(isRangeRequest ? httpPartialContent : httpOK);
     response.setHTTPStatusText(isRangeRequest ? httpPartialContentText : httpOKText);
-    if (!m_blobData->contentDisposition().isEmpty())
-        response.setHTTPHeaderField("Content-Disposition", m_blobData->contentDisposition());
-
-    // BlobResourceHandle cannot be used with downloading, and doesn't even wait for continueDidReceiveResponse.
-    // It's currently client's responsibility to know that didReceiveResponseAsync cannot be used to convert a
-    // load into a download or blobs.
-    if (client()->usesAsyncCallbacks())
-        client()->didReceiveResponseAsync(this, response);
-    else
-        client()->didReceiveResponse(this, response);
+
+    response.setHTTPHeaderField(HTTPHeaderName::ContentType, m_blobData->contentType());
+    response.setHTTPHeaderField(HTTPHeaderName::ContentLength, String::number(m_totalRemainingSize));
+
+    if (isRangeRequest)
+        response.setHTTPHeaderField(HTTPHeaderName::ContentRange, ParsedContentRange(m_rangeOffset, m_rangeEnd, m_totalSize).headerValue());
+    // FIXME: If a resource identified with a blob: URL is a File object, user agents must use that file's name attribute,
+    // as if the response had a Content-Disposition header with the filename parameter set to the File's name attribute.
+    // Notably, this will affect a name suggested in "File Save As".
+
+    client()->didReceiveResponseAsync(this, WTFMove(response), [this, protectedThis = makeRef(*this)] {
+        m_buffer.resize(bufferSize);
+        readAsync();
+    });
 }
 
 void BlobResourceHandle::notifyResponseOnError()
 {
-    ASSERT(m_errorCode);
+    ASSERT(m_errorCode != Error::NoError);
 
-    ResourceResponse response(firstRequest().url(), "text/plain", 0, String(), String());
+    ResourceResponse response(firstRequest().url(), "text/plain", 0, String());
     switch (m_errorCode) {
-    case rangeError:
+    case Error::RangeError:
         response.setHTTPStatusCode(httpRequestedRangeNotSatisfiable);
         response.setHTTPStatusText(httpRequestedRangeNotSatisfiableText);
         break;
-    case notFoundError:
-        response.setHTTPStatusCode(httpNotFound);
-        response.setHTTPStatusText(httpNotFoundText);
-        break;
-    case securityError:
+    case Error::SecurityError:
         response.setHTTPStatusCode(httpNotAllowed);
         response.setHTTPStatusText(httpNotAllowedText);
         break;
@@ -617,51 +604,48 @@ void BlobResourceHandle::notifyResponseOnError()
         break;
     }
 
-    // Note that we don't wait for continueDidReceiveResponse when using didReceiveResponseAsync.
-    // This is not formally correct, but the client has to be a no-op anyway, because blobs can't be downloaded.
-    if (client()->usesAsyncCallbacks())
-        client()->didReceiveResponseAsync(this, response);
-    else
-        client()->didReceiveResponse(this, response);
+    client()->didReceiveResponseAsync(this, WTFMove(response), [this, protectedThis = makeRef(*this)] {
+        m_buffer.resize(bufferSize);
+        readAsync();
+    });
 }
 
 void BlobResourceHandle::notifyReceiveData(const char* data, int bytesRead)
 {
     if (client())
-        client()->didReceiveBuffer(this, SharedBuffer::create(data, bytesRead), bytesRead);
+        client()->didReceiveBuffer(this, SharedBuffer::create(reinterpret_cast<const uint8_t*>(data), bytesRead), bytesRead);
 }
 
-void BlobResourceHandle::notifyFail(int errorCode)
+void BlobResourceHandle::notifyFail(Error errorCode)
 {
     if (client())
-        client()->didFail(this, ResourceError(webKitBlobResourceDomain, errorCode, firstRequest().url(), String()));
+        client()->didFail(this, ResourceError(webKitBlobResourceDomain, static_cast<int>(errorCode), firstRequest().url(), String()));
 }
 
-static void doNotifyFinish(void* context)
+static void doNotifyFinish(BlobResourceHandle& handle)
 {
-    BlobResourceHandle* handle = static_cast<BlobResourceHandle*>(context);
-    if (!handle->aborted() && handle->client())
-        handle->client()->didFinishLoading(handle, 0);
+    if (handle.aborted())
+        return;
+
+    if (!handle.client())
+        return;
 
-    // Balance the ref() in BlobResourceHandle::notfyFinish().
-    handle->deref();
+    handle.client()->didFinishLoading(&handle);
 }
 
 void BlobResourceHandle::notifyFinish()
 {
-    // Balanced in doNotifyFinish().
-    ref();
-
-    if (m_async) {
-        // Schedule to notify the client from a standalone function because the client might dispose the handle immediately from the callback function
-        // while we still have BlobResourceHandle calls in the stack.
-        callOnMainThread(doNotifyFinish, this);
+    if (!m_async) {
+        doNotifyFinish(*this);
         return;
     }
 
-    doNotifyFinish(this);
+    // Schedule to notify the client from a standalone function because the client might dispose the handle immediately from the callback function
+    // while we still have BlobResourceHandle calls in the stack.
+    callOnMainThread([protectedThis = makeRef(*this)]() mutable {
+        doNotifyFinish(protectedThis);
+    });
+
 }
 
 } // namespace WebCore
-
-#endif // ENABLE(BLOB)