зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1018320 - RequestSync API - patch 1 - webIDL and basic logic, r=ehsan
This commit is contained in:
Родитель
fc28efa6e0
Коммит
d29adb7aca
|
@ -15,6 +15,7 @@ Cu.import('resource://gre/modules/UserAgentOverrides.jsm');
|
|||
Cu.import('resource://gre/modules/Keyboard.jsm');
|
||||
Cu.import('resource://gre/modules/ErrorPage.jsm');
|
||||
Cu.import('resource://gre/modules/AlertsHelper.jsm');
|
||||
Cu.import('resource://gre/modules/RequestSyncService.jsm');
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
Cu.import('resource://gre/modules/NetworkStatsService.jsm');
|
||||
Cu.import('resource://gre/modules/ResourceStatsService.jsm');
|
||||
|
|
|
@ -342,6 +342,9 @@
|
|||
@BINPATH@/components/zipwriter.xpt
|
||||
|
||||
; JavaScript components
|
||||
@BINPATH@/components/RequestSync.manifest
|
||||
@BINPATH@/components/RequestSyncManager.js
|
||||
@BINPATH@/components/RequestSyncScheduler.js
|
||||
@BINPATH@/components/ChromeNotifications.js
|
||||
@BINPATH@/components/ChromeNotifications.manifest
|
||||
@BINPATH@/components/ConsoleAPI.manifest
|
||||
|
|
|
@ -545,6 +545,10 @@
|
|||
@RESPATH@/components/htmlMenuBuilder.js
|
||||
@RESPATH@/components/htmlMenuBuilder.manifest
|
||||
|
||||
@RESPATH@/components/RequestSync.manifest
|
||||
@RESPATH@/components/RequestSyncManager.js
|
||||
@RESPATH@/components/RequestSyncScheduler.js
|
||||
|
||||
@RESPATH@/components/PermissionSettings.js
|
||||
@RESPATH@/components/PermissionSettings.manifest
|
||||
@RESPATH@/components/ContactManager.js
|
||||
|
|
|
@ -84,6 +84,7 @@ this.SystemMessagePermissionsTable = {
|
|||
"push-register": {
|
||||
"push": []
|
||||
},
|
||||
"request-sync": { },
|
||||
"sms-delivery-success": {
|
||||
"sms": []
|
||||
},
|
||||
|
|
|
@ -43,6 +43,7 @@ DIRS += [
|
|||
'base',
|
||||
'activities',
|
||||
'archivereader',
|
||||
'requestsync',
|
||||
'bindings',
|
||||
'battery',
|
||||
'browser-element',
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
component {8ee5ab74-15c4-478f-9d32-67627b9f0f1a} RequestSyncScheduler.js
|
||||
contract @mozilla.org/dom/request-sync-scheduler;1 {8ee5ab74-15c4-478f-9d32-67627b9f0f1a}
|
||||
|
||||
component {e6f55080-e549-4e30-9d00-15f240fb763c} RequestSyncManager.js
|
||||
contract @mozilla.org/dom/request-sync-manager;1 {e6f55080-e549-4e30-9d00-15f240fb763c}
|
|
@ -0,0 +1,80 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
'use strict';
|
||||
|
||||
function debug(s) {
|
||||
//dump('DEBUG RequestSyncManager: ' + s + '\n');
|
||||
}
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
|
||||
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
|
||||
"@mozilla.org/childprocessmessagemanager;1",
|
||||
"nsIMessageSender");
|
||||
|
||||
function RequestSyncManager() {
|
||||
debug('created');
|
||||
}
|
||||
|
||||
RequestSyncManager.prototype = {
|
||||
__proto__: DOMRequestIpcHelper.prototype,
|
||||
|
||||
classDescription: 'RequestSyncManager XPCOM Component',
|
||||
classID: Components.ID('{e6f55080-e549-4e30-9d00-15f240fb763c}'),
|
||||
contractID: '@mozilla.org/dom/request-sync-manager;1',
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference,
|
||||
Ci.nsIObserver,
|
||||
Ci.nsIDOMGlobalPropertyInitializer]),
|
||||
|
||||
_messages: [ "RequestSyncManager:Registrations:Return" ],
|
||||
|
||||
init: function(aWindow) {
|
||||
debug("init");
|
||||
|
||||
// DOMRequestIpcHelper.initHelper sets this._window
|
||||
this.initDOMRequestHelper(aWindow, this._messages);
|
||||
},
|
||||
|
||||
sendMessage: function(aMsg, aObj) {
|
||||
let self = this;
|
||||
return this.createPromise(function(aResolve, aReject) {
|
||||
aObj.requestID =
|
||||
self.getPromiseResolverId({ resolve: aResolve, reject: aReject });
|
||||
cpmm.sendAsyncMessage(aMsg, aObj, null,
|
||||
self._window.document.nodePrincipal);
|
||||
});
|
||||
},
|
||||
|
||||
registrations: function() {
|
||||
debug('registrations');
|
||||
return this.sendMessage("RequestSyncManager:Registrations", {});
|
||||
},
|
||||
|
||||
receiveMessage: function(aMessage) {
|
||||
debug('receiveMessage');
|
||||
|
||||
let req = this.getPromiseResolver(aMessage.data.requestID);
|
||||
if (!req) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ('error' in aMessage.data) {
|
||||
req.reject(Cu.cloneInto(aMessage.data.error, this._window));
|
||||
return;
|
||||
}
|
||||
|
||||
if ('results' in aMessage.data) {
|
||||
req.resolve(Cu.cloneInto(aMessage.data.results, this._window));
|
||||
return;
|
||||
}
|
||||
|
||||
req.resolve();
|
||||
}
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([RequestSyncManager]);
|
|
@ -0,0 +1,101 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
'use strict';
|
||||
|
||||
function debug(s) {
|
||||
//dump('DEBUG RequestSyncScheduler: ' + s + '\n');
|
||||
}
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
Cu.import('resource://gre/modules/DOMRequestHelper.jsm');
|
||||
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, 'cpmm',
|
||||
'@mozilla.org/childprocessmessagemanager;1',
|
||||
'nsIMessageSender');
|
||||
|
||||
function RequestSyncScheduler() {
|
||||
debug('created');
|
||||
}
|
||||
|
||||
RequestSyncScheduler.prototype = {
|
||||
__proto__: DOMRequestIpcHelper.prototype,
|
||||
|
||||
classDescription: 'RequestSyncScheduler XPCOM Component',
|
||||
classID: Components.ID('{8ee5ab74-15c4-478f-9d32-67627b9f0f1a}'),
|
||||
contractID: '@mozilla.org/dom/request-sync-scheduler;1',
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference,
|
||||
Ci.nsIObserver,
|
||||
Ci.nsIDOMGlobalPropertyInitializer]),
|
||||
|
||||
_messages: [ 'RequestSync:Register:Return',
|
||||
'RequestSync:Unregister:Return',
|
||||
'RequestSync:Registrations:Return',
|
||||
'RequestSync:Registration:Return' ],
|
||||
|
||||
init: function(aWindow) {
|
||||
debug('init');
|
||||
|
||||
// DOMRequestIpcHelper.initHelper sets this._window
|
||||
this.initDOMRequestHelper(aWindow, this._messages);
|
||||
},
|
||||
|
||||
register: function(aTask, aParams) {
|
||||
debug('register');
|
||||
return this.sendMessage('RequestSync:Register',
|
||||
{ task: aTask, params: aParams });
|
||||
},
|
||||
|
||||
unregister: function(aTask) {
|
||||
debug('unregister');
|
||||
return this.sendMessage('RequestSync:Unregister',
|
||||
{ task: aTask });
|
||||
},
|
||||
|
||||
registrations: function() {
|
||||
debug('registrations');
|
||||
return this.sendMessage('RequestSync:Registrations', {});
|
||||
},
|
||||
|
||||
registration: function(aTask) {
|
||||
debug('registration');
|
||||
return this.sendMessage('RequestSync:Registration',
|
||||
{ task: aTask });
|
||||
},
|
||||
|
||||
sendMessage: function(aMsg, aObj) {
|
||||
let self = this;
|
||||
return this.createPromise(function(aResolve, aReject) {
|
||||
aObj.requestID =
|
||||
self.getPromiseResolverId({ resolve: aResolve, reject: aReject });
|
||||
cpmm.sendAsyncMessage(aMsg, aObj, null,
|
||||
self._window.document.nodePrincipal);
|
||||
});
|
||||
},
|
||||
|
||||
receiveMessage: function(aMessage) {
|
||||
debug('receiveMessage');
|
||||
|
||||
let req = this.getPromiseResolver(aMessage.data.requestID);
|
||||
if (!req) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ('error' in aMessage.data) {
|
||||
req.reject(Cu.cloneInto(aMessage.data.error, this._window));
|
||||
return;
|
||||
}
|
||||
|
||||
if ('results' in aMessage.data) {
|
||||
req.resolve(Cu.cloneInto(aMessage.data.results, this._window));
|
||||
return;
|
||||
}
|
||||
|
||||
req.resolve();
|
||||
}
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([RequestSyncScheduler]);
|
|
@ -0,0 +1,625 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
'use strict'
|
||||
|
||||
/* TODO:
|
||||
- wifi
|
||||
*/
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
function debug(s) {
|
||||
//dump('DEBUG RequestSyncService: ' + s + '\n');
|
||||
}
|
||||
|
||||
const RSYNCDB_VERSION = 1;
|
||||
const RSYNCDB_NAME = "requestSync";
|
||||
const RSYNC_MIN_INTERVAL = 100;
|
||||
|
||||
Cu.import('resource://gre/modules/IndexedDBHelper.jsm');
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.importGlobalProperties(["indexedDB"]);
|
||||
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "appsService",
|
||||
"@mozilla.org/AppsService;1",
|
||||
"nsIAppsService");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
|
||||
"@mozilla.org/childprocessmessagemanager;1",
|
||||
"nsISyncMessageSender");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
|
||||
"@mozilla.org/parentprocessmessagemanager;1",
|
||||
"nsIMessageBroadcaster");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "systemMessenger",
|
||||
"@mozilla.org/system-message-internal;1",
|
||||
"nsISystemMessagesInternal");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "secMan",
|
||||
"@mozilla.org/scriptsecuritymanager;1",
|
||||
"nsIScriptSecurityManager");
|
||||
|
||||
this.RequestSyncService = {
|
||||
__proto__: IndexedDBHelper.prototype,
|
||||
|
||||
children: [],
|
||||
|
||||
_messages: [ "RequestSync:Register", "RequestSync:Unregister",
|
||||
"RequestSync:Registrations", "RequestSync:Registration",
|
||||
"RequestSyncManager:Registrations" ],
|
||||
|
||||
_pendingOperation: false,
|
||||
_pendingMessages: [],
|
||||
|
||||
_registrations: {},
|
||||
|
||||
// Initialization of the RequestSyncService.
|
||||
init: function() {
|
||||
debug("init");
|
||||
|
||||
this._messages.forEach((function(msgName) {
|
||||
ppmm.addMessageListener(msgName, this);
|
||||
}).bind(this));
|
||||
|
||||
Services.obs.addObserver(this, 'xpcom-shutdown', false);
|
||||
Services.obs.addObserver(this, 'webapps-clear-data', false);
|
||||
|
||||
this.initDBHelper("requestSync", RSYNCDB_VERSION, [RSYNCDB_NAME]);
|
||||
|
||||
// Loading all the data from the database into the _registrations map.
|
||||
// Any incoming message will be stored and processed when the async
|
||||
// operation is completed.
|
||||
|
||||
let self = this;
|
||||
this.dbTxn("readonly", function(aStore) {
|
||||
aStore.openCursor().onsuccess = function(event) {
|
||||
let cursor = event.target.result;
|
||||
if (cursor) {
|
||||
self.addRegistration(cursor.value);
|
||||
cursor.continue();
|
||||
}
|
||||
}
|
||||
},
|
||||
function() {
|
||||
debug("initialization done");
|
||||
},
|
||||
function() {
|
||||
dump("ERROR!! RequestSyncService - Failed to retrieve data from the database.\n");
|
||||
});
|
||||
},
|
||||
|
||||
// Shutdown the RequestSyncService.
|
||||
shutdown: function() {
|
||||
debug("shutdown");
|
||||
|
||||
this._messages.forEach((function(msgName) {
|
||||
ppmm.removeMessageListener(msgName, this);
|
||||
}).bind(this));
|
||||
|
||||
Services.obs.removeObserver(this, 'xpcom-shutdown');
|
||||
Services.obs.removeObserver(this, 'webapps-clear-data');
|
||||
|
||||
this.close();
|
||||
|
||||
// Removing all the registrations will delete the pending timers.
|
||||
for (let key in this._registrations) {
|
||||
for (let task in this._registrations[key]) {
|
||||
this.removeRegistrationInternal(task, key);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
debug("observe");
|
||||
|
||||
switch (aTopic) {
|
||||
case 'xpcom-shutdown':
|
||||
this.shutdown();
|
||||
break;
|
||||
|
||||
case 'webapps-clear-data':
|
||||
this.clearData(aSubject);
|
||||
break;
|
||||
|
||||
default:
|
||||
debug("Wrong observer topic: " + aTopic);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
// When an app is uninstalled, we have to clean all its tasks.
|
||||
clearData: function(aData) {
|
||||
debug('clearData');
|
||||
|
||||
if (!aData) {
|
||||
return;
|
||||
}
|
||||
|
||||
let params =
|
||||
aData.QueryInterface(Ci.mozIApplicationClearPrivateDataParams);
|
||||
if (!params) {
|
||||
return;
|
||||
}
|
||||
|
||||
// At this point we don't have the origin, so we cannot create the full
|
||||
// key. Using the partial one is enough to detect the uninstalled app.
|
||||
var partialKey = params.appId + '|' + params.browserOnly + '|';
|
||||
var dbKeys = [];
|
||||
|
||||
for (let key in this._registrations) {
|
||||
if (key.indexOf(partialKey) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (let task in this._registrations[key]) {
|
||||
dbKeys = this._registrations[key][task].dbKey;
|
||||
this.removeRegistrationInternal(task, key);
|
||||
}
|
||||
}
|
||||
|
||||
if (dbKeys.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove the tasks from the database.
|
||||
this.dbTxn('readwrite', function(aStore) {
|
||||
for (let i = 0; i < dbKeys.length; ++i) {
|
||||
aStore.delete(dbKeys[i]);
|
||||
}
|
||||
},
|
||||
function() {
|
||||
debug("ClearData completed");
|
||||
}, function() {
|
||||
debug("ClearData failed");
|
||||
});
|
||||
},
|
||||
|
||||
// Creation of the schema for the database.
|
||||
upgradeSchema: function(aTransaction, aDb, aOldVersion, aNewVersion) {
|
||||
debug('updateSchema');
|
||||
aDb.createObjectStore(RSYNCDB_NAME, { autoIncrement: true });
|
||||
},
|
||||
|
||||
// This method generates the key for the indexedDB object storage.
|
||||
principalToKey: function(aPrincipal) {
|
||||
return aPrincipal.appId + '|' +
|
||||
aPrincipal.isInBrowserElement + '|' +
|
||||
aPrincipal.origin;
|
||||
},
|
||||
|
||||
// Add a task to the _registrations map and create the timer if it's needed.
|
||||
addRegistration: function(aObj) {
|
||||
debug('addRegistration');
|
||||
|
||||
let key = this.principalToKey(aObj.principal);
|
||||
if (!(key in this._registrations)) {
|
||||
this._registrations[key] = {};
|
||||
}
|
||||
|
||||
this.scheduleTimer(aObj);
|
||||
this._registrations[key][aObj.data.task] = aObj;
|
||||
},
|
||||
|
||||
// Remove a task from the _registrations map and delete the timer if it's
|
||||
// needed. It also checks if the principal is correct before doing the real
|
||||
// operation.
|
||||
removeRegistration: function(aTaskName, aKey, aPrincipal) {
|
||||
debug('removeRegistration');
|
||||
|
||||
if (!(aKey in this._registrations) ||
|
||||
!(aTaskName in this._registrations[aKey])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Additional security check.
|
||||
if (!aPrincipal.equals(this._registrations[aKey][aTaskName].principal)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.removeRegistrationInternal(aTaskName, aKey);
|
||||
return true;
|
||||
},
|
||||
|
||||
removeRegistrationInternal: function(aTaskName, aKey) {
|
||||
debug('removeRegistrationInternal');
|
||||
|
||||
if (this._registrations[aKey][aTaskName].timer) {
|
||||
this._registrations[aKey][aTaskName].timer.cancel();
|
||||
}
|
||||
|
||||
delete this._registrations[aKey][aTaskName];
|
||||
|
||||
// Lets remove the key in case there are not tasks registered.
|
||||
for (var key in this._registrations[aKey]) {
|
||||
return;
|
||||
}
|
||||
delete this._registrations[aKey];
|
||||
},
|
||||
|
||||
// The communication from the exposed objects and the service is done using
|
||||
// messages. This function receives and processes them.
|
||||
receiveMessage: function(aMessage) {
|
||||
debug("receiveMessage");
|
||||
|
||||
// We cannot process this request now.
|
||||
if (this._pendingOperation) {
|
||||
this._pendingMessages.push(aMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
// The principal is used to validate the message.
|
||||
if (!aMessage.principal) {
|
||||
return;
|
||||
}
|
||||
|
||||
let uri = Services.io.newURI(aMessage.principal.origin, null, null);
|
||||
|
||||
let principal;
|
||||
try {
|
||||
principal = secMan.getAppCodebasePrincipal(uri,
|
||||
aMessage.principal.appId, aMessage.principal.isInBrowserElement);
|
||||
} catch(e) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!principal) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (aMessage.name) {
|
||||
case "RequestSync:Register":
|
||||
this.register(aMessage.target, aMessage.data, principal);
|
||||
break;
|
||||
|
||||
case "RequestSync:Unregister":
|
||||
this.unregister(aMessage.target, aMessage.data, principal);
|
||||
break;
|
||||
|
||||
case "RequestSync:Registrations":
|
||||
this.registrations(aMessage.target, aMessage.data, principal);
|
||||
break;
|
||||
|
||||
case "RequestSync:Registration":
|
||||
this.registration(aMessage.target, aMessage.data, principal);
|
||||
break;
|
||||
|
||||
case "RequestSyncManager:Registrations":
|
||||
this.managerRegistrations(aMessage.target, aMessage.data, principal);
|
||||
break;
|
||||
|
||||
default:
|
||||
debug("Wrong message: " + aMessage.name);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
// Basic validation.
|
||||
validateRegistrationParams: function(aParams) {
|
||||
if (aParams === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We must have a page.
|
||||
if (!("wakeUpPage" in aParams) ||
|
||||
aParams.wakeUpPage.length == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let minInterval = RSYNC_MIN_INTERVAL;
|
||||
try {
|
||||
minInterval = Services.prefs.getIntPref("dom.requestSync.minInterval");
|
||||
} catch(e) {}
|
||||
|
||||
if (!("minInterval" in aParams) ||
|
||||
aParams.minInterval < minInterval) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
// Registration of a new task.
|
||||
register: function(aTarget, aData, aPrincipal) {
|
||||
debug("register");
|
||||
|
||||
if (!this.validateRegistrationParams(aData.params)) {
|
||||
aTarget.sendAsyncMessage("RequestSync:Register:Return",
|
||||
{ requestID: aData.requestID,
|
||||
error: "ParamsError" } );
|
||||
return;
|
||||
}
|
||||
|
||||
let key = this.principalToKey(aPrincipal);
|
||||
if (key in this._registrations &&
|
||||
aData.task in this._registrations[key]) {
|
||||
// if this task already exists we overwrite it.
|
||||
this.removeRegistrationInternal(aData.task, key);
|
||||
}
|
||||
|
||||
// This creates a RequestTaskFull object.
|
||||
aData.params.task = aData.task;
|
||||
aData.params.lastSync = 0;
|
||||
aData.params.principal = aPrincipal;
|
||||
|
||||
let dbKey = aData.task + "|" +
|
||||
aPrincipal.appId + '|' +
|
||||
aPrincipal.isInBrowserElement + '|' +
|
||||
aPrincipal.origin;
|
||||
|
||||
let data = { principal: aPrincipal,
|
||||
dbKey: dbKey,
|
||||
data: aData.params,
|
||||
active: true,
|
||||
timer: null };
|
||||
|
||||
let self = this;
|
||||
this.dbTxn('readwrite', function(aStore) {
|
||||
aStore.put(data, data.dbKey);
|
||||
},
|
||||
function() {
|
||||
self.addRegistration(data);
|
||||
aTarget.sendAsyncMessage("RequestSync:Register:Return",
|
||||
{ requestID: aData.requestID });
|
||||
},
|
||||
function() {
|
||||
aTarget.sendAsyncMessage("RequestSync:Register:Return",
|
||||
{ requestID: aData.requestID,
|
||||
error: "IndexDBError" } );
|
||||
});
|
||||
},
|
||||
|
||||
// Unregister a task.
|
||||
unregister: function(aTarget, aData, aPrincipal) {
|
||||
debug("unregister");
|
||||
|
||||
let key = this.principalToKey(aPrincipal);
|
||||
if (!(key in this._registrations) ||
|
||||
!(aData.task in this._registrations[key])) {
|
||||
aTarget.sendAsyncMessage("RequestSync:Unregister:Return",
|
||||
{ requestID: aData.requestID,
|
||||
error: "UnknownTaskError" });
|
||||
return;
|
||||
}
|
||||
|
||||
let dbKey = this._registrations[key][aData.task].dbKey;
|
||||
this.removeRegistration(aData.task, key, aPrincipal);
|
||||
|
||||
let self = this;
|
||||
this.dbTxn('readwrite', function(aStore) {
|
||||
aStore.delete(dbKey);
|
||||
},
|
||||
function() {
|
||||
aTarget.sendAsyncMessage("RequestSync:Unregister:Return",
|
||||
{ requestID: aData.requestID });
|
||||
},
|
||||
function() {
|
||||
aTarget.sendAsyncMessage("RequestSync:Unregister:Return",
|
||||
{ requestID: aData.requestID,
|
||||
error: "IndexDBError" } );
|
||||
});
|
||||
},
|
||||
|
||||
// Get the list of registered tasks for this principal.
|
||||
registrations: function(aTarget, aData, aPrincipal) {
|
||||
debug("registrations");
|
||||
|
||||
let results = [];
|
||||
let key = this.principalToKey(aPrincipal);
|
||||
if (key in this._registrations) {
|
||||
for (let i in this._registrations[key]) {
|
||||
results.push(this.createPartialTaskObject(
|
||||
this._registrations[key][i].data));
|
||||
}
|
||||
}
|
||||
|
||||
aTarget.sendAsyncMessage("RequestSync:Registrations:Return",
|
||||
{ requestID: aData.requestID,
|
||||
results: results });
|
||||
},
|
||||
|
||||
// Get a particular registered task for this principal.
|
||||
registration: function(aTarget, aData, aPrincipal) {
|
||||
debug("registration");
|
||||
|
||||
let results = null;
|
||||
let key = this.principalToKey(aPrincipal);
|
||||
if (key in this._registrations &&
|
||||
aData.task in this._registrations[key]) {
|
||||
results = this.createPartialTaskObject(
|
||||
this._registrations[key][aData.task].data);
|
||||
}
|
||||
|
||||
aTarget.sendAsyncMessage("RequestSync:Registration:Return",
|
||||
{ requestID: aData.requestID,
|
||||
results: results });
|
||||
},
|
||||
|
||||
// Get the list of the registered tasks.
|
||||
managerRegistrations: function(aTarget, aData, aPrincipal) {
|
||||
debug("managerRegistrations");
|
||||
|
||||
let results = [];
|
||||
for (var key in this._registrations) {
|
||||
for (var task in this._registrations[key]) {
|
||||
results.push(
|
||||
this.createFullTaskObject(this._registrations[key][task].data));
|
||||
}
|
||||
}
|
||||
|
||||
aTarget.sendAsyncMessage("RequestSyncManager:Registrations:Return",
|
||||
{ requestID: aData.requestID,
|
||||
results: results });
|
||||
},
|
||||
|
||||
// We cannot expose the full internal object to content but just a subset.
|
||||
// This method creates this subset.
|
||||
createPartialTaskObject: function(aObj) {
|
||||
return { task: aObj.task,
|
||||
lastSync: aObj.lastSync,
|
||||
oneShot: aObj.oneShot,
|
||||
minInterval: aObj.minInterval,
|
||||
wakeUpPage: aObj.wakeUpPage,
|
||||
wifiOnly: aObj.wifiOnly,
|
||||
data: aObj.data };
|
||||
},
|
||||
|
||||
createFullTaskObject: function(aObj) {
|
||||
let obj = this.createPartialTaskObject(aObj);
|
||||
|
||||
obj.app = { manifestURL: '',
|
||||
origin: aObj.principal.origin,
|
||||
isInBrowserElement: aObj.principal.isInBrowserElement };
|
||||
|
||||
let app = appsService.getAppByLocalId(aObj.principal.appId);
|
||||
if (app) {
|
||||
obj.app.manifestURL = app.manifestURL;
|
||||
}
|
||||
|
||||
return obj;
|
||||
},
|
||||
|
||||
// Creation of the timer for a particular task object.
|
||||
scheduleTimer: function(aObj) {
|
||||
debug("scheduleTimer");
|
||||
|
||||
// A registration can be already inactive if it was 1 shot.
|
||||
if (aObj.active) {
|
||||
aObj.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
|
||||
let self = this;
|
||||
aObj.timer.initWithCallback(function() { self.timeout(aObj); },
|
||||
aObj.data.minInterval * 1000,
|
||||
Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
}
|
||||
},
|
||||
|
||||
timeout: function(aObj) {
|
||||
debug("timeout");
|
||||
|
||||
let app = appsService.getAppByLocalId(aObj.principal.appId);
|
||||
if (!app) {
|
||||
dump("ERROR!! RequestSyncService - Failed to retrieve app data from a principal.\n");
|
||||
aObj.active = false;
|
||||
this.updateObjectInDB(aObj);
|
||||
return;
|
||||
}
|
||||
|
||||
let manifestURL = Services.io.newURI(app.manifestURL, null, null);
|
||||
let pageURL = Services.io.newURI(aObj.data.wakeUpPage, null, aObj.principal.URI);
|
||||
|
||||
// Maybe need to be rescheduled?
|
||||
if (this.needRescheduling('request-sync', manifestURL, pageURL)) {
|
||||
this.scheduleTimer(aObj);
|
||||
return;
|
||||
}
|
||||
|
||||
aObj.timer = null;
|
||||
|
||||
if (!manifestURL || !pageURL) {
|
||||
dump("ERROR!! RequestSyncService - Failed to create URI for the page or the manifest\n");
|
||||
aObj.active = false;
|
||||
this.updateObjectInDB(aObj);
|
||||
return;
|
||||
}
|
||||
|
||||
// Sending the message.
|
||||
systemMessenger.sendMessage('request-sync',
|
||||
this.createPartialTaskObject(aObj.data),
|
||||
pageURL, manifestURL);
|
||||
|
||||
// One shot? Then this is not active.
|
||||
aObj.active = !aObj.data.oneShot;
|
||||
aObj.data.lastSync = new Date();
|
||||
|
||||
let self = this;
|
||||
this.updateObjectInDB(aObj, function() {
|
||||
// SchedulerTimer creates a timer and a nsITimer cannot be cloned. This
|
||||
// is the reason why this operation has to be done after storing the aObj
|
||||
// into IDB.
|
||||
if (!aObj.data.oneShot) {
|
||||
self.scheduleTimer(aObj);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
needRescheduling: function(aMessageName, aManifestURL, aPageURL) {
|
||||
let hasPendingMessages =
|
||||
cpmm.sendSyncMessage("SystemMessageManager:HasPendingMessages",
|
||||
{ type: aMessageName,
|
||||
pageURL: aPageURL.spec,
|
||||
manifestURL: aManifestURL.spec })[0];
|
||||
|
||||
debug("Pending messages: " + hasPendingMessages);
|
||||
|
||||
if (hasPendingMessages) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// FIXME: other reasons?
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
// Update the object into the database.
|
||||
updateObjectInDB: function(aObj, aCb) {
|
||||
debug("updateObjectInDB");
|
||||
|
||||
this.dbTxn('readwrite', function(aStore) {
|
||||
aStore.put(aObj, aObj.dbKey);
|
||||
},
|
||||
function() {
|
||||
if (aCb) {
|
||||
aCb();
|
||||
}
|
||||
debug("UpdateObjectInDB completed");
|
||||
}, function() {
|
||||
debug("UpdateObjectInDB failed");
|
||||
});
|
||||
},
|
||||
|
||||
pendingOperationStarted: function() {
|
||||
debug('pendingOperationStarted');
|
||||
this._pendingOperation = true;
|
||||
},
|
||||
|
||||
pendingOperationDone: function() {
|
||||
debug('pendingOperationDone');
|
||||
|
||||
this._pendingOperation = false;
|
||||
|
||||
// managing the pending messages now that the initialization is completed.
|
||||
while (this._pendingMessages.length) {
|
||||
this.receiveMessage(this._pendingMessages.shift());
|
||||
}
|
||||
},
|
||||
|
||||
// This method creates a transaction and runs callbacks. Plus it manages the
|
||||
// pending operations system.
|
||||
dbTxn: function(aType, aCb, aSuccessCb, aErrorCb) {
|
||||
debug('dbTxn');
|
||||
|
||||
this.pendingOperationStarted();
|
||||
|
||||
let self = this;
|
||||
this.newTxn(aType, RSYNCDB_NAME, function(aTxn, aStore) {
|
||||
aCb(aStore);
|
||||
},
|
||||
function() {
|
||||
self.pendingOperationDone();
|
||||
aSuccessCb();
|
||||
},
|
||||
function() {
|
||||
self.pendingOperationDone();
|
||||
aErrorCb();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
RequestSyncService.init();
|
||||
|
||||
this.EXPORTED_SYMBOLS = [""];
|
|
@ -0,0 +1,17 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
MOCHITEST_MANIFESTS += ['tests/mochitest.ini']
|
||||
|
||||
EXTRA_COMPONENTS += [
|
||||
'RequestSync.manifest',
|
||||
'RequestSyncManager.js',
|
||||
'RequestSyncScheduler.js',
|
||||
]
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
'RequestSyncService.jsm',
|
||||
]
|
|
@ -0,0 +1,15 @@
|
|||
function is(a, b, msg) {
|
||||
alert((a === b ? 'OK' : 'KO') + ' ' + msg)
|
||||
}
|
||||
|
||||
function ok(a, msg) {
|
||||
alert((a ? 'OK' : 'KO')+ ' ' + msg)
|
||||
}
|
||||
|
||||
function cbError() {
|
||||
alert('KO error');
|
||||
}
|
||||
|
||||
function finish() {
|
||||
alert('DONE');
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
function test_registerFailure() {
|
||||
ok("sync" in navigator, "navigator.sync exists");
|
||||
|
||||
navigator.sync.register().then(
|
||||
function() {
|
||||
ok(false, "navigator.sync.register() throws without a task name");
|
||||
}, function() {
|
||||
ok(true, "navigator.sync.register() throws without a task name");
|
||||
})
|
||||
|
||||
.then(function() {
|
||||
return navigator.sync.register(42);
|
||||
}).then(function() {
|
||||
ok(false, "navigator.sync.register() throws without a string task name");
|
||||
}, function() {
|
||||
ok(true, "navigator.sync.register() throws without a string task name");
|
||||
})
|
||||
|
||||
.then(function() {
|
||||
return navigator.sync.register('foobar');
|
||||
}).then(function() {
|
||||
ok(false, "navigator.sync.register() throws without a param dictionary");
|
||||
}, function() {
|
||||
ok(true, "navigator.sync.register() throws without a param dictionary");
|
||||
})
|
||||
|
||||
.then(function() {
|
||||
return navigator.sync.register('foobar', 42);
|
||||
}).then(function() {
|
||||
ok(false, "navigator.sync.register() throws without a real dictionary");
|
||||
}, function() {
|
||||
ok(true, "navigator.sync.register() throws without a real dictionary");
|
||||
})
|
||||
|
||||
.then(function() {
|
||||
return navigator.sync.register('foobar', {});
|
||||
}).then(function() {
|
||||
ok(false, "navigator.sync.register() throws without a minInterval and wakeUpPage");
|
||||
}, function() {
|
||||
ok(true, "navigator.sync.register() throws without a minInterval and wakeUpPage");
|
||||
})
|
||||
|
||||
.then(function() {
|
||||
return navigator.sync.register('foobar', { minInterval: 100 });
|
||||
}).then(function() {
|
||||
ok(false, "navigator.sync.register() throws without a wakeUpPage");
|
||||
}, function() {
|
||||
ok(true, "navigator.sync.register() throws without a wakeUpPage");
|
||||
})
|
||||
|
||||
.then(function() {
|
||||
return navigator.sync.register('foobar', { wakeUpPage: 100 });
|
||||
}).then(function() {
|
||||
ok(false, "navigator.sync.register() throws without a minInterval");
|
||||
}, function() {
|
||||
ok(true, "navigator.sync.register() throws without a minInterval");
|
||||
})
|
||||
|
||||
.then(function() {
|
||||
runTests();
|
||||
});
|
||||
}
|
||||
|
||||
function genericError() {
|
||||
ok(false, "Some promise failed");
|
||||
}
|
||||
|
||||
function test_register() {
|
||||
navigator.sync.register('foobar', { minInterval: 5, wakeUpPage:'/' }).then(
|
||||
function() {
|
||||
ok(true, "navigator.sync.register() worked!");
|
||||
runTests();
|
||||
}, genericError);
|
||||
}
|
||||
|
||||
function test_unregister() {
|
||||
navigator.sync.unregister('foobar').then(
|
||||
function() {
|
||||
ok(true, "navigator.sync.unregister() worked!");
|
||||
runTests();
|
||||
}, genericError);
|
||||
}
|
||||
|
||||
function test_unregisterDuplicate() {
|
||||
navigator.sync.unregister('foobar').then(
|
||||
genericError,
|
||||
function(error) {
|
||||
ok(true, "navigator.sync.unregister() should throw if the task doesn't exist.");
|
||||
ok(error, "UnknownTaskError", "Duplicate unregistration error is correct");
|
||||
runTests();
|
||||
});
|
||||
}
|
||||
|
||||
function test_registrationEmpty() {
|
||||
navigator.sync.registration('bar').then(
|
||||
function(results) {
|
||||
is(results, null, "navigator.sync.registration() should return null.");
|
||||
runTests();
|
||||
},
|
||||
genericError);
|
||||
}
|
||||
|
||||
function test_registration() {
|
||||
navigator.sync.registration('foobar').then(
|
||||
function(results) {
|
||||
is(results.task, 'foobar', "navigator.sync.registration().task is correct");
|
||||
ok("lastSync" in results, "navigator.sync.registration().lastSync is correct");
|
||||
is(results.oneShot, true, "navigator.sync.registration().oneShot is correct");
|
||||
is(results.minInterval, 5, "navigator.sync.registration().minInterval is correct");
|
||||
ok("wakeUpPage" in results, "navigator.sync.registration().wakeUpPage is correct");
|
||||
ok("wifiOnly" in results, "navigator.sync.registration().wifiOnly is correct");
|
||||
ok("data" in results, "navigator.sync.registration().data is correct");
|
||||
ok(!("app" in results), "navigator.sync.registrations().app is correct");
|
||||
runTests();
|
||||
},
|
||||
genericError);
|
||||
}
|
||||
|
||||
function test_registrationsEmpty() {
|
||||
navigator.sync.registrations().then(
|
||||
function(results) {
|
||||
is(results.length, 0, "navigator.sync.registrations() should return an empty array.");
|
||||
runTests();
|
||||
},
|
||||
genericError);
|
||||
}
|
||||
|
||||
function test_registrations() {
|
||||
navigator.sync.registrations().then(
|
||||
function(results) {
|
||||
is(results.length, 1, "navigator.sync.registrations() should not return an empty array.");
|
||||
is(results[0].task, 'foobar', "navigator.sync.registrations()[0].task is correct");
|
||||
ok("lastSync" in results[0], "navigator.sync.registrations()[0].lastSync is correct");
|
||||
is(results[0].oneShot, true, "navigator.sync.registrations()[0].oneShot is correct");
|
||||
is(results[0].minInterval, 5, "navigator.sync.registrations()[0].minInterval is correct");
|
||||
ok("wakeUpPage" in results[0], "navigator.sync.registration()[0].wakeUpPage is correct");
|
||||
ok("wifiOnly" in results[0], "navigator.sync.registrations()[0].wifiOnly is correct");
|
||||
ok("data" in results[0], "navigator.sync.registrations()[0].data is correct");
|
||||
ok(!("app" in results[0]), "navigator.sync.registrations()[0].app is correct");
|
||||
runTests();
|
||||
},
|
||||
genericError);
|
||||
}
|
||||
|
||||
function test_managerRegistrationsEmpty() {
|
||||
navigator.syncManager.registrations().then(
|
||||
function(results) {
|
||||
is(results.length, 0, "navigator.syncManager.registrations() should return an empty array.");
|
||||
runTests();
|
||||
},
|
||||
genericError);
|
||||
}
|
||||
|
||||
function test_managerRegistrations() {
|
||||
navigator.syncManager.registrations().then(
|
||||
function(results) {
|
||||
is(results.length, 1, "navigator.sync.registrations() should not return an empty array.");
|
||||
is(results[0].task, 'foobar', "navigator.sync.registrations()[0].task is correct");
|
||||
ok("lastSync" in results[0], "navigator.sync.registrations()[0].lastSync is correct");
|
||||
is(results[0].oneShot, true, "navigator.sync.registrations()[0].oneShot is correct");
|
||||
is(results[0].minInterval, 5, "navigator.sync.registrations()[0].minInterval is correct");
|
||||
ok("wakeUpPage" in results[0], "navigator.sync.registration()[0].wakeUpPage is correct");
|
||||
ok("wifiOnly" in results[0], "navigator.sync.registrations()[0].wifiOnly is correct");
|
||||
ok("data" in results[0], "navigator.sync.registrations()[0].data is correct");
|
||||
ok("app" in results[0], "navigator.sync.registrations()[0].app is correct");
|
||||
ok("manifestURL" in results[0].app, "navigator.sync.registrations()[0].app.manifestURL is correct");
|
||||
is(results[0].app.origin, 'http://mochi.test:8888', "navigator.sync.registrations()[0].app.origin is correct");
|
||||
is(results[0].app.isInBrowserElement, false, "navigator.sync.registrations()[0].app.isInBrowserElement is correct");
|
||||
runTests();
|
||||
},
|
||||
genericError);
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
var gBasePath = "tests/dom/requestsync/tests/";
|
||||
var gTemplate = "file_app.template.webapp";
|
||||
|
||||
function handleRequest(request, response) {
|
||||
var query = getQuery(request);
|
||||
|
||||
var testToken = '';
|
||||
if ('testToken' in query) {
|
||||
testToken = query.testToken;
|
||||
}
|
||||
|
||||
var template = gBasePath + gTemplate;
|
||||
response.setHeader("Content-Type", "application/x-web-app-manifest+json", false);
|
||||
response.write(readTemplate(template).replace(/TESTTOKEN/g, testToken));
|
||||
}
|
||||
|
||||
// Copy-pasted incantations. There ought to be a better way to synchronously read
|
||||
// a file into a string, but I guess we're trying to discourage that.
|
||||
function readTemplate(path) {
|
||||
var file = Components.classes["@mozilla.org/file/directory_service;1"].
|
||||
getService(Components.interfaces.nsIProperties).
|
||||
get("CurWorkD", Components.interfaces.nsILocalFile);
|
||||
var fis = Components.classes['@mozilla.org/network/file-input-stream;1'].
|
||||
createInstance(Components.interfaces.nsIFileInputStream);
|
||||
var cis = Components.classes["@mozilla.org/intl/converter-input-stream;1"].
|
||||
createInstance(Components.interfaces.nsIConverterInputStream);
|
||||
var split = path.split("/");
|
||||
for(var i = 0; i < split.length; ++i) {
|
||||
file.append(split[i]);
|
||||
}
|
||||
fis.init(file, -1, -1, false);
|
||||
cis.init(fis, "UTF-8", 0, 0);
|
||||
|
||||
var data = "";
|
||||
let (str = {}) {
|
||||
let read = 0;
|
||||
do {
|
||||
read = cis.readString(0xffffffff, str); // read as much as we can and put it in str.value
|
||||
data += str.value;
|
||||
} while (read != 0);
|
||||
}
|
||||
cis.close();
|
||||
return data;
|
||||
}
|
||||
|
||||
function getQuery(request) {
|
||||
var query = {};
|
||||
request.queryString.split('&').forEach(function (val) {
|
||||
var [name, value] = val.split('=');
|
||||
query[name] = unescape(value);
|
||||
});
|
||||
return query;
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "Really Rapid Release (hosted)",
|
||||
"description": "Updated even faster than <a href='http://mozilla.org'>Firefox</a>, just to annoy slashdotters.",
|
||||
"launch_path": "/tests/dom/requestsync/tests/TESTTOKEN",
|
||||
"icons": { "128": "default_icon" }
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<script type="application/javascript" src="common_app.js"></script>
|
||||
<script type="application/javascript" src="common_basic.js"></script>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
<div id="container"></div>
|
||||
<script type="application/javascript;version=1.7">
|
||||
|
||||
function test_sync_interface() {
|
||||
ok("sync" in navigator, "navigator.sync should exist with permissions");
|
||||
ok(!("syncManager" in navigator), "navigator.syncManager should not exist without permissions");
|
||||
|
||||
ok("register" in navigator.sync, "navigator.sync.register exists");
|
||||
ok("unregister" in navigator.sync, "navigator.sync.unregister exists");
|
||||
ok("registrations" in navigator.sync, "navigator.sync.registrations exists");
|
||||
ok("registration" in navigator.sync, "navigator.sync.registration exists");
|
||||
|
||||
runTests();
|
||||
}
|
||||
|
||||
var tests = [
|
||||
test_sync_interface,
|
||||
|
||||
test_registrationsEmpty,
|
||||
|
||||
test_registerFailure,
|
||||
test_register,
|
||||
// overwrite the same registration.
|
||||
test_register,
|
||||
|
||||
test_registrations,
|
||||
|
||||
test_registrationEmpty,
|
||||
test_registration,
|
||||
|
||||
test_unregister,
|
||||
test_unregisterDuplicate,
|
||||
|
||||
test_registrationsEmpty,
|
||||
|
||||
// Let's keep a registration active when the app is uninstall...
|
||||
test_register,
|
||||
test_registrations
|
||||
];
|
||||
|
||||
function runTests() {
|
||||
if (!tests.length) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
var test = tests.shift();
|
||||
test();
|
||||
}
|
||||
|
||||
runTests();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,21 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<script type="application/javascript" src="common_app.js"></script>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
<div id="container"></div>
|
||||
<script type="application/javascript;version=1.7">
|
||||
|
||||
ok("sync" in navigator, "navigator.sync should exist with permissions");
|
||||
ok("register" in navigator.sync, "navigator.sync.register exists");
|
||||
ok("unregister" in navigator.sync, "navigator.sync.unregister exists");
|
||||
ok("registrations" in navigator.sync, "navigator.sync.registrations exists");
|
||||
ok("registration" in navigator.sync, "navigator.sync.registration exists");
|
||||
|
||||
finish();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,16 @@
|
|||
[DEFAULT]
|
||||
skip-if = e10s
|
||||
support-files =
|
||||
file_app.template.webapp
|
||||
file_app.sjs
|
||||
file_basic_app.html
|
||||
common_app.js
|
||||
common_basic.js
|
||||
|
||||
[test_webidl.html]
|
||||
[test_minInterval.html]
|
||||
[test_basic.html]
|
||||
[test_basic_app.html]
|
||||
run-if = buildapp != 'b2g'
|
||||
[test_wakeUp.html]
|
||||
run-if = buildapp == 'b2g' && toolkit == 'gonk'
|
|
@ -0,0 +1,73 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for RequestSync basic use</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="common_basic.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<script type="application/javascript;version=1.7">
|
||||
|
||||
|
||||
var tests = [
|
||||
function() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.requestSync.enabled", true],
|
||||
["dom.requestSync.minInterval", 1],
|
||||
["dom.ignore_webidl_scope_checks", true]]}, runTests);
|
||||
},
|
||||
|
||||
function() {
|
||||
SpecialPowers.pushPermissions(
|
||||
[{ "type": "requestsync-manager", "allow": 1, "context": document } ], runTests);
|
||||
},
|
||||
|
||||
function() {
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/RequestSyncService.jsm");
|
||||
}
|
||||
runTests();
|
||||
},
|
||||
|
||||
test_managerRegistrationsEmpty,
|
||||
test_registrationsEmpty,
|
||||
|
||||
test_registerFailure,
|
||||
test_register,
|
||||
// overwrite the same registration.
|
||||
test_register,
|
||||
|
||||
test_managerRegistrations,
|
||||
test_registrations,
|
||||
|
||||
test_registrationEmpty,
|
||||
test_registration,
|
||||
|
||||
test_unregister,
|
||||
test_unregisterDuplicate,
|
||||
|
||||
test_managerRegistrationsEmpty,
|
||||
test_registrationsEmpty,
|
||||
];
|
||||
|
||||
function runTests() {
|
||||
if (!tests.length) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
var test = tests.shift();
|
||||
test();
|
||||
}
|
||||
|
||||
function finish() {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
runTests();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,135 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for requestSync - basic operations in app</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="common_basic.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container"></div>
|
||||
<script type="application/javascript;version=1.7">
|
||||
|
||||
var gHostedManifestURL = 'http://test/tests/dom/requestsync/tests/file_app.sjs?testToken=file_basic_app.html';
|
||||
var gApp;
|
||||
|
||||
function cbError() {
|
||||
ok(false, "Error callback invoked");
|
||||
finish();
|
||||
}
|
||||
|
||||
function installApp() {
|
||||
var request = navigator.mozApps.install(gHostedManifestURL);
|
||||
request.onerror = cbError;
|
||||
request.onsuccess = function() {
|
||||
gApp = request.result;
|
||||
runTests();
|
||||
}
|
||||
}
|
||||
|
||||
function uninstallApp() {
|
||||
// Uninstall the app.
|
||||
var request = navigator.mozApps.mgmt.uninstall(gApp);
|
||||
request.onerror = cbError;
|
||||
request.onsuccess = function() {
|
||||
// All done.
|
||||
info("All done");
|
||||
runTests();
|
||||
}
|
||||
}
|
||||
|
||||
function testApp() {
|
||||
var ifr = document.createElement('iframe');
|
||||
ifr.setAttribute('mozbrowser', 'true');
|
||||
ifr.setAttribute('mozapp', gApp.manifestURL);
|
||||
ifr.setAttribute('src', gApp.manifest.launch_path);
|
||||
var domParent = document.getElementById('container');
|
||||
|
||||
// Set us up to listen for messages from the app.
|
||||
var listener = function(e) {
|
||||
var message = e.detail.message;
|
||||
if (/^OK/.exec(message)) {
|
||||
ok(true, "Message from app: " + message);
|
||||
} else if (/KO/.exec(message)) {
|
||||
ok(false, "Message from app: " + message);
|
||||
} else if (/DONE/.exec(message)) {
|
||||
ok(true, "Messaging from app complete");
|
||||
ifr.removeEventListener('mozbrowsershowmodalprompt', listener);
|
||||
domParent.removeChild(ifr);
|
||||
runTests();
|
||||
}
|
||||
}
|
||||
|
||||
// This event is triggered when the app calls "alert".
|
||||
ifr.addEventListener('mozbrowsershowmodalprompt', listener, false);
|
||||
domParent.appendChild(ifr);
|
||||
}
|
||||
|
||||
var tests = [
|
||||
// Permissions
|
||||
function() {
|
||||
SpecialPowers.pushPermissions(
|
||||
[{ "type": "browser", "allow": 1, "context": document },
|
||||
{ "type": "embed-apps", "allow": 1, "context": document },
|
||||
{"type": "requestsync-manager", "allow": 1, "context": document },
|
||||
{ "type": "webapps-manage", "allow": 1, "context": document }], runTests);
|
||||
},
|
||||
|
||||
// Preferences
|
||||
function() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.requestSync.enabled", true],
|
||||
["dom.requestSync.minInterval", 1],
|
||||
["dom.ignore_webidl_scope_checks", true],
|
||||
["dom.testing.ignore_ipc_principal", true]]}, runTests);
|
||||
},
|
||||
|
||||
function() {
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/RequestSyncService.jsm");
|
||||
}
|
||||
|
||||
SpecialPowers.setAllAppsLaunchable(true);
|
||||
SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true);
|
||||
runTests();
|
||||
},
|
||||
|
||||
// No confirmation needed when an app is installed
|
||||
function() {
|
||||
SpecialPowers.autoConfirmAppInstall(() =>
|
||||
SpecialPowers.autoConfirmAppUninstall(runTests));
|
||||
},
|
||||
|
||||
test_managerRegistrationsEmpty,
|
||||
|
||||
// Installing the app
|
||||
installApp,
|
||||
|
||||
// Run tests in app
|
||||
testApp,
|
||||
|
||||
// Uninstall the app
|
||||
uninstallApp,
|
||||
|
||||
test_managerRegistrationsEmpty
|
||||
];
|
||||
|
||||
function runTests() {
|
||||
if (!tests.length) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
var test = tests.shift();
|
||||
test();
|
||||
}
|
||||
|
||||
function finish() {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
runTests();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,65 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for RequestSync minInterval pref</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<script type="application/javascript;version=1.7">
|
||||
|
||||
function test_minInterval(expected) {
|
||||
navigator.sync.register('foobar', { minInterval: 1, wakeUpPage: '/' }).then(
|
||||
function() {
|
||||
ok(expected, "MinInterval succeeded");
|
||||
},
|
||||
function(e) {
|
||||
ok(!expected, "MinInterval failed");
|
||||
is(e, "ParamsError", "Correct error received");
|
||||
})
|
||||
|
||||
.then(runTests);
|
||||
}
|
||||
|
||||
var tests = [
|
||||
function() {
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/RequestSyncService.jsm");
|
||||
}
|
||||
runTests();
|
||||
},
|
||||
|
||||
function() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.ignore_webidl_scope_checks", true],
|
||||
["dom.requestSync.enabled", true]]}, runTests);
|
||||
},
|
||||
|
||||
function() { test_minInterval(false); },
|
||||
|
||||
function() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.requestSync.minInterval", 1]]}, runTests);
|
||||
},
|
||||
|
||||
function() { test_minInterval(true); },
|
||||
];
|
||||
|
||||
function runTests() {
|
||||
if (!tests.length) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
var test = tests.shift();
|
||||
test();
|
||||
}
|
||||
|
||||
function finish() {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
runTests();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,132 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for requestSync - wakeUp</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="common_basic.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container"></div>
|
||||
<script type="application/javascript;version=1.7">
|
||||
|
||||
var oneShotCounter = 0;
|
||||
var multiShotCounter = 0;
|
||||
|
||||
function maybeDone() {
|
||||
if (oneShotCounter == 1 && multiShotCounter == 3) {
|
||||
runTests();
|
||||
}
|
||||
}
|
||||
|
||||
function setMessageHandler() {
|
||||
navigator.mozSetMessageHandler('request-sync', function(e) {
|
||||
ok(true, "One event has been received!");
|
||||
|
||||
if (e.task == "oneShot") {
|
||||
is(e.data, 42, "e.data is correct");
|
||||
is(e.lastSync, 0, "e.lastSync is correct");
|
||||
is(e.oneShot, true, "e.oneShot is correct");
|
||||
is(e.minInterval, 2, "e.minInterval is correct");
|
||||
is(e.wifiOnly, false, "e.wifiOnly is correct");
|
||||
|
||||
is(++oneShotCounter, 1, "Only 1 shot should be received here");
|
||||
maybeDone();
|
||||
}
|
||||
|
||||
else if (e.task == "multiShots") {
|
||||
is(e.data, 'hello world!', "e.data is correct");
|
||||
|
||||
if (multiShotCounter == 0) {
|
||||
is(e.lastSync, 0, "e.lastSync is correct");
|
||||
} else {
|
||||
isnot(e.lastSync, 0, "e.lastSync is correct");
|
||||
}
|
||||
|
||||
is(e.oneShot, false, "e.oneShot is correct");
|
||||
is(e.minInterval, 3, "e.minInterval is correct");
|
||||
is(e.wifiOnly, false, "e.wifiOnly is correct");
|
||||
|
||||
++multiShotCounter;
|
||||
maybeDone();
|
||||
}
|
||||
|
||||
else {
|
||||
ok(false, "Unknown event has been received!");
|
||||
}
|
||||
});
|
||||
|
||||
runTests();
|
||||
}
|
||||
|
||||
function test_register_oneShot() {
|
||||
navigator.sync.register('oneShot', { minInterval: 2,
|
||||
oneShot: true,
|
||||
data: 42,
|
||||
wifiOnly: false,
|
||||
wakeUpPage: location.href }).then(
|
||||
function() {
|
||||
ok(true, "navigator.sync.register() oneShot done");
|
||||
runTests();
|
||||
}, genericError);
|
||||
}
|
||||
|
||||
function test_register_multiShots() {
|
||||
navigator.sync.register('multiShots', { minInterval: 3,
|
||||
oneShot: false,
|
||||
data: 'hello world!',
|
||||
wifiOnly: false,
|
||||
wakeUpPage: location.href }).then(
|
||||
function() {
|
||||
ok(true, "navigator.sync.register() multiShots done");
|
||||
runTests();
|
||||
}, genericError);
|
||||
}
|
||||
|
||||
function test_wait() {
|
||||
// nothing to do here.
|
||||
}
|
||||
|
||||
var tests = [
|
||||
function() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.requestSync.enabled", true],
|
||||
["dom.requestSync.minInterval", 1],
|
||||
["dom.ignore_webidl_scope_checks", true]]}, runTests);
|
||||
},
|
||||
|
||||
function() {
|
||||
SpecialPowers.pushPermissions(
|
||||
[{ "type": "requestsync-manager", "allow": 1, "context": document } ], runTests);
|
||||
},
|
||||
|
||||
function() {
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.Cu.import("resource://gre/modules/RequestSyncService.jsm");
|
||||
}
|
||||
runTests();
|
||||
},
|
||||
|
||||
setMessageHandler,
|
||||
|
||||
test_register_oneShot,
|
||||
test_register_multiShots,
|
||||
|
||||
test_wait,
|
||||
];
|
||||
|
||||
function runTests() {
|
||||
if (!tests.length) {
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
var test = tests.shift();
|
||||
test();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
runTests();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,86 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for RequestSync interfaces</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<script type="application/javascript;version=1.7">
|
||||
|
||||
function test_no_interface() {
|
||||
ok(!("sync" in navigator), "navigator.sync should not exist without permissions");
|
||||
ok(!("syncManager" in navigator), "navigator.syncManager should not exist without permissions");
|
||||
runTests();
|
||||
}
|
||||
|
||||
function test_sync() {
|
||||
ok("register" in navigator.sync, "navigator.sync.register exists");
|
||||
ok("unregister" in navigator.sync, "navigator.sync.unregister exists");
|
||||
ok("registrations" in navigator.sync, "navigator.sync.registrations exists");
|
||||
ok("registration" in navigator.sync, "navigator.sync.registration exists");
|
||||
}
|
||||
|
||||
function test_sync_interface() {
|
||||
ok("sync" in navigator, "navigator.sync should exist with permissions");
|
||||
ok(!("syncManager" in navigator), "navigator.syncManager should not exist without permissions");
|
||||
|
||||
test_sync();
|
||||
runTests();
|
||||
}
|
||||
|
||||
function test_sync_manager_interface() {
|
||||
ok("sync" in navigator, "navigator.sync should exist with permissions");
|
||||
ok("syncManager" in navigator, "navigator.syncManager should exist with permissions");
|
||||
|
||||
test_sync();
|
||||
|
||||
ok("registrations" in navigator.syncManager, "navigator.syncManager.registrations exists");
|
||||
runTests();
|
||||
}
|
||||
|
||||
var tests = [
|
||||
test_no_interface,
|
||||
|
||||
function() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.ignore_webidl_scope_checks", true]]}, runTests);
|
||||
},
|
||||
|
||||
test_no_interface,
|
||||
|
||||
function() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.requestSync.enabled", true],
|
||||
["dom.requestSync.minInterval", 1]]}, runTests);
|
||||
},
|
||||
|
||||
test_sync_interface,
|
||||
|
||||
// Permissions
|
||||
function() {
|
||||
SpecialPowers.pushPermissions(
|
||||
[{ "type": "requestsync-manager", "allow": 1, "context": document } ], runTests);
|
||||
},
|
||||
|
||||
test_sync_manager_interface,
|
||||
];
|
||||
|
||||
function runTests() {
|
||||
if (!tests.length) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
var test = tests.shift();
|
||||
test();
|
||||
}
|
||||
|
||||
function finish() {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
runTests();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1209,6 +1209,10 @@ var interfaceNamesInGlobalScope =
|
|||
"SVGZoomAndPan",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"SVGZoomEvent",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "RequestSyncManager", b2g: true, pref: "dom.requestSync.enabled", premission: "requestsync-manager" },
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "RequestSyncScheduler", b2g: true, pref: "dom.requestSync.enabled" },
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "Telephony", b2g: true, pref: "dom.telephony.enabled"},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
// Rappresentation of the app in the RequestTaskFull.
|
||||
dictionary RequestTaskApp {
|
||||
USVString origin;
|
||||
USVString manifestURL;
|
||||
boolean isInBrowserElement;
|
||||
};
|
||||
|
||||
// Like a normal task, but with info about the app.
|
||||
dictionary RequestTaskFull : RequestTask {
|
||||
RequestTaskApp app;
|
||||
};
|
||||
|
||||
[NavigatorProperty="syncManager",
|
||||
AvailableIn=CertifiedApps,
|
||||
Pref="dom.requestSync.enabled",
|
||||
CheckPermissions="requestsync-manager",
|
||||
JSImplementation="@mozilla.org/dom/request-sync-manager;1"]
|
||||
// This interface will be used only by the B2G SystemApp
|
||||
interface RequestSyncManager {
|
||||
Promise<sequence<RequestTaskFull>> registrations();
|
||||
};
|
|
@ -0,0 +1,38 @@
|
|||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
// This is the dictionary for the creation of a new task.
|
||||
dictionary RequestTaskParams {
|
||||
required USVString wakeUpPage;
|
||||
boolean oneShot = true;
|
||||
required long minInterval; // in seconds >= dom.requestSync.minInterval or 100 secs
|
||||
boolean wifiOnly = true;
|
||||
any data = null;
|
||||
};
|
||||
|
||||
|
||||
// This is the dictionary you can have back from registration{s}().
|
||||
dictionary RequestTask : RequestTaskParams {
|
||||
USVString task = "";
|
||||
|
||||
// Last synchonization date.. maybe it's useful to know.
|
||||
DOMTimeStamp lastSync;
|
||||
};
|
||||
|
||||
[NavigatorProperty="sync",
|
||||
AvailableIn=CertifiedApps,
|
||||
Pref="dom.requestSync.enabled",
|
||||
JSImplementation="@mozilla.org/dom/request-sync-scheduler;1"]
|
||||
interface RequestSyncScheduler {
|
||||
|
||||
Promise<void> register(USVString task,
|
||||
optional RequestTaskParams params);
|
||||
Promise<void> unregister(USVString task);
|
||||
|
||||
// Useful methods to get registrations
|
||||
Promise<sequence<RequestTask>> registrations();
|
||||
Promise<RequestTask> registration(USVString task);
|
||||
};
|
|
@ -341,6 +341,8 @@ WEBIDL_FILES = [
|
|||
'Range.webidl',
|
||||
'Rect.webidl',
|
||||
'Request.webidl',
|
||||
'RequestSyncManager.webidl',
|
||||
'RequestSyncScheduler.webidl',
|
||||
'ResourceStats.webidl',
|
||||
'ResourceStatsManager.webidl',
|
||||
'Response.webidl',
|
||||
|
|
|
@ -286,6 +286,9 @@
|
|||
@BINPATH@/components/zipwriter.xpt
|
||||
|
||||
; JavaScript components
|
||||
@BINPATH@/components/RequestSync.manifest
|
||||
@BINPATH@/components/RequestSyncManager.js
|
||||
@BINPATH@/components/RequestSyncScheduler.js
|
||||
@BINPATH@/components/ChromeNotifications.js
|
||||
@BINPATH@/components/ChromeNotifications.manifest
|
||||
@BINPATH@/components/ConsoleAPI.manifest
|
||||
|
|
Загрузка…
Ссылка в новой задаче