Fri, 16 Jan 2015 18:13:44 +0100
Integrate suggestion from review to improve consistency with existing code.
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/.
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?
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;
53 this.observerServiceObserver_ = new G_ObserverServiceObserver(
54 'xpcom-shutdown',
55 BindToObject(this.cancel, this));
57 // Ask the timer to use nsITimerCallback (.notify()) when ready
58 this.timer_.initWithCallback(this, delayMS, type);
59 }
61 /**
62 * Cancel this timer
63 */
64 G_Alarm.prototype.cancel = function() {
65 if (!this.timer_) {
66 return;
67 }
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;
75 // We don't need the shutdown observer anymore
76 this.observerServiceObserver_.unregister();
77 }
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_();
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.
102 return ret;
103 }
105 G_Alarm.prototype.setDelay = function(delay) {
106 this.timer_.delay = delay;
107 }
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;
117 throw Components.results.NS_ERROR_NO_INTERFACE;
118 }
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 }
132 G_ConditionalAlarm.inherits(G_Alarm);
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);
144 if (this.repeating_ && rv) {
145 G_Debug(this, "Callback of a repeating alarm returned true; cancelling.");
146 this.cancel();
147 }
148 }