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