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