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