diff options
author | Steve Block <steveblock@google.com> | 2010-08-27 11:02:25 +0100 |
---|---|---|
committer | Steve Block <steveblock@google.com> | 2010-09-02 17:17:20 +0100 |
commit | e8b154fd68f9b33be40a3590e58347f353835f5c (patch) | |
tree | 0733ce26384183245aaa5656af26c653636fe6c1 /WebCore/platform/network | |
parent | da56157816334089526a7a115a85fd85a6e9a1dc (diff) | |
download | external_webkit-e8b154fd68f9b33be40a3590e58347f353835f5c.zip external_webkit-e8b154fd68f9b33be40a3590e58347f353835f5c.tar.gz external_webkit-e8b154fd68f9b33be40a3590e58347f353835f5c.tar.bz2 |
Merge WebKit at r66079 : Initial merge by git
Change-Id: Ie2e1440fb9d487d24e52c247342c076fecaecac7
Diffstat (limited to 'WebCore/platform/network')
19 files changed, 1042 insertions, 131 deletions
diff --git a/WebCore/platform/network/BlobData.cpp b/WebCore/platform/network/BlobData.cpp index bb256d0..21e8917 100644 --- a/WebCore/platform/network/BlobData.cpp +++ b/WebCore/platform/network/BlobData.cpp @@ -64,6 +64,11 @@ void BlobData::appendData(const CString& data) m_items.append(BlobDataItem(data)); } +void BlobData::appendData(const CString& data, long long offset, long long length) +{ + m_items.append(BlobDataItem(data, offset, length)); +} + void BlobData::appendFile(const String& path) { m_items.append(BlobDataItem(path)); diff --git a/WebCore/platform/network/BlobData.h b/WebCore/platform/network/BlobData.h index 17cdfdd..13e3b9c 100644 --- a/WebCore/platform/network/BlobData.h +++ b/WebCore/platform/network/BlobData.h @@ -45,14 +45,15 @@ struct BlobDataItem { // Default constructor. BlobDataItem() - : offset(0) + : type(Data) + , offset(0) , length(toEndOfFile) , expectedModificationTime(doNotCheckFileChange) { } - // Constructor for String type. - BlobDataItem(const CString& data) + // Constructor for String type (complete string). + explicit BlobDataItem(const CString& data) : type(Data) , data(data) , offset(0) @@ -61,8 +62,18 @@ struct BlobDataItem { { } + // Constructor for String type (partial string). + BlobDataItem(const CString& data, long long offset, long long length) + : type(Data) + , data(data) + , offset(offset) + , length(length) + , expectedModificationTime(doNotCheckFileChange) + { + } + // Constructor for File type (complete file). - BlobDataItem(const String& path) + explicit BlobDataItem(const String& path) : type(File) , path(path) , offset(0) @@ -105,11 +116,8 @@ struct BlobDataItem { // For Blob type. KURL url; - // For File and Blob type. long long offset; long long length; - - // For File type only. double expectedModificationTime; }; @@ -140,8 +148,14 @@ public: void appendBlob(const KURL&, long long offset, long long length); private: + friend class BlobRegistryImpl; + friend class BlobStorageData; + BlobData() { } + // This is only exposed to BlobStorageData. + void appendData(const CString&, long long offset, long long length); + String m_contentType; String m_contentDisposition; BlobDataItemList m_items; diff --git a/WebCore/platform/network/BlobRegistry.h b/WebCore/platform/network/BlobRegistry.h index fcd7b1c..7e64233 100644 --- a/WebCore/platform/network/BlobRegistry.h +++ b/WebCore/platform/network/BlobRegistry.h @@ -38,6 +38,7 @@ namespace WebCore { class BlobData; +class BlobRegistry; class KURL; class ResourceError; class ResourceHandle; @@ -45,17 +46,23 @@ class ResourceHandleClient; class ResourceRequest; class ResourceResponse; +// Returns a single instance of BlobRegistry. +BlobRegistry& blobRegistry(); + // BlobRegistry is not thread-safe. It should only be called from main thread. class BlobRegistry { public: - static BlobRegistry& instance(); - + // Registers a blob URL referring to the specified blob data. virtual void registerBlobURL(const KURL&, PassOwnPtr<BlobData>) = 0; + + // Registers a blob URL referring to the blob data identified by the specified srcURL. virtual void registerBlobURL(const KURL&, const KURL& srcURL) = 0; + virtual void unregisterBlobURL(const KURL&) = 0; virtual PassRefPtr<ResourceHandle> createResourceHandle(const ResourceRequest&, ResourceHandleClient*) = 0; virtual bool loadResourceSynchronously(const ResourceRequest&, ResourceError&, ResourceResponse&, Vector<char>& data) = 0; +protected: virtual ~BlobRegistry() { } }; diff --git a/WebCore/platform/network/BlobRegistryImpl.cpp b/WebCore/platform/network/BlobRegistryImpl.cpp index bbbb8f0..ee872e6 100644 --- a/WebCore/platform/network/BlobRegistryImpl.cpp +++ b/WebCore/platform/network/BlobRegistryImpl.cpp @@ -32,9 +32,6 @@ #include "BlobRegistryImpl.h" -#include "FileStream.h" -#include "FileStreamProxy.h" -#include "FileSystem.h" #include "ResourceError.h" #include "ResourceHandle.h" #include "ResourceLoader.h" @@ -45,6 +42,15 @@ namespace WebCore { +#if !PLATFORM(CHROMIUM) +BlobRegistry& blobRegistry() +{ + ASSERT(isMainThread()); + DEFINE_STATIC_LOCAL(BlobRegistryImpl, instance, ()); + return instance; +} +#endif + bool BlobRegistryImpl::shouldLoadResource(const ResourceRequest& request) const { // If the resource is not fetched using the GET method, bail out. @@ -72,30 +78,23 @@ bool BlobRegistryImpl::loadResourceSynchronously(const ResourceRequest& request, return false; } -BlobRegistry& BlobRegistry::instance() -{ - ASSERT(isMainThread()); - DEFINE_STATIC_LOCAL(BlobRegistryImpl, instance, ()); - return instance; -} - -void BlobRegistryImpl::appendStorageItems(BlobStorageData* blobStorageData, const BlobStorageDataItemList& items) +void BlobRegistryImpl::appendStorageItems(BlobStorageData* blobStorageData, const BlobDataItemList& items) { - for (BlobStorageDataItemList::const_iterator iter = items.begin(); iter != items.end(); ++iter) { - if (iter->type == BlobStorageDataItem::Data) - blobStorageData->appendData(iter->data, iter->offset, iter->length); + for (BlobDataItemList::const_iterator iter = items.begin(); iter != items.end(); ++iter) { + if (iter->type == BlobDataItem::Data) + blobStorageData->m_data.appendData(iter->data, iter->offset, iter->length); else { - ASSERT(iter->type == BlobStorageDataItem::File); - blobStorageData->appendFile(iter->path, iter->offset, iter->length, iter->expectedModificationTime); + ASSERT(iter->type == BlobDataItem::File); + blobStorageData->m_data.appendFile(iter->path, iter->offset, iter->length, iter->expectedModificationTime); } } } -void BlobRegistryImpl::appendStorageItems(BlobStorageData* blobStorageData, const BlobStorageDataItemList& items, long long offset, long long length) +void BlobRegistryImpl::appendStorageItems(BlobStorageData* blobStorageData, const BlobDataItemList& items, long long offset, long long length) { ASSERT(length != BlobDataItem::toEndOfFile); - BlobStorageDataItemList::const_iterator iter = items.begin(); + BlobDataItemList::const_iterator iter = items.begin(); if (offset) { for (; iter != items.end(); ++iter) { if (offset >= iter->length) @@ -108,12 +107,13 @@ void BlobRegistryImpl::appendStorageItems(BlobStorageData* blobStorageData, cons for (; iter != items.end() && length > 0; ++iter) { long long currentLength = iter->length - offset; long long newLength = currentLength > length ? length : currentLength; - if (iter->type == BlobStorageDataItem::Data) - blobStorageData->appendData(iter->data, iter->offset + offset, newLength); + if (iter->type == BlobDataItem::Data) + blobStorageData->m_data.appendData(iter->data, iter->offset + offset, newLength); else { - ASSERT(iter->type == BlobStorageDataItem::File); - blobStorageData->appendFile(iter->path, iter->offset + offset, newLength, iter->expectedModificationTime); + ASSERT(iter->type == BlobDataItem::File); + blobStorageData->m_data.appendFile(iter->path, iter->offset + offset, newLength, iter->expectedModificationTime); } + length -= newLength; offset = 0; } } @@ -122,17 +122,20 @@ void BlobRegistryImpl::registerBlobURL(const KURL& url, PassOwnPtr<BlobData> blo { ASSERT(isMainThread()); - RefPtr<BlobStorageData> blobStorageData = BlobStorageData::create(); - blobStorageData->setContentType(blobData->contentType()); - blobStorageData->setContentDisposition(blobData->contentDisposition()); + RefPtr<BlobStorageData> blobStorageData = BlobStorageData::create(blobData->contentType(), blobData->contentDisposition()); + + // The blob data is stored in the "canonical" way. That is, it only contains a list of Data and File items. + // 1) The Data item is denoted by the raw data and the range. + // 2) The File item is denoted by the file path, the range and the expected modification time. + // All the Blob items in the passing blob data are resolved and expanded into a set of Data and File items. for (BlobDataItemList::const_iterator iter = blobData->items().begin(); iter != blobData->items().end(); ++iter) { switch (iter->type) { case BlobDataItem::Data: - blobStorageData->appendData(iter->data, 0, iter->data.length()); + blobStorageData->m_data.appendData(iter->data, 0, iter->data.length()); break; case BlobDataItem::File: - blobStorageData->appendFile(iter->path, iter->offset, iter->length, iter->expectedModificationTime); + blobStorageData->m_data.appendFile(iter->path, iter->offset, iter->length, iter->expectedModificationTime); break; case BlobDataItem::Blob: if (m_blobs.contains(iter->url.string())) @@ -141,7 +144,6 @@ void BlobRegistryImpl::registerBlobURL(const KURL& url, PassOwnPtr<BlobData> blo } } - m_blobs.set(url.string(), blobStorageData); } @@ -154,9 +156,7 @@ void BlobRegistryImpl::registerBlobURL(const KURL& url, const KURL& srcURL) if (!src) return; - RefPtr<BlobStorageData> blobStorageData = BlobStorageData::create(); - blobStorageData->setContentType(src->contentType()); - blobStorageData->setContentDisposition(src->contentDisposition()); + RefPtr<BlobStorageData> blobStorageData = BlobStorageData::create(src->contentType(), src->contentDisposition()); appendStorageItems(blobStorageData.get(), src->items()); m_blobs.set(url.string(), blobStorageData); diff --git a/WebCore/platform/network/BlobRegistryImpl.h b/WebCore/platform/network/BlobRegistryImpl.h index 42693bc..f616664 100644 --- a/WebCore/platform/network/BlobRegistryImpl.h +++ b/WebCore/platform/network/BlobRegistryImpl.h @@ -63,8 +63,8 @@ public: private: bool shouldLoadResource(const ResourceRequest& request) const; - void appendStorageItems(BlobStorageData*, const BlobStorageDataItemList&); - void appendStorageItems(BlobStorageData*, const BlobStorageDataItemList&, long long offset, long long length); + void appendStorageItems(BlobStorageData*, const BlobDataItemList&); + void appendStorageItems(BlobStorageData*, const BlobDataItemList&, long long offset, long long length); HashMap<String, RefPtr<BlobStorageData> > m_blobs; }; diff --git a/WebCore/platform/network/BlobResourceHandle.cpp b/WebCore/platform/network/BlobResourceHandle.cpp new file mode 100644 index 0000000..63335f6 --- /dev/null +++ b/WebCore/platform/network/BlobResourceHandle.cpp @@ -0,0 +1,589 @@ +/* + * Copyright (C) 2010 Google 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER OR 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" + +#if ENABLE(BLOB) + +#include "BlobResourceHandle.h" + +#include "AsyncFileStream.h" +#include "BlobRegistryImpl.h" +#include "FileStream.h" +#include "FileSystem.h" +#include "HTTPParsers.h" +#include "KURL.h" +#include "ResourceError.h" +#include "ResourceLoader.h" +#include "ResourceRequest.h" +#include "ResourceResponse.h" + +namespace WebCore { + +static const unsigned bufferSize = 1024; +static const int maxVectorLength = 0x7fffffff; +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 int notFoundError = 1; +static const int securityError = 2; +static const int rangeError = 3; +static const int notReadableError = 4; + +/////////////////////////////////////////////////////////////////////////////// +// BlobResourceSynchronousLoader + +namespace { + +class BlobResourceSynchronousLoader : public ResourceHandleClient { +public: + BlobResourceSynchronousLoader(ResourceError&, ResourceResponse&, Vector<char>&); + + virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&); + virtual void didReceiveData(ResourceHandle*, const char*, int, int /*lengthReceived*/); + virtual void didFinishLoading(ResourceHandle*); + virtual void didFail(ResourceHandle*, const ResourceError&); + +private: + ResourceError& m_error; + ResourceResponse& m_response; + Vector<char>& m_data; +}; + +BlobResourceSynchronousLoader::BlobResourceSynchronousLoader(ResourceError& error, ResourceResponse& response, Vector<char>& data) + : m_error(error) + , m_response(response) + , m_data(data) +{ +} + +void BlobResourceSynchronousLoader::didReceiveResponse(ResourceHandle* handle, const ResourceResponse& response) +{ + // We cannot handle the size that is more than maximum integer. + const int intMaxForLength = 0x7fffffff; + if (response.expectedContentLength() > intMaxForLength) { + m_error = ResourceError(String(), notReadableError, response.url(), String()); + return; + } + + m_response = response; + + // 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*) +{ +} + +void BlobResourceSynchronousLoader::didFail(ResourceHandle*, const ResourceError& error) +{ + m_error = error; +} + +} + +/////////////////////////////////////////////////////////////////////////////// +// BlobResourceHandle + +// static +void BlobResourceHandle::loadResourceSynchronously(PassRefPtr<BlobStorageData> blobData, const ResourceRequest& request, ResourceError& error, ResourceResponse& response, Vector<char>& data) +{ + BlobResourceSynchronousLoader loader(error, response, data); + RefPtr<BlobResourceHandle> handle = BlobResourceHandle::create(blobData, request, &loader, false); + handle->start(); +} + +static void delayedStart(void* context) +{ + static_cast<BlobResourceHandle*>(context)->start(); +} + +BlobResourceHandle::BlobResourceHandle(PassRefPtr<BlobStorageData> blobData, const ResourceRequest& request, ResourceHandleClient* client, bool async) + : ResourceHandle(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) +{ + if (m_async) { + m_asyncStream = adoptRef(client->createAsyncFileStream(this)); + callOnMainThread(delayedStart, this); + } else + m_stream = FileStream::create(); +} + +BlobResourceHandle::~BlobResourceHandle() +{ + if (m_async) { + if (m_asyncStream) + m_asyncStream->stop(); + } else { + if (m_stream) + m_stream->stop(); + } +} + +void BlobResourceHandle::cancel() +{ + if (m_async) { + if (m_asyncStream) { + m_asyncStream->stop(); + m_asyncStream = 0; + } + } + + m_aborted = true; +} + +void BlobResourceHandle::start() +{ + // Do not continue if the request is aborted or an error occurs. + if (m_aborted || m_errorCode) + return; + + // If the blob data is not found, fail now. + if (!m_blobData) { + m_errorCode = notFoundError; + notifyResponse(); + return; + } + + // Parse the "Range" header we care about. + String range = firstRequest().httpHeaderField("Range"); + if (!range.isEmpty() && !parseRange(range, m_rangeOffset, m_rangeEnd, m_rangeSuffixLength)) { + m_errorCode = rangeError; + notifyResponse(); + return; + } + + if (m_async) + getSizeForNext(); + else { + for (size_t i = 0; i < m_blobData->items().size() && !m_aborted && !m_errorCode; ++i) + getSizeForNext(); + notifyResponse(); + } +} + +void BlobResourceHandle::getSizeForNext() +{ + // 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) { + 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); + break; + case BlobDataItem::File: + if (m_async) + m_asyncStream->getSize(item.path, item.expectedModificationTime); + else + didGetSize(m_stream->getSize(item.path, item.expectedModificationTime)); + break; + default: + ASSERT_NOT_REACHED(); + } +} + +void BlobResourceHandle::didGetSize(long long size) +{ + // Do not continue if the request is aborted or an error occurs. + if (m_aborted || m_errorCode) + return; + + // If the size is -1, it means the file has been moved or changed. Fail now. + if (size == -1) { + m_errorCode = notFoundError; + notifyResponse(); + 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; + + // Cache the size. + m_itemLengthList.append(size); + + // Count the size. + m_totalRemainingSize += size; + m_sizeItemCount++; + + // Continue with the next item. + getSizeForNext(); +} + +void BlobResourceHandle::seek() +{ + // Convert from the suffix length to the range. + if (m_rangeSuffixLength != positionNotSpecified) { + 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) + return; + + // Skip the initial items that are not in the range. + long long offset = m_rangeOffset; + for (m_readItemCount = 0; m_readItemCount < m_blobData->items().size() && offset >= m_itemLengthList[m_readItemCount]; ++m_readItemCount) + offset -= m_itemLengthList[m_readItemCount]; + + // Set the offset that need to jump to for the first item in the range. + m_currentItemReadSize = offset; + + // Adjust the total remaining size in order not to go beyond the range. + if (m_rangeEnd != positionNotSpecified) { + long long rangeSize = m_rangeEnd - m_rangeOffset + 1; + if (m_totalRemainingSize > rangeSize) + m_totalRemainingSize = rangeSize; + } else + m_totalRemainingSize -= m_rangeOffset; +} + +int BlobResourceHandle::readSync(char* buf, int length) +{ + ASSERT(!m_async); + + 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) + 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) + bytesRead = readDataSync(item, buf + offset, remaining); + else if (item.type == BlobDataItem::File) + bytesRead = readFileSync(item, buf + offset, remaining); + else + ASSERT_NOT_REACHED(); + + if (bytesRead > 0) { + offset += bytesRead; + remaining -= bytesRead; + } + } + + int result; + if (m_aborted || m_errorCode) + result = -1; + else + result = length - remaining; + + notifyReceiveData(buf, result); + if (!result) + notifyFinish(); + + return result; +} + +int BlobResourceHandle::readDataSync(const BlobDataItem& item, char* buf, int length) +{ + ASSERT(!m_async); + + 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); + m_totalRemainingSize -= bytesToRead; + + m_currentItemReadSize += bytesToRead; + if (m_currentItemReadSize == item.length) { + m_readItemCount++; + m_currentItemReadSize = 0; + } + + return bytesToRead; +} + +int BlobResourceHandle::readFileSync(const BlobDataItem& item, char* buf, int length) +{ + 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); + m_currentItemReadSize = 0; + if (!success) { + m_errorCode = notReadableError; + return 0; + } + + m_fileOpened = true; + } + + int bytesRead = m_stream->read(buf, length); + if (bytesRead < 0) { + m_errorCode = notReadableError; + return 0; + } + if (!bytesRead) { + m_stream->close(); + m_fileOpened = false; + m_readItemCount++; + } else + m_totalRemainingSize -= bytesRead; + + return bytesRead; +} + +void BlobResourceHandle::readAsync() +{ + ASSERT(m_async); + + // Do not continue if the request is aborted or an error occurs. + if (m_aborted || m_errorCode) + return; + + // If there is no more remaining data to read, we are done. + if (!m_totalRemainingSize || m_readItemCount >= m_blobData->items().size()) { + notifyFinish(); + return; + } + + const BlobDataItem& item = m_blobData->items().at(m_readItemCount); + if (item.type == BlobDataItem::Data) + readDataAsync(item); + else if (item.type == BlobDataItem::File) + readFileAsync(item); + else + ASSERT_NOT_REACHED(); +} + +void BlobResourceHandle::readDataAsync(const BlobDataItem& item) +{ + ASSERT(m_async); + + 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)); + m_currentItemReadSize = 0; +} + +void BlobResourceHandle::readFileAsync(const BlobDataItem& item) +{ + ASSERT(m_async); + + if (m_fileOpened) { + m_asyncStream->read(m_buffer.data(), m_buffer.size()); + return; + } + + 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_fileOpened = true; + m_currentItemReadSize = 0; +} + +void BlobResourceHandle::didOpen(bool success) +{ + ASSERT(m_async); + + if (!success) { + failed(notReadableError); + return; + } + + // Continue the reading. + readAsync(); +} + +void BlobResourceHandle::didRead(int bytesRead) +{ + consumeData(m_buffer.data(), bytesRead); +} + +void BlobResourceHandle::consumeData(const char* data, int bytesRead) +{ + ASSERT(m_async); + + m_totalRemainingSize -= bytesRead; + + // Notify the client. + if (bytesRead) + notifyReceiveData(data, bytesRead); + + if (m_fileOpened) { + // When the current item is a file item, the reading is completed only if bytesRead is 0. + if (!bytesRead) { + // Close the file. + m_fileOpened = false; + m_asyncStream->close(); + + // Move to the next item. + m_readItemCount++; + } + } else { + // Otherwise, we read the current text item as a whole and move to the next item. + m_readItemCount++; + } + + // Continue the reading. + readAsync(); +} + +void BlobResourceHandle::failed(int errorCode) +{ + ASSERT(m_async); + + // Notify the client. + notifyFail(errorCode); + + // Close the file if needed. + if (m_fileOpened) { + m_fileOpened = false; + m_asyncStream->close(); + } +} + +void BlobResourceHandle::notifyResponse() +{ + if (!client()) + return; + + if (m_errorCode) { + notifyResponseOnError(); + notifyFinish(); + } else + notifyResponseOnSuccess(); +} + +void BlobResourceHandle::notifyResponseOnSuccess() +{ + bool isRangeRequest = m_rangeOffset != positionNotSpecified; + ResourceResponse response(firstRequest().url(), m_blobData->contentType(), m_totalRemainingSize, String(), String()); + response.setExpectedContentLength(m_totalRemainingSize); + response.setHTTPStatusCode(isRangeRequest ? httpPartialContent : httpOK); + response.setHTTPStatusText(isRangeRequest ? httpPartialContentText : httpOKText); + if (!m_blobData->contentDisposition().isEmpty()) + response.setHTTPHeaderField("Content-Disposition", m_blobData->contentDisposition()); + client()->didReceiveResponse(this, response); +} + +void BlobResourceHandle::notifyResponseOnError() +{ + ASSERT(m_errorCode); + + ResourceResponse response(firstRequest().url(), String(), 0, String(), String()); + switch (m_errorCode) { + case rangeError: + response.setHTTPStatusCode(httpRequestedRangeNotSatisfiable); + response.setHTTPStatusText(httpRequestedRangeNotSatisfiableText); + break; + case notFoundError: + response.setHTTPStatusCode(httpNotFound); + response.setHTTPStatusText(httpNotFoundText); + break; + case securityError: + response.setHTTPStatusCode(httpNotAllowed); + response.setHTTPStatusText(httpNotAllowedText); + break; + default: + response.setHTTPStatusCode(httpInternalError); + response.setHTTPStatusText(httpInternalErrorText); + break; + } + client()->didReceiveResponse(this, response); +} + +void BlobResourceHandle::notifyReceiveData(const char* data, int bytesRead) +{ + if (client()) + client()->didReceiveData(this, data, bytesRead, bytesRead); +} + +void BlobResourceHandle::notifyFail(int errorCode) +{ + if (client()) + client()->didFail(this, ResourceError(String(), errorCode, firstRequest().url(), String())); +} + +void BlobResourceHandle::notifyFinish() +{ + if (client()) + client()->didFinishLoading(this); +} + +} // namespace WebCore + +#endif // ENABLE(BLOB) + diff --git a/WebCore/platform/network/BlobResourceHandle.h b/WebCore/platform/network/BlobResourceHandle.h new file mode 100644 index 0000000..b2a0854 --- /dev/null +++ b/WebCore/platform/network/BlobResourceHandle.h @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2010 Google 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER OR 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. + */ + +#ifndef BlobResourceHandle_h +#define BlobResourceHandle_h + +#if ENABLE(BLOB) + +#include "FileStreamClient.h" +#include "PlatformString.h" +#include "ResourceHandle.h" +#include <wtf/PassRefPtr.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class AsyncFileStream; +class BlobDataItem; +class BlobStorageData; +class FileStream; +class ResourceHandleClient; +class ResourceRequest; + +class BlobResourceHandle : public FileStreamClient, public ResourceHandle { +public: + static PassRefPtr<BlobResourceHandle> create(PassRefPtr<BlobStorageData> blobData, const ResourceRequest& request, ResourceHandleClient* client, bool async = true) + { + return adoptRef(new BlobResourceHandle(blobData, request, client, async)); + } + + static void loadResourceSynchronously(PassRefPtr<BlobStorageData> blobData, const ResourceRequest& request, ResourceError& error, ResourceResponse& response, Vector<char>& data); + + // FileStreamClient methods. + virtual void didGetSize(long long); + virtual void didOpen(bool); + virtual void didRead(int); + + // ResourceHandle methods. + virtual void cancel(); + + void start(); + int readSync(char*, int); + +private: + BlobResourceHandle(PassRefPtr<BlobStorageData>, const ResourceRequest&, ResourceHandleClient*, bool async); + virtual ~BlobResourceHandle(); + + void getSizeForNext(); + void seek(); + void consumeData(const char* data, int bytesRead); + void failed(int errorCode); + + void readAsync(); + void readDataAsync(const BlobDataItem&); + void readFileAsync(const BlobDataItem&); + + int readDataSync(const BlobDataItem&, char*, int); + int readFileSync(const BlobDataItem&, char*, int); + + void notifyResponse(); + void notifyResponseOnSuccess(); + void notifyResponseOnError(); + void notifyReceiveData(const char*, int); + void notifyFail(int errorCode); + void notifyFinish(); + + RefPtr<BlobStorageData> m_blobData; + bool m_async; + RefPtr<AsyncFileStream> m_asyncStream; // For asynchronous loading. + RefPtr<FileStream> m_stream; // For synchronous loading. + Vector<char> m_buffer; + Vector<long long> m_itemLengthList; + int m_errorCode; + bool m_aborted; + long long m_rangeOffset; + long long m_rangeEnd; + long long m_rangeSuffixLength; + long long m_totalRemainingSize; + long long m_currentItemReadSize; + unsigned m_sizeItemCount; + unsigned m_readItemCount; + bool m_fileOpened; +}; + +} // namespace WebCore + +#endif // ENABLE(BLOB) + +#endif // BlobResourceHandle_h diff --git a/WebCore/platform/network/BlobStorageData.h b/WebCore/platform/network/BlobStorageData.h index f4125a4..6535e62 100644 --- a/WebCore/platform/network/BlobStorageData.h +++ b/WebCore/platform/network/BlobStorageData.h @@ -31,76 +31,33 @@ #ifndef BlobStorageData_h #define BlobStorageData_h -#include "PlatformString.h" +#include "BlobData.h" #include <wtf/PassRefPtr.h> -#include <wtf/Vector.h> -#include <wtf/text/CString.h> +#include <wtf/RefCounted.h> namespace WebCore { -struct BlobStorageDataItem { - enum BlobStoreDataItemType { Data, File }; - BlobStoreDataItemType type; - long long offset; - long long length; - - // For string data. - CString data; - - // For file data. - String path; - double expectedModificationTime; - - BlobStorageDataItem(const CString& data, long long offset, long long length) - : type(Data) - , offset(offset) - , length(length) - , data(data) - , expectedModificationTime(0) - { - } - - BlobStorageDataItem(const String& path, long long offset, long long length, double expectedModificationTime) - : type(File) - , offset(offset) - , length(length) - , path(path) - , expectedModificationTime(expectedModificationTime) - { - } -}; - -typedef Vector<BlobStorageDataItem> BlobStorageDataItemList; - class BlobStorageData : public RefCounted<BlobStorageData> { public: - static PassRefPtr<BlobStorageData> create() + static PassRefPtr<BlobStorageData> create(const String& contentType, const String& contentDisposition) { - return adoptRef(new BlobStorageData()); + return adoptRef(new BlobStorageData(contentType, contentDisposition)); } - const String& contentType() const { return m_contentType; } - void setContentType(const String& contentType) { m_contentType = contentType; } - - const String& contentDisposition() const { return m_contentDisposition; } - void setContentDisposition(const String& contentDisposition) { m_contentDisposition = contentDisposition; } - - const BlobStorageDataItemList& items() const { return m_items; } + const String& contentType() const { return m_data.contentType(); } + const String& contentDisposition() const { return m_data.contentDisposition(); } + const BlobDataItemList& items() const { return m_data.items(); } - void appendData(const CString& data, long long offset, long long length) - { - m_items.append(BlobStorageDataItem(data, offset, length)); - } +private: + friend class BlobRegistryImpl; - void appendFile(const String& path, long long offset, long long length, double expectedModificationTime) + BlobStorageData(const String& contentType, const String& contentDisposition) { - m_items.append(BlobStorageDataItem(path, offset, length, expectedModificationTime)); + m_data.setContentType(contentType); + m_data.setContentDisposition(contentDisposition); } -private: - String m_contentType; - String m_contentDisposition; - BlobStorageDataItemList m_items; + BlobData m_data; }; } // namespace WebCore diff --git a/WebCore/platform/network/CredentialStorage.cpp b/WebCore/platform/network/CredentialStorage.cpp index 4fb7799..38f71a4 100644 --- a/WebCore/platform/network/CredentialStorage.cpp +++ b/WebCore/platform/network/CredentialStorage.cpp @@ -79,7 +79,6 @@ static String protectionSpaceMapKeyFromURL(const KURL& url) ASSERT(index != notFound); directoryURL = directoryURL.substring(0, (index != directoryURLPathStart) ? index : directoryURLPathStart + 1); } - ASSERT(directoryURL.length() == directoryURLPathStart + 1 || directoryURL[directoryURL.length() - 1] != '/'); return directoryURL; } diff --git a/WebCore/platform/network/FormData.cpp b/WebCore/platform/network/FormData.cpp index 31506ea..4f2b365 100644 --- a/WebCore/platform/network/FormData.cpp +++ b/WebCore/platform/network/FormData.cpp @@ -139,6 +139,11 @@ PassRefPtr<FormData> FormData::deepCopy() const formData->m_elements.append(FormDataElement(e.m_filename, e.m_shouldGenerateFile)); #endif break; +#if ENABLE(BLOB) + case FormDataElement::encodedBlob: + formData->m_elements.append(FormDataElement(e.m_blobURL)); + break; +#endif } } return formData.release(); @@ -200,6 +205,11 @@ void FormData::appendFileRange(const String& filename, long long start, long lon { m_elements.append(FormDataElement(filename, start, length, expectedModificationTime, shouldGenerateFile)); } + +void FormData::appendBlob(const KURL& blobURL) +{ + m_elements.append(FormDataElement(blobURL)); +} #endif void FormData::appendKeyValuePairItems(const BlobItemList& items, const TextEncoding& encoding, bool isMultiPartForm, Document* document) diff --git a/WebCore/platform/network/FormData.h b/WebCore/platform/network/FormData.h index a1964e3..d7faa89 100644 --- a/WebCore/platform/network/FormData.h +++ b/WebCore/platform/network/FormData.h @@ -20,6 +20,7 @@ #ifndef FormData_h #define FormData_h +#include "KURL.h" #include "PlatformString.h" #include <wtf/Forward.h> #include <wtf/RefCounted.h> @@ -39,17 +40,25 @@ public: #if ENABLE(BLOB) FormDataElement(const String& filename, long long fileStart, long long fileLength, double expectedFileModificationTime, bool shouldGenerateFile) : m_type(encodedFile), m_filename(filename), m_fileStart(fileStart), m_fileLength(fileLength), m_expectedFileModificationTime(expectedFileModificationTime), m_shouldGenerateFile(shouldGenerateFile) { } + FormDataElement(const KURL& blobURL) : m_type(encodedBlob), m_blobURL(blobURL) { } #else FormDataElement(const String& filename, bool shouldGenerateFile) : m_type(encodedFile), m_filename(filename), m_shouldGenerateFile(shouldGenerateFile) { } #endif - enum { data, encodedFile } m_type; + enum { + data, + encodedFile +#if ENABLE(BLOB) + , encodedBlob +#endif + } m_type; Vector<char> m_data; String m_filename; #if ENABLE(BLOB) long long m_fileStart; long long m_fileLength; double m_expectedFileModificationTime; + KURL m_blobURL; #endif String m_generatedFilename; bool m_shouldGenerateFile; @@ -67,14 +76,16 @@ inline bool operator==(const FormDataElement& a, const FormDataElement& b) if (a.m_type != b.m_type) return false; - if (a.m_data != b.m_data) - return false; + if (a.m_type == FormDataElement::data) + return a.m_data == b.m_data; + if (a.m_type == FormDataElement::encodedFile) #if ENABLE(BLOB) - if (a.m_filename != b.m_filename || a.m_fileStart != b.m_fileStart || a.m_fileLength != b.m_fileLength || a.m_expectedFileModificationTime != b.m_expectedFileModificationTime) + return a.m_filename == b.m_filename && a.m_fileStart == b.m_fileStart && a.m_fileLength == b.m_fileLength && a.m_expectedFileModificationTime == b.m_expectedFileModificationTime; + if (a.m_type == FormDataElement::encodedBlob) + return a.m_blobURL == b.m_blobURL; #else - if (a.m_filename != b.m_filename) + return a.m_filename == b.m_filename; #endif - return false; return true; } @@ -101,6 +112,7 @@ public: void appendFile(const String& filename, bool shouldGenerateFile = false); #if ENABLE(BLOB) void appendFileRange(const String& filename, long long start, long long length, double expectedModificationTime, bool shouldGenerateFile = false); + void appendBlob(const KURL& blobURL); #endif void flatten(Vector<char>&) const; // omits files diff --git a/WebCore/platform/network/HTTPParsers.cpp b/WebCore/platform/network/HTTPParsers.cpp index b3f3d45..a1ba9d3 100644 --- a/WebCore/platform/network/HTTPParsers.cpp +++ b/WebCore/platform/network/HTTPParsers.cpp @@ -315,4 +315,61 @@ String extractReasonPhraseFromHTTPStatusLine(const String& statusLine) return statusLine.substring(spacePos + 1); } +bool parseRange(const String& range, long long& rangeOffset, long long& rangeEnd, long long& rangeSuffixLength) +{ + // The format of "Range" header is defined in RFC 2616 Section 14.35.1. + // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35.1 + // We don't support multiple range requests. + + rangeOffset = rangeEnd = rangeSuffixLength = -1; + + // The "bytes" unit identifier should be present. + static const char bytesStart[] = "bytes="; + if (!range.startsWith(bytesStart, false)) + return false; + String byteRange = range.substring(sizeof(bytesStart) - 1); + + // The '-' character needs to be present. + int index = byteRange.find('-'); + if (index == -1) + return false; + + // If the '-' character is at the beginning, the suffix length, which specifies the last N bytes, is provided. + // Example: + // -500 + if (!index) { + String suffixLengthString = byteRange.substring(index + 1).stripWhiteSpace(); + bool ok; + long long value = suffixLengthString.toInt64Strict(&ok); + if (ok) + rangeSuffixLength = value; + return true; + } + + // Otherwise, the first-byte-position and the last-byte-position are provied. + // Examples: + // 0-499 + // 500- + String firstBytePosStr = byteRange.left(index).stripWhiteSpace(); + bool ok; + long long firstBytePos = firstBytePosStr.toInt64Strict(&ok); + if (!ok) + return false; + + String lastBytePosStr = byteRange.substring(index + 1).stripWhiteSpace(); + long long lastBytePos = -1; + if (!lastBytePosStr.isEmpty()) { + lastBytePos = lastBytePosStr.toInt64Strict(&ok); + if (!ok) + return false; + } + + if (firstBytePos < 0 || !(lastBytePos == -1 || lastBytePos >= firstBytePos)) + return false; + + rangeOffset = firstBytePos; + rangeEnd = lastBytePos; + return true; +} + } diff --git a/WebCore/platform/network/HTTPParsers.h b/WebCore/platform/network/HTTPParsers.h index 9d6971b..55b8c7b 100644 --- a/WebCore/platform/network/HTTPParsers.h +++ b/WebCore/platform/network/HTTPParsers.h @@ -59,6 +59,9 @@ void findCharsetInMediaType(const String& mediaType, unsigned int& charsetPos, u XSSProtectionDisposition parseXSSProtectionHeader(const String&); String extractReasonPhraseFromHTTPStatusLine(const String&); +// -1 could be set to one of the return parameters to indicate the value is not specified. +bool parseRange(const String&, long long& rangeOffset, long long& rangeEnd, long long& rangeSuffixLength); + } #endif diff --git a/WebCore/platform/network/ResourceHandle.cpp b/WebCore/platform/network/ResourceHandle.cpp index 0575523..2da1d77 100644 --- a/WebCore/platform/network/ResourceHandle.cpp +++ b/WebCore/platform/network/ResourceHandle.cpp @@ -27,6 +27,7 @@ #include "ResourceHandle.h" #include "ResourceHandleInternal.h" +#include "BlobRegistry.h" #include "DNS.h" #include "Logging.h" #include "ResourceHandleClient.h" @@ -54,6 +55,14 @@ ResourceHandle::ResourceHandle(const ResourceRequest& request, ResourceHandleCli PassRefPtr<ResourceHandle> ResourceHandle::create(const ResourceRequest& request, ResourceHandleClient* client, Frame* frame, bool defersLoading, bool shouldContentSniff) { +#if ENABLE(BLOB) + if (request.url().protocolIs("blob")) { + PassRefPtr<ResourceHandle> handle = blobRegistry().createResourceHandle(request, client); + if (handle) + return handle; + } +#endif + RefPtr<ResourceHandle> newHandle(adoptRef(new ResourceHandle(request, client, defersLoading, shouldContentSniff))); if (newHandle->d->m_scheduledFailureType != NoFailure) diff --git a/WebCore/platform/network/ResourceHandle.h b/WebCore/platform/network/ResourceHandle.h index 3dc38f8..1167715 100644 --- a/WebCore/platform/network/ResourceHandle.h +++ b/WebCore/platform/network/ResourceHandle.h @@ -98,9 +98,10 @@ class ResourceHandle : public RefCounted<ResourceHandle> , public AuthenticationClient #endif { -private: +protected: ResourceHandle(const ResourceRequest&, ResourceHandleClient*, bool defersLoading, bool shouldContentSniff); +private: enum FailureType { NoFailure, BlockedFailure, diff --git a/WebCore/platform/network/ResourceHandleClient.h b/WebCore/platform/network/ResourceHandleClient.h index 0098010..97c0f54 100644 --- a/WebCore/platform/network/ResourceHandleClient.h +++ b/WebCore/platform/network/ResourceHandleClient.h @@ -44,8 +44,10 @@ class NSCachedURLResponse; #endif namespace WebCore { + class AsyncFileStream; class AuthenticationChallenge; class Credential; + class FileStreamClient; class KURL; class ProtectionSpace; class ResourceHandle; @@ -92,6 +94,9 @@ namespace WebCore { #if USE(CFNETWORK) virtual bool shouldCacheResponse(ResourceHandle*, CFCachedURLResponseRef response) { return true; } #endif +#if ENABLE(BLOB) + virtual AsyncFileStream* createAsyncFileStream(FileStreamClient*) { return 0; } +#endif }; } diff --git a/WebCore/platform/network/ResourceHandleInternal.h b/WebCore/platform/network/ResourceHandleInternal.h index 7b6e960..24b00bf 100644 --- a/WebCore/platform/network/ResourceHandleInternal.h +++ b/WebCore/platform/network/ResourceHandleInternal.h @@ -92,7 +92,6 @@ namespace WebCore { , m_connection(0) #endif #if USE(WININET) - , m_fileHandle(INVALID_HANDLE_VALUE) , m_fileLoadTimer(loader, &ResourceHandle::fileLoadTimer) , m_resourceHandle(0) , m_secondaryHandle(0) @@ -170,7 +169,6 @@ namespace WebCore { bool m_needsSiteSpecificQuirks; #endif #if USE(WININET) - HANDLE m_fileHandle; Timer<ResourceHandle> m_fileLoadTimer; HINTERNET m_resourceHandle; HINTERNET m_secondaryHandle; diff --git a/WebCore/platform/network/mac/ResourceHandleMac.mm b/WebCore/platform/network/mac/ResourceHandleMac.mm index f7161ec..d014bb3 100644 --- a/WebCore/platform/network/mac/ResourceHandleMac.mm +++ b/WebCore/platform/network/mac/ResourceHandleMac.mm @@ -29,6 +29,7 @@ #import "AuthenticationChallenge.h" #import "AuthenticationMac.h" #import "Base64.h" +#import "BlobRegistry.h" #import "BlockExceptions.h" #import "CredentialStorage.h" #import "DocLoader.h" @@ -464,6 +465,12 @@ void ResourceHandle::loadResourceSynchronously(const ResourceRequest& request, S { LOG(Network, "ResourceHandle::loadResourceSynchronously:%@ allowStoredCredentials:%u", request.nsURLRequest(), storedCredentials); +#if ENABLE(BLOB) + if (request.url().protocolIs("blob")) + if (blobRegistry().loadResourceSynchronously(request, error, response, data)) + return; +#endif + NSError *nsError = nil; NSURLResponse *nsURLResponse = nil; NSData *result = nil; diff --git a/WebCore/platform/network/win/ResourceHandleWin.cpp b/WebCore/platform/network/win/ResourceHandleWin.cpp index 63c84a9..3dabd91 100644 --- a/WebCore/platform/network/win/ResourceHandleWin.cpp +++ b/WebCore/platform/network/win/ResourceHandleWin.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2004, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2010 Patrick Gansterer <paroga@paroga.com> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -63,6 +64,20 @@ static const ResourceHandleEventHandler messageHandlers[] = { &ResourceHandle::onRequestComplete }; +static String queryHTTPHeader(HINTERNET requestHandle, DWORD infoLevel) +{ + DWORD bufferSize = 0; + HttpQueryInfoW(requestHandle, infoLevel, 0, &bufferSize, 0); + + Vector<UChar> characters(bufferSize / sizeof(UChar)); + + if (!HttpQueryInfoW(requestHandle, infoLevel, characters.data(), &bufferSize, 0)) + return String(); + + characters.removeLast(); // Remove NullTermination. + return String::adopt(characters); +} + static int addToOutstandingJobs(ResourceHandle* job) { if (!jobIdMap) @@ -122,6 +137,49 @@ static void initializeOffScreenResourceHandleWindow() HWND_MESSAGE, 0, WebCore::instanceHandle(), 0); } + +class WebCoreSynchronousLoader : public ResourceHandleClient, public Noncopyable { +public: + WebCoreSynchronousLoader(ResourceError&, ResourceResponse&, Vector<char>&, const String& userAgent); + + virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&); + virtual void didReceiveData(ResourceHandle*, const char*, int, int lengthReceived); + virtual void didFinishLoading(ResourceHandle*); + virtual void didFail(ResourceHandle*, const ResourceError&); + +private: + ResourceError& m_error; + ResourceResponse& m_response; + Vector<char>& m_data; +}; + +WebCoreSynchronousLoader::WebCoreSynchronousLoader(ResourceError& error, ResourceResponse& response, Vector<char>& data, const String& userAgent) + : m_error(error) + , m_response(response) + , m_data(data) +{ +} + +void WebCoreSynchronousLoader::didReceiveResponse(ResourceHandle*, const ResourceResponse& response) +{ + m_response = response; +} + +void WebCoreSynchronousLoader::didReceiveData(ResourceHandle*, const char* data, int length, int) +{ + m_data.append(data, length); +} + +void WebCoreSynchronousLoader::didFinishLoading(ResourceHandle*) +{ +} + +void WebCoreSynchronousLoader::didFail(ResourceHandle*, const ResourceError& error) +{ + m_error = error; +} + + ResourceHandleInternal::~ResourceHandleInternal() { if (m_fileHandle != INVALID_HANDLE_VALUE) @@ -225,7 +283,6 @@ void ResourceHandle::onRequestComplete(LPARAM lParam) } HINTERNET handle = (request().httpMethod() == "POST") ? d->m_secondaryHandle : d->m_resourceHandle; - BOOL ok = FALSE; static const int bufferSize = 32768; char buffer[bufferSize]; @@ -234,11 +291,31 @@ void ResourceHandle::onRequestComplete(LPARAM lParam) buffers.lpvBuffer = buffer; buffers.dwBufferLength = bufferSize; - bool receivedAnyData = false; + BOOL ok = FALSE; while ((ok = InternetReadFileExA(handle, &buffers, IRF_NO_WAIT, (DWORD_PTR)this)) && buffers.dwBufferLength) { if (!hasReceivedResponse()) { setHasReceivedResponse(); ResourceResponse response; + response.setURL(firstRequest().url()); + + String httpStatusText = queryHTTPHeader(d->m_requestHandle, HTTP_QUERY_STATUS_TEXT); + if (!httpStatusText.isNull()) + response.setHTTPStatusText(httpStatusText); + + String httpStatusCode = queryHTTPHeader(d->m_requestHandle, HTTP_QUERY_STATUS_CODE); + if (!httpStatusCode.isNull()) + response.setHTTPStatusCode(httpStatusCode.toInt()); + + String httpContentLength = queryHTTPHeader(d->m_requestHandle, HTTP_QUERY_CONTENT_LENGTH); + if (!httpContentLength.isNull()) + response.setExpectedContentLength(httpContentLength.toInt()); + + String httpContentType = queryHTTPHeader(d->m_requestHandle, HTTP_QUERY_CONTENT_TYPE); + if (!httpContentType.isNull()) { + response.setMimeType(extractMIMETypeFromMediaType(httpContentType)); + response.setTextEncodingName(extractCharsetFromMediaType(httpContentType)); + } + client()->didReceiveResponse(this, response); } client()->didReceiveData(this, buffer, buffers.dwBufferLength, 0); @@ -333,20 +410,6 @@ bool ResourceHandle::start(Frame* frame) { ref(); if (request().url().isLocalFile()) { - String path = request().url().path(); - // windows does not enjoy a leading slash on paths - if (path[0] == '/') - path = path.substring(1); - // FIXME: This is wrong. Need to use wide version of this call. - d->m_fileHandle = CreateFileA(path.utf8().data(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - - // FIXME: perhaps this error should be reported asynchronously for - // consistency. - if (d->m_fileHandle == INVALID_HANDLE_VALUE) { - delete this; - return false; - } - d->m_fileLoadTimer.startOneShot(0.0); return true; } else { @@ -409,9 +472,29 @@ bool ResourceHandle::start(Frame* frame) } } -void ResourceHandle::fileLoadTimer(Timer<ResourceHandle>* timer) +void ResourceHandle::fileLoadTimer(Timer<ResourceHandle>*) { + RefPtr<ResourceHandle> protector(this); + deref(); // balances ref in start + + String fileName = firstRequest().url().fileSystemPath(); + HANDLE fileHandle = CreateFileW(fileName.charactersWithNullTermination(), GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + + if (fileHandle == INVALID_HANDLE_VALUE) { + client()->didFail(this, ResourceError()); + return; + } + ResourceResponse response; + + int dotPos = fileName.reverseFind('.'); + int slashPos = fileName.reverseFind('/'); + + if (slashPos < dotPos && dotPos != -1) { + String ext = fileName.substring(dotPos + 1); + response.setMimeType(MIMETypeRegistry::getMIMETypeForExtension(ext)); + } + client()->didReceiveResponse(this, response); bool result = false; @@ -420,16 +503,13 @@ void ResourceHandle::fileLoadTimer(Timer<ResourceHandle>* timer) do { const int bufferSize = 8192; char buffer[bufferSize]; - result = ReadFile(d->m_fileHandle, &buffer, bufferSize, &bytesRead, NULL); + result = ReadFile(fileHandle, &buffer, bufferSize, &bytesRead, 0); if (result && bytesRead) client()->didReceiveData(this, buffer, bytesRead, 0); - // Check for end of file. + // Check for end of file. } while (result && bytesRead); - // FIXME: handle errors better - - CloseHandle(d->m_fileHandle); - d->m_fileHandle = INVALID_HANDLE_VALUE; + CloseHandle(fileHandle); client()->didFinishLoading(this); } @@ -449,6 +529,16 @@ void ResourceHandle::cancel() client()->didFail(this, ResourceError()); } +void ResourceHandle::loadResourceSynchronously(const ResourceRequest& request, StoredCredentials storedCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& data, Frame* frame) +{ + UNUSED_PARAM(storedCredentials); + + WebCoreSynchronousLoader syncLoader(error, response, data, request.httpUserAgent()); + ResourceHandle handle(request, &syncLoader, true, false); + + handle.start(frame); +} + void ResourceHandle::setHasReceivedResponse(bool b) { d->m_hasReceivedResponse = b; @@ -459,4 +549,36 @@ bool ResourceHandle::hasReceivedResponse() const return d->m_hasReceivedResponse; } +bool ResourceHandle::willLoadFromCache(ResourceRequest&, Frame*) +{ + notImplemented(); + return false; +} + +void prefetchDNS(const String&) +{ + notImplemented(); +} + +PassRefPtr<SharedBuffer> ResourceHandle::bufferedData() +{ + ASSERT_NOT_REACHED(); + return 0; +} + +bool ResourceHandle::supportsBufferedData() +{ + return false; +} + +bool ResourceHandle::loadsBlocked() +{ + return false; +} + +void ResourceHandle::platformSetDefersLoading(bool) +{ + notImplemented(); +} + } // namespace WebCore |