Bug 1018320 - RequestSync API - patch 1 - webIDL and basic logic, r=ehsan

This commit is contained in:
Andrea Marchesini 2015-01-05 13:41:59 +01:00
parent ccd76e1eaa
commit f13806b672
27 changed files with 1749 additions and 0 deletions

View File

@ -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');

View File

@ -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

View File

@ -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

View File

@ -84,6 +84,7 @@ this.SystemMessagePermissionsTable = {
"push-register": {
"push": []
},
"request-sync": { },
"sms-delivery-success": {
"sms": []
},

View File

@ -43,6 +43,7 @@ DIRS += [
'base',
'activities',
'archivereader',
'requestsync',
'bindings',
'battery',
'browser-element',

View File

@ -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}

View File

@ -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]);

View File

@ -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]);

View File

@ -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 = [""];

17
dom/requestsync/moz.build Normal file
View File

@ -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',
]

View File

@ -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');
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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" }
}

View File

@ -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>

View File

@ -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>

View File

@ -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'

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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!

View File

@ -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();
};

View File

@ -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);
};

View File

@ -341,6 +341,8 @@ WEBIDL_FILES = [
'Range.webidl',
'Rect.webidl',
'Request.webidl',
'RequestSyncManager.webidl',
'RequestSyncScheduler.webidl',
'ResourceStats.webidl',
'ResourceStatsManager.webidl',
'Response.webidl',

View File

@ -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