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 "ScheduledAction.h"
31 #include "ScriptExecutionContext.h"
32 #include <wtf/HashSet.h>
33 #include <wtf/StdLibExtras.h>
34
35 using namespace std;
36
37 namespace WebCore {
38
39 static const int maxTimerNestingLevel = 5;
40 static const double oneMillisecond = 0.001;
41 double DOMTimer::s_minTimerInterval = 0.010; // 10 milliseconds
42
43 static int timerNestingLevel = 0;
44
DOMTimer(ScriptExecutionContext * context,ScheduledAction * action,int timeout,bool singleShot)45 DOMTimer::DOMTimer(ScriptExecutionContext* context, ScheduledAction* action, int timeout, bool singleShot)
46 : ActiveDOMObject(context, this)
47 , m_action(action)
48 , m_nextFireInterval(0)
49 , m_repeatInterval(0)
50 {
51 static int lastUsedTimeoutId = 0;
52 ++lastUsedTimeoutId;
53 // Avoid wraparound going negative on us.
54 if (lastUsedTimeoutId <= 0)
55 lastUsedTimeoutId = 1;
56 m_timeoutId = lastUsedTimeoutId;
57
58 m_nestingLevel = timerNestingLevel + 1;
59
60 scriptExecutionContext()->addTimeout(m_timeoutId, this);
61
62 double intervalMilliseconds = max(oneMillisecond, timeout * oneMillisecond);
63
64 // Use a minimum interval of 10 ms to match other browsers, but only once we've
65 // nested enough to notice that we're repeating.
66 // Faster timers might be "better", but they're incompatible.
67 if (intervalMilliseconds < s_minTimerInterval && m_nestingLevel >= maxTimerNestingLevel)
68 intervalMilliseconds = s_minTimerInterval;
69 if (singleShot)
70 startOneShot(intervalMilliseconds);
71 else
72 startRepeating(intervalMilliseconds);
73 }
74
~DOMTimer()75 DOMTimer::~DOMTimer()
76 {
77 if (scriptExecutionContext())
78 scriptExecutionContext()->removeTimeout(m_timeoutId);
79 }
80
install(ScriptExecutionContext * context,ScheduledAction * action,int timeout,bool singleShot)81 int DOMTimer::install(ScriptExecutionContext* context, ScheduledAction* action, int timeout, bool singleShot)
82 {
83 // DOMTimer constructor links the new timer into a list of ActiveDOMObjects held by the 'context'.
84 // The timer is deleted when context is deleted (DOMTimer::contextDestroyed) or explicitly via DOMTimer::removeById(),
85 // or if it is a one-time timer and it has fired (DOMTimer::fired).
86 DOMTimer* timer = new DOMTimer(context, action, timeout, singleShot);
87 return timer->m_timeoutId;
88 }
89
removeById(ScriptExecutionContext * context,int timeoutId)90 void DOMTimer::removeById(ScriptExecutionContext* context, int timeoutId)
91 {
92 // timeout IDs have to be positive, and 0 and -1 are unsafe to
93 // even look up since they are the empty and deleted value
94 // respectively
95 if (timeoutId <= 0)
96 return;
97 delete context->findTimeout(timeoutId);
98 }
99
fired()100 void DOMTimer::fired()
101 {
102 ScriptExecutionContext* context = scriptExecutionContext();
103 timerNestingLevel = m_nestingLevel;
104
105 // Simple case for non-one-shot timers.
106 if (isActive()) {
107 if (repeatInterval() && repeatInterval() < s_minTimerInterval) {
108 m_nestingLevel++;
109 if (m_nestingLevel >= maxTimerNestingLevel)
110 augmentRepeatInterval(s_minTimerInterval - repeatInterval());
111 }
112
113 // No access to member variables after this point, it can delete the timer.
114 m_action->execute(context);
115 return;
116 }
117
118 // Delete timer before executing the action for one-shot timers.
119 ScheduledAction* action = m_action.release();
120
121 // No access to member variables after this point.
122 delete this;
123
124 action->execute(context);
125 delete action;
126 timerNestingLevel = 0;
127 }
128
hasPendingActivity() const129 bool DOMTimer::hasPendingActivity() const
130 {
131 return isActive();
132 }
133
contextDestroyed()134 void DOMTimer::contextDestroyed()
135 {
136 ActiveDOMObject::contextDestroyed();
137 delete this;
138 }
139
stop()140 void DOMTimer::stop()
141 {
142 TimerBase::stop();
143 // Need to release JS objects potentially protected by ScheduledAction
144 // because they can form circular references back to the ScriptExecutionContext
145 // which will cause a memory leak.
146 m_action.clear();
147 }
148
suspend()149 void DOMTimer::suspend()
150 {
151 ASSERT(!m_nextFireInterval && !m_repeatInterval);
152 m_nextFireInterval = nextFireInterval();
153 m_repeatInterval = repeatInterval();
154 TimerBase::stop();
155 }
156
resume()157 void DOMTimer::resume()
158 {
159 start(m_nextFireInterval, m_repeatInterval);
160 m_nextFireInterval = 0;
161 m_repeatInterval = 0;
162 }
163
164
canSuspend() const165 bool DOMTimer::canSuspend() const
166 {
167 return true;
168 }
169
170 } // namespace WebCore
171