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