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