Add WTF::move()
[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_hasThrottledProgressEvent(false)
40     , m_lengthComputable(false)
41     , m_loaded(0)
42     , m_total(0)
43     , m_deferEvents(false)
44     , m_dispatchDeferredEventsTimer(this, &XMLHttpRequestProgressEventThrottle::dispatchDeferredEvents)
45 {
46     ASSERT(target);
47 }
48
49 XMLHttpRequestProgressEventThrottle::~XMLHttpRequestProgressEventThrottle()
50 {
51 }
52
53 void XMLHttpRequestProgressEventThrottle::dispatchThrottledProgressEvent(bool lengthComputable, unsigned long long loaded, unsigned long long total)
54 {
55     m_lengthComputable = lengthComputable;
56     m_loaded = loaded;
57     m_total = total;
58     
59     if (m_deferEvents) {
60         // Only store the latest progress event while suspended.
61         m_deferredProgressEvent = XMLHttpRequestProgressEvent::create(eventNames().progressEvent, lengthComputable, loaded, total);
62         return;
63     }
64
65     if (!isActive()) {
66         // The timer is not active so the least frequent event for now is every byte.
67         // Just go ahead and dispatch the event.
68
69         // We should not have any throttled progress event.
70         ASSERT(!m_hasThrottledProgressEvent);
71
72         dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().progressEvent, lengthComputable, loaded, total));
73         startRepeating(minimumProgressEventDispatchingIntervalInSeconds);
74         m_hasThrottledProgressEvent = false;
75         return;
76     }
77
78     // The timer is already active so minimumProgressEventDispatchingIntervalInSeconds is the least frequent event.
79     m_hasThrottledProgressEvent = true;
80 }
81
82 void XMLHttpRequestProgressEventThrottle::dispatchReadyStateChangeEvent(PassRefPtr<Event> event, ProgressEventAction progressEventAction)
83 {
84     if (progressEventAction == FlushProgressEvent)
85         flushProgressEvent();
86
87     dispatchEvent(event);
88 }
89
90 void XMLHttpRequestProgressEventThrottle::dispatchEvent(PassRefPtr<Event> event)
91 {
92     ASSERT(event);
93     if (m_deferEvents) {
94         if (m_deferredEvents.size() > 1 && event->type() == eventNames().readystatechangeEvent && event->type() == m_deferredEvents.last()->type()) {
95             // Readystatechange events are state-less so avoid repeating two identical events in a row on resume.
96             return;
97         }
98         m_deferredEvents.append(event);
99     } else
100         m_target->dispatchEvent(event);
101 }
102
103 void XMLHttpRequestProgressEventThrottle::dispatchProgressEvent(const AtomicString &type)
104 {
105     ASSERT(type == eventNames().loadstartEvent || type == eventNames().progressEvent || type == eventNames().loadEvent || type == eventNames().loadendEvent || type == eventNames().abortEvent || type == eventNames().errorEvent || type == eventNames().timeoutEvent);
106
107     if (type == eventNames().loadstartEvent) {
108         m_lengthComputable = false;
109         m_loaded = 0;
110         m_total = 0;
111     }
112
113     dispatchEvent(XMLHttpRequestProgressEvent::create(type, m_lengthComputable, m_loaded, m_total));
114 }
115
116 void XMLHttpRequestProgressEventThrottle::flushProgressEvent()
117 {
118     if (m_deferEvents && m_deferredProgressEvent) {
119         // Move the progress event to the queue, to get it in the right order on resume.
120         m_deferredEvents.append(m_deferredProgressEvent);
121         m_deferredProgressEvent = 0;
122         return;
123     }
124
125     if (!hasEventToDispatch())
126         return;
127     PassRefPtr<Event> event = XMLHttpRequestProgressEvent::create(eventNames().progressEvent, m_lengthComputable, m_loaded, m_total);
128     m_hasThrottledProgressEvent = false;
129
130     // We stop the timer as this is called when no more events are supposed to occur.
131     stop();
132
133     dispatchEvent(event);
134 }
135
136 void XMLHttpRequestProgressEventThrottle::dispatchDeferredEvents(Timer<XMLHttpRequestProgressEventThrottle>* timer)
137 {
138     ASSERT_UNUSED(timer, timer == &m_dispatchDeferredEventsTimer);
139     ASSERT(m_deferEvents);
140     m_deferEvents = false;
141
142     // Take over the deferred events before dispatching them which can potentially add more.
143     auto deferredEvents = WTF::move(m_deferredEvents);
144
145     RefPtr<Event> deferredProgressEvent = m_deferredProgressEvent;
146     m_deferredProgressEvent = nullptr;
147
148     for (auto& deferredEvent : deferredEvents)
149         dispatchEvent(deferredEvent.release());
150
151     // The progress event will be in the m_deferredEvents vector if the load was finished while suspended.
152     // If not, just send the most up-to-date progress on resume.
153     if (deferredProgressEvent)
154         dispatchEvent(deferredProgressEvent);
155 }
156
157 void XMLHttpRequestProgressEventThrottle::fired()
158 {
159     ASSERT(isActive());
160     if (!hasEventToDispatch()) {
161         // No progress event was queued since the previous dispatch, we can safely stop the timer.
162         stop();
163         return;
164     }
165
166     dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().progressEvent, m_lengthComputable, m_loaded, m_total));
167     m_hasThrottledProgressEvent = false;
168 }
169
170 bool XMLHttpRequestProgressEventThrottle::hasEventToDispatch() const
171 {
172     return m_hasThrottledProgressEvent && isActive();
173 }
174
175 void XMLHttpRequestProgressEventThrottle::suspend()
176 {
177     // If re-suspended before deferred events have been dispatched, just stop the dispatch
178     // and continue the last suspend.
179     if (m_dispatchDeferredEventsTimer.isActive()) {
180         ASSERT(m_deferEvents);
181         m_dispatchDeferredEventsTimer.stop();
182         return;
183     }
184     ASSERT(!m_deferredProgressEvent);
185     ASSERT(m_deferredEvents.isEmpty());
186     ASSERT(!m_deferEvents);
187
188     m_deferEvents = true;
189     // If we have a progress event waiting to be dispatched,
190     // just defer it.
191     if (hasEventToDispatch()) {
192         m_deferredProgressEvent = XMLHttpRequestProgressEvent::create(eventNames().progressEvent, m_lengthComputable, m_loaded, m_total);
193         m_hasThrottledProgressEvent = false;
194     }
195     stop();
196 }
197
198 void XMLHttpRequestProgressEventThrottle::resume()
199 {
200     ASSERT(!m_hasThrottledProgressEvent);
201
202     if (m_deferredEvents.isEmpty() && !m_deferredProgressEvent) {
203         m_deferEvents = false;
204         return;
205     }
206
207     // Do not dispatch events inline here, since ScriptExecutionContext is iterating over
208     // the list of active DOM objects to resume them, and any activated JS event-handler
209     // could insert new active DOM objects to the list.
210     // m_deferEvents is kept true until all deferred events have been dispatched.
211     m_dispatchDeferredEventsTimer.startOneShot(0);
212 }
213
214 } // namespace WebCore