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