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