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(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(EventTypeNames::progress, 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(EventTypeNames::progress, 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 || progressEventAction == FlushDeferredProgressEvent) { 81 if (!flushDeferredProgressEvent() && progressEventAction == FlushProgressEvent) 82 deliverProgressEvent(); 83 } 84 85 dispatchEvent(event); 86 } 87 88 void XMLHttpRequestProgressEventThrottle::dispatchEvent(PassRefPtr<Event> event) 89 { 90 ASSERT(event); 91 if (m_deferEvents) { 92 if (m_deferredEvents.size() > 1 && event->type() == EventTypeNames::readystatechange && event->type() == m_deferredEvents.last()->type()) { 93 // Readystatechange events are state-less so avoid repeating two identical events in a row on resume. 94 return; 95 } 96 m_deferredEvents.append(event); 97 } else 98 m_target->dispatchEvent(event); 99 } 100 101 void XMLHttpRequestProgressEventThrottle::dispatchEventAndLoadEnd(const AtomicString& type, bool lengthComputable, unsigned long long bytesSent, unsigned long long total) 102 { 103 ASSERT(type == EventTypeNames::load || type == EventTypeNames::abort || type == EventTypeNames::error || type == EventTypeNames::timeout); 104 105 dispatchEvent(XMLHttpRequestProgressEvent::create(type, lengthComputable, bytesSent, total)); 106 dispatchEvent(XMLHttpRequestProgressEvent::create(EventTypeNames::loadend, lengthComputable, bytesSent, total)); 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 = 0; 115 return true; 116 } 117 return false; 118 } 119 120 void XMLHttpRequestProgressEventThrottle::deliverProgressEvent() 121 { 122 if (!hasEventToDispatch()) 123 return; 124 125 PassRefPtr<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 Vector<RefPtr<Event> > deferredEvents; 143 m_deferredEvents.swap(deferredEvents); 144 145 RefPtr<Event> deferredProgressEvent = m_deferredProgressEvent; 146 m_deferredProgressEvent = 0; 147 148 Vector<RefPtr<Event> >::const_iterator it = deferredEvents.begin(); 149 const Vector<RefPtr<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); 217 } 218 219 } // namespace WebCore 220