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