LCOV - code coverage report
Current view: directory - widget/xpwidgets - nsIdleService.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 183 92 50.3 %
Date: 2012-06-02 Functions: 23 18 78.3 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2                 : /* vim:expandtab:shiftwidth=2:tabstop=2:
       3                 :  */
       4                 : /* ***** BEGIN LICENSE BLOCK *****
       5                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       6                 :  *
       7                 :  * The contents of this file are subject to the Mozilla Public License Version
       8                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       9                 :  * the License. You may obtain a copy of the License at
      10                 :  * http://www.mozilla.org/MPL/
      11                 :  *
      12                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      13                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      14                 :  * for the specific language governing rights and limitations under the
      15                 :  * License.
      16                 :  *
      17                 :  * The Original Code is mozilla.org code.
      18                 :  *
      19                 :  * The Initial Developer of the Original Code is
      20                 :  * Gijs Kruitbosch <gijskruitbosch@gmail.com>.
      21                 :  * Portions created by the Initial Developer are Copyright (C) 2007
      22                 :  * the Initial Developer. All Rights Reserved.
      23                 :  *
      24                 :  * Contributor(s):
      25                 :  *  Gijs Kruitbosch <gijskruitbosch@gmail.com>
      26                 :  *  Edward Lee <edward.lee@engineering.uiuc.edu>
      27                 :  *  Mike Kristoffersen <mozstuff@mikek.dk>
      28                 :  *
      29                 :  * Alternatively, the contents of this file may be used under the terms of
      30                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      31                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      32                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      33                 :  * of those above. If you wish to allow use of your version of this file only
      34                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      35                 :  * use your version of this file under the terms of the MPL, indicate your
      36                 :  * decision by deleting the provisions above and replace them with the notice
      37                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      38                 :  * the provisions above, a recipient may use your version of this file under
      39                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      40                 :  *
      41                 :  * ***** END LICENSE BLOCK ***** */
      42                 : 
      43                 : #include "nsIdleService.h"
      44                 : #include "nsString.h"
      45                 : #include "nsIObserverService.h"
      46                 : #include "nsIServiceManager.h"
      47                 : #include "nsDebug.h"
      48                 : #include "nsCOMArray.h"
      49                 : #include "prinrval.h"
      50                 : #include "mozilla/Services.h"
      51                 : #include "mozilla/Preferences.h"
      52                 : #include "mozilla/Telemetry.h"
      53                 : 
      54                 : using namespace mozilla;
      55                 : 
      56                 : // observer topics used:
      57                 : #define OBSERVER_TOPIC_IDLE "idle"
      58                 : #define OBSERVER_TOPIC_BACK "back"
      59                 : #define OBSERVER_TOPIC_IDLE_DAILY "idle-daily"
      60                 : // interval in milliseconds between internal idle time requests.
      61                 : #define MIN_IDLE_POLL_INTERVAL_MSEC (5 * PR_MSEC_PER_SEC) /* 5 sec */
      62                 : 
      63                 : // Time used by the daily idle serivce to determine a significant idle time.
      64                 : #define DAILY_SIGNIFICANT_IDLE_SERVICE_SEC 300 /* 5 min */
      65                 : // Pref for last time (seconds since epoch) daily notification was sent.
      66                 : #define PREF_LAST_DAILY "idle.lastDailyNotification"
      67                 : // Number of seconds in a day.
      68                 : #define SECONDS_PER_DAY 86400
      69                 : 
      70                 : // Use this to find previously added observers in our array:
      71                 : class IdleListenerComparator
      72                 : {
      73                 : public:
      74               0 :   bool Equals(IdleListener a, IdleListener b) const
      75                 :   {
      76               0 :     return (a.observer == b.observer) &&
      77               0 :            (a.reqIdleTime == b.reqIdleTime);
      78                 :   }
      79                 : };
      80                 : 
      81                 : ////////////////////////////////////////////////////////////////////////////////
      82                 : //// nsIdleServiceDaily
      83                 : 
      84              26 : NS_IMPL_ISUPPORTS2(nsIdleServiceDaily, nsIObserver, nsISupportsWeakReference)
      85                 : 
      86                 : NS_IMETHODIMP
      87               1 : nsIdleServiceDaily::Observe(nsISupports *,
      88                 :                             const char *aTopic,
      89                 :                             const PRUnichar *)
      90                 : {
      91               1 :   if (strcmp(aTopic, "profile-after-change") == 0) {
      92                 :     // We are back. Start sending notifications again.
      93               0 :     mShutdownInProgress = false;
      94               0 :     return NS_OK;
      95                 :   }
      96                 : 
      97               1 :   if (strcmp(aTopic, "xpcom-will-shutdown") == 0 ||
      98               0 :       strcmp(aTopic, "profile-change-teardown") == 0) {
      99               1 :     mShutdownInProgress = true;
     100                 :   }
     101                 : 
     102               1 :   if (mShutdownInProgress || strcmp(aTopic, OBSERVER_TOPIC_BACK) == 0) {
     103               1 :     return NS_OK;
     104                 :   }
     105               0 :   MOZ_ASSERT(strcmp(aTopic, OBSERVER_TOPIC_IDLE) == 0);
     106                 : 
     107                 :   // Notify anyone who cares.
     108                 :   nsCOMPtr<nsIObserverService> observerService =
     109               0 :     mozilla::services::GetObserverService();
     110               0 :   NS_ENSURE_STATE(observerService);
     111               0 :   (void)observerService->NotifyObservers(nsnull,
     112                 :                                          OBSERVER_TOPIC_IDLE_DAILY,
     113               0 :                                          nsnull);
     114                 : 
     115                 :   // Notify the category observers.
     116               0 :   const nsCOMArray<nsIObserver> &entries = mCategoryObservers.GetEntries();
     117               0 :   for (PRInt32 i = 0; i < entries.Count(); ++i) {
     118               0 :     (void)entries[i]->Observe(nsnull, OBSERVER_TOPIC_IDLE_DAILY, nsnull);
     119                 :   }
     120                 : 
     121                 :   // Stop observing idle for today.
     122                 :   (void)mIdleService->RemoveIdleObserver(this,
     123               0 :                                          DAILY_SIGNIFICANT_IDLE_SERVICE_SEC);
     124                 : 
     125                 :   // Set the last idle-daily time pref.
     126               0 :   PRInt32 nowSec = static_cast<PRInt32>(PR_Now() / PR_USEC_PER_SEC);
     127               0 :   Preferences::SetInt(PREF_LAST_DAILY, nowSec);
     128                 : 
     129                 :   // Start timer for the next check in one day.
     130               0 :   (void)mTimer->InitWithFuncCallback(DailyCallback,
     131                 :                                      this,
     132                 :                                      SECONDS_PER_DAY * PR_MSEC_PER_SEC,
     133               0 :                                      nsITimer::TYPE_ONE_SHOT);
     134                 : 
     135               0 :   return NS_OK;
     136                 : }
     137                 : 
     138               1 : nsIdleServiceDaily::nsIdleServiceDaily(nsIIdleService* aIdleService)
     139                 :   : mIdleService(aIdleService)
     140                 :   , mTimer(do_CreateInstance(NS_TIMER_CONTRACTID))
     141                 :   , mCategoryObservers(OBSERVER_TOPIC_IDLE_DAILY)
     142               1 :   , mShutdownInProgress(false)
     143                 : {
     144               1 : }
     145                 : 
     146                 : void
     147               1 : nsIdleServiceDaily::Init()
     148                 : {
     149                 :   // Check time of the last idle-daily notification.  If it was more than 24
     150                 :   // hours ago listen for idle, otherwise set a timer for 24 hours from now.
     151               1 :   PRInt32 nowSec = static_cast<PRInt32>(PR_Now() / PR_USEC_PER_SEC);
     152               1 :   PRInt32 lastDaily = Preferences::GetInt(PREF_LAST_DAILY, 0);
     153               1 :   if (lastDaily < 0 || lastDaily > nowSec) {
     154                 :     // The time is bogus, use default.
     155               0 :     lastDaily = 0;
     156                 :   }
     157                 : 
     158                 :   // Check if it has been a day since the last notification.
     159               1 :   if (nowSec - lastDaily > SECONDS_PER_DAY) {
     160                 :     // Wait for the user to become idle, so we can do todays idle tasks.
     161               1 :     DailyCallback(nsnull, this);
     162                 :   }
     163                 :   else {
     164                 :     // Start timer for the next check in one day.
     165               0 :     (void)mTimer->InitWithFuncCallback(DailyCallback,
     166                 :                                        this,
     167                 :                                        SECONDS_PER_DAY * PR_MSEC_PER_SEC,
     168               0 :                                        nsITimer::TYPE_ONE_SHOT);
     169                 :   }
     170                 : 
     171                 :   // Register for when we should terminate/pause
     172               2 :   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     173               1 :   if (obs) {
     174               1 :     obs->AddObserver(this, "xpcom-will-shutdown", true);
     175               1 :     obs->AddObserver(this, "profile-change-teardown", true);
     176               1 :     obs->AddObserver(this, "profile-after-change", true);
     177                 :   }
     178               1 : }
     179                 : 
     180               3 : nsIdleServiceDaily::~nsIdleServiceDaily()
     181                 : {
     182               1 :   if (mTimer) {
     183               1 :     mTimer->Cancel();
     184               1 :     mTimer = nsnull;
     185                 :   }
     186               4 : }
     187                 : 
     188                 : // static
     189                 : void
     190               1 : nsIdleServiceDaily::DailyCallback(nsITimer* aTimer, void* aClosure)
     191                 : {
     192               1 :   nsIdleServiceDaily* me = static_cast<nsIdleServiceDaily*>(aClosure);
     193                 : 
     194                 :   // The one thing we do every day is to start waiting for the user to "have
     195                 :   // a significant idle time".
     196                 :   (void)me->mIdleService->AddIdleObserver(me,
     197               1 :                                           DAILY_SIGNIFICANT_IDLE_SERVICE_SEC);
     198               1 : }
     199                 : 
     200                 : 
     201                 : /**
     202                 :  * The idle services goal is to notify subscribers when a certain time has
     203                 :  * passed since the last user interaction with the system.
     204                 :  *
     205                 :  * On some platforms this is defined as the last time user events reached this
     206                 :  * application, on other platforms it is a system wide thing - the preferred
     207                 :  * implementation is to use the system idle time, rather than the application
     208                 :  * idle time, as the things depending on the idle service are likely to use
     209                 :  * significant resources (network, disk, memory, cpu, etc.).
     210                 :  *
     211                 :  * When the idle service needs to use the system wide idle timer, it typically
     212                 :  * needs to poll the idle time value by the means of a timer.  It needs to
     213                 :  * poll fast when it is in active idle mode (when it has a listener in the idle
     214                 :  * mode) as it needs to detect if the user is active in other applications.
     215                 :  *
     216                 :  * When the service is waiting for the first listener to become idle, or when
     217                 :  * it is only monitoring application idle time, it only needs to have the timer
     218                 :  * expire at the time the next listener goes idle.
     219                 :  *
     220                 :  * The core state of the service is determined by:
     221                 :  *
     222                 :  * - A list of listeners.
     223                 :  *
     224                 :  * - A boolean that tells if any listeners are in idle mode.
     225                 :  *
     226                 :  * - A delta value that indicates when, measured from the last non-idle time,
     227                 :  *   the next listener should switch to idle mode.
     228                 :  *
     229                 :  * - An absolute time of the last time idle mode was detected (this is used to
     230                 :  *   judge if we have been out of idle mode since the last invocation of the
     231                 :  *   service.
     232                 :  *
     233                 :  * There are four entry points into the system:
     234                 :  *
     235                 :  * - A new listener is registered.
     236                 :  *
     237                 :  * - An existing listener is deregistered.
     238                 :  *
     239                 :  * - User interaction is detected.
     240                 :  *
     241                 :  * - The timer expires.
     242                 :  *
     243                 :  * When a new listener is added its idle timeout, is compared with the next idle
     244                 :  * timeout, and if lower, that time is stored as the new timeout, and the timer
     245                 :  * is reconfigured to ensure a timeout around the time the new listener should
     246                 :  * timeout.
     247                 :  *
     248                 :  * If the next idle time is above the idle time requested by the new listener
     249                 :  * it won't be informed until the timer expires, this is to avoid recursive
     250                 :  * behavior and to simplify the code.  In this case the timer will be set to
     251                 :  * about 10 ms.
     252                 :  *
     253                 :  * When an existing listener is deregistered, it is just removed from the list
     254                 :  * of active listeners, we don't stop the timer, we just let it expire.
     255                 :  *
     256                 :  * When user interaction is detected, either because it was directly detected or
     257                 :  * because we polled the system timer and found it to be unexpected low, then we
     258                 :  * check the flag that tells us if any listeners are in idle mode, if there are
     259                 :  * they are removed from idle mode and told so, and we reset our state
     260                 :  * caculating the next timeout and restart the timer if needed.
     261                 :  *
     262                 :  * ---- Build in logic
     263                 :  *
     264                 :  * In order to avoid restarting the timer endlessly, the timer function has
     265                 :  * logic that will only restart the timer, if the requested timeout is before
     266                 :  * the current timeout.
     267                 :  *
     268                 :  */
     269                 : 
     270                 : 
     271                 : ////////////////////////////////////////////////////////////////////////////////
     272                 : //// nsIdleService
     273                 : 
     274               1 : nsIdleService::nsIdleService() : mCurrentlySetToTimeoutAtInPR(0),
     275                 :                                  mAnyObserverIdle(false),
     276                 :                                  mDeltaToNextIdleSwitchInS(PR_UINT32_MAX),
     277               1 :                                  mLastUserInteractionInPR(0)
     278                 : {
     279               1 :   mDailyIdle = new nsIdleServiceDaily(this);
     280               1 :   mDailyIdle->Init();
     281               1 : }
     282                 : 
     283               2 : nsIdleService::~nsIdleService()
     284                 : {
     285               1 :   if(mTimer) {
     286               1 :     mTimer->Cancel();
     287                 :   }
     288               2 : }
     289                 : 
     290                 : NS_IMETHODIMP
     291               1 : nsIdleService::AddIdleObserver(nsIObserver* aObserver, PRUint32 aIdleTimeInS)
     292                 : {
     293               1 :   NS_ENSURE_ARG_POINTER(aObserver);
     294                 :   // We don't accept idle time at 0, and we can't handle idle time that are too
     295                 :   // high either - no more than ~136 years.
     296               1 :   NS_ENSURE_ARG_RANGE(aIdleTimeInS, 1, (PR_UINT32_MAX / 10) - 1);
     297                 : 
     298                 :   // Put the time + observer in a struct we can keep:
     299               2 :   IdleListener listener(aObserver, aIdleTimeInS);
     300                 : 
     301               1 :   if (!mArrayListeners.AppendElement(listener)) {
     302               0 :     return NS_ERROR_OUT_OF_MEMORY;
     303                 :   }
     304                 : 
     305                 :   // Create our timer callback if it's not there already.
     306               1 :   if (!mTimer) {
     307                 :     nsresult rv;
     308               1 :     mTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
     309               1 :     NS_ENSURE_SUCCESS(rv, rv);
     310                 :   }
     311                 : 
     312                 :   // Check if the newly added observer has a smaller wait time than what we
     313                 :   // are wating for now.
     314               1 :   if (mDeltaToNextIdleSwitchInS > aIdleTimeInS) {
     315                 :     // If it is, then this is the next to move to idle (at this point we
     316                 :     // don't care if it should have switched already).
     317               1 :     mDeltaToNextIdleSwitchInS = aIdleTimeInS;
     318                 :   }
     319                 : 
     320                 :   // Ensure timer is running.
     321               1 :   ReconfigureTimer();
     322                 : 
     323               1 :   return NS_OK;
     324                 : }
     325                 : 
     326                 : NS_IMETHODIMP
     327               0 : nsIdleService::RemoveIdleObserver(nsIObserver* aObserver, PRUint32 aTimeInS)
     328                 : {
     329                 : 
     330               0 :   NS_ENSURE_ARG_POINTER(aObserver);
     331               0 :   NS_ENSURE_ARG(aTimeInS);
     332               0 :   IdleListener listener(aObserver, aTimeInS);
     333                 : 
     334                 :   // Find the entry and remove it, if it was the last entry, we just let the
     335                 :   // existing timer run to completion (there might be a new registration in a
     336                 :   // little while.
     337                 :   IdleListenerComparator c;
     338               0 :   if (mArrayListeners.RemoveElement(listener, c)) {
     339               0 :     return NS_OK;
     340                 :   }
     341                 : 
     342                 :   // If we get here, we haven't removed anything:
     343               0 :   return NS_ERROR_FAILURE;
     344                 : }
     345                 : 
     346                 : void
     347               1 : nsIdleService::ResetIdleTimeOut(PRUint32 idleDeltaInMS)
     348                 : {
     349                 :   // Store the time
     350               1 :   mLastUserInteractionInPR = PR_Now() - (idleDeltaInMS * PR_USEC_PER_MSEC);
     351                 : 
     352                 :   // If no one is idle, then we are done, any existing timers can keep running.
     353               1 :   if (!mAnyObserverIdle) {
     354               1 :     return;
     355                 :   }
     356                 : 
     357                 :   // Mark all idle services as non-idle, and calculate the next idle timeout.
     358               0 :   Telemetry::AutoTimer<Telemetry::IDLE_NOTIFY_BACK_MS> timer;
     359               0 :   nsCOMArray<nsIObserver> notifyList;
     360               0 :   mDeltaToNextIdleSwitchInS = PR_UINT32_MAX;
     361                 : 
     362                 :   // Loop through all listeners, and find any that have detected idle.
     363               0 :   for (PRUint32 i = 0; i < mArrayListeners.Length(); i++) {
     364               0 :     IdleListener& curListener = mArrayListeners.ElementAt(i);
     365                 : 
     366                 :     // If the listener was idle, then he shouldn't be any longer.
     367               0 :     if (curListener.isIdle) {
     368               0 :       notifyList.AppendObject(curListener.observer);
     369               0 :       curListener.isIdle = false;
     370                 :     }
     371                 : 
     372                 :     // Check if the listener is the next one to timeout.
     373                 :     mDeltaToNextIdleSwitchInS = PR_MIN(mDeltaToNextIdleSwitchInS,
     374               0 :                                        curListener.reqIdleTime);
     375                 :   }
     376                 : 
     377                 :   // When we are done, then we wont have anyone idle.
     378               0 :   mAnyObserverIdle = false;
     379                 : 
     380                 :   // Restart the idle timer, and do so before anyone can delay us.
     381               0 :   ReconfigureTimer();
     382                 : 
     383               0 :   PRInt32 numberOfPendingNotifications = notifyList.Count();
     384                 :   Telemetry::Accumulate(Telemetry::IDLE_NOTIFY_BACK_LISTENERS,
     385               0 :                         numberOfPendingNotifications);
     386                 : 
     387                 :   // Bail if nothing to do.
     388               0 :   if (!numberOfPendingNotifications) {
     389                 :     return;
     390                 :   }
     391                 : 
     392                 :   // Now send "back" events to all, if any should have timed out allready, then
     393                 :   // they will be reawaken by the timer that is already running.
     394                 : 
     395                 :   // We need a text string to send with any state change events.
     396               0 :   nsAutoString timeStr;
     397                 : 
     398               0 :   timeStr.AppendInt((PRInt32)(idleDeltaInMS / PR_MSEC_PER_SEC));
     399                 : 
     400                 :   // Send the "non-idle" events.
     401               0 :   while (numberOfPendingNotifications--) {
     402               0 :     notifyList[numberOfPendingNotifications]->Observe(this,
     403                 :                                                       OBSERVER_TOPIC_BACK,
     404               0 :                                                       timeStr.get());
     405                 :   }
     406                 : 
     407                 : }
     408                 : 
     409                 : NS_IMETHODIMP
     410               1 : nsIdleService::GetIdleTime(PRUint32* idleTime)
     411                 : {
     412                 :   // Check sanity of in parameter.
     413               1 :   if (!idleTime) {
     414               0 :     return NS_ERROR_NULL_POINTER;
     415                 :   }
     416                 : 
     417                 :   // Polled idle time in ms.
     418                 :   PRUint32 polledIdleTimeMS;
     419                 : 
     420               1 :   bool polledIdleTimeIsValid = PollIdleTime(&polledIdleTimeMS);
     421                 : 
     422                 :   // If we don't have any valid data, then we are not in idle - pr. definition.
     423               1 :   if (!polledIdleTimeIsValid && 0 == mLastUserInteractionInPR) {
     424               1 :     *idleTime = 0;
     425               1 :     return NS_OK;
     426                 :   }
     427                 : 
     428                 :   // If we never got a reset, just return the pulled time.
     429               0 :   if (0 == mLastUserInteractionInPR) {
     430               0 :     *idleTime = polledIdleTimeMS;
     431               0 :     return NS_OK;
     432                 :   }
     433                 : 
     434                 :   // timeSinceReset is in milliseconds.
     435               0 :   PRUint32 timeSinceResetInMS = (PR_Now() - mLastUserInteractionInPR) /
     436               0 :                                 PR_USEC_PER_MSEC;
     437                 : 
     438                 :   // If we did't get pulled data, return the time since last idle reset.
     439               0 :   if (!polledIdleTimeIsValid) {
     440                 :     // We need to convert to ms before returning the time.
     441               0 :     *idleTime = timeSinceResetInMS;
     442               0 :     return NS_OK;
     443                 :   }
     444                 : 
     445                 :   // Otherwise return the shortest time detected (in ms).
     446               0 :   *idleTime = NS_MIN(timeSinceResetInMS, polledIdleTimeMS);
     447                 : 
     448               0 :   return NS_OK;
     449                 : }
     450                 : 
     451                 : 
     452                 : bool
     453               0 : nsIdleService::PollIdleTime(PRUint32* /*aIdleTime*/)
     454                 : {
     455                 :   // Default behavior is not to have the ability to poll an idle time.
     456               0 :   return false;
     457                 : }
     458                 : 
     459                 : bool
     460               0 : nsIdleService::UsePollMode()
     461                 : {
     462                 :   PRUint32 dummy;
     463               0 :   return PollIdleTime(&dummy);
     464                 : }
     465                 : 
     466                 : void
     467               1 : nsIdleService::StaticIdleTimerCallback(nsITimer* aTimer, void* aClosure)
     468                 : {
     469               1 :   static_cast<nsIdleService*>(aClosure)->IdleTimerCallback();
     470               1 : }
     471                 : 
     472                 : void
     473               1 : nsIdleService::IdleTimerCallback(void)
     474                 : {
     475                 :   // Remember that we no longer have a timer running.
     476               1 :   mCurrentlySetToTimeoutAtInPR = 0;
     477                 : 
     478                 :   // Get the current idle time.
     479                 :   PRUint32 currentIdleTimeInMS;
     480                 : 
     481               1 :   if (NS_FAILED(GetIdleTime(&currentIdleTimeInMS))) {
     482               0 :     return;
     483                 :   }
     484                 : 
     485                 :   // Check if we have had some user interaction we didn't handle previously
     486                 :   // we do the caluculation in ms to lessen the chance for rounding errors to
     487                 :   // trigger wrong results, it is also very important that we call PR_Now AFTER
     488                 :   // the call to GetIdleTime().
     489               1 :   if (((PR_Now() - mLastUserInteractionInPR) / PR_USEC_PER_MSEC) >
     490                 :       currentIdleTimeInMS)
     491                 :   {
     492                 :     // We had user activity, so handle that part first (to ensure the listeners
     493                 :     // don't risk getting an non-idle after they get a new idle indication.
     494               1 :     ResetIdleTimeOut(currentIdleTimeInMS);
     495                 : 
     496                 :     // NOTE: We can't bail here, as we might have something already timed out.
     497                 :   }
     498                 : 
     499                 :   // Find the idle time in S.
     500               1 :   PRUint32 currentIdleTimeInS = currentIdleTimeInMS / PR_MSEC_PER_SEC;
     501                 : 
     502                 :   // Restart timer and bail if no-one are expected to be in idle
     503               1 :   if (mDeltaToNextIdleSwitchInS > currentIdleTimeInS) {
     504                 :     // If we didn't expect anyone to be idle, then just re-start the timer.
     505               1 :     ReconfigureTimer();
     506               1 :     return;
     507                 :   }
     508                 : 
     509                 :   // Tell expired listeners they are expired,and find the next timeout
     510               0 :   Telemetry::AutoTimer<Telemetry::IDLE_NOTIFY_IDLE_MS> timer;
     511                 : 
     512                 :   // We need to initialise the time to the next idle switch.
     513               0 :   mDeltaToNextIdleSwitchInS = PR_UINT32_MAX;
     514                 : 
     515                 :   // Create list of observers that should be notified.
     516               0 :   nsCOMArray<nsIObserver> notifyList;
     517                 : 
     518               0 :   for (PRUint32 i = 0; i < mArrayListeners.Length(); i++) {
     519               0 :     IdleListener& curListener = mArrayListeners.ElementAt(i);
     520                 : 
     521                 :     // We are only interested in items, that are not in the idle state.
     522               0 :     if (!curListener.isIdle) {
     523                 :       // If they have an idle time smaller than the actual idle time.
     524               0 :       if (curListener.reqIdleTime <= currentIdleTimeInS) {
     525                 :         // Then add the listener to the list of listeners that should be
     526                 :         // notified.
     527               0 :         notifyList.AppendObject(curListener.observer);
     528                 :         // This listener is now idle.
     529               0 :         curListener.isIdle = true;
     530                 :       } else {
     531                 :         // Listeners that are not timed out yet are candidates for timing out.
     532                 :         mDeltaToNextIdleSwitchInS = PR_MIN(mDeltaToNextIdleSwitchInS,
     533               0 :                                            curListener.reqIdleTime);
     534                 :       }
     535                 :     }
     536                 :   }
     537                 : 
     538                 :   // Restart the timer before any notifications that could slow us down are
     539                 :   // done.
     540               0 :   ReconfigureTimer();
     541                 : 
     542               0 :   PRInt32 numberOfPendingNotifications = notifyList.Count();
     543                 :   Telemetry::Accumulate(Telemetry::IDLE_NOTIFY_IDLE_LISTENERS,
     544               0 :                         numberOfPendingNotifications);
     545                 : 
     546                 :   // Bail if nothing to do.
     547               0 :   if (!numberOfPendingNotifications) {
     548                 :     return;
     549                 :   }
     550                 : 
     551                 :   // Remember we have someone idle.
     552               0 :   mAnyObserverIdle = true;
     553                 : 
     554                 :   // We need a text string to send with any state change events.
     555               0 :   nsAutoString timeStr;
     556               0 :   timeStr.AppendInt(currentIdleTimeInS);
     557                 : 
     558                 :   // Notify all listeners that just timed out.
     559               0 :   while (numberOfPendingNotifications--) {
     560               0 :     notifyList[numberOfPendingNotifications]->Observe(this,
     561                 :                                                       OBSERVER_TOPIC_IDLE,
     562               0 :                                                       timeStr.get());
     563                 :   }
     564                 : }
     565                 : 
     566                 : void
     567               2 : nsIdleService::SetTimerExpiryIfBefore(PRTime aNextTimeoutInPR)
     568                 : {
     569                 :   // Bail if we don't have a timer service.
     570               2 :   if (!mTimer) {
     571               0 :     return;
     572                 :   }
     573                 : 
     574                 :   // If the new timeout is before the old one or we don't have a timer running,
     575                 :   // then restart the timer.
     576               4 :   if (mCurrentlySetToTimeoutAtInPR > aNextTimeoutInPR ||
     577               2 :       !mCurrentlySetToTimeoutAtInPR) {
     578                 : 
     579               2 :     mCurrentlySetToTimeoutAtInPR = aNextTimeoutInPR ;
     580                 : 
     581                 :     // Stop the current timer (it's ok to try'n stop it, even it isn't running).
     582               2 :     mTimer->Cancel();
     583                 : 
     584                 :     // Check that the timeout is actually in the future, otherwise make it so.
     585               2 :     PRTime currentTimeInPR = PR_Now();
     586               2 :     if (currentTimeInPR > mCurrentlySetToTimeoutAtInPR) {
     587               1 :       mCurrentlySetToTimeoutAtInPR = currentTimeInPR;
     588                 :     }
     589                 : 
     590                 :     // Add 10 ms to ensure we don't undershoot, and never get a "0" timer.
     591               2 :     mCurrentlySetToTimeoutAtInPR += 10 * PR_USEC_PER_MSEC;
     592                 : 
     593                 :     // Start the timer
     594               2 :     mTimer->InitWithFuncCallback(StaticIdleTimerCallback,
     595                 :                                  this,
     596                 :                                  (mCurrentlySetToTimeoutAtInPR -
     597                 :                                   currentTimeInPR) / PR_USEC_PER_MSEC,
     598               2 :                                  nsITimer::TYPE_ONE_SHOT);
     599                 : 
     600                 :   }
     601                 : }
     602                 : 
     603                 : 
     604                 : void
     605               2 : nsIdleService::ReconfigureTimer(void)
     606                 : {
     607                 :   // Check if either someone is idle, or someone will become idle.
     608               2 :   if (!mAnyObserverIdle && PR_UINT32_MAX == mDeltaToNextIdleSwitchInS) {
     609                 :     // If not, just let any existing timers run to completion
     610                 :     // And bail out.
     611               0 :     return;
     612                 :   }
     613                 : 
     614                 :   // Find the next timeout value, assuming we are not polling.
     615                 : 
     616                 :   // We need to store the current time, so we don't get artifacts from the time
     617                 :   // ticking while we are processing.
     618               2 :   PRTime curTimeInPR = PR_Now();
     619                 : 
     620                 :   PRTime nextTimeoutAtInPR = mLastUserInteractionInPR +
     621                 :                              (((PRTime)mDeltaToNextIdleSwitchInS) *
     622               2 :                               PR_USEC_PER_SEC);
     623                 : 
     624                 :   // Check if we should correct the timeout time because we should poll before.
     625               2 :   if (mAnyObserverIdle && UsePollMode()) {
     626                 :     PRTime pollTimeout = curTimeInPR +
     627               0 :                          MIN_IDLE_POLL_INTERVAL_MSEC * PR_USEC_PER_SEC;
     628                 : 
     629               0 :     if (nextTimeoutAtInPR > pollTimeout) {
     630               0 :       nextTimeoutAtInPR = pollTimeout;
     631                 :     }
     632                 :   }
     633                 : 
     634               2 :   SetTimerExpiryIfBefore(nextTimeoutAtInPR);
     635                 : }
     636                 : 

Generated by: LCOV version 1.7