Fix problems with cross-origin redirects
[WebKit-https.git] / Source / WebCore / xml / XMLHttpRequest.cpp
1 /*
2  *  Copyright (C) 2004, 2006, 2008 Apple Inc. All rights reserved.
3  *  Copyright (C) 2005-2007 Alexey Proskuryakov <ap@webkit.org>
4  *  Copyright (C) 2007, 2008 Julien Chaffraix <jchaffraix@webkit.org>
5  *  Copyright (C) 2008, 2011 Google Inc. All rights reserved.
6  *  Copyright (C) 2012 Intel Corporation
7  *
8  *  This library is free software; you can redistribute it and/or
9  *  modify it under the terms of the GNU Lesser General Public
10  *  License as published by the Free Software Foundation; either
11  *  version 2 of the License, or (at your option) any later version.
12  *
13  *  This library is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  *  Lesser General Public License for more details.
17  *
18  *  You should have received a copy of the GNU Lesser General Public
19  *  License along with this library; if not, write to the Free Software
20  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21  */
22
23 #include "config.h"
24 #include "XMLHttpRequest.h"
25
26 #include "Blob.h"
27 #include "CachedResourceRequestInitiators.h"
28 #include "ContentSecurityPolicy.h"
29 #include "CrossOriginAccessControl.h"
30 #include "DOMFormData.h"
31 #include "DOMImplementation.h"
32 #include "Event.h"
33 #include "ExceptionCode.h"
34 #include "File.h"
35 #include "HTMLDocument.h"
36 #include "HTTPHeaderNames.h"
37 #include "HTTPParsers.h"
38 #include "InspectorInstrumentation.h"
39 #include "JSDOMBinding.h"
40 #include "JSDOMWindow.h"
41 #include "MemoryCache.h"
42 #include "ParsedContentType.h"
43 #include "ResourceError.h"
44 #include "ResourceRequest.h"
45 #include "ScriptController.h"
46 #include "SecurityOriginPolicy.h"
47 #include "Settings.h"
48 #include "SharedBuffer.h"
49 #include "TextResourceDecoder.h"
50 #include "ThreadableLoader.h"
51 #include "XMLHttpRequestException.h"
52 #include "XMLHttpRequestProgressEvent.h"
53 #include "XMLHttpRequestUpload.h"
54 #include "markup.h"
55 #include <heap/Strong.h>
56 #include <mutex>
57 #include <runtime/ArrayBuffer.h>
58 #include <runtime/ArrayBufferView.h>
59 #include <runtime/JSCInlines.h>
60 #include <runtime/JSLock.h>
61 #include <wtf/NeverDestroyed.h>
62 #include <wtf/Ref.h>
63 #include <wtf/RefCountedLeakCounter.h>
64 #include <wtf/StdLibExtras.h>
65 #include <wtf/text/CString.h>
66
67 namespace WebCore {
68
69 DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, xmlHttpRequestCounter, ("XMLHttpRequest"));
70
71 // Histogram enum to see when we can deprecate xhr.send(ArrayBuffer).
72 enum XMLHttpRequestSendArrayBufferOrView {
73     XMLHttpRequestSendArrayBuffer,
74     XMLHttpRequestSendArrayBufferView,
75     XMLHttpRequestSendArrayBufferOrViewMax,
76 };
77
78 static bool isSetCookieHeader(const String& name)
79 {
80     return equalIgnoringCase(name, "set-cookie") || equalIgnoringCase(name, "set-cookie2");
81 }
82
83 static void replaceCharsetInMediaType(String& mediaType, const String& charsetValue)
84 {
85     unsigned int 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 charset.
95     while (len) {
96         mediaType.replace(pos, len, charsetValue);
97         unsigned int start = pos + charsetValue.length();
98         findCharsetInMediaType(mediaType, pos, len, start);
99     }
100 }
101
102 static void logConsoleError(ScriptExecutionContext* context, const String& message)
103 {
104     if (!context)
105         return;
106     // FIXME: It's not good to report the bad usage without indicating what source line it came from.
107     // We should pass additional parameters so we can tell the console where the mistake occurred.
108     context->addConsoleMessage(MessageSource::JS, MessageLevel::Error, message);
109 }
110
111 Ref<XMLHttpRequest> XMLHttpRequest::create(ScriptExecutionContext& context)
112 {
113     Ref<XMLHttpRequest> xmlHttpRequest = adoptRef(*new XMLHttpRequest(context));
114     xmlHttpRequest->suspendIfNeeded();
115     return xmlHttpRequest;
116 }
117
118 XMLHttpRequest::XMLHttpRequest(ScriptExecutionContext& context)
119     : ActiveDOMObject(&context)
120     , m_async(true)
121     , m_includeCredentials(false)
122     , m_state(UNSENT)
123     , m_createdDocument(false)
124     , m_error(false)
125     , m_uploadEventsAllowed(true)
126     , m_uploadComplete(false)
127     , m_sameOriginRequest(true)
128     , m_receivedLength(0)
129     , m_lastSendLineNumber(0)
130     , m_lastSendColumnNumber(0)
131     , m_exceptionCode(0)
132     , m_progressEventThrottle(this)
133     , m_responseTypeCode(ResponseTypeDefault)
134     , m_responseCacheIsValid(false)
135     , m_resumeTimer(*this, &XMLHttpRequest::resumeTimerFired)
136     , m_dispatchErrorOnResuming(false)
137     , m_networkErrorTimer(*this, &XMLHttpRequest::networkErrorTimerFired)
138     , m_timeoutTimer(*this, &XMLHttpRequest::didReachTimeout)
139 {
140 #ifndef NDEBUG
141     xmlHttpRequestCounter.increment();
142 #endif
143 }
144
145 XMLHttpRequest::~XMLHttpRequest()
146 {
147 #ifndef NDEBUG
148     xmlHttpRequestCounter.decrement();
149 #endif
150 }
151
152 Document* XMLHttpRequest::document() const
153 {
154     ASSERT(scriptExecutionContext());
155     return downcast<Document>(scriptExecutionContext());
156 }
157
158 SecurityOrigin* XMLHttpRequest::securityOrigin() const
159 {
160     return scriptExecutionContext()->securityOrigin();
161 }
162
163 #if ENABLE(DASHBOARD_SUPPORT)
164 bool XMLHttpRequest::usesDashboardBackwardCompatibilityMode() const
165 {
166     if (scriptExecutionContext()->isWorkerGlobalScope())
167         return false;
168     Settings* settings = document()->settings();
169     return settings && settings->usesDashboardBackwardCompatibilityMode();
170 }
171 #endif
172
173 XMLHttpRequest::State XMLHttpRequest::readyState() const
174 {
175     return m_state;
176 }
177
178 String XMLHttpRequest::responseText(ExceptionCode& ec)
179 {
180     if (m_responseTypeCode != ResponseTypeDefault && m_responseTypeCode != ResponseTypeText) {
181         ec = INVALID_STATE_ERR;
182         return "";
183     }
184     return responseTextIgnoringResponseType();
185 }
186
187 void XMLHttpRequest::didCacheResponseJSON()
188 {
189     ASSERT(m_responseTypeCode == ResponseTypeJSON);
190     ASSERT(doneWithoutErrors());
191     m_responseCacheIsValid = true;
192     m_responseBuilder.clear();
193 }
194
195 Document* XMLHttpRequest::responseXML(ExceptionCode& ec)
196 {
197     if (m_responseTypeCode != ResponseTypeDefault && m_responseTypeCode != ResponseTypeDocument) {
198         ec = INVALID_STATE_ERR;
199         return nullptr;
200     }
201
202     if (!doneWithoutErrors())
203         return nullptr;
204
205     if (!m_createdDocument) {
206         String mimeType = responseMIMEType();
207         bool isHTML = equalIgnoringCase(mimeType, "text/html");
208
209         // The W3C spec requires the final MIME type to be some valid XML type, or text/html.
210         // If it is text/html, then the responseType of "document" must have been supplied explicitly.
211         if ((m_response.isHTTP() && !responseIsXML() && !isHTML)
212             || (isHTML && m_responseTypeCode == ResponseTypeDefault)
213             || scriptExecutionContext()->isWorkerGlobalScope()) {
214             m_responseDocument = nullptr;
215         } else {
216             if (isHTML)
217                 m_responseDocument = HTMLDocument::create(0, m_url);
218             else
219                 m_responseDocument = Document::create(0, m_url);
220             // FIXME: Set Last-Modified.
221             m_responseDocument->setContent(m_responseBuilder.toStringPreserveCapacity());
222             m_responseDocument->setSecurityOriginPolicy(scriptExecutionContext()->securityOriginPolicy());
223             m_responseDocument->overrideMIMEType(mimeType);
224
225             if (!m_responseDocument->wellFormed())
226                 m_responseDocument = nullptr;
227         }
228         m_createdDocument = true;
229     }
230
231     return m_responseDocument.get();
232 }
233
234 Blob* XMLHttpRequest::responseBlob()
235 {
236     ASSERT(m_responseTypeCode == ResponseTypeBlob);
237     ASSERT(doneWithoutErrors());
238
239     if (!m_responseBlob) {
240         if (m_binaryResponseBuilder) {
241             // FIXME: We just received the data from NetworkProcess, and are sending it back. This is inefficient.
242             Vector<char> data;
243             data.append(m_binaryResponseBuilder->data(), m_binaryResponseBuilder->size());
244             String normalizedContentType = Blob::normalizedContentType(responseMIMEType()); // responseMIMEType defaults to text/xml which may be incorrect.
245             m_responseBlob = Blob::create(WTFMove(data), normalizedContentType);
246             m_binaryResponseBuilder = nullptr;
247         } else {
248             // If we errored out or got no data, we still return a blob, just an empty one.
249             m_responseBlob = Blob::create();
250         }
251     }
252
253     return m_responseBlob.get();
254 }
255
256 ArrayBuffer* XMLHttpRequest::responseArrayBuffer()
257 {
258     ASSERT(m_responseTypeCode == ResponseTypeArrayBuffer);
259     ASSERT(doneWithoutErrors());
260
261     if (!m_responseArrayBuffer) {
262         if (m_binaryResponseBuilder)
263             m_responseArrayBuffer = m_binaryResponseBuilder->createArrayBuffer();
264         else
265             m_responseArrayBuffer = ArrayBuffer::create(nullptr, 0);
266         m_binaryResponseBuilder = nullptr;
267     }
268
269     return m_responseArrayBuffer.get();
270 }
271
272 void XMLHttpRequest::setTimeout(unsigned timeout, ExceptionCode& ec)
273 {
274     if (scriptExecutionContext()->isDocument() && !m_async) {
275         logConsoleError(scriptExecutionContext(), "XMLHttpRequest.timeout cannot be set for synchronous HTTP(S) requests made from the window context.");
276         ec = INVALID_ACCESS_ERR;
277         return;
278     }
279     m_timeoutMilliseconds = timeout;
280     if (!m_timeoutTimer.isActive())
281         return;
282     if (!m_timeoutMilliseconds) {
283         m_timeoutTimer.stop();
284         return;
285     }
286     std::chrono::duration<double> interval = std::chrono::milliseconds { m_timeoutMilliseconds } - (std::chrono::steady_clock::now() - m_sendingTime);
287     m_timeoutTimer.startOneShot(std::max(0.0, interval.count()));
288 }
289
290 void XMLHttpRequest::setResponseType(const String& responseType, ExceptionCode& ec)
291 {
292     if (m_state >= LOADING) {
293         ec = INVALID_STATE_ERR;
294         return;
295     }
296
297     // Newer functionality is not available to synchronous requests in window contexts, as a spec-mandated
298     // attempt to discourage synchronous XHR use. responseType is one such piece of functionality.
299     // We'll only disable this functionality for HTTP(S) requests since sync requests for local protocols
300     // such as file: and data: still make sense to allow.
301     if (!m_async && scriptExecutionContext()->isDocument() && m_url.protocolIsInHTTPFamily()) {
302         logConsoleError(scriptExecutionContext(), "XMLHttpRequest.responseType cannot be changed for synchronous HTTP(S) requests made from the window context.");
303         ec = INVALID_ACCESS_ERR;
304         return;
305     }
306
307     if (responseType == "")
308         m_responseTypeCode = ResponseTypeDefault;
309     else if (responseType == "text")
310         m_responseTypeCode = ResponseTypeText;
311     else if (responseType == "json")
312         m_responseTypeCode = ResponseTypeJSON;
313     else if (responseType == "document")
314         m_responseTypeCode = ResponseTypeDocument;
315     else if (responseType == "blob")
316         m_responseTypeCode = ResponseTypeBlob;
317     else if (responseType == "arraybuffer")
318         m_responseTypeCode = ResponseTypeArrayBuffer;
319     else
320         ASSERT_NOT_REACHED();
321 }
322
323 String XMLHttpRequest::responseType()
324 {
325     switch (m_responseTypeCode) {
326     case ResponseTypeDefault:
327         return "";
328     case ResponseTypeText:
329         return "text";
330     case ResponseTypeJSON:
331         return "json";
332     case ResponseTypeDocument:
333         return "document";
334     case ResponseTypeBlob:
335         return "blob";
336     case ResponseTypeArrayBuffer:
337         return "arraybuffer";
338     }
339     return "";
340 }
341
342 String XMLHttpRequest::responseURL() const
343 {
344     URL responseURL(m_response.url());
345     responseURL.removeFragmentIdentifier();
346
347     return responseURL.string();
348 }
349
350 void XMLHttpRequest::setLastSendLineAndColumnNumber(unsigned lineNumber, unsigned columnNumber)
351 {
352     m_lastSendLineNumber = lineNumber;
353     m_lastSendColumnNumber = columnNumber;
354 }
355
356 XMLHttpRequestUpload* XMLHttpRequest::upload()
357 {
358     if (!m_upload)
359         m_upload = std::make_unique<XMLHttpRequestUpload>(this);
360     return m_upload.get();
361 }
362
363 void XMLHttpRequest::changeState(State newState)
364 {
365     if (m_state != newState) {
366         m_state = newState;
367         callReadyStateChangeListener();
368     }
369 }
370
371 void XMLHttpRequest::callReadyStateChangeListener()
372 {
373     if (!scriptExecutionContext())
374         return;
375
376     // Check whether sending load and loadend events before sending readystatechange event, as it may change m_error/m_state values.
377     bool shouldSendLoadEvent = (m_state == DONE && !m_error);
378
379     if (m_async || (m_state <= OPENED || m_state == DONE))
380         m_progressEventThrottle.dispatchReadyStateChangeEvent(Event::create(eventNames().readystatechangeEvent, false, false), m_state == DONE ? FlushProgressEvent : DoNotFlushProgressEvent);
381
382     if (shouldSendLoadEvent) {
383         m_progressEventThrottle.dispatchProgressEvent(eventNames().loadEvent);
384         m_progressEventThrottle.dispatchProgressEvent(eventNames().loadendEvent);
385     }
386 }
387
388 void XMLHttpRequest::setWithCredentials(bool value, ExceptionCode& ec)
389 {
390     if (m_state > OPENED || m_sendFlag) {
391         ec = INVALID_STATE_ERR;
392         return;
393     }
394
395     m_includeCredentials = value;
396 }
397
398 bool XMLHttpRequest::isAllowedHTTPMethod(const String& method)
399 {
400     return !equalIgnoringCase(method, "TRACE")
401         && !equalIgnoringCase(method, "TRACK")
402         && !equalIgnoringCase(method, "CONNECT");
403 }
404
405 String XMLHttpRequest::uppercaseKnownHTTPMethod(const String& method)
406 {
407     const char* const methods[] = { "DELETE", "GET", "HEAD", "OPTIONS", "POST", "PUT" };
408     for (auto* value : methods) {
409         if (equalIgnoringCase(method, value)) {
410             // Don't bother allocating a new string if it's already all uppercase.
411             if (method == value)
412                 break;
413             return ASCIILiteral(value);
414         }
415     }
416     return method;
417 }
418
419 static bool isForbiddenRequestHeader(const String& name)
420 {
421     HTTPHeaderName headerName;
422     if (!findHTTPHeaderName(name, headerName))
423         return false;
424
425     switch (headerName) {
426     case HTTPHeaderName::AcceptCharset:
427     case HTTPHeaderName::AcceptEncoding:
428     case HTTPHeaderName::AccessControlRequestHeaders:
429     case HTTPHeaderName::AccessControlRequestMethod:
430     case HTTPHeaderName::Connection:
431     case HTTPHeaderName::ContentLength:
432     case HTTPHeaderName::ContentTransferEncoding:
433     case HTTPHeaderName::Cookie:
434     case HTTPHeaderName::Cookie2:
435     case HTTPHeaderName::Date:
436     case HTTPHeaderName::DNT:
437     case HTTPHeaderName::Expect:
438     case HTTPHeaderName::Host:
439     case HTTPHeaderName::KeepAlive:
440     case HTTPHeaderName::Origin:
441     case HTTPHeaderName::Referer:
442     case HTTPHeaderName::TE:
443     case HTTPHeaderName::Trailer:
444     case HTTPHeaderName::TransferEncoding:
445     case HTTPHeaderName::Upgrade:
446     case HTTPHeaderName::UserAgent:
447     case HTTPHeaderName::Via:
448         return true;
449
450     default:
451         return false;
452     }
453 }
454
455 bool XMLHttpRequest::isAllowedHTTPHeader(const String& name)
456 {
457     if (isForbiddenRequestHeader(name))
458         return false;
459
460     if (name.startsWith("proxy-", false))
461         return false;
462
463     if (name.startsWith("sec-", false))
464         return false;
465
466     return true;
467 }
468
469 void XMLHttpRequest::open(const String& method, const URL& url, ExceptionCode& ec)
470 {
471     open(method, url, true, ec);
472 }
473
474 void XMLHttpRequest::open(const String& method, const URL& url, bool async, ExceptionCode& ec)
475 {
476     if (!internalAbort())
477         return;
478
479     State previousState = m_state;
480     m_state = UNSENT;
481     m_error = false;
482     m_sendFlag = false;
483     m_uploadComplete = false;
484
485     // clear stuff from possible previous load
486     clearResponse();
487     clearRequest();
488
489     ASSERT(m_state == UNSENT);
490
491     if (!isValidHTTPToken(method)) {
492         ec = SYNTAX_ERR;
493         return;
494     }
495
496     if (!isAllowedHTTPMethod(method)) {
497         ec = SECURITY_ERR;
498         return;
499     }
500
501     // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved.
502     bool shouldBypassMainWorldContentSecurityPolicy = ContentSecurityPolicy::shouldBypassMainWorldContentSecurityPolicy(*scriptExecutionContext());
503     if (!scriptExecutionContext()->contentSecurityPolicy()->allowConnectToSource(url, shouldBypassMainWorldContentSecurityPolicy)) {
504         // FIXME: Should this be throwing an exception?
505         ec = SECURITY_ERR;
506         return;
507     }
508
509     if (!async && scriptExecutionContext()->isDocument()) {
510         if (document()->settings() && !document()->settings()->syncXHRInDocumentsEnabled()) {
511             logConsoleError(scriptExecutionContext(), "Synchronous XMLHttpRequests are disabled for this page.");
512             ec = INVALID_ACCESS_ERR;
513             return;
514         }
515
516         // Newer functionality is not available to synchronous requests in window contexts, as a spec-mandated
517         // attempt to discourage synchronous XHR use. responseType is one such piece of functionality.
518         // We'll only disable this functionality for HTTP(S) requests since sync requests for local protocols
519         // such as file: and data: still make sense to allow.
520         if (url.protocolIsInHTTPFamily() && m_responseTypeCode != ResponseTypeDefault) {
521             logConsoleError(scriptExecutionContext(), "Synchronous HTTP(S) requests made from the window context cannot have XMLHttpRequest.responseType set.");
522             ec = INVALID_ACCESS_ERR;
523             return;
524         }
525
526         // Similarly, timeouts are disabled for synchronous requests as well.
527         if (m_timeoutMilliseconds > 0) {
528             logConsoleError(scriptExecutionContext(), "Synchronous XMLHttpRequests must not have a timeout value set.");
529             ec = INVALID_ACCESS_ERR;
530             return;
531         }
532     }
533
534     m_method = uppercaseKnownHTTPMethod(method);
535
536     m_url = url;
537
538     m_async = async;
539
540     ASSERT(!m_loader);
541
542     // Check previous state to avoid dispatching readyState event
543     // when calling open several times in a row.
544     if (previousState != OPENED)
545         changeState(OPENED);
546     else
547         m_state = OPENED;
548 }
549
550 void XMLHttpRequest::open(const String& method, const URL& url, bool async, const String& user, ExceptionCode& ec)
551 {
552     URL urlWithCredentials(url);
553     urlWithCredentials.setUser(user);
554
555     open(method, urlWithCredentials, async, ec);
556 }
557
558 void XMLHttpRequest::open(const String& method, const URL& url, bool async, const String& user, const String& password, ExceptionCode& ec)
559 {
560     URL urlWithCredentials(url);
561     urlWithCredentials.setUser(user);
562     urlWithCredentials.setPass(password);
563
564     open(method, urlWithCredentials, async, ec);
565 }
566
567 bool XMLHttpRequest::initSend(ExceptionCode& ec)
568 {
569     if (!scriptExecutionContext())
570         return false;
571
572     if (m_state != OPENED || m_sendFlag) {
573         ec = INVALID_STATE_ERR;
574         return false;
575     }
576     ASSERT(!m_loader);
577
578     m_error = false;
579     return true;
580 }
581
582 void XMLHttpRequest::send(ExceptionCode& ec)
583 {
584     send(String(), ec);
585 }
586
587 void XMLHttpRequest::send(Document* document, ExceptionCode& ec)
588 {
589     ASSERT(document);
590
591     if (!initSend(ec))
592         return;
593
594     if (m_method != "GET" && m_method != "HEAD" && m_url.protocolIsInHTTPFamily()) {
595         if (!m_requestHeaders.contains(HTTPHeaderName::ContentType)) {
596 #if ENABLE(DASHBOARD_SUPPORT)
597             if (usesDashboardBackwardCompatibilityMode())
598                 m_requestHeaders.set(HTTPHeaderName::ContentType, ASCIILiteral("application/x-www-form-urlencoded"));
599             else
600 #endif
601                 // FIXME: this should include the charset used for encoding.
602                 m_requestHeaders.set(HTTPHeaderName::ContentType, document->isHTMLDocument() ? ASCIILiteral("text/html;charset=UTF-8") : ASCIILiteral("application/xml;charset=UTF-8"));
603         }
604
605         // FIXME: According to XMLHttpRequest Level 2, this should use the Document.innerHTML algorithm
606         // from the HTML5 specification to serialize the document.
607         String body = createMarkup(*document);
608
609         m_requestEntityBody = FormData::create(UTF8Encoding().encode(body, EntitiesForUnencodables));
610         if (m_upload)
611             m_requestEntityBody->setAlwaysStream(true);
612     }
613
614     createRequest(ec);
615 }
616
617 void XMLHttpRequest::send(const String& body, ExceptionCode& ec)
618 {
619     if (!initSend(ec))
620         return;
621
622     if (!body.isNull() && m_method != "GET" && m_method != "HEAD" && m_url.protocolIsInHTTPFamily()) {
623         String contentType = m_requestHeaders.get(HTTPHeaderName::ContentType);
624         if (contentType.isNull()) {
625 #if ENABLE(DASHBOARD_SUPPORT)
626             if (usesDashboardBackwardCompatibilityMode())
627                 m_requestHeaders.set(HTTPHeaderName::ContentType, ASCIILiteral("application/x-www-form-urlencoded"));
628             else
629 #endif
630                 m_requestHeaders.set(HTTPHeaderName::ContentType, ASCIILiteral("text/plain;charset=UTF-8"));
631         } else {
632             replaceCharsetInMediaType(contentType, "UTF-8");
633             m_requestHeaders.set(HTTPHeaderName::ContentType, contentType);
634         }
635
636         m_requestEntityBody = FormData::create(UTF8Encoding().encode(body, EntitiesForUnencodables));
637         if (m_upload)
638             m_requestEntityBody->setAlwaysStream(true);
639     }
640
641     createRequest(ec);
642 }
643
644 void XMLHttpRequest::send(Blob* body, ExceptionCode& ec)
645 {
646     if (!initSend(ec))
647         return;
648
649     if (m_method != "GET" && m_method != "HEAD" && m_url.protocolIsInHTTPFamily()) {
650         if (!m_requestHeaders.contains(HTTPHeaderName::ContentType)) {
651             const String& blobType = body->type();
652             if (!blobType.isEmpty() && isValidContentType(blobType))
653                 m_requestHeaders.set(HTTPHeaderName::ContentType, blobType);
654             else {
655                 // From FileAPI spec, whenever media type cannot be determined, empty string must be returned.
656                 m_requestHeaders.set(HTTPHeaderName::ContentType, emptyString());
657             }
658         }
659
660         m_requestEntityBody = FormData::create();
661         m_requestEntityBody->appendBlob(body->url());
662     }
663
664     createRequest(ec);
665 }
666
667 void XMLHttpRequest::send(DOMFormData* body, ExceptionCode& ec)
668 {
669     if (!initSend(ec))
670         return;
671
672     if (m_method != "GET" && m_method != "HEAD" && m_url.protocolIsInHTTPFamily()) {
673         m_requestEntityBody = FormData::createMultiPart(*(static_cast<FormDataList*>(body)), body->encoding(), document());
674
675         m_requestEntityBody->generateFiles(document());
676
677         if (!m_requestHeaders.contains(HTTPHeaderName::ContentType))
678             m_requestHeaders.set(HTTPHeaderName::ContentType, makeString("multipart/form-data; boundary=", m_requestEntityBody->boundary().data()));
679     }
680
681     createRequest(ec);
682 }
683
684 void XMLHttpRequest::send(ArrayBuffer* body, ExceptionCode& ec)
685 {
686     String consoleMessage("ArrayBuffer is deprecated in XMLHttpRequest.send(). Use ArrayBufferView instead.");
687     scriptExecutionContext()->addConsoleMessage(MessageSource::JS, MessageLevel::Warning, consoleMessage);
688
689     sendBytesData(body->data(), body->byteLength(), ec);
690 }
691
692 void XMLHttpRequest::send(ArrayBufferView* body, ExceptionCode& ec)
693 {
694     sendBytesData(body->baseAddress(), body->byteLength(), ec);
695 }
696
697 void XMLHttpRequest::sendBytesData(const void* data, size_t length, ExceptionCode& ec)
698 {
699     if (!initSend(ec))
700         return;
701
702     if (m_method != "GET" && m_method != "HEAD" && m_url.protocolIsInHTTPFamily()) {
703         m_requestEntityBody = FormData::create(data, length);
704         if (m_upload)
705             m_requestEntityBody->setAlwaysStream(true);
706     }
707
708     createRequest(ec);
709 }
710
711 void XMLHttpRequest::createRequest(ExceptionCode& ec)
712 {
713     // Only GET request is supported for blob URL.
714     if (m_url.protocolIs("blob") && m_method != "GET") {
715         ec = XMLHttpRequestException::NETWORK_ERR;
716         return;
717     }
718
719     m_sendFlag = true;
720
721     // The presence of upload event listeners forces us to use preflighting because POSTing to an URL that does not
722     // permit cross origin requests should look exactly like POSTing to an URL that does not respond at all.
723     // Also, only async requests support upload progress events.
724     bool uploadEvents = false;
725     if (m_async) {
726         m_progressEventThrottle.dispatchProgressEvent(eventNames().loadstartEvent);
727         if (m_requestEntityBody && m_upload) {
728             uploadEvents = m_upload->hasEventListeners();
729             m_upload->dispatchProgressEvent(eventNames().loadstartEvent);
730         }
731     }
732
733     m_sameOriginRequest = securityOrigin()->canRequest(m_url);
734
735     // We also remember whether upload events should be allowed for this request in case the upload listeners are
736     // added after the request is started.
737     m_uploadEventsAllowed = m_sameOriginRequest || uploadEvents || !isSimpleCrossOriginAccessRequest(m_method, m_requestHeaders);
738
739     ResourceRequest request(m_url);
740     request.setRequester(ResourceRequest::Requester::XHR);
741     request.setHTTPMethod(m_method);
742
743     if (m_requestEntityBody) {
744         ASSERT(m_method != "GET");
745         ASSERT(m_method != "HEAD");
746         request.setHTTPBody(m_requestEntityBody.release());
747     }
748
749     if (!m_requestHeaders.isEmpty())
750         request.setHTTPHeaderFields(m_requestHeaders);
751
752     ThreadableLoaderOptions options;
753     options.setSendLoadCallbacks(SendCallbacks);
754     options.setSniffContent(DoNotSniffContent);
755     options.preflightPolicy = uploadEvents ? ForcePreflight : ConsiderPreflight;
756     options.setAllowCredentials((m_sameOriginRequest || m_includeCredentials) ? AllowStoredCredentials : DoNotAllowStoredCredentials);
757     options.setCredentialRequest(m_includeCredentials ? ClientRequestedCredentials : ClientDidNotRequestCredentials);
758     options.crossOriginRequestPolicy = UseAccessControl;
759     options.securityOrigin = securityOrigin();
760     options.initiator = cachedResourceRequestInitiators().xmlhttprequest;
761
762     if (m_timeoutMilliseconds) {
763         if (!m_async)
764             request.setTimeoutInterval(m_timeoutMilliseconds / 1000.0);
765         else {
766             m_sendingTime = std::chrono::steady_clock::now();
767             m_timeoutTimer.startOneShot(std::chrono::milliseconds { m_timeoutMilliseconds });
768         }
769     }
770
771     m_exceptionCode = 0;
772     m_error = false;
773
774     if (m_async) {
775         if (m_upload)
776             request.setReportUploadProgress(true);
777
778         // ThreadableLoader::create can return null here, for example if we're no longer attached to a page or if a content blocker blocks the load.
779         // This is true while running onunload handlers.
780         // FIXME: Maybe we need to be able to send XMLHttpRequests from onunload, <http://bugs.webkit.org/show_bug.cgi?id=10904>.
781         m_loader = ThreadableLoader::create(scriptExecutionContext(), this, request, options);
782
783         // Neither this object nor the JavaScript wrapper should be deleted while
784         // a request is in progress because we need to keep the listeners alive,
785         // and they are referenced by the JavaScript wrapper.
786         setPendingActivity(this);
787         if (!m_loader) {
788             m_sendFlag = false;
789             m_timeoutTimer.stop();
790             m_networkErrorTimer.startOneShot(0);
791         }
792     } else {
793         InspectorInstrumentation::willLoadXHRSynchronously(scriptExecutionContext());
794         ThreadableLoader::loadResourceSynchronously(scriptExecutionContext(), request, *this, options);
795         InspectorInstrumentation::didLoadXHRSynchronously(scriptExecutionContext());
796     }
797
798     if (!m_exceptionCode && m_error)
799         m_exceptionCode = XMLHttpRequestException::NETWORK_ERR;
800     ec = m_exceptionCode;
801 }
802
803 void XMLHttpRequest::abort()
804 {
805     // internalAbort() calls dropProtection(), which may release the last reference.
806     Ref<XMLHttpRequest> protect(*this);
807
808     if (!internalAbort())
809         return;
810
811     clearResponseBuffers();
812
813     // Clear headers as required by the spec
814     m_requestHeaders.clear();
815     if ((m_state == OPENED && m_sendFlag) || m_state == HEADERS_RECEIVED || m_state == LOADING) {
816         ASSERT(!m_loader);
817         m_sendFlag = false;
818         changeState(DONE);
819         dispatchErrorEvents(eventNames().abortEvent);
820     }
821     m_state = UNSENT;
822 }
823
824 bool XMLHttpRequest::internalAbort()
825 {
826     m_error = true;
827
828     // FIXME: when we add the support for multi-part XHR, we will have to think be careful with this initialization.
829     m_receivedLength = 0;
830
831     m_decoder = nullptr;
832
833     m_timeoutTimer.stop();
834
835     if (!m_loader)
836         return true;
837
838     // Cancelling m_loader may trigger a window.onload callback which can call open() on the same xhr.
839     // This would create internalAbort reentrant call.
840     // m_loader is set to null before being cancelled to exit early in any reentrant internalAbort() call.
841     RefPtr<ThreadableLoader> loader = m_loader.release();
842     loader->cancel();
843
844     // If window.onload callback calls open() and send() on the same xhr, m_loader is now set to a new value.
845     // The function calling internalAbort() should abort to let the open() and send() calls continue properly.
846     // We ask the function calling internalAbort() to exit by returning false.
847     // Save this information to a local variable since we are going to drop protection.
848     bool newLoadStarted = m_loader;
849
850     dropProtection();
851
852     return !newLoadStarted;
853 }
854
855 void XMLHttpRequest::clearResponse()
856 {
857     m_response = ResourceResponse();
858     clearResponseBuffers();
859 }
860
861 void XMLHttpRequest::clearResponseBuffers()
862 {
863     m_responseBuilder.clear();
864     m_responseEncoding = String();
865     m_createdDocument = false;
866     m_responseDocument = nullptr;
867     m_responseBlob = nullptr;
868     m_binaryResponseBuilder = nullptr;
869     m_responseArrayBuffer = nullptr;
870     m_responseCacheIsValid = false;
871 }
872
873 void XMLHttpRequest::clearRequest()
874 {
875     m_requestHeaders.clear();
876     m_requestEntityBody = nullptr;
877 }
878
879 void XMLHttpRequest::genericError()
880 {
881     clearResponse();
882     clearRequest();
883     m_sendFlag = false;
884     m_error = true;
885
886     changeState(DONE);
887 }
888
889 void XMLHttpRequest::networkError()
890 {
891     genericError();
892     dispatchErrorEvents(eventNames().errorEvent);
893     internalAbort();
894 }
895
896 void XMLHttpRequest::networkErrorTimerFired()
897 {
898     networkError();
899     dropProtection();
900 }
901     
902 void XMLHttpRequest::abortError()
903 {
904     genericError();
905     dispatchErrorEvents(eventNames().abortEvent);
906 }
907
908 void XMLHttpRequest::dropProtection()
909 {
910     // The XHR object itself holds on to the responseText, and
911     // thus has extra cost even independent of any
912     // responseText or responseXML objects it has handed
913     // out. But it is protected from GC while loading, so this
914     // can't be recouped until the load is done, so only
915     // report the extra cost at that point.
916     JSC::VM& vm = scriptExecutionContext()->vm();
917     JSC::JSLockHolder lock(vm);
918     // FIXME: Adopt reportExtraMemoryVisited, and switch to reportExtraMemoryAllocated.
919     // https://bugs.webkit.org/show_bug.cgi?id=142595
920     vm.heap.deprecatedReportExtraMemory(m_responseBuilder.length() * 2);
921
922     unsetPendingActivity(this);
923 }
924
925 void XMLHttpRequest::overrideMimeType(const String& override, ExceptionCode& ec)
926 {
927     if (m_state == LOADING || m_state == DONE) {
928         ec = INVALID_STATE_ERR;
929         return;
930     }
931
932     m_mimeTypeOverride = override;
933 }
934
935 void XMLHttpRequest::setRequestHeader(const String& name, const String& value, ExceptionCode& ec)
936 {
937     if (m_state != OPENED || m_sendFlag) {
938 #if ENABLE(DASHBOARD_SUPPORT)
939         if (usesDashboardBackwardCompatibilityMode())
940             return;
941 #endif
942
943         ec = INVALID_STATE_ERR;
944         return;
945     }
946
947     String normalizedValue = stripLeadingAndTrailingHTTPSpaces(value);
948     if (!isValidHTTPToken(name) || !isValidHTTPHeaderValue(normalizedValue)) {
949         ec = SYNTAX_ERR;
950         return;
951     }
952
953     // A privileged script (e.g. a Dashboard widget) can set any headers.
954     if (!securityOrigin()->canLoadLocalResources() && !isAllowedHTTPHeader(name)) {
955         logConsoleError(scriptExecutionContext(), "Refused to set unsafe header \"" + name + "\"");
956         return;
957     }
958
959     m_requestHeaders.add(name, normalizedValue);
960 }
961
962 String XMLHttpRequest::getAllResponseHeaders() const
963 {
964     if (m_state < HEADERS_RECEIVED || m_error)
965         return "";
966
967     StringBuilder stringBuilder;
968
969     HTTPHeaderSet accessControlExposeHeaderSet;
970     parseAccessControlExposeHeadersAllowList(m_response.httpHeaderField(HTTPHeaderName::AccessControlExposeHeaders), accessControlExposeHeaderSet);
971     
972     for (const auto& header : m_response.httpHeaderFields()) {
973         // Hide Set-Cookie header fields from the XMLHttpRequest client for these reasons:
974         //     1) If the client did have access to the fields, then it could read HTTP-only
975         //        cookies; those cookies are supposed to be hidden from scripts.
976         //     2) There's no known harm in hiding Set-Cookie header fields entirely; we don't
977         //        know any widely used technique that requires access to them.
978         //     3) Firefox has implemented this policy.
979         if (isSetCookieHeader(header.key) && !securityOrigin()->canLoadLocalResources())
980             continue;
981
982         if (!m_sameOriginRequest && !isOnAccessControlResponseHeaderWhitelist(header.key) && !accessControlExposeHeaderSet.contains(header.key))
983             continue;
984
985         stringBuilder.append(header.key);
986         stringBuilder.append(':');
987         stringBuilder.append(' ');
988         stringBuilder.append(header.value);
989         stringBuilder.append('\r');
990         stringBuilder.append('\n');
991     }
992
993     return stringBuilder.toString();
994 }
995
996 String XMLHttpRequest::getResponseHeader(const String& name) const
997 {
998     if (m_state < HEADERS_RECEIVED || m_error)
999         return String();
1000
1001     // See comment in getAllResponseHeaders above.
1002     if (isSetCookieHeader(name) && !securityOrigin()->canLoadLocalResources()) {
1003         logConsoleError(scriptExecutionContext(), "Refused to get unsafe header \"" + name + "\"");
1004         return String();
1005     }
1006
1007     HTTPHeaderSet accessControlExposeHeaderSet;
1008     parseAccessControlExposeHeadersAllowList(m_response.httpHeaderField(HTTPHeaderName::AccessControlExposeHeaders), accessControlExposeHeaderSet);
1009
1010     if (!m_sameOriginRequest && !isOnAccessControlResponseHeaderWhitelist(name) && !accessControlExposeHeaderSet.contains(name)) {
1011         logConsoleError(scriptExecutionContext(), "Refused to get unsafe header \"" + name + "\"");
1012         return String();
1013     }
1014     return m_response.httpHeaderField(name);
1015 }
1016
1017 String XMLHttpRequest::responseMIMEType() const
1018 {
1019     String mimeType = extractMIMETypeFromMediaType(m_mimeTypeOverride);
1020     if (mimeType.isEmpty()) {
1021         if (m_response.isHTTP())
1022             mimeType = extractMIMETypeFromMediaType(m_response.httpHeaderField(HTTPHeaderName::ContentType));
1023         else
1024             mimeType = m_response.mimeType();
1025     }
1026     if (mimeType.isEmpty())
1027         mimeType = "text/xml";
1028
1029     return mimeType;
1030 }
1031
1032 bool XMLHttpRequest::responseIsXML() const
1033 {
1034     // FIXME: Remove the lower() call when DOMImplementation.isXMLMIMEType() is modified
1035     //        to do case insensitive MIME type matching.
1036     return DOMImplementation::isXMLMIMEType(responseMIMEType().lower());
1037 }
1038
1039 int XMLHttpRequest::status() const
1040 {
1041     if (m_state == UNSENT || m_state == OPENED || m_error)
1042         return 0;
1043
1044     if (m_response.httpStatusCode())
1045         return m_response.httpStatusCode();
1046
1047     return 0;
1048 }
1049
1050 String XMLHttpRequest::statusText() const
1051 {
1052     if (m_state == UNSENT || m_state == OPENED || m_error)
1053         return String();
1054
1055     if (!m_response.httpStatusText().isNull())
1056         return m_response.httpStatusText();
1057
1058     return String();
1059 }
1060
1061 void XMLHttpRequest::didFail(const ResourceError& error)
1062 {
1063
1064     // If we are already in an error state, for instance we called abort(), bail out early.
1065     if (m_error)
1066         return;
1067
1068     if (error.isCancellation()) {
1069         m_exceptionCode = XMLHttpRequestException::ABORT_ERR;
1070         abortError();
1071         return;
1072     }
1073
1074     // In case of worker sync timeouts.
1075     if (error.isTimeout()) {
1076         didReachTimeout();
1077         return;
1078     }
1079
1080     // Network failures are already reported to Web Inspector by ResourceLoader.
1081     if (error.domain() == errorDomainWebKitInternal) {
1082         String message = makeString("XMLHttpRequest cannot load ", error.failingURL().string(), ". ", error.localizedDescription());
1083         logConsoleError(scriptExecutionContext(), message);
1084     }
1085
1086     m_exceptionCode = XMLHttpRequestException::NETWORK_ERR;
1087     networkError();
1088 }
1089
1090 void XMLHttpRequest::didFailRedirectCheck()
1091 {
1092     networkError();
1093 }
1094
1095 void XMLHttpRequest::didFinishLoading(unsigned long identifier, double)
1096 {
1097     if (m_error)
1098         return;
1099
1100     if (m_state < HEADERS_RECEIVED)
1101         changeState(HEADERS_RECEIVED);
1102
1103     if (m_decoder)
1104         m_responseBuilder.append(m_decoder->flush());
1105
1106     m_responseBuilder.shrinkToFit();
1107
1108     InspectorInstrumentation::didFinishXHRLoading(scriptExecutionContext(), this, identifier, m_responseBuilder.toStringPreserveCapacity(), m_url, m_lastSendURL, m_lastSendLineNumber, m_lastSendColumnNumber);
1109
1110     bool hadLoader = m_loader;
1111     m_loader = nullptr;
1112
1113     m_sendFlag = false;
1114     changeState(DONE);
1115     m_responseEncoding = String();
1116     m_decoder = nullptr;
1117
1118     m_timeoutTimer.stop();
1119
1120     if (hadLoader)
1121         dropProtection();
1122 }
1123
1124 void XMLHttpRequest::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
1125 {
1126     if (!m_upload)
1127         return;
1128
1129     if (m_uploadEventsAllowed)
1130         m_upload->dispatchThrottledProgressEvent(true, bytesSent, totalBytesToBeSent);
1131     if (bytesSent == totalBytesToBeSent && !m_uploadComplete) {
1132         m_uploadComplete = true;
1133         if (m_uploadEventsAllowed) {
1134             m_upload->dispatchProgressEvent(eventNames().loadEvent);
1135             m_upload->dispatchProgressEvent(eventNames().loadendEvent);
1136         }
1137     }
1138 }
1139
1140 void XMLHttpRequest::didReceiveResponse(unsigned long identifier, const ResourceResponse& response)
1141 {
1142     InspectorInstrumentation::didReceiveXHRResponse(scriptExecutionContext(), identifier);
1143
1144     m_response = response;
1145     if (!m_mimeTypeOverride.isEmpty())
1146         m_response.setHTTPHeaderField(HTTPHeaderName::ContentType, m_mimeTypeOverride);
1147 }
1148
1149 void XMLHttpRequest::didReceiveData(const char* data, int len)
1150 {
1151     if (m_error)
1152         return;
1153
1154     if (m_state < HEADERS_RECEIVED)
1155         changeState(HEADERS_RECEIVED);
1156
1157     // FIXME: Should we update "Content-Type" header field with m_mimeTypeOverride value in case it has changed since didReceiveResponse?
1158     if (!m_mimeTypeOverride.isEmpty())
1159         m_responseEncoding = extractCharsetFromMediaType(m_mimeTypeOverride);
1160     if (m_responseEncoding.isEmpty())
1161         m_responseEncoding = m_response.textEncodingName();
1162
1163     bool useDecoder = shouldDecodeResponse();
1164
1165     if (useDecoder && !m_decoder) {
1166         if (!m_responseEncoding.isEmpty())
1167             m_decoder = TextResourceDecoder::create("text/plain", m_responseEncoding);
1168         // allow TextResourceDecoder to look inside the m_response if it's XML or HTML
1169         else if (responseIsXML()) {
1170             m_decoder = TextResourceDecoder::create("application/xml");
1171             // Don't stop on encoding errors, unlike it is done for other kinds of XML resources. This matches the behavior of previous WebKit versions, Firefox and Opera.
1172             m_decoder->useLenientXMLDecoding();
1173         } else if (equalIgnoringCase(responseMIMEType(), "text/html"))
1174             m_decoder = TextResourceDecoder::create("text/html", "UTF-8");
1175         else
1176             m_decoder = TextResourceDecoder::create("text/plain", "UTF-8");
1177     }
1178
1179     if (!len)
1180         return;
1181
1182     if (len == -1)
1183         len = strlen(data);
1184
1185     if (useDecoder)
1186         m_responseBuilder.append(m_decoder->decode(data, len));
1187     else if (m_responseTypeCode == ResponseTypeArrayBuffer || m_responseTypeCode == ResponseTypeBlob) {
1188         // Buffer binary data.
1189         if (!m_binaryResponseBuilder)
1190             m_binaryResponseBuilder = SharedBuffer::create();
1191         m_binaryResponseBuilder->append(data, len);
1192     }
1193
1194     if (!m_error) {
1195         m_receivedLength += len;
1196
1197         if (m_async) {
1198             long long expectedLength = m_response.expectedContentLength();
1199             bool lengthComputable = expectedLength > 0 && m_receivedLength <= expectedLength;
1200             unsigned long long total = lengthComputable ? expectedLength : 0;
1201             m_progressEventThrottle.dispatchThrottledProgressEvent(lengthComputable, m_receivedLength, total);
1202         }
1203
1204         if (m_state != LOADING)
1205             changeState(LOADING);
1206         else
1207             // Firefox calls readyStateChanged every time it receives data, 4449442
1208             callReadyStateChangeListener();
1209     }
1210 }
1211
1212 void XMLHttpRequest::dispatchErrorEvents(const AtomicString& type)
1213 {
1214     if (!m_uploadComplete) {
1215         m_uploadComplete = true;
1216         if (m_upload && m_uploadEventsAllowed) {
1217             m_upload->dispatchProgressEvent(eventNames().progressEvent);
1218             m_upload->dispatchProgressEvent(type);
1219             m_upload->dispatchProgressEvent(eventNames().loadendEvent);
1220         }
1221     }
1222     m_progressEventThrottle.dispatchProgressEvent(eventNames().progressEvent);
1223     m_progressEventThrottle.dispatchProgressEvent(type);
1224     m_progressEventThrottle.dispatchProgressEvent(eventNames().loadendEvent);
1225 }
1226
1227 void XMLHttpRequest::didReachTimeout()
1228 {
1229     // internalAbort() calls dropProtection(), which may release the last reference.
1230     Ref<XMLHttpRequest> protect(*this);
1231     if (!internalAbort())
1232         return;
1233
1234     clearResponse();
1235     clearRequest();
1236
1237     m_sendFlag = false;
1238     m_error = true;
1239     m_exceptionCode = XMLHttpRequestException::TIMEOUT_ERR;
1240
1241     if (!m_async) {
1242         m_state = DONE;
1243         m_exceptionCode = TIMEOUT_ERR;
1244         return;
1245     }
1246
1247     changeState(DONE);
1248
1249     dispatchErrorEvents(eventNames().timeoutEvent);
1250 }
1251
1252 bool XMLHttpRequest::canSuspendForDocumentSuspension() const
1253 {
1254     // If the load event has not fired yet, cancelling the load in suspend() may cause
1255     // the load event to be fired and arbitrary JS execution, which would be unsafe.
1256     // Therefore, we prevent suspending in this case.
1257     return document()->loadEventFinished();
1258 }
1259
1260 const char* XMLHttpRequest::activeDOMObjectName() const
1261 {
1262     return "XMLHttpRequest";
1263 }
1264
1265 void XMLHttpRequest::suspend(ReasonForSuspension reason)
1266 {
1267     m_progressEventThrottle.suspend();
1268
1269     if (m_resumeTimer.isActive()) {
1270         m_resumeTimer.stop();
1271         m_dispatchErrorOnResuming = true;
1272     }
1273
1274     if (reason == ActiveDOMObject::PageCache && m_loader) {
1275         // Going into PageCache, abort the request and dispatch a network error on resuming.
1276         genericError();
1277         m_dispatchErrorOnResuming = true;
1278         bool aborted = internalAbort();
1279         // It should not be possible to restart the load when aborting in suspend() because
1280         // we are not allowed to execute in JS in suspend().
1281         ASSERT_UNUSED(aborted, aborted);
1282     }
1283 }
1284
1285 void XMLHttpRequest::resume()
1286 {
1287     m_progressEventThrottle.resume();
1288
1289     // We are not allowed to execute arbitrary JS in resume() so dispatch
1290     // the error event in a timer.
1291     if (m_dispatchErrorOnResuming && !m_resumeTimer.isActive())
1292         m_resumeTimer.startOneShot(0);
1293 }
1294
1295 void XMLHttpRequest::resumeTimerFired()
1296 {
1297     ASSERT(m_dispatchErrorOnResuming);
1298     m_dispatchErrorOnResuming = false;
1299     dispatchErrorEvents(eventNames().errorEvent);
1300 }
1301
1302 void XMLHttpRequest::stop()
1303 {
1304     internalAbort();
1305 }
1306
1307 void XMLHttpRequest::contextDestroyed()
1308 {
1309     ASSERT(!m_loader);
1310     ActiveDOMObject::contextDestroyed();
1311 }
1312
1313 } // namespace WebCore