Use "= default" to denote default constructor or destructor
[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 "EventNames.h"
31 #include "EventTarget.h"
32 #include "XMLHttpRequestProgressEvent.h"
33
34 namespace WebCore {
35
36 const Seconds XMLHttpRequestProgressEventThrottle::minimumProgressEventDispatchingInterval { 50_ms }; // 50 ms per specification.
37
38 XMLHttpRequestProgressEventThrottle::XMLHttpRequestProgressEventThrottle(EventTarget* target)
39     : m_target(target)
40     , m_hasThrottledProgressEvent(false)
41     , m_lengthComputable(false)
42     , m_loaded(0)
43     , m_total(0)
44     , m_deferEvents(false)
45     , m_dispatchDeferredEventsTimer(*this, &XMLHttpRequestProgressEventThrottle::dispatchDeferredEvents)
46 {
47     ASSERT(target);
48 }
49
50 XMLHttpRequestProgressEventThrottle::~XMLHttpRequestProgressEventThrottle() = default;
51
52 void XMLHttpRequestProgressEventThrottle::dispatchThrottledProgressEvent(bool lengthComputable, unsigned long long loaded, unsigned long long total)
53 {
54     m_lengthComputable = lengthComputable;
55     m_loaded = loaded;
56     m_total = total;
57
58     if (!m_target->hasEventListeners(eventNames().progressEvent))
59         return;
60     
61     if (m_deferEvents) {
62         // Only store the latest progress event while suspended.
63         m_deferredProgressEvent = XMLHttpRequestProgressEvent::create(eventNames().progressEvent, lengthComputable, loaded, total);
64         return;
65     }
66
67     if (!isActive()) {
68         // The timer is not active so the least frequent event for now is every byte. Just dispatch the event.
69
70         // We should not have any throttled progress event.
71         ASSERT(!m_hasThrottledProgressEvent);
72
73         dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().progressEvent, lengthComputable, loaded, total));
74         startRepeating(minimumProgressEventDispatchingInterval);
75         m_hasThrottledProgressEvent = false;
76         return;
77     }
78
79     // The timer is already active so minimumProgressEventDispatchingInterval is the least frequent event.
80     m_hasThrottledProgressEvent = true;
81 }
82
83 void XMLHttpRequestProgressEventThrottle::dispatchReadyStateChangeEvent(Event& event, ProgressEventAction progressEventAction)
84 {
85     if (progressEventAction == FlushProgressEvent)
86         flushProgressEvent();
87
88     dispatchEvent(event);
89 }
90
91 void XMLHttpRequestProgressEventThrottle::dispatchEvent(Event& event)
92 {
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     if (m_target->hasEventListeners(type))
114         dispatchEvent(XMLHttpRequestProgressEvent::create(type, m_lengthComputable, m_loaded, m_total));
115 }
116
117 void XMLHttpRequestProgressEventThrottle::flushProgressEvent()
118 {
119     if (m_deferEvents && m_deferredProgressEvent) {
120         // Move the progress event to the queue, to get it in the right order on resume.
121         m_deferredEvents.append(m_deferredProgressEvent.releaseNonNull());
122         return;
123     }
124
125     if (!hasEventToDispatch())
126         return;
127     Ref<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(WTFMove(event));
134 }
135
136 void XMLHttpRequestProgressEventThrottle::dispatchDeferredEvents()
137 {
138     ASSERT(m_deferEvents);
139     m_deferEvents = false;
140
141     // Take over the deferred events before dispatching them which can potentially add more.
142     auto deferredEvents = WTFMove(m_deferredEvents);
143
144     RefPtr<Event> deferredProgressEvent = WTFMove(m_deferredProgressEvent);
145
146     for (auto& deferredEvent : deferredEvents)
147         dispatchEvent(deferredEvent);
148
149     // The progress event will be in the m_deferredEvents vector if the load was finished while suspended.
150     // If not, just send the most up-to-date progress on resume.
151     if (deferredProgressEvent)
152         dispatchEvent(*deferredProgressEvent);
153 }
154
155 void XMLHttpRequestProgressEventThrottle::fired()
156 {
157     ASSERT(isActive());
158     if (!hasEventToDispatch()) {
159         // No progress event was queued since the previous dispatch, we can safely stop the timer.
160         stop();
161         return;
162     }
163
164     dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().progressEvent, m_lengthComputable, m_loaded, m_total));
165     m_hasThrottledProgressEvent = false;
166 }
167
168 bool XMLHttpRequestProgressEventThrottle::hasEventToDispatch() const
169 {
170     return m_hasThrottledProgressEvent && isActive();
171 }
172
173 void XMLHttpRequestProgressEventThrottle::suspend()
174 {
175     // If re-suspended before deferred events have been dispatched, just stop the dispatch
176     // and continue the last suspend.
177     if (m_dispatchDeferredEventsTimer.isActive()) {
178         ASSERT(m_deferEvents);
179         m_dispatchDeferredEventsTimer.stop();
180         return;
181     }
182     ASSERT(!m_deferredProgressEvent);
183     ASSERT(m_deferredEvents.isEmpty());
184     ASSERT(!m_deferEvents);
185
186     m_deferEvents = true;
187     // If we have a progress event waiting to be dispatched,
188     // just defer it.
189     if (hasEventToDispatch()) {
190         m_deferredProgressEvent = XMLHttpRequestProgressEvent::create(eventNames().progressEvent, m_lengthComputable, m_loaded, m_total);
191         m_hasThrottledProgressEvent = false;
192     }
193     stop();
194 }
195
196 void XMLHttpRequestProgressEventThrottle::resume()
197 {
198     ASSERT(!m_hasThrottledProgressEvent);
199
200     if (m_deferredEvents.isEmpty() && !m_deferredProgressEvent) {
201         m_deferEvents = false;
202         return;
203     }
204
205     // Do not dispatch events inline here, since ScriptExecutionContext is iterating over
206     // the list of active DOM objects to resume them, and any activated JS event-handler
207     // could insert new active DOM objects to the list.
208     // m_deferEvents is kept true until all deferred events have been dispatched.
209     m_dispatchDeferredEventsTimer.startOneShot(0_s);
210 }
211
212 } // namespace WebCore