5efe05492d59648dd194f4d94dc0efe4aad930d9
[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     }
356 }
357
358 void XMLHttpRequest::setWithCredentials(bool value, ExceptionCode& ec)
359 {
360     if (m_state != OPENED || m_loader) {
361         ec = INVALID_STATE_ERR;
362         return;
363     }
364
365     m_includeCredentials = value;
366 }
367
368 #if ENABLE(XHR_RESPONSE_BLOB)
369 void XMLHttpRequest::setAsBlob(bool value, ExceptionCode& ec)
370 {
371     if (m_state != OPENED || m_loader) {
372         ec = INVALID_STATE_ERR;
373         return;
374     }
375     
376     m_responseTypeCode = value ? ResponseTypeBlob : ResponseTypeDefault;
377 }
378 #endif
379
380 bool XMLHttpRequest::isAllowedHTTPMethod(const String& method)
381 {
382     return !equalIgnoringCase(method, "TRACE")
383         && !equalIgnoringCase(method, "TRACK")
384         && !equalIgnoringCase(method, "CONNECT");
385 }
386
387 String XMLHttpRequest::uppercaseKnownHTTPMethod(const String& method)
388 {
389     if (equalIgnoringCase(method, "COPY") || equalIgnoringCase(method, "DELETE") || equalIgnoringCase(method, "GET")
390         || equalIgnoringCase(method, "HEAD") || equalIgnoringCase(method, "INDEX") || equalIgnoringCase(method, "LOCK")
391         || equalIgnoringCase(method, "M-POST") || equalIgnoringCase(method, "MKCOL") || equalIgnoringCase(method, "MOVE")
392         || equalIgnoringCase(method, "OPTIONS") || equalIgnoringCase(method, "POST") || equalIgnoringCase(method, "PROPFIND")
393         || equalIgnoringCase(method, "PROPPATCH") || equalIgnoringCase(method, "PUT") || equalIgnoringCase(method, "UNLOCK")) {
394         return method.upper();
395     }
396     return method;
397 }
398
399 bool XMLHttpRequest::isAllowedHTTPHeader(const String& name)
400 {
401     initializeXMLHttpRequestStaticData();
402     return !staticData->m_forbiddenRequestHeaders.contains(name) && !name.startsWith(staticData->m_proxyHeaderPrefix, false)
403         && !name.startsWith(staticData->m_secHeaderPrefix, false);
404 }
405
406 void XMLHttpRequest::open(const String& method, const KURL& url, ExceptionCode& ec)
407 {
408     open(method, url, true, ec);
409 }
410
411 void XMLHttpRequest::open(const String& method, const KURL& url, bool async, ExceptionCode& ec)
412 {
413     internalAbort();
414     State previousState = m_state;
415     m_state = UNSENT;
416     m_error = false;
417     m_responseTypeCode = ResponseTypeDefault;
418     m_uploadComplete = false;
419
420     // clear stuff from possible previous load
421     clearResponse();
422     clearRequest();
423
424     ASSERT(m_state == UNSENT);
425
426     if (!isValidHTTPToken(method)) {
427         ec = SYNTAX_ERR;
428         return;
429     }
430
431     if (!isAllowedHTTPMethod(method)) {
432         ec = SECURITY_ERR;
433         return;
434     }
435
436     if (!scriptExecutionContext()->contentSecurityPolicy()->allowConnectFromSource(url)) {
437         // FIXME: Should this be throwing an exception?
438         ec = SECURITY_ERR;
439         return;
440     }
441
442     m_method = uppercaseKnownHTTPMethod(method);
443
444     m_url = url;
445
446     m_async = async;
447
448     ASSERT(!m_loader);
449
450     // Check previous state to avoid dispatching readyState event
451     // when calling open several times in a row.
452     if (previousState != OPENED)
453         changeState(OPENED);
454     else
455         m_state = OPENED;
456 }
457
458 void XMLHttpRequest::open(const String& method, const KURL& url, bool async, const String& user, ExceptionCode& ec)
459 {
460     KURL urlWithCredentials(url);
461     urlWithCredentials.setUser(user);
462
463     open(method, urlWithCredentials, async, ec);
464 }
465
466 void XMLHttpRequest::open(const String& method, const KURL& url, bool async, const String& user, const String& password, ExceptionCode& ec)
467 {
468     KURL urlWithCredentials(url);
469     urlWithCredentials.setUser(user);
470     urlWithCredentials.setPass(password);
471
472     open(method, urlWithCredentials, async, ec);
473 }
474
475 bool XMLHttpRequest::initSend(ExceptionCode& ec)
476 {
477     if (!scriptExecutionContext())
478         return false;
479
480     if (m_state != OPENED || m_loader) {
481         ec = INVALID_STATE_ERR;
482         return false;
483     }
484
485     m_error = false;
486     return true;
487 }
488
489 void XMLHttpRequest::send(ExceptionCode& ec)
490 {
491     send(String(), ec);
492 }
493
494 void XMLHttpRequest::send(Document* document, ExceptionCode& ec)
495 {
496     ASSERT(document);
497
498     if (!initSend(ec))
499         return;
500
501     if (m_method != "GET" && m_method != "HEAD" && m_url.protocolInHTTPFamily()) {
502         String contentType = getRequestHeader("Content-Type");
503         if (contentType.isEmpty()) {
504 #if ENABLE(DASHBOARD_SUPPORT)
505             if (usesDashboardBackwardCompatibilityMode())
506                 setRequestHeaderInternal("Content-Type", "application/x-www-form-urlencoded");
507             else
508 #endif
509                 // FIXME: this should include the charset used for encoding.
510                 setRequestHeaderInternal("Content-Type", "application/xml");
511         }
512
513         // FIXME: According to XMLHttpRequest Level 2, this should use the Document.innerHTML algorithm
514         // from the HTML5 specification to serialize the document.
515         String body = createMarkup(document);
516
517         // FIXME: this should use value of document.inputEncoding to determine the encoding to use.
518         TextEncoding encoding = UTF8Encoding();
519         m_requestEntityBody = FormData::create(encoding.encode(body.characters(), body.length(), EntitiesForUnencodables));
520         if (m_upload)
521             m_requestEntityBody->setAlwaysStream(true);
522     }
523
524     createRequest(ec);
525 }
526
527 void XMLHttpRequest::send(const String& body, ExceptionCode& ec)
528 {
529     if (!initSend(ec))
530         return;
531
532     if (!body.isNull() && m_method != "GET" && m_method != "HEAD" && m_url.protocolInHTTPFamily()) {
533         String contentType = getRequestHeader("Content-Type");
534         if (contentType.isEmpty()) {
535 #if ENABLE(DASHBOARD_SUPPORT)
536             if (usesDashboardBackwardCompatibilityMode())
537                 setRequestHeaderInternal("Content-Type", "application/x-www-form-urlencoded");
538             else
539 #endif
540                 setRequestHeaderInternal("Content-Type", "application/xml");
541         } else {
542             replaceCharsetInMediaType(contentType, "UTF-8");
543             m_requestHeaders.set("Content-Type", contentType);
544         }
545
546         m_requestEntityBody = FormData::create(UTF8Encoding().encode(body.characters(), body.length(), EntitiesForUnencodables));
547         if (m_upload)
548             m_requestEntityBody->setAlwaysStream(true);
549     }
550
551     createRequest(ec);
552 }
553
554 void XMLHttpRequest::send(Blob* body, ExceptionCode& ec)
555 {
556     if (!initSend(ec))
557         return;
558
559     if (m_method != "GET" && m_method != "HEAD" && m_url.protocolInHTTPFamily()) {
560         // FIXME: Should we set a Content-Type if one is not set.
561         // FIXME: add support for uploading bundles.
562         m_requestEntityBody = FormData::create();
563         if (body->isFile())
564             m_requestEntityBody->appendFile(static_cast<File*>(body)->path());
565 #if ENABLE(BLOB)
566         else
567             m_requestEntityBody->appendBlob(body->url());
568 #endif
569     }
570
571     createRequest(ec);
572 }
573
574 void XMLHttpRequest::send(DOMFormData* body, ExceptionCode& ec)
575 {
576     if (!initSend(ec))
577         return;
578
579     if (m_method != "GET" && m_method != "HEAD" && m_url.protocolInHTTPFamily()) {
580         m_requestEntityBody = FormData::createMultiPart(*(static_cast<FormDataList*>(body)), body->encoding(), document());
581
582         // We need to ask the client to provide the generated file names if needed. When FormData fills the element
583         // for the file, it could set a flag to use the generated file name, i.e. a package file on Mac.
584         m_requestEntityBody->generateFiles(document());
585
586         String contentType = getRequestHeader("Content-Type");
587         if (contentType.isEmpty()) {
588             contentType = "multipart/form-data; boundary=";
589             contentType += m_requestEntityBody->boundary().data();
590             setRequestHeaderInternal("Content-Type", contentType);
591         }
592     }
593
594     createRequest(ec);
595 }
596
597 void XMLHttpRequest::send(ArrayBuffer* body, ExceptionCode& ec)
598 {
599     if (!initSend(ec))
600         return;
601
602     if (m_method != "GET" && m_method != "HEAD" && m_url.protocolInHTTPFamily()) {
603         m_requestEntityBody = FormData::create(body->data(), body->byteLength());
604         if (m_upload)
605             m_requestEntityBody->setAlwaysStream(true);
606     }
607
608     createRequest(ec);
609 }
610
611 void XMLHttpRequest::createRequest(ExceptionCode& ec)
612 {
613 #if ENABLE(BLOB)
614     // Only GET request is supported for blob URL.
615     if (m_url.protocolIs("blob") && m_method != "GET") {
616         ec = XMLHttpRequestException::NETWORK_ERR;
617         return;
618     }
619 #endif
620
621     // The presence of upload event listeners forces us to use preflighting because POSTing to an URL that does not
622     // permit cross origin requests should look exactly like POSTing to an URL that does not respond at all.
623     // Also, only async requests support upload progress events.
624     bool uploadEvents = false;
625     if (m_async) {
626         m_progressEventThrottle.dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().loadstartEvent));
627         if (m_requestEntityBody && m_upload) {
628             uploadEvents = m_upload->hasEventListeners();
629             m_upload->dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().loadstartEvent));
630         }
631     }
632
633     m_sameOriginRequest = securityOrigin()->canRequest(m_url);
634
635     // We also remember whether upload events should be allowed for this request in case the upload listeners are
636     // added after the request is started.
637     m_uploadEventsAllowed = m_sameOriginRequest || uploadEvents || !isSimpleCrossOriginAccessRequest(m_method, m_requestHeaders);
638
639     ResourceRequest request(m_url);
640     request.setHTTPMethod(m_method);
641 #if PLATFORM(CHROMIUM)
642     request.setTargetType(ResourceRequest::TargetIsXHR);
643 #endif
644
645     if (m_requestEntityBody) {
646         ASSERT(m_method != "GET");
647         ASSERT(m_method != "HEAD");
648         request.setHTTPBody(m_requestEntityBody.release());
649     }
650
651     if (m_requestHeaders.size() > 0)
652         request.addHTTPHeaderFields(m_requestHeaders);
653
654     ThreadableLoaderOptions options;
655     options.sendLoadCallbacks = SendCallbacks;
656     options.sniffContent = DoNotSniffContent;
657     options.preflightPolicy = uploadEvents ? ForcePreflight : ConsiderPreflight;
658     options.allowCredentials = (m_sameOriginRequest || m_includeCredentials) ? AllowStoredCredentials : DoNotAllowStoredCredentials;
659     options.crossOriginRequestPolicy = UseAccessControl;
660     options.securityOrigin = securityOrigin();
661
662     m_exceptionCode = 0;
663     m_error = false;
664
665     if (m_async) {
666         if (m_upload)
667             request.setReportUploadProgress(true);
668
669         // ThreadableLoader::create can return null here, for example if we're no longer attached to a page.
670         // This is true while running onunload handlers.
671         // FIXME: Maybe we need to be able to send XMLHttpRequests from onunload, <http://bugs.webkit.org/show_bug.cgi?id=10904>.
672         // FIXME: Maybe create() can return null for other reasons too?
673         m_loader = ThreadableLoader::create(scriptExecutionContext(), this, request, options);
674         if (m_loader) {
675             // Neither this object nor the JavaScript wrapper should be deleted while
676             // a request is in progress because we need to keep the listeners alive,
677             // and they are referenced by the JavaScript wrapper.
678             setPendingActivity(this);
679         }
680     } else {
681         InspectorInstrumentation::willLoadXHRSynchronously(scriptExecutionContext());
682         ThreadableLoader::loadResourceSynchronously(scriptExecutionContext(), request, *this, options);
683         InspectorInstrumentation::didLoadXHRSynchronously(scriptExecutionContext());
684     }
685
686     if (!m_exceptionCode && m_error)
687         m_exceptionCode = XMLHttpRequestException::NETWORK_ERR;
688     ec = m_exceptionCode;
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_responseDocument = 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     // FIXME: Remove the lower() call when DOMImplementation.isXMLMIMEType() is modified
938     //        to do case insensitive MIME type matching.
939     return DOMImplementation::isXMLMIMEType(responseMIMEType().lower());
940 }
941
942 int XMLHttpRequest::status(ExceptionCode& ec) const
943 {
944     if (m_response.httpStatusCode())
945         return m_response.httpStatusCode();
946
947     if (m_state == OPENED) {
948         // Firefox only raises an exception in this state; we match it.
949         // 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.
950         ec = INVALID_STATE_ERR;
951     }
952
953     return 0;
954 }
955
956 String XMLHttpRequest::statusText(ExceptionCode& ec) const
957 {
958     if (!m_response.httpStatusText().isNull())
959         return m_response.httpStatusText();
960
961     if (m_state == OPENED) {
962         // See comments in status() above.
963         ec = INVALID_STATE_ERR;
964     }
965
966     return String();
967 }
968
969 void XMLHttpRequest::didFail(const ResourceError& error)
970 {
971
972     // If we are already in an error state, for instance we called abort(), bail out early.
973     if (m_error)
974         return;
975
976     if (error.isCancellation()) {
977         m_exceptionCode = XMLHttpRequestException::ABORT_ERR;
978         abortError();
979         return;
980     }
981
982     // Network failures are already reported to Web Inspector by ResourceLoader.
983     if (error.domain() == errorDomainWebKitInternal)
984         reportUnsafeUsage(scriptExecutionContext(), "XMLHttpRequest cannot load " + error.failingURL() + ". " + error.localizedDescription());
985
986     m_exceptionCode = XMLHttpRequestException::NETWORK_ERR;
987     networkError();
988 }
989
990 void XMLHttpRequest::didFailRedirectCheck()
991 {
992     networkError();
993 }
994
995 void XMLHttpRequest::didFinishLoading(unsigned long identifier, double)
996 {
997     if (m_error)
998         return;
999
1000     if (m_state < HEADERS_RECEIVED)
1001         changeState(HEADERS_RECEIVED);
1002
1003     if (m_decoder)
1004         m_responseBuilder.append(m_decoder->flush());
1005
1006     m_responseBuilder.shrinkToFit();
1007
1008 #if ENABLE(XHR_RESPONSE_BLOB)
1009     // FIXME: Set m_responseBlob to something here in the ResponseTypeBlob case.
1010 #endif
1011
1012     InspectorInstrumentation::resourceRetrievedByXMLHttpRequest(scriptExecutionContext(), identifier, m_responseBuilder.toStringPreserveCapacity(), m_url, m_lastSendURL, m_lastSendLineNumber);
1013
1014     bool hadLoader = m_loader;
1015     m_loader = 0;
1016
1017     changeState(DONE);
1018     m_decoder = 0;
1019
1020     if (hadLoader)
1021         dropProtection();
1022 }
1023
1024 void XMLHttpRequest::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
1025 {
1026     if (!m_upload)
1027         return;
1028
1029     if (m_uploadEventsAllowed)
1030         m_upload->dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().progressEvent, true, bytesSent, totalBytesToBeSent));
1031
1032     if (bytesSent == totalBytesToBeSent && !m_uploadComplete) {
1033         m_uploadComplete = true;
1034         if (m_uploadEventsAllowed)
1035             m_upload->dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().loadEvent));
1036     }
1037 }
1038
1039 void XMLHttpRequest::didReceiveResponse(unsigned long identifier, const ResourceResponse& response)
1040 {
1041     InspectorInstrumentation::didReceiveXHRResponse(scriptExecutionContext(), identifier);
1042
1043     m_response = response;
1044     m_responseEncoding = extractCharsetFromMediaType(m_mimeTypeOverride);
1045     if (m_responseEncoding.isEmpty())
1046         m_responseEncoding = response.textEncodingName();
1047 }
1048
1049 void XMLHttpRequest::didReceiveData(const char* data, int len)
1050 {
1051     if (m_error)
1052         return;
1053
1054     if (m_state < HEADERS_RECEIVED)
1055         changeState(HEADERS_RECEIVED);
1056
1057     bool useDecoder = m_responseTypeCode == ResponseTypeDefault || m_responseTypeCode == ResponseTypeText || m_responseTypeCode == ResponseTypeDocument;
1058
1059     if (useDecoder && !m_decoder) {
1060         if (!m_responseEncoding.isEmpty())
1061             m_decoder = TextResourceDecoder::create("text/plain", m_responseEncoding);
1062         // allow TextResourceDecoder to look inside the m_response if it's XML or HTML
1063         else if (responseIsXML()) {
1064             m_decoder = TextResourceDecoder::create("application/xml");
1065             // 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.
1066             m_decoder->useLenientXMLDecoding();
1067         } else if (equalIgnoringCase(responseMIMEType(), "text/html"))
1068             m_decoder = TextResourceDecoder::create("text/html", "UTF-8");
1069         else
1070             m_decoder = TextResourceDecoder::create("text/plain", "UTF-8");
1071     }
1072
1073     if (!len)
1074         return;
1075
1076     if (len == -1)
1077         len = strlen(data);
1078
1079     if (useDecoder)
1080         m_responseBuilder.append(m_decoder->decode(data, len));
1081     else if (m_responseTypeCode == ResponseTypeArrayBuffer) {
1082         // Buffer binary data.
1083         if (!m_binaryResponseBuilder)
1084             m_binaryResponseBuilder = SharedBuffer::create();
1085         m_binaryResponseBuilder->append(data, len);
1086     }
1087
1088     if (!m_error) {
1089         long long expectedLength = m_response.expectedContentLength();
1090         m_receivedLength += len;
1091
1092         if (m_async) {
1093             bool lengthComputable = expectedLength > 0 && m_receivedLength <= expectedLength;
1094             unsigned long long total = lengthComputable ? expectedLength : 0;
1095             m_progressEventThrottle.dispatchProgressEvent(lengthComputable, m_receivedLength, total);
1096         }
1097
1098         if (m_state != LOADING)
1099             changeState(LOADING);
1100         else
1101             // Firefox calls readyStateChanged every time it receives data, 4449442
1102             callReadyStateChangeListener();
1103     }
1104 }
1105
1106 bool XMLHttpRequest::canSuspend() const
1107 {
1108     return !m_loader;
1109 }
1110
1111 void XMLHttpRequest::suspend(ReasonForSuspension)
1112 {
1113     m_progressEventThrottle.suspend();
1114 }
1115
1116 void XMLHttpRequest::resume()
1117 {
1118     m_progressEventThrottle.resume();
1119 }
1120
1121 void XMLHttpRequest::stop()
1122 {
1123     internalAbort();
1124 }
1125
1126 void XMLHttpRequest::contextDestroyed()
1127 {
1128     ASSERT(!m_loader);
1129     ActiveDOMObject::contextDestroyed();
1130 }
1131
1132 const AtomicString& XMLHttpRequest::interfaceName() const
1133 {
1134     return eventNames().interfaceForXMLHttpRequest;
1135 }
1136
1137 ScriptExecutionContext* XMLHttpRequest::scriptExecutionContext() const
1138 {
1139     return ActiveDOMObject::scriptExecutionContext();
1140 }
1141
1142 EventTargetData* XMLHttpRequest::eventTargetData()
1143 {
1144     return &m_eventTargetData;
1145 }
1146
1147 EventTargetData* XMLHttpRequest::ensureEventTargetData()
1148 {
1149     return &m_eventTargetData;
1150 }
1151
1152 } // namespace WebCore