Merge fx-sync to mozilla-central

--HG--
rename : services/sync/tests/unit/test_engines_forms_store.js => services/sync/tests/unit/test_forms_store.js
This commit is contained in:
Philipp von Weitershausen 2010-08-12 02:33:25 +02:00
commit aca08d7d44
22 changed files with 1023 additions and 216 deletions

View File

@ -1,67 +0,0 @@
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
function FormNotifier() {
let formClass = Components.classesByID["{a2059c0e-5a58-4c55-ab7c-26f0557546ef}"] ||
Components.classesByID["{0c1bb408-71a2-403f-854a-3a0659829ded}"];
let baseForm = formClass.getService(Ci.nsIFormHistory2);
let obs = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
function wrap(method) {
return function() {
let args = Array.slice(arguments);
let notify = function(type) {
obs.notifyObservers(null, "form-notifier", JSON.stringify({
args: args,
func: method,
type: type
}));
};
notify("before");
try {
return baseForm[method].apply(this, arguments);
}
finally {
notify("after");
}
};
}
this.__defineGetter__("DBConnection", function() baseForm.DBConnection);
this.__defineGetter__("hasEntries", function() baseForm.hasEntries);
this.addEntry = wrap("addEntry");
this.entryExists = wrap("entryExists");
this.nameExists = wrap("nameExists");
this.removeAllEntries = wrap("removeAllEntries");
this.removeEntriesByTimeframe = wrap("removeEntriesByTimeframe");
this.removeEntriesForName = wrap("removeEntriesForName");
this.removeEntry = wrap("removeEntry");
// Avoid leaking the base form service.
obs.addObserver({
observe: function() {
obs.removeObserver(this, "profile-before-change");
baseForm = null;
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference,
Ci.nsIObserver])
}, "profile-before-change", true);
}
FormNotifier.prototype = {
classDescription: "Form Notifier Wrapper",
contractID: "@mozilla.org/satchel/form-history;1",
classID: Components.ID("{be5a097b-6ee6-4c6a-8eca-6bce87d570e9}"),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIFormHistory2]),
};
// Gecko <2.0
function NSGetModule(compMgr, fileSpec) XPCOMUtils.generateModule([FormNotifier]);
// Gecko >=2.0
if (typeof XPCOMUtils.generateNSGetFactory == "function")
const NSGetFactory = XPCOMUtils.generateNSGetFactory([FormNotifier]);

View File

@ -53,12 +53,13 @@ WeaveService.prototype = {
switch (topic) {
case "app-startup":
let os = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
getService(Ci.nsIObserverService);
os.addObserver(this, "final-ui-startup", true);
this.addResourceAlias();
break;
case "final-ui-startup":
this.addResourceAlias();
// Force Weave service to load if it hasn't triggered from overlays
this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
this.timer.initWithCallback({

View File

@ -54,7 +54,6 @@ catch(ex) {
}
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://services-sync/engines.js");
Cu.import("resource://services-sync/ext/Observers.js");
Cu.import("resource://services-sync/stores.js");
Cu.import("resource://services-sync/trackers.js");
Cu.import("resource://services-sync/type_records/bookmark.js");
@ -115,12 +114,12 @@ BookmarksEngine.prototype = {
_trackerObj: BookmarksTracker,
_handleImport: function _handleImport() {
Observers.add("bookmarks-restore-begin", function() {
Svc.Obs.add("bookmarks-restore-begin", function() {
this._log.debug("Ignoring changes from importing bookmarks");
this._tracker.ignoreAll = true;
}, this);
Observers.add("bookmarks-restore-success", function() {
Svc.Obs.add("bookmarks-restore-success", function() {
this._log.debug("Tracking all items on successful import");
this._tracker.ignoreAll = false;
@ -129,7 +128,7 @@ BookmarksEngine.prototype = {
this._tracker.addChangedID(id);
}, this);
Observers.add("bookmarks-restore-failed", function() {
Svc.Obs.add("bookmarks-restore-failed", function() {
this._tracker.ignoreAll = false;
}, this);
},
@ -251,7 +250,7 @@ function BookmarksStore(name) {
Store.call(this, name);
// Explicitly nullify our references to our cached services so we don't leak
Observers.add("places-shutdown", function() {
Svc.Obs.add("places-shutdown", function() {
this.__bms = null;
this.__hsvc = null;
this.__ls = null;
@ -852,10 +851,12 @@ BookmarksStore.prototype = {
get _frecencyStm() {
if (!this.__frecencyStm) {
this._log.trace("Creating SQL statement: _frecencyStm");
this.__frecencyStm = Svc.History.DBConnection.createStatement(
this.__frecencyStm = Utils.createStatement(
Svc.History.DBConnection,
"SELECT frecency " +
"FROM moz_places " +
"WHERE url = :url");
"WHERE url = :url " +
"LIMIT 1");
}
return this.__frecencyStm;
},
@ -869,14 +870,10 @@ BookmarksStore.prototype = {
// Add in the bookmark's frecency if we have something
if (record.bmkUri != null) {
try {
this._frecencyStm.params.url = record.bmkUri;
if (this._frecencyStm.step())
index += this._frecencyStm.row.frecency;
}
finally {
this._frecencyStm.reset();
}
this._frecencyStm.params.url = record.bmkUri;
let result = Utils.queryAsync(this._frecencyStm, ["frecency"]);
if (result.length)
index += result[0].frecency;
}
return index;
@ -1001,17 +998,37 @@ function BookmarksTracker(name) {
for (let guid in kSpecialIds)
this.ignoreID(guid);
Svc.Bookmark.addObserver(this, false);
// Explicitly nullify our references to our cached services so we don't leak
Observers.add("places-shutdown", function() {
this.__ls = null;
this.__bms = null;
}, this);
Svc.Obs.add("places-shutdown", this);
Svc.Obs.add("weave:engine:start-tracking", this);
Svc.Obs.add("weave:engine:stop-tracking", this);
}
BookmarksTracker.prototype = {
__proto__: Tracker.prototype,
_enabled: false,
observe: function observe(subject, topic, data) {
switch (topic) {
case "weave:engine:start-tracking":
if (!this._enabled) {
Svc.Bookmark.addObserver(this, true);
this._enabled = true;
}
break;
case "weave:engine:stop-tracking":
if (this._enabled) {
Svc.Bookmark.removeObserver(this);
this._enabled = false;
}
// Fall through to clean up.
case "places-shutdown":
// Explicitly nullify our references to our cached services so
// we don't leak
this.__ls = null;
this.__bms = null;
break;
}
},
__bms: null,
get _bms() {
if (!this.__bms)
@ -1030,7 +1047,8 @@ BookmarksTracker.prototype = {
QueryInterface: XPCOMUtils.generateQI([
Ci.nsINavBookmarkObserver,
Ci.nsINavBookmarkObserver_MOZILLA_1_9_1_ADDITIONS
Ci.nsINavBookmarkObserver_MOZILLA_1_9_1_ADDITIONS,
Ci.nsISupportsWeakReference
]),
/**

View File

@ -110,7 +110,7 @@ let FormWrapper = {
createStatement: function createStatement(query) {
try {
// Just return the statement right away if it's okay
return Svc.Form.DBConnection.createStatement(query);
return Utils.createStatement(Svc.Form.DBConnection, query);
}
catch(ex) {
// Assume guid column must not exist yet, so add it with an index
@ -121,7 +121,7 @@ let FormWrapper = {
"ON moz_formhistory (guid)");
// Try creating the query now that the column exists
return Svc.Form.DBConnection.createStatement(query);
return Utils.createStatement(Svc.Form.DBConnection, query);
}
}
};
@ -202,26 +202,68 @@ FormStore.prototype = {
function FormTracker(name) {
Tracker.call(this, name);
Svc.Obs.add("form-notifier", this);
// nsHTMLFormElement doesn't use the normal observer/observe pattern and looks
// up nsIFormSubmitObservers to .notify() them so add manually to observers
Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService).
addObserver(this, "earlyformsubmit", false);
Svc.Obs.add("weave:engine:start-tracking", this);
Svc.Obs.add("weave:engine:stop-tracking", this);
}
FormTracker.prototype = {
__proto__: Tracker.prototype,
QueryInterface: XPCOMUtils.generateQI([
Ci.nsIFormSubmitObserver,
Ci.nsIObserver]),
Ci.nsIObserver,
Ci.nsISupportsWeakReference]),
trackEntry: function trackEntry(name, value) {
this.addChangedID(FormWrapper.getGUID(name, value));
this.score += 10;
},
_enabled: false,
observe: function observe(subject, topic, data) {
switch (topic) {
case "weave:engine:start-tracking":
if (!this._enabled) {
Svc.Obs.add("form-notifier", this);
Svc.Obs.add("satchel-storage-changed", this);
// nsHTMLFormElement doesn't use the normal observer/observe
// pattern and looks up nsIFormSubmitObservers to .notify()
// them so add manually to observers
Cc["@mozilla.org/observer-service;1"]
.getService(Ci.nsIObserverService)
.addObserver(this, "earlyformsubmit", true);
this._enabled = true;
}
break;
case "weave:engine:stop-tracking":
if (this._enabled) {
Svc.Obs.remove("form-notifier", this);
Svc.Obs.remove("satchel-storage-changed", this);
Cc["@mozilla.org/observer-service;1"]
.getService(Ci.nsIObserverService)
.removeObserver(this, "earlyformsubmit");
this._enabled = false;
}
break;
// Firefox 4.0
case "satchel-storage-changed":
if (data == "addEntry" || data == "before-removeEntry") {
subject = subject.QueryInterface(Ci.nsIArray);
let name = subject.queryElementAt(0, Ci.nsISupportsString)
.toString();
let value = subject.queryElementAt(1, Ci.nsISupportsString)
.toString();
this.trackEntry(name, value);
}
break;
// Firefox 3.5/3.6
case "form-notifier":
this.onFormNotifier(data);
break;
}
},
// Firefox 3.5/3.6
onFormNotifier: function onFormNotifier(data) {
let name, value;
// Figure out if it's a function that we care about tracking

View File

@ -88,8 +88,8 @@ function HistoryStore(name) {
Store.call(this, name);
// Explicitly nullify our references to our cached services so we don't leak
Observers.add("places-shutdown", function() {
for each([query, stmt] in Iterator(this._stmts))
Svc.Obs.add("places-shutdown", function() {
for each ([query, stmt] in Iterator(this._stmts))
stmt.finalize();
this.__hsvc = null;
this._stmts = [];
@ -113,43 +113,87 @@ HistoryStore.prototype = {
return this._hsvc.DBConnection;
},
_stmts: [],
_stmts: {},
_getStmt: function(query) {
if (query in this._stmts)
return this._stmts[query];
this._log.trace("Creating SQL statement: " + query);
return this._stmts[query] = this._db.createStatement(query);
return this._stmts[query] = Utils.createStatement(this._db, query);
},
get _haveTempTablesStm() {
return this._getStmt(
"SELECT name FROM sqlite_temp_master " +
"WHERE name IN ('moz_places_temp', 'moz_historyvisits_temp')");
},
get _haveTempTables() {
if (this.__haveTempTables == null)
this.__haveTempTables = !!Utils.queryAsync(this._haveTempTablesStm,
["name"]).length;
return this.__haveTempTables;
},
get _visitStm() {
// Gecko <2.0
if (this._haveTempTables) {
let where =
"WHERE place_id = IFNULL( " +
"(SELECT id FROM moz_places_temp WHERE url = :url), " +
"(SELECT id FROM moz_places WHERE url = :url) " +
") ";
return this._getStmt(
"SELECT visit_type type, visit_date date " +
"FROM moz_historyvisits_temp " + where + "UNION " +
"SELECT visit_type type, visit_date date " +
"FROM moz_historyvisits " + where +
"ORDER BY date DESC LIMIT 10 ");
}
// Gecko 2.0
return this._getStmt(
"SELECT visit_type type, visit_date date " +
"FROM moz_historyvisits_view " +
"WHERE place_id = (" +
"SELECT id " +
"FROM moz_places_view " +
"WHERE url = :url) " +
"FROM moz_historyvisits " +
"WHERE place_id = (SELECT id FROM moz_places WHERE url = :url) " +
"ORDER BY date DESC LIMIT 10");
},
get _urlStm() {
return this._getStmt(
"SELECT url, title, frecency " +
"FROM moz_places_view " +
let where =
"WHERE id = (" +
"SELECT place_id " +
"FROM moz_annos " +
"WHERE content = :guid AND anno_attribute_id = (" +
"SELECT id " +
"FROM moz_anno_attributes " +
"WHERE name = '" + GUID_ANNO + "'))");
"WHERE name = '" + GUID_ANNO + "')) ";
// Gecko <2.0
if (this._haveTempTables)
return this._getStmt(
"SELECT url, title, frecency FROM moz_places_temp " + where +
"UNION ALL " +
"SELECT url, title, frecency FROM moz_places " + where + "LIMIT 1");
// Gecko 2.0
return this._getStmt(
"SELECT url, title, frecency FROM moz_places " + where + "LIMIT 1");
},
get _allUrlStm() {
// Gecko <2.0
if (this._haveTempTables)
return this._getStmt(
"SELECT url, frecency FROM moz_places_temp " +
"WHERE last_visit_date > :cutoff_date " +
"UNION " +
"SELECT url, frecency FROM moz_places " +
"WHERE last_visit_date > :cutoff_date " +
"ORDER BY 2 DESC " +
"LIMIT :max_results");
// Gecko 2.0
return this._getStmt(
"SELECT url " +
"FROM moz_places_view " +
"FROM moz_places " +
"WHERE last_visit_date > :cutoff_date " +
"ORDER BY frecency DESC " +
"LIMIT :max_results");
@ -191,7 +235,7 @@ HistoryStore.prototype = {
},
remove: function HistStore_remove(record) {
let page = this._findURLByGUID(record.id)
let page = this._findURLByGUID(record.id);
if (page == null) {
this._log.debug("Page already removed: " + record.id);
return;
@ -257,14 +301,34 @@ HistoryStore.prototype = {
function HistoryTracker(name) {
Tracker.call(this, name);
Svc.History.addObserver(this, false);
Svc.Obs.add("weave:engine:start-tracking", this);
Svc.Obs.add("weave:engine:stop-tracking", this);
}
HistoryTracker.prototype = {
__proto__: Tracker.prototype,
_enabled: false,
observe: function observe(subject, topic, data) {
switch (topic) {
case "weave:engine:start-tracking":
if (!this._enabled) {
Svc.History.addObserver(this, true);
this._enabled = true;
}
break;
case "weave:engine:stop-tracking":
if (this._enabled) {
Svc.History.removeObserver(this);
this._enabled = false;
}
break;
}
},
QueryInterface: XPCOMUtils.generateQI([
Ci.nsINavHistoryObserver,
Ci.nsINavHistoryObserver_MOZILLA_1_9_1_ADDITIONS
Ci.nsINavHistoryObserver_MOZILLA_1_9_1_ADDITIONS,
Ci.nsISupportsWeakReference
]),
onBeginUpdateBatch: function HT_onBeginUpdateBatch() {},

View File

@ -44,7 +44,6 @@ const Ci = Components.interfaces;
Cu.import("resource://services-sync/base_records/collection.js");
Cu.import("resource://services-sync/constants.js");
Cu.import("resource://services-sync/engines.js");
Cu.import("resource://services-sync/ext/Observers.js");
Cu.import("resource://services-sync/stores.js");
Cu.import("resource://services-sync/trackers.js");
Cu.import("resource://services-sync/type_records/passwords.js");
@ -224,16 +223,33 @@ PasswordStore.prototype = {
function PasswordTracker(name) {
Tracker.call(this, name);
Observers.add("passwordmgr-storage-changed", this);
Svc.Obs.add("weave:engine:start-tracking", this);
Svc.Obs.add("weave:engine:stop-tracking", this);
}
PasswordTracker.prototype = {
__proto__: Tracker.prototype,
/* A single add, remove or change is 15 points, all items removed is 50 */
_enabled: false,
observe: function PasswordTracker_observe(aSubject, aTopic, aData) {
switch (aTopic) {
case "weave:engine:start-tracking":
if (!this._enabled) {
Svc.Obs.add("passwordmgr-storage-changed", this);
this._enabled = true;
}
return;
case "weave:engine:stop-tracking":
if (this._enabled) {
Svc.Obs.remove("passwordmgr-storage-changed", this);
this._enabled = false;
}
return;
}
if (this.ignoreAll)
return;
// A single add, remove or change is 15 points, all items removed is 50
switch (aData) {
case 'modifyLogin':
aSubject = aSubject.QueryInterface(Ci.nsIArray).

View File

@ -76,25 +76,30 @@ PrefsEngine.prototype = {
function PrefStore(name) {
Store.call(this, name);
Svc.Obs.add("profile-before-change", function() {
this.__prefs = null;
this.__syncPrefs = null;
}, this);
}
PrefStore.prototype = {
__proto__: Store.prototype,
__prefs: null,
get _prefs() {
let prefs = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefBranch);
this.__defineGetter__("_prefs", function() prefs);
return prefs;
if (!this.__prefs)
this.__prefs = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefBranch2);
return this.__prefs;
},
__syncPrefs: null,
get _syncPrefs() {
let service = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefService);
let syncPrefs = service.getBranch(WEAVE_SYNC_PREFS).getChildList("", {});
this.__defineGetter__("_syncPrefs", function() syncPrefs);
return syncPrefs;
if (!this.__syncPrefs)
this.__syncPrefs = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefService).
getBranch(WEAVE_SYNC_PREFS).
getChildList("", {});
return this.__syncPrefs;
},
_getAllPrefs: function PrefStore__getAllPrefs() {
@ -211,7 +216,7 @@ PrefStore.prototype = {
},
remove: function PrefStore_remove(record) {
this._log.trace("Ignoring remove request")
this._log.trace("Ignoring remove request");
},
update: function PrefStore_update(record) {
@ -226,37 +231,59 @@ PrefStore.prototype = {
function PrefTracker(name) {
Tracker.call(this, name);
this._prefs.addObserver("", this, false);
Svc.Obs.add("profile-before-change", this);
Svc.Obs.add("weave:engine:start-tracking", this);
Svc.Obs.add("weave:engine:stop-tracking", this);
}
PrefTracker.prototype = {
__proto__: Tracker.prototype,
__prefs: null,
get _prefs() {
let prefs = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefBranch2);
this.__defineGetter__("_prefs", function() prefs);
return prefs;
if (!this.__prefs)
this.__prefs = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefBranch2);
return this.__prefs;
},
__syncPrefs: null,
get _syncPrefs() {
let service = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefService);
let syncPrefs = service.getBranch(WEAVE_SYNC_PREFS).getChildList("", {});
this.__defineGetter__("_syncPrefs", function() syncPrefs);
return syncPrefs;
if (!this.__syncPrefs)
this.__syncPrefs = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefService).
getBranch(WEAVE_SYNC_PREFS).
getChildList("", {});
return this.__syncPrefs;
},
/* 25 points per pref change */
_enabled: false,
observe: function(aSubject, aTopic, aData) {
if (aTopic != "nsPref:changed")
return;
if (this._syncPrefs.indexOf(aData) != -1) {
this.score += 1;
this.addChangedID(WEAVE_PREFS_GUID);
this._log.trace("Preference " + aData + " changed");
switch (aTopic) {
case "weave:engine:start-tracking":
if (!this._enabled) {
this._prefs.addObserver("", this, false);
this._enabled = true;
}
break;
case "weave:engine:stop-tracking":
if (this._enabled) {
this._prefs.removeObserver("", this);
this._enabled = false;
}
// Fall through to clean up.
case "profile-before-change":
this.__prefs = null;
this.__syncPrefs = null;
this._prefs.removeObserver("", this);
break;
case "nsPref:changed":
// 25 points per pref change
if (this._syncPrefs.indexOf(aData) != -1) {
this.score += 1;
this.addChangedID(WEAVE_PREFS_GUID);
this._log.trace("Preference " + aData + " changed");
}
break;
}
}
};

View File

@ -225,53 +225,75 @@ TabStore.prototype = {
function TabTracker(name) {
Tracker.call(this, name);
Svc.Obs.add("private-browsing", this);
Svc.Obs.add("weave:engine:start-tracking", this);
Svc.Obs.add("weave:engine:stop-tracking", this);
// Make sure "this" pointer is always set correctly for event listeners
this.onTab = Utils.bind2(this, this.onTab);
// Register as an observer so we can catch windows opening and closing:
Svc.WinWatcher.registerNotification(this);
// Also register listeners on already open windows
let wins = Svc.WinMediator.getEnumerator("navigator:browser");
while (wins.hasMoreElements())
this._registerListenersForWindow(wins.getNext());
this._unregisterListeners = Utils.bind2(this, this._unregisterListeners);
}
TabTracker.prototype = {
__proto__: Tracker.prototype,
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
_registerListenersForWindow: function TabTracker__registerListen(window) {
this._log.trace("Registering tab listeners in new window");
// For each topic, add or remove onTab as the listener
let topics = ["pageshow", "TabOpen", "TabClose", "TabSelect"];
let onTab = this.onTab;
let addRem = function(add) topics.forEach(function(topic) {
window[(add ? "add" : "remove") + "EventListener"](topic, onTab, false);
});
// Add the listeners now and remove them on unload
addRem(true);
window.addEventListener("unload", function() addRem(false), false);
_topics: ["pageshow", "TabOpen", "TabClose", "TabSelect"],
_registerListenersForWindow: function registerListenersFW(window) {
this._log.trace("Registering tab listeners in window");
for each (let topic in this._topics) {
window.addEventListener(topic, this.onTab, false);
}
window.addEventListener("unload", this._unregisterListeners, false);
},
observe: function TabTracker_observe(aSubject, aTopic, aData) {
// Add tab listeners now that a window has opened
if (aTopic == "domwindowopened") {
let self = this;
aSubject.addEventListener("load", function onLoad(event) {
aSubject.removeEventListener("load", onLoad, false);
// Only register after the window is done loading to avoid unloads
self._registerListenersForWindow(aSubject);
}, false);
_unregisterListeners: function unregisterListeners(event) {
this._unregisterListenersForWindow(event.target);
},
_unregisterListenersForWindow: function unregisterListenersFW(window) {
this._log.trace("Removing tab listeners in window");
window.removeEventListener("unload", this._unregisterListeners, false);
for each (let topic in this._topics) {
window.removeEventListener(topic, this.onTab, false);
}
},
_enabled: false,
observe: function TabTracker_observe(aSubject, aTopic, aData) {
switch (aTopic) {
case "weave:engine:start-tracking":
if (!this._enabled) {
Svc.Obs.add("private-browsing", this);
Svc.Obs.add("domwindowopened", this);
let wins = Svc.WinMediator.getEnumerator("navigator:browser");
while (wins.hasMoreElements())
this._registerListenersForWindow(wins.getNext());
this._enabled = true;
}
break;
case "weave:engine:stop-tracking":
if (this._enabled) {
Svc.Obs.remove("private-browsing", this);
Svc.Obs.remove("domwindowopened", this);
let wins = Svc.WinMediator.getEnumerator("navigator:browser");
while (wins.hasMoreElements())
this._unregisterListenersForWindow(wins.getNext());
this._enabled = false;
}
return;
case "domwindowopened":
// Add tab listeners now that a window has opened
let self = this;
aSubject.addEventListener("load", function onLoad(event) {
aSubject.removeEventListener("load", onLoad, false);
// Only register after the window is done loading to avoid unloads
self._registerListenersForWindow(aSubject);
}, false);
break;
case "private-browsing":
if (aData == "enter" && !PBPrefs.get("autostart"))
this.clearChangedIDs();
}
else if (aTopic == "private-browsing" && aData == "enter"
&& !PBPrefs.get("autostart"))
this.clearChangedIDs();
},
onTab: function onTab(event) {

View File

@ -273,6 +273,7 @@ WeaveSvc.prototype = {
"Weave, since it will not work correctly.");
}
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:sync:error", this);
@ -292,6 +293,10 @@ WeaveSvc.prototype = {
this._updateCachedURLs();
let status = this._checkSetup();
if (status != STATUS_DISABLED && status != CLIENT_NOT_CONFIGURED)
Svc.Obs.notify("weave:engine:start-tracking");
// Applications can specify this preference if they want autoconnect
// to happen after a fixed delay.
let delay = Svc.Prefs.get("autoconnectDelay");
@ -306,7 +311,10 @@ WeaveSvc.prototype = {
},
_checkSetup: function WeaveSvc__checkSetup() {
if (!this.username) {
if (!this.enabled) {
Status.service = STATUS_DISABLED;
}
else if (!this.username) {
this._log.debug("checkSetup: no username set");
Status.login = LOGIN_FAILED_NO_USERNAME;
}
@ -404,6 +412,11 @@ WeaveSvc.prototype = {
observe: function WeaveSvc__observe(subject, topic, data) {
switch (topic) {
case "weave:service:setup-complete":
let status = this._checkSetup();
if (status != STATUS_DISABLED && status != CLIENT_NOT_CONFIGURED)
Svc.Obs.notify("weave:engine:start-tracking");
break;
case "network:offline-status-changed":
// Whether online or offline, we'll reschedule syncs
this._log.trace("Network offline status change: " + data);
@ -704,6 +717,7 @@ WeaveSvc.prototype = {
Svc.Login.removeLogin(login);
});
Svc.Obs.notify("weave:service:start-over");
Svc.Obs.notify("weave:engine:stop-tracking");
},
delayedAutoConnect: function delayedAutoConnect(delay) {
@ -752,6 +766,7 @@ WeaveSvc.prototype = {
if (Svc.IO.offline)
throw "Application is offline, login should not be called";
let initialStatus = this._checkSetup();
if (username)
this.username = username;
if (password)
@ -762,6 +777,12 @@ WeaveSvc.prototype = {
if (this._checkSetup() == CLIENT_NOT_CONFIGURED)
throw "aborting login, client not configured";
// Calling login() with parameters when the client was
// previously not configured means setup was completed.
if (initialStatus == CLIENT_NOT_CONFIGURED
&& (username || password || passphrase))
Svc.Obs.notify("weave:service:setup-complete");
this._log.info("Logging in user " + this.username);
if (!this.verifyLogin()) {
@ -1317,8 +1338,6 @@ WeaveSvc.prototype = {
// we'll handle that later
Status.resetBackoff();
this.globalScore = 0;
// Ping the server with a special info request once a day
let infoURL = this.infoURL;
let now = Math.floor(Date.now() / 1000);
@ -1330,8 +1349,15 @@ WeaveSvc.prototype = {
// Figure out what the last modified time is for each collection
let info = new Resource(infoURL).get();
if (!info.success)
if (!info.success) {
if (info.status == 401) {
this.logout();
Status.login = LOGIN_FAILED_LOGIN_REJECTED;
}
throw "aborting sync, failed to get collections";
}
this.globalScore = 0;
// Convert the response to an object and read out the modified times
for each (let engine in [Clients].concat(Engines.getAll()))
@ -1498,7 +1524,7 @@ WeaveSvc.prototype = {
if (Utils.checkStatus(resp.status, null, [500, [502, 504]])) {
Status.enforceBackoff = true;
if (resp.status == 503 && resp.headers["retry-after"])
Observers.notify("weave:service:backoff:interval", parseInt(resp.headers["retry-after"], 10));
Svc.Obs.notify("weave:service:backoff:interval", parseInt(resp.headers["retry-after"], 10));
}
},
/**
@ -1521,7 +1547,7 @@ WeaveSvc.prototype = {
* Array of collections to wipe. If not given, all collections are wiped.
*/
wipeServer: function WeaveSvc_wipeServer(collections)
this._catch(this._notify("wipe-server", "", function() {
this._notify("wipe-server", "", function() {
if (!collections) {
collections = [];
let info = new Resource(this.infoURL).get();
@ -1529,19 +1555,23 @@ WeaveSvc.prototype = {
collections.push(name);
}
for each (let name in collections) {
try {
new Resource(this.storageURL + name).delete();
// Remove the crypto record from the server and local cache
let crypto = this.storageURL + "crypto/" + name;
new Resource(crypto).delete();
CryptoMetas.del(crypto);
let url = this.storageURL + name;
let response = new Resource(url).delete();
if (response.status != 200 && response.status != 404) {
throw "Aborting wipeServer. Server responded with "
+ response.status + " response for " + url;
}
catch(ex) {
this._log.debug("Exception on wipe of '" + name + "': " + Utils.exceptionStr(ex));
// Remove the crypto record from the server and local cache
let crypto = this.storageURL + "crypto/" + name;
response = new Resource(crypto).delete();
CryptoMetas.del(crypto);
if (response.status != 200 && response.status != 404) {
throw "Aborting wipeServer. Server responded with "
+ response.status + " response for " + crypto;
}
}
}))(),
})(),
/**
* Wipe all local user data.

View File

@ -146,6 +146,15 @@ let Utils = {
throw batchEx;
};
},
createStatement: function createStatement(db, query) {
// Gecko 2.0
if (db.createAsyncStatement)
return db.createAsyncStatement(query);
// Gecko <2.0
return db.createStatement(query);
},
queryAsync: function(query, names) {
// Allow array of names, single name, and no name
@ -909,3 +918,8 @@ Svc.Obs = Observers;
let Str = {};
["errors", "sync"]
.forEach(function(lazy) Utils.lazy2(Str, lazy, Utils.lazyStrings(lazy)));
Svc.Obs.add("xpcom-shutdown", function () {
for (let name in Svc)
delete Svc[name];
});

View File

@ -8,11 +8,18 @@ let ds = Cc["@mozilla.org/file/directory_service;1"]
let provider = {
getFile: function(prop, persistent) {
persistent.value = true;
if (prop == "ExtPrefDL")
return [ds.get("CurProcD", Ci.nsIFile)];
else if (prop == "ProfD")
return ds.get("CurProcD", Ci.nsIFile);
throw Cr.NS_ERROR_FAILURE;
switch (prop) {
case "ExtPrefDL":
return [ds.get("CurProcD", Ci.nsIFile)];
case "ProfD":
return ds.get("CurProcD", Ci.nsIFile);
case "UHist":
let histFile = ds.get("CurProcD", Ci.nsIFile);
histFile.append("history.dat");
return histFile;
default:
throw Cr.NS_ERROR_FAILURE;
}
},
QueryInterface: function(iid) {
if (iid.equals(Ci.nsIDirectoryServiceProvider) ||

View File

@ -0,0 +1,49 @@
Cu.import("resource://services-sync/engines/bookmarks.js");
Cu.import("resource://services-sync/util.js");
function run_test() {
let store = new BookmarksEngine()._store;
store.wipe();
try {
_("Ensure the record isn't present yet.");
let fxuri = Utils.makeURI("http://getfirefox.com/");
let ids = Svc.Bookmark.getBookmarkIdsForURI(fxuri, {});
do_check_eq(ids.length, 0);
_("Let's create a new record.");
let fxrecord = {id: "{5d81b87c-d5fc-42d9-a114-d69b7342f10e}0",
type: "bookmark",
bmkUri: fxuri.spec,
title: "Get Firefox!",
tags: [],
keyword: "awesome",
loadInSidebar: false,
parentName: "Bookmarks Toolbar",
parentid: "toolbar"};
store.applyIncoming(fxrecord);
_("Verify it has been created correctly.");
ids = Svc.Bookmark.getBookmarkIdsForURI(fxuri, {});
do_check_eq(ids.length, 1);
let id = ids[0];
do_check_eq(Svc.Bookmark.getItemGUID(id), fxrecord.id);
do_check_eq(Svc.Bookmark.getItemType(id), Svc.Bookmark.TYPE_BOOKMARK);
do_check_eq(Svc.Bookmark.getItemTitle(id), fxrecord.title);
do_check_eq(Svc.Bookmark.getFolderIdForItem(id),
Svc.Bookmark.toolbarFolder);
do_check_eq(Svc.Bookmark.getKeywordForBookmark(id), fxrecord.keyword);
_("Have the store create a new record object. Verify that it has the same data.");
let newrecord = store.createRecord(fxrecord.id);
for each (let property in ["type", "bmkUri", "title", "keyword",
"parentName", "parentid"])
do_check_eq(newrecord[property], fxrecord[property]);
_("The calculated sort index is based on frecency data.");
do_check_true(newrecord.sortindex >= 150);
} finally {
_("Clean up.");
store.wipe();
}
}

View File

@ -0,0 +1,51 @@
Cu.import("resource://services-sync/engines/bookmarks.js");
Cu.import("resource://services-sync/util.js");
function run_test() {
let engine = new BookmarksEngine();
engine._store.wipe();
_("Verify we've got an empty tracker to work with.");
let tracker = engine._tracker;
do_check_eq([id for (id in tracker.changedIDs)].length, 0);
let folder = Svc.Bookmark.createFolder(Svc.Bookmark.bookmarksMenuFolder,
"Test Folder",
Svc.Bookmark.DEFAULT_INDEX);
function createBmk() {
Svc.Bookmark.insertBookmark(folder,
Utils.makeURI("http://getfirefox.com"),
Svc.Bookmark.DEFAULT_INDEX,
"Get Firefox!");
}
try {
_("Create bookmark. Won't show because we haven't started tracking yet");
createBmk();
do_check_eq([id for (id in tracker.changedIDs)].length, 0);
_("Tell the tracker to start tracking changes.");
Svc.Obs.notify("weave:engine:start-tracking");
createBmk();
do_check_eq([id for (id in tracker.changedIDs)].length, 1);
_("Notifying twice won't do any harm.");
Svc.Obs.notify("weave:engine:start-tracking");
createBmk();
do_check_eq([id for (id in tracker.changedIDs)].length, 2);
_("Let's stop tracking again.");
tracker.clearChangedIDs();
Svc.Obs.notify("weave:engine:stop-tracking");
createBmk();
do_check_eq([id for (id in tracker.changedIDs)].length, 0);
_("Notifying twice won't do any harm.");
Svc.Obs.notify("weave:engine:stop-tracking");
createBmk();
do_check_eq([id for (id in tracker.changedIDs)].length, 0);
} finally {
_("Clean up.");
engine._store.wipe();
}
}

View File

@ -0,0 +1,39 @@
Cu.import("resource://services-sync/engines/forms.js");
Cu.import("resource://services-sync/util.js");
function run_test() {
_("Verify we've got an empty tracker to work with.");
let tracker = new FormEngine()._tracker;
do_check_eq([id for (id in tracker.changedIDs)].length, 0);
try {
_("Create an entry. Won't show because we haven't started tracking yet");
Svc.Form.addEntry("name", "John Doe");
do_check_eq([id for (id in tracker.changedIDs)].length, 0);
_("Tell the tracker to start tracking changes.");
Svc.Obs.notify("weave:engine:start-tracking");
Svc.Form.removeEntry("name", "John Doe");
Svc.Form.addEntry("email", "john@doe.com");
do_check_eq([id for (id in tracker.changedIDs)].length, 2);
_("Notifying twice won't do any harm.");
Svc.Obs.notify("weave:engine:start-tracking");
Svc.Form.addEntry("address", "Memory Lane");
do_check_eq([id for (id in tracker.changedIDs)].length, 3);
_("Let's stop tracking again.");
tracker.clearChangedIDs();
Svc.Obs.notify("weave:engine:stop-tracking");
Svc.Form.removeEntry("address", "Memory Lane");
do_check_eq([id for (id in tracker.changedIDs)].length, 0);
_("Notifying twice won't do any harm.");
Svc.Obs.notify("weave:engine:stop-tracking");
Svc.Form.removeEntry("email", "john@doe.com");
do_check_eq([id for (id in tracker.changedIDs)].length, 0);
} finally {
_("Clean up.");
Svc.Form.removeAllEntries();
}
}

View File

@ -0,0 +1,135 @@
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://services-sync/engines/history.js");
Cu.import("resource://services-sync/type_records/history.js");
Cu.import("resource://services-sync/ext/Sync.js");
Cu.import("resource://services-sync/util.js");
const TIMESTAMP1 = 1281077113313976;
const TIMESTAMP2 = 1281088209595212;
const TIMESTAMP3 = 1281199249129950;
function queryPlaces(uri, options) {
let query = Svc.History.getNewQuery();
query.uri = uri;
let res = Svc.History.executeQuery(query, options);
res.root.containerOpen = true;
let results = [];
for (let i = 0; i < res.root.childCount; i++)
results.push(res.root.getChild(i));
return results;
}
function queryHistoryVisits(uri) {
let options = Svc.History.getNewQueryOptions();
options.queryType = Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY;
options.resultType = Ci.nsINavHistoryQueryOptions.RESULTS_AS_VISIT;
options.sortingMode = Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_ASCENDING;
return queryPlaces(uri, options);
}
function waitForTitleChanged(test) {
let [exec, cb] = Sync.withCb(function (callback) {
Svc.History.addObserver({
onBeginUpdateBatch: function onBeginUpdateBatch() {},
onEndUpdateBatch: function onEndUpdateBatch() {},
onPageChanged: function onPageChanged() {},
onTitleChanged: function onTitleChanged() {
Svc.History.removeObserver(this);
callback();
},
onVisit: function onVisit() {},
onDeleteVisits: function onDeleteVisits() {},
onPageExpired: function onPageExpired() {},
onBeforeDeleteURI: function onBeforeDeleteURI() {},
onDeleteURI: function onDeleteURI() {},
onClearHistory: function onClearHistory() {},
QueryInterface: XPCOMUtils.generateQI([
Ci.nsINavHistoryObserver,
Ci.nsINavHistoryObserver_MOZILLA_1_9_1_ADDITIONS,
Ci.nsISupportsWeakReference
])
}, true);
test();
});
exec(cb);
}
function run_test() {
_("Verify that we've got an empty store to work with.");
let store = new HistoryEngine()._store;
do_check_eq([id for (id in store.getAllIDs())].length, 0);
try {
_("Let's create an entry in the database.");
let fxuri = Utils.makeURI("http://getfirefox.com/");
Svc.History.addPageWithDetails(fxuri, "Get Firefox!", TIMESTAMP1);
_("Verify that the entry exists.");
let ids = [id for (id in store.getAllIDs())];
do_check_eq(ids.length, 1);
let fxguid = ids[0];
do_check_true(store.itemExists(fxguid));
_("If we query a non-existent record, it's marked as deleted.");
let record = store.createRecord("non-existent");
do_check_true(record.deleted);
_("Verify createRecord() returns a complete record.");
record = store.createRecord(fxguid);
do_check_eq(record.histUri, fxuri.spec);
do_check_eq(record.title, "Get Firefox!");
do_check_eq(record.visits.length, 1);
do_check_eq(record.visits[0].date, TIMESTAMP1);
do_check_eq(record.visits[0].type, Ci.nsINavHistoryService.TRANSITION_LINK);
_("Let's modify the record and have the store update the database.");
let secondvisit = {date: TIMESTAMP2,
type: Ci.nsINavHistoryService.TRANSITION_TYPED};
waitForTitleChanged(function() {
store.update({histUri: record.histUri,
title: "Hol Dir Firefox!",
visits: [record.visits[0], secondvisit]});
});
let queryres = queryHistoryVisits(fxuri);
do_check_eq(queryres.length, 2);
do_check_eq(queryres[0].time, TIMESTAMP1);
do_check_eq(queryres[0].title, "Hol Dir Firefox!");
do_check_eq(queryres[1].time, TIMESTAMP2);
do_check_eq(queryres[1].title, "Hol Dir Firefox!");
_("Create a brand new record through the store.");
let tbguid = Utils.makeGUID();
let tburi = Utils.makeURI("http://getthunderbird.com");
waitForTitleChanged(function() {
store.create({id: tbguid,
histUri: tburi.spec,
title: "The bird is the word!",
visits: [{date: TIMESTAMP3,
type: Ci.nsINavHistoryService.TRANSITION_TYPED}]});
});
do_check_eq([id for (id in store.getAllIDs())].length, 2);
queryres = queryHistoryVisits(tburi);
do_check_eq(queryres.length, 1);
do_check_eq(queryres[0].time, TIMESTAMP3);
do_check_eq(queryres[0].title, "The bird is the word!");
_("Remove a record from the store.");
store.remove({id: fxguid});
do_check_false(store.itemExists(fxguid));
queryres = queryHistoryVisits(fxuri);
do_check_eq(queryres.length, 0);
_("Make sure wipe works.");
store.wipe();
do_check_eq([id for (id in store.getAllIDs())].length, 0);
queryres = queryHistoryVisits(fxuri);
do_check_eq(queryres.length, 0);
queryres = queryHistoryVisits(tburi);
do_check_eq(queryres.length, 0);
} finally {
_("Clean up.");
Svc.History.removeAllPages();
}
}

View File

@ -0,0 +1,46 @@
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://services-sync/engines/history.js");
Cu.import("resource://services-sync/util.js");
function run_test() {
_("Verify we've got an empty tracker to work with.");
let tracker = new HistoryEngine()._tracker;
do_check_eq([id for (id in tracker.changedIDs)].length, 0);
let _counter = 0;
function addVisit() {
Svc.History.addVisit(Utils.makeURI("http://getfirefox.com/" + _counter),
Date.now() * 1000, null, 1, false, 0);
_counter += 1;
}
try {
_("Create bookmark. Won't show because we haven't started tracking yet");
addVisit();
do_check_eq([id for (id in tracker.changedIDs)].length, 0);
_("Tell the tracker to start tracking changes.");
Svc.Obs.notify("weave:engine:start-tracking");
addVisit();
do_check_eq([id for (id in tracker.changedIDs)].length, 1);
_("Notifying twice won't do any harm.");
Svc.Obs.notify("weave:engine:start-tracking");
addVisit();
do_check_eq([id for (id in tracker.changedIDs)].length, 2);
_("Let's stop tracking again.");
tracker.clearChangedIDs();
Svc.Obs.notify("weave:engine:stop-tracking");
addVisit();
do_check_eq([id for (id in tracker.changedIDs)].length, 0);
_("Notifying twice won't do any harm.");
Svc.Obs.notify("weave:engine:stop-tracking");
addVisit();
do_check_eq([id for (id in tracker.changedIDs)].length, 0);
} finally {
_("Clean up.");
Svc.History.removeAllPages();
}
}

View File

@ -35,7 +35,8 @@ function run_test() {
Weave.Service.clusterURL = "http://localhost:8080/";
Svc.Prefs.set("autoconnect", false);
_("Initial state is ok.");
_("Force the initial state.");
Status.service = STATUS_OK;
do_check_eq(Status.service, STATUS_OK);
_("Try logging in. It wont' work because we're not configured yet.");
@ -77,6 +78,21 @@ function run_test() {
do_check_eq(Status.login, LOGIN_SUCCEEDED);
do_check_true(Weave.Service.isLoggedIn);
do_check_true(Svc.Prefs.get("autoconnect"));
_("Calling login() with parameters when the client is unconfigured sends notification.");
let notified = false;
Weave.Svc.Obs.add("weave:service:setup-complete", function() {
notified = true;
});
Weave.Service.username = "";
Weave.Service.password = "";
Weave.Service.passphrase = "";
Weave.Service.login("janedoe", "ilovejohn", "bar");
do_check_true(notified);
do_check_eq(Status.service, STATUS_OK);
do_check_eq(Status.login, LOGIN_SUCCEEDED);
do_check_true(Weave.Service.isLoggedIn);
do_check_true(Svc.Prefs.get("autoconnect"));
_("Logout.");
Weave.Service.logout();

View File

@ -0,0 +1,71 @@
Cu.import("resource://services-sync/service.js");
function login_handler(request, response) {
// btoa('johndoe:ilovejane') == am9obmRvZTppbG92ZWphbmU=
let body;
if (request.hasHeader("Authorization") &&
request.getHeader("Authorization") == "Basic am9obmRvZTppbG92ZWphbmU=") {
body = "{}";
response.setStatusLine(request.httpVersion, 200, "OK");
} else {
body = "Unauthorized";
response.setStatusLine(request.httpVersion, 401, "Unauthorized");
}
response.bodyOutputStream.write(body, body.length);
}
function run_test() {
let logger = Log4Moz.repository.rootLogger;
Log4Moz.repository.rootLogger.addAppender(new Log4Moz.DumpAppender());
do_test_pending();
let server = httpd_setup({
"/1.0/johndoe/info/collections": login_handler
});
const GLOBAL_SCORE = 42;
try {
_("Set up test fixtures.");
Weave.Service.serverURL = "http://localhost:8080/";
Weave.Service.clusterURL = "http://localhost:8080/";
Weave.Service.username = "johndoe";
Weave.Service.password = "ilovejane";
Weave.Service.passphrase = "foo";
Weave.Service.globalScore = GLOBAL_SCORE;
// Avoid daily ping
Weave.Svc.Prefs.set("lastPing", Math.floor(Date.now() / 1000));
let threw = false;
Weave.Svc.Obs.add("weave:service:sync:error", function (subject, data) {
threw = true;
});
_("Initial state: We're successfully logged in.");
Weave.Service.login();
do_check_true(Weave.Service.isLoggedIn);
do_check_eq(Weave.Status.login, Weave.LOGIN_SUCCEEDED);
_("Simulate having changed the password somehwere else.");
Weave.Service.password = "ilovejosephine";
_("Let's try to sync.");
Weave.Service.sync();
_("Verify that sync() threw an exception.");
do_check_true(threw);
_("We're no longer logged in.");
do_check_false(Weave.Service.isLoggedIn);
_("Sync status.");
do_check_eq(Weave.Status.login, Weave.LOGIN_FAILED_LOGIN_REJECTED);
_("globalScore is unchanged.");
do_check_eq(Weave.Service.globalScore, GLOBAL_SCORE);
} finally {
Weave.Svc.Prefs.resetBranch("");
server.stop(do_test_finished);
}
}

View File

@ -18,20 +18,36 @@ function login_handler(request, response) {
response.bodyOutputStream.write(body, body.length);
}
function send(statusCode, status, body) {
return function(request, response) {
response.setStatusLine(request.httpVersion, statusCode, status);
response.bodyOutputStream.write(body, body.length);
};
}
function service_unavailable(request, response) {
let body = "Service Unavailable";
response.setStatusLine(request.httpVersion, 503, "Service Unavailable");
response.setHeader("Retry-After", "42");
response.bodyOutputStream.write(body, body.length);
}
function run_test() {
let logger = Log4Moz.repository.rootLogger;
Log4Moz.repository.rootLogger.addAppender(new Log4Moz.DumpAppender());
do_test_pending();
let server = httpd_setup({
"/1.0/johndoe/info/collections": login_handler
"/api/1.0/johndoe/info/collections": login_handler,
"/api/1.0/janedoe/info/collections": service_unavailable,
"/user/1.0/johndoe/node/weave": send(200, "OK", "http://localhost:8080/api/")
});
try {
Weave.Service.serverURL = "http://localhost:8080/";
Weave.Service.clusterURL = "http://localhost:8080/";
_("Initial state is ok.");
_("Force the initial state.");
Status.service = STATUS_OK;
do_check_eq(Status.service, STATUS_OK);
_("Credentials won't check out because we're not configured yet.");
@ -46,12 +62,27 @@ function run_test() {
do_check_eq(Status.service, CLIENT_NOT_CONFIGURED);
do_check_eq(Status.login, LOGIN_FAILED_NO_PASSPHRASE);
_("verifyLogin() has found out the user's cluster URL, though.");
do_check_eq(Weave.Service.clusterURL, "http://localhost:8080/api/");
_("Success if passphrase is set.");
Weave.Service.passphrase = "foo";
Weave.Service.login();
do_check_true(Weave.Service.verifyLogin());
do_check_eq(Status.service, STATUS_OK);
do_check_eq(Status.login, LOGIN_SUCCEEDED);
do_check_true(Weave.Service.isLoggedIn);
_("If verifyLogin() encounters a server error, it flips on the backoff flag and notifies observers on a 503 with Retry-After.");
Weave.Service.username = "janedoe";
do_check_false(Status.enforceBackoff);
let backoffInterval;
Svc.Obs.add("weave:service:backoff:interval", function(subject, data) {
backoffInterval = subject;
});
do_check_false(Weave.Service.verifyLogin());
do_check_true(Status.enforceBackoff);
do_check_eq(backoffInterval, 42);
do_check_eq(Status.service, LOGIN_FAILED);
do_check_eq(Status.login, LOGIN_FAILED_SERVER_ERROR);
} finally {
Svc.Prefs.resetBranch("");

View File

@ -0,0 +1,194 @@
Cu.import("resource://services-sync/util.js");
Cu.import("resource://services-sync/service.js");
Cu.import("resource://services-sync/base_records/crypto.js");
Cu.import("resource://services-sync/base_records/keys.js");
Cu.import("resource://services-sync/resource.js");
function FakeCollection() {
this.deleted = false;
}
FakeCollection.prototype = {
handler: function() {
let self = this;
return function(request, response) {
let body = "";
if (request.method == "DELETE") {
body = JSON.stringify(Date.now() / 1000);
self.deleted = true;
}
response.setStatusLine(request.httpVersion, 200, "OK");
response.bodyOutputStream.write(body, body.length);
};
}
};
function serviceUnavailable(request, response) {
let body = "Service Unavailable";
response.setStatusLine(request.httpVersion, 503, "Service Unavailable");
response.bodyOutputStream.write(body, body.length);
}
function createAndUploadKeypair() {
let keys = PubKeys.createKeypair(ID.get("WeaveCryptoID"),
PubKeys.defaultKeyUri,
PrivKeys.defaultKeyUri);
PubKeys.uploadKeypair(keys);
}
function createAndUploadSymKey(url) {
let symkey = Svc.Crypto.generateRandomKey();
let pubkey = PubKeys.getDefaultKey();
let meta = new CryptoMeta(url);
meta.addUnwrappedKey(pubkey, symkey);
let res = new Resource(meta.uri);
res.put(meta);
CryptoMetas.set(url, meta);
}
function setUpTestFixtures() {
let cryptoService = new FakeCryptoService();
Weave.Service.clusterURL = "http://localhost:8080/";
Weave.Service.username = "johndoe";
Weave.Service.passphrase = "secret";
createAndUploadKeypair();
createAndUploadSymKey("http://localhost:8080/1.0/johndoe/storage/crypto/steam");
createAndUploadSymKey("http://localhost:8080/1.0/johndoe/storage/crypto/petrol");
createAndUploadSymKey("http://localhost:8080/1.0/johndoe/storage/crypto/diesel");
}
function test_withCollectionList_failOnCrypto() {
_("Weave.Service.wipeServer() deletes collections given as argument and aborts if a collection delete fails.");
let steam_coll = new FakeCollection();
let petrol_coll = new FakeCollection();
let diesel_coll = new FakeCollection();
let crypto_steam = new ServerWBO('steam');
let crypto_diesel = new ServerWBO('diesel');
let server = httpd_setup({
"/1.0/johndoe/storage/keys/pubkey": (new ServerWBO('pubkey')).handler(),
"/1.0/johndoe/storage/keys/privkey": (new ServerWBO('privkey')).handler(),
"/1.0/johndoe/storage/steam": steam_coll.handler(),
"/1.0/johndoe/storage/petrol": petrol_coll.handler(),
"/1.0/johndoe/storage/diesel": diesel_coll.handler(),
"/1.0/johndoe/storage/crypto/steam": crypto_steam.handler(),
"/1.0/johndoe/storage/crypto/petrol": serviceUnavailable,
"/1.0/johndoe/storage/crypto/diesel": crypto_diesel.handler()
});
do_test_pending();
try {
setUpTestFixtures();
_("Confirm initial environment.");
do_check_false(steam_coll.deleted);
do_check_false(petrol_coll.deleted);
do_check_false(diesel_coll.deleted);
do_check_true(crypto_steam.payload != undefined);
do_check_true(crypto_diesel.payload != undefined);
do_check_true(CryptoMetas.contains("http://localhost:8080/1.0/johndoe/storage/crypto/steam"));
do_check_true(CryptoMetas.contains("http://localhost:8080/1.0/johndoe/storage/crypto/petrol"));
do_check_true(CryptoMetas.contains("http://localhost:8080/1.0/johndoe/storage/crypto/diesel"));
_("wipeServer() will happily ignore the non-existent collection, delete the 'steam' collection and abort after an receiving an error on the 'petrol' collection's symkey.");
let error;
try {
Weave.Service.wipeServer(["non-existent", "steam", "petrol", "diesel"]);
} catch(ex) {
error = ex;
}
_("wipeServer() threw this exception: " + error);
do_check_true(error != undefined);
_("wipeServer stopped deleting after encountering an error with the 'petrol' collection's symkey, thus only 'steam' and 'petrol' have been deleted.");
do_check_true(steam_coll.deleted);
do_check_true(petrol_coll.deleted);
do_check_false(diesel_coll.deleted);
do_check_true(crypto_steam.payload == undefined);
do_check_true(crypto_diesel.payload != undefined);
do_check_false(CryptoMetas.contains("http://localhost:8080/1.0/johndoe/storage/crypto/steam"));
do_check_false(CryptoMetas.contains("http://localhost:8080/1.0/johndoe/storage/crypto/petrol"));
do_check_true(CryptoMetas.contains("http://localhost:8080/1.0/johndoe/storage/crypto/diesel"));
} finally {
server.stop(do_test_finished);
Svc.Prefs.resetBranch("");
CryptoMetas.clearCache();
}
}
function test_withCollectionList_failOnCollection() {
_("Weave.Service.wipeServer() deletes collections given as argument.");
let steam_coll = new FakeCollection();
let diesel_coll = new FakeCollection();
let crypto_steam = new ServerWBO('steam');
let crypto_petrol = new ServerWBO('petrol');
let crypto_diesel = new ServerWBO('diesel');
let server = httpd_setup({
"/1.0/johndoe/storage/keys/pubkey": (new ServerWBO('pubkey')).handler(),
"/1.0/johndoe/storage/keys/privkey": (new ServerWBO('privkey')).handler(),
"/1.0/johndoe/storage/steam": steam_coll.handler(),
"/1.0/johndoe/storage/petrol": serviceUnavailable,
"/1.0/johndoe/storage/diesel": diesel_coll.handler(),
"/1.0/johndoe/storage/crypto/steam": crypto_steam.handler(),
"/1.0/johndoe/storage/crypto/petrol": crypto_petrol.handler(),
"/1.0/johndoe/storage/crypto/diesel": crypto_diesel.handler()
});
do_test_pending();
try {
setUpTestFixtures();
_("Confirm initial environment.");
do_check_false(steam_coll.deleted);
do_check_false(diesel_coll.deleted);
do_check_true(crypto_steam.payload != undefined);
do_check_true(crypto_petrol.payload != undefined);
do_check_true(crypto_diesel.payload != undefined);
do_check_true(CryptoMetas.contains("http://localhost:8080/1.0/johndoe/storage/crypto/steam"));
do_check_true(CryptoMetas.contains("http://localhost:8080/1.0/johndoe/storage/crypto/petrol"));
do_check_true(CryptoMetas.contains("http://localhost:8080/1.0/johndoe/storage/crypto/diesel"));
_("wipeServer() will happily ignore the non-existent collection, delete the 'steam' collection and abort after an receiving an error on the 'petrol' collection.");
let error;
try {
Weave.Service.wipeServer(["non-existent", "steam", "petrol", "diesel"]);
} catch(ex) {
error = ex;
}
_("wipeServer() threw this exception: " + error);
do_check_true(error != undefined);
_("wipeServer stopped deleting after encountering an error with the 'petrol' collection, thus only 'steam' has been deleted.");
do_check_true(steam_coll.deleted);
do_check_false(diesel_coll.deleted);
do_check_true(crypto_steam.payload == undefined);
do_check_true(crypto_petrol.payload != undefined);
do_check_true(crypto_diesel.payload != undefined);
do_check_false(CryptoMetas.contains("http://localhost:8080/1.0/johndoe/storage/crypto/steam"));
do_check_true(CryptoMetas.contains("http://localhost:8080/1.0/johndoe/storage/crypto/petrol"));
do_check_true(CryptoMetas.contains("http://localhost:8080/1.0/johndoe/storage/crypto/diesel"));
} finally {
server.stop(do_test_finished);
Svc.Prefs.resetBranch("");
CryptoMetas.clearCache();
}
}
function run_test() {
test_withCollectionList_failOnCollection();
test_withCollectionList_failOnCrypto();
}

View File

@ -863,6 +863,7 @@ function test_uploadOutgoing_failed() {
"/1.0/foo/storage/crypto/steam": crypto_steam.handler(),
"/1.0/foo/storage/steam": collection.handler()
});
do_test_pending();
createAndUploadKeypair();
createAndUploadSymKey("http://localhost:8080/1.0/foo/storage/crypto/steam");
@ -898,7 +899,7 @@ function test_uploadOutgoing_failed() {
do_check_eq(engine._tracker.changedIDs['peppercorn'], PEPPERCORN_CHANGED);
} finally {
server.stop(function() {});
server.stop(do_test_finished);
Svc.Prefs.resetBranch("");
Records.clearCache();
CryptoMetas.clearCache();