Title: [258159] trunk/Source/WebCore
Revision
258159
Author
cdu...@apple.com
Date
2020-03-09 13:17:21 -0700 (Mon, 09 Mar 2020)

Log Message

Align garbage collection for XMLHttpRequest objects with the specification
https://bugs.webkit.org/show_bug.cgi?id=208481

Reviewed by Ryosuke Niwa.

Align garbage collection for XMLHttpRequest objects with the specification:
- https://xhr.spec.whatwg.org/#garbage-collection

We now override ActiveDOMObject::hasPendingActivity() to match exactly the text
in the specification:
"""
An XMLHttpRequest object must not be garbage collected if its state is either
opened with the send() flag set, headers received, or loading, and it has one or
more event listeners registered whose type is one of readystatechange, progress,
abort, error, load, timeout, and loadend.
"""

Previously, we were trying to implement this behavior with ActiveDOMObject's
setPendingActivity() / unsetPendingActivity() but this was error and leak prone.
It was also keeping the JS wrapper alive too long in the cases where the JS
does not have any event listeners. If the JS has not event listeners, then we
can collect the JS wrapper, we just need to keep the implementation
XMLHttpRequest object for the duration of the load.

No new tests, covered by existing test such as:
fast/xmlhttprequest/xmlhttprequest-gc.html

* dom/EventTarget.cpp:
(WebCore::EventTarget::addEventListener):
(WebCore::EventTarget::removeEventListener):
(WebCore::EventTarget::removeAllEventListeners):
* dom/EventTarget.h:
(WebCore::EventTarget::eventListenersDidChange):
* xml/XMLHttpRequest.cpp:
(WebCore::XMLHttpRequest::XMLHttpRequest):
(WebCore::XMLHttpRequest::changeState):
(WebCore::XMLHttpRequest::open):
(WebCore::XMLHttpRequest::prepareToSend):
(WebCore::XMLHttpRequest::createRequest):
(WebCore::XMLHttpRequest::abort):
(WebCore::XMLHttpRequest::internalAbort):
(WebCore::XMLHttpRequest::networkError):
(WebCore::XMLHttpRequest::didFail):
(WebCore::XMLHttpRequest::didFinishLoading):
(WebCore::XMLHttpRequest::didReachTimeout):
(WebCore::XMLHttpRequest::contextDestroyed):
(WebCore::XMLHttpRequest::eventListenersDidChange):
(WebCore::XMLHttpRequest::hasPendingActivity const):
* xml/XMLHttpRequest.h:
* xml/XMLHttpRequestProgressEventThrottle.cpp:
(WebCore::XMLHttpRequestProgressEventThrottle::XMLHttpRequestProgressEventThrottle):
(WebCore::XMLHttpRequestProgressEventThrottle::dispatchEventWhenPossible):
(WebCore::XMLHttpRequestProgressEventThrottle::suspend):
(WebCore::XMLHttpRequestProgressEventThrottle::resume):
* xml/XMLHttpRequestProgressEventThrottle.h:

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (258158 => 258159)


