Bug 1018320 - RequestSync API - patch 6 - Manager API, r=ehsan

This commit is contained in:
Andrea Marchesini 2015-01-13 09:53:24 +00:00
Родитель d6ae21b741
Коммит bf1781a498
10 изменённых файлов: 346 добавлений и 20 удалений

Просмотреть файл

@ -0,0 +1,48 @@
/* 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';
this.EXPORTED_SYMBOLS = ['RequestSyncApp'];
function debug(s) {
//dump('DEBUG RequestSyncApp: ' + s + '\n');
}
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
this.RequestSyncApp = function(aData) {
debug('created');
let keys = [ 'origin', 'manifestURL', 'isInBrowserElement' ];
for (let i = 0; i < keys.length; ++i) {
if (!(keys[i] in aData)) {
dump("ERROR - RequestSyncApp must receive a full app object: " + keys[i] + " missing.");
throw "ERROR!";
}
this["_" + keys[i]] = aData[keys[i]];
}
}
this.RequestSyncApp.prototype = {
classDescription: 'RequestSyncApp XPCOM Component',
classID: Components.ID('{5a0b64db-a2be-4f08-a6c5-8bf2e3ae0c57}'),
contractID: '@mozilla.org/dom/request-sync-manager;1',
QueryInterface: XPCOMUtils.generateQI([]),
get origin() {
return this._origin;
},
get manifestURL() {
return this._manifestURL;
},
get isInBrowserElement() {
return this._isInBrowserElement;
}
};

Просмотреть файл

@ -12,6 +12,8 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/DOMRequestHelper.jsm"); Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
Cu.import('resource://gre/modules/XPCOMUtils.jsm'); Cu.import('resource://gre/modules/XPCOMUtils.jsm');
Cu.import('resource://gre/modules/RequestSyncApp.jsm');
Cu.import('resource://gre/modules/RequestSyncTask.jsm');
XPCOMUtils.defineLazyServiceGetter(this, "cpmm", XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
"@mozilla.org/childprocessmessagemanager;1", "@mozilla.org/childprocessmessagemanager;1",
@ -31,7 +33,8 @@ RequestSyncManager.prototype = {
Ci.nsIObserver, Ci.nsIObserver,
Ci.nsIDOMGlobalPropertyInitializer]), Ci.nsIDOMGlobalPropertyInitializer]),
_messages: [ "RequestSyncManager:Registrations:Return" ], _messages: [ "RequestSyncManager:Registrations:Return",
"RequestSyncManager:SetPolicy:Return" ],
init: function(aWindow) { init: function(aWindow) {
debug("init"); debug("init");
@ -55,6 +58,42 @@ RequestSyncManager.prototype = {
return this.sendMessage("RequestSyncManager:Registrations", {}); return this.sendMessage("RequestSyncManager:Registrations", {});
}, },
setPolicy: function(aTask, aOrigin, aManifestURL, aIsInBrowserElement,
aState, aOverwrittenMinInterval) {
debug('setPolicy');
return this.sendMessage("RequestSyncManager:SetPolicy",
{ task: aTask,
origin: aOrigin,
manifestURL: aManifestURL,
isInBrowserElement: aIsInBrowserElement,
state: aState,
overwrittenMinInterval: aOverwrittenMinInterval });
},
registrationsResult: function(aData) {
debug("registrationsResult");
let results = new this._window.Array();
for (let i = 0; i < aData.length; ++i) {
if (!("app" in aData[i])) {
dump("ERROR - Serialization error in RequestSyncManager.\n");
continue;
}
let app = new RequestSyncApp(aData[i].app);
let exposedApp =
this._window.RequestSyncApp._create(this._window, app);
let task = new RequestSyncTask(this, this._window, exposedApp, aData[i]);
let exposedTask =
this._window.RequestSyncTask._create(this._window, task);
results.push(exposedTask);
}
return results;
},
receiveMessage: function(aMessage) { receiveMessage: function(aMessage) {
debug('receiveMessage'); debug('receiveMessage');
@ -68,6 +107,11 @@ RequestSyncManager.prototype = {
return; return;
} }
if (aMessage.name == 'RequestSyncManager:Registrations:Return') {
req.resolve(this.registrationsResult(aMessage.data.results));
return;
}
if ('results' in aMessage.data) { if ('results' in aMessage.data) {
req.resolve(Cu.cloneInto(aMessage.data.results, this._window)); req.resolve(Cu.cloneInto(aMessage.data.results, this._window));
return; return;

Просмотреть файл

@ -16,6 +16,10 @@ const RSYNC_MIN_INTERVAL = 100;
const RSYNC_OPERATION_TIMEOUT = 120000 // 2 minutes const RSYNC_OPERATION_TIMEOUT = 120000 // 2 minutes
const RSYNC_STATE_ENABLED = "enabled";
const RSYNC_STATE_DISABLED = "disabled";
const RSYNC_STATE_WIFIONLY = "wifiOnly";
Cu.import('resource://gre/modules/IndexedDBHelper.jsm'); Cu.import('resource://gre/modules/IndexedDBHelper.jsm');
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Services.jsm");
@ -47,9 +51,12 @@ this.RequestSyncService = {
children: [], children: [],
_messages: [ "RequestSync:Register", "RequestSync:Unregister", _messages: [ "RequestSync:Register",
"RequestSync:Registrations", "RequestSync:Registration", "RequestSync:Unregister",
"RequestSyncManager:Registrations" ], "RequestSync:Registrations",
"RequestSync:Registration",
"RequestSyncManager:Registrations",
"RequestSyncManager:SetPolicy" ],
_pendingOperation: false, _pendingOperation: false,
_pendingMessages: [], _pendingMessages: [],
@ -316,6 +323,10 @@ this.RequestSyncService = {
this.managerRegistrations(aMessage.target, aMessage.data, principal); this.managerRegistrations(aMessage.target, aMessage.data, principal);
break; break;
case "RequestSyncManager:SetPolicy":
this.managerSetPolicy(aMessage.target, aMessage.data, principal);
break;
default: default:
debug("Wrong message: " + aMessage.name); debug("Wrong message: " + aMessage.name);
break; break;
@ -370,6 +381,13 @@ this.RequestSyncService = {
aData.params.lastSync = 0; aData.params.lastSync = 0;
aData.params.principal = aPrincipal; aData.params.principal = aPrincipal;
aData.params.state = RSYNC_STATE_ENABLED;
if (aData.params.wifiOnly) {
aData.params.state = RSYNC_STATE_WIFIONLY;
}
aData.params.overwrittenMinInterval = 0;
let dbKey = aData.task + "|" + let dbKey = aData.task + "|" +
aPrincipal.appId + '|' + aPrincipal.appId + '|' +
aPrincipal.isInBrowserElement + '|' + aPrincipal.isInBrowserElement + '|' +
@ -478,6 +496,51 @@ this.RequestSyncService = {
results: results }); results: results });
}, },
// Set a policy to a task.
managerSetPolicy: function(aTarget, aData, aPrincipal) {
debug("managerSetPolicy");
let toSave = null;
let self = this;
this.forEachRegistration(function(aObj) {
if (aObj.principal.isInBrowserElement != aData.isInBrowserElement ||
aObj.principal.origin != aData.origin) {
return;
}
let app = appsService.getAppByLocalId(aObj.principal.appId);
if (app && app.manifestURL != aData.manifestURL ||
(!app && aData.manifestURL != "")) {
return;
}
if ("overwrittenMinInterval" in aData) {
aObj.data.overwrittenMinInterval = aData.overwrittenMinInterval;
}
aObj.data.state = aData.state;
if (toSave) {
dump("ERROR!! RequestSyncService - SetPolicy matches more than 1 task.\n");
return;
}
toSave = aObj;
});
if (!toSave) {
aTarget.sendAsyncMessage("RequestSyncManager:SetPolicy:Return",
{ requestID: aData.requestID, error: "UnknownTaskError" });
return;
}
this.updateObjectInDB(toSave, function() {
self.scheduleTimer(toSave);
aTarget.sendAsyncMessage("RequestSyncManager:SetPolicy:Return",
{ requestID: aData.requestID });
});
},
// We cannot expose the full internal object to content but just a subset. // We cannot expose the full internal object to content but just a subset.
// This method creates this subset. // This method creates this subset.
createPartialTaskObject: function(aObj) { createPartialTaskObject: function(aObj) {
@ -502,6 +565,8 @@ this.RequestSyncService = {
obj.app.manifestURL = app.manifestURL; obj.app.manifestURL = app.manifestURL;
} }
obj.state = aObj.state;
obj.overwrittenMinInterval = aObj.overwrittenMinInterval;
return obj; return obj;
}, },
@ -509,21 +574,35 @@ this.RequestSyncService = {
scheduleTimer: function(aObj) { scheduleTimer: function(aObj) {
debug("scheduleTimer"); debug("scheduleTimer");
if (aObj.timer) {
aObj.timer.cancel();
aObj.timer = null;
}
// A registration can be already inactive if it was 1 shot. // A registration can be already inactive if it was 1 shot.
if (!aObj.active) { if (!aObj.active) {
return; return;
} }
if (aObj.data.state == RSYNC_STATE_DISABLED) {
return;
}
// WifiOnly check. // WifiOnly check.
if (aObj.data.wifiOnly && !this._wifi) { if (aObj.data.state == RSYNC_STATE_WIFIONLY && !this._wifi) {
return; return;
} }
aObj.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); aObj.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
let interval = aObj.data.minInterval;
if (aObj.data.overwrittenMinInterval > 0) {
interval = aObj.data.overwrittenMinInterval;
}
let self = this; let self = this;
aObj.timer.initWithCallback(function() { self.timeout(aObj); }, aObj.timer.initWithCallback(function() { self.timeout(aObj); },
aObj.data.minInterval * 1000, interval * 1000,
Ci.nsITimer.TYPE_ONE_SHOT); Ci.nsITimer.TYPE_ONE_SHOT);
}, },
@ -739,7 +818,7 @@ this.RequestSyncService = {
// Disable all the wifiOnly tasks. // Disable all the wifiOnly tasks.
let self = this; let self = this;
this.forEachRegistration(function(aObj) { this.forEachRegistration(function(aObj) {
if (aObj.data.wifiOnly && aObj.timer) { if (aObj.data.state == RSYNC_STATE_WIFIONLY && aObj.timer) {
aObj.timer.cancel(); aObj.timer.cancel();
aObj.timer = null; aObj.timer = null;

Просмотреть файл

@ -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';
this.EXPORTED_SYMBOLS = ['RequestSyncTask'];
function debug(s) {
//dump('DEBUG RequestSyncTask: ' + s + '\n');
}
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
this.RequestSyncTask = function(aManager, aWindow, aApp, aData) {
debug('created');
this._manager = aManager;
this._window = aWindow;
this._app = aApp;
let keys = [ 'task', 'lastSync', 'oneShot', 'minInterval', 'wakeUpPage',
'wifiOnly', 'data', 'state', 'overwrittenMinInterval' ];
for (let i = 0; i < keys.length; ++i) {
if (!(keys[i] in aData)) {
dump("ERROR - RequestSyncTask must receive a fully app object: " + keys[i] + " missing.");
throw "ERROR!";
}
this["_" + keys[i]] = aData[keys[i]];
}
}
this.RequestSyncTask.prototype = {
classDescription: 'RequestSyncTask XPCOM Component',
classID: Components.ID('{a1e1c9c6-ce42-49d4-b8b4-fbd686d8fdd9}'),
contractID: '@mozilla.org/dom/request-sync-manager;1',
QueryInterface: XPCOMUtils.generateQI([]),
get app() {
return this._app;
},
get state() {
return this._state;
},
get overwrittenMinInterval() {
return this._overwrittenMinInterval;
},
get task() {
return this._task;
},
get lastSync() {
return this._lastSync;
},
get wakeUpPage() {
return this._wakeUpPage;
},
get oneShot() {
return this._oneShot;
},
get minInterval() {
return this._minInterval;
},
get wifiOnly() {
return this._wifiOnly;
},
get data() {
return this._data;
},
setPolicy: function(aState, aOverwrittenMinInterval) {
debug("setPolicy");
let self = this;
return new this._window.Promise(function(aResolve, aReject) {
let p = self._manager.setPolicy(self._task, self._app.origin,
self._app.manifestURL,
self._app.isInBrowserElement,
aState,
aOverwrittenMinInterval);
// Set the new value only when the promise is resolved.
p.then(function() {
self._state = aState;
self._overwrittenMinInterval = aOverwrittenMinInterval;
aResolve();
}, aReject);
});
}
};

Просмотреть файл

@ -17,7 +17,9 @@ EXTRA_COMPONENTS += [
] ]
EXTRA_JS_MODULES += [ EXTRA_JS_MODULES += [
'RequestSyncApp.jsm',
'RequestSyncService.jsm', 'RequestSyncService.jsm',
'RequestSyncTask.jsm',
] ]
SOURCES += [ SOURCES += [

Просмотреть файл

@ -151,7 +151,7 @@ function test_managerRegistrationsEmpty() {
genericError); genericError);
} }
function test_managerRegistrations() { function test_managerRegistrations(state, overwrittenMinInterval) {
navigator.syncManager.registrations().then( navigator.syncManager.registrations().then(
function(results) { function(results) {
is(results.length, 1, "navigator.sync.registrations() should not return an empty array."); is(results.length, 1, "navigator.sync.registrations() should not return an empty array.");
@ -166,7 +166,22 @@ function test_managerRegistrations() {
ok("manifestURL" in results[0].app, "navigator.sync.registrations()[0].app.manifestURL 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.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"); is(results[0].app.isInBrowserElement, false, "navigator.sync.registrations()[0].app.isInBrowserElement is correct");
is(results[0].state, state, "navigator.sync.registrations()[0].state is correct");
is(results[0].overwrittenMinInterval, overwrittenMinInterval, "navigator.sync.registrations()[0].overwrittenMinInterval is correct");
ok("setPolicy" in results[0], "navigator.sync.registrations()[0].setPolicy is correct");
runTests(); runTests();
}, },
genericError); genericError);
} }
function test_managerSetPolicy(state, overwrittenMinInterval) {
navigator.syncManager.registrations().then(
function(results) {
results[0].setPolicy(state, overwrittenMinInterval).then(
function() {
ok(state, results[0].state, "State matches");
ok(overwrittenMinInterval, results[0].overwrittenMinInterval, "OverwrittenMinInterval matches");
runTests();
}, genericError);
}).catch(genericError);
}

Просмотреть файл

@ -38,12 +38,18 @@
// overwrite the same registration. // overwrite the same registration.
test_register, test_register,
test_managerRegistrations, function() { test_managerRegistrations('wifiOnly', 0); },
test_registrations, test_registrations,
test_registrationEmpty, test_registrationEmpty,
test_registration, test_registration,
function() { test_managerSetPolicy('disabled', 123); },
function() { test_managerRegistrations('disabled', 123); },
function() { test_managerSetPolicy('enabled', 42); },
function() { test_managerRegistrations('enabled', 42); },
test_unregister, test_unregister,
test_unregisterDuplicate, test_unregisterDuplicate,

Просмотреть файл

@ -1209,6 +1209,10 @@ var interfaceNamesInGlobalScope =
"SVGZoomEvent", "SVGZoomEvent",
// IMPORTANT: Do not change this list without review from a DOM peer! // IMPORTANT: Do not change this list without review from a DOM peer!
{name: "RequestSyncManager", b2g: true, pref: "dom.requestSync.enabled", permission: ["requestsync-manager"] }, {name: "RequestSyncManager", b2g: true, pref: "dom.requestSync.enabled", permission: ["requestsync-manager"] },
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "RequestSyncApp", b2g: true, pref: "dom.requestSync.enabled", permission: ["requestsync-manager"] },
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "RequestSyncTask", b2g: true, pref: "dom.requestSync.enabled", permission: ["requestsync-manager"] },
// IMPORTANT: Do not change this list without review from a DOM peer! // IMPORTANT: Do not change this list without review from a DOM peer!
{name: "Telephony", b2g: true, pref: "dom.telephony.enabled"}, {name: "Telephony", b2g: true, pref: "dom.telephony.enabled"},
// IMPORTANT: Do not change this list without review from a DOM peer! // IMPORTANT: Do not change this list without review from a DOM peer!

Просмотреть файл

@ -4,16 +4,43 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. * You can obtain one at http://mozilla.org/MPL/2.0/.
*/ */
// Rappresentation of the app in the RequestTaskFull. [AvailableIn=CertifiedApps,
dictionary RequestTaskApp { Pref="dom.requestSync.enabled",
USVString origin; CheckPermissions="requestsync-manager",
USVString manifestURL; JSImplementation="@mozilla.org/dom/request-sync-task-app;1"]
boolean isInBrowserElement; interface RequestSyncApp {
readonly attribute USVString origin;
readonly attribute USVString manifestURL;
readonly attribute boolean isInBrowserElement;
}; };
enum RequestSyncTaskPolicyState { "enabled", "disabled", "wifiOnly" };
// Like a normal task, but with info about the app. // Like a normal task, but with info about the app.
dictionary RequestTaskFull : RequestTask { [AvailableIn=CertifiedApps,
RequestTaskApp app; Pref="dom.requestSync.enabled",
CheckPermissions="requestsync-manager",
JSImplementation="@mozilla.org/dom/request-sync-task-manager;1"]
interface RequestSyncTask {
// This object describes the app that is owning the task.
readonly attribute RequestSyncApp app;
// Using setPolicy it's possible to owerwrite the state and the minInterval.
readonly attribute RequestSyncTaskPolicyState state;
readonly attribute long overwrittenMinInterval;
// These attributes are taken from the configuration of the task:
readonly attribute USVString task;
readonly attribute DOMTimeStamp lastSync;
readonly attribute USVString wakeUpPage;
readonly attribute boolean oneShot;
readonly attribute long minInterval;
readonly attribute boolean wifiOnly;
readonly attribute any data;
Promise<void> setPolicy(RequestSyncTaskPolicyState aState,
optional long ovewrittenMinInterval);
}; };
[NavigatorProperty="syncManager", [NavigatorProperty="syncManager",
@ -23,5 +50,5 @@ dictionary RequestTaskFull : RequestTask {
JSImplementation="@mozilla.org/dom/request-sync-manager;1"] JSImplementation="@mozilla.org/dom/request-sync-manager;1"]
// This interface will be used only by the B2G SystemApp // This interface will be used only by the B2G SystemApp
interface RequestSyncManager { interface RequestSyncManager {
Promise<sequence<RequestTaskFull>> registrations(); Promise<sequence<RequestSyncTask>> registrations();
}; };

Просмотреть файл

@ -15,7 +15,7 @@ dictionary RequestTaskParams {
// This is the dictionary you can have back from registration{s}(). // This is the dictionary you can have back from registration{s}().
dictionary RequestTask : RequestTaskParams { dictionary RequestTaskFull : RequestTaskParams {
USVString task = ""; USVString task = "";
// Last synchonization date.. maybe it's useful to know. // Last synchonization date.. maybe it's useful to know.
@ -33,6 +33,6 @@ interface RequestSyncScheduler {
Promise<void> unregister(USVString task); Promise<void> unregister(USVString task);
// Useful methods to get registrations // Useful methods to get registrations
Promise<sequence<RequestTask>> registrations(); Promise<sequence<RequestTaskFull>> registrations();
Promise<RequestTask> registration(USVString task); Promise<RequestTaskFull> registration(USVString task);
}; };