Numeric identifiers of events are not guaranteed to be unique
[WebKit-https.git] / Source / WebCore / page / DOMTimer.cpp
1 /*
2  * Copyright (C) 2008 Apple Inc. All Rights Reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  *
25  */
26
27 #include "config.h"
28 #include "DOMTimer.h"
29
30 #include "InspectorInstrumentation.h"
31 #include "ScheduledAction.h"
32 #include "ScriptExecutionContext.h"
33 #include "WebCoreMemoryInstrumentation.h"
34 #include <wtf/CurrentTime.h>
35 #include <wtf/HashSet.h>
36 #include <wtf/StdLibExtras.h>
37
38 using namespace std;
39
40 namespace WebCore {
41
42 static const int maxIntervalForUserGestureForwarding = 1000; // One second matches Gecko.
43 static const int maxTimerNestingLevel = 5;
44 static const double oneMillisecond = 0.001;
45
46 static int timerNestingLevel = 0;
47     
48 static inline bool shouldForwardUserGesture(int interval, int nestingLevel)
49 {
50     return UserGestureIndicator::processingUserGesture()
51         && interval <= maxIntervalForUserGestureForwarding
52         && nestingLevel == 1; // Gestures should not be forwarded to nested timers.
53 }
54
55 DOMTimer::DOMTimer(ScriptExecutionContext* context, PassOwnPtr<ScheduledAction> action, int interval, bool singleShot)
56     : SuspendableTimer(context)
57     , m_nestingLevel(timerNestingLevel + 1)
58     , m_action(action)
59     , m_originalInterval(interval)
60 {
61     if (shouldForwardUserGesture(interval, m_nestingLevel))
62         m_userGestureToken = UserGestureIndicator::currentToken();
63
64     // Keep asking for the next id until we're given one that we don't already have.
65     do {
66         m_timeoutId = context->circularSequentialID();
67     } while (!context->addTimeout(m_timeoutId, this));
68
69     double intervalMilliseconds = intervalClampedToMinimum(interval, context->minimumTimerInterval());
70     if (singleShot)
71         startOneShot(intervalMilliseconds);
72     else
73         startRepeating(intervalMilliseconds);
74 }
75
76 DOMTimer::~DOMTimer()
77 {
78     if (scriptExecutionContext())
79         scriptExecutionContext()->removeTimeout(m_timeoutId);
80 }
81
82 int DOMTimer::install(ScriptExecutionContext* context, PassOwnPtr<ScheduledAction> action, int timeout, bool singleShot)
83 {
84     // DOMTimer constructor links the new timer into a list of ActiveDOMObjects held by the 'context'.
85     // The timer is deleted when context is deleted (DOMTimer::contextDestroyed) or explicitly via DOMTimer::removeById(),
86     // or if it is a one-time timer and it has fired (DOMTimer::fired).
87     DOMTimer* timer = new DOMTimer(context, action, timeout, singleShot);
88
89     timer->suspendIfNeeded();
90     InspectorInstrumentation::didInstallTimer(context, timer->m_timeoutId, timeout, singleShot);
91
92     return timer->m_timeoutId;
93 }
94
95 void DOMTimer::removeById(ScriptExecutionContext* context, int timeoutId)
96 {
97     // timeout IDs have to be positive, and 0 and -1 are unsafe to
98     // even look up since they are the empty and deleted value
99     // respectively
100     if (timeoutId <= 0)
101         return;
102
103     InspectorInstrumentation::didRemoveTimer(context, timeoutId);
104
105     delete context->findTimeout(timeoutId);
106 }
107
108 void DOMTimer::fired()
109 {
110     ScriptExecutionContext* context = scriptExecutionContext();
111     timerNestingLevel = m_nestingLevel;
112     ASSERT(!context->activeDOMObjectsAreSuspended());
113     // Only the first execution of a multi-shot timer should get an affirmative user gesture indicator.
114     UserGestureIndicator gestureIndicator(m_userGestureToken.release());
115
116     InspectorInstrumentationCookie cookie = InspectorInstrumentation::willFireTimer(context, m_timeoutId);
117
118     // Simple case for non-one-shot timers.
119     if (isActive()) {
120         double minimumInterval = context->minimumTimerInterval();
121         if (repeatInterval() && repeatInterval() < minimumInterval) {
122             m_nestingLevel++;
123             if (m_nestingLevel >= maxTimerNestingLevel)
124                 augmentRepeatInterval(minimumInterval - repeatInterval());
125         }
126
127         // No access to member variables after this point, it can delete the timer.
128         m_action->execute(context);
129
130         InspectorInstrumentation::didFireTimer(cookie);
131
132         return;
133     }
134
135     // Delete timer before executing the action for one-shot timers.
136     OwnPtr<ScheduledAction> action = m_action.release();
137
138     // No access to member variables after this point.
139     delete this;
140
141     action->execute(context);
142
143     InspectorInstrumentation::didFireTimer(cookie);
144
145     timerNestingLevel = 0;
146 }
147
148 void DOMTimer::contextDestroyed()
149 {
150     SuspendableTimer::contextDestroyed();
151     delete this;
152 }
153
154 void DOMTimer::stop()
155 {
156     SuspendableTimer::stop();
157     // Need to release JS objects potentially protected by ScheduledAction
158     // because they can form circular references back to the ScriptExecutionContext
159     // which will cause a memory leak.
160     m_action.clear();
161 }
162
163 void DOMTimer::adjustMinimumTimerInterval(double oldMinimumTimerInterval)
164 {
165     if (m_nestingLevel < maxTimerNestingLevel)
166         return;
167
168     double newMinimumInterval = scriptExecutionContext()->minimumTimerInterval();
169     double newClampedInterval = intervalClampedToMinimum(m_originalInterval, newMinimumInterval);
170
171     if (repeatInterval()) {
172         augmentRepeatInterval(newClampedInterval - repeatInterval());
173         return;
174     }
175
176     double previousClampedInterval = intervalClampedToMinimum(m_originalInterval, oldMinimumTimerInterval);
177     augmentFireInterval(newClampedInterval - previousClampedInterval);
178 }
179
180 double DOMTimer::intervalClampedToMinimum(int timeout, double minimumTimerInterval) const
181 {
182     double intervalMilliseconds = max(oneMillisecond, timeout * oneMillisecond);
183
184     if (intervalMilliseconds < minimumTimerInterval && m_nestingLevel >= maxTimerNestingLevel)
185         intervalMilliseconds = minimumTimerInterval;
186     return intervalMilliseconds;
187 }
188
189 double DOMTimer::alignedFireTime(double fireTime) const
190 {
191     double alignmentInterval = scriptExecutionContext()->timerAlignmentInterval();
192     if (alignmentInterval) {
193         double currentTime = monotonicallyIncreasingTime();
194         if (fireTime <= currentTime)
195             return fireTime;
196
197         double alignedTime = ceil(fireTime / alignmentInterval) * alignmentInterval;
198         return alignedTime;
199     }
200
201     return fireTime;
202 }
203
204 void DOMTimer::reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const
205 {
206     MemoryClassInfo info(memoryObjectInfo, this, WebCoreMemoryTypes::DOM);
207     SuspendableTimer::reportMemoryUsage(memoryObjectInfo);
208     info.addMember(m_action, "action");
209     info.addMember(m_userGestureToken, "userGestureToken");
210 }
211
212 } // namespace WebCore