--- trunk/Source/WebCore/ChangeLog	2020-03-09 20:10:06 UTC (rev 258158)
+++ trunk/Source/WebCore/ChangeLog	2020-03-09 20:17:21 UTC (rev 258159)
@@ -1,3 +1,61 @@
+2020-03-09  Chris Dumez  <cdu...@apple.com>
+
+        Align garbage collection for XMLHttpRequest objects with the specification
+        https://bugs.webkit.org/show_bug.cgi?id=208481
+
+        Reviewed by Ryosuke Niwa.
+
+        Align garbage collection for XMLHttpRequest objects with the specification:
+        - https://xhr.spec.whatwg.org/#garbage-collection
+
+        We now override ActiveDOMObject::hasPendingActivity() to match exactly the text
+        in the specification:
+        """
+        An XMLHttpRequest object must not be garbage collected if its state is either
+        opened with the send() flag set, headers received, or loading, and it has one or
+        more event listeners registered whose type is one of readystatechange, progress,
+        abort, error, load, timeout, and loadend.
+        """
+
+        Previously, we were trying to implement this behavior with ActiveDOMObject's
+        setPendingActivity() / unsetPendingActivity() but this was error and leak prone.
+        It was also keeping the JS wrapper alive too long in the cases where the JS
+        does not have any event listeners. If the JS has not event listeners, then we
+        can collect the JS wrapper, we just need to keep the implementation
+        XMLHttpRequest object for the duration of the load.
+
+        No new tests, covered by existing test such as:
+        fast/xmlhttprequest/xmlhttprequest-gc.html
+
+        * dom/EventTarget.cpp:
+        (WebCore::EventTarget::addEventListener):
+        (WebCore::EventTarget::removeEventListener):
+        (WebCore::EventTarget::removeAllEventListeners):
+        * dom/EventTarget.h:
+        (WebCore::EventTarget::eventListenersDidChange):
+        * xml/XMLHttpRequest.cpp:
+        (WebCore::XMLHttpRequest::XMLHttpRequest):
+        (WebCore::XMLHttpRequest::changeState):
+        (WebCore::XMLHttpRequest::open):
+        (WebCore::XMLHttpRequest::prepareToSend):
+        (WebCore::XMLHttpRequest::createRequest):
+        (WebCore::XMLHttpRequest::abort):
+        (WebCore::XMLHttpRequest::internalAbort):
+        (WebCore::XMLHttpRequest::networkError):
+        (WebCore::XMLHttpRequest::didFail):
+        (WebCore::XMLHttpRequest::didFinishLoading):
+        (WebCore::XMLHttpRequest::didReachTimeout):
+        (WebCore::XMLHttpRequest::contextDestroyed):
+        (WebCore::XMLHttpRequest::eventListenersDidChange):
+        (WebCore::XMLHttpRequest::hasPendingActivity const):
+        * xml/XMLHttpRequest.h:
+        * xml/XMLHttpRequestProgressEventThrottle.cpp:
+        (WebCore::XMLHttpRequestProgressEventThrottle::XMLHttpRequestProgressEventThrottle):
+        (WebCore::XMLHttpRequestProgressEventThrottle::dispatchEventWhenPossible):
+        (WebCore::XMLHttpRequestProgressEventThrottle::suspend):
+        (WebCore::XMLHttpRequestProgressEventThrottle::resume):
+        * xml/XMLHttpRequestProgressEventThrottle.h:
+
 2020-03-09  Wenson Hsieh  <wenson_hs...@apple.com>
 
         Creating paths that are single-segment bezier or quad curves should not require a CGPath allocation

Modified: trunk/Source/WebCore/dom/EventTarget.cpp (258158 => 258159)


--- trunk/Source/WebCore/dom/EventTarget.cpp	2020-03-09 20:10:06 UTC (rev 258158)
+++ trunk/Source/WebCore/dom/EventTarget.cpp	2020-03-09 20:17:21 UTC (rev 258159)
@@ -94,6 +94,7 @@
     if (listenerCreatedFromScript)
         InspectorInstrumentation::didAddEventListener(*this, eventType, listenerRef.get(), options.capture);
 
+    eventListenersDidChange();
     return true;
 }
 
@@ -133,7 +134,11 @@
 
     InspectorInstrumentation::willRemoveEventListener(*this, eventType, listener, options.capture);
 
-    return data->eventListenerMap.remove(eventType, listener, options.capture);
+    if (data->eventListenerMap.remove(eventType, listener, options.capture)) {
+        eventListenersDidChange();
+        return true;
+    }
+    return false;
 }
 
 bool EventTarget::setAttributeEventListener(const AtomString& eventType, RefPtr<EventListener>&& listener, DOMWrapperWorld& isolatedWorld)
@@ -345,8 +350,10 @@
     threadData.setIsInRemoveAllEventListeners(true);
 
     auto* data = ""
