| Index: Source/core/xml/XMLHttpRequest.cpp
|
| diff --git a/Source/core/xml/XMLHttpRequest.cpp b/Source/core/xml/XMLHttpRequest.cpp
|
| index 4b041d4ec7e998a3f5e89b9c8366a8f3dc2c5a54..797318b8c5d62c57fb3b21b4f8de4dbdee325e64 100644
|
| --- a/Source/core/xml/XMLHttpRequest.cpp
|
| +++ b/Source/core/xml/XMLHttpRequest.cpp
|
| @@ -276,31 +276,26 @@ Document* XMLHttpRequest::responseXML(ExceptionState& es)
|
| Blob* XMLHttpRequest::responseBlob()
|
| {
|
| ASSERT(m_responseTypeCode == ResponseTypeBlob);
|
| + ASSERT(!m_binaryResponseBuilder.get());
|
|
|
| // We always return null before DONE.
|
| if (m_error || m_state != DONE)
|
| return 0;
|
|
|
| if (!m_responseBlob) {
|
| - // FIXME: This causes two (or more) unnecessary copies of the data.
|
| - // Chromium stores blob data in the browser process, so we're pulling the data
|
| - // from the network only to copy it into the renderer to copy it back to the browser.
|
| - // Ideally we'd get the blob/file-handle from the ResourceResponse directly
|
| - // instead of copying the bytes. Embedders who store blob data in the
|
| - // same process as WebCore would at least to teach BlobData to take
|
| - // a SharedBuffer, even if they don't get the Blob from the network layer directly.
|
| + // When "blob" is specified for the responseType attribute,
|
| + // we redirect the downloaded data to the file-handle directly
|
| + // in the browser process.
|
| + // We get the file-path from the ResourceResponse directly
|
| + // instead of copying the bytes between the browser and the renderer.
|
| OwnPtr<BlobData> blobData = BlobData::create();
|
| + const String& filePath = m_response.downloadedFilePath();
|
| // If we errored out or got no data, we still return a blob, just an empty one.
|
| - size_t size = 0;
|
| - if (m_binaryResponseBuilder) {
|
| - RefPtr<RawData> rawData = RawData::create();
|
| - size = m_binaryResponseBuilder->size();
|
| - rawData->mutableData()->append(m_binaryResponseBuilder->data(), size);
|
| - blobData->appendData(rawData, 0, BlobDataItem::toEndOfFile);
|
| + if (!filePath.isEmpty() && m_receivedLength) {
|
| + blobData->appendFile(filePath);
|
| blobData->setContentType(responseMIMEType()); // responseMIMEType defaults to text/xml which may be incorrect.
|
| - m_binaryResponseBuilder.clear();
|
| }
|
| - m_responseBlob = Blob::create(blobData.release(), size);
|
| + m_responseBlob = Blob::create(blobData.release(), m_receivedLength);
|
| }
|
|
|
| return m_responseBlob.get();
|
| @@ -752,6 +747,12 @@ void XMLHttpRequest::createRequest(ExceptionState& es)
|
| request.setHTTPMethod(m_method);
|
| request.setTargetType(ResourceRequest::TargetIsXHR);
|
|
|
| + // When "blob" is specified for the responseType attribute,
|
| + // we redirect the downloaded data to the file-handle directly
|
| + // and get the file-path as the result.
|
| + if (responseTypeCode() == ResponseTypeBlob)
|
| + request.setDownloadToFile(true);
|
| +
|
| InspectorInstrumentation::willLoadXHR(scriptExecutionContext(), this, m_method, m_url, m_async, m_requestEntityBody ? m_requestEntityBody->deepCopy() : 0, m_requestHeaders, m_includeCredentials);
|
|
|
| if (m_requestEntityBody) {
|
| @@ -776,6 +777,12 @@ void XMLHttpRequest::createRequest(ExceptionState& es)
|
| options.mixedContentBlockingTreatment = TreatAsActiveContent;
|
| options.timeoutMilliseconds = m_timeoutMilliseconds;
|
|
|
| + // Since we redirect the downloaded data to the file-handle directly
|
| + // when "blob" is specified for the responseType attribute,
|
| + // buffering is not needed.
|
| + if (responseTypeCode() == ResponseTypeBlob)
|
| + options.dataBufferingPolicy = DoNotBufferData;
|
| +
|
| m_exceptionCode = 0;
|
| m_error = false;
|
|
|
| @@ -1176,6 +1183,8 @@ void XMLHttpRequest::didReceiveResponse(unsigned long identifier, const Resource
|
|
|
| void XMLHttpRequest::didReceiveData(const char* data, int len)
|
| {
|
| + ASSERT(m_responseTypeCode != ResponseTypeBlob);
|
| +
|
| if (m_error)
|
| return;
|
|
|
| @@ -1208,7 +1217,7 @@ void XMLHttpRequest::didReceiveData(const char* data, int len)
|
|
|
| if (useDecoder) {
|
| m_responseText = m_responseText.concatenateWith(m_decoder->decode(data, len));
|
| - } else if (m_responseTypeCode == ResponseTypeArrayBuffer || m_responseTypeCode == ResponseTypeBlob) {
|
| + } else if (m_responseTypeCode == ResponseTypeArrayBuffer) {
|
| // Buffer binary data.
|
| if (!m_binaryResponseBuilder)
|
| m_binaryResponseBuilder = SharedBuffer::create();
|
| @@ -1237,6 +1246,38 @@ void XMLHttpRequest::didReceiveData(const char* data, int len)
|
| }
|
| }
|
|
|
| +void XMLHttpRequest::didDownloadData(int len)
|
| +{
|
| + ASSERT(m_responseTypeCode == ResponseTypeBlob);
|
| +
|
| + if (m_error)
|
| + return;
|
| +
|
| + if (m_state < HEADERS_RECEIVED)
|
| + changeState(HEADERS_RECEIVED);
|
| +
|
| + if (!len)
|
| + return;
|
| +
|
| + if (!m_error) {
|
| + long long expectedLength = m_response.expectedContentLength();
|
| + m_receivedLength += len;
|
| +
|
| + if (m_async) {
|
| + bool lengthComputable = expectedLength > 0 && m_receivedLength <= expectedLength;
|
| + unsigned long long total = lengthComputable ? expectedLength : 0;
|
| + m_progressEventThrottle.dispatchProgressEvent(lengthComputable, m_receivedLength, total);
|
| + }
|
| +
|
| + if (m_state != LOADING) {
|
| + changeState(LOADING);
|
| + } else {
|
| + // Firefox calls readyStateChanged every time it receives data, 4449442
|
| + callReadyStateChangeListener();
|
| + }
|
| + }
|
| +}
|
| +
|
| void XMLHttpRequest::didTimeout()
|
| {
|
| // internalAbort() calls dropProtection(), which may release the last reference.
|
|
|