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