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