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