Home | History | Annotate | Download | only in xml
      1 /*
      2  *  Copyright (C) 2004, 2006, 2008 Apple Inc. All rights reserved.
      3  *  Copyright (C) 2005-2007 Alexey Proskuryakov <ap (at) webkit.org>
      4  *  Copyright (C) 2007, 2008 Julien Chaffraix <jchaffraix (at) webkit.org>
      5  *  Copyright (C) 2008, 2011 Google Inc. All rights reserved.
      6  *  Copyright (C) 2012 Intel Corporation
      7  *
      8  *  This library is free software; you can redistribute it and/or
      9  *  modify it under the terms of the GNU Lesser General Public
     10  *  License as published by the Free Software Foundation; either
     11  *  version 2 of the License, or (at your option) any later version.
     12  *
     13  *  This library is distributed in the hope that it will be useful,
     14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     16  *  Lesser General Public License for more details.
     17  *
     18  *  You should have received a copy of the GNU Lesser General Public
     19  *  License along with this library; if not, write to the Free Software
     20  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
     21  */
     22 
     23 #include "config.h"
     24 #include "core/xml/XMLHttpRequest.h"
     25 
     26 #include "FetchInitiatorTypeNames.h"
     27 #include "bindings/v8/ExceptionState.h"
     28 #include "core/dom/ContextFeatures.h"
     29 #include "core/dom/DOMImplementation.h"
     30 #include "core/dom/Event.h"
     31 #include "core/dom/EventListener.h"
     32 #include "core/dom/EventNames.h"
     33 #include "core/dom/ExceptionCode.h"
     34 #include "core/editing/markup.h"
     35 #include "core/fileapi/Blob.h"
     36 #include "core/fileapi/File.h"
     37 #include "core/html/DOMFormData.h"
     38 #include "core/html/HTMLDocument.h"
     39 #include "core/inspector/InspectorInstrumentation.h"
     40 #include "core/loader/CrossOriginAccessControl.h"
     41 #include "core/loader/TextResourceDecoder.h"
     42 #include "core/loader/ThreadableLoader.h"
     43 #include "core/page/ContentSecurityPolicy.h"
     44 #include "core/page/Settings.h"
     45 #include "core/platform/HistogramSupport.h"
     46 #include "core/platform/SharedBuffer.h"
     47 #include "core/platform/network/BlobData.h"
     48 #include "core/platform/network/HTTPParsers.h"
     49 #include "core/platform/network/ParsedContentType.h"
     50 #include "core/platform/network/ResourceError.h"
     51 #include "core/platform/network/ResourceRequest.h"
     52 #include "core/xml/XMLHttpRequestProgressEvent.h"
     53 #include "core/xml/XMLHttpRequestUpload.h"
     54 #include "weborigin/SecurityOrigin.h"
     55 #include "wtf/ArrayBuffer.h"
     56 #include "wtf/ArrayBufferView.h"
     57 #include "wtf/RefCountedLeakCounter.h"
     58 #include "wtf/StdLibExtras.h"
     59 #include "wtf/UnusedParam.h"
     60 #include "wtf/text/CString.h"
     61 
     62 namespace WebCore {
     63 
     64 DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, xmlHttpRequestCounter, ("XMLHttpRequest"));
     65 
     66 // Histogram enum to see when we can deprecate xhr.send(ArrayBuffer).
     67 enum XMLHttpRequestSendArrayBufferOrView {
     68     XMLHttpRequestSendArrayBuffer,
     69     XMLHttpRequestSendArrayBufferView,
     70     XMLHttpRequestSendArrayBufferOrViewMax,
     71 };
     72 
     73 struct XMLHttpRequestStaticData {
     74     WTF_MAKE_NONCOPYABLE(XMLHttpRequestStaticData); WTF_MAKE_FAST_ALLOCATED;
     75 public:
     76     XMLHttpRequestStaticData();
     77     String m_proxyHeaderPrefix;
     78     String m_secHeaderPrefix;
     79     HashSet<String, CaseFoldingHash> m_forbiddenRequestHeaders;
     80 };
     81 
     82 XMLHttpRequestStaticData::XMLHttpRequestStaticData()
     83     : m_proxyHeaderPrefix("proxy-")
     84     , m_secHeaderPrefix("sec-")
     85 {
     86     m_forbiddenRequestHeaders.add("accept-charset");
     87     m_forbiddenRequestHeaders.add("accept-encoding");
     88     m_forbiddenRequestHeaders.add("access-control-request-headers");
     89     m_forbiddenRequestHeaders.add("access-control-request-method");
     90     m_forbiddenRequestHeaders.add("connection");
     91     m_forbiddenRequestHeaders.add("content-length");
     92     m_forbiddenRequestHeaders.add("content-transfer-encoding");
     93     m_forbiddenRequestHeaders.add("cookie");
     94     m_forbiddenRequestHeaders.add("cookie2");
     95     m_forbiddenRequestHeaders.add("date");
     96     m_forbiddenRequestHeaders.add("expect");
     97     m_forbiddenRequestHeaders.add("host");
     98     m_forbiddenRequestHeaders.add("keep-alive");
     99     m_forbiddenRequestHeaders.add("origin");
    100     m_forbiddenRequestHeaders.add("referer");
    101     m_forbiddenRequestHeaders.add("te");
    102     m_forbiddenRequestHeaders.add("trailer");
    103     m_forbiddenRequestHeaders.add("transfer-encoding");
    104     m_forbiddenRequestHeaders.add("upgrade");
    105     m_forbiddenRequestHeaders.add("user-agent");
    106     m_forbiddenRequestHeaders.add("via");
    107 }
    108 
    109 static bool isSetCookieHeader(const AtomicString& name)
    110 {
    111     return equalIgnoringCase(name, "set-cookie") || equalIgnoringCase(name, "set-cookie2");
    112 }
    113 
    114 static void replaceCharsetInMediaType(String& mediaType, const String& charsetValue)
    115 {
    116     unsigned int pos = 0, len = 0;
    117 
    118     findCharsetInMediaType(mediaType, pos, len);
    119 
    120     if (!len) {
    121         // When no charset found, do nothing.
    122         return;
    123     }
    124 
    125     // Found at least one existing charset, replace all occurrences with new charset.
    126     while (len) {
    127         mediaType.replace(pos, len, charsetValue);
    128         unsigned int start = pos + charsetValue.length();
    129         findCharsetInMediaType(mediaType, pos, len, start);
    130     }
    131 }
    132 
    133 static const XMLHttpRequestStaticData* staticData = 0;
    134 
    135 static const XMLHttpRequestStaticData* createXMLHttpRequestStaticData()
    136 {
    137     staticData = new XMLHttpRequestStaticData;
    138     return staticData;
    139 }
    140 
    141 static const XMLHttpRequestStaticData* initializeXMLHttpRequestStaticData()
    142 {
    143     // Uses dummy to avoid warnings about an unused variable.
    144     AtomicallyInitializedStatic(const XMLHttpRequestStaticData*, dummy = createXMLHttpRequestStaticData());
    145     return dummy;
    146 }
    147 
    148 static void logConsoleError(ScriptExecutionContext* context, const String& message)
    149 {
    150     if (!context)
    151         return;
    152     // FIXME: It's not good to report the bad usage without indicating what source line it came from.
    153     // We should pass additional parameters so we can tell the console where the mistake occurred.
    154     context->addConsoleMessage(JSMessageSource, ErrorMessageLevel, message);
    155 }
    156 
    157 PassRefPtr<XMLHttpRequest> XMLHttpRequest::create(ScriptExecutionContext* context, PassRefPtr<SecurityOrigin> securityOrigin)
    158 {
    159     RefPtr<XMLHttpRequest> xmlHttpRequest(adoptRef(new XMLHttpRequest(context, securityOrigin)));
    160     xmlHttpRequest->suspendIfNeeded();
    161 
    162     return xmlHttpRequest.release();
    163 }
    164 
    165 XMLHttpRequest::XMLHttpRequest(ScriptExecutionContext* context, PassRefPtr<SecurityOrigin> securityOrigin)
    166     : ActiveDOMObject(context)
    167     , m_async(true)
    168     , m_includeCredentials(false)
    169     , m_timeoutMilliseconds(0)
    170     , m_state(UNSENT)
    171     , m_createdDocument(false)
    172     , m_error(false)
    173     , m_uploadEventsAllowed(true)
    174     , m_uploadComplete(false)
    175     , m_sameOriginRequest(true)
    176     , m_allowCrossOriginRequests(false)
    177     , m_receivedLength(0)
    178     , m_lastSendLineNumber(0)
    179     , m_exceptionCode(0)
    180     , m_progressEventThrottle(this)
    181     , m_responseTypeCode(ResponseTypeDefault)
    182     , m_protectionTimer(this, &XMLHttpRequest::dropProtection)
    183     , m_securityOrigin(securityOrigin)
    184 {
    185     initializeXMLHttpRequestStaticData();
    186 #ifndef NDEBUG
    187     xmlHttpRequestCounter.increment();
    188 #endif
    189     ScriptWrappable::init(this);
    190 }
    191 
    192 XMLHttpRequest::~XMLHttpRequest()
    193 {
    194 #ifndef NDEBUG
    195     xmlHttpRequestCounter.decrement();
    196 #endif
    197 }
    198 
    199 Document* XMLHttpRequest::document() const
    200 {
    201     ASSERT(scriptExecutionContext()->isDocument());
    202     return toDocument(scriptExecutionContext());
    203 }
    204 
    205 SecurityOrigin* XMLHttpRequest::securityOrigin() const
    206 {
    207     return m_securityOrigin ? m_securityOrigin.get() : scriptExecutionContext()->securityOrigin();
    208 }
    209 
    210 XMLHttpRequest::State XMLHttpRequest::readyState() const
    211 {
    212     return m_state;
    213 }
    214 
    215 ScriptString XMLHttpRequest::responseText(ExceptionState& es)
    216 {
    217     if (m_responseTypeCode != ResponseTypeDefault && m_responseTypeCode != ResponseTypeText) {
    218         es.throwDOMException(InvalidStateError);
    219         return ScriptString();
    220     }
    221     if (m_error || (m_state != LOADING && m_state != DONE))
    222         return ScriptString();
    223     return m_responseText;
    224 }
    225 
    226 Document* XMLHttpRequest::responseXML(ExceptionState& es)
    227 {
    228     if (m_responseTypeCode != ResponseTypeDefault && m_responseTypeCode != ResponseTypeDocument) {
    229         es.throwDOMException(InvalidStateError);
    230         return 0;
    231     }
    232 
    233     if (m_error || m_state != DONE)
    234         return 0;
    235 
    236     if (!m_createdDocument) {
    237         bool isHTML = equalIgnoringCase(responseMIMEType(), "text/html");
    238 
    239         // The W3C spec requires the final MIME type to be some valid XML type, or text/html.
    240         // If it is text/html, then the responseType of "document" must have been supplied explicitly.
    241         if ((m_response.isHTTP() && !responseIsXML() && !isHTML)
    242             || (isHTML && m_responseTypeCode == ResponseTypeDefault)
    243             || scriptExecutionContext()->isWorkerGlobalScope()) {
    244             m_responseDocument = 0;
    245         } else {
    246             if (isHTML)
    247                 m_responseDocument = HTMLDocument::create(DocumentInit(m_url));
    248             else
    249                 m_responseDocument = Document::create(DocumentInit(m_url));
    250             // FIXME: Set Last-Modified.
    251             m_responseDocument->setContent(m_responseText.flattenToString());
    252             m_responseDocument->setSecurityOrigin(securityOrigin());
    253             m_responseDocument->setContextFeatures(document()->contextFeatures());
    254             if (!m_responseDocument->wellFormed())
    255                 m_responseDocument = 0;
    256         }
    257         m_createdDocument = true;
    258     }
    259 
    260     return m_responseDocument.get();
    261 }
    262 
    263 Blob* XMLHttpRequest::responseBlob(ExceptionState& es)
    264 {
    265     if (m_responseTypeCode != ResponseTypeBlob) {
    266         es.throwDOMException(InvalidStateError);
    267         return 0;
    268     }
    269     // We always return null before DONE.
    270     if (m_error || m_state != DONE)
    271         return 0;
    272 
    273     if (!m_responseBlob) {
    274         // FIXME: This causes two (or more) unnecessary copies of the data.
    275         // Chromium stores blob data in the browser process, so we're pulling the data
    276         // from the network only to copy it into the renderer to copy it back to the browser.
    277         // Ideally we'd get the blob/file-handle from the ResourceResponse directly
    278         // instead of copying the bytes. Embedders who store blob data in the
    279         // same process as WebCore would at least to teach BlobData to take
    280         // a SharedBuffer, even if they don't get the Blob from the network layer directly.
    281         OwnPtr<BlobData> blobData = BlobData::create();
    282         // If we errored out or got no data, we still return a blob, just an empty one.
    283         size_t size = 0;
    284         if (m_binaryResponseBuilder) {
    285             RefPtr<RawData> rawData = RawData::create();
    286             size = m_binaryResponseBuilder->size();
    287             rawData->mutableData()->append(m_binaryResponseBuilder->data(), size);
    288             blobData->appendData(rawData, 0, BlobDataItem::toEndOfFile);
    289             blobData->setContentType(responseMIMEType()); // responseMIMEType defaults to text/xml which may be incorrect.
    290             m_binaryResponseBuilder.clear();
    291         }
    292         m_responseBlob = Blob::create(blobData.release(), size);
    293     }
    294 
    295     return m_responseBlob.get();
    296 }
    297 
    298 ArrayBuffer* XMLHttpRequest::responseArrayBuffer(ExceptionState& es)
    299 {
    300     if (m_responseTypeCode != ResponseTypeArrayBuffer) {
    301         es.throwDOMException(InvalidStateError);
    302         return 0;
    303     }
    304 
    305     if (m_error || m_state != DONE)
    306         return 0;
    307 
    308     if (!m_responseArrayBuffer.get() && m_binaryResponseBuilder.get() && m_binaryResponseBuilder->size() > 0) {
    309         m_responseArrayBuffer = m_binaryResponseBuilder->getAsArrayBuffer();
    310         m_binaryResponseBuilder.clear();
    311     }
    312 
    313     return m_responseArrayBuffer.get();
    314 }
    315 
    316 void XMLHttpRequest::setTimeout(unsigned long timeout, ExceptionState& es)
    317 {
    318     // FIXME: Need to trigger or update the timeout Timer here, if needed. http://webkit.org/b/98156
    319     // 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."
    320     if (scriptExecutionContext()->isDocument() && !m_async) {
    321         logConsoleError(scriptExecutionContext(), "XMLHttpRequest.timeout cannot be set for synchronous HTTP(S) requests made from the window context.");
    322         es.throwDOMException(InvalidAccessError);
    323         return;
    324     }
    325     m_timeoutMilliseconds = timeout;
    326 }
    327 
    328 void XMLHttpRequest::setResponseType(const String& responseType, ExceptionState& es)
    329 {
    330     if (m_state >= LOADING) {
    331         es.throwDOMException(InvalidStateError);
    332         return;
    333     }
    334 
    335     // Newer functionality is not available to synchronous requests in window contexts, as a spec-mandated
    336     // attempt to discourage synchronous XHR use. responseType is one such piece of functionality.
    337     // We'll only disable this functionality for HTTP(S) requests since sync requests for local protocols
    338     // such as file: and data: still make sense to allow.
    339     if (!m_async && scriptExecutionContext()->isDocument() && m_url.protocolIsInHTTPFamily()) {
    340         logConsoleError(scriptExecutionContext(), "XMLHttpRequest.responseType cannot be changed for synchronous HTTP(S) requests made from the window context.");
    341         es.throwDOMException(InvalidAccessError);
    342         return;
    343     }
    344 
    345     if (responseType == "")
    346         m_responseTypeCode = ResponseTypeDefault;
    347     else if (responseType == "text")
    348         m_responseTypeCode = ResponseTypeText;
    349     else if (responseType == "document")
    350         m_responseTypeCode = ResponseTypeDocument;
    351     else if (responseType == "blob")
    352         m_responseTypeCode = ResponseTypeBlob;
    353     else if (responseType == "arraybuffer")
    354         m_responseTypeCode = ResponseTypeArrayBuffer;
    355     else
    356         ASSERT_NOT_REACHED();
    357 }
    358 
    359 String XMLHttpRequest::responseType()
    360 {
    361     switch (m_responseTypeCode) {
    362     case ResponseTypeDefault:
    363         return "";
    364     case ResponseTypeText:
    365         return "text";
    366     case ResponseTypeDocument:
    367         return "document";
    368     case ResponseTypeBlob:
    369         return "blob";
    370     case ResponseTypeArrayBuffer:
    371         return "arraybuffer";
    372     }
    373     return "";
    374 }
    375 
    376 XMLHttpRequestUpload* XMLHttpRequest::upload()
    377 {
    378     if (!m_upload)
    379         m_upload = XMLHttpRequestUpload::create(this);
    380     return m_upload.get();
    381 }
    382 
    383 void XMLHttpRequest::changeState(State newState)
    384 {
    385     if (m_state != newState) {
    386         m_state = newState;
    387         callReadyStateChangeListener();
    388     }
    389 }
    390 
    391 void XMLHttpRequest::callReadyStateChangeListener()
    392 {
    393     if (!scriptExecutionContext())
    394         return;
    395 
    396     InspectorInstrumentationCookie cookie = InspectorInstrumentation::willDispatchXHRReadyStateChangeEvent(scriptExecutionContext(), this);
    397 
    398     if (m_async || (m_state <= OPENED || m_state == DONE))
    399         m_progressEventThrottle.dispatchReadyStateChangeEvent(XMLHttpRequestProgressEvent::create(eventNames().readystatechangeEvent), m_state == DONE ? FlushProgressEvent : DoNotFlushProgressEvent);
    400 
    401     InspectorInstrumentation::didDispatchXHRReadyStateChangeEvent(cookie);
    402     if (m_state == DONE && !m_error) {
    403         InspectorInstrumentationCookie cookie = InspectorInstrumentation::willDispatchXHRLoadEvent(scriptExecutionContext(), this);
    404         m_progressEventThrottle.dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().loadEvent));
    405         InspectorInstrumentation::didDispatchXHRLoadEvent(cookie);
    406         m_progressEventThrottle.dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().loadendEvent));
    407     }
    408 }
    409 
    410 void XMLHttpRequest::setWithCredentials(bool value, ExceptionState& es)
    411 {
    412     if (m_state > OPENED || m_loader) {
    413         es.throwDOMException(InvalidStateError);
    414         return;
    415     }
    416 
    417     m_includeCredentials = value;
    418 }
    419 
    420 bool XMLHttpRequest::isAllowedHTTPMethod(const String& method)
    421 {
    422     return !equalIgnoringCase(method, "TRACE")
    423         && !equalIgnoringCase(method, "TRACK")
    424         && !equalIgnoringCase(method, "CONNECT");
    425 }
    426 
    427 String XMLHttpRequest::uppercaseKnownHTTPMethod(const String& method)
    428 {
    429     if (equalIgnoringCase(method, "COPY") || equalIgnoringCase(method, "DELETE") || equalIgnoringCase(method, "GET")
    430         || equalIgnoringCase(method, "HEAD") || equalIgnoringCase(method, "INDEX") || equalIgnoringCase(method, "LOCK")
    431         || equalIgnoringCase(method, "M-POST") || equalIgnoringCase(method, "MKCOL") || equalIgnoringCase(method, "MOVE")
    432         || equalIgnoringCase(method, "OPTIONS") || equalIgnoringCase(method, "POST") || equalIgnoringCase(method, "PROPFIND")
    433         || equalIgnoringCase(method, "PROPPATCH") || equalIgnoringCase(method, "PUT") || equalIgnoringCase(method, "UNLOCK")) {
    434         return method.upper();
    435     }
    436     return method;
    437 }
    438 
    439 bool XMLHttpRequest::isAllowedHTTPHeader(const String& name)
    440 {
    441     initializeXMLHttpRequestStaticData();
    442     return !staticData->m_forbiddenRequestHeaders.contains(name) && !name.startsWith(staticData->m_proxyHeaderPrefix, false)
    443         && !name.startsWith(staticData->m_secHeaderPrefix, false);
    444 }
    445 
    446 void XMLHttpRequest::open(const String& method, const KURL& url, ExceptionState& es)
    447 {
    448     open(method, url, true, es);
    449 }
    450 
    451 void XMLHttpRequest::open(const String& method, const KURL& url, bool async, ExceptionState& es)
    452 {
    453     if (!internalAbort())
    454         return;
    455 
    456     State previousState = m_state;
    457     m_state = UNSENT;
    458     m_error = false;
    459     m_uploadComplete = false;
    460 
    461     // clear stuff from possible previous load
    462     clearResponse();
    463     clearRequest();
    464 
    465     ASSERT(m_state == UNSENT);
    466 
    467     if (!isValidHTTPToken(method)) {
    468         es.throwDOMException(SyntaxError);
    469         return;
    470     }
    471 
    472     if (!isAllowedHTTPMethod(method)) {
    473         es.throwDOMException(SecurityError, "'XMLHttpRequest.open' does not support the '" + method + "' method.");
    474         return;
    475     }
    476 
    477     if (!ContentSecurityPolicy::shouldBypassMainWorld(scriptExecutionContext()) && !scriptExecutionContext()->contentSecurityPolicy()->allowConnectToSource(url)) {
    478         es.throwDOMException(SecurityError, "Refused to connect to '" + url.elidedString() + "' because it violates the document's Content Security Policy.");
    479         return;
    480     }
    481 
    482     if (!async && scriptExecutionContext()->isDocument()) {
    483         if (document()->settings() && !document()->settings()->syncXHRInDocumentsEnabled()) {
    484             logConsoleError(scriptExecutionContext(), "Synchronous XMLHttpRequests are disabled for this page.");
    485             es.throwDOMException(InvalidAccessError);
    486             return;
    487         }
    488 
    489         // Newer functionality is not available to synchronous requests in window contexts, as a spec-mandated
    490         // attempt to discourage synchronous XHR use. responseType is one such piece of functionality.
    491         // We'll only disable this functionality for HTTP(S) requests since sync requests for local protocols
    492         // such as file: and data: still make sense to allow.
    493         if (url.protocolIsInHTTPFamily() && m_responseTypeCode != ResponseTypeDefault) {
    494             logConsoleError(scriptExecutionContext(), "Synchronous HTTP(S) requests made from the window context cannot have XMLHttpRequest.responseType set.");
    495             es.throwDOMException(InvalidAccessError);
    496             return;
    497         }
    498 
    499         // Similarly, timeouts are disabled for synchronous requests as well.
    500         if (m_timeoutMilliseconds > 0) {
    501             logConsoleError(scriptExecutionContext(), "Synchronous XMLHttpRequests must not have a timeout value set.");
    502             es.throwDOMException(InvalidAccessError);
    503             return;
    504         }
    505     }
    506 
    507     m_method = uppercaseKnownHTTPMethod(method);
    508 
    509     m_url = url;
    510 
    511     m_async = async;
    512 
    513     ASSERT(!m_loader);
    514 
    515     // Check previous state to avoid dispatching readyState event
    516     // when calling open several times in a row.
    517     if (previousState != OPENED)
    518         changeState(OPENED);
    519     else
    520         m_state = OPENED;
    521 }
    522 
    523 void XMLHttpRequest::open(const String& method, const KURL& url, bool async, const String& user, ExceptionState& es)
    524 {
    525     KURL urlWithCredentials(url);
    526     urlWithCredentials.setUser(user);
    527 
    528     open(method, urlWithCredentials, async, es);
    529 }
    530 
    531 void XMLHttpRequest::open(const String& method, const KURL& url, bool async, const String& user, const String& password, ExceptionState& es)
    532 {
    533     KURL urlWithCredentials(url);
    534     urlWithCredentials.setUser(user);
    535     urlWithCredentials.setPass(password);
    536 
    537     open(method, urlWithCredentials, async, es);
    538 }
    539 
    540 bool XMLHttpRequest::initSend(ExceptionState& es)
    541 {
    542     if (!scriptExecutionContext())
    543         return false;
    544 
    545     if (m_state != OPENED || m_loader) {
    546         es.throwDOMException(InvalidStateError);
    547         return false;
    548     }
    549 
    550     m_error = false;
    551     return true;
    552 }
    553 
    554 void XMLHttpRequest::send(ExceptionState& es)
    555 {
    556     send(String(), es);
    557 }
    558 
    559 bool XMLHttpRequest::areMethodAndURLValidForSend()
    560 {
    561     return m_method != "GET" && m_method != "HEAD" && m_url.protocolIsInHTTPFamily();
    562 }
    563 
    564 void XMLHttpRequest::send(Document* document, ExceptionState& es)
    565 {
    566     ASSERT(document);
    567 
    568     if (!initSend(es))
    569         return;
    570 
    571     if (areMethodAndURLValidForSend()) {
    572         String contentType = getRequestHeader("Content-Type");
    573         if (contentType.isEmpty()) {
    574             // FIXME: this should include the charset used for encoding.
    575             setRequestHeaderInternal("Content-Type", "application/xml");
    576         }
    577 
    578         // FIXME: According to XMLHttpRequest Level 2, this should use the Document.innerHTML algorithm
    579         // from the HTML5 specification to serialize the document.
    580         String body = createMarkup(document);
    581 
    582         // FIXME: This should use value of document.inputEncoding to determine the encoding to use.
    583         m_requestEntityBody = FormData::create(UTF8Encoding().normalizeAndEncode(body, WTF::EntitiesForUnencodables));
    584         if (m_upload)
    585             m_requestEntityBody->setAlwaysStream(true);
    586     }
    587 
    588     createRequest(es);
    589 }
    590 
    591 void XMLHttpRequest::send(const String& body, ExceptionState& es)
    592 {
    593     if (!initSend(es))
    594         return;
    595 
    596     if (!body.isNull() && areMethodAndURLValidForSend()) {
    597         String contentType = getRequestHeader("Content-Type");
    598         if (contentType.isEmpty()) {
    599             setRequestHeaderInternal("Content-Type", "text/plain;charset=UTF-8");
    600         } else {
    601             replaceCharsetInMediaType(contentType, "UTF-8");
    602             m_requestHeaders.set("Content-Type", contentType);
    603         }
    604 
    605         m_requestEntityBody = FormData::create(UTF8Encoding().normalizeAndEncode(body, WTF::EntitiesForUnencodables));
    606         if (m_upload)
    607             m_requestEntityBody->setAlwaysStream(true);
    608     }
    609 
    610     createRequest(es);
    611 }
    612 
    613 void XMLHttpRequest::send(Blob* body, ExceptionState& es)
    614 {
    615     if (!initSend(es))
    616         return;
    617 
    618     if (areMethodAndURLValidForSend()) {
    619         const String& contentType = getRequestHeader("Content-Type");
    620         if (contentType.isEmpty()) {
    621             const String& blobType = body->type();
    622             if (!blobType.isEmpty() && isValidContentType(blobType))
    623                 setRequestHeaderInternal("Content-Type", blobType);
    624             else {
    625                 // From FileAPI spec, whenever media type cannot be determined, empty string must be returned.
    626                 setRequestHeaderInternal("Content-Type", "");
    627             }
    628         }
    629 
    630         // FIXME: add support for uploading bundles.
    631         m_requestEntityBody = FormData::create();
    632         if (body->isFile())
    633             m_requestEntityBody->appendFile(toFile(body)->path());
    634         else
    635             m_requestEntityBody->appendBlob(body->url());
    636     }
    637 
    638     createRequest(es);
    639 }
    640 
    641 void XMLHttpRequest::send(DOMFormData* body, ExceptionState& es)
    642 {
    643     if (!initSend(es))
    644         return;
    645 
    646     if (areMethodAndURLValidForSend()) {
    647         m_requestEntityBody = FormData::createMultiPart(*(static_cast<FormDataList*>(body)), body->encoding(), document());
    648 
    649         String contentType = getRequestHeader("Content-Type");
    650         if (contentType.isEmpty()) {
    651             contentType = String("multipart/form-data; boundary=") + m_requestEntityBody->boundary().data();
    652             setRequestHeaderInternal("Content-Type", contentType);
    653         }
    654     }
    655 
    656     createRequest(es);
    657 }
    658 
    659 void XMLHttpRequest::send(ArrayBuffer* body, ExceptionState& es)
    660 {
    661     String consoleMessage("ArrayBuffer is deprecated in XMLHttpRequest.send(). Use ArrayBufferView instead.");
    662     scriptExecutionContext()->addConsoleMessage(JSMessageSource, WarningMessageLevel, consoleMessage);
    663 
    664     HistogramSupport::histogramEnumeration("WebCore.XHR.send.ArrayBufferOrView", XMLHttpRequestSendArrayBuffer, XMLHttpRequestSendArrayBufferOrViewMax);
    665 
    666     sendBytesData(body->data(), body->byteLength(), es);
    667 }
    668 
    669 void XMLHttpRequest::send(ArrayBufferView* body, ExceptionState& es)
    670 {
    671     HistogramSupport::histogramEnumeration("WebCore.XHR.send.ArrayBufferOrView", XMLHttpRequestSendArrayBufferView, XMLHttpRequestSendArrayBufferOrViewMax);
    672 
    673     sendBytesData(body->baseAddress(), body->byteLength(), es);
    674 }
    675 
    676 void XMLHttpRequest::sendBytesData(const void* data, size_t length, ExceptionState& es)
    677 {
    678     if (!initSend(es))
    679         return;
    680 
    681     if (areMethodAndURLValidForSend()) {
    682         m_requestEntityBody = FormData::create(data, length);
    683         if (m_upload)
    684             m_requestEntityBody->setAlwaysStream(true);
    685     }
    686 
    687     createRequest(es);
    688 }
    689 
    690 void XMLHttpRequest::sendForInspectorXHRReplay(PassRefPtr<FormData> formData, ExceptionState& es)
    691 {
    692     m_requestEntityBody = formData ? formData->deepCopy() : 0;
    693     createRequest(es);
    694     m_exceptionCode = es.code();
    695 }
    696 
    697 void XMLHttpRequest::createRequest(ExceptionState& es)
    698 {
    699     // Only GET request is supported for blob URL.
    700     if (m_url.protocolIs("blob") && m_method != "GET") {
    701         es.throwDOMException(NetworkError);
    702         return;
    703     }
    704 
    705     // The presence of upload event listeners forces us to use preflighting because POSTing to an URL that does not
    706     // permit cross origin requests should look exactly like POSTing to an URL that does not respond at all.
    707     // Also, only async requests support upload progress events.
    708     bool uploadEvents = false;
    709     if (m_async) {
    710         m_progressEventThrottle.dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().loadstartEvent));
    711         if (m_requestEntityBody && m_upload) {
    712             uploadEvents = m_upload->hasEventListeners();
    713             m_upload->dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().loadstartEvent));
    714         }
    715     }
    716 
    717     m_sameOriginRequest = securityOrigin()->canRequest(m_url);
    718 
    719     // We also remember whether upload events should be allowed for this request in case the upload listeners are
    720     // added after the request is started.
    721     m_uploadEventsAllowed = m_sameOriginRequest || uploadEvents || !isSimpleCrossOriginAccessRequest(m_method, m_requestHeaders);
    722 
    723     ResourceRequest request(m_url);
    724     request.setHTTPMethod(m_method);
    725     request.setTargetType(ResourceRequest::TargetIsXHR);
    726 
    727     InspectorInstrumentation::willLoadXHR(scriptExecutionContext(), this, m_method, m_url, m_async, m_requestEntityBody ? m_requestEntityBody->deepCopy() : 0, m_requestHeaders, m_includeCredentials);
    728 
    729     if (m_requestEntityBody) {
    730         ASSERT(m_method != "GET");
    731         ASSERT(m_method != "HEAD");
    732         request.setHTTPBody(m_requestEntityBody.release());
    733     }
    734 
    735     if (m_requestHeaders.size() > 0)
    736         request.addHTTPHeaderFields(m_requestHeaders);
    737 
    738     ThreadableLoaderOptions options;
    739     options.sendLoadCallbacks = SendCallbacks;
    740     options.sniffContent = DoNotSniffContent;
    741     options.preflightPolicy = uploadEvents ? ForcePreflight : ConsiderPreflight;
    742     options.allowCredentials = (m_sameOriginRequest || m_includeCredentials) ? AllowStoredCredentials : DoNotAllowStoredCredentials;
    743     options.credentialsRequested = m_includeCredentials ? ClientRequestedCredentials : ClientDidNotRequestCredentials;
    744     options.crossOriginRequestPolicy = m_allowCrossOriginRequests ? AllowCrossOriginRequests : UseAccessControl;
    745     options.securityOrigin = securityOrigin();
    746     options.initiator = FetchInitiatorTypeNames::xmlhttprequest;
    747     options.contentSecurityPolicyEnforcement = ContentSecurityPolicy::shouldBypassMainWorld(scriptExecutionContext()) ? DoNotEnforceContentSecurityPolicy : EnforceConnectSrcDirective;
    748     options.timeoutMilliseconds = m_timeoutMilliseconds;
    749 
    750     m_exceptionCode = 0;
    751     m_error = false;
    752 
    753     if (m_async) {
    754         if (m_upload)
    755             request.setReportUploadProgress(true);
    756 
    757         // ThreadableLoader::create can return null here, for example if we're no longer attached to a page.
    758         // This is true while running onunload handlers.
    759         // FIXME: Maybe we need to be able to send XMLHttpRequests from onunload, <http://bugs.webkit.org/show_bug.cgi?id=10904>.
    760         // FIXME: Maybe create() can return null for other reasons too?
    761         m_loader = ThreadableLoader::create(scriptExecutionContext(), this, request, options);
    762         if (m_loader) {
    763             // Neither this object nor the JavaScript wrapper should be deleted while
    764             // a request is in progress because we need to keep the listeners alive,
    765             // and they are referenced by the JavaScript wrapper.
    766             setPendingActivity(this);
    767         }
    768     } else {
    769         request.setPriority(ResourceLoadPriorityVeryHigh);
    770         InspectorInstrumentation::willLoadXHRSynchronously(scriptExecutionContext());
    771         ThreadableLoader::loadResourceSynchronously(scriptExecutionContext(), request, *this, options);
    772         InspectorInstrumentation::didLoadXHRSynchronously(scriptExecutionContext());
    773     }
    774 
    775     if (!m_exceptionCode && m_error)
    776         m_exceptionCode = NetworkError;
    777     if (m_exceptionCode)
    778         es.throwDOMException(m_exceptionCode);
    779 }
    780 
    781 void XMLHttpRequest::abort()
    782 {
    783     // internalAbort() calls dropProtection(), which may release the last reference.
    784     RefPtr<XMLHttpRequest> protect(this);
    785 
    786     bool sendFlag = m_loader;
    787 
    788     if (!internalAbort())
    789         return;
    790 
    791     clearResponseBuffers();
    792 
    793     // Clear headers as required by the spec
    794     m_requestHeaders.clear();
    795 
    796     if ((m_state <= OPENED && !sendFlag) || m_state == DONE)
    797         m_state = UNSENT;
    798     else {
    799         ASSERT(!m_loader);
    800         changeState(DONE);
    801         m_state = UNSENT;
    802     }
    803 
    804     m_progressEventThrottle.dispatchEventAndLoadEnd(XMLHttpRequestProgressEvent::create(eventNames().abortEvent));
    805     if (!m_uploadComplete) {
    806         m_uploadComplete = true;
    807         if (m_upload && m_uploadEventsAllowed)
    808             m_upload->dispatchEventAndLoadEnd(XMLHttpRequestProgressEvent::create(eventNames().abortEvent));
    809     }
    810 }
    811 
    812 bool XMLHttpRequest::internalAbort()
    813 {
    814     bool hadLoader = m_loader;
    815 
    816     m_error = true;
    817 
    818     // FIXME: when we add the support for multi-part XHR, we will have to think be careful with this initialization.
    819     m_receivedLength = 0;
    820 
    821     if (hadLoader) {
    822         // Cancelling the ThreadableLoader m_loader may result in calling
    823         // window.onload synchronously. If such an onload handler contains
    824         // open() call on the same XMLHttpRequest object, reentry happens. If
    825         // m_loader is left to be non 0, internalAbort() call for the inner
    826         // open() makes an extra dropProtection() call (when we're back to the
    827         // outer open(), we'll call dropProtection()). To avoid that, clears
    828         // m_loader before calling cancel.
    829         //
    830         // If, window.onload contains open() and send(), m_loader will be set to
    831         // non 0 value. So, we cannot continue the outer open(). In such case,
    832         // just abort the outer open() by returning false.
    833         RefPtr<ThreadableLoader> loader = m_loader.release();
    834         loader->cancel();
    835     }
    836 
    837     m_decoder = 0;
    838 
    839     InspectorInstrumentation::didFailXHRLoading(scriptExecutionContext(), this);
    840 
    841     if (hadLoader)
    842         dropProtectionSoon();
    843 
    844     return !m_loader;
    845 }
    846 
    847 void XMLHttpRequest::clearResponse()
    848 {
    849     m_response = ResourceResponse();
    850     clearResponseBuffers();
    851 }
    852 
    853 void XMLHttpRequest::clearResponseBuffers()
    854 {
    855     m_responseText.clear();
    856     m_responseEncoding = String();
    857     m_createdDocument = false;
    858     m_responseDocument = 0;
    859     m_responseBlob = 0;
    860     m_binaryResponseBuilder.clear();
    861     m_responseArrayBuffer.clear();
    862 }
    863 
    864 void XMLHttpRequest::clearRequest()
    865 {
    866     m_requestHeaders.clear();
    867     m_requestEntityBody = 0;
    868 }
    869 
    870 void XMLHttpRequest::genericError()
    871 {
    872     clearResponse();
    873     clearRequest();
    874     m_error = true;
    875 
    876     changeState(DONE);
    877 }
    878 
    879 void XMLHttpRequest::networkError()
    880 {
    881     genericError();
    882     if (!m_uploadComplete) {
    883         m_uploadComplete = true;
    884         if (m_upload && m_uploadEventsAllowed)
    885             m_upload->dispatchEventAndLoadEnd(XMLHttpRequestProgressEvent::create(eventNames().errorEvent));
    886     }
    887     m_progressEventThrottle.dispatchEventAndLoadEnd(XMLHttpRequestProgressEvent::create(eventNames().errorEvent));
    888     internalAbort();
    889 }
    890 
    891 void XMLHttpRequest::abortError()
    892 {
    893     genericError();
    894     if (!m_uploadComplete) {
    895         m_uploadComplete = true;
    896         if (m_upload && m_uploadEventsAllowed)
    897             m_upload->dispatchEventAndLoadEnd(XMLHttpRequestProgressEvent::create(eventNames().abortEvent));
    898     }
    899     m_progressEventThrottle.dispatchEventAndLoadEnd(XMLHttpRequestProgressEvent::create(eventNames().abortEvent));
    900 }
    901 
    902 void XMLHttpRequest::dropProtectionSoon()
    903 {
    904     if (m_protectionTimer.isActive())
    905         return;
    906     m_protectionTimer.startOneShot(0);
    907 }
    908 
    909 void XMLHttpRequest::dropProtection(Timer<XMLHttpRequest>*)
    910 {
    911     unsetPendingActivity(this);
    912 }
    913 
    914 void XMLHttpRequest::overrideMimeType(const String& override)
    915 {
    916     m_mimeTypeOverride = override;
    917 }
    918 
    919 void XMLHttpRequest::setRequestHeader(const AtomicString& name, const String& value, ExceptionState& es)
    920 {
    921     if (m_state != OPENED || m_loader) {
    922         es.throwDOMException(InvalidStateError);
    923         return;
    924     }
    925 
    926     if (!isValidHTTPToken(name) || !isValidHTTPHeaderValue(value)) {
    927         es.throwDOMException(SyntaxError);
    928         return;
    929     }
    930 
    931     // No script (privileged or not) can set unsafe headers.
    932     if (!isAllowedHTTPHeader(name)) {
    933         logConsoleError(scriptExecutionContext(), "Refused to set unsafe header \"" + name + "\"");
    934         return;
    935     }
    936 
    937     setRequestHeaderInternal(name, value);
    938 }
    939 
    940 void XMLHttpRequest::setRequestHeaderInternal(const AtomicString& name, const String& value)
    941 {
    942     HTTPHeaderMap::AddResult result = m_requestHeaders.add(name, value);
    943     if (!result.isNewEntry)
    944         result.iterator->value = result.iterator->value + ", " + value;
    945 }
    946 
    947 String XMLHttpRequest::getRequestHeader(const AtomicString& name) const
    948 {
    949     return m_requestHeaders.get(name);
    950 }
    951 
    952 String XMLHttpRequest::getAllResponseHeaders(ExceptionState& es) const
    953 {
    954     if (m_state < HEADERS_RECEIVED) {
    955         es.throwDOMException(InvalidStateError);
    956         return "";
    957     }
    958 
    959     StringBuilder stringBuilder;
    960 
    961     HTTPHeaderSet accessControlExposeHeaderSet;
    962     parseAccessControlExposeHeadersAllowList(m_response.httpHeaderField("Access-Control-Expose-Headers"), accessControlExposeHeaderSet);
    963     HTTPHeaderMap::const_iterator end = m_response.httpHeaderFields().end();
    964     for (HTTPHeaderMap::const_iterator it = m_response.httpHeaderFields().begin(); it!= end; ++it) {
    965         // Hide Set-Cookie header fields from the XMLHttpRequest client for these reasons:
    966         //     1) If the client did have access to the fields, then it could read HTTP-only
    967         //        cookies; those cookies are supposed to be hidden from scripts.
    968         //     2) There's no known harm in hiding Set-Cookie header fields entirely; we don't
    969         //        know any widely used technique that requires access to them.
    970         //     3) Firefox has implemented this policy.
    971         if (isSetCookieHeader(it->key) && !securityOrigin()->canLoadLocalResources())
    972             continue;
    973 
    974         if (!m_sameOriginRequest && !isOnAccessControlResponseHeaderWhitelist(it->key) && !accessControlExposeHeaderSet.contains(it->key))
    975             continue;
    976 
    977         stringBuilder.append(it->key);
    978         stringBuilder.append(':');
    979         stringBuilder.append(' ');
    980         stringBuilder.append(it->value);
    981         stringBuilder.append('\r');
    982         stringBuilder.append('\n');
    983     }
    984 
    985     return stringBuilder.toString();
    986 }
    987 
    988 String XMLHttpRequest::getResponseHeader(const AtomicString& name, ExceptionState& es) const
    989 {
    990     if (m_state < HEADERS_RECEIVED) {
    991         es.throwDOMException(InvalidStateError);
    992         return String();
    993     }
    994 
    995     // See comment in getAllResponseHeaders above.
    996     if (isSetCookieHeader(name) && !securityOrigin()->canLoadLocalResources()) {
    997         logConsoleError(scriptExecutionContext(), "Refused to get unsafe header \"" + name + "\"");
    998         return String();
    999     }
   1000 
   1001     HTTPHeaderSet accessControlExposeHeaderSet;
   1002     parseAccessControlExposeHeadersAllowList(m_response.httpHeaderField("Access-Control-Expose-Headers"), accessControlExposeHeaderSet);
   1003 
   1004     if (!m_sameOriginRequest && !isOnAccessControlResponseHeaderWhitelist(name) && !accessControlExposeHeaderSet.contains(name)) {
   1005         logConsoleError(scriptExecutionContext(), "Refused to get unsafe header \"" + name + "\"");
   1006         return String();
   1007     }
   1008     return m_response.httpHeaderField(name);
   1009 }
   1010 
   1011 String XMLHttpRequest::responseMIMEType() const
   1012 {
   1013     String mimeType = extractMIMETypeFromMediaType(m_mimeTypeOverride);
   1014     if (mimeType.isEmpty()) {
   1015         if (m_response.isHTTP())
   1016             mimeType = extractMIMETypeFromMediaType(m_response.httpHeaderField("Content-Type"));
   1017         else
   1018             mimeType = m_response.mimeType();
   1019     }
   1020     if (mimeType.isEmpty())
   1021         mimeType = "text/xml";
   1022 
   1023     return mimeType;
   1024 }
   1025 
   1026 bool XMLHttpRequest::responseIsXML() const
   1027 {
   1028     // FIXME: Remove the lower() call when DOMImplementation.isXMLMIMEType() is modified
   1029     //        to do case insensitive MIME type matching.
   1030     return DOMImplementation::isXMLMIMEType(responseMIMEType().lower());
   1031 }
   1032 
   1033 int XMLHttpRequest::status(ExceptionState& es) const
   1034 {
   1035     if (m_response.httpStatusCode())
   1036         return m_response.httpStatusCode();
   1037 
   1038     if (m_state == OPENED) {
   1039         // Firefox only raises an exception in this state; we match it.
   1040         // Note the case of local file requests, where we have no HTTP response code! Firefox never raises an exception for those, but we match HTTP case for consistency.
   1041         es.throwDOMException(InvalidStateError);
   1042     }
   1043 
   1044     return 0;
   1045 }
   1046 
   1047 String XMLHttpRequest::statusText(ExceptionState& es) const
   1048 {
   1049     if (!m_response.httpStatusText().isNull())
   1050         return m_response.httpStatusText();
   1051 
   1052     if (m_state == OPENED) {
   1053         // See comments in status() above.
   1054         es.throwDOMException(InvalidStateError);
   1055     }
   1056 
   1057     return String();
   1058 }
   1059 
   1060 void XMLHttpRequest::didFail(const ResourceError& error)
   1061 {
   1062 
   1063     // If we are already in an error state, for instance we called abort(), bail out early.
   1064     if (m_error)
   1065         return;
   1066 
   1067     if (error.isCancellation()) {
   1068         m_exceptionCode = AbortError;
   1069         abortError();
   1070         return;
   1071     }
   1072 
   1073     if (error.isTimeout()) {
   1074         didTimeout();
   1075         return;
   1076     }
   1077 
   1078     // Network failures are already reported to Web Inspector by ResourceLoader.
   1079     if (error.domain() == errorDomainWebKitInternal)
   1080         logConsoleError(scriptExecutionContext(), "XMLHttpRequest cannot load " + error.failingURL() + ". " + error.localizedDescription());
   1081 
   1082     m_exceptionCode = NetworkError;
   1083     networkError();
   1084 }
   1085 
   1086 void XMLHttpRequest::didFailRedirectCheck()
   1087 {
   1088     networkError();
   1089 }
   1090 
   1091 void XMLHttpRequest::didFinishLoading(unsigned long identifier, double)
   1092 {
   1093     if (m_error)
   1094         return;
   1095 
   1096     if (m_state < HEADERS_RECEIVED)
   1097         changeState(HEADERS_RECEIVED);
   1098 
   1099     if (m_decoder)
   1100         m_responseText = m_responseText.concatenateWith(m_decoder->flush());
   1101 
   1102     InspectorInstrumentation::didFinishXHRLoading(scriptExecutionContext(), this, identifier, m_responseText, m_url, m_lastSendURL, m_lastSendLineNumber);
   1103 
   1104     bool hadLoader = m_loader;
   1105     m_loader = 0;
   1106 
   1107     changeState(DONE);
   1108     m_responseEncoding = String();
   1109     m_decoder = 0;
   1110 
   1111     if (hadLoader)
   1112         dropProtection();
   1113 }
   1114 
   1115 void XMLHttpRequest::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
   1116 {
   1117     if (!m_upload)
   1118         return;
   1119 
   1120     if (m_uploadEventsAllowed)
   1121         m_upload->dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().progressEvent, true, bytesSent, totalBytesToBeSent));
   1122 
   1123     if (bytesSent == totalBytesToBeSent && !m_uploadComplete) {
   1124         m_uploadComplete = true;
   1125         if (m_uploadEventsAllowed)
   1126             m_upload->dispatchEventAndLoadEnd(XMLHttpRequestProgressEvent::create(eventNames().loadEvent));
   1127     }
   1128 }
   1129 
   1130 void XMLHttpRequest::didReceiveResponse(unsigned long identifier, const ResourceResponse& response)
   1131 {
   1132     InspectorInstrumentation::didReceiveXHRResponse(scriptExecutionContext(), identifier);
   1133 
   1134     m_response = response;
   1135     if (!m_mimeTypeOverride.isEmpty()) {
   1136         m_response.setHTTPHeaderField("Content-Type", m_mimeTypeOverride);
   1137         m_responseEncoding = extractCharsetFromMediaType(m_mimeTypeOverride);
   1138     }
   1139 
   1140     if (m_responseEncoding.isEmpty())
   1141         m_responseEncoding = response.textEncodingName();
   1142 }
   1143 
   1144 void XMLHttpRequest::didReceiveData(const char* data, int len)
   1145 {
   1146     if (m_error)
   1147         return;
   1148 
   1149     if (m_state < HEADERS_RECEIVED)
   1150         changeState(HEADERS_RECEIVED);
   1151 
   1152     bool useDecoder = m_responseTypeCode == ResponseTypeDefault || m_responseTypeCode == ResponseTypeText || m_responseTypeCode == ResponseTypeDocument;
   1153 
   1154     if (useDecoder && !m_decoder) {
   1155         if (!m_responseEncoding.isEmpty())
   1156             m_decoder = TextResourceDecoder::create("text/plain", m_responseEncoding);
   1157         // allow TextResourceDecoder to look inside the m_response if it's XML or HTML
   1158         else if (responseIsXML()) {
   1159             m_decoder = TextResourceDecoder::create("application/xml");
   1160             // Don't stop on encoding errors, unlike it is done for other kinds of XML resources. This matches the behavior of previous WebKit versions, Firefox and Opera.
   1161             m_decoder->useLenientXMLDecoding();
   1162         } else if (equalIgnoringCase(responseMIMEType(), "text/html"))
   1163             m_decoder = TextResourceDecoder::create("text/html", "UTF-8");
   1164         else
   1165             m_decoder = TextResourceDecoder::create("text/plain", "UTF-8");
   1166     }
   1167 
   1168     if (!len)
   1169         return;
   1170 
   1171     if (len == -1)
   1172         len = strlen(data);
   1173 
   1174     if (useDecoder)
   1175         m_responseText = m_responseText.concatenateWith(m_decoder->decode(data, len));
   1176     else if (m_responseTypeCode == ResponseTypeArrayBuffer || m_responseTypeCode == ResponseTypeBlob) {
   1177         // Buffer binary data.
   1178         if (!m_binaryResponseBuilder)
   1179             m_binaryResponseBuilder = SharedBuffer::create();
   1180         m_binaryResponseBuilder->append(data, len);
   1181     }
   1182 
   1183     if (!m_error) {
   1184         long long expectedLength = m_response.expectedContentLength();
   1185         m_receivedLength += len;
   1186 
   1187         if (m_async) {
   1188             bool lengthComputable = expectedLength > 0 && m_receivedLength <= expectedLength;
   1189             unsigned long long total = lengthComputable ? expectedLength : 0;
   1190             m_progressEventThrottle.dispatchProgressEvent(lengthComputable, m_receivedLength, total);
   1191         }
   1192 
   1193         if (m_state != LOADING)
   1194             changeState(LOADING);
   1195         else
   1196             // Firefox calls readyStateChanged every time it receives data, 4449442
   1197             callReadyStateChangeListener();
   1198     }
   1199 }
   1200 
   1201 void XMLHttpRequest::didTimeout()
   1202 {
   1203     // internalAbort() calls dropProtection(), which may release the last reference.
   1204     RefPtr<XMLHttpRequest> protect(this);
   1205     if (!internalAbort())
   1206         return;
   1207 
   1208     clearResponse();
   1209     clearRequest();
   1210 
   1211     m_error = true;
   1212     m_exceptionCode = TimeoutError;
   1213 
   1214     if (!m_async) {
   1215         m_state = DONE;
   1216         m_exceptionCode = TimeoutError;
   1217         return;
   1218     }
   1219 
   1220     changeState(DONE);
   1221 
   1222     if (!m_uploadComplete) {
   1223         m_uploadComplete = true;
   1224         if (m_upload && m_uploadEventsAllowed)
   1225             m_upload->dispatchEventAndLoadEnd(XMLHttpRequestProgressEvent::create(eventNames().timeoutEvent));
   1226     }
   1227     m_progressEventThrottle.dispatchEventAndLoadEnd(XMLHttpRequestProgressEvent::create(eventNames().timeoutEvent));
   1228 }
   1229 
   1230 bool XMLHttpRequest::canSuspend() const
   1231 {
   1232     return !m_loader;
   1233 }
   1234 
   1235 void XMLHttpRequest::suspend(ReasonForSuspension)
   1236 {
   1237     m_progressEventThrottle.suspend();
   1238 }
   1239 
   1240 void XMLHttpRequest::resume()
   1241 {
   1242     m_progressEventThrottle.resume();
   1243 }
   1244 
   1245 void XMLHttpRequest::stop()
   1246 {
   1247     internalAbort();
   1248 }
   1249 
   1250 void XMLHttpRequest::contextDestroyed()
   1251 {
   1252     ASSERT(!m_loader);
   1253     ActiveDOMObject::contextDestroyed();
   1254 }
   1255 
   1256 const AtomicString& XMLHttpRequest::interfaceName() const
   1257 {
   1258     return eventNames().interfaceForXMLHttpRequest;
   1259 }
   1260 
   1261 ScriptExecutionContext* XMLHttpRequest::scriptExecutionContext() const
   1262 {
   1263     return ActiveDOMObject::scriptExecutionContext();
   1264 }
   1265 
   1266 EventTargetData* XMLHttpRequest::eventTargetData()
   1267 {
   1268     return &m_eventTargetData;
   1269 }
   1270 
   1271 EventTargetData* XMLHttpRequest::ensureEventTargetData()
   1272 {
   1273     return &m_eventTargetData;
   1274 }
   1275 
   1276 } // namespace WebCore
   1277