Index: Source/core/xmlhttprequest/XMLHttpRequest.cpp |
diff --git a/Source/core/xmlhttprequest/XMLHttpRequest.cpp b/Source/core/xmlhttprequest/XMLHttpRequest.cpp |
index 065d9e10784c780b857c63125bad758d95b958e8..fce174ac9fb5dfa9ecf9b40f9eb54edb85b0fd9d 100644 |
--- a/Source/core/xmlhttprequest/XMLHttpRequest.cpp |
+++ b/Source/core/xmlhttprequest/XMLHttpRequest.cpp |
@@ -75,12 +75,33 @@ namespace blink { |
DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, xmlHttpRequestCounter, ("XMLHttpRequest")); |
-static bool isSetCookieHeader(const AtomicString& name) |
+namespace { |
+ |
+// This class protects the wrapper of the associated XMLHttpRequest object |
+// via hasPendingActivity method which returns true if |
+// m_eventDispatchRecursionLevel is positive. |
+class ScopedEventDispatchProtect final { |
+public: |
+ explicit ScopedEventDispatchProtect(int* level) : m_level(level) |
+ { |
+ ++*m_level; |
+ } |
+ ~ScopedEventDispatchProtect() |
+ { |
+ ASSERT(*m_level > 0); |
+ --*m_level; |
+ } |
+ |
+private: |
+ int* const m_level; |
+}; |
+ |
+bool isSetCookieHeader(const AtomicString& name) |
{ |
return equalIgnoringCase(name, "set-cookie") || equalIgnoringCase(name, "set-cookie2"); |
} |
-static void replaceCharsetInMediaType(String& mediaType, const String& charsetValue) |
+void replaceCharsetInMediaType(String& mediaType, const String& charsetValue) |
{ |
unsigned pos = 0, len = 0; |
@@ -99,7 +120,7 @@ static void replaceCharsetInMediaType(String& mediaType, const String& charsetVa |
} |
} |
-static void logConsoleError(ExecutionContext* context, const String& message) |
+void logConsoleError(ExecutionContext* context, const String& message) |
{ |
if (!context) |
return; |
@@ -108,6 +129,8 @@ static void logConsoleError(ExecutionContext* context, const String& message) |
context->addConsoleMessage(ConsoleMessage::create(JSMessageSource, ErrorMessageLevel, message)); |
} |
+} // namespace |
+ |
using Result = WebDataConsumerHandle::Result; |
// ReadableStreamSource is the underlying source for the response stream of |
@@ -308,6 +331,7 @@ XMLHttpRequest::XMLHttpRequest(ExecutionContext* context, PassRefPtr<SecurityOri |
, m_progressEventThrottle(this) |
, m_responseTypeCode(ResponseTypeDefault) |
, m_securityOrigin(securityOrigin) |
+ , m_eventDispatchRecursionLevel(0) |
, m_async(true) |
, m_includeCredentials(false) |
, m_parsedResponse(false) |
@@ -1114,7 +1138,7 @@ bool XMLHttpRequest::internalAbort() |
// If abort() called internalAbort() and a nested open() ended up |
// clearing the error flag, but didn't send(), make sure the error |
// flag is still set. |
- bool newLoadStarted = hasPendingActivity(); |
+ bool newLoadStarted = m_loader; |
if (!newLoadStarted) |
m_error = true; |
@@ -1179,10 +1203,6 @@ void XMLHttpRequest::handleNetworkError() |
long long expectedLength = m_response.expectedContentLength(); |
long long receivedLength = m_receivedLength; |
- // Prevent the XMLHttpRequest instance from being destoryed during |
- // |internalAbort()|. |
- RefPtrWillBeRawPtr<XMLHttpRequest> protect(this); |
- |
if (!internalAbort()) |
return; |
@@ -1197,10 +1217,6 @@ void XMLHttpRequest::handleDidCancel() |
long long expectedLength = m_response.expectedContentLength(); |
long long receivedLength = m_receivedLength; |
- // Prevent the XMLHttpRequest instance from being destoryed during |
- // |internalAbort()|. |
- RefPtrWillBeRawPtr<XMLHttpRequest> protect(this); |
- |
if (!internalAbort()) |
return; |
@@ -1400,6 +1416,7 @@ String XMLHttpRequest::statusText() const |
void XMLHttpRequest::didFail(const ResourceError& error) |
{ |
WTF_LOG(Network, "XMLHttpRequest %p didFail()", this); |
+ ScopedEventDispatchProtect protect(&m_eventDispatchRecursionLevel); |
// If we are already in an error state, for instance we called abort(), bail out early. |
if (m_error) |
@@ -1428,6 +1445,7 @@ void XMLHttpRequest::didFail(const ResourceError& error) |
void XMLHttpRequest::didFailRedirectCheck() |
{ |
WTF_LOG(Network, "XMLHttpRequest %p didFailRedirectCheck()", this); |
+ ScopedEventDispatchProtect protect(&m_eventDispatchRecursionLevel); |
handleNetworkError(); |
// Now the XMLHttpRequest instance may be dead. |
@@ -1436,6 +1454,7 @@ void XMLHttpRequest::didFailRedirectCheck() |
void XMLHttpRequest::didFinishLoading(unsigned long identifier, double) |
{ |
WTF_LOG(Network, "XMLHttpRequest %p didFinishLoading(%lu)", this, identifier); |
+ ScopedEventDispatchProtect protect(&m_eventDispatchRecursionLevel); |
if (m_error) |
return; |
@@ -1489,6 +1508,7 @@ void XMLHttpRequest::didFinishLoadingInternal() |
void XMLHttpRequest::didFinishLoadingFromBlob() |
{ |
WTF_LOG(Network, "XMLHttpRequest %p didFinishLoadingFromBlob", this); |
+ ScopedEventDispatchProtect protect(&m_eventDispatchRecursionLevel); |
didFinishLoadingInternal(); |
} |
@@ -1496,6 +1516,7 @@ void XMLHttpRequest::didFinishLoadingFromBlob() |
void XMLHttpRequest::didFailLoadingFromBlob() |
{ |
WTF_LOG(Network, "XMLHttpRequest %p didFailLoadingFromBlob()", this); |
+ ScopedEventDispatchProtect protect(&m_eventDispatchRecursionLevel); |
if (m_error) |
return; |
@@ -1520,6 +1541,8 @@ PassRefPtr<BlobDataHandle> XMLHttpRequest::createBlobDataHandleFromResponse() |
void XMLHttpRequest::notifyParserStopped() |
{ |
+ ScopedEventDispatchProtect protect(&m_eventDispatchRecursionLevel); |
+ |
// This should only be called when response document is parsed asynchronously. |
ASSERT(m_responseDocumentParser); |
ASSERT(!m_responseDocumentParser->isParsing()); |
@@ -1556,6 +1579,7 @@ void XMLHttpRequest::endLoading() |
void XMLHttpRequest::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent) |
{ |
WTF_LOG(Network, "XMLHttpRequest %p didSendData(%llu, %llu)", this, bytesSent, totalBytesToBeSent); |
+ ScopedEventDispatchProtect protect(&m_eventDispatchRecursionLevel); |
if (!m_upload) |
return; |
@@ -1573,6 +1597,7 @@ void XMLHttpRequest::didSendData(unsigned long long bytesSent, unsigned long lon |
void XMLHttpRequest::didReceiveResponse(unsigned long identifier, const ResourceResponse& response, PassOwnPtr<WebDataConsumerHandle> handle) |
{ |
WTF_LOG(Network, "XMLHttpRequest %p didReceiveResponse(%lu)", this, identifier); |
+ ScopedEventDispatchProtect protect(&m_eventDispatchRecursionLevel); |
m_response = response; |
if (!m_mimeTypeOverride.isEmpty()) { |
@@ -1590,9 +1615,6 @@ void XMLHttpRequest::didReceiveResponse(unsigned long identifier, const Resource |
m_responseStream = new ReadableStreamImpl<ReadableStreamChunkTypeTraits<DOMArrayBuffer>>(executionContext(), m_responseStreamSource); |
m_responseStreamSource->startStream(m_responseStream); |
- // This protection seems needed to keep |this| alive after changeState |
- // calling which may call event listeners. |
- RefPtrWillBeRawPtr<XMLHttpRequest> protect(this); |
changeState(HEADERS_RECEIVED); |
if (m_error) { |
// We need to check for |m_error| because |changeState| may trigger |
@@ -1649,6 +1671,7 @@ PassOwnPtr<TextResourceDecoder> XMLHttpRequest::createDecoder() const |
void XMLHttpRequest::didReceiveData(const char* data, unsigned len) |
{ |
+ ScopedEventDispatchProtect protect(&m_eventDispatchRecursionLevel); |
if (m_error) |
return; |
@@ -1703,6 +1726,7 @@ void XMLHttpRequest::didReceiveData(const char* data, unsigned len) |
void XMLHttpRequest::didDownloadData(int dataLength) |
{ |
+ ScopedEventDispatchProtect protect(&m_eventDispatchRecursionLevel); |
if (m_error) |
return; |
@@ -1732,10 +1756,6 @@ void XMLHttpRequest::handleDidTimeout() |
long long expectedLength = m_response.expectedContentLength(); |
long long receivedLength = m_receivedLength; |
- // Prevent the XMLHttpRequest instance from being destoryed during |
- // |internalAbort()|. |
- RefPtrWillBeRawPtr<XMLHttpRequest> protect(this); |
- |
if (!internalAbort()) |
return; |
@@ -1769,7 +1789,7 @@ bool XMLHttpRequest::hasPendingActivity() const |
return true; |
if (m_responseStream && m_responseStream->hasPendingActivity()) |
return true; |
- return false; |
+ return m_eventDispatchRecursionLevel > 0; |
} |
void XMLHttpRequest::contextDestroyed() |