ExceptionCode enumeration should use modern exception names
[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()
51 {
52 }
53
54 void XMLHttpRequestProgressEventThrottle::dispatchThrottledProgressEvent(bool lengthComputable, unsigned long long loaded, unsigned long long total)
55 {
56     m_lengthComputable = lengthComputable;
57     m_loaded = loaded;
58     m_total = total;
59
60     if (!m_target->hasEventListeners(eventNames().progressEvent))
61         return;
62     
63     if (m_deferEvents) {
64         // Only store the latest progress event while suspended.
65         m_deferredProgressEvent = XMLHttpRequestProgressEvent::create(eventNames().progressEvent, lengthComputable, loaded, total);
66         return;
67     }
68
69     if (!isActive()) {
70         // The timer is not active so the least frequent event for now is every byte. Just dispatch the event.
71
72         // We should not have any throttled progress event.
73         ASSERT(!m_hasThrottledProgressEvent);
74
75         dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().progressEvent, lengthComputable, loaded, total));
76         startRepeating(minimumProgressEventDispatchingInterval);
77         m_hasThrottledProgressEvent = false;
78         return;
79     }
80
81     // The timer is already active so minimumProgressEventDispatchingInterval is the least frequent event.
82     m_hasThrottledProgressEvent = true;
83 }
84
85 void XMLHttpRequestProgressEventThrottle::dispatchReadyStateChangeEvent(Event& event, ProgressEventAction progressEventAction)
86 {
87     if (progressEventAction == FlushProgressEvent)
88         flushProgressEvent();
89
90     dispatchEvent(event);
91 }
92
93 void XMLHttpRequestProgressEventThrottle::dispatchEvent(Event& event)
94 {
95     if (m_deferEvents) {
96         if (m_deferredEvents.size() > 1 && event.type() == eventNames().readystatechangeEvent && event.type() == m_deferredEvents.last()->type()) {
97             // Readystatechange events are state-less so avoid repeating two identical events in a row on resume.
98             return;
99         }
100         m_deferredEvents.append(event);
101     } else
102         m_target->dispatchEvent(event);
103 }
104
105 void XMLHttpRequestProgressEventThrottle::dispatchProgressEvent(const AtomicString& type)
106 {
107     ASSERT(type == eventNames().loadstartEvent || type == eventNames().progressEvent || type == eventNames().loadEvent || type == eventNames().loadendEvent || type == eventNames().abortEvent || type == eventNames().errorEvent || type == eventNames().timeoutEvent);
108
109     if (type == eventNames().loadstartEvent) {
110         m_lengthComputable = false;
111         m_loaded = 0;
112         m_total = 0;
113     }
114
115     if (m_target->hasEventListeners(type))
116         dispatchEvent(XMLHttpRequestProgressEvent::create(type, m_lengthComputable, m_loaded, m_total));
117 }
118
119 void XMLHttpRequestProgressEventThrottle::flushProgressEvent()
120 {
121     if (m_deferEvents && m_deferredProgressEvent) {
122         // Move the progress event to the queue, to get it in the right order on resume.
123         m_deferredEvents.append(m_deferredProgressEvent.releaseNonNull());
124         return;
125     }
126
127     if (!hasEventToDispatch())
128         return;
129     Ref<Event> event = XMLHttpRequestProgressEvent::create(eventNames().progressEvent, m_lengthComputable, m_loaded, m_total);
130     m_hasThrottledProgressEvent = false;
131
132     // We stop the timer as this is called when no more events are supposed to occur.
133     stop();
134
135     dispatchEvent(WTFMove(event));
136 }
137
138 void XMLHttpRequestProgressEventThrottle::dispatchDeferredEvents()
139 {
140     ASSERT(m_deferEvents);
141     m_deferEvents = false;
142
143     // Take over the deferred events before dispatching them which can potentially add more.
144     auto deferredEvents = WTFMove(m_deferredEvents);
145
146     RefPtr<Event> deferredProgressEvent = WTFMove(m_deferredProgressEvent);
147
148     for (auto& deferredEvent : deferredEvents)
149         dispatchEvent(deferredEvent);
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_s);
212 }
213
214 } // namespace WebCore