1635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project/*
2635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project * Copyright (C) 2008 Apple Inc. All Rights Reserved.
3635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project *
4635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project * Redistribution and use in source and binary forms, with or without
5635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project * modification, are permitted provided that the following conditions
6635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project * are met:
7635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project * 1. Redistributions of source code must retain the above copyright
8635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project *    notice, this list of conditions and the following disclaimer.
9635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project * 2. Redistributions in binary form must reproduce the above copyright
10635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project *    notice, this list of conditions and the following disclaimer in the
11635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project *    documentation and/or other materials provided with the distribution.
12635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project *
13635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project *
25635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project */
26635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
27635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project#include "config.h"
28635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project#include "DOMTimer.h"
29635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
30a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch#include "InspectorInstrumentation.h"
31635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project#include "ScheduledAction.h"
32635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project#include "ScriptExecutionContext.h"
332bde8e466a4451c7319e3a072d118917957d6554Steve Block#include "UserGestureIndicator.h"
34635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project#include <wtf/HashSet.h>
35635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project#include <wtf/StdLibExtras.h>
36635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
37635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Projectusing namespace std;
38635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
39635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Projectnamespace WebCore {
40635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
412bde8e466a4451c7319e3a072d118917957d6554Steve Blockstatic const int maxIntervalForUserGestureForwarding = 1000; // One second matches Gecko.
42635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Projectstatic const int maxTimerNestingLevel = 5;
43635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Projectstatic const double oneMillisecond = 0.001;
4481bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdochdouble DOMTimer::s_minDefaultTimerInterval = 0.010; // 10 milliseconds
45635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
46635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Projectstatic int timerNestingLevel = 0;
472bde8e466a4451c7319e3a072d118917957d6554Steve Block
482bde8e466a4451c7319e3a072d118917957d6554Steve Blockstatic int timeoutId()
49635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project{
50635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    static int lastUsedTimeoutId = 0;
51635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    ++lastUsedTimeoutId;
52635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    // Avoid wraparound going negative on us.
53635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    if (lastUsedTimeoutId <= 0)
54635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project        lastUsedTimeoutId = 1;
552bde8e466a4451c7319e3a072d118917957d6554Steve Block    return lastUsedTimeoutId;
562bde8e466a4451c7319e3a072d118917957d6554Steve Block}
572bde8e466a4451c7319e3a072d118917957d6554Steve Block
582bde8e466a4451c7319e3a072d118917957d6554Steve Blockstatic inline bool shouldForwardUserGesture(int interval, int nestingLevel)
592bde8e466a4451c7319e3a072d118917957d6554Steve Block{
602bde8e466a4451c7319e3a072d118917957d6554Steve Block    return UserGestureIndicator::processingUserGesture()
612bde8e466a4451c7319e3a072d118917957d6554Steve Block        && interval <= maxIntervalForUserGestureForwarding
622bde8e466a4451c7319e3a072d118917957d6554Steve Block        && nestingLevel == 1; // Gestures should not be forwarded to nested timers.
632bde8e466a4451c7319e3a072d118917957d6554Steve Block}
64635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
652bde8e466a4451c7319e3a072d118917957d6554Steve BlockDOMTimer::DOMTimer(ScriptExecutionContext* context, PassOwnPtr<ScheduledAction> action, int interval, bool singleShot)
662bde8e466a4451c7319e3a072d118917957d6554Steve Block    : SuspendableTimer(context)
672bde8e466a4451c7319e3a072d118917957d6554Steve Block    , m_timeoutId(timeoutId())
682bde8e466a4451c7319e3a072d118917957d6554Steve Block    , m_nestingLevel(timerNestingLevel + 1)
692bde8e466a4451c7319e3a072d118917957d6554Steve Block    , m_action(action)
702bde8e466a4451c7319e3a072d118917957d6554Steve Block    , m_originalInterval(interval)
712bde8e466a4451c7319e3a072d118917957d6554Steve Block    , m_shouldForwardUserGesture(shouldForwardUserGesture(interval, m_nestingLevel))
722bde8e466a4451c7319e3a072d118917957d6554Steve Block{
738f72e70a9fd78eec56623b3a62e68f16b7b27e28Feng Qian    scriptExecutionContext()->addTimeout(m_timeoutId, this);
74635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
752bde8e466a4451c7319e3a072d118917957d6554Steve Block    double intervalMilliseconds = intervalClampedToMinimum(interval, context->minimumTimerInterval());
76635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    if (singleShot)
77635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project        startOneShot(intervalMilliseconds);
78635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    else
79635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project        startRepeating(intervalMilliseconds);
80635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project}
81635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
82635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source ProjectDOMTimer::~DOMTimer()
83635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project{
840bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    if (scriptExecutionContext())
858f72e70a9fd78eec56623b3a62e68f16b7b27e28Feng Qian        scriptExecutionContext()->removeTimeout(m_timeoutId);
86635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project}
870bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
88692e5dbf12901edacf14812a6fae25462920af42Steve Blockint DOMTimer::install(ScriptExecutionContext* context, PassOwnPtr<ScheduledAction> action, int timeout, bool singleShot)
89635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project{
90635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    // DOMTimer constructor links the new timer into a list of ActiveDOMObjects held by the 'context'.
91635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    // The timer is deleted when context is deleted (DOMTimer::contextDestroyed) or explicitly via DOMTimer::removeById(),
92635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    // or if it is a one-time timer and it has fired (DOMTimer::fired).
93635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    DOMTimer* timer = new DOMTimer(context, action, timeout, singleShot);
94cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block
95a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    InspectorInstrumentation::didInstallTimer(context, timer->m_timeoutId, timeout, singleShot);
96cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block
97635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    return timer->m_timeoutId;
98635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project}
99635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
100635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Projectvoid DOMTimer::removeById(ScriptExecutionContext* context, int timeoutId)
101635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project{
102635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    // timeout IDs have to be positive, and 0 and -1 are unsafe to
103635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    // even look up since they are the empty and deleted value
104635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    // respectively
105635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    if (timeoutId <= 0)
106635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project        return;
107cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block
108a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    InspectorInstrumentation::didRemoveTimer(context, timeoutId);
109cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block
1108f72e70a9fd78eec56623b3a62e68f16b7b27e28Feng Qian    delete context->findTimeout(timeoutId);
111635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project}
112635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
113635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Projectvoid DOMTimer::fired()
114635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project{
115635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    ScriptExecutionContext* context = scriptExecutionContext();
116635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    timerNestingLevel = m_nestingLevel;
1172bde8e466a4451c7319e3a072d118917957d6554Steve Block
1182bde8e466a4451c7319e3a072d118917957d6554Steve Block    UserGestureIndicator gestureIndicator(m_shouldForwardUserGesture ? DefinitelyProcessingUserGesture : PossiblyProcessingUserGesture);
1192bde8e466a4451c7319e3a072d118917957d6554Steve Block
1202bde8e466a4451c7319e3a072d118917957d6554Steve Block    // Only the first execution of a multi-shot timer should get an affirmative user gesture indicator.
1212bde8e466a4451c7319e3a072d118917957d6554Steve Block    m_shouldForwardUserGesture = false;
122635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
123a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    InspectorInstrumentationCookie cookie = InspectorInstrumentation::willFireTimer(context, m_timeoutId);
124cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block
125635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    // Simple case for non-one-shot timers.
126635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    if (isActive()) {
12781bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch        double minimumInterval = context->minimumTimerInterval();
12881bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch        if (repeatInterval() && repeatInterval() < minimumInterval) {
129635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project            m_nestingLevel++;
130635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project            if (m_nestingLevel >= maxTimerNestingLevel)
13181bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch                augmentRepeatInterval(minimumInterval - repeatInterval());
132635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project        }
1330bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
134635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project        // No access to member variables after this point, it can delete the timer.
135635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project        m_action->execute(context);
136a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
137a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch        InspectorInstrumentation::didFireTimer(cookie);
138a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
139635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project        return;
140635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    }
141635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
142635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    // Delete timer before executing the action for one-shot timers.
143e458d70a0d18538346f41b503114c9ebe6b2ce12Leon Clarke    OwnPtr<ScheduledAction> action = m_action.release();
144635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
145635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    // No access to member variables after this point.
146635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    delete this;
1470bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
148635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    action->execute(context);
149e458d70a0d18538346f41b503114c9ebe6b2ce12Leon Clarke
150a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    InspectorInstrumentation::didFireTimer(cookie);
151e458d70a0d18538346f41b503114c9ebe6b2ce12Leon Clarke
152635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    timerNestingLevel = 0;
153635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project}
154635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
155635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Projectvoid DOMTimer::contextDestroyed()
156635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project{
1575af96e2c7b73ebc627c6894727826a7576d31758Leon Clarke    SuspendableTimer::contextDestroyed();
158635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    delete this;
159635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project}
160635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
161635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Projectvoid DOMTimer::stop()
162635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project{
1635af96e2c7b73ebc627c6894727826a7576d31758Leon Clarke    SuspendableTimer::stop();
164635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    // Need to release JS objects potentially protected by ScheduledAction
165635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    // because they can form circular references back to the ScriptExecutionContext
166635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    // which will cause a memory leak.
167635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    m_action.clear();
168635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project}
169635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
17081bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdochvoid DOMTimer::adjustMinimumTimerInterval(double oldMinimumTimerInterval)
17181bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch{
17281bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch    if (m_nestingLevel < maxTimerNestingLevel)
17381bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch        return;
17481bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch
17581bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch    double newMinimumInterval = scriptExecutionContext()->minimumTimerInterval();
1762bde8e466a4451c7319e3a072d118917957d6554Steve Block    double newClampedInterval = intervalClampedToMinimum(m_originalInterval, newMinimumInterval);
17781bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch
17881bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch    if (repeatInterval()) {
17981bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch        augmentRepeatInterval(newClampedInterval - repeatInterval());
18081bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch        return;
18181bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch    }
18281bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch
1832bde8e466a4451c7319e3a072d118917957d6554Steve Block    double previousClampedInterval = intervalClampedToMinimum(m_originalInterval, oldMinimumTimerInterval);
18481bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch    augmentFireInterval(newClampedInterval - previousClampedInterval);
18581bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch}
18681bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch
18781bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdochdouble DOMTimer::intervalClampedToMinimum(int timeout, double minimumTimerInterval) const
18881bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch{
18981bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch    double intervalMilliseconds = max(oneMillisecond, timeout * oneMillisecond);
19081bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch
19181bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch    if (intervalMilliseconds < minimumTimerInterval && m_nestingLevel >= maxTimerNestingLevel)
19281bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch        intervalMilliseconds = minimumTimerInterval;
19381bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch    return intervalMilliseconds;
19481bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch}
19581bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch
196635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project} // namespace WebCore
197