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