-    if (data)
+    if (data && !data->eventListenerMap.isEmpty()) {
         data->eventListenerMap.clear();
+        eventListenersDidChange();
+    }
 
     threadData.setIsInRemoveAllEventListeners(false);
 }

Modified: trunk/Source/WebCore/dom/EventTarget.h (258158 => 258159)


--- trunk/Source/WebCore/dom/EventTarget.h	2020-03-09 20:10:06 UTC (rev 258158)
+++ trunk/Source/WebCore/dom/EventTarget.h	2020-03-09 20:17:21 UTC (rev 258159)
@@ -124,6 +124,8 @@
     virtual EventTargetData& ensureEventTargetData() = 0;
     const EventTargetData* eventTargetData() const;
 
+    virtual void eventListenersDidChange() { }
+
 private:
     virtual void refEventTarget() = 0;
     virtual void derefEventTarget() = 0;

Modified: trunk/Source/WebCore/xml/XMLHttpRequest.cpp (258158 => 258159)


--- trunk/Source/WebCore/xml/XMLHttpRequest.cpp	2020-03-09 20:10:06 UTC (rev 258158)
+++ trunk/Source/WebCore/xml/XMLHttpRequest.cpp	2020-03-09 20:17:21 UTC (rev 258159)
@@ -120,7 +120,6 @@
     , m_readyState(static_cast<unsigned>(UNSENT))
     , m_responseType(static_cast<unsigned>(ResponseType::EmptyString))
     , m_progressEventThrottle(*this)
-    , m_networkErrorTimer(*this, &XMLHttpRequest::networkErrorTimerFired)
     , m_timeoutTimer(*this, &XMLHttpRequest::didReachTimeout)
     , m_maximumIntervalForUserGestureForwarding(maximumIntervalForUserGestureForwarding)
 {
@@ -281,6 +280,11 @@
 void XMLHttpRequest::changeState(State newState)
 {
     if (readyState() != newState) {
+        // Setting the readyState to DONE could get the wrapper collected before we get a chance to fire the JS
+        // events in callReadyStateChangeListener() below so we extend the lifetime of the JS wrapper until the
+        // of this scope.
+        auto eventFiringActivity = makePendingActivity(*this);
+
         m_readyState = static_cast<State>(newState);
         if (readyState() == DONE) {
             // The XHR object itself holds on to the responseText, and
@@ -385,7 +389,7 @@
 
     m_async = async;
 
-    ASSERT(!m_loader);
+    ASSERT(!m_loadingActivity);
 
     changeState(OPENED);
 
@@ -420,15 +424,16 @@
 
     if (readyState() != OPENED || m_sendFlag)
         return ExceptionOr<void> { Exception { InvalidStateError } };
-    ASSERT(!m_loader);
+    ASSERT(!m_loadingActivity);
 
     // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved.
     if (!context.shouldBypassMainWorldContentSecurityPolicy() && !context.contentSecurityPolicy()->allowConnectToSource(m_url)) {
         if (!m_async)
             return ExceptionOr<void> { Exception { NetworkError } };
-        setPendingActivity(*this);
         m_timeoutTimer.stop();
-        m_networkErrorTimer.startOneShot(0_s);
+        queueTaskKeepingObjectAlive(*this, TaskSource::Networking, [this] {
+            networkError();
+        });
         return ExceptionOr<void> { };
     }
 
@@ -634,22 +639,18 @@
         if (!m_uploadComplete && m_uploadListenerFlag)
             m_upload->dispatchProgressEvent(eventNames().loadstartEvent, 0, request.httpBody()->lengthInBytes());
 
-        if (readyState() != OPENED || !m_sendFlag || m_loader)
+        if (readyState() != OPENED || !m_sendFlag || m_loadingActivity)
             return { };
 
         // 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.
         // This is true while running onunload handlers.
         // FIXME: Maybe we need to be able to send XMLHttpRequests from onunload, <http://bugs.webkit.org/show_bug.cgi?id=10904>.
-        m_loader = ThreadableLoader::create(*scriptExecutionContext(), *this, WTFMove(request), options);
+        auto loader = ThreadableLoader::create(*scriptExecutionContext(), *this, WTFMove(request), options);
+        if (loader)
+            m_loadingActivity = LoadingActivity { makeRef(*this), loader.releaseNonNull() };
 
         // Either loader is null or some error was synchronously sent to us.
-        ASSERT(m_loader || !m_sendFlag);
-
-        // Neither this object nor the _javascript_ wrapper should be deleted while
-        // a request is in progress because we need to keep the listeners alive,
-        // and they are referenced by the _javascript_ wrapper.
-        if (m_loader)
-            setPendingActivity(*this);
+        ASSERT(m_loadingActivity || !m_sendFlag);
     } else {
         if (scriptExecutionContext()->isDocument() && !isFeaturePolicyAllowedByDocumentAndAllOwners(FeaturePolicy::Type::SyncXHR, *document()))
             return Exception { NetworkError };
@@ -669,7 +670,6 @@
 
 void XMLHttpRequest::abort()
 {
-    // internalAbort() calls unsetPendingActivity(this), which may release the last reference.
     Ref<XMLHttpRequest> protectedThis(*this);
 
     m_wasAbortedByClient = true;
@@ -680,7 +680,7 @@
 
     m_requestHeaders.clear();
     if ((readyState() == OPENED && m_sendFlag) || readyState() == HEADERS_RECEIVED || readyState() == LOADING) {
-        ASSERT(!m_loader);
+        ASSERT(!m_loadingActivity);
         m_sendFlag = false;
         changeState(DONE);
         dispatchErrorEvents(eventNames().abortEvent);
@@ -700,23 +700,21 @@
 
     m_timeoutTimer.stop();
 
-    if (!m_loader)
+    if (!m_loadingActivity)
         return true;
 
-    // Cancelling m_loader may trigger a window.onload callback which can call open() on the same xhr.
+    // Cancelling m_loadingActivity may trigger a window.onload callback which can call open() on the same xhr.
     // This would create internalAbort reentrant call.
-    // m_loader is set to null before being cancelled to exit early in any reentrant internalAbort() call.
-    auto loader = WTFMove(m_loader);
-    loader->cancel();
+    // m_loadingActivity is set to WTF::nullopt before being cancelled to exit early in any reentrant internalAbort() call.
+    auto loadingActivity = std::exchange(m_loadingActivity, WTF::nullopt);
+    loadingActivity->loader->cancel();
 
-    // If window.onload callback calls open() and send() on the same xhr, m_loader is now set to a new value.
+    // If window.onload callback calls open() and send() on the same xhr, m_loadingActivity is now set to a new value.
     // The function calling internalAbort() should abort to let the open() and send() calls continue properly.
     // We ask the function calling internalAbort() to exit by returning false.
     // Save this information to a local variable since we are going to drop protection.
-    bool newLoadStarted = m_loader;
+    bool newLoadStarted = !!m_loadingActivity;
 
-    unsetPendingActivity(*this);
-
     return !newLoadStarted;
 }
 
@@ -758,12 +756,6 @@
     dispatchErrorEvents(eventNames().errorEvent);
     internalAbort();
 }
-
-void XMLHttpRequest::networkErrorTimerFired()
-{
-    networkError();
-    unsetPendingActivity(*this);
-}
     
 void XMLHttpRequest::abortError()
 {
@@ -888,6 +880,8 @@
 
 void XMLHttpRequest::didFail(const ResourceError& error)
 {
+    auto protectedThis = makeRef(*this);
+
     // If we are already in an error state, for instance we called abort(), bail out early.
     if (m_error)
         return;
@@ -906,11 +900,12 @@
     }
 
     // In case didFail is called synchronously on an asynchronous XHR call, let's dispatch network error asynchronously
-    if (m_async && m_sendFlag && !m_loader) {
+    if (m_async && m_sendFlag && !m_loadingActivity) {
         m_sendFlag = false;
-        setPendingActivity(*this);
         m_timeoutTimer.stop();
-        m_networkErrorTimer.startOneShot(0_s);
+        queueTaskKeepingObjectAlive(*this, TaskSource::Networking, [this] {
+            networkError();
+        });
         return;
     }
     m_exceptionCode = NetworkError;
@@ -919,6 +914,8 @@
 
 void XMLHttpRequest::didFinishLoading(unsigned long)
 {
+    auto protectedThis = makeRef(*this);
+
     if (m_error)
         return;
 
@@ -930,8 +927,7 @@
 
     m_responseBuilder.shrinkToFit();
 
-    bool hadLoader = m_loader;
-    m_loader = nullptr;
+    m_loadingActivity = WTF::nullopt;
 
     m_sendFlag = false;
     changeState(DONE);
@@ -939,9 +935,6 @@
     m_decoder = nullptr;
 
     m_timeoutTimer.stop();
-
-    if (hadLoader)
-        unsetPendingActivity(*this);
 }
 
 void XMLHttpRequest::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
@@ -1111,7 +1104,6 @@
 
 void XMLHttpRequest::didReachTimeout()
 {
-    // internalAbort() calls unsetPendingActivity(this), which may release the last reference.
     Ref<XMLHttpRequest> protectedThis(*this);
     if (!internalAbort())
         return;
@@ -1156,7 +1148,7 @@
 
 void XMLHttpRequest::contextDestroyed()
 {
-    ASSERT(!m_loader);
+    ASSERT(!m_loadingActivity);
     ActiveDOMObject::contextDestroyed();
 }
 
@@ -1165,4 +1157,38 @@
     m_maximumIntervalForUserGestureForwarding = Seconds(interval);    
 }
 
+void XMLHttpRequest::eventListenersDidChange()
+{
+    m_hasRelevantEventListener = hasEventListeners(eventNames().abortEvent)
+        || hasEventListeners(eventNames().errorEvent)
+        || hasEventListeners(eventNames().loadEvent)
+        || hasEventListeners(eventNames().loadendEvent)
+        || hasEventListeners(eventNames().progressEvent)
+        || hasEventListeners(eventNames().readystatechangeEvent)
+        || hasEventListeners(eventNames().timeoutEvent);
+}
+
+// An XMLHttpRequest object must not be garbage collected if its state is either opened with the send() flag set, headers received, or loading, and
+// it has one or more event listeners registered whose type is one of readystatechange, progress, abort, error, load, timeout, and loadend.
+bool XMLHttpRequest::hasPendingActivity() const
+{
+    if (ActiveDOMObject::hasPendingActivity())
+        return true;
+
+    if (!m_hasRelevantEventListener)
+        return false;
+
+    switch (readyState()) {
+    case OPENED:
+        return m_sendFlag;
+    case HEADERS_RECEIVED:
+    case LOADING:
+        return true;
+    case UNSENT:
+    case DONE:
+        break;
+    }
+    return false;
+}
+
 } // namespace WebCore

Modified: trunk/Source/WebCore/xml/XMLHttpRequest.h (258158 => 258159)


--- trunk/Source/WebCore/xml/XMLHttpRequest.h	2020-03-09 20:10:06 UTC (rev 258158)
+++ trunk/Source/WebCore/xml/XMLHttpRequest.h	2020-03-09 20:17:21 UTC (rev 258159)
@@ -130,9 +130,17 @@
 
     WEBCORE_EXPORT void setMaximumIntervalForUserGestureForwarding(double);
 
+    bool hasPendingActivity() const final;
+
+    using EventTarget::dispatchEvent;
+    void dispatchEvent(Event&) override;
+
 private:
     explicit XMLHttpRequest(ScriptExecutionContext&);
 
+    // EventTarget.
+    void eventListenersDidChange() final;
+
     TextEncoding finalResponseCharset() const;
 
     // ActiveDOMObject
@@ -185,13 +193,8 @@
 
     void dispatchErrorEvents(const AtomString&);
 
-    using EventTarget::dispatchEvent;
-    void dispatchEvent(Event&) override;
-
     Ref<TextResourceDecoder> createDecoder() const;
 
-    void networkErrorTimerFired();
-
     unsigned m_async : 1;
     unsigned m_includeCredentials : 1;
     unsigned m_sendFlag : 1;
@@ -214,7 +217,11 @@
     RefPtr<FormData> m_requestEntityBody;
     String m_mimeTypeOverride;
 
-    RefPtr<ThreadableLoader> m_loader;
+    struct LoadingActivity {
+        Ref<XMLHttpRequest> protectedThis; // Keep object alive while loading even if there is no longer a JS wrapper.
+        Ref<ThreadableLoader> loader;
+    };
+    Optional<LoadingActivity> m_loadingActivity;
 
     String m_responseEncoding;
 
@@ -235,7 +242,6 @@
 
     mutable String m_allResponseHeaders;
 
-    Timer m_networkErrorTimer;
     Timer m_timeoutTimer;
 
     MonotonicTime m_sendingTime;
@@ -243,6 +249,7 @@
     Optional<ExceptionCode> m_exceptionCode;
     RefPtr<UserGestureToken> m_userGestureToken;
     Seconds m_maximumIntervalForUserGestureForwarding;
+    bool m_hasRelevantEventListener { false };
 };
 
 inline auto XMLHttpRequest::responseType() const -> ResponseType

Modified: trunk/Source/WebCore/xml/XMLHttpRequestProgressEventThrottle.cpp (258158 => 258159)


--- trunk/Source/WebCore/xml/XMLHttpRequestProgressEventThrottle.cpp	2020-03-09 20:10:06 UTC (rev 258158)
+++ trunk/Source/WebCore/xml/XMLHttpRequestProgressEventThrottle.cpp	2020-03-09 20:17:21 UTC (rev 258159)
@@ -29,6 +29,7 @@
 
 #include "EventNames.h"
 #include "EventTarget.h"
+#include "XMLHttpRequest.h"
 #include "XMLHttpRequestProgressEvent.h"
 
 namespace WebCore {
@@ -35,13 +36,11 @@
 
 const Seconds XMLHttpRequestProgressEventThrottle::minimumProgressEventDispatchingInterval { 50_ms }; // 50 ms per specification.
 
-XMLHttpRequestProgressEventThrottle::XMLHttpRequestProgressEventThrottle(EventTarget& target)
+XMLHttpRequestProgressEventThrottle::XMLHttpRequestProgressEventThrottle(XMLHttpRequest& target)
     : m_target(target)
     , m_dispatchThrottledProgressEventTimer(target.scriptExecutionContext(), *this, &XMLHttpRequestProgressEventThrottle::dispatchThrottledProgressEventTimerFired)
-    , m_dispatchDeferredEventsAfterResumingTimer(target.scriptExecutionContext(), *this, &XMLHttpRequestProgressEventThrottle::dispatchDeferredEventsAfterResuming)
 {
     m_dispatchThrottledProgressEventTimer.suspendIfNeeded();
-    m_dispatchDeferredEventsAfterResumingTimer.suspendIfNeeded();
 }
 
 XMLHttpRequestProgressEventThrottle::~XMLHttpRequestProgressEventThrottle() = default;
@@ -81,13 +80,9 @@
 
 void XMLHttpRequestProgressEventThrottle::dispatchEventWhenPossible(Event& event)
 {
-    if (m_shouldDeferEventsDueToSuspension) {
-        if (m_eventsDeferredDueToSuspension.size() > 1 && event.type() == eventNames().readystatechangeEvent && event.type() == m_eventsDeferredDueToSuspension.last()->type()) {
-            // Readystatechange events are state-less so avoid repeating two identical events in a row on resume.
-            return;
-        }
-        m_eventsDeferredDueToSuspension.append(event);
-    } else
+    if (m_shouldDeferEventsDueToSuspension)
+        m_target.queueTaskToDispatchEvent(m_target, TaskSource::Networking, makeRef(event));
+    else
         m_target.dispatchEvent(event);
 }
 
@@ -117,20 +112,6 @@
     dispatchEventWhenPossible(XMLHttpRequestProgressEvent::create(eventNames().progressEvent, m_lengthComputable, m_loaded, m_total));
 }
 
-void XMLHttpRequestProgressEventThrottle::dispatchDeferredEventsAfterResuming()
-{
-    ASSERT(m_shouldDeferEventsDueToSuspension);
-    m_shouldDeferEventsDueToSuspension = false;
-
-    // Take over the deferred events before dispatching them which can potentially add more.
-    auto eventsDeferredDueToSuspension = WTFMove(m_eventsDeferredDueToSuspension);
-
-    flushProgressEvent();
-
-    for (auto& deferredEvent : eventsDeferredDueToSuspension)
-        dispatchEventWhenPossible(deferredEvent);
-}
-
 void XMLHttpRequestProgressEventThrottle::dispatchThrottledProgressEventTimerFired()
 {
     ASSERT(m_dispatchThrottledProgressEventTimer.isActive());
@@ -147,16 +128,19 @@
 void XMLHttpRequestProgressEventThrottle::suspend()
 {
     m_shouldDeferEventsDueToSuspension = true;
+
+    if (m_hasPendingThrottledProgressEvent) {
+        m_target.queueTaskKeepingObjectAlive(m_target, TaskSource::Networking, [this] {
+            flushProgressEvent();
+        });
+    }
 }
 
 void XMLHttpRequestProgressEventThrottle::resume()
 {
-    if (m_eventsDeferredDueToSuspension.isEmpty() && !m_hasPendingThrottledProgressEvent) {
+    m_target.queueTaskKeepingObjectAlive(m_target, TaskSource::Networking, [this] {
         m_shouldDeferEventsDueToSuspension = false;
-        return;
-    }
-
-    m_dispatchDeferredEventsAfterResumingTimer.startOneShot(0_s);
+    });
 }
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/xml/XMLHttpRequestProgressEventThrottle.h (258158 => 258159)


--- trunk/Source/WebCore/xml/XMLHttpRequestProgressEventThrottle.h	2020-03-09 20:10:06 UTC (rev 258158)
+++ trunk/Source/WebCore/xml/XMLHttpRequestProgressEventThrottle.h	2020-03-09 20:17:21 UTC (rev 258159)
@@ -34,6 +34,7 @@
 
 class Event;
 class EventTarget;
+class XMLHttpRequest;
 
 enum ProgressEventAction {
     DoNotFlushProgressEvent,
@@ -44,7 +45,7 @@
 // about every 50ms or for every byte received, whichever is least frequent".
 class XMLHttpRequestProgressEventThrottle {
 public:
-    explicit XMLHttpRequestProgressEventThrottle(EventTarget&);
+    explicit XMLHttpRequestProgressEventThrottle(XMLHttpRequest&);
     virtual ~XMLHttpRequestProgressEventThrottle();
 
     void dispatchThrottledProgressEvent(bool lengthComputable, unsigned long long loaded, unsigned long long total);
@@ -58,20 +59,16 @@
     static const Seconds minimumProgressEventDispatchingInterval;
 
     void dispatchThrottledProgressEventTimerFired();
-    void dispatchDeferredEventsAfterResuming();
     void flushProgressEvent();
     void dispatchEventWhenPossible(Event&);
 
     // Weak pointer to our XMLHttpRequest object as it is the one holding us.
-    EventTarget& m_target;
+    XMLHttpRequest& m_target;
 
     unsigned long long m_loaded { 0 };
     unsigned long long m_total { 0 };
 
-    RefPtr<Event> m_deferredProgressEvent;
-    Vector<Ref<Event>> m_eventsDeferredDueToSuspension;
     SuspendableTimer m_dispatchThrottledProgressEventTimer;
-    SuspendableTimer m_dispatchDeferredEventsAfterResumingTimer;
 
     bool m_hasPendingThrottledProgressEvent { false };
     bool m_lengthComputable { false };
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to