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