toolkit/mozapps/update/nsUpdateTimerManager.js

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
michael@0 6 Components.utils.import("resource://gre/modules/Services.jsm");
michael@0 7
michael@0 8 const Cc = Components.classes;
michael@0 9 const Ci = Components.interfaces;
michael@0 10
michael@0 11 const PREF_APP_UPDATE_LASTUPDATETIME_FMT = "app.update.lastUpdateTime.%ID%";
michael@0 12 const PREF_APP_UPDATE_TIMERMINIMUMDELAY = "app.update.timerMinimumDelay";
michael@0 13 const PREF_APP_UPDATE_TIMERFIRSTINTERVAL = "app.update.timerFirstInterval";
michael@0 14 const PREF_APP_UPDATE_LOG = "app.update.log";
michael@0 15
michael@0 16 const CATEGORY_UPDATE_TIMER = "update-timer";
michael@0 17
michael@0 18 XPCOMUtils.defineLazyGetter(this, "gLogEnabled", function tm_gLogEnabled() {
michael@0 19 return getPref("getBoolPref", PREF_APP_UPDATE_LOG, false);
michael@0 20 });
michael@0 21
michael@0 22 /**
michael@0 23 * Gets a preference value, handling the case where there is no default.
michael@0 24 * @param func
michael@0 25 * The name of the preference function to call, on nsIPrefBranch
michael@0 26 * @param preference
michael@0 27 * The name of the preference
michael@0 28 * @param defaultValue
michael@0 29 * The default value to return in the event the preference has
michael@0 30 * no setting
michael@0 31 * @returns The value of the preference, or undefined if there was no
michael@0 32 * user or default value.
michael@0 33 */
michael@0 34 function getPref(func, preference, defaultValue) {
michael@0 35 try {
michael@0 36 return Services.prefs[func](preference);
michael@0 37 }
michael@0 38 catch (e) {
michael@0 39 }
michael@0 40 return defaultValue;
michael@0 41 }
michael@0 42
michael@0 43 /**
michael@0 44 * Logs a string to the error console.
michael@0 45 * @param string
michael@0 46 * The string to write to the error console.
michael@0 47 */
michael@0 48 function LOG(string) {
michael@0 49 if (gLogEnabled) {
michael@0 50 dump("*** UTM:SVC " + string + "\n");
michael@0 51 Services.console.logStringMessage("UTM:SVC " + string);
michael@0 52 }
michael@0 53 }
michael@0 54
michael@0 55 /**
michael@0 56 * A manager for timers. Manages timers that fire over long periods of time
michael@0 57 * (e.g. days, weeks, months).
michael@0 58 * @constructor
michael@0 59 */
michael@0 60 function TimerManager() {
michael@0 61 Services.obs.addObserver(this, "xpcom-shutdown", false);
michael@0 62 }
michael@0 63 TimerManager.prototype = {
michael@0 64 /**
michael@0 65 * The Checker Timer
michael@0 66 */
michael@0 67 _timer: null,
michael@0 68
michael@0 69 /**
michael@0 70 * The Checker Timer minimum delay interval as specified by the
michael@0 71 * app.update.timerMinimumDelay pref. If the app.update.timerMinimumDelay
michael@0 72 * pref doesn't exist this will default to 120000.
michael@0 73 */
michael@0 74 _timerMinimumDelay: null,
michael@0 75
michael@0 76 /**
michael@0 77 * The set of registered timers.
michael@0 78 */
michael@0 79 _timers: { },
michael@0 80
michael@0 81 /**
michael@0 82 * See nsIObserver.idl
michael@0 83 */
michael@0 84 observe: function TM_observe(aSubject, aTopic, aData) {
michael@0 85 // Prevent setting the timer interval to a value of less than 30 seconds.
michael@0 86 var minInterval = 30000;
michael@0 87 // Prevent setting the first timer interval to a value of less than 10
michael@0 88 // seconds.
michael@0 89 var minFirstInterval = 10000;
michael@0 90 switch (aTopic) {
michael@0 91 case "utm-test-init":
michael@0 92 // Enforce a minimum timer interval of 500 ms for tests and fall through
michael@0 93 // to profile-after-change to initialize the timer.
michael@0 94 minInterval = 500;
michael@0 95 minFirstInterval = 500;
michael@0 96 case "profile-after-change":
michael@0 97 // Cancel the timer if it has already been initialized. This is primarily
michael@0 98 // for tests.
michael@0 99 this._timerMinimumDelay = Math.max(1000 * getPref("getIntPref", PREF_APP_UPDATE_TIMERMINIMUMDELAY, 120),
michael@0 100 minInterval);
michael@0 101 let firstInterval = Math.max(getPref("getIntPref", PREF_APP_UPDATE_TIMERFIRSTINTERVAL,
michael@0 102 this._timerMinimumDelay), minFirstInterval);
michael@0 103 this._canEnsureTimer = true;
michael@0 104 this._ensureTimer(firstInterval);
michael@0 105 break;
michael@0 106 case "xpcom-shutdown":
michael@0 107 Services.obs.removeObserver(this, "xpcom-shutdown");
michael@0 108
michael@0 109 // Release everything we hold onto.
michael@0 110 this._cancelTimer();
michael@0 111 for (var timerID in this._timers)
michael@0 112 delete this._timers[timerID];
michael@0 113 this._timers = null;
michael@0 114 break;
michael@0 115 }
michael@0 116 },
michael@0 117
michael@0 118 /**
michael@0 119 * Called when the checking timer fires.
michael@0 120 *
michael@0 121 * We only fire one notification each time, so that the operations are
michael@0 122 * staggered. We don't want too many to happen at once, which could
michael@0 123 * negatively impact responsiveness.
michael@0 124 *
michael@0 125 * @param timer
michael@0 126 * The checking timer that fired.
michael@0 127 */
michael@0 128 notify: function TM_notify(timer) {
michael@0 129 var nextDelay = null;
michael@0 130 function updateNextDelay(delay) {
michael@0 131 if (nextDelay === null || delay < nextDelay)
michael@0 132 nextDelay = delay;
michael@0 133 }
michael@0 134
michael@0 135 // Each timer calls tryFire(), which figures out which is the one that
michael@0 136 // wanted to be called earliest. That one will be fired; the others are
michael@0 137 // skipped and will be done later.
michael@0 138 var now = Math.round(Date.now() / 1000);
michael@0 139
michael@0 140 var callbackToFire = null;
michael@0 141 var earliestIntendedTime = null;
michael@0 142 var skippedFirings = false;
michael@0 143 function tryFire(callback, intendedTime) {
michael@0 144 var selected = false;
michael@0 145 if (intendedTime <= now) {
michael@0 146 if (intendedTime < earliestIntendedTime ||
michael@0 147 earliestIntendedTime === null) {
michael@0 148 callbackToFire = callback;
michael@0 149 earliestIntendedTime = intendedTime;
michael@0 150 selected = true;
michael@0 151 }
michael@0 152 else if (earliestIntendedTime !== null)
michael@0 153 skippedFirings = true;
michael@0 154 }
michael@0 155 // We do not need to updateNextDelay for the timer that actually fires;
michael@0 156 // we'll update right after it fires, with the proper intended time.
michael@0 157 // Note that we might select one, then select another later (with an
michael@0 158 // earlier intended time); it is still ok that we did not update for
michael@0 159 // the first one, since if we have skipped firings, the next delay
michael@0 160 // will be the minimum delay anyhow.
michael@0 161 if (!selected)
michael@0 162 updateNextDelay(intendedTime - now);
michael@0 163 }
michael@0 164
michael@0 165 var catMan = Cc["@mozilla.org/categorymanager;1"].
michael@0 166 getService(Ci.nsICategoryManager);
michael@0 167 var entries = catMan.enumerateCategory(CATEGORY_UPDATE_TIMER);
michael@0 168 while (entries.hasMoreElements()) {
michael@0 169 let entry = entries.getNext().QueryInterface(Ci.nsISupportsCString).data;
michael@0 170 let value = catMan.getCategoryEntry(CATEGORY_UPDATE_TIMER, entry);
michael@0 171 let [cid, method, timerID, prefInterval, defaultInterval] = value.split(",");
michael@0 172
michael@0 173 defaultInterval = parseInt(defaultInterval);
michael@0 174 // cid and method are validated below when calling notify.
michael@0 175 if (!timerID || !defaultInterval || isNaN(defaultInterval)) {
michael@0 176 LOG("TimerManager:notify - update-timer category registered" +
michael@0 177 (cid ? " for " + cid : "") + " without required parameters - " +
michael@0 178 "skipping");
michael@0 179 continue;
michael@0 180 }
michael@0 181
michael@0 182 let interval = getPref("getIntPref", prefInterval, defaultInterval);
michael@0 183 let prefLastUpdate = PREF_APP_UPDATE_LASTUPDATETIME_FMT.replace(/%ID%/,
michael@0 184 timerID);
michael@0 185 // Initialize the last update time to 0 when the preference isn't set so
michael@0 186 // the timer will be notified soon after a new profile's first use.
michael@0 187 let lastUpdateTime = getPref("getIntPref", prefLastUpdate, 0);
michael@0 188
michael@0 189 // If the last update time is greater than the current time then reset
michael@0 190 // it to 0 and the timer manager will correct the value when it fires
michael@0 191 // next for this consumer.
michael@0 192 if (lastUpdateTime > now)
michael@0 193 lastUpdateTime = 0;
michael@0 194
michael@0 195 if (lastUpdateTime == 0)
michael@0 196 Services.prefs.setIntPref(prefLastUpdate, lastUpdateTime);
michael@0 197
michael@0 198 tryFire(function() {
michael@0 199 try {
michael@0 200 Components.classes[cid][method](Ci.nsITimerCallback).notify(timer);
michael@0 201 LOG("TimerManager:notify - notified " + cid);
michael@0 202 }
michael@0 203 catch (e) {
michael@0 204 LOG("TimerManager:notify - error notifying component id: " +
michael@0 205 cid + " ,error: " + e);
michael@0 206 }
michael@0 207 lastUpdateTime = now;
michael@0 208 Services.prefs.setIntPref(prefLastUpdate, lastUpdateTime);
michael@0 209 updateNextDelay(lastUpdateTime + interval - now);
michael@0 210 }, lastUpdateTime + interval);
michael@0 211 }
michael@0 212
michael@0 213 for (let _timerID in this._timers) {
michael@0 214 let timerID = _timerID; // necessary for the closure to work properly
michael@0 215 let timerData = this._timers[timerID];
michael@0 216 // If the last update time is greater than the current time then reset
michael@0 217 // it to 0 and the timer manager will correct the value when it fires
michael@0 218 // next for this consumer.
michael@0 219 if (timerData.lastUpdateTime > now) {
michael@0 220 let prefLastUpdate = PREF_APP_UPDATE_LASTUPDATETIME_FMT.replace(/%ID%/, timerID);
michael@0 221 timerData.lastUpdateTime = 0;
michael@0 222 Services.prefs.setIntPref(prefLastUpdate, timerData.lastUpdateTime);
michael@0 223 }
michael@0 224 tryFire(function() {
michael@0 225 if (timerData.callback && timerData.callback.notify) {
michael@0 226 try {
michael@0 227 timerData.callback.notify(timer);
michael@0 228 LOG("TimerManager:notify - notified timerID: " + timerID);
michael@0 229 }
michael@0 230 catch (e) {
michael@0 231 LOG("TimerManager:notify - error notifying timerID: " + timerID +
michael@0 232 ", error: " + e);
michael@0 233 }
michael@0 234 }
michael@0 235 else {
michael@0 236 LOG("TimerManager:notify - timerID: " + timerID + " doesn't " +
michael@0 237 "implement nsITimerCallback - skipping");
michael@0 238 }
michael@0 239 lastUpdateTime = now;
michael@0 240 timerData.lastUpdateTime = lastUpdateTime;
michael@0 241 let prefLastUpdate = PREF_APP_UPDATE_LASTUPDATETIME_FMT.replace(/%ID%/, timerID);
michael@0 242 Services.prefs.setIntPref(prefLastUpdate, lastUpdateTime);
michael@0 243 updateNextDelay(timerData.lastUpdateTime + timerData.interval - now);
michael@0 244 }, timerData.lastUpdateTime + timerData.interval);
michael@0 245 }
michael@0 246
michael@0 247 if (callbackToFire)
michael@0 248 callbackToFire();
michael@0 249
michael@0 250 if (nextDelay !== null) {
michael@0 251 if (skippedFirings)
michael@0 252 timer.delay = this._timerMinimumDelay;
michael@0 253 else
michael@0 254 timer.delay = Math.max(nextDelay * 1000, this._timerMinimumDelay);
michael@0 255 this.lastTimerReset = Date.now();
michael@0 256 } else {
michael@0 257 this._cancelTimer();
michael@0 258 }
michael@0 259 },
michael@0 260
michael@0 261 /**
michael@0 262 * Starts the timer, if necessary, and ensures that it will fire soon enough
michael@0 263 * to happen after time |interval| (in milliseconds).
michael@0 264 */
michael@0 265 _ensureTimer: function(interval) {
michael@0 266 if (!this._canEnsureTimer)
michael@0 267 return;
michael@0 268 if (!this._timer) {
michael@0 269 this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
michael@0 270 this._timer.initWithCallback(this, interval,
michael@0 271 Ci.nsITimer.TYPE_REPEATING_SLACK);
michael@0 272 this.lastTimerReset = Date.now();
michael@0 273 } else {
michael@0 274 if (Date.now() + interval < this.lastTimerReset + this._timer.delay)
michael@0 275 this._timer.delay = Math.max(this.lastTimerReset + interval - Date.now(), 0);
michael@0 276 }
michael@0 277 },
michael@0 278
michael@0 279 /**
michael@0 280 * Stops the timer, if it is running.
michael@0 281 */
michael@0 282 _cancelTimer: function() {
michael@0 283 if (this._timer) {
michael@0 284 this._timer.cancel();
michael@0 285 this._timer = null;
michael@0 286 }
michael@0 287 },
michael@0 288
michael@0 289 /**
michael@0 290 * See nsIUpdateTimerManager.idl
michael@0 291 */
michael@0 292 registerTimer: function TM_registerTimer(id, callback, interval) {
michael@0 293 LOG("TimerManager:registerTimer - id: " + id);
michael@0 294 let prefLastUpdate = PREF_APP_UPDATE_LASTUPDATETIME_FMT.replace(/%ID%/, id);
michael@0 295 // Initialize the last update time to 0 when the preference isn't set so
michael@0 296 // the timer will be notified soon after a new profile's first use.
michael@0 297 let lastUpdateTime = getPref("getIntPref", prefLastUpdate, 0);
michael@0 298 let now = Math.round(Date.now() / 1000);
michael@0 299 if (lastUpdateTime > now)
michael@0 300 lastUpdateTime = 0;
michael@0 301 if (lastUpdateTime == 0)
michael@0 302 Services.prefs.setIntPref(prefLastUpdate, lastUpdateTime);
michael@0 303 this._timers[id] = { callback : callback,
michael@0 304 interval : interval,
michael@0 305 lastUpdateTime : lastUpdateTime };
michael@0 306
michael@0 307 this._ensureTimer(interval * 1000);
michael@0 308 },
michael@0 309
michael@0 310 classID: Components.ID("{B322A5C0-A419-484E-96BA-D7182163899F}"),
michael@0 311 QueryInterface: XPCOMUtils.generateQI([Ci.nsIUpdateTimerManager,
michael@0 312 Ci.nsITimerCallback,
michael@0 313 Ci.nsIObserver])
michael@0 314 };
michael@0 315
michael@0 316 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TimerManager]);

mercurial