XMLHTTPRequest.send for Document should have same Content-Type processing rules as...
[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 "RuntimeApplicationChecks.h"
48 #include "SecurityOriginPolicy.h"
49 #include "Settings.h"
50 #include "SharedBuffer.h"
51 #include "StringAdaptors.h"
52 #include "TextResourceDecoder.h"
53 #include "ThreadableLoader.h"
54 #include "XMLDocument.h"
55 #include "XMLHttpRequestProgressEvent.h"
56 #include "XMLHttpRequestUpload.h"
57 #include "markup.h"
58 #include <JavaScriptCore/ArrayBuffer.h>
59 #include <JavaScriptCore/ArrayBufferView.h>
60 #include <JavaScriptCore/JSCInlines.h>
61 #include <JavaScriptCore/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_async(true)
115     , m_includeCredentials(false)
116     , m_sendFlag(false)
117     , m_createdDocument(false)
118     , m_error(false)
119     , m_uploadListenerFlag(false)
120     , m_uploadComplete(false)
121     , m_wasAbortedByClient(false)
122     , m_responseCacheIsValid(false)
123     , m_dispatchErrorOnResuming(false)
124     , m_readyState(static_cast<unsigned>(UNSENT))
125     , m_responseType(static_cast<unsigned>(ResponseType::EmptyString))
126     , m_progressEventThrottle(this)
127     , m_resumeTimer(*this, &XMLHttpRequest::resumeTimerFired)
128     , m_networkErrorTimer(*this, &XMLHttpRequest::networkErrorTimerFired)
129     , m_timeoutTimer(*this, &XMLHttpRequest::didReachTimeout)
130 {
131 #ifndef NDEBUG
132     xmlHttpRequestCounter.increment();
133 #endif
134 }
135
136 XMLHttpRequest::~XMLHttpRequest()
137 {
138 #ifndef NDEBUG
139     xmlHttpRequestCounter.decrement();
140 #endif
141 }
142
143 Document* XMLHttpRequest::document() const
144 {
145     ASSERT(scriptExecutionContext());
146     return downcast<Document>(scriptExecutionContext());
147 }
148
149 SecurityOrigin* XMLHttpRequest::securityOrigin() const
150 {
151     return scriptExecutionContext()->securityOrigin();
152 }
153
154 #if ENABLE(DASHBOARD_SUPPORT)
155
156 bool XMLHttpRequest::usesDashboardBackwardCompatibilityMode() const
157 {
158     if (scriptExecutionContext()->isWorkerGlobalScope())
159         return false;
160     return document()->settings().usesDashboardBackwardCompatibilityMode();
161 }
162
163 #endif
164
165 ExceptionOr<OwnedString> XMLHttpRequest::responseText()
166 {
167     if (responseType() != ResponseType::EmptyString && responseType() != ResponseType::Text)
168         return Exception { InvalidStateError };
169     return OwnedString { responseTextIgnoringResponseType() };
170 }
171
172 void XMLHttpRequest::didCacheResponse()
173 {
174     ASSERT(doneWithoutErrors());
175     m_responseCacheIsValid = true;
176     m_responseBuilder.clear();
177 }
178
179 ExceptionOr<Document*> XMLHttpRequest::responseXML()
180 {
181     ASSERT(scriptExecutionContext()->isDocument());
182     
183     if (responseType() != ResponseType::EmptyString && responseType() != ResponseType::Document)
184         return Exception { InvalidStateError };
185
186     if (!doneWithoutErrors())
187         return nullptr;
188
189     if (!m_createdDocument) {
190         String mimeType = responseMIMEType();
191         bool isHTML = equalLettersIgnoringASCIICase(mimeType, "text/html");
192
193         // The W3C spec requires the final MIME type to be some valid XML type, or text/html.
194         // If it is text/html, then the responseType of "document" must have been supplied explicitly.
195         if ((m_response.isHTTP() && !responseIsXML() && !isHTML)
196             || (isHTML && responseType() == ResponseType::EmptyString)) {
197             m_responseDocument = nullptr;
198         } else {
199             if (isHTML)
200                 m_responseDocument = HTMLDocument::create(0, m_url);
201             else
202                 m_responseDocument = XMLDocument::create(0, m_url);
203             // FIXME: Set Last-Modified.
204             m_responseDocument->setContent(m_responseBuilder.toStringPreserveCapacity());
205             m_responseDocument->setContextDocument(downcast<Document>(*scriptExecutionContext()));
206             m_responseDocument->setSecurityOriginPolicy(scriptExecutionContext()->securityOriginPolicy());
207             m_responseDocument->overrideMIMEType(mimeType);
208
209             if (!m_responseDocument->wellFormed())
210                 m_responseDocument = nullptr;
211         }
212         m_createdDocument = true;
213     }
214
215     return m_responseDocument.get();
216 }
217
218 Ref<Blob> XMLHttpRequest::createResponseBlob()
219 {
220     ASSERT(responseType() == ResponseType::Blob);
221     ASSERT(doneWithoutErrors());
222
223     if (!m_binaryResponseBuilder)
224         return Blob::create();
225
226     // FIXME: We just received the data from NetworkProcess, and are sending it back. This is inefficient.
227     Vector<uint8_t> data;
228     data.append(m_binaryResponseBuilder->data(), m_binaryResponseBuilder->size());
229     m_binaryResponseBuilder = nullptr;
230     String normalizedContentType = Blob::normalizedContentType(responseMIMEType()); // responseMIMEType defaults to text/xml which may be incorrect.
231     return Blob::create(WTFMove(data), normalizedContentType);
232 }
233
234 RefPtr<ArrayBuffer> XMLHttpRequest::createResponseArrayBuffer()
235 {
236     ASSERT(responseType() == ResponseType::Arraybuffer);
237     ASSERT(doneWithoutErrors());
238
239     auto result = m_binaryResponseBuilder ? m_binaryResponseBuilder->tryCreateArrayBuffer() : ArrayBuffer::create(nullptr, 0);
240     m_binaryResponseBuilder = nullptr;
241     return result;
242 }
243
244 ExceptionOr<void> XMLHttpRequest::setTimeout(unsigned timeout)
245 {
246     if (scriptExecutionContext()->isDocument() && !m_async) {
247         logConsoleError(scriptExecutionContext(), "XMLHttpRequest.timeout cannot be set for synchronous HTTP(S) requests made from the window context.");
248         return Exception { InvalidAccessError };
249     }
250     m_timeoutMilliseconds = timeout;
251     if (!m_timeoutTimer.isActive())
252         return { };
253
254     // 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.
255     Seconds interval = Seconds { m_timeoutMilliseconds ? m_timeoutMilliseconds / 1000. : 60. } - (MonotonicTime::now() - m_sendingTime);
256     m_timeoutTimer.startOneShot(std::max(interval, 0_s));
257     return { };
258 }
259
260 ExceptionOr<void> XMLHttpRequest::setResponseType(ResponseType type)
261 {
262     if (!scriptExecutionContext()->isDocument() && type == ResponseType::Document)
263         return { };
264
265     if (readyState() >= LOADING)
266         return Exception { InvalidStateError };
267
268     // Newer functionality is not available to synchronous requests in window contexts, as a spec-mandated
269     // attempt to discourage synchronous XHR use. responseType is one such piece of functionality.
270     // We'll only disable this functionality for HTTP(S) requests since sync requests for local protocols
271     // such as file: and data: still make sense to allow.
272     if (!m_async && scriptExecutionContext()->isDocument() && m_url.protocolIsInHTTPFamily()) {
273         logConsoleError(scriptExecutionContext(), "XMLHttpRequest.responseType cannot be changed for synchronous HTTP(S) requests made from the window context.");
274         return Exception { InvalidAccessError };
275     }
276
277     m_responseType = static_cast<unsigned>(type);
278     return { };
279 }
280
281 String XMLHttpRequest::responseURL() const
282 {
283     URL responseURL(m_response.url());
284     responseURL.removeFragmentIdentifier();
285
286     return responseURL.string();
287 }
288
289 XMLHttpRequestUpload& XMLHttpRequest::upload()
290 {
291     if (!m_upload)
292         m_upload = std::make_unique<XMLHttpRequestUpload>(*this);
293     return *m_upload;
294 }
295
296 void XMLHttpRequest::changeState(State newState)
297 {
298     if (readyState() != newState) {
299         m_readyState = static_cast<State>(newState);
300         callReadyStateChangeListener();
301     }
302 }
303
304 void XMLHttpRequest::callReadyStateChangeListener()
305 {
306     if (!scriptExecutionContext())
307         return;
308
309     // Check whether sending load and loadend events before sending readystatechange event, as it may change m_error/m_readyState values.
310     bool shouldSendLoadEvent = (readyState() == DONE && !m_error);
311
312     if (m_async || (readyState() <= OPENED || readyState() == DONE)) {
313         m_progressEventThrottle.dispatchReadyStateChangeEvent(Event::create(eventNames().readystatechangeEvent, Event::CanBubble::No, Event::IsCancelable::No),
314             readyState() == DONE ? FlushProgressEvent : DoNotFlushProgressEvent);
315     }
316
317     if (shouldSendLoadEvent) {
318         m_progressEventThrottle.dispatchProgressEvent(eventNames().loadEvent);
319         m_progressEventThrottle.dispatchProgressEvent(eventNames().loadendEvent);
320     }
321 }
322
323 ExceptionOr<void> XMLHttpRequest::setWithCredentials(bool value)
324 {
325     if (readyState() > OPENED || m_sendFlag)
326         return Exception { InvalidStateError };
327
328     m_includeCredentials = value;
329     return { };
330 }
331
332 ExceptionOr<void> XMLHttpRequest::open(const String& method, const String& url)
333 {
334     // If the async argument is omitted, set async to true.
335     return open(method, scriptExecutionContext()->completeURL(url), true);
336 }
337
338 ExceptionOr<void> XMLHttpRequest::open(const String& method, const URL& url, bool async)
339 {
340     if (!isValidHTTPToken(method))
341         return Exception { SyntaxError };
342
343     if (isForbiddenMethod(method))
344         return Exception { SecurityError };
345
346     if (!async && scriptExecutionContext()->isDocument()) {
347         // Newer functionality is not available to synchronous requests in window contexts, as a spec-mandated
348         // attempt to discourage synchronous XHR use. responseType is one such piece of functionality.
349         // We'll only disable this functionality for HTTP(S) requests since sync requests for local protocols
350         // such as file: and data: still make sense to allow.
351         if (url.protocolIsInHTTPFamily() && responseType() != ResponseType::EmptyString) {
352             logConsoleError(scriptExecutionContext(), "Synchronous HTTP(S) requests made from the window context cannot have XMLHttpRequest.responseType set.");
353             return Exception { InvalidAccessError };
354         }
355
356         // Similarly, timeouts are disabled for synchronous requests as well.
357         if (m_timeoutMilliseconds > 0) {
358             logConsoleError(scriptExecutionContext(), "Synchronous XMLHttpRequests must not have a timeout value set.");
359             return Exception { InvalidAccessError };
360         }
361     }
362
363     if (!internalAbort())
364         return { };
365
366     m_sendFlag = false;
367     m_uploadListenerFlag = false;
368     m_method = normalizeHTTPMethod(method);
369     m_error = false;
370     m_uploadComplete = false;
371     m_wasAbortedByClient = false;
372
373     // clear stuff from possible previous load
374     clearResponse();
375     clearRequest();
376
377     m_url = url;
378     scriptExecutionContext()->contentSecurityPolicy()->upgradeInsecureRequestIfNeeded(m_url, ContentSecurityPolicy::InsecureRequestType::Load);
379
380     m_async = async;
381
382     ASSERT(!m_loader);
383
384     changeState(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 (readyState() != 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, "application/x-www-form-urlencoded"_s);
460             else
461 #endif
462                 // FIXME: this should include the charset used for encoding.
463                 m_requestHeaders.set(HTTPHeaderName::ContentType, document.isHTMLDocument() ? "text/html;charset=UTF-8"_s : "application/xml;charset=UTF-8"_s);
464         } else {
465             String contentType = m_requestHeaders.get(HTTPHeaderName::ContentType);
466             replaceCharsetInMediaType(contentType, "UTF-8");
467             m_requestHeaders.set(HTTPHeaderName::ContentType, contentType);
468         }
469
470         // FIXME: According to XMLHttpRequest Level 2, this should use the Document.innerHTML algorithm
471         // from the HTML5 specification to serialize the document.
472         m_requestEntityBody = FormData::create(UTF8Encoding().encode(createMarkup(document), UnencodableHandling::Entities));
473         if (m_upload)
474             m_requestEntityBody->setAlwaysStream(true);
475     }
476
477     return createRequest();
478 }
479
480 ExceptionOr<void> XMLHttpRequest::send(const String& body)
481 {
482     if (auto result = prepareToSend())
483         return WTFMove(result.value());
484
485     if (!body.isNull() && m_method != "GET" && m_method != "HEAD" && m_url.protocolIsInHTTPFamily()) {
486         String contentType = m_requestHeaders.get(HTTPHeaderName::ContentType);
487         if (contentType.isNull()) {
488 #if ENABLE(DASHBOARD_SUPPORT)
489             if (usesDashboardBackwardCompatibilityMode())
490                 m_requestHeaders.set(HTTPHeaderName::ContentType, "application/x-www-form-urlencoded"_s);
491             else
492 #endif
493                 m_requestHeaders.set(HTTPHeaderName::ContentType, HTTPHeaderValues::textPlainContentType());
494         } else {
495             replaceCharsetInMediaType(contentType, "UTF-8");
496             m_requestHeaders.set(HTTPHeaderName::ContentType, contentType);
497         }
498
499         m_requestEntityBody = FormData::create(UTF8Encoding().encode(body, UnencodableHandling::Entities));
500         if (m_upload)
501             m_requestEntityBody->setAlwaysStream(true);
502     }
503
504     return createRequest();
505 }
506
507 ExceptionOr<void> XMLHttpRequest::send(Blob& body)
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             const String& blobType = body.type();
515             if (!blobType.isEmpty() && isValidContentType(blobType))
516                 m_requestHeaders.set(HTTPHeaderName::ContentType, blobType);
517             else {
518                 // From FileAPI spec, whenever media type cannot be determined, empty string must be returned.
519                 m_requestHeaders.set(HTTPHeaderName::ContentType, emptyString());
520             }
521         }
522
523         m_requestEntityBody = FormData::create();
524         m_requestEntityBody->appendBlob(body.url());
525     }
526
527     return createRequest();
528 }
529
530 ExceptionOr<void> XMLHttpRequest::send(DOMFormData& body)
531 {
532     if (auto result = prepareToSend())
533         return WTFMove(result.value());
534
535     if (m_method != "GET" && m_method != "HEAD" && m_url.protocolIsInHTTPFamily()) {
536         m_requestEntityBody = FormData::createMultiPart(body, document());
537         m_requestEntityBody->generateFiles(document());
538         if (!m_requestHeaders.contains(HTTPHeaderName::ContentType))
539             m_requestHeaders.set(HTTPHeaderName::ContentType, makeString("multipart/form-data; boundary=", m_requestEntityBody->boundary().data()));
540     }
541
542     return createRequest();
543 }
544
545 ExceptionOr<void> XMLHttpRequest::send(ArrayBuffer& body)
546 {
547     ASCIILiteral consoleMessage { "ArrayBuffer is deprecated in XMLHttpRequest.send(). Use ArrayBufferView instead."_s };
548     scriptExecutionContext()->addConsoleMessage(MessageSource::JS, MessageLevel::Warning, consoleMessage);
549     return sendBytesData(body.data(), body.byteLength());
550 }
551
552 ExceptionOr<void> XMLHttpRequest::send(ArrayBufferView& body)
553 {
554     return sendBytesData(body.baseAddress(), body.byteLength());
555 }
556
557 ExceptionOr<void> XMLHttpRequest::sendBytesData(const void* data, size_t length)
558 {
559     if (auto result = prepareToSend())
560         return WTFMove(result.value());
561
562     if (m_method != "GET" && m_method != "HEAD" && m_url.protocolIsInHTTPFamily()) {
563         m_requestEntityBody = FormData::create(data, length);
564         if (m_upload)
565             m_requestEntityBody->setAlwaysStream(true);
566     }
567
568     return createRequest();
569 }
570
571 ExceptionOr<void> XMLHttpRequest::createRequest()
572 {
573     // Only GET request is supported for blob URL.
574     if (!m_async && m_url.protocolIsBlob() && m_method != "GET")
575         return Exception { NetworkError };
576
577     if (m_async && m_upload && m_upload->hasEventListeners())
578         m_uploadListenerFlag = true;
579
580     ResourceRequest request(m_url);
581     request.setRequester(ResourceRequest::Requester::XHR);
582     request.setInitiatorIdentifier(scriptExecutionContext()->resourceRequestIdentifier());
583     request.setHTTPMethod(m_method);
584
585     if (m_requestEntityBody) {
586         ASSERT(m_method != "GET");
587         ASSERT(m_method != "HEAD");
588         request.setHTTPBody(WTFMove(m_requestEntityBody));
589     }
590
591     if (!m_requestHeaders.isEmpty())
592         request.setHTTPHeaderFields(m_requestHeaders);
593
594     ThreadableLoaderOptions options;
595     options.sendLoadCallbacks = SendCallbackPolicy::SendCallbacks;
596     // The presence of upload event listeners forces us to use preflighting because POSTing to an URL that does not
597     // permit cross origin requests should look exactly like POSTing to an URL that does not respond at all.
598     options.preflightPolicy = m_uploadListenerFlag ? PreflightPolicy::Force : PreflightPolicy::Consider;
599     options.credentials = m_includeCredentials ? FetchOptions::Credentials::Include : FetchOptions::Credentials::SameOrigin;
600     options.mode = FetchOptions::Mode::Cors;
601     options.contentSecurityPolicyEnforcement = scriptExecutionContext()->shouldBypassMainWorldContentSecurityPolicy() ? ContentSecurityPolicyEnforcement::DoNotEnforce : ContentSecurityPolicyEnforcement::EnforceConnectSrcDirective;
602     options.initiator = cachedResourceRequestInitiators().xmlhttprequest;
603     options.sameOriginDataURLFlag = SameOriginDataURLFlag::Set;
604     options.filteringPolicy = ResponseFilteringPolicy::Enable;
605     options.sniffContentEncoding = ContentEncodingSniffingPolicy::DoNotSniff;
606
607     if (m_timeoutMilliseconds) {
608         if (!m_async)
609             request.setTimeoutInterval(m_timeoutMilliseconds / 1000.0);
610         else {
611             request.setTimeoutInterval(std::numeric_limits<double>::infinity());
612             m_sendingTime = MonotonicTime::now();
613             m_timeoutTimer.startOneShot(1_ms * m_timeoutMilliseconds);
614         }
615     }
616
617     m_exceptionCode = std::nullopt;
618     m_error = false;
619     m_uploadComplete = !request.httpBody();
620     m_sendFlag = true;
621
622     if (m_async) {
623         m_progressEventThrottle.dispatchProgressEvent(eventNames().loadstartEvent);
624         if (!m_uploadComplete && m_uploadListenerFlag)
625             m_upload->dispatchProgressEvent(eventNames().loadstartEvent);
626
627         if (readyState() != OPENED || !m_sendFlag || m_loader)
628             return { };
629
630         // 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.
631         // This is true while running onunload handlers.
632         // FIXME: Maybe we need to be able to send XMLHttpRequests from onunload, <http://bugs.webkit.org/show_bug.cgi?id=10904>.
633         m_loader = ThreadableLoader::create(*scriptExecutionContext(), *this, WTFMove(request), options);
634
635         // Either loader is null or some error was synchronously sent to us.
636         ASSERT(m_loader || !m_sendFlag);
637
638         // Neither this object nor the JavaScript wrapper should be deleted while
639         // a request is in progress because we need to keep the listeners alive,
640         // and they are referenced by the JavaScript wrapper.
641         if (m_loader)
642             setPendingActivity(this);
643     } else {
644         request.setDomainForCachePartition(scriptExecutionContext()->domainForCachePartition());
645         InspectorInstrumentation::willLoadXHRSynchronously(scriptExecutionContext());
646         ThreadableLoader::loadResourceSynchronously(*scriptExecutionContext(), WTFMove(request), *this, options);
647         InspectorInstrumentation::didLoadXHRSynchronously(scriptExecutionContext());
648     }
649
650     if (m_exceptionCode)
651         return Exception { m_exceptionCode.value() };
652     if (m_error)
653         return Exception { NetworkError };
654     return { };
655 }
656
657 void XMLHttpRequest::abort()
658 {
659     // internalAbort() calls dropProtection(), which may release the last reference.
660     Ref<XMLHttpRequest> protectedThis(*this);
661
662     m_wasAbortedByClient = true;
663     if (!internalAbort())
664         return;
665
666     clearResponseBuffers();
667
668     m_requestHeaders.clear();
669     if ((readyState() == OPENED && m_sendFlag) || readyState() == HEADERS_RECEIVED || readyState() == LOADING) {
670         ASSERT(!m_loader);
671         m_sendFlag = false;
672         changeState(DONE);
673         dispatchErrorEvents(eventNames().abortEvent);
674     }
675     if (readyState() == DONE)
676         m_readyState = static_cast<State>(UNSENT);
677 }
678
679 bool XMLHttpRequest::internalAbort()
680 {
681     m_error = true;
682
683     // FIXME: when we add the support for multi-part XHR, we will have to think be careful with this initialization.
684     m_receivedLength = 0;
685
686     m_decoder = nullptr;
687
688     m_timeoutTimer.stop();
689
690     if (!m_loader)
691         return true;
692
693     // Cancelling m_loader may trigger a window.onload callback which can call open() on the same xhr.
694     // This would create internalAbort reentrant call.
695     // m_loader is set to null before being cancelled to exit early in any reentrant internalAbort() call.
696     auto loader = WTFMove(m_loader);
697     loader->cancel();
698
699     // If window.onload callback calls open() and send() on the same xhr, m_loader is now set to a new value.
700     // The function calling internalAbort() should abort to let the open() and send() calls continue properly.
701     // We ask the function calling internalAbort() to exit by returning false.
702     // Save this information to a local variable since we are going to drop protection.
703     bool newLoadStarted = m_loader;
704
705     dropProtection();
706
707     return !newLoadStarted;
708 }
709
710 void XMLHttpRequest::clearResponse()
711 {
712     m_response = ResourceResponse();
713     clearResponseBuffers();
714 }
715
716 void XMLHttpRequest::clearResponseBuffers()
717 {
718     m_responseBuilder.clear();
719     m_responseEncoding = String();
720     m_createdDocument = false;
721     m_responseDocument = nullptr;
722     m_binaryResponseBuilder = nullptr;
723     m_responseCacheIsValid = false;
724 }
725
726 void XMLHttpRequest::clearRequest()
727 {
728     m_requestHeaders.clear();
729     m_requestEntityBody = nullptr;
730 }
731
732 void XMLHttpRequest::genericError()
733 {
734     clearResponse();
735     clearRequest();
736     m_sendFlag = false;
737     m_error = true;
738
739     changeState(DONE);
740 }
741
742 void XMLHttpRequest::networkError()
743 {
744     genericError();
745     dispatchErrorEvents(eventNames().errorEvent);
746     internalAbort();
747 }
748
749 void XMLHttpRequest::networkErrorTimerFired()
750 {
751     networkError();
752     dropProtection();
753 }
754     
755 void XMLHttpRequest::abortError()
756 {
757     ASSERT(m_wasAbortedByClient);
758     genericError();
759     dispatchErrorEvents(eventNames().abortEvent);
760 }
761
762 void XMLHttpRequest::dropProtection()
763 {
764     // The XHR object itself holds on to the responseText, and
765     // thus has extra cost even independent of any
766     // responseText or responseXML objects it has handed
767     // out. But it is protected from GC while loading, so this
768     // can't be recouped until the load is done, so only
769     // report the extra cost at that point.
770     JSC::VM& vm = scriptExecutionContext()->vm();
771     JSC::JSLockHolder lock(vm);
772     // FIXME: Adopt reportExtraMemoryVisited, and switch to reportExtraMemoryAllocated.
773     // https://bugs.webkit.org/show_bug.cgi?id=142595
774     vm.heap.deprecatedReportExtraMemory(m_responseBuilder.length() * 2);
775
776     unsetPendingActivity(this);
777 }
778
779 ExceptionOr<void> XMLHttpRequest::overrideMimeType(const String& override)
780 {
781     if (readyState() == LOADING || readyState() == DONE)
782         return Exception { InvalidStateError };
783
784     m_mimeTypeOverride = override;
785     return { };
786 }
787
788 ExceptionOr<void> XMLHttpRequest::setRequestHeader(const String& name, const String& value)
789 {
790     if (readyState() != OPENED || m_sendFlag) {
791 #if ENABLE(DASHBOARD_SUPPORT)
792         if (usesDashboardBackwardCompatibilityMode())
793             return { };
794 #endif
795         return Exception { InvalidStateError };
796     }
797
798     String normalizedValue = stripLeadingAndTrailingHTTPSpaces(value);
799     if (!isValidHTTPToken(name) || !isValidHTTPHeaderValue(normalizedValue))
800         return Exception { SyntaxError };
801
802     bool allowUnsafeHeaderField = false;
803 #if ENABLE(DASHBOARD_SUPPORT)
804     allowUnsafeHeaderField = usesDashboardBackwardCompatibilityMode();
805 #endif
806     if (securityOrigin()->canLoadLocalResources() && document()->settings().allowSettingAnyXHRHeaderFromFileURLs())
807         allowUnsafeHeaderField = true;
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 (readyState() < 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 (readyState() < 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 = "text/xml"_s;
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 (readyState() == UNSENT || readyState() == OPENED || m_error)
875         return 0;
876
877     return m_response.httpStatusCode();
878 }
879
880 String XMLHttpRequest::statusText() const
881 {
882     if (readyState() == UNSENT || readyState() == 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     // In case didFail is called synchronously on an asynchronous XHR call, let's dispatch network error asynchronously
908     if (m_async && m_sendFlag && !m_loader) {
909         m_sendFlag = false;
910         setPendingActivity(this);
911         m_timeoutTimer.stop();
912         m_networkErrorTimer.startOneShot(0_s);
913         return;
914     }
915     m_exceptionCode = NetworkError;
916     networkError();
917 }
918
919 void XMLHttpRequest::didFinishLoading(unsigned long)
920 {
921     if (m_error)
922         return;
923
924     if (readyState() < HEADERS_RECEIVED)
925         changeState(HEADERS_RECEIVED);
926
927     if (m_decoder)
928         m_responseBuilder.append(m_decoder->flush());
929
930     m_responseBuilder.shrinkToFit();
931
932     bool hadLoader = m_loader;
933     m_loader = nullptr;
934
935     m_sendFlag = false;
936     changeState(DONE);
937     m_responseEncoding = String();
938     m_decoder = nullptr;
939
940     m_timeoutTimer.stop();
941
942     if (hadLoader)
943         dropProtection();
944 }
945
946 void XMLHttpRequest::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
947 {
948     if (!m_upload)
949         return;
950
951     if (m_uploadListenerFlag)
952         m_upload->dispatchThrottledProgressEvent(true, bytesSent, totalBytesToBeSent);
953
954     if (bytesSent == totalBytesToBeSent && !m_uploadComplete) {
955         m_uploadComplete = true;
956         if (m_uploadListenerFlag) {
957             m_upload->dispatchProgressEvent(eventNames().loadEvent);
958             m_upload->dispatchProgressEvent(eventNames().loadendEvent);
959         }
960     }
961 }
962
963 void XMLHttpRequest::didReceiveResponse(unsigned long, const ResourceResponse& response)
964 {
965     m_response = response;
966     if (!m_mimeTypeOverride.isEmpty())
967         m_response.setHTTPHeaderField(HTTPHeaderName::ContentType, m_mimeTypeOverride);
968 }
969
970 static inline bool shouldDecodeResponse(XMLHttpRequest::ResponseType type)
971 {
972     switch (type) {
973     case XMLHttpRequest::ResponseType::EmptyString:
974     case XMLHttpRequest::ResponseType::Document:
975     case XMLHttpRequest::ResponseType::Json:
976     case XMLHttpRequest::ResponseType::Text:
977         return true;
978     case XMLHttpRequest::ResponseType::Arraybuffer:
979     case XMLHttpRequest::ResponseType::Blob:
980         return false;
981     }
982     ASSERT_NOT_REACHED();
983     return true;
984 }
985
986 Ref<TextResourceDecoder> XMLHttpRequest::createDecoder() const
987 {
988     if (!m_responseEncoding.isEmpty())
989         return TextResourceDecoder::create("text/plain", m_responseEncoding);
990
991     switch (responseType()) {
992     case ResponseType::EmptyString:
993         if (responseIsXML()) {
994             auto decoder = TextResourceDecoder::create("application/xml");
995             // 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.
996             decoder->useLenientXMLDecoding();
997             return decoder;
998         }
999         FALLTHROUGH;
1000     case ResponseType::Text:
1001     case ResponseType::Json:
1002         return TextResourceDecoder::create("text/plain", "UTF-8");
1003     case ResponseType::Document: {
1004         if (equalLettersIgnoringASCIICase(responseMIMEType(), "text/html"))
1005             return TextResourceDecoder::create("text/html", "UTF-8");
1006         auto decoder = TextResourceDecoder::create("application/xml");
1007         // 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.
1008         decoder->useLenientXMLDecoding();
1009         return decoder;
1010     }
1011     case ResponseType::Arraybuffer:
1012     case ResponseType::Blob:
1013         ASSERT_NOT_REACHED();
1014         break;
1015     }
1016     return TextResourceDecoder::create("text/plain", "UTF-8");
1017 }
1018
1019 void XMLHttpRequest::didReceiveData(const char* data, int len)
1020 {
1021     if (m_error)
1022         return;
1023
1024     if (readyState() < HEADERS_RECEIVED)
1025         changeState(HEADERS_RECEIVED);
1026
1027     // FIXME: Should we update "Content-Type" header field with m_mimeTypeOverride value in case it has changed since didReceiveResponse?
1028     if (!m_mimeTypeOverride.isEmpty())
1029         m_responseEncoding = extractCharsetFromMediaType(m_mimeTypeOverride);
1030     if (m_responseEncoding.isEmpty())
1031         m_responseEncoding = m_response.textEncodingName();
1032
1033     bool useDecoder = shouldDecodeResponse(responseType());
1034
1035     if (useDecoder && !m_decoder)
1036         m_decoder = createDecoder();
1037
1038     if (!len)
1039         return;
1040
1041     if (len == -1)
1042         len = strlen(data);
1043
1044     if (useDecoder)
1045         m_responseBuilder.append(m_decoder->decode(data, len));
1046     else {
1047         // Buffer binary data.
1048         if (!m_binaryResponseBuilder)
1049             m_binaryResponseBuilder = SharedBuffer::create();
1050         m_binaryResponseBuilder->append(data, len);
1051     }
1052
1053     if (!m_error) {
1054         m_receivedLength += len;
1055
1056         if (m_async) {
1057             long long expectedLength = m_response.expectedContentLength();
1058             bool lengthComputable = expectedLength > 0 && m_receivedLength <= expectedLength;
1059             unsigned long long total = lengthComputable ? expectedLength : 0;
1060             m_progressEventThrottle.dispatchThrottledProgressEvent(lengthComputable, m_receivedLength, total);
1061         }
1062
1063         if (readyState() != LOADING)
1064             changeState(LOADING);
1065         else
1066             // Firefox calls readyStateChanged every time it receives data, 4449442
1067             callReadyStateChangeListener();
1068     }
1069 }
1070
1071 void XMLHttpRequest::dispatchErrorEvents(const AtomicString& type)
1072 {
1073     if (!m_uploadComplete) {
1074         m_uploadComplete = true;
1075         if (m_upload && m_uploadListenerFlag) {
1076             m_upload->dispatchProgressEvent(type);
1077             m_upload->dispatchProgressEvent(eventNames().loadendEvent);
1078         }
1079     }
1080     m_progressEventThrottle.dispatchProgressEvent(type);
1081     m_progressEventThrottle.dispatchProgressEvent(eventNames().loadendEvent);
1082 }
1083
1084 void XMLHttpRequest::didReachTimeout()
1085 {
1086     // internalAbort() calls dropProtection(), which may release the last reference.
1087     Ref<XMLHttpRequest> protectedThis(*this);
1088     if (!internalAbort())
1089         return;
1090
1091     clearResponse();
1092     clearRequest();
1093
1094     m_sendFlag = false;
1095     m_error = true;
1096     m_exceptionCode = TimeoutError;
1097
1098     if (!m_async) {
1099         m_readyState = static_cast<State>(DONE);
1100         m_exceptionCode = TimeoutError;
1101         return;
1102     }
1103
1104     changeState(DONE);
1105
1106     dispatchErrorEvents(eventNames().timeoutEvent);
1107 }
1108
1109 bool XMLHttpRequest::canSuspendForDocumentSuspension() const
1110 {
1111     // If the load event has not fired yet, cancelling the load in suspend() may cause
1112     // the load event to be fired and arbitrary JS execution, which would be unsafe.
1113     // Therefore, we prevent suspending in this case.
1114     return document()->loadEventFinished();
1115 }
1116
1117 const char* XMLHttpRequest::activeDOMObjectName() const
1118 {
1119     return "XMLHttpRequest";
1120 }
1121
1122 void XMLHttpRequest::suspend(ReasonForSuspension reason)
1123 {
1124     m_progressEventThrottle.suspend();
1125
1126     if (m_resumeTimer.isActive()) {
1127         m_resumeTimer.stop();
1128         m_dispatchErrorOnResuming = true;
1129     }
1130
1131     if (reason == ReasonForSuspension::PageCache && m_loader) {
1132         // Going into PageCache, abort the request and dispatch a network error on resuming.
1133         genericError();
1134         m_dispatchErrorOnResuming = true;
1135         bool aborted = internalAbort();
1136         // It should not be possible to restart the load when aborting in suspend() because
1137         // we are not allowed to execute in JS in suspend().
1138         ASSERT_UNUSED(aborted, aborted);
1139     }
1140 }
1141
1142 void XMLHttpRequest::resume()
1143 {
1144     m_progressEventThrottle.resume();
1145
1146     // We are not allowed to execute arbitrary JS in resume() so dispatch
1147     // the error event in a timer.
1148     if (m_dispatchErrorOnResuming && !m_resumeTimer.isActive())
1149         m_resumeTimer.startOneShot(0_s);
1150 }
1151
1152 void XMLHttpRequest::resumeTimerFired()
1153 {
1154     ASSERT(m_dispatchErrorOnResuming);
1155     m_dispatchErrorOnResuming = false;
1156     dispatchErrorEvents(eventNames().errorEvent);
1157 }
1158
1159 void XMLHttpRequest::stop()
1160 {
1161     internalAbort();
1162 }
1163
1164 void XMLHttpRequest::contextDestroyed()
1165 {
1166     ASSERT(!m_loader);
1167     ActiveDOMObject::contextDestroyed();
1168 }
1169
1170 } // namespace WebCore