Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(232)

Side by Side Diff: Source/core/xml/XMLHttpRequest.cpp

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

Powered by Google App Engine
This is Rietveld 408576698