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