|
1 # This Source Code Form is subject to the terms of the Mozilla Public |
|
2 # License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 # file, You can obtain one at http://mozilla.org/MPL/2.0/. |
|
4 |
|
5 |
|
6 // An Alarm fires a callback after a certain amount of time, or at |
|
7 // regular intervals. It's a convenient replacement for |
|
8 // setTimeout/Interval when you don't want to bind to a specific |
|
9 // window. |
|
10 // |
|
11 // The ConditionalAlarm is an Alarm that cancels itself if its callback |
|
12 // returns a value that type-converts to true. |
|
13 // |
|
14 // Example: |
|
15 // |
|
16 // function foo() { dump('hi'); }; |
|
17 // new G_Alarm(foo, 10*1000); // Fire foo in 10 seconds |
|
18 // new G_Alarm(foo, 10*1000, true /*repeat*/); // Fire foo every 10 seconds |
|
19 // new G_Alarm(foo, 10*1000, true, 7); // Fire foo every 10 seconds |
|
20 // // seven times |
|
21 // new G_ConditionalAlarm(foo, 1000, true); // Fire every sec until foo()==true |
|
22 // |
|
23 // // Fire foo every 10 seconds until foo returns true or until it fires seven |
|
24 // // times, whichever happens first. |
|
25 // new G_ConditionalAlarm(foo, 10*1000, true /*repeating*/, 7); |
|
26 // |
|
27 // TODO: maybe pass an isFinal flag to the callback if they opted to |
|
28 // set maxTimes and this is the last iteration? |
|
29 |
|
30 |
|
31 /** |
|
32 * Set an alarm to fire after a given amount of time, or at specific |
|
33 * intervals. |
|
34 * |
|
35 * @param callback Function to call when the alarm fires |
|
36 * @param delayMS Number indicating the length of the alarm period in ms |
|
37 * @param opt_repeating Boolean indicating whether this should fire |
|
38 * periodically |
|
39 * @param opt_maxTimes Number indicating a maximum number of times to |
|
40 * repeat (obviously only useful when opt_repeating==true) |
|
41 */ |
|
42 function G_Alarm(callback, delayMS, opt_repeating, opt_maxTimes) { |
|
43 this.debugZone = "alarm"; |
|
44 this.callback_ = callback; |
|
45 this.repeating_ = !!opt_repeating; |
|
46 this.timer_ = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); |
|
47 var type = opt_repeating ? |
|
48 this.timer_.TYPE_REPEATING_SLACK : |
|
49 this.timer_.TYPE_ONE_SHOT; |
|
50 this.maxTimes_ = opt_maxTimes ? opt_maxTimes : null; |
|
51 this.nTimes_ = 0; |
|
52 |
|
53 this.observerServiceObserver_ = new G_ObserverServiceObserver( |
|
54 'xpcom-shutdown', |
|
55 BindToObject(this.cancel, this)); |
|
56 |
|
57 // Ask the timer to use nsITimerCallback (.notify()) when ready |
|
58 this.timer_.initWithCallback(this, delayMS, type); |
|
59 } |
|
60 |
|
61 /** |
|
62 * Cancel this timer |
|
63 */ |
|
64 G_Alarm.prototype.cancel = function() { |
|
65 if (!this.timer_) { |
|
66 return; |
|
67 } |
|
68 |
|
69 this.timer_.cancel(); |
|
70 // Break circular reference created between this.timer_ and the G_Alarm |
|
71 // instance (this) |
|
72 this.timer_ = null; |
|
73 this.callback_ = null; |
|
74 |
|
75 // We don't need the shutdown observer anymore |
|
76 this.observerServiceObserver_.unregister(); |
|
77 } |
|
78 |
|
79 /** |
|
80 * Invoked by the timer when it fires |
|
81 * |
|
82 * @param timer Reference to the nsITimer which fired (not currently |
|
83 * passed along) |
|
84 */ |
|
85 G_Alarm.prototype.notify = function(timer) { |
|
86 // fire callback and save results |
|
87 var ret = this.callback_(); |
|
88 |
|
89 // If they've given us a max number of times to fire, enforce it |
|
90 this.nTimes_++; |
|
91 if (this.repeating_ && |
|
92 typeof this.maxTimes_ == "number" |
|
93 && this.nTimes_ >= this.maxTimes_) { |
|
94 this.cancel(); |
|
95 } else if (!this.repeating_) { |
|
96 // Clear out the callback closure for TYPE_ONE_SHOT timers |
|
97 this.cancel(); |
|
98 } |
|
99 // We don't cancel/cleanup timers that repeat forever until either |
|
100 // xpcom-shutdown occurs or cancel() is called explicitly. |
|
101 |
|
102 return ret; |
|
103 } |
|
104 |
|
105 G_Alarm.prototype.setDelay = function(delay) { |
|
106 this.timer_.delay = delay; |
|
107 } |
|
108 |
|
109 /** |
|
110 * XPCOM cruft |
|
111 */ |
|
112 G_Alarm.prototype.QueryInterface = function(iid) { |
|
113 if (iid.equals(Components.interfaces.nsISupports) || |
|
114 iid.equals(Components.interfaces.nsITimerCallback)) |
|
115 return this; |
|
116 |
|
117 throw Components.results.NS_ERROR_NO_INTERFACE; |
|
118 } |
|
119 |
|
120 |
|
121 /** |
|
122 * An alarm with the additional property that it cancels itself if its |
|
123 * callback returns true. |
|
124 * |
|
125 * For parameter documentation, see G_Alarm |
|
126 */ |
|
127 function G_ConditionalAlarm(callback, delayMS, opt_repeating, opt_maxTimes) { |
|
128 G_Alarm.call(this, callback, delayMS, opt_repeating, opt_maxTimes); |
|
129 this.debugZone = "conditionalalarm"; |
|
130 } |
|
131 |
|
132 G_ConditionalAlarm.inherits(G_Alarm); |
|
133 |
|
134 /** |
|
135 * Invoked by the timer when it fires |
|
136 * |
|
137 * @param timer Reference to the nsITimer which fired (not currently |
|
138 * passed along) |
|
139 */ |
|
140 G_ConditionalAlarm.prototype.notify = function(timer) { |
|
141 // Call G_Alarm::notify |
|
142 var rv = G_Alarm.prototype.notify.call(this, timer); |
|
143 |
|
144 if (this.repeating_ && rv) { |
|
145 G_Debug(this, "Callback of a repeating alarm returned true; cancelling."); |
|
146 this.cancel(); |
|
147 } |
|
148 } |