OLD | NEW |
| (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 | |
OLD | NEW |