michael@0: # This Source Code Form is subject to the terms of the Mozilla Public michael@0: # License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: # file, You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: michael@0: michael@0: // An Alarm fires a callback after a certain amount of time, or at michael@0: // regular intervals. It's a convenient replacement for michael@0: // setTimeout/Interval when you don't want to bind to a specific michael@0: // window. michael@0: // michael@0: // The ConditionalAlarm is an Alarm that cancels itself if its callback michael@0: // returns a value that type-converts to true. michael@0: // michael@0: // Example: michael@0: // michael@0: // function foo() { dump('hi'); }; michael@0: // new G_Alarm(foo, 10*1000); // Fire foo in 10 seconds michael@0: // new G_Alarm(foo, 10*1000, true /*repeat*/); // Fire foo every 10 seconds michael@0: // new G_Alarm(foo, 10*1000, true, 7); // Fire foo every 10 seconds michael@0: // // seven times michael@0: // new G_ConditionalAlarm(foo, 1000, true); // Fire every sec until foo()==true michael@0: // michael@0: // // Fire foo every 10 seconds until foo returns true or until it fires seven michael@0: // // times, whichever happens first. michael@0: // new G_ConditionalAlarm(foo, 10*1000, true /*repeating*/, 7); michael@0: // michael@0: // TODO: maybe pass an isFinal flag to the callback if they opted to michael@0: // set maxTimes and this is the last iteration? michael@0: michael@0: michael@0: /** michael@0: * Set an alarm to fire after a given amount of time, or at specific michael@0: * intervals. michael@0: * michael@0: * @param callback Function to call when the alarm fires michael@0: * @param delayMS Number indicating the length of the alarm period in ms michael@0: * @param opt_repeating Boolean indicating whether this should fire michael@0: * periodically michael@0: * @param opt_maxTimes Number indicating a maximum number of times to michael@0: * repeat (obviously only useful when opt_repeating==true) michael@0: */ michael@0: function G_Alarm(callback, delayMS, opt_repeating, opt_maxTimes) { michael@0: this.debugZone = "alarm"; michael@0: this.callback_ = callback; michael@0: this.repeating_ = !!opt_repeating; michael@0: this.timer_ = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); michael@0: var type = opt_repeating ? michael@0: this.timer_.TYPE_REPEATING_SLACK : michael@0: this.timer_.TYPE_ONE_SHOT; michael@0: this.maxTimes_ = opt_maxTimes ? opt_maxTimes : null; michael@0: this.nTimes_ = 0; michael@0: michael@0: this.observerServiceObserver_ = new G_ObserverServiceObserver( michael@0: 'xpcom-shutdown', michael@0: BindToObject(this.cancel, this)); michael@0: michael@0: // Ask the timer to use nsITimerCallback (.notify()) when ready michael@0: this.timer_.initWithCallback(this, delayMS, type); michael@0: } michael@0: michael@0: /** michael@0: * Cancel this timer michael@0: */ michael@0: G_Alarm.prototype.cancel = function() { michael@0: if (!this.timer_) { michael@0: return; michael@0: } michael@0: michael@0: this.timer_.cancel(); michael@0: // Break circular reference created between this.timer_ and the G_Alarm michael@0: // instance (this) michael@0: this.timer_ = null; michael@0: this.callback_ = null; michael@0: michael@0: // We don't need the shutdown observer anymore michael@0: this.observerServiceObserver_.unregister(); michael@0: } michael@0: michael@0: /** michael@0: * Invoked by the timer when it fires michael@0: * michael@0: * @param timer Reference to the nsITimer which fired (not currently michael@0: * passed along) michael@0: */ michael@0: G_Alarm.prototype.notify = function(timer) { michael@0: // fire callback and save results michael@0: var ret = this.callback_(); michael@0: michael@0: // If they've given us a max number of times to fire, enforce it michael@0: this.nTimes_++; michael@0: if (this.repeating_ && michael@0: typeof this.maxTimes_ == "number" michael@0: && this.nTimes_ >= this.maxTimes_) { michael@0: this.cancel(); michael@0: } else if (!this.repeating_) { michael@0: // Clear out the callback closure for TYPE_ONE_SHOT timers michael@0: this.cancel(); michael@0: } michael@0: // We don't cancel/cleanup timers that repeat forever until either michael@0: // xpcom-shutdown occurs or cancel() is called explicitly. michael@0: michael@0: return ret; michael@0: } michael@0: michael@0: G_Alarm.prototype.setDelay = function(delay) { michael@0: this.timer_.delay = delay; michael@0: } michael@0: michael@0: /** michael@0: * XPCOM cruft michael@0: */ michael@0: G_Alarm.prototype.QueryInterface = function(iid) { michael@0: if (iid.equals(Components.interfaces.nsISupports) || michael@0: iid.equals(Components.interfaces.nsITimerCallback)) michael@0: return this; michael@0: michael@0: throw Components.results.NS_ERROR_NO_INTERFACE; michael@0: } michael@0: michael@0: michael@0: /** michael@0: * An alarm with the additional property that it cancels itself if its michael@0: * callback returns true. michael@0: * michael@0: * For parameter documentation, see G_Alarm michael@0: */ michael@0: function G_ConditionalAlarm(callback, delayMS, opt_repeating, opt_maxTimes) { michael@0: G_Alarm.call(this, callback, delayMS, opt_repeating, opt_maxTimes); michael@0: this.debugZone = "conditionalalarm"; michael@0: } michael@0: michael@0: G_ConditionalAlarm.inherits(G_Alarm); michael@0: michael@0: /** michael@0: * Invoked by the timer when it fires michael@0: * michael@0: * @param timer Reference to the nsITimer which fired (not currently michael@0: * passed along) michael@0: */ michael@0: G_ConditionalAlarm.prototype.notify = function(timer) { michael@0: // Call G_Alarm::notify michael@0: var rv = G_Alarm.prototype.notify.call(this, timer); michael@0: michael@0: if (this.repeating_ && rv) { michael@0: G_Debug(this, "Callback of a repeating alarm returned true; cancelling."); michael@0: this.cancel(); michael@0: } michael@0: }