Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(32)

Side by Side Diff: Source/core/xml/XMLHttpRequest.cpp

Issue 23444058: Use downloadToFile option when XHR downloads a Blob (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Rebase Created 7 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « Source/core/xml/XMLHttpRequest.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2004, 2006, 2008 Apple Inc. All rights reserved. 2 * Copyright (C) 2004, 2006, 2008 Apple Inc. All rights reserved.
3 * Copyright (C) 2005-2007 Alexey Proskuryakov <ap@webkit.org> 3 * Copyright (C) 2005-2007 Alexey Proskuryakov <ap@webkit.org>
4 * Copyright (C) 2007, 2008 Julien Chaffraix <jchaffraix@webkit.org> 4 * Copyright (C) 2007, 2008 Julien Chaffraix <jchaffraix@webkit.org>
5 * Copyright (C) 2008, 2011 Google Inc. All rights reserved. 5 * Copyright (C) 2008, 2011 Google Inc. All rights reserved.
6 * Copyright (C) 2012 Intel Corporation 6 * Copyright (C) 2012 Intel Corporation
7 * 7 *
8 * This library is free software; you can redistribute it and/or 8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public 9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either 10 * License as published by the Free Software Foundation; either
(...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after
166 return xmlHttpRequest.release(); 166 return xmlHttpRequest.release();
167 } 167 }
168 168
169 XMLHttpRequest::XMLHttpRequest(ExecutionContext* context, PassRefPtr<SecurityOri gin> securityOrigin) 169 XMLHttpRequest::XMLHttpRequest(ExecutionContext* context, PassRefPtr<SecurityOri gin> securityOrigin)
170 : ActiveDOMObject(context) 170 : ActiveDOMObject(context)
171 , m_async(true) 171 , m_async(true)
172 , m_includeCredentials(false) 172 , m_includeCredentials(false)
173 , m_timeoutMilliseconds(0) 173 , m_timeoutMilliseconds(0)
174 , m_state(UNSENT) 174 , m_state(UNSENT)
175 , m_createdDocument(false) 175 , m_createdDocument(false)
176 , m_downloadedBlobLength(0)
176 , m_error(false) 177 , m_error(false)
177 , m_uploadEventsAllowed(true) 178 , m_uploadEventsAllowed(true)
178 , m_uploadComplete(false) 179 , m_uploadComplete(false)
179 , m_sameOriginRequest(true) 180 , m_sameOriginRequest(true)
180 , m_receivedLength(0) 181 , m_receivedLength(0)
181 , m_lastSendLineNumber(0) 182 , m_lastSendLineNumber(0)
182 , m_exceptionCode(0) 183 , m_exceptionCode(0)
183 , m_progressEventThrottle(this) 184 , m_progressEventThrottle(this)
184 , m_responseTypeCode(ResponseTypeDefault) 185 , m_responseTypeCode(ResponseTypeDefault)
185 , m_dropProtectionRunner(this, &XMLHttpRequest::dropProtection) 186 , m_dropProtectionRunner(this, &XMLHttpRequest::dropProtection)
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after
269 } 270 }
270 m_createdDocument = true; 271 m_createdDocument = true;
271 } 272 }
272 273
273 return m_responseDocument.get(); 274 return m_responseDocument.get();
274 } 275 }
275 276
276 Blob* XMLHttpRequest::responseBlob() 277 Blob* XMLHttpRequest::responseBlob()
277 { 278 {
278 ASSERT(m_responseTypeCode == ResponseTypeBlob); 279 ASSERT(m_responseTypeCode == ResponseTypeBlob);
280 ASSERT(!m_binaryResponseBuilder.get());
279 281
280 // We always return null before DONE. 282 // We always return null before DONE.
281 if (m_error || m_state != DONE) 283 if (m_error || m_state != DONE)
282 return 0; 284 return 0;
283 285
284 if (!m_responseBlob) { 286 if (!m_responseBlob) {
285 // FIXME: This causes two (or more) unnecessary copies of the data. 287 // When "blob" is specified for the responseType attribute,
286 // Chromium stores blob data in the browser process, so we're pulling th e data 288 // we redirect the downloaded data to a file-handle directly
287 // from the network only to copy it into the renderer to copy it back to the browser. 289 // in the browser process.
288 // Ideally we'd get the blob/file-handle from the ResourceResponse direc tly 290 // We get the file-path from the ResourceResponse directly
289 // instead of copying the bytes. Embedders who store blob data in the 291 // instead of copying the bytes between the browser and the renderer.
290 // same process as WebCore would at least to teach BlobData to take
291 // a SharedBuffer, even if they don't get the Blob from the network laye r directly.
292 OwnPtr<BlobData> blobData = BlobData::create(); 292 OwnPtr<BlobData> blobData = BlobData::create();
293 const String& filePath = m_response.downloadedFilePath();
abarth-chromium 2013/11/05 07:01:51 const String& -> String String is a value type wi
yusukesuzuki 2013/11/05 11:04:00 That's reasonable. Done.
293 // If we errored out or got no data, we still return a blob, just an emp ty one. 294 // If we errored out or got no data, we still return a blob, just an emp ty one.
294 size_t size = 0; 295 if (!filePath.isEmpty() && m_downloadedBlobLength) {
yusukesuzuki 2013/10/24 15:22:41 In the previous patch, I used m_receivedLength as
295 if (m_binaryResponseBuilder) { 296 blobData->appendFile(filePath);
296 RefPtr<RawData> rawData = RawData::create();
297 size = m_binaryResponseBuilder->size();
298 rawData->mutableData()->append(m_binaryResponseBuilder->data(), size );
299 blobData->appendData(rawData, 0, BlobDataItem::toEndOfFile);
300 blobData->setContentType(responseMIMEType()); // responseMIMEType de faults to text/xml which may be incorrect. 297 blobData->setContentType(responseMIMEType()); // responseMIMEType de faults to text/xml which may be incorrect.
301 m_binaryResponseBuilder.clear();
302 } 298 }
303 m_responseBlob = Blob::create(BlobDataHandle::create(blobData.release(), size)); 299 m_responseBlob = Blob::create(BlobDataHandle::create(blobData.release(), m_downloadedBlobLength));
304 } 300 }
305 301
306 return m_responseBlob.get(); 302 return m_responseBlob.get();
307 } 303 }
308 304
309 ArrayBuffer* XMLHttpRequest::responseArrayBuffer() 305 ArrayBuffer* XMLHttpRequest::responseArrayBuffer()
310 { 306 {
311 ASSERT(m_responseTypeCode == ResponseTypeArrayBuffer); 307 ASSERT(m_responseTypeCode == ResponseTypeArrayBuffer);
312 308
313 if (m_error || m_state != DONE) 309 if (m_error || m_state != DONE)
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after
405 return ""; 401 return "";
406 } 402 }
407 403
408 XMLHttpRequestUpload* XMLHttpRequest::upload() 404 XMLHttpRequestUpload* XMLHttpRequest::upload()
409 { 405 {
410 if (!m_upload) 406 if (!m_upload)
411 m_upload = XMLHttpRequestUpload::create(this); 407 m_upload = XMLHttpRequestUpload::create(this);
412 return m_upload.get(); 408 return m_upload.get();
413 } 409 }
414 410
411 void XMLHttpRequest::trackProgress(int length)
412 {
413 m_receivedLength += length;
414
415 if (m_async) {
416 long long expectedLength = m_response.expectedContentLength();
417 bool lengthComputable = expectedLength > 0 && m_receivedLength <= expect edLength;
418 unsigned long long total = lengthComputable ? expectedLength : 0;
419
420 m_progressEventThrottle.dispatchProgressEvent(lengthComputable, m_receiv edLength, total);
421 }
422
423 if (m_state != LOADING) {
424 changeState(LOADING);
425 } else {
426 // Firefox calls readyStateChanged every time it receives data. Do
427 // the same to align with Firefox.
428 //
429 // FIXME: Make our implementation and the spec consistent. This
430 // behavior was needed when the progress event was not available.
431 dispatchReadyStateChangeEvent();
432 }
433 }
434
415 void XMLHttpRequest::changeState(State newState) 435 void XMLHttpRequest::changeState(State newState)
416 { 436 {
417 if (m_state != newState) { 437 if (m_state != newState) {
418 m_state = newState; 438 m_state = newState;
419 dispatchReadyStateChangeEvent(); 439 dispatchReadyStateChangeEvent();
420 } 440 }
421 } 441 }
422 442
423 void XMLHttpRequest::dispatchReadyStateChangeEvent() 443 void XMLHttpRequest::dispatchReadyStateChangeEvent()
424 { 444 {
(...skipping 353 matching lines...) Expand 10 before | Expand all | Expand 10 after
778 m_sameOriginRequest = securityOrigin()->canRequest(m_url); 798 m_sameOriginRequest = securityOrigin()->canRequest(m_url);
779 799
780 // We also remember whether upload events should be allowed for this request in case the upload listeners are 800 // We also remember whether upload events should be allowed for this request in case the upload listeners are
781 // added after the request is started. 801 // added after the request is started.
782 m_uploadEventsAllowed = m_sameOriginRequest || uploadEvents || !isSimpleCros sOriginAccessRequest(m_method, m_requestHeaders); 802 m_uploadEventsAllowed = m_sameOriginRequest || uploadEvents || !isSimpleCros sOriginAccessRequest(m_method, m_requestHeaders);
783 803
784 ResourceRequest request(m_url); 804 ResourceRequest request(m_url);
785 request.setHTTPMethod(m_method); 805 request.setHTTPMethod(m_method);
786 request.setTargetType(ResourceRequest::TargetIsXHR); 806 request.setTargetType(ResourceRequest::TargetIsXHR);
787 807
808 // When "blob" is specified for the responseType attribute,
809 // we redirect the downloaded data to a file-handle directly
810 // and get the file-path as the result.
811 if (responseTypeCode() == ResponseTypeBlob)
812 request.setDownloadToFile(true);
813
788 InspectorInstrumentation::willLoadXHR(executionContext(), this, m_method, m_ url, m_async, m_requestEntityBody ? m_requestEntityBody->deepCopy() : 0, m_reque stHeaders, m_includeCredentials); 814 InspectorInstrumentation::willLoadXHR(executionContext(), this, m_method, m_ url, m_async, m_requestEntityBody ? m_requestEntityBody->deepCopy() : 0, m_reque stHeaders, m_includeCredentials);
789 815
790 if (m_requestEntityBody) { 816 if (m_requestEntityBody) {
791 ASSERT(m_method != "GET"); 817 ASSERT(m_method != "GET");
792 ASSERT(m_method != "HEAD"); 818 ASSERT(m_method != "HEAD");
793 request.setHTTPBody(m_requestEntityBody.release()); 819 request.setHTTPBody(m_requestEntityBody.release());
794 } 820 }
795 821
796 if (m_requestHeaders.size() > 0) 822 if (m_requestHeaders.size() > 0)
797 request.addHTTPHeaderFields(m_requestHeaders); 823 request.addHTTPHeaderFields(m_requestHeaders);
798 824
799 ThreadableLoaderOptions options; 825 ThreadableLoaderOptions options;
800 options.sendLoadCallbacks = SendCallbacks; 826 options.sendLoadCallbacks = SendCallbacks;
801 options.sniffContent = DoNotSniffContent; 827 options.sniffContent = DoNotSniffContent;
802 options.preflightPolicy = uploadEvents ? ForcePreflight : ConsiderPreflight; 828 options.preflightPolicy = uploadEvents ? ForcePreflight : ConsiderPreflight;
803 options.allowCredentials = (m_sameOriginRequest || m_includeCredentials) ? A llowStoredCredentials : DoNotAllowStoredCredentials; 829 options.allowCredentials = (m_sameOriginRequest || m_includeCredentials) ? A llowStoredCredentials : DoNotAllowStoredCredentials;
804 options.credentialsRequested = m_includeCredentials ? ClientRequestedCredent ials : ClientDidNotRequestCredentials; 830 options.credentialsRequested = m_includeCredentials ? ClientRequestedCredent ials : ClientDidNotRequestCredentials;
805 options.crossOriginRequestPolicy = UseAccessControl; 831 options.crossOriginRequestPolicy = UseAccessControl;
806 options.securityOrigin = securityOrigin(); 832 options.securityOrigin = securityOrigin();
807 options.initiator = FetchInitiatorTypeNames::xmlhttprequest; 833 options.initiator = FetchInitiatorTypeNames::xmlhttprequest;
808 options.contentSecurityPolicyEnforcement = ContentSecurityPolicy::shouldBypa ssMainWorld(executionContext()) ? DoNotEnforceContentSecurityPolicy : EnforceCon nectSrcDirective; 834 options.contentSecurityPolicyEnforcement = ContentSecurityPolicy::shouldBypa ssMainWorld(executionContext()) ? DoNotEnforceContentSecurityPolicy : EnforceCon nectSrcDirective;
809 // TODO(tsepez): Specify TreatAsActiveContent per http://crbug.com/305303. 835 // TODO(tsepez): Specify TreatAsActiveContent per http://crbug.com/305303.
810 options.mixedContentBlockingTreatment = TreatAsPassiveContent; 836 options.mixedContentBlockingTreatment = TreatAsPassiveContent;
811 options.timeoutMilliseconds = m_timeoutMilliseconds; 837 options.timeoutMilliseconds = m_timeoutMilliseconds;
812 838
839 // Since we redirect the downloaded data to a file-handle directly
840 // when "blob" is specified for the responseType attribute,
841 // buffering is not needed.
842 if (responseTypeCode() == ResponseTypeBlob)
843 options.dataBufferingPolicy = DoNotBufferData;
844
813 m_exceptionCode = 0; 845 m_exceptionCode = 0;
814 m_error = false; 846 m_error = false;
815 847
816 if (m_async) { 848 if (m_async) {
817 if (m_upload) 849 if (m_upload)
818 request.setReportUploadProgress(true); 850 request.setReportUploadProgress(true);
819 851
820 // ThreadableLoader::create can return null here, for example if we're n o longer attached to a page. 852 // ThreadableLoader::create can return null here, for example if we're n o longer attached to a page.
821 // This is true while running onunload handlers. 853 // This is true while running onunload handlers.
822 // FIXME: Maybe we need to be able to send XMLHttpRequests from onunload , <http://bugs.webkit.org/show_bug.cgi?id=10904>. 854 // FIXME: Maybe we need to be able to send XMLHttpRequests from onunload , <http://bugs.webkit.org/show_bug.cgi?id=10904>.
(...skipping 426 matching lines...) Expand 10 before | Expand all | Expand 10 after
1249 m_response.setHTTPHeaderField("Content-Type", m_mimeTypeOverride); 1281 m_response.setHTTPHeaderField("Content-Type", m_mimeTypeOverride);
1250 m_responseEncoding = extractCharsetFromMediaType(m_mimeTypeOverride); 1282 m_responseEncoding = extractCharsetFromMediaType(m_mimeTypeOverride);
1251 } 1283 }
1252 1284
1253 if (m_responseEncoding.isEmpty()) 1285 if (m_responseEncoding.isEmpty())
1254 m_responseEncoding = response.textEncodingName(); 1286 m_responseEncoding = response.textEncodingName();
1255 } 1287 }
1256 1288
1257 void XMLHttpRequest::didReceiveData(const char* data, int len) 1289 void XMLHttpRequest::didReceiveData(const char* data, int len)
1258 { 1290 {
1291 ASSERT(m_responseTypeCode != ResponseTypeBlob);
1292
1259 if (m_error) 1293 if (m_error)
1260 return; 1294 return;
1261 1295
1262 if (m_state < HEADERS_RECEIVED) 1296 if (m_state < HEADERS_RECEIVED)
1263 changeState(HEADERS_RECEIVED); 1297 changeState(HEADERS_RECEIVED);
1264 1298
1265 bool useDecoder = m_responseTypeCode == ResponseTypeDefault || m_responseTyp eCode == ResponseTypeText || m_responseTypeCode == ResponseTypeJSON || m_respons eTypeCode == ResponseTypeDocument; 1299 bool useDecoder = m_responseTypeCode == ResponseTypeDefault || m_responseTyp eCode == ResponseTypeText || m_responseTypeCode == ResponseTypeJSON || m_respons eTypeCode == ResponseTypeDocument;
1266 1300
1267 if (useDecoder && !m_decoder) { 1301 if (useDecoder && !m_decoder) {
1268 if (m_responseTypeCode == ResponseTypeJSON) 1302 if (m_responseTypeCode == ResponseTypeJSON)
(...skipping 12 matching lines...) Expand all
1281 } 1315 }
1282 1316
1283 if (!len) 1317 if (!len)
1284 return; 1318 return;
1285 1319
1286 if (len == -1) 1320 if (len == -1)
1287 len = strlen(data); 1321 len = strlen(data);
1288 1322
1289 if (useDecoder) { 1323 if (useDecoder) {
1290 m_responseText = m_responseText.concatenateWith(m_decoder->decode(data, len)); 1324 m_responseText = m_responseText.concatenateWith(m_decoder->decode(data, len));
1291 } else if (m_responseTypeCode == ResponseTypeArrayBuffer || m_responseTypeCo de == ResponseTypeBlob) { 1325 } else if (m_responseTypeCode == ResponseTypeArrayBuffer) {
1292 // Buffer binary data. 1326 // Buffer binary data.
1293 if (!m_binaryResponseBuilder) 1327 if (!m_binaryResponseBuilder)
1294 m_binaryResponseBuilder = SharedBuffer::create(); 1328 m_binaryResponseBuilder = SharedBuffer::create();
1295 m_binaryResponseBuilder->append(data, len); 1329 m_binaryResponseBuilder->append(data, len);
1296 } else if (m_responseTypeCode == ResponseTypeStream) { 1330 } else if (m_responseTypeCode == ResponseTypeStream) {
1297 if (!m_responseStream) 1331 if (!m_responseStream)
1298 m_responseStream = Stream::create(executionContext(), responseMIMETy pe()); 1332 m_responseStream = Stream::create(executionContext(), responseMIMETy pe());
1299 m_responseStream->addData(data, len); 1333 m_responseStream->addData(data, len);
1300 } 1334 }
1301 1335
1302 if (m_error) 1336 if (m_error)
1303 return; 1337 return;
1304 1338
1305 m_receivedLength += len; 1339 trackProgress(len);
1340 }
1306 1341
1307 if (m_async) { 1342 void XMLHttpRequest::didDownloadData(int dataLength)
1308 long long expectedLength = m_response.expectedContentLength(); 1343 {
1309 bool lengthComputable = expectedLength > 0 && m_receivedLength <= expect edLength; 1344 ASSERT(m_responseTypeCode == ResponseTypeBlob);
1310 unsigned long long total = lengthComputable ? expectedLength : 0;
1311 1345
1312 m_progressEventThrottle.dispatchProgressEvent(lengthComputable, m_receiv edLength, total); 1346 if (m_error)
1313 } 1347 return;
1314 1348
1315 if (m_state != LOADING) { 1349 if (m_state < HEADERS_RECEIVED)
1316 changeState(LOADING); 1350 changeState(HEADERS_RECEIVED);
1317 } else { 1351
1318 // Firefox calls readyStateChanged every time it receives data. Do 1352 if (!dataLength)
1319 // the same to align with Firefox. 1353 return;
1320 // 1354
1321 // FIXME: Make our implementation and the spec consistent. This 1355 if (m_error)
1322 // behavior was needed when the progress event was not available. 1356 return;
abarth-chromium 2013/11/05 07:01:51 Can changeState set m_error? We just checked it a
yusukesuzuki 2013/11/05 11:04:00 Yeah, changeState can set m_error. changeState cau
1323 dispatchReadyStateChangeEvent(); 1357
1324 } 1358 m_downloadedBlobLength += dataLength;
1359 trackProgress(dataLength);
1325 } 1360 }
1326 1361
1327 void XMLHttpRequest::handleDidTimeout() 1362 void XMLHttpRequest::handleDidTimeout()
1328 { 1363 {
1329 LOG(Network, "XMLHttpRequest %p handleDidTimeout()", this); 1364 LOG(Network, "XMLHttpRequest %p handleDidTimeout()", this);
1330 1365
1331 // internalAbort() calls dropProtection(), which may release the last refere nce. 1366 // internalAbort() calls dropProtection(), which may release the last refere nce.
1332 RefPtr<XMLHttpRequest> protect(this); 1367 RefPtr<XMLHttpRequest> protect(this);
1333 1368
1334 if (!internalAbort()) 1369 if (!internalAbort())
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
1372 { 1407 {
1373 return EventTargetNames::XMLHttpRequest; 1408 return EventTargetNames::XMLHttpRequest;
1374 } 1409 }
1375 1410
1376 ExecutionContext* XMLHttpRequest::executionContext() const 1411 ExecutionContext* XMLHttpRequest::executionContext() const
1377 { 1412 {
1378 return ActiveDOMObject::executionContext(); 1413 return ActiveDOMObject::executionContext();
1379 } 1414 }
1380 1415
1381 } // namespace WebCore 1416 } // namespace WebCore
OLDNEW
« no previous file with comments | « Source/core/xml/XMLHttpRequest.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698