namespace blink {
+class XMLHttpRequestProgressEventThrottle::DeferredEvent {
+public:
+ DeferredEvent() { clear(); }
+ void set(bool lengthComputable, unsigned long long loaded, unsigned long long total)
+ {
+ m_isDeferred = true;
+ m_lengthComputable = lengthComputable;
+ m_loaded = loaded;
+ m_total = total;
+ }
+ void clear()
+ {
+ m_isDeferred = false;
+ m_lengthComputable = false;
+ m_loaded = 0;
+ m_total = 0;
+ }
+ bool isDeferred() const { return m_isDeferred; }
+ bool lengthComputable() const { return m_lengthComputable; }
+ unsigned long long loaded() const { return m_loaded; }
+ unsigned long long total() const { return m_total; }
+
+private:
+ unsigned long long m_loaded;
+ unsigned long long m_total;
+ bool m_isDeferred;
+ bool m_lengthComputable;
+};
+
const double XMLHttpRequestProgressEventThrottle::minimumProgressEventDispatchingIntervalInSeconds = .05; // 50 ms per specification.
XMLHttpRequestProgressEventThrottle::XMLHttpRequestProgressEventThrottle(EventTarget* target)
: m_target(target)
- , m_loaded(0)
- , m_total(0)
- , m_deferEvents(false)
- , m_dispatchDeferredEventsTimer(this, &XMLHttpRequestProgressEventThrottle::dispatchDeferredEvents)
+ , m_deferred(adoptPtr(new DeferredEvent))
{
ASSERT(target);
}
void XMLHttpRequestProgressEventThrottle::dispatchProgressEvent(const AtomicString& type, bool lengthComputable, unsigned long long loaded, unsigned long long total)
{
- RefPtrWillBeRawPtr<XMLHttpRequestProgressEvent> progressEvent = XMLHttpRequestProgressEvent::create(type, lengthComputable, loaded, total);
-
+ // Given that ResourceDispatcher doesn't deliver an event when suspended,
+ // we don't have to worry about event dispatching while suspended.
if (type != EventTypeNames::progress) {
- dispatchEvent(progressEvent);
+ m_target->dispatchEvent(XMLHttpRequestProgressEvent::create(type, lengthComputable, loaded, total));
return;
}
- if (m_deferEvents) {
- // Only store the latest progress event while suspended.
- m_deferredProgressEvent = progressEvent;
- return;
- }
-
- if (!isActive()) {
- // The timer is not active so the least frequent event for now is every byte.
- // Just go ahead and dispatch the event.
-
- // We should not have any pending loaded & total information from a previous run.
- ASSERT(!m_loaded);
- ASSERT(!m_total);
-
- dispatchEvent(progressEvent);
- startRepeating(minimumProgressEventDispatchingIntervalInSeconds, FROM_HERE);
- return;
- }
-
- // The timer is already active so minimumProgressEventDispatchingIntervalInSeconds is the least frequent event.
- m_lengthComputable = lengthComputable;
- m_loaded = loaded;
- m_total = total;
-}
-
-void XMLHttpRequestProgressEventThrottle::dispatchReadyStateChangeEvent(PassRefPtrWillBeRawPtr<Event> event, ProgressEventAction progressEventAction)
-{
- if (progressEventAction == FlushProgressEvent || progressEventAction == FlushDeferredProgressEvent) {
- if (!flushDeferredProgressEvent() && progressEventAction == FlushProgressEvent)
- deliverProgressEvent();
- }
-
- dispatchEvent(event);
-}
-
-void XMLHttpRequestProgressEventThrottle::dispatchEvent(PassRefPtrWillBeRawPtr<Event> event)
-{
- ASSERT(event);
- if (m_deferEvents) {
- if (m_deferredEvents.size() > 1 && event->type() == EventTypeNames::readystatechange && event->type() == m_deferredEvents.last()->type()) {
- // Readystatechange events are state-less so avoid repeating two identical events in a row on resume.
- return;
- }
- m_deferredEvents.append(event);
+ if (isActive()) {
+ m_deferred->set(lengthComputable, loaded, total);
} else {
- m_target->dispatchEvent(event);
+ m_target->dispatchEvent(XMLHttpRequestProgressEvent::create(type, lengthComputable, loaded, total));
+ startOneShot(minimumProgressEventDispatchingIntervalInSeconds, FROM_HERE);
}
}
-bool XMLHttpRequestProgressEventThrottle::flushDeferredProgressEvent()
+void XMLHttpRequestProgressEventThrottle::dispatchReadyStateChangeEvent(PassRefPtrWillBeRawPtr<Event> event, DeferredEventAction action)
{
- if (m_deferEvents && m_deferredProgressEvent) {
- // Move the progress event to the queue, to get it in the right order on resume.
- m_deferredEvents.append(m_deferredProgressEvent);
- m_deferredProgressEvent = nullptr;
- return true;
+ // Given that ResourceDispatcher doesn't deliver an event when suspended,
+ // we don't have to worry about event dispatching while suspended.
+ if (action == Flush) {
+ dispatchDeferredEvent();
+ stop();
+ } else if (action == Clear) {
+ m_deferred->clear();
+ stop();
}
- return false;
-}
-void XMLHttpRequestProgressEventThrottle::deliverProgressEvent()
-{
- if (!hasEventToDispatch())
- return;
-
- RefPtrWillBeRawPtr<Event> event = XMLHttpRequestProgressEvent::create(EventTypeNames::progress, m_lengthComputable, m_loaded, m_total);
- m_loaded = 0;
- m_total = 0;
-
- // We stop the timer as this is called when no more events are supposed to occur.
- stop();
-
- dispatchEvent(event);
+ m_target->dispatchEvent(event);
}
-void XMLHttpRequestProgressEventThrottle::dispatchDeferredEvents(Timer<XMLHttpRequestProgressEventThrottle>* timer)
+void XMLHttpRequestProgressEventThrottle::dispatchDeferredEvent()
{
- ASSERT_UNUSED(timer, timer == &m_dispatchDeferredEventsTimer);
- ASSERT(m_deferEvents);
- m_deferEvents = false;
-
- // Take over the deferred events before dispatching them which can potentially add more.
- WillBeHeapVector<RefPtrWillBeMember<Event> > deferredEvents;
- m_deferredEvents.swap(deferredEvents);
-
- RefPtrWillBeRawPtr<Event> deferredProgressEvent = m_deferredProgressEvent;
- m_deferredProgressEvent = nullptr;
-
- WillBeHeapVector<RefPtrWillBeMember<Event> >::const_iterator it = deferredEvents.begin();
- const WillBeHeapVector<RefPtrWillBeMember<Event> >::const_iterator end = deferredEvents.end();
- for (; it != end; ++it)
- dispatchEvent(*it);
-
- // The progress event will be in the m_deferredEvents vector if the load was finished while suspended.
- // If not, just send the most up-to-date progress on resume.
- if (deferredProgressEvent)
- dispatchEvent(deferredProgressEvent);
+ if (m_deferred->isDeferred()) {
+ m_target->dispatchEvent(XMLHttpRequestProgressEvent::create(EventTypeNames::progress, m_deferred->lengthComputable(), m_deferred->loaded(), m_deferred->total()));
+ m_deferred->clear();
+ }
}
void XMLHttpRequestProgressEventThrottle::fired()
{
- ASSERT(isActive());
- if (!hasEventToDispatch()) {
- // No progress event was queued since the previous dispatch, we can safely stop the timer.
- stop();
+ if (!m_deferred->isDeferred()) {
+ // No "progress" event was queued since the previous dispatch, we can
+ // safely stop the timer.
return;
}
- dispatchEvent(XMLHttpRequestProgressEvent::create(EventTypeNames::progress, m_lengthComputable, m_loaded, m_total));
- m_total = 0;
- m_loaded = 0;
-}
+ dispatchDeferredEvent();
-bool XMLHttpRequestProgressEventThrottle::hasEventToDispatch() const
-{
- return (m_total || m_loaded) && isActive();
+ // Watch if another "progress" ProgressEvent arrives in the next 50ms.
+ startOneShot(minimumProgressEventDispatchingIntervalInSeconds, FROM_HERE);
}
void XMLHttpRequestProgressEventThrottle::suspend()
{
- // If re-suspended before deferred events have been dispatched, just stop the dispatch
- // and continue the last suspend.
- if (m_dispatchDeferredEventsTimer.isActive()) {
- ASSERT(m_deferEvents);
- m_dispatchDeferredEventsTimer.stop();
- return;
- }
- ASSERT(!m_deferredProgressEvent);
- ASSERT(m_deferredEvents.isEmpty());
- ASSERT(!m_deferEvents);
-
- m_deferEvents = true;
- // If we have a progress event waiting to be dispatched,
- // just defer it.
- if (hasEventToDispatch()) {
- m_deferredProgressEvent = XMLHttpRequestProgressEvent::create(EventTypeNames::progress, m_lengthComputable, m_loaded, m_total);
- m_total = 0;
- m_loaded = 0;
- }
stop();
}
void XMLHttpRequestProgressEventThrottle::resume()
{
- ASSERT(!m_loaded);
- ASSERT(!m_total);
-
- if (m_deferredEvents.isEmpty() && !m_deferredProgressEvent) {
- m_deferEvents = false;
+ if (!m_deferred->isDeferred())
return;
- }
- // Do not dispatch events inline here, since ExecutionContext is iterating over
- // the list of active DOM objects to resume them, and any activated JS event-handler
- // could insert new active DOM objects to the list.
- // m_deferEvents is kept true until all deferred events have been dispatched.
- m_dispatchDeferredEventsTimer.startOneShot(0, FROM_HERE);
+ // Do not dispatch events inline here, since ExecutionContext is iterating
+ // over the list of active DOM objects to resume them, and any activated JS
+ // event-handler could insert new active DOM objects to the list.
+ startOneShot(0, FROM_HERE);
}
void XMLHttpRequestProgressEventThrottle::trace(Visitor* visitor)
{
visitor->trace(m_target);
- visitor->trace(m_deferredProgressEvent);
- visitor->trace(m_deferredEvents);
}
} // namespace blink