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