Bug 964447 - Add sync APIs to HomeProvider. r=mcomella

This commit is contained in:
Margaret Leibovic 2014-02-06 18:08:07 -08:00
Родитель caae68189e
Коммит ff80549e7d
3 изменённых файлов: 154 добавлений и 2 удалений

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

@ -825,3 +825,9 @@ pref("browser.snippets.syncPromo.enabled", false);
// This currently points to the development server.
pref("browser.webapps.apkFactoryUrl", "http://dapk.net/application.apk");
#endif
// Whether or not to only sync home provider data when the user is on wifi.
pref("home.sync.wifiOnly", false);
// How frequently to check if we should sync home provider data.
pref("home.sync.checkIntervalSecs", 3600);

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

@ -7,14 +7,43 @@ const { utils: Cu } = Components;
Cu.import("resource://gre/modules/HomeProvider.jsm");
Cu.import("resource://gre/modules/osfile.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Sqlite.jsm");
Cu.import("resource://gre/modules/Task.jsm");
const TEST_DATASET_ID = "test-dataset-id";
const TEST_URL = "http://test.com";
const PREF_SYNC_CHECK_INTERVAL_SECS = "home.sync.checkIntervalSecs";
const TEST_INTERVAL_SECS = 1;
const DB_PATH = OS.Path.join(OS.Constants.Path.profileDir, "home.sqlite");
add_test(function test_request_sync() {
// The current implementation of requestSync is synchronous.
let success = HomeProvider.requestSync(TEST_DATASET_ID, function callback(datasetId) {
do_check_eq(datasetId, TEST_DATASET_ID);
});
do_check_true(success);
run_next_test();
});
add_test(function test_periodic_sync() {
do_register_cleanup(function cleanup() {
Services.prefs.clearUserPref(PREF_SYNC_CHECK_INTERVAL_SECS);
HomeProvider.removePeriodicSync(TEST_DATASET_ID);
});
// Lower the check interval for testing purposes.
Services.prefs.setIntPref(PREF_SYNC_CHECK_INTERVAL_SECS, TEST_INTERVAL_SECS);
HomeProvider.addPeriodicSync(TEST_DATASET_ID, TEST_INTERVAL_SECS, function callback(datasetId) {
do_check_eq(datasetId, TEST_DATASET_ID);
run_next_test();
});
});
add_task(function test_save_and_delete() {
// Use the HomeProvider API to save some data.
let storage = HomeProvider.getStorage(TEST_DATASET_ID);

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

@ -7,16 +7,31 @@
this.EXPORTED_SYMBOLS = [ "HomeProvider" ];
const { utils: Cu } = Components;
const { utils: Cu, classes: Cc, interfaces: Ci } = Components;
Cu.import("resource://gre/modules/osfile.jsm");
Cu.import("resource://gre/modules/Promise.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Sqlite.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
const SCHEMA_VERSION = 1;
const DB_PATH = OS.Path.join(OS.Constants.Path.profileDir, "home.sqlite");
XPCOMUtils.defineLazyGetter(this, "DB_PATH", function() {
return OS.Path.join(OS.Constants.Path.profileDir, "home.sqlite");
});
const PREF_STORAGE_LAST_SYNC_TIME_PREFIX = "home.storage.lastSyncTime.";
const PREF_SYNC_WIFI_ONLY = "home.sync.wifiOnly";
const PREF_SYNC_CHECK_INTERVAL_SECS = "home.sync.checkIntervalSecs";
XPCOMUtils.defineLazyGetter(this, "gSyncCheckIntervalSecs", function() {
return Services.prefs.getIntPref(PREF_SYNC_CHECK_INTERVAL_SECS);
});
XPCOMUtils.defineLazyServiceGetter(this,
"gUpdateTimerManager", "@mozilla.org/updates/timer-manager;1", "nsIUpdateTimerManager");
/**
* All SQL statements should be defined here.
@ -41,6 +56,53 @@ const SQL = {
"DELETE FROM items WHERE dataset_id = :dataset_id"
}
/**
* Technically this function checks to see if the user is on a local network,
* but we express this as "wifi" to the user.
*/
function isUsingWifi() {
let network = Cc["@mozilla.org/network/network-link-service;1"].getService(Ci.nsINetworkLinkService);
return (network.linkType === Ci.nsINetworkLinkService.LINK_TYPE_WIFI || network.linkType === Ci.nsINetworkLinkService.LINK_TYPE_ETHERNET);
}
function getNowInSeconds() {
return Math.round(Date.now() / 1000);
}
function getLastSyncPrefName(datasetId) {
return PREF_STORAGE_LAST_SYNC_TIME_PREFIX + datasetId;
}
// Whether or not we've registered an update timer.
var gTimerRegistered = false;
// Map of datasetId -> { interval: <integer>, callback: <function> }
var gSyncCallbacks = {};
/**
* nsITimerCallback implementation. Checks to see if it's time to sync any registered datasets.
*
* @param timer The timer which has expired.
*/
function syncTimerCallback(timer) {
for (let datasetId in gSyncCallbacks) {
let lastSyncTime = 0;
try {
lastSyncTime = Services.prefs.getIntPref(getLastSyncPrefName(datasetId));
} catch(e) { }
let now = getNowInSeconds();
let { interval: interval, callback: callback } = gSyncCallbacks[datasetId];
if (lastSyncTime < now - interval) {
let success = HomeProvider.requestSync(datasetId, callback);
if (success) {
Services.prefs.setIntPref(getLastSyncPrefName(datasetId), now);
}
}
}
}
this.HomeProvider = Object.freeze({
/**
* Returns a storage associated with a given dataset identifer.
@ -52,6 +114,61 @@ this.HomeProvider = Object.freeze({
*/
getStorage: function(datasetId) {
return new HomeStorage(datasetId);
},
/**
* Checks to see if it's an appropriate time to sync.
*
* @param datasetId Unique identifier for the dataset to sync.
* @param callback Function to call when it's time to sync, called with datasetId as a parameter.
*
* @return boolean Whether or not we were able to sync.
*/
requestSync: function(datasetId, callback) {
// Make sure it's a good time to sync.
if (Services.prefs.getBoolPref(PREF_SYNC_WIFI_ONLY) && !isUsingWifi()) {
Cu.reportError("HomeProvider: Failed to sync because device is not on a local network");
return false;
}
callback(datasetId);
return true;
},
/**
* Specifies that a sync should be requested for the given dataset and update interval.
*
* @param datasetId Unique identifier for the dataset to sync.
* @param interval Update interval in seconds. By default, this is throttled to 3600 seconds (1 hour).
* @param callback Function to call when it's time to sync, called with datasetId as a parameter.
*/
addPeriodicSync: function(datasetId, interval, callback) {
// Warn developers if they're expecting more frequent notifications that we allow.
if (interval < gSyncCheckIntervalSecs) {
Cu.reportError("HomeProvider: Warning for dataset " + datasetId +
" : Sync notifications are throttled to " + gSyncCheckIntervalSecs + " seconds");
}
gSyncCallbacks[datasetId] = {
interval: interval,
callback: callback
};
if (!gTimerRegistered) {
gUpdateTimerManager.registerTimer("home-provider-sync-timer", syncTimerCallback, gSyncCheckIntervalSecs);
gTimerRegistered = true;
}
},
/**
* Removes a periodic sync timer.
*
* @param datasetId Dataset to sync.
*/
removePeriodicSync: function(datasetId) {
delete gSyncCallbacks[datasetId];
Services.prefs.clearUserPref(getLastSyncPrefName(datasetId));
// You can't unregister a update timer, so we don't try to do that.
}
});