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