зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1213169 - requestsync assumes all in-memory mozAlarms will never be purged (and these alarms get persisted anyways), but they are purged on timezone/clock changes, r=asuth
This commit is contained in:
Родитель
5731d2c32f
Коммит
8d4a4c315a
|
@ -49,6 +49,8 @@ XPCOMUtils.defineLazyGetter(this, "powerManagerService", function() {
|
|||
*/
|
||||
|
||||
this.AlarmService = {
|
||||
lastChromeId: 0,
|
||||
|
||||
init: function init() {
|
||||
debug("init()");
|
||||
|
||||
|
@ -205,6 +207,12 @@ this.AlarmService = {
|
|||
};
|
||||
}
|
||||
|
||||
// Is this a chrome alarm?
|
||||
if (aId < 0) {
|
||||
aRemoveSuccessCb();
|
||||
return;
|
||||
}
|
||||
|
||||
this._db.remove(aId, aManifestURL, aRemoveSuccessCb,
|
||||
function removeErrorCb(aErrorMsg) {
|
||||
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
|
||||
|
@ -241,11 +249,19 @@ this.AlarmService = {
|
|||
_notifyAlarmObserver: function _notifyAlarmObserver(aAlarm) {
|
||||
debug("_notifyAlarmObserver()");
|
||||
|
||||
if (aAlarm.manifestURL) {
|
||||
this._fireSystemMessage(aAlarm);
|
||||
} else if (typeof aAlarm.alarmFiredCb === "function") {
|
||||
aAlarm.alarmFiredCb(this._publicAlarm(aAlarm));
|
||||
}
|
||||
let wakeLock = powerManagerService.newWakeLock("cpu");
|
||||
|
||||
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
timer.initWithCallback(() => {
|
||||
debug("_notifyAlarmObserver - timeout()");
|
||||
if (aAlarm.manifestURL) {
|
||||
this._fireSystemMessage(aAlarm);
|
||||
} else if (typeof aAlarm.alarmFiredCb === "function") {
|
||||
aAlarm.alarmFiredCb(this._publicAlarm(aAlarm));
|
||||
}
|
||||
|
||||
wakeLock.unlock();
|
||||
}, 0, Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
},
|
||||
|
||||
_onAlarmFired: function _onAlarmFired() {
|
||||
|
@ -265,8 +281,12 @@ this.AlarmService = {
|
|||
}
|
||||
|
||||
this._removeAlarmFromDb(this._currentAlarm.id, null);
|
||||
this._notifyAlarmObserver(this._currentAlarm);
|
||||
// We need to clear the current alarm before notifying because chrome
|
||||
// alarms may add a new alarm during their callback, and we do not want
|
||||
// to clobber it.
|
||||
let firingAlarm = this._currentAlarm;
|
||||
this._currentAlarm = null;
|
||||
this._notifyAlarmObserver(firingAlarm);
|
||||
}
|
||||
|
||||
// Reset the next alarm from the queue.
|
||||
|
@ -309,15 +329,25 @@ this.AlarmService = {
|
|||
debug("Callback after getting alarms from database: " +
|
||||
JSON.stringify(aAlarms));
|
||||
|
||||
// Clear any alarms set or queued in the cache.
|
||||
// Clear any alarms set or queued in the cache if coming from db.
|
||||
let alarmQueue = this._alarmQueue;
|
||||
alarmQueue.length = 0;
|
||||
this._currentAlarm = null;
|
||||
if (this._currentAlarm) {
|
||||
alarmQueue.unshift(this._currentAlarm);
|
||||
this._currentAlarm = null;
|
||||
}
|
||||
for (let i = 0; i < alarmQueue.length;) {
|
||||
if (alarmQueue[i]['id'] < 0) {
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
alarmQueue.splice(i, 1);
|
||||
}
|
||||
|
||||
// Only restore the alarm that's not yet expired; otherwise, remove it
|
||||
// from the database and notify the observer.
|
||||
aAlarms.forEach(function addAlarm(aAlarm) {
|
||||
if (this._getAlarmTime(aAlarm) > Date.now()) {
|
||||
if ("manifestURL" in aAlarm && aAlarm.manifestURL &&
|
||||
this._getAlarmTime(aAlarm) > Date.now()) {
|
||||
alarmQueue.push(aAlarm);
|
||||
} else {
|
||||
this._removeAlarmFromDb(aAlarm.id, null);
|
||||
|
@ -416,55 +446,65 @@ this.AlarmService = {
|
|||
|
||||
aNewAlarm['timezoneOffset'] = this._currentTimezoneOffset;
|
||||
|
||||
this._db.add(aNewAlarm,
|
||||
function addSuccessCb(aNewId) {
|
||||
debug("Callback after adding alarm in database.");
|
||||
if ("manifestURL" in aNewAlarm) {
|
||||
this._db.add(aNewAlarm,
|
||||
function addSuccessCb(aNewId) {
|
||||
debug("Callback after adding alarm in database.");
|
||||
this.processNewAlarm(aNewAlarm, aNewId, aAlarmFiredCb, aSuccessCb);
|
||||
}.bind(this),
|
||||
function addErrorCb(aErrorMsg) {
|
||||
aErrorCb(aErrorMsg);
|
||||
}.bind(this));
|
||||
} else {
|
||||
// alarms without manifests are managed by chrome code. For them we use
|
||||
// negative IDs.
|
||||
this.processNewAlarm(aNewAlarm, --this.lastChromeId, aAlarmFiredCb,
|
||||
aSuccessCb);
|
||||
}
|
||||
},
|
||||
|
||||
aNewAlarm['id'] = aNewId;
|
||||
processNewAlarm: function(aNewAlarm, aNewId, aAlarmFiredCb, aSuccessCb) {
|
||||
aNewAlarm['id'] = aNewId;
|
||||
|
||||
// Now that the alarm has been added to the database, we can tack on
|
||||
// the non-serializable callback to the in-memory object.
|
||||
aNewAlarm['alarmFiredCb'] = aAlarmFiredCb;
|
||||
// Now that the alarm has been added to the database, we can tack on
|
||||
// the non-serializable callback to the in-memory object.
|
||||
aNewAlarm['alarmFiredCb'] = aAlarmFiredCb;
|
||||
|
||||
// If the new alarm already expired at this moment, we directly
|
||||
// notify this alarm
|
||||
let aNewAlarmTime = this._getAlarmTime(aNewAlarm);
|
||||
if (aNewAlarmTime < Date.now()) {
|
||||
aSuccessCb(aNewId);
|
||||
this._removeAlarmFromDb(aNewAlarm.id, null);
|
||||
this._notifyAlarmObserver(aNewAlarm);
|
||||
return;
|
||||
}
|
||||
// If the new alarm already expired at this moment, we directly
|
||||
// notify this alarm
|
||||
let newAlarmTime = this._getAlarmTime(aNewAlarm);
|
||||
if (newAlarmTime < Date.now()) {
|
||||
aSuccessCb(aNewId);
|
||||
this._removeAlarmFromDb(aNewAlarm.id, null);
|
||||
this._notifyAlarmObserver(aNewAlarm);
|
||||
return;
|
||||
}
|
||||
|
||||
// If there is no alarm being set in system, set the new alarm.
|
||||
if (this._currentAlarm == null) {
|
||||
this._currentAlarm = aNewAlarm;
|
||||
this._debugCurrentAlarm();
|
||||
aSuccessCb(aNewId);
|
||||
return;
|
||||
}
|
||||
// If there is no alarm being set in system, set the new alarm.
|
||||
if (this._currentAlarm == null) {
|
||||
this._currentAlarm = aNewAlarm;
|
||||
this._debugCurrentAlarm();
|
||||
aSuccessCb(aNewId);
|
||||
return;
|
||||
}
|
||||
|
||||
// If the new alarm is earlier than the current alarm, swap them and
|
||||
// push the previous alarm back to the queue.
|
||||
let alarmQueue = this._alarmQueue;
|
||||
let currentAlarmTime = this._getAlarmTime(this._currentAlarm);
|
||||
if (aNewAlarmTime < currentAlarmTime) {
|
||||
alarmQueue.unshift(this._currentAlarm);
|
||||
this._currentAlarm = aNewAlarm;
|
||||
this._debugCurrentAlarm();
|
||||
aSuccessCb(aNewId);
|
||||
return;
|
||||
}
|
||||
// If the new alarm is earlier than the current alarm, swap them and
|
||||
// push the previous alarm back to the queue.
|
||||
let alarmQueue = this._alarmQueue;
|
||||
let currentAlarmTime = this._getAlarmTime(this._currentAlarm);
|
||||
if (newAlarmTime < currentAlarmTime) {
|
||||
alarmQueue.unshift(this._currentAlarm);
|
||||
this._currentAlarm = aNewAlarm;
|
||||
this._debugCurrentAlarm();
|
||||
aSuccessCb(aNewId);
|
||||
return;
|
||||
}
|
||||
|
||||
// Push the new alarm in the queue.
|
||||
alarmQueue.push(aNewAlarm);
|
||||
alarmQueue.sort(this._sortAlarmByTimeStamps.bind(this));
|
||||
this._debugCurrentAlarm();
|
||||
aSuccessCb(aNewId);
|
||||
}.bind(this),
|
||||
function addErrorCb(aErrorMsg) {
|
||||
aErrorCb(aErrorMsg);
|
||||
}.bind(this));
|
||||
// Push the new alarm in the queue.
|
||||
alarmQueue.push(aNewAlarm);
|
||||
alarmQueue.sort(this._sortAlarmByTimeStamps.bind(this));
|
||||
this._debugCurrentAlarm();
|
||||
aSuccessCb(aNewId);
|
||||
},
|
||||
|
||||
/*
|
||||
|
|
|
@ -46,6 +46,10 @@ XPCOMUtils.defineLazyServiceGetter(this, "secMan",
|
|||
"@mozilla.org/scriptsecuritymanager;1",
|
||||
"nsIScriptSecurityManager");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "powerManagerService",
|
||||
"@mozilla.org/power/powermanagerservice;1",
|
||||
"nsIPowerManagerService");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AlarmService",
|
||||
"resource://gre/modules/AlarmService.jsm");
|
||||
|
||||
|
@ -595,7 +599,7 @@ this.RequestSyncService = {
|
|||
|
||||
// Storing the requestID into the task for the callback.
|
||||
this.storePendingRequest(task, aTarget, aData.requestID);
|
||||
this.timeout(task);
|
||||
this.timeout(task, null);
|
||||
},
|
||||
|
||||
// We cannot expose the full internal object to content but just a subset.
|
||||
|
@ -681,7 +685,7 @@ this.RequestSyncService = {
|
|||
this._afterSchedulingTasks.push(aCb);
|
||||
},
|
||||
|
||||
timeout: function(aObj) {
|
||||
timeout: function(aObj, aWakeLock) {
|
||||
debug("timeout");
|
||||
|
||||
if (this._activeTask) {
|
||||
|
@ -690,6 +694,7 @@ this.RequestSyncService = {
|
|||
if (this._queuedTasks.indexOf(aObj) == -1) {
|
||||
this._queuedTasks.push(aObj);
|
||||
}
|
||||
this.maybeReleaseWakeLock(aWakeLock);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -697,7 +702,9 @@ this.RequestSyncService = {
|
|||
if (!app) {
|
||||
dump("ERROR!! RequestSyncService - Failed to retrieve app data from a principal.\n");
|
||||
aObj.active = false;
|
||||
this.updateObjectInDB(aObj);
|
||||
this.updateObjectInDB(aObj, () => {
|
||||
this.maybeReleaseWakeLock(aWakeLock);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -706,20 +713,25 @@ this.RequestSyncService = {
|
|||
|
||||
// Maybe need to be rescheduled?
|
||||
if (this.hasPendingMessages('request-sync', manifestURL, pageURL)) {
|
||||
this.scheduleTimer(aObj);
|
||||
this.scheduleTimer(aObj, () => {
|
||||
this.maybeReleaseWakeLock(aWakeLock);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.removeTimer(aObj);
|
||||
this._activeTask = aObj;
|
||||
|
||||
if (!manifestURL || !pageURL) {
|
||||
dump("ERROR!! RequestSyncService - Failed to create URI for the page or the manifest\n");
|
||||
aObj.active = false;
|
||||
this.updateObjectInDB(aObj);
|
||||
this.updateObjectInDB(aObj, () => {
|
||||
this.maybeReleaseWakeLock(aWakeLock);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this._activeTask = aObj;
|
||||
|
||||
// We don't want to run more than 1 task at the same time. We do this using
|
||||
// the promise created by sendMessage(). But if the task takes more than
|
||||
// RSYNC_OPERATION_TIMEOUT millisecs, we have to ignore the promise and
|
||||
|
@ -727,6 +739,14 @@ this.RequestSyncService = {
|
|||
|
||||
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
|
||||
// We need a wakelock to keep the device alive and we want to release it
|
||||
// only when all the steps are fully completely. This can involve calling
|
||||
// timeout() again if we have something in _queuedTasks. In this scenario
|
||||
// we want to reuse the same wakelock and we receive it as param.
|
||||
// The Wakelock is passed to operationCompleted() because we want to wait
|
||||
// until the data is written into IDB and maybe until all the pending next
|
||||
// tasks are executed too.
|
||||
let wakeLock = aWakeLock ? aWakeLock : powerManagerService.newWakeLock("cpu");
|
||||
let done = false;
|
||||
let self = this;
|
||||
function taskCompleted() {
|
||||
|
@ -734,7 +754,7 @@ this.RequestSyncService = {
|
|||
|
||||
if (!done) {
|
||||
done = true;
|
||||
self.operationCompleted();
|
||||
self.operationCompleted(wakeLock);
|
||||
}
|
||||
|
||||
timer.cancel();
|
||||
|
@ -768,11 +788,12 @@ this.RequestSyncService = {
|
|||
});
|
||||
},
|
||||
|
||||
operationCompleted: function() {
|
||||
operationCompleted: function(aWakeLock) {
|
||||
debug("operationCompleted");
|
||||
|
||||
if (!this._activeTask) {
|
||||
dump("ERROR!! RequestSyncService - OperationCompleted called without an active task\n");
|
||||
aWakeLock.unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -790,25 +811,26 @@ this.RequestSyncService = {
|
|||
this.updateObjectInDB(this._activeTask, function() {
|
||||
if (!this._activeTask.data.oneShot) {
|
||||
this.scheduleTimer(this._activeTask, function() {
|
||||
this.processNextTask();
|
||||
this.processNextTask(aWakeLock);
|
||||
}.bind(this));
|
||||
} else {
|
||||
this.processNextTask();
|
||||
this.processNextTask(aWakeLock);
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
processNextTask: function() {
|
||||
processNextTask: function(aWakeLock) {
|
||||
debug("processNextTask");
|
||||
|
||||
this._activeTask = null;
|
||||
|
||||
if (this._queuedTasks.length == 0) {
|
||||
aWakeLock.unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
let task = this._queuedTasks.shift();
|
||||
this.timeout(task);
|
||||
this.timeout(task, aWakeLock);
|
||||
},
|
||||
|
||||
hasPendingMessages: function(aMessageName, aManifestURL, aPageURL) {
|
||||
|
@ -932,7 +954,7 @@ this.RequestSyncService = {
|
|||
AlarmService.add(
|
||||
{ date: new Date(Date.now() + interval * 1000),
|
||||
ignoreTimezone: false },
|
||||
() => this.timeout(aObj),
|
||||
() => this.timeout(aObj, null),
|
||||
function(aTimerId) {
|
||||
this._timers[aObj.dbKey] = aTimerId;
|
||||
aCb();
|
||||
|
@ -968,6 +990,12 @@ this.RequestSyncService = {
|
|||
let requests = this._pendingRequests[aObj.dbKey];
|
||||
delete this._pendingRequests[aObj.dbKey];
|
||||
return requests;
|
||||
},
|
||||
|
||||
maybeReleaseWakeLock: function(aWakeLock) {
|
||||
if (aWakeLock) {
|
||||
aWakeLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче