• 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, 2011 Google Inc. All rights reserved.
6  *  Copyright (C) 2012 Intel Corporation
7  *
8  *  This library is free software; you can redistribute it and/or
9  *  modify it under the terms of the GNU Lesser General Public
10  *  License as published by the Free Software Foundation; either
11  *  version 2 of the License, or (at your option) any later version.
12  *
13  *  This library is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  *  Lesser General Public License for more details.
17  *
18  *  You should have received a copy of the GNU Lesser General Public
19  *  License along with this library; if not, write to the Free Software
20  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21  */
22 
23 #include "config.h"
24 #include "core/xml/XMLHttpRequest.h"
25 
26 #include "bindings/v8/ExceptionState.h"
27 #include "core/FetchInitiatorTypeNames.h"
28 #include "core/dom/ContextFeatures.h"
29 #include "core/dom/DOMImplementation.h"
30 #include "core/dom/ExceptionCode.h"
31 #include "core/dom/XMLDocument.h"
32 #include "core/editing/markup.h"
33 #include "core/events/Event.h"
34 #include "core/fetch/CrossOriginAccessControl.h"
35 #include "core/fileapi/Blob.h"
36 #include "core/fileapi/File.h"
37 #include "core/fileapi/Stream.h"
38 #include "core/frame/Settings.h"
39 #include "core/frame/UseCounter.h"
40 #include "core/frame/csp/ContentSecurityPolicy.h"
41 #include "core/html/DOMFormData.h"
42 #include "core/html/HTMLDocument.h"
43 #include "core/html/parser/TextResourceDecoder.h"
44 #include "core/inspector/InspectorInstrumentation.h"
45 #include "core/inspector/InspectorTraceEvents.h"
46 #include "core/loader/ThreadableLoader.h"
47 #include "core/xml/XMLHttpRequestProgressEvent.h"
48 #include "core/xml/XMLHttpRequestUpload.h"
49 #include "platform/Logging.h"
50 #include "platform/RuntimeEnabledFeatures.h"
51 #include "platform/SharedBuffer.h"
52 #include "platform/blob/BlobData.h"
53 #include "platform/network/HTTPParsers.h"
54 #include "platform/network/ParsedContentType.h"
55 #include "platform/network/ResourceError.h"
56 #include "platform/network/ResourceRequest.h"
57 #include "public/platform/Platform.h"
58 #include "wtf/ArrayBuffer.h"
59 #include "wtf/ArrayBufferView.h"
60 #include "wtf/Assertions.h"
61 #include "wtf/RefCountedLeakCounter.h"
62 #include "wtf/StdLibExtras.h"
63 #include "wtf/text/CString.h"
64 
65 namespace WebCore {
66 
67 DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, xmlHttpRequestCounter, ("XMLHttpRequest"));
68 
69 // Histogram enum to see when we can deprecate xhr.send(ArrayBuffer).
70 enum XMLHttpRequestSendArrayBufferOrView {
71     XMLHttpRequestSendArrayBuffer,
72     XMLHttpRequestSendArrayBufferView,
73     XMLHttpRequestSendArrayBufferOrViewMax,
74 };
75 
76 struct XMLHttpRequestStaticData {
77     WTF_MAKE_NONCOPYABLE(XMLHttpRequestStaticData); WTF_MAKE_FAST_ALLOCATED;
78 public:
79     XMLHttpRequestStaticData();
80     String m_proxyHeaderPrefix;
81     String m_secHeaderPrefix;
82     HashSet<String, CaseFoldingHash> m_forbiddenRequestHeaders;
83 };
84 
XMLHttpRequestStaticData()85 XMLHttpRequestStaticData::XMLHttpRequestStaticData()
86     : m_proxyHeaderPrefix("proxy-")
87     , m_secHeaderPrefix("sec-")
88 {
89     m_forbiddenRequestHeaders.add("accept-charset");
90     m_forbiddenRequestHeaders.add("accept-encoding");
91     m_forbiddenRequestHeaders.add("access-control-request-headers");
92     m_forbiddenRequestHeaders.add("access-control-request-method");
93     m_forbiddenRequestHeaders.add("connection");
94     m_forbiddenRequestHeaders.add("content-length");
95     m_forbiddenRequestHeaders.add("content-transfer-encoding");
96     m_forbiddenRequestHeaders.add("cookie");
97     m_forbiddenRequestHeaders.add("cookie2");
98     m_forbiddenRequestHeaders.add("date");
99     m_forbiddenRequestHeaders.add("expect");
100     m_forbiddenRequestHeaders.add("host");
101     m_forbiddenRequestHeaders.add("keep-alive");
102     m_forbiddenRequestHeaders.add("origin");
103     m_forbiddenRequestHeaders.add("referer");
104     m_forbiddenRequestHeaders.add("te");
105     m_forbiddenRequestHeaders.add("trailer");
106     m_forbiddenRequestHeaders.add("transfer-encoding");
107     m_forbiddenRequestHeaders.add("upgrade");
108     m_forbiddenRequestHeaders.add("user-agent");
109     m_forbiddenRequestHeaders.add("via");
110 }
111 
isSetCookieHeader(const AtomicString & name)112 static bool isSetCookieHeader(const AtomicString& name)
113 {
114     return equalIgnoringCase(name, "set-cookie") || equalIgnoringCase(name, "set-cookie2");
115 }
116 
replaceCharsetInMediaType(String & mediaType,const String & charsetValue)117 static void replaceCharsetInMediaType(String& mediaType, const String& charsetValue)
118 {
119     unsigned pos = 0, len = 0;
120 
121     findCharsetInMediaType(mediaType, pos, len);
122 
123     if (!len) {
124         // When no charset found, do nothing.
125         return;
126     }
127 
128     // Found at least one existing charset, replace all occurrences with new charset.
129     while (len) {
130         mediaType.replace(pos, len, charsetValue);
131         unsigned start = pos + charsetValue.length();
132         findCharsetInMediaType(mediaType, pos, len, start);
133     }
134 }
135 
136 static const XMLHttpRequestStaticData* staticData = 0;
137 
createXMLHttpRequestStaticData()138 static const XMLHttpRequestStaticData* createXMLHttpRequestStaticData()
139 {
140     staticData = new XMLHttpRequestStaticData;
141     return staticData;
142 }
143 
initializeXMLHttpRequestStaticData()144 static const XMLHttpRequestStaticData* initializeXMLHttpRequestStaticData()
145 {
146     // Uses dummy to avoid warnings about an unused variable.
147     AtomicallyInitializedStatic(const XMLHttpRequestStaticData*, dummy = createXMLHttpRequestStaticData());
148     return dummy;
149 }
150 
logConsoleError(ExecutionContext * context,const String & message)151 static void logConsoleError(ExecutionContext* context, const String& message)
152 {
153     if (!context)
154         return;
155     // FIXME: It's not good to report the bad usage without indicating what source line it came from.
156     // We should pass additional parameters so we can tell the console where the mistake occurred.
157     context->addConsoleMessage(JSMessageSource, ErrorMessageLevel, message);
158 }
159 
create(ExecutionContext * context,PassRefPtr<SecurityOrigin> securityOrigin)160 PassRefPtrWillBeRawPtr<XMLHttpRequest> XMLHttpRequest::create(ExecutionContext* context, PassRefPtr<SecurityOrigin> securityOrigin)
161 {
162     RefPtrWillBeRawPtr<XMLHttpRequest> xmlHttpRequest = adoptRefWillBeRefCountedGarbageCollected(new XMLHttpRequest(context, securityOrigin));
163     xmlHttpRequest->suspendIfNeeded();
164 
165     return xmlHttpRequest.release();
166 }
167 
XMLHttpRequest(ExecutionContext * context,PassRefPtr<SecurityOrigin> securityOrigin)168 XMLHttpRequest::XMLHttpRequest(ExecutionContext* context, PassRefPtr<SecurityOrigin> securityOrigin)
169     : ActiveDOMObject(context)
170     , m_async(true)
171     , m_includeCredentials(false)
172     , m_timeoutMilliseconds(0)
173     , m_state(UNSENT)
174     , m_createdDocument(false)
175     , m_downloadedBlobLength(0)
176     , m_error(false)
177     , m_uploadEventsAllowed(true)
178     , m_uploadComplete(false)
179     , m_sameOriginRequest(true)
180     , m_receivedLength(0)
181     , m_lastSendLineNumber(0)
182     , m_exceptionCode(0)
183     , m_progressEventThrottle(this)
184     , m_responseTypeCode(ResponseTypeDefault)
185     , m_dropProtectionRunner(this, &XMLHttpRequest::dropProtection)
186     , m_securityOrigin(securityOrigin)
187 {
188     initializeXMLHttpRequestStaticData();
189 #ifndef NDEBUG
190     xmlHttpRequestCounter.increment();
191 #endif
192     ScriptWrappable::init(this);
193 }
194 
~XMLHttpRequest()195 XMLHttpRequest::~XMLHttpRequest()
196 {
197 #ifndef NDEBUG
198     xmlHttpRequestCounter.decrement();
199 #endif
200 }
201 
document() const202 Document* XMLHttpRequest::document() const
203 {
204     ASSERT(executionContext()->isDocument());
205     return toDocument(executionContext());
206 }
207 
securityOrigin() const208 SecurityOrigin* XMLHttpRequest::securityOrigin() const
209 {
210     return m_securityOrigin ? m_securityOrigin.get() : executionContext()->securityOrigin();
211 }
212 
readyState() const213 XMLHttpRequest::State XMLHttpRequest::readyState() const
214 {
215     return m_state;
216 }
217 
responseText(ExceptionState & exceptionState)218 ScriptString XMLHttpRequest::responseText(ExceptionState& exceptionState)
219 {
220     if (m_responseTypeCode != ResponseTypeDefault && m_responseTypeCode != ResponseTypeText) {
221         exceptionState.throwDOMException(InvalidStateError, "The value is only accessible if the object's 'responseType' is '' or 'text' (was '" + responseType() + "').");
222         return ScriptString();
223     }
224     if (m_error || (m_state != LOADING && m_state != DONE))
225         return ScriptString();
226     return m_responseText;
227 }
228 
responseJSONSource()229 ScriptString XMLHttpRequest::responseJSONSource()
230 {
231     ASSERT(m_responseTypeCode == ResponseTypeJSON);
232 
233     if (m_error || m_state != DONE)
234         return ScriptString();
235     return m_responseText;
236 }
237 
responseXML(ExceptionState & exceptionState)238 Document* XMLHttpRequest::responseXML(ExceptionState& exceptionState)
239 {
240     if (m_responseTypeCode != ResponseTypeDefault && m_responseTypeCode != ResponseTypeDocument) {
241         exceptionState.throwDOMException(InvalidStateError, "The value is only accessible if the object's 'responseType' is '' or 'document' (was '" + responseType() + "').");
242         return 0;
243     }
244 
245     if (m_error || m_state != DONE)
246         return 0;
247 
248     if (!m_createdDocument) {
249         AtomicString mimeType = responseMIMEType();
250         bool isHTML = equalIgnoringCase(mimeType, "text/html");
251 
252         // The W3C spec requires the final MIME type to be some valid XML type, or text/html.
253         // If it is text/html, then the responseType of "document" must have been supplied explicitly.
254         if ((m_response.isHTTP() && !responseIsXML() && !isHTML)
255             || (isHTML && m_responseTypeCode == ResponseTypeDefault)
256             || executionContext()->isWorkerGlobalScope()) {
257             m_responseDocument = nullptr;
258         } else {
259             DocumentInit init = DocumentInit::fromContext(document()->contextDocument(), m_url);
260             if (isHTML)
261                 m_responseDocument = HTMLDocument::create(init);
262             else
263                 m_responseDocument = XMLDocument::create(init);
264             // FIXME: Set Last-Modified.
265             m_responseDocument->setContent(m_responseText.flattenToString());
266             m_responseDocument->setSecurityOrigin(securityOrigin());
267             m_responseDocument->setContextFeatures(document()->contextFeatures());
268             m_responseDocument->setMimeType(mimeType);
269             if (!m_responseDocument->wellFormed())
270                 m_responseDocument = nullptr;
271         }
272         m_createdDocument = true;
273     }
274 
275     return m_responseDocument.get();
276 }
277 
responseBlob()278 Blob* XMLHttpRequest::responseBlob()
279 {
280     ASSERT(m_responseTypeCode == ResponseTypeBlob);
281     ASSERT(!m_binaryResponseBuilder.get());
282 
283     // We always return null before DONE.
284     if (m_error || m_state != DONE)
285         return 0;
286 
287     if (!m_responseBlob) {
288         // When responseType is set to "blob", we redirect the downloaded data
289         // to a file-handle directly in the browser process. We get the
290         // file-path from the ResourceResponse directly instead of copying the
291         // bytes between the browser and the renderer.
292         OwnPtr<BlobData> blobData = BlobData::create();
293         String filePath = m_response.downloadedFilePath();
294         // If we errored out or got no data, we still return a blob, just an
295         // empty one.
296         if (!filePath.isEmpty() && m_downloadedBlobLength) {
297             blobData->appendFile(filePath);
298             blobData->setContentType(responseMIMEType()); // responseMIMEType defaults to text/xml which may be incorrect.
299         }
300         m_responseBlob = Blob::create(BlobDataHandle::create(blobData.release(), m_downloadedBlobLength));
301     }
302 
303     return m_responseBlob.get();
304 }
305 
responseArrayBuffer()306 ArrayBuffer* XMLHttpRequest::responseArrayBuffer()
307 {
308     ASSERT(m_responseTypeCode == ResponseTypeArrayBuffer);
309 
310     if (m_error || m_state != DONE)
311         return 0;
312 
313     if (!m_responseArrayBuffer.get()) {
314         if (m_binaryResponseBuilder.get() && m_binaryResponseBuilder->size() > 0) {
315             m_responseArrayBuffer = m_binaryResponseBuilder->getAsArrayBuffer();
316             if (!m_responseArrayBuffer) {
317                 // m_binaryResponseBuilder failed to allocate an ArrayBuffer.
318                 // We need to crash the renderer since there's no way defined in
319                 // the spec to tell this to the user.
320                 CRASH();
321             }
322             m_binaryResponseBuilder.clear();
323         } else {
324             m_responseArrayBuffer = ArrayBuffer::create(static_cast<void*>(0), 0);
325         }
326     }
327 
328     return m_responseArrayBuffer.get();
329 }
330 
responseStream()331 Stream* XMLHttpRequest::responseStream()
332 {
333     ASSERT(m_responseTypeCode == ResponseTypeStream);
334 
335     if (m_error || (m_state != LOADING && m_state != DONE))
336         return 0;
337 
338     return m_responseStream.get();
339 }
340 
setTimeout(unsigned long timeout,ExceptionState & exceptionState)341 void XMLHttpRequest::setTimeout(unsigned long timeout, ExceptionState& exceptionState)
342 {
343     // FIXME: Need to trigger or update the timeout Timer here, if needed. http://webkit.org/b/98156
344     // XHR2 spec, 4.7.3. "This implies that the timeout attribute can be set while fetching is in progress. If that occurs it will still be measured relative to the start of fetching."
345     if (executionContext()->isDocument() && !m_async) {
346         exceptionState.throwDOMException(InvalidAccessError, "Timeouts cannot be set for synchronous requests made from a document.");
347         return;
348     }
349     m_timeoutMilliseconds = timeout;
350 }
351 
setResponseType(const String & responseType,ExceptionState & exceptionState)352 void XMLHttpRequest::setResponseType(const String& responseType, ExceptionState& exceptionState)
353 {
354     if (m_state >= LOADING) {
355         exceptionState.throwDOMException(InvalidStateError, "The response type cannot be set if the object's state is LOADING or DONE.");
356         return;
357     }
358 
359     // Newer functionality is not available to synchronous requests in window contexts, as a spec-mandated
360     // attempt to discourage synchronous XHR use. responseType is one such piece of functionality.
361     if (!m_async && executionContext()->isDocument()) {
362         exceptionState.throwDOMException(InvalidAccessError, "The response type can only be changed for asynchronous HTTP requests made from a document.");
363         return;
364     }
365 
366     if (responseType == "") {
367         m_responseTypeCode = ResponseTypeDefault;
368     } else if (responseType == "text") {
369         m_responseTypeCode = ResponseTypeText;
370     } else if (responseType == "json") {
371         m_responseTypeCode = ResponseTypeJSON;
372     } else if (responseType == "document") {
373         m_responseTypeCode = ResponseTypeDocument;
374     } else if (responseType == "blob") {
375         m_responseTypeCode = ResponseTypeBlob;
376     } else if (responseType == "arraybuffer") {
377         m_responseTypeCode = ResponseTypeArrayBuffer;
378     } else if (responseType == "stream") {
379         if (RuntimeEnabledFeatures::streamEnabled())
380             m_responseTypeCode = ResponseTypeStream;
381         else
382             return;
383     } else {
384         ASSERT_NOT_REACHED();
385     }
386 }
387 
responseType()388 String XMLHttpRequest::responseType()
389 {
390     switch (m_responseTypeCode) {
391     case ResponseTypeDefault:
392         return "";
393     case ResponseTypeText:
394         return "text";
395     case ResponseTypeJSON:
396         return "json";
397     case ResponseTypeDocument:
398         return "document";
399     case ResponseTypeBlob:
400         return "blob";
401     case ResponseTypeArrayBuffer:
402         return "arraybuffer";
403     case ResponseTypeStream:
404         return "stream";
405     }
406     return "";
407 }
408 
responseURL()409 String XMLHttpRequest::responseURL()
410 {
411     return m_response.url().string();
412 }
413 
upload()414 XMLHttpRequestUpload* XMLHttpRequest::upload()
415 {
416     if (!m_upload)
417         m_upload = XMLHttpRequestUpload::create(this);
418     return m_upload.get();
419 }
420 
trackProgress(int length)421 void XMLHttpRequest::trackProgress(int length)
422 {
423     m_receivedLength += length;
424 
425     if (m_async)
426         dispatchProgressEventFromSnapshot(EventTypeNames::progress);
427 
428     if (m_state != LOADING) {
429         changeState(LOADING);
430     } else {
431         // Firefox calls readyStateChanged every time it receives data. Do
432         // the same to align with Firefox.
433         //
434         // FIXME: Make our implementation and the spec consistent. This
435         // behavior was needed when the progress event was not available.
436         dispatchReadyStateChangeEvent();
437     }
438 }
439 
changeState(State newState)440 void XMLHttpRequest::changeState(State newState)
441 {
442     if (m_state != newState) {
443         m_state = newState;
444         dispatchReadyStateChangeEvent();
445     }
446 }
447 
dispatchReadyStateChangeEvent()448 void XMLHttpRequest::dispatchReadyStateChangeEvent()
449 {
450     if (!executionContext())
451         return;
452 
453     InspectorInstrumentationCookie cookie = InspectorInstrumentation::willDispatchXHRReadyStateChangeEvent(executionContext(), this);
454 
455     if (m_async || (m_state <= OPENED || m_state == DONE)) {
456         TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "XHRReadyStateChange", "data", InspectorXhrReadyStateChangeEvent::data(executionContext(), this));
457         TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline.stack"), "CallStack", "stack", InspectorCallStackEvent::currentCallStack());
458         ProgressEventAction flushAction = DoNotFlushProgressEvent;
459         if (m_state == DONE) {
460             if (m_error)
461                 flushAction = FlushDeferredProgressEvent;
462             else
463                 flushAction = FlushProgressEvent;
464         }
465         m_progressEventThrottle.dispatchReadyStateChangeEvent(XMLHttpRequestProgressEvent::create(EventTypeNames::readystatechange), flushAction);
466         TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "UpdateCounters", "data", InspectorUpdateCountersEvent::data());
467     }
468 
469     InspectorInstrumentation::didDispatchXHRReadyStateChangeEvent(cookie);
470     if (m_state == DONE && !m_error) {
471         {
472             TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "XHRLoad", "data", InspectorXhrLoadEvent::data(executionContext(), this));
473             TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline.stack"), "CallStack", "stack", InspectorCallStackEvent::currentCallStack());
474             InspectorInstrumentationCookie cookie = InspectorInstrumentation::willDispatchXHRLoadEvent(executionContext(), this);
475             dispatchProgressEventFromSnapshot(EventTypeNames::load);
476             InspectorInstrumentation::didDispatchXHRLoadEvent(cookie);
477         }
478         dispatchProgressEventFromSnapshot(EventTypeNames::loadend);
479         TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "UpdateCounters", "data", InspectorUpdateCountersEvent::data());
480     }
481 }
482 
setWithCredentials(bool value,ExceptionState & exceptionState)483 void XMLHttpRequest::setWithCredentials(bool value, ExceptionState& exceptionState)
484 {
485     if (m_state > OPENED || m_loader) {
486         exceptionState.throwDOMException(InvalidStateError,  "The value may only be set if the object's state is UNSENT or OPENED.");
487         return;
488     }
489 
490     // FIXME: According to XMLHttpRequest Level 2 we should throw InvalidAccessError exception here.
491     // However for time being only print warning message to warn web developers.
492     if (!m_async)
493         UseCounter::countDeprecation(executionContext(), UseCounter::SyncXHRWithCredentials);
494 
495     m_includeCredentials = value;
496 }
497 
isAllowedHTTPMethod(const String & method)498 bool XMLHttpRequest::isAllowedHTTPMethod(const String& method)
499 {
500     return !equalIgnoringCase(method, "TRACE")
501         && !equalIgnoringCase(method, "TRACK")
502         && !equalIgnoringCase(method, "CONNECT");
503 }
504 
uppercaseKnownHTTPMethod(const AtomicString & method)505 AtomicString XMLHttpRequest::uppercaseKnownHTTPMethod(const AtomicString& method)
506 {
507     const char* const methods[] = {
508         "COPY",
509         "DELETE",
510         "GET",
511         "HEAD",
512         "INDEX",
513         "LOCK",
514         "M-POST",
515         "MKCOL",
516         "MOVE",
517         "OPTIONS",
518         "POST",
519         "PROPFIND",
520         "PROPPATCH",
521         "PUT",
522         "UNLOCK" };
523     for (unsigned i = 0; i < WTF_ARRAY_LENGTH(methods); ++i) {
524         if (equalIgnoringCase(method, methods[i])) {
525             // Don't bother allocating a new string if it's already all uppercase.
526             if (method == methods[i])
527                 return method;
528             return methods[i];
529         }
530     }
531     return method;
532 }
533 
isAllowedHTTPHeader(const String & name)534 bool XMLHttpRequest::isAllowedHTTPHeader(const String& name)
535 {
536     initializeXMLHttpRequestStaticData();
537     return !staticData->m_forbiddenRequestHeaders.contains(name) && !name.startsWith(staticData->m_proxyHeaderPrefix, false)
538         && !name.startsWith(staticData->m_secHeaderPrefix, false);
539 }
540 
open(const AtomicString & method,const KURL & url,ExceptionState & exceptionState)541 void XMLHttpRequest::open(const AtomicString& method, const KURL& url, ExceptionState& exceptionState)
542 {
543     open(method, url, true, exceptionState);
544 }
545 
open(const AtomicString & method,const KURL & url,bool async,ExceptionState & exceptionState)546 void XMLHttpRequest::open(const AtomicString& method, const KURL& url, bool async, ExceptionState& exceptionState)
547 {
548     WTF_LOG(Network, "XMLHttpRequest %p open('%s', '%s', %d)", this, method.utf8().data(), url.elidedString().utf8().data(), async);
549 
550     if (!internalAbort())
551         return;
552 
553     State previousState = m_state;
554     m_state = UNSENT;
555     m_error = false;
556     m_uploadComplete = false;
557 
558     // clear stuff from possible previous load
559     clearResponse();
560     clearRequest();
561 
562     ASSERT(m_state == UNSENT);
563 
564     if (!isValidHTTPToken(method)) {
565         exceptionState.throwDOMException(SyntaxError, "'" + method + "' is not a valid HTTP method.");
566         return;
567     }
568 
569     if (!isAllowedHTTPMethod(method)) {
570         exceptionState.throwSecurityError("'" + method + "' HTTP method is unsupported.");
571         return;
572     }
573 
574     if (!ContentSecurityPolicy::shouldBypassMainWorld(executionContext()) && !executionContext()->contentSecurityPolicy()->allowConnectToSource(url)) {
575         // We can safely expose the URL to JavaScript, as these checks happen synchronously before redirection. JavaScript receives no new information.
576         exceptionState.throwSecurityError("Refused to connect to '" + url.elidedString() + "' because it violates the document's Content Security Policy.");
577         return;
578     }
579 
580     if (!async && executionContext()->isDocument()) {
581         if (document()->settings() && !document()->settings()->syncXHRInDocumentsEnabled()) {
582             exceptionState.throwDOMException(InvalidAccessError, "Synchronous requests are disabled for this page.");
583             return;
584         }
585 
586         // Newer functionality is not available to synchronous requests in window contexts, as a spec-mandated
587         // attempt to discourage synchronous XHR use. responseType is one such piece of functionality.
588         if (m_responseTypeCode != ResponseTypeDefault) {
589             exceptionState.throwDOMException(InvalidAccessError, "Synchronous requests from a document must not set a response type.");
590             return;
591         }
592 
593         // Similarly, timeouts are disabled for synchronous requests as well.
594         if (m_timeoutMilliseconds > 0) {
595             exceptionState.throwDOMException(InvalidAccessError, "Synchronous requests must not set a timeout.");
596             return;
597         }
598     }
599 
600     m_method = uppercaseKnownHTTPMethod(method);
601 
602     m_url = url;
603 
604     m_async = async;
605 
606     ASSERT(!m_loader);
607 
608     // Check previous state to avoid dispatching readyState event
609     // when calling open several times in a row.
610     if (previousState != OPENED)
611         changeState(OPENED);
612     else
613         m_state = OPENED;
614 }
615 
open(const AtomicString & method,const KURL & url,bool async,const String & user,ExceptionState & exceptionState)616 void XMLHttpRequest::open(const AtomicString& method, const KURL& url, bool async, const String& user, ExceptionState& exceptionState)
617 {
618     KURL urlWithCredentials(url);
619     urlWithCredentials.setUser(user);
620 
621     open(method, urlWithCredentials, async, exceptionState);
622 }
623 
open(const AtomicString & method,const KURL & url,bool async,const String & user,const String & password,ExceptionState & exceptionState)624 void XMLHttpRequest::open(const AtomicString& method, const KURL& url, bool async, const String& user, const String& password, ExceptionState& exceptionState)
625 {
626     KURL urlWithCredentials(url);
627     urlWithCredentials.setUser(user);
628     urlWithCredentials.setPass(password);
629 
630     open(method, urlWithCredentials, async, exceptionState);
631 }
632 
initSend(ExceptionState & exceptionState)633 bool XMLHttpRequest::initSend(ExceptionState& exceptionState)
634 {
635     if (!executionContext())
636         return false;
637 
638     if (m_state != OPENED || m_loader) {
639         exceptionState.throwDOMException(InvalidStateError, "The object's state must be OPENED.");
640         return false;
641     }
642 
643     m_error = false;
644     return true;
645 }
646 
send(ExceptionState & exceptionState)647 void XMLHttpRequest::send(ExceptionState& exceptionState)
648 {
649     send(String(), exceptionState);
650 }
651 
areMethodAndURLValidForSend()652 bool XMLHttpRequest::areMethodAndURLValidForSend()
653 {
654     return m_method != "GET" && m_method != "HEAD" && m_url.protocolIsInHTTPFamily();
655 }
656 
send(Document * document,ExceptionState & exceptionState)657 void XMLHttpRequest::send(Document* document, ExceptionState& exceptionState)
658 {
659     WTF_LOG(Network, "XMLHttpRequest %p send() Document %p", this, document);
660 
661     ASSERT(document);
662 
663     if (!initSend(exceptionState))
664         return;
665 
666     RefPtr<FormData> httpBody;
667 
668     if (areMethodAndURLValidForSend()) {
669         if (getRequestHeader("Content-Type").isEmpty()) {
670             // FIXME: this should include the charset used for encoding.
671             setRequestHeaderInternal("Content-Type", "application/xml");
672         }
673 
674         // FIXME: According to XMLHttpRequest Level 2, this should use the Document.innerHTML algorithm
675         // from the HTML5 specification to serialize the document.
676         String body = createMarkup(document);
677 
678         // FIXME: This should use value of document.inputEncoding to determine the encoding to use.
679         httpBody = FormData::create(UTF8Encoding().encode(body, WTF::EntitiesForUnencodables));
680         if (m_upload)
681             httpBody->setAlwaysStream(true);
682     }
683 
684     createRequest(httpBody.release(), exceptionState);
685 }
686 
send(const String & body,ExceptionState & exceptionState)687 void XMLHttpRequest::send(const String& body, ExceptionState& exceptionState)
688 {
689     WTF_LOG(Network, "XMLHttpRequest %p send() String '%s'", this, body.utf8().data());
690 
691     if (!initSend(exceptionState))
692         return;
693 
694     RefPtr<FormData> httpBody;
695 
696     if (!body.isNull() && areMethodAndURLValidForSend()) {
697         String contentType = getRequestHeader("Content-Type");
698         if (contentType.isEmpty()) {
699             setRequestHeaderInternal("Content-Type", "text/plain;charset=UTF-8");
700         } else {
701             replaceCharsetInMediaType(contentType, "UTF-8");
702             m_requestHeaders.set("Content-Type", AtomicString(contentType));
703         }
704 
705         httpBody = FormData::create(UTF8Encoding().encode(body, WTF::EntitiesForUnencodables));
706         if (m_upload)
707             httpBody->setAlwaysStream(true);
708     }
709 
710     createRequest(httpBody.release(), exceptionState);
711 }
712 
send(Blob * body,ExceptionState & exceptionState)713 void XMLHttpRequest::send(Blob* body, ExceptionState& exceptionState)
714 {
715     WTF_LOG(Network, "XMLHttpRequest %p send() Blob '%s'", this, body->uuid().utf8().data());
716 
717     if (!initSend(exceptionState))
718         return;
719 
720     RefPtr<FormData> httpBody;
721 
722     if (areMethodAndURLValidForSend()) {
723         if (getRequestHeader("Content-Type").isEmpty()) {
724             const String& blobType = body->type();
725             if (!blobType.isEmpty() && isValidContentType(blobType)) {
726                 setRequestHeaderInternal("Content-Type", AtomicString(blobType));
727             } else {
728                 // From FileAPI spec, whenever media type cannot be determined,
729                 // empty string must be returned.
730                 setRequestHeaderInternal("Content-Type", "");
731             }
732         }
733 
734         // FIXME: add support for uploading bundles.
735         httpBody = FormData::create();
736         if (body->hasBackingFile()) {
737             File* file = toFile(body);
738             if (!file->path().isEmpty())
739                 httpBody->appendFile(file->path());
740             else if (!file->fileSystemURL().isEmpty())
741                 httpBody->appendFileSystemURL(file->fileSystemURL());
742             else
743                 ASSERT_NOT_REACHED();
744         } else {
745             httpBody->appendBlob(body->uuid(), body->blobDataHandle());
746         }
747     }
748 
749     createRequest(httpBody.release(), exceptionState);
750 }
751 
send(DOMFormData * body,ExceptionState & exceptionState)752 void XMLHttpRequest::send(DOMFormData* body, ExceptionState& exceptionState)
753 {
754     WTF_LOG(Network, "XMLHttpRequest %p send() DOMFormData %p", this, body);
755 
756     if (!initSend(exceptionState))
757         return;
758 
759     RefPtr<FormData> httpBody;
760 
761     if (areMethodAndURLValidForSend()) {
762         httpBody = body->createMultiPartFormData();
763 
764         if (getRequestHeader("Content-Type").isEmpty()) {
765             AtomicString contentType = AtomicString("multipart/form-data; boundary=", AtomicString::ConstructFromLiteral) + httpBody->boundary().data();
766             setRequestHeaderInternal("Content-Type", contentType);
767         }
768     }
769 
770     createRequest(httpBody.release(), exceptionState);
771 }
772 
send(ArrayBuffer * body,ExceptionState & exceptionState)773 void XMLHttpRequest::send(ArrayBuffer* body, ExceptionState& exceptionState)
774 {
775     WTF_LOG(Network, "XMLHttpRequest %p send() ArrayBuffer %p", this, body);
776 
777     String consoleMessage("ArrayBuffer is deprecated in XMLHttpRequest.send(). Use ArrayBufferView instead.");
778     executionContext()->addConsoleMessage(JSMessageSource, WarningMessageLevel, consoleMessage);
779 
780     blink::Platform::current()->histogramEnumeration("WebCore.XHR.send.ArrayBufferOrView", XMLHttpRequestSendArrayBuffer, XMLHttpRequestSendArrayBufferOrViewMax);
781 
782     sendBytesData(body->data(), body->byteLength(), exceptionState);
783 }
784 
send(ArrayBufferView * body,ExceptionState & exceptionState)785 void XMLHttpRequest::send(ArrayBufferView* body, ExceptionState& exceptionState)
786 {
787     WTF_LOG(Network, "XMLHttpRequest %p send() ArrayBufferView %p", this, body);
788 
789     blink::Platform::current()->histogramEnumeration("WebCore.XHR.send.ArrayBufferOrView", XMLHttpRequestSendArrayBufferView, XMLHttpRequestSendArrayBufferOrViewMax);
790 
791     sendBytesData(body->baseAddress(), body->byteLength(), exceptionState);
792 }
793 
sendBytesData(const void * data,size_t length,ExceptionState & exceptionState)794 void XMLHttpRequest::sendBytesData(const void* data, size_t length, ExceptionState& exceptionState)
795 {
796     if (!initSend(exceptionState))
797         return;
798 
799     RefPtr<FormData> httpBody;
800 
801     if (areMethodAndURLValidForSend()) {
802         httpBody = FormData::create(data, length);
803         if (m_upload)
804             httpBody->setAlwaysStream(true);
805     }
806 
807     createRequest(httpBody.release(), exceptionState);
808 }
809 
sendForInspectorXHRReplay(PassRefPtr<FormData> formData,ExceptionState & exceptionState)810 void XMLHttpRequest::sendForInspectorXHRReplay(PassRefPtr<FormData> formData, ExceptionState& exceptionState)
811 {
812     createRequest(formData ? formData->deepCopy() : nullptr, exceptionState);
813     m_exceptionCode = exceptionState.code();
814 }
815 
createRequest(PassRefPtr<FormData> httpBody,ExceptionState & exceptionState)816 void XMLHttpRequest::createRequest(PassRefPtr<FormData> httpBody, ExceptionState& exceptionState)
817 {
818     // Only GET request is supported for blob URL.
819     if (m_url.protocolIs("blob") && m_method != "GET") {
820         exceptionState.throwDOMException(NetworkError, "'GET' is the only method allowed for 'blob:' URLs.");
821         return;
822     }
823 
824     // The presence of upload event listeners forces us to use preflighting because POSTing to an URL that does not
825     // permit cross origin requests should look exactly like POSTing to an URL that does not respond at all.
826     // Also, only async requests support upload progress events.
827     bool uploadEvents = false;
828     if (m_async) {
829         dispatchProgressEvent(EventTypeNames::loadstart, 0, 0);
830         if (httpBody && m_upload) {
831             uploadEvents = m_upload->hasEventListeners();
832             m_upload->dispatchEvent(XMLHttpRequestProgressEvent::create(EventTypeNames::loadstart));
833         }
834     }
835 
836     m_sameOriginRequest = securityOrigin()->canRequest(m_url);
837 
838     // We also remember whether upload events should be allowed for this request in case the upload listeners are
839     // added after the request is started.
840     m_uploadEventsAllowed = m_sameOriginRequest || uploadEvents || !isSimpleCrossOriginAccessRequest(m_method, m_requestHeaders);
841 
842     ASSERT(executionContext());
843     ExecutionContext& executionContext = *this->executionContext();
844 
845     ResourceRequest request(m_url);
846     request.setHTTPMethod(m_method);
847     request.setTargetType(ResourceRequest::TargetIsXHR);
848 
849     InspectorInstrumentation::willLoadXHR(&executionContext, this, this, m_method, m_url, m_async, httpBody ? httpBody->deepCopy() : nullptr, m_requestHeaders, m_includeCredentials);
850 
851     if (httpBody) {
852         ASSERT(m_method != "GET");
853         ASSERT(m_method != "HEAD");
854         request.setHTTPBody(httpBody);
855     }
856 
857     if (m_requestHeaders.size() > 0)
858         request.addHTTPHeaderFields(m_requestHeaders);
859 
860     ThreadableLoaderOptions options;
861     options.preflightPolicy = uploadEvents ? ForcePreflight : ConsiderPreflight;
862     options.crossOriginRequestPolicy = UseAccessControl;
863     options.initiator = FetchInitiatorTypeNames::xmlhttprequest;
864     options.contentSecurityPolicyEnforcement = ContentSecurityPolicy::shouldBypassMainWorld(&executionContext) ? DoNotEnforceContentSecurityPolicy : EnforceConnectSrcDirective;
865     options.timeoutMilliseconds = m_timeoutMilliseconds;
866 
867     ResourceLoaderOptions resourceLoaderOptions;
868     resourceLoaderOptions.allowCredentials = (m_sameOriginRequest || m_includeCredentials) ? AllowStoredCredentials : DoNotAllowStoredCredentials;
869     resourceLoaderOptions.credentialsRequested = m_includeCredentials ? ClientRequestedCredentials : ClientDidNotRequestCredentials;
870     resourceLoaderOptions.securityOrigin = securityOrigin();
871     // TODO(tsepez): Specify TreatAsActiveContent per http://crbug.com/305303.
872     resourceLoaderOptions.mixedContentBlockingTreatment = TreatAsPassiveContent;
873 
874     // When responseType is set to "blob", we redirect the downloaded data to a
875     // file-handle directly.
876     if (responseTypeCode() == ResponseTypeBlob) {
877         request.setDownloadToFile(true);
878         resourceLoaderOptions.dataBufferingPolicy = DoNotBufferData;
879     }
880 
881     m_exceptionCode = 0;
882     m_error = false;
883 
884     if (m_async) {
885         if (m_upload)
886             request.setReportUploadProgress(true);
887 
888         // ThreadableLoader::create can return null here, for example if we're no longer attached to a page.
889         // This is true while running onunload handlers.
890         // FIXME: Maybe we need to be able to send XMLHttpRequests from onunload, <http://bugs.webkit.org/show_bug.cgi?id=10904>.
891         // FIXME: Maybe create() can return null for other reasons too?
892         ASSERT(!m_loader);
893         m_loader = ThreadableLoader::create(executionContext, this, request, options, resourceLoaderOptions);
894         if (m_loader) {
895             // Neither this object nor the JavaScript wrapper should be deleted while
896             // a request is in progress because we need to keep the listeners alive,
897             // and they are referenced by the JavaScript wrapper.
898             setPendingActivity(this);
899         }
900     } else {
901         // Use count for XHR synchronous requests.
902         UseCounter::count(&executionContext, UseCounter::XMLHttpRequestSynchronous);
903         ThreadableLoader::loadResourceSynchronously(executionContext, request, *this, options, resourceLoaderOptions);
904     }
905 
906     if (!m_exceptionCode && m_error)
907         m_exceptionCode = NetworkError;
908     if (m_exceptionCode)
909         exceptionState.throwDOMException(m_exceptionCode, "Failed to load '" + m_url.elidedString() + "'.");
910 }
911 
abort()912 void XMLHttpRequest::abort()
913 {
914     WTF_LOG(Network, "XMLHttpRequest %p abort()", this);
915 
916     // internalAbort() calls dropProtection(), which may release the last reference.
917     RefPtrWillBeRawPtr<XMLHttpRequest> protect(this);
918 
919     bool sendFlag = m_loader;
920 
921     // Response is cleared next, save needed progress event data.
922     long long expectedLength = m_response.expectedContentLength();
923     long long receivedLength = m_receivedLength;
924 
925     if (!internalAbort())
926         return;
927 
928     clearResponse();
929 
930     // Clear headers as required by the spec
931     m_requestHeaders.clear();
932 
933     if (!((m_state <= OPENED && !sendFlag) || m_state == DONE)) {
934         ASSERT(!m_loader);
935         handleRequestError(0, EventTypeNames::abort, receivedLength, expectedLength);
936     }
937     m_state = UNSENT;
938 }
939 
clearVariablesForLoading()940 void XMLHttpRequest::clearVariablesForLoading()
941 {
942     m_decoder.clear();
943 
944     m_responseEncoding = String();
945 }
946 
internalAbort(DropProtection async)947 bool XMLHttpRequest::internalAbort(DropProtection async)
948 {
949     m_error = true;
950 
951     clearVariablesForLoading();
952 
953     InspectorInstrumentation::didFailXHRLoading(executionContext(), this, this);
954 
955     if (m_responseStream && m_state != DONE)
956         m_responseStream->abort();
957 
958     if (!m_loader)
959         return true;
960 
961     // Cancelling the ThreadableLoader m_loader may result in calling
962     // window.onload synchronously. If such an onload handler contains open()
963     // call on the same XMLHttpRequest object, reentry happens. If m_loader
964     // is left to be non 0, internalAbort() call for the inner open() makes
965     // an extra dropProtection() call (when we're back to the outer open(),
966     // we'll call dropProtection()). To avoid that, clears m_loader before
967     // calling cancel.
968     //
969     // If, window.onload contains open() and send(), m_loader will be set to
970     // non 0 value. So, we cannot continue the outer open(). In such case,
971     // just abort the outer open() by returning false.
972     RefPtr<ThreadableLoader> loader = m_loader.release();
973     loader->cancel();
974 
975     // Save to a local variable since we're going to drop protection.
976     bool newLoadStarted = m_loader;
977 
978     // If abort() called internalAbort() and a nested open() ended up
979     // clearing the error flag, but didn't send(), make sure the error
980     // flag is still set.
981     if (!newLoadStarted)
982         m_error = true;
983 
984     if (async == DropProtectionAsync)
985         dropProtectionSoon();
986     else
987         dropProtection();
988 
989     return !newLoadStarted;
990 }
991 
clearResponse()992 void XMLHttpRequest::clearResponse()
993 {
994     // FIXME: when we add the support for multi-part XHR, we will have to
995     // be careful with this initialization.
996     m_receivedLength = 0;
997 
998     m_response = ResourceResponse();
999 
1000     m_responseText.clear();
1001 
1002     m_createdDocument = false;
1003     m_responseDocument = nullptr;
1004 
1005     m_responseBlob = nullptr;
1006     m_downloadedBlobLength = 0;
1007 
1008     m_responseStream = nullptr;
1009 
1010     // These variables may referred by the response accessors. So, we can clear
1011     // this only when we clear the response holder variables above.
1012     m_binaryResponseBuilder.clear();
1013     m_responseArrayBuffer.clear();
1014 }
1015 
clearRequest()1016 void XMLHttpRequest::clearRequest()
1017 {
1018     m_requestHeaders.clear();
1019 }
1020 
handleDidFailGeneric()1021 void XMLHttpRequest::handleDidFailGeneric()
1022 {
1023     clearResponse();
1024     clearRequest();
1025 
1026     m_error = true;
1027 }
1028 
dispatchProgressEvent(const AtomicString & type,long long receivedLength,long long expectedLength)1029 void XMLHttpRequest::dispatchProgressEvent(const AtomicString& type, long long receivedLength, long long expectedLength)
1030 {
1031     bool lengthComputable = expectedLength > 0 && receivedLength <= expectedLength;
1032     unsigned long long loaded = receivedLength >= 0 ? static_cast<unsigned long long>(receivedLength) : 0;
1033     unsigned long long total = lengthComputable ? static_cast<unsigned long long>(expectedLength) : 0;
1034 
1035     m_progressEventThrottle.dispatchProgressEvent(type, lengthComputable, loaded, total);
1036 }
1037 
dispatchProgressEventFromSnapshot(const AtomicString & type)1038 void XMLHttpRequest::dispatchProgressEventFromSnapshot(const AtomicString& type)
1039 {
1040     dispatchProgressEvent(type, m_receivedLength, m_response.expectedContentLength());
1041 }
1042 
handleNetworkError()1043 void XMLHttpRequest::handleNetworkError()
1044 {
1045     WTF_LOG(Network, "XMLHttpRequest %p handleNetworkError()", this);
1046 
1047     // Response is cleared next, save needed progress event data.
1048     long long expectedLength = m_response.expectedContentLength();
1049     long long receivedLength = m_receivedLength;
1050 
1051     handleDidFailGeneric();
1052     handleRequestError(NetworkError, EventTypeNames::error, receivedLength, expectedLength);
1053     internalAbort();
1054 }
1055 
handleDidCancel()1056 void XMLHttpRequest::handleDidCancel()
1057 {
1058     WTF_LOG(Network, "XMLHttpRequest %p handleDidCancel()", this);
1059 
1060     // Response is cleared next, save needed progress event data.
1061     long long expectedLength = m_response.expectedContentLength();
1062     long long receivedLength = m_receivedLength;
1063 
1064     handleDidFailGeneric();
1065     handleRequestError(AbortError, EventTypeNames::abort, receivedLength, expectedLength);
1066 }
1067 
handleRequestError(ExceptionCode exceptionCode,const AtomicString & type,long long receivedLength,long long expectedLength)1068 void XMLHttpRequest::handleRequestError(ExceptionCode exceptionCode, const AtomicString& type, long long receivedLength, long long expectedLength)
1069 {
1070     WTF_LOG(Network, "XMLHttpRequest %p handleRequestError()", this);
1071 
1072     // The request error steps for event 'type' and exception 'exceptionCode'.
1073 
1074     if (!m_async && exceptionCode) {
1075         m_state = DONE;
1076         m_exceptionCode = exceptionCode;
1077         return;
1078     }
1079     // With m_error set, the state change steps are minimal: any pending
1080     // progress event is flushed + a readystatechange is dispatched.
1081     // No new progress events dispatched; as required, that happens at
1082     // the end here.
1083     ASSERT(m_error);
1084     changeState(DONE);
1085 
1086     if (!m_uploadComplete) {
1087         m_uploadComplete = true;
1088         if (m_upload && m_uploadEventsAllowed)
1089             m_upload->handleRequestError(type);
1090     }
1091 
1092     dispatchProgressEvent(EventTypeNames::progress, receivedLength, expectedLength);
1093     dispatchProgressEvent(type, receivedLength, expectedLength);
1094     dispatchProgressEvent(EventTypeNames::loadend, receivedLength, expectedLength);
1095 }
1096 
dropProtectionSoon()1097 void XMLHttpRequest::dropProtectionSoon()
1098 {
1099     m_dropProtectionRunner.runAsync();
1100 }
1101 
dropProtection()1102 void XMLHttpRequest::dropProtection()
1103 {
1104     unsetPendingActivity(this);
1105 }
1106 
overrideMimeType(const AtomicString & override)1107 void XMLHttpRequest::overrideMimeType(const AtomicString& override)
1108 {
1109     m_mimeTypeOverride = override;
1110 }
1111 
setRequestHeader(const AtomicString & name,const AtomicString & value,ExceptionState & exceptionState)1112 void XMLHttpRequest::setRequestHeader(const AtomicString& name, const AtomicString& value, ExceptionState& exceptionState)
1113 {
1114     if (m_state != OPENED || m_loader) {
1115         exceptionState.throwDOMException(InvalidStateError, "The object's state must be OPENED.");
1116         return;
1117     }
1118 
1119     if (!isValidHTTPToken(name)) {
1120         exceptionState.throwDOMException(SyntaxError, "'" + name + "' is not a valid HTTP header field name.");
1121         return;
1122     }
1123 
1124     if (!isValidHTTPHeaderValue(value)) {
1125         exceptionState.throwDOMException(SyntaxError, "'" + value + "' is not a valid HTTP header field value.");
1126         return;
1127     }
1128 
1129     // No script (privileged or not) can set unsafe headers.
1130     if (!isAllowedHTTPHeader(name)) {
1131         logConsoleError(executionContext(), "Refused to set unsafe header \"" + name + "\"");
1132         return;
1133     }
1134 
1135     setRequestHeaderInternal(name, value);
1136 }
1137 
setRequestHeaderInternal(const AtomicString & name,const AtomicString & value)1138 void XMLHttpRequest::setRequestHeaderInternal(const AtomicString& name, const AtomicString& value)
1139 {
1140     HTTPHeaderMap::AddResult result = m_requestHeaders.add(name, value);
1141     if (!result.isNewEntry)
1142         result.storedValue->value = result.storedValue->value + ", " + value;
1143 }
1144 
getRequestHeader(const AtomicString & name) const1145 const AtomicString& XMLHttpRequest::getRequestHeader(const AtomicString& name) const
1146 {
1147     return m_requestHeaders.get(name);
1148 }
1149 
getAllResponseHeaders() const1150 String XMLHttpRequest::getAllResponseHeaders() const
1151 {
1152     if (m_state < HEADERS_RECEIVED || m_error)
1153         return "";
1154 
1155     StringBuilder stringBuilder;
1156 
1157     HTTPHeaderSet accessControlExposeHeaderSet;
1158     parseAccessControlExposeHeadersAllowList(m_response.httpHeaderField("Access-Control-Expose-Headers"), accessControlExposeHeaderSet);
1159     HTTPHeaderMap::const_iterator end = m_response.httpHeaderFields().end();
1160     for (HTTPHeaderMap::const_iterator it = m_response.httpHeaderFields().begin(); it!= end; ++it) {
1161         // Hide Set-Cookie header fields from the XMLHttpRequest client for these reasons:
1162         //     1) If the client did have access to the fields, then it could read HTTP-only
1163         //        cookies; those cookies are supposed to be hidden from scripts.
1164         //     2) There's no known harm in hiding Set-Cookie header fields entirely; we don't
1165         //        know any widely used technique that requires access to them.
1166         //     3) Firefox has implemented this policy.
1167         if (isSetCookieHeader(it->key) && !securityOrigin()->canLoadLocalResources())
1168             continue;
1169 
1170         if (!m_sameOriginRequest && !isOnAccessControlResponseHeaderWhitelist(it->key) && !accessControlExposeHeaderSet.contains(it->key))
1171             continue;
1172 
1173         stringBuilder.append(it->key);
1174         stringBuilder.append(':');
1175         stringBuilder.append(' ');
1176         stringBuilder.append(it->value);
1177         stringBuilder.append('\r');
1178         stringBuilder.append('\n');
1179     }
1180 
1181     return stringBuilder.toString();
1182 }
1183 
getResponseHeader(const AtomicString & name) const1184 const AtomicString& XMLHttpRequest::getResponseHeader(const AtomicString& name) const
1185 {
1186     if (m_state < HEADERS_RECEIVED || m_error)
1187         return nullAtom;
1188 
1189     // See comment in getAllResponseHeaders above.
1190     if (isSetCookieHeader(name) && !securityOrigin()->canLoadLocalResources()) {
1191         logConsoleError(executionContext(), "Refused to get unsafe header \"" + name + "\"");
1192         return nullAtom;
1193     }
1194 
1195     HTTPHeaderSet accessControlExposeHeaderSet;
1196     parseAccessControlExposeHeadersAllowList(m_response.httpHeaderField("Access-Control-Expose-Headers"), accessControlExposeHeaderSet);
1197 
1198     if (!m_sameOriginRequest && !isOnAccessControlResponseHeaderWhitelist(name) && !accessControlExposeHeaderSet.contains(name)) {
1199         logConsoleError(executionContext(), "Refused to get unsafe header \"" + name + "\"");
1200         return nullAtom;
1201     }
1202     return m_response.httpHeaderField(name);
1203 }
1204 
responseMIMEType() const1205 AtomicString XMLHttpRequest::responseMIMEType() const
1206 {
1207     AtomicString mimeType = extractMIMETypeFromMediaType(m_mimeTypeOverride);
1208     if (mimeType.isEmpty()) {
1209         if (m_response.isHTTP())
1210             mimeType = extractMIMETypeFromMediaType(m_response.httpHeaderField("Content-Type"));
1211         else
1212             mimeType = m_response.mimeType();
1213     }
1214     if (mimeType.isEmpty())
1215         mimeType = AtomicString("text/xml", AtomicString::ConstructFromLiteral);
1216 
1217     return mimeType;
1218 }
1219 
responseIsXML() const1220 bool XMLHttpRequest::responseIsXML() const
1221 {
1222     return DOMImplementation::isXMLMIMEType(responseMIMEType());
1223 }
1224 
status() const1225 int XMLHttpRequest::status() const
1226 {
1227     if (m_state == UNSENT || m_state == OPENED || m_error)
1228         return 0;
1229 
1230     if (m_response.httpStatusCode())
1231         return m_response.httpStatusCode();
1232 
1233     return 0;
1234 }
1235 
statusText() const1236 String XMLHttpRequest::statusText() const
1237 {
1238     if (m_state == UNSENT || m_state == OPENED || m_error)
1239         return String();
1240 
1241     if (!m_response.httpStatusText().isNull())
1242         return m_response.httpStatusText();
1243 
1244     return String();
1245 }
1246 
didFail(const ResourceError & error)1247 void XMLHttpRequest::didFail(const ResourceError& error)
1248 {
1249     WTF_LOG(Network, "XMLHttpRequest %p didFail()", this);
1250 
1251     // If we are already in an error state, for instance we called abort(), bail out early.
1252     if (m_error)
1253         return;
1254 
1255     if (error.isCancellation()) {
1256         handleDidCancel();
1257         return;
1258     }
1259 
1260     if (error.isTimeout()) {
1261         handleDidTimeout();
1262         return;
1263     }
1264 
1265     // Network failures are already reported to Web Inspector by ResourceLoader.
1266     if (error.domain() == errorDomainBlinkInternal)
1267         logConsoleError(executionContext(), "XMLHttpRequest cannot load " + error.failingURL() + ". " + error.localizedDescription());
1268 
1269     handleNetworkError();
1270 }
1271 
didFailRedirectCheck()1272 void XMLHttpRequest::didFailRedirectCheck()
1273 {
1274     WTF_LOG(Network, "XMLHttpRequest %p didFailRedirectCheck()", this);
1275 
1276     handleNetworkError();
1277 }
1278 
didFinishLoading(unsigned long identifier,double)1279 void XMLHttpRequest::didFinishLoading(unsigned long identifier, double)
1280 {
1281     WTF_LOG(Network, "XMLHttpRequest %p didFinishLoading(%lu)", this, identifier);
1282 
1283     if (m_error)
1284         return;
1285 
1286     if (m_state < HEADERS_RECEIVED)
1287         changeState(HEADERS_RECEIVED);
1288 
1289     if (m_decoder)
1290         m_responseText = m_responseText.concatenateWith(m_decoder->flush());
1291 
1292     if (m_responseStream)
1293         m_responseStream->finalize();
1294 
1295     clearVariablesForLoading();
1296 
1297     InspectorInstrumentation::didFinishXHRLoading(executionContext(), this, this, identifier, m_responseText, m_method, m_url, m_lastSendURL, m_lastSendLineNumber);
1298 
1299     // Prevent dropProtection releasing the last reference, and retain |this| until the end of this method.
1300     RefPtrWillBeRawPtr<XMLHttpRequest> protect(this);
1301 
1302     if (m_loader) {
1303         m_loader = nullptr;
1304         dropProtection();
1305     }
1306 
1307     changeState(DONE);
1308 }
1309 
didSendData(unsigned long long bytesSent,unsigned long long totalBytesToBeSent)1310 void XMLHttpRequest::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
1311 {
1312     WTF_LOG(Network, "XMLHttpRequest %p didSendData(%llu, %llu)", this, bytesSent, totalBytesToBeSent);
1313 
1314     if (!m_upload)
1315         return;
1316 
1317     if (m_uploadEventsAllowed)
1318         m_upload->dispatchProgressEvent(bytesSent, totalBytesToBeSent);
1319 
1320     if (bytesSent == totalBytesToBeSent && !m_uploadComplete) {
1321         m_uploadComplete = true;
1322         if (m_uploadEventsAllowed)
1323             m_upload->dispatchEventAndLoadEnd(EventTypeNames::load, true, bytesSent, totalBytesToBeSent);
1324     }
1325 }
1326 
didReceiveResponse(unsigned long identifier,const ResourceResponse & response)1327 void XMLHttpRequest::didReceiveResponse(unsigned long identifier, const ResourceResponse& response)
1328 {
1329     WTF_LOG(Network, "XMLHttpRequest %p didReceiveResponse(%lu)", this, identifier);
1330 
1331     m_response = response;
1332     if (!m_mimeTypeOverride.isEmpty()) {
1333         m_response.setHTTPHeaderField("Content-Type", m_mimeTypeOverride);
1334         m_responseEncoding = extractCharsetFromMediaType(m_mimeTypeOverride);
1335     }
1336 
1337     if (m_responseEncoding.isEmpty())
1338         m_responseEncoding = response.textEncodingName();
1339 }
1340 
didReceiveData(const char * data,int len)1341 void XMLHttpRequest::didReceiveData(const char* data, int len)
1342 {
1343     ASSERT(m_responseTypeCode != ResponseTypeBlob);
1344 
1345     if (m_error)
1346         return;
1347 
1348     if (m_state < HEADERS_RECEIVED)
1349         changeState(HEADERS_RECEIVED);
1350 
1351     bool useDecoder = m_responseTypeCode == ResponseTypeDefault || m_responseTypeCode == ResponseTypeText || m_responseTypeCode == ResponseTypeJSON || m_responseTypeCode == ResponseTypeDocument;
1352 
1353     if (useDecoder && !m_decoder) {
1354         if (m_responseTypeCode == ResponseTypeJSON) {
1355             m_decoder = TextResourceDecoder::create("application/json", "UTF-8");
1356         } else if (!m_responseEncoding.isEmpty()) {
1357             m_decoder = TextResourceDecoder::create("text/plain", m_responseEncoding);
1358         // allow TextResourceDecoder to look inside the m_response if it's XML or HTML
1359         } else if (responseIsXML()) {
1360             m_decoder = TextResourceDecoder::create("application/xml");
1361             // Don't stop on encoding errors, unlike it is done for other kinds
1362             // of XML resources. This matches the behavior of previous WebKit
1363             // versions, Firefox and Opera.
1364             m_decoder->useLenientXMLDecoding();
1365         } else if (equalIgnoringCase(responseMIMEType(), "text/html")) {
1366             m_decoder = TextResourceDecoder::create("text/html", "UTF-8");
1367         } else {
1368             m_decoder = TextResourceDecoder::create("text/plain", "UTF-8");
1369         }
1370     }
1371 
1372     if (!len)
1373         return;
1374 
1375     if (len == -1)
1376         len = strlen(data);
1377 
1378     if (useDecoder) {
1379         m_responseText = m_responseText.concatenateWith(m_decoder->decode(data, len));
1380     } else if (m_responseTypeCode == ResponseTypeArrayBuffer) {
1381         // Buffer binary data.
1382         if (!m_binaryResponseBuilder)
1383             m_binaryResponseBuilder = SharedBuffer::create();
1384         m_binaryResponseBuilder->append(data, len);
1385     } else if (m_responseTypeCode == ResponseTypeStream) {
1386         if (!m_responseStream)
1387             m_responseStream = Stream::create(executionContext(), responseMIMEType());
1388         m_responseStream->addData(data, len);
1389     }
1390 
1391     if (m_error)
1392         return;
1393 
1394     trackProgress(len);
1395 }
1396 
didDownloadData(int dataLength)1397 void XMLHttpRequest::didDownloadData(int dataLength)
1398 {
1399     ASSERT(m_responseTypeCode == ResponseTypeBlob);
1400 
1401     if (m_error)
1402         return;
1403 
1404     if (m_state < HEADERS_RECEIVED)
1405         changeState(HEADERS_RECEIVED);
1406 
1407     if (!dataLength)
1408         return;
1409 
1410     // readystatechange event handler may do something to put this XHR in error
1411     // state. We need to check m_error again here.
1412     if (m_error)
1413         return;
1414 
1415     m_downloadedBlobLength += dataLength;
1416 
1417     trackProgress(dataLength);
1418 }
1419 
handleDidTimeout()1420 void XMLHttpRequest::handleDidTimeout()
1421 {
1422     WTF_LOG(Network, "XMLHttpRequest %p handleDidTimeout()", this);
1423 
1424     // internalAbort() calls dropProtection(), which may release the last reference.
1425     RefPtrWillBeRawPtr<XMLHttpRequest> protect(this);
1426 
1427     // Response is cleared next, save needed progress event data.
1428     long long expectedLength = m_response.expectedContentLength();
1429     long long receivedLength = m_receivedLength;
1430 
1431     if (!internalAbort())
1432         return;
1433 
1434     handleDidFailGeneric();
1435     handleRequestError(TimeoutError, EventTypeNames::timeout, receivedLength, expectedLength);
1436 }
1437 
suspend()1438 void XMLHttpRequest::suspend()
1439 {
1440     m_progressEventThrottle.suspend();
1441 }
1442 
resume()1443 void XMLHttpRequest::resume()
1444 {
1445     m_progressEventThrottle.resume();
1446 }
1447 
stop()1448 void XMLHttpRequest::stop()
1449 {
1450     internalAbort(DropProtectionAsync);
1451 }
1452 
contextDestroyed()1453 void XMLHttpRequest::contextDestroyed()
1454 {
1455     ASSERT(!m_loader);
1456     ActiveDOMObject::contextDestroyed();
1457 }
1458 
interfaceName() const1459 const AtomicString& XMLHttpRequest::interfaceName() const
1460 {
1461     return EventTargetNames::XMLHttpRequest;
1462 }
1463 
executionContext() const1464 ExecutionContext* XMLHttpRequest::executionContext() const
1465 {
1466     return ActiveDOMObject::executionContext();
1467 }
1468 
trace(Visitor * visitor)1469 void XMLHttpRequest::trace(Visitor* visitor)
1470 {
1471     visitor->trace(m_responseBlob);
1472     visitor->trace(m_responseStream);
1473     visitor->trace(m_responseDocument);
1474     visitor->trace(m_progressEventThrottle);
1475     visitor->trace(m_upload);
1476     XMLHttpRequestEventTarget::trace(visitor);
1477 }
1478 
1479 } // namespace WebCore
1480