Modernize some aspects of text codecs, eliminate WebKit use of strcasecmp
[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 "DOMWindow.h"
32 #include "Event.h"
33 #include "EventNames.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 "SecurityOriginPolicy.h"
48 #include "Settings.h"
49 #include "SharedBuffer.h"
50 #include "StringAdaptors.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     return document()->settings().usesDashboardBackwardCompatibilityMode();
149 }
150
151 #endif
152
153 XMLHttpRequest::State XMLHttpRequest::readyState() const
154 {
155     return m_state;
156 }
157
158 ExceptionOr<OwnedString> XMLHttpRequest::responseText()
159 {
160     if (m_responseType != ResponseType::EmptyString && m_responseType != ResponseType::Text)
161         return Exception { InvalidStateError };
162     return OwnedString { responseTextIgnoringResponseType() };
163 }
164
165 void XMLHttpRequest::didCacheResponse()
166 {
167     ASSERT(doneWithoutErrors());
168     m_responseCacheIsValid = true;
169     m_responseBuilder.clear();
170 }
171
172 ExceptionOr<Document*> XMLHttpRequest::responseXML()
173 {
174     ASSERT(scriptExecutionContext()->isDocument());
175     
176     if (m_responseType != ResponseType::EmptyString && m_responseType != ResponseType::Document)
177         return Exception { InvalidStateError };
178
179     if (!doneWithoutErrors())
180         return nullptr;
181
182     if (!m_createdDocument) {
183         String mimeType = responseMIMEType();
184         bool isHTML = equalLettersIgnoringASCIICase(mimeType, "text/html");
185
186         // The W3C spec requires the final MIME type to be some valid XML type, or text/html.
187         // If it is text/html, then the responseType of "document" must have been supplied explicitly.
188         if ((m_response.isHTTP() && !responseIsXML() && !isHTML)
189             || (isHTML && m_responseType == ResponseType::EmptyString)) {
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->tryCreateArrayBuffer() : 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 { InvalidAccessError };
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     Seconds interval = Seconds { m_timeoutMilliseconds ? m_timeoutMilliseconds / 1000. : 60. } - (MonotonicTime::now() - m_sendingTime);
249     m_timeoutTimer.startOneShot(std::max(interval, 0_s));
250     return { };
251 }
252
253 ExceptionOr<void> XMLHttpRequest::setResponseType(ResponseType type)
254 {
255     if (!scriptExecutionContext()->isDocument() && type == ResponseType::Document)
256         return { };
257
258     if (m_state >= LOADING)
259         return Exception { InvalidStateError };
260
261     // Newer functionality is not available to synchronous requests in window contexts, as a spec-mandated
262     // attempt to discourage synchronous XHR use. responseType is one such piece of functionality.
263     // We'll only disable this functionality for HTTP(S) requests since sync requests for local protocols
264     // such as file: and data: still make sense to allow.
265     if (!m_async && scriptExecutionContext()->isDocument() && m_url.protocolIsInHTTPFamily()) {
266         logConsoleError(scriptExecutionContext(), "XMLHttpRequest.responseType cannot be changed for synchronous HTTP(S) requests made from the window context.");
267         return Exception { InvalidAccessError };
268     }
269
270     m_responseType = type;
271     return { };
272 }
273
274 String XMLHttpRequest::responseURL() const
275 {
276     URL responseURL(m_response.url());
277     responseURL.removeFragmentIdentifier();
278
279     return responseURL.string();
280 }
281
282 XMLHttpRequestUpload* XMLHttpRequest::upload()
283 {
284     if (!m_upload)
285         m_upload = std::make_unique<XMLHttpRequestUpload>(this);
286     return m_upload.get();
287 }
288
289 void XMLHttpRequest::changeState(State newState)
290 {
291     if (m_state != newState) {
292         m_state = newState;
293         callReadyStateChangeListener();
294     }
295 }
296
297 void XMLHttpRequest::callReadyStateChangeListener()
298 {
299     if (!scriptExecutionContext())
300         return;
301
302     // Check whether sending load and loadend events before sending readystatechange event, as it may change m_error/m_state values.
303     bool shouldSendLoadEvent = (m_state == DONE && !m_error);
304
305     if (m_async || (m_state <= OPENED || m_state == DONE))
306         m_progressEventThrottle.dispatchReadyStateChangeEvent(Event::create(eventNames().readystatechangeEvent, false, false), m_state == DONE ? FlushProgressEvent : DoNotFlushProgressEvent);
307
308     if (shouldSendLoadEvent) {
309         m_progressEventThrottle.dispatchProgressEvent(eventNames().loadEvent);
310         m_progressEventThrottle.dispatchProgressEvent(eventNames().loadendEvent);
311     }
312 }
313
314 ExceptionOr<void> XMLHttpRequest::setWithCredentials(bool value)
315 {
316     if (m_state > OPENED || m_sendFlag)
317         return Exception { InvalidStateError };
318
319     m_includeCredentials = value;
320     return { };
321 }
322
323 ExceptionOr<void> XMLHttpRequest::open(const String& method, const String& url)
324 {
325     // If the async argument is omitted, set async to true.
326     return open(method, scriptExecutionContext()->completeURL(url), true);
327 }
328
329 ExceptionOr<void> XMLHttpRequest::open(const String& method, const URL& url, bool async)
330 {
331     if (!internalAbort())
332         return { };
333
334     State previousState = m_state;
335     m_state = UNSENT;
336     m_error = false;
337     m_sendFlag = false;
338     m_uploadComplete = false;
339     m_wasAbortedByClient = false;
340
341     // clear stuff from possible previous load
342     clearResponse();
343     clearRequest();
344
345     ASSERT(m_state == UNSENT);
346
347     if (!isValidHTTPToken(method))
348         return Exception { SyntaxError };
349
350     if (isForbiddenMethod(method))
351         return Exception { SecurityError };
352
353     if (!async && scriptExecutionContext()->isDocument()) {
354         // Newer functionality is not available to synchronous requests in window contexts, as a spec-mandated
355         // attempt to discourage synchronous XHR use. responseType is one such piece of functionality.
356         // We'll only disable this functionality for HTTP(S) requests since sync requests for local protocols
357         // such as file: and data: still make sense to allow.
358         if (url.protocolIsInHTTPFamily() && m_responseType != ResponseType::EmptyString) {
359             logConsoleError(scriptExecutionContext(), "Synchronous HTTP(S) requests made from the window context cannot have XMLHttpRequest.responseType set.");
360             return Exception { InvalidAccessError };
361         }
362
363         // Similarly, timeouts are disabled for synchronous requests as well.
364         if (m_timeoutMilliseconds > 0) {
365             logConsoleError(scriptExecutionContext(), "Synchronous XMLHttpRequests must not have a timeout value set.");
366             return Exception { InvalidAccessError };
367         }
368     }
369
370     m_method = normalizeHTTPMethod(method);
371
372     m_url = url;
373     scriptExecutionContext()->contentSecurityPolicy()->upgradeInsecureRequestIfNeeded(m_url, ContentSecurityPolicy::InsecureRequestType::Load);
374
375     m_async = async;
376
377     ASSERT(!m_loader);
378
379     // Check previous state to avoid dispatching readyState event
380     // when calling open several times in a row.
381     if (previousState != OPENED)
382         changeState(OPENED);
383     else
384         m_state = OPENED;
385
386     return { };
387 }
388
389 ExceptionOr<void> XMLHttpRequest::open(const String& method, const String& url, bool async, const String& user, const String& password)
390 {
391     URL urlWithCredentials = scriptExecutionContext()->completeURL(url);
392     if (!user.isNull()) {
393         urlWithCredentials.setUser(user);
394         if (!password.isNull())
395             urlWithCredentials.setPass(password);
396     }
397
398     return open(method, urlWithCredentials, async);
399 }
400
401 std::optional<ExceptionOr<void>> XMLHttpRequest::prepareToSend()
402 {
403     // A return value other than std::nullopt means we should not try to send, and we should return that value to the caller.
404     // std::nullopt means we are ready to send and should continue with the send algorithm.
405
406     if (!scriptExecutionContext())
407         return ExceptionOr<void> { };
408
409     auto& context = *scriptExecutionContext();
410
411     if (m_state != OPENED || m_sendFlag)
412         return ExceptionOr<void> { Exception { InvalidStateError } };
413     ASSERT(!m_loader);
414
415     // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved.
416     if (!context.shouldBypassMainWorldContentSecurityPolicy() && !context.contentSecurityPolicy()->allowConnectToSource(m_url)) {
417         if (!m_async)
418             return ExceptionOr<void> { Exception { NetworkError } };
419         setPendingActivity(this);
420         m_timeoutTimer.stop();
421         m_networkErrorTimer.startOneShot(0_s);
422         return ExceptionOr<void> { };
423     }
424
425     m_error = false;
426     return std::nullopt;
427 }
428
429 ExceptionOr<void> XMLHttpRequest::send(std::optional<SendTypes>&& sendType)
430 {
431     InspectorInstrumentation::willSendXMLHttpRequest(scriptExecutionContext(), url());
432
433     ExceptionOr<void> result;
434     if (!sendType)
435         result = send();
436     else {
437         result = WTF::switchOn(sendType.value(),
438             [this] (const RefPtr<Document>& document) -> ExceptionOr<void> { return send(*document); },
439             [this] (const RefPtr<Blob>& blob) -> ExceptionOr<void> { return send(*blob); },
440             [this] (const RefPtr<JSC::ArrayBufferView>& arrayBufferView) -> ExceptionOr<void> { return send(*arrayBufferView); },
441             [this] (const RefPtr<JSC::ArrayBuffer>& arrayBuffer) -> ExceptionOr<void> { return send(*arrayBuffer); },
442             [this] (const RefPtr<DOMFormData>& formData) -> ExceptionOr<void> { return send(*formData); },
443             [this] (const String& string) -> ExceptionOr<void> { return send(string); }
444         );
445     }
446
447     return result;
448 }
449
450 ExceptionOr<void> XMLHttpRequest::send(Document& document)
451 {
452     if (auto result = prepareToSend())
453         return WTFMove(result.value());
454
455     if (m_method != "GET" && m_method != "HEAD" && m_url.protocolIsInHTTPFamily()) {
456         if (!m_requestHeaders.contains(HTTPHeaderName::ContentType)) {
457 #if ENABLE(DASHBOARD_SUPPORT)
458             if (usesDashboardBackwardCompatibilityMode())
459                 m_requestHeaders.set(HTTPHeaderName::ContentType, ASCIILiteral("application/x-www-form-urlencoded"));
460             else
461 #endif
462                 // FIXME: this should include the charset used for encoding.
463                 m_requestHeaders.set(HTTPHeaderName::ContentType, document.isHTMLDocument() ? ASCIILiteral("text/html;charset=UTF-8") : ASCIILiteral("application/xml;charset=UTF-8"));
464         }
465
466         // FIXME: According to XMLHttpRequest Level 2, this should use the Document.innerHTML algorithm
467         // from the HTML5 specification to serialize the document.
468         m_requestEntityBody = FormData::create(UTF8Encoding().encode(createMarkup(document), UnencodableHandling::Entities));
469         if (m_upload)
470             m_requestEntityBody->setAlwaysStream(true);
471     }
472
473     return createRequest();
474 }
475
476 ExceptionOr<void> XMLHttpRequest::send(const String& body)
477 {
478     if (auto result = prepareToSend())
479         return WTFMove(result.value());
480
481     if (!body.isNull() && m_method != "GET" && m_method != "HEAD" && m_url.protocolIsInHTTPFamily()) {
482         String contentType = m_requestHeaders.get(HTTPHeaderName::ContentType);
483         if (contentType.isNull()) {
484 #if ENABLE(DASHBOARD_SUPPORT)
485             if (usesDashboardBackwardCompatibilityMode())
486                 m_requestHeaders.set(HTTPHeaderName::ContentType, ASCIILiteral("application/x-www-form-urlencoded"));
487             else
488 #endif
489                 m_requestHeaders.set(HTTPHeaderName::ContentType, HTTPHeaderValues::textPlainContentType());
490         } else {
491             replaceCharsetInMediaType(contentType, "UTF-8");
492             m_requestHeaders.set(HTTPHeaderName::ContentType, contentType);
493         }
494
495         m_requestEntityBody = FormData::create(UTF8Encoding().encode(body, UnencodableHandling::Entities));
496         if (m_upload)
497             m_requestEntityBody->setAlwaysStream(true);
498     }
499
500     return createRequest();
501 }
502
503 ExceptionOr<void> XMLHttpRequest::send(Blob& body)
504 {
505     if (auto result = prepareToSend())
506         return WTFMove(result.value());
507
508     if (m_method != "GET" && m_method != "HEAD" && m_url.protocolIsInHTTPFamily()) {
509         if (!m_requestHeaders.contains(HTTPHeaderName::ContentType)) {
510             const String& blobType = body.type();
511             if (!blobType.isEmpty() && isValidContentType(blobType))
512                 m_requestHeaders.set(HTTPHeaderName::ContentType, blobType);
513             else {
514                 // From FileAPI spec, whenever media type cannot be determined, empty string must be returned.
515                 m_requestHeaders.set(HTTPHeaderName::ContentType, emptyString());
516             }
517         }
518
519         m_requestEntityBody = FormData::create();
520         m_requestEntityBody->appendBlob(body.url());
521     }
522
523     return createRequest();
524 }
525
526 ExceptionOr<void> XMLHttpRequest::send(DOMFormData& body)
527 {
528     if (auto result = prepareToSend())
529         return WTFMove(result.value());
530
531     if (m_method != "GET" && m_method != "HEAD" && m_url.protocolIsInHTTPFamily()) {
532         m_requestEntityBody = FormData::createMultiPart(body, document());
533         m_requestEntityBody->generateFiles(document());
534         if (!m_requestHeaders.contains(HTTPHeaderName::ContentType))
535             m_requestHeaders.set(HTTPHeaderName::ContentType, makeString("multipart/form-data; boundary=", m_requestEntityBody->boundary().data()));
536     }
537
538     return createRequest();
539 }
540
541 ExceptionOr<void> XMLHttpRequest::send(ArrayBuffer& body)
542 {
543     ASCIILiteral consoleMessage("ArrayBuffer is deprecated in XMLHttpRequest.send(). Use ArrayBufferView instead.");
544     scriptExecutionContext()->addConsoleMessage(MessageSource::JS, MessageLevel::Warning, consoleMessage);
545     return sendBytesData(body.data(), body.byteLength());
546 }
547
548 ExceptionOr<void> XMLHttpRequest::send(ArrayBufferView& body)
549 {
550     return sendBytesData(body.baseAddress(), body.byteLength());
551 }
552
553 ExceptionOr<void> XMLHttpRequest::sendBytesData(const void* data, size_t length)
554 {
555     if (auto result = prepareToSend())
556         return WTFMove(result.value());
557
558     if (m_method != "GET" && m_method != "HEAD" && m_url.protocolIsInHTTPFamily()) {
559         m_requestEntityBody = FormData::create(data, length);
560         if (m_upload)
561             m_requestEntityBody->setAlwaysStream(true);
562     }
563
564     return createRequest();
565 }
566
567 ExceptionOr<void> XMLHttpRequest::createRequest()
568 {
569     // Only GET request is supported for blob URL.
570     if (!m_async && m_url.protocolIsBlob() && m_method != "GET")
571         return Exception { NetworkError };
572
573     m_sendFlag = true;
574
575     // The presence of upload event listeners forces us to use preflighting because POSTing to an URL that does not
576     // permit cross origin requests should look exactly like POSTing to an URL that does not respond at all.
577     // Also, only async requests support upload progress events.
578     bool uploadEvents = false;
579     if (m_async) {
580         m_progressEventThrottle.dispatchProgressEvent(eventNames().loadstartEvent);
581         if (m_requestEntityBody && m_upload) {
582             uploadEvents = m_upload->hasEventListeners();
583             m_upload->dispatchProgressEvent(eventNames().loadstartEvent);
584         }
585     }
586
587     m_sameOriginRequest = securityOrigin()->canRequest(m_url);
588
589     // We also remember whether upload events should be allowed for this request in case the upload listeners are
590     // added after the request is started.
591     m_uploadEventsAllowed = m_sameOriginRequest || uploadEvents || !isSimpleCrossOriginAccessRequest(m_method, m_requestHeaders);
592
593     ResourceRequest request(m_url);
594     request.setRequester(ResourceRequest::Requester::XHR);
595     request.setInitiatorIdentifier(scriptExecutionContext()->resourceRequestIdentifier());
596     request.setHTTPMethod(m_method);
597
598     if (m_requestEntityBody) {
599         ASSERT(m_method != "GET");
600         ASSERT(m_method != "HEAD");
601         request.setHTTPBody(WTFMove(m_requestEntityBody));
602     }
603
604     if (!m_requestHeaders.isEmpty())
605         request.setHTTPHeaderFields(m_requestHeaders);
606
607     ThreadableLoaderOptions options;
608     options.sendLoadCallbacks = SendCallbacks;
609     options.preflightPolicy = uploadEvents ? ForcePreflight : ConsiderPreflight;
610     options.credentials = m_includeCredentials ? FetchOptions::Credentials::Include : FetchOptions::Credentials::SameOrigin;
611     options.mode = FetchOptions::Mode::Cors;
612     options.contentSecurityPolicyEnforcement = scriptExecutionContext()->shouldBypassMainWorldContentSecurityPolicy() ? ContentSecurityPolicyEnforcement::DoNotEnforce : ContentSecurityPolicyEnforcement::EnforceConnectSrcDirective;
613     options.initiator = cachedResourceRequestInitiators().xmlhttprequest;
614     options.sameOriginDataURLFlag = SameOriginDataURLFlag::Set;
615     options.filteringPolicy = ResponseFilteringPolicy::Enable;
616     options.sniffContentEncoding = ContentEncodingSniffingPolicy::DoNotSniff;
617
618     if (m_timeoutMilliseconds) {
619         if (!m_async)
620             request.setTimeoutInterval(m_timeoutMilliseconds / 1000.0);
621         else {
622             request.setTimeoutInterval(std::numeric_limits<double>::infinity());
623             m_sendingTime = MonotonicTime::now();
624             m_timeoutTimer.startOneShot(1_ms * m_timeoutMilliseconds);
625         }
626     }
627
628     m_exceptionCode = std::nullopt;
629     m_error = false;
630
631     if (m_async) {
632         // 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.
633         // This is true while running onunload handlers.
634         // FIXME: Maybe we need to be able to send XMLHttpRequests from onunload, <http://bugs.webkit.org/show_bug.cgi?id=10904>.
635         m_loader = ThreadableLoader::create(*scriptExecutionContext(), *this, WTFMove(request), options);
636
637         // Either loader is null or some error was synchronously sent to us.
638         ASSERT(m_loader || !m_sendFlag);
639
640         // Neither this object nor the JavaScript wrapper should be deleted while
641         // a request is in progress because we need to keep the listeners alive,
642         // and they are referenced by the JavaScript wrapper.
643         if (m_loader)
644             setPendingActivity(this);
645     } else {
646         request.setDomainForCachePartition(scriptExecutionContext()->topOrigin().domainForCachePartition());
647         InspectorInstrumentation::willLoadXHRSynchronously(scriptExecutionContext());
648         ThreadableLoader::loadResourceSynchronously(*scriptExecutionContext(), WTFMove(request), *this, options);
649         InspectorInstrumentation::didLoadXHRSynchronously(scriptExecutionContext());
650     }
651
652     if (m_exceptionCode)
653         return Exception { m_exceptionCode.value() };
654     if (m_error)
655         return Exception { NetworkError };
656     return { };
657 }
658
659 void XMLHttpRequest::abort()
660 {
661     // internalAbort() calls dropProtection(), which may release the last reference.
662     Ref<XMLHttpRequest> protectedThis(*this);
663
664     m_wasAbortedByClient = true;
665     if (!internalAbort())
666         return;
667
668     clearResponseBuffers();
669
670     // Clear headers as required by the spec
671     m_requestHeaders.clear();
672     if ((m_state == OPENED && m_sendFlag) || m_state == HEADERS_RECEIVED || m_state == LOADING) {
673         ASSERT(!m_loader);
674         m_sendFlag = false;
675         changeState(DONE);
676         dispatchErrorEvents(eventNames().abortEvent);
677     }
678     m_state = UNSENT;
679 }
680
681 bool XMLHttpRequest::internalAbort()
682 {
683     m_error = true;
684
685     // FIXME: when we add the support for multi-part XHR, we will have to think be careful with this initialization.
686     m_receivedLength = 0;
687
688     m_decoder = nullptr;
689
690     m_timeoutTimer.stop();
691
692     if (!m_loader)
693         return true;
694
695     // Cancelling m_loader may trigger a window.onload callback which can call open() on the same xhr.
696     // This would create internalAbort reentrant call.
697     // m_loader is set to null before being cancelled to exit early in any reentrant internalAbort() call.
698     auto loader = WTFMove(m_loader);
699     loader->cancel();
700
701     // If window.onload callback calls open() and send() on the same xhr, m_loader is now set to a new value.
702     // The function calling internalAbort() should abort to let the open() and send() calls continue properly.
703     // We ask the function calling internalAbort() to exit by returning false.
704     // Save this information to a local variable since we are going to drop protection.
705     bool newLoadStarted = m_loader;
706
707     dropProtection();
708
709     return !newLoadStarted;
710 }
711
712 void XMLHttpRequest::clearResponse()
713 {
714     m_response = ResourceResponse();
715     clearResponseBuffers();
716 }
717
718 void XMLHttpRequest::clearResponseBuffers()
719 {
720     m_responseBuilder.clear();
721     m_responseEncoding = String();
722     m_createdDocument = false;
723     m_responseDocument = nullptr;
724     m_binaryResponseBuilder = nullptr;
725     m_responseCacheIsValid = false;
726 }
727
728 void XMLHttpRequest::clearRequest()
729 {
730     m_requestHeaders.clear();
731     m_requestEntityBody = nullptr;
732 }
733
734 void XMLHttpRequest::genericError()
735 {
736     clearResponse();
737     clearRequest();
738     m_sendFlag = false;
739     m_error = true;
740
741     changeState(DONE);
742 }
743
744 void XMLHttpRequest::networkError()
745 {
746     genericError();
747     dispatchErrorEvents(eventNames().errorEvent);
748     internalAbort();
749 }
750
751 void XMLHttpRequest::networkErrorTimerFired()
752 {
753     networkError();
754     dropProtection();
755 }
756     
757 void XMLHttpRequest::abortError()
758 {
759     ASSERT(m_wasAbortedByClient);
760     genericError();
761     dispatchErrorEvents(eventNames().abortEvent);
762 }
763
764 void XMLHttpRequest::dropProtection()
765 {
766     // The XHR object itself holds on to the responseText, and
767     // thus has extra cost even independent of any
768     // responseText or responseXML objects it has handed
769     // out. But it is protected from GC while loading, so this
770     // can't be recouped until the load is done, so only
771     // report the extra cost at that point.
772     JSC::VM& vm = scriptExecutionContext()->vm();
773     JSC::JSLockHolder lock(vm);
774     // FIXME: Adopt reportExtraMemoryVisited, and switch to reportExtraMemoryAllocated.
775     // https://bugs.webkit.org/show_bug.cgi?id=142595
776     vm.heap.deprecatedReportExtraMemory(m_responseBuilder.length() * 2);
777
778     unsetPendingActivity(this);
779 }
780
781 ExceptionOr<void> XMLHttpRequest::overrideMimeType(const String& override)
782 {
783     if (m_state == LOADING || m_state == DONE)
784         return Exception { InvalidStateError };
785
786     m_mimeTypeOverride = override;
787     return { };
788 }
789
790 ExceptionOr<void> XMLHttpRequest::setRequestHeader(const String& name, const String& value)
791 {
792     if (m_state != OPENED || m_sendFlag) {
793 #if ENABLE(DASHBOARD_SUPPORT)
794         if (usesDashboardBackwardCompatibilityMode())
795             return { };
796 #endif
797         return Exception { InvalidStateError };
798     }
799
800     String normalizedValue = stripLeadingAndTrailingHTTPSpaces(value);
801     if (!isValidHTTPToken(name) || !isValidHTTPHeaderValue(normalizedValue))
802         return Exception { SyntaxError };
803
804     bool allowUnsafeHeaderField = false;
805 #if ENABLE(DASHBOARD_SUPPORT)
806     allowUnsafeHeaderField = usesDashboardBackwardCompatibilityMode();
807 #endif
808     if (!allowUnsafeHeaderField && isForbiddenHeaderName(name)) {
809         logConsoleError(scriptExecutionContext(), "Refused to set unsafe header \"" + name + "\"");
810         return { };
811     }
812
813     m_requestHeaders.add(name, normalizedValue);
814     return { };
815 }
816
817 String XMLHttpRequest::getAllResponseHeaders() const
818 {
819     if (m_state < HEADERS_RECEIVED || m_error)
820         return emptyString();
821
822     if (!m_allResponseHeaders) {
823         Vector<String> headers;
824         headers.reserveInitialCapacity(m_response.httpHeaderFields().size());
825
826         for (auto& header : m_response.httpHeaderFields()) {
827             StringBuilder stringBuilder;
828             stringBuilder.append(header.key.convertToASCIILowercase());
829             stringBuilder.appendLiteral(": ");
830             stringBuilder.append(header.value);
831             stringBuilder.appendLiteral("\r\n");
832             headers.uncheckedAppend(stringBuilder.toString());
833         }
834         std::sort(headers.begin(), headers.end(), WTF::codePointCompareLessThan);
835
836         StringBuilder stringBuilder;
837         for (auto& header : headers)
838             stringBuilder.append(header);
839         m_allResponseHeaders = stringBuilder.toString();
840     }
841
842     return m_allResponseHeaders;
843 }
844
845 String XMLHttpRequest::getResponseHeader(const String& name) const
846 {
847     if (m_state < HEADERS_RECEIVED || m_error)
848         return String();
849
850     return m_response.httpHeaderField(name);
851 }
852
853 String XMLHttpRequest::responseMIMEType() const
854 {
855     String mimeType = extractMIMETypeFromMediaType(m_mimeTypeOverride);
856     if (mimeType.isEmpty()) {
857         if (m_response.isHTTP())
858             mimeType = extractMIMETypeFromMediaType(m_response.httpHeaderField(HTTPHeaderName::ContentType));
859         else
860             mimeType = m_response.mimeType();
861         if (mimeType.isEmpty())
862             mimeType = ASCIILiteral("text/xml");
863     }
864     return mimeType;
865 }
866
867 bool XMLHttpRequest::responseIsXML() const
868 {
869     return MIMETypeRegistry::isXMLMIMEType(responseMIMEType());
870 }
871
872 int XMLHttpRequest::status() const
873 {
874     if (m_state == UNSENT || m_state == OPENED || m_error)
875         return 0;
876
877     return m_response.httpStatusCode();
878 }
879
880 String XMLHttpRequest::statusText() const
881 {
882     if (m_state == UNSENT || m_state == OPENED || m_error)
883         return String();
884
885     return m_response.httpStatusText();
886 }
887
888 void XMLHttpRequest::didFail(const ResourceError& error)
889 {
890     // If we are already in an error state, for instance we called abort(), bail out early.
891     if (m_error)
892         return;
893
894     // The XHR specification says we should only fire an abort event if the cancelation was requested by the client.
895     if (m_wasAbortedByClient && error.isCancellation()) {
896         m_exceptionCode = AbortError;
897         abortError();
898         return;
899     }
900
901     // In case of worker sync timeouts.
902     if (error.isTimeout()) {
903         didReachTimeout();
904         return;
905     }
906
907     // Network failures are already reported to Web Inspector by ResourceLoader.
908     if (error.domain() == errorDomainWebKitInternal) {
909         String message = makeString("XMLHttpRequest cannot load ", error.failingURL().string(), ". ", error.localizedDescription());
910         logConsoleError(scriptExecutionContext(), message);
911     } else if (error.isAccessControl()) {
912         String message = makeString("XMLHttpRequest cannot load ", error.failingURL().string(), " due to access control checks.");
913         logConsoleError(scriptExecutionContext(), message);
914     }
915
916     // In case didFail is called synchronously on an asynchronous XHR call, let's dispatch network error asynchronously
917     if (m_async && m_sendFlag && !m_loader) {
918         m_sendFlag = false;
919         setPendingActivity(this);
920         m_timeoutTimer.stop();
921         m_networkErrorTimer.startOneShot(0_s);
922         return;
923     }
924     m_exceptionCode = NetworkError;
925     networkError();
926 }
927
928 void XMLHttpRequest::didFinishLoading(unsigned long)
929 {
930     if (m_error)
931         return;
932
933     if (m_state < HEADERS_RECEIVED)
934         changeState(HEADERS_RECEIVED);
935
936     if (m_decoder)
937         m_responseBuilder.append(m_decoder->flush());
938
939     m_responseBuilder.shrinkToFit();
940
941     bool hadLoader = m_loader;
942     m_loader = nullptr;
943
944     m_sendFlag = false;
945     changeState(DONE);
946     m_responseEncoding = String();
947     m_decoder = nullptr;
948
949     m_timeoutTimer.stop();
950
951     if (hadLoader)
952         dropProtection();
953 }
954
955 void XMLHttpRequest::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
956 {
957     if (!m_upload)
958         return;
959
960     if (m_uploadEventsAllowed)
961         m_upload->dispatchThrottledProgressEvent(true, bytesSent, totalBytesToBeSent);
962     if (bytesSent == totalBytesToBeSent && !m_uploadComplete) {
963         m_uploadComplete = true;
964         if (m_uploadEventsAllowed) {
965             m_upload->dispatchProgressEvent(eventNames().loadEvent);
966             m_upload->dispatchProgressEvent(eventNames().loadendEvent);
967         }
968     }
969 }
970
971 void XMLHttpRequest::didReceiveResponse(unsigned long, const ResourceResponse& response)
972 {
973     m_response = response;
974     if (!m_mimeTypeOverride.isEmpty())
975         m_response.setHTTPHeaderField(HTTPHeaderName::ContentType, m_mimeTypeOverride);
976 }
977
978 static inline bool shouldDecodeResponse(XMLHttpRequest::ResponseType type)
979 {
980     switch (type) {
981     case XMLHttpRequest::ResponseType::EmptyString:
982     case XMLHttpRequest::ResponseType::Document:
983     case XMLHttpRequest::ResponseType::Json:
984     case XMLHttpRequest::ResponseType::Text:
985         return true;
986     case XMLHttpRequest::ResponseType::Arraybuffer:
987     case XMLHttpRequest::ResponseType::Blob:
988         return false;
989     }
990     ASSERT_NOT_REACHED();
991     return true;
992 }
993
994 Ref<TextResourceDecoder> XMLHttpRequest::createDecoder() const
995 {
996     if (!m_responseEncoding.isEmpty())
997         return TextResourceDecoder::create("text/plain", m_responseEncoding);
998
999     switch (m_responseType) {
1000     case ResponseType::EmptyString:
1001         if (responseIsXML()) {
1002             auto decoder = TextResourceDecoder::create("application/xml");
1003             // 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.
1004             decoder->useLenientXMLDecoding();
1005             return decoder;
1006         }
1007         FALLTHROUGH;
1008     case ResponseType::Text:
1009     case ResponseType::Json:
1010         return TextResourceDecoder::create("text/plain", "UTF-8");
1011     case ResponseType::Document: {
1012         if (equalLettersIgnoringASCIICase(responseMIMEType(), "text/html"))
1013             return TextResourceDecoder::create("text/html", "UTF-8");
1014         auto decoder = TextResourceDecoder::create("application/xml");
1015         // 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.
1016         decoder->useLenientXMLDecoding();
1017         return decoder;
1018     }
1019     case ResponseType::Arraybuffer:
1020     case ResponseType::Blob:
1021         ASSERT_NOT_REACHED();
1022         break;
1023     }
1024     return TextResourceDecoder::create("text/plain", "UTF-8");
1025 }
1026
1027 void XMLHttpRequest::didReceiveData(const char* data, int len)
1028 {
1029     if (m_error)
1030         return;
1031
1032     if (m_state < HEADERS_RECEIVED)
1033         changeState(HEADERS_RECEIVED);
1034
1035     // FIXME: Should we update "Content-Type" header field with m_mimeTypeOverride value in case it has changed since didReceiveResponse?
1036     if (!m_mimeTypeOverride.isEmpty())
1037         m_responseEncoding = extractCharsetFromMediaType(m_mimeTypeOverride);
1038     if (m_responseEncoding.isEmpty())
1039         m_responseEncoding = m_response.textEncodingName();
1040
1041     bool useDecoder = shouldDecodeResponse(m_responseType);
1042
1043     if (useDecoder && !m_decoder)
1044         m_decoder = createDecoder();
1045
1046     if (!len)
1047         return;
1048
1049     if (len == -1)
1050         len = strlen(data);
1051
1052     if (useDecoder)
1053         m_responseBuilder.append(m_decoder->decode(data, len));
1054     else {
1055         // Buffer binary data.
1056         if (!m_binaryResponseBuilder)
1057             m_binaryResponseBuilder = SharedBuffer::create();
1058         m_binaryResponseBuilder->append(data, len);
1059     }
1060
1061     if (!m_error) {
1062         m_receivedLength += len;
1063
1064         if (m_async) {
1065             long long expectedLength = m_response.expectedContentLength();
1066             bool lengthComputable = expectedLength > 0 && m_receivedLength <= expectedLength;
1067             unsigned long long total = lengthComputable ? expectedLength : 0;
1068             m_progressEventThrottle.dispatchThrottledProgressEvent(lengthComputable, m_receivedLength, total);
1069         }
1070
1071         if (m_state != LOADING)
1072             changeState(LOADING);
1073         else
1074             // Firefox calls readyStateChanged every time it receives data, 4449442
1075             callReadyStateChangeListener();
1076     }
1077 }
1078
1079 void XMLHttpRequest::dispatchErrorEvents(const AtomicString& type)
1080 {
1081     if (!m_uploadComplete) {
1082         m_uploadComplete = true;
1083         if (m_upload && m_uploadEventsAllowed) {
1084             m_upload->dispatchProgressEvent(eventNames().progressEvent);
1085             m_upload->dispatchProgressEvent(type);
1086             m_upload->dispatchProgressEvent(eventNames().loadendEvent);
1087         }
1088     }
1089     m_progressEventThrottle.dispatchProgressEvent(eventNames().progressEvent);
1090     m_progressEventThrottle.dispatchProgressEvent(type);
1091     m_progressEventThrottle.dispatchProgressEvent(eventNames().loadendEvent);
1092 }
1093
1094 void XMLHttpRequest::didReachTimeout()
1095 {
1096     // internalAbort() calls dropProtection(), which may release the last reference.
1097     Ref<XMLHttpRequest> protectedThis(*this);
1098     if (!internalAbort())
1099         return;
1100
1101     clearResponse();
1102     clearRequest();
1103
1104     m_sendFlag = false;
1105     m_error = true;
1106     m_exceptionCode = TimeoutError;
1107
1108     if (!m_async) {
1109         m_state = DONE;
1110         m_exceptionCode = TimeoutError;
1111         return;
1112     }
1113
1114     changeState(DONE);
1115
1116     dispatchErrorEvents(eventNames().timeoutEvent);
1117 }
1118
1119 bool XMLHttpRequest::canSuspendForDocumentSuspension() const
1120 {
1121     // If the load event has not fired yet, cancelling the load in suspend() may cause
1122     // the load event to be fired and arbitrary JS execution, which would be unsafe.
1123     // Therefore, we prevent suspending in this case.
1124     return document()->loadEventFinished();
1125 }
1126
1127 const char* XMLHttpRequest::activeDOMObjectName() const
1128 {
1129     return "XMLHttpRequest";
1130 }
1131
1132 void XMLHttpRequest::suspend(ReasonForSuspension reason)
1133 {
1134     m_progressEventThrottle.suspend();
1135
1136     if (m_resumeTimer.isActive()) {
1137         m_resumeTimer.stop();
1138         m_dispatchErrorOnResuming = true;
1139     }
1140
1141     if (reason == ActiveDOMObject::PageCache && m_loader) {
1142         // Going into PageCache, abort the request and dispatch a network error on resuming.
1143         genericError();
1144         m_dispatchErrorOnResuming = true;
1145         bool aborted = internalAbort();
1146         // It should not be possible to restart the load when aborting in suspend() because
1147         // we are not allowed to execute in JS in suspend().
1148         ASSERT_UNUSED(aborted, aborted);
1149     }
1150 }
1151
1152 void XMLHttpRequest::resume()
1153 {
1154     m_progressEventThrottle.resume();
1155
1156     // We are not allowed to execute arbitrary JS in resume() so dispatch
1157     // the error event in a timer.
1158     if (m_dispatchErrorOnResuming && !m_resumeTimer.isActive())
1159         m_resumeTimer.startOneShot(0_s);
1160 }
1161
1162 void XMLHttpRequest::resumeTimerFired()
1163 {
1164     ASSERT(m_dispatchErrorOnResuming);
1165     m_dispatchErrorOnResuming = false;
1166     dispatchErrorEvents(eventNames().errorEvent);
1167 }
1168
1169 void XMLHttpRequest::stop()
1170 {
1171     internalAbort();
1172 }
1173
1174 void XMLHttpRequest::contextDestroyed()
1175 {
1176     ASSERT(!m_loader);
1177     ActiveDOMObject::contextDestroyed();
1178 }
1179
1180 } // namespace WebCore