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