Ignore:
Timestamp:
Apr 17, 2012, 7:02:07 AM (13 years ago)
Author:
[email protected]
Message:

Asserts in XMLHttpRequestProgressEventThrottle
https://bugs.webkit.org/show_bug.cgi?id=81506

Patch by Allan Sandfeld Jensen <[email protected]> on 2012-04-17
Reviewed by Julien Chaffraix.

Source/WebCore:

The asserts were incorrectly triggered because suspending active DOM objects
(which suspends the XMLHttpRequestProgressEventThrottle) doesn't stop JavaScript
from running or suspend any running loader we may have. The previous code would
assume those 2 cases were impossible.

When XmlHttpRequest::open is called or data is received while the XmlHttpRequest object
is suspended the object may attempt to dispatch events. This patch defers these events
until the object is resumed.

Progress events are coalesced similar to normal throttling, and readystate-change events
are coalesced to avoid identical events emitted right after eachother.

On resume the events are dispatched after a timer to avoid interfering with
ScriptExecutionContext which is iterating over suspended objects.

Test: fast/events/pagehide-xhr-open.html

  • xml/XMLHttpRequestProgressEventThrottle.cpp:

(WebCore::XMLHttpRequestProgressEventThrottle::XMLHttpRequestProgressEventThrottle):
(WebCore::XMLHttpRequestProgressEventThrottle::dispatchProgressEvent):
(WebCore::XMLHttpRequestProgressEventThrottle::dispatchEvent):
(WebCore::XMLHttpRequestProgressEventThrottle::flushProgressEvent):
(WebCore::XMLHttpRequestProgressEventThrottle::dispatchDeferredEvents):
(WebCore::XMLHttpRequestProgressEventThrottle::fired):
(WebCore::XMLHttpRequestProgressEventThrottle::suspend):
(WebCore::XMLHttpRequestProgressEventThrottle::resume):

  • xml/XMLHttpRequestProgressEventThrottle.h:

(XMLHttpRequestProgressEventThrottle):

LayoutTests:

Tests that xmlhttprequest.open does not assert in when called from onpagehide.

  • fast/events/pagehide-xhr-open-expected.txt: Added.
  • fast/events/pagehide-xhr-open.html: Added.
  • fast/events/resources/subframe-xmlhttprequest.html: Added.
  • platform/chromium/test_expectations.txt:
File:
1 edited

Legend:

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

    r111717 r114374  
    11/*
    2  * Copyright (C) 2010 Julien Chaffraix <[email protected]>
    3  * All right reserved.
     2 * Copyright (C) 2010 Julien Chaffraix <[email protected]>  All right reserved.
     3 * Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies)
    44 *
    55 * Redistribution and use in source and binary forms, with or without
     
    3939    , m_loaded(0)
    4040    , m_total(0)
    41     , m_suspended(false)
     41    , m_deferEvents(false)
     42    , m_dispatchDeferredEventsTimer(this, &XMLHttpRequestProgressEventThrottle::dispatchDeferredEvents)
    4243{
    4344    ASSERT(target);
     
    5051void XMLHttpRequestProgressEventThrottle::dispatchProgressEvent(bool lengthComputable, unsigned long long loaded, unsigned long long total)
    5152{
    52     ASSERT(!suspended());
     53    if (m_deferEvents) {
     54        // Only store the latest progress event while suspended.
     55        m_deferredProgressEvent = XMLHttpRequestProgressEvent::create(eventNames().progressEvent, lengthComputable, loaded, total);
     56        return;
     57    }
     58
    5359    if (!isActive()) {
    5460        // The timer is not active so the least frequent event for now is every byte.
     
    8086void XMLHttpRequestProgressEventThrottle::dispatchEvent(PassRefPtr<Event> event)
    8187{
    82     ASSERT(!suspended());
    83     // We should not have any pending events from a previous resume.
    84     ASSERT(!m_pausedEvent);
    85 
    86     m_target->dispatchEvent(event);
     88    ASSERT(event);
     89    if (m_deferEvents) {
     90        if (m_deferredEvents.size() > 1 && event->type() == eventNames().readystatechangeEvent && event->type() == m_deferredEvents.last()->type()) {
     91            // Readystatechange events are state-less so avoid repeating two identical events in a row on resume.
     92            return;
     93        }
     94        m_deferredEvents.append(event);
     95    } else
     96        m_target->dispatchEvent(event);
    8797}
    8898
     
    97107void XMLHttpRequestProgressEventThrottle::flushProgressEvent()
    98108{
     109    if (m_deferEvents && m_deferredProgressEvent) {
     110        // Move the progress event to the queue, to get it in the right order on resume.
     111        m_deferredEvents.append(m_deferredProgressEvent);
     112        m_deferredProgressEvent = 0;
     113        return;
     114    }
     115
    99116    if (!hasEventToDispatch())
    100117        return;
     
    110127}
    111128
    112 void XMLHttpRequestProgressEventThrottle::dispatchPausedEvent()
    113 {
    114     ASSERT(!suspended());
    115     if (!m_pausedEvent)
    116         return;
    117 
    118     dispatchEvent(m_pausedEvent);
    119     m_pausedEvent = 0;
     129void XMLHttpRequestProgressEventThrottle::dispatchDeferredEvents(Timer<XMLHttpRequestProgressEventThrottle>* timer)
     130{
     131    ASSERT_UNUSED(timer, timer == &m_dispatchDeferredEventsTimer);
     132    ASSERT(m_deferEvents);
     133    m_deferEvents = false;
     134
     135    // Take over the deferred events before dispatching them which can potentially add more.
     136    Vector<RefPtr<Event> > deferredEvents;
     137    m_deferredEvents.swap(deferredEvents);
     138
     139    RefPtr<Event> deferredProgressEvent = m_deferredProgressEvent;
     140    m_deferredProgressEvent = 0;
     141
     142    Vector<RefPtr<Event> >::const_iterator it = deferredEvents.begin();
     143    const Vector<RefPtr<Event> >::const_iterator end = deferredEvents.end();
     144    for (; it != end; ++it)
     145        dispatchEvent(*it);
     146
     147    // The progress event will be in the m_deferredEvents vector if the load was finished while suspended.
     148    // If not, just send the most up-to-date progress on resume.
     149    if (deferredProgressEvent)
     150        dispatchEvent(deferredProgressEvent);
    120151}
    121152
     
    123154{
    124155    ASSERT(isActive());
    125     ASSERT(!suspended());
    126     ASSERT(!m_pausedEvent);
    127156    if (!hasEventToDispatch()) {
    128157        // No progress event was queued since the previous dispatch, we can safely stop the timer.
     
    143172void XMLHttpRequestProgressEventThrottle::suspend()
    144173{
    145     ASSERT(!m_pausedEvent);
    146 
    147     m_suspended = true;
     174    // If re-suspended before deferred events have been dispatched, just stop the dispatch
     175    // and continue the last suspend.
     176    if (m_dispatchDeferredEventsTimer.isActive()) {
     177        ASSERT(m_deferEvents);
     178        m_dispatchDeferredEventsTimer.stop();
     179        return;
     180    }
     181    ASSERT(!m_deferredProgressEvent);
     182    ASSERT(m_deferredEvents.isEmpty());
     183    ASSERT(!m_deferEvents);
     184
     185    m_deferEvents = true;
    148186    // If we have a progress event waiting to be dispatched,
    149     // just queue it.
     187    // just defer it.
    150188    if (hasEventToDispatch()) {
    151         m_pausedEvent = XMLHttpRequestProgressEvent::create(eventNames().progressEvent, m_lengthComputable, m_loaded, m_total);
     189        m_deferredProgressEvent = XMLHttpRequestProgressEvent::create(eventNames().progressEvent, m_lengthComputable, m_loaded, m_total);
    152190        m_total = 0;
    153191        m_loaded = 0;
     
    161199    ASSERT(!m_total);
    162200
    163     m_suspended = false;
    164     dispatchPausedEvent();
     201    // Do not dispatch events inline here, since ScriptExecutionContext is iterating over
     202    // the list of active DOM objects to resume them, and any activated JS event-handler
     203    // could insert new active DOM objects to the list.
     204    // m_deferEvents is kept true until all deferred events have been dispatched.
     205    m_dispatchDeferredEventsTimer.startOneShot(0);
    165206}
    166207
Note: See TracChangeset for help on using the changeset viewer.