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 void XMLHttpRequest::open(const AtomicString& method, const KURL& url, Exception
State& exceptionState) | |
558 { | |
559 open(method, url, true, exceptionState); | |
560 } | |
561 | |
562 void XMLHttpRequest::open(const AtomicString& method, const KURL& url, bool asyn
c, ExceptionState& exceptionState) | |
563 { | |
564 WTF_LOG(Network, "XMLHttpRequest %p open('%s', '%s', %d)", this, method.utf8
().data(), url.elidedString().utf8().data(), async); | |
565 | |
566 if (!internalAbort()) | |
567 return; | |
568 | |
569 State previousState = m_state; | |
570 m_state = UNSENT; | |
571 m_error = false; | |
572 m_uploadComplete = false; | |
573 | |
574 if (!isValidHTTPToken(method)) { | |
575 exceptionState.throwDOMException(SyntaxError, "'" + method + "' is not a
valid HTTP method."); | |
576 return; | |
577 } | |
578 | |
579 if (FetchUtils::isForbiddenMethod(method)) { | |
580 exceptionState.throwSecurityError("'" + method + "' HTTP method is unsup
ported."); | |
581 return; | |
582 } | |
583 | |
584 if (!ContentSecurityPolicy::shouldBypassMainWorld(executionContext()) && !ex
ecutionContext()->contentSecurityPolicy()->allowConnectToSource(url)) { | |
585 // We can safely expose the URL to JavaScript, as these checks happen sy
nchronously before redirection. JavaScript receives no new information. | |
586 exceptionState.throwSecurityError("Refused to connect to '" + url.elided
String() + "' because it violates the document's Content Security Policy."); | |
587 return; | |
588 } | |
589 | |
590 if (!async && executionContext()->isDocument()) { | |
591 if (document()->settings() && !document()->settings()->syncXHRInDocument
sEnabled()) { | |
592 exceptionState.throwDOMException(InvalidAccessError, "Synchronous re
quests are disabled for this page."); | |
593 return; | |
594 } | |
595 | |
596 // Newer functionality is not available to synchronous requests in windo
w contexts, as a spec-mandated | |
597 // attempt to discourage synchronous XHR use. responseType is one such p
iece of functionality. | |
598 if (m_responseTypeCode != ResponseTypeDefault) { | |
599 exceptionState.throwDOMException(InvalidAccessError, "Synchronous re
quests from a document must not set a response type."); | |
600 return; | |
601 } | |
602 | |
603 // Similarly, timeouts are disabled for synchronous requests as well. | |
604 if (m_timeoutMilliseconds > 0) { | |
605 exceptionState.throwDOMException(InvalidAccessError, "Synchronous re
quests must not set a timeout."); | |
606 return; | |
607 } | |
608 } | |
609 | |
610 m_method = FetchUtils::normalizeMethod(method); | |
611 | |
612 m_url = url; | |
613 | |
614 m_async = async; | |
615 | |
616 ASSERT(!m_loader); | |
617 | |
618 // Check previous state to avoid dispatching readyState event | |
619 // when calling open several times in a row. | |
620 if (previousState != OPENED) | |
621 changeState(OPENED); | |
622 else | |
623 m_state = OPENED; | |
624 } | |
625 | |
626 void XMLHttpRequest::open(const AtomicString& method, const KURL& url, bool asyn
c, const String& user, ExceptionState& exceptionState) | |
627 { | |
628 KURL urlWithCredentials(url); | |
629 urlWithCredentials.setUser(user); | |
630 | |
631 open(method, urlWithCredentials, async, exceptionState); | |
632 } | |
633 | |
634 void XMLHttpRequest::open(const AtomicString& method, const KURL& url, bool asyn
c, const String& user, const String& password, ExceptionState& exceptionState) | |
635 { | |
636 KURL urlWithCredentials(url); | |
637 urlWithCredentials.setUser(user); | |
638 urlWithCredentials.setPass(password); | |
639 | |
640 open(method, urlWithCredentials, async, exceptionState); | |
641 } | |
642 | |
643 bool XMLHttpRequest::initSend(ExceptionState& exceptionState) | |
644 { | |
645 if (!executionContext()) | |
646 return false; | |
647 | |
648 if (m_state != OPENED || m_loader) { | |
649 exceptionState.throwDOMException(InvalidStateError, "The object's state
must be OPENED."); | |
650 return false; | |
651 } | |
652 | |
653 m_error = false; | |
654 return true; | |
655 } | |
656 | |
657 void XMLHttpRequest::send(ExceptionState& exceptionState) | |
658 { | |
659 send(String(), exceptionState); | |
660 } | |
661 | |
662 bool XMLHttpRequest::areMethodAndURLValidForSend() | |
663 { | |
664 return m_method != "GET" && m_method != "HEAD" && m_url.protocolIsInHTTPFami
ly(); | |
665 } | |
666 | |
667 void XMLHttpRequest::send(Document* document, ExceptionState& exceptionState) | |
668 { | |
669 WTF_LOG(Network, "XMLHttpRequest %p send() Document %p", this, document); | |
670 | |
671 ASSERT(document); | |
672 | |
673 if (!initSend(exceptionState)) | |
674 return; | |
675 | |
676 RefPtr<FormData> httpBody; | |
677 | |
678 if (areMethodAndURLValidForSend()) { | |
679 if (getRequestHeader("Content-Type").isEmpty()) { | |
680 // FIXME: this should include the charset used for encoding. | |
681 setRequestHeaderInternal("Content-Type", "application/xml"); | |
682 } | |
683 | |
684 // FIXME: According to XMLHttpRequest Level 2, this should use the Docum
ent.innerHTML algorithm | |
685 // from the HTML5 specification to serialize the document. | |
686 String body = createMarkup(document); | |
687 | |
688 // FIXME: This should use value of document.inputEncoding to determine t
he encoding to use. | |
689 httpBody = FormData::create(UTF8Encoding().encode(body, WTF::EntitiesFor
Unencodables)); | |
690 if (m_upload) | |
691 httpBody->setAlwaysStream(true); | |
692 } | |
693 | |
694 createRequest(httpBody.release(), exceptionState); | |
695 } | |
696 | |
697 void XMLHttpRequest::send(const String& body, ExceptionState& exceptionState) | |
698 { | |
699 WTF_LOG(Network, "XMLHttpRequest %p send() String '%s'", this, body.utf8().d
ata()); | |
700 | |
701 if (!initSend(exceptionState)) | |
702 return; | |
703 | |
704 RefPtr<FormData> httpBody; | |
705 | |
706 if (!body.isNull() && areMethodAndURLValidForSend()) { | |
707 String contentType = getRequestHeader("Content-Type"); | |
708 if (contentType.isEmpty()) { | |
709 setRequestHeaderInternal("Content-Type", "text/plain;charset=UTF-8")
; | |
710 } else { | |
711 replaceCharsetInMediaType(contentType, "UTF-8"); | |
712 m_requestHeaders.set("Content-Type", AtomicString(contentType)); | |
713 } | |
714 | |
715 httpBody = FormData::create(UTF8Encoding().encode(body, WTF::EntitiesFor
Unencodables)); | |
716 if (m_upload) | |
717 httpBody->setAlwaysStream(true); | |
718 } | |
719 | |
720 createRequest(httpBody.release(), exceptionState); | |
721 } | |
722 | |
723 void XMLHttpRequest::send(Blob* body, ExceptionState& exceptionState) | |
724 { | |
725 WTF_LOG(Network, "XMLHttpRequest %p send() Blob '%s'", this, body->uuid().ut
f8().data()); | |
726 | |
727 if (!initSend(exceptionState)) | |
728 return; | |
729 | |
730 RefPtr<FormData> httpBody; | |
731 | |
732 if (areMethodAndURLValidForSend()) { | |
733 if (getRequestHeader("Content-Type").isEmpty()) { | |
734 const String& blobType = body->type(); | |
735 if (!blobType.isEmpty() && isValidContentType(blobType)) { | |
736 setRequestHeaderInternal("Content-Type", AtomicString(blobType))
; | |
737 } else { | |
738 // From FileAPI spec, whenever media type cannot be determined, | |
739 // empty string must be returned. | |
740 setRequestHeaderInternal("Content-Type", ""); | |
741 } | |
742 } | |
743 | |
744 // FIXME: add support for uploading bundles. | |
745 httpBody = FormData::create(); | |
746 if (body->hasBackingFile()) { | |
747 File* file = toFile(body); | |
748 if (!file->path().isEmpty()) | |
749 httpBody->appendFile(file->path()); | |
750 else if (!file->fileSystemURL().isEmpty()) | |
751 httpBody->appendFileSystemURL(file->fileSystemURL()); | |
752 else | |
753 ASSERT_NOT_REACHED(); | |
754 } else { | |
755 httpBody->appendBlob(body->uuid(), body->blobDataHandle()); | |
756 } | |
757 } | |
758 | |
759 createRequest(httpBody.release(), exceptionState); | |
760 } | |
761 | |
762 void XMLHttpRequest::send(DOMFormData* body, ExceptionState& exceptionState) | |
763 { | |
764 WTF_LOG(Network, "XMLHttpRequest %p send() DOMFormData %p", this, body); | |
765 | |
766 if (!initSend(exceptionState)) | |
767 return; | |
768 | |
769 RefPtr<FormData> httpBody; | |
770 | |
771 if (areMethodAndURLValidForSend()) { | |
772 httpBody = body->createMultiPartFormData(); | |
773 | |
774 if (getRequestHeader("Content-Type").isEmpty()) { | |
775 AtomicString contentType = AtomicString("multipart/form-data; bounda
ry=", AtomicString::ConstructFromLiteral) + httpBody->boundary().data(); | |
776 setRequestHeaderInternal("Content-Type", contentType); | |
777 } | |
778 } | |
779 | |
780 createRequest(httpBody.release(), exceptionState); | |
781 } | |
782 | |
783 void XMLHttpRequest::send(ArrayBuffer* body, ExceptionState& exceptionState) | |
784 { | |
785 WTF_LOG(Network, "XMLHttpRequest %p send() ArrayBuffer %p", this, body); | |
786 | |
787 sendBytesData(body->data(), body->byteLength(), exceptionState); | |
788 } | |
789 | |
790 void XMLHttpRequest::send(ArrayBufferView* body, ExceptionState& exceptionState) | |
791 { | |
792 WTF_LOG(Network, "XMLHttpRequest %p send() ArrayBufferView %p", this, body); | |
793 | |
794 sendBytesData(body->baseAddress(), body->byteLength(), exceptionState); | |
795 } | |
796 | |
797 void XMLHttpRequest::sendBytesData(const void* data, size_t length, ExceptionSta
te& exceptionState) | |
798 { | |
799 if (!initSend(exceptionState)) | |
800 return; | |
801 | |
802 RefPtr<FormData> httpBody; | |
803 | |
804 if (areMethodAndURLValidForSend()) { | |
805 httpBody = FormData::create(data, length); | |
806 if (m_upload) | |
807 httpBody->setAlwaysStream(true); | |
808 } | |
809 | |
810 createRequest(httpBody.release(), exceptionState); | |
811 } | |
812 | |
813 void XMLHttpRequest::sendForInspectorXHRReplay(PassRefPtr<FormData> formData, Ex
ceptionState& exceptionState) | |
814 { | |
815 createRequest(formData ? formData->deepCopy() : nullptr, exceptionState); | |
816 m_exceptionCode = exceptionState.code(); | |
817 } | |
818 | |
819 void XMLHttpRequest::createRequest(PassRefPtr<FormData> httpBody, ExceptionState
& exceptionState) | |
820 { | |
821 // Only GET request is supported for blob URL. | |
822 if (m_url.protocolIs("blob") && m_method != "GET") { | |
823 exceptionState.throwDOMException(NetworkError, "'GET' is the only method
allowed for 'blob:' URLs."); | |
824 return; | |
825 } | |
826 | |
827 // The presence of upload event listeners forces us to use preflighting beca
use POSTing to an URL that does not | |
828 // permit cross origin requests should look exactly like POSTing to an URL t
hat does not respond at all. | |
829 // Also, only async requests support upload progress events. | |
830 bool uploadEvents = false; | |
831 if (m_async) { | |
832 dispatchProgressEvent(EventTypeNames::loadstart, 0, 0); | |
833 if (httpBody && m_upload) { | |
834 uploadEvents = m_upload->hasEventListeners(); | |
835 m_upload->dispatchEvent(XMLHttpRequestProgressEvent::create(EventTyp
eNames::loadstart)); | |
836 } | |
837 } | |
838 | |
839 m_sameOriginRequest = securityOrigin()->canRequest(m_url); | |
840 | |
841 // We also remember whether upload events should be allowed for this request
in case the upload listeners are | |
842 // added after the request is started. | |
843 m_uploadEventsAllowed = m_sameOriginRequest || uploadEvents || !FetchUtils::
isSimpleRequest(m_method, m_requestHeaders); | |
844 | |
845 ASSERT(executionContext()); | |
846 ExecutionContext& executionContext = *this->executionContext(); | |
847 | |
848 ResourceRequest request(m_url); | |
849 request.setHTTPMethod(m_method); | |
850 request.setRequestContext(blink::WebURLRequest::RequestContextXMLHttpRequest
); | |
851 | |
852 InspectorInstrumentation::willLoadXHR(&executionContext, this, this, m_metho
d, m_url, m_async, httpBody ? httpBody->deepCopy() : nullptr, m_requestHeaders,
m_includeCredentials); | |
853 | |
854 if (httpBody) { | |
855 ASSERT(m_method != "GET"); | |
856 ASSERT(m_method != "HEAD"); | |
857 request.setHTTPBody(httpBody); | |
858 } | |
859 | |
860 if (m_requestHeaders.size() > 0) | |
861 request.addHTTPHeaderFields(m_requestHeaders); | |
862 | |
863 ThreadableLoaderOptions options; | |
864 options.preflightPolicy = uploadEvents ? ForcePreflight : ConsiderPreflight; | |
865 options.crossOriginRequestPolicy = UseAccessControl; | |
866 options.initiator = FetchInitiatorTypeNames::xmlhttprequest; | |
867 options.contentSecurityPolicyEnforcement = ContentSecurityPolicy::shouldBypa
ssMainWorld(&executionContext) ? DoNotEnforceContentSecurityPolicy : EnforceConn
ectSrcDirective; | |
868 options.timeoutMilliseconds = m_timeoutMilliseconds; | |
869 | |
870 ResourceLoaderOptions resourceLoaderOptions; | |
871 resourceLoaderOptions.allowCredentials = (m_sameOriginRequest || m_includeCr
edentials) ? AllowStoredCredentials : DoNotAllowStoredCredentials; | |
872 resourceLoaderOptions.credentialsRequested = m_includeCredentials ? ClientRe
questedCredentials : ClientDidNotRequestCredentials; | |
873 resourceLoaderOptions.securityOrigin = securityOrigin(); | |
874 resourceLoaderOptions.mixedContentBlockingTreatment = RuntimeEnabledFeatures
::laxMixedContentCheckingEnabled() ? TreatAsPassiveContent : TreatAsActiveConten
t; | |
875 | |
876 // When responseType is set to "blob", we redirect the downloaded data to a | |
877 // file-handle directly. | |
878 m_downloadingToFile = responseTypeCode() == ResponseTypeBlob; | |
879 if (m_downloadingToFile) { | |
880 request.setDownloadToFile(true); | |
881 resourceLoaderOptions.dataBufferingPolicy = DoNotBufferData; | |
882 } | |
883 | |
884 m_exceptionCode = 0; | |
885 m_error = false; | |
886 | |
887 if (m_async) { | |
888 if (m_upload) | |
889 request.setReportUploadProgress(true); | |
890 | |
891 // ThreadableLoader::create can return null here, for example if we're n
o longer attached to a page. | |
892 // This is true while running onunload handlers. | |
893 // FIXME: Maybe we need to be able to send XMLHttpRequests from onunload
, <http://bugs.webkit.org/show_bug.cgi?id=10904>. | |
894 // FIXME: Maybe create() can return null for other reasons too? | |
895 ASSERT(!m_loader); | |
896 m_loader = ThreadableLoader::create(executionContext, this, request, opt
ions, resourceLoaderOptions); | |
897 } else { | |
898 // Use count for XHR synchronous requests. | |
899 UseCounter::count(&executionContext, UseCounter::XMLHttpRequestSynchrono
us); | |
900 ThreadableLoader::loadResourceSynchronously(executionContext, request, *
this, options, resourceLoaderOptions); | |
901 } | |
902 | |
903 if (!m_exceptionCode && m_error) | |
904 m_exceptionCode = NetworkError; | |
905 if (m_exceptionCode) | |
906 exceptionState.throwDOMException(m_exceptionCode, "Failed to load '" + m
_url.elidedString() + "'."); | |
907 } | |
908 | |
909 void XMLHttpRequest::abort() | |
910 { | |
911 WTF_LOG(Network, "XMLHttpRequest %p abort()", this); | |
912 | |
913 // internalAbort() clears |m_loader|. Compute |sendFlag| now. | |
914 // | |
915 // |sendFlag| corresponds to "the send() flag" defined in the XHR spec. | |
916 // | |
917 // |sendFlag| is only set when we have an active, asynchronous loader. | |
918 // Don't use it as "the send() flag" when the XHR is in sync mode. | |
919 bool sendFlag = m_loader; | |
920 | |
921 // internalAbort() clears the response. Save the data needed for | |
922 // dispatching ProgressEvents. | |
923 long long expectedLength = m_response.expectedContentLength(); | |
924 long long receivedLength = m_receivedLength; | |
925 | |
926 if (!internalAbort()) | |
927 return; | |
928 | |
929 // The script never gets any chance to call abort() on a sync XHR between | |
930 // send() call and transition to the DONE state. It's because a sync XHR | |
931 // doesn't dispatch any event between them. So, if |m_async| is false, we | |
932 // can skip the "request error steps" (defined in the XHR spec) without any | |
933 // state check. | |
934 // | |
935 // FIXME: It's possible open() is invoked in internalAbort() and |m_async| | |
936 // becomes true by that. We should implement more reliable treatment for | |
937 // nested method invocations at some point. | |
938 if (m_async) { | |
939 if ((m_state == OPENED && sendFlag) || m_state == HEADERS_RECEIVED || m_
state == LOADING) { | |
940 ASSERT(!m_loader); | |
941 handleRequestError(0, EventTypeNames::abort, receivedLength, expecte
dLength); | |
942 } | |
943 } | |
944 m_state = UNSENT; | |
945 } | |
946 | |
947 void XMLHttpRequest::clearVariablesForLoading() | |
948 { | |
949 if (m_blobLoader) { | |
950 m_blobLoader->cancel(); | |
951 m_blobLoader = nullptr; | |
952 } | |
953 | |
954 m_decoder.clear(); | |
955 | |
956 if (m_responseDocumentParser) { | |
957 m_responseDocumentParser->removeClient(this); | |
958 #if !ENABLE(OILPAN) | |
959 m_responseDocumentParser->detach(); | |
960 #endif | |
961 m_responseDocumentParser = nullptr; | |
962 } | |
963 | |
964 m_finalResponseCharset = String(); | |
965 } | |
966 | |
967 bool XMLHttpRequest::internalAbort() | |
968 { | |
969 m_error = true; | |
970 | |
971 if (m_responseDocumentParser && !m_responseDocumentParser->isStopped()) | |
972 m_responseDocumentParser->stopParsing(); | |
973 | |
974 clearVariablesForLoading(); | |
975 | |
976 InspectorInstrumentation::didFailXHRLoading(executionContext(), this, this); | |
977 | |
978 if (m_responseLegacyStream && m_state != DONE) | |
979 m_responseLegacyStream->abort(); | |
980 | |
981 if (m_responseStream) { | |
982 // When the stream is already closed (including canceled from the | |
983 // user), |error| does nothing. | |
984 // FIXME: Create a more specific error. | |
985 m_responseStream->error(DOMException::create(!m_async && m_exceptionCode
? m_exceptionCode : AbortError, "XMLHttpRequest::abort")); | |
986 } | |
987 | |
988 clearResponse(); | |
989 clearRequest(); | |
990 | |
991 if (!m_loader) | |
992 return true; | |
993 | |
994 // Cancelling the ThreadableLoader m_loader may result in calling | |
995 // window.onload synchronously. If such an onload handler contains open() | |
996 // call on the same XMLHttpRequest object, reentry happens. | |
997 // | |
998 // If, window.onload contains open() and send(), m_loader will be set to | |
999 // non 0 value. So, we cannot continue the outer open(). In such case, | |
1000 // just abort the outer open() by returning false. | |
1001 RefPtr<ThreadableLoader> loader = m_loader.release(); | |
1002 loader->cancel(); | |
1003 | |
1004 // If abort() called internalAbort() and a nested open() ended up | |
1005 // clearing the error flag, but didn't send(), make sure the error | |
1006 // flag is still set. | |
1007 bool newLoadStarted = hasPendingActivity(); | |
1008 if (!newLoadStarted) | |
1009 m_error = true; | |
1010 | |
1011 return !newLoadStarted; | |
1012 } | |
1013 | |
1014 void XMLHttpRequest::clearResponse() | |
1015 { | |
1016 // FIXME: when we add the support for multi-part XHR, we will have to | |
1017 // be careful with this initialization. | |
1018 m_receivedLength = 0; | |
1019 | |
1020 m_response = ResourceResponse(); | |
1021 | |
1022 m_responseText.clear(); | |
1023 | |
1024 m_parsedResponse = false; | |
1025 m_responseDocument = nullptr; | |
1026 | |
1027 m_responseBlob = nullptr; | |
1028 | |
1029 m_downloadingToFile = false; | |
1030 m_lengthDownloadedToFile = 0; | |
1031 | |
1032 m_responseLegacyStream = nullptr; | |
1033 m_responseStream = nullptr; | |
1034 | |
1035 // These variables may referred by the response accessors. So, we can clear | |
1036 // this only when we clear the response holder variables above. | |
1037 m_binaryResponseBuilder.clear(); | |
1038 m_responseArrayBuffer.clear(); | |
1039 } | |
1040 | |
1041 void XMLHttpRequest::clearRequest() | |
1042 { | |
1043 m_requestHeaders.clear(); | |
1044 } | |
1045 | |
1046 void XMLHttpRequest::dispatchProgressEvent(const AtomicString& type, long long r
eceivedLength, long long expectedLength) | |
1047 { | |
1048 bool lengthComputable = expectedLength > 0 && receivedLength <= expectedLeng
th; | |
1049 unsigned long long loaded = receivedLength >= 0 ? static_cast<unsigned long
long>(receivedLength) : 0; | |
1050 unsigned long long total = lengthComputable ? static_cast<unsigned long long
>(expectedLength) : 0; | |
1051 | |
1052 m_progressEventThrottle.dispatchProgressEvent(type, lengthComputable, loaded
, total); | |
1053 | |
1054 if (type == EventTypeNames::loadend) | |
1055 InspectorInstrumentation::didDispatchXHRLoadendEvent(executionContext(),
this); | |
1056 } | |
1057 | |
1058 void XMLHttpRequest::dispatchProgressEventFromSnapshot(const AtomicString& type) | |
1059 { | |
1060 dispatchProgressEvent(type, m_receivedLength, m_response.expectedContentLeng
th()); | |
1061 } | |
1062 | |
1063 void XMLHttpRequest::handleNetworkError() | |
1064 { | |
1065 WTF_LOG(Network, "XMLHttpRequest %p handleNetworkError()", this); | |
1066 | |
1067 // Response is cleared next, save needed progress event data. | |
1068 long long expectedLength = m_response.expectedContentLength(); | |
1069 long long receivedLength = m_receivedLength; | |
1070 | |
1071 // Prevent the XMLHttpRequest instance from being destoryed during | |
1072 // |internalAbort()|. | |
1073 RefPtrWillBeRawPtr<XMLHttpRequest> protect(this); | |
1074 | |
1075 if (!internalAbort()) | |
1076 return; | |
1077 | |
1078 handleRequestError(NetworkError, EventTypeNames::error, receivedLength, expe
ctedLength); | |
1079 } | |
1080 | |
1081 void XMLHttpRequest::handleDidCancel() | |
1082 { | |
1083 WTF_LOG(Network, "XMLHttpRequest %p handleDidCancel()", this); | |
1084 | |
1085 // Response is cleared next, save needed progress event data. | |
1086 long long expectedLength = m_response.expectedContentLength(); | |
1087 long long receivedLength = m_receivedLength; | |
1088 | |
1089 // Prevent the XMLHttpRequest instance from being destoryed during | |
1090 // |internalAbort()|. | |
1091 RefPtrWillBeRawPtr<XMLHttpRequest> protect(this); | |
1092 | |
1093 if (!internalAbort()) | |
1094 return; | |
1095 | |
1096 handleRequestError(AbortError, EventTypeNames::abort, receivedLength, expect
edLength); | |
1097 } | |
1098 | |
1099 void XMLHttpRequest::handleRequestError(ExceptionCode exceptionCode, const Atomi
cString& type, long long receivedLength, long long expectedLength) | |
1100 { | |
1101 WTF_LOG(Network, "XMLHttpRequest %p handleRequestError()", this); | |
1102 | |
1103 // The request error steps for event 'type' and exception 'exceptionCode'. | |
1104 | |
1105 if (!m_async && exceptionCode) { | |
1106 m_state = DONE; | |
1107 m_exceptionCode = exceptionCode; | |
1108 return; | |
1109 } | |
1110 // With m_error set, the state change steps are minimal: any pending | |
1111 // progress event is flushed + a readystatechange is dispatched. | |
1112 // No new progress events dispatched; as required, that happens at | |
1113 // the end here. | |
1114 ASSERT(m_error); | |
1115 changeState(DONE); | |
1116 | |
1117 if (!m_uploadComplete) { | |
1118 m_uploadComplete = true; | |
1119 if (m_upload && m_uploadEventsAllowed) | |
1120 m_upload->handleRequestError(type); | |
1121 } | |
1122 | |
1123 // Note: The below event dispatch may be called while |hasPendingActivity()
== false|, | |
1124 // when |handleRequestError| is called after |internalAbort()|. | |
1125 // This is safe, however, as |this| will be kept alive from a strong ref |Ev
ent::m_target|. | |
1126 dispatchProgressEvent(EventTypeNames::progress, receivedLength, expectedLeng
th); | |
1127 dispatchProgressEvent(type, receivedLength, expectedLength); | |
1128 dispatchProgressEvent(EventTypeNames::loadend, receivedLength, expectedLengt
h); | |
1129 } | |
1130 | |
1131 void XMLHttpRequest::overrideMimeType(const AtomicString& mimeType, ExceptionSta
te& exceptionState) | |
1132 { | |
1133 if (m_state == LOADING || m_state == DONE) { | |
1134 exceptionState.throwDOMException(InvalidStateError, "MimeType cannot be
overridden when the state is LOADING or DONE."); | |
1135 return; | |
1136 } | |
1137 | |
1138 m_mimeTypeOverride = mimeType; | |
1139 } | |
1140 | |
1141 void XMLHttpRequest::setRequestHeader(const AtomicString& name, const AtomicStri
ng& value, ExceptionState& exceptionState) | |
1142 { | |
1143 if (m_state != OPENED || m_loader) { | |
1144 exceptionState.throwDOMException(InvalidStateError, "The object's state
must be OPENED."); | |
1145 return; | |
1146 } | |
1147 | |
1148 if (!isValidHTTPToken(name)) { | |
1149 exceptionState.throwDOMException(SyntaxError, "'" + name + "' is not a v
alid HTTP header field name."); | |
1150 return; | |
1151 } | |
1152 | |
1153 if (!isValidHTTPHeaderValue(value)) { | |
1154 exceptionState.throwDOMException(SyntaxError, "'" + value + "' is not a
valid HTTP header field value."); | |
1155 return; | |
1156 } | |
1157 | |
1158 // No script (privileged or not) can set unsafe headers. | |
1159 if (FetchUtils::isForbiddenHeaderName(name)) { | |
1160 logConsoleError(executionContext(), "Refused to set unsafe header \"" +
name + "\""); | |
1161 return; | |
1162 } | |
1163 | |
1164 setRequestHeaderInternal(name, value); | |
1165 } | |
1166 | |
1167 void XMLHttpRequest::setRequestHeaderInternal(const AtomicString& name, const At
omicString& value) | |
1168 { | |
1169 HTTPHeaderMap::AddResult result = m_requestHeaders.add(name, value); | |
1170 if (!result.isNewEntry) | |
1171 result.storedValue->value = result.storedValue->value + ", " + value; | |
1172 } | |
1173 | |
1174 const AtomicString& XMLHttpRequest::getRequestHeader(const AtomicString& name) c
onst | |
1175 { | |
1176 return m_requestHeaders.get(name); | |
1177 } | |
1178 | |
1179 String XMLHttpRequest::getAllResponseHeaders() const | |
1180 { | |
1181 if (m_state < HEADERS_RECEIVED || m_error) | |
1182 return ""; | |
1183 | |
1184 StringBuilder stringBuilder; | |
1185 | |
1186 HTTPHeaderSet accessControlExposeHeaderSet; | |
1187 parseAccessControlExposeHeadersAllowList(m_response.httpHeaderField("Access-
Control-Expose-Headers"), accessControlExposeHeaderSet); | |
1188 HTTPHeaderMap::const_iterator end = m_response.httpHeaderFields().end(); | |
1189 for (HTTPHeaderMap::const_iterator it = m_response.httpHeaderFields().begin(
); it!= end; ++it) { | |
1190 // Hide Set-Cookie header fields from the XMLHttpRequest client for thes
e reasons: | |
1191 // 1) If the client did have access to the fields, then it could rea
d HTTP-only | |
1192 // cookies; those cookies are supposed to be hidden from scripts. | |
1193 // 2) There's no known harm in hiding Set-Cookie header fields entir
ely; we don't | |
1194 // know any widely used technique that requires access to them. | |
1195 // 3) Firefox has implemented this policy. | |
1196 if (isSetCookieHeader(it->key) && !securityOrigin()->canLoadLocalResourc
es()) | |
1197 continue; | |
1198 | |
1199 if (!m_sameOriginRequest && !isOnAccessControlResponseHeaderWhitelist(it
->key) && !accessControlExposeHeaderSet.contains(it->key)) | |
1200 continue; | |
1201 | |
1202 stringBuilder.append(it->key); | |
1203 stringBuilder.append(':'); | |
1204 stringBuilder.append(' '); | |
1205 stringBuilder.append(it->value); | |
1206 stringBuilder.append('\r'); | |
1207 stringBuilder.append('\n'); | |
1208 } | |
1209 | |
1210 return stringBuilder.toString(); | |
1211 } | |
1212 | |
1213 const AtomicString& XMLHttpRequest::getResponseHeader(const AtomicString& name)
const | |
1214 { | |
1215 if (m_state < HEADERS_RECEIVED || m_error) | |
1216 return nullAtom; | |
1217 | |
1218 // See comment in getAllResponseHeaders above. | |
1219 if (isSetCookieHeader(name) && !securityOrigin()->canLoadLocalResources()) { | |
1220 logConsoleError(executionContext(), "Refused to get unsafe header \"" +
name + "\""); | |
1221 return nullAtom; | |
1222 } | |
1223 | |
1224 HTTPHeaderSet accessControlExposeHeaderSet; | |
1225 parseAccessControlExposeHeadersAllowList(m_response.httpHeaderField("Access-
Control-Expose-Headers"), accessControlExposeHeaderSet); | |
1226 | |
1227 if (!m_sameOriginRequest && !isOnAccessControlResponseHeaderWhitelist(name)
&& !accessControlExposeHeaderSet.contains(name)) { | |
1228 logConsoleError(executionContext(), "Refused to get unsafe header \"" +
name + "\""); | |
1229 return nullAtom; | |
1230 } | |
1231 return m_response.httpHeaderField(name); | |
1232 } | |
1233 | |
1234 AtomicString XMLHttpRequest::finalResponseMIMEType() const | |
1235 { | |
1236 AtomicString overriddenType = extractMIMETypeFromMediaType(m_mimeTypeOverrid
e); | |
1237 if (!overriddenType.isEmpty()) | |
1238 return overriddenType; | |
1239 | |
1240 if (m_response.isHTTP()) | |
1241 return extractMIMETypeFromMediaType(m_response.httpHeaderField("Content-
Type")); | |
1242 | |
1243 return m_response.mimeType(); | |
1244 } | |
1245 | |
1246 AtomicString XMLHttpRequest::finalResponseMIMETypeWithFallback() const | |
1247 { | |
1248 AtomicString finalType = finalResponseMIMEType(); | |
1249 if (!finalType.isEmpty()) | |
1250 return finalType; | |
1251 | |
1252 // FIXME: This fallback is not specified in the final MIME type algorithm | |
1253 // of the XHR spec. Move this to more appropriate place. | |
1254 return AtomicString("text/xml", AtomicString::ConstructFromLiteral); | |
1255 } | |
1256 | |
1257 bool XMLHttpRequest::responseIsXML() const | |
1258 { | |
1259 return DOMImplementation::isXMLMIMEType(finalResponseMIMETypeWithFallback())
; | |
1260 } | |
1261 | |
1262 bool XMLHttpRequest::responseIsHTML() const | |
1263 { | |
1264 return equalIgnoringCase(finalResponseMIMEType(), "text/html"); | |
1265 } | |
1266 | |
1267 int XMLHttpRequest::status() const | |
1268 { | |
1269 if (m_state == UNSENT || m_state == OPENED || m_error) | |
1270 return 0; | |
1271 | |
1272 if (m_response.httpStatusCode()) | |
1273 return m_response.httpStatusCode(); | |
1274 | |
1275 return 0; | |
1276 } | |
1277 | |
1278 String XMLHttpRequest::statusText() const | |
1279 { | |
1280 if (m_state == UNSENT || m_state == OPENED || m_error) | |
1281 return String(); | |
1282 | |
1283 if (!m_response.httpStatusText().isNull()) | |
1284 return m_response.httpStatusText(); | |
1285 | |
1286 return String(); | |
1287 } | |
1288 | |
1289 void XMLHttpRequest::didFail(const ResourceError& error) | |
1290 { | |
1291 WTF_LOG(Network, "XMLHttpRequest %p didFail()", this); | |
1292 | |
1293 // If we are already in an error state, for instance we called abort(), bail
out early. | |
1294 if (m_error) | |
1295 return; | |
1296 | |
1297 if (error.isCancellation()) { | |
1298 handleDidCancel(); | |
1299 // Now the XMLHttpRequest instance may be dead. | |
1300 return; | |
1301 } | |
1302 | |
1303 if (error.isTimeout()) { | |
1304 handleDidTimeout(); | |
1305 // Now the XMLHttpRequest instance may be dead. | |
1306 return; | |
1307 } | |
1308 | |
1309 // Network failures are already reported to Web Inspector by ResourceLoader. | |
1310 if (error.domain() == errorDomainBlinkInternal) | |
1311 logConsoleError(executionContext(), "XMLHttpRequest cannot load " + erro
r.failingURL() + ". " + error.localizedDescription()); | |
1312 | |
1313 handleNetworkError(); | |
1314 // Now the XMLHttpRequest instance may be dead. | |
1315 } | |
1316 | |
1317 void XMLHttpRequest::didFailRedirectCheck() | |
1318 { | |
1319 WTF_LOG(Network, "XMLHttpRequest %p didFailRedirectCheck()", this); | |
1320 | |
1321 handleNetworkError(); | |
1322 // Now the XMLHttpRequest instance may be dead. | |
1323 } | |
1324 | |
1325 void XMLHttpRequest::didFinishLoading(unsigned long identifier, double) | |
1326 { | |
1327 WTF_LOG(Network, "XMLHttpRequest %p didFinishLoading(%lu)", this, identifier
); | |
1328 | |
1329 if (m_error) | |
1330 return; | |
1331 | |
1332 if (m_state < HEADERS_RECEIVED) | |
1333 changeState(HEADERS_RECEIVED); | |
1334 | |
1335 m_loaderIdentifier = identifier; | |
1336 | |
1337 if (m_downloadingToFile && m_responseTypeCode != ResponseTypeBlob && m_lengt
hDownloadedToFile) { | |
1338 ASSERT(m_state == LOADING); | |
1339 // In this case, we have sent the request with DownloadToFile true, | |
1340 // but the user changed the response type after that. Hence we need to | |
1341 // read the response data and provide it to this object. | |
1342 m_blobLoader = BlobLoader::create(this, createBlobDataHandleFromResponse
()); | |
1343 } else { | |
1344 didFinishLoadingInternal(); | |
1345 } | |
1346 } | |
1347 | |
1348 void XMLHttpRequest::didFinishLoadingInternal() | |
1349 { | |
1350 if (m_responseDocumentParser) { | |
1351 // |DocumentParser::finish()| tells the parser that we have reached end
of the data. | |
1352 // When using |HTMLDocumentParser|, which works asynchronously, we do no
t have the | |
1353 // complete document just after the |DocumentParser::finish()| call. | |
1354 // Wait for the parser to call us back in |notifyParserStopped| to progr
ess state. | |
1355 m_responseDocumentParser->finish(); | |
1356 ASSERT(m_responseDocument); | |
1357 return; | |
1358 } | |
1359 | |
1360 if (m_decoder) | |
1361 m_responseText = m_responseText.concatenateWith(m_decoder->flush()); | |
1362 | |
1363 if (m_responseLegacyStream) | |
1364 m_responseLegacyStream->finalize(); | |
1365 | |
1366 if (m_responseStream) | |
1367 m_responseStream->close(); | |
1368 | |
1369 clearVariablesForLoading(); | |
1370 endLoading(); | |
1371 } | |
1372 | |
1373 void XMLHttpRequest::didFinishLoadingFromBlob() | |
1374 { | |
1375 WTF_LOG(Network, "XMLHttpRequest %p didFinishLoadingFromBlob", this); | |
1376 | |
1377 didFinishLoadingInternal(); | |
1378 } | |
1379 | |
1380 void XMLHttpRequest::didFailLoadingFromBlob() | |
1381 { | |
1382 WTF_LOG(Network, "XMLHttpRequest %p didFailLoadingFromBlob()", this); | |
1383 | |
1384 if (m_error) | |
1385 return; | |
1386 handleNetworkError(); | |
1387 } | |
1388 | |
1389 PassRefPtr<BlobDataHandle> XMLHttpRequest::createBlobDataHandleFromResponse() | |
1390 { | |
1391 ASSERT(m_downloadingToFile); | |
1392 OwnPtr<BlobData> blobData = BlobData::create(); | |
1393 String filePath = m_response.downloadedFilePath(); | |
1394 // If we errored out or got no data, we return an empty handle. | |
1395 if (!filePath.isEmpty() && m_lengthDownloadedToFile) { | |
1396 blobData->appendFile(filePath); | |
1397 // FIXME: finalResponseMIMETypeWithFallback() defaults to | |
1398 // text/xml which may be incorrect. Replace it with | |
1399 // finalResponseMIMEType() after compatibility investigation. | |
1400 blobData->setContentType(finalResponseMIMETypeWithFallback()); | |
1401 } | |
1402 return BlobDataHandle::create(blobData.release(), m_lengthDownloadedToFile); | |
1403 } | |
1404 | |
1405 void XMLHttpRequest::notifyParserStopped() | |
1406 { | |
1407 // This should only be called when response document is parsed asynchronousl
y. | |
1408 ASSERT(m_responseDocumentParser); | |
1409 ASSERT(!m_responseDocumentParser->isParsing()); | |
1410 ASSERT(!m_responseLegacyStream); | |
1411 ASSERT(!m_responseStream); | |
1412 | |
1413 // Do nothing if we are called from |internalAbort()|. | |
1414 if (m_error) | |
1415 return; | |
1416 | |
1417 clearVariablesForLoading(); | |
1418 | |
1419 m_responseDocument->implicitClose(); | |
1420 | |
1421 if (!m_responseDocument->wellFormed()) | |
1422 m_responseDocument = nullptr; | |
1423 | |
1424 m_parsedResponse = true; | |
1425 | |
1426 endLoading(); | |
1427 } | |
1428 | |
1429 void XMLHttpRequest::endLoading() | |
1430 { | |
1431 InspectorInstrumentation::didFinishXHRLoading(executionContext(), this, this
, m_loaderIdentifier, m_responseText, m_method, m_url); | |
1432 | |
1433 if (m_loader) | |
1434 m_loader = nullptr; | |
1435 m_loaderIdentifier = 0; | |
1436 | |
1437 changeState(DONE); | |
1438 } | |
1439 | |
1440 void XMLHttpRequest::didSendData(unsigned long long bytesSent, unsigned long lon
g totalBytesToBeSent) | |
1441 { | |
1442 WTF_LOG(Network, "XMLHttpRequest %p didSendData(%llu, %llu)", this, bytesSen
t, totalBytesToBeSent); | |
1443 | |
1444 if (!m_upload) | |
1445 return; | |
1446 | |
1447 if (m_uploadEventsAllowed) | |
1448 m_upload->dispatchProgressEvent(bytesSent, totalBytesToBeSent); | |
1449 | |
1450 if (bytesSent == totalBytesToBeSent && !m_uploadComplete) { | |
1451 m_uploadComplete = true; | |
1452 if (m_uploadEventsAllowed) | |
1453 m_upload->dispatchEventAndLoadEnd(EventTypeNames::load, true, bytesS
ent, totalBytesToBeSent); | |
1454 } | |
1455 } | |
1456 | |
1457 void XMLHttpRequest::didReceiveResponse(unsigned long identifier, const Resource
Response& response) | |
1458 { | |
1459 WTF_LOG(Network, "XMLHttpRequest %p didReceiveResponse(%lu)", this, identifi
er); | |
1460 | |
1461 m_response = response; | |
1462 if (!m_mimeTypeOverride.isEmpty()) { | |
1463 m_response.setHTTPHeaderField("Content-Type", m_mimeTypeOverride); | |
1464 m_finalResponseCharset = extractCharsetFromMediaType(m_mimeTypeOverride)
; | |
1465 } | |
1466 | |
1467 if (m_finalResponseCharset.isEmpty()) | |
1468 m_finalResponseCharset = response.textEncodingName(); | |
1469 } | |
1470 | |
1471 void XMLHttpRequest::parseDocumentChunk(const char* data, unsigned len) | |
1472 { | |
1473 if (!m_responseDocumentParser) { | |
1474 ASSERT(!m_responseDocument); | |
1475 initResponseDocument(); | |
1476 if (!m_responseDocument) | |
1477 return; | |
1478 | |
1479 m_responseDocumentParser = m_responseDocument->implicitOpen(); | |
1480 m_responseDocumentParser->addClient(this); | |
1481 } | |
1482 ASSERT(m_responseDocumentParser); | |
1483 | |
1484 if (m_responseDocumentParser->needsDecoder()) | |
1485 m_responseDocumentParser->setDecoder(createDecoder()); | |
1486 | |
1487 m_responseDocumentParser->appendBytes(data, len); | |
1488 } | |
1489 | |
1490 PassOwnPtr<TextResourceDecoder> XMLHttpRequest::createDecoder() const | |
1491 { | |
1492 if (m_responseTypeCode == ResponseTypeJSON) | |
1493 return TextResourceDecoder::create("application/json", "UTF-8"); | |
1494 | |
1495 if (!m_finalResponseCharset.isEmpty()) | |
1496 return TextResourceDecoder::create("text/plain", m_finalResponseCharset)
; | |
1497 | |
1498 // allow TextResourceDecoder to look inside the m_response if it's XML or HT
ML | |
1499 if (responseIsXML()) { | |
1500 OwnPtr<TextResourceDecoder> decoder = TextResourceDecoder::create("appli
cation/xml"); | |
1501 // Don't stop on encoding errors, unlike it is done for other kinds | |
1502 // of XML resources. This matches the behavior of previous WebKit | |
1503 // versions, Firefox and Opera. | |
1504 decoder->useLenientXMLDecoding(); | |
1505 | |
1506 return decoder.release(); | |
1507 } | |
1508 | |
1509 if (responseIsHTML()) | |
1510 return TextResourceDecoder::create("text/html", "UTF-8"); | |
1511 | |
1512 return TextResourceDecoder::create("text/plain", "UTF-8"); | |
1513 } | |
1514 | |
1515 void XMLHttpRequest::didReceiveData(const char* data, unsigned len) | |
1516 { | |
1517 if (m_error) | |
1518 return; | |
1519 | |
1520 if (m_state < HEADERS_RECEIVED) | |
1521 changeState(HEADERS_RECEIVED); | |
1522 | |
1523 // We need to check for |m_error| again, because |changeState| may trigger | |
1524 // readystatechange, and user javascript can cause |abort()|. | |
1525 if (m_error) | |
1526 return; | |
1527 | |
1528 if (!len) | |
1529 return; | |
1530 | |
1531 if (m_responseTypeCode == ResponseTypeDocument && responseIsHTML()) { | |
1532 parseDocumentChunk(data, len); | |
1533 } else if (m_responseTypeCode == ResponseTypeDefault || m_responseTypeCode =
= ResponseTypeText || m_responseTypeCode == ResponseTypeJSON || m_responseTypeCo
de == ResponseTypeDocument) { | |
1534 if (!m_decoder) | |
1535 m_decoder = createDecoder(); | |
1536 | |
1537 m_responseText = m_responseText.concatenateWith(m_decoder->decode(data,
len)); | |
1538 } else if (m_responseTypeCode == ResponseTypeArrayBuffer || m_responseTypeCo
de == ResponseTypeBlob) { | |
1539 // Buffer binary data. | |
1540 if (!m_binaryResponseBuilder) | |
1541 m_binaryResponseBuilder = SharedBuffer::create(); | |
1542 m_binaryResponseBuilder->append(data, len); | |
1543 } else if (m_responseTypeCode == ResponseTypeLegacyStream) { | |
1544 if (!m_responseLegacyStream) | |
1545 m_responseLegacyStream = Stream::create(executionContext(), response
Type()); | |
1546 m_responseLegacyStream->addData(data, len); | |
1547 } else if (m_responseTypeCode == ResponseTypeStream) { | |
1548 if (!m_responseStream) { | |
1549 m_responseStream = new ReadableStreamImpl<ReadableStreamChunkTypeTra
its<DOMArrayBuffer> >(executionContext(), new ReadableStreamSource(this)); | |
1550 m_responseStream->didSourceStart(); | |
1551 } | |
1552 m_responseStream->enqueue(DOMArrayBuffer::create(data, len)); | |
1553 } | |
1554 | |
1555 if (m_blobLoader) { | |
1556 // In this case, the data is provided by m_blobLoader. As progress | |
1557 // events are already fired, we should return here. | |
1558 return; | |
1559 } | |
1560 trackProgress(len); | |
1561 } | |
1562 | |
1563 void XMLHttpRequest::didDownloadData(int dataLength) | |
1564 { | |
1565 if (m_error) | |
1566 return; | |
1567 | |
1568 ASSERT(m_downloadingToFile); | |
1569 | |
1570 if (m_state < HEADERS_RECEIVED) | |
1571 changeState(HEADERS_RECEIVED); | |
1572 | |
1573 if (!dataLength) | |
1574 return; | |
1575 | |
1576 // readystatechange event handler may do something to put this XHR in error | |
1577 // state. We need to check m_error again here. | |
1578 if (m_error) | |
1579 return; | |
1580 | |
1581 m_lengthDownloadedToFile += dataLength; | |
1582 | |
1583 trackProgress(dataLength); | |
1584 } | |
1585 | |
1586 void XMLHttpRequest::handleDidTimeout() | |
1587 { | |
1588 WTF_LOG(Network, "XMLHttpRequest %p handleDidTimeout()", this); | |
1589 | |
1590 // Response is cleared next, save needed progress event data. | |
1591 long long expectedLength = m_response.expectedContentLength(); | |
1592 long long receivedLength = m_receivedLength; | |
1593 | |
1594 // Prevent the XMLHttpRequest instance from being destoryed during | |
1595 // |internalAbort()|. | |
1596 RefPtrWillBeRawPtr<XMLHttpRequest> protect(this); | |
1597 | |
1598 if (!internalAbort()) | |
1599 return; | |
1600 | |
1601 handleRequestError(TimeoutError, EventTypeNames::timeout, receivedLength, ex
pectedLength); | |
1602 } | |
1603 | |
1604 void XMLHttpRequest::suspend() | |
1605 { | |
1606 m_progressEventThrottle.suspend(); | |
1607 } | |
1608 | |
1609 void XMLHttpRequest::resume() | |
1610 { | |
1611 m_progressEventThrottle.resume(); | |
1612 } | |
1613 | |
1614 void XMLHttpRequest::stop() | |
1615 { | |
1616 internalAbort(); | |
1617 } | |
1618 | |
1619 bool XMLHttpRequest::hasPendingActivity() const | |
1620 { | |
1621 // Neither this object nor the JavaScript wrapper should be deleted while | |
1622 // a request is in progress because we need to keep the listeners alive, | |
1623 // and they are referenced by the JavaScript wrapper. | |
1624 // |m_loader| is non-null while request is active and ThreadableLoaderClient | |
1625 // callbacks may be called, and |m_responseDocumentParser| is non-null while | |
1626 // DocumentParserClient callbacks may be called. | |
1627 return m_loader || m_responseDocumentParser; | |
1628 } | |
1629 | |
1630 void XMLHttpRequest::contextDestroyed() | |
1631 { | |
1632 ASSERT(!m_loader); | |
1633 ActiveDOMObject::contextDestroyed(); | |
1634 } | |
1635 | |
1636 const AtomicString& XMLHttpRequest::interfaceName() const | |
1637 { | |
1638 return EventTargetNames::XMLHttpRequest; | |
1639 } | |
1640 | |
1641 ExecutionContext* XMLHttpRequest::executionContext() const | |
1642 { | |
1643 return ActiveDOMObject::executionContext(); | |
1644 } | |
1645 | |
1646 void XMLHttpRequest::trace(Visitor* visitor) | |
1647 { | |
1648 visitor->trace(m_responseBlob); | |
1649 visitor->trace(m_responseLegacyStream); | |
1650 visitor->trace(m_responseStream); | |
1651 visitor->trace(m_streamSource); | |
1652 visitor->trace(m_responseDocument); | |
1653 visitor->trace(m_responseDocumentParser); | |
1654 visitor->trace(m_progressEventThrottle); | |
1655 visitor->trace(m_upload); | |
1656 visitor->trace(m_blobLoader); | |
1657 XMLHttpRequestEventTarget::trace(visitor); | |
1658 DocumentParserClient::trace(visitor); | |
1659 } | |
1660 | |
1661 } // namespace blink | |
OLD | NEW |