2 * Copyright (C) 2004, 2006, 2008 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
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.
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.
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
24 #include "core/xmlhttprequest/XMLHttpRequest.h"
26 #include "bindings/core/v8/ExceptionState.h"
27 #include "core/FetchInitiatorTypeNames.h"
28 #include "core/dom/ContextFeatures.h"
29 #include "core/dom/DOMArrayBuffer.h"
30 #include "core/dom/DOMException.h"
31 #include "core/dom/DOMImplementation.h"
32 #include "core/dom/DocumentParser.h"
33 #include "core/dom/ExceptionCode.h"
34 #include "core/dom/XMLDocument.h"
35 #include "core/editing/markup.h"
36 #include "core/events/Event.h"
37 #include "core/fetch/FetchUtils.h"
38 #include "core/fileapi/Blob.h"
39 #include "core/fileapi/File.h"
40 #include "core/fileapi/FileReaderLoader.h"
41 #include "core/fileapi/FileReaderLoaderClient.h"
42 #include "core/frame/Settings.h"
43 #include "core/frame/UseCounter.h"
44 #include "core/frame/csp/ContentSecurityPolicy.h"
45 #include "core/html/DOMFormData.h"
46 #include "core/html/HTMLDocument.h"
47 #include "core/html/parser/TextResourceDecoder.h"
48 #include "core/inspector/ConsoleMessage.h"
49 #include "core/inspector/InspectorInstrumentation.h"
50 #include "core/inspector/InspectorTraceEvents.h"
51 #include "core/loader/ThreadableLoader.h"
52 #include "core/streams/ReadableStream.h"
53 #include "core/streams/ReadableStreamImpl.h"
54 #include "core/streams/Stream.h"
55 #include "core/streams/UnderlyingSource.h"
56 #include "core/xmlhttprequest/XMLHttpRequestProgressEvent.h"
57 #include "core/xmlhttprequest/XMLHttpRequestUpload.h"
58 #include "platform/Logging.h"
59 #include "platform/RuntimeEnabledFeatures.h"
60 #include "platform/SharedBuffer.h"
61 #include "platform/blob/BlobData.h"
62 #include "platform/network/HTTPParsers.h"
63 #include "platform/network/ParsedContentType.h"
64 #include "platform/network/ResourceError.h"
65 #include "platform/network/ResourceRequest.h"
66 #include "public/platform/WebURLRequest.h"
67 #include "wtf/ArrayBuffer.h"
68 #include "wtf/ArrayBufferView.h"
69 #include "wtf/Assertions.h"
70 #include "wtf/RefCountedLeakCounter.h"
71 #include "wtf/StdLibExtras.h"
72 #include "wtf/text/CString.h"
76 DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, xmlHttpRequestCounter, ("XMLHttpRequest"));
78 static bool isSetCookieHeader(const AtomicString& name)
80 return equalIgnoringCase(name, "set-cookie") || equalIgnoringCase(name, "set-cookie2");
83 static void replaceCharsetInMediaType(String& mediaType, const String& charsetValue)
85 unsigned pos = 0, len = 0;
87 findCharsetInMediaType(mediaType, pos, len);
90 // When no charset found, do nothing.
94 // Found at least one existing charset, replace all occurrences with new charset.
96 mediaType.replace(pos, len, charsetValue);
97 unsigned start = pos + charsetValue.length();
98 findCharsetInMediaType(mediaType, pos, len, start);
102 static void logConsoleError(ExecutionContext* context, const String& message)
106 // FIXME: It's not good to report the bad usage without indicating what source line it came from.
107 // We should pass additional parameters so we can tell the console where the mistake occurred.
108 context->addConsoleMessage(ConsoleMessage::create(JSMessageSource, ErrorMessageLevel, message));
113 class ReadableStreamSource : public GarbageCollectedFinalized<ReadableStreamSource>, public UnderlyingSource {
114 USING_GARBAGE_COLLECTED_MIXIN(ReadableStreamSource);
116 ReadableStreamSource(XMLHttpRequest* owner) : m_owner(owner) { }
117 virtual ~ReadableStreamSource() { }
118 virtual void pullSource() override { }
119 virtual ScriptPromise cancelSource(ScriptState* scriptState, ScriptValue reason) override
122 return ScriptPromise::cast(scriptState, v8::Undefined(scriptState->isolate()));
124 virtual void trace(Visitor* visitor) override
126 visitor->trace(m_owner);
127 UnderlyingSource::trace(visitor);
131 // This is RawPtr in non-oilpan build to avoid the reference cycle. To
132 // avoid use-after free, the associated ReadableStream must be closed
133 // or errored when m_owner is gone.
134 RawPtrWillBeMember<XMLHttpRequest> m_owner;
139 class XMLHttpRequest::BlobLoader final : public NoBaseWillBeGarbageCollectedFinalized<XMLHttpRequest::BlobLoader>, public FileReaderLoaderClient {
141 static PassOwnPtrWillBeRawPtr<BlobLoader> create(XMLHttpRequest* xhr, PassRefPtr<BlobDataHandle> handle)
143 return adoptPtrWillBeNoop(new BlobLoader(xhr, handle));
146 // FileReaderLoaderClient functions.
147 virtual void didStartLoading() override { }
148 virtual void didReceiveDataForClient(const char* data, unsigned length) override
150 ASSERT(length <= INT_MAX);
151 m_xhr->didReceiveData(data, length);
153 virtual void didFinishLoading() override
155 m_xhr->didFinishLoadingFromBlob();
157 virtual void didFail(FileError::ErrorCode error) override
159 m_xhr->didFailLoadingFromBlob();
167 void trace(Visitor* visitor)
169 visitor->trace(m_xhr);
173 BlobLoader(XMLHttpRequest* xhr, PassRefPtr<BlobDataHandle> handle)
175 , m_loader(FileReaderLoader::ReadByClient, this)
177 m_loader.start(m_xhr->executionContext(), handle);
180 RawPtrWillBeMember<XMLHttpRequest> m_xhr;
181 FileReaderLoader m_loader;
184 PassRefPtrWillBeRawPtr<XMLHttpRequest> XMLHttpRequest::create(ExecutionContext* context, PassRefPtr<SecurityOrigin> securityOrigin)
186 RefPtrWillBeRawPtr<XMLHttpRequest> xmlHttpRequest = adoptRefWillBeNoop(new XMLHttpRequest(context, securityOrigin));
187 xmlHttpRequest->suspendIfNeeded();
189 return xmlHttpRequest.release();
192 XMLHttpRequest::XMLHttpRequest(ExecutionContext* context, PassRefPtr<SecurityOrigin> securityOrigin)
193 : ActiveDOMObject(context)
194 , m_timeoutMilliseconds(0)
195 , m_loaderIdentifier(0)
197 , m_lengthDownloadedToFile(0)
198 , m_receivedLength(0)
200 , m_progressEventThrottle(this)
201 , m_responseTypeCode(ResponseTypeDefault)
202 , m_securityOrigin(securityOrigin)
204 , m_includeCredentials(false)
205 , m_parsedResponse(false)
207 , m_uploadEventsAllowed(true)
208 , m_uploadComplete(false)
209 , m_sameOriginRequest(true)
210 , m_downloadingToFile(false)
213 xmlHttpRequestCounter.increment();
217 XMLHttpRequest::~XMLHttpRequest()
220 xmlHttpRequestCounter.decrement();
224 Document* XMLHttpRequest::document() const
226 ASSERT(executionContext()->isDocument());
227 return toDocument(executionContext());
230 SecurityOrigin* XMLHttpRequest::securityOrigin() const
232 return m_securityOrigin ? m_securityOrigin.get() : executionContext()->securityOrigin();
235 XMLHttpRequest::State XMLHttpRequest::readyState() const
240 ScriptString XMLHttpRequest::responseText(ExceptionState& exceptionState)
242 if (m_responseTypeCode != ResponseTypeDefault && m_responseTypeCode != ResponseTypeText) {
243 exceptionState.throwDOMException(InvalidStateError, "The value is only accessible if the object's 'responseType' is '' or 'text' (was '" + responseType() + "').");
244 return ScriptString();
246 if (m_error || (m_state != LOADING && m_state != DONE))
247 return ScriptString();
248 return m_responseText;
251 ScriptString XMLHttpRequest::responseJSONSource()
253 ASSERT(m_responseTypeCode == ResponseTypeJSON);
255 if (m_error || m_state != DONE)
256 return ScriptString();
257 return m_responseText;
260 void XMLHttpRequest::initResponseDocument()
262 // The W3C spec requires the final MIME type to be some valid XML type, or text/html.
263 // If it is text/html, then the responseType of "document" must have been supplied explicitly.
264 bool isHTML = responseIsHTML();
265 if ((m_response.isHTTP() && !responseIsXML() && !isHTML)
266 || (isHTML && m_responseTypeCode == ResponseTypeDefault)
267 || executionContext()->isWorkerGlobalScope()) {
268 m_responseDocument = nullptr;
272 DocumentInit init = DocumentInit::fromContext(document()->contextDocument(), m_url);
274 m_responseDocument = HTMLDocument::create(init);
276 m_responseDocument = XMLDocument::create(init);
278 // FIXME: Set Last-Modified.
279 m_responseDocument->setSecurityOrigin(securityOrigin());
280 m_responseDocument->setContextFeatures(document()->contextFeatures());
281 m_responseDocument->setMimeType(finalResponseMIMETypeWithFallback());
284 Document* XMLHttpRequest::responseXML(ExceptionState& exceptionState)
286 if (m_responseTypeCode != ResponseTypeDefault && m_responseTypeCode != ResponseTypeDocument) {
287 exceptionState.throwDOMException(InvalidStateError, "The value is only accessible if the object's 'responseType' is '' or 'document' (was '" + responseType() + "').");
291 if (m_error || m_state != DONE)
294 if (!m_parsedResponse) {
295 initResponseDocument();
296 if (!m_responseDocument)
299 m_responseDocument->setContent(m_responseText.flattenToString());
300 if (!m_responseDocument->wellFormed())
301 m_responseDocument = nullptr;
303 m_parsedResponse = true;
306 return m_responseDocument.get();
309 Blob* XMLHttpRequest::responseBlob()
311 ASSERT(m_responseTypeCode == ResponseTypeBlob);
313 // We always return null before DONE.
314 if (m_error || m_state != DONE)
317 if (!m_responseBlob) {
318 if (m_downloadingToFile) {
319 ASSERT(!m_binaryResponseBuilder);
321 // When responseType is set to "blob", we redirect the downloaded
322 // data to a file-handle directly in the browser process. We get
323 // the file-path from the ResourceResponse directly instead of
324 // copying the bytes between the browser and the renderer.
325 m_responseBlob = Blob::create(createBlobDataHandleFromResponse());
327 OwnPtr<BlobData> blobData = BlobData::create();
329 if (m_binaryResponseBuilder && m_binaryResponseBuilder->size()) {
330 size = m_binaryResponseBuilder->size();
331 blobData->appendBytes(m_binaryResponseBuilder->data(), size);
332 blobData->setContentType(finalResponseMIMETypeWithFallback());
333 m_binaryResponseBuilder.clear();
335 m_responseBlob = Blob::create(BlobDataHandle::create(blobData.release(), size));
339 return m_responseBlob.get();
342 DOMArrayBuffer* XMLHttpRequest::responseArrayBuffer()
344 ASSERT(m_responseTypeCode == ResponseTypeArrayBuffer);
346 if (m_error || m_state != DONE)
349 if (!m_responseArrayBuffer) {
350 if (m_binaryResponseBuilder && m_binaryResponseBuilder->size()) {
351 m_responseArrayBuffer = DOMArrayBuffer::create(m_binaryResponseBuilder->getAsArrayBuffer());
352 if (!m_responseArrayBuffer) {
353 // m_binaryResponseBuilder failed to allocate an ArrayBuffer.
354 // We need to crash the renderer since there's no way defined in
355 // the spec to tell this to the user.
358 m_binaryResponseBuilder.clear();
360 m_responseArrayBuffer = DOMArrayBuffer::create(static_cast<void*>(0), 0);
364 return m_responseArrayBuffer.get();
367 Stream* XMLHttpRequest::responseLegacyStream()
369 ASSERT(m_responseTypeCode == ResponseTypeLegacyStream);
371 if (m_error || (m_state != LOADING && m_state != DONE))
374 return m_responseLegacyStream.get();
377 ReadableStream* XMLHttpRequest::responseStream()
379 ASSERT(m_responseTypeCode == ResponseTypeStream);
380 if (m_error || (m_state != LOADING && m_state != DONE))
383 return m_responseStream;
386 void XMLHttpRequest::setTimeout(unsigned long timeout, ExceptionState& exceptionState)
388 // FIXME: Need to trigger or update the timeout Timer here, if needed. http://webkit.org/b/98156
389 // XHR2 spec, 4.7.3. "This implies that the timeout attribute can be set while fetching is in progress. If that occurs it will still be measured relative to the start of fetching."
390 if (executionContext()->isDocument() && !m_async) {
391 exceptionState.throwDOMException(InvalidAccessError, "Timeouts cannot be set for synchronous requests made from a document.");
395 m_timeoutMilliseconds = timeout;
397 // From http://www.w3.org/TR/XMLHttpRequest/#the-timeout-attribute:
398 // Note: This implies that the timeout attribute can be set while fetching is in progress. If
399 // that occurs it will still be measured relative to the start of fetching.
401 // The timeout may be overridden after send.
403 m_loader->overrideTimeout(timeout);
406 void XMLHttpRequest::setResponseType(const String& responseType, ExceptionState& exceptionState)
408 if (m_state >= LOADING) {
409 exceptionState.throwDOMException(InvalidStateError, "The response type cannot be set if the object's state is LOADING or DONE.");
413 // Newer functionality is not available to synchronous requests in window contexts, as a spec-mandated
414 // attempt to discourage synchronous XHR use. responseType is one such piece of functionality.
415 if (!m_async && executionContext()->isDocument()) {
416 exceptionState.throwDOMException(InvalidAccessError, "The response type cannot be changed for synchronous requests made from a document.");
420 if (responseType == "") {
421 m_responseTypeCode = ResponseTypeDefault;
422 } else if (responseType == "text") {
423 m_responseTypeCode = ResponseTypeText;
424 } else if (responseType == "json") {
425 m_responseTypeCode = ResponseTypeJSON;
426 } else if (responseType == "document") {
427 m_responseTypeCode = ResponseTypeDocument;
428 } else if (responseType == "blob") {
429 m_responseTypeCode = ResponseTypeBlob;
430 } else if (responseType == "arraybuffer") {
431 m_responseTypeCode = ResponseTypeArrayBuffer;
432 } else if (responseType == "legacystream") {
433 if (RuntimeEnabledFeatures::streamEnabled())
434 m_responseTypeCode = ResponseTypeLegacyStream;
437 } else if (responseType == "stream") {
438 if (RuntimeEnabledFeatures::streamEnabled())
439 m_responseTypeCode = ResponseTypeStream;
443 ASSERT_NOT_REACHED();
447 String XMLHttpRequest::responseType()
449 switch (m_responseTypeCode) {
450 case ResponseTypeDefault:
452 case ResponseTypeText:
454 case ResponseTypeJSON:
456 case ResponseTypeDocument:
458 case ResponseTypeBlob:
460 case ResponseTypeArrayBuffer:
461 return "arraybuffer";
462 case ResponseTypeLegacyStream:
463 return "legacystream";
464 case ResponseTypeStream:
470 String XMLHttpRequest::responseURL()
472 KURL responseURL(m_response.url());
473 if (!responseURL.isNull())
474 responseURL.removeFragmentIdentifier();
475 return responseURL.string();
478 XMLHttpRequestUpload* XMLHttpRequest::upload()
481 m_upload = XMLHttpRequestUpload::create(this);
482 return m_upload.get();
485 void XMLHttpRequest::trackProgress(long long length)
487 m_receivedLength += length;
489 if (m_state != LOADING) {
490 changeState(LOADING);
492 // Dispatch a readystatechange event because many applications use
493 // it to track progress although this is not specified.
495 // FIXME: Stop dispatching this event for progress tracking.
496 dispatchReadyStateChangeEvent();
499 dispatchProgressEventFromSnapshot(EventTypeNames::progress);
502 void XMLHttpRequest::changeState(State newState)
504 if (m_state != newState) {
506 dispatchReadyStateChangeEvent();
510 void XMLHttpRequest::dispatchReadyStateChangeEvent()
512 if (!executionContext())
515 InspectorInstrumentationCookie cookie = InspectorInstrumentation::willDispatchXHRReadyStateChangeEvent(executionContext(), this);
517 if (m_async || (m_state <= OPENED || m_state == DONE)) {
518 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "XHRReadyStateChange", "data", InspectorXhrReadyStateChangeEvent::data(executionContext(), this));
519 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline.stack"), "CallStack", "stack", InspectorCallStackEvent::currentCallStack());
520 XMLHttpRequestProgressEventThrottle::DeferredEventAction action = XMLHttpRequestProgressEventThrottle::Ignore;
521 if (m_state == DONE) {
523 action = XMLHttpRequestProgressEventThrottle::Clear;
525 action = XMLHttpRequestProgressEventThrottle::Flush;
527 m_progressEventThrottle.dispatchReadyStateChangeEvent(Event::create(EventTypeNames::readystatechange), action);
528 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "UpdateCounters", "data", InspectorUpdateCountersEvent::data());
531 InspectorInstrumentation::didDispatchXHRReadyStateChangeEvent(cookie);
532 if (m_state == DONE && !m_error) {
533 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "XHRLoad", "data", InspectorXhrLoadEvent::data(executionContext(), this));
534 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline.stack"), "CallStack", "stack", InspectorCallStackEvent::currentCallStack());
535 InspectorInstrumentationCookie cookie = InspectorInstrumentation::willDispatchXHRLoadEvent(executionContext(), this);
536 dispatchProgressEventFromSnapshot(EventTypeNames::load);
537 InspectorInstrumentation::didDispatchXHRLoadEvent(cookie);
539 dispatchProgressEventFromSnapshot(EventTypeNames::loadend);
540 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "UpdateCounters", "data", InspectorUpdateCountersEvent::data());
544 void XMLHttpRequest::setWithCredentials(bool value, ExceptionState& exceptionState)
546 if (m_state > OPENED || m_loader) {
547 exceptionState.throwDOMException(InvalidStateError, "The value may only be set if the object's state is UNSENT or OPENED.");
551 // FIXME: According to XMLHttpRequest Level 2 we should throw InvalidAccessError exception here.
552 // However for time being only print warning message to warn web developers.
554 UseCounter::countDeprecation(executionContext(), UseCounter::SyncXHRWithCredentials);
556 m_includeCredentials = value;
559 void XMLHttpRequest::open(const AtomicString& method, const KURL& url, ExceptionState& exceptionState)
561 open(method, url, true, exceptionState);
564 void XMLHttpRequest::open(const AtomicString& method, const KURL& url, bool async, ExceptionState& exceptionState)
566 WTF_LOG(Network, "XMLHttpRequest %p open('%s', '%s', %d)", this, method.utf8().data(), url.elidedString().utf8().data(), async);
568 if (!internalAbort())
571 State previousState = m_state;
574 m_uploadComplete = false;
576 if (!isValidHTTPToken(method)) {
577 exceptionState.throwDOMException(SyntaxError, "'" + method + "' is not a valid HTTP method.");
581 if (FetchUtils::isForbiddenMethod(method)) {
582 exceptionState.throwSecurityError("'" + method + "' HTTP method is unsupported.");
586 if (!ContentSecurityPolicy::shouldBypassMainWorld(executionContext()) && !executionContext()->contentSecurityPolicy()->allowConnectToSource(url)) {
587 // We can safely expose the URL to JavaScript, as these checks happen synchronously before redirection. JavaScript receives no new information.
588 exceptionState.throwSecurityError("Refused to connect to '" + url.elidedString() + "' because it violates the document's Content Security Policy.");
592 if (!async && executionContext()->isDocument()) {
593 if (document()->settings() && !document()->settings()->syncXHRInDocumentsEnabled()) {
594 exceptionState.throwDOMException(InvalidAccessError, "Synchronous requests are disabled for this page.");
598 // Newer functionality is not available to synchronous requests in window contexts, as a spec-mandated
599 // attempt to discourage synchronous XHR use. responseType is one such piece of functionality.
600 if (m_responseTypeCode != ResponseTypeDefault) {
601 exceptionState.throwDOMException(InvalidAccessError, "Synchronous requests from a document must not set a response type.");
605 // Similarly, timeouts are disabled for synchronous requests as well.
606 if (m_timeoutMilliseconds > 0) {
607 exceptionState.throwDOMException(InvalidAccessError, "Synchronous requests must not set a timeout.");
611 // Here we just warn that firing sync XHR's may affect responsiveness.
612 // Eventually sync xhr will be deprecated and an "InvalidAccessError" exception thrown.
613 // Refer : https://xhr.spec.whatwg.org/#sync-warning
614 // Use count for XHR synchronous requests on main thread only.
615 if (!document()->processingBeforeUnload())
616 UseCounter::countDeprecation(executionContext(), UseCounter::XMLHttpRequestSynchronousInNonWorkerOutsideBeforeUnload);
619 m_method = FetchUtils::normalizeMethod(method);
627 // Check previous state to avoid dispatching readyState event
628 // when calling open several times in a row.
629 if (previousState != OPENED)
635 void XMLHttpRequest::open(const AtomicString& method, const KURL& url, bool async, const String& user, ExceptionState& exceptionState)
637 KURL urlWithCredentials(url);
638 urlWithCredentials.setUser(user);
640 open(method, urlWithCredentials, async, exceptionState);
643 void XMLHttpRequest::open(const AtomicString& method, const KURL& url, bool async, const String& user, const String& password, ExceptionState& exceptionState)
645 KURL urlWithCredentials(url);
646 urlWithCredentials.setUser(user);
647 urlWithCredentials.setPass(password);
649 open(method, urlWithCredentials, async, exceptionState);
652 bool XMLHttpRequest::initSend(ExceptionState& exceptionState)
654 if (!executionContext())
657 if (m_state != OPENED || m_loader) {
658 exceptionState.throwDOMException(InvalidStateError, "The object's state must be OPENED.");
666 void XMLHttpRequest::send(ExceptionState& exceptionState)
668 send(String(), exceptionState);
671 bool XMLHttpRequest::areMethodAndURLValidForSend()
673 return m_method != "GET" && m_method != "HEAD" && m_url.protocolIsInHTTPFamily();
676 void XMLHttpRequest::send(Document* document, ExceptionState& exceptionState)
678 WTF_LOG(Network, "XMLHttpRequest %p send() Document %p", this, document);
682 if (!initSend(exceptionState))
685 RefPtr<FormData> httpBody;
687 if (areMethodAndURLValidForSend()) {
688 if (getRequestHeader("Content-Type").isEmpty()) {
689 // FIXME: this should include the charset used for encoding.
690 setRequestHeaderInternal("Content-Type", "application/xml");
693 // FIXME: According to XMLHttpRequest Level 2, this should use the Document.innerHTML algorithm
694 // from the HTML5 specification to serialize the document.
695 String body = createMarkup(document);
697 // FIXME: This should use value of document.inputEncoding to determine the encoding to use.
698 httpBody = FormData::create(UTF8Encoding().encode(body, WTF::EntitiesForUnencodables));
701 createRequest(httpBody.release(), exceptionState);
704 void XMLHttpRequest::send(const String& body, ExceptionState& exceptionState)
706 WTF_LOG(Network, "XMLHttpRequest %p send() String '%s'", this, body.utf8().data());
708 if (!initSend(exceptionState))
711 RefPtr<FormData> httpBody;
713 if (!body.isNull() && areMethodAndURLValidForSend()) {
714 String contentType = getRequestHeader("Content-Type");
715 if (contentType.isEmpty()) {
716 setRequestHeaderInternal("Content-Type", "text/plain;charset=UTF-8");
718 replaceCharsetInMediaType(contentType, "UTF-8");
719 m_requestHeaders.set("Content-Type", AtomicString(contentType));
722 httpBody = FormData::create(UTF8Encoding().encode(body, WTF::EntitiesForUnencodables));
725 createRequest(httpBody.release(), exceptionState);
728 void XMLHttpRequest::send(Blob* body, ExceptionState& exceptionState)
730 WTF_LOG(Network, "XMLHttpRequest %p send() Blob '%s'", this, body->uuid().utf8().data());
732 if (!initSend(exceptionState))
735 RefPtr<FormData> httpBody;
737 if (areMethodAndURLValidForSend()) {
738 if (getRequestHeader("Content-Type").isEmpty()) {
739 const String& blobType = body->type();
740 if (!blobType.isEmpty() && isValidContentType(blobType)) {
741 setRequestHeaderInternal("Content-Type", AtomicString(blobType));
743 // From FileAPI spec, whenever media type cannot be determined,
744 // empty string must be returned.
745 setRequestHeaderInternal("Content-Type", "");
749 // FIXME: add support for uploading bundles.
750 httpBody = FormData::create();
751 if (body->hasBackingFile()) {
752 File* file = toFile(body);
753 if (!file->path().isEmpty())
754 httpBody->appendFile(file->path());
755 else if (!file->fileSystemURL().isEmpty())
756 httpBody->appendFileSystemURL(file->fileSystemURL());
758 ASSERT_NOT_REACHED();
760 httpBody->appendBlob(body->uuid(), body->blobDataHandle());
764 createRequest(httpBody.release(), exceptionState);
767 void XMLHttpRequest::send(DOMFormData* body, ExceptionState& exceptionState)
769 WTF_LOG(Network, "XMLHttpRequest %p send() DOMFormData %p", this, body);
771 if (!initSend(exceptionState))
774 RefPtr<FormData> httpBody;
776 if (areMethodAndURLValidForSend()) {
777 httpBody = body->createMultiPartFormData();
779 if (getRequestHeader("Content-Type").isEmpty()) {
780 AtomicString contentType = AtomicString("multipart/form-data; boundary=", AtomicString::ConstructFromLiteral) + httpBody->boundary().data();
781 setRequestHeaderInternal("Content-Type", contentType);
785 createRequest(httpBody.release(), exceptionState);
788 void XMLHttpRequest::send(ArrayBuffer* body, ExceptionState& exceptionState)
790 WTF_LOG(Network, "XMLHttpRequest %p send() ArrayBuffer %p", this, body);
792 sendBytesData(body->data(), body->byteLength(), exceptionState);
795 void XMLHttpRequest::send(ArrayBufferView* body, ExceptionState& exceptionState)
797 WTF_LOG(Network, "XMLHttpRequest %p send() ArrayBufferView %p", this, body);
799 sendBytesData(body->baseAddress(), body->byteLength(), exceptionState);
802 void XMLHttpRequest::sendBytesData(const void* data, size_t length, ExceptionState& exceptionState)
804 if (!initSend(exceptionState))
807 RefPtr<FormData> httpBody;
809 if (areMethodAndURLValidForSend()) {
810 httpBody = FormData::create(data, length);
813 createRequest(httpBody.release(), exceptionState);
816 void XMLHttpRequest::sendForInspectorXHRReplay(PassRefPtr<FormData> formData, ExceptionState& exceptionState)
818 createRequest(formData ? formData->deepCopy() : nullptr, exceptionState);
819 m_exceptionCode = exceptionState.code();
822 void XMLHttpRequest::createRequest(PassRefPtr<FormData> httpBody, ExceptionState& exceptionState)
824 // Only GET request is supported for blob URL.
825 if (m_url.protocolIs("blob") && m_method != "GET") {
826 exceptionState.throwDOMException(NetworkError, "'GET' is the only method allowed for 'blob:' URLs.");
830 // The presence of upload event listeners forces us to use preflighting because POSTing to an URL that does not
831 // permit cross origin requests should look exactly like POSTing to an URL that does not respond at all.
832 // Also, only async requests support upload progress events.
833 bool uploadEvents = false;
835 dispatchProgressEvent(EventTypeNames::loadstart, 0, 0);
836 if (httpBody && m_upload) {
837 uploadEvents = m_upload->hasEventListeners();
838 m_upload->dispatchEvent(XMLHttpRequestProgressEvent::create(EventTypeNames::loadstart));
842 m_sameOriginRequest = securityOrigin()->canRequest(m_url);
844 // We also remember whether upload events should be allowed for this request in case the upload listeners are
845 // added after the request is started.
846 m_uploadEventsAllowed = m_sameOriginRequest || uploadEvents || !FetchUtils::isSimpleRequest(m_method, m_requestHeaders);
848 ASSERT(executionContext());
849 ExecutionContext& executionContext = *this->executionContext();
851 ResourceRequest request(m_url);
852 request.setHTTPMethod(m_method);
853 request.setRequestContext(blink::WebURLRequest::RequestContextXMLHttpRequest);
854 request.setFetchCredentialsMode(m_includeCredentials ? WebURLRequest::FetchCredentialsModeInclude : WebURLRequest::FetchCredentialsModeSameOrigin);
856 InspectorInstrumentation::willLoadXHR(&executionContext, this, this, m_method, m_url, m_async, httpBody ? httpBody->deepCopy() : nullptr, m_requestHeaders, m_includeCredentials);
859 ASSERT(m_method != "GET");
860 ASSERT(m_method != "HEAD");
861 request.setHTTPBody(httpBody);
864 if (m_requestHeaders.size() > 0)
865 request.addHTTPHeaderFields(m_requestHeaders);
867 ThreadableLoaderOptions options;
868 options.preflightPolicy = uploadEvents ? ForcePreflight : ConsiderPreflight;
869 options.crossOriginRequestPolicy = UseAccessControl;
870 options.initiator = FetchInitiatorTypeNames::xmlhttprequest;
871 options.contentSecurityPolicyEnforcement = ContentSecurityPolicy::shouldBypassMainWorld(&executionContext) ? DoNotEnforceContentSecurityPolicy : EnforceConnectSrcDirective;
872 options.timeoutMilliseconds = m_timeoutMilliseconds;
874 ResourceLoaderOptions resourceLoaderOptions;
875 resourceLoaderOptions.allowCredentials = (m_sameOriginRequest || m_includeCredentials) ? AllowStoredCredentials : DoNotAllowStoredCredentials;
876 resourceLoaderOptions.credentialsRequested = m_includeCredentials ? ClientRequestedCredentials : ClientDidNotRequestCredentials;
877 resourceLoaderOptions.securityOrigin = securityOrigin();
878 resourceLoaderOptions.mixedContentBlockingTreatment = RuntimeEnabledFeatures::laxMixedContentCheckingEnabled() ? TreatAsPassiveContent : TreatAsActiveContent;
880 // When responseType is set to "blob", we redirect the downloaded data to a
881 // file-handle directly.
882 m_downloadingToFile = responseTypeCode() == ResponseTypeBlob;
883 if (m_downloadingToFile) {
884 request.setDownloadToFile(true);
885 resourceLoaderOptions.dataBufferingPolicy = DoNotBufferData;
893 request.setReportUploadProgress(true);
895 // ThreadableLoader::create can return null here, for example if we're no longer attached to a page.
896 // This is true while running onunload handlers.
897 // FIXME: Maybe we need to be able to send XMLHttpRequests from onunload, <http://bugs.webkit.org/show_bug.cgi?id=10904>.
898 // FIXME: Maybe create() can return null for other reasons too?
900 m_loader = ThreadableLoader::create(executionContext, this, request, options, resourceLoaderOptions);
902 // Use count for XHR synchronous requests.
903 UseCounter::count(&executionContext, UseCounter::XMLHttpRequestSynchronous);
904 ThreadableLoader::loadResourceSynchronously(executionContext, request, *this, options, resourceLoaderOptions);
907 if (!m_exceptionCode && m_error)
908 m_exceptionCode = NetworkError;
910 exceptionState.throwDOMException(m_exceptionCode, "Failed to load '" + m_url.elidedString() + "'.");
913 void XMLHttpRequest::abort()
915 WTF_LOG(Network, "XMLHttpRequest %p abort()", this);
917 // internalAbort() clears |m_loader|. Compute |sendFlag| now.
919 // |sendFlag| corresponds to "the send() flag" defined in the XHR spec.
921 // |sendFlag| is only set when we have an active, asynchronous loader.
922 // Don't use it as "the send() flag" when the XHR is in sync mode.
923 bool sendFlag = m_loader;
925 // internalAbort() clears the response. Save the data needed for
926 // dispatching ProgressEvents.
927 long long expectedLength = m_response.expectedContentLength();
928 long long receivedLength = m_receivedLength;
930 if (!internalAbort())
933 // The script never gets any chance to call abort() on a sync XHR between
934 // send() call and transition to the DONE state. It's because a sync XHR
935 // doesn't dispatch any event between them. So, if |m_async| is false, we
936 // can skip the "request error steps" (defined in the XHR spec) without any
939 // FIXME: It's possible open() is invoked in internalAbort() and |m_async|
940 // becomes true by that. We should implement more reliable treatment for
941 // nested method invocations at some point.
943 if ((m_state == OPENED && sendFlag) || m_state == HEADERS_RECEIVED || m_state == LOADING) {
945 handleRequestError(0, EventTypeNames::abort, receivedLength, expectedLength);
951 void XMLHttpRequest::clearVariablesForLoading()
954 m_blobLoader->cancel();
955 m_blobLoader = nullptr;
960 if (m_responseDocumentParser) {
961 m_responseDocumentParser->removeClient(this);
963 m_responseDocumentParser->detach();
965 m_responseDocumentParser = nullptr;
968 m_finalResponseCharset = String();
971 bool XMLHttpRequest::internalAbort()
975 if (m_responseDocumentParser && !m_responseDocumentParser->isStopped())
976 m_responseDocumentParser->stopParsing();
978 clearVariablesForLoading();
980 InspectorInstrumentation::didFailXHRLoading(executionContext(), this, this);
982 if (m_responseLegacyStream && m_state != DONE)
983 m_responseLegacyStream->abort();
985 if (m_responseStream) {
986 // When the stream is already closed (including canceled from the
987 // user), |error| does nothing.
988 // FIXME: Create a more specific error.
989 m_responseStream->error(DOMException::create(!m_async && m_exceptionCode ? m_exceptionCode : AbortError, "XMLHttpRequest::abort"));
998 // Cancelling the ThreadableLoader m_loader may result in calling
999 // window.onload synchronously. If such an onload handler contains open()
1000 // call on the same XMLHttpRequest object, reentry happens.
1002 // If, window.onload contains open() and send(), m_loader will be set to
1003 // non 0 value. So, we cannot continue the outer open(). In such case,
1004 // just abort the outer open() by returning false.
1005 RefPtr<ThreadableLoader> loader = m_loader.release();
1008 // If abort() called internalAbort() and a nested open() ended up
1009 // clearing the error flag, but didn't send(), make sure the error
1010 // flag is still set.
1011 bool newLoadStarted = hasPendingActivity();
1012 if (!newLoadStarted)
1015 return !newLoadStarted;
1018 void XMLHttpRequest::clearResponse()
1020 // FIXME: when we add the support for multi-part XHR, we will have to
1021 // be careful with this initialization.
1022 m_receivedLength = 0;
1024 m_response = ResourceResponse();
1026 m_responseText.clear();
1028 m_parsedResponse = false;
1029 m_responseDocument = nullptr;
1031 m_responseBlob = nullptr;
1033 m_downloadingToFile = false;
1034 m_lengthDownloadedToFile = 0;
1036 m_responseLegacyStream = nullptr;
1037 m_responseStream = nullptr;
1039 // These variables may referred by the response accessors. So, we can clear
1040 // this only when we clear the response holder variables above.
1041 m_binaryResponseBuilder.clear();
1042 m_responseArrayBuffer.clear();
1045 void XMLHttpRequest::clearRequest()
1047 m_requestHeaders.clear();
1050 void XMLHttpRequest::dispatchProgressEvent(const AtomicString& type, long long receivedLength, long long expectedLength)
1052 bool lengthComputable = expectedLength > 0 && receivedLength <= expectedLength;
1053 unsigned long long loaded = receivedLength >= 0 ? static_cast<unsigned long long>(receivedLength) : 0;
1054 unsigned long long total = lengthComputable ? static_cast<unsigned long long>(expectedLength) : 0;
1056 m_progressEventThrottle.dispatchProgressEvent(type, lengthComputable, loaded, total);
1058 if (type == EventTypeNames::loadend)
1059 InspectorInstrumentation::didDispatchXHRLoadendEvent(executionContext(), this);
1062 void XMLHttpRequest::dispatchProgressEventFromSnapshot(const AtomicString& type)
1064 dispatchProgressEvent(type, m_receivedLength, m_response.expectedContentLength());
1067 void XMLHttpRequest::handleNetworkError()
1069 WTF_LOG(Network, "XMLHttpRequest %p handleNetworkError()", this);
1071 // Response is cleared next, save needed progress event data.
1072 long long expectedLength = m_response.expectedContentLength();
1073 long long receivedLength = m_receivedLength;
1075 // Prevent the XMLHttpRequest instance from being destoryed during
1076 // |internalAbort()|.
1077 RefPtrWillBeRawPtr<XMLHttpRequest> protect(this);
1079 if (!internalAbort())
1082 handleRequestError(NetworkError, EventTypeNames::error, receivedLength, expectedLength);
1085 void XMLHttpRequest::handleDidCancel()
1087 WTF_LOG(Network, "XMLHttpRequest %p handleDidCancel()", this);
1089 // Response is cleared next, save needed progress event data.
1090 long long expectedLength = m_response.expectedContentLength();
1091 long long receivedLength = m_receivedLength;
1093 // Prevent the XMLHttpRequest instance from being destoryed during
1094 // |internalAbort()|.
1095 RefPtrWillBeRawPtr<XMLHttpRequest> protect(this);
1097 if (!internalAbort())
1100 handleRequestError(AbortError, EventTypeNames::abort, receivedLength, expectedLength);
1103 void XMLHttpRequest::handleRequestError(ExceptionCode exceptionCode, const AtomicString& type, long long receivedLength, long long expectedLength)
1105 WTF_LOG(Network, "XMLHttpRequest %p handleRequestError()", this);
1107 // The request error steps for event 'type' and exception 'exceptionCode'.
1109 if (!m_async && exceptionCode) {
1111 m_exceptionCode = exceptionCode;
1114 // With m_error set, the state change steps are minimal: any pending
1115 // progress event is flushed + a readystatechange is dispatched.
1116 // No new progress events dispatched; as required, that happens at
1121 if (!m_uploadComplete) {
1122 m_uploadComplete = true;
1123 if (m_upload && m_uploadEventsAllowed)
1124 m_upload->handleRequestError(type);
1127 // Note: The below event dispatch may be called while |hasPendingActivity() == false|,
1128 // when |handleRequestError| is called after |internalAbort()|.
1129 // This is safe, however, as |this| will be kept alive from a strong ref |Event::m_target|.
1130 dispatchProgressEvent(EventTypeNames::progress, receivedLength, expectedLength);
1131 dispatchProgressEvent(type, receivedLength, expectedLength);
1132 dispatchProgressEvent(EventTypeNames::loadend, receivedLength, expectedLength);
1135 void XMLHttpRequest::overrideMimeType(const AtomicString& mimeType, ExceptionState& exceptionState)
1137 if (m_state == LOADING || m_state == DONE) {
1138 exceptionState.throwDOMException(InvalidStateError, "MimeType cannot be overridden when the state is LOADING or DONE.");
1142 m_mimeTypeOverride = mimeType;
1145 void XMLHttpRequest::setRequestHeader(const AtomicString& name, const AtomicString& value, ExceptionState& exceptionState)
1147 if (m_state != OPENED || m_loader) {
1148 exceptionState.throwDOMException(InvalidStateError, "The object's state must be OPENED.");
1152 if (!isValidHTTPToken(name)) {
1153 exceptionState.throwDOMException(SyntaxError, "'" + name + "' is not a valid HTTP header field name.");
1157 if (!isValidHTTPHeaderValue(value)) {
1158 exceptionState.throwDOMException(SyntaxError, "'" + value + "' is not a valid HTTP header field value.");
1162 // No script (privileged or not) can set unsafe headers.
1163 if (FetchUtils::isForbiddenHeaderName(name)) {
1164 logConsoleError(executionContext(), "Refused to set unsafe header \"" + name + "\"");
1168 setRequestHeaderInternal(name, value);
1171 void XMLHttpRequest::setRequestHeaderInternal(const AtomicString& name, const AtomicString& value)
1173 HTTPHeaderMap::AddResult result = m_requestHeaders.add(name, value);
1174 if (!result.isNewEntry)
1175 result.storedValue->value = result.storedValue->value + ", " + value;
1178 const AtomicString& XMLHttpRequest::getRequestHeader(const AtomicString& name) const
1180 return m_requestHeaders.get(name);
1183 String XMLHttpRequest::getAllResponseHeaders() const
1185 if (m_state < HEADERS_RECEIVED || m_error)
1188 StringBuilder stringBuilder;
1190 HTTPHeaderSet accessControlExposeHeaderSet;
1191 parseAccessControlExposeHeadersAllowList(m_response.httpHeaderField("Access-Control-Expose-Headers"), accessControlExposeHeaderSet);
1192 HTTPHeaderMap::const_iterator end = m_response.httpHeaderFields().end();
1193 for (HTTPHeaderMap::const_iterator it = m_response.httpHeaderFields().begin(); it!= end; ++it) {
1194 // Hide Set-Cookie header fields from the XMLHttpRequest client for these reasons:
1195 // 1) If the client did have access to the fields, then it could read HTTP-only
1196 // cookies; those cookies are supposed to be hidden from scripts.
1197 // 2) There's no known harm in hiding Set-Cookie header fields entirely; we don't
1198 // know any widely used technique that requires access to them.
1199 // 3) Firefox has implemented this policy.
1200 if (isSetCookieHeader(it->key) && !securityOrigin()->canLoadLocalResources())
1203 if (!m_sameOriginRequest && !isOnAccessControlResponseHeaderWhitelist(it->key) && !accessControlExposeHeaderSet.contains(it->key))
1206 stringBuilder.append(it->key);
1207 stringBuilder.append(':');
1208 stringBuilder.append(' ');
1209 stringBuilder.append(it->value);
1210 stringBuilder.append('\r');
1211 stringBuilder.append('\n');
1214 return stringBuilder.toString();
1217 const AtomicString& XMLHttpRequest::getResponseHeader(const AtomicString& name) const
1219 if (m_state < HEADERS_RECEIVED || m_error)
1222 // See comment in getAllResponseHeaders above.
1223 if (isSetCookieHeader(name) && !securityOrigin()->canLoadLocalResources()) {
1224 logConsoleError(executionContext(), "Refused to get unsafe header \"" + name + "\"");
1228 HTTPHeaderSet accessControlExposeHeaderSet;
1229 parseAccessControlExposeHeadersAllowList(m_response.httpHeaderField("Access-Control-Expose-Headers"), accessControlExposeHeaderSet);
1231 if (!m_sameOriginRequest && !isOnAccessControlResponseHeaderWhitelist(name) && !accessControlExposeHeaderSet.contains(name)) {
1232 logConsoleError(executionContext(), "Refused to get unsafe header \"" + name + "\"");
1235 return m_response.httpHeaderField(name);
1238 AtomicString XMLHttpRequest::finalResponseMIMEType() const
1240 AtomicString overriddenType = extractMIMETypeFromMediaType(m_mimeTypeOverride);
1241 if (!overriddenType.isEmpty())
1242 return overriddenType;
1244 if (m_response.isHTTP())
1245 return extractMIMETypeFromMediaType(m_response.httpHeaderField("Content-Type"));
1247 return m_response.mimeType();
1250 AtomicString XMLHttpRequest::finalResponseMIMETypeWithFallback() const
1252 AtomicString finalType = finalResponseMIMEType();
1253 if (!finalType.isEmpty())
1256 // FIXME: This fallback is not specified in the final MIME type algorithm
1257 // of the XHR spec. Move this to more appropriate place.
1258 return AtomicString("text/xml", AtomicString::ConstructFromLiteral);
1261 bool XMLHttpRequest::responseIsXML() const
1263 return DOMImplementation::isXMLMIMEType(finalResponseMIMETypeWithFallback());
1266 bool XMLHttpRequest::responseIsHTML() const
1268 return equalIgnoringCase(finalResponseMIMEType(), "text/html");
1271 int XMLHttpRequest::status() const
1273 if (m_state == UNSENT || m_state == OPENED || m_error)
1276 if (m_response.httpStatusCode())
1277 return m_response.httpStatusCode();
1282 String XMLHttpRequest::statusText() const
1284 if (m_state == UNSENT || m_state == OPENED || m_error)
1287 if (!m_response.httpStatusText().isNull())
1288 return m_response.httpStatusText();
1293 void XMLHttpRequest::didFail(const ResourceError& error)
1295 WTF_LOG(Network, "XMLHttpRequest %p didFail()", this);
1297 // If we are already in an error state, for instance we called abort(), bail out early.
1301 if (error.isCancellation()) {
1303 // Now the XMLHttpRequest instance may be dead.
1307 if (error.isTimeout()) {
1309 // Now the XMLHttpRequest instance may be dead.
1313 // Network failures are already reported to Web Inspector by ResourceLoader.
1314 if (error.domain() == errorDomainBlinkInternal)
1315 logConsoleError(executionContext(), "XMLHttpRequest cannot load " + error.failingURL() + ". " + error.localizedDescription());
1317 handleNetworkError();
1318 // Now the XMLHttpRequest instance may be dead.
1321 void XMLHttpRequest::didFailRedirectCheck()
1323 WTF_LOG(Network, "XMLHttpRequest %p didFailRedirectCheck()", this);
1325 handleNetworkError();
1326 // Now the XMLHttpRequest instance may be dead.
1329 void XMLHttpRequest::didFinishLoading(unsigned long identifier, double)
1331 WTF_LOG(Network, "XMLHttpRequest %p didFinishLoading(%lu)", this, identifier);
1336 if (m_state < HEADERS_RECEIVED)
1337 changeState(HEADERS_RECEIVED);
1339 m_loaderIdentifier = identifier;
1341 if (m_downloadingToFile && m_responseTypeCode != ResponseTypeBlob && m_lengthDownloadedToFile) {
1342 ASSERT(m_state == LOADING);
1343 // In this case, we have sent the request with DownloadToFile true,
1344 // but the user changed the response type after that. Hence we need to
1345 // read the response data and provide it to this object.
1346 m_blobLoader = BlobLoader::create(this, createBlobDataHandleFromResponse());
1348 didFinishLoadingInternal();
1352 void XMLHttpRequest::didFinishLoadingInternal()
1354 if (m_responseDocumentParser) {
1355 // |DocumentParser::finish()| tells the parser that we have reached end of the data.
1356 // When using |HTMLDocumentParser|, which works asynchronously, we do not have the
1357 // complete document just after the |DocumentParser::finish()| call.
1358 // Wait for the parser to call us back in |notifyParserStopped| to progress state.
1359 m_responseDocumentParser->finish();
1360 ASSERT(m_responseDocument);
1365 m_responseText = m_responseText.concatenateWith(m_decoder->flush());
1367 if (m_responseLegacyStream)
1368 m_responseLegacyStream->finalize();
1370 if (m_responseStream)
1371 m_responseStream->close();
1373 clearVariablesForLoading();
1377 void XMLHttpRequest::didFinishLoadingFromBlob()
1379 WTF_LOG(Network, "XMLHttpRequest %p didFinishLoadingFromBlob", this);
1381 didFinishLoadingInternal();
1384 void XMLHttpRequest::didFailLoadingFromBlob()
1386 WTF_LOG(Network, "XMLHttpRequest %p didFailLoadingFromBlob()", this);
1390 handleNetworkError();
1393 PassRefPtr<BlobDataHandle> XMLHttpRequest::createBlobDataHandleFromResponse()
1395 ASSERT(m_downloadingToFile);
1396 OwnPtr<BlobData> blobData = BlobData::create();
1397 String filePath = m_response.downloadedFilePath();
1398 // If we errored out or got no data, we return an empty handle.
1399 if (!filePath.isEmpty() && m_lengthDownloadedToFile) {
1400 blobData->appendFile(filePath);
1401 // FIXME: finalResponseMIMETypeWithFallback() defaults to
1402 // text/xml which may be incorrect. Replace it with
1403 // finalResponseMIMEType() after compatibility investigation.
1404 blobData->setContentType(finalResponseMIMETypeWithFallback());
1406 return BlobDataHandle::create(blobData.release(), m_lengthDownloadedToFile);
1409 void XMLHttpRequest::notifyParserStopped()
1411 // This should only be called when response document is parsed asynchronously.
1412 ASSERT(m_responseDocumentParser);
1413 ASSERT(!m_responseDocumentParser->isParsing());
1414 ASSERT(!m_responseLegacyStream);
1415 ASSERT(!m_responseStream);
1417 // Do nothing if we are called from |internalAbort()|.
1421 clearVariablesForLoading();
1423 m_responseDocument->implicitClose();
1425 if (!m_responseDocument->wellFormed())
1426 m_responseDocument = nullptr;
1428 m_parsedResponse = true;
1433 void XMLHttpRequest::endLoading()
1435 InspectorInstrumentation::didFinishXHRLoading(executionContext(), this, this, m_loaderIdentifier, m_responseText, m_method, m_url);
1439 m_loaderIdentifier = 0;
1444 void XMLHttpRequest::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
1446 WTF_LOG(Network, "XMLHttpRequest %p didSendData(%llu, %llu)", this, bytesSent, totalBytesToBeSent);
1451 if (m_uploadEventsAllowed)
1452 m_upload->dispatchProgressEvent(bytesSent, totalBytesToBeSent);
1454 if (bytesSent == totalBytesToBeSent && !m_uploadComplete) {
1455 m_uploadComplete = true;
1456 if (m_uploadEventsAllowed)
1457 m_upload->dispatchEventAndLoadEnd(EventTypeNames::load, true, bytesSent, totalBytesToBeSent);
1461 void XMLHttpRequest::didReceiveResponse(unsigned long identifier, const ResourceResponse& response, PassOwnPtr<WebDataConsumerHandle>)
1463 WTF_LOG(Network, "XMLHttpRequest %p didReceiveResponse(%lu)", this, identifier);
1465 m_response = response;
1466 if (!m_mimeTypeOverride.isEmpty()) {
1467 m_response.setHTTPHeaderField("Content-Type", m_mimeTypeOverride);
1468 m_finalResponseCharset = extractCharsetFromMediaType(m_mimeTypeOverride);
1471 if (m_finalResponseCharset.isEmpty())
1472 m_finalResponseCharset = response.textEncodingName();
1475 void XMLHttpRequest::parseDocumentChunk(const char* data, unsigned len)
1477 if (!m_responseDocumentParser) {
1478 ASSERT(!m_responseDocument);
1479 initResponseDocument();
1480 if (!m_responseDocument)
1483 m_responseDocumentParser = m_responseDocument->implicitOpen();
1484 m_responseDocumentParser->addClient(this);
1486 ASSERT(m_responseDocumentParser);
1488 if (m_responseDocumentParser->needsDecoder())
1489 m_responseDocumentParser->setDecoder(createDecoder());
1491 m_responseDocumentParser->appendBytes(data, len);
1494 PassOwnPtr<TextResourceDecoder> XMLHttpRequest::createDecoder() const
1496 if (m_responseTypeCode == ResponseTypeJSON)
1497 return TextResourceDecoder::create("application/json", "UTF-8");
1499 if (!m_finalResponseCharset.isEmpty())
1500 return TextResourceDecoder::create("text/plain", m_finalResponseCharset);
1502 // allow TextResourceDecoder to look inside the m_response if it's XML or HTML
1503 if (responseIsXML()) {
1504 OwnPtr<TextResourceDecoder> decoder = TextResourceDecoder::create("application/xml");
1505 // Don't stop on encoding errors, unlike it is done for other kinds
1506 // of XML resources. This matches the behavior of previous WebKit
1507 // versions, Firefox and Opera.
1508 decoder->useLenientXMLDecoding();
1510 return decoder.release();
1513 if (responseIsHTML())
1514 return TextResourceDecoder::create("text/html", "UTF-8");
1516 return TextResourceDecoder::create("text/plain", "UTF-8");
1519 void XMLHttpRequest::didReceiveData(const char* data, unsigned len)
1524 if (m_state < HEADERS_RECEIVED)
1525 changeState(HEADERS_RECEIVED);
1527 // We need to check for |m_error| again, because |changeState| may trigger
1528 // readystatechange, and user javascript can cause |abort()|.
1535 if (m_responseTypeCode == ResponseTypeDocument && responseIsHTML()) {
1536 parseDocumentChunk(data, len);
1537 } else if (m_responseTypeCode == ResponseTypeDefault || m_responseTypeCode == ResponseTypeText || m_responseTypeCode == ResponseTypeJSON || m_responseTypeCode == ResponseTypeDocument) {
1539 m_decoder = createDecoder();
1541 m_responseText = m_responseText.concatenateWith(m_decoder->decode(data, len));
1542 } else if (m_responseTypeCode == ResponseTypeArrayBuffer || m_responseTypeCode == ResponseTypeBlob) {
1543 // Buffer binary data.
1544 if (!m_binaryResponseBuilder)
1545 m_binaryResponseBuilder = SharedBuffer::create();
1546 m_binaryResponseBuilder->append(data, len);
1547 } else if (m_responseTypeCode == ResponseTypeLegacyStream) {
1548 if (!m_responseLegacyStream)
1549 m_responseLegacyStream = Stream::create(executionContext(), responseType());
1550 m_responseLegacyStream->addData(data, len);
1551 } else if (m_responseTypeCode == ResponseTypeStream) {
1552 if (!m_responseStream) {
1553 m_responseStream = new ReadableStreamImpl<ReadableStreamChunkTypeTraits<DOMArrayBuffer> >(executionContext(), new ReadableStreamSource(this));
1554 m_responseStream->didSourceStart();
1556 m_responseStream->enqueue(DOMArrayBuffer::create(data, len));
1560 // In this case, the data is provided by m_blobLoader. As progress
1561 // events are already fired, we should return here.
1567 void XMLHttpRequest::didDownloadData(int dataLength)
1572 ASSERT(m_downloadingToFile);
1574 if (m_state < HEADERS_RECEIVED)
1575 changeState(HEADERS_RECEIVED);
1580 // readystatechange event handler may do something to put this XHR in error
1581 // state. We need to check m_error again here.
1585 m_lengthDownloadedToFile += dataLength;
1587 trackProgress(dataLength);
1590 void XMLHttpRequest::handleDidTimeout()
1592 WTF_LOG(Network, "XMLHttpRequest %p handleDidTimeout()", this);
1594 // Response is cleared next, save needed progress event data.
1595 long long expectedLength = m_response.expectedContentLength();
1596 long long receivedLength = m_receivedLength;
1598 // Prevent the XMLHttpRequest instance from being destoryed during
1599 // |internalAbort()|.
1600 RefPtrWillBeRawPtr<XMLHttpRequest> protect(this);
1602 if (!internalAbort())
1605 handleRequestError(TimeoutError, EventTypeNames::timeout, receivedLength, expectedLength);
1608 void XMLHttpRequest::suspend()
1610 m_progressEventThrottle.suspend();
1613 void XMLHttpRequest::resume()
1615 m_progressEventThrottle.resume();
1618 void XMLHttpRequest::stop()
1623 bool XMLHttpRequest::hasPendingActivity() const
1625 // Neither this object nor the JavaScript wrapper should be deleted while
1626 // a request is in progress because we need to keep the listeners alive,
1627 // and they are referenced by the JavaScript wrapper.
1628 // |m_loader| is non-null while request is active and ThreadableLoaderClient
1629 // callbacks may be called, and |m_responseDocumentParser| is non-null while
1630 // DocumentParserClient callbacks may be called.
1631 return m_loader || m_responseDocumentParser;
1634 void XMLHttpRequest::contextDestroyed()
1637 ActiveDOMObject::contextDestroyed();
1640 const AtomicString& XMLHttpRequest::interfaceName() const
1642 return EventTargetNames::XMLHttpRequest;
1645 ExecutionContext* XMLHttpRequest::executionContext() const
1647 return ActiveDOMObject::executionContext();
1650 void XMLHttpRequest::trace(Visitor* visitor)
1652 visitor->trace(m_responseBlob);
1653 visitor->trace(m_responseLegacyStream);
1654 visitor->trace(m_responseStream);
1655 visitor->trace(m_streamSource);
1656 visitor->trace(m_responseDocument);
1657 visitor->trace(m_responseDocumentParser);
1658 visitor->trace(m_progressEventThrottle);
1659 visitor->trace(m_upload);
1660 visitor->trace(m_blobLoader);
1661 XMLHttpRequestEventTarget::trace(visitor);
1662 DocumentParserClient::trace(visitor);
1665 } // namespace blink