Ignore:
Timestamp:
Mar 9, 2020, 1:17:21 PM (5 years ago)
Author:
Chris Dumez
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:

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:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/xml/XMLHttpRequestProgressEventThrottle.cpp

    r251366 r258159  
    3030#include "EventNames.h"
    3131#include "EventTarget.h"
     32#include "XMLHttpRequest.h"
    3233#include "XMLHttpRequestProgressEvent.h"
    3334
     
    3637const Seconds XMLHttpRequestProgressEventThrottle::minimumProgressEventDispatchingInterval { 50_ms }; // 50 ms per specification.
    3738
    38 XMLHttpRequestProgressEventThrottle::XMLHttpRequestProgressEventThrottle(EventTarget& target)
     39XMLHttpRequestProgressEventThrottle::XMLHttpRequestProgressEventThrottle(XMLHttpRequest& target)
    3940    : m_target(target)
    4041    , m_dispatchThrottledProgressEventTimer(target.scriptExecutionContext(), *this, &XMLHttpRequestProgressEventThrottle::dispatchThrottledProgressEventTimerFired)
    41     , m_dispatchDeferredEventsAfterResumingTimer(target.scriptExecutionContext(), *this, &XMLHttpRequestProgressEventThrottle::dispatchDeferredEventsAfterResuming)
    4242{
    4343    m_dispatchThrottledProgressEventTimer.suspendIfNeeded();
    44     m_dispatchDeferredEventsAfterResumingTimer.suspendIfNeeded();
    4544}
    4645
     
    8281void XMLHttpRequestProgressEventThrottle::dispatchEventWhenPossible(Event& event)
    8382{
    84     if (m_shouldDeferEventsDueToSuspension) {
    85         if (m_eventsDeferredDueToSuspension.size() > 1 && event.type() == eventNames().readystatechangeEvent && event.type() == m_eventsDeferredDueToSuspension.last()->type()) {
    86             // Readystatechange events are state-less so avoid repeating two identical events in a row on resume.
    87             return;
    88         }
    89         m_eventsDeferredDueToSuspension.append(event);
    90     } else
     83    if (m_shouldDeferEventsDueToSuspension)
     84        m_target.queueTaskToDispatchEvent(m_target, TaskSource::Networking, makeRef(event));
     85    else
    9186        m_target.dispatchEvent(event);
    9287}
     
    118113}
    119114
    120 void XMLHttpRequestProgressEventThrottle::dispatchDeferredEventsAfterResuming()
    121 {
    122     ASSERT(m_shouldDeferEventsDueToSuspension);
    123     m_shouldDeferEventsDueToSuspension = false;
    124 
    125     // Take over the deferred events before dispatching them which can potentially add more.
    126     auto eventsDeferredDueToSuspension = WTFMove(m_eventsDeferredDueToSuspension);
    127 
    128     flushProgressEvent();
    129 
    130     for (auto& deferredEvent : eventsDeferredDueToSuspension)
    131         dispatchEventWhenPossible(deferredEvent);
    132 }
    133 
    134115void XMLHttpRequestProgressEventThrottle::dispatchThrottledProgressEventTimerFired()
    135116{
     
    148129{
    149130    m_shouldDeferEventsDueToSuspension = true;
     131
     132    if (m_hasPendingThrottledProgressEvent) {
     133        m_target.queueTaskKeepingObjectAlive(m_target, TaskSource::Networking, [this] {
     134            flushProgressEvent();
     135        });
     136    }
    150137}
    151138
    152139void XMLHttpRequestProgressEventThrottle::resume()
    153140{
    154     if (m_eventsDeferredDueToSuspension.isEmpty() && !m_hasPendingThrottledProgressEvent) {
     141    m_target.queueTaskKeepingObjectAlive(m_target, TaskSource::Networking, [this] {
    155142        m_shouldDeferEventsDueToSuspension = false;
    156         return;
    157     }
    158 
    159     m_dispatchDeferredEventsAfterResumingTimer.startOneShot(0_s);
     143    });
    160144}
    161145
Note: See TracChangeset for help on using the changeset viewer.