• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 Julien Chaffraix <jchaffraix@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 
XMLHttpRequestProgressEventThrottle(EventTarget * target)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 
~XMLHttpRequestProgressEventThrottle()47 XMLHttpRequestProgressEventThrottle::~XMLHttpRequestProgressEventThrottle()
48 {
49 }
50 
dispatchProgressEvent(const AtomicString & type,bool lengthComputable,unsigned long long loaded,unsigned long long total)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 
dispatchReadyStateChangeEvent(PassRefPtrWillBeRawPtr<Event> event,ProgressEventAction progressEventAction)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 
dispatchEvent(PassRefPtrWillBeRawPtr<Event> event)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 
flushDeferredProgressEvent()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 
deliverProgressEvent()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 
dispatchDeferredEvents(Timer<XMLHttpRequestProgressEventThrottle> * timer)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 
fired()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 
hasEventToDispatch() const173 bool XMLHttpRequestProgressEventThrottle::hasEventToDispatch() const
174 {
175     return (m_total || m_loaded) && isActive();
176 }
177 
suspend()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 
resume()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 
trace(Visitor * visitor)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