bac7a2d33cb50693dd63e762ecc470a0236586ee
[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  *
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU Lesser General Public
8  *  License as published by the Free Software Foundation; either
9  *  version 2 of the License, or (at your option) any later version.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Lesser General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Lesser General Public
17  *  License along with this library; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  */
20
21 #include "config.h"
22 #include "XMLHttpRequest.h"
23
24 #include "CString.h"
25 #include "Console.h"
26 #include "DOMImplementation.h"
27 #include "DOMWindow.h"
28 #include "Event.h"
29 #include "EventException.h"
30 #include "EventListener.h"
31 #include "EventNames.h"
32 #include "Frame.h"
33 #include "FrameLoader.h"
34 #include "HTTPParsers.h"
35 #include "InspectorController.h"
36 #include "Page.h"
37 #include "Settings.h"
38 #include "SubresourceLoader.h"
39 #include "TextResourceDecoder.h"
40 #include "XMLHttpRequestException.h"
41 #include "XMLHttpRequestProgressEvent.h"
42 #include "kjs_binding.h"
43
44 namespace WebCore {
45
46 using namespace EventNames;
47
48 typedef HashSet<XMLHttpRequest*> RequestsSet;
49
50 static HashMap<Document*, RequestsSet*>& requestsByDocument()
51 {
52     static HashMap<Document*, RequestsSet*> map;
53     return map;
54 }
55
56 static void addToRequestsByDocument(Document* doc, XMLHttpRequest* req)
57 {
58     ASSERT(doc);
59     ASSERT(req);
60
61     RequestsSet* requests = requestsByDocument().get(doc);
62     if (!requests) {
63         requests = new RequestsSet;
64         requestsByDocument().set(doc, requests);
65     }
66
67     ASSERT(!requests->contains(req));
68     requests->add(req);
69 }
70
71 static void removeFromRequestsByDocument(Document* doc, XMLHttpRequest* req)
72 {
73     ASSERT(doc);
74     ASSERT(req);
75
76     RequestsSet* requests = requestsByDocument().get(doc);
77     ASSERT(requests);
78     ASSERT(requests->contains(req));
79     requests->remove(req);
80     if (requests->isEmpty()) {
81         requestsByDocument().remove(doc);
82         delete requests;
83     }
84 }
85
86 static bool isSafeRequestHeader(const String& name)
87 {
88     static HashSet<String, CaseFoldingHash> forbiddenHeaders;
89     static String proxyString("proxy-");
90     static String secString("sec-");
91     
92     if (forbiddenHeaders.isEmpty()) {
93         forbiddenHeaders.add("accept-charset");
94         forbiddenHeaders.add("accept-encoding");
95         forbiddenHeaders.add("connection");
96         forbiddenHeaders.add("content-length");
97         forbiddenHeaders.add("content-transfer-encoding");
98         forbiddenHeaders.add("date");
99         forbiddenHeaders.add("expect");
100         forbiddenHeaders.add("host");
101         forbiddenHeaders.add("keep-alive");
102         forbiddenHeaders.add("referer");
103         forbiddenHeaders.add("te");
104         forbiddenHeaders.add("trailer");
105         forbiddenHeaders.add("transfer-encoding");
106         forbiddenHeaders.add("upgrade");
107         forbiddenHeaders.add("via");
108     }
109     
110     return !forbiddenHeaders.contains(name) && !name.startsWith(proxyString, false) &&
111            !name.startsWith(secString, false);
112 }
113
114 // Determines if a string is a valid token, as defined by
115 // "token" in section 2.2 of RFC 2616.
116 static bool isValidToken(const String& name)
117 {
118     unsigned length = name.length();
119     for (unsigned i = 0; i < length; i++) {
120         UChar c = name[i];
121         
122         if (c >= 127 || c <= 32)
123             return false;
124         
125         if (c == '(' || c == ')' || c == '<' || c == '>' || c == '@' ||
126             c == ',' || c == ';' || c == ':' || c == '\\' || c == '\"' ||
127             c == '/' || c == '[' || c == ']' || c == '?' || c == '=' ||
128             c == '{' || c == '}')
129             return false;
130     }
131     
132     return true;
133 }
134     
135 static bool isValidHeaderValue(const String& name)
136 {
137     // FIXME: This should really match name against 
138     // field-value in section 4.2 of RFC 2616.
139         
140     return !name.contains('\r') && !name.contains('\n');
141 }
142     
143 XMLHttpRequestState XMLHttpRequest::readyState() const
144 {
145     return m_state;
146 }
147
148 const KJS::UString& XMLHttpRequest::responseText() const
149 {
150     return m_responseText;
151 }
152
153 Document* XMLHttpRequest::responseXML() const
154 {
155     if (m_state != Loaded)
156         return 0;
157
158     if (!m_createdDocument) {
159         if (m_response.isHTTP() && !responseIsXML()) {
160             // The W3C spec requires this.
161             m_responseXML = 0;
162         } else {
163             m_responseXML = m_doc->implementation()->createDocument(0);
164             m_responseXML->open();
165             m_responseXML->setURL(m_url);
166             // FIXME: set Last-Modified and cookies (currently, those are only available for HTMLDocuments).
167             m_responseXML->write(String(m_responseText));
168             m_responseXML->finishParsing();
169             m_responseXML->close();
170             
171             if (!m_responseXML->wellFormed())
172                 m_responseXML = 0;
173         }
174         m_createdDocument = true;
175     }
176
177     return m_responseXML.get();
178 }
179
180 EventListener* XMLHttpRequest::onReadyStateChangeListener() const
181 {
182     return m_onReadyStateChangeListener.get();
183 }
184
185 void XMLHttpRequest::setOnReadyStateChangeListener(EventListener* eventListener)
186 {
187     m_onReadyStateChangeListener = eventListener;
188 }
189
190 EventListener* XMLHttpRequest::onLoadListener() const
191 {
192     return m_onLoadListener.get();
193 }
194
195 EventListener* XMLHttpRequest::onProgressListener() const
196 {
197     return m_onProgressListener.get();
198 }
199
200 void XMLHttpRequest::setOnLoadListener(EventListener* eventListener)
201 {
202     m_onLoadListener = eventListener;
203 }
204
205 void XMLHttpRequest::setOnProgressListener(EventListener* eventListener)
206 {
207     m_onProgressListener = eventListener;
208 }
209
210 void XMLHttpRequest::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> eventListener, bool)
211 {
212     EventListenersMap::iterator iter = m_eventListeners.find(eventType.impl());
213     if (iter == m_eventListeners.end()) {
214         ListenerVector listeners;
215         listeners.append(eventListener);
216         m_eventListeners.add(eventType.impl(), listeners);
217     } else {
218         ListenerVector& listeners = iter->second;
219         for (ListenerVector::iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter)
220             if (*listenerIter == eventListener)
221                 return;
222         
223         listeners.append(eventListener);
224         m_eventListeners.add(eventType.impl(), listeners);
225     }
226 }
227
228 void XMLHttpRequest::removeEventListener(const AtomicString& eventType, EventListener* eventListener, bool)
229 {
230     EventListenersMap::iterator iter = m_eventListeners.find(eventType.impl());
231     if (iter == m_eventListeners.end())
232         return;
233
234     ListenerVector& listeners = iter->second;
235     for (ListenerVector::const_iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter)
236         if (*listenerIter == eventListener) {
237             listeners.remove(listenerIter - listeners.begin());
238             return;
239         }
240 }
241
242 bool XMLHttpRequest::dispatchEvent(PassRefPtr<Event> evt, ExceptionCode& ec, bool /*tempEvent*/)
243 {
244     // FIXME: check for other error conditions enumerated in the spec.
245     if (evt->type().isEmpty()) {
246         ec = EventException::UNSPECIFIED_EVENT_TYPE_ERR;
247         return true;
248     }
249
250     ListenerVector listenersCopy = m_eventListeners.get(evt->type().impl());
251     for (ListenerVector::const_iterator listenerIter = listenersCopy.begin(); listenerIter != listenersCopy.end(); ++listenerIter) {
252         evt->setTarget(this);
253         evt->setCurrentTarget(this);
254         listenerIter->get()->handleEvent(evt.get(), false);
255     }
256
257     return !evt->defaultPrevented();
258 }
259
260 XMLHttpRequest::XMLHttpRequest(Document* d)
261     : m_doc(d)
262     , m_async(true)
263     , m_state(Uninitialized)
264     , m_identifier(-1)
265     , m_responseText("")
266     , m_createdDocument(false)
267     , m_aborted(false)
268     , m_receivedLength(0)
269 {
270     ASSERT(m_doc);
271     addToRequestsByDocument(m_doc, this);
272 }
273
274 XMLHttpRequest::~XMLHttpRequest()
275 {
276     if (m_doc)
277         removeFromRequestsByDocument(m_doc, this);
278 }
279
280 void XMLHttpRequest::changeState(XMLHttpRequestState newState)
281 {
282     if (m_state != newState) {
283         m_state = newState;
284         callReadyStateChangeListener();
285     }
286 }
287
288 void XMLHttpRequest::callReadyStateChangeListener()
289 {
290     if (!m_doc || !m_doc->frame())
291         return;
292
293     RefPtr<Event> evt = new Event(readystatechangeEvent, false, false);
294     if (m_onReadyStateChangeListener) {
295         evt->setTarget(this);
296         evt->setCurrentTarget(this);
297         m_onReadyStateChangeListener->handleEvent(evt.get(), false);
298     }
299
300     ExceptionCode ec = 0;
301     dispatchEvent(evt.release(), ec, false);
302     ASSERT(!ec);
303     
304     if (m_state == Loaded) {
305         evt = new Event(loadEvent, false, false);
306         if (m_onLoadListener) {
307             evt->setTarget(this);
308             evt->setCurrentTarget(this);
309             m_onLoadListener->handleEvent(evt.get(), false);
310         }
311         
312         dispatchEvent(evt, ec, false);
313         ASSERT(!ec);
314     }
315 }
316
317 bool XMLHttpRequest::urlMatchesDocumentDomain(const KURL& url) const
318 {
319     // a local file can load anything
320     if (m_doc->isAllowedToLoadLocalResources())
321         return true;
322
323     // but a remote document can only load from the same port on the server
324     KURL documentURL(m_doc->url());
325     if (documentURL.protocol().lower() == url.protocol().lower()
326             && documentURL.host().lower() == url.host().lower()
327             && documentURL.port() == url.port())
328         return true;
329
330     return false;
331 }
332
333 void XMLHttpRequest::open(const String& method, const KURL& url, bool async, ExceptionCode& ec)
334 {
335     internalAbort();
336     XMLHttpRequestState previousState = m_state;
337     m_state = Uninitialized;
338     m_aborted = false;
339
340     // clear stuff from possible previous load
341     m_requestHeaders.clear();
342     m_response = ResourceResponse();
343     {
344         KJS::JSLock lock;
345         m_responseText = "";
346     }
347     m_createdDocument = false;
348     m_responseXML = 0;
349
350     ASSERT(m_state == Uninitialized);
351
352     if (!urlMatchesDocumentDomain(url)) {
353         ec = SECURITY_ERR;
354         return;
355     }
356
357     if (!isValidToken(method)) {
358         ec = SYNTAX_ERR;
359         return;
360     }
361     
362     // Method names are case sensitive. But since Firefox uppercases method names it knows, we'll do the same.
363     String methodUpper(method.upper());
364     
365     if (methodUpper == "TRACE" || methodUpper == "TRACK" || methodUpper == "CONNECT") {
366         ec = SECURITY_ERR;
367         return;
368     }
369
370     m_url = url;
371
372     if (methodUpper == "COPY" || methodUpper == "DELETE" || methodUpper == "GET" || methodUpper == "HEAD"
373         || methodUpper == "INDEX" || methodUpper == "LOCK" || methodUpper == "M-POST" || methodUpper == "MKCOL" || methodUpper == "MOVE"
374         || methodUpper == "OPTIONS" || methodUpper == "POST" || methodUpper == "PROPFIND" || methodUpper == "PROPPATCH" || methodUpper == "PUT" 
375         || methodUpper == "UNLOCK")
376         m_method = methodUpper;
377     else
378         m_method = method;
379
380     m_async = async;
381
382     ASSERT(!m_loader);
383
384     // Check previous state to avoid dispatching readyState event
385     // when calling open several times in a row.
386     if (previousState != Open)
387         changeState(Open);
388     else
389         m_state = Open;
390 }
391
392 void XMLHttpRequest::open(const String& method, const KURL& url, bool async, const String& user, ExceptionCode& ec)
393 {
394     KURL urlWithCredentials(url);
395     urlWithCredentials.setUser(user);
396     
397     open(method, urlWithCredentials, async, ec);
398 }
399
400 void XMLHttpRequest::open(const String& method, const KURL& url, bool async, const String& user, const String& password, ExceptionCode& ec)
401 {
402     KURL urlWithCredentials(url);
403     urlWithCredentials.setUser(user);
404     urlWithCredentials.setPass(password);
405     
406     open(method, urlWithCredentials, async, ec);
407 }
408
409 void XMLHttpRequest::send(const String& body, ExceptionCode& ec)
410 {
411     if (!m_doc)
412         return;
413
414     if (m_state != Open || m_loader) {
415         ec = INVALID_STATE_ERR;
416         return;
417     }
418
419     m_aborted = false;
420
421     ResourceRequest request(m_url);
422     request.setHTTPMethod(m_method);
423
424     if (!body.isNull() && m_method != "GET" && m_method != "HEAD" && (m_url.protocol().lower() == "http" || m_url.protocol().lower() == "https")) {
425         String contentType = getRequestHeader("Content-Type");
426         if (contentType.isEmpty()) {
427             ExceptionCode ec = 0;
428 #if ENABLE(DASHBOARD_SUPPORT)
429             Settings* settings = m_doc->settings();
430             if (settings && settings->usesDashboardBackwardCompatibilityMode())
431                 setRequestHeader("Content-Type", "application/x-www-form-urlencoded", ec);
432             else
433 #endif
434                 setRequestHeader("Content-Type", "application/xml", ec);
435             ASSERT(ec == 0);
436         }
437
438         // FIXME: must use xmlEncoding for documents.
439         String charset = "UTF-8";
440       
441         TextEncoding encoding(charset);
442         if (!encoding.isValid()) // FIXME: report an error?
443             encoding = UTF8Encoding();
444         request.setHTTPBody(FormData::create(encoding.encode(body.characters(), body.length(), EntitiesForUnencodables)));
445     }
446
447     if (m_requestHeaders.size() > 0)
448         request.addHTTPHeaderFields(m_requestHeaders);
449
450     if (!m_async) {
451         Vector<char> data;
452         ResourceError error;
453         ResourceResponse response;
454
455         {
456             // avoid deadlock in case the loader wants to use JS on a background thread
457             KJS::JSLock::DropAllLocks dropLocks;
458             if (m_doc->frame())
459                 m_identifier = m_doc->frame()->loader()->loadResourceSynchronously(request, error, response, data);
460         }
461
462         m_loader = 0;
463         
464         // No exception for file:/// resources, see <rdar://problem/4962298>.
465         // Also, if we have an HTTP response, then it wasn't a network error in fact.
466         if (error.isNull() || request.url().isLocalFile() || response.httpStatusCode() > 0)
467             processSyncLoadResults(data, response);
468         else
469             ec = XMLHttpRequestException::NETWORK_ERR;
470
471         return;
472     }
473
474     // SubresourceLoader::create can return null here, for example if we're no longer attached to a page.
475     // This is true while running onunload handlers.
476     // FIXME: We need to be able to send XMLHttpRequests from onunload, <http://bugs.webkit.org/show_bug.cgi?id=10904>.
477     // FIXME: Maybe create can return null for other reasons too?
478     // We need to keep content sniffing enabled for local files due to CFNetwork not providing a MIME type
479     // for local files otherwise, <rdar://problem/5671813>.
480     m_loader = SubresourceLoader::create(m_doc->frame(), this, request, false, true, request.url().isLocalFile());
481
482     if (m_loader) {
483         // Neither this object nor the JavaScript wrapper should be deleted while
484         // a request is in progress because we need to keep the listeners alive,
485         // and they are referenced by the JavaScript wrapper.
486         ref();
487
488         KJS::JSLock lock;
489         gcProtectNullTolerant(ScriptInterpreter::getDOMObject(this));
490     }
491 }
492
493 void XMLHttpRequest::abort()
494 {
495     bool sendFlag = m_loader;
496
497     internalAbort();
498
499     // Clear headers as required by the spec
500     m_requestHeaders.clear();
501
502     if ((m_state <= Open && !sendFlag) || m_state == Loaded)
503         m_state = Uninitialized;
504      else {
505         ASSERT(!m_loader);
506         changeState(Loaded);
507         m_state = Uninitialized;
508     }
509 }
510
511 void XMLHttpRequest::internalAbort()
512 {
513     bool hadLoader = m_loader;
514
515     m_aborted = true;
516
517     // FIXME: when we add the support for multi-part XHR, we will have to think be careful with this initialization.
518     m_receivedLength = 0;
519
520     if (hadLoader) {
521         m_loader->cancel();
522         m_loader = 0;
523     }
524
525     m_decoder = 0;
526
527     if (hadLoader)
528         dropProtection();
529 }
530
531 void XMLHttpRequest::dropProtection()        
532 {
533     {
534         KJS::JSLock lock;
535         KJS::JSValue* wrapper = ScriptInterpreter::getDOMObject(this);
536         KJS::gcUnprotectNullTolerant(wrapper);
537     
538         // the XHR object itself holds on to the responseText, and
539         // thus has extra cost even independent of any
540         // responseText or responseXML objects it has handed
541         // out. But it is protected from GC while loading, so this
542         // can't be recouped until the load is done, so only
543         // report the extra cost at that point.
544     
545         if (wrapper)
546             KJS::Heap::threadHeap()->reportExtraMemoryCost(m_responseText.size() * 2);
547     }
548
549     deref();
550 }
551
552 void XMLHttpRequest::overrideMimeType(const String& override)
553 {
554     m_mimeTypeOverride = override;
555 }
556     
557 void XMLHttpRequest::setRequestHeader(const String& name, const String& value, ExceptionCode& ec)
558 {
559     if (m_state != Open || m_loader) {
560 #if ENABLE(DASHBOARD_SUPPORT)
561         Settings* settings = m_doc ? m_doc->settings() : 0;
562         if (settings && settings->usesDashboardBackwardCompatibilityMode())
563             return;
564 #endif
565
566         ec = INVALID_STATE_ERR;
567         return;
568     }
569
570     if (!isValidToken(name) || !isValidHeaderValue(value)) {
571         ec = SYNTAX_ERR;
572         return;
573     }
574         
575     // A privileged script (e.g. a Dashboard widget) can set any headers.
576     if (!m_doc->isAllowedToLoadLocalResources() && !isSafeRequestHeader(name)) {
577         if (m_doc && m_doc->frame())
578             m_doc->frame()->domWindow()->console()->addMessage(JSMessageSource, ErrorMessageLevel, "Refused to set unsafe header " + name, 1, String());
579         return;
580     }
581
582     if (!m_requestHeaders.contains(name)) {
583         m_requestHeaders.set(name, value);
584         return;
585     }
586     
587     String oldValue = m_requestHeaders.get(name);
588     m_requestHeaders.set(name, oldValue + ", " + value);
589 }
590
591 String XMLHttpRequest::getRequestHeader(const String& name) const
592 {
593     return m_requestHeaders.get(name);
594 }
595
596 String XMLHttpRequest::getAllResponseHeaders(ExceptionCode& ec) const
597 {
598     if (m_state < Receiving) {
599         ec = INVALID_STATE_ERR;
600         return "";
601     }
602
603     Vector<UChar> stringBuilder;
604     String separator(": ");
605
606     HTTPHeaderMap::const_iterator end = m_response.httpHeaderFields().end();
607     for (HTTPHeaderMap::const_iterator it = m_response.httpHeaderFields().begin(); it!= end; ++it) {
608         stringBuilder.append(it->first.characters(), it->first.length());
609         stringBuilder.append(separator.characters(), separator.length());
610         stringBuilder.append(it->second.characters(), it->second.length());
611         stringBuilder.append((UChar)'\r');
612         stringBuilder.append((UChar)'\n');
613     }
614
615     return String::adopt(stringBuilder);
616 }
617
618 String XMLHttpRequest::getResponseHeader(const String& name, ExceptionCode& ec) const
619 {
620     if (m_state < Receiving) {
621         ec = INVALID_STATE_ERR;
622         return "";
623     }
624
625     if (!isValidToken(name))
626         return "";
627
628     return m_response.httpHeaderField(name);
629 }
630
631 String XMLHttpRequest::responseMIMEType() const
632 {
633     String mimeType = extractMIMETypeFromMediaType(m_mimeTypeOverride);
634     if (mimeType.isEmpty()) {
635         if (m_response.isHTTP())
636             mimeType = extractMIMETypeFromMediaType(m_response.httpHeaderField("Content-Type"));
637         else
638             mimeType = m_response.mimeType();
639     }
640     if (mimeType.isEmpty())
641         mimeType = "text/xml";
642     
643     return mimeType;
644 }
645
646 bool XMLHttpRequest::responseIsXML() const
647 {
648     return DOMImplementation::isXMLMIMEType(responseMIMEType());
649 }
650
651 int XMLHttpRequest::status(ExceptionCode& ec) const
652 {
653     if (m_response.httpStatusCode())
654         return m_response.httpStatusCode();
655
656     if (m_state == Open) {
657         // Firefox only raises an exception in this state; we match it.
658         // 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.
659         ec = INVALID_STATE_ERR;
660     }
661
662     return 0;
663 }
664
665 String XMLHttpRequest::statusText(ExceptionCode& ec) const
666 {
667     // FIXME: <http://bugs.webkit.org/show_bug.cgi?id=3547> XMLHttpRequest.statusText returns always "OK".
668     if (m_response.httpStatusCode())
669         return "OK";
670
671     if (m_state == Open) {
672         // See comments in getStatus() above.
673         ec = INVALID_STATE_ERR;
674     }
675
676     return String();
677 }
678
679 void XMLHttpRequest::processSyncLoadResults(const Vector<char>& data, const ResourceResponse& response)
680 {
681     if (!urlMatchesDocumentDomain(response.url())) {
682         internalAbort();
683         return;
684     }
685
686     didReceiveResponse(0, response);
687     changeState(Sent);
688     if (m_aborted)
689         return;
690
691     const char* bytes = static_cast<const char*>(data.data());
692     int len = static_cast<int>(data.size());
693
694     didReceiveData(0, bytes, len);
695     if (m_aborted)
696         return;
697
698     didFinishLoading(0);
699 }
700
701 void XMLHttpRequest::didFail(SubresourceLoader* loader, const ResourceError&)
702 {
703     didFinishLoading(loader);
704 }
705
706 void XMLHttpRequest::didFinishLoading(SubresourceLoader* loader)
707 {
708     if (m_aborted)
709         return;
710         
711     ASSERT(loader == m_loader);
712
713     if (m_state < Sent)
714         changeState(Sent);
715
716     {
717         KJS::JSLock lock;
718         if (m_decoder)
719             m_responseText += m_decoder->flush();
720     }
721
722     if (Frame* frame = m_doc->frame()) {
723         if (Page* page = frame->page())
724             page->inspectorController()->resourceRetrievedByXMLHttpRequest(m_loader ? m_loader->identifier() : m_identifier, m_responseText);
725     }
726
727     bool hadLoader = m_loader;
728     m_loader = 0;
729
730     changeState(Loaded);
731     m_decoder = 0;
732
733     if (hadLoader)
734         dropProtection();
735 }
736
737 void XMLHttpRequest::willSendRequest(SubresourceLoader*, ResourceRequest& request, const ResourceResponse& redirectResponse)
738 {
739     if (!urlMatchesDocumentDomain(request.url()))
740         internalAbort();
741 }
742
743 void XMLHttpRequest::didReceiveResponse(SubresourceLoader*, const ResourceResponse& response)
744 {
745     m_response = response;
746     m_responseEncoding = extractCharsetFromMediaType(m_mimeTypeOverride);
747     if (m_responseEncoding.isEmpty())
748         m_responseEncoding = response.textEncodingName();
749
750 }
751
752 void XMLHttpRequest::receivedCancellation(SubresourceLoader*, const AuthenticationChallenge& challenge)
753 {
754     m_response = challenge.failureResponse();
755 }
756
757 void XMLHttpRequest::didReceiveData(SubresourceLoader*, const char* data, int len)
758 {
759     if (m_state < Sent)
760         changeState(Sent);
761   
762     if (!m_decoder) {
763         if (!m_responseEncoding.isEmpty())
764             m_decoder = new TextResourceDecoder("text/plain", m_responseEncoding);
765         // allow TextResourceDecoder to look inside the m_response if it's XML or HTML
766         else if (responseIsXML())
767             m_decoder = new TextResourceDecoder("application/xml");
768         else if (responseMIMEType() == "text/html")
769             m_decoder = new TextResourceDecoder("text/html", "UTF-8");
770         else
771             m_decoder = new TextResourceDecoder("text/plain", "UTF-8");
772     }
773     if (len == 0)
774         return;
775
776     if (len == -1)
777         len = strlen(data);
778
779     String decoded = m_decoder->decode(data, len);
780
781     {
782         KJS::JSLock lock;
783         m_responseText += decoded;
784     }
785
786     if (!m_aborted) {
787         updateAndDispatchOnProgress(len);
788
789         if (m_state != Receiving)
790             changeState(Receiving);
791         else
792             // Firefox calls readyStateChanged every time it receives data, 4449442
793             callReadyStateChangeListener();
794     }
795 }
796
797 void XMLHttpRequest::updateAndDispatchOnProgress(unsigned int len)
798 {
799     long long expectedLength = m_response.expectedContentLength();
800
801     m_receivedLength += len;
802
803     // FIXME: the spec requires that we dispatch the event according to the least
804     // frequent method between every 350ms (+/-200ms) and for every byte received.
805     dispatchProgressEvent(expectedLength);
806 }
807
808 void XMLHttpRequest::dispatchProgressEvent(long long expectedLength)
809 {
810     RefPtr<XMLHttpRequestProgressEvent> evt;
811
812     // If we do not have the information or it is odd, set lengthComputable to false.
813     evt = new XMLHttpRequestProgressEvent(progressEvent, expectedLength && m_receivedLength <= expectedLength, static_cast<unsigned>(m_receivedLength), static_cast<unsigned>(expectedLength));
814
815     if (m_onProgressListener) {
816         evt->setTarget(this);
817         evt->setCurrentTarget(this);
818         m_onProgressListener->handleEvent(evt.get(), false);
819     }
820
821     ExceptionCode ec = 0;
822     dispatchEvent(evt, ec, false);
823     ASSERT(!ec);
824 }
825
826 void XMLHttpRequest::cancelRequests(Document* m_doc)
827 {
828     RequestsSet* requests = requestsByDocument().get(m_doc);
829     if (!requests)
830         return;
831     RequestsSet copy = *requests;
832     RequestsSet::const_iterator end = copy.end();
833     for (RequestsSet::const_iterator it = copy.begin(); it != end; ++it)
834         (*it)->internalAbort();
835 }
836
837 void XMLHttpRequest::detachRequests(Document* m_doc)
838 {
839     RequestsSet* requests = requestsByDocument().get(m_doc);
840     if (!requests)
841         return;
842     requestsByDocument().remove(m_doc);
843     RequestsSet::const_iterator end = requests->end();
844     for (RequestsSet::const_iterator it = requests->begin(); it != end; ++it) {
845         (*it)->m_doc = 0;
846         (*it)->internalAbort();
847     }
848     delete requests;
849 }
850
851 } // end namespace