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