Home | History | Annotate | Download | only in xml
      1 /*
      2  * Copyright (C) 2010 Julien Chaffraix <jchaffraix (at) webkit.org>  All right reserved.
      3  * Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies)
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  * 1. Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  * 2. Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in the
     12  *    documentation and/or other materials provided with the distribution.
     13  *
     14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     15  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     16  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     17  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     18  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     19  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     20  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     25  */
     26 
     27 #include "config.h"
     28 #include "core/xml/XMLHttpRequestProgressEventThrottle.h"
     29 
     30 #include "core/dom/EventTarget.h"
     31 #include "core/xml/XMLHttpRequestProgressEvent.h"
     32 
     33 namespace WebCore {
     34 
     35 const double XMLHttpRequestProgressEventThrottle::minimumProgressEventDispatchingIntervalInSeconds = .05; // 50 ms per specification.
     36 
     37 XMLHttpRequestProgressEventThrottle::XMLHttpRequestProgressEventThrottle(EventTarget* target)
     38     : m_target(target)
     39     , m_loaded(0)
     40     , m_total(0)
     41     , m_deferEvents(false)
     42     , m_dispatchDeferredEventsTimer(this, &XMLHttpRequestProgressEventThrottle::dispatchDeferredEvents)
     43 {
     44     ASSERT(target);
     45 }
     46 
     47 XMLHttpRequestProgressEventThrottle::~XMLHttpRequestProgressEventThrottle()
     48 {
     49 }
     50 
     51 void XMLHttpRequestProgressEventThrottle::dispatchProgressEvent(bool lengthComputable, unsigned long long loaded, unsigned long long total)
     52 {
     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 
     59     if (!isActive()) {
     60         // The timer is not active so the least frequent event for now is every byte.
     61         // Just go ahead and dispatch the event.
     62 
     63         // We should not have any pending loaded & total information from a previous run.
     64         ASSERT(!m_loaded);
     65         ASSERT(!m_total);
     66 
     67         dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().progressEvent, lengthComputable, loaded, total));
     68         startRepeating(minimumProgressEventDispatchingIntervalInSeconds);
     69         return;
     70     }
     71 
     72     // The timer is already active so minimumProgressEventDispatchingIntervalInSeconds is the least frequent event.
     73     m_lengthComputable = lengthComputable;
     74     m_loaded = loaded;
     75     m_total = total;
     76 }
     77 
     78 void XMLHttpRequestProgressEventThrottle::dispatchReadyStateChangeEvent(PassRefPtr<Event> event, ProgressEventAction progressEventAction)
     79 {
     80     if (progressEventAction == FlushProgressEvent)
     81         flushProgressEvent();
     82 
     83     dispatchEvent(event);
     84 }
     85 
     86 void XMLHttpRequestProgressEventThrottle::dispatchEvent(PassRefPtr<Event> event)
     87 {
     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);
     97 }
     98 
     99 void XMLHttpRequestProgressEventThrottle::dispatchEventAndLoadEnd(PassRefPtr<Event> event)
    100 {
    101     ASSERT(event->type() == eventNames().loadEvent || event->type() == eventNames().abortEvent || event->type() == eventNames().errorEvent || event->type() == eventNames().timeoutEvent);
    102 
    103     dispatchEvent(event);
    104     dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().loadendEvent));
    105 }
    106 
    107 void XMLHttpRequestProgressEventThrottle::flushProgressEvent()
    108 {
    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 
    116     if (!hasEventToDispatch())
    117         return;
    118 
    119     PassRefPtr<Event> event = XMLHttpRequestProgressEvent::create(eventNames().progressEvent, m_lengthComputable, m_loaded, m_total);
    120     m_loaded = 0;
    121     m_total = 0;
    122 
    123     // We stop the timer as this is called when no more events are supposed to occur.
    124     stop();
    125 
    126     dispatchEvent(event);
    127 }
    128 
    129 void 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);
    151 }
    152 
    153 void XMLHttpRequestProgressEventThrottle::fired()
    154 {
    155     ASSERT(isActive());
    156     if (!hasEventToDispatch()) {
    157         // No progress event was queued since the previous dispatch, we can safely stop the timer.
    158         stop();
    159         return;
    160     }
    161 
    162     dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().progressEvent, m_lengthComputable, m_loaded, m_total));
    163     m_total = 0;
    164     m_loaded = 0;
    165 }
    166 
    167 bool XMLHttpRequestProgressEventThrottle::hasEventToDispatch() const
    168 {
    169     return (m_total || m_loaded) && isActive();
    170 }
    171 
    172 void XMLHttpRequestProgressEventThrottle::suspend()
    173 {
    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;
    186     // If we have a progress event waiting to be dispatched,
    187     // just defer it.
    188     if (hasEventToDispatch()) {
    189         m_deferredProgressEvent = XMLHttpRequestProgressEvent::create(eventNames().progressEvent, m_lengthComputable, m_loaded, m_total);
    190         m_total = 0;
    191         m_loaded = 0;
    192     }
    193     stop();
    194 }
    195 
    196 void XMLHttpRequestProgressEventThrottle::resume()
    197 {
    198     ASSERT(!m_loaded);
    199     ASSERT(!m_total);
    200 
    201     if (m_deferredEvents.isEmpty() && !m_deferredProgressEvent) {
    202         m_deferEvents = false;
    203         return;
    204     }
    205 
    206     // Do not dispatch events inline here, since ScriptExecutionContext is iterating over
    207     // the list of active DOM objects to resume them, and any activated JS event-handler
    208     // could insert new active DOM objects to the list.
    209     // m_deferEvents is kept true until all deferred events have been dispatched.
    210     m_dispatchDeferredEventsTimer.startOneShot(0);
    211 }
    212 
    213 } // namespace WebCore
    214