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