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