Unreviewed, rolling out r161051.
[WebKit-https.git] / Source / WebCore / xml / XMLHttpRequestProgressEventThrottle.cpp
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 "XMLHttpRequestProgressEventThrottle.h"
29
30 #include "EventTarget.h"
31 #include "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