зеркало из https://github.com/mozilla/gecko-dev.git
Bug 964447 - Add sync APIs to HomeProvider. r=mcomella
This commit is contained in:
Родитель
caae68189e
Коммит
ff80549e7d
|
@ -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.
|
||||
}
|
||||
});
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче