50233941c395375e9250e72477219173770b8690
[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_timeoutId(context->newUniqueID())
58     , m_nestingLevel(timerNestingLevel + 1)
59     , m_action(action)
60     , m_originalInterval(interval)
61 {
62     if (shouldForwardUserGesture(interval, m_nestingLevel))
63         m_userGestureToken = UserGestureIndicator::currentToken();
64
65     context->addTimeout(m_timeoutId, this);
66
67     double intervalMilliseconds = intervalClampedToMinimum(interval, context->minimumTimerInterval());
68     if (singleShot)
69         startOneShot(intervalMilliseconds);
70     else
71         startRepeating(intervalMilliseconds);
72 }
73
74 DOMTimer::~DOMTimer()
75 {
76     if (scriptExecutionContext())
77         scriptExecutionContext()->removeTimeout(m_timeoutId);
78 }
79
80 int DOMTimer::install(ScriptExecutionContext* context, PassOwnPtr<ScheduledAction> action, int timeout, bool singleShot)
81 {
82     // DOMTimer constructor links the new timer into a list of ActiveDOMObjects held by the 'context'.
83     // The timer is deleted when context is deleted (DOMTimer::contextDestroyed) or explicitly via DOMTimer::removeById(),
84     // or if it is a one-time timer and it has fired (DOMTimer::fired).
85     DOMTimer* timer = new DOMTimer(context, action, timeout, singleShot);
86
87     timer->suspendIfNeeded();
88     InspectorInstrumentation::didInstallTimer(context, timer->m_timeoutId, timeout, singleShot);
89
90     return timer->m_timeoutId;
91 }
92
93 void DOMTimer::removeById(ScriptExecutionContext* context, int timeoutId)
94 {
95     // timeout IDs have to be positive, and 0 and -1 are unsafe to
96     // even look up since they are the empty and deleted value
97     // respectively
98     if (timeoutId <= 0)
99         return;
100
101     InspectorInstrumentation::didRemoveTimer(context, timeoutId);
102
103     delete context->findTimeout(timeoutId);
104 }
105
106 void DOMTimer::fired()
107 {
108     ScriptExecutionContext* context = scriptExecutionContext();
109     timerNestingLevel = m_nestingLevel;
110     ASSERT(!context->activeDOMObjectsAreSuspended());
111     // Only the first execution of a multi-shot timer should get an affirmative user gesture indicator.
112     UserGestureIndicator gestureIndicator(m_userGestureToken.release());
113
114     InspectorInstrumentationCookie cookie = InspectorInstrumentation::willFireTimer(context, m_timeoutId);
115
116     // Simple case for non-one-shot timers.
117     if (isActive()) {
118         double minimumInterval = context->minimumTimerInterval();
119         if (repeatInterval() && repeatInterval() < minimumInterval) {
120             m_nestingLevel++;
121             if (m_nestingLevel >= maxTimerNestingLevel)
122                 augmentRepeatInterval(minimumInterval - repeatInterval());
123         }
124
125         // No access to member variables after this point, it can delete the timer.
126         m_action->execute(context);
127
128         InspectorInstrumentation::didFireTimer(cookie);
129
130         return;
131     }
132
133     // Delete timer before executing the action for one-shot timers.
134     OwnPtr<ScheduledAction> action = m_action.release();
135
136     // No access to member variables after this point.
137     delete this;
138
139     action->execute(context);
140
141     InspectorInstrumentation::didFireTimer(cookie);
142
143     timerNestingLevel = 0;
144 }
145
146 void DOMTimer::contextDestroyed()
147 {
148     SuspendableTimer::contextDestroyed();
149     delete this;
150 }
151
152 void DOMTimer::stop()
153 {
154     SuspendableTimer::stop();
155     // Need to release JS objects potentially protected by ScheduledAction
156     // because they can form circular references back to the ScriptExecutionContext
157     // which will cause a memory leak.
158     m_action.clear();
159 }
160
161 void DOMTimer::adjustMinimumTimerInterval(double oldMinimumTimerInterval)
162 {
163     if (m_nestingLevel < maxTimerNestingLevel)
164         return;
165
166     double newMinimumInterval = scriptExecutionContext()->minimumTimerInterval();
167     double newClampedInterval = intervalClampedToMinimum(m_originalInterval, newMinimumInterval);
168
169     if (repeatInterval()) {
170         augmentRepeatInterval(newClampedInterval - repeatInterval());
171         return;
172     }
173
174     double previousClampedInterval = intervalClampedToMinimum(m_originalInterval, oldMinimumTimerInterval);
175     augmentFireInterval(newClampedInterval - previousClampedInterval);
176 }
177
178 double DOMTimer::intervalClampedToMinimum(int timeout, double minimumTimerInterval) const
179 {
180     double intervalMilliseconds = max(oneMillisecond, timeout * oneMillisecond);
181
182     if (intervalMilliseconds < minimumTimerInterval && m_nestingLevel >= maxTimerNestingLevel)
183         intervalMilliseconds = minimumTimerInterval;
184     return intervalMilliseconds;
185 }
186
187 double DOMTimer::alignedFireTime(double fireTime) const
188 {
189     double alignmentInterval = scriptExecutionContext()->timerAlignmentInterval();
190     if (alignmentInterval) {
191         double currentTime = monotonicallyIncreasingTime();
192         if (fireTime <= currentTime)
193             return fireTime;
194
195         double alignedTime = ceil(fireTime / alignmentInterval) * alignmentInterval;
196         return alignedTime;
197     }
198
199     return fireTime;
200 }
201
202 void DOMTimer::reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const
203 {
204     MemoryClassInfo info(memoryObjectInfo, this, WebCoreMemoryTypes::DOM);
205     SuspendableTimer::reportMemoryUsage(memoryObjectInfo);
206     info.addMember(m_action, "action");
207     info.addMember(m_userGestureToken, "userGestureToken");
208 }
209
210 } // namespace WebCore