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