• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright (C) 2004, 2006, 2008 Apple Inc. All rights reserved.
3  *  Copyright (C) 2005-2007 Alexey Proskuryakov <ap@webkit.org>
4  *  Copyright (C) 2007, 2008 Julien Chaffraix <jchaffraix@webkit.org>
5  *  Copyright (C) 2008 David Levin <levin@chromium.org>
6  *
7  *  This library is free software; you can redistribute it and/or
8  *  modify it under the terms of the GNU Lesser General Public
9  *  License as published by the Free Software Foundation; either
10  *  version 2 of the License, or (at your option) any later version.
11  *
12  *  This library is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public
18  *  License along with this library; if not, write to the Free Software
19  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21 
22 #include "config.h"
23 #include "XMLHttpRequest.h"
24 
25 #include "Cache.h"
26 #include "CString.h"
27 #include "CrossOriginAccessControl.h"
28 #include "CrossOriginPreflightResultCache.h"
29 #include "DOMImplementation.h"
30 #include "Document.h"
31 #include "Event.h"
32 #include "EventException.h"
33 #include "EventListener.h"
34 #include "EventNames.h"
35 #include "File.h"
36 #include "HTTPParsers.h"
37 #include "ResourceError.h"
38 #include "ResourceRequest.h"
39 #include "SecurityOrigin.h"
40 #include "Settings.h"
41 #include "TextResourceDecoder.h"
42 #include "ThreadableLoader.h"
43 #include "XMLHttpRequestException.h"
44 #include "XMLHttpRequestProgressEvent.h"
45 #include "XMLHttpRequestUpload.h"
46 #include "markup.h"
47 #include <wtf/StdLibExtras.h>
48 
49 #if USE(JSC)
50 #include "JSDOMWindow.h"
51 #endif
52 
53 namespace WebCore {
54 
55 struct XMLHttpRequestStaticData {
56     XMLHttpRequestStaticData();
57     String m_proxyHeaderPrefix;
58     String m_secHeaderPrefix;
59     HashSet<String, CaseFoldingHash> m_forbiddenRequestHeaders;
60 };
61 
XMLHttpRequestStaticData()62 XMLHttpRequestStaticData::XMLHttpRequestStaticData()
63     : m_proxyHeaderPrefix("proxy-")
64     , m_secHeaderPrefix("sec-")
65 {
66     m_forbiddenRequestHeaders.add("accept-charset");
67     m_forbiddenRequestHeaders.add("accept-encoding");
68     m_forbiddenRequestHeaders.add("access-control-request-headers");
69     m_forbiddenRequestHeaders.add("access-control-request-method");
70     m_forbiddenRequestHeaders.add("connection");
71     m_forbiddenRequestHeaders.add("content-length");
72     m_forbiddenRequestHeaders.add("content-transfer-encoding");
73     m_forbiddenRequestHeaders.add("cookie");
74     m_forbiddenRequestHeaders.add("cookie2");
75     m_forbiddenRequestHeaders.add("date");
76     m_forbiddenRequestHeaders.add("expect");
77     m_forbiddenRequestHeaders.add("host");
78     m_forbiddenRequestHeaders.add("keep-alive");
79     m_forbiddenRequestHeaders.add("origin");
80     m_forbiddenRequestHeaders.add("referer");
81     m_forbiddenRequestHeaders.add("te");
82     m_forbiddenRequestHeaders.add("trailer");
83     m_forbiddenRequestHeaders.add("transfer-encoding");
84     m_forbiddenRequestHeaders.add("upgrade");
85     m_forbiddenRequestHeaders.add("user-agent");
86     m_forbiddenRequestHeaders.add("via");
87 }
88 
89 // Determines if a string is a valid token, as defined by
90 // "token" in section 2.2 of RFC 2616.
isValidToken(const String & name)91 static bool isValidToken(const String& name)
92 {
93     unsigned length = name.length();
94     for (unsigned i = 0; i < length; i++) {
95         UChar c = name[i];
96 
97         if (c >= 127 || c <= 32)
98             return false;
99 
100         if (c == '(' || c == ')' || c == '<' || c == '>' || c == '@' ||
101             c == ',' || c == ';' || c == ':' || c == '\\' || c == '\"' ||
102             c == '/' || c == '[' || c == ']' || c == '?' || c == '=' ||
103             c == '{' || c == '}')
104             return false;
105     }
106 
107     return true;
108 }
109 
isValidHeaderValue(const String & name)110 static bool isValidHeaderValue(const String& name)
111 {
112     // FIXME: This should really match name against
113     // field-value in section 4.2 of RFC 2616.
114 
115     return !name.contains('\r') && !name.contains('\n');
116 }
117 
isSetCookieHeader(const AtomicString & name)118 static bool isSetCookieHeader(const AtomicString& name)
119 {
120     return equalIgnoringCase(name, "set-cookie") || equalIgnoringCase(name, "set-cookie2");
121 }
122 
123 static const XMLHttpRequestStaticData* staticData = 0;
124 
createXMLHttpRequestStaticData()125 static const XMLHttpRequestStaticData* createXMLHttpRequestStaticData()
126 {
127     staticData = new XMLHttpRequestStaticData;
128     return staticData;
129 }
130 
initializeXMLHttpRequestStaticData()131 static const XMLHttpRequestStaticData* initializeXMLHttpRequestStaticData()
132 {
133     // Uses dummy to avoid warnings about an unused variable.
134     AtomicallyInitializedStatic(const XMLHttpRequestStaticData*, dummy = createXMLHttpRequestStaticData());
135     return dummy;
136 }
137 
XMLHttpRequest(ScriptExecutionContext * context)138 XMLHttpRequest::XMLHttpRequest(ScriptExecutionContext* context)
139     : ActiveDOMObject(context, this)
140     , m_async(true)
141     , m_includeCredentials(false)
142     , m_state(UNSENT)
143     , m_responseText("")
144     , m_createdDocument(false)
145     , m_error(false)
146     , m_uploadComplete(false)
147     , m_sameOriginRequest(true)
148     , m_inPreflight(false)
149     , m_didTellLoaderAboutRequest(false)
150     , m_receivedLength(0)
151     , m_lastSendLineNumber(0)
152     , m_exceptionCode(0)
153 {
154     initializeXMLHttpRequestStaticData();
155 }
156 
~XMLHttpRequest()157 XMLHttpRequest::~XMLHttpRequest()
158 {
159     if (m_didTellLoaderAboutRequest) {
160         cache()->loader()->nonCacheRequestComplete(m_url);
161         m_didTellLoaderAboutRequest = false;
162     }
163     if (m_upload)
164         m_upload->disconnectXMLHttpRequest();
165 }
166 
document() const167 Document* XMLHttpRequest::document() const
168 {
169     ASSERT(scriptExecutionContext()->isDocument());
170     return static_cast<Document*>(scriptExecutionContext());
171 }
172 
173 #if ENABLE(DASHBOARD_SUPPORT)
usesDashboardBackwardCompatibilityMode() const174 bool XMLHttpRequest::usesDashboardBackwardCompatibilityMode() const
175 {
176     if (scriptExecutionContext()->isWorkerContext())
177         return false;
178     Settings* settings = document()->settings();
179     return settings && settings->usesDashboardBackwardCompatibilityMode();
180 }
181 #endif
182 
readyState() const183 XMLHttpRequest::State XMLHttpRequest::readyState() const
184 {
185     return m_state;
186 }
187 
responseText() const188 const ScriptString& XMLHttpRequest::responseText() const
189 {
190     return m_responseText;
191 }
192 
responseXML() const193 Document* XMLHttpRequest::responseXML() const
194 {
195     if (m_state != DONE)
196         return 0;
197 
198     if (!m_createdDocument) {
199         if ((m_response.isHTTP() && !responseIsXML()) || scriptExecutionContext()->isWorkerContext()) {
200             // The W3C spec requires this.
201             m_responseXML = 0;
202         } else {
203             m_responseXML = document()->implementation()->createDocument(0);
204             m_responseXML->open();
205             m_responseXML->setURL(m_url);
206             // FIXME: Set Last-Modified.
207             m_responseXML->write(String(m_responseText));
208             m_responseXML->finishParsing();
209             m_responseXML->close();
210 
211             if (!m_responseXML->wellFormed())
212                 m_responseXML = 0;
213         }
214         m_createdDocument = true;
215     }
216 
217     return m_responseXML.get();
218 }
219 
upload()220 XMLHttpRequestUpload* XMLHttpRequest::upload()
221 {
222     if (!m_upload)
223         m_upload = XMLHttpRequestUpload::create(this);
224     return m_upload.get();
225 }
226 
addEventListener(const AtomicString & eventType,PassRefPtr<EventListener> eventListener,bool)227 void XMLHttpRequest::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> eventListener, bool)
228 {
229     EventListenersMap::iterator iter = m_eventListeners.find(eventType);
230     if (iter == m_eventListeners.end()) {
231         ListenerVector listeners;
232         listeners.append(eventListener);
233         m_eventListeners.add(eventType, listeners);
234     } else {
235         ListenerVector& listeners = iter->second;
236         for (ListenerVector::iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter)
237             if (*listenerIter == eventListener)
238                 return;
239 
240         listeners.append(eventListener);
241         m_eventListeners.add(eventType, listeners);
242     }
243 }
244 
removeEventListener(const AtomicString & eventType,EventListener * eventListener,bool)245 void XMLHttpRequest::removeEventListener(const AtomicString& eventType, EventListener* eventListener, bool)
246 {
247     EventListenersMap::iterator iter = m_eventListeners.find(eventType);
248     if (iter == m_eventListeners.end())
249         return;
250 
251     ListenerVector& listeners = iter->second;
252     for (ListenerVector::const_iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter)
253         if (*listenerIter == eventListener) {
254             listeners.remove(listenerIter - listeners.begin());
255             return;
256         }
257 }
258 
dispatchEvent(PassRefPtr<Event> evt,ExceptionCode & ec)259 bool XMLHttpRequest::dispatchEvent(PassRefPtr<Event> evt, ExceptionCode& ec)
260 {
261     // FIXME: check for other error conditions enumerated in the spec.
262     if (!evt || evt->type().isEmpty()) {
263         ec = EventException::UNSPECIFIED_EVENT_TYPE_ERR;
264         return true;
265     }
266 
267     ListenerVector listenersCopy = m_eventListeners.get(evt->type());
268     for (ListenerVector::const_iterator listenerIter = listenersCopy.begin(); listenerIter != listenersCopy.end(); ++listenerIter) {
269         evt->setTarget(this);
270         evt->setCurrentTarget(this);
271         listenerIter->get()->handleEvent(evt.get(), false);
272     }
273 
274     return !evt->defaultPrevented();
275 }
276 
changeState(State newState)277 void XMLHttpRequest::changeState(State newState)
278 {
279     if (m_state != newState) {
280         m_state = newState;
281         callReadyStateChangeListener();
282     }
283 }
284 
callReadyStateChangeListener()285 void XMLHttpRequest::callReadyStateChangeListener()
286 {
287     if (!scriptExecutionContext())
288         return;
289 
290     dispatchReadyStateChangeEvent();
291 
292     if (m_state == DONE && !m_error)
293         dispatchLoadEvent();
294 }
295 
setWithCredentials(bool value,ExceptionCode & ec)296 void XMLHttpRequest::setWithCredentials(bool value, ExceptionCode& ec)
297 {
298     if (m_state != OPENED || m_loader) {
299         ec = INVALID_STATE_ERR;
300         return;
301     }
302 
303     m_includeCredentials = value;
304 }
305 
open(const String & method,const KURL & url,bool async,ExceptionCode & ec)306 void XMLHttpRequest::open(const String& method, const KURL& url, bool async, ExceptionCode& ec)
307 {
308     internalAbort();
309     State previousState = m_state;
310     m_state = UNSENT;
311     m_error = false;
312 
313     m_uploadComplete = false;
314 
315     // clear stuff from possible previous load
316     clearResponse();
317     clearRequest();
318 
319     ASSERT(m_state == UNSENT);
320 
321     if (!isValidToken(method)) {
322         ec = SYNTAX_ERR;
323         return;
324     }
325 
326     // Method names are case sensitive. But since Firefox uppercases method names it knows, we'll do the same.
327     String methodUpper(method.upper());
328 
329     if (methodUpper == "TRACE" || methodUpper == "TRACK" || methodUpper == "CONNECT") {
330         ec = SECURITY_ERR;
331         return;
332     }
333 
334     m_url = url;
335 
336     if (methodUpper == "COPY" || methodUpper == "DELETE" || methodUpper == "GET" || methodUpper == "HEAD"
337         || methodUpper == "INDEX" || methodUpper == "LOCK" || methodUpper == "M-POST" || methodUpper == "MKCOL" || methodUpper == "MOVE"
338         || methodUpper == "OPTIONS" || methodUpper == "POST" || methodUpper == "PROPFIND" || methodUpper == "PROPPATCH" || methodUpper == "PUT"
339         || methodUpper == "UNLOCK")
340         m_method = methodUpper;
341     else
342         m_method = method;
343 
344     m_async = async;
345 
346     ASSERT(!m_loader);
347 
348     // Check previous state to avoid dispatching readyState event
349     // when calling open several times in a row.
350     if (previousState != OPENED)
351         changeState(OPENED);
352     else
353         m_state = OPENED;
354 }
355 
open(const String & method,const KURL & url,bool async,const String & user,ExceptionCode & ec)356 void XMLHttpRequest::open(const String& method, const KURL& url, bool async, const String& user, ExceptionCode& ec)
357 {
358     KURL urlWithCredentials(url);
359     urlWithCredentials.setUser(user);
360 
361     open(method, urlWithCredentials, async, ec);
362 }
363 
open(const String & method,const KURL & url,bool async,const String & user,const String & password,ExceptionCode & ec)364 void XMLHttpRequest::open(const String& method, const KURL& url, bool async, const String& user, const String& password, ExceptionCode& ec)
365 {
366     KURL urlWithCredentials(url);
367     urlWithCredentials.setUser(user);
368     urlWithCredentials.setPass(password);
369 
370     open(method, urlWithCredentials, async, ec);
371 }
372 
initSend(ExceptionCode & ec)373 bool XMLHttpRequest::initSend(ExceptionCode& ec)
374 {
375     if (!scriptExecutionContext())
376         return false;
377 
378     if (m_state != OPENED || m_loader) {
379         ec = INVALID_STATE_ERR;
380         return false;
381     }
382 
383     m_error = false;
384     return true;
385 }
386 
send(ExceptionCode & ec)387 void XMLHttpRequest::send(ExceptionCode& ec)
388 {
389     send(String(), ec);
390 }
391 
send(Document * document,ExceptionCode & ec)392 void XMLHttpRequest::send(Document* document, ExceptionCode& ec)
393 {
394     ASSERT(document);
395 
396     if (!initSend(ec))
397         return;
398 
399     if (m_method != "GET" && m_method != "HEAD" && m_url.protocolInHTTPFamily()) {
400         String contentType = getRequestHeader("Content-Type");
401         if (contentType.isEmpty()) {
402 #if ENABLE(DASHBOARD_SUPPORT)
403             if (usesDashboardBackwardCompatibilityMode())
404                 setRequestHeaderInternal("Content-Type", "application/x-www-form-urlencoded");
405             else
406 #endif
407                 // FIXME: this should include the charset used for encoding.
408                 setRequestHeaderInternal("Content-Type", "application/xml");
409         }
410 
411         // FIXME: According to XMLHttpRequest Level 2, this should use the Document.innerHTML algorithm
412         // from the HTML5 specification to serialize the document.
413         String body = createMarkup(document);
414 
415         // FIXME: this should use value of document.inputEncoding to determine the encoding to use.
416         TextEncoding encoding = UTF8Encoding();
417         m_requestEntityBody = FormData::create(encoding.encode(body.characters(), body.length(), EntitiesForUnencodables));
418         if (m_upload)
419             m_requestEntityBody->setAlwaysStream(true);
420     }
421 
422     createRequest(ec);
423 }
424 
send(const String & body,ExceptionCode & ec)425 void XMLHttpRequest::send(const String& body, ExceptionCode& ec)
426 {
427     if (!initSend(ec))
428         return;
429 
430     if (!body.isNull() && m_method != "GET" && m_method != "HEAD" && m_url.protocolInHTTPFamily()) {
431         String contentType = getRequestHeader("Content-Type");
432         if (contentType.isEmpty()) {
433 #if ENABLE(DASHBOARD_SUPPORT)
434             if (usesDashboardBackwardCompatibilityMode())
435                 setRequestHeaderInternal("Content-Type", "application/x-www-form-urlencoded");
436             else
437 #endif
438                 setRequestHeaderInternal("Content-Type", "application/xml");
439         }
440 
441         m_requestEntityBody = FormData::create(UTF8Encoding().encode(body.characters(), body.length(), EntitiesForUnencodables));
442         if (m_upload)
443             m_requestEntityBody->setAlwaysStream(true);
444     }
445 
446     createRequest(ec);
447 }
448 
send(File * body,ExceptionCode & ec)449 void XMLHttpRequest::send(File* body, ExceptionCode& ec)
450 {
451     if (!initSend(ec))
452         return;
453 
454     if (m_method != "GET" && m_method != "HEAD" && m_url.protocolInHTTPFamily()) {
455         // FIXME: Should we set a Content-Type if one is not set.
456         // FIXME: add support for uploading bundles.
457         m_requestEntityBody = FormData::create();
458         m_requestEntityBody->appendFile(body->path(), false);
459     }
460 
461     createRequest(ec);
462 }
463 
createRequest(ExceptionCode & ec)464 void XMLHttpRequest::createRequest(ExceptionCode& ec)
465 {
466     // Upload event listeners should be disallowed for simple cross-origin requests, because POSTing to an URL that does not
467     // permit cross origin requests should look exactly like POSTing to an URL that does not respond at all. If a listener exists
468     // when creating the request, it will force preflight.
469     // Also, only async requests support upload progress events.
470     m_uploadEventsAllowed = false;
471     if (m_async) {
472         dispatchLoadStartEvent();
473         if (m_requestEntityBody && m_upload) {
474             m_uploadEventsAllowed = m_upload->hasListeners();
475             m_upload->dispatchLoadStartEvent();
476         }
477     }
478 
479     m_sameOriginRequest = scriptExecutionContext()->securityOrigin()->canRequest(m_url);
480 
481     if (!m_sameOriginRequest) {
482         makeCrossOriginAccessRequest(ec);
483         return;
484     }
485 
486     m_uploadEventsAllowed = true;
487 
488     makeSameOriginRequest(ec);
489 }
490 
makeSameOriginRequest(ExceptionCode & ec)491 void XMLHttpRequest::makeSameOriginRequest(ExceptionCode& ec)
492 {
493     ASSERT(m_sameOriginRequest);
494 
495     ResourceRequest request(m_url);
496     request.setHTTPMethod(m_method);
497 
498     if (m_requestEntityBody) {
499         ASSERT(m_method != "GET");
500         ASSERT(m_method != "HEAD");
501         request.setHTTPBody(m_requestEntityBody.release());
502     }
503 
504     if (m_requestHeaders.size() > 0)
505         request.addHTTPHeaderFields(m_requestHeaders);
506 
507     if (m_async)
508         loadRequestAsynchronously(request);
509     else
510         loadRequestSynchronously(request, ec);
511 }
512 
makeCrossOriginAccessRequest(ExceptionCode & ec)513 void XMLHttpRequest::makeCrossOriginAccessRequest(ExceptionCode& ec)
514 {
515     ASSERT(!m_sameOriginRequest);
516 
517     if (!m_uploadEventsAllowed && isSimpleCrossOriginAccessRequest(m_method, m_requestHeaders))
518         makeSimpleCrossOriginAccessRequest(ec);
519     else
520         makeCrossOriginAccessRequestWithPreflight(ec);
521 }
522 
makeSimpleCrossOriginAccessRequest(ExceptionCode & ec)523 void XMLHttpRequest::makeSimpleCrossOriginAccessRequest(ExceptionCode& ec)
524 {
525     ASSERT(isSimpleCrossOriginAccessRequest(m_method, m_requestHeaders));
526 
527     // Cross-origin requests are only defined for HTTP. We would catch this when checking response headers later, but there is no reason to send a request that's guaranteed to be denied.
528     if (!m_url.protocolInHTTPFamily()) {
529         ec = XMLHttpRequestException::NETWORK_ERR;
530         networkError();
531         return;
532     }
533 
534     KURL url = m_url;
535     url.setUser(String());
536     url.setPass(String());
537 
538     ResourceRequest request(url);
539     request.setHTTPMethod(m_method);
540     request.setAllowHTTPCookies(m_includeCredentials);
541     request.setHTTPOrigin(scriptExecutionContext()->securityOrigin()->toString());
542 
543     if (m_requestHeaders.size() > 0)
544         request.addHTTPHeaderFields(m_requestHeaders);
545 
546     if (m_requestEntityBody) {
547         ASSERT(m_method != "GET");
548         ASSERT(m_method != "HEAD");
549         request.setHTTPBody(m_requestEntityBody.release());
550     }
551 
552     if (m_async)
553         loadRequestAsynchronously(request);
554     else
555         loadRequestSynchronously(request, ec);
556 }
557 
makeCrossOriginAccessRequestWithPreflight(ExceptionCode & ec)558 void XMLHttpRequest::makeCrossOriginAccessRequestWithPreflight(ExceptionCode& ec)
559 {
560     String origin = scriptExecutionContext()->securityOrigin()->toString();
561     KURL url = m_url;
562     url.setUser(String());
563     url.setPass(String());
564 
565     if (!CrossOriginPreflightResultCache::shared().canSkipPreflight(origin, url, m_includeCredentials, m_method, m_requestHeaders)) {
566         m_inPreflight = true;
567         ResourceRequest preflightRequest(url);
568         preflightRequest.setHTTPMethod("OPTIONS");
569         preflightRequest.setHTTPHeaderField("Origin", origin);
570         preflightRequest.setHTTPHeaderField("Access-Control-Request-Method", m_method);
571 
572         if (m_requestHeaders.size() > 0) {
573             Vector<UChar> headerBuffer;
574             HTTPHeaderMap::const_iterator it = m_requestHeaders.begin();
575             append(headerBuffer, it->first);
576             ++it;
577 
578             HTTPHeaderMap::const_iterator end = m_requestHeaders.end();
579             for (; it != end; ++it) {
580                 headerBuffer.append(',');
581                 headerBuffer.append(' ');
582                 append(headerBuffer, it->first);
583             }
584 
585             preflightRequest.setHTTPHeaderField("Access-Control-Request-Headers", String::adopt(headerBuffer));
586             preflightRequest.addHTTPHeaderFields(m_requestHeaders);
587         }
588 
589         if (m_async) {
590             m_uploadEventsAllowed = true;
591             loadRequestAsynchronously(preflightRequest);
592             return;
593         }
594 
595         loadRequestSynchronously(preflightRequest, ec);
596         m_inPreflight = false;
597 
598         if (ec)
599             return;
600     }
601 
602     // Send the actual request.
603     ResourceRequest request(url);
604     request.setHTTPMethod(m_method);
605     request.setAllowHTTPCookies(m_includeCredentials);
606     request.setHTTPHeaderField("Origin", origin);
607 
608     if (m_requestHeaders.size() > 0)
609         request.addHTTPHeaderFields(m_requestHeaders);
610 
611     if (m_requestEntityBody) {
612         ASSERT(m_method != "GET");
613         ASSERT(m_method != "HEAD");
614         request.setHTTPBody(m_requestEntityBody.release());
615     }
616 
617     if (m_async) {
618         m_uploadEventsAllowed = true;
619         loadRequestAsynchronously(request);
620         return;
621     }
622 
623     loadRequestSynchronously(request, ec);
624 }
625 
handleAsynchronousPreflightResult()626 void XMLHttpRequest::handleAsynchronousPreflightResult()
627 {
628     ASSERT(m_inPreflight);
629     ASSERT(m_async);
630 
631     m_inPreflight = false;
632 
633     KURL url = m_url;
634     url.setUser(String());
635     url.setPass(String());
636 
637     ResourceRequest request(url);
638     request.setHTTPMethod(m_method);
639     request.setAllowHTTPCookies(m_includeCredentials);
640     request.setHTTPOrigin(scriptExecutionContext()->securityOrigin()->toString());
641 
642     if (m_requestHeaders.size() > 0)
643         request.addHTTPHeaderFields(m_requestHeaders);
644 
645     if (m_requestEntityBody) {
646         ASSERT(m_method != "GET");
647         ASSERT(m_method != "HEAD");
648         request.setHTTPBody(m_requestEntityBody.release());
649     }
650 
651     m_uploadEventsAllowed = true;
652     loadRequestAsynchronously(request);
653 }
654 
loadRequestSynchronously(ResourceRequest & request,ExceptionCode & ec)655 void XMLHttpRequest::loadRequestSynchronously(ResourceRequest& request, ExceptionCode& ec)
656 {
657     ASSERT(!m_async);
658 
659     m_loader = 0;
660     m_exceptionCode = 0;
661     StoredCredentials storedCredentials = (m_sameOriginRequest || m_includeCredentials) ? AllowStoredCredentials : DoNotAllowStoredCredentials;
662 
663     ThreadableLoader::loadResourceSynchronously(scriptExecutionContext(), request, *this, storedCredentials);
664     if (!m_exceptionCode && m_error)
665         m_exceptionCode = XMLHttpRequestException::NETWORK_ERR;
666     ec = m_exceptionCode;
667 }
668 
loadRequestAsynchronously(ResourceRequest & request)669 void XMLHttpRequest::loadRequestAsynchronously(ResourceRequest& request)
670 {
671     ASSERT(m_async);
672     m_exceptionCode = 0;
673     // SubresourceLoader::create can return null here, for example if we're no longer attached to a page.
674     // This is true while running onunload handlers.
675     // FIXME: We need to be able to send XMLHttpRequests from onunload, <http://bugs.webkit.org/show_bug.cgi?id=10904>.
676     // FIXME: Maybe create can return null for other reasons too?
677     LoadCallbacks callbacks = m_inPreflight ? DoNotSendLoadCallbacks : SendLoadCallbacks;
678     StoredCredentials storedCredentials = (m_sameOriginRequest || m_includeCredentials) ? AllowStoredCredentials : DoNotAllowStoredCredentials;
679 
680     if (m_upload)
681         request.setReportUploadProgress(true);
682 
683     m_loader = ThreadableLoader::create(scriptExecutionContext(), this, request, callbacks, DoNotSniffContent, storedCredentials, DenyCrossOriginRedirect);
684 
685     if (m_loader) {
686         // Neither this object nor the JavaScript wrapper should be deleted while
687         // a request is in progress because we need to keep the listeners alive,
688         // and they are referenced by the JavaScript wrapper.
689         setPendingActivity(this);
690 
691         // For now we should only balance the nonCached request count for main-thread XHRs and not
692         // Worker XHRs, as the Cache is not thread-safe.
693         // This will become irrelevant after https://bugs.webkit.org/show_bug.cgi?id=27165 is resolved.
694         if (!scriptExecutionContext()->isWorkerContext()) {
695             ASSERT(isMainThread());
696             ASSERT(!m_didTellLoaderAboutRequest);
697             cache()->loader()->nonCacheRequestInFlight(m_url);
698             m_didTellLoaderAboutRequest = true;
699         }
700     }
701 }
702 
abort()703 void XMLHttpRequest::abort()
704 {
705     // internalAbort() calls dropProtection(), which may release the last reference.
706     RefPtr<XMLHttpRequest> protect(this);
707 
708     bool sendFlag = m_loader;
709 
710     internalAbort();
711 
712     // Clear headers as required by the spec
713     m_requestHeaders.clear();
714 
715     if ((m_state <= OPENED && !sendFlag) || m_state == DONE)
716         m_state = UNSENT;
717     else {
718         ASSERT(!m_loader);
719         changeState(DONE);
720         m_state = UNSENT;
721     }
722 
723     dispatchAbortEvent();
724     if (!m_uploadComplete) {
725         m_uploadComplete = true;
726         if (m_upload && m_uploadEventsAllowed)
727             m_upload->dispatchAbortEvent();
728     }
729 }
730 
internalAbort()731 void XMLHttpRequest::internalAbort()
732 {
733     bool hadLoader = m_loader;
734 
735     m_error = true;
736 
737     // FIXME: when we add the support for multi-part XHR, we will have to think be careful with this initialization.
738     m_receivedLength = 0;
739 
740     if (hadLoader) {
741         m_loader->cancel();
742         m_loader = 0;
743     }
744 
745     m_decoder = 0;
746 
747     if (hadLoader)
748         dropProtection();
749 }
750 
clearResponse()751 void XMLHttpRequest::clearResponse()
752 {
753     m_response = ResourceResponse();
754     m_responseText = "";
755     m_createdDocument = false;
756     m_responseXML = 0;
757 }
758 
clearRequest()759 void XMLHttpRequest::clearRequest()
760 {
761     m_requestHeaders.clear();
762     m_requestEntityBody = 0;
763 }
764 
genericError()765 void XMLHttpRequest::genericError()
766 {
767     clearResponse();
768     clearRequest();
769     m_error = true;
770 
771     changeState(DONE);
772 }
773 
networkError()774 void XMLHttpRequest::networkError()
775 {
776     genericError();
777     dispatchErrorEvent();
778     if (!m_uploadComplete) {
779         m_uploadComplete = true;
780         if (m_upload && m_uploadEventsAllowed)
781             m_upload->dispatchErrorEvent();
782     }
783     internalAbort();
784 }
785 
abortError()786 void XMLHttpRequest::abortError()
787 {
788     genericError();
789     dispatchAbortEvent();
790     if (!m_uploadComplete) {
791         m_uploadComplete = true;
792         if (m_upload && m_uploadEventsAllowed)
793             m_upload->dispatchAbortEvent();
794     }
795 }
796 
dropProtection()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 
807     if (JSDOMGlobalObject* globalObject = toJSDOMGlobalObject(scriptExecutionContext()))
808         if (DOMObject* wrapper = getCachedDOMObjectWrapper(*globalObject->globalData(), this))
809             JSC::Heap::heap(wrapper)->reportExtraMemoryCost(m_responseText.size() * 2);
810 #endif
811 
812     unsetPendingActivity(this);
813 }
814 
overrideMimeType(const String & override)815 void XMLHttpRequest::overrideMimeType(const String& override)
816 {
817     m_mimeTypeOverride = override;
818 }
819 
reportUnsafeUsage(ScriptExecutionContext * context,const String & message)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->addMessage(ConsoleDestination, JSMessageSource, LogMessageType, ErrorMessageLevel, message, 1, String());
827 }
828 
setRequestHeader(const AtomicString & name,const String & value,ExceptionCode & ec)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 (!isValidToken(name) || !isValidHeaderValue(value)) {
842         ec = SYNTAX_ERR;
843         return;
844     }
845 
846     // A privileged script (e.g. a Dashboard widget) can set any headers.
847     if (!scriptExecutionContext()->securityOrigin()->canLoadLocalResources() && !isSafeRequestHeader(name)) {
848         reportUnsafeUsage(scriptExecutionContext(), "Refused to set unsafe header \"" + name + "\"");
849         return;
850     }
851 
852     setRequestHeaderInternal(name, value);
853 }
854 
setRequestHeaderInternal(const AtomicString & name,const String & value)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 
isSafeRequestHeader(const String & name) const862 bool XMLHttpRequest::isSafeRequestHeader(const String& name) const
863 {
864     return !staticData->m_forbiddenRequestHeaders.contains(name) && !name.startsWith(staticData->m_proxyHeaderPrefix, false)
865         && !name.startsWith(staticData->m_secHeaderPrefix, false);
866 }
867 
getRequestHeader(const AtomicString & name) const868 String XMLHttpRequest::getRequestHeader(const AtomicString& name) const
869 {
870     return m_requestHeaders.get(name);
871 }
872 
getAllResponseHeaders(ExceptionCode & ec) const873 String XMLHttpRequest::getAllResponseHeaders(ExceptionCode& ec) const
874 {
875     if (m_state < LOADING) {
876         ec = INVALID_STATE_ERR;
877         return "";
878     }
879 
880     Vector<UChar> stringBuilder;
881 
882     HTTPHeaderMap::const_iterator end = m_response.httpHeaderFields().end();
883     for (HTTPHeaderMap::const_iterator it = m_response.httpHeaderFields().begin(); it!= end; ++it) {
884         // Hide Set-Cookie header fields from the XMLHttpRequest client for these reasons:
885         //     1) If the client did have access to the fields, then it could read HTTP-only
886         //        cookies; those cookies are supposed to be hidden from scripts.
887         //     2) There's no known harm in hiding Set-Cookie header fields entirely; we don't
888         //        know any widely used technique that requires access to them.
889         //     3) Firefox has implemented this policy.
890         if (isSetCookieHeader(it->first) && !scriptExecutionContext()->securityOrigin()->canLoadLocalResources())
891             continue;
892 
893         if (!m_sameOriginRequest && !isOnAccessControlResponseHeaderWhitelist(it->first))
894             continue;
895 
896         stringBuilder.append(it->first.characters(), it->first.length());
897         stringBuilder.append(':');
898         stringBuilder.append(' ');
899         stringBuilder.append(it->second.characters(), it->second.length());
900         stringBuilder.append('\r');
901         stringBuilder.append('\n');
902     }
903 
904     return String::adopt(stringBuilder);
905 }
906 
getResponseHeader(const AtomicString & name,ExceptionCode & ec) const907 String XMLHttpRequest::getResponseHeader(const AtomicString& name, ExceptionCode& ec) const
908 {
909     if (m_state < LOADING) {
910         ec = INVALID_STATE_ERR;
911         return "";
912     }
913 
914     if (!isValidToken(name))
915         return "";
916 
917     // See comment in getAllResponseHeaders above.
918     if (isSetCookieHeader(name) && !scriptExecutionContext()->securityOrigin()->canLoadLocalResources()) {
919         reportUnsafeUsage(scriptExecutionContext(), "Refused to get unsafe header \"" + name + "\"");
920         return "";
921     }
922 
923     if (!m_sameOriginRequest && !isOnAccessControlResponseHeaderWhitelist(name)) {
924         reportUnsafeUsage(scriptExecutionContext(), "Refused to get unsafe header \"" + name + "\"");
925         return "";
926     }
927 
928     return m_response.httpHeaderField(name);
929 }
930 
responseMIMEType() const931 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 
responseIsXML() const946 bool XMLHttpRequest::responseIsXML() const
947 {
948     return DOMImplementation::isXMLMIMEType(responseMIMEType());
949 }
950 
status(ExceptionCode & ec) const951 int XMLHttpRequest::status(ExceptionCode& ec) const
952 {
953     if (m_response.httpStatusCode())
954         return m_response.httpStatusCode();
955 
956     if (m_state == OPENED) {
957         // Firefox only raises an exception in this state; we match it.
958         // 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.
959         ec = INVALID_STATE_ERR;
960     }
961 
962     return 0;
963 }
964 
statusText(ExceptionCode & ec) const965 String XMLHttpRequest::statusText(ExceptionCode& ec) const
966 {
967     if (!m_response.httpStatusText().isNull())
968         return m_response.httpStatusText();
969 
970     if (m_state == OPENED) {
971         // See comments in status() above.
972         ec = INVALID_STATE_ERR;
973     }
974 
975     return String();
976 }
977 
didFail(const ResourceError & error)978 void XMLHttpRequest::didFail(const ResourceError& error)
979 {
980     if (m_didTellLoaderAboutRequest) {
981         cache()->loader()->nonCacheRequestComplete(m_url);
982         m_didTellLoaderAboutRequest = false;
983     }
984 
985     // If we are already in an error state, for instance we called abort(), bail out early.
986     if (m_error)
987         return;
988 
989     if (error.isCancellation()) {
990         m_exceptionCode = XMLHttpRequestException::ABORT_ERR;
991         abortError();
992         return;
993     }
994 
995     m_exceptionCode = XMLHttpRequestException::NETWORK_ERR;
996     networkError();
997 }
998 
didFailRedirectCheck()999 void XMLHttpRequest::didFailRedirectCheck()
1000 {
1001     networkError();
1002 }
1003 
didFinishLoading(unsigned long identifier)1004 void XMLHttpRequest::didFinishLoading(unsigned long identifier)
1005 {
1006     if (m_didTellLoaderAboutRequest) {
1007         cache()->loader()->nonCacheRequestComplete(m_url);
1008         m_didTellLoaderAboutRequest = false;
1009     }
1010 
1011     if (m_error)
1012         return;
1013 
1014     if (m_inPreflight) {
1015         didFinishLoadingPreflight();
1016         return;
1017     }
1018 
1019     if (m_state < HEADERS_RECEIVED)
1020         changeState(HEADERS_RECEIVED);
1021 
1022     if (m_decoder)
1023         m_responseText += m_decoder->flush();
1024 
1025     scriptExecutionContext()->resourceRetrievedByXMLHttpRequest(identifier, m_responseText);
1026     scriptExecutionContext()->addMessage(InspectorControllerDestination, JSMessageSource, LogMessageType, LogMessageLevel, "XHR finished loading: \"" + m_url + "\".", m_lastSendLineNumber, m_lastSendURL);
1027 
1028     bool hadLoader = m_loader;
1029     m_loader = 0;
1030 
1031     changeState(DONE);
1032     m_decoder = 0;
1033 
1034     if (hadLoader)
1035         dropProtection();
1036 }
1037 
didFinishLoadingPreflight()1038 void XMLHttpRequest::didFinishLoadingPreflight()
1039 {
1040     ASSERT(m_inPreflight);
1041     ASSERT(!m_sameOriginRequest);
1042 
1043     // FIXME: this can probably be moved to didReceiveResponsePreflight.
1044     if (m_async)
1045         handleAsynchronousPreflightResult();
1046 
1047     if (m_loader)
1048         unsetPendingActivity(this);
1049 }
1050 
didSendData(unsigned long long bytesSent,unsigned long long totalBytesToBeSent)1051 void XMLHttpRequest::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
1052 {
1053     if (!m_upload)
1054         return;
1055 
1056     if (m_uploadEventsAllowed)
1057         m_upload->dispatchProgressEvent(bytesSent, totalBytesToBeSent);
1058 
1059     if (bytesSent == totalBytesToBeSent && !m_uploadComplete) {
1060         m_uploadComplete = true;
1061         if (m_uploadEventsAllowed)
1062             m_upload->dispatchLoadEvent();
1063     }
1064 }
1065 
didReceiveResponse(const ResourceResponse & response)1066 void XMLHttpRequest::didReceiveResponse(const ResourceResponse& response)
1067 {
1068     if (m_inPreflight) {
1069         didReceiveResponsePreflight(response);
1070         return;
1071     }
1072 
1073     if (!m_sameOriginRequest) {
1074         if (!passesAccessControlCheck(response, m_includeCredentials, scriptExecutionContext()->securityOrigin())) {
1075             networkError();
1076             return;
1077         }
1078     }
1079 
1080     m_response = response;
1081     m_responseEncoding = extractCharsetFromMediaType(m_mimeTypeOverride);
1082     if (m_responseEncoding.isEmpty())
1083         m_responseEncoding = response.textEncodingName();
1084 }
1085 
didReceiveResponsePreflight(const ResourceResponse & response)1086 void XMLHttpRequest::didReceiveResponsePreflight(const ResourceResponse& response)
1087 {
1088     ASSERT(m_inPreflight);
1089     ASSERT(!m_sameOriginRequest);
1090 
1091     if (!passesAccessControlCheck(response, m_includeCredentials, scriptExecutionContext()->securityOrigin())) {
1092         networkError();
1093         return;
1094     }
1095 
1096     OwnPtr<CrossOriginPreflightResultCacheItem> preflightResult(new CrossOriginPreflightResultCacheItem(m_includeCredentials));
1097     if (!preflightResult->parse(response)
1098         || !preflightResult->allowsCrossOriginMethod(m_method)
1099         || !preflightResult->allowsCrossOriginHeaders(m_requestHeaders)) {
1100         networkError();
1101         return;
1102     }
1103 
1104     CrossOriginPreflightResultCache::shared().appendEntry(scriptExecutionContext()->securityOrigin()->toString(), m_url, preflightResult.release());
1105 }
1106 
didReceiveAuthenticationCancellation(const ResourceResponse & failureResponse)1107 void XMLHttpRequest::didReceiveAuthenticationCancellation(const ResourceResponse& failureResponse)
1108 {
1109     m_response = failureResponse;
1110 }
1111 
didReceiveData(const char * data,int len)1112 void XMLHttpRequest::didReceiveData(const char* data, int len)
1113 {
1114     if (m_inPreflight || m_error)
1115         return;
1116 
1117     if (m_state < HEADERS_RECEIVED)
1118         changeState(HEADERS_RECEIVED);
1119 
1120     if (!m_decoder) {
1121         if (!m_responseEncoding.isEmpty())
1122             m_decoder = TextResourceDecoder::create("text/plain", m_responseEncoding);
1123         // allow TextResourceDecoder to look inside the m_response if it's XML or HTML
1124         else if (responseIsXML()) {
1125             m_decoder = TextResourceDecoder::create("application/xml");
1126             // 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.
1127             m_decoder->useLenientXMLDecoding();
1128         } else if (responseMIMEType() == "text/html")
1129             m_decoder = TextResourceDecoder::create("text/html", "UTF-8");
1130         else
1131             m_decoder = TextResourceDecoder::create("text/plain", "UTF-8");
1132     }
1133 
1134     if (!len)
1135         return;
1136 
1137     if (len == -1)
1138         len = strlen(data);
1139 
1140     m_responseText += m_decoder->decode(data, len);
1141 
1142     if (!m_error) {
1143         updateAndDispatchOnProgress(len);
1144 
1145         if (m_state != LOADING)
1146             changeState(LOADING);
1147         else
1148             // Firefox calls readyStateChanged every time it receives data, 4449442
1149             callReadyStateChangeListener();
1150     }
1151 }
1152 
updateAndDispatchOnProgress(unsigned int len)1153 void XMLHttpRequest::updateAndDispatchOnProgress(unsigned int len)
1154 {
1155     long long expectedLength = m_response.expectedContentLength();
1156     m_receivedLength += len;
1157 
1158     // FIXME: the spec requires that we dispatch the event according to the least
1159     // frequent method between every 350ms (+/-200ms) and for every byte received.
1160     dispatchProgressEvent(expectedLength);
1161 }
1162 
dispatchReadyStateChangeEvent()1163 void XMLHttpRequest::dispatchReadyStateChangeEvent()
1164 {
1165     RefPtr<Event> evt = Event::create(eventNames().readystatechangeEvent, false, false);
1166     if (m_onReadyStateChangeListener) {
1167         evt->setTarget(this);
1168         evt->setCurrentTarget(this);
1169         m_onReadyStateChangeListener->handleEvent(evt.get(), false);
1170     }
1171 
1172     ExceptionCode ec = 0;
1173     dispatchEvent(evt.release(), ec);
1174     ASSERT(!ec);
1175 }
1176 
dispatchXMLHttpRequestProgressEvent(EventListener * listener,const AtomicString & type,bool lengthComputable,unsigned loaded,unsigned total)1177 void XMLHttpRequest::dispatchXMLHttpRequestProgressEvent(EventListener* listener, const AtomicString& type, bool lengthComputable, unsigned loaded, unsigned total)
1178 {
1179     RefPtr<XMLHttpRequestProgressEvent> evt = XMLHttpRequestProgressEvent::create(type, lengthComputable, loaded, total);
1180     if (listener) {
1181         evt->setTarget(this);
1182         evt->setCurrentTarget(this);
1183         listener->handleEvent(evt.get(), false);
1184     }
1185 
1186     ExceptionCode ec = 0;
1187     dispatchEvent(evt.release(), ec);
1188     ASSERT(!ec);
1189 }
1190 
dispatchAbortEvent()1191 void XMLHttpRequest::dispatchAbortEvent()
1192 {
1193     dispatchXMLHttpRequestProgressEvent(m_onAbortListener.get(), eventNames().abortEvent, false, 0, 0);
1194 }
1195 
dispatchErrorEvent()1196 void XMLHttpRequest::dispatchErrorEvent()
1197 {
1198     dispatchXMLHttpRequestProgressEvent(m_onErrorListener.get(), eventNames().errorEvent, false, 0, 0);
1199 }
1200 
dispatchLoadEvent()1201 void XMLHttpRequest::dispatchLoadEvent()
1202 {
1203     dispatchXMLHttpRequestProgressEvent(m_onLoadListener.get(), eventNames().loadEvent, false, 0, 0);
1204 }
1205 
dispatchLoadStartEvent()1206 void XMLHttpRequest::dispatchLoadStartEvent()
1207 {
1208     dispatchXMLHttpRequestProgressEvent(m_onLoadStartListener.get(), eventNames().loadstartEvent, false, 0, 0);
1209 }
1210 
dispatchProgressEvent(long long expectedLength)1211 void XMLHttpRequest::dispatchProgressEvent(long long expectedLength)
1212 {
1213     dispatchXMLHttpRequestProgressEvent(m_onProgressListener.get(), eventNames().progressEvent, expectedLength && m_receivedLength <= expectedLength,
1214                                         static_cast<unsigned>(m_receivedLength), static_cast<unsigned>(expectedLength));
1215 }
1216 
canSuspend() const1217 bool XMLHttpRequest::canSuspend() const
1218 {
1219     return !m_loader;
1220 }
1221 
stop()1222 void XMLHttpRequest::stop()
1223 {
1224     internalAbort();
1225 }
1226 
contextDestroyed()1227 void XMLHttpRequest::contextDestroyed()
1228 {
1229     ASSERT(!m_loader);
1230     ActiveDOMObject::contextDestroyed();
1231 }
1232 
scriptExecutionContext() const1233 ScriptExecutionContext* XMLHttpRequest::scriptExecutionContext() const
1234 {
1235     return ActiveDOMObject::scriptExecutionContext();
1236 }
1237 
1238 } // namespace WebCore
1239