1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:expandtab:shiftwidth=2:tabstop=2:
3  */
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 
8 #ifndef nsUserIdleService_h__
9 #define nsUserIdleService_h__
10 
11 #include "nsIUserIdleServiceInternal.h"
12 #include "nsCOMPtr.h"
13 #include "nsITimer.h"
14 #include "nsTArray.h"
15 #include "nsIObserver.h"
16 #include "nsIUserIdleService.h"
17 #include "nsCategoryCache.h"
18 #include "nsWeakReference.h"
19 #include "mozilla/TimeStamp.h"
20 
21 /**
22  * Class we can use to store an observer with its associated idle time
23  * requirement and whether or not the observer thinks it's "idle".
24  */
25 class IdleListener {
26  public:
27   nsCOMPtr<nsIObserver> observer;
28   uint32_t reqIdleTime;
29   bool isIdle;
30 
31   IdleListener(nsIObserver* obs, uint32_t reqIT, bool aIsIdle = false)
observer(obs)32       : observer(obs), reqIdleTime(reqIT), isIdle(aIsIdle) {}
33   ~IdleListener() = default;
34 };
35 
36 // This one will be declared later.
37 class nsUserIdleService;
38 
39 /**
40  * Class to handle the daily idle timer.
41  */
42 class nsUserIdleServiceDaily : public nsIObserver,
43                                public nsSupportsWeakReference {
44  public:
45   NS_DECL_ISUPPORTS
46   NS_DECL_NSIOBSERVER
47 
48   explicit nsUserIdleServiceDaily(nsIUserIdleService* aIdleService);
49 
50   /**
51    * Initializes the daily idle observer.
52    * Keep this separated from the constructor, since it could cause pointer
53    * corruption due to AddRef/Release of "this".
54    */
55   void Init();
56 
57  private:
58   virtual ~nsUserIdleServiceDaily();
59 
60   /**
61    * StageIdleDaily is the interim call made when an idle-daily event is due.
62    * However we don't want to fire idle-daily until the user is idle for this
63    * session, so this sets up a short wait for an idle event which triggers
64    * the actual idle-daily event.
65    *
66    * @param aHasBeenLongWait Pass true indicating nsUserIdleServiceDaily is
67    * having trouble getting the idle-daily event fired. If true StageIdleDaily
68    * will use a shorter idle wait time before firing idle-daily.
69    */
70   void StageIdleDaily(bool aHasBeenLongWait);
71 
72   /**
73    * @note This is a normal pointer, part to avoid creating a cycle with the
74    * idle service, part to avoid potential pointer corruption due to this class
75    * being instantiated in the constructor of the service itself.
76    */
77   nsIUserIdleService* mIdleService;
78 
79   /**
80    * Place to hold the timer used by this class to determine when a day has
81    * passed, after that it will wait for idle time to be detected.
82    */
83   nsCOMPtr<nsITimer> mTimer;
84 
85   /**
86    * Function that is called back once a day.
87    */
88   static void DailyCallback(nsITimer* aTimer, void* aClosure);
89 
90   /**
91    * Cache of observers for the "idle-daily" category.
92    */
93   nsCategoryCache<nsIObserver> mCategoryObservers;
94 
95   /**
96    * Boolean set to true when daily idle notifications should be disabled.
97    */
98   bool mShutdownInProgress;
99 
100   /**
101    * Next time we expect an idle-daily timer to fire, in case timers aren't
102    * very reliable on the platform. Value is in PR_Now microsecond units.
103    */
104   PRTime mExpectedTriggerTime;
105 
106   /**
107    * Tracks which idle daily observer callback we ask for. There are two: a
108    * regular long idle wait and a shorter wait if we've been waiting to fire
109    * idle daily for an extended period. Set by StageIdleDaily.
110    */
111   int32_t mIdleDailyTriggerWait;
112 };
113 
114 class nsUserIdleService : public nsIUserIdleServiceInternal {
115  public:
116   NS_DECL_ISUPPORTS
117   NS_DECL_NSIUSERIDLESERVICE NS_DECL_NSIUSERIDLESERVICEINTERNAL
118 
119       protected : static already_AddRefed<nsUserIdleService>
120                   GetInstance();
121 
122   nsUserIdleService();
123   virtual ~nsUserIdleService();
124 
125   /**
126    * If there is a platform specific function to poll the system idel time
127    * then that must be returned in this function, and the function MUST return
128    * true, otherwise then the function should be left unimplemented or made
129    * to return false (this can also be used for systems where it depends on
130    * the configuration of the system if the idle time can be determined)
131    *
132    * @param aIdleTime
133    *        The idle time in ms.
134    *
135    * @return true if the idle time could be polled, false otherwise.
136    *
137    * @note The time returned by this function can be different than the one
138    *       returned by GetIdleTime, as that is corrected by any calls to
139    *       ResetIdleTimeOut(), unless you overwrite that function too...
140    */
141   virtual bool PollIdleTime(uint32_t* aIdleTime);
142 
143   /**
144    * Function that determines if we are in poll mode or not.
145    *
146    * @return true if polling is supported, false otherwise.
147    */
148   virtual bool UsePollMode();
149 
150  private:
151   /**
152    * Ensure that the timer is expiring at least at the given time
153    *
154    * The function might not restart the timer if there is one running currently
155    *
156    * @param aNextTimeout
157    *        The last absolute time the timer should expire
158    */
159   void SetTimerExpiryIfBefore(mozilla::TimeStamp aNextTimeout);
160 
161   /**
162    * Stores the next timeout time, 0 means timer not running
163    */
164   mozilla::TimeStamp mCurrentlySetToTimeoutAt;
165 
166   /**
167    * mTimer holds the internal timer used by this class to detect when to poll
168    * for idle time, when to check if idle timers should expire etc.
169    */
170   nsCOMPtr<nsITimer> mTimer;
171 
172   /**
173    * Array of listeners that wants to be notified about idle time.
174    */
175   nsTArray<IdleListener> mArrayListeners;
176 
177   /**
178    * Object keeping track of the daily idle thingy.
179    */
180   RefPtr<nsUserIdleServiceDaily> mDailyIdle;
181 
182   /**
183    * Number of observers currently in idle mode.
184    */
185   uint32_t mIdleObserverCount;
186 
187   /**
188    * Delta time from last non idle time to when the next observer should switch
189    * to idle mode
190    *
191    * Time in seconds
192    *
193    * If this value is 0 it means there are no active observers
194    */
195   uint32_t mDeltaToNextIdleSwitchInS;
196 
197   /**
198    * If true, the idle service is temporarily disabled, and all idle events
199    * will be ignored.
200    */
201   bool mDisabled = false;
202 
203   /**
204    * Absolute value for when the last user interaction took place.
205    */
206   mozilla::TimeStamp mLastUserInteraction;
207 
208   /**
209    * Function that ensures the timer is running with at least the minimum time
210    * needed.  It will kill the timer if there are no active observers.
211    */
212   void ReconfigureTimer(void);
213 
214   /**
215    * Callback function that is called when the internal timer expires.
216    *
217    * This in turn calls the IdleTimerCallback that does the real processing
218    */
219   static void StaticIdleTimerCallback(nsITimer* aTimer, void* aClosure);
220 
221   /**
222    * Function that handles when a timer has expired
223    */
224   void IdleTimerCallback(void);
225 };
226 
227 #endif  // nsUserIdleService_h__
228