2009-03-06 Jay Campan <jcampan@google.com>
[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     , m_exceptionCode(0)
335 {
336     initializeXMLHttpRequestStaticData();
337 }
338
339 XMLHttpRequest::~XMLHttpRequest()
340 {
341     if (m_upload)
342         m_upload->disconnectXMLHttpRequest();
343 }
344
345 Document* XMLHttpRequest::document() const
346 {
347     ASSERT(scriptExecutionContext()->isDocument());
348     return static_cast<Document*>(scriptExecutionContext());
349 }
350
351 #if ENABLE(DASHBOARD_SUPPORT)
352 bool XMLHttpRequest::usesDashboardBackwardCompatibilityMode() const
353 {
354     if (scriptExecutionContext()->isWorkerContext())
355         return false;
356     Settings* settings = document()->settings();
357     return settings && settings->usesDashboardBackwardCompatibilityMode();
358 }
359 #endif
360
361 XMLHttpRequest::State XMLHttpRequest::readyState() const
362 {
363     return m_state;
364 }
365
366 const ScriptString& XMLHttpRequest::responseText() const
367 {
368     return m_responseText;
369 }
370
371 Document* XMLHttpRequest::responseXML() const
372 {
373     if (m_state != DONE)
374         return 0;
375
376     if (!m_createdDocument) {
377         if ((m_response.isHTTP() && !responseIsXML()) || scriptExecutionContext()->isWorkerContext()) {
378             // The W3C spec requires this.
379             m_responseXML = 0;
380         } else {
381             m_responseXML = document()->implementation()->createDocument(0);
382             m_responseXML->open();
383             m_responseXML->setURL(m_url);
384             // FIXME: set Last-Modified and cookies (currently, those are only available for HTMLDocuments).
385             m_responseXML->write(String(m_responseText));
386             m_responseXML->finishParsing();
387             m_responseXML->close();
388             
389             if (!m_responseXML->wellFormed())
390                 m_responseXML = 0;
391         }
392         m_createdDocument = true;
393     }
394
395     return m_responseXML.get();
396 }
397
398 XMLHttpRequestUpload* XMLHttpRequest::upload()
399 {
400     if (!m_upload)
401         m_upload = XMLHttpRequestUpload::create(this);
402     return m_upload.get();
403 }
404
405 void XMLHttpRequest::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> eventListener, bool)
406 {
407     EventListenersMap::iterator iter = m_eventListeners.find(eventType);
408     if (iter == m_eventListeners.end()) {
409         ListenerVector listeners;
410         listeners.append(eventListener);
411         m_eventListeners.add(eventType, listeners);
412     } else {
413         ListenerVector& listeners = iter->second;
414         for (ListenerVector::iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter)
415             if (*listenerIter == eventListener)
416                 return;
417         
418         listeners.append(eventListener);
419         m_eventListeners.add(eventType, listeners);
420     }
421 }
422
423 void XMLHttpRequest::removeEventListener(const AtomicString& eventType, EventListener* eventListener, bool)
424 {
425     EventListenersMap::iterator iter = m_eventListeners.find(eventType);
426     if (iter == m_eventListeners.end())
427         return;
428
429     ListenerVector& listeners = iter->second;
430     for (ListenerVector::const_iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter)
431         if (*listenerIter == eventListener) {
432             listeners.remove(listenerIter - listeners.begin());
433             return;
434         }
435 }
436
437 bool XMLHttpRequest::dispatchEvent(PassRefPtr<Event> evt, ExceptionCode& ec)
438 {
439     // FIXME: check for other error conditions enumerated in the spec.
440     if (!evt || evt->type().isEmpty()) {
441         ec = EventException::UNSPECIFIED_EVENT_TYPE_ERR;
442         return true;
443     }
444
445     ListenerVector listenersCopy = m_eventListeners.get(evt->type());
446     for (ListenerVector::const_iterator listenerIter = listenersCopy.begin(); listenerIter != listenersCopy.end(); ++listenerIter) {
447         evt->setTarget(this);
448         evt->setCurrentTarget(this);
449         listenerIter->get()->handleEvent(evt.get(), false);
450     }
451
452     return !evt->defaultPrevented();
453 }
454
455 void XMLHttpRequest::changeState(State newState)
456 {
457     if (m_state != newState) {
458         m_state = newState;
459         callReadyStateChangeListener();
460     }
461 }
462
463 void XMLHttpRequest::callReadyStateChangeListener()
464 {
465     if (!scriptExecutionContext())
466         return;
467
468     dispatchReadyStateChangeEvent();
469
470     if (m_state == DONE)
471         dispatchLoadEvent();
472 }
473
474 void XMLHttpRequest::open(const String& method, const KURL& url, bool async, ExceptionCode& ec)
475 {
476     internalAbort();
477     State previousState = m_state;
478     m_state = UNSENT;
479     m_error = false;
480
481     m_uploadComplete = false;
482
483     // clear stuff from possible previous load
484     clearResponse();
485     clearRequest();
486
487     ASSERT(m_state == UNSENT);
488
489     if (!isValidToken(method)) {
490         ec = SYNTAX_ERR;
491         return;
492     }
493     
494     // Method names are case sensitive. But since Firefox uppercases method names it knows, we'll do the same.
495     String methodUpper(method.upper());
496     
497     if (methodUpper == "TRACE" || methodUpper == "TRACK" || methodUpper == "CONNECT") {
498         ec = SECURITY_ERR;
499         return;
500     }
501
502     m_url = url;
503
504     if (methodUpper == "COPY" || methodUpper == "DELETE" || methodUpper == "GET" || methodUpper == "HEAD"
505         || methodUpper == "INDEX" || methodUpper == "LOCK" || methodUpper == "M-POST" || methodUpper == "MKCOL" || methodUpper == "MOVE"
506         || methodUpper == "OPTIONS" || methodUpper == "POST" || methodUpper == "PROPFIND" || methodUpper == "PROPPATCH" || methodUpper == "PUT" 
507         || methodUpper == "UNLOCK")
508         m_method = methodUpper;
509     else
510         m_method = method;
511
512     m_async = async;
513
514     ASSERT(!m_loader);
515
516     // Check previous state to avoid dispatching readyState event
517     // when calling open several times in a row.
518     if (previousState != OPENED)
519         changeState(OPENED);
520     else
521         m_state = OPENED;
522 }
523
524 void XMLHttpRequest::open(const String& method, const KURL& url, bool async, const String& user, ExceptionCode& ec)
525 {
526     KURL urlWithCredentials(url);
527     urlWithCredentials.setUser(user);
528     
529     open(method, urlWithCredentials, async, ec);
530 }
531
532 void XMLHttpRequest::open(const String& method, const KURL& url, bool async, const String& user, const String& password, ExceptionCode& ec)
533 {
534     KURL urlWithCredentials(url);
535     urlWithCredentials.setUser(user);
536     urlWithCredentials.setPass(password);
537     
538     open(method, urlWithCredentials, async, ec);
539 }
540
541 bool XMLHttpRequest::initSend(ExceptionCode& ec)
542 {
543     if (!scriptExecutionContext())
544         return false;
545
546     if (m_state != OPENED || m_loader) {
547         ec = INVALID_STATE_ERR;
548         return false;
549     }
550
551     m_error = false;
552     return true;
553 }
554
555 void XMLHttpRequest::send(ExceptionCode& ec)
556 {
557     send(String(), ec);
558 }
559
560 void XMLHttpRequest::send(Document* document, ExceptionCode& ec)
561 {
562     ASSERT(document);
563
564     if (!initSend(ec))
565         return;
566
567     if (m_method != "GET" && m_method != "HEAD" && (m_url.protocolIs("http") || m_url.protocolIs("https"))) {
568         String contentType = getRequestHeader("Content-Type");
569         if (contentType.isEmpty()) {
570 #if ENABLE(DASHBOARD_SUPPORT)
571             if (usesDashboardBackwardCompatibilityMode())
572                 setRequestHeaderInternal("Content-Type", "application/x-www-form-urlencoded");
573             else
574 #endif
575                 // FIXME: this should include the charset used for encoding.
576                 setRequestHeaderInternal("Content-Type", "application/xml");
577         }
578
579         // FIXME: According to XMLHttpRequest Level 2, this should use the Document.innerHTML algorithm
580         // from the HTML5 specification to serialize the document.
581         String body = createMarkup(document);
582
583         // FIXME: this should use value of document.inputEncoding to determine the encoding to use.
584         TextEncoding encoding = UTF8Encoding();
585         m_requestEntityBody = FormData::create(encoding.encode(body.characters(), body.length(), EntitiesForUnencodables));
586         if (m_upload)
587             m_requestEntityBody->setAlwaysStream(true);
588     }
589
590     createRequest(ec);
591 }
592
593 void XMLHttpRequest::send(const String& body, ExceptionCode& ec)
594 {
595     if (!initSend(ec))
596         return;
597
598     if (!body.isNull() && m_method != "GET" && m_method != "HEAD" && (m_url.protocolIs("http") || m_url.protocolIs("https"))) {
599         String contentType = getRequestHeader("Content-Type");
600         if (contentType.isEmpty()) {
601 #if ENABLE(DASHBOARD_SUPPORT)
602             if (usesDashboardBackwardCompatibilityMode())
603                 setRequestHeaderInternal("Content-Type", "application/x-www-form-urlencoded");
604             else
605 #endif
606                 setRequestHeaderInternal("Content-Type", "application/xml");
607         }
608
609         m_requestEntityBody = FormData::create(UTF8Encoding().encode(body.characters(), body.length(), EntitiesForUnencodables));
610         if (m_upload)
611             m_requestEntityBody->setAlwaysStream(true);
612     }
613
614     createRequest(ec);
615 }
616
617 void XMLHttpRequest::send(File* body, ExceptionCode& ec)
618 {
619     if (!initSend(ec))
620         return;
621
622     if (m_method != "GET" && m_method != "HEAD" && (m_url.protocolIs("http") || m_url.protocolIs("https"))) {
623         // FIXME: Should we set a Content-Type if one is not set.
624         // FIXME: add support for uploading bundles.
625         m_requestEntityBody = FormData::create();
626         m_requestEntityBody->appendFile(body->path(), false);
627     }
628
629     createRequest(ec);
630 }
631
632 void XMLHttpRequest::createRequest(ExceptionCode& ec)
633 {
634     if (m_async) {
635         dispatchLoadStartEvent();
636         if (m_requestEntityBody && m_upload)
637             m_upload->dispatchLoadStartEvent();
638     }
639
640     m_sameOriginRequest = scriptExecutionContext()->securityOrigin()->canRequest(m_url);
641
642     if (!m_sameOriginRequest) {
643         makeCrossSiteAccessRequest(ec);
644         return;
645     }
646
647     makeSameOriginRequest(ec);
648 }
649
650 void XMLHttpRequest::makeSameOriginRequest(ExceptionCode& ec)
651 {
652     ASSERT(m_sameOriginRequest);
653
654     ResourceRequest request(m_url);
655     request.setHTTPMethod(m_method);
656
657     if (m_requestEntityBody) {
658         ASSERT(m_method != "GET");
659         request.setHTTPBody(m_requestEntityBody.release());
660     }
661
662     if (m_requestHeaders.size() > 0)
663         request.addHTTPHeaderFields(m_requestHeaders);
664
665     if (m_async)
666         loadRequestAsynchronously(request);
667     else
668         loadRequestSynchronously(request, ec);
669 }
670
671 bool XMLHttpRequest::isSimpleCrossSiteAccessRequest() const
672 {
673     if (m_method != "GET" && m_method != "POST")
674         return false;
675
676     HTTPHeaderMap::const_iterator end = m_requestHeaders.end();
677     for (HTTPHeaderMap::const_iterator it = m_requestHeaders.begin(); it != end; ++it) {
678         if (!isOnAccessControlSimpleRequestHeaderWhitelist(it->first))
679             return false;
680     }
681
682     return true;
683 }
684
685 void XMLHttpRequest::makeCrossSiteAccessRequest(ExceptionCode& ec)
686 {
687     ASSERT(!m_sameOriginRequest);
688
689     if (isSimpleCrossSiteAccessRequest())
690         makeSimpleCrossSiteAccessRequest(ec);
691     else
692         makeCrossSiteAccessRequestWithPreflight(ec);
693 }
694
695 void XMLHttpRequest::makeSimpleCrossSiteAccessRequest(ExceptionCode& ec)
696 {
697     ASSERT(isSimpleCrossSiteAccessRequest());
698
699     KURL url = m_url;
700     url.setUser(String());
701     url.setPass(String());
702  
703     ResourceRequest request(url);
704     request.setHTTPMethod(m_method);
705     request.setAllowHTTPCookies(m_includeCredentials);
706     request.setHTTPOrigin(scriptExecutionContext()->securityOrigin()->toString());
707
708     if (m_requestHeaders.size() > 0)
709         request.addHTTPHeaderFields(m_requestHeaders);
710
711     if (m_async)
712         loadRequestAsynchronously(request);
713     else
714         loadRequestSynchronously(request, ec);
715 }
716
717 void XMLHttpRequest::makeCrossSiteAccessRequestWithPreflight(ExceptionCode& ec)
718 {
719     String origin = scriptExecutionContext()->securityOrigin()->toString();
720     KURL url = m_url;
721     url.setUser(String());
722     url.setPass(String());
723
724     if (!PreflightResultCache::shared().canSkipPreflight(origin, url, m_includeCredentials, m_method, m_requestHeaders)) {
725         m_inPreflight = true;
726         ResourceRequest preflightRequest(url);
727         preflightRequest.setHTTPMethod("OPTIONS");
728         preflightRequest.setHTTPHeaderField("Origin", origin);
729         preflightRequest.setHTTPHeaderField("Access-Control-Request-Method", m_method);
730
731         if (m_requestHeaders.size() > 0) {
732             Vector<UChar> headerBuffer;
733             HTTPHeaderMap::const_iterator it = m_requestHeaders.begin();
734             append(headerBuffer, it->first);
735             ++it;
736
737             HTTPHeaderMap::const_iterator end = m_requestHeaders.end();
738             for (; it != end; ++it) {
739                 headerBuffer.append(',');
740                 headerBuffer.append(' ');
741                 append(headerBuffer, it->first);
742             }
743
744             preflightRequest.setHTTPHeaderField("Access-Control-Request-Headers", String::adopt(headerBuffer));
745             preflightRequest.addHTTPHeaderFields(m_requestHeaders);
746         }
747
748         if (m_async) {
749             loadRequestAsynchronously(preflightRequest);
750             return;
751         }
752
753         loadRequestSynchronously(preflightRequest, ec);
754         m_inPreflight = false;
755
756         if (ec)
757             return;
758     }
759
760     // Send the actual request.
761     ResourceRequest request(url);
762     request.setHTTPMethod(m_method);
763     request.setAllowHTTPCookies(m_includeCredentials);
764     request.setHTTPHeaderField("Origin", origin);
765
766     if (m_requestHeaders.size() > 0)
767         request.addHTTPHeaderFields(m_requestHeaders);
768
769     if (m_requestEntityBody) {
770         ASSERT(m_method != "GET");
771         request.setHTTPBody(m_requestEntityBody.release());
772     }
773
774     if (m_async) {
775         loadRequestAsynchronously(request);
776         return;
777     }
778
779     loadRequestSynchronously(request, ec);
780 }
781
782 void XMLHttpRequest::handleAsynchronousPreflightResult()
783 {
784     ASSERT(m_inPreflight);
785     ASSERT(m_async);
786
787     m_inPreflight = false;
788
789     KURL url = m_url;
790     url.setUser(String());
791     url.setPass(String());
792
793     ResourceRequest request(url);
794     request.setHTTPMethod(m_method);
795     request.setAllowHTTPCookies(m_includeCredentials);
796     request.setHTTPOrigin(scriptExecutionContext()->securityOrigin()->toString());
797
798     if (m_requestHeaders.size() > 0)
799         request.addHTTPHeaderFields(m_requestHeaders);
800
801     if (m_requestEntityBody) {
802         ASSERT(m_method != "GET");
803         request.setHTTPBody(m_requestEntityBody.release());
804     }
805
806     loadRequestAsynchronously(request);
807 }
808
809 void XMLHttpRequest::loadRequestSynchronously(ResourceRequest& request, ExceptionCode& ec)
810 {
811     ASSERT(!m_async);
812
813     m_loader = 0;
814     m_exceptionCode = 0;
815     ThreadableLoader::loadResourceSynchronously(scriptExecutionContext(), request, *this);
816     if (!m_exceptionCode && m_error)
817         m_exceptionCode = XMLHttpRequestException::NETWORK_ERR;
818     ec = m_exceptionCode;
819 }
820
821 void XMLHttpRequest::loadRequestAsynchronously(ResourceRequest& request)
822 {
823     ASSERT(m_async);
824     m_exceptionCode = 0;
825     // SubresourceLoader::create can return null here, for example if we're no longer attached to a page.
826     // This is true while running onunload handlers.
827     // FIXME: We need to be able to send XMLHttpRequests from onunload, <http://bugs.webkit.org/show_bug.cgi?id=10904>.
828     // FIXME: Maybe create can return null for other reasons too?
829     // We need to keep content sniffing enabled for local files due to CFNetwork not providing a MIME type
830     // for local files otherwise, <rdar://problem/5671813>.
831     LoadCallbacks callbacks = m_inPreflight ? DoNotSendLoadCallbacks : SendLoadCallbacks;
832     ContentSniff contentSniff = request.url().isLocalFile() ? SniffContent : DoNotSniffContent;
833
834     if (m_upload)
835         request.setReportUploadProgress(true);
836
837     m_loader = ThreadableLoader::create(scriptExecutionContext(), this, request, callbacks, contentSniff);
838
839     if (m_loader) {
840         // Neither this object nor the JavaScript wrapper should be deleted while
841         // a request is in progress because we need to keep the listeners alive,
842         // and they are referenced by the JavaScript wrapper.
843         setPendingActivity(this);
844     }
845 }
846
847 void XMLHttpRequest::abort()
848 {
849     // internalAbort() calls dropProtection(), which may release the last reference.
850     RefPtr<XMLHttpRequest> protect(this);
851
852     bool sendFlag = m_loader;
853
854     internalAbort();
855
856     // Clear headers as required by the spec
857     m_requestHeaders.clear();
858     
859     if ((m_state <= OPENED && !sendFlag) || m_state == DONE)
860         m_state = UNSENT;
861      else {
862         ASSERT(!m_loader);
863         changeState(DONE);
864         m_state = UNSENT;
865     }
866
867     dispatchAbortEvent();
868     if (!m_uploadComplete) {
869         m_uploadComplete = true;
870         if (m_upload)
871             m_upload->dispatchAbortEvent();
872     }
873 }
874
875 void XMLHttpRequest::internalAbort()
876 {
877     bool hadLoader = m_loader;
878
879     m_error = true;
880
881     // FIXME: when we add the support for multi-part XHR, we will have to think be careful with this initialization.
882     m_receivedLength = 0;
883
884     if (hadLoader) {
885         m_loader->cancel();
886         m_loader = 0;
887     }
888
889     m_decoder = 0;
890
891     if (hadLoader)
892         dropProtection();
893 }
894
895 void XMLHttpRequest::clearResponse()
896 {
897     m_response = ResourceResponse();
898     m_responseText = "";
899     m_createdDocument = false;
900     m_responseXML = 0;
901 }
902
903 void XMLHttpRequest::clearRequest()
904 {
905     m_requestHeaders.clear();
906     m_requestEntityBody = 0;
907 }
908
909 void XMLHttpRequest::genericError()
910 {
911     clearResponse();
912     clearRequest();
913     m_error = true;
914
915     // The spec says we should "Synchronously switch the state to DONE." and then "Synchronously dispatch a readystatechange event on the object"
916     // but this does not match Firefox.
917 }
918
919 void XMLHttpRequest::networkError()
920 {
921     genericError();
922     dispatchErrorEvent();
923     if (!m_uploadComplete) {
924         m_uploadComplete = true;
925         if (m_upload)
926             m_upload->dispatchErrorEvent();
927     }
928 }
929
930 void XMLHttpRequest::abortError()
931 {
932     genericError();
933     dispatchAbortEvent();
934     if (!m_uploadComplete) {
935         m_uploadComplete = true;
936         if (m_upload)
937             m_upload->dispatchAbortEvent();
938     }
939 }
940
941 void XMLHttpRequest::dropProtection()        
942 {
943 #if USE(JSC)
944     // The XHR object itself holds on to the responseText, and
945     // thus has extra cost even independent of any
946     // responseText or responseXML objects it has handed
947     // out. But it is protected from GC while loading, so this
948     // can't be recouped until the load is done, so only
949     // report the extra cost at that point.
950
951    if (JSDOMGlobalObject* globalObject = toJSDOMGlobalObject(scriptExecutionContext()))
952        if (DOMObject* wrapper = getCachedDOMObjectWrapper(*globalObject->globalData(), this))
953            JSC::Heap::heap(wrapper)->reportExtraMemoryCost(m_responseText.size() * 2);
954 #endif
955
956     unsetPendingActivity(this);
957 }
958
959 void XMLHttpRequest::overrideMimeType(const String& override)
960 {
961     m_mimeTypeOverride = override;
962 }
963
964 static void reportUnsafeUsage(ScriptExecutionContext* context, const String& message)
965 {
966     if (!context)
967         return;
968     // FIXME: It's not good to report the bad usage without indicating what source line it came from.
969     // We should pass additional parameters so we can tell the console where the mistake occurred.
970     context->addMessage(ConsoleDestination, JSMessageSource, ErrorMessageLevel, message, 1, String());
971 }
972
973 void XMLHttpRequest::setRequestHeader(const AtomicString& name, const String& value, ExceptionCode& ec)
974 {
975     if (m_state != OPENED || m_loader) {
976 #if ENABLE(DASHBOARD_SUPPORT)
977         if (usesDashboardBackwardCompatibilityMode())
978             return;
979 #endif
980
981         ec = INVALID_STATE_ERR;
982         return;
983     }
984
985     if (!isValidToken(name) || !isValidHeaderValue(value)) {
986         ec = SYNTAX_ERR;
987         return;
988     }
989
990     // A privileged script (e.g. a Dashboard widget) can set any headers.
991     if (!scriptExecutionContext()->securityOrigin()->canLoadLocalResources() && !isSafeRequestHeader(name)) {
992         reportUnsafeUsage(scriptExecutionContext(), "Refused to set unsafe header \"" + name + "\"");
993         return;
994     }
995
996     setRequestHeaderInternal(name, value);
997 }
998
999 void XMLHttpRequest::setRequestHeaderInternal(const AtomicString& name, const String& value)
1000 {
1001     pair<HTTPHeaderMap::iterator, bool> result = m_requestHeaders.add(name, value); 
1002     if (!result.second)
1003         result.first->second += ", " + value;
1004 }
1005
1006 bool XMLHttpRequest::isSafeRequestHeader(const String& name) const
1007 {
1008     return !staticData->m_forbiddenRequestHeaders.contains(name) && !name.startsWith(staticData->m_proxyHeaderPrefix, false)
1009         && !name.startsWith(staticData->m_secHeaderPrefix, false);
1010 }
1011
1012 String XMLHttpRequest::getRequestHeader(const AtomicString& name) const
1013 {
1014     return m_requestHeaders.get(name);
1015 }
1016
1017 String XMLHttpRequest::getAllResponseHeaders(ExceptionCode& ec) const
1018 {
1019     if (m_state < LOADING) {
1020         ec = INVALID_STATE_ERR;
1021         return "";
1022     }
1023
1024     Vector<UChar> stringBuilder;
1025
1026     HTTPHeaderMap::const_iterator end = m_response.httpHeaderFields().end();
1027     for (HTTPHeaderMap::const_iterator it = m_response.httpHeaderFields().begin(); it!= end; ++it) {
1028         // Hide Set-Cookie header fields from the XMLHttpRequest client for these reasons:
1029         //     1) If the client did have access to the fields, then it could read HTTP-only
1030         //        cookies; those cookies are supposed to be hidden from scripts.
1031         //     2) There's no known harm in hiding Set-Cookie header fields entirely; we don't
1032         //        know any widely used technique that requires access to them.
1033         //     3) Firefox has implemented this policy.
1034         if (isSetCookieHeader(it->first) && !scriptExecutionContext()->securityOrigin()->canLoadLocalResources())
1035             continue;
1036
1037         if (!m_sameOriginRequest && !isOnAccessControlResponseHeaderWhitelist(it->first))
1038             continue;
1039
1040         stringBuilder.append(it->first.characters(), it->first.length());
1041         stringBuilder.append(':');
1042         stringBuilder.append(' ');
1043         stringBuilder.append(it->second.characters(), it->second.length());
1044         stringBuilder.append('\r');
1045         stringBuilder.append('\n');
1046     }
1047
1048     return String::adopt(stringBuilder);
1049 }
1050
1051 String XMLHttpRequest::getResponseHeader(const AtomicString& name, ExceptionCode& ec) const
1052 {
1053     if (m_state < LOADING) {
1054         ec = INVALID_STATE_ERR;
1055         return "";
1056     }
1057
1058     if (!isValidToken(name))
1059         return "";
1060
1061     // See comment in getAllResponseHeaders above.
1062     if (isSetCookieHeader(name) && !scriptExecutionContext()->securityOrigin()->canLoadLocalResources()) {
1063         reportUnsafeUsage(scriptExecutionContext(), "Refused to get unsafe header \"" + name + "\"");
1064         return "";
1065     }
1066
1067     if (!m_sameOriginRequest && !isOnAccessControlResponseHeaderWhitelist(name)) {
1068         reportUnsafeUsage(scriptExecutionContext(), "Refused to get unsafe header \"" + name + "\"");
1069         return "";
1070     }
1071
1072     return m_response.httpHeaderField(name);
1073 }
1074
1075 bool XMLHttpRequest::isOnAccessControlResponseHeaderWhitelist(const String& name) const
1076 {
1077     return staticData->m_allowedCrossSiteResponseHeaders.contains(name);
1078 }
1079
1080 String XMLHttpRequest::responseMIMEType() const
1081 {
1082     String mimeType = extractMIMETypeFromMediaType(m_mimeTypeOverride);
1083     if (mimeType.isEmpty()) {
1084         if (m_response.isHTTP())
1085             mimeType = extractMIMETypeFromMediaType(m_response.httpHeaderField("Content-Type"));
1086         else
1087             mimeType = m_response.mimeType();
1088     }
1089     if (mimeType.isEmpty())
1090         mimeType = "text/xml";
1091     
1092     return mimeType;
1093 }
1094
1095 bool XMLHttpRequest::responseIsXML() const
1096 {
1097     return DOMImplementation::isXMLMIMEType(responseMIMEType());
1098 }
1099
1100 int XMLHttpRequest::status(ExceptionCode& ec) const
1101 {
1102     if (m_response.httpStatusCode())
1103         return m_response.httpStatusCode();
1104
1105     if (m_state == OPENED) {
1106         // Firefox only raises an exception in this state; we match it.
1107         // 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.
1108         ec = INVALID_STATE_ERR;
1109     }
1110
1111     return 0;
1112 }
1113
1114 String XMLHttpRequest::statusText(ExceptionCode& ec) const
1115 {
1116     // FIXME: <http://bugs.webkit.org/show_bug.cgi?id=3547> XMLHttpRequest.statusText returns always "OK".
1117     if (m_response.httpStatusCode())
1118         return "OK";
1119
1120     if (m_state == OPENED) {
1121         // See comments in getStatus() above.
1122         ec = INVALID_STATE_ERR;
1123     }
1124
1125     return String();
1126 }
1127
1128 void XMLHttpRequest::didFail(const ResourceError& error)
1129 {
1130     // If we are already in an error state, for instance we called abort(), bail out early.
1131     if (m_error)
1132         return;
1133
1134     if (error.isCancellation()) {
1135         m_exceptionCode = XMLHttpRequestException::ABORT_ERR;
1136         abortError();
1137         return;
1138     }
1139
1140     m_exceptionCode = XMLHttpRequestException::NETWORK_ERR;
1141     networkError();
1142 }
1143
1144 void XMLHttpRequest::didFailRedirectCheck()
1145 {
1146     internalAbort();
1147     networkError();
1148 }
1149
1150 void XMLHttpRequest::didFinishLoading(unsigned long identifier)
1151 {
1152     if (m_error)
1153         return;
1154
1155     if (m_inPreflight) {
1156         didFinishLoadingPreflight();
1157         return;
1158     }
1159
1160     if (m_state < HEADERS_RECEIVED)
1161         changeState(HEADERS_RECEIVED);
1162
1163     if (m_decoder)
1164         m_responseText += m_decoder->flush();
1165
1166     scriptExecutionContext()->resourceRetrievedByXMLHttpRequest(identifier, m_responseText);
1167     scriptExecutionContext()->addMessage(InspectorControllerDestination, JSMessageSource, LogMessageLevel, "XHR finished loading: \"" + m_url + "\".", m_lastSendLineNumber, m_lastSendURL);
1168
1169     bool hadLoader = m_loader;
1170     m_loader = 0;
1171
1172     changeState(DONE);
1173     m_decoder = 0;
1174
1175     if (hadLoader)
1176         dropProtection();
1177 }
1178
1179 void XMLHttpRequest::didFinishLoadingPreflight()
1180 {
1181     ASSERT(m_inPreflight);
1182     ASSERT(!m_sameOriginRequest);
1183
1184     // FIXME: this can probably be moved to didReceiveResponsePreflight.
1185     if (m_async)
1186         handleAsynchronousPreflightResult();
1187
1188     if (m_loader)
1189         unsetPendingActivity(this);
1190 }
1191
1192 void XMLHttpRequest::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
1193 {
1194     if (!m_upload)
1195         return;
1196
1197     m_upload->dispatchProgressEvent(bytesSent, totalBytesToBeSent);
1198
1199     if (bytesSent == totalBytesToBeSent && !m_uploadComplete) {
1200         m_uploadComplete = true;
1201         m_upload->dispatchLoadEvent();
1202     }
1203 }
1204
1205 bool XMLHttpRequest::accessControlCheck(const ResourceResponse& response)
1206 {
1207     const String& accessControlOriginString = response.httpHeaderField("Access-Control-Allow-Origin");
1208     if (accessControlOriginString == "*" && !m_includeCredentials)
1209         return true;
1210
1211     RefPtr<SecurityOrigin> accessControlOrigin = SecurityOrigin::createFromString(accessControlOriginString);
1212     if (!accessControlOrigin->isSameSchemeHostPort(scriptExecutionContext()->securityOrigin()))
1213         return false;
1214
1215     if (m_includeCredentials) {
1216         const String& accessControlCredentialsString = response.httpHeaderField("Access-Control-Allow-Credentials");
1217         if (accessControlCredentialsString != "true")
1218             return false;
1219     }
1220
1221     return true;
1222 }
1223
1224 void XMLHttpRequest::didReceiveResponse(const ResourceResponse& response)
1225 {
1226     if (m_inPreflight) {
1227         didReceiveResponsePreflight(response);
1228         return;
1229     }
1230
1231     if (!m_sameOriginRequest) {
1232         if (!accessControlCheck(response)) {
1233             networkError();
1234             return;
1235         }
1236     }
1237
1238     m_response = response;
1239     m_responseEncoding = extractCharsetFromMediaType(m_mimeTypeOverride);
1240     if (m_responseEncoding.isEmpty())
1241         m_responseEncoding = response.textEncodingName();
1242 }
1243
1244 void XMLHttpRequest::didReceiveResponsePreflight(const ResourceResponse& response)
1245 {
1246     ASSERT(m_inPreflight);
1247     ASSERT(!m_sameOriginRequest);
1248
1249     if (!accessControlCheck(response)) {
1250         networkError();
1251         return;
1252     }
1253
1254     OwnPtr<PreflightResultCacheItem> preflightResult(new PreflightResultCacheItem(m_includeCredentials));
1255     if (!preflightResult->parse(response)
1256         || !preflightResult->allowsCrossSiteMethod(m_method)
1257         || !preflightResult->allowsCrossSiteHeaders(m_requestHeaders)) {
1258         networkError();
1259         return;
1260     }
1261
1262     PreflightResultCache::shared().appendEntry(scriptExecutionContext()->securityOrigin()->toString(), m_url, preflightResult.release());
1263 }
1264
1265 void XMLHttpRequest::didReceiveAuthenticationCancellation(const ResourceResponse& failureResponse)
1266 {
1267     m_response = failureResponse;
1268 }
1269
1270 void XMLHttpRequest::didReceiveData(const char* data, int len)
1271 {
1272     if (m_inPreflight)
1273         return;
1274
1275     if (m_state < HEADERS_RECEIVED)
1276         changeState(HEADERS_RECEIVED);
1277   
1278     if (!m_decoder) {
1279         if (!m_responseEncoding.isEmpty())
1280             m_decoder = TextResourceDecoder::create("text/plain", m_responseEncoding);
1281         // allow TextResourceDecoder to look inside the m_response if it's XML or HTML
1282         else if (responseIsXML()) {
1283             m_decoder = TextResourceDecoder::create("application/xml");
1284             // 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.
1285             m_decoder->useLenientXMLDecoding();
1286         } else if (responseMIMEType() == "text/html")
1287             m_decoder = TextResourceDecoder::create("text/html", "UTF-8");
1288         else
1289             m_decoder = TextResourceDecoder::create("text/plain", "UTF-8");
1290     }
1291
1292     if (!len)
1293         return;
1294
1295     if (len == -1)
1296         len = strlen(data);
1297
1298     m_responseText += m_decoder->decode(data, len);
1299
1300     if (!m_error) {
1301         updateAndDispatchOnProgress(len);
1302
1303         if (m_state != LOADING)
1304             changeState(LOADING);
1305         else
1306             // Firefox calls readyStateChanged every time it receives data, 4449442
1307             callReadyStateChangeListener();
1308     }
1309 }
1310
1311 void XMLHttpRequest::updateAndDispatchOnProgress(unsigned int len)
1312 {
1313     long long expectedLength = m_response.expectedContentLength();
1314     m_receivedLength += len;
1315
1316     // FIXME: the spec requires that we dispatch the event according to the least
1317     // frequent method between every 350ms (+/-200ms) and for every byte received.
1318     dispatchProgressEvent(expectedLength);
1319 }
1320
1321 void XMLHttpRequest::dispatchReadyStateChangeEvent()
1322 {
1323     RefPtr<Event> evt = Event::create(eventNames().readystatechangeEvent, false, false);
1324     if (m_onReadyStateChangeListener) {
1325         evt->setTarget(this);
1326         evt->setCurrentTarget(this);
1327         m_onReadyStateChangeListener->handleEvent(evt.get(), false);
1328     }
1329
1330     ExceptionCode ec = 0;
1331     dispatchEvent(evt.release(), ec);
1332     ASSERT(!ec);
1333 }
1334
1335 void XMLHttpRequest::dispatchXMLHttpRequestProgressEvent(EventListener* listener, const AtomicString& type, bool lengthComputable, unsigned loaded, unsigned total)
1336 {
1337     RefPtr<XMLHttpRequestProgressEvent> evt = XMLHttpRequestProgressEvent::create(type, lengthComputable, loaded, total);
1338     if (listener) {
1339         evt->setTarget(this);
1340         evt->setCurrentTarget(this);
1341         listener->handleEvent(evt.get(), false);
1342     }
1343
1344     ExceptionCode ec = 0;
1345     dispatchEvent(evt.release(), ec);
1346     ASSERT(!ec);
1347 }
1348
1349 void XMLHttpRequest::dispatchAbortEvent()
1350 {
1351     dispatchXMLHttpRequestProgressEvent(m_onAbortListener.get(), eventNames().abortEvent, false, 0, 0);
1352 }
1353
1354 void XMLHttpRequest::dispatchErrorEvent()
1355 {
1356     dispatchXMLHttpRequestProgressEvent(m_onErrorListener.get(), eventNames().errorEvent, false, 0, 0);
1357 }
1358
1359 void XMLHttpRequest::dispatchLoadEvent()
1360 {
1361     dispatchXMLHttpRequestProgressEvent(m_onLoadListener.get(), eventNames().loadEvent, false, 0, 0);
1362 }
1363
1364 void XMLHttpRequest::dispatchLoadStartEvent()
1365 {
1366     dispatchXMLHttpRequestProgressEvent(m_onLoadStartListener.get(), eventNames().loadstartEvent, false, 0, 0);
1367 }
1368
1369 void XMLHttpRequest::dispatchProgressEvent(long long expectedLength)
1370 {
1371     dispatchXMLHttpRequestProgressEvent(m_onProgressListener.get(), eventNames().progressEvent, expectedLength && m_receivedLength <= expectedLength, 
1372                                         static_cast<unsigned>(m_receivedLength), static_cast<unsigned>(expectedLength));
1373 }
1374
1375 bool XMLHttpRequest::canSuspend() const
1376 {
1377     return !m_loader;
1378 }
1379
1380 void XMLHttpRequest::stop()
1381 {
1382     internalAbort();
1383 }
1384
1385 void XMLHttpRequest::contextDestroyed()
1386 {
1387     ASSERT(!m_loader);
1388     ActiveDOMObject::contextDestroyed();
1389 }
1390
1391 ScriptExecutionContext* XMLHttpRequest::scriptExecutionContext() const
1392 {
1393     return ActiveDOMObject::scriptExecutionContext();
1394 }
1395
1396 } // namespace WebCore