mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 629780 - Merge fx-sync to mozilla-central. a=blockers
This commit is contained in:
commit
dc892b7a85
@ -1,3 +1,4 @@
|
||||
#filter substitution
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
@ -38,7 +39,7 @@
|
||||
// Process each item in the "constants hash" to add to "global" and give a name
|
||||
let EXPORTED_SYMBOLS = [((this[key] = val), key) for ([key, val] in Iterator({
|
||||
|
||||
WEAVE_CHANNEL: "@xpi_type@",
|
||||
WEAVE_CHANNEL: "@weave_channel@",
|
||||
WEAVE_VERSION: "@weave_version@",
|
||||
WEAVE_ID: "@weave_id@",
|
||||
|
||||
@ -47,7 +48,7 @@ WEAVE_ID: "@weave_id@",
|
||||
// the per-engine cleartext formats.
|
||||
STORAGE_VERSION: 5,
|
||||
|
||||
UPDATED_DEV_URL: "https://services.mozilla.com/sync/updated/?version=@weave_version@&channel=@xpi_type@",
|
||||
UPDATED_DEV_URL: "https://services.mozilla.com/sync/updated/?version=@weave_version@&channel=@weave_channel@",
|
||||
UPDATED_REL_URL: "http://www.mozilla.com/firefox/sync/updated.html",
|
||||
|
||||
PREFS_BRANCH: "services.sync.",
|
||||
@ -76,6 +77,9 @@ MULTI_DESKTOP_SYNC: 60 * 60 * 1000, // 1 hour
|
||||
MULTI_MOBILE_SYNC: 5 * 60 * 1000, // 5 minutes
|
||||
PARTIAL_DATA_SYNC: 60 * 1000, // 1 minute
|
||||
|
||||
MAX_ERROR_COUNT_BEFORE_BACKOFF: 3,
|
||||
MAX_IGNORE_ERROR_COUNT: 5,
|
||||
|
||||
// HMAC event handling timeout.
|
||||
// 10 minutes: a compromise between the multi-desktop sync interval
|
||||
// and the mobile sync interval.
|
||||
@ -91,6 +95,7 @@ MOBILE_BATCH_SIZE: 50,
|
||||
// Default batch size for applying incoming records.
|
||||
DEFAULT_STORE_BATCH_SIZE: 1,
|
||||
HISTORY_STORE_BATCH_SIZE: 50, // same as MOBILE_BATCH_SIZE
|
||||
FORMS_STORE_BATCH_SIZE: 50, // same as MOBILE_BATCH_SIZE
|
||||
|
||||
// score thresholds for early syncs
|
||||
SINGLE_USER_THRESHOLD: 1000,
|
||||
|
@ -23,6 +23,7 @@
|
||||
* Jono DiCarlo <jdicarlo@mozilla.org>
|
||||
* Anant Narayanan <anant@kix.in>
|
||||
* Philipp von Weitershausen <philipp@weitershausen.de>
|
||||
* Richard Newman <rnewman@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
|
@ -44,6 +44,7 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://services-sync/engines.js");
|
||||
Cu.import("resource://services-sync/record.js");
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
Cu.import("resource://services-sync/constants.js");
|
||||
Cu.import("resource://services-sync/log4moz.js");
|
||||
|
||||
const FORMS_TTL = 5184000; // 60 days
|
||||
@ -159,6 +160,8 @@ FormEngine.prototype = {
|
||||
_storeObj: FormStore,
|
||||
_trackerObj: FormTracker,
|
||||
_recordObj: FormRec,
|
||||
applyIncomingBatchSize: FORMS_STORE_BATCH_SIZE,
|
||||
|
||||
get prefName() "history",
|
||||
|
||||
_findDupe: function _findDupe(item) {
|
||||
@ -173,6 +176,12 @@ function FormStore(name) {
|
||||
FormStore.prototype = {
|
||||
__proto__: Store.prototype,
|
||||
|
||||
applyIncomingBatch: function applyIncomingBatch(records) {
|
||||
return Utils.runInTransaction(Svc.Form.DBConnection, function() {
|
||||
return Store.prototype.applyIncomingBatch.call(this, records);
|
||||
}, this);
|
||||
},
|
||||
|
||||
getAllIDs: function FormStore_getAllIDs() {
|
||||
let guids = {};
|
||||
for each (let {name, value} in FormWrapper.getAllEntries())
|
||||
|
@ -21,6 +21,7 @@
|
||||
* Justin Dolske <dolske@mozilla.com>
|
||||
* Anant Narayanan <anant@kix.in>
|
||||
* Philipp von Weitershausen <philipp@weitershausen.de>
|
||||
* Richard Newman <rnewman@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
@ -91,6 +92,9 @@ PasswordEngine.prototype = {
|
||||
|
||||
_findDupe: function _findDupe(item) {
|
||||
let login = this._store._nsLoginInfoFromRecord(item);
|
||||
if (!login)
|
||||
return;
|
||||
|
||||
let logins = Svc.Login.findLogins({}, login.hostname, login.formSubmitURL,
|
||||
login.httpRealm);
|
||||
|
||||
@ -110,9 +114,20 @@ PasswordStore.prototype = {
|
||||
__proto__: Store.prototype,
|
||||
|
||||
_nsLoginInfoFromRecord: function PasswordStore__nsLoginInfoRec(record) {
|
||||
if (record.formSubmitURL &&
|
||||
record.httpRealm) {
|
||||
this._log.warn("Record " + record.id +
|
||||
" has both formSubmitURL and httpRealm. Skipping.");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Passing in "undefined" results in an empty string, which later
|
||||
// counts as a value. Explicitly `|| null` these fields according to JS
|
||||
// truthiness. Records with empty strings or null will be unmolested.
|
||||
function nullUndefined(x) (x == undefined) ? null : x;
|
||||
let info = new this._nsLoginInfo(record.hostname,
|
||||
record.formSubmitURL,
|
||||
record.httpRealm,
|
||||
nullUndefined(record.formSubmitURL),
|
||||
nullUndefined(record.httpRealm),
|
||||
record.username,
|
||||
record.password,
|
||||
record.usernameField,
|
||||
@ -198,8 +213,18 @@ PasswordStore.prototype = {
|
||||
},
|
||||
|
||||
create: function PasswordStore__create(record) {
|
||||
let login = this._nsLoginInfoFromRecord(record);
|
||||
if (!login)
|
||||
return;
|
||||
this._log.debug("Adding login for " + record.hostname);
|
||||
Svc.Login.addLogin(this._nsLoginInfoFromRecord(record));
|
||||
this._log.trace("httpRealm: " + JSON.stringify(login.httpRealm) + "; " +
|
||||
"formSubmitURL: " + JSON.stringify(login.formSubmitURL));
|
||||
try {
|
||||
Svc.Login.addLogin(login);
|
||||
} catch(ex) {
|
||||
this._log.debug("Adding record " + record.id +
|
||||
" resulted in exception " + Utils.exceptionStr(ex));
|
||||
}
|
||||
},
|
||||
|
||||
remove: function PasswordStore__remove(record) {
|
||||
@ -223,7 +248,15 @@ PasswordStore.prototype = {
|
||||
|
||||
this._log.debug("Updating " + record.hostname);
|
||||
let newinfo = this._nsLoginInfoFromRecord(record);
|
||||
Svc.Login.modifyLogin(loginItem, newinfo);
|
||||
if (!newinfo)
|
||||
return;
|
||||
try {
|
||||
Svc.Login.modifyLogin(loginItem, newinfo);
|
||||
} catch(ex) {
|
||||
this._log.debug("Modifying record " + record.id +
|
||||
" resulted in exception " + Utils.exceptionStr(ex) +
|
||||
". Not modifying.");
|
||||
}
|
||||
},
|
||||
|
||||
wipe: function PasswordStore_wipe() {
|
||||
|
@ -142,6 +142,9 @@ function AsyncResource(uri) {
|
||||
AsyncResource.prototype = {
|
||||
_logName: "Net.Resource",
|
||||
|
||||
// Wait 5 minutes before killing a request
|
||||
ABORT_TIMEOUT: 300000,
|
||||
|
||||
// ** {{{ Resource.authenticator }}} **
|
||||
//
|
||||
// Getter and setter for the authenticator module
|
||||
@ -270,7 +273,7 @@ AsyncResource.prototype = {
|
||||
// Setup a channel listener so that the actual network operation
|
||||
// is performed asynchronously.
|
||||
let listener = new ChannelListener(this._onComplete, this._onProgress,
|
||||
this._log);
|
||||
this._log, this.ABORT_TIMEOUT);
|
||||
channel.requestMethod = action;
|
||||
channel.asyncOpen(listener, null);
|
||||
},
|
||||
@ -411,8 +414,10 @@ Resource.prototype = {
|
||||
try {
|
||||
return doRequest(action, data, callback);
|
||||
} catch(ex) {
|
||||
// Combine the channel stack with this request stack
|
||||
// Combine the channel stack with this request stack. Need to create
|
||||
// a new error object for that.
|
||||
let error = Error(ex.message);
|
||||
error.result = ex.result;
|
||||
let chanStack = [];
|
||||
if (ex.stack)
|
||||
chanStack = ex.stack.trim().split(/\n/).slice(1);
|
||||
@ -466,15 +471,14 @@ Resource.prototype = {
|
||||
//
|
||||
// This object implements the {{{nsIStreamListener}}} interface
|
||||
// and is called as the network operation proceeds.
|
||||
function ChannelListener(onComplete, onProgress, logger) {
|
||||
function ChannelListener(onComplete, onProgress, logger, timeout) {
|
||||
this._onComplete = onComplete;
|
||||
this._onProgress = onProgress;
|
||||
this._log = logger;
|
||||
this._timeout = timeout;
|
||||
this.delayAbort();
|
||||
}
|
||||
ChannelListener.prototype = {
|
||||
// Wait 5 minutes before killing a request
|
||||
ABORT_TIMEOUT: 300000,
|
||||
|
||||
onStartRequest: function Channel_onStartRequest(channel) {
|
||||
channel.QueryInterface(Ci.nsIHttpChannel);
|
||||
@ -497,9 +501,13 @@ ChannelListener.prototype = {
|
||||
if (this._data == '')
|
||||
this._data = null;
|
||||
|
||||
// Throw the failure code name (and stop execution)
|
||||
// Throw the failure code and stop execution. Use Components.Exception()
|
||||
// instead of Error() so the exception is QI-able and can be passed across
|
||||
// XPCOM borders while preserving the status code.
|
||||
if (!Components.isSuccessCode(status)) {
|
||||
this._onComplete(Error(Components.Exception("", status).name));
|
||||
let message = Components.Exception("", status).name;
|
||||
let error = Components.Exception(message, status);
|
||||
this._onComplete(error);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -535,14 +543,15 @@ ChannelListener.prototype = {
|
||||
* Create or push back the abort timer that kills this request
|
||||
*/
|
||||
delayAbort: function delayAbort() {
|
||||
Utils.delay(this.abortRequest, this.ABORT_TIMEOUT, this, "abortTimer");
|
||||
Utils.delay(this.abortRequest, this._timeout, this, "abortTimer");
|
||||
},
|
||||
|
||||
abortRequest: function abortRequest() {
|
||||
// Ignore any callbacks if we happen to get any now
|
||||
this.onStopRequest = function() {};
|
||||
this.onDataAvailable = function() {};
|
||||
this._onComplete(Error("Aborting due to channel inactivity."));
|
||||
let error = Components.Exception("Aborting due to channel inactivity.",
|
||||
Cr.NS_ERROR_NET_TIMEOUT);
|
||||
this._onComplete(error);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -398,6 +398,7 @@ WeaveSvc.prototype = {
|
||||
Svc.Obs.add("weave:service:setup-complete", this);
|
||||
Svc.Obs.add("network:offline-status-changed", this);
|
||||
Svc.Obs.add("weave:service:sync:finish", this);
|
||||
Svc.Obs.add("weave:service:login:error", this);
|
||||
Svc.Obs.add("weave:service:sync:error", this);
|
||||
Svc.Obs.add("weave:service:backoff:interval", this);
|
||||
Svc.Obs.add("weave:engine:score:updated", this);
|
||||
@ -542,15 +543,28 @@ WeaveSvc.prototype = {
|
||||
this._log.trace("Network offline status change: " + data);
|
||||
this._checkSyncStatus();
|
||||
break;
|
||||
case "weave:service:login:error":
|
||||
if (Status.login == LOGIN_FAILED_NETWORK_ERROR && !Svc.IO.offline) {
|
||||
this._ignorableErrorCount += 1;
|
||||
}
|
||||
break;
|
||||
case "weave:service:sync:error":
|
||||
this._handleSyncError();
|
||||
if (Status.sync == CREDENTIALS_CHANGED) {
|
||||
this.logout();
|
||||
switch (Status.sync) {
|
||||
case LOGIN_FAILED_NETWORK_ERROR:
|
||||
if (!Svc.IO.offline) {
|
||||
this._ignorableErrorCount += 1;
|
||||
}
|
||||
break;
|
||||
case CREDENTIALS_CHANGED:
|
||||
this.logout();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case "weave:service:sync:finish":
|
||||
this._scheduleNextSync();
|
||||
this._syncErrors = 0;
|
||||
this._ignorableErrorCount = 0;
|
||||
break;
|
||||
case "weave:service:backoff:interval":
|
||||
let interval = (data + Math.random() * data * 0.25) * 1000; // required backoff + up to 25%
|
||||
@ -694,18 +708,19 @@ WeaveSvc.prototype = {
|
||||
/**
|
||||
* Perform the info fetch as part of a login or key fetch.
|
||||
*/
|
||||
_fetchInfo: function _fetchInfo(url, logout) {
|
||||
_fetchInfo: function _fetchInfo(url) {
|
||||
let infoURL = url || this.infoURL;
|
||||
|
||||
this._log.trace("In _fetchInfo: " + infoURL);
|
||||
let info = new Resource(infoURL).get();
|
||||
let info;
|
||||
try {
|
||||
info = new Resource(infoURL).get();
|
||||
} catch (ex) {
|
||||
this._checkServerError(ex);
|
||||
throw ex;
|
||||
}
|
||||
if (!info.success) {
|
||||
if (info.status == 401) {
|
||||
if (logout) {
|
||||
this.logout();
|
||||
Status.login = LOGIN_FAILED_LOGIN_REJECTED;
|
||||
}
|
||||
}
|
||||
this._checkServerError(info);
|
||||
throw "aborting sync, failed to get collections";
|
||||
}
|
||||
return info;
|
||||
@ -830,15 +845,6 @@ WeaveSvc.prototype = {
|
||||
|
||||
verifyLogin: function verifyLogin()
|
||||
this._notify("verify-login", "", function() {
|
||||
// Make sure we have a cluster to verify against
|
||||
// this is a little weird, if we don't get a node we pretend
|
||||
// to succeed, since that probably means we just don't have storage
|
||||
if (this.clusterURL == "" && !this._setCluster()) {
|
||||
Status.sync = NO_SYNC_NODE_FOUND;
|
||||
Svc.Obs.notify("weave:service:sync:delayed");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!this.username) {
|
||||
this._log.warn("No username in verifyLogin.");
|
||||
Status.login = LOGIN_FAILED_NO_USERNAME;
|
||||
@ -846,21 +852,30 @@ WeaveSvc.prototype = {
|
||||
}
|
||||
|
||||
// Unlock master password, or return.
|
||||
// Attaching auth credentials to a request requires access to
|
||||
// passwords, which means that Resource.get can throw MP-related
|
||||
// exceptions!
|
||||
// Try to fetch the passphrase first, while we still have control.
|
||||
try {
|
||||
// Fetch collection info on every startup.
|
||||
// Attaching auth credentials to a request requires access to
|
||||
// passwords, which means that Resource.get can throw MP-related
|
||||
// exceptions!
|
||||
// Try to fetch the passphrase first, while we still have control.
|
||||
try {
|
||||
this.passphrase;
|
||||
} catch (ex) {
|
||||
this._log.debug("Fetching passphrase threw " + ex +
|
||||
"; assuming master password locked.");
|
||||
Status.login = MASTER_PASSWORD_LOCKED;
|
||||
return false;
|
||||
this.passphrase;
|
||||
} catch (ex) {
|
||||
this._log.debug("Fetching passphrase threw " + ex +
|
||||
"; assuming master password locked.");
|
||||
Status.login = MASTER_PASSWORD_LOCKED;
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
// Make sure we have a cluster to verify against
|
||||
// this is a little weird, if we don't get a node we pretend
|
||||
// to succeed, since that probably means we just don't have storage
|
||||
if (this.clusterURL == "" && !this._setCluster()) {
|
||||
Status.sync = NO_SYNC_NODE_FOUND;
|
||||
Svc.Obs.notify("weave:service:sync:delayed");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Fetch collection info on every startup.
|
||||
let test = new Resource(this.infoURL).get();
|
||||
|
||||
switch (test.status) {
|
||||
@ -1077,8 +1092,10 @@ WeaveSvc.prototype = {
|
||||
this._catch(this._lock("service.js: login",
|
||||
this._notify("login", "", function() {
|
||||
this._loggedIn = false;
|
||||
if (Svc.IO.offline)
|
||||
if (Svc.IO.offline) {
|
||||
Status.login = LOGIN_FAILED_NETWORK_ERROR;
|
||||
throw "Application is offline, login should not be called";
|
||||
}
|
||||
|
||||
let initialStatus = this._checkSetup();
|
||||
if (username)
|
||||
@ -1616,9 +1633,10 @@ WeaveSvc.prototype = {
|
||||
_handleSyncError: function WeaveSvc__handleSyncError() {
|
||||
this._syncErrors++;
|
||||
|
||||
// do nothing on the first couple of failures, if we're not in backoff due to 5xx errors
|
||||
// Do nothing on the first couple of failures, if we're not in
|
||||
// backoff due to 5xx errors.
|
||||
if (!Status.enforceBackoff) {
|
||||
if (this._syncErrors < 3) {
|
||||
if (this._syncErrors < MAX_ERROR_COUNT_BEFORE_BACKOFF) {
|
||||
this._scheduleNextSync();
|
||||
return;
|
||||
}
|
||||
@ -1628,6 +1646,12 @@ WeaveSvc.prototype = {
|
||||
this._scheduleAtInterval();
|
||||
},
|
||||
|
||||
_ignorableErrorCount: 0,
|
||||
shouldIgnoreError: function shouldIgnoreError() {
|
||||
return ([Status.login, Status.sync].indexOf(LOGIN_FAILED_NETWORK_ERROR) != -1
|
||||
&& this._ignorableErrorCount < MAX_IGNORE_ERROR_COUNT);
|
||||
},
|
||||
|
||||
_skipScheduledRetry: function _skipScheduledRetry() {
|
||||
return [LOGIN_FAILED_INVALID_PASSPHRASE,
|
||||
LOGIN_FAILED_LOGIN_REJECTED].indexOf(Status.login) == -1;
|
||||
@ -1674,6 +1698,9 @@ WeaveSvc.prototype = {
|
||||
// Make sure we should sync or record why we shouldn't
|
||||
let reason = this._checkSync();
|
||||
if (reason) {
|
||||
if (reason == kSyncNetworkOffline) {
|
||||
Status.sync = LOGIN_FAILED_NETWORK_ERROR;
|
||||
}
|
||||
// this is a purposeful abort rather than a failure, so don't set
|
||||
// any status bits
|
||||
reason = "Can't sync: " + reason;
|
||||
@ -1706,7 +1733,7 @@ WeaveSvc.prototype = {
|
||||
}
|
||||
|
||||
// Figure out what the last modified time is for each collection
|
||||
let info = this._fetchInfo(infoURL, true);
|
||||
let info = this._fetchInfo(infoURL);
|
||||
this.globalScore = 0;
|
||||
|
||||
// Convert the response to an object and read out the modified times
|
||||
@ -1974,18 +2001,46 @@ WeaveSvc.prototype = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Check to see if this is a failure
|
||||
*
|
||||
* Handle HTTP response results or exceptions and set the appropriate
|
||||
* Status.* bits.
|
||||
*/
|
||||
_checkServerError: function WeaveSvc__checkServerError(resp) {
|
||||
if (Utils.checkStatus(resp.status, null, [500, [502, 504]])) {
|
||||
Status.enforceBackoff = true;
|
||||
if (resp.status == 503 && resp.headers["retry-after"])
|
||||
Svc.Obs.notify("weave:service:backoff:interval",
|
||||
parseInt(resp.headers["retry-after"], 10));
|
||||
switch (resp.status) {
|
||||
case 400:
|
||||
if (resp == RESPONSE_OVER_QUOTA) {
|
||||
Status.sync = OVER_QUOTA;
|
||||
}
|
||||
break;
|
||||
|
||||
case 401:
|
||||
this.logout();
|
||||
Status.login = LOGIN_FAILED_LOGIN_REJECTED;
|
||||
break;
|
||||
|
||||
case 500:
|
||||
case 502:
|
||||
case 503:
|
||||
case 504:
|
||||
Status.enforceBackoff = true;
|
||||
if (resp.status == 503 && resp.headers["retry-after"]) {
|
||||
Svc.Obs.notify("weave:service:backoff:interval",
|
||||
parseInt(resp.headers["retry-after"], 10));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
switch (resp.result) {
|
||||
case Cr.NS_ERROR_UNKNOWN_HOST:
|
||||
case Cr.NS_ERROR_CONNECTION_REFUSED:
|
||||
case Cr.NS_ERROR_NET_TIMEOUT:
|
||||
case Cr.NS_ERROR_NET_RESET:
|
||||
case Cr.NS_ERROR_NET_INTERRUPT:
|
||||
case Cr.NS_ERROR_PROXY_CONNECTION_REFUSED:
|
||||
// The constant says it's about login, but in fact it just
|
||||
// indicates general network error.
|
||||
Status.sync = LOGIN_FAILED_NETWORK_ERROR;
|
||||
break;
|
||||
}
|
||||
if (resp.status == 400 && resp == RESPONSE_OVER_QUOTA)
|
||||
Status.sync = OVER_QUOTA;
|
||||
},
|
||||
/**
|
||||
* Return a value for a backoff interval. Maximum is eight hours, unless
|
||||
|
@ -198,7 +198,23 @@ let Utils = {
|
||||
throw batchEx;
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
runInTransaction: function(db, callback, thisObj) {
|
||||
let hasTransaction = false;
|
||||
try {
|
||||
db.beginTransaction();
|
||||
hasTransaction = true;
|
||||
} catch(e) { /* om nom nom exceptions */ }
|
||||
|
||||
try {
|
||||
return callback.call(thisObj);
|
||||
} finally {
|
||||
if (hasTransaction) {
|
||||
db.commitTransaction();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
createStatement: function createStatement(db, query) {
|
||||
// Gecko 2.0
|
||||
if (db.createAsyncStatement)
|
||||
|
@ -60,6 +60,7 @@ function test_ID_caching() {
|
||||
function test_processIncoming_error_orderChildren() {
|
||||
_("Ensure that _orderChildren() is called even when _processIncoming() throws an error.");
|
||||
|
||||
do_test_pending();
|
||||
Svc.Prefs.set("clusterURL", "http://localhost:8080/");
|
||||
Svc.Prefs.set("username", "foo");
|
||||
|
||||
@ -139,6 +140,7 @@ function test_processIncoming_error_orderChildren() {
|
||||
|
||||
function test_restorePromptsReupload() {
|
||||
_("Ensure that restoring from a backup will reupload all records.");
|
||||
do_test_pending();
|
||||
Svc.Prefs.set("username", "foo");
|
||||
Service.serverURL = "http://localhost:8080/";
|
||||
Service.clusterURL = "http://localhost:8080/";
|
||||
|
@ -1,10 +1,15 @@
|
||||
_("Make sure the form store follows the Store api and correctly accesses the backend form storage");
|
||||
Cu.import("resource://services-sync/engines/forms.js");
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
|
||||
function run_test() {
|
||||
let baseuri = "http://fake/uri/";
|
||||
let store = new FormEngine()._store;
|
||||
|
||||
function applyEnsureNoFailures(records) {
|
||||
do_check_eq(store.applyIncomingBatch(records).length, 0);
|
||||
}
|
||||
|
||||
_("Remove any existing entries");
|
||||
store.wipe();
|
||||
for (let id in store.getAllIDs()) {
|
||||
@ -12,10 +17,11 @@ function run_test() {
|
||||
}
|
||||
|
||||
_("Add a form entry");
|
||||
store.create({
|
||||
applyEnsureNoFailures([{
|
||||
id: Utils.makeGUID(),
|
||||
name: "name!!",
|
||||
value: "value??"
|
||||
});
|
||||
}]);
|
||||
|
||||
_("Should have 1 entry now");
|
||||
let id = "";
|
||||
@ -45,10 +51,11 @@ function run_test() {
|
||||
}
|
||||
|
||||
_("Add another entry");
|
||||
store.create({
|
||||
applyEnsureNoFailures([{
|
||||
id: Utils.makeGUID(),
|
||||
name: "another",
|
||||
value: "entry"
|
||||
});
|
||||
}]);
|
||||
id = "";
|
||||
for (let _id in store.getAllIDs()) {
|
||||
if (id == "")
|
||||
|
92
services/sync/tests/unit/test_password_engine.js
Normal file
92
services/sync/tests/unit/test_password_engine.js
Normal file
@ -0,0 +1,92 @@
|
||||
Cu.import("resource://services-sync/record.js");
|
||||
Cu.import("resource://services-sync/engines.js");
|
||||
Cu.import("resource://services-sync/engines/passwords.js");
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
Cu.import("resource://services-sync/service.js");
|
||||
|
||||
Engines.register(PasswordEngine);
|
||||
|
||||
function makeEngine() {
|
||||
return new PasswordEngine();
|
||||
}
|
||||
var syncTesting = new SyncTestingInfrastructure(makeEngine);
|
||||
|
||||
function run_test() {
|
||||
initTestLogging("Trace");
|
||||
Log4Moz.repository.getLogger("Engine.Passwords").level = Log4Moz.Level.Trace;
|
||||
Log4Moz.repository.getLogger("Store.Passwords").level = Log4Moz.Level.Trace;
|
||||
do_test_pending();
|
||||
|
||||
CollectionKeys.generateNewKeys();
|
||||
|
||||
// Verify that password entries with redundant fields don't cause an exception.
|
||||
|
||||
const BOGUS_GUID_A = "zzzzzzzzzzzz";
|
||||
const BOGUS_GUID_B = "yyyyyyyyyyyy";
|
||||
let payloadA = {id: BOGUS_GUID_A,
|
||||
hostname: "http://foo.bar.com",
|
||||
formSubmitURL: "http://foo.bar.com/baz",
|
||||
httpRealm: "secure",
|
||||
username: "john",
|
||||
password: "smith",
|
||||
usernameField: "username",
|
||||
passwordField: "password"};
|
||||
let payloadB = {id: BOGUS_GUID_B,
|
||||
hostname: "http://foo.baz.com",
|
||||
formSubmitURL: "http://foo.baz.com/baz",
|
||||
username: "john",
|
||||
password: "smith",
|
||||
usernameField: "username",
|
||||
passwordField: "password"};
|
||||
let badWBO = new ServerWBO(BOGUS_GUID_A, encryptPayload(payloadA));
|
||||
let goodWBO = new ServerWBO(BOGUS_GUID_B, encryptPayload(payloadB));
|
||||
|
||||
Svc.Prefs.set("clusterURL", "http://localhost:8080/");
|
||||
Svc.Prefs.set("username", "foo");
|
||||
|
||||
let engine = new PasswordEngine();
|
||||
let store = engine._store;
|
||||
let global = new ServerWBO("global",
|
||||
{engines: {passwords: {version: engine.version,
|
||||
syncID: engine.syncID}}});
|
||||
|
||||
let collection = new ServerCollection({BOGUS_GUID_A: badWBO,
|
||||
BOGUS_GUID_B: goodWBO});
|
||||
let server = httpd_setup({
|
||||
"/1.0/foo/storage/meta/global": global.handler(),
|
||||
"/1.0/foo/storage/passwords": collection.handler()
|
||||
});
|
||||
|
||||
try {
|
||||
let err;
|
||||
try {
|
||||
engine.sync();
|
||||
} catch (ex) {
|
||||
err = ex;
|
||||
}
|
||||
// No exception thrown when encountering bad record.
|
||||
do_check_true(!err);
|
||||
|
||||
// Only the good record makes it to Svc.Login.
|
||||
let badCount = {};
|
||||
let goodCount = {};
|
||||
let badLogins = Svc.Login.findLogins(badCount, payloadA.hostname, payloadA.formSubmitURL, payloadA.httpRealm);
|
||||
let goodLogins = Svc.Login.findLogins(goodCount, payloadB.hostname, payloadB.formSubmitURL, null);
|
||||
|
||||
_("Bad: " + JSON.stringify(badLogins));
|
||||
_("Good: " + JSON.stringify(goodLogins));
|
||||
_("Count: " + badCount.value + ", " + goodCount.value);
|
||||
|
||||
do_check_eq(goodCount.value, 1);
|
||||
do_check_eq(badCount.value, 0);
|
||||
|
||||
do_check_true(!!store.getAllIDs()[BOGUS_GUID_B]);
|
||||
do_check_true(!store.getAllIDs()[BOGUS_GUID_A]);
|
||||
}
|
||||
finally {
|
||||
store.wipe();
|
||||
server.stop(do_test_finished);
|
||||
Svc.Prefs.resetBranch("");
|
||||
Records.clearCache();
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ Cu.import("resource://services-sync/identity.js");
|
||||
Cu.import("resource://services-sync/log4moz.js");
|
||||
Cu.import("resource://services-sync/resource.js");
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
Cu.import("resource://services-sync/ext/Sync.js");
|
||||
|
||||
let logger;
|
||||
|
||||
@ -189,7 +190,7 @@ function run_test() {
|
||||
|
||||
Utils.prefs.setIntPref("network.numRetries", 1); // speed up test
|
||||
|
||||
_("Resource object memebers");
|
||||
_("Resource object members");
|
||||
let res = new Resource("http://localhost:8080/open");
|
||||
do_check_true(res.uri instanceof Ci.nsIURI);
|
||||
do_check_eq(res.uri.spec, "http://localhost:8080/open");
|
||||
@ -406,6 +407,7 @@ function run_test() {
|
||||
} catch(ex) {
|
||||
error = ex;
|
||||
}
|
||||
do_check_eq(error.result, Cr.NS_ERROR_CONNECTION_REFUSED);
|
||||
do_check_eq(error.message, "NS_ERROR_CONNECTION_REFUSED");
|
||||
do_check_eq(typeof error.stack, "string");
|
||||
|
||||
@ -489,6 +491,7 @@ function run_test() {
|
||||
}
|
||||
|
||||
// It throws and logs.
|
||||
do_check_eq(error.result, Cr.NS_ERROR_MALFORMED_URI);
|
||||
do_check_eq(error, "Error: NS_ERROR_MALFORMED_URI");
|
||||
do_check_eq(warnings.pop(),
|
||||
"Got exception calling onProgress handler during fetch of " +
|
||||
@ -511,11 +514,23 @@ function run_test() {
|
||||
}
|
||||
|
||||
// It throws and logs.
|
||||
do_check_eq(error.result, Cr.NS_ERROR_XPC_JS_THREW_STRING);
|
||||
do_check_eq(error, "Error: NS_ERROR_XPC_JS_THREW_STRING");
|
||||
do_check_eq(warnings.pop(),
|
||||
"Got exception calling onProgress handler during fetch of " +
|
||||
"http://localhost:8080/json");
|
||||
|
||||
|
||||
|
||||
|
||||
_("Ensure channel timeouts are thrown appropriately.");
|
||||
let res19 = new Resource("http://localhost:8080/json");
|
||||
res19.ABORT_TIMEOUT = 0;
|
||||
error = undefined;
|
||||
try {
|
||||
content = res19.get();
|
||||
} catch (ex) {
|
||||
error = ex;
|
||||
}
|
||||
do_check_eq(error.result, Cr.NS_ERROR_NET_TIMEOUT);
|
||||
|
||||
server.stop(do_test_finished);
|
||||
}
|
||||
|
@ -556,8 +556,8 @@ function run_test() {
|
||||
let res11 = new AsyncResource("http://localhost:12345/does/not/exist");
|
||||
res11.get(ensureThrows(function (error, content) {
|
||||
do_check_neq(error, null);
|
||||
do_check_eq(error.result, Cr.NS_ERROR_CONNECTION_REFUSED);
|
||||
do_check_eq(error.message, "NS_ERROR_CONNECTION_REFUSED");
|
||||
do_check_eq(typeof error.stack, "string");
|
||||
do_test_finished();
|
||||
next();
|
||||
}));
|
||||
@ -621,7 +621,8 @@ function run_test() {
|
||||
res14._log.warn = function(msg) { warnings.push(msg) };
|
||||
|
||||
res14.get(ensureThrows(function (error, content) {
|
||||
do_check_eq(error, "Error: NS_ERROR_MALFORMED_URI");
|
||||
do_check_eq(error.result, Cr.NS_ERROR_MALFORMED_URI);
|
||||
do_check_eq(error.message, "NS_ERROR_MALFORMED_URI");
|
||||
do_check_eq(content, null);
|
||||
do_check_eq(warnings.pop(),
|
||||
"Got exception calling onProgress handler during fetch of " +
|
||||
@ -643,7 +644,8 @@ function run_test() {
|
||||
res15._log.warn = function(msg) { warnings.push(msg) };
|
||||
|
||||
res15.get(ensureThrows(function (error, content) {
|
||||
do_check_eq(error, "Error: NS_ERROR_XPC_JS_THREW_STRING");
|
||||
do_check_eq(error.result, Cr.NS_ERROR_XPC_JS_THREW_STRING);
|
||||
do_check_eq(error.message, "NS_ERROR_XPC_JS_THREW_STRING");
|
||||
do_check_eq(content, null);
|
||||
do_check_eq(warnings.pop(),
|
||||
"Got exception calling onProgress handler during fetch of " +
|
||||
@ -653,6 +655,16 @@ function run_test() {
|
||||
next();
|
||||
}));
|
||||
|
||||
}, function (next) {
|
||||
|
||||
_("Ensure channel timeouts are thrown appropriately.");
|
||||
let res19 = new AsyncResource("http://localhost:8080/json");
|
||||
res19.ABORT_TIMEOUT = 0;
|
||||
res19.get(ensureThrows(function (error, content) {
|
||||
do_check_eq(error.result, Cr.NS_ERROR_NET_TIMEOUT);
|
||||
next();
|
||||
}));
|
||||
|
||||
}, function (next) {
|
||||
|
||||
// Don't quit test harness before server shuts down.
|
||||
|
@ -24,6 +24,18 @@ function run_test() {
|
||||
let logger = Log4Moz.repository.rootLogger;
|
||||
Log4Moz.repository.rootLogger.addAppender(new Log4Moz.DumpAppender());
|
||||
|
||||
try {
|
||||
_("The right bits are set when we're offline.");
|
||||
Svc.IO.offline = true;
|
||||
do_check_eq(Service._ignorableErrorCount, 0);
|
||||
do_check_false(!!Service.login());
|
||||
do_check_eq(Status.login, LOGIN_FAILED_NETWORK_ERROR);
|
||||
do_check_eq(Service._ignorableErrorCount, 0);
|
||||
Svc.IO.offline = false;
|
||||
} finally {
|
||||
Svc.Prefs.resetBranch("");
|
||||
}
|
||||
|
||||
do_test_pending();
|
||||
let server = httpd_setup({
|
||||
"/1.0/johndoe/info/collections": login_handler,
|
||||
@ -111,7 +123,7 @@ function run_test() {
|
||||
Service.logout();
|
||||
do_check_false(Service.isLoggedIn);
|
||||
do_check_false(Svc.Prefs.get("autoconnect"));
|
||||
|
||||
|
||||
/*
|
||||
* Testing login-on-sync.
|
||||
*/
|
||||
|
@ -69,8 +69,8 @@ function test_backoff500(next) {
|
||||
Engines.unregister("catapult");
|
||||
Status.resetBackoff();
|
||||
Service.startOver();
|
||||
server.stop(next);
|
||||
}
|
||||
server.stop(next);
|
||||
}
|
||||
|
||||
function test_backoff503(next) {
|
||||
@ -102,8 +102,8 @@ function test_backoff503(next) {
|
||||
Engines.unregister("catapult");
|
||||
Status.resetBackoff();
|
||||
Service.startOver();
|
||||
server.stop(next);
|
||||
}
|
||||
server.stop(next);
|
||||
}
|
||||
|
||||
function test_overQuota(next) {
|
||||
@ -128,8 +128,101 @@ function test_overQuota(next) {
|
||||
Engines.unregister("catapult");
|
||||
Status.resetSync();
|
||||
Service.startOver();
|
||||
server.stop(next);
|
||||
}
|
||||
server.stop(next);
|
||||
}
|
||||
|
||||
function test_service_networkError(next) {
|
||||
_("Test: Connection refused error from Service.sync() leads to the right status code.");
|
||||
setUp();
|
||||
// Provoke connection refused.
|
||||
Service.clusterURL = "http://localhost:12345/";
|
||||
Service._ignorableErrorCount = 0;
|
||||
|
||||
try {
|
||||
do_check_eq(Status.sync, SYNC_SUCCEEDED);
|
||||
|
||||
Service._loggedIn = true;
|
||||
Service.sync();
|
||||
|
||||
do_check_eq(Status.sync, LOGIN_FAILED_NETWORK_ERROR);
|
||||
do_check_eq(Service._ignorableErrorCount, 1);
|
||||
} finally {
|
||||
Status.resetSync();
|
||||
Service.startOver();
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
function test_service_offline(next) {
|
||||
_("Test: Wanting to sync in offline mode leads to the right status code but does not increment the ignorable error count.");
|
||||
setUp();
|
||||
Svc.IO.offline = true;
|
||||
Service._ignorableErrorCount = 0;
|
||||
|
||||
try {
|
||||
do_check_eq(Status.sync, SYNC_SUCCEEDED);
|
||||
|
||||
Service._loggedIn = true;
|
||||
Service.sync();
|
||||
|
||||
do_check_eq(Status.sync, LOGIN_FAILED_NETWORK_ERROR);
|
||||
do_check_eq(Service._ignorableErrorCount, 0);
|
||||
} finally {
|
||||
Status.resetSync();
|
||||
Service.startOver();
|
||||
}
|
||||
Svc.IO.offline = false;
|
||||
next();
|
||||
}
|
||||
|
||||
function test_service_reset_ignorableErrorCount(next) {
|
||||
_("Test: Successful sync resets the ignorable error count.");
|
||||
let server = sync_httpd_setup();
|
||||
setUp();
|
||||
Service._ignorableErrorCount = 10;
|
||||
|
||||
try {
|
||||
do_check_eq(Status.sync, SYNC_SUCCEEDED);
|
||||
|
||||
Service.login();
|
||||
Service.sync();
|
||||
|
||||
do_check_eq(Status.sync, SYNC_SUCCEEDED);
|
||||
do_check_eq(Service._ignorableErrorCount, 0);
|
||||
} finally {
|
||||
Status.resetSync();
|
||||
Service.startOver();
|
||||
}
|
||||
server.stop(next);
|
||||
}
|
||||
|
||||
function test_engine_networkError(next) {
|
||||
_("Test: Network related exceptions from engine.sync() lead to the right status code.");
|
||||
let server = sync_httpd_setup();
|
||||
setUp();
|
||||
Service._ignorableErrorCount = 0;
|
||||
|
||||
Engines.register(CatapultEngine);
|
||||
let engine = Engines.get("catapult");
|
||||
engine.enabled = true;
|
||||
engine.exception = Components.Exception("NS_ERROR_UNKNOWN_HOST",
|
||||
Cr.NS_ERROR_UNKNOWN_HOST);
|
||||
|
||||
try {
|
||||
do_check_eq(Status.sync, SYNC_SUCCEEDED);
|
||||
|
||||
Service.login();
|
||||
Service.sync();
|
||||
|
||||
do_check_eq(Status.sync, LOGIN_FAILED_NETWORK_ERROR);
|
||||
do_check_eq(Service._ignorableErrorCount, 1);
|
||||
} finally {
|
||||
Engines.unregister("catapult");
|
||||
Status.resetSync();
|
||||
Service.startOver();
|
||||
}
|
||||
server.stop(next);
|
||||
}
|
||||
|
||||
// Slightly misplaced test as it doesn't actually test checkServerError,
|
||||
@ -156,8 +249,8 @@ function test_engine_applyFailed(next) {
|
||||
Engines.unregister("catapult");
|
||||
Status.resetSync();
|
||||
Service.startOver();
|
||||
server.stop(next);
|
||||
}
|
||||
server.stop(next);
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
@ -168,6 +261,10 @@ function run_test() {
|
||||
asyncChainTests(test_backoff500,
|
||||
test_backoff503,
|
||||
test_overQuota,
|
||||
test_service_networkError,
|
||||
test_service_offline,
|
||||
test_service_reset_ignorableErrorCount,
|
||||
test_engine_networkError,
|
||||
test_engine_applyFailed,
|
||||
do_test_finished)();
|
||||
}
|
||||
|
@ -49,11 +49,13 @@ function run_test() {
|
||||
do_check_eq(Status.service, STATUS_OK);
|
||||
|
||||
_("Credentials won't check out because we're not configured yet.");
|
||||
Status.resetSync();
|
||||
do_check_false(Service.verifyLogin());
|
||||
do_check_eq(Status.service, CLIENT_NOT_CONFIGURED);
|
||||
do_check_eq(Status.login, LOGIN_FAILED_NO_USERNAME);
|
||||
|
||||
_("Try again with username and password set.");
|
||||
Status.resetSync();
|
||||
Service.username = "johndoe";
|
||||
Service.password = "ilovejane";
|
||||
do_check_false(Service.verifyLogin());
|
||||
@ -64,12 +66,14 @@ function run_test() {
|
||||
do_check_eq(Service.clusterURL, "http://localhost:8080/api/");
|
||||
|
||||
_("Success if passphrase is set.");
|
||||
Status.resetSync();
|
||||
Service.passphrase = "foo";
|
||||
do_check_true(Service.verifyLogin());
|
||||
do_check_eq(Status.service, STATUS_OK);
|
||||
do_check_eq(Status.login, LOGIN_SUCCEEDED);
|
||||
|
||||
_("If verifyLogin() encounters a server error, it flips on the backoff flag and notifies observers on a 503 with Retry-After.");
|
||||
Status.resetSync();
|
||||
Service.username = "janedoe";
|
||||
do_check_false(Status.enforceBackoff);
|
||||
let backoffInterval;
|
||||
@ -82,6 +86,20 @@ function run_test() {
|
||||
do_check_eq(Status.service, LOGIN_FAILED);
|
||||
do_check_eq(Status.login, LOGIN_FAILED_SERVER_ERROR);
|
||||
|
||||
_("Ensure a network error when finding the cluster sets the right Status bits.");
|
||||
Status.resetSync();
|
||||
Service.serverURL = "http://localhost:12345/";
|
||||
do_check_false(Service.verifyLogin());
|
||||
do_check_eq(Status.service, LOGIN_FAILED);
|
||||
do_check_eq(Status.login, LOGIN_FAILED_NETWORK_ERROR);
|
||||
|
||||
_("Ensure a network error when getting the collection info sets the right Status bits.");
|
||||
Status.resetSync();
|
||||
Service.clusterURL = "http://localhost:12345/";
|
||||
do_check_false(Service.verifyLogin());
|
||||
do_check_eq(Status.service, LOGIN_FAILED);
|
||||
do_check_eq(Status.login, LOGIN_FAILED_NETWORK_ERROR);
|
||||
|
||||
} finally {
|
||||
Svc.Prefs.resetBranch("");
|
||||
server.stop(do_test_finished);
|
||||
|
1
services/sync/version.txt
Normal file
1
services/sync/version.txt
Normal file
@ -0,0 +1 @@
|
||||
1.7b1pre
|
Loading…
Reference in New Issue
Block a user