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/events/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(const AtomicString& type, bool lengthComputable, unsigned long long loaded, unsigned long long total)
     52 {
     53     RefPtrWillBeRawPtr<XMLHttpRequestProgressEvent> progressEvent = XMLHttpRequestProgressEvent::create(type, lengthComputable, loaded, total);
     54 
     55     if (type != EventTypeNames::progress) {
     56         dispatchEvent(progressEvent);
     57         return;
     58     }
     59 
     60     if (m_deferEvents) {
     61         // Only store the latest progress event while suspended.
     62         m_deferredProgressEvent = progressEvent;
     63         return;
     64     }
     65 
     66     if (!isActive()) {
     67         // The timer is not active so the least frequent event for now is every byte.
     68         // Just go ahead and dispatch the event.
     69 
     70         // We should not have any pending loaded & total information from a previous run.
     71         ASSERT(!m_loaded);
     72         ASSERT(!m_total);
     73 
     74         dispatchEvent(progressEvent);
     75         startRepeating(minimumProgressEventDispatchingIntervalInSeconds, FROM_HERE);
     76         return;
     77     }
     78 
     79     // The timer is already active so minimumProgressEventDispatchingIntervalInSeconds is the least frequent event.
     80     m_lengthComputable = lengthComputable;
     81     m_loaded = loaded;
     82     m_total = total;
     83 }
     84 
     85 void XMLHttpRequestProgressEventThrottle::dispatchReadyStateChangeEvent(PassRefPtrWillBeRawPtr<Event> event, ProgressEventAction progressEventAction)
     86 {
     87     if (progressEventAction == FlushProgressEvent || progressEventAction == FlushDeferredProgressEvent) {
     88         if (!flushDeferredProgressEvent() && progressEventAction == FlushProgressEvent)
     89             deliverProgressEvent();
     90     }
     91 
     92     dispatchEvent(event);
     93 }
     94 
     95 void XMLHttpRequestProgressEventThrottle::dispatchEvent(PassRefPtrWillBeRawPtr<Event> event)
     96 {
     97     ASSERT(event);
     98     if (m_deferEvents) {
     99         if (m_deferredEvents.size() > 1 && event->type() == EventTypeNames::readystatechange && event->type() == m_deferredEvents.last()->type()) {
    100             // Readystatechange events are state-less so avoid repeating two identical events in a row on resume.
    101             return;
    102         }
    103         m_deferredEvents.append(event);
    104     } else {
    105         m_target->dispatchEvent(event);
    106     }
    107 }
    108 
    109 bool XMLHttpRequestProgressEventThrottle::flushDeferredProgressEvent()
    110 {
    111     if (m_deferEvents && m_deferredProgressEvent) {
    112         // Move the progress event to the queue, to get it in the right order on resume.
    113         m_deferredEvents.append(m_deferredProgressEvent);
    114         m_deferredProgressEvent = nullptr;
    115         return true;
    116     }
    117     return false;
    118 }
    119 
    120 void XMLHttpRequestProgressEventThrottle::deliverProgressEvent()
    121 {
    122     if (!hasEventToDispatch())
    123         return;
    124 
    125     RefPtrWillBeRawPtr<Event> event = XMLHttpRequestProgressEvent::create(EventTypeNames::progress, m_lengthComputable, m_loaded, m_total);
    126     m_loaded = 0;
    127     m_total = 0;
    128 
    129     // We stop the timer as this is called when no more events are supposed to occur.
    130     stop();
    131 
    132     dispatchEvent(event);
    133 }
    134 
    135 void XMLHttpRequestProgressEventThrottle::dispatchDeferredEvents(Timer<XMLHttpRequestProgressEventThrottle>* timer)
    136 {
    137     ASSERT_UNUSED(timer, timer == &m_dispatchDeferredEventsTimer);
    138     ASSERT(m_deferEvents);
    139     m_deferEvents = false;
    140 
    141     // Take over the deferred events before dispatching them which can potentially add more.
    142     WillBeHeapVector<RefPtrWillBeMember<Event> > deferredEvents;
    143     m_deferredEvents.swap(deferredEvents);
    144 
    145     RefPtrWillBeRawPtr<Event> deferredProgressEvent = m_deferredProgressEvent;
    146     m_deferredProgressEvent = nullptr;
    147 
    148     WillBeHeapVector<RefPtrWillBeMember<Event> >::const_iterator it = deferredEvents.begin();
    149     const WillBeHeapVector<RefPtrWillBeMember<Event> >::const_iterator end = deferredEvents.end();
    150     for (; it != end; ++it)
    151         dispatchEvent(*it);
    152 
    153     // The progress event will be in the m_deferredEvents vector if the load was finished while suspended.
    154     // If not, just send the most up-to-date progress on resume.
    155     if (deferredProgressEvent)
    156         dispatchEvent(deferredProgressEvent);
    157 }
    158 
    159 void XMLHttpRequestProgressEventThrottle::fired()
    160 {
    161     ASSERT(isActive());
    162     if (!hasEventToDispatch()) {
    163         // No progress event was queued since the previous dispatch, we can safely stop the timer.
    164         stop();
    165         return;
    166     }
    167 
    168     dispatchEvent(XMLHttpRequestProgressEvent::create(EventTypeNames::progress, m_lengthComputable, m_loaded, m_total));
    169     m_total = 0;
    170     m_loaded = 0;
    171 }
    172 
    173 bool XMLHttpRequestProgressEventThrottle::hasEventToDispatch() const
    174 {
    175     return (m_total || m_loaded) && isActive();
    176 }
    177 
    178 void XMLHttpRequestProgressEventThrottle::suspend()
    179 {
    180     // If re-suspended before deferred events have been dispatched, just stop the dispatch
    181     // and continue the last suspend.
    182     if (m_dispatchDeferredEventsTimer.isActive()) {
    183         ASSERT(m_deferEvents);
    184         m_dispatchDeferredEventsTimer.stop();
    185         return;
    186     }
    187     ASSERT(!m_deferredProgressEvent);
    188     ASSERT(m_deferredEvents.isEmpty());
    189     ASSERT(!m_deferEvents);
    190 
    191     m_deferEvents = true;
    192     // If we have a progress event waiting to be dispatched,
    193     // just defer it.
    194     if (hasEventToDispatch()) {
    195         m_deferredProgressEvent = XMLHttpRequestProgressEvent::create(EventTypeNames::progress, m_lengthComputable, m_loaded, m_total);
    196         m_total = 0;
    197         m_loaded = 0;
    198     }
    199     stop();
    200 }
    201 
    202 void XMLHttpRequestProgressEventThrottle::resume()
    203 {
    204     ASSERT(!m_loaded);
    205     ASSERT(!m_total);
    206 
    207     if (m_deferredEvents.isEmpty() && !m_deferredProgressEvent) {
    208         m_deferEvents = false;
    209         return;
    210     }
    211 
    212     // Do not dispatch events inline here, since ExecutionContext is iterating over
    213     // the list of active DOM objects to resume them, and any activated JS event-handler
    214     // could insert new active DOM objects to the list.
    215     // m_deferEvents is kept true until all deferred events have been dispatched.
    216     m_dispatchDeferredEventsTimer.startOneShot(0, FROM_HERE);
    217 }
    218 
    219 void XMLHttpRequestProgressEventThrottle::trace(Visitor* visitor)
    220 {
    221     visitor->trace(m_target);
    222     visitor->trace(m_deferredProgressEvent);
    223     visitor->trace(m_deferredEvents);
    224 }
    225 
    226 } // namespace WebCore
    227