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/xml/XMLHttpRequest.h"
26 #include "bindings/core/v8/ExceptionState.h"
27 #include "core/FetchInitiatorTypeNames.h"
28 #include "core/dom/ContextFeatures.h"
29 #include "core/dom/DOMException.h"
30 #include "core/dom/DOMImplementation.h"
31 #include "core/dom/DocumentParser.h"
32 #include "core/dom/ExceptionCode.h"
33 #include "core/dom/XMLDocument.h"
34 #include "core/editing/markup.h"
35 #include "core/events/Event.h"
36 #include "core/fetch/FetchUtils.h"
37 #include "core/fileapi/Blob.h"
38 #include "core/fileapi/File.h"
39 #include "core/frame/Settings.h"
40 #include "core/frame/UseCounter.h"
41 #include "core/frame/csp/ContentSecurityPolicy.h"
42 #include "core/html/DOMFormData.h"
43 #include "core/html/HTMLDocument.h"
44 #include "core/html/parser/TextResourceDecoder.h"
45 #include "core/inspector/ConsoleMessage.h"
46 #include "core/inspector/InspectorInstrumentation.h"
47 #include "core/inspector/InspectorTraceEvents.h"
48 #include "core/loader/ThreadableLoader.h"
49 #include "core/streams/ReadableStream.h"
50 #include "core/streams/ReadableStreamImpl.h"
51 #include "core/streams/Stream.h"
52 #include "core/streams/UnderlyingSource.h"
53 #include "core/xml/XMLHttpRequestProgressEvent.h"
54 #include "core/xml/XMLHttpRequestUpload.h"
55 #include "platform/Logging.h"
56 #include "platform/RuntimeEnabledFeatures.h"
57 #include "platform/SharedBuffer.h"
58 #include "platform/blob/BlobData.h"
59 #include "platform/network/HTTPParsers.h"
60 #include "platform/network/ParsedContentType.h"
61 #include "platform/network/ResourceError.h"
62 #include "platform/network/ResourceRequest.h"
63 #include "public/platform/WebURLRequest.h"
64 #include "wtf/ArrayBuffer.h"
65 #include "wtf/ArrayBufferView.h"
66 #include "wtf/Assertions.h"
67 #include "wtf/RefCountedLeakCounter.h"
68 #include "wtf/StdLibExtras.h"
69 #include "wtf/text/CString.h"
73 DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, xmlHttpRequestCounter, ("XMLHttpRequest"));
75 static bool isSetCookieHeader(const AtomicString& name)
77 return equalIgnoringCase(name, "set-cookie") || equalIgnoringCase(name, "set-cookie2");
80 static void replaceCharsetInMediaType(String& mediaType, const String& charsetValue)
82 unsigned pos = 0, len = 0;
84 findCharsetInMediaType(mediaType, pos, len);
87 // When no charset found, do nothing.
91 // Found at least one existing charset, replace all occurrences with new charset.
93 mediaType.replace(pos, len, charsetValue);
94 unsigned start = pos + charsetValue.length();
95 findCharsetInMediaType(mediaType, pos, len, start);
99 static void logConsoleError(ExecutionContext* context, const String& message)
103 // FIXME: It's not good to report the bad usage without indicating what source line it came from.
104 // We should pass additional parameters so we can tell the console where the mistake occurred.
105 context->addConsoleMessage(ConsoleMessage::create(JSMessageSource, ErrorMessageLevel, message));
110 class ReadableStreamSource : public GarbageCollectedFinalized<ReadableStreamSource>, public UnderlyingSource {
111 USING_GARBAGE_COLLECTED_MIXIN(ReadableStreamSource);
113 ReadableStreamSource(XMLHttpRequest* owner) : m_owner(owner) { }
114 virtual ~ReadableStreamSource() { }
115 virtual void pullSource() OVERRIDE { }
116 virtual ScriptPromise cancelSource(ScriptState* scriptState, ScriptValue reason) OVERRIDE
119 return ScriptPromise::cast(scriptState, v8::Undefined(scriptState->isolate()));
121 virtual void trace(Visitor* visitor) OVERRIDE
123 visitor->trace(m_owner);
124 UnderlyingSource::trace(visitor);
128 // This is RawPtr in non-oilpan build to avoid the reference cycle. To
129 // avoid use-after free, the associated ReadableStream must be closed
130 // or errored when m_owner is gone.
131 RawPtrWillBeMember<XMLHttpRequest> m_owner;
136 PassRefPtrWillBeRawPtr<XMLHttpRequest> XMLHttpRequest::create(ExecutionContext* context, PassRefPtr<SecurityOrigin> securityOrigin)
138 RefPtrWillBeRawPtr<XMLHttpRequest> xmlHttpRequest = adoptRefWillBeNoop(new XMLHttpRequest(context, securityOrigin));
139 xmlHttpRequest->suspendIfNeeded();
141 return xmlHttpRequest.release();
144 XMLHttpRequest::XMLHttpRequest(ExecutionContext* context, PassRefPtr<SecurityOrigin> securityOrigin)
145 : ActiveDOMObject(context)
146 , m_timeoutMilliseconds(0)
147 , m_loaderIdentifier(0)
149 , m_lengthDownloadedToFile(0)
150 , m_receivedLength(0)
151 , m_lastSendLineNumber(0)
153 , m_progressEventThrottle(this)
154 , m_responseTypeCode(ResponseTypeDefault)
155 , m_securityOrigin(securityOrigin)
157 , m_includeCredentials(false)
158 , m_parsedResponse(false)
160 , m_uploadEventsAllowed(true)
161 , m_uploadComplete(false)
162 , m_sameOriginRequest(true)
163 , m_downloadingToFile(false)
166 xmlHttpRequestCounter.increment();
170 XMLHttpRequest::~XMLHttpRequest()
173 xmlHttpRequestCounter.decrement();
177 Document* XMLHttpRequest::document() const
179 ASSERT(executionContext()->isDocument());
180 return toDocument(executionContext());
183 SecurityOrigin* XMLHttpRequest::securityOrigin() const
185 return m_securityOrigin ? m_securityOrigin.get() : executionContext()->securityOrigin();
188 XMLHttpRequest::State XMLHttpRequest::readyState() const
193 ScriptString XMLHttpRequest::responseText(ExceptionState& exceptionState)
195 if (m_responseTypeCode != ResponseTypeDefault && m_responseTypeCode != ResponseTypeText) {
196 exceptionState.throwDOMException(InvalidStateError, "The value is only accessible if the object's 'responseType' is '' or 'text' (was '" + responseType() + "').");
197 return ScriptString();
199 if (m_error || (m_state != LOADING && m_state != DONE))
200 return ScriptString();
201 return m_responseText;
204 ScriptString XMLHttpRequest::responseJSONSource()
206 ASSERT(m_responseTypeCode == ResponseTypeJSON);
208 if (m_error || m_state != DONE)
209 return ScriptString();
210 return m_responseText;
213 void XMLHttpRequest::initResponseDocument()
215 // The W3C spec requires the final MIME type to be some valid XML type, or text/html.
216 // If it is text/html, then the responseType of "document" must have been supplied explicitly.
217 bool isHTML = responseIsHTML();
218 if ((m_response.isHTTP() && !responseIsXML() && !isHTML)
219 || (isHTML && m_responseTypeCode == ResponseTypeDefault)
220 || executionContext()->isWorkerGlobalScope()) {
221 m_responseDocument = nullptr;
225 DocumentInit init = DocumentInit::fromContext(document()->contextDocument(), m_url);
227 m_responseDocument = HTMLDocument::create(init);
229 m_responseDocument = XMLDocument::create(init);
231 // FIXME: Set Last-Modified.
232 m_responseDocument->setSecurityOrigin(securityOrigin());
233 m_responseDocument->setContextFeatures(document()->contextFeatures());
234 m_responseDocument->setMimeType(finalResponseMIMETypeWithFallback());
237 Document* XMLHttpRequest::responseXML(ExceptionState& exceptionState)
239 if (m_responseTypeCode != ResponseTypeDefault && m_responseTypeCode != ResponseTypeDocument) {
240 exceptionState.throwDOMException(InvalidStateError, "The value is only accessible if the object's 'responseType' is '' or 'document' (was '" + responseType() + "').");
244 if (m_error || m_state != DONE)
247 if (!m_parsedResponse) {
248 initResponseDocument();
249 if (!m_responseDocument)
252 m_responseDocument->setContent(m_responseText.flattenToString());
253 if (!m_responseDocument->wellFormed())
254 m_responseDocument = nullptr;
256 m_parsedResponse = true;
259 return m_responseDocument.get();
262 Blob* XMLHttpRequest::responseBlob()
264 ASSERT(m_responseTypeCode == ResponseTypeBlob);
266 // We always return null before DONE.
267 if (m_error || m_state != DONE)
270 if (!m_responseBlob) {
271 OwnPtr<BlobData> blobData = BlobData::create();
272 if (m_downloadingToFile) {
273 ASSERT(!m_binaryResponseBuilder.get());
275 // When responseType is set to "blob", we redirect the downloaded
276 // data to a file-handle directly in the browser process. We get
277 // the file-path from the ResourceResponse directly instead of
278 // copying the bytes between the browser and the renderer.
279 String filePath = m_response.downloadedFilePath();
280 // If we errored out or got no data, we still return a blob, just
282 if (!filePath.isEmpty() && m_lengthDownloadedToFile) {
283 blobData->appendFile(filePath);
284 // FIXME: finalResponseMIMETypeWithFallback() defaults to
285 // text/xml which may be incorrect. Replace it with
286 // finalResponseMIMEType() after compatibility investigation.
287 blobData->setContentType(finalResponseMIMETypeWithFallback());
289 m_responseBlob = Blob::create(BlobDataHandle::create(blobData.release(), m_lengthDownloadedToFile));
292 if (m_binaryResponseBuilder.get() && m_binaryResponseBuilder->size()) {
293 size = m_binaryResponseBuilder->size();
294 blobData->appendBytes(m_binaryResponseBuilder->data(), size);
295 blobData->setContentType(finalResponseMIMETypeWithFallback());
296 m_binaryResponseBuilder.clear();
298 m_responseBlob = Blob::create(BlobDataHandle::create(blobData.release(), size));
302 return m_responseBlob.get();
305 ArrayBuffer* XMLHttpRequest::responseArrayBuffer()
307 ASSERT(m_responseTypeCode == ResponseTypeArrayBuffer);
309 if (m_error || m_state != DONE)
312 if (!m_responseArrayBuffer.get()) {
313 if (m_binaryResponseBuilder.get() && m_binaryResponseBuilder->size() > 0) {
314 m_responseArrayBuffer = m_binaryResponseBuilder->getAsArrayBuffer();
315 if (!m_responseArrayBuffer) {
316 // m_binaryResponseBuilder failed to allocate an ArrayBuffer.
317 // We need to crash the renderer since there's no way defined in
318 // the spec to tell this to the user.
321 m_binaryResponseBuilder.clear();
323 m_responseArrayBuffer = ArrayBuffer::create(static_cast<void*>(0), 0);
327 return m_responseArrayBuffer.get();
330 Stream* XMLHttpRequest::responseLegacyStream()
332 ASSERT(m_responseTypeCode == ResponseTypeLegacyStream);
334 if (m_error || (m_state != LOADING && m_state != DONE))
337 return m_responseLegacyStream.get();
340 ReadableStream* XMLHttpRequest::responseStream()
342 ASSERT(m_responseTypeCode == ResponseTypeStream);
343 if (m_error || (m_state != LOADING && m_state != DONE))
346 return m_responseStream;
349 void XMLHttpRequest::setTimeout(unsigned long timeout, ExceptionState& exceptionState)
351 // FIXME: Need to trigger or update the timeout Timer here, if needed. http://webkit.org/b/98156
352 // 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."
353 if (executionContext()->isDocument() && !m_async) {
354 exceptionState.throwDOMException(InvalidAccessError, "Timeouts cannot be set for synchronous requests made from a document.");
358 m_timeoutMilliseconds = timeout;
360 // From http://www.w3.org/TR/XMLHttpRequest/#the-timeout-attribute:
361 // Note: This implies that the timeout attribute can be set while fetching is in progress. If
362 // that occurs it will still be measured relative to the start of fetching.
364 // The timeout may be overridden after send.
366 m_loader->overrideTimeout(timeout);
369 void XMLHttpRequest::setResponseType(const String& responseType, ExceptionState& exceptionState)
371 if (m_state >= LOADING) {
372 exceptionState.throwDOMException(InvalidStateError, "The response type cannot be set if the object's state is LOADING or DONE.");
376 // Newer functionality is not available to synchronous requests in window contexts, as a spec-mandated
377 // attempt to discourage synchronous XHR use. responseType is one such piece of functionality.
378 if (!m_async && executionContext()->isDocument()) {
379 exceptionState.throwDOMException(InvalidAccessError, "The response type cannot be changed for synchronous requests made from a document.");
383 if (responseType == "") {
384 m_responseTypeCode = ResponseTypeDefault;
385 } else if (responseType == "text") {
386 m_responseTypeCode = ResponseTypeText;
387 } else if (responseType == "json") {
388 m_responseTypeCode = ResponseTypeJSON;
389 } else if (responseType == "document") {
390 m_responseTypeCode = ResponseTypeDocument;
391 } else if (responseType == "blob") {
392 m_responseTypeCode = ResponseTypeBlob;
393 } else if (responseType == "arraybuffer") {
394 m_responseTypeCode = ResponseTypeArrayBuffer;
395 } else if (responseType == "legacystream") {
396 if (RuntimeEnabledFeatures::streamEnabled())
397 m_responseTypeCode = ResponseTypeLegacyStream;
400 } else if (responseType == "stream") {
401 if (RuntimeEnabledFeatures::streamEnabled())
402 m_responseTypeCode = ResponseTypeStream;
406 ASSERT_NOT_REACHED();
410 String XMLHttpRequest::responseType()
412 switch (m_responseTypeCode) {
413 case ResponseTypeDefault:
415 case ResponseTypeText:
417 case ResponseTypeJSON:
419 case ResponseTypeDocument:
421 case ResponseTypeBlob:
423 case ResponseTypeArrayBuffer:
424 return "arraybuffer";
425 case ResponseTypeLegacyStream:
426 return "legacystream";
427 case ResponseTypeStream:
433 String XMLHttpRequest::responseURL()
435 return m_response.url().string();
438 XMLHttpRequestUpload* XMLHttpRequest::upload()
441 m_upload = XMLHttpRequestUpload::create(this);
442 return m_upload.get();
445 void XMLHttpRequest::trackProgress(int length)
447 m_receivedLength += length;
449 if (m_state != LOADING) {
450 changeState(LOADING);
452 // Dispatch a readystatechange event because many applications use
453 // it to track progress although this is not specified.
455 // FIXME: Stop dispatching this event for progress tracking.
456 dispatchReadyStateChangeEvent();
459 dispatchProgressEventFromSnapshot(EventTypeNames::progress);
462 void XMLHttpRequest::changeState(State newState)
464 if (m_state != newState) {
466 dispatchReadyStateChangeEvent();
470 void XMLHttpRequest::dispatchReadyStateChangeEvent()
472 if (!executionContext())
475 InspectorInstrumentationCookie cookie = InspectorInstrumentation::willDispatchXHRReadyStateChangeEvent(executionContext(), this);
477 if (m_async || (m_state <= OPENED || m_state == DONE)) {
478 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "XHRReadyStateChange", "data", InspectorXhrReadyStateChangeEvent::data(executionContext(), this));
479 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline.stack"), "CallStack", "stack", InspectorCallStackEvent::currentCallStack());
480 XMLHttpRequestProgressEventThrottle::DeferredEventAction action = XMLHttpRequestProgressEventThrottle::Ignore;
481 if (m_state == DONE) {
483 action = XMLHttpRequestProgressEventThrottle::Clear;
485 action = XMLHttpRequestProgressEventThrottle::Flush;
487 m_progressEventThrottle.dispatchReadyStateChangeEvent(Event::create(EventTypeNames::readystatechange), action);
488 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "UpdateCounters", "data", InspectorUpdateCountersEvent::data());
491 InspectorInstrumentation::didDispatchXHRReadyStateChangeEvent(cookie);
492 if (m_state == DONE && !m_error) {
494 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "XHRLoad", "data", InspectorXhrLoadEvent::data(executionContext(), this));
495 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline.stack"), "CallStack", "stack", InspectorCallStackEvent::currentCallStack());
496 InspectorInstrumentationCookie cookie = InspectorInstrumentation::willDispatchXHRLoadEvent(executionContext(), this);
497 dispatchProgressEventFromSnapshot(EventTypeNames::load);
498 InspectorInstrumentation::didDispatchXHRLoadEvent(cookie);
500 dispatchProgressEventFromSnapshot(EventTypeNames::loadend);
501 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "UpdateCounters", "data", InspectorUpdateCountersEvent::data());
505 void XMLHttpRequest::setWithCredentials(bool value, ExceptionState& exceptionState)
507 if (m_state > OPENED || m_loader) {
508 exceptionState.throwDOMException(InvalidStateError, "The value may only be set if the object's state is UNSENT or OPENED.");
512 // FIXME: According to XMLHttpRequest Level 2 we should throw InvalidAccessError exception here.
513 // However for time being only print warning message to warn web developers.
515 UseCounter::countDeprecation(executionContext(), UseCounter::SyncXHRWithCredentials);
517 m_includeCredentials = value;
520 AtomicString XMLHttpRequest::uppercaseKnownHTTPMethod(const AtomicString& method)
522 // Valid methods per step-5 of http://xhr.spec.whatwg.org/#the-open()-method.
523 const char* const methods[] = {
531 for (unsigned i = 0; i < WTF_ARRAY_LENGTH(methods); ++i) {
532 if (equalIgnoringCase(method, methods[i])) {
533 // Don't bother allocating a new string if it's already all uppercase.
534 if (method == methods[i])
542 void XMLHttpRequest::open(const AtomicString& method, const KURL& url, ExceptionState& exceptionState)
544 open(method, url, true, exceptionState);
547 void XMLHttpRequest::open(const AtomicString& method, const KURL& url, bool async, ExceptionState& exceptionState)
549 WTF_LOG(Network, "XMLHttpRequest %p open('%s', '%s', %d)", this, method.utf8().data(), url.elidedString().utf8().data(), async);
551 if (!internalAbort())
554 State previousState = m_state;
557 m_uploadComplete = false;
559 if (!isValidHTTPToken(method)) {
560 exceptionState.throwDOMException(SyntaxError, "'" + method + "' is not a valid HTTP method.");
564 if (FetchUtils::isForbiddenMethod(method)) {
565 exceptionState.throwSecurityError("'" + method + "' HTTP method is unsupported.");
569 if (!ContentSecurityPolicy::shouldBypassMainWorld(executionContext()) && !executionContext()->contentSecurityPolicy()->allowConnectToSource(url)) {
570 // We can safely expose the URL to JavaScript, as these checks happen synchronously before redirection. JavaScript receives no new information.
571 exceptionState.throwSecurityError("Refused to connect to '" + url.elidedString() + "' because it violates the document's Content Security Policy.");
575 if (!async && executionContext()->isDocument()) {
576 if (document()->settings() && !document()->settings()->syncXHRInDocumentsEnabled()) {
577 exceptionState.throwDOMException(InvalidAccessError, "Synchronous requests are disabled for this page.");
581 // Newer functionality is not available to synchronous requests in window contexts, as a spec-mandated
582 // attempt to discourage synchronous XHR use. responseType is one such piece of functionality.
583 if (m_responseTypeCode != ResponseTypeDefault) {
584 exceptionState.throwDOMException(InvalidAccessError, "Synchronous requests from a document must not set a response type.");
588 // Similarly, timeouts are disabled for synchronous requests as well.
589 if (m_timeoutMilliseconds > 0) {
590 exceptionState.throwDOMException(InvalidAccessError, "Synchronous requests must not set a timeout.");
595 m_method = uppercaseKnownHTTPMethod(method);
603 // Check previous state to avoid dispatching readyState event
604 // when calling open several times in a row.
605 if (previousState != OPENED)
611 void XMLHttpRequest::open(const AtomicString& method, const KURL& url, bool async, const String& user, ExceptionState& exceptionState)
613 KURL urlWithCredentials(url);
614 urlWithCredentials.setUser(user);
616 open(method, urlWithCredentials, async, exceptionState);
619 void XMLHttpRequest::open(const AtomicString& method, const KURL& url, bool async, const String& user, const String& password, ExceptionState& exceptionState)
621 KURL urlWithCredentials(url);
622 urlWithCredentials.setUser(user);
623 urlWithCredentials.setPass(password);
625 open(method, urlWithCredentials, async, exceptionState);
628 bool XMLHttpRequest::initSend(ExceptionState& exceptionState)
630 if (!executionContext())
633 if (m_state != OPENED || m_loader) {
634 exceptionState.throwDOMException(InvalidStateError, "The object's state must be OPENED.");
642 void XMLHttpRequest::send(ExceptionState& exceptionState)
644 send(String(), exceptionState);
647 bool XMLHttpRequest::areMethodAndURLValidForSend()
649 return m_method != "GET" && m_method != "HEAD" && m_url.protocolIsInHTTPFamily();
652 void XMLHttpRequest::send(Document* document, ExceptionState& exceptionState)
654 WTF_LOG(Network, "XMLHttpRequest %p send() Document %p", this, document);
658 if (!initSend(exceptionState))
661 RefPtr<FormData> httpBody;
663 if (areMethodAndURLValidForSend()) {
664 if (getRequestHeader("Content-Type").isEmpty()) {
665 // FIXME: this should include the charset used for encoding.
666 setRequestHeaderInternal("Content-Type", "application/xml");
669 // FIXME: According to XMLHttpRequest Level 2, this should use the Document.innerHTML algorithm
670 // from the HTML5 specification to serialize the document.
671 String body = createMarkup(document);
673 // FIXME: This should use value of document.inputEncoding to determine the encoding to use.
674 httpBody = FormData::create(UTF8Encoding().encode(body, WTF::EntitiesForUnencodables));
676 httpBody->setAlwaysStream(true);
679 createRequest(httpBody.release(), exceptionState);
682 void XMLHttpRequest::send(const String& body, ExceptionState& exceptionState)
684 WTF_LOG(Network, "XMLHttpRequest %p send() String '%s'", this, body.utf8().data());
686 if (!initSend(exceptionState))
689 RefPtr<FormData> httpBody;
691 if (!body.isNull() && areMethodAndURLValidForSend()) {
692 String contentType = getRequestHeader("Content-Type");
693 if (contentType.isEmpty()) {
694 setRequestHeaderInternal("Content-Type", "text/plain;charset=UTF-8");
696 replaceCharsetInMediaType(contentType, "UTF-8");
697 m_requestHeaders.set("Content-Type", AtomicString(contentType));
700 httpBody = FormData::create(UTF8Encoding().encode(body, WTF::EntitiesForUnencodables));
702 httpBody->setAlwaysStream(true);
705 createRequest(httpBody.release(), exceptionState);
708 void XMLHttpRequest::send(Blob* body, ExceptionState& exceptionState)
710 WTF_LOG(Network, "XMLHttpRequest %p send() Blob '%s'", this, body->uuid().utf8().data());
712 if (!initSend(exceptionState))
715 RefPtr<FormData> httpBody;
717 if (areMethodAndURLValidForSend()) {
718 if (getRequestHeader("Content-Type").isEmpty()) {
719 const String& blobType = body->type();
720 if (!blobType.isEmpty() && isValidContentType(blobType)) {
721 setRequestHeaderInternal("Content-Type", AtomicString(blobType));
723 // From FileAPI spec, whenever media type cannot be determined,
724 // empty string must be returned.
725 setRequestHeaderInternal("Content-Type", "");
729 // FIXME: add support for uploading bundles.
730 httpBody = FormData::create();
731 if (body->hasBackingFile()) {
732 File* file = toFile(body);
733 if (!file->path().isEmpty())
734 httpBody->appendFile(file->path());
735 else if (!file->fileSystemURL().isEmpty())
736 httpBody->appendFileSystemURL(file->fileSystemURL());
738 ASSERT_NOT_REACHED();
740 httpBody->appendBlob(body->uuid(), body->blobDataHandle());
744 createRequest(httpBody.release(), exceptionState);
747 void XMLHttpRequest::send(DOMFormData* body, ExceptionState& exceptionState)
749 WTF_LOG(Network, "XMLHttpRequest %p send() DOMFormData %p", this, body);
751 if (!initSend(exceptionState))
754 RefPtr<FormData> httpBody;
756 if (areMethodAndURLValidForSend()) {
757 httpBody = body->createMultiPartFormData();
759 if (getRequestHeader("Content-Type").isEmpty()) {
760 AtomicString contentType = AtomicString("multipart/form-data; boundary=", AtomicString::ConstructFromLiteral) + httpBody->boundary().data();
761 setRequestHeaderInternal("Content-Type", contentType);
765 createRequest(httpBody.release(), exceptionState);
768 void XMLHttpRequest::send(ArrayBuffer* body, ExceptionState& exceptionState)
770 WTF_LOG(Network, "XMLHttpRequest %p send() ArrayBuffer %p", this, body);
772 sendBytesData(body->data(), body->byteLength(), exceptionState);
775 void XMLHttpRequest::send(ArrayBufferView* body, ExceptionState& exceptionState)
777 WTF_LOG(Network, "XMLHttpRequest %p send() ArrayBufferView %p", this, body);
779 sendBytesData(body->baseAddress(), body->byteLength(), exceptionState);
782 void XMLHttpRequest::sendBytesData(const void* data, size_t length, ExceptionState& exceptionState)
784 if (!initSend(exceptionState))
787 RefPtr<FormData> httpBody;
789 if (areMethodAndURLValidForSend()) {
790 httpBody = FormData::create(data, length);
792 httpBody->setAlwaysStream(true);
795 createRequest(httpBody.release(), exceptionState);
798 void XMLHttpRequest::sendForInspectorXHRReplay(PassRefPtr<FormData> formData, ExceptionState& exceptionState)
800 createRequest(formData ? formData->deepCopy() : nullptr, exceptionState);
801 m_exceptionCode = exceptionState.code();
804 void XMLHttpRequest::createRequest(PassRefPtr<FormData> httpBody, ExceptionState& exceptionState)
806 // Only GET request is supported for blob URL.
807 if (m_url.protocolIs("blob") && m_method != "GET") {
808 exceptionState.throwDOMException(NetworkError, "'GET' is the only method allowed for 'blob:' URLs.");
812 // The presence of upload event listeners forces us to use preflighting because POSTing to an URL that does not
813 // permit cross origin requests should look exactly like POSTing to an URL that does not respond at all.
814 // Also, only async requests support upload progress events.
815 bool uploadEvents = false;
817 dispatchProgressEvent(EventTypeNames::loadstart, 0, 0);
818 if (httpBody && m_upload) {
819 uploadEvents = m_upload->hasEventListeners();
820 m_upload->dispatchEvent(XMLHttpRequestProgressEvent::create(EventTypeNames::loadstart));
824 m_sameOriginRequest = securityOrigin()->canRequest(m_url);
826 // We also remember whether upload events should be allowed for this request in case the upload listeners are
827 // added after the request is started.
828 m_uploadEventsAllowed = m_sameOriginRequest || uploadEvents || !FetchUtils::isSimpleRequest(m_method, m_requestHeaders);
830 ASSERT(executionContext());
831 ExecutionContext& executionContext = *this->executionContext();
833 ResourceRequest request(m_url);
834 request.setHTTPMethod(m_method);
835 request.setRequestContext(blink::WebURLRequest::RequestContextXMLHttpRequest);
837 InspectorInstrumentation::willLoadXHR(&executionContext, this, this, m_method, m_url, m_async, httpBody ? httpBody->deepCopy() : nullptr, m_requestHeaders, m_includeCredentials);
840 ASSERT(m_method != "GET");
841 ASSERT(m_method != "HEAD");
842 request.setHTTPBody(httpBody);
845 if (m_requestHeaders.size() > 0)
846 request.addHTTPHeaderFields(m_requestHeaders);
848 ThreadableLoaderOptions options;
849 options.preflightPolicy = uploadEvents ? ForcePreflight : ConsiderPreflight;
850 options.crossOriginRequestPolicy = UseAccessControl;
851 options.initiator = FetchInitiatorTypeNames::xmlhttprequest;
852 options.contentSecurityPolicyEnforcement = ContentSecurityPolicy::shouldBypassMainWorld(&executionContext) ? DoNotEnforceContentSecurityPolicy : EnforceConnectSrcDirective;
853 options.timeoutMilliseconds = m_timeoutMilliseconds;
855 ResourceLoaderOptions resourceLoaderOptions;
856 resourceLoaderOptions.allowCredentials = (m_sameOriginRequest || m_includeCredentials) ? AllowStoredCredentials : DoNotAllowStoredCredentials;
857 resourceLoaderOptions.credentialsRequested = m_includeCredentials ? ClientRequestedCredentials : ClientDidNotRequestCredentials;
858 resourceLoaderOptions.securityOrigin = securityOrigin();
859 resourceLoaderOptions.mixedContentBlockingTreatment = RuntimeEnabledFeatures::laxMixedContentCheckingEnabled() ? TreatAsPassiveContent : TreatAsActiveContent;
861 // When responseType is set to "blob", we redirect the downloaded data to a
862 // file-handle directly.
863 m_downloadingToFile = responseTypeCode() == ResponseTypeBlob;
864 if (m_downloadingToFile) {
865 request.setDownloadToFile(true);
866 resourceLoaderOptions.dataBufferingPolicy = DoNotBufferData;
874 request.setReportUploadProgress(true);
876 // ThreadableLoader::create can return null here, for example if we're no longer attached to a page.
877 // This is true while running onunload handlers.
878 // FIXME: Maybe we need to be able to send XMLHttpRequests from onunload, <http://bugs.webkit.org/show_bug.cgi?id=10904>.
879 // FIXME: Maybe create() can return null for other reasons too?
881 m_loader = ThreadableLoader::create(executionContext, this, request, options, resourceLoaderOptions);
883 // Use count for XHR synchronous requests.
884 UseCounter::count(&executionContext, UseCounter::XMLHttpRequestSynchronous);
885 ThreadableLoader::loadResourceSynchronously(executionContext, request, *this, options, resourceLoaderOptions);
888 if (!m_exceptionCode && m_error)
889 m_exceptionCode = NetworkError;
891 exceptionState.throwDOMException(m_exceptionCode, "Failed to load '" + m_url.elidedString() + "'.");
894 void XMLHttpRequest::abort()
896 WTF_LOG(Network, "XMLHttpRequest %p abort()", this);
898 // internalAbort() clears |m_loader|. Compute |sendFlag| now.
900 // |sendFlag| corresponds to "the send() flag" defined in the XHR spec.
902 // |sendFlag| is only set when we have an active, asynchronous loader.
903 // Don't use it as "the send() flag" when the XHR is in sync mode.
904 bool sendFlag = m_loader;
906 // internalAbort() clears the response. Save the data needed for
907 // dispatching ProgressEvents.
908 long long expectedLength = m_response.expectedContentLength();
909 long long receivedLength = m_receivedLength;
911 if (!internalAbort())
914 // The script never gets any chance to call abort() on a sync XHR between
915 // send() call and transition to the DONE state. It's because a sync XHR
916 // doesn't dispatch any event between them. So, if |m_async| is false, we
917 // can skip the "request error steps" (defined in the XHR spec) without any
920 // FIXME: It's possible open() is invoked in internalAbort() and |m_async|
921 // becomes true by that. We should implement more reliable treatment for
922 // nested method invocations at some point.
924 if ((m_state == OPENED && sendFlag) || m_state == HEADERS_RECEIVED || m_state == LOADING) {
926 handleRequestError(0, EventTypeNames::abort, receivedLength, expectedLength);
932 void XMLHttpRequest::clearVariablesForLoading()
936 if (m_responseDocumentParser) {
937 m_responseDocumentParser->removeClient(this);
939 m_responseDocumentParser->detach();
941 m_responseDocumentParser = nullptr;
944 m_finalResponseCharset = String();
947 bool XMLHttpRequest::internalAbort()
951 if (m_responseDocumentParser && !m_responseDocumentParser->isStopped())
952 m_responseDocumentParser->stopParsing();
954 clearVariablesForLoading();
956 InspectorInstrumentation::didFailXHRLoading(executionContext(), this, this);
958 if (m_responseLegacyStream && m_state != DONE)
959 m_responseLegacyStream->abort();
961 if (m_responseStream) {
962 // When the stream is already closed (including canceled from the
963 // user), |error| does nothing.
964 // FIXME: Create a more specific error.
965 m_responseStream->error(DOMException::create(!m_async && m_exceptionCode ? m_exceptionCode : AbortError, "XMLHttpRequest::abort"));
974 // Cancelling the ThreadableLoader m_loader may result in calling
975 // window.onload synchronously. If such an onload handler contains open()
976 // call on the same XMLHttpRequest object, reentry happens.
978 // If, window.onload contains open() and send(), m_loader will be set to
979 // non 0 value. So, we cannot continue the outer open(). In such case,
980 // just abort the outer open() by returning false.
981 RefPtrWillBeRawPtr<XMLHttpRequest> protect(this);
982 RefPtr<ThreadableLoader> loader = m_loader.release();
985 // If abort() called internalAbort() and a nested open() ended up
986 // clearing the error flag, but didn't send(), make sure the error
987 // flag is still set.
988 bool newLoadStarted = hasPendingActivity();
992 return !newLoadStarted;
995 void XMLHttpRequest::clearResponse()
997 // FIXME: when we add the support for multi-part XHR, we will have to
998 // be careful with this initialization.
999 m_receivedLength = 0;
1001 m_response = ResourceResponse();
1003 m_responseText.clear();
1005 m_parsedResponse = false;
1006 m_responseDocument = nullptr;
1008 m_responseBlob = nullptr;
1010 m_downloadingToFile = false;
1011 m_lengthDownloadedToFile = 0;
1013 m_responseLegacyStream = nullptr;
1014 m_responseStream = nullptr;
1016 // These variables may referred by the response accessors. So, we can clear
1017 // this only when we clear the response holder variables above.
1018 m_binaryResponseBuilder.clear();
1019 m_responseArrayBuffer.clear();
1022 void XMLHttpRequest::clearRequest()
1024 m_requestHeaders.clear();
1027 void XMLHttpRequest::dispatchProgressEvent(const AtomicString& type, long long receivedLength, long long expectedLength)
1029 bool lengthComputable = expectedLength > 0 && receivedLength <= expectedLength;
1030 unsigned long long loaded = receivedLength >= 0 ? static_cast<unsigned long long>(receivedLength) : 0;
1031 unsigned long long total = lengthComputable ? static_cast<unsigned long long>(expectedLength) : 0;
1033 m_progressEventThrottle.dispatchProgressEvent(type, lengthComputable, loaded, total);
1035 if (type == EventTypeNames::loadend)
1036 InspectorInstrumentation::didDispatchXHRLoadendEvent(executionContext(), this);
1039 void XMLHttpRequest::dispatchProgressEventFromSnapshot(const AtomicString& type)
1041 dispatchProgressEvent(type, m_receivedLength, m_response.expectedContentLength());
1044 void XMLHttpRequest::handleNetworkError()
1046 WTF_LOG(Network, "XMLHttpRequest %p handleNetworkError()", this);
1048 // Response is cleared next, save needed progress event data.
1049 long long expectedLength = m_response.expectedContentLength();
1050 long long receivedLength = m_receivedLength;
1052 if (!internalAbort())
1055 handleRequestError(NetworkError, EventTypeNames::error, receivedLength, expectedLength);
1058 void XMLHttpRequest::handleDidCancel()
1060 WTF_LOG(Network, "XMLHttpRequest %p handleDidCancel()", this);
1062 // Response is cleared next, save needed progress event data.
1063 long long expectedLength = m_response.expectedContentLength();
1064 long long receivedLength = m_receivedLength;
1066 if (!internalAbort())
1069 handleRequestError(AbortError, EventTypeNames::abort, receivedLength, expectedLength);
1072 void XMLHttpRequest::handleRequestError(ExceptionCode exceptionCode, const AtomicString& type, long long receivedLength, long long expectedLength)
1074 WTF_LOG(Network, "XMLHttpRequest %p handleRequestError()", this);
1076 // The request error steps for event 'type' and exception 'exceptionCode'.
1078 if (!m_async && exceptionCode) {
1080 m_exceptionCode = exceptionCode;
1083 // With m_error set, the state change steps are minimal: any pending
1084 // progress event is flushed + a readystatechange is dispatched.
1085 // No new progress events dispatched; as required, that happens at
1090 if (!m_uploadComplete) {
1091 m_uploadComplete = true;
1092 if (m_upload && m_uploadEventsAllowed)
1093 m_upload->handleRequestError(type);
1096 // Note: The below event dispatch may be called while |hasPendingActivity() == false|,
1097 // when |handleRequestError| is called after |internalAbort()|.
1098 // This is safe, however, as |this| will be kept alive from a strong ref |Event::m_target|.
1099 dispatchProgressEvent(EventTypeNames::progress, receivedLength, expectedLength);
1100 dispatchProgressEvent(type, receivedLength, expectedLength);
1101 dispatchProgressEvent(EventTypeNames::loadend, receivedLength, expectedLength);
1104 void XMLHttpRequest::overrideMimeType(const AtomicString& mimeType, ExceptionState& exceptionState)
1106 if (m_state == LOADING || m_state == DONE) {
1107 exceptionState.throwDOMException(InvalidStateError, "MimeType cannot be overridden when the state is LOADING or DONE.");
1111 m_mimeTypeOverride = mimeType;
1114 void XMLHttpRequest::setRequestHeader(const AtomicString& name, const AtomicString& value, ExceptionState& exceptionState)
1116 if (m_state != OPENED || m_loader) {
1117 exceptionState.throwDOMException(InvalidStateError, "The object's state must be OPENED.");
1121 if (!isValidHTTPToken(name)) {
1122 exceptionState.throwDOMException(SyntaxError, "'" + name + "' is not a valid HTTP header field name.");
1126 if (!isValidHTTPHeaderValue(value)) {
1127 exceptionState.throwDOMException(SyntaxError, "'" + value + "' is not a valid HTTP header field value.");
1131 // No script (privileged or not) can set unsafe headers.
1132 if (FetchUtils::isForbiddenHeaderName(name)) {
1133 logConsoleError(executionContext(), "Refused to set unsafe header \"" + name + "\"");
1137 setRequestHeaderInternal(name, value);
1140 void XMLHttpRequest::setRequestHeaderInternal(const AtomicString& name, const AtomicString& value)
1142 HTTPHeaderMap::AddResult result = m_requestHeaders.add(name, value);
1143 if (!result.isNewEntry)
1144 result.storedValue->value = result.storedValue->value + ", " + value;
1147 const AtomicString& XMLHttpRequest::getRequestHeader(const AtomicString& name) const
1149 return m_requestHeaders.get(name);
1152 String XMLHttpRequest::getAllResponseHeaders() const
1154 if (m_state < HEADERS_RECEIVED || m_error)
1157 StringBuilder stringBuilder;
1159 HTTPHeaderSet accessControlExposeHeaderSet;
1160 parseAccessControlExposeHeadersAllowList(m_response.httpHeaderField("Access-Control-Expose-Headers"), accessControlExposeHeaderSet);
1161 HTTPHeaderMap::const_iterator end = m_response.httpHeaderFields().end();
1162 for (HTTPHeaderMap::const_iterator it = m_response.httpHeaderFields().begin(); it!= end; ++it) {
1163 // Hide Set-Cookie header fields from the XMLHttpRequest client for these reasons:
1164 // 1) If the client did have access to the fields, then it could read HTTP-only
1165 // cookies; those cookies are supposed to be hidden from scripts.
1166 // 2) There's no known harm in hiding Set-Cookie header fields entirely; we don't
1167 // know any widely used technique that requires access to them.
1168 // 3) Firefox has implemented this policy.
1169 if (isSetCookieHeader(it->key) && !securityOrigin()->canLoadLocalResources())
1172 if (!m_sameOriginRequest && !isOnAccessControlResponseHeaderWhitelist(it->key) && !accessControlExposeHeaderSet.contains(it->key))
1175 stringBuilder.append(it->key);
1176 stringBuilder.append(':');
1177 stringBuilder.append(' ');
1178 stringBuilder.append(it->value);
1179 stringBuilder.append('\r');
1180 stringBuilder.append('\n');
1183 return stringBuilder.toString();
1186 const AtomicString& XMLHttpRequest::getResponseHeader(const AtomicString& name) const
1188 if (m_state < HEADERS_RECEIVED || m_error)
1191 // See comment in getAllResponseHeaders above.
1192 if (isSetCookieHeader(name) && !securityOrigin()->canLoadLocalResources()) {
1193 logConsoleError(executionContext(), "Refused to get unsafe header \"" + name + "\"");
1197 HTTPHeaderSet accessControlExposeHeaderSet;
1198 parseAccessControlExposeHeadersAllowList(m_response.httpHeaderField("Access-Control-Expose-Headers"), accessControlExposeHeaderSet);
1200 if (!m_sameOriginRequest && !isOnAccessControlResponseHeaderWhitelist(name) && !accessControlExposeHeaderSet.contains(name)) {
1201 logConsoleError(executionContext(), "Refused to get unsafe header \"" + name + "\"");
1204 return m_response.httpHeaderField(name);
1207 AtomicString XMLHttpRequest::finalResponseMIMEType() const
1209 AtomicString overriddenType = extractMIMETypeFromMediaType(m_mimeTypeOverride);
1210 if (!overriddenType.isEmpty())
1211 return overriddenType;
1213 if (m_response.isHTTP())
1214 return extractMIMETypeFromMediaType(m_response.httpHeaderField("Content-Type"));
1216 return m_response.mimeType();
1219 AtomicString XMLHttpRequest::finalResponseMIMETypeWithFallback() const
1221 AtomicString finalType = finalResponseMIMEType();
1222 if (!finalType.isEmpty())
1225 // FIXME: This fallback is not specified in the final MIME type algorithm
1226 // of the XHR spec. Move this to more appropriate place.
1227 return AtomicString("text/xml", AtomicString::ConstructFromLiteral);
1230 bool XMLHttpRequest::responseIsXML() const
1232 return DOMImplementation::isXMLMIMEType(finalResponseMIMETypeWithFallback());
1235 bool XMLHttpRequest::responseIsHTML() const
1237 return equalIgnoringCase(finalResponseMIMEType(), "text/html");
1240 int XMLHttpRequest::status() const
1242 if (m_state == UNSENT || m_state == OPENED || m_error)
1245 if (m_response.httpStatusCode())
1246 return m_response.httpStatusCode();
1251 String XMLHttpRequest::statusText() const
1253 if (m_state == UNSENT || m_state == OPENED || m_error)
1256 if (!m_response.httpStatusText().isNull())
1257 return m_response.httpStatusText();
1262 void XMLHttpRequest::didFail(const ResourceError& error)
1264 WTF_LOG(Network, "XMLHttpRequest %p didFail()", this);
1266 // If we are already in an error state, for instance we called abort(), bail out early.
1270 if (error.isCancellation()) {
1275 if (error.isTimeout()) {
1280 // Network failures are already reported to Web Inspector by ResourceLoader.
1281 if (error.domain() == errorDomainBlinkInternal)
1282 logConsoleError(executionContext(), "XMLHttpRequest cannot load " + error.failingURL() + ". " + error.localizedDescription());
1284 handleNetworkError();
1287 void XMLHttpRequest::didFailRedirectCheck()
1289 WTF_LOG(Network, "XMLHttpRequest %p didFailRedirectCheck()", this);
1291 handleNetworkError();
1294 void XMLHttpRequest::didFinishLoading(unsigned long identifier, double)
1296 WTF_LOG(Network, "XMLHttpRequest %p didFinishLoading(%lu)", this, identifier);
1301 if (m_state < HEADERS_RECEIVED)
1302 changeState(HEADERS_RECEIVED);
1304 m_loaderIdentifier = identifier;
1306 if (m_responseDocumentParser) {
1307 // |DocumentParser::finish()| tells the parser that we have reached end of the data.
1308 // When using |HTMLDocumentParser|, which works asynchronously, we do not have the
1309 // complete document just after the |DocumentParser::finish()| call.
1310 // Wait for the parser to call us back in |notifyParserStopped| to progress state.
1311 m_responseDocumentParser->finish();
1312 ASSERT(m_responseDocument);
1317 m_responseText = m_responseText.concatenateWith(m_decoder->flush());
1319 if (m_responseLegacyStream)
1320 m_responseLegacyStream->finalize();
1322 if (m_responseStream)
1323 m_responseStream->close();
1325 clearVariablesForLoading();
1329 void XMLHttpRequest::notifyParserStopped()
1331 // This should only be called when response document is parsed asynchronously.
1332 ASSERT(m_responseDocumentParser);
1333 ASSERT(!m_responseDocumentParser->isParsing());
1334 ASSERT(!m_responseLegacyStream);
1335 ASSERT(!m_responseStream);
1337 // Do nothing if we are called from |internalAbort()|.
1341 clearVariablesForLoading();
1343 m_responseDocument->implicitClose();
1345 if (!m_responseDocument->wellFormed())
1346 m_responseDocument = nullptr;
1348 m_parsedResponse = true;
1353 void XMLHttpRequest::endLoading()
1355 InspectorInstrumentation::didFinishXHRLoading(executionContext(), this, this, m_loaderIdentifier, m_responseText, m_method, m_url, m_lastSendURL, m_lastSendLineNumber);
1359 m_loaderIdentifier = 0;
1364 void XMLHttpRequest::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
1366 WTF_LOG(Network, "XMLHttpRequest %p didSendData(%llu, %llu)", this, bytesSent, totalBytesToBeSent);
1371 if (m_uploadEventsAllowed)
1372 m_upload->dispatchProgressEvent(bytesSent, totalBytesToBeSent);
1374 if (bytesSent == totalBytesToBeSent && !m_uploadComplete) {
1375 m_uploadComplete = true;
1376 if (m_uploadEventsAllowed)
1377 m_upload->dispatchEventAndLoadEnd(EventTypeNames::load, true, bytesSent, totalBytesToBeSent);
1381 void XMLHttpRequest::didReceiveResponse(unsigned long identifier, const ResourceResponse& response)
1383 WTF_LOG(Network, "XMLHttpRequest %p didReceiveResponse(%lu)", this, identifier);
1385 m_response = response;
1386 if (!m_mimeTypeOverride.isEmpty()) {
1387 m_response.setHTTPHeaderField("Content-Type", m_mimeTypeOverride);
1388 m_finalResponseCharset = extractCharsetFromMediaType(m_mimeTypeOverride);
1391 if (m_finalResponseCharset.isEmpty())
1392 m_finalResponseCharset = response.textEncodingName();
1395 void XMLHttpRequest::parseDocumentChunk(const char* data, int len)
1397 if (!m_responseDocumentParser) {
1398 ASSERT(!m_responseDocument);
1399 initResponseDocument();
1400 if (!m_responseDocument)
1403 m_responseDocumentParser = m_responseDocument->implicitOpen();
1404 m_responseDocumentParser->addClient(this);
1406 ASSERT(m_responseDocumentParser);
1408 if (m_responseDocumentParser->needsDecoder())
1409 m_responseDocumentParser->setDecoder(createDecoder());
1411 m_responseDocumentParser->appendBytes(data, len);
1414 PassOwnPtr<TextResourceDecoder> XMLHttpRequest::createDecoder() const
1416 if (m_responseTypeCode == ResponseTypeJSON)
1417 return TextResourceDecoder::create("application/json", "UTF-8");
1419 if (!m_finalResponseCharset.isEmpty())
1420 return TextResourceDecoder::create("text/plain", m_finalResponseCharset);
1422 // allow TextResourceDecoder to look inside the m_response if it's XML or HTML
1423 if (responseIsXML()) {
1424 OwnPtr<TextResourceDecoder> decoder = TextResourceDecoder::create("application/xml");
1425 // Don't stop on encoding errors, unlike it is done for other kinds
1426 // of XML resources. This matches the behavior of previous WebKit
1427 // versions, Firefox and Opera.
1428 decoder->useLenientXMLDecoding();
1430 return decoder.release();
1433 if (responseIsHTML())
1434 return TextResourceDecoder::create("text/html", "UTF-8");
1436 return TextResourceDecoder::create("text/plain", "UTF-8");
1439 void XMLHttpRequest::didReceiveData(const char* data, int len)
1441 ASSERT(!m_downloadingToFile);
1446 if (m_state < HEADERS_RECEIVED)
1447 changeState(HEADERS_RECEIVED);
1449 // We need to check for |m_error| again, because |changeState| may trigger
1450 // readystatechange, and user javascript can cause |abort()|.
1460 if (m_responseTypeCode == ResponseTypeDocument && responseIsHTML()) {
1461 parseDocumentChunk(data, len);
1462 } else if (m_responseTypeCode == ResponseTypeDefault || m_responseTypeCode == ResponseTypeText || m_responseTypeCode == ResponseTypeJSON || m_responseTypeCode == ResponseTypeDocument) {
1464 m_decoder = createDecoder();
1466 m_responseText = m_responseText.concatenateWith(m_decoder->decode(data, len));
1467 } else if (m_responseTypeCode == ResponseTypeArrayBuffer || m_responseTypeCode == ResponseTypeBlob) {
1468 // Buffer binary data.
1469 if (!m_binaryResponseBuilder)
1470 m_binaryResponseBuilder = SharedBuffer::create();
1471 m_binaryResponseBuilder->append(data, len);
1472 } else if (m_responseTypeCode == ResponseTypeLegacyStream) {
1473 if (!m_responseLegacyStream)
1474 m_responseLegacyStream = Stream::create(executionContext(), responseType());
1475 m_responseLegacyStream->addData(data, len);
1476 } else if (m_responseTypeCode == ResponseTypeStream) {
1477 if (!m_responseStream) {
1478 m_responseStream = new ReadableStreamImpl<ReadableStreamChunkTypeTraits<ArrayBuffer> >(executionContext(), new ReadableStreamSource(this));
1479 m_responseStream->didSourceStart();
1481 m_responseStream->enqueue(ArrayBuffer::create(data, len));
1487 void XMLHttpRequest::didDownloadData(int dataLength)
1492 ASSERT(m_downloadingToFile);
1494 if (m_state < HEADERS_RECEIVED)
1495 changeState(HEADERS_RECEIVED);
1500 // readystatechange event handler may do something to put this XHR in error
1501 // state. We need to check m_error again here.
1505 m_lengthDownloadedToFile += dataLength;
1507 trackProgress(dataLength);
1510 void XMLHttpRequest::handleDidTimeout()
1512 WTF_LOG(Network, "XMLHttpRequest %p handleDidTimeout()", this);
1514 // Response is cleared next, save needed progress event data.
1515 long long expectedLength = m_response.expectedContentLength();
1516 long long receivedLength = m_receivedLength;
1518 if (!internalAbort())
1521 handleRequestError(TimeoutError, EventTypeNames::timeout, receivedLength, expectedLength);
1524 void XMLHttpRequest::suspend()
1526 m_progressEventThrottle.suspend();
1529 void XMLHttpRequest::resume()
1531 m_progressEventThrottle.resume();
1534 void XMLHttpRequest::stop()
1539 bool XMLHttpRequest::hasPendingActivity() const
1541 // Neither this object nor the JavaScript wrapper should be deleted while
1542 // a request is in progress because we need to keep the listeners alive,
1543 // and they are referenced by the JavaScript wrapper.
1544 // |m_loader| is non-null while request is active and ThreadableLoaderClient
1545 // callbacks may be called, and |m_responseDocumentParser| is non-null while
1546 // DocumentParserClient callbacks may be called.
1547 return m_loader || m_responseDocumentParser;
1550 void XMLHttpRequest::contextDestroyed()
1553 ActiveDOMObject::contextDestroyed();
1556 const AtomicString& XMLHttpRequest::interfaceName() const
1558 return EventTargetNames::XMLHttpRequest;
1561 ExecutionContext* XMLHttpRequest::executionContext() const
1563 return ActiveDOMObject::executionContext();
1566 void XMLHttpRequest::trace(Visitor* visitor)
1568 visitor->trace(m_responseBlob);
1569 visitor->trace(m_responseLegacyStream);
1570 visitor->trace(m_responseStream);
1571 visitor->trace(m_streamSource);
1572 visitor->trace(m_responseDocument);
1573 visitor->trace(m_responseDocumentParser);
1574 visitor->trace(m_progressEventThrottle);
1575 visitor->trace(m_upload);
1576 XMLHttpRequestEventTarget::trace(visitor);
1579 } // namespace blink