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 RefPtr<ThreadableLoader> loader = m_loader.release();
984 // If abort() called internalAbort() and a nested open() ended up
985 // clearing the error flag, but didn't send(), make sure the error
986 // flag is still set.
987 bool newLoadStarted = hasPendingActivity();
991 return !newLoadStarted;
994 void XMLHttpRequest::clearResponse()
996 // FIXME: when we add the support for multi-part XHR, we will have to
997 // be careful with this initialization.
998 m_receivedLength = 0;
1000 m_response = ResourceResponse();
1002 m_responseText.clear();
1004 m_parsedResponse = false;
1005 m_responseDocument = nullptr;
1007 m_responseBlob = nullptr;
1009 m_downloadingToFile = false;
1010 m_lengthDownloadedToFile = 0;
1012 m_responseLegacyStream = nullptr;
1013 m_responseStream = nullptr;
1015 // These variables may referred by the response accessors. So, we can clear
1016 // this only when we clear the response holder variables above.
1017 m_binaryResponseBuilder.clear();
1018 m_responseArrayBuffer.clear();
1021 void XMLHttpRequest::clearRequest()
1023 m_requestHeaders.clear();
1026 void XMLHttpRequest::dispatchProgressEvent(const AtomicString& type, long long receivedLength, long long expectedLength)
1028 bool lengthComputable = expectedLength > 0 && receivedLength <= expectedLength;
1029 unsigned long long loaded = receivedLength >= 0 ? static_cast<unsigned long long>(receivedLength) : 0;
1030 unsigned long long total = lengthComputable ? static_cast<unsigned long long>(expectedLength) : 0;
1032 m_progressEventThrottle.dispatchProgressEvent(type, lengthComputable, loaded, total);
1034 if (type == EventTypeNames::loadend)
1035 InspectorInstrumentation::didDispatchXHRLoadendEvent(executionContext(), this);
1038 void XMLHttpRequest::dispatchProgressEventFromSnapshot(const AtomicString& type)
1040 dispatchProgressEvent(type, m_receivedLength, m_response.expectedContentLength());
1043 void XMLHttpRequest::handleNetworkError()
1045 WTF_LOG(Network, "XMLHttpRequest %p handleNetworkError()", this);
1047 // Response is cleared next, save needed progress event data.
1048 long long expectedLength = m_response.expectedContentLength();
1049 long long receivedLength = m_receivedLength;
1051 // Prevent the XMLHttpRequest instance from being destoryed during
1052 // |internalAbort()|.
1053 RefPtrWillBeRawPtr<XMLHttpRequest> protect(this);
1055 if (!internalAbort())
1058 handleRequestError(NetworkError, EventTypeNames::error, receivedLength, expectedLength);
1061 void XMLHttpRequest::handleDidCancel()
1063 WTF_LOG(Network, "XMLHttpRequest %p handleDidCancel()", this);
1065 // Response is cleared next, save needed progress event data.
1066 long long expectedLength = m_response.expectedContentLength();
1067 long long receivedLength = m_receivedLength;
1069 // Prevent the XMLHttpRequest instance from being destoryed during
1070 // |internalAbort()|.
1071 RefPtrWillBeRawPtr<XMLHttpRequest> protect(this);
1073 if (!internalAbort())
1076 handleRequestError(AbortError, EventTypeNames::abort, receivedLength, expectedLength);
1079 void XMLHttpRequest::handleRequestError(ExceptionCode exceptionCode, const AtomicString& type, long long receivedLength, long long expectedLength)
1081 WTF_LOG(Network, "XMLHttpRequest %p handleRequestError()", this);
1083 // The request error steps for event 'type' and exception 'exceptionCode'.
1085 if (!m_async && exceptionCode) {
1087 m_exceptionCode = exceptionCode;
1090 // With m_error set, the state change steps are minimal: any pending
1091 // progress event is flushed + a readystatechange is dispatched.
1092 // No new progress events dispatched; as required, that happens at
1097 if (!m_uploadComplete) {
1098 m_uploadComplete = true;
1099 if (m_upload && m_uploadEventsAllowed)
1100 m_upload->handleRequestError(type);
1103 // Note: The below event dispatch may be called while |hasPendingActivity() == false|,
1104 // when |handleRequestError| is called after |internalAbort()|.
1105 // This is safe, however, as |this| will be kept alive from a strong ref |Event::m_target|.
1106 dispatchProgressEvent(EventTypeNames::progress, receivedLength, expectedLength);
1107 dispatchProgressEvent(type, receivedLength, expectedLength);
1108 dispatchProgressEvent(EventTypeNames::loadend, receivedLength, expectedLength);
1111 void XMLHttpRequest::overrideMimeType(const AtomicString& mimeType, ExceptionState& exceptionState)
1113 if (m_state == LOADING || m_state == DONE) {
1114 exceptionState.throwDOMException(InvalidStateError, "MimeType cannot be overridden when the state is LOADING or DONE.");
1118 m_mimeTypeOverride = mimeType;
1121 void XMLHttpRequest::setRequestHeader(const AtomicString& name, const AtomicString& value, ExceptionState& exceptionState)
1123 if (m_state != OPENED || m_loader) {
1124 exceptionState.throwDOMException(InvalidStateError, "The object's state must be OPENED.");
1128 if (!isValidHTTPToken(name)) {
1129 exceptionState.throwDOMException(SyntaxError, "'" + name + "' is not a valid HTTP header field name.");
1133 if (!isValidHTTPHeaderValue(value)) {
1134 exceptionState.throwDOMException(SyntaxError, "'" + value + "' is not a valid HTTP header field value.");
1138 // No script (privileged or not) can set unsafe headers.
1139 if (FetchUtils::isForbiddenHeaderName(name)) {
1140 logConsoleError(executionContext(), "Refused to set unsafe header \"" + name + "\"");
1144 setRequestHeaderInternal(name, value);
1147 void XMLHttpRequest::setRequestHeaderInternal(const AtomicString& name, const AtomicString& value)
1149 HTTPHeaderMap::AddResult result = m_requestHeaders.add(name, value);
1150 if (!result.isNewEntry)
1151 result.storedValue->value = result.storedValue->value + ", " + value;
1154 const AtomicString& XMLHttpRequest::getRequestHeader(const AtomicString& name) const
1156 return m_requestHeaders.get(name);
1159 String XMLHttpRequest::getAllResponseHeaders() const
1161 if (m_state < HEADERS_RECEIVED || m_error)
1164 StringBuilder stringBuilder;
1166 HTTPHeaderSet accessControlExposeHeaderSet;
1167 parseAccessControlExposeHeadersAllowList(m_response.httpHeaderField("Access-Control-Expose-Headers"), accessControlExposeHeaderSet);
1168 HTTPHeaderMap::const_iterator end = m_response.httpHeaderFields().end();
1169 for (HTTPHeaderMap::const_iterator it = m_response.httpHeaderFields().begin(); it!= end; ++it) {
1170 // Hide Set-Cookie header fields from the XMLHttpRequest client for these reasons:
1171 // 1) If the client did have access to the fields, then it could read HTTP-only
1172 // cookies; those cookies are supposed to be hidden from scripts.
1173 // 2) There's no known harm in hiding Set-Cookie header fields entirely; we don't
1174 // know any widely used technique that requires access to them.
1175 // 3) Firefox has implemented this policy.
1176 if (isSetCookieHeader(it->key) && !securityOrigin()->canLoadLocalResources())
1179 if (!m_sameOriginRequest && !isOnAccessControlResponseHeaderWhitelist(it->key) && !accessControlExposeHeaderSet.contains(it->key))
1182 stringBuilder.append(it->key);
1183 stringBuilder.append(':');
1184 stringBuilder.append(' ');
1185 stringBuilder.append(it->value);
1186 stringBuilder.append('\r');
1187 stringBuilder.append('\n');
1190 return stringBuilder.toString();
1193 const AtomicString& XMLHttpRequest::getResponseHeader(const AtomicString& name) const
1195 if (m_state < HEADERS_RECEIVED || m_error)
1198 // See comment in getAllResponseHeaders above.
1199 if (isSetCookieHeader(name) && !securityOrigin()->canLoadLocalResources()) {
1200 logConsoleError(executionContext(), "Refused to get unsafe header \"" + name + "\"");
1204 HTTPHeaderSet accessControlExposeHeaderSet;
1205 parseAccessControlExposeHeadersAllowList(m_response.httpHeaderField("Access-Control-Expose-Headers"), accessControlExposeHeaderSet);
1207 if (!m_sameOriginRequest && !isOnAccessControlResponseHeaderWhitelist(name) && !accessControlExposeHeaderSet.contains(name)) {
1208 logConsoleError(executionContext(), "Refused to get unsafe header \"" + name + "\"");
1211 return m_response.httpHeaderField(name);
1214 AtomicString XMLHttpRequest::finalResponseMIMEType() const
1216 AtomicString overriddenType = extractMIMETypeFromMediaType(m_mimeTypeOverride);
1217 if (!overriddenType.isEmpty())
1218 return overriddenType;
1220 if (m_response.isHTTP())
1221 return extractMIMETypeFromMediaType(m_response.httpHeaderField("Content-Type"));
1223 return m_response.mimeType();
1226 AtomicString XMLHttpRequest::finalResponseMIMETypeWithFallback() const
1228 AtomicString finalType = finalResponseMIMEType();
1229 if (!finalType.isEmpty())
1232 // FIXME: This fallback is not specified in the final MIME type algorithm
1233 // of the XHR spec. Move this to more appropriate place.
1234 return AtomicString("text/xml", AtomicString::ConstructFromLiteral);
1237 bool XMLHttpRequest::responseIsXML() const
1239 return DOMImplementation::isXMLMIMEType(finalResponseMIMETypeWithFallback());
1242 bool XMLHttpRequest::responseIsHTML() const
1244 return equalIgnoringCase(finalResponseMIMEType(), "text/html");
1247 int XMLHttpRequest::status() const
1249 if (m_state == UNSENT || m_state == OPENED || m_error)
1252 if (m_response.httpStatusCode())
1253 return m_response.httpStatusCode();
1258 String XMLHttpRequest::statusText() const
1260 if (m_state == UNSENT || m_state == OPENED || m_error)
1263 if (!m_response.httpStatusText().isNull())
1264 return m_response.httpStatusText();
1269 void XMLHttpRequest::didFail(const ResourceError& error)
1271 WTF_LOG(Network, "XMLHttpRequest %p didFail()", this);
1273 // If we are already in an error state, for instance we called abort(), bail out early.
1277 if (error.isCancellation()) {
1279 // Now the XMLHttpRequest instance may be dead.
1283 if (error.isTimeout()) {
1285 // Now the XMLHttpRequest instance may be dead.
1289 // Network failures are already reported to Web Inspector by ResourceLoader.
1290 if (error.domain() == errorDomainBlinkInternal)
1291 logConsoleError(executionContext(), "XMLHttpRequest cannot load " + error.failingURL() + ". " + error.localizedDescription());
1293 handleNetworkError();
1294 // Now the XMLHttpRequest instance may be dead.
1297 void XMLHttpRequest::didFailRedirectCheck()
1299 WTF_LOG(Network, "XMLHttpRequest %p didFailRedirectCheck()", this);
1301 handleNetworkError();
1302 // Now the XMLHttpRequest instance may be dead.
1305 void XMLHttpRequest::didFinishLoading(unsigned long identifier, double)
1307 WTF_LOG(Network, "XMLHttpRequest %p didFinishLoading(%lu)", this, identifier);
1312 if (m_state < HEADERS_RECEIVED)
1313 changeState(HEADERS_RECEIVED);
1315 m_loaderIdentifier = identifier;
1317 if (m_responseDocumentParser) {
1318 // |DocumentParser::finish()| tells the parser that we have reached end of the data.
1319 // When using |HTMLDocumentParser|, which works asynchronously, we do not have the
1320 // complete document just after the |DocumentParser::finish()| call.
1321 // Wait for the parser to call us back in |notifyParserStopped| to progress state.
1322 m_responseDocumentParser->finish();
1323 ASSERT(m_responseDocument);
1328 m_responseText = m_responseText.concatenateWith(m_decoder->flush());
1330 if (m_responseLegacyStream)
1331 m_responseLegacyStream->finalize();
1333 if (m_responseStream)
1334 m_responseStream->close();
1336 clearVariablesForLoading();
1340 void XMLHttpRequest::notifyParserStopped()
1342 // This should only be called when response document is parsed asynchronously.
1343 ASSERT(m_responseDocumentParser);
1344 ASSERT(!m_responseDocumentParser->isParsing());
1345 ASSERT(!m_responseLegacyStream);
1346 ASSERT(!m_responseStream);
1348 // Do nothing if we are called from |internalAbort()|.
1352 clearVariablesForLoading();
1354 m_responseDocument->implicitClose();
1356 if (!m_responseDocument->wellFormed())
1357 m_responseDocument = nullptr;
1359 m_parsedResponse = true;
1364 void XMLHttpRequest::endLoading()
1366 InspectorInstrumentation::didFinishXHRLoading(executionContext(), this, this, m_loaderIdentifier, m_responseText, m_method, m_url, m_lastSendURL, m_lastSendLineNumber);
1370 m_loaderIdentifier = 0;
1375 void XMLHttpRequest::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
1377 WTF_LOG(Network, "XMLHttpRequest %p didSendData(%llu, %llu)", this, bytesSent, totalBytesToBeSent);
1382 if (m_uploadEventsAllowed)
1383 m_upload->dispatchProgressEvent(bytesSent, totalBytesToBeSent);
1385 if (bytesSent == totalBytesToBeSent && !m_uploadComplete) {
1386 m_uploadComplete = true;
1387 if (m_uploadEventsAllowed)
1388 m_upload->dispatchEventAndLoadEnd(EventTypeNames::load, true, bytesSent, totalBytesToBeSent);
1392 void XMLHttpRequest::didReceiveResponse(unsigned long identifier, const ResourceResponse& response)
1394 WTF_LOG(Network, "XMLHttpRequest %p didReceiveResponse(%lu)", this, identifier);
1396 m_response = response;
1397 if (!m_mimeTypeOverride.isEmpty()) {
1398 m_response.setHTTPHeaderField("Content-Type", m_mimeTypeOverride);
1399 m_finalResponseCharset = extractCharsetFromMediaType(m_mimeTypeOverride);
1402 if (m_finalResponseCharset.isEmpty())
1403 m_finalResponseCharset = response.textEncodingName();
1406 void XMLHttpRequest::parseDocumentChunk(const char* data, int len)
1408 if (!m_responseDocumentParser) {
1409 ASSERT(!m_responseDocument);
1410 initResponseDocument();
1411 if (!m_responseDocument)
1414 m_responseDocumentParser = m_responseDocument->implicitOpen();
1415 m_responseDocumentParser->addClient(this);
1417 ASSERT(m_responseDocumentParser);
1419 if (m_responseDocumentParser->needsDecoder())
1420 m_responseDocumentParser->setDecoder(createDecoder());
1422 m_responseDocumentParser->appendBytes(data, len);
1425 PassOwnPtr<TextResourceDecoder> XMLHttpRequest::createDecoder() const
1427 if (m_responseTypeCode == ResponseTypeJSON)
1428 return TextResourceDecoder::create("application/json", "UTF-8");
1430 if (!m_finalResponseCharset.isEmpty())
1431 return TextResourceDecoder::create("text/plain", m_finalResponseCharset);
1433 // allow TextResourceDecoder to look inside the m_response if it's XML or HTML
1434 if (responseIsXML()) {
1435 OwnPtr<TextResourceDecoder> decoder = TextResourceDecoder::create("application/xml");
1436 // Don't stop on encoding errors, unlike it is done for other kinds
1437 // of XML resources. This matches the behavior of previous WebKit
1438 // versions, Firefox and Opera.
1439 decoder->useLenientXMLDecoding();
1441 return decoder.release();
1444 if (responseIsHTML())
1445 return TextResourceDecoder::create("text/html", "UTF-8");
1447 return TextResourceDecoder::create("text/plain", "UTF-8");
1450 void XMLHttpRequest::didReceiveData(const char* data, int len)
1452 ASSERT(!m_downloadingToFile);
1457 if (m_state < HEADERS_RECEIVED)
1458 changeState(HEADERS_RECEIVED);
1460 // We need to check for |m_error| again, because |changeState| may trigger
1461 // readystatechange, and user javascript can cause |abort()|.
1471 if (m_responseTypeCode == ResponseTypeDocument && responseIsHTML()) {
1472 parseDocumentChunk(data, len);
1473 } else if (m_responseTypeCode == ResponseTypeDefault || m_responseTypeCode == ResponseTypeText || m_responseTypeCode == ResponseTypeJSON || m_responseTypeCode == ResponseTypeDocument) {
1475 m_decoder = createDecoder();
1477 m_responseText = m_responseText.concatenateWith(m_decoder->decode(data, len));
1478 } else if (m_responseTypeCode == ResponseTypeArrayBuffer || m_responseTypeCode == ResponseTypeBlob) {
1479 // Buffer binary data.
1480 if (!m_binaryResponseBuilder)
1481 m_binaryResponseBuilder = SharedBuffer::create();
1482 m_binaryResponseBuilder->append(data, len);
1483 } else if (m_responseTypeCode == ResponseTypeLegacyStream) {
1484 if (!m_responseLegacyStream)
1485 m_responseLegacyStream = Stream::create(executionContext(), responseType());
1486 m_responseLegacyStream->addData(data, len);
1487 } else if (m_responseTypeCode == ResponseTypeStream) {
1488 if (!m_responseStream) {
1489 m_responseStream = new ReadableStreamImpl<ReadableStreamChunkTypeTraits<ArrayBuffer> >(executionContext(), new ReadableStreamSource(this));
1490 m_responseStream->didSourceStart();
1492 m_responseStream->enqueue(ArrayBuffer::create(data, len));
1498 void XMLHttpRequest::didDownloadData(int dataLength)
1503 ASSERT(m_downloadingToFile);
1505 if (m_state < HEADERS_RECEIVED)
1506 changeState(HEADERS_RECEIVED);
1511 // readystatechange event handler may do something to put this XHR in error
1512 // state. We need to check m_error again here.
1516 m_lengthDownloadedToFile += dataLength;
1518 trackProgress(dataLength);
1521 void XMLHttpRequest::handleDidTimeout()
1523 WTF_LOG(Network, "XMLHttpRequest %p handleDidTimeout()", this);
1525 // Response is cleared next, save needed progress event data.
1526 long long expectedLength = m_response.expectedContentLength();
1527 long long receivedLength = m_receivedLength;
1529 // Prevent the XMLHttpRequest instance from being destoryed during
1530 // |internalAbort()|.
1531 RefPtrWillBeRawPtr<XMLHttpRequest> protect(this);
1533 if (!internalAbort())
1536 handleRequestError(TimeoutError, EventTypeNames::timeout, receivedLength, expectedLength);
1539 void XMLHttpRequest::suspend()
1541 m_progressEventThrottle.suspend();
1544 void XMLHttpRequest::resume()
1546 m_progressEventThrottle.resume();
1549 void XMLHttpRequest::stop()
1554 bool XMLHttpRequest::hasPendingActivity() const
1556 // Neither this object nor the JavaScript wrapper should be deleted while
1557 // a request is in progress because we need to keep the listeners alive,
1558 // and they are referenced by the JavaScript wrapper.
1559 // |m_loader| is non-null while request is active and ThreadableLoaderClient
1560 // callbacks may be called, and |m_responseDocumentParser| is non-null while
1561 // DocumentParserClient callbacks may be called.
1562 return m_loader || m_responseDocumentParser;
1565 void XMLHttpRequest::contextDestroyed()
1568 ActiveDOMObject::contextDestroyed();
1571 const AtomicString& XMLHttpRequest::interfaceName() const
1573 return EventTargetNames::XMLHttpRequest;
1576 ExecutionContext* XMLHttpRequest::executionContext() const
1578 return ActiveDOMObject::executionContext();
1581 void XMLHttpRequest::trace(Visitor* visitor)
1583 visitor->trace(m_responseBlob);
1584 visitor->trace(m_responseLegacyStream);
1585 visitor->trace(m_responseStream);
1586 visitor->trace(m_streamSource);
1587 visitor->trace(m_responseDocument);
1588 visitor->trace(m_responseDocumentParser);
1589 visitor->trace(m_progressEventThrottle);
1590 visitor->trace(m_upload);
1591 XMLHttpRequestEventTarget::trace(visitor);
1594 } // namespace blink