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