|
|
@@ -9,6 +9,15 @@ define(function(require) { |
|
|
console.log('cronsync-main: ' + str); |
|
|
} |
|
|
|
|
|
function makeData(accountIds, interval, date) { |
|
|
return { |
|
|
type: 'sync', |
|
|
accountIds: accountIds, |
|
|
interval: interval, |
|
|
timestamp: date.getTime() |
|
|
}; |
|
|
} |
|
|
|
|
|
// Creates a string key from an array of string IDs. Uses a space |
|
|
// separator since that cannot show up in an ID. |
|
|
function makeAccountKey(accountIds) { |
|
|
@@ -24,9 +33,8 @@ define(function(require) { |
|
|
|
|
|
// Makes sure two arrays have the same values, account IDs. |
|
|
function hasSameValues(ary1, ary2) { |
|
|
if (ary1.length !== ary2.length) { |
|
|
if (ary1.length !== ary2.length) |
|
|
return false; |
|
|
} |
|
|
|
|
|
var hasMismatch = ary1.some(function(item, i) { |
|
|
return item !== ary2[i]; |
|
|
@@ -36,10 +44,13 @@ define(function(require) { |
|
|
} |
|
|
|
|
|
if (navigator.mozSetMessageHandler) { |
|
|
navigator.mozSetMessageHandler('request-sync', |
|
|
function onRequestSync(e) { |
|
|
var data = e.data; |
|
|
// Need to acquire the wake locks during this notification |
|
|
navigator.mozSetMessageHandler('alarm', function onAlarm(alarm) { |
|
|
// Do not bother with alarms that are not sync alarms. |
|
|
var data = alarm.data; |
|
|
if (!data || data.type !== 'sync') |
|
|
return; |
|
|
|
|
|
// Need to acquire the wake locks during this alarm notification |
|
|
// turn of the event loop -- later turns are not guaranteed to |
|
|
// be up and running. However, knowing when to release the locks |
|
|
// is only known to the front end, so publish event about it. |
|
|
@@ -61,10 +72,7 @@ define(function(require) { |
|
|
makeAccountKey(data.accountIds), locks); |
|
|
} |
|
|
|
|
|
debug('request-sync started at ' + (new Date())); |
|
|
|
|
|
dispatcher._sendMessage('requestSync', |
|
|
[data.accountIds, data.interval]); |
|
|
dispatcher._sendMessage('alarm', [data.accountIds, data.interval]); |
|
|
}); |
|
|
} |
|
|
|
|
|
@@ -95,157 +103,161 @@ define(function(require) { |
|
|
}, |
|
|
|
|
|
/** |
|
|
* Clears all sync-based tasks. Normally not called, except perhaps for |
|
|
* Clears all sync-based alarms. Normally not called, except perhaps for |
|
|
* tests or debugging. |
|
|
*/ |
|
|
clearAll: function() { |
|
|
var navSync = navigator.sync; |
|
|
if (!navSync) { |
|
|
var mozAlarms = navigator.mozAlarms; |
|
|
if (!mozAlarms) |
|
|
return; |
|
|
} |
|
|
|
|
|
navSync.registrations().then(function(registrations) { |
|
|
if (!registrations.length) { |
|
|
var r = mozAlarms.getAll(); |
|
|
|
|
|
r.onsuccess = function(event) { |
|
|
var alarms = event.target.result; |
|
|
if (!alarms) |
|
|
return; |
|
|
} |
|
|
|
|
|
registrations.forEach(function(registeredTask) { |
|
|
navSync.unregister(registeredTask.task); |
|
|
alarms.forEach(function(alarm) { |
|
|
if (alarm.data && alarm.data.type === 'sync') |
|
|
mozAlarms.remove(alarm.id); |
|
|
}); |
|
|
}.bind(this), |
|
|
function(err) { |
|
|
console.error('cronsync-main clearAll navigator.sync.registrations ' + |
|
|
'error: ' + err); |
|
|
}.bind(this)); |
|
|
}.bind(this); |
|
|
r.onerror = function(err) { |
|
|
console.error('cronsync-main clearAll mozAlarms.getAll: error: ' + |
|
|
err); |
|
|
}.bind(this); |
|
|
}, |
|
|
|
|
|
/** |
|
|
* Makes sure there is an sync task set for every account in |
|
|
* Makes sure there is an alarm set for every account in |
|
|
* the list. |
|
|
* @param {Object} syncData. An object with keys that are |
|
|
* 'interval' + intervalInMilliseconds, and values are arrays |
|
|
* of account IDs that should be synced at that interval. |
|
|
*/ |
|
|
ensureSync: function (syncData) { |
|
|
var navSync = navigator.sync; |
|
|
if (!navSync) { |
|
|
console.warn('no navigator.sync support!'); |
|
|
// Let backend know work has finished, even though it was a no-op. |
|
|
this._sendMessage('syncEnsured'); |
|
|
var mozAlarms = navigator.mozAlarms; |
|
|
if (!mozAlarms) { |
|
|
console.warn('no mozAlarms support!'); |
|
|
return; |
|
|
} |
|
|
|
|
|
debug('ensureSync called'); |
|
|
|
|
|
navSync.registrations().then(function(registrations) { |
|
|
var request = mozAlarms.getAll(); |
|
|
|
|
|
request.onsuccess = function(event) { |
|
|
debug('success!'); |
|
|
|
|
|
// Find all IDs being tracked by sync tasks |
|
|
var expiredTasks = [], |
|
|
okTaskIntervals = {}, |
|
|
uniqueTasks = {}; |
|
|
var alarms = event.target.result; |
|
|
// If there are no alarms a falsey value may be returned. We want |
|
|
// to not die and also make sure to signal we completed, so just make |
|
|
// an empty list. |
|
|
if (!alarms) { |
|
|
alarms = []; |
|
|
} |
|
|
|
|
|
// Find all IDs being tracked by alarms |
|
|
var expiredAlarmIds = [], |
|
|
okAlarmIntervals = {}, |
|
|
uniqueAlarms = {}; |
|
|
|
|
|
alarms.forEach(function(alarm) { |
|
|
// Only care about sync alarms. |
|
|
if (!alarm.data || !alarm.data.type || alarm.data.type !== 'sync') |
|
|
return; |
|
|
|
|
|
registrations.forEach(function(task) { |
|
|
// minInterval in seconds, but use milliseconds for sync values |
|
|
// internally. |
|
|
var intervalKey = 'interval' + (task.minInterval * 1000), |
|
|
var intervalKey = 'interval' + alarm.data.interval, |
|
|
wantedAccountIds = syncData[intervalKey]; |
|
|
|
|
|
if (!wantedAccountIds || !hasSameValues(wantedAccountIds, |
|
|
task.data.accountIds)) { |
|
|
debug('account array mismatch, canceling existing sync task'); |
|
|
expiredTasks.push(task); |
|
|
alarm.data.accountIds)) { |
|
|
debug('account array mismatch, canceling existing alarm'); |
|
|
expiredAlarmIds.push(alarm.id); |
|
|
} else { |
|
|
// Confirm the existing sync task is still good. |
|
|
// Confirm the existing alarm is still good. |
|
|
var interval = toInterval(intervalKey), |
|
|
now = Date.now(), |
|
|
alarmTime = alarm.data.timestamp, |
|
|
accountKey = makeAccountKey(wantedAccountIds); |
|
|
|
|
|
// If the interval is nonzero, and there is no other task found |
|
|
// If the interval is nonzero, and there is no other alarm found |
|
|
// for that account combo, and if it is not in the past and if it |
|
|
// is not too far in the future, it is OK to keep. |
|
|
if (interval && !uniqueTasks.hasOwnProperty(accountKey)) { |
|
|
debug('existing sync task is OK: ' + interval); |
|
|
uniqueTasks[accountKey] = true; |
|
|
okTaskIntervals[intervalKey] = true; |
|
|
if (interval && !uniqueAlarms.hasOwnProperty(accountKey) && |
|
|
alarmTime > now && alarmTime < now + interval) { |
|
|
debug('existing alarm is OK'); |
|
|
uniqueAlarms[accountKey] = true; |
|
|
okAlarmIntervals[intervalKey] = true; |
|
|
} else { |
|
|
debug('existing sync task is out of interval range, canceling'); |
|
|
expiredTasks.push(task); |
|
|
debug('existing alarm is out of interval range, canceling'); |
|
|
expiredAlarmIds.push(alarm.id); |
|
|
} |
|
|
} |
|
|
}); |
|
|
|
|
|
expiredTasks.forEach(function(expiredTask) { |
|
|
navSync.unregister(expiredTask.task); |
|
|
expiredAlarmIds.forEach(function(alarmId) { |
|
|
mozAlarms.remove(alarmId); |
|
|
}); |
|
|
|
|
|
var taskMax = 0, |
|
|
taskCount = 0, |
|
|
var alarmMax = 0, |
|
|
alarmCount = 0, |
|
|
self = this; |
|
|
|
|
|
// Called when sync tasks are confirmed to be set. |
|
|
// Called when alarms are confirmed to be set. |
|
|
function done() { |
|
|
taskCount += 1; |
|
|
if (taskCount < taskMax) { |
|
|
alarmCount += 1; |
|
|
if (alarmCount < alarmMax) |
|
|
return; |
|
|
} |
|
|
|
|
|
debug('ensureSync completed'); |
|
|
// Indicate ensureSync has completed because the |
|
|
// back end is waiting to hear sync task was set |
|
|
// before triggering sync complete. |
|
|
// back end is waiting to hear alarm was set before |
|
|
// triggering sync complete. |
|
|
self._sendMessage('syncEnsured'); |
|
|
} |
|
|
|
|
|
Object.keys(syncData).forEach(function(intervalKey) { |
|
|
// Skip if the existing sync task is already good. |
|
|
if (okTaskIntervals.hasOwnProperty(intervalKey)) { |
|
|
// Skip if the existing alarm is already good. |
|
|
if (okAlarmIntervals.hasOwnProperty(intervalKey)) |
|
|
return; |
|
|
} |
|
|
|
|
|
var interval = toInterval(intervalKey), |
|
|
accountIds = syncData[intervalKey]; |
|
|
accountIds = syncData[intervalKey], |
|
|
date = new Date(Date.now() + interval); |
|
|
|
|
|
// Do not set an timer for a 0 interval, bad things happen. |
|
|
if (!interval) { |
|
|
if (!interval) |
|
|
return; |
|
|
} |
|
|
|
|
|
taskMax += 1; |
|
|
|
|
|
navSync.register('interval' + interval, { |
|
|
// minInterval is in seconds. |
|
|
minInterval: interval / 1000, |
|
|
oneShot: false, |
|
|
data: { |
|
|
accountIds: accountIds, |
|
|
interval: interval |
|
|
}, |
|
|
wifiOnly: false, |
|
|
// TODO: allow this to be more generic, getting this passed in |
|
|
// from the page using this module. This assumes the current page |
|
|
// without query strings or fragment IDs is the desired entry point. |
|
|
wakeUpPage: location.href.split('?')[0].split('#')[0] }) |
|
|
.then(function() { |
|
|
debug('success: navigator.sync.register for ' + 'IDs: ' + |
|
|
accountIds + |
|
|
alarmMax += 1; |
|
|
|
|
|
var alarmRequest = mozAlarms.add(date, 'ignoreTimezone', |
|
|
makeData(accountIds, interval, date)); |
|
|
|
|
|
alarmRequest.onsuccess = function() { |
|
|
debug('success: mozAlarms.add for ' + 'IDs: ' + accountIds + |
|
|
' at ' + interval + 'ms'); |
|
|
done(); |
|
|
}, function(err) { |
|
|
console.error('cronsync-main navigator.sync.register for IDs: ' + |
|
|
}; |
|
|
|
|
|
alarmRequest.onerror = function(err) { |
|
|
console.error('cronsync-main mozAlarms.add for IDs: ' + |
|
|
accountIds + |
|
|
' failed: ' + err); |
|
|
}); |
|
|
}; |
|
|
}); |
|
|
|
|
|
// If no sync tasks were added, indicate ensureSync is done. |
|
|
if (!taskMax) { |
|
|
// If no alarms were added, indicate ensureSync is done. |
|
|
if (!alarmMax) |
|
|
done(); |
|
|
} |
|
|
}.bind(this), |
|
|
function(err) { |
|
|
console.error('cronsync-main ensureSync navigator.sync.register: ' + |
|
|
'error: ' + err); |
|
|
}); |
|
|
}.bind(this); |
|
|
|
|
|
request.onerror = function(err) { |
|
|
console.error('cronsync-main ensureSync mozAlarms.getAll: error: ' + |
|
|
err); |
|
|
}; |
|
|
} |
|
|
}; |
|
|
|
|
|
|