Fri, 16 Jan 2015 18:13:44 +0100
Integrate suggestion from review to improve consistency with existing code.
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 | |
michael@0 | 6 | // This is the only implementation of nsIUrlListManager. |
michael@0 | 7 | // A class that manages lists, namely white and black lists for |
michael@0 | 8 | // phishing or malware protection. The ListManager knows how to fetch, |
michael@0 | 9 | // update, and store lists. |
michael@0 | 10 | // |
michael@0 | 11 | // There is a single listmanager for the whole application. |
michael@0 | 12 | // |
michael@0 | 13 | // TODO more comprehensive update tests, for example add unittest check |
michael@0 | 14 | // that the listmanagers tables are properly written on updates |
michael@0 | 15 | |
michael@0 | 16 | function QueryAdapter(callback) { |
michael@0 | 17 | this.callback_ = callback; |
michael@0 | 18 | }; |
michael@0 | 19 | |
michael@0 | 20 | QueryAdapter.prototype.handleResponse = function(value) { |
michael@0 | 21 | this.callback_.handleEvent(value); |
michael@0 | 22 | } |
michael@0 | 23 | |
michael@0 | 24 | /** |
michael@0 | 25 | * A ListManager keeps track of black and white lists and knows |
michael@0 | 26 | * how to update them. |
michael@0 | 27 | * |
michael@0 | 28 | * @constructor |
michael@0 | 29 | */ |
michael@0 | 30 | function PROT_ListManager() { |
michael@0 | 31 | this.debugZone = "listmanager"; |
michael@0 | 32 | G_debugService.enableZone(this.debugZone); |
michael@0 | 33 | |
michael@0 | 34 | this.currentUpdateChecker_ = null; // set when we toggle updates |
michael@0 | 35 | this.prefs_ = new G_Preferences(); |
michael@0 | 36 | this.updateInterval = this.prefs_.getPref("urlclassifier.updateinterval", 30 * 60) * 1000; |
michael@0 | 37 | |
michael@0 | 38 | this.updateserverURL_ = null; |
michael@0 | 39 | this.gethashURL_ = null; |
michael@0 | 40 | |
michael@0 | 41 | this.isTesting_ = false; |
michael@0 | 42 | |
michael@0 | 43 | this.tablesData = {}; |
michael@0 | 44 | |
michael@0 | 45 | this.observerServiceObserver_ = new G_ObserverServiceObserver( |
michael@0 | 46 | 'quit-application', |
michael@0 | 47 | BindToObject(this.shutdown_, this), |
michael@0 | 48 | true /*only once*/); |
michael@0 | 49 | |
michael@0 | 50 | this.cookieObserver_ = new G_ObserverServiceObserver( |
michael@0 | 51 | 'cookie-changed', |
michael@0 | 52 | BindToObject(this.cookieChanged_, this), |
michael@0 | 53 | false); |
michael@0 | 54 | |
michael@0 | 55 | /* Backoff interval should be between 30 and 60 minutes. */ |
michael@0 | 56 | var backoffInterval = 30 * 60 * 1000; |
michael@0 | 57 | backoffInterval += Math.floor(Math.random() * (30 * 60 * 1000)); |
michael@0 | 58 | |
michael@0 | 59 | this.requestBackoff_ = new RequestBackoff(2 /* max errors */, |
michael@0 | 60 | 60*1000 /* retry interval, 1 min */, |
michael@0 | 61 | 4 /* num requests */, |
michael@0 | 62 | 60*60*1000 /* request time, 60 min */, |
michael@0 | 63 | backoffInterval /* backoff interval, 60 min */, |
michael@0 | 64 | 8*60*60*1000 /* max backoff, 8hr */); |
michael@0 | 65 | |
michael@0 | 66 | this.dbService_ = Cc["@mozilla.org/url-classifier/dbservice;1"] |
michael@0 | 67 | .getService(Ci.nsIUrlClassifierDBService); |
michael@0 | 68 | |
michael@0 | 69 | this.hashCompleter_ = Cc["@mozilla.org/url-classifier/hashcompleter;1"] |
michael@0 | 70 | .getService(Ci.nsIUrlClassifierHashCompleter); |
michael@0 | 71 | } |
michael@0 | 72 | |
michael@0 | 73 | /** |
michael@0 | 74 | * xpcom-shutdown callback |
michael@0 | 75 | * Delete all of our data tables which seem to leak otherwise. |
michael@0 | 76 | */ |
michael@0 | 77 | PROT_ListManager.prototype.shutdown_ = function() { |
michael@0 | 78 | for (var name in this.tablesData) { |
michael@0 | 79 | delete this.tablesData[name]; |
michael@0 | 80 | } |
michael@0 | 81 | } |
michael@0 | 82 | |
michael@0 | 83 | /** |
michael@0 | 84 | * Set the url we check for updates. If the new url is valid and different, |
michael@0 | 85 | * update our table list. |
michael@0 | 86 | * |
michael@0 | 87 | * After setting the update url, the caller is responsible for registering |
michael@0 | 88 | * tables and then toggling update checking. All the code for this logic is |
michael@0 | 89 | * currently in browser/components/safebrowsing. Maybe it should be part of |
michael@0 | 90 | * the listmanger? |
michael@0 | 91 | */ |
michael@0 | 92 | PROT_ListManager.prototype.setUpdateUrl = function(url) { |
michael@0 | 93 | G_Debug(this, "Set update url: " + url); |
michael@0 | 94 | if (url != this.updateserverURL_) { |
michael@0 | 95 | this.updateserverURL_ = url; |
michael@0 | 96 | this.requestBackoff_.reset(); |
michael@0 | 97 | |
michael@0 | 98 | // Remove old tables which probably aren't valid for the new provider. |
michael@0 | 99 | for (var name in this.tablesData) { |
michael@0 | 100 | delete this.tablesData[name]; |
michael@0 | 101 | } |
michael@0 | 102 | } |
michael@0 | 103 | } |
michael@0 | 104 | |
michael@0 | 105 | /** |
michael@0 | 106 | * Set the gethash url. |
michael@0 | 107 | */ |
michael@0 | 108 | PROT_ListManager.prototype.setGethashUrl = function(url) { |
michael@0 | 109 | G_Debug(this, "Set gethash url: " + url); |
michael@0 | 110 | if (url != this.gethashURL_) { |
michael@0 | 111 | this.gethashURL_ = url; |
michael@0 | 112 | this.hashCompleter_.gethashUrl = url; |
michael@0 | 113 | } |
michael@0 | 114 | } |
michael@0 | 115 | |
michael@0 | 116 | /** |
michael@0 | 117 | * Register a new table table |
michael@0 | 118 | * @param tableName - the name of the table |
michael@0 | 119 | * @param opt_requireMac true if a mac is required on update, false otherwise |
michael@0 | 120 | * @returns true if the table could be created; false otherwise |
michael@0 | 121 | */ |
michael@0 | 122 | PROT_ListManager.prototype.registerTable = function(tableName, |
michael@0 | 123 | opt_requireMac) { |
michael@0 | 124 | this.tablesData[tableName] = {}; |
michael@0 | 125 | this.tablesData[tableName].needsUpdate = false; |
michael@0 | 126 | |
michael@0 | 127 | return true; |
michael@0 | 128 | } |
michael@0 | 129 | |
michael@0 | 130 | /** |
michael@0 | 131 | * Enable updates for some tables |
michael@0 | 132 | * @param tables - an array of table names that need updating |
michael@0 | 133 | */ |
michael@0 | 134 | PROT_ListManager.prototype.enableUpdate = function(tableName) { |
michael@0 | 135 | var changed = false; |
michael@0 | 136 | var table = this.tablesData[tableName]; |
michael@0 | 137 | if (table) { |
michael@0 | 138 | G_Debug(this, "Enabling table updates for " + tableName); |
michael@0 | 139 | table.needsUpdate = true; |
michael@0 | 140 | changed = true; |
michael@0 | 141 | } |
michael@0 | 142 | |
michael@0 | 143 | if (changed === true) |
michael@0 | 144 | this.maybeToggleUpdateChecking(); |
michael@0 | 145 | } |
michael@0 | 146 | |
michael@0 | 147 | /** |
michael@0 | 148 | * Disables updates for some tables |
michael@0 | 149 | * @param tables - an array of table names that no longer need updating |
michael@0 | 150 | */ |
michael@0 | 151 | PROT_ListManager.prototype.disableUpdate = function(tableName) { |
michael@0 | 152 | var changed = false; |
michael@0 | 153 | var table = this.tablesData[tableName]; |
michael@0 | 154 | if (table) { |
michael@0 | 155 | G_Debug(this, "Disabling table updates for " + tableName); |
michael@0 | 156 | table.needsUpdate = false; |
michael@0 | 157 | changed = true; |
michael@0 | 158 | } |
michael@0 | 159 | |
michael@0 | 160 | if (changed === true) |
michael@0 | 161 | this.maybeToggleUpdateChecking(); |
michael@0 | 162 | } |
michael@0 | 163 | |
michael@0 | 164 | /** |
michael@0 | 165 | * Determine if we have some tables that need updating. |
michael@0 | 166 | */ |
michael@0 | 167 | PROT_ListManager.prototype.requireTableUpdates = function() { |
michael@0 | 168 | for (var type in this.tablesData) { |
michael@0 | 169 | // Tables that need updating even if other tables dont require it |
michael@0 | 170 | if (this.tablesData[type].needsUpdate) |
michael@0 | 171 | return true; |
michael@0 | 172 | } |
michael@0 | 173 | |
michael@0 | 174 | return false; |
michael@0 | 175 | } |
michael@0 | 176 | |
michael@0 | 177 | /** |
michael@0 | 178 | * Start managing the lists we know about. We don't do this automatically |
michael@0 | 179 | * when the listmanager is instantiated because their profile directory |
michael@0 | 180 | * (where we store the lists) might not be available. |
michael@0 | 181 | */ |
michael@0 | 182 | PROT_ListManager.prototype.maybeStartManagingUpdates = function() { |
michael@0 | 183 | if (this.isTesting_) |
michael@0 | 184 | return; |
michael@0 | 185 | |
michael@0 | 186 | // We might have been told about tables already, so see if we should be |
michael@0 | 187 | // actually updating. |
michael@0 | 188 | this.maybeToggleUpdateChecking(); |
michael@0 | 189 | } |
michael@0 | 190 | |
michael@0 | 191 | /** |
michael@0 | 192 | * Acts as a nsIUrlClassifierCallback for getTables. |
michael@0 | 193 | */ |
michael@0 | 194 | PROT_ListManager.prototype.kickoffUpdate_ = function (onDiskTableData) |
michael@0 | 195 | { |
michael@0 | 196 | this.startingUpdate_ = false; |
michael@0 | 197 | var initialUpdateDelay = 3000; |
michael@0 | 198 | |
michael@0 | 199 | // Check if any table registered for updates has ever been downloaded. |
michael@0 | 200 | var diskTablesAreUpdating = false; |
michael@0 | 201 | for (var tableName in this.tablesData) { |
michael@0 | 202 | if (this.tablesData[tableName].needsUpdate) { |
michael@0 | 203 | if (onDiskTableData.indexOf(tableName) != -1) { |
michael@0 | 204 | diskTablesAreUpdating = true; |
michael@0 | 205 | } |
michael@0 | 206 | } |
michael@0 | 207 | } |
michael@0 | 208 | |
michael@0 | 209 | // If the user has never downloaded tables, do the check now. |
michael@0 | 210 | // If the user has tables, add a fuzz of a few minutes. |
michael@0 | 211 | if (diskTablesAreUpdating) { |
michael@0 | 212 | // Add a fuzz of 0-5 minutes. |
michael@0 | 213 | initialUpdateDelay += Math.floor(Math.random() * (5 * 60 * 1000)); |
michael@0 | 214 | } |
michael@0 | 215 | |
michael@0 | 216 | this.currentUpdateChecker_ = |
michael@0 | 217 | new G_Alarm(BindToObject(this.checkForUpdates, this), |
michael@0 | 218 | initialUpdateDelay); |
michael@0 | 219 | } |
michael@0 | 220 | |
michael@0 | 221 | /** |
michael@0 | 222 | * Determine if we have any tables that require updating. Different |
michael@0 | 223 | * Wardens may call us with new tables that need to be updated. |
michael@0 | 224 | */ |
michael@0 | 225 | PROT_ListManager.prototype.maybeToggleUpdateChecking = function() { |
michael@0 | 226 | // If we are testing or dont have an application directory yet, we should |
michael@0 | 227 | // not start reading tables from disk or schedule remote updates |
michael@0 | 228 | if (this.isTesting_) |
michael@0 | 229 | return; |
michael@0 | 230 | |
michael@0 | 231 | // We update tables if we have some tables that want updates. If there |
michael@0 | 232 | // are no tables that want to be updated - we dont need to check anything. |
michael@0 | 233 | if (this.requireTableUpdates() === true) { |
michael@0 | 234 | G_Debug(this, "Starting managing lists"); |
michael@0 | 235 | this.startUpdateChecker(); |
michael@0 | 236 | |
michael@0 | 237 | // Multiple warden can ask us to reenable updates at the same time, but we |
michael@0 | 238 | // really just need to schedule a single update. |
michael@0 | 239 | if (!this.currentUpdateChecker && !this.startingUpdate_) { |
michael@0 | 240 | this.startingUpdate_ = true; |
michael@0 | 241 | // check the current state of tables in the database |
michael@0 | 242 | this.dbService_.getTables(BindToObject(this.kickoffUpdate_, this)); |
michael@0 | 243 | } |
michael@0 | 244 | } else { |
michael@0 | 245 | G_Debug(this, "Stopping managing lists (if currently active)"); |
michael@0 | 246 | this.stopUpdateChecker(); // Cancel pending updates |
michael@0 | 247 | } |
michael@0 | 248 | } |
michael@0 | 249 | |
michael@0 | 250 | /** |
michael@0 | 251 | * Start periodic checks for updates. Idempotent. |
michael@0 | 252 | * We want to distribute update checks evenly across the update period (an |
michael@0 | 253 | * hour). The first update is scheduled for a random time between 0.5 and 1.5 |
michael@0 | 254 | * times the update interval. |
michael@0 | 255 | */ |
michael@0 | 256 | PROT_ListManager.prototype.startUpdateChecker = function() { |
michael@0 | 257 | this.stopUpdateChecker(); |
michael@0 | 258 | |
michael@0 | 259 | // Schedule the first check for between 15 and 45 minutes. |
michael@0 | 260 | var repeatingUpdateDelay = this.updateInterval / 2; |
michael@0 | 261 | repeatingUpdateDelay += Math.floor(Math.random() * this.updateInterval); |
michael@0 | 262 | this.updateChecker_ = new G_Alarm(BindToObject(this.initialUpdateCheck_, |
michael@0 | 263 | this), |
michael@0 | 264 | repeatingUpdateDelay); |
michael@0 | 265 | } |
michael@0 | 266 | |
michael@0 | 267 | /** |
michael@0 | 268 | * Callback for the first update check. |
michael@0 | 269 | * We go ahead and check for table updates, then start a regular timer (once |
michael@0 | 270 | * every update interval). |
michael@0 | 271 | */ |
michael@0 | 272 | PROT_ListManager.prototype.initialUpdateCheck_ = function() { |
michael@0 | 273 | this.checkForUpdates(); |
michael@0 | 274 | this.updateChecker_ = new G_Alarm(BindToObject(this.checkForUpdates, this), |
michael@0 | 275 | this.updateInterval, true /* repeat */); |
michael@0 | 276 | } |
michael@0 | 277 | |
michael@0 | 278 | /** |
michael@0 | 279 | * Stop checking for updates. Idempotent. |
michael@0 | 280 | */ |
michael@0 | 281 | PROT_ListManager.prototype.stopUpdateChecker = function() { |
michael@0 | 282 | if (this.updateChecker_) { |
michael@0 | 283 | this.updateChecker_.cancel(); |
michael@0 | 284 | this.updateChecker_ = null; |
michael@0 | 285 | } |
michael@0 | 286 | // Cancel the oneoff check from maybeToggleUpdateChecking. |
michael@0 | 287 | if (this.currentUpdateChecker_) { |
michael@0 | 288 | this.currentUpdateChecker_.cancel(); |
michael@0 | 289 | this.currentUpdateChecker_ = null; |
michael@0 | 290 | } |
michael@0 | 291 | } |
michael@0 | 292 | |
michael@0 | 293 | /** |
michael@0 | 294 | * Provides an exception free way to look up the data in a table. We |
michael@0 | 295 | * use this because at certain points our tables might not be loaded, |
michael@0 | 296 | * and querying them could throw. |
michael@0 | 297 | * |
michael@0 | 298 | * @param table String Name of the table that we want to consult |
michael@0 | 299 | * @param key Principal being used to lookup the database |
michael@0 | 300 | * @param callback nsIUrlListManagerCallback (ie., Function) given false or the |
michael@0 | 301 | * value in the table corresponding to key. If the table name does not |
michael@0 | 302 | * exist, we return false, too. |
michael@0 | 303 | */ |
michael@0 | 304 | PROT_ListManager.prototype.safeLookup = function(key, callback) { |
michael@0 | 305 | try { |
michael@0 | 306 | G_Debug(this, "safeLookup: " + key); |
michael@0 | 307 | var cb = new QueryAdapter(callback); |
michael@0 | 308 | this.dbService_.lookup(key, |
michael@0 | 309 | BindToObject(cb.handleResponse, cb), |
michael@0 | 310 | true); |
michael@0 | 311 | } catch(e) { |
michael@0 | 312 | G_Debug(this, "safeLookup masked failure for key " + key + ": " + e); |
michael@0 | 313 | callback.handleEvent(""); |
michael@0 | 314 | } |
michael@0 | 315 | } |
michael@0 | 316 | |
michael@0 | 317 | /** |
michael@0 | 318 | * Updates our internal tables from the update server |
michael@0 | 319 | * |
michael@0 | 320 | * @returns true when a new request was scheduled, false if an old request |
michael@0 | 321 | * was still pending. |
michael@0 | 322 | */ |
michael@0 | 323 | PROT_ListManager.prototype.checkForUpdates = function() { |
michael@0 | 324 | // Allow new updates to be scheduled from maybeToggleUpdateChecking() |
michael@0 | 325 | this.currentUpdateChecker_ = null; |
michael@0 | 326 | |
michael@0 | 327 | if (!this.updateserverURL_) { |
michael@0 | 328 | G_Debug(this, 'checkForUpdates: no update server url'); |
michael@0 | 329 | return false; |
michael@0 | 330 | } |
michael@0 | 331 | |
michael@0 | 332 | // See if we've triggered the request backoff logic. |
michael@0 | 333 | if (!this.requestBackoff_.canMakeRequest()) |
michael@0 | 334 | return false; |
michael@0 | 335 | |
michael@0 | 336 | // Grab the current state of the tables from the database |
michael@0 | 337 | this.dbService_.getTables(BindToObject(this.makeUpdateRequest_, this)); |
michael@0 | 338 | return true; |
michael@0 | 339 | } |
michael@0 | 340 | |
michael@0 | 341 | /** |
michael@0 | 342 | * Method that fires the actual HTTP update request. |
michael@0 | 343 | * First we reset any tables that have disappeared. |
michael@0 | 344 | * @param tableData List of table data already in the database, in the form |
michael@0 | 345 | * tablename;<chunk ranges>\n |
michael@0 | 346 | */ |
michael@0 | 347 | PROT_ListManager.prototype.makeUpdateRequest_ = function(tableData) { |
michael@0 | 348 | var tableList; |
michael@0 | 349 | var tableNames = {}; |
michael@0 | 350 | for (var tableName in this.tablesData) { |
michael@0 | 351 | if (this.tablesData[tableName].needsUpdate) |
michael@0 | 352 | tableNames[tableName] = true; |
michael@0 | 353 | if (!tableList) { |
michael@0 | 354 | tableList = tableName; |
michael@0 | 355 | } else { |
michael@0 | 356 | tableList += "," + tableName; |
michael@0 | 357 | } |
michael@0 | 358 | } |
michael@0 | 359 | |
michael@0 | 360 | var request = ""; |
michael@0 | 361 | |
michael@0 | 362 | // For each table already in the database, include the chunk data from |
michael@0 | 363 | // the database |
michael@0 | 364 | var lines = tableData.split("\n"); |
michael@0 | 365 | for (var i = 0; i < lines.length; i++) { |
michael@0 | 366 | var fields = lines[i].split(";"); |
michael@0 | 367 | if (tableNames[fields[0]]) { |
michael@0 | 368 | request += lines[i] + "\n"; |
michael@0 | 369 | delete tableNames[fields[0]]; |
michael@0 | 370 | } |
michael@0 | 371 | } |
michael@0 | 372 | |
michael@0 | 373 | // For each requested table that didn't have chunk data in the database, |
michael@0 | 374 | // request it fresh |
michael@0 | 375 | for (var tableName in tableNames) { |
michael@0 | 376 | request += tableName + ";\n"; |
michael@0 | 377 | } |
michael@0 | 378 | |
michael@0 | 379 | G_Debug(this, 'checkForUpdates: scheduling request..'); |
michael@0 | 380 | var streamer = Cc["@mozilla.org/url-classifier/streamupdater;1"] |
michael@0 | 381 | .getService(Ci.nsIUrlClassifierStreamUpdater); |
michael@0 | 382 | try { |
michael@0 | 383 | streamer.updateUrl = this.updateserverURL_; |
michael@0 | 384 | } catch (e) { |
michael@0 | 385 | G_Debug(this, 'invalid url'); |
michael@0 | 386 | return; |
michael@0 | 387 | } |
michael@0 | 388 | |
michael@0 | 389 | this.requestBackoff_.noteRequest(); |
michael@0 | 390 | |
michael@0 | 391 | if (!streamer.downloadUpdates(tableList, |
michael@0 | 392 | request, |
michael@0 | 393 | BindToObject(this.updateSuccess_, this), |
michael@0 | 394 | BindToObject(this.updateError_, this), |
michael@0 | 395 | BindToObject(this.downloadError_, this))) { |
michael@0 | 396 | G_Debug(this, "pending update, wait until later"); |
michael@0 | 397 | } |
michael@0 | 398 | } |
michael@0 | 399 | |
michael@0 | 400 | /** |
michael@0 | 401 | * Callback function if the update request succeeded. |
michael@0 | 402 | * @param waitForUpdate String The number of seconds that the client should |
michael@0 | 403 | * wait before requesting again. |
michael@0 | 404 | */ |
michael@0 | 405 | PROT_ListManager.prototype.updateSuccess_ = function(waitForUpdate) { |
michael@0 | 406 | G_Debug(this, "update success: " + waitForUpdate); |
michael@0 | 407 | if (waitForUpdate) { |
michael@0 | 408 | var delay = parseInt(waitForUpdate, 10); |
michael@0 | 409 | // As long as the delay is something sane (5 minutes or more), update |
michael@0 | 410 | // our delay time for requesting updates |
michael@0 | 411 | if (delay >= (5 * 60) && this.updateChecker_) |
michael@0 | 412 | this.updateChecker_.setDelay(delay * 1000); |
michael@0 | 413 | } |
michael@0 | 414 | |
michael@0 | 415 | // Let the backoff object know that we completed successfully. |
michael@0 | 416 | this.requestBackoff_.noteServerResponse(200); |
michael@0 | 417 | } |
michael@0 | 418 | |
michael@0 | 419 | /** |
michael@0 | 420 | * Callback function if the update request succeeded. |
michael@0 | 421 | * @param result String The error code of the failure |
michael@0 | 422 | */ |
michael@0 | 423 | PROT_ListManager.prototype.updateError_ = function(result) { |
michael@0 | 424 | G_Debug(this, "update error: " + result); |
michael@0 | 425 | // XXX: there was some trouble applying the updates. |
michael@0 | 426 | } |
michael@0 | 427 | |
michael@0 | 428 | /** |
michael@0 | 429 | * Callback function when the download failed |
michael@0 | 430 | * @param status String http status or an empty string if connection refused. |
michael@0 | 431 | */ |
michael@0 | 432 | PROT_ListManager.prototype.downloadError_ = function(status) { |
michael@0 | 433 | G_Debug(this, "download error: " + status); |
michael@0 | 434 | // If status is empty, then we assume that we got an NS_CONNECTION_REFUSED |
michael@0 | 435 | // error. In this case, we treat this is a http 500 error. |
michael@0 | 436 | if (!status) { |
michael@0 | 437 | status = 500; |
michael@0 | 438 | } |
michael@0 | 439 | status = parseInt(status, 10); |
michael@0 | 440 | this.requestBackoff_.noteServerResponse(status); |
michael@0 | 441 | |
michael@0 | 442 | if (this.requestBackoff_.isErrorStatus(status)) { |
michael@0 | 443 | // Schedule an update for when our backoff is complete |
michael@0 | 444 | this.currentUpdateChecker_ = |
michael@0 | 445 | new G_Alarm(BindToObject(this.checkForUpdates, this), |
michael@0 | 446 | this.requestBackoff_.nextRequestDelay()); |
michael@0 | 447 | } |
michael@0 | 448 | } |
michael@0 | 449 | |
michael@0 | 450 | /** |
michael@0 | 451 | * Called when cookies are cleared |
michael@0 | 452 | */ |
michael@0 | 453 | PROT_ListManager.prototype.cookieChanged_ = function(subject, topic, data) { |
michael@0 | 454 | if (data != "cleared") |
michael@0 | 455 | return; |
michael@0 | 456 | |
michael@0 | 457 | G_Debug(this, "cookies cleared"); |
michael@0 | 458 | } |
michael@0 | 459 | |
michael@0 | 460 | PROT_ListManager.prototype.QueryInterface = function(iid) { |
michael@0 | 461 | if (iid.equals(Ci.nsISupports) || |
michael@0 | 462 | iid.equals(Ci.nsIUrlListManager) || |
michael@0 | 463 | iid.equals(Ci.nsITimerCallback)) |
michael@0 | 464 | return this; |
michael@0 | 465 | |
michael@0 | 466 | throw Components.results.NS_ERROR_NO_INTERFACE; |
michael@0 | 467 | } |