dom/alarm/AlarmService.jsm

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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 file,
michael@0 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 "use strict";
michael@0 6
michael@0 7 /* static functions */
michael@0 8 const DEBUG = false;
michael@0 9
michael@0 10 function debug(aStr) {
michael@0 11 if (DEBUG)
michael@0 12 dump("AlarmService: " + aStr + "\n");
michael@0 13 }
michael@0 14
michael@0 15 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
michael@0 16
michael@0 17 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
michael@0 18 Cu.import("resource://gre/modules/Services.jsm");
michael@0 19 Cu.import("resource://gre/modules/AlarmDB.jsm");
michael@0 20
michael@0 21 this.EXPORTED_SYMBOLS = ["AlarmService"];
michael@0 22
michael@0 23 XPCOMUtils.defineLazyGetter(this, "appsService", function() {
michael@0 24 return Cc["@mozilla.org/AppsService;1"].getService(Ci.nsIAppsService);
michael@0 25 });
michael@0 26
michael@0 27 XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
michael@0 28 "@mozilla.org/parentprocessmessagemanager;1",
michael@0 29 "nsIMessageListenerManager");
michael@0 30
michael@0 31 XPCOMUtils.defineLazyGetter(this, "messenger", function() {
michael@0 32 return Cc["@mozilla.org/system-message-internal;1"].getService(Ci.nsISystemMessagesInternal);
michael@0 33 });
michael@0 34
michael@0 35 XPCOMUtils.defineLazyGetter(this, "powerManagerService", function() {
michael@0 36 return Cc["@mozilla.org/power/powermanagerservice;1"].getService(Ci.nsIPowerManagerService);
michael@0 37 });
michael@0 38
michael@0 39 /**
michael@0 40 * AlarmService provides an API to schedule alarms using the device's RTC.
michael@0 41 *
michael@0 42 * AlarmService is primarily used by the mozAlarms API (navigator.mozAlarms)
michael@0 43 * which uses IPC to communicate with the service.
michael@0 44 *
michael@0 45 * AlarmService can also be used by Gecko code by importing the module and then
michael@0 46 * using AlarmService.add() and AlarmService.remove(). Only Gecko code running
michael@0 47 * in the parent process should do this.
michael@0 48 */
michael@0 49
michael@0 50 this.AlarmService = {
michael@0 51 init: function init() {
michael@0 52 debug("init()");
michael@0 53 Services.obs.addObserver(this, "profile-change-teardown", false);
michael@0 54 Services.obs.addObserver(this, "webapps-clear-data",false);
michael@0 55
michael@0 56 this._currentTimezoneOffset = (new Date()).getTimezoneOffset();
michael@0 57
michael@0 58 let alarmHalService =
michael@0 59 this._alarmHalService = Cc["@mozilla.org/alarmHalService;1"]
michael@0 60 .getService(Ci.nsIAlarmHalService);
michael@0 61
michael@0 62 alarmHalService.setAlarmFiredCb(this._onAlarmFired.bind(this));
michael@0 63 alarmHalService.setTimezoneChangedCb(this._onTimezoneChanged.bind(this));
michael@0 64
michael@0 65 // Add the messages to be listened to.
michael@0 66 this._messages = ["AlarmsManager:GetAll",
michael@0 67 "AlarmsManager:Add",
michael@0 68 "AlarmsManager:Remove"];
michael@0 69 this._messages.forEach(function addMessage(msgName) {
michael@0 70 ppmm.addMessageListener(msgName, this);
michael@0 71 }.bind(this));
michael@0 72
michael@0 73 // Set the indexeddb database.
michael@0 74 this._db = new AlarmDB();
michael@0 75 this._db.init();
michael@0 76
michael@0 77 // Variable to save alarms waiting to be set.
michael@0 78 this._alarmQueue = [];
michael@0 79
michael@0 80 this._restoreAlarmsFromDb();
michael@0 81 },
michael@0 82
michael@0 83 // Getter/setter to access the current alarm set in system.
michael@0 84 _alarm: null,
michael@0 85 get _currentAlarm() {
michael@0 86 return this._alarm;
michael@0 87 },
michael@0 88 set _currentAlarm(aAlarm) {
michael@0 89 this._alarm = aAlarm;
michael@0 90 if (!aAlarm) {
michael@0 91 return;
michael@0 92 }
michael@0 93
michael@0 94 let alarmTimeInMs = this._getAlarmTime(aAlarm);
michael@0 95 let ns = (alarmTimeInMs % 1000) * 1000000;
michael@0 96 if (!this._alarmHalService.setAlarm(alarmTimeInMs / 1000, ns)) {
michael@0 97 throw Components.results.NS_ERROR_FAILURE;
michael@0 98 }
michael@0 99 },
michael@0 100
michael@0 101 receiveMessage: function receiveMessage(aMessage) {
michael@0 102 debug("receiveMessage(): " + aMessage.name);
michael@0 103 let json = aMessage.json;
michael@0 104
michael@0 105 // To prevent the hacked child process from sending commands to parent
michael@0 106 // to schedule alarms, we need to check its permission and manifest URL.
michael@0 107 if (this._messages.indexOf(aMessage.name) != -1) {
michael@0 108 if (!aMessage.target.assertPermission("alarms")) {
michael@0 109 debug("Got message from a child process with no 'alarms' permission.");
michael@0 110 return null;
michael@0 111 }
michael@0 112 if (!aMessage.target.assertContainApp(json.manifestURL)) {
michael@0 113 debug("Got message from a child process containing illegal manifest URL.");
michael@0 114 return null;
michael@0 115 }
michael@0 116 }
michael@0 117
michael@0 118 let mm = aMessage.target.QueryInterface(Ci.nsIMessageSender);
michael@0 119 switch (aMessage.name) {
michael@0 120 case "AlarmsManager:GetAll":
michael@0 121 this._db.getAll(
michael@0 122 json.manifestURL,
michael@0 123 function getAllSuccessCb(aAlarms) {
michael@0 124 debug("Callback after getting alarms from database: " +
michael@0 125 JSON.stringify(aAlarms));
michael@0 126 this._sendAsyncMessage(mm, "GetAll", true, json.requestId, aAlarms);
michael@0 127 }.bind(this),
michael@0 128 function getAllErrorCb(aErrorMsg) {
michael@0 129 this._sendAsyncMessage(mm, "GetAll", false, json.requestId, aErrorMsg);
michael@0 130 }.bind(this)
michael@0 131 );
michael@0 132 break;
michael@0 133
michael@0 134 case "AlarmsManager:Add":
michael@0 135 // Prepare a record for the new alarm to be added.
michael@0 136 let newAlarm = {
michael@0 137 date: json.date,
michael@0 138 ignoreTimezone: json.ignoreTimezone,
michael@0 139 data: json.data,
michael@0 140 pageURL: json.pageURL,
michael@0 141 manifestURL: json.manifestURL
michael@0 142 };
michael@0 143
michael@0 144 this.add(newAlarm, null,
michael@0 145 // Receives the alarm ID as the last argument.
michael@0 146 this._sendAsyncMessage.bind(this, mm, "Add", true, json.requestId),
michael@0 147 // Receives the error message as the last argument.
michael@0 148 this._sendAsyncMessage.bind(this, mm, "Add", false, json.requestId)
michael@0 149 );
michael@0 150 break;
michael@0 151
michael@0 152 case "AlarmsManager:Remove":
michael@0 153 this.remove(json.id, json.manifestURL);
michael@0 154 break;
michael@0 155
michael@0 156 default:
michael@0 157 throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
michael@0 158 break;
michael@0 159 }
michael@0 160 },
michael@0 161
michael@0 162 _sendAsyncMessage: function _sendAsyncMessage(aMessageManager, aMessageName,
michael@0 163 aSuccess, aRequestId, aData) {
michael@0 164 debug("_sendAsyncMessage()");
michael@0 165
michael@0 166 if (!aMessageManager) {
michael@0 167 debug("Invalid message manager: null");
michael@0 168 throw Components.results.NS_ERROR_FAILURE;
michael@0 169 }
michael@0 170
michael@0 171 let json = null;
michael@0 172 switch (aMessageName)
michael@0 173 {
michael@0 174 case "Add":
michael@0 175 json = aSuccess ?
michael@0 176 { requestId: aRequestId, id: aData } :
michael@0 177 { requestId: aRequestId, errorMsg: aData };
michael@0 178 break;
michael@0 179
michael@0 180 case "GetAll":
michael@0 181 json = aSuccess ?
michael@0 182 { requestId: aRequestId, alarms: aData } :
michael@0 183 { requestId: aRequestId, errorMsg: aData };
michael@0 184 break;
michael@0 185
michael@0 186 default:
michael@0 187 throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
michael@0 188 break;
michael@0 189 }
michael@0 190
michael@0 191 aMessageManager.sendAsyncMessage("AlarmsManager:" + aMessageName +
michael@0 192 ":Return:" + (aSuccess ? "OK" : "KO"), json);
michael@0 193 },
michael@0 194
michael@0 195 _removeAlarmFromDb: function _removeAlarmFromDb(aId, aManifestURL,
michael@0 196 aRemoveSuccessCb) {
michael@0 197 debug("_removeAlarmFromDb()");
michael@0 198
michael@0 199 // If the aRemoveSuccessCb is undefined or null, set a dummy callback for
michael@0 200 // it which is needed for _db.remove().
michael@0 201 if (!aRemoveSuccessCb) {
michael@0 202 aRemoveSuccessCb = function removeSuccessCb() {
michael@0 203 debug("Remove alarm from DB successfully.");
michael@0 204 };
michael@0 205 }
michael@0 206
michael@0 207 this._db.remove(
michael@0 208 aId,
michael@0 209 aManifestURL,
michael@0 210 aRemoveSuccessCb,
michael@0 211 function removeErrorCb(aErrorMsg) {
michael@0 212 throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
michael@0 213 }
michael@0 214 );
michael@0 215 },
michael@0 216
michael@0 217 /**
michael@0 218 * Create a copy of the alarm that does not expose internal fields to
michael@0 219 * receivers and sticks to the public |respectTimezone| API rather than the
michael@0 220 * boolean |ignoreTimezone| field.
michael@0 221 */
michael@0 222 _publicAlarm: function _publicAlarm(aAlarm) {
michael@0 223 let alarm = {
michael@0 224 "id": aAlarm.id,
michael@0 225 "date": aAlarm.date,
michael@0 226 "respectTimezone": aAlarm.ignoreTimezone ?
michael@0 227 "ignoreTimezone" : "honorTimezone",
michael@0 228 "data": aAlarm.data
michael@0 229 };
michael@0 230
michael@0 231 return alarm;
michael@0 232 },
michael@0 233
michael@0 234 _fireSystemMessage: function _fireSystemMessage(aAlarm) {
michael@0 235 debug("Fire system message: " + JSON.stringify(aAlarm));
michael@0 236
michael@0 237 let manifestURI = Services.io.newURI(aAlarm.manifestURL, null, null);
michael@0 238 let pageURI = Services.io.newURI(aAlarm.pageURL, null, null);
michael@0 239
michael@0 240 messenger.sendMessage("alarm", this._publicAlarm(aAlarm),
michael@0 241 pageURI, manifestURI);
michael@0 242 },
michael@0 243
michael@0 244 _notifyAlarmObserver: function _notifyAlarmObserver(aAlarm) {
michael@0 245 debug("_notifyAlarmObserver()");
michael@0 246
michael@0 247 if (aAlarm.manifestURL) {
michael@0 248 this._fireSystemMessage(aAlarm);
michael@0 249 } else if (typeof aAlarm.alarmFiredCb === "function") {
michael@0 250 aAlarm.alarmFiredCb(this._publicAlarm(aAlarm));
michael@0 251 }
michael@0 252 },
michael@0 253
michael@0 254 _onAlarmFired: function _onAlarmFired() {
michael@0 255 debug("_onAlarmFired()");
michael@0 256
michael@0 257 if (this._currentAlarm) {
michael@0 258 this._removeAlarmFromDb(this._currentAlarm.id, null);
michael@0 259 this._notifyAlarmObserver(this._currentAlarm);
michael@0 260 this._currentAlarm = null;
michael@0 261 }
michael@0 262
michael@0 263 // Reset the next alarm from the queue.
michael@0 264 let alarmQueue = this._alarmQueue;
michael@0 265 while (alarmQueue.length > 0) {
michael@0 266 let nextAlarm = alarmQueue.shift();
michael@0 267 let nextAlarmTime = this._getAlarmTime(nextAlarm);
michael@0 268
michael@0 269 // If the next alarm has been expired, directly notify the observer.
michael@0 270 // it instead of setting it.
michael@0 271 if (nextAlarmTime <= Date.now()) {
michael@0 272 this._removeAlarmFromDb(nextAlarm.id, null);
michael@0 273 this._notifyAlarmObserver(nextAlarm);
michael@0 274 } else {
michael@0 275 this._currentAlarm = nextAlarm;
michael@0 276 break;
michael@0 277 }
michael@0 278 }
michael@0 279 this._debugCurrentAlarm();
michael@0 280 },
michael@0 281
michael@0 282 _onTimezoneChanged: function _onTimezoneChanged(aTimezoneOffset) {
michael@0 283 debug("_onTimezoneChanged()");
michael@0 284
michael@0 285 this._currentTimezoneOffset = aTimezoneOffset;
michael@0 286 this._restoreAlarmsFromDb();
michael@0 287 },
michael@0 288
michael@0 289 _restoreAlarmsFromDb: function _restoreAlarmsFromDb() {
michael@0 290 debug("_restoreAlarmsFromDb()");
michael@0 291
michael@0 292 this._db.getAll(
michael@0 293 null,
michael@0 294 function getAllSuccessCb(aAlarms) {
michael@0 295 debug("Callback after getting alarms from database: " +
michael@0 296 JSON.stringify(aAlarms));
michael@0 297
michael@0 298 // Clear any alarms set or queued in the cache.
michael@0 299 let alarmQueue = this._alarmQueue;
michael@0 300 alarmQueue.length = 0;
michael@0 301 this._currentAlarm = null;
michael@0 302
michael@0 303 // Only restore the alarm that's not yet expired; otherwise, remove it
michael@0 304 // from the database and notify the observer.
michael@0 305 aAlarms.forEach(function addAlarm(aAlarm) {
michael@0 306 if (this._getAlarmTime(aAlarm) > Date.now()) {
michael@0 307 alarmQueue.push(aAlarm);
michael@0 308 } else {
michael@0 309 this._removeAlarmFromDb(aAlarm.id, null);
michael@0 310 this._notifyAlarmObserver(aAlarm);
michael@0 311 }
michael@0 312 }.bind(this));
michael@0 313
michael@0 314 // Set the next alarm from queue.
michael@0 315 if (alarmQueue.length) {
michael@0 316 alarmQueue.sort(this._sortAlarmByTimeStamps.bind(this));
michael@0 317 this._currentAlarm = alarmQueue.shift();
michael@0 318 }
michael@0 319
michael@0 320 this._debugCurrentAlarm();
michael@0 321 }.bind(this),
michael@0 322 function getAllErrorCb(aErrorMsg) {
michael@0 323 throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
michael@0 324 }
michael@0 325 );
michael@0 326 },
michael@0 327
michael@0 328 _getAlarmTime: function _getAlarmTime(aAlarm) {
michael@0 329 // Avoid casting a Date object to a Date again to
michael@0 330 // preserve milliseconds. See bug 810973.
michael@0 331 let alarmTime;
michael@0 332 if (aAlarm.date instanceof Date) {
michael@0 333 alarmTime = aAlarm.date.getTime();
michael@0 334 } else {
michael@0 335 alarmTime = (new Date(aAlarm.date)).getTime();
michael@0 336 }
michael@0 337
michael@0 338 // For an alarm specified with "ignoreTimezone", it must be fired respect
michael@0 339 // to the user's timezone. Supposing an alarm was set at 7:00pm at Tokyo,
michael@0 340 // it must be gone off at 7:00pm respect to Paris' local time when the user
michael@0 341 // is located at Paris. We can adjust the alarm UTC time by calculating
michael@0 342 // the difference of the orginal timezone and the current timezone.
michael@0 343 if (aAlarm.ignoreTimezone) {
michael@0 344 alarmTime += (this._currentTimezoneOffset - aAlarm.timezoneOffset) * 60000;
michael@0 345 }
michael@0 346 return alarmTime;
michael@0 347 },
michael@0 348
michael@0 349 _sortAlarmByTimeStamps: function _sortAlarmByTimeStamps(aAlarm1, aAlarm2) {
michael@0 350 return this._getAlarmTime(aAlarm1) - this._getAlarmTime(aAlarm2);
michael@0 351 },
michael@0 352
michael@0 353 _debugCurrentAlarm: function _debugCurrentAlarm() {
michael@0 354 debug("Current alarm: " + JSON.stringify(this._currentAlarm));
michael@0 355 debug("Alarm queue: " + JSON.stringify(this._alarmQueue));
michael@0 356 },
michael@0 357
michael@0 358 /**
michael@0 359 *
michael@0 360 * Add a new alarm. This will set the RTC to fire at the selected date and
michael@0 361 * notify the caller. Notifications are delivered via System Messages if the
michael@0 362 * alarm is added on behalf of a app. Otherwise aAlarmFiredCb is called.
michael@0 363 *
michael@0 364 * @param object aNewAlarm
michael@0 365 * Should contain the following literal properties:
michael@0 366 * - |date| date: when the alarm should timeout.
michael@0 367 * - |ignoreTimezone| boolean: See [1] for the details.
michael@0 368 * - |manifestURL| string: Manifest of app on whose behalf the alarm
michael@0 369 * is added.
michael@0 370 * - |pageURL| string: The page in the app that receives the system
michael@0 371 * message.
michael@0 372 * - |data| object [optional]: Data that can be stored in DB.
michael@0 373 * @param function aAlarmFiredCb
michael@0 374 * Callback function invoked when the alarm is fired.
michael@0 375 * It receives a single argument, the alarm object.
michael@0 376 * May be null.
michael@0 377 * @param function aSuccessCb
michael@0 378 * Callback function to receive an alarm ID (number).
michael@0 379 * @param function aErrorCb
michael@0 380 * Callback function to receive an error message (string).
michael@0 381 * @returns void
michael@0 382 *
michael@0 383 * Notes:
michael@0 384 * [1] https://wiki.mozilla.org/WebAPI/AlarmAPI#Proposed_API
michael@0 385 */
michael@0 386
michael@0 387 add: function(aNewAlarm, aAlarmFiredCb, aSuccessCb, aErrorCb) {
michael@0 388 debug("add(" + aNewAlarm.date + ")");
michael@0 389
michael@0 390 aSuccessCb = aSuccessCb || function() {};
michael@0 391 aErrorCb = aErrorCb || function() {};
michael@0 392
michael@0 393 if (!aNewAlarm) {
michael@0 394 aErrorCb("alarm is null");
michael@0 395 return;
michael@0 396 }
michael@0 397
michael@0 398 if (!aNewAlarm.date) {
michael@0 399 aErrorCb("alarm.date is null");
michael@0 400 return;
michael@0 401 }
michael@0 402
michael@0 403 aNewAlarm['timezoneOffset'] = this._currentTimezoneOffset;
michael@0 404
michael@0 405 this._db.add(
michael@0 406 aNewAlarm,
michael@0 407 function addSuccessCb(aNewId) {
michael@0 408 debug("Callback after adding alarm in database.");
michael@0 409
michael@0 410 aNewAlarm['id'] = aNewId;
michael@0 411
michael@0 412 // Now that the alarm has been added to the database, we can tack on
michael@0 413 // the non-serializable callback to the in-memory object.
michael@0 414 aNewAlarm['alarmFiredCb'] = aAlarmFiredCb;
michael@0 415
michael@0 416 // If there is no alarm being set in system, set the new alarm.
michael@0 417 if (this._currentAlarm == null) {
michael@0 418 this._currentAlarm = aNewAlarm;
michael@0 419 this._debugCurrentAlarm();
michael@0 420 aSuccessCb(aNewId);
michael@0 421 return;
michael@0 422 }
michael@0 423
michael@0 424 // If the new alarm is earlier than the current alarm, swap them and
michael@0 425 // push the previous alarm back to queue.
michael@0 426 let alarmQueue = this._alarmQueue;
michael@0 427 let aNewAlarmTime = this._getAlarmTime(aNewAlarm);
michael@0 428 let currentAlarmTime = this._getAlarmTime(this._currentAlarm);
michael@0 429 if (aNewAlarmTime < currentAlarmTime) {
michael@0 430 alarmQueue.unshift(this._currentAlarm);
michael@0 431 this._currentAlarm = aNewAlarm;
michael@0 432 this._debugCurrentAlarm();
michael@0 433 aSuccessCb(aNewId);
michael@0 434 return;
michael@0 435 }
michael@0 436
michael@0 437 // Push the new alarm in the queue.
michael@0 438 alarmQueue.push(aNewAlarm);
michael@0 439 alarmQueue.sort(this._sortAlarmByTimeStamps.bind(this));
michael@0 440 this._debugCurrentAlarm();
michael@0 441 aSuccessCb(aNewId);
michael@0 442 }.bind(this),
michael@0 443 function addErrorCb(aErrorMsg) {
michael@0 444 aErrorCb(aErrorMsg);
michael@0 445 }.bind(this)
michael@0 446 );
michael@0 447 },
michael@0 448
michael@0 449 /*
michael@0 450 * Remove the alarm associated with an ID.
michael@0 451 *
michael@0 452 * @param number aAlarmId
michael@0 453 * The ID of the alarm to be removed.
michael@0 454 * @param string aManifestURL
michael@0 455 * Manifest URL for application which added the alarm. (Optional)
michael@0 456 * @returns void
michael@0 457 */
michael@0 458 remove: function(aAlarmId, aManifestURL) {
michael@0 459 debug("remove(" + aAlarmId + ", " + aManifestURL + ")");
michael@0 460 this._removeAlarmFromDb(
michael@0 461 aAlarmId,
michael@0 462 aManifestURL,
michael@0 463 function removeSuccessCb() {
michael@0 464 debug("Callback after removing alarm from database.");
michael@0 465
michael@0 466 // If there are no alarms set, nothing to do.
michael@0 467 if (!this._currentAlarm) {
michael@0 468 debug("No alarms set.");
michael@0 469 return;
michael@0 470 }
michael@0 471
michael@0 472 // Check if the alarm to be removed is in the queue and whether it
michael@0 473 // belongs to the requesting app.
michael@0 474 let alarmQueue = this._alarmQueue;
michael@0 475 if (this._currentAlarm.id != aAlarmId ||
michael@0 476 this._currentAlarm.manifestURL != aManifestURL) {
michael@0 477
michael@0 478 for (let i = 0; i < alarmQueue.length; i++) {
michael@0 479 if (alarmQueue[i].id == aAlarmId &&
michael@0 480 alarmQueue[i].manifestURL == aManifestURL) {
michael@0 481
michael@0 482 alarmQueue.splice(i, 1);
michael@0 483 break;
michael@0 484 }
michael@0 485 }
michael@0 486 this._debugCurrentAlarm();
michael@0 487 return;
michael@0 488 }
michael@0 489
michael@0 490 // The alarm to be removed is the current alarm reset the next alarm
michael@0 491 // from queue if any.
michael@0 492 if (alarmQueue.length) {
michael@0 493 this._currentAlarm = alarmQueue.shift();
michael@0 494 this._debugCurrentAlarm();
michael@0 495 return;
michael@0 496 }
michael@0 497
michael@0 498 // No alarm waiting to be set in the queue.
michael@0 499 this._currentAlarm = null;
michael@0 500 this._debugCurrentAlarm();
michael@0 501 }.bind(this)
michael@0 502 );
michael@0 503 },
michael@0 504
michael@0 505 observe: function(aSubject, aTopic, aData) {
michael@0 506 switch (aTopic) {
michael@0 507 case "profile-change-teardown":
michael@0 508 this.uninit();
michael@0 509 break;
michael@0 510 case "webapps-clear-data":
michael@0 511 let params =
michael@0 512 aSubject.QueryInterface(Ci.mozIApplicationClearPrivateDataParams);
michael@0 513 if (!params) {
michael@0 514 debug("Error! Fail to remove alarms for an uninstalled app.");
michael@0 515 return;
michael@0 516 }
michael@0 517
michael@0 518 // Only remove alarms for apps.
michael@0 519 if (params.browserOnly) {
michael@0 520 return;
michael@0 521 }
michael@0 522
michael@0 523 let manifestURL = appsService.getManifestURLByLocalId(params.appId);
michael@0 524 if (!manifestURL) {
michael@0 525 debug("Error! Fail to remove alarms for an uninstalled app.");
michael@0 526 return;
michael@0 527 }
michael@0 528
michael@0 529 this._db.getAll(
michael@0 530 manifestURL,
michael@0 531 function getAllSuccessCb(aAlarms) {
michael@0 532 aAlarms.forEach(function removeAlarm(aAlarm) {
michael@0 533 this.remove(aAlarm.id, manifestURL);
michael@0 534 }, this);
michael@0 535 }.bind(this),
michael@0 536 function getAllErrorCb(aErrorMsg) {
michael@0 537 throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
michael@0 538 }
michael@0 539 );
michael@0 540 break;
michael@0 541 }
michael@0 542 },
michael@0 543
michael@0 544 uninit: function uninit() {
michael@0 545 debug("uninit()");
michael@0 546 Services.obs.removeObserver(this, "profile-change-teardown");
michael@0 547 Services.obs.removeObserver(this, "webapps-clear-data");
michael@0 548
michael@0 549 this._messages.forEach(function(aMsgName) {
michael@0 550 ppmm.removeMessageListener(aMsgName, this);
michael@0 551 }.bind(this));
michael@0 552 ppmm = null;
michael@0 553
michael@0 554 if (this._db) {
michael@0 555 this._db.close();
michael@0 556 }
michael@0 557 this._db = null;
michael@0 558
michael@0 559 this._alarmHalService = null;
michael@0 560 }
michael@0 561 }
michael@0 562
michael@0 563 AlarmService.init();

mercurial