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