1 /* 2 * Copyright (C) 2004, 2006, 2008 Apple Inc. All rights reserved. 3 * Copyright (C) 2005-2007 Alexey Proskuryakov <ap (at) webkit.org> 4 * Copyright (C) 2007, 2008 Julien Chaffraix <jchaffraix (at) webkit.org> 5 * Copyright (C) 2008, 2011 Google Inc. All rights reserved. 6 * Copyright (C) 2012 Intel Corporation 7 * 8 * This library is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU Lesser General Public 10 * License as published by the Free Software Foundation; either 11 * version 2 of the License, or (at your option) any later version. 12 * 13 * This library is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * Lesser General Public License for more details. 17 * 18 * You should have received a copy of the GNU Lesser General Public 19 * License along with this library; if not, write to the Free Software 20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 21 */ 22 23 #include "config.h" 24 #include "core/xml/XMLHttpRequest.h" 25 26 #include "FetchInitiatorTypeNames.h" 27 #include "RuntimeEnabledFeatures.h" 28 #include "bindings/v8/ExceptionState.h" 29 #include "core/dom/ContextFeatures.h" 30 #include "core/dom/DOMImplementation.h" 31 #include "core/dom/ExceptionCode.h" 32 #include "core/editing/markup.h" 33 #include "core/events/Event.h" 34 #include "core/fetch/CrossOriginAccessControl.h" 35 #include "core/fetch/TextResourceDecoder.h" 36 #include "core/fileapi/Blob.h" 37 #include "core/fileapi/File.h" 38 #include "core/fileapi/Stream.h" 39 #include "core/frame/ContentSecurityPolicy.h" 40 #include "core/html/DOMFormData.h" 41 #include "core/html/HTMLDocument.h" 42 #include "core/inspector/InspectorInstrumentation.h" 43 #include "core/loader/ThreadableLoader.h" 44 #include "core/frame/Settings.h" 45 #include "core/xml/XMLHttpRequestProgressEvent.h" 46 #include "core/xml/XMLHttpRequestUpload.h" 47 #include "platform/Logging.h" 48 #include "platform/SharedBuffer.h" 49 #include "platform/blob/BlobData.h" 50 #include "platform/network/HTTPParsers.h" 51 #include "platform/network/ParsedContentType.h" 52 #include "platform/network/ResourceError.h" 53 #include "platform/network/ResourceRequest.h" 54 #include "public/platform/Platform.h" 55 #include "wtf/ArrayBuffer.h" 56 #include "wtf/ArrayBufferView.h" 57 #include "wtf/RefCountedLeakCounter.h" 58 #include "wtf/StdLibExtras.h" 59 #include "wtf/text/CString.h" 60 61 namespace WebCore { 62 63 DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, xmlHttpRequestCounter, ("XMLHttpRequest")); 64 65 // Histogram enum to see when we can deprecate xhr.send(ArrayBuffer). 66 enum XMLHttpRequestSendArrayBufferOrView { 67 XMLHttpRequestSendArrayBuffer, 68 XMLHttpRequestSendArrayBufferView, 69 XMLHttpRequestSendArrayBufferOrViewMax, 70 }; 71 72 struct XMLHttpRequestStaticData { 73 WTF_MAKE_NONCOPYABLE(XMLHttpRequestStaticData); WTF_MAKE_FAST_ALLOCATED; 74 public: 75 XMLHttpRequestStaticData(); 76 String m_proxyHeaderPrefix; 77 String m_secHeaderPrefix; 78 HashSet<String, CaseFoldingHash> m_forbiddenRequestHeaders; 79 }; 80 81 XMLHttpRequestStaticData::XMLHttpRequestStaticData() 82 : m_proxyHeaderPrefix("proxy-") 83 , m_secHeaderPrefix("sec-") 84 { 85 m_forbiddenRequestHeaders.add("accept-charset"); 86 m_forbiddenRequestHeaders.add("accept-encoding"); 87 m_forbiddenRequestHeaders.add("access-control-request-headers"); 88 m_forbiddenRequestHeaders.add("access-control-request-method"); 89 m_forbiddenRequestHeaders.add("connection"); 90 m_forbiddenRequestHeaders.add("content-length"); 91 m_forbiddenRequestHeaders.add("content-transfer-encoding"); 92 m_forbiddenRequestHeaders.add("cookie"); 93 m_forbiddenRequestHeaders.add("cookie2"); 94 m_forbiddenRequestHeaders.add("date"); 95 m_forbiddenRequestHeaders.add("expect"); 96 m_forbiddenRequestHeaders.add("host"); 97 m_forbiddenRequestHeaders.add("keep-alive"); 98 m_forbiddenRequestHeaders.add("origin"); 99 m_forbiddenRequestHeaders.add("referer"); 100 m_forbiddenRequestHeaders.add("te"); 101 m_forbiddenRequestHeaders.add("trailer"); 102 m_forbiddenRequestHeaders.add("transfer-encoding"); 103 m_forbiddenRequestHeaders.add("upgrade"); 104 m_forbiddenRequestHeaders.add("user-agent"); 105 m_forbiddenRequestHeaders.add("via"); 106 } 107 108 static bool isSetCookieHeader(const AtomicString& name) 109 { 110 return equalIgnoringCase(name, "set-cookie") || equalIgnoringCase(name, "set-cookie2"); 111 } 112 113 static void replaceCharsetInMediaType(String& mediaType, const String& charsetValue) 114 { 115 unsigned int pos = 0, len = 0; 116 117 findCharsetInMediaType(mediaType, pos, len); 118 119 if (!len) { 120 // When no charset found, do nothing. 121 return; 122 } 123 124 // Found at least one existing charset, replace all occurrences with new charset. 125 while (len) { 126 mediaType.replace(pos, len, charsetValue); 127 unsigned int start = pos + charsetValue.length(); 128 findCharsetInMediaType(mediaType, pos, len, start); 129 } 130 } 131 132 static const XMLHttpRequestStaticData* staticData = 0; 133 134 static const XMLHttpRequestStaticData* createXMLHttpRequestStaticData() 135 { 136 staticData = new XMLHttpRequestStaticData; 137 return staticData; 138 } 139 140 static const XMLHttpRequestStaticData* initializeXMLHttpRequestStaticData() 141 { 142 // Uses dummy to avoid warnings about an unused variable. 143 AtomicallyInitializedStatic(const XMLHttpRequestStaticData*, dummy = createXMLHttpRequestStaticData()); 144 return dummy; 145 } 146 147 static void logConsoleError(ExecutionContext* context, const String& message) 148 { 149 if (!context) 150 return; 151 // FIXME: It's not good to report the bad usage without indicating what source line it came from. 152 // We should pass additional parameters so we can tell the console where the mistake occurred. 153 context->addConsoleMessage(JSMessageSource, ErrorMessageLevel, message); 154 } 155 156 PassRefPtr<XMLHttpRequest> XMLHttpRequest::create(ExecutionContext* context, PassRefPtr<SecurityOrigin> securityOrigin) 157 { 158 RefPtr<XMLHttpRequest> xmlHttpRequest(adoptRef(new XMLHttpRequest(context, securityOrigin))); 159 xmlHttpRequest->suspendIfNeeded(); 160 161 return xmlHttpRequest.release(); 162 } 163 164 XMLHttpRequest::XMLHttpRequest(ExecutionContext* context, PassRefPtr<SecurityOrigin> securityOrigin) 165 : ActiveDOMObject(context) 166 , m_async(true) 167 , m_includeCredentials(false) 168 , m_timeoutMilliseconds(0) 169 , m_state(UNSENT) 170 , m_createdDocument(false) 171 , m_downloadedBlobLength(0) 172 , m_error(false) 173 , m_uploadEventsAllowed(true) 174 , m_uploadComplete(false) 175 , m_sameOriginRequest(true) 176 , m_receivedLength(0) 177 , m_lastSendLineNumber(0) 178 , m_exceptionCode(0) 179 , m_progressEventThrottle(this) 180 , m_responseTypeCode(ResponseTypeDefault) 181 , m_dropProtectionRunner(this, &XMLHttpRequest::dropProtection) 182 , m_securityOrigin(securityOrigin) 183 { 184 initializeXMLHttpRequestStaticData(); 185 #ifndef NDEBUG 186 xmlHttpRequestCounter.increment(); 187 #endif 188 ScriptWrappable::init(this); 189 } 190 191 XMLHttpRequest::~XMLHttpRequest() 192 { 193 #ifndef NDEBUG 194 xmlHttpRequestCounter.decrement(); 195 #endif 196 } 197 198 Document* XMLHttpRequest::document() const 199 { 200 ASSERT(executionContext()->isDocument()); 201 return toDocument(executionContext()); 202 } 203 204 SecurityOrigin* XMLHttpRequest::securityOrigin() const 205 { 206 return m_securityOrigin ? m_securityOrigin.get() : executionContext()->securityOrigin(); 207 } 208 209 XMLHttpRequest::State XMLHttpRequest::readyState() const 210 { 211 return m_state; 212 } 213 214 ScriptString XMLHttpRequest::responseText(ExceptionState& exceptionState) 215 { 216 if (m_responseTypeCode != ResponseTypeDefault && m_responseTypeCode != ResponseTypeText) { 217 exceptionState.throwDOMException(InvalidStateError, "The value is only accessible if the object's 'responseType' is '' or 'text' (was '" + responseType() + "')."); 218 return ScriptString(); 219 } 220 if (m_error || (m_state != LOADING && m_state != DONE)) 221 return ScriptString(); 222 return m_responseText; 223 } 224 225 ScriptString XMLHttpRequest::responseJSONSource() 226 { 227 ASSERT(m_responseTypeCode == ResponseTypeJSON); 228 229 if (m_error || m_state != DONE) 230 return ScriptString(); 231 return m_responseText; 232 } 233 234 Document* XMLHttpRequest::responseXML(ExceptionState& exceptionState) 235 { 236 if (m_responseTypeCode != ResponseTypeDefault && m_responseTypeCode != ResponseTypeDocument) { 237 exceptionState.throwDOMException(InvalidStateError, "The value is only accessible if the object's 'responseType' is '' or 'document' (was '" + responseType() + "')."); 238 return 0; 239 } 240 241 if (m_error || m_state != DONE) 242 return 0; 243 244 if (!m_createdDocument) { 245 bool isHTML = equalIgnoringCase(responseMIMEType(), "text/html"); 246 247 // The W3C spec requires the final MIME type to be some valid XML type, or text/html. 248 // If it is text/html, then the responseType of "document" must have been supplied explicitly. 249 if ((m_response.isHTTP() && !responseIsXML() && !isHTML) 250 || (isHTML && m_responseTypeCode == ResponseTypeDefault) 251 || executionContext()->isWorkerGlobalScope()) { 252 m_responseDocument = 0; 253 } else { 254 DocumentInit init = DocumentInit::fromContext(document()->contextDocument(), m_url); 255 if (isHTML) 256 m_responseDocument = HTMLDocument::create(init); 257 else 258 m_responseDocument = Document::create(init); 259 // FIXME: Set Last-Modified. 260 m_responseDocument->setContent(m_responseText.flattenToString()); 261 m_responseDocument->setSecurityOrigin(securityOrigin()); 262 m_responseDocument->setContextFeatures(document()->contextFeatures()); 263 if (!m_responseDocument->wellFormed()) 264 m_responseDocument = 0; 265 } 266 m_createdDocument = true; 267 } 268 269 return m_responseDocument.get(); 270 } 271 272 Blob* XMLHttpRequest::responseBlob() 273 { 274 ASSERT(m_responseTypeCode == ResponseTypeBlob); 275 ASSERT(!m_binaryResponseBuilder.get()); 276 277 // We always return null before DONE. 278 if (m_error || m_state != DONE) 279 return 0; 280 281 if (!m_responseBlob) { 282 // When "blob" is specified for the responseType attribute, 283 // we redirect the downloaded data to a file-handle directly 284 // in the browser process. 285 // We get the file-path from the ResourceResponse directly 286 // instead of copying the bytes between the browser and the renderer. 287 OwnPtr<BlobData> blobData = BlobData::create(); 288 String filePath = m_response.downloadedFilePath(); 289 // If we errored out or got no data, we still return a blob, just an empty one. 290 if (!filePath.isEmpty() && m_downloadedBlobLength) { 291 blobData->appendFile(filePath); 292 blobData->setContentType(responseMIMEType()); // responseMIMEType defaults to text/xml which may be incorrect. 293 } 294 m_responseBlob = Blob::create(BlobDataHandle::create(blobData.release(), m_downloadedBlobLength)); 295 } 296 297 return m_responseBlob.get(); 298 } 299 300 ArrayBuffer* XMLHttpRequest::responseArrayBuffer() 301 { 302 ASSERT(m_responseTypeCode == ResponseTypeArrayBuffer); 303 304 if (m_error || m_state != DONE) 305 return 0; 306 307 if (!m_responseArrayBuffer.get()) { 308 if (m_binaryResponseBuilder.get() && m_binaryResponseBuilder->size() > 0) { 309 m_responseArrayBuffer = m_binaryResponseBuilder->getAsArrayBuffer(); 310 m_binaryResponseBuilder.clear(); 311 } else { 312 m_responseArrayBuffer = ArrayBuffer::create(static_cast<void*>(0), 0); 313 } 314 } 315 316 return m_responseArrayBuffer.get(); 317 } 318 319 Stream* XMLHttpRequest::responseStream() 320 { 321 ASSERT(m_responseTypeCode == ResponseTypeStream); 322 323 if (m_error || (m_state != LOADING && m_state != DONE)) 324 return 0; 325 326 return m_responseStream.get(); 327 } 328 329 void XMLHttpRequest::setTimeout(unsigned long timeout, ExceptionState& exceptionState) 330 { 331 // FIXME: Need to trigger or update the timeout Timer here, if needed. http://webkit.org/b/98156 332 // XHR2 spec, 4.7.3. "This implies that the timeout attribute can be set while fetching is in progress. If that occurs it will still be measured relative to the start of fetching." 333 if (executionContext()->isDocument() && !m_async) { 334 exceptionState.throwDOMException(InvalidAccessError, "Timeouts cannot be set for synchronous requests made from a document."); 335 return; 336 } 337 m_timeoutMilliseconds = timeout; 338 } 339 340 void XMLHttpRequest::setResponseType(const String& responseType, ExceptionState& exceptionState) 341 { 342 if (m_state >= LOADING) { 343 exceptionState.throwDOMException(InvalidStateError, "The response type cannot be set if the object's state is LOADING or DONE."); 344 return; 345 } 346 347 // Newer functionality is not available to synchronous requests in window contexts, as a spec-mandated 348 // attempt to discourage synchronous XHR use. responseType is one such piece of functionality. 349 // We'll only disable this functionality for HTTP(S) requests since sync requests for local protocols 350 // such as file: and data: still make sense to allow. 351 if (!m_async && executionContext()->isDocument() && m_url.protocolIsInHTTPFamily()) { 352 exceptionState.throwDOMException(InvalidAccessError, "The response type can only be changed for asynchronous HTTP requests made from a document."); 353 return; 354 } 355 356 if (responseType == "") { 357 m_responseTypeCode = ResponseTypeDefault; 358 } else if (responseType == "text") { 359 m_responseTypeCode = ResponseTypeText; 360 } else if (responseType == "json") { 361 m_responseTypeCode = ResponseTypeJSON; 362 } else if (responseType == "document") { 363 m_responseTypeCode = ResponseTypeDocument; 364 } else if (responseType == "blob") { 365 m_responseTypeCode = ResponseTypeBlob; 366 } else if (responseType == "arraybuffer") { 367 m_responseTypeCode = ResponseTypeArrayBuffer; 368 } else if (responseType == "stream") { 369 if (RuntimeEnabledFeatures::streamEnabled()) 370 m_responseTypeCode = ResponseTypeStream; 371 else 372 return; 373 } else { 374 ASSERT_NOT_REACHED(); 375 } 376 } 377 378 String XMLHttpRequest::responseType() 379 { 380 switch (m_responseTypeCode) { 381 case ResponseTypeDefault: 382 return ""; 383 case ResponseTypeText: 384 return "text"; 385 case ResponseTypeJSON: 386 return "json"; 387 case ResponseTypeDocument: 388 return "document"; 389 case ResponseTypeBlob: 390 return "blob"; 391 case ResponseTypeArrayBuffer: 392 return "arraybuffer"; 393 case ResponseTypeStream: 394 return "stream"; 395 } 396 return ""; 397 } 398 399 XMLHttpRequestUpload* XMLHttpRequest::upload() 400 { 401 if (!m_upload) 402 m_upload = XMLHttpRequestUpload::create(this); 403 return m_upload.get(); 404 } 405 406 void XMLHttpRequest::trackProgress(int length) 407 { 408 m_receivedLength += length; 409 410 if (m_async) 411 dispatchThrottledProgressEventSnapshot(EventTypeNames::progress); 412 413 if (m_state != LOADING) { 414 changeState(LOADING); 415 } else { 416 // Firefox calls readyStateChanged every time it receives data. Do 417 // the same to align with Firefox. 418 // 419 // FIXME: Make our implementation and the spec consistent. This 420 // behavior was needed when the progress event was not available. 421 dispatchReadyStateChangeEvent(); 422 } 423 } 424 425 void XMLHttpRequest::changeState(State newState) 426 { 427 if (m_state != newState) { 428 m_state = newState; 429 dispatchReadyStateChangeEvent(); 430 } 431 } 432 433 void XMLHttpRequest::dispatchReadyStateChangeEvent() 434 { 435 if (!executionContext()) 436 return; 437 438 InspectorInstrumentationCookie cookie = InspectorInstrumentation::willDispatchXHRReadyStateChangeEvent(executionContext(), this); 439 440 if (m_async || (m_state <= OPENED || m_state == DONE)) { 441 ProgressEventAction flushAction = DoNotFlushProgressEvent; 442 if (m_state == DONE) { 443 if (m_error) 444 flushAction = FlushDeferredProgressEvent; 445 else 446 flushAction = FlushProgressEvent; 447 } 448 m_progressEventThrottle.dispatchReadyStateChangeEvent(XMLHttpRequestProgressEvent::create(EventTypeNames::readystatechange), flushAction); 449 } 450 451 InspectorInstrumentation::didDispatchXHRReadyStateChangeEvent(cookie); 452 if (m_state == DONE && !m_error) { 453 InspectorInstrumentationCookie cookie = InspectorInstrumentation::willDispatchXHRLoadEvent(executionContext(), this); 454 dispatchThrottledProgressEventSnapshot(EventTypeNames::load); 455 InspectorInstrumentation::didDispatchXHRLoadEvent(cookie); 456 dispatchThrottledProgressEventSnapshot(EventTypeNames::loadend); 457 } 458 } 459 460 void XMLHttpRequest::setWithCredentials(bool value, ExceptionState& exceptionState) 461 { 462 if (m_state > OPENED || m_loader) { 463 exceptionState.throwDOMException(InvalidStateError, "The value may only be set if the object's state is UNSENT or OPENED."); 464 return; 465 } 466 467 m_includeCredentials = value; 468 } 469 470 bool XMLHttpRequest::isAllowedHTTPMethod(const String& method) 471 { 472 return !equalIgnoringCase(method, "TRACE") 473 && !equalIgnoringCase(method, "TRACK") 474 && !equalIgnoringCase(method, "CONNECT"); 475 } 476 477 AtomicString XMLHttpRequest::uppercaseKnownHTTPMethod(const AtomicString& method) 478 { 479 const char* const methods[] = { 480 "COPY", 481 "DELETE", 482 "GET", 483 "HEAD", 484 "INDEX", 485 "LOCK", 486 "M-POST", 487 "MKCOL", 488 "MOVE", 489 "OPTIONS", 490 "POST", 491 "PROPFIND", 492 "PROPPATCH", 493 "PUT", 494 "UNLOCK" }; 495 for (unsigned i = 0; i < WTF_ARRAY_LENGTH(methods); ++i) { 496 if (equalIgnoringCase(method, methods[i])) { 497 // Don't bother allocating a new string if it's already all uppercase. 498 if (method == methods[i]) 499 return method; 500 return methods[i]; 501 } 502 } 503 return method; 504 } 505 506 bool XMLHttpRequest::isAllowedHTTPHeader(const String& name) 507 { 508 initializeXMLHttpRequestStaticData(); 509 return !staticData->m_forbiddenRequestHeaders.contains(name) && !name.startsWith(staticData->m_proxyHeaderPrefix, false) 510 && !name.startsWith(staticData->m_secHeaderPrefix, false); 511 } 512 513 void XMLHttpRequest::open(const AtomicString& method, const KURL& url, ExceptionState& exceptionState) 514 { 515 open(method, url, true, exceptionState); 516 } 517 518 void XMLHttpRequest::open(const AtomicString& method, const KURL& url, bool async, ExceptionState& exceptionState) 519 { 520 WTF_LOG(Network, "XMLHttpRequest %p open('%s', '%s', %d)", this, method.string().utf8().data(), url.elidedString().utf8().data(), async); 521 522 if (!internalAbort()) 523 return; 524 525 State previousState = m_state; 526 m_state = UNSENT; 527 m_error = false; 528 m_uploadComplete = false; 529 530 // clear stuff from possible previous load 531 clearResponse(); 532 clearRequest(); 533 534 ASSERT(m_state == UNSENT); 535 536 if (!isValidHTTPToken(method)) { 537 exceptionState.throwDOMException(SyntaxError, "'" + method + "' is not a valid HTTP method."); 538 return; 539 } 540 541 if (!isAllowedHTTPMethod(method)) { 542 exceptionState.throwSecurityError("'" + method + "' HTTP method is unsupported."); 543 return; 544 } 545 546 if (!ContentSecurityPolicy::shouldBypassMainWorld(executionContext()) && !executionContext()->contentSecurityPolicy()->allowConnectToSource(url)) { 547 // We can safely expose the URL to JavaScript, as these checks happen synchronously before redirection. JavaScript receives no new information. 548 exceptionState.throwSecurityError("Refused to connect to '" + url.elidedString() + "' because it violates the document's Content Security Policy."); 549 return; 550 } 551 552 if (!async && executionContext()->isDocument()) { 553 if (document()->settings() && !document()->settings()->syncXHRInDocumentsEnabled()) { 554 exceptionState.throwDOMException(InvalidAccessError, "Synchronous requests are disabled for this page."); 555 return; 556 } 557 558 // Newer functionality is not available to synchronous requests in window contexts, as a spec-mandated 559 // attempt to discourage synchronous XHR use. responseType is one such piece of functionality. 560 // We'll only disable this functionality for HTTP(S) requests since sync requests for local protocols 561 // such as file: and data: still make sense to allow. 562 if (url.protocolIsInHTTPFamily() && m_responseTypeCode != ResponseTypeDefault) { 563 exceptionState.throwDOMException(InvalidAccessError, "Synchronous HTTP requests from a document must not set a response type."); 564 return; 565 } 566 567 // Similarly, timeouts are disabled for synchronous requests as well. 568 if (m_timeoutMilliseconds > 0) { 569 exceptionState.throwDOMException(InvalidAccessError, "Synchronous requests must not set a timeout."); 570 return; 571 } 572 } 573 574 m_method = uppercaseKnownHTTPMethod(method); 575 576 m_url = url; 577 578 m_async = async; 579 580 ASSERT(!m_loader); 581 582 // Check previous state to avoid dispatching readyState event 583 // when calling open several times in a row. 584 if (previousState != OPENED) 585 changeState(OPENED); 586 else 587 m_state = OPENED; 588 } 589 590 void XMLHttpRequest::open(const AtomicString& method, const KURL& url, bool async, const String& user, ExceptionState& exceptionState) 591 { 592 KURL urlWithCredentials(url); 593 urlWithCredentials.setUser(user); 594 595 open(method, urlWithCredentials, async, exceptionState); 596 } 597 598 void XMLHttpRequest::open(const AtomicString& method, const KURL& url, bool async, const String& user, const String& password, ExceptionState& exceptionState) 599 { 600 KURL urlWithCredentials(url); 601 urlWithCredentials.setUser(user); 602 urlWithCredentials.setPass(password); 603 604 open(method, urlWithCredentials, async, exceptionState); 605 } 606 607 bool XMLHttpRequest::initSend(ExceptionState& exceptionState) 608 { 609 if (!executionContext()) 610 return false; 611 612 if (m_state != OPENED || m_loader) { 613 exceptionState.throwDOMException(InvalidStateError, "The object's state must be OPENED."); 614 return false; 615 } 616 617 m_error = false; 618 return true; 619 } 620 621 void XMLHttpRequest::send(ExceptionState& exceptionState) 622 { 623 send(String(), exceptionState); 624 } 625 626 bool XMLHttpRequest::areMethodAndURLValidForSend() 627 { 628 return m_method != "GET" && m_method != "HEAD" && m_url.protocolIsInHTTPFamily(); 629 } 630 631 void XMLHttpRequest::send(Document* document, ExceptionState& exceptionState) 632 { 633 WTF_LOG(Network, "XMLHttpRequest %p send() Document %p", this, document); 634 635 ASSERT(document); 636 637 if (!initSend(exceptionState)) 638 return; 639 640 if (areMethodAndURLValidForSend()) { 641 if (getRequestHeader("Content-Type").isEmpty()) { 642 // FIXME: this should include the charset used for encoding. 643 setRequestHeaderInternal("Content-Type", "application/xml"); 644 } 645 646 // FIXME: According to XMLHttpRequest Level 2, this should use the Document.innerHTML algorithm 647 // from the HTML5 specification to serialize the document. 648 String body = createMarkup(document); 649 650 // FIXME: This should use value of document.inputEncoding to determine the encoding to use. 651 m_requestEntityBody = FormData::create(UTF8Encoding().encode(body, WTF::EntitiesForUnencodables)); 652 if (m_upload) 653 m_requestEntityBody->setAlwaysStream(true); 654 } 655 656 createRequest(exceptionState); 657 } 658 659 void XMLHttpRequest::send(const String& body, ExceptionState& exceptionState) 660 { 661 WTF_LOG(Network, "XMLHttpRequest %p send() String '%s'", this, body.utf8().data()); 662 663 if (!initSend(exceptionState)) 664 return; 665 666 if (!body.isNull() && areMethodAndURLValidForSend()) { 667 String contentType = getRequestHeader("Content-Type"); 668 if (contentType.isEmpty()) { 669 setRequestHeaderInternal("Content-Type", "text/plain;charset=UTF-8"); 670 } else { 671 replaceCharsetInMediaType(contentType, "UTF-8"); 672 m_requestHeaders.set("Content-Type", AtomicString(contentType)); 673 } 674 675 m_requestEntityBody = FormData::create(UTF8Encoding().encode(body, WTF::EntitiesForUnencodables)); 676 if (m_upload) 677 m_requestEntityBody->setAlwaysStream(true); 678 } 679 680 createRequest(exceptionState); 681 } 682 683 void XMLHttpRequest::send(Blob* body, ExceptionState& exceptionState) 684 { 685 WTF_LOG(Network, "XMLHttpRequest %p send() Blob '%s'", this, body->uuid().utf8().data()); 686 687 if (!initSend(exceptionState)) 688 return; 689 690 if (areMethodAndURLValidForSend()) { 691 if (getRequestHeader("Content-Type").isEmpty()) { 692 const String& blobType = body->type(); 693 if (!blobType.isEmpty() && isValidContentType(blobType)) 694 setRequestHeaderInternal("Content-Type", AtomicString(blobType)); 695 else { 696 // From FileAPI spec, whenever media type cannot be determined, empty string must be returned. 697 setRequestHeaderInternal("Content-Type", ""); 698 } 699 } 700 701 // FIXME: add support for uploading bundles. 702 m_requestEntityBody = FormData::create(); 703 if (body->hasBackingFile()) 704 m_requestEntityBody->appendFile(toFile(body)->path()); 705 else 706 m_requestEntityBody->appendBlob(body->uuid(), body->blobDataHandle()); 707 } 708 709 createRequest(exceptionState); 710 } 711 712 void XMLHttpRequest::send(DOMFormData* body, ExceptionState& exceptionState) 713 { 714 WTF_LOG(Network, "XMLHttpRequest %p send() DOMFormData %p", this, body); 715 716 if (!initSend(exceptionState)) 717 return; 718 719 if (areMethodAndURLValidForSend()) { 720 m_requestEntityBody = body->createMultiPartFormData(body->encoding()); 721 722 if (getRequestHeader("Content-Type").isEmpty()) { 723 AtomicString contentType = AtomicString("multipart/form-data; boundary=", AtomicString::ConstructFromLiteral) + m_requestEntityBody->boundary().data(); 724 setRequestHeaderInternal("Content-Type", contentType); 725 } 726 } 727 728 createRequest(exceptionState); 729 } 730 731 void XMLHttpRequest::send(ArrayBuffer* body, ExceptionState& exceptionState) 732 { 733 WTF_LOG(Network, "XMLHttpRequest %p send() ArrayBuffer %p", this, body); 734 735 String consoleMessage("ArrayBuffer is deprecated in XMLHttpRequest.send(). Use ArrayBufferView instead."); 736 executionContext()->addConsoleMessage(JSMessageSource, WarningMessageLevel, consoleMessage); 737 738 blink::Platform::current()->histogramEnumeration("WebCore.XHR.send.ArrayBufferOrView", XMLHttpRequestSendArrayBuffer, XMLHttpRequestSendArrayBufferOrViewMax); 739 740 sendBytesData(body->data(), body->byteLength(), exceptionState); 741 } 742 743 void XMLHttpRequest::send(ArrayBufferView* body, ExceptionState& exceptionState) 744 { 745 WTF_LOG(Network, "XMLHttpRequest %p send() ArrayBufferView %p", this, body); 746 747 blink::Platform::current()->histogramEnumeration("WebCore.XHR.send.ArrayBufferOrView", XMLHttpRequestSendArrayBufferView, XMLHttpRequestSendArrayBufferOrViewMax); 748 749 sendBytesData(body->baseAddress(), body->byteLength(), exceptionState); 750 } 751 752 void XMLHttpRequest::sendBytesData(const void* data, size_t length, ExceptionState& exceptionState) 753 { 754 if (!initSend(exceptionState)) 755 return; 756 757 if (areMethodAndURLValidForSend()) { 758 m_requestEntityBody = FormData::create(data, length); 759 if (m_upload) 760 m_requestEntityBody->setAlwaysStream(true); 761 } 762 763 createRequest(exceptionState); 764 } 765 766 void XMLHttpRequest::sendForInspectorXHRReplay(PassRefPtr<FormData> formData, ExceptionState& exceptionState) 767 { 768 m_requestEntityBody = formData ? formData->deepCopy() : 0; 769 createRequest(exceptionState); 770 m_exceptionCode = exceptionState.code(); 771 } 772 773 void XMLHttpRequest::createRequest(ExceptionState& exceptionState) 774 { 775 // Only GET request is supported for blob URL. 776 if (m_url.protocolIs("blob") && m_method != "GET") { 777 exceptionState.throwDOMException(NetworkError, "'GET' is the only method allowed for 'blob:' URLs."); 778 return; 779 } 780 781 // The presence of upload event listeners forces us to use preflighting because POSTing to an URL that does not 782 // permit cross origin requests should look exactly like POSTing to an URL that does not respond at all. 783 // Also, only async requests support upload progress events. 784 bool uploadEvents = false; 785 if (m_async) { 786 m_progressEventThrottle.dispatchEvent(XMLHttpRequestProgressEvent::create(EventTypeNames::loadstart)); 787 if (m_requestEntityBody && m_upload) { 788 uploadEvents = m_upload->hasEventListeners(); 789 m_upload->dispatchEvent(XMLHttpRequestProgressEvent::create(EventTypeNames::loadstart)); 790 } 791 } 792 793 m_sameOriginRequest = securityOrigin()->canRequest(m_url); 794 795 // We also remember whether upload events should be allowed for this request in case the upload listeners are 796 // added after the request is started. 797 m_uploadEventsAllowed = m_sameOriginRequest || uploadEvents || !isSimpleCrossOriginAccessRequest(m_method, m_requestHeaders); 798 799 ResourceRequest request(m_url); 800 request.setHTTPMethod(m_method); 801 request.setTargetType(ResourceRequest::TargetIsXHR); 802 803 // When "blob" is specified for the responseType attribute, 804 // we redirect the downloaded data to a file-handle directly 805 // and get the file-path as the result. 806 if (responseTypeCode() == ResponseTypeBlob) 807 request.setDownloadToFile(true); 808 809 InspectorInstrumentation::willLoadXHR(executionContext(), this, this, m_method, m_url, m_async, m_requestEntityBody ? m_requestEntityBody->deepCopy() : 0, m_requestHeaders, m_includeCredentials); 810 811 if (m_requestEntityBody) { 812 ASSERT(m_method != "GET"); 813 ASSERT(m_method != "HEAD"); 814 request.setHTTPBody(m_requestEntityBody.release()); 815 } 816 817 if (m_requestHeaders.size() > 0) 818 request.addHTTPHeaderFields(m_requestHeaders); 819 820 ThreadableLoaderOptions options; 821 options.sendLoadCallbacks = SendCallbacks; 822 options.sniffContent = DoNotSniffContent; 823 options.preflightPolicy = uploadEvents ? ForcePreflight : ConsiderPreflight; 824 options.allowCredentials = (m_sameOriginRequest || m_includeCredentials) ? AllowStoredCredentials : DoNotAllowStoredCredentials; 825 options.credentialsRequested = m_includeCredentials ? ClientRequestedCredentials : ClientDidNotRequestCredentials; 826 options.crossOriginRequestPolicy = UseAccessControl; 827 options.securityOrigin = securityOrigin(); 828 options.initiator = FetchInitiatorTypeNames::xmlhttprequest; 829 options.contentSecurityPolicyEnforcement = ContentSecurityPolicy::shouldBypassMainWorld(executionContext()) ? DoNotEnforceContentSecurityPolicy : EnforceConnectSrcDirective; 830 // TODO(tsepez): Specify TreatAsActiveContent per http://crbug.com/305303. 831 options.mixedContentBlockingTreatment = TreatAsPassiveContent; 832 options.timeoutMilliseconds = m_timeoutMilliseconds; 833 834 // Since we redirect the downloaded data to a file-handle directly 835 // when "blob" is specified for the responseType attribute, 836 // buffering is not needed. 837 if (responseTypeCode() == ResponseTypeBlob) 838 options.dataBufferingPolicy = DoNotBufferData; 839 840 m_exceptionCode = 0; 841 m_error = false; 842 843 if (m_async) { 844 if (m_upload) 845 request.setReportUploadProgress(true); 846 847 // ThreadableLoader::create can return null here, for example if we're no longer attached to a page. 848 // This is true while running onunload handlers. 849 // FIXME: Maybe we need to be able to send XMLHttpRequests from onunload, <http://bugs.webkit.org/show_bug.cgi?id=10904>. 850 // FIXME: Maybe create() can return null for other reasons too? 851 ASSERT(!m_loader); 852 m_loader = ThreadableLoader::create(executionContext(), this, request, options); 853 if (m_loader) { 854 // Neither this object nor the JavaScript wrapper should be deleted while 855 // a request is in progress because we need to keep the listeners alive, 856 // and they are referenced by the JavaScript wrapper. 857 setPendingActivity(this); 858 } 859 } else { 860 ThreadableLoader::loadResourceSynchronously(executionContext(), request, *this, options); 861 } 862 863 if (!m_exceptionCode && m_error) 864 m_exceptionCode = NetworkError; 865 if (m_exceptionCode) 866 exceptionState.throwUninformativeAndGenericDOMException(m_exceptionCode); 867 } 868 869 void XMLHttpRequest::abort() 870 { 871 WTF_LOG(Network, "XMLHttpRequest %p abort()", this); 872 873 // internalAbort() calls dropProtection(), which may release the last reference. 874 RefPtr<XMLHttpRequest> protect(this); 875 876 bool sendFlag = m_loader; 877 878 // Response is cleared next, save needed progress event data. 879 long long expectedLength = m_response.expectedContentLength(); 880 long long receivedLength = m_receivedLength; 881 882 if (!internalAbort()) 883 return; 884 885 clearResponse(); 886 887 // Clear headers as required by the spec 888 m_requestHeaders.clear(); 889 890 if (!((m_state <= OPENED && !sendFlag) || m_state == DONE)) { 891 ASSERT(!m_loader); 892 handleRequestError(0, EventTypeNames::abort, receivedLength, expectedLength); 893 } 894 m_state = UNSENT; 895 } 896 897 void XMLHttpRequest::clearVariablesForLoading() 898 { 899 m_decoder.clear(); 900 901 m_responseEncoding = String(); 902 } 903 904 bool XMLHttpRequest::internalAbort(DropProtection async) 905 { 906 m_error = true; 907 908 clearVariablesForLoading(); 909 910 InspectorInstrumentation::didFailXHRLoading(executionContext(), this, this); 911 912 if (m_responseStream && m_state != DONE) 913 m_responseStream->abort(); 914 915 if (!m_loader) 916 return true; 917 918 // Cancelling the ThreadableLoader m_loader may result in calling 919 // window.onload synchronously. If such an onload handler contains open() 920 // call on the same XMLHttpRequest object, reentry happens. If m_loader 921 // is left to be non 0, internalAbort() call for the inner open() makes 922 // an extra dropProtection() call (when we're back to the outer open(), 923 // we'll call dropProtection()). To avoid that, clears m_loader before 924 // calling cancel. 925 // 926 // If, window.onload contains open() and send(), m_loader will be set to 927 // non 0 value. So, we cannot continue the outer open(). In such case, 928 // just abort the outer open() by returning false. 929 RefPtr<ThreadableLoader> loader = m_loader.release(); 930 loader->cancel(); 931 932 // Save to a local variable since we're going to drop protection. 933 bool newLoadStarted = m_loader; 934 935 // If abort() called internalAbort() and a nested open() ended up 936 // clearing the error flag, but didn't send(), make sure the error 937 // flag is still set. 938 if (!newLoadStarted) 939 m_error = true; 940 941 if (async == DropProtectionAsync) 942 dropProtectionSoon(); 943 else 944 dropProtection(); 945 946 return !newLoadStarted; 947 } 948 949 void XMLHttpRequest::clearResponse() 950 { 951 // FIXME: when we add the support for multi-part XHR, we will have to 952 // be careful with this initialization. 953 m_receivedLength = 0; 954 955 m_response = ResourceResponse(); 956 957 m_responseText.clear(); 958 959 m_createdDocument = false; 960 m_responseDocument = 0; 961 962 m_responseBlob = 0; 963 964 m_responseStream = 0; 965 966 // These variables may referred by the response accessors. So, we can clear 967 // this only when we clear the response holder variables above. 968 m_binaryResponseBuilder.clear(); 969 m_responseArrayBuffer.clear(); 970 } 971 972 void XMLHttpRequest::clearRequest() 973 { 974 m_requestHeaders.clear(); 975 m_requestEntityBody = 0; 976 } 977 978 void XMLHttpRequest::handleDidFailGeneric() 979 { 980 clearResponse(); 981 clearRequest(); 982 983 m_error = true; 984 } 985 986 void XMLHttpRequest::dispatchEventAndLoadEnd(const AtomicString& type, long long receivedLength, long long expectedLength) 987 { 988 bool lengthComputable = expectedLength > 0 && receivedLength <= expectedLength; 989 unsigned long long loaded = receivedLength >= 0 ? static_cast<unsigned long long>(receivedLength) : 0; 990 unsigned long long total = lengthComputable ? static_cast<unsigned long long>(expectedLength) : 0; 991 992 m_progressEventThrottle.dispatchEventAndLoadEnd(type, lengthComputable, loaded, total); 993 } 994 995 void XMLHttpRequest::dispatchThrottledProgressEvent(const AtomicString& type, long long receivedLength, long long expectedLength) 996 { 997 bool lengthComputable = expectedLength > 0 && receivedLength <= expectedLength; 998 unsigned long long loaded = receivedLength >= 0 ? static_cast<unsigned long long>(receivedLength) : 0; 999 unsigned long long total = lengthComputable ? static_cast<unsigned long long>(expectedLength) : 0; 1000 1001 if (type == EventTypeNames::progress) 1002 m_progressEventThrottle.dispatchProgressEvent(lengthComputable, loaded, total); 1003 else 1004 m_progressEventThrottle.dispatchEvent(XMLHttpRequestProgressEvent::create(type, lengthComputable, loaded, total)); 1005 } 1006 1007 void XMLHttpRequest::dispatchThrottledProgressEventSnapshot(const AtomicString& type) 1008 { 1009 return dispatchThrottledProgressEvent(type, m_receivedLength, m_response.expectedContentLength()); 1010 } 1011 1012 void XMLHttpRequest::handleNetworkError() 1013 { 1014 WTF_LOG(Network, "XMLHttpRequest %p handleNetworkError()", this); 1015 1016 // Response is cleared next, save needed progress event data. 1017 long long expectedLength = m_response.expectedContentLength(); 1018 long long receivedLength = m_receivedLength; 1019 1020 handleDidFailGeneric(); 1021 handleRequestError(NetworkError, EventTypeNames::error, receivedLength, expectedLength); 1022 internalAbort(); 1023 } 1024 1025 void XMLHttpRequest::handleDidCancel() 1026 { 1027 WTF_LOG(Network, "XMLHttpRequest %p handleDidCancel()", this); 1028 1029 // Response is cleared next, save needed progress event data. 1030 long long expectedLength = m_response.expectedContentLength(); 1031 long long receivedLength = m_receivedLength; 1032 1033 handleDidFailGeneric(); 1034 handleRequestError(AbortError, EventTypeNames::abort, receivedLength, expectedLength); 1035 } 1036 1037 void XMLHttpRequest::handleRequestError(ExceptionCode exceptionCode, const AtomicString& type, long long receivedLength, long long expectedLength) 1038 { 1039 WTF_LOG(Network, "XMLHttpRequest %p handleRequestError()", this); 1040 1041 // The request error steps for event 'type' and exception 'exceptionCode'. 1042 1043 if (!m_async && exceptionCode) { 1044 m_state = DONE; 1045 m_exceptionCode = exceptionCode; 1046 return; 1047 } 1048 // With m_error set, the state change steps are minimal: any pending 1049 // progress event is flushed + a readystatechange is dispatched. 1050 // No new progress events dispatched; as required, that happens at 1051 // the end here. 1052 ASSERT(m_error); 1053 changeState(DONE); 1054 1055 if (!m_uploadComplete) { 1056 m_uploadComplete = true; 1057 if (m_upload && m_uploadEventsAllowed) 1058 m_upload->handleRequestError(type); 1059 } 1060 1061 dispatchThrottledProgressEvent(EventTypeNames::progress, receivedLength, expectedLength); 1062 dispatchEventAndLoadEnd(type, receivedLength, expectedLength); 1063 } 1064 1065 void XMLHttpRequest::dropProtectionSoon() 1066 { 1067 m_dropProtectionRunner.runAsync(); 1068 } 1069 1070 void XMLHttpRequest::dropProtection() 1071 { 1072 unsetPendingActivity(this); 1073 } 1074 1075 void XMLHttpRequest::overrideMimeType(const AtomicString& override) 1076 { 1077 m_mimeTypeOverride = override; 1078 } 1079 1080 void XMLHttpRequest::setRequestHeader(const AtomicString& name, const AtomicString& value, ExceptionState& exceptionState) 1081 { 1082 if (m_state != OPENED || m_loader) { 1083 exceptionState.throwDOMException(InvalidStateError, "The object's state must be OPENED."); 1084 return; 1085 } 1086 1087 if (!isValidHTTPToken(name)) { 1088 exceptionState.throwDOMException(SyntaxError, "'" + name + "' is not a valid HTTP header field name."); 1089 return; 1090 } 1091 1092 if (!isValidHTTPHeaderValue(value)) { 1093 exceptionState.throwDOMException(SyntaxError, "'" + value + "' is not a valid HTTP header field value."); 1094 return; 1095 } 1096 1097 // No script (privileged or not) can set unsafe headers. 1098 if (!isAllowedHTTPHeader(name)) { 1099 logConsoleError(executionContext(), "Refused to set unsafe header \"" + name + "\""); 1100 return; 1101 } 1102 1103 setRequestHeaderInternal(name, value); 1104 } 1105 1106 void XMLHttpRequest::setRequestHeaderInternal(const AtomicString& name, const AtomicString& value) 1107 { 1108 HTTPHeaderMap::AddResult result = m_requestHeaders.add(name, value); 1109 if (!result.isNewEntry) 1110 result.iterator->value = result.iterator->value + ", " + value; 1111 } 1112 1113 const AtomicString& XMLHttpRequest::getRequestHeader(const AtomicString& name) const 1114 { 1115 return m_requestHeaders.get(name); 1116 } 1117 1118 String XMLHttpRequest::getAllResponseHeaders() const 1119 { 1120 if (m_state < HEADERS_RECEIVED || m_error) 1121 return ""; 1122 1123 StringBuilder stringBuilder; 1124 1125 HTTPHeaderSet accessControlExposeHeaderSet; 1126 parseAccessControlExposeHeadersAllowList(m_response.httpHeaderField("Access-Control-Expose-Headers"), accessControlExposeHeaderSet); 1127 HTTPHeaderMap::const_iterator end = m_response.httpHeaderFields().end(); 1128 for (HTTPHeaderMap::const_iterator it = m_response.httpHeaderFields().begin(); it!= end; ++it) { 1129 // Hide Set-Cookie header fields from the XMLHttpRequest client for these reasons: 1130 // 1) If the client did have access to the fields, then it could read HTTP-only 1131 // cookies; those cookies are supposed to be hidden from scripts. 1132 // 2) There's no known harm in hiding Set-Cookie header fields entirely; we don't 1133 // know any widely used technique that requires access to them. 1134 // 3) Firefox has implemented this policy. 1135 if (isSetCookieHeader(it->key) && !securityOrigin()->canLoadLocalResources()) 1136 continue; 1137 1138 if (!m_sameOriginRequest && !isOnAccessControlResponseHeaderWhitelist(it->key) && !accessControlExposeHeaderSet.contains(it->key)) 1139 continue; 1140 1141 stringBuilder.append(it->key); 1142 stringBuilder.append(':'); 1143 stringBuilder.append(' '); 1144 stringBuilder.append(it->value); 1145 stringBuilder.append('\r'); 1146 stringBuilder.append('\n'); 1147 } 1148 1149 return stringBuilder.toString(); 1150 } 1151 1152 const AtomicString& XMLHttpRequest::getResponseHeader(const AtomicString& name) const 1153 { 1154 if (m_state < HEADERS_RECEIVED || m_error) 1155 return nullAtom; 1156 1157 // See comment in getAllResponseHeaders above. 1158 if (isSetCookieHeader(name) && !securityOrigin()->canLoadLocalResources()) { 1159 logConsoleError(executionContext(), "Refused to get unsafe header \"" + name + "\""); 1160 return nullAtom; 1161 } 1162 1163 HTTPHeaderSet accessControlExposeHeaderSet; 1164 parseAccessControlExposeHeadersAllowList(m_response.httpHeaderField("Access-Control-Expose-Headers"), accessControlExposeHeaderSet); 1165 1166 if (!m_sameOriginRequest && !isOnAccessControlResponseHeaderWhitelist(name) && !accessControlExposeHeaderSet.contains(name)) { 1167 logConsoleError(executionContext(), "Refused to get unsafe header \"" + name + "\""); 1168 return nullAtom; 1169 } 1170 return m_response.httpHeaderField(name); 1171 } 1172 1173 AtomicString XMLHttpRequest::responseMIMEType() const 1174 { 1175 AtomicString mimeType = extractMIMETypeFromMediaType(m_mimeTypeOverride); 1176 if (mimeType.isEmpty()) { 1177 if (m_response.isHTTP()) 1178 mimeType = extractMIMETypeFromMediaType(m_response.httpHeaderField("Content-Type")); 1179 else 1180 mimeType = m_response.mimeType(); 1181 } 1182 if (mimeType.isEmpty()) 1183 mimeType = AtomicString("text/xml", AtomicString::ConstructFromLiteral); 1184 1185 return mimeType; 1186 } 1187 1188 bool XMLHttpRequest::responseIsXML() const 1189 { 1190 // FIXME: Remove the lower() call when DOMImplementation.isXMLMIMEType() is modified 1191 // to do case insensitive MIME type matching. 1192 return DOMImplementation::isXMLMIMEType(responseMIMEType().lower()); 1193 } 1194 1195 int XMLHttpRequest::status() const 1196 { 1197 if (m_state == UNSENT || m_state == OPENED || m_error) 1198 return 0; 1199 1200 if (m_response.httpStatusCode()) 1201 return m_response.httpStatusCode(); 1202 1203 return 0; 1204 } 1205 1206 String XMLHttpRequest::statusText() const 1207 { 1208 if (m_state == UNSENT || m_state == OPENED || m_error) 1209 return String(); 1210 1211 if (!m_response.httpStatusText().isNull()) 1212 return m_response.httpStatusText(); 1213 1214 return String(); 1215 } 1216 1217 void XMLHttpRequest::didFail(const ResourceError& error) 1218 { 1219 WTF_LOG(Network, "XMLHttpRequest %p didFail()", this); 1220 1221 // If we are already in an error state, for instance we called abort(), bail out early. 1222 if (m_error) 1223 return; 1224 1225 if (error.isCancellation()) { 1226 handleDidCancel(); 1227 return; 1228 } 1229 1230 if (error.isTimeout()) { 1231 handleDidTimeout(); 1232 return; 1233 } 1234 1235 // Network failures are already reported to Web Inspector by ResourceLoader. 1236 if (error.domain() == errorDomainBlinkInternal) 1237 logConsoleError(executionContext(), "XMLHttpRequest cannot load " + error.failingURL() + ". " + error.localizedDescription()); 1238 1239 handleNetworkError(); 1240 } 1241 1242 void XMLHttpRequest::didFailRedirectCheck() 1243 { 1244 WTF_LOG(Network, "XMLHttpRequest %p didFailRedirectCheck()", this); 1245 1246 handleNetworkError(); 1247 } 1248 1249 void XMLHttpRequest::didFinishLoading(unsigned long identifier, double) 1250 { 1251 WTF_LOG(Network, "XMLHttpRequest %p didFinishLoading(%lu)", this, identifier); 1252 1253 if (m_error) 1254 return; 1255 1256 if (m_state < HEADERS_RECEIVED) 1257 changeState(HEADERS_RECEIVED); 1258 1259 if (m_decoder) 1260 m_responseText = m_responseText.concatenateWith(m_decoder->flush()); 1261 1262 if (m_responseStream) 1263 m_responseStream->finalize(); 1264 1265 clearVariablesForLoading(); 1266 1267 InspectorInstrumentation::didFinishXHRLoading(executionContext(), this, this, identifier, m_responseText, m_url, m_lastSendURL, m_lastSendLineNumber); 1268 1269 // Prevent dropProtection releasing the last reference, and retain |this| until the end of this method. 1270 RefPtr<XMLHttpRequest> protect(this); 1271 1272 if (m_loader) { 1273 m_loader = 0; 1274 dropProtection(); 1275 } 1276 1277 changeState(DONE); 1278 } 1279 1280 void XMLHttpRequest::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent) 1281 { 1282 WTF_LOG(Network, "XMLHttpRequest %p didSendData(%llu, %llu)", this, bytesSent, totalBytesToBeSent); 1283 1284 if (!m_upload) 1285 return; 1286 1287 if (m_uploadEventsAllowed) 1288 m_upload->dispatchProgressEvent(bytesSent, totalBytesToBeSent); 1289 1290 if (bytesSent == totalBytesToBeSent && !m_uploadComplete) { 1291 m_uploadComplete = true; 1292 if (m_uploadEventsAllowed) 1293 m_upload->dispatchEventAndLoadEnd(EventTypeNames::load, true, bytesSent, totalBytesToBeSent); 1294 } 1295 } 1296 1297 void XMLHttpRequest::didReceiveResponse(unsigned long identifier, const ResourceResponse& response) 1298 { 1299 WTF_LOG(Network, "XMLHttpRequest %p didReceiveResponse(%lu)", this, identifier); 1300 1301 m_response = response; 1302 if (!m_mimeTypeOverride.isEmpty()) { 1303 m_response.setHTTPHeaderField("Content-Type", m_mimeTypeOverride); 1304 m_responseEncoding = extractCharsetFromMediaType(m_mimeTypeOverride); 1305 } 1306 1307 if (m_responseEncoding.isEmpty()) 1308 m_responseEncoding = response.textEncodingName(); 1309 } 1310 1311 void XMLHttpRequest::didReceiveData(const char* data, int len) 1312 { 1313 ASSERT(m_responseTypeCode != ResponseTypeBlob); 1314 1315 if (m_error) 1316 return; 1317 1318 if (m_state < HEADERS_RECEIVED) 1319 changeState(HEADERS_RECEIVED); 1320 1321 bool useDecoder = m_responseTypeCode == ResponseTypeDefault || m_responseTypeCode == ResponseTypeText || m_responseTypeCode == ResponseTypeJSON || m_responseTypeCode == ResponseTypeDocument; 1322 1323 if (useDecoder && !m_decoder) { 1324 if (m_responseTypeCode == ResponseTypeJSON) 1325 m_decoder = TextResourceDecoder::create("application/json", "UTF-8"); 1326 else if (!m_responseEncoding.isEmpty()) 1327 m_decoder = TextResourceDecoder::create("text/plain", m_responseEncoding); 1328 // allow TextResourceDecoder to look inside the m_response if it's XML or HTML 1329 else if (responseIsXML()) { 1330 m_decoder = TextResourceDecoder::create("application/xml"); 1331 // Don't stop on encoding errors, unlike it is done for other kinds of XML resources. This matches the behavior of previous WebKit versions, Firefox and Opera. 1332 m_decoder->useLenientXMLDecoding(); 1333 } else if (equalIgnoringCase(responseMIMEType(), "text/html")) 1334 m_decoder = TextResourceDecoder::create("text/html", "UTF-8"); 1335 else 1336 m_decoder = TextResourceDecoder::create("text/plain", "UTF-8"); 1337 } 1338 1339 if (!len) 1340 return; 1341 1342 if (len == -1) 1343 len = strlen(data); 1344 1345 if (useDecoder) { 1346 m_responseText = m_responseText.concatenateWith(m_decoder->decode(data, len)); 1347 } else if (m_responseTypeCode == ResponseTypeArrayBuffer) { 1348 // Buffer binary data. 1349 if (!m_binaryResponseBuilder) 1350 m_binaryResponseBuilder = SharedBuffer::create(); 1351 m_binaryResponseBuilder->append(data, len); 1352 } else if (m_responseTypeCode == ResponseTypeStream) { 1353 if (!m_responseStream) 1354 m_responseStream = Stream::create(executionContext(), responseMIMEType()); 1355 m_responseStream->addData(data, len); 1356 } 1357 1358 if (m_error) 1359 return; 1360 1361 trackProgress(len); 1362 } 1363 1364 void XMLHttpRequest::didDownloadData(int dataLength) 1365 { 1366 ASSERT(m_responseTypeCode == ResponseTypeBlob); 1367 1368 if (m_error) 1369 return; 1370 1371 if (m_state < HEADERS_RECEIVED) 1372 changeState(HEADERS_RECEIVED); 1373 1374 if (!dataLength) 1375 return; 1376 1377 if (m_error) 1378 return; 1379 1380 m_downloadedBlobLength += dataLength; 1381 trackProgress(dataLength); 1382 } 1383 1384 void XMLHttpRequest::handleDidTimeout() 1385 { 1386 WTF_LOG(Network, "XMLHttpRequest %p handleDidTimeout()", this); 1387 1388 // internalAbort() calls dropProtection(), which may release the last reference. 1389 RefPtr<XMLHttpRequest> protect(this); 1390 1391 // Response is cleared next, save needed progress event data. 1392 long long expectedLength = m_response.expectedContentLength(); 1393 long long receivedLength = m_receivedLength; 1394 1395 if (!internalAbort()) 1396 return; 1397 1398 handleDidFailGeneric(); 1399 handleRequestError(TimeoutError, EventTypeNames::timeout, receivedLength, expectedLength); 1400 } 1401 1402 void XMLHttpRequest::suspend() 1403 { 1404 m_progressEventThrottle.suspend(); 1405 } 1406 1407 void XMLHttpRequest::resume() 1408 { 1409 m_progressEventThrottle.resume(); 1410 } 1411 1412 void XMLHttpRequest::stop() 1413 { 1414 internalAbort(DropProtectionAsync); 1415 } 1416 1417 void XMLHttpRequest::contextDestroyed() 1418 { 1419 ASSERT(!m_loader); 1420 ActiveDOMObject::contextDestroyed(); 1421 } 1422 1423 const AtomicString& XMLHttpRequest::interfaceName() const 1424 { 1425 return EventTargetNames::XMLHttpRequest; 1426 } 1427 1428 ExecutionContext* XMLHttpRequest::executionContext() const 1429 { 1430 return ActiveDOMObject::executionContext(); 1431 } 1432 1433 } // namespace WebCore 1434