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