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