Bug 1042939 - Alarms, periodic sync, notifications refactor r=millermedeiros

_Note_: This is a work in progress!

### Summary of changes

There are a few different pieces of functionality affected.

#### Periodic sync

1. When the calendar starts, if periodic sync is enabled, we should write an alarm to mozAlarms to wake us up for periodic sync.
2. If the user changes the periodic sync interval, we should revoke any previous periodic sync alarms and issue a new one at the modified interval.
3. When we get woken up by a periodic sync alarm, sync and then issue a new alarm.

#### Notifications

1. When we receive an event reminder from the alarms api, send a notification via the notifications api about the reminder. When that notification gets clicked, navigate to the alarm url.

### What's coming!!

1. Fix for calendar alarm spam.
2.  Don't start periodic sync until we have a calendar that needs a real sync operation.
3. Calendar opens and stays open after using an alarm.
I think the easiest solution will be something like:

```js
var syncing;
var scheduling;

function onSync() {
  // avoid calling scheduleSync multiple times!!
  // this only works because sync is only called by this function!
  if (!syncing) {
    sync().then(scheduleSync);
  }
}

function onSyncFrequencyChange() {
  // we will only need to schedule a new sync if the value really changed since
  // last schedule. important for cases where frequency changes DURING
  // a schedule, otherwise we could get into a weird state (trying to delete an
  // alarm before it updated the database)
  cacheSyncFrequency(value).then(maybeScheduleSync);
}

function sync() {
  if (!syncing) {
    syncing = return new Promise((resolve) => {
      debug('Will request cpu and wifi wake locks.');
      var cpuLock = navigator.requestWakeLock('cpu'),
          wifiLock = navigator.requestWakeLock('wifi');
      debug('Will sync.');
      exports.app.syncController.all(() => {
        debug('Sync complete! Will release cpu and wifi locks.');
        cpuLock.unlock();
        wifiLock.unlock();
        syncing = null;
        resolve();
      });
    });
  }

  return syncing;
}

function scheduleSync() {
  if (!scheduling) {
    prevSyncFrequency = curSyncFrequency;
    scheduling = revokePreviousAlarm()
      .then(issueSyncAlarm)
      .then(cacheSyncAlarm)
      .then(maybeScheduleSync)
      .then(() => {
        scheduling = null;
      })
      .catch((error) => {
        debug('Error scheduling sync:', error);
        console.error(error.toString());
        scheduling = null;
      });
  }

  return scheduling;
}

function maybeScheduleSync() {
  var action;
  if (prevSyncFrequency !== curSyncFrequency) {
    // Oh noes! That means that the sync interval was changed while
    // we were scheduling. We need to take a mulligan.
    var s = scheduling;
    // need to reset scheduling to null otherwise scheduleSync won't execute!
    scheduling = null;
    action = s ? s.then(scheduleSync) : scheduleSync();
  }
  return action || Promise.resolve();
}
```