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 David Levin <levin (at) chromium.org>
      6  *
      7  *  This library is free software; you can redistribute it and/or
      8  *  modify it under the terms of the GNU Lesser General Public
      9  *  License as published by the Free Software Foundation; either
     10  *  version 2 of the License, or (at your option) any later version.
     11  *
     12  *  This library is distributed in the hope that it will be useful,
     13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15  *  Lesser General Public License for more details.
     16  *
     17  *  You should have received a copy of the GNU Lesser General Public
     18  *  License along with this library; if not, write to the Free Software
     19  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
     20  */
     21 
     22 #include "config.h"
     23 #include "XMLHttpRequest.h"
     24 
     25 #include "ArrayBuffer.h"
     26 #include "Blob.h"
     27 #include "MemoryCache.h"
     28 #include "CrossOriginAccessControl.h"
     29 #include "DOMFormData.h"
     30 #include "DOMImplementation.h"
     31 #include "Document.h"
     32 #include "Event.h"
     33 #include "EventException.h"
     34 #include "EventListener.h"
     35 #include "EventNames.h"
     36 #include "File.h"
     37 #include "HTTPParsers.h"
     38 #include "InspectorInstrumentation.h"
     39 #include "ResourceError.h"
     40 #include "ResourceRequest.h"
     41 #include "ScriptCallStack.h"
     42 #include "SecurityOrigin.h"
     43 #include "Settings.h"
     44 #include "SharedBuffer.h"
     45 #include "TextResourceDecoder.h"
     46 #include "ThreadableLoader.h"
     47 #include "XMLHttpRequestException.h"
     48 #include "XMLHttpRequestProgressEvent.h"
     49 #include "XMLHttpRequestUpload.h"
     50 #include "markup.h"
     51 #include <wtf/text/CString.h>
     52 #include <wtf/StdLibExtras.h>
     53 #include <wtf/RefCountedLeakCounter.h>
     54 #include <wtf/UnusedParam.h>
     55 
     56 #if USE(JSC)
     57 #include "JSDOMBinding.h"
     58 #include "JSDOMWindow.h"
     59 #include <heap/Strong.h>
     60 #include <runtime/JSLock.h>
     61 #endif
     62 
     63 namespace WebCore {
     64 
     65 #ifndef NDEBUG
     66 static WTF::RefCountedLeakCounter xmlHttpRequestCounter("XMLHttpRequest");
     67 #endif
     68 
     69 struct XMLHttpRequestStaticData {
     70     WTF_MAKE_NONCOPYABLE(XMLHttpRequestStaticData); WTF_MAKE_FAST_ALLOCATED;
     71 public:
     72     XMLHttpRequestStaticData();
     73     String m_proxyHeaderPrefix;
     74     String m_secHeaderPrefix;
     75     HashSet<String, CaseFoldingHash> m_forbiddenRequestHeaders;
     76 };
     77 
     78 XMLHttpRequestStaticData::XMLHttpRequestStaticData()
     79     : m_proxyHeaderPrefix("proxy-")
     80     , m_secHeaderPrefix("sec-")
     81 {
     82     m_forbiddenRequestHeaders.add("accept-charset");
     83     m_forbiddenRequestHeaders.add("accept-encoding");
     84     m_forbiddenRequestHeaders.add("access-control-request-headers");
     85     m_forbiddenRequestHeaders.add("access-control-request-method");
     86     m_forbiddenRequestHeaders.add("connection");
     87     m_forbiddenRequestHeaders.add("content-length");
     88     m_forbiddenRequestHeaders.add("content-transfer-encoding");
     89     m_forbiddenRequestHeaders.add("cookie");
     90     m_forbiddenRequestHeaders.add("cookie2");
     91     m_forbiddenRequestHeaders.add("date");
     92     m_forbiddenRequestHeaders.add("expect");
     93     m_forbiddenRequestHeaders.add("host");
     94     m_forbiddenRequestHeaders.add("keep-alive");
     95     m_forbiddenRequestHeaders.add("origin");
     96     m_forbiddenRequestHeaders.add("referer");
     97     m_forbiddenRequestHeaders.add("te");
     98     m_forbiddenRequestHeaders.add("trailer");
     99     m_forbiddenRequestHeaders.add("transfer-encoding");
    100     m_forbiddenRequestHeaders.add("upgrade");
    101     m_forbiddenRequestHeaders.add("user-agent");
    102     m_forbiddenRequestHeaders.add("via");
    103 }
    104 
    105 // Determines if a string is a valid token, as defined by
    106 // "token" in section 2.2 of RFC 2616.
    107 static bool isValidToken(const String& name)
    108 {
    109     unsigned length = name.length();
    110     for (unsigned i = 0; i < length; i++) {
    111         UChar c = name[i];
    112 
    113         if (c >= 127 || c <= 32)
    114             return false;
    115 
    116         if (c == '(' || c == ')' || c == '<' || c == '>' || c == '@' ||
    117             c == ',' || c == ';' || c == ':' || c == '\\' || c == '\"' ||
    118             c == '/' || c == '[' || c == ']' || c == '?' || c == '=' ||
    119             c == '{' || c == '}')
    120             return false;
    121     }
    122 
    123     return length > 0;
    124 }
    125 
    126 static bool isValidHeaderValue(const String& name)
    127 {
    128     // FIXME: This should really match name against
    129     // field-value in section 4.2 of RFC 2616.
    130 
    131     return !name.contains('\r') && !name.contains('\n');
    132 }
    133 
    134 static bool isSetCookieHeader(const AtomicString& name)
    135 {
    136     return equalIgnoringCase(name, "set-cookie") || equalIgnoringCase(name, "set-cookie2");
    137 }
    138 
    139 static void replaceCharsetInMediaType(String& mediaType, const String& charsetValue)
    140 {
    141     unsigned int pos = 0, len = 0;
    142 
    143     findCharsetInMediaType(mediaType, pos, len);
    144 
    145     if (!len) {
    146         // When no charset found, do nothing.
    147         return;
    148     }
    149 
    150     // Found at least one existing charset, replace all occurrences with new charset.
    151     while (len) {
    152         mediaType.replace(pos, len, charsetValue);
    153         unsigned int start = pos + charsetValue.length();
    154         findCharsetInMediaType(mediaType, pos, len, start);
    155     }
    156 }
    157 
    158 static const XMLHttpRequestStaticData* staticData = 0;
    159 
    160 static const XMLHttpRequestStaticData* createXMLHttpRequestStaticData()
    161 {
    162     staticData = new XMLHttpRequestStaticData;
    163     return staticData;
    164 }
    165 
    166 static const XMLHttpRequestStaticData* initializeXMLHttpRequestStaticData()
    167 {
    168     // Uses dummy to avoid warnings about an unused variable.
    169     AtomicallyInitializedStatic(const XMLHttpRequestStaticData*, dummy = createXMLHttpRequestStaticData());
    170     return dummy;
    171 }
    172 
    173 XMLHttpRequest::XMLHttpRequest(ScriptExecutionContext* context)
    174     : ActiveDOMObject(context, this)
    175     , m_async(true)
    176     , m_includeCredentials(false)
    177     , m_state(UNSENT)
    178     , m_createdDocument(false)
    179     , m_error(false)
    180     , m_uploadEventsAllowed(true)
    181     , m_uploadComplete(false)
    182     , m_sameOriginRequest(true)
    183     , m_receivedLength(0)
    184     , m_lastSendLineNumber(0)
    185     , m_exceptionCode(0)
    186     , m_progressEventThrottle(this)
    187     , m_responseTypeCode(ResponseTypeDefault)
    188 {
    189     initializeXMLHttpRequestStaticData();
    190 #ifndef NDEBUG
    191     xmlHttpRequestCounter.increment();
    192 #endif
    193 }
    194 
    195 XMLHttpRequest::~XMLHttpRequest()
    196 {
    197     if (m_upload)
    198         m_upload->disconnectXMLHttpRequest();
    199 
    200 #ifndef NDEBUG
    201     xmlHttpRequestCounter.decrement();
    202 #endif
    203 }
    204 
    205 Document* XMLHttpRequest::document() const
    206 {
    207     ASSERT(scriptExecutionContext()->isDocument());
    208     return static_cast<Document*>(scriptExecutionContext());
    209 }
    210 
    211 #if ENABLE(DASHBOARD_SUPPORT)
    212 bool XMLHttpRequest::usesDashboardBackwardCompatibilityMode() const
    213 {
    214     if (scriptExecutionContext()->isWorkerContext())
    215         return false;
    216     Settings* settings = document()->settings();
    217     return settings && settings->usesDashboardBackwardCompatibilityMode();
    218 }
    219 #endif
    220 
    221 XMLHttpRequest::State XMLHttpRequest::readyState() const
    222 {
    223     return m_state;
    224 }
    225 
    226 String XMLHttpRequest::responseText(ExceptionCode& ec)
    227 {
    228     if (responseTypeCode() != ResponseTypeDefault && responseTypeCode() != ResponseTypeText) {
    229         ec = INVALID_STATE_ERR;
    230         return "";
    231     }
    232     return m_responseBuilder.toStringPreserveCapacity();
    233 }
    234 
    235 Document* XMLHttpRequest::responseXML(ExceptionCode& ec)
    236 {
    237     if (responseTypeCode() != ResponseTypeDefault && responseTypeCode() != ResponseTypeText && responseTypeCode() != ResponseTypeDocument) {
    238         ec = INVALID_STATE_ERR;
    239         return 0;
    240     }
    241 
    242     if (m_state != DONE)
    243         return 0;
    244 
    245     if (!m_createdDocument) {
    246         if ((m_response.isHTTP() && !responseIsXML()) || scriptExecutionContext()->isWorkerContext()) {
    247             // The W3C spec requires this.
    248             m_responseXML = 0;
    249         } else {
    250             m_responseXML = Document::create(0, m_url);
    251             // FIXME: Set Last-Modified.
    252             m_responseXML->setContent(m_responseBuilder.toStringPreserveCapacity());
    253             m_responseXML->setSecurityOrigin(document()->securityOrigin());
    254             if (!m_responseXML->wellFormed())
    255                 m_responseXML = 0;
    256         }
    257         m_createdDocument = true;
    258     }
    259 
    260     return m_responseXML.get();
    261 }
    262 
    263 #if ENABLE(XHR_RESPONSE_BLOB)
    264 Blob* XMLHttpRequest::responseBlob(ExceptionCode& ec) const
    265 {
    266     if (responseTypeCode() != ResponseTypeBlob) {
    267         ec = INVALID_STATE_ERR;
    268         return 0;
    269     }
    270     return m_responseBlob.get();
    271 }
    272 #endif
    273 
    274 ArrayBuffer* XMLHttpRequest::responseArrayBuffer(ExceptionCode& ec)
    275 {
    276     if (m_responseTypeCode != ResponseTypeArrayBuffer) {
    277         ec = INVALID_STATE_ERR;
    278         return 0;
    279     }
    280 
    281     if (m_state != DONE)
    282         return 0;
    283 
    284     if (!m_responseArrayBuffer.get() && m_binaryResponseBuilder.get() && m_binaryResponseBuilder->size() > 0) {
    285         m_responseArrayBuffer = ArrayBuffer::create(const_cast<char*>(m_binaryResponseBuilder->data()), static_cast<unsigned>(m_binaryResponseBuilder->size()));
    286         m_binaryResponseBuilder.clear();
    287     }
    288 
    289     if (m_responseArrayBuffer.get())
    290         return m_responseArrayBuffer.get();
    291 
    292     return 0;
    293 }
    294 
    295 void XMLHttpRequest::setResponseType(const String& responseType, ExceptionCode& ec)
    296 {
    297     if (m_state != OPENED || m_loader) {
    298         ec = INVALID_STATE_ERR;
    299         return;
    300     }
    301 
    302     if (responseType == "")
    303         m_responseTypeCode = ResponseTypeDefault;
    304     else if (responseType == "text")
    305         m_responseTypeCode = ResponseTypeText;
    306     else if (responseType == "document")
    307         m_responseTypeCode = ResponseTypeDocument;
    308     else if (responseType == "blob") {
    309 #if ENABLE(XHR_RESPONSE_BLOB)
    310         m_responseTypeCode = ResponseTypeBlob;
    311 #endif
    312     } else if (responseType == "arraybuffer") {
    313         m_responseTypeCode = ResponseTypeArrayBuffer;
    314     } else
    315         ec = SYNTAX_ERR;
    316 }
    317 
    318 String XMLHttpRequest::responseType()
    319 {
    320     switch (m_responseTypeCode) {
    321     case ResponseTypeDefault:
    322         return "";
    323     case ResponseTypeText:
    324         return "text";
    325     case ResponseTypeDocument:
    326         return "document";
    327     case ResponseTypeBlob:
    328         return "blob";
    329     case ResponseTypeArrayBuffer:
    330         return "arraybuffer";
    331     }
    332     return "";
    333 }
    334 
    335 XMLHttpRequestUpload* XMLHttpRequest::upload()
    336 {
    337     if (!m_upload)
    338         m_upload = XMLHttpRequestUpload::create(this);
    339     return m_upload.get();
    340 }
    341 
    342 void XMLHttpRequest::changeState(State newState)
    343 {
    344     if (m_state != newState) {
    345         m_state = newState;
    346         callReadyStateChangeListener();
    347     }
    348 }
    349 
    350 void XMLHttpRequest::callReadyStateChangeListener()
    351 {
    352     if (!scriptExecutionContext())
    353         return;
    354 
    355     InspectorInstrumentationCookie cookie = InspectorInstrumentation::willChangeXHRReadyState(scriptExecutionContext(), this);
    356 
    357     if (m_async || (m_state <= OPENED || m_state == DONE))
    358         m_progressEventThrottle.dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().readystatechangeEvent), m_state == DONE ? FlushProgressEvent : DoNotFlushProgressEvent);
    359 
    360     InspectorInstrumentation::didChangeXHRReadyState(cookie);
    361 
    362     if (m_state == DONE && !m_error) {
    363         InspectorInstrumentationCookie cookie = InspectorInstrumentation::willLoadXHR(scriptExecutionContext(), this);
    364         m_progressEventThrottle.dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().loadEvent));
    365         InspectorInstrumentation::didLoadXHR(cookie);
    366     }
    367 }
    368 
    369 void XMLHttpRequest::setWithCredentials(bool value, ExceptionCode& ec)
    370 {
    371     if (m_state != OPENED || m_loader) {
    372         ec = INVALID_STATE_ERR;
    373         return;
    374     }
    375 
    376     m_includeCredentials = value;
    377 }
    378 
    379 #if ENABLE(XHR_RESPONSE_BLOB)
    380 void XMLHttpRequest::setAsBlob(bool value, ExceptionCode& ec)
    381 {
    382     if (m_state != OPENED || m_loader) {
    383         ec = INVALID_STATE_ERR;
    384         return;
    385     }
    386 
    387     m_responseTypeCode = value ? ResponseTypeBlob : ResponseTypeDefault;
    388 }
    389 #endif
    390 
    391 void XMLHttpRequest::open(const String& method, const KURL& url, ExceptionCode& ec)
    392 {
    393     open(method, url, true, ec);
    394 }
    395 
    396 void XMLHttpRequest::open(const String& method, const KURL& url, bool async, ExceptionCode& ec)
    397 {
    398     internalAbort();
    399     State previousState = m_state;
    400     m_state = UNSENT;
    401     m_error = false;
    402     m_responseTypeCode = ResponseTypeDefault;
    403     m_uploadComplete = false;
    404 
    405     // clear stuff from possible previous load
    406     clearResponse();
    407     clearRequest();
    408 
    409     ASSERT(m_state == UNSENT);
    410 
    411     if (!isValidToken(method)) {
    412         ec = SYNTAX_ERR;
    413         return;
    414     }
    415 
    416     // Method names are case sensitive. But since Firefox uppercases method names it knows, we'll do the same.
    417     String methodUpper(method.upper());
    418 
    419     if (methodUpper == "TRACE" || methodUpper == "TRACK" || methodUpper == "CONNECT") {
    420         ec = SECURITY_ERR;
    421         return;
    422     }
    423 
    424     m_url = url;
    425 
    426     if (methodUpper == "COPY" || methodUpper == "DELETE" || methodUpper == "GET" || methodUpper == "HEAD"
    427         || methodUpper == "INDEX" || methodUpper == "LOCK" || methodUpper == "M-POST" || methodUpper == "MKCOL" || methodUpper == "MOVE"
    428         || methodUpper == "OPTIONS" || methodUpper == "POST" || methodUpper == "PROPFIND" || methodUpper == "PROPPATCH" || methodUpper == "PUT"
    429         || methodUpper == "UNLOCK")
    430         m_method = methodUpper;
    431     else
    432         m_method = method;
    433 
    434     m_async = async;
    435 
    436     ASSERT(!m_loader);
    437 
    438     // Check previous state to avoid dispatching readyState event
    439     // when calling open several times in a row.
    440     if (previousState != OPENED)
    441         changeState(OPENED);
    442     else
    443         m_state = OPENED;
    444 }
    445 
    446 void XMLHttpRequest::open(const String& method, const KURL& url, bool async, const String& user, ExceptionCode& ec)
    447 {
    448     KURL urlWithCredentials(url);
    449     urlWithCredentials.setUser(user);
    450 
    451     open(method, urlWithCredentials, async, ec);
    452 }
    453 
    454 void XMLHttpRequest::open(const String& method, const KURL& url, bool async, const String& user, const String& password, ExceptionCode& ec)
    455 {
    456     KURL urlWithCredentials(url);
    457     urlWithCredentials.setUser(user);
    458     urlWithCredentials.setPass(password);
    459 
    460     open(method, urlWithCredentials, async, ec);
    461 }
    462 
    463 bool XMLHttpRequest::initSend(ExceptionCode& ec)
    464 {
    465     if (!scriptExecutionContext())
    466         return false;
    467 
    468     if (m_state != OPENED || m_loader) {
    469         ec = INVALID_STATE_ERR;
    470         return false;
    471     }
    472 
    473     m_error = false;
    474     return true;
    475 }
    476 
    477 void XMLHttpRequest::send(ExceptionCode& ec)
    478 {
    479     send(String(), ec);
    480 }
    481 
    482 void XMLHttpRequest::send(Document* document, ExceptionCode& ec)
    483 {
    484     ASSERT(document);
    485 
    486     if (!initSend(ec))
    487         return;
    488 
    489     if (m_method != "GET" && m_method != "HEAD" && m_url.protocolInHTTPFamily()) {
    490         String contentType = getRequestHeader("Content-Type");
    491         if (contentType.isEmpty()) {
    492 #if ENABLE(DASHBOARD_SUPPORT)
    493             if (usesDashboardBackwardCompatibilityMode())
    494                 setRequestHeaderInternal("Content-Type", "application/x-www-form-urlencoded");
    495             else
    496 #endif
    497                 // FIXME: this should include the charset used for encoding.
    498                 setRequestHeaderInternal("Content-Type", "application/xml");
    499         }
    500 
    501         // FIXME: According to XMLHttpRequest Level 2, this should use the Document.innerHTML algorithm
    502         // from the HTML5 specification to serialize the document.
    503         String body = createMarkup(document);
    504 
    505         // FIXME: this should use value of document.inputEncoding to determine the encoding to use.
    506         TextEncoding encoding = UTF8Encoding();
    507         m_requestEntityBody = FormData::create(encoding.encode(body.characters(), body.length(), EntitiesForUnencodables));
    508         if (m_upload)
    509             m_requestEntityBody->setAlwaysStream(true);
    510     }
    511 
    512     createRequest(ec);
    513 }
    514 
    515 void XMLHttpRequest::send(const String& body, ExceptionCode& ec)
    516 {
    517     if (!initSend(ec))
    518         return;
    519 
    520     if (!body.isNull() && m_method != "GET" && m_method != "HEAD" && m_url.protocolInHTTPFamily()) {
    521         String contentType = getRequestHeader("Content-Type");
    522         if (contentType.isEmpty()) {
    523 #if ENABLE(DASHBOARD_SUPPORT)
    524             if (usesDashboardBackwardCompatibilityMode())
    525                 setRequestHeaderInternal("Content-Type", "application/x-www-form-urlencoded");
    526             else
    527 #endif
    528                 setRequestHeaderInternal("Content-Type", "application/xml");
    529         } else {
    530             replaceCharsetInMediaType(contentType, "UTF-8");
    531             m_requestHeaders.set("Content-Type", contentType);
    532         }
    533 
    534         m_requestEntityBody = FormData::create(UTF8Encoding().encode(body.characters(), body.length(), EntitiesForUnencodables));
    535         if (m_upload)
    536             m_requestEntityBody->setAlwaysStream(true);
    537     }
    538 
    539     createRequest(ec);
    540 }
    541 
    542 void XMLHttpRequest::send(Blob* body, ExceptionCode& ec)
    543 {
    544     if (!initSend(ec))
    545         return;
    546 
    547     if (m_method != "GET" && m_method != "HEAD" && m_url.protocolInHTTPFamily()) {
    548         // FIXME: Should we set a Content-Type if one is not set.
    549         // FIXME: add support for uploading bundles.
    550         m_requestEntityBody = FormData::create();
    551         if (body->isFile())
    552             m_requestEntityBody->appendFile(static_cast<File*>(body)->path());
    553 #if ENABLE(BLOB)
    554         else
    555             m_requestEntityBody->appendBlob(body->url());
    556 #endif
    557     }
    558 
    559     createRequest(ec);
    560 }
    561 
    562 void XMLHttpRequest::send(DOMFormData* body, ExceptionCode& ec)
    563 {
    564     if (!initSend(ec))
    565         return;
    566 
    567     if (m_method != "GET" && m_method != "HEAD" && m_url.protocolInHTTPFamily()) {
    568         m_requestEntityBody = FormData::createMultiPart(*(static_cast<FormDataList*>(body)), body->encoding(), document());
    569 
    570         // We need to ask the client to provide the generated file names if needed. When FormData fills the element
    571         // for the file, it could set a flag to use the generated file name, i.e. a package file on Mac.
    572         m_requestEntityBody->generateFiles(document());
    573 
    574         String contentType = getRequestHeader("Content-Type");
    575         if (contentType.isEmpty()) {
    576             contentType = "multipart/form-data; boundary=";
    577             contentType += m_requestEntityBody->boundary().data();
    578             setRequestHeaderInternal("Content-Type", contentType);
    579         }
    580     }
    581 
    582     createRequest(ec);
    583 }
    584 
    585 void XMLHttpRequest::send(ArrayBuffer* body, ExceptionCode& ec)
    586 {
    587     if (!initSend(ec))
    588         return;
    589 
    590     if (m_method != "GET" && m_method != "HEAD" && m_url.protocolInHTTPFamily()) {
    591         m_requestEntityBody = FormData::create(body->data(), body->byteLength());
    592         if (m_upload)
    593             m_requestEntityBody->setAlwaysStream(true);
    594     }
    595 
    596     createRequest(ec);
    597 }
    598 
    599 void XMLHttpRequest::createRequest(ExceptionCode& ec)
    600 {
    601 #if ENABLE(BLOB)
    602     // Only GET request is supported for blob URL.
    603     if (m_url.protocolIs("blob") && m_method != "GET") {
    604         ec = XMLHttpRequestException::NETWORK_ERR;
    605         return;
    606     }
    607 #endif
    608 
    609     // The presence of upload event listeners forces us to use preflighting because POSTing to an URL that does not
    610     // permit cross origin requests should look exactly like POSTing to an URL that does not respond at all.
    611     // Also, only async requests support upload progress events.
    612     bool uploadEvents = false;
    613     if (m_async) {
    614         m_progressEventThrottle.dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().loadstartEvent));
    615         if (m_requestEntityBody && m_upload) {
    616             uploadEvents = m_upload->hasEventListeners();
    617             m_upload->dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().loadstartEvent));
    618         }
    619     }
    620 
    621     m_sameOriginRequest = scriptExecutionContext()->securityOrigin()->canRequest(m_url);
    622 
    623     // We also remember whether upload events should be allowed for this request in case the upload listeners are
    624     // added after the request is started.
    625     m_uploadEventsAllowed = m_sameOriginRequest || uploadEvents || !isSimpleCrossOriginAccessRequest(m_method, m_requestHeaders);
    626 
    627     ResourceRequest request(m_url);
    628     request.setHTTPMethod(m_method);
    629 
    630     if (m_requestEntityBody) {
    631         ASSERT(m_method != "GET");
    632         ASSERT(m_method != "HEAD");
    633         request.setHTTPBody(m_requestEntityBody.release());
    634     }
    635 
    636     if (m_requestHeaders.size() > 0)
    637         request.addHTTPHeaderFields(m_requestHeaders);
    638 
    639     ThreadableLoaderOptions options;
    640     options.sendLoadCallbacks = true;
    641     options.sniffContent = false;
    642     options.forcePreflight = uploadEvents;
    643     options.allowCredentials = m_sameOriginRequest || m_includeCredentials;
    644     options.crossOriginRequestPolicy = UseAccessControl;
    645 
    646     m_exceptionCode = 0;
    647     m_error = false;
    648 
    649     if (m_async) {
    650         if (m_upload)
    651             request.setReportUploadProgress(true);
    652 
    653         // ThreadableLoader::create can return null here, for example if we're no longer attached to a page.
    654         // This is true while running onunload handlers.
    655         // FIXME: Maybe we need to be able to send XMLHttpRequests from onunload, <http://bugs.webkit.org/show_bug.cgi?id=10904>.
    656         // FIXME: Maybe create() can return null for other reasons too?
    657         m_loader = ThreadableLoader::create(scriptExecutionContext(), this, request, options);
    658         if (m_loader) {
    659             // Neither this object nor the JavaScript wrapper should be deleted while
    660             // a request is in progress because we need to keep the listeners alive,
    661             // and they are referenced by the JavaScript wrapper.
    662             setPendingActivity(this);
    663         }
    664     } else
    665         ThreadableLoader::loadResourceSynchronously(scriptExecutionContext(), request, *this, options);
    666 
    667     if (!m_exceptionCode && m_error)
    668         m_exceptionCode = XMLHttpRequestException::NETWORK_ERR;
    669     ec = m_exceptionCode;
    670 }
    671 
    672 void XMLHttpRequest::abort()
    673 {
    674     // internalAbort() calls dropProtection(), which may release the last reference.
    675     RefPtr<XMLHttpRequest> protect(this);
    676 
    677     bool sendFlag = m_loader;
    678 
    679     internalAbort();
    680 
    681     clearResponseBuffers();
    682 
    683     // Clear headers as required by the spec
    684     m_requestHeaders.clear();
    685 
    686     if ((m_state <= OPENED && !sendFlag) || m_state == DONE)
    687         m_state = UNSENT;
    688     else {
    689         ASSERT(!m_loader);
    690         changeState(DONE);
    691         m_state = UNSENT;
    692     }
    693 
    694     m_progressEventThrottle.dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().abortEvent));
    695     if (!m_uploadComplete) {
    696         m_uploadComplete = true;
    697         if (m_upload && m_uploadEventsAllowed)
    698             m_upload->dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().abortEvent));
    699     }
    700 }
    701 
    702 void XMLHttpRequest::internalAbort()
    703 {
    704     bool hadLoader = m_loader;
    705 
    706     m_error = true;
    707 
    708     // FIXME: when we add the support for multi-part XHR, we will have to think be careful with this initialization.
    709     m_receivedLength = 0;
    710 
    711     if (hadLoader) {
    712         m_loader->cancel();
    713         m_loader = 0;
    714     }
    715 
    716     m_decoder = 0;
    717 
    718     if (hadLoader)
    719         dropProtection();
    720 }
    721 
    722 void XMLHttpRequest::clearResponse()
    723 {
    724     m_response = ResourceResponse();
    725     clearResponseBuffers();
    726 }
    727 
    728 void XMLHttpRequest::clearResponseBuffers()
    729 {
    730     m_responseBuilder.clear();
    731     m_createdDocument = false;
    732     m_responseXML = 0;
    733 #if ENABLE(XHR_RESPONSE_BLOB)
    734     m_responseBlob = 0;
    735 #endif
    736     m_binaryResponseBuilder.clear();
    737     m_responseArrayBuffer.clear();
    738 }
    739 
    740 void XMLHttpRequest::clearRequest()
    741 {
    742     m_requestHeaders.clear();
    743     m_requestEntityBody = 0;
    744 }
    745 
    746 void XMLHttpRequest::genericError()
    747 {
    748     clearResponse();
    749     clearRequest();
    750     m_error = true;
    751 
    752     changeState(DONE);
    753 }
    754 
    755 void XMLHttpRequest::networkError()
    756 {
    757     genericError();
    758     m_progressEventThrottle.dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().errorEvent));
    759     if (!m_uploadComplete) {
    760         m_uploadComplete = true;
    761         if (m_upload && m_uploadEventsAllowed)
    762             m_upload->dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().errorEvent));
    763     }
    764     internalAbort();
    765 }
    766 
    767 void XMLHttpRequest::abortError()
    768 {
    769     genericError();
    770     m_progressEventThrottle.dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().abortEvent));
    771     if (!m_uploadComplete) {
    772         m_uploadComplete = true;
    773         if (m_upload && m_uploadEventsAllowed)
    774             m_upload->dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().abortEvent));
    775     }
    776 }
    777 
    778 void XMLHttpRequest::dropProtection()
    779 {
    780 #if USE(JSC)
    781     // The XHR object itself holds on to the responseText, and
    782     // thus has extra cost even independent of any
    783     // responseText or responseXML objects it has handed
    784     // out. But it is protected from GC while loading, so this
    785     // can't be recouped until the load is done, so only
    786     // report the extra cost at that point.
    787     JSC::JSLock lock(JSC::SilenceAssertionsOnly);
    788     JSC::JSGlobalData* globalData = scriptExecutionContext()->globalData();
    789     globalData->heap.reportExtraMemoryCost(m_responseBuilder.length() * 2);
    790 #endif
    791 
    792     unsetPendingActivity(this);
    793 }
    794 
    795 void XMLHttpRequest::overrideMimeType(const String& override)
    796 {
    797     m_mimeTypeOverride = override;
    798 }
    799 
    800 static void reportUnsafeUsage(ScriptExecutionContext* context, const String& message)
    801 {
    802     if (!context)
    803         return;
    804     // FIXME: It's not good to report the bad usage without indicating what source line it came from.
    805     // We should pass additional parameters so we can tell the console where the mistake occurred.
    806     context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, message, 1, String(), 0);
    807 }
    808 
    809 void XMLHttpRequest::setRequestHeader(const AtomicString& name, const String& value, ExceptionCode& ec)
    810 {
    811     if (m_state != OPENED || m_loader) {
    812 #if ENABLE(DASHBOARD_SUPPORT)
    813         if (usesDashboardBackwardCompatibilityMode())
    814             return;
    815 #endif
    816 
    817         ec = INVALID_STATE_ERR;
    818         return;
    819     }
    820 
    821     if (!isValidToken(name) || !isValidHeaderValue(value)) {
    822         ec = SYNTAX_ERR;
    823         return;
    824     }
    825 
    826     // A privileged script (e.g. a Dashboard widget) can set any headers.
    827     if (!scriptExecutionContext()->securityOrigin()->canLoadLocalResources() && !isSafeRequestHeader(name)) {
    828         reportUnsafeUsage(scriptExecutionContext(), "Refused to set unsafe header \"" + name + "\"");
    829         return;
    830     }
    831 
    832     setRequestHeaderInternal(name, value);
    833 }
    834 
    835 void XMLHttpRequest::setRequestHeaderInternal(const AtomicString& name, const String& value)
    836 {
    837     pair<HTTPHeaderMap::iterator, bool> result = m_requestHeaders.add(name, value);
    838     if (!result.second)
    839         result.first->second += ", " + value;
    840 }
    841 
    842 bool XMLHttpRequest::isSafeRequestHeader(const String& name) const
    843 {
    844     return !staticData->m_forbiddenRequestHeaders.contains(name) && !name.startsWith(staticData->m_proxyHeaderPrefix, false)
    845         && !name.startsWith(staticData->m_secHeaderPrefix, false);
    846 }
    847 
    848 String XMLHttpRequest::getRequestHeader(const AtomicString& name) const
    849 {
    850     return m_requestHeaders.get(name);
    851 }
    852 
    853 String XMLHttpRequest::getAllResponseHeaders(ExceptionCode& ec) const
    854 {
    855     if (m_state < HEADERS_RECEIVED) {
    856         ec = INVALID_STATE_ERR;
    857         return "";
    858     }
    859 
    860     Vector<UChar> stringBuilder;
    861 
    862     HTTPHeaderMap::const_iterator end = m_response.httpHeaderFields().end();
    863     for (HTTPHeaderMap::const_iterator it = m_response.httpHeaderFields().begin(); it!= end; ++it) {
    864         // Hide Set-Cookie header fields from the XMLHttpRequest client for these reasons:
    865         //     1) If the client did have access to the fields, then it could read HTTP-only
    866         //        cookies; those cookies are supposed to be hidden from scripts.
    867         //     2) There's no known harm in hiding Set-Cookie header fields entirely; we don't
    868         //        know any widely used technique that requires access to them.
    869         //     3) Firefox has implemented this policy.
    870         if (isSetCookieHeader(it->first) && !scriptExecutionContext()->securityOrigin()->canLoadLocalResources())
    871             continue;
    872 
    873         if (!m_sameOriginRequest && !isOnAccessControlResponseHeaderWhitelist(it->first))
    874             continue;
    875 
    876         stringBuilder.append(it->first.characters(), it->first.length());
    877         stringBuilder.append(':');
    878         stringBuilder.append(' ');
    879         stringBuilder.append(it->second.characters(), it->second.length());
    880         stringBuilder.append('\r');
    881         stringBuilder.append('\n');
    882     }
    883 
    884     return String::adopt(stringBuilder);
    885 }
    886 
    887 String XMLHttpRequest::getResponseHeader(const AtomicString& name, ExceptionCode& ec) const
    888 {
    889     if (m_state < HEADERS_RECEIVED) {
    890         ec = INVALID_STATE_ERR;
    891         return String();
    892     }
    893 
    894     // See comment in getAllResponseHeaders above.
    895     if (isSetCookieHeader(name) && !scriptExecutionContext()->securityOrigin()->canLoadLocalResources()) {
    896         reportUnsafeUsage(scriptExecutionContext(), "Refused to get unsafe header \"" + name + "\"");
    897         return String();
    898     }
    899 
    900     if (!m_sameOriginRequest && !isOnAccessControlResponseHeaderWhitelist(name)) {
    901         reportUnsafeUsage(scriptExecutionContext(), "Refused to get unsafe header \"" + name + "\"");
    902         return String();
    903     }
    904     return m_response.httpHeaderField(name);
    905 }
    906 
    907 String XMLHttpRequest::responseMIMEType() const
    908 {
    909     String mimeType = extractMIMETypeFromMediaType(m_mimeTypeOverride);
    910     if (mimeType.isEmpty()) {
    911         if (m_response.isHTTP())
    912             mimeType = extractMIMETypeFromMediaType(m_response.httpHeaderField("Content-Type"));
    913         else
    914             mimeType = m_response.mimeType();
    915     }
    916     if (mimeType.isEmpty())
    917         mimeType = "text/xml";
    918 
    919     return mimeType;
    920 }
    921 
    922 bool XMLHttpRequest::responseIsXML() const
    923 {
    924     return DOMImplementation::isXMLMIMEType(responseMIMEType());
    925 }
    926 
    927 int XMLHttpRequest::status(ExceptionCode& ec) const
    928 {
    929     if (m_response.httpStatusCode())
    930         return m_response.httpStatusCode();
    931 
    932     if (m_state == OPENED) {
    933         // Firefox only raises an exception in this state; we match it.
    934         // 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.
    935         ec = INVALID_STATE_ERR;
    936     }
    937 
    938     return 0;
    939 }
    940 
    941 String XMLHttpRequest::statusText(ExceptionCode& ec) const
    942 {
    943     if (!m_response.httpStatusText().isNull())
    944         return m_response.httpStatusText();
    945 
    946     if (m_state == OPENED) {
    947         // See comments in status() above.
    948         ec = INVALID_STATE_ERR;
    949     }
    950 
    951     return String();
    952 }
    953 
    954 void XMLHttpRequest::didFail(const ResourceError& error)
    955 {
    956 
    957     // If we are already in an error state, for instance we called abort(), bail out early.
    958     if (m_error)
    959         return;
    960 
    961     if (error.isCancellation()) {
    962         m_exceptionCode = XMLHttpRequestException::ABORT_ERR;
    963         abortError();
    964         return;
    965     }
    966 
    967     // Network failures are already reported to Web Inspector by ResourceLoader.
    968     if (error.domain() == errorDomainWebKitInternal)
    969         reportUnsafeUsage(scriptExecutionContext(), "XMLHttpRequest cannot load " + error.failingURL() + ". " + error.localizedDescription());
    970 
    971     m_exceptionCode = XMLHttpRequestException::NETWORK_ERR;
    972     networkError();
    973 }
    974 
    975 void XMLHttpRequest::didFailRedirectCheck()
    976 {
    977     networkError();
    978 }
    979 
    980 void XMLHttpRequest::didFinishLoading(unsigned long identifier, double)
    981 {
    982     if (m_error)
    983         return;
    984 
    985     if (m_state < HEADERS_RECEIVED)
    986         changeState(HEADERS_RECEIVED);
    987 
    988     if (m_decoder)
    989         m_responseBuilder.append(m_decoder->flush());
    990 
    991     m_responseBuilder.shrinkToFit();
    992 
    993 #if ENABLE(XHR_RESPONSE_BLOB)
    994     // FIXME: Set m_responseBlob to something here in the ResponseTypeBlob case.
    995 #endif
    996 
    997     InspectorInstrumentation::resourceRetrievedByXMLHttpRequest(scriptExecutionContext(), identifier, m_responseBuilder.toStringPreserveCapacity(), m_url, m_lastSendURL, m_lastSendLineNumber);
    998 
    999     bool hadLoader = m_loader;
   1000     m_loader = 0;
   1001 
   1002     changeState(DONE);
   1003     m_decoder = 0;
   1004 
   1005     if (hadLoader)
   1006         dropProtection();
   1007 }
   1008 
   1009 void XMLHttpRequest::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
   1010 {
   1011     if (!m_upload)
   1012         return;
   1013 
   1014     if (m_uploadEventsAllowed)
   1015         m_upload->dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().progressEvent, true, bytesSent, totalBytesToBeSent));
   1016 
   1017     if (bytesSent == totalBytesToBeSent && !m_uploadComplete) {
   1018         m_uploadComplete = true;
   1019         if (m_uploadEventsAllowed)
   1020             m_upload->dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().loadEvent));
   1021     }
   1022 }
   1023 
   1024 void XMLHttpRequest::didReceiveResponse(const ResourceResponse& response)
   1025 {
   1026     m_response = response;
   1027     m_responseEncoding = extractCharsetFromMediaType(m_mimeTypeOverride);
   1028     if (m_responseEncoding.isEmpty())
   1029         m_responseEncoding = response.textEncodingName();
   1030 }
   1031 
   1032 void XMLHttpRequest::didReceiveAuthenticationCancellation(const ResourceResponse& failureResponse)
   1033 {
   1034     m_response = failureResponse;
   1035 }
   1036 
   1037 void XMLHttpRequest::didReceiveData(const char* data, int len)
   1038 {
   1039     if (m_error)
   1040         return;
   1041 
   1042     if (m_state < HEADERS_RECEIVED)
   1043         changeState(HEADERS_RECEIVED);
   1044 
   1045     bool useDecoder = responseTypeCode() == ResponseTypeDefault || responseTypeCode() == ResponseTypeText || responseTypeCode() == ResponseTypeDocument;
   1046 
   1047     if (useDecoder && !m_decoder) {
   1048         if (!m_responseEncoding.isEmpty())
   1049             m_decoder = TextResourceDecoder::create("text/plain", m_responseEncoding);
   1050         // allow TextResourceDecoder to look inside the m_response if it's XML or HTML
   1051         else if (responseIsXML()) {
   1052             m_decoder = TextResourceDecoder::create("application/xml");
   1053             // 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.
   1054             m_decoder->useLenientXMLDecoding();
   1055         } else if (responseMIMEType() == "text/html")
   1056             m_decoder = TextResourceDecoder::create("text/html", "UTF-8");
   1057         else
   1058             m_decoder = TextResourceDecoder::create("text/plain", "UTF-8");
   1059     }
   1060 
   1061     if (!len)
   1062         return;
   1063 
   1064     if (len == -1)
   1065         len = strlen(data);
   1066 
   1067     if (useDecoder)
   1068         m_responseBuilder.append(m_decoder->decode(data, len));
   1069     else if (responseTypeCode() == ResponseTypeArrayBuffer) {
   1070         // Buffer binary data.
   1071         if (!m_binaryResponseBuilder)
   1072             m_binaryResponseBuilder = SharedBuffer::create();
   1073         m_binaryResponseBuilder->append(data, len);
   1074     }
   1075 
   1076     if (!m_error) {
   1077         long long expectedLength = m_response.expectedContentLength();
   1078         m_receivedLength += len;
   1079 
   1080         if (m_async) {
   1081             bool lengthComputable = expectedLength && m_receivedLength <= expectedLength;
   1082             m_progressEventThrottle.dispatchProgressEvent(lengthComputable, m_receivedLength, expectedLength);
   1083         }
   1084 
   1085         if (m_state != LOADING)
   1086             changeState(LOADING);
   1087         else
   1088             // Firefox calls readyStateChanged every time it receives data, 4449442
   1089             callReadyStateChangeListener();
   1090     }
   1091 }
   1092 
   1093 bool XMLHttpRequest::canSuspend() const
   1094 {
   1095     return !m_loader;
   1096 }
   1097 
   1098 void XMLHttpRequest::suspend(ReasonForSuspension)
   1099 {
   1100     m_progressEventThrottle.suspend();
   1101 }
   1102 
   1103 void XMLHttpRequest::resume()
   1104 {
   1105     m_progressEventThrottle.resume();
   1106 }
   1107 
   1108 void XMLHttpRequest::stop()
   1109 {
   1110     internalAbort();
   1111 }
   1112 
   1113 void XMLHttpRequest::contextDestroyed()
   1114 {
   1115     ASSERT(!m_loader);
   1116     ActiveDOMObject::contextDestroyed();
   1117 }
   1118 
   1119 ScriptExecutionContext* XMLHttpRequest::scriptExecutionContext() const
   1120 {
   1121     return ActiveDOMObject::scriptExecutionContext();
   1122 }
   1123 
   1124 EventTargetData* XMLHttpRequest::eventTargetData()
   1125 {
   1126     return &m_eventTargetData;
   1127 }
   1128 
   1129 EventTargetData* XMLHttpRequest::ensureEventTargetData()
   1130 {
   1131     return &m_eventTargetData;
   1132 }
   1133 
   1134 } // namespace WebCore
   1135