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