mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge services-central with mozilla-central
This commit is contained in:
commit
d69fb6bf5a
@ -369,6 +369,14 @@ body[dir=rtl] #restorePreviousSession::before {
|
||||
bottom: 2%;
|
||||
}
|
||||
|
||||
#syncLinksContainer {
|
||||
padding-top: 1em;
|
||||
}
|
||||
|
||||
.sync-link {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
@media all and (max-height: 370px) {
|
||||
#bottomSection {
|
||||
visibility: hidden;
|
||||
|
@ -102,6 +102,10 @@
|
||||
<div id="aboutMozilla">
|
||||
<a href="http://www.mozilla.com/about/">&abouthome.aboutMozilla;</a>
|
||||
</div>
|
||||
<div id="syncLinksContainer">
|
||||
<a href="javascript:void(0);" class="sync-link" id="setupSyncLink">&abouthome.syncSetup.label;</a>
|
||||
<a href="javascript:void(0);" class="sync-link" id="pairDeviceLink">&abouthome.pairDevice.label;</a>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -211,6 +211,24 @@ let gSyncUI = {
|
||||
this.clearError(title);
|
||||
},
|
||||
|
||||
// Set visibility of "Setup Sync" link
|
||||
showSetupSyncAboutHome: function SUI_showSetupSyncAboutHome(toShow) {
|
||||
let browsers = gBrowser.browsers;
|
||||
for (let i = 0; i < browsers.length; i++) {
|
||||
let b = browsers[i];
|
||||
if ("about:home" == b.currentURI.spec) {
|
||||
b.contentDocument.getElementById("setupSyncLink").hidden = !toShow;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onSetupComplete: function SUI_onSetupComplete() {
|
||||
// Remove "setup sync" link in about:home if it is open.
|
||||
this.showSetupSyncAboutHome(false);
|
||||
|
||||
onLoginFinish();
|
||||
},
|
||||
|
||||
onLoginError: function SUI_onLoginError() {
|
||||
// if login fails, any other notifications are essentially moot
|
||||
Weave.Notifications.removeAll();
|
||||
@ -255,6 +273,8 @@ let gSyncUI = {
|
||||
|
||||
onStartOver: function SUI_onStartOver() {
|
||||
this.clearError();
|
||||
// Make "setup sync" link visible in about:home if it is open.
|
||||
this.showSetupSyncAboutHome(true);
|
||||
},
|
||||
|
||||
onQuotaNotice: function onQuotaNotice(subject, data) {
|
||||
@ -291,16 +311,40 @@ let gSyncUI = {
|
||||
|
||||
//XXXzpao should be part of syncCommon.js - which we might want to make a module...
|
||||
// To be fixed in a followup (bug 583366)
|
||||
openSetup: function SUI_openSetup() {
|
||||
|
||||
/**
|
||||
* Invoke the Sync setup wizard.
|
||||
*
|
||||
* @param wizardType
|
||||
* Indicates type of wizard to launch:
|
||||
* null -- regular set up wizard
|
||||
* "pair" -- pair a device first
|
||||
* "reset" -- reset sync
|
||||
*/
|
||||
|
||||
openSetup: function SUI_openSetup(wizardType) {
|
||||
let win = Services.wm.getMostRecentWindow("Weave:AccountSetup");
|
||||
if (win)
|
||||
win.focus();
|
||||
else {
|
||||
window.openDialog("chrome://browser/content/syncSetup.xul",
|
||||
"weaveSetup", "centerscreen,chrome,resizable=no");
|
||||
"weaveSetup", "centerscreen,chrome,resizable=no",
|
||||
wizardType);
|
||||
}
|
||||
},
|
||||
|
||||
openAddDevice: function () {
|
||||
if (!Weave.Utils.ensureMPUnlocked())
|
||||
return;
|
||||
|
||||
let win = Services.wm.getMostRecentWindow("Sync:AddDevice");
|
||||
if (win)
|
||||
win.focus();
|
||||
else
|
||||
window.openDialog("chrome://browser/content/syncAddDevice.xul",
|
||||
"syncAddDevice", "centerscreen,chrome,resizable=no");
|
||||
},
|
||||
|
||||
openQuotaDialog: function SUI_openQuotaDialog() {
|
||||
let win = Services.wm.getMostRecentWindow("Sync:ViewQuota");
|
||||
if (win)
|
||||
@ -462,7 +506,7 @@ let gSyncUI = {
|
||||
this.onQuotaNotice();
|
||||
break;
|
||||
case "weave:service:setup-complete":
|
||||
this.onLoginFinish();
|
||||
this.onSetupComplete();
|
||||
break;
|
||||
case "weave:service:login:start":
|
||||
this.onActivityStart();
|
||||
|
@ -2675,6 +2675,10 @@ function BrowserOnAboutPageLoad(document) {
|
||||
getService(Components.interfaces.nsISessionStore);
|
||||
if (!ss.canRestoreLastSession)
|
||||
document.getElementById("sessionRestoreContainer").hidden = true;
|
||||
// Sync-related links
|
||||
if (Services.prefs.prefHasUserValue("services.sync.username")) {
|
||||
document.getElementById("setupSyncLink").hidden = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2683,7 +2687,9 @@ function BrowserOnAboutPageLoad(document) {
|
||||
*/
|
||||
function BrowserOnClick(event) {
|
||||
// Don't trust synthetic events
|
||||
if (!event.isTrusted || event.target.localName != "button")
|
||||
if (!event.isTrusted ||
|
||||
(event.target.localName != "button" &&
|
||||
event.target.className != "sync-link"))
|
||||
return;
|
||||
|
||||
var ot = event.originalTarget;
|
||||
@ -2813,6 +2819,16 @@ function BrowserOnClick(event) {
|
||||
ss.restoreLastSession();
|
||||
errorDoc.getElementById("sessionRestoreContainer").hidden = true;
|
||||
}
|
||||
else if (ot == errorDoc.getElementById("pairDeviceLink")) {
|
||||
if (Services.prefs.prefHasUserValue("services.sync.username")) {
|
||||
gSyncUI.openAddDevice();
|
||||
} else {
|
||||
gSyncUI.openSetup("pair");
|
||||
}
|
||||
}
|
||||
else if (ot == errorDoc.getElementById("setupSyncLink")) {
|
||||
gSyncUI.openSetup(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,12 +44,14 @@ let gProgressBar;
|
||||
let gCounter = 0;
|
||||
|
||||
function onLoad(event) {
|
||||
Services.obs.addObserver(increaseProgressBar, "weave:engine:sync:finish", false);
|
||||
Services.obs.addObserver(increaseProgressBar, "weave:engine:sync:error", false);
|
||||
Services.obs.addObserver(onEngineSync, "weave:engine:sync:finish", false);
|
||||
Services.obs.addObserver(onEngineSync, "weave:engine:sync:error", false);
|
||||
Services.obs.addObserver(onServiceSync, "weave:service:sync:finish", false);
|
||||
Services.obs.addObserver(onServiceSync, "weave:service:sync:error", false);
|
||||
|
||||
gProgressBar = document.getElementById('uploadProgressBar');
|
||||
|
||||
if (Services.prefs.getPrefType("services.sync.firstSync") != Ci.nsIPrefBranch.PREF_INVALID) {
|
||||
gProgressBar.max = Weave.Engines.getEnabled().length;
|
||||
gProgressBar.style.display = "inline";
|
||||
}
|
||||
else {
|
||||
@ -58,15 +60,46 @@ function onLoad(event) {
|
||||
}
|
||||
|
||||
function onUnload(event) {
|
||||
Services.obs.removeObserver(increaseProgressBar, "weave:engine:sync:finish");
|
||||
Services.obs.removeObserver(increaseProgressBar, "weave:engine:sync:error");
|
||||
cleanUpObservers();
|
||||
}
|
||||
|
||||
function increaseProgressBar(){
|
||||
function cleanUpObservers() {
|
||||
try {
|
||||
Services.obs.removeObserver(onEngineSync, "weave:engine:sync:finish", false);
|
||||
Services.obs.removeObserver(onEngineSync, "weave:engine:sync:error", false);
|
||||
Services.obs.removeObserver(onServiceSync, "weave:service:sync:finish", false);
|
||||
Services.obs.removeObserver(onServiceSync, "weave:service:sync:error", false);
|
||||
}
|
||||
catch (e) {
|
||||
// may be double called by unload & exit. Ignore.
|
||||
}
|
||||
}
|
||||
|
||||
function onEngineSync(subject, topic, data) {
|
||||
// The Clients engine syncs first. At this point we don't necessarily know
|
||||
// yet how many engines will be enabled, so we'll ignore the Clients engine
|
||||
// and evaluate how many engines are enabled when the first "real" engine
|
||||
// syncs.
|
||||
if (data == "clients") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!gCounter &&
|
||||
Services.prefs.getPrefType("services.sync.firstSync") != Ci.nsIPrefBranch.PREF_INVALID) {
|
||||
gProgressBar.max = Weave.Engines.getEnabled().length;
|
||||
}
|
||||
|
||||
gCounter += 1;
|
||||
gProgressBar.setAttribute("value", gCounter);
|
||||
}
|
||||
|
||||
function onServiceSync(subject, topic, data) {
|
||||
// To address the case where 0 engines are synced, we will fill the
|
||||
// progress bar so the user knows that the sync has finished.
|
||||
gProgressBar.setAttribute("value", gProgressBar.max);
|
||||
cleanUpObservers();
|
||||
}
|
||||
|
||||
function closeTab() {
|
||||
window.close();
|
||||
}
|
||||
|
@ -10,6 +10,9 @@ registerCleanupFunction(function() {
|
||||
try {
|
||||
Services.prefs.clearUserPref("network.cookie.lifetimePolicy");
|
||||
} catch (ex) {}
|
||||
try {
|
||||
Services.prefs.clearUserPref("services.sync.username");
|
||||
} catch (ex) {}
|
||||
});
|
||||
|
||||
let gTests = [
|
||||
@ -114,6 +117,116 @@ let gTests = [
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
desc: "Check sync links visibility before and after Sync setup",
|
||||
setup: function ()
|
||||
{
|
||||
try {
|
||||
Services.prefs.clearUserPref("services.sync.username");
|
||||
} catch (ex) {}
|
||||
Services.obs.notifyObservers(null, "weave:service:ready", null);
|
||||
},
|
||||
run: function ()
|
||||
{
|
||||
let doc = gBrowser.selectedTab.linkedBrowser.contentDocument;
|
||||
let pairLink = doc.getElementById("pairDeviceLink");
|
||||
let setupLink = doc.getElementById("setupSyncLink");
|
||||
|
||||
ok(pairLink, "Found 'Pair Device' link");
|
||||
ok(setupLink, "Found 'Set Up Sync' link");
|
||||
ok(!pairLink.hidden, "'Pair' link is visible before setup");
|
||||
ok(!setupLink.hidden, "'Set Up' link is visible before setup");
|
||||
|
||||
Services.obs.notifyObservers(null, "weave:service:setup-complete", null);
|
||||
|
||||
executeSoon(function () {
|
||||
setupLink = doc.getElementById("setupSyncLink");
|
||||
ok(setupLink.hidden, "'Set Up' link is hidden after setup");
|
||||
ok(!pairLink.hidden, "'Pair' link is visible after setup");
|
||||
|
||||
executeSoon(runNextTest);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
desc: "Check sync links visibility before and after Sync unlink",
|
||||
setup: function ()
|
||||
{
|
||||
Services.prefs.setCharPref("services.sync.username", "someuser@domain.com");
|
||||
Services.obs.notifyObservers(null, "weave:service:ready", null);
|
||||
},
|
||||
run: function ()
|
||||
{
|
||||
let doc = gBrowser.selectedTab.linkedBrowser.contentDocument;
|
||||
let pairLink = doc.getElementById("pairDeviceLink");
|
||||
let setupLink = doc.getElementById("setupSyncLink");
|
||||
|
||||
ok(!pairLink.hidden, "'Pair' link is visible before unlink");
|
||||
ok(setupLink.hidden, "'Set Up' link is hidden before unlink");
|
||||
|
||||
Services.obs.notifyObservers(null, "weave:service:start-over", null);
|
||||
|
||||
executeSoon(function () {
|
||||
setupLink = doc.getElementById("setupSyncLink");
|
||||
ok(!setupLink.hidden, "'Set Up' link is visible after unlink");
|
||||
ok(!pairLink.hidden, "'Pair' link is visible after unlink");
|
||||
executeSoon(runNextTest);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
desc: "Check Pair Device link opens correct dialog with Sync account ",
|
||||
setup: function ()
|
||||
{
|
||||
Services.prefs.setCharPref("services.sync.username", "someuser@domain.com");
|
||||
Services.obs.notifyObservers(null, "weave:service:ready", null);
|
||||
},
|
||||
run: function ()
|
||||
{
|
||||
expectDialogWindow("Sync:AddDevice");
|
||||
let browser = gBrowser.selectedTab.linkedBrowser;
|
||||
let button = browser.contentDocument.getElementById("pairDeviceLink");
|
||||
EventUtils.sendMouseEvent({type: "click"}, button, browser.contentWindow);
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
desc: "Check Pair Device link opens correct dialog without Sync account",
|
||||
setup: function ()
|
||||
{
|
||||
try {
|
||||
Services.prefs.clearUserPref("services.sync.username");
|
||||
} catch (ex) {}
|
||||
Services.obs.notifyObservers(null, "weave:service:ready", null);
|
||||
},
|
||||
run: function ()
|
||||
{
|
||||
expectDialogWindow("Weave:AccountSetup");
|
||||
let browser = gBrowser.selectedTab.linkedBrowser;
|
||||
let button = browser.contentDocument.getElementById("pairDeviceLink");
|
||||
EventUtils.sendMouseEvent({type: "click"}, button, browser.contentWindow);
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
desc: "Check Sync Setup link opens correct dialog (without Sync account)",
|
||||
setup: function ()
|
||||
{
|
||||
try {
|
||||
Services.prefs.clearUserPref("services.sync.username");
|
||||
} catch (ex) {}
|
||||
Services.obs.notifyObservers(null, "weave:service:ready", null);
|
||||
},
|
||||
run: function ()
|
||||
{
|
||||
expectDialogWindow("Weave:AccountSetup");
|
||||
let browser = gBrowser.selectedTab.linkedBrowser;
|
||||
let button = browser.contentDocument.getElementById("setupSyncLink");
|
||||
EventUtils.sendMouseEvent({type: "click"}, button, browser.contentWindow);
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
function test()
|
||||
@ -159,6 +272,22 @@ function runNextTest()
|
||||
}
|
||||
}
|
||||
|
||||
function expectDialogWindow(expectedDialog) {
|
||||
Services.ww.registerNotification(function onWindow(subject, topic) {
|
||||
let win = subject.QueryInterface(Components.interfaces.nsIDOMWindow);
|
||||
win.addEventListener("load", function onLoad() {
|
||||
win.removeEventListener("load", onLoad, false);
|
||||
let wintype = win.document.documentElement.getAttribute("windowtype");
|
||||
if (topic == "domwindowopened" && wintype == expectedDialog) {
|
||||
Services.ww.unregisterNotification(onWindow);
|
||||
// Clean up dialog.
|
||||
win.close();
|
||||
executeSoon(runNextTest);
|
||||
}
|
||||
}, false);
|
||||
});
|
||||
}
|
||||
|
||||
function getStorage()
|
||||
{
|
||||
let aboutHomeURI = Services.io.newURI("moz-safe-about:home", null, null);
|
||||
|
@ -18,3 +18,6 @@
|
||||
text in <a/> will be linked to the featured add-ons on addons.mozilla.org
|
||||
-->
|
||||
<!ENTITY abouthome.defaultSnippet2.v1 "It's easy to customize your Firefox exactly the way you want it. <a>Choose from thousands of add-ons</a>.">
|
||||
|
||||
<!ENTITY abouthome.syncSetup.label "Set Up Sync">
|
||||
<!ENTITY abouthome.pairDevice.label "Pair a Device">
|
||||
|
@ -96,6 +96,7 @@
|
||||
</div>
|
||||
<div id="sync-setup">
|
||||
<a id="syncSetupSync" href="#" onclick="openSetupSyncWizard();">&aboutHome.setupSync;</a>
|
||||
<a id="syncPairDevice" href="#" onclick="openPairDeviceWizard();">&aboutHome.syncPairDevice;</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -433,8 +434,10 @@
|
||||
function initSetupSync() {
|
||||
let services = getChromeWin().Services;
|
||||
if (services.prefs.prefHasUserValue("services.sync.username")) {
|
||||
document.getElementById("syncSetupSync").style.display = "none";
|
||||
}
|
||||
syncConnected();
|
||||
} else {
|
||||
syncDisconnected();
|
||||
}
|
||||
services.obs.addObserver(syncConnected, "weave:service:setup-complete", false);
|
||||
services.obs.addObserver(syncDisconnected, "weave:service:start-over", false);
|
||||
}
|
||||
@ -447,10 +450,12 @@
|
||||
|
||||
function syncConnected() {
|
||||
document.getElementById("syncSetupSync").style.display = "none";
|
||||
document.getElementById("syncPairDevice").style.display = "inline";
|
||||
}
|
||||
|
||||
function syncDisconnected() {
|
||||
document.getElementById("syncSetupSync").style.display = "inline";
|
||||
document.getElementById("syncPairDevice").style.display = "none";
|
||||
}
|
||||
|
||||
function openSetupSyncWizard() {
|
||||
@ -458,6 +463,11 @@
|
||||
chromeWin.WeaveGlue.open();
|
||||
}
|
||||
|
||||
function openPairDeviceWizard() {
|
||||
let chromeWin = getChromeWin();
|
||||
chromeWin.SyncPairDevice.open();
|
||||
}
|
||||
|
||||
function initLightbox() {
|
||||
let prefs = getChromeWin().Services.prefs;
|
||||
let channel = prefs.getCharPref("app.update.channel");
|
||||
|
@ -113,6 +113,7 @@ XPCOMUtils.defineLazyGetter(this, "CommonUI", function() {
|
||||
["MasterPasswordUI", "chrome://browser/content/MasterPasswordUI.js"],
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
["WeaveGlue", "chrome://browser/content/sync.js"],
|
||||
["SyncPairDevice", "chrome://browser/content/sync.js"],
|
||||
#endif
|
||||
["WebappsUI", "chrome://browser/content/WebappsUI.js"],
|
||||
["SSLExceptions", "chrome://browser/content/exceptions.js"],
|
||||
|
@ -24,6 +24,7 @@
|
||||
- Brad Lassey <blassey@mozilla.com>
|
||||
- Mark Finkle <mfinkle@mozila.com>
|
||||
- Matt Brubeck <mbrubeck@mozila.com>
|
||||
- Allison Naaktgeboren <ally@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
|
||||
@ -487,6 +488,7 @@
|
||||
<button label="&sync.connect;" oncommand="WeaveGlue.tryConnect();" />
|
||||
</setting>
|
||||
<setting id="sync-connected" class="setting-group" title="&sync.connected;" type="control" collapsed="true">
|
||||
<button id="sync-pairdevice" label="&sync.pair.title;" oncommand="SyncPairDevice.open();" />
|
||||
<button id="sync-details" label="&sync.details;" type="checkbox" autocheck="false" checked="false" oncommand="WeaveGlue.showDetails();" />
|
||||
</setting>
|
||||
<setting id="sync-sync" class="setting-subgroup" type="control" collapsed="true">
|
||||
@ -593,7 +595,15 @@
|
||||
</hbox>
|
||||
</vbox>
|
||||
<vbox id="syncsetup-waiting" class="syncsetup-page" flex="1" hidden="true">
|
||||
<description class="syncsetup-desc syncsetup-center" flex="1">&sync.setup.waiting;</description>
|
||||
<progressmeter id="syncsetup-progressbar" mode="undetermined"/>
|
||||
<vbox id="syncsetup-waiting-top" align="center" flex="1">
|
||||
<description id="syncsetup-waiting-desc" class="syncsetup-desc syncsetup-center" flex="1">&sync.setup.waiting2;</description>
|
||||
<description id="syncsetup-waitingdownload-desc" class="syncsetup-desc syncsetup-center" hidden="true" flex="1">&sync.setup.waitingdownload;</description>
|
||||
</vbox>
|
||||
<hbox class="prompt-buttons" pack="center" align="end">
|
||||
<button id="syncsetup-waiting-cancel" class="prompt-button" oncommand="WeaveGlue.close();">&sync.setup.cancel;</button>
|
||||
<button id="syncsetup-waiting-close" class="prompt-button" hidden="true" oncommand="WeaveGlue.close();">&sync.setup.close;</button>
|
||||
</hbox>
|
||||
</vbox>
|
||||
<vbox id="syncsetup-fallback" class="syncsetup-page" flex="1" hidden="true">
|
||||
<scrollbox class="prompt-message" orient="vertical" flex="1">
|
||||
@ -618,6 +628,32 @@
|
||||
</vbox>
|
||||
</dialog>
|
||||
</box>
|
||||
|
||||
<box id="syncpair-container" class="perm-modal-block" hidden="true">
|
||||
<dialog id="syncpair-dialog" class="content-dialog" flex="1">
|
||||
<hbox class="prompt-title">
|
||||
<description>&sync.pair.title;</description>
|
||||
</hbox>
|
||||
<separator class="prompt-line"/>
|
||||
<vbox id="syncpair-simple" class="syncsetup-page" flex="1">
|
||||
<scrollbox id="sync-message" class="prompt-message" orient="vertical" flex="1">
|
||||
<description class="syncsetup-desc syncsetup-center" flex="1">&sync.pair.description;</description>
|
||||
<description class="syncsetup-center link" flex="1" onclick="SyncPairDevice.close(); WeaveGlue.openTutorial();">&sync.setup.tutorial;</description>
|
||||
<separator/>
|
||||
<vbox align="center" flex="1">
|
||||
<textbox id="syncpair-code1" class="syncsetup-code" oninput="SyncPairDevice.onTextBoxInput(this);"/>
|
||||
<textbox id="syncpair-code2" class="syncsetup-code" oninput="SyncPairDevice.onTextBoxInput(this);"/>
|
||||
<textbox id="syncpair-code3" class="syncsetup-code" oninput="SyncPairDevice.onTextBoxInput(this);"/>
|
||||
</vbox>
|
||||
<separator flex="1"/>
|
||||
</scrollbox>
|
||||
<hbox class="prompt-buttons" pack="center">
|
||||
<button class="prompt-button" oncommand="SyncPairDevice.close();">&sync.setup.cancel;</button>
|
||||
<button id="syncpair-connectbutton" class="prompt-button" disabled="true" oncommand="SyncPairDevice.connect();">&sync.setup.connect;</button>
|
||||
</hbox>
|
||||
</vbox>
|
||||
</dialog>
|
||||
</box>
|
||||
#endif
|
||||
|
||||
<arrowbox id="search-engines-popup" hidden="true" offset="18" flex="1" type="dialog">
|
||||
|
@ -21,6 +21,7 @@
|
||||
* Mark Finkle <mfinkle@mozila.com>
|
||||
* Matt Brubeck <mbrubeck@mozila.com>
|
||||
* Jono DiCarlo <jdicarlo@mozilla.com>
|
||||
* Allison Naaktgeboren <ally@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
|
||||
@ -38,9 +39,14 @@
|
||||
|
||||
let WeaveGlue = {
|
||||
setupData: null,
|
||||
_boundOnEngineSync: null, // Needed to unhook the observers in close().
|
||||
_boundOnServiceSync: null,
|
||||
jpake: null,
|
||||
_bundle: null,
|
||||
_loginError: false,
|
||||
_progressBar: null,
|
||||
_progressValue: 0,
|
||||
_progressMax: null,
|
||||
|
||||
init: function init() {
|
||||
if (this._bundle)
|
||||
@ -64,6 +70,9 @@ let WeaveGlue = {
|
||||
} else if (Weave.Status.login != Weave.LOGIN_FAILED_NO_USERNAME) {
|
||||
this.loadSetupData();
|
||||
}
|
||||
this._boundOnEngineSync = this.onEngineSync.bind(this);
|
||||
this._boundOnServiceSync = this.onServiceSync.bind(this);
|
||||
this._progressBar = document.getElementById("syncsetup-progressbar");
|
||||
},
|
||||
|
||||
abortEasySetup: function abortEasySetup() {
|
||||
@ -128,7 +137,16 @@ let WeaveGlue = {
|
||||
|
||||
onComplete: function onComplete(aCredentials) {
|
||||
self.jpake = null;
|
||||
self.close();
|
||||
|
||||
self._progressBar.mode = "determined";
|
||||
document.getElementById("syncsetup-waiting-desc").hidden = true;
|
||||
document.getElementById("syncsetup-waiting-cancel").hidden = true;
|
||||
document.getElementById("syncsetup-waitingdownload-desc").hidden = false;
|
||||
document.getElementById("syncsetup-waiting-close").hidden = false;
|
||||
Services.obs.addObserver(self._boundOnEngineSync, "weave:engine:sync:finish", false);
|
||||
Services.obs.addObserver(self._boundOnEngineSync, "weave:engine:sync:error", false);
|
||||
Services.obs.addObserver(self._boundOnServiceSync, "weave:service:sync:finish", false);
|
||||
Services.obs.addObserver(self._boundOnServiceSync, "weave:service:sync:error", false);
|
||||
self.setupData = aCredentials;
|
||||
self.connect();
|
||||
},
|
||||
@ -180,6 +198,7 @@ let WeaveGlue = {
|
||||
this._resetScrollPosition();
|
||||
|
||||
document.getElementById("syncsetup-simple").hidden = true;
|
||||
document.getElementById("syncsetup-waiting").hidden = true;
|
||||
document.getElementById("syncsetup-fallback").hidden = false;
|
||||
|
||||
// Push the current setup data into the UI
|
||||
@ -204,7 +223,37 @@ let WeaveGlue = {
|
||||
this.canConnect();
|
||||
},
|
||||
|
||||
onEngineSync: function onEngineSync(subject, topic, data) {
|
||||
// The Clients engine syncs first. At this point we don't necessarily know
|
||||
// yet how many engines will be enabled, so we'll ignore the Clients engine
|
||||
// and evaluate how many engines are enabled when the first "real" engine
|
||||
// syncs.
|
||||
if (data == 'clients') {
|
||||
return;
|
||||
}
|
||||
if (this._progressMax == null) {
|
||||
this._progressMax = Weave.Engines.getEnabled().length;
|
||||
this._progressBar.max = this._progressMax;
|
||||
}
|
||||
this._progressValue += 1;
|
||||
this._progressBar.setAttribute("value", this._progressValue);
|
||||
},
|
||||
|
||||
onServiceSync: function onServiceSync() {
|
||||
this.close();
|
||||
},
|
||||
|
||||
close: function close() {
|
||||
try {
|
||||
Services.obs.removeObserver(this._boundOnEngineSync, "weave:engine:sync:finish");
|
||||
Services.obs.removeObserver(this._boundOnEngineSync, "weave:engine:sync:error");
|
||||
Services.obs.removeObserver(this._boundOnServiceSync, "weave:service:sync:finish");
|
||||
Services.obs.removeObserver(this._boundOnServiceSync, "weave:service:sync:error");
|
||||
}
|
||||
catch(e) {
|
||||
// Observers weren't registered because we never got as far as onComplete.
|
||||
}
|
||||
|
||||
if (this.jpake)
|
||||
this.abortEasySetup();
|
||||
|
||||
@ -226,6 +275,15 @@ let WeaveGlue = {
|
||||
this._elements.usecustomserver.checked = false;
|
||||
this._elements.customserver.disabled = true;
|
||||
this._elements.customserver.value = "";
|
||||
document.getElementById("syncsetup-waiting-desc").hidden = false;
|
||||
document.getElementById("syncsetup-waiting-cancel").hidden = false;
|
||||
document.getElementById("syncsetup-waitingdownload-desc").hidden = true;
|
||||
document.getElementById("syncsetup-waiting-close").hidden = true;
|
||||
this._progressMax = null;
|
||||
this._progressValue = 0;
|
||||
this._progressBar.max = 0;
|
||||
this._progressBar.value = 0;
|
||||
this._progressBar.mode = "undetermined";
|
||||
|
||||
// Close the connect UI
|
||||
document.getElementById("syncsetup-container").hidden = true;
|
||||
@ -372,7 +430,7 @@ let WeaveGlue = {
|
||||
elements[id] = document.getElementById("syncsetup-" + id);
|
||||
});
|
||||
|
||||
let settingids = ["device", "connect", "connected", "disconnect", "sync", "details"];
|
||||
let settingids = ["device", "connect", "connected", "disconnect", "sync", "details", "pairdevice"];
|
||||
settingids.forEach(function(id) {
|
||||
elements[id] = document.getElementById("sync-" + id);
|
||||
});
|
||||
@ -397,6 +455,7 @@ let WeaveGlue = {
|
||||
let device = this._elements.device;
|
||||
let disconnect = this._elements.disconnect;
|
||||
let sync = this._elements.sync;
|
||||
let pairdevice = this._elements.pairdevice;
|
||||
|
||||
// Show what went wrong with login if necessary
|
||||
if (aTopic == "weave:ui:login:error") {
|
||||
@ -541,3 +600,95 @@ let WeaveGlue = {
|
||||
this.setupData.serverURL = serverURL;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const PIN_PART_LENGTH = 4;
|
||||
|
||||
let SyncPairDevice = {
|
||||
jpake: null,
|
||||
|
||||
open: function open() {
|
||||
this.code1.setAttribute("maxlength", PIN_PART_LENGTH);
|
||||
this.code2.setAttribute("maxlength", PIN_PART_LENGTH);
|
||||
this.code3.setAttribute("maxlength", PIN_PART_LENGTH);
|
||||
this.nextFocusEl = {code1: this.code2,
|
||||
code2: this.code3,
|
||||
code3: this.connectbutton};
|
||||
|
||||
document.getElementById("syncpair-container").hidden = false;
|
||||
BrowserUI.pushDialog(this);
|
||||
this.code1.focus();
|
||||
|
||||
// Kick off a sync. That way the server will have the most recent data from
|
||||
// this computer and it will show up immediately on the new device.
|
||||
Weave.SyncScheduler.scheduleNextSync(0);
|
||||
},
|
||||
|
||||
close: function close() {
|
||||
this.code1.value = this.code2.value = this.code3.value = "";
|
||||
this.code1.disabled = this.code2.disabled = this.code3.disabled = false;
|
||||
this.connectbutton.disabled = true;
|
||||
if (this.jpake) {
|
||||
this.jpake.abort();
|
||||
this.jpake = null;
|
||||
}
|
||||
document.getElementById("syncpair-container").hidden = true;
|
||||
BrowserUI.popDialog();
|
||||
},
|
||||
|
||||
onTextBoxInput: function onTextBoxInput(textbox) {
|
||||
if (textbox && textbox.value.length == PIN_PART_LENGTH) {
|
||||
let name = textbox.id.split("-")[1];
|
||||
this.nextFocusEl[name].focus();
|
||||
}
|
||||
|
||||
this.connectbutton.disabled =
|
||||
!(this.code1.value.length == PIN_PART_LENGTH &&
|
||||
this.code2.value.length == PIN_PART_LENGTH &&
|
||||
this.code3.value.length == PIN_PART_LENGTH);
|
||||
},
|
||||
|
||||
connect: function connect() {
|
||||
let self = this;
|
||||
let jpake = this.jpake = new Weave.JPAKEClient({
|
||||
onPaired: function onPaired() {
|
||||
let credentials = {account: Weave.Service.account,
|
||||
password: Weave.Service.password,
|
||||
synckey: Weave.Service.passphrase,
|
||||
serverURL: Weave.Service.serverURL};
|
||||
jpake.sendAndComplete(credentials);
|
||||
},
|
||||
onComplete: function onComplete() {
|
||||
self.jpake = null;
|
||||
self.close();
|
||||
|
||||
// Schedule a Sync for soonish to fetch the data uploaded by the
|
||||
// device with which we just paired.
|
||||
Weave.SyncScheduler.scheduleNextSync(Weave.SyncScheduler.activeInterval);
|
||||
},
|
||||
onAbort: function onAbort(error) {
|
||||
self.jpake = null;
|
||||
|
||||
// Aborted by user, ignore.
|
||||
if (error == Weave.JPAKE_ERROR_USERABORT) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.code1.value = self.code2.value = self.code3.value = "";
|
||||
self.code1.disabled = self.code2.disabled = self.code3.disabled = false;
|
||||
self.code1.focus();
|
||||
}
|
||||
});
|
||||
this.code1.disabled = this.code2.disabled = this.code3.disabled = true;
|
||||
this.connectbutton.disabled = true;
|
||||
|
||||
let pin = this.code1.value + this.code2.value + this.code3.value;
|
||||
let expectDelay = false;
|
||||
jpake.pairWithPIN(pin, expectDelay);
|
||||
}
|
||||
};
|
||||
["code1", "code2", "code3", "connectbutton"].forEach(function (id) {
|
||||
XPCOMUtils.defineLazyGetter(SyncPairDevice, id, function() {
|
||||
return document.getElementById("syncpair-" + id);
|
||||
});
|
||||
});
|
||||
|
@ -19,4 +19,13 @@
|
||||
(aboutHome.forAndroid): Second line of a multi-line button. Treat as a subtitle.
|
||||
-->
|
||||
<!ENTITY aboutHome.forAndroid "for Android">
|
||||
<!ENTITY aboutHome.setupSync "Set Up Sync">
|
||||
<!-- LOCALIZATION NOTE:
|
||||
(aboutHome.setupSync): This string should match the desktop
|
||||
equivalent, in particular concerning syncBrand.fullName.label.
|
||||
-->
|
||||
<!ENTITY aboutHome.setupSync "Set Up Sync">
|
||||
<!-- LOCALIZATION NOTE:
|
||||
(aboutHome.syncPairDevice): This string should match the desktop
|
||||
equivalent, in particular concerning syncBrand.fullName.label.
|
||||
-->
|
||||
<!ENTITY aboutHome.syncPairDevice "Pair a Device">
|
||||
|
@ -19,4 +19,9 @@
|
||||
<!ENTITY sync.setup.connect "Connect">
|
||||
<!ENTITY sync.setup.cancel "Cancel">
|
||||
<!ENTITY sync.setup.tutorial "Show me how">
|
||||
<!ENTITY sync.setup.waiting "Pairing in progress…">
|
||||
<!ENTITY sync.setup.waiting2 "Waiting for other device…">
|
||||
|
||||
<!ENTITY sync.pair.title "Pair a Device">
|
||||
<!ENTITY sync.pair.description "To activate your new device, select "Set Up Sync" on the device.">
|
||||
<!ENTITY sync.setup.close "Close">
|
||||
<!ENTITY sync.setup.waitingdownload "Your data is now being downloaded in the background. You can close this window at any time.">
|
||||
|
@ -267,7 +267,8 @@ body[dir="rtl"] {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#syncSetupSync {
|
||||
#syncSetupSync,
|
||||
#syncPairDevice {
|
||||
text-decoration: underline;
|
||||
color: blue;
|
||||
}
|
||||
|
@ -1534,7 +1534,11 @@ setting {
|
||||
}
|
||||
|
||||
#syncsetup-waiting {
|
||||
padding: 10em 0;
|
||||
padding: 2em 0 0 0;
|
||||
}
|
||||
|
||||
#syncsetup-waiting-top {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
/* content scrollbars */
|
||||
|
@ -724,7 +724,7 @@ SyncEngine.prototype = {
|
||||
// Delete any existing data and reupload on bad version or missing meta.
|
||||
// No crypto component here...? We could regenerate per-collection keys...
|
||||
if (needsWipe) {
|
||||
this.wipeServer(true);
|
||||
this.wipeServer();
|
||||
}
|
||||
|
||||
// Save objects that need to be uploaded in this._modified. We also save
|
||||
@ -1248,7 +1248,10 @@ SyncEngine.prototype = {
|
||||
},
|
||||
|
||||
wipeServer: function wipeServer() {
|
||||
new Resource(this.engineURL).delete();
|
||||
let response = new Resource(this.engineURL).delete();
|
||||
if (response.status != 200 && response.status != 404) {
|
||||
throw response;
|
||||
}
|
||||
this._resetClient();
|
||||
},
|
||||
|
||||
|
@ -22,6 +22,7 @@
|
||||
* Marina Samuel <msamuel@mozilla.com>
|
||||
* Philipp von Weitershausen <philipp@weitershausen.de>
|
||||
* Chenxia Liu <liuche@mozilla.com>
|
||||
* 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
|
||||
@ -55,6 +56,12 @@ Cu.import("resource://services-sync/main.js"); // So we can get to Service fo
|
||||
let SyncScheduler = {
|
||||
_log: Log4Moz.repository.getLogger("Sync.SyncScheduler"),
|
||||
|
||||
_fatalLoginStatus: [LOGIN_FAILED_NO_USERNAME,
|
||||
LOGIN_FAILED_NO_PASSWORD,
|
||||
LOGIN_FAILED_NO_PASSPHRASE,
|
||||
LOGIN_FAILED_INVALID_PASSPHRASE,
|
||||
LOGIN_FAILED_LOGIN_REJECTED],
|
||||
|
||||
/**
|
||||
* The nsITimer object that schedules the next sync. See scheduleNextSync().
|
||||
*/
|
||||
@ -116,6 +123,7 @@ let SyncScheduler = {
|
||||
},
|
||||
|
||||
observe: function observe(subject, topic, data) {
|
||||
this._log.trace("Handling " + topic);
|
||||
switch(topic) {
|
||||
case "weave:engine:score:updated":
|
||||
if (Status.login == LOGIN_SUCCEEDED) {
|
||||
@ -163,7 +171,7 @@ let SyncScheduler = {
|
||||
}
|
||||
break;
|
||||
case "weave:engine:sync:error":
|
||||
// subject is the exception thrown by an engine's sync() method
|
||||
// `subject` is the exception thrown by an engine's sync() method.
|
||||
let exception = subject;
|
||||
if (exception.status >= 500 && exception.status <= 504) {
|
||||
this.requiresBackoff = true;
|
||||
@ -172,12 +180,16 @@ let SyncScheduler = {
|
||||
case "weave:service:login:error":
|
||||
this.clearSyncTriggers();
|
||||
|
||||
// Try again later, just as if we threw an error... only without the
|
||||
// error count.
|
||||
if (Status.login == MASTER_PASSWORD_LOCKED) {
|
||||
// Try again later, just as if we threw an error... only without the
|
||||
// error count.
|
||||
this._log.debug("Couldn't log in: master password is locked.");
|
||||
this._log.trace("Scheduling a sync at MASTER_PASSWORD_LOCKED_RETRY_INTERVAL");
|
||||
this.scheduleAtInterval(MASTER_PASSWORD_LOCKED_RETRY_INTERVAL);
|
||||
} else if (this._fatalLoginStatus.indexOf(Status.login) == -1) {
|
||||
// Not a fatal login error, just an intermittent network or server
|
||||
// issue. Keep on syncin'.
|
||||
this.checkSyncStatus();
|
||||
}
|
||||
break;
|
||||
case "weave:service:logout:finish":
|
||||
@ -449,6 +461,7 @@ let SyncScheduler = {
|
||||
* Deal with sync errors appropriately
|
||||
*/
|
||||
handleSyncError: function handleSyncError() {
|
||||
this._log.trace("In handleSyncError. Error count: " + this._syncErrors);
|
||||
this._syncErrors++;
|
||||
|
||||
// Do nothing on the first couple of failures, if we're not in
|
||||
@ -458,6 +471,8 @@ let SyncScheduler = {
|
||||
this.scheduleNextSync();
|
||||
return;
|
||||
}
|
||||
this._log.debug("Sync error count has exceeded " +
|
||||
MAX_ERROR_COUNT_BEFORE_BACKOFF + "; enforcing backoff.");
|
||||
Status.enforceBackoff = true;
|
||||
}
|
||||
|
||||
@ -469,7 +484,8 @@ let SyncScheduler = {
|
||||
* Remove any timers/observers that might trigger a sync
|
||||
*/
|
||||
clearSyncTriggers: function clearSyncTriggers() {
|
||||
this._log.debug("Clearing sync triggers.");
|
||||
this._log.debug("Clearing sync triggers and the global score.");
|
||||
this.globalScore = this.nextSync = 0;
|
||||
|
||||
// Clear out any scheduled syncs
|
||||
if (this.syncTimer)
|
||||
@ -521,6 +537,7 @@ let ErrorHandler = {
|
||||
},
|
||||
|
||||
observe: function observe(subject, topic, data) {
|
||||
this._log.trace("Handling " + topic);
|
||||
switch(topic) {
|
||||
case "weave:engine:sync:applied":
|
||||
if (subject.newFailed) {
|
||||
@ -569,6 +586,19 @@ let ErrorHandler = {
|
||||
this.dontIgnoreErrors = false;
|
||||
break;
|
||||
case "weave:service:sync:finish":
|
||||
this._log.trace("Status.service is " + Status.service);
|
||||
|
||||
// Check both of these status codes: in the event of a failure in one
|
||||
// engine, Status.service will be SYNC_FAILED_PARTIAL despite
|
||||
// Status.sync being SYNC_SUCCEEDED.
|
||||
// *facepalm*
|
||||
if (Status.sync == SYNC_SUCCEEDED &&
|
||||
Status.service == STATUS_OK) {
|
||||
// Great. Let's clear our mid-sync 401 note.
|
||||
this._log.trace("Clearing lastSyncReassigned.");
|
||||
Svc.Prefs.reset("lastSyncReassigned");
|
||||
}
|
||||
|
||||
if (Status.service == SYNC_FAILED_PARTIAL) {
|
||||
this._log.debug("Some engines did not sync correctly.");
|
||||
this.resetFileLog(Svc.Prefs.get("log.appender.file.logOnError"),
|
||||
@ -590,7 +620,12 @@ let ErrorHandler = {
|
||||
},
|
||||
|
||||
notifyOnNextTick: function notifyOnNextTick(topic) {
|
||||
Utils.nextTick(function() Svc.Obs.notify(topic));
|
||||
Utils.nextTick(function() {
|
||||
this._log.trace("Notifying " + topic +
|
||||
". Status.login is " + Status.login +
|
||||
". Status.sync is " + Status.sync);
|
||||
Svc.Obs.notify(topic);
|
||||
}, this);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -710,6 +745,7 @@ let ErrorHandler = {
|
||||
|
||||
shouldReportError: function shouldReportError() {
|
||||
if (Status.login == MASTER_PASSWORD_LOCKED) {
|
||||
this._log.trace("shouldReportError: false (master password locked).");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -721,8 +757,18 @@ let ErrorHandler = {
|
||||
if (lastSync && ((Date.now() - Date.parse(lastSync)) >
|
||||
Svc.Prefs.get("errorhandler.networkFailureReportTimeout") * 1000)) {
|
||||
Status.sync = PROLONGED_SYNC_FAILURE;
|
||||
this._log.trace("shouldReportError: true (prolonged sync failure).");
|
||||
return true;
|
||||
}
|
||||
|
||||
// We got a 401 mid-sync. Wait for the next sync before actually handling
|
||||
// an error. This assumes that we'll get a 401 again on a login fetch in
|
||||
// order to report the error.
|
||||
if (!Weave.Service.clusterURL) {
|
||||
this._log.trace("shouldReportError: false (no cluster URL; " +
|
||||
"possible node reassignment).");
|
||||
return false;
|
||||
}
|
||||
|
||||
return ([Status.login, Status.sync].indexOf(SERVER_MAINTENANCE) == -1 &&
|
||||
[Status.login, Status.sync].indexOf(LOGIN_FAILED_NETWORK_ERROR) == -1);
|
||||
@ -742,7 +788,24 @@ let ErrorHandler = {
|
||||
|
||||
case 401:
|
||||
Weave.Service.logout();
|
||||
Status.login = LOGIN_FAILED_LOGIN_REJECTED;
|
||||
this._log.info("Got 401 response; resetting clusterURL.");
|
||||
Svc.Prefs.reset("clusterURL");
|
||||
|
||||
let delay = 0;
|
||||
if (Svc.Prefs.get("lastSyncReassigned")) {
|
||||
// We got a 401 in the middle of the previous sync, and we just got
|
||||
// another. Login must have succeeded in order for us to get here, so
|
||||
// the password should be correct.
|
||||
// This is likely to be an intermittent server issue, so back off and
|
||||
// give it time to recover.
|
||||
this._log.warn("Last sync also failed for 401. Delaying next sync.");
|
||||
delay = MINIMUM_BACKOFF_INTERVAL;
|
||||
} else {
|
||||
this._log.debug("New mid-sync 401 failure. Making a note.");
|
||||
Svc.Prefs.set("lastSyncReassigned", true);
|
||||
}
|
||||
this._log.info("Attempting to schedule another sync.");
|
||||
SyncScheduler.scheduleNextSync(delay);
|
||||
break;
|
||||
|
||||
case 500:
|
||||
|
@ -270,7 +270,7 @@ AsyncResource.prototype = {
|
||||
_doRequest: function _doRequest(action, data, callback) {
|
||||
this._log.trace("In _doRequest.");
|
||||
this._callback = callback;
|
||||
let channel = this._channel = this._createRequest();
|
||||
let channel = this._createRequest();
|
||||
|
||||
if ("undefined" != typeof(data))
|
||||
this._data = data;
|
||||
@ -303,7 +303,7 @@ AsyncResource.prototype = {
|
||||
channel.asyncOpen(listener, null);
|
||||
},
|
||||
|
||||
_onComplete: function _onComplete(error, data) {
|
||||
_onComplete: function _onComplete(error, data, channel) {
|
||||
this._log.trace("In _onComplete. Error is " + error + ".");
|
||||
|
||||
if (error) {
|
||||
@ -312,7 +312,6 @@ AsyncResource.prototype = {
|
||||
}
|
||||
|
||||
this._data = data;
|
||||
let channel = this._channel;
|
||||
let action = channel.requestMethod;
|
||||
|
||||
this._log.trace("Channel: " + channel);
|
||||
@ -539,69 +538,29 @@ ChannelListener.prototype = {
|
||||
// Clear the abort timer now that the channel is done.
|
||||
this.abortTimer.clear();
|
||||
|
||||
/**
|
||||
* Shim to help investigate Bug 672878.
|
||||
* We seem to be seeing a situation in which onStopRequest is being called
|
||||
* with a success code, but no mResponseHead yet set (a failure condition).
|
||||
* One possibility, according to bzbarsky, is that the channel status and
|
||||
* the status argument don't match up. This block will log that situation.
|
||||
*
|
||||
* Fetching channel.status should not throw, but if it does requestStatus
|
||||
* will be NS_ERROR_UNEXPECTED, which will cause this method to report
|
||||
* failure.
|
||||
*/
|
||||
let requestStatus = Cr.NS_ERROR_UNEXPECTED;
|
||||
let statusSuccess = Components.isSuccessCode(status);
|
||||
try {
|
||||
// From nsIRequest.
|
||||
requestStatus = channel.status;
|
||||
this._log.trace("Request status is " + requestStatus);
|
||||
} catch (ex) {
|
||||
this._log.warn("Got exception " + Utils.exceptionStr(ex) +
|
||||
" fetching channel.status.");
|
||||
}
|
||||
if (statusSuccess && (status != requestStatus)) {
|
||||
this._log.error("Request status " + requestStatus +
|
||||
" does not match status arg " + status);
|
||||
try {
|
||||
channel.responseStatus;
|
||||
} catch (ex) {
|
||||
this._log.error("... and we got " + Utils.exceptionStr(ex) +
|
||||
" retrieving responseStatus.");
|
||||
}
|
||||
}
|
||||
|
||||
let requestStatusSuccess = Components.isSuccessCode(requestStatus);
|
||||
|
||||
let uri = channel && channel.URI && channel.URI.spec || "<unknown>";
|
||||
this._log.trace("Channel for " + channel.requestMethod + " " + uri + ": " +
|
||||
"isSuccessCode(" + status + ")? " + statusSuccess + ", " +
|
||||
"isSuccessCode(" + requestStatus + ")? " +
|
||||
requestStatusSuccess);
|
||||
"isSuccessCode(" + status + ")? " + statusSuccess);
|
||||
|
||||
if (this._data == '') {
|
||||
this._data = null;
|
||||
}
|
||||
|
||||
// Check both the nsIRequest status and the status we were provided.
|
||||
// If either is unsuccessful, don't take the success branch!
|
||||
//
|
||||
// Pass back 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 (!statusSuccess || !requestStatusSuccess) {
|
||||
// Pick the code that caused us to fail.
|
||||
let code = statusSuccess ? requestStatus : status;
|
||||
let message = Components.Exception("", code).name;
|
||||
let error = Components.Exception(message, code);
|
||||
this._onComplete(error);
|
||||
if (!statusSuccess) {
|
||||
let message = Components.Exception("", status).name;
|
||||
let error = Components.Exception(message, status);
|
||||
this._onComplete(error, undefined, channel);
|
||||
return;
|
||||
}
|
||||
|
||||
this._log.trace("Channel: flags = " + channel.loadFlags +
|
||||
", URI = " + uri +
|
||||
", HTTP success? " + channel.requestSucceeded);
|
||||
this._onComplete(null, this._data);
|
||||
this._onComplete(null, this._data, channel);
|
||||
},
|
||||
|
||||
onDataAvailable: function Channel_onDataAvail(req, cb, stream, off, count) {
|
||||
|
@ -370,6 +370,9 @@ RESTRequest.prototype = {
|
||||
/*** nsIStreamListener ***/
|
||||
|
||||
onStartRequest: function onStartRequest(channel) {
|
||||
// Update the channel in case we got redirected.
|
||||
this.channel = channel;
|
||||
|
||||
if (this.status == this.ABORTED) {
|
||||
this._log.trace("Not proceeding with onStartRequest, request was aborted.");
|
||||
return;
|
||||
@ -394,6 +397,9 @@ RESTRequest.prototype = {
|
||||
},
|
||||
|
||||
onStopRequest: function onStopRequest(channel, context, statusCode) {
|
||||
// Update the channel in case we got redirected.
|
||||
this.channel = channel;
|
||||
|
||||
if (this.timeoutTimer) {
|
||||
// Clear the abort timer now that the channel is done.
|
||||
this.timeoutTimer.clear();
|
||||
@ -406,42 +412,7 @@ RESTRequest.prototype = {
|
||||
}
|
||||
this.status = this.COMPLETED;
|
||||
|
||||
/**
|
||||
* Shim to help investigate Bug 672878.
|
||||
* We seem to be seeing a situation in which onStopRequest is being called
|
||||
* with a success code, but no mResponseHead yet set (a failure condition).
|
||||
* One possibility, according to bzbarsky, is that the channel status and
|
||||
* the status argument don't match up. This block will log that situation.
|
||||
*
|
||||
* Fetching channel.status should not throw, but if it does requestStatus
|
||||
* will be NS_ERROR_UNEXPECTED, which will cause this method to report
|
||||
* failure.
|
||||
*
|
||||
* This code parallels nearly identical shimming in resource.js.
|
||||
*/
|
||||
let requestStatus = Cr.NS_ERROR_UNEXPECTED;
|
||||
let statusSuccess = Components.isSuccessCode(statusCode);
|
||||
try {
|
||||
// From nsIRequest.
|
||||
requestStatus = channel.status;
|
||||
this._log.trace("Request status is " + requestStatus);
|
||||
} catch (ex) {
|
||||
this._log.warn("Got exception " + Utils.exceptionStr(ex) +
|
||||
" fetching channel.status.");
|
||||
}
|
||||
if (statusSuccess && (statusCode != requestStatus)) {
|
||||
this._log.error("Request status " + requestStatus +
|
||||
" does not match status arg " + statusCode);
|
||||
try {
|
||||
channel.responseStatus;
|
||||
} catch (ex) {
|
||||
this._log.error("... and we got " + Utils.exceptionStr(ex) +
|
||||
" retrieving responseStatus.");
|
||||
}
|
||||
}
|
||||
|
||||
let requestStatusSuccess = Components.isSuccessCode(requestStatus);
|
||||
|
||||
let uri = channel && channel.URI && channel.URI.spec || "<unknown>";
|
||||
this._log.trace("Channel for " + channel.requestMethod + " " + uri +
|
||||
" returned status code " + statusCode);
|
||||
@ -449,7 +420,7 @@ RESTRequest.prototype = {
|
||||
// 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 (!statusSuccess || !requestStatusSuccess) {
|
||||
if (!statusSuccess) {
|
||||
let message = Components.Exception("", statusCode).name;
|
||||
let error = Components.Exception(message, statusCode);
|
||||
this.onComplete(error);
|
||||
|
@ -523,8 +523,10 @@ WeaveSvc.prototype = {
|
||||
return this.serverURL;
|
||||
case 0:
|
||||
case 200:
|
||||
if (node == "null")
|
||||
if (node == "null") {
|
||||
node = null;
|
||||
}
|
||||
this._log.trace("_findCluster successfully returning " + node);
|
||||
return node;
|
||||
default:
|
||||
ErrorHandler.checkServerError(node);
|
||||
@ -702,8 +704,11 @@ WeaveSvc.prototype = {
|
||||
return true;
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
} catch (ex) {
|
||||
// This means no keys are present, or there's a network error.
|
||||
this._log.debug("Failed to fetch and verify keys: "
|
||||
+ Utils.exceptionStr(ex));
|
||||
ErrorHandler.checkServerError(ex);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
@ -731,9 +736,9 @@ WeaveSvc.prototype = {
|
||||
}
|
||||
|
||||
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
|
||||
// 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");
|
||||
@ -826,6 +831,7 @@ WeaveSvc.prototype = {
|
||||
let uploadRes = wbo.upload(this.cryptoKeysURL);
|
||||
if (uploadRes.status != 200) {
|
||||
this._log.warn("Got status " + uploadRes.status + " uploading new keys. What to do? Throw!");
|
||||
ErrorHandler.checkServerError(uploadRes);
|
||||
throw new Error("Unable to upload symmetric keys.");
|
||||
}
|
||||
this._log.info("Got status " + uploadRes.status + " uploading keys.");
|
||||
@ -915,6 +921,7 @@ WeaveSvc.prototype = {
|
||||
}))(),
|
||||
|
||||
startOver: function() {
|
||||
this._log.trace("Invoking Service.startOver.");
|
||||
Svc.Obs.notify("weave:engine:stop-tracking");
|
||||
Status.resetSync();
|
||||
|
||||
@ -1367,7 +1374,14 @@ WeaveSvc.prototype = {
|
||||
}
|
||||
|
||||
// Update engines because it might change what we sync.
|
||||
this._updateEnabledEngines();
|
||||
try {
|
||||
this._updateEnabledEngines();
|
||||
} catch (ex) {
|
||||
this._log.debug("Updating enabled engines failed: " +
|
||||
Utils.exceptionStr(ex));
|
||||
ErrorHandler.checkServerError(ex);
|
||||
throw ex;
|
||||
}
|
||||
|
||||
try {
|
||||
for each (let engine in Engines.getEnabled()) {
|
||||
@ -1480,22 +1494,18 @@ WeaveSvc.prototype = {
|
||||
_syncEngine: function WeaveSvc__syncEngine(engine) {
|
||||
try {
|
||||
engine.sync();
|
||||
return true;
|
||||
}
|
||||
catch(e) {
|
||||
// Maybe a 401, cluster update perhaps needed?
|
||||
if (e.status == 401) {
|
||||
// Log out and clear the cluster URL pref. That will make us perform
|
||||
// cluster detection and password check on next sync, which handles
|
||||
// both causes of 401s; in either case, we won't proceed with this
|
||||
// sync so return false, but kick off a sync for next time.
|
||||
this.logout();
|
||||
Svc.Prefs.reset("clusterURL");
|
||||
Utils.nextTick(this.sync, this);
|
||||
// Maybe a 401, cluster update perhaps needed?
|
||||
// We rely on ErrorHandler observing the sync failure notification to
|
||||
// schedule another sync and clear node assignment values.
|
||||
// Here we simply want to muffle the exception and return an
|
||||
// appropriate value.
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1555,22 +1565,34 @@ WeaveSvc.prototype = {
|
||||
CollectionKeys.clear();
|
||||
this.upgradeSyncKey(this.syncID);
|
||||
|
||||
// Wipe the server.
|
||||
let wipeTimestamp = this.wipeServer();
|
||||
|
||||
// Upload a new meta/global record.
|
||||
let meta = new WBORecord("meta", "global");
|
||||
meta.payload.syncID = this.syncID;
|
||||
meta.payload.storageVersion = STORAGE_VERSION;
|
||||
meta.isNew = true;
|
||||
|
||||
this._log.debug("New metadata record: " + JSON.stringify(meta.payload));
|
||||
let resp = new Resource(this.metaURL).put(meta);
|
||||
if (!resp.success)
|
||||
let res = new Resource(this.metaURL);
|
||||
// It would be good to set the X-If-Unmodified-Since header to `timestamp`
|
||||
// for this PUT to ensure at least some level of transactionality.
|
||||
// Unfortunately, the servers don't support it after a wipe right now
|
||||
// (bug 693893), so we're going to defer this until bug 692700.
|
||||
let resp = res.put(meta);
|
||||
if (!resp.success) {
|
||||
// If we got into a race condition, we'll abort the sync this way, too.
|
||||
// That's fine. We'll just wait till the next sync. The client that we're
|
||||
// racing is probably busy uploading stuff right now anyway.
|
||||
throw resp;
|
||||
}
|
||||
Records.set(this.metaURL, meta);
|
||||
|
||||
// Wipe everything we know about except meta because we just uploaded it
|
||||
let collections = [Clients].concat(Engines.getAll()).map(function(engine) {
|
||||
return engine.name;
|
||||
});
|
||||
this.wipeServer(collections);
|
||||
|
||||
// Generate, upload, and download new keys. Do this last so we don't wipe
|
||||
// them...
|
||||
@ -1581,36 +1603,51 @@ WeaveSvc.prototype = {
|
||||
* Wipe user data from the server.
|
||||
*
|
||||
* @param collections [optional]
|
||||
* Array of collections to wipe. If not given, all collections are wiped.
|
||||
* Array of collections to wipe. If not given, all collections are
|
||||
* wiped by issuing a DELETE request for `storageURL`.
|
||||
*
|
||||
* @param includeKeys [optional]
|
||||
* If true, keys/pubkey and keys/privkey are deleted from the server.
|
||||
* This is false by default, which will cause the usual upgrade paths
|
||||
* to leave those keys on the server. This is to solve Bug 614737: old
|
||||
* clients check for keys *before* checking storage versions.
|
||||
*
|
||||
* Note that this parameter only has an effect if `collections` is not
|
||||
* passed. If you explicitly pass a list of collections, they will be
|
||||
* processed regardless of the value of `includeKeys`.
|
||||
* @return the server's timestamp of the (last) DELETE.
|
||||
*/
|
||||
wipeServer: function wipeServer(collections, includeKeyPairs)
|
||||
wipeServer: function wipeServer(collections)
|
||||
this._notify("wipe-server", "", function() {
|
||||
let response;
|
||||
if (!collections) {
|
||||
collections = [];
|
||||
let info = new Resource(this.infoURL).get();
|
||||
for (let name in info.obj) {
|
||||
if (includeKeyPairs || (name != "keys"))
|
||||
collections.push(name);
|
||||
// Strip the trailing slash.
|
||||
let res = new Resource(this.storageURL.slice(0, -1));
|
||||
res.setHeader("X-Confirm-Delete", "1");
|
||||
try {
|
||||
response = res.delete();
|
||||
} catch (ex) {
|
||||
this._log.debug("Failed to wipe server: " + Utils.exceptionStr(ex));
|
||||
throw ex;
|
||||
}
|
||||
if (response.status != 200 && response.status != 404) {
|
||||
this._log.debug("Aborting wipeServer. Server responded with " +
|
||||
response.status + " response for " + this.storageURL);
|
||||
throw response;
|
||||
}
|
||||
return response.headers["x-weave-timestamp"];
|
||||
}
|
||||
let timestamp;
|
||||
for each (let name in collections) {
|
||||
let url = this.storageURL + name;
|
||||
let response = new Resource(url).delete();
|
||||
try {
|
||||
response = new Resource(url).delete();
|
||||
} catch (ex) {
|
||||
this._log.debug("Failed to wipe '" + name + "' collection: " +
|
||||
Utils.exceptionStr(ex));
|
||||
throw ex;
|
||||
}
|
||||
if (response.status != 200 && response.status != 404) {
|
||||
throw "Aborting wipeServer. Server responded with "
|
||||
+ response.status + " response for " + url;
|
||||
this._log.debug("Aborting wipeServer. Server responded with " +
|
||||
response.status + " response for " + url);
|
||||
throw response;
|
||||
}
|
||||
if ("x-weave-timestamp" in response.headers) {
|
||||
timestamp = response.headers["x-weave-timestamp"];
|
||||
}
|
||||
}
|
||||
return timestamp;
|
||||
})(),
|
||||
|
||||
/**
|
||||
@ -1620,7 +1657,7 @@ WeaveSvc.prototype = {
|
||||
* Array of engine names to wipe. If not given, all engines are used.
|
||||
*/
|
||||
wipeClient: function WeaveSvc_wipeClient(engines)
|
||||
this._catch(this._notify("wipe-client", "", function() {
|
||||
this._notify("wipe-client", "", function() {
|
||||
// If we don't have any engines, reset the service and wipe all engines
|
||||
if (!engines) {
|
||||
// Clear out any service data
|
||||
@ -1639,7 +1676,7 @@ WeaveSvc.prototype = {
|
||||
|
||||
// Save the password/passphrase just in-case they aren't restored by sync
|
||||
this.persistLogin();
|
||||
}))(),
|
||||
})(),
|
||||
|
||||
/**
|
||||
* Wipe all remote user data by wiping the server then telling each remote
|
||||
@ -1648,8 +1685,8 @@ WeaveSvc.prototype = {
|
||||
* @param engines [optional]
|
||||
* Array of engine names to wipe. If not given, all engines are used.
|
||||
*/
|
||||
wipeRemote: function WeaveSvc_wipeRemote(engines)
|
||||
this._catch(this._notify("wipe-remote", "", function() {
|
||||
wipeRemote: function wipeRemote(engines) {
|
||||
try {
|
||||
// Make sure stuff gets uploaded.
|
||||
this.resetClient(engines);
|
||||
|
||||
@ -1667,7 +1704,11 @@ WeaveSvc.prototype = {
|
||||
|
||||
// Make sure the changed clients get updated.
|
||||
Clients.sync();
|
||||
}))(),
|
||||
} catch (ex) {
|
||||
ErrorHandler.checkServerError(ex);
|
||||
throw ex;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Reset local service information like logs, sync times, caches.
|
||||
@ -1678,7 +1719,6 @@ WeaveSvc.prototype = {
|
||||
|
||||
// Pretend we've never synced to the server and drop cached data
|
||||
this.syncID = "";
|
||||
Svc.Prefs.reset("lastSync");
|
||||
Records.clearCache();
|
||||
}))(),
|
||||
|
||||
|
@ -1092,6 +1092,9 @@ let Utils = {
|
||||
// If Master Password is enabled and locked, present a dialog to unlock it.
|
||||
// Return whether the system is unlocked.
|
||||
ensureMPUnlocked: function ensureMPUnlocked() {
|
||||
if (!Utils.mpLocked()) {
|
||||
return true;
|
||||
}
|
||||
let sdr = Cc["@mozilla.org/security/sdr;1"]
|
||||
.getService(Ci.nsISecretDecoderRing);
|
||||
try {
|
||||
|
@ -17,6 +17,23 @@ let provider = {
|
||||
};
|
||||
Services.dirsvc.QueryInterface(Ci.nsIDirectoryService).registerProvider(provider);
|
||||
|
||||
let timer;
|
||||
function waitForZeroTimer(callback) {
|
||||
// First wait >100ms (nsITimers can take up to that much time to fire, so
|
||||
// we can account for the timer in delayedAutoconnect) and then two event
|
||||
// loop ticks (to account for the Utils.nextTick() in autoConnect).
|
||||
let ticks = 2;
|
||||
function wait() {
|
||||
if (ticks) {
|
||||
ticks -= 1;
|
||||
Utils.nextTick(wait);
|
||||
return;
|
||||
}
|
||||
callback();
|
||||
}
|
||||
timer = Utils.namedTimer(wait, 150, {}, "timer");
|
||||
}
|
||||
|
||||
btoa = Cu.import("resource://services-sync/log4moz.js").btoa;
|
||||
function getTestLogger(component) {
|
||||
return Log4Moz.repository.getLogger("Testing");
|
||||
|
@ -1,3 +1,5 @@
|
||||
const Cm = Components.manager;
|
||||
|
||||
// Shared logging for all HTTP server functions.
|
||||
Cu.import("resource://services-sync/log4moz.js");
|
||||
const SYNC_HTTP_LOGGER = "Sync.Test.Server";
|
||||
@ -10,6 +12,17 @@ function new_timestamp() {
|
||||
return Math.round(Date.now() / 10) / 100;
|
||||
}
|
||||
|
||||
function return_timestamp(request, response, timestamp) {
|
||||
if (!timestamp) {
|
||||
timestamp = new_timestamp();
|
||||
}
|
||||
let body = "" + timestamp;
|
||||
response.setHeader("X-Weave-Timestamp", body);
|
||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||
response.bodyOutputStream.write(body, body.length);
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
function httpd_setup (handlers) {
|
||||
let server = new nsHttpServer();
|
||||
let port = 8080;
|
||||
@ -608,7 +621,7 @@ SyncServer.prototype = {
|
||||
* subject to change: see Bug 650435.
|
||||
*/
|
||||
timestamp: function timestamp() {
|
||||
return Math.round(Date.now() / 10) / 100;
|
||||
return new_timestamp();
|
||||
},
|
||||
|
||||
/**
|
||||
@ -988,3 +1001,51 @@ function serverForUsers(users, contents, callback) {
|
||||
server.start();
|
||||
return server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Proxy auth helpers.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Fake a PAC to prompt a channel replacement.
|
||||
*/
|
||||
let PACSystemSettings = {
|
||||
CID: Components.ID("{5645d2c1-d6d8-4091-b117-fe7ee4027db7}"),
|
||||
contractID: "@mozilla.org/system-proxy-settings;1",
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory,
|
||||
Ci.nsISystemProxySettings]),
|
||||
|
||||
createInstance: function createInstance(outer, iid) {
|
||||
if (outer) {
|
||||
throw Cr.NS_ERROR_NO_AGGREGATION;
|
||||
}
|
||||
return this.QueryInterface(iid);
|
||||
},
|
||||
|
||||
lockFactory: function lockFactory(lock) {
|
||||
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
|
||||
},
|
||||
|
||||
// Replace this URI for each test to avoid caching. We want to ensure that
|
||||
// each test gets a completely fresh setup.
|
||||
PACURI: null,
|
||||
getProxyForURI: function getProxyForURI(aURI) {
|
||||
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
};
|
||||
|
||||
function installFakePAC() {
|
||||
_("Installing fake PAC.");
|
||||
Cm.nsIComponentRegistrar
|
||||
.registerFactory(PACSystemSettings.CID,
|
||||
"Fake system proxy-settings",
|
||||
PACSystemSettings.contractID,
|
||||
PACSystemSettings);
|
||||
}
|
||||
|
||||
function uninstallFakePAC() {
|
||||
_("Uninstalling fake PAC.");
|
||||
let CID = PACSystemSettings.CID;
|
||||
Cm.nsIComponentRegistrar.unregisterFactory(CID, PACSystemSettings);
|
||||
}
|
||||
|
@ -20,8 +20,7 @@ const NON_PROLONGED_ERROR_DURATION =
|
||||
(Svc.Prefs.get('errorhandler.networkFailureReportTimeout') / 2) * 1000;
|
||||
|
||||
function setLastSync(lastSyncValue) {
|
||||
Svc.Prefs.set("lastSync", (new Date(Date.now() -
|
||||
lastSyncValue)).toString());
|
||||
Svc.Prefs.set("lastSync", (new Date(Date.now() - lastSyncValue)).toString());
|
||||
}
|
||||
|
||||
function CatapultEngine() {
|
||||
@ -31,7 +30,9 @@ CatapultEngine.prototype = {
|
||||
__proto__: SyncEngine.prototype,
|
||||
exception: null, // tests fill this in
|
||||
_sync: function _sync() {
|
||||
throw this.exception;
|
||||
if (this.exception) {
|
||||
throw this.exception;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -69,7 +70,9 @@ function sync_httpd_setup() {
|
||||
syncID: Service.syncID,
|
||||
storageVersion: STORAGE_VERSION,
|
||||
engines: {clients: {version: Clients.version,
|
||||
syncID: Clients.syncID}}
|
||||
syncID: Clients.syncID},
|
||||
catapult: {version: Engines.get("catapult").version,
|
||||
syncID: Engines.get("catapult").syncID}}
|
||||
});
|
||||
let clientsColl = new ServerCollection({}, true);
|
||||
|
||||
@ -79,23 +82,37 @@ function sync_httpd_setup() {
|
||||
|
||||
let handler_401 = httpd_handler(401, "Unauthorized");
|
||||
return httpd_setup({
|
||||
// Normal server behaviour.
|
||||
"/1.1/johndoe/storage/meta/global": upd("meta", global.handler()),
|
||||
"/1.1/johndoe/info/collections": collectionsHelper.handler,
|
||||
"/1.1/johndoe/storage/crypto/keys":
|
||||
upd("crypto", (new ServerWBO("keys")).handler()),
|
||||
"/1.1/johndoe/storage/clients": upd("clients", clientsColl.handler()),
|
||||
|
||||
// Credentials are wrong or node reallocated.
|
||||
"/1.1/janedoe/storage/meta/global": handler_401,
|
||||
"/1.1/janedoe/info/collections": handler_401,
|
||||
|
||||
"/maintenance/1.1/johnsmith/info/collections": service_unavailable,
|
||||
// Maintenance or overloaded (503 + Retry-After) at info/collections.
|
||||
"/maintenance/1.1/broken.info/info/collections": service_unavailable,
|
||||
|
||||
"/maintenance/1.1/janesmith/storage/meta/global": service_unavailable,
|
||||
"/maintenance/1.1/janesmith/info/collections": collectionsHelper.handler,
|
||||
// Maintenance or overloaded (503 + Retry-After) at meta/global.
|
||||
"/maintenance/1.1/broken.meta/storage/meta/global": service_unavailable,
|
||||
"/maintenance/1.1/broken.meta/info/collections": collectionsHelper.handler,
|
||||
|
||||
"/maintenance/1.1/foo/storage/meta/global": upd("meta", global.handler()),
|
||||
"/maintenance/1.1/foo/info/collections": collectionsHelper.handler,
|
||||
"/maintenance/1.1/foo/storage/crypto/keys": service_unavailable,
|
||||
// Maintenance or overloaded (503 + Retry-After) at crypto/keys.
|
||||
"/maintenance/1.1/broken.keys/storage/meta/global": upd("meta", global.handler()),
|
||||
"/maintenance/1.1/broken.keys/info/collections": collectionsHelper.handler,
|
||||
"/maintenance/1.1/broken.keys/storage/crypto/keys": service_unavailable,
|
||||
|
||||
// Maintenance or overloaded (503 + Retry-After) at wiping collection.
|
||||
"/maintenance/1.1/broken.wipe/info/collections": collectionsHelper.handler,
|
||||
"/maintenance/1.1/broken.wipe/storage/meta/global": upd("meta", global.handler()),
|
||||
"/maintenance/1.1/broken.wipe/storage/crypto/keys":
|
||||
upd("crypto", (new ServerWBO("keys")).handler()),
|
||||
"/maintenance/1.1/broken.wipe/storage": service_unavailable,
|
||||
"/maintenance/1.1/broken.wipe/storage/clients": upd("clients", clientsColl.handler()),
|
||||
"/maintenance/1.1/broken.wipe/storage/catapult": service_unavailable
|
||||
});
|
||||
}
|
||||
|
||||
@ -104,7 +121,10 @@ function setUp() {
|
||||
Service.password = "ilovejane";
|
||||
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
|
||||
Service.clusterURL = "http://localhost:8080/";
|
||||
return generateAndUploadKeys();
|
||||
}
|
||||
|
||||
function generateAndUploadKeys() {
|
||||
generateNewKeys();
|
||||
let serverKeys = CollectionKeys.asWBO("crypto", "keys");
|
||||
serverKeys.encrypt(Service.syncKeyBundle);
|
||||
@ -126,16 +146,34 @@ add_test(function test_401_logout() {
|
||||
do_check_eq(Status.sync, SYNC_SUCCEEDED);
|
||||
do_check_true(Service.isLoggedIn);
|
||||
|
||||
Svc.Obs.add("weave:service:sync:error", onSyncError);
|
||||
function onSyncError() {
|
||||
_("Got weave:service:sync:error in first sync.");
|
||||
Svc.Obs.remove("weave:service:sync:error", onSyncError);
|
||||
|
||||
// Wait for the automatic next sync.
|
||||
function onLoginError() {
|
||||
_("Got weave:service:login:error in second sync.");
|
||||
Svc.Obs.remove("weave:service:login:error", onLoginError);
|
||||
|
||||
do_check_eq(Status.login, LOGIN_FAILED_LOGIN_REJECTED);
|
||||
do_check_false(Service.isLoggedIn);
|
||||
|
||||
// Clean up.
|
||||
Utils.nextTick(function () {
|
||||
Service.startOver();
|
||||
server.stop(run_next_test);
|
||||
});
|
||||
}
|
||||
Svc.Obs.add("weave:service:login:error", onLoginError);
|
||||
}
|
||||
|
||||
// Make sync fail due to login rejected.
|
||||
Service.username = "janedoe";
|
||||
|
||||
_("Starting first sync.");
|
||||
Service.sync();
|
||||
|
||||
do_check_eq(Status.login, LOGIN_FAILED_LOGIN_REJECTED);
|
||||
do_check_false(Service.isLoggedIn);
|
||||
|
||||
// Clean up.
|
||||
Service.startOver();
|
||||
server.stop(run_next_test);
|
||||
_("First sync done.");
|
||||
});
|
||||
|
||||
add_test(function test_credentials_changed_logout() {
|
||||
@ -178,6 +216,10 @@ add_test(function test_shouldReportError() {
|
||||
Status.login = MASTER_PASSWORD_LOCKED;
|
||||
do_check_false(ErrorHandler.shouldReportError());
|
||||
|
||||
// Give ourselves a clusterURL so that the temporary 401 no-error situation
|
||||
// doesn't come into play.
|
||||
Service.clusterURL = "http://localhost:8080/";
|
||||
|
||||
// Test dontIgnoreErrors, non-network, non-prolonged, login error reported
|
||||
Status.resetSync();
|
||||
setLastSync(NON_PROLONGED_ERROR_DURATION);
|
||||
@ -733,9 +775,9 @@ add_test(function test_info_collections_login_server_maintenance_error() {
|
||||
let server = sync_httpd_setup();
|
||||
setUp();
|
||||
|
||||
Service.username = "broken.info";
|
||||
Service.clusterURL = "http://localhost:8080/maintenance/";
|
||||
|
||||
Service.username = "johnsmith";
|
||||
let backoffInterval;
|
||||
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
|
||||
Svc.Obs.remove("weave:service:backoff:interval", observe);
|
||||
@ -772,9 +814,9 @@ add_test(function test_meta_global_login_server_maintenance_error() {
|
||||
let server = sync_httpd_setup();
|
||||
setUp();
|
||||
|
||||
Service.username = "broken.meta";
|
||||
Service.clusterURL = "http://localhost:8080/maintenance/";
|
||||
|
||||
Service.username = "janesmith";
|
||||
let backoffInterval;
|
||||
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
|
||||
Svc.Obs.remove("weave:service:backoff:interval", observe);
|
||||
@ -811,8 +853,8 @@ add_test(function test_crypto_keys_login_server_maintenance_error() {
|
||||
let server = sync_httpd_setup();
|
||||
setUp();
|
||||
|
||||
Service.username = "broken.keys";
|
||||
Service.clusterURL = "http://localhost:8080/maintenance/";
|
||||
Service.username = "foo";
|
||||
// Force re-download of keys
|
||||
CollectionKeys.clear();
|
||||
|
||||
@ -878,9 +920,9 @@ add_test(function test_info_collections_login_prolonged_server_maintenance_error
|
||||
let server = sync_httpd_setup();
|
||||
setUp();
|
||||
|
||||
Service.username = "broken.info";
|
||||
Service.clusterURL = "http://localhost:8080/maintenance/";
|
||||
|
||||
Service.username = "johnsmith";
|
||||
let backoffInterval;
|
||||
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
|
||||
Svc.Obs.remove("weave:service:backoff:interval", observe);
|
||||
@ -910,9 +952,9 @@ add_test(function test_meta_global_login_prolonged_server_maintenance_error(){
|
||||
let server = sync_httpd_setup();
|
||||
setUp();
|
||||
|
||||
Service.username = "broken.meta";
|
||||
Service.clusterURL = "http://localhost:8080/maintenance/";
|
||||
|
||||
Service.username = "janesmith";
|
||||
let backoffInterval;
|
||||
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
|
||||
Svc.Obs.remove("weave:service:backoff:interval", observe);
|
||||
@ -937,13 +979,13 @@ add_test(function test_meta_global_login_prolonged_server_maintenance_error(){
|
||||
Service.sync();
|
||||
});
|
||||
|
||||
add_test(function test_crypto_keys_login_prolonged_server_maintenance_error(){
|
||||
add_test(function test_download_crypto_keys_login_prolonged_server_maintenance_error(){
|
||||
// Test crypto/keys prolonged server maintenance errors are reported.
|
||||
let server = sync_httpd_setup();
|
||||
setUp();
|
||||
|
||||
Service.username = "broken.keys";
|
||||
Service.clusterURL = "http://localhost:8080/maintenance/";
|
||||
Service.username = "foo";
|
||||
// Force re-download of keys
|
||||
CollectionKeys.clear();
|
||||
|
||||
@ -971,6 +1013,116 @@ add_test(function test_crypto_keys_login_prolonged_server_maintenance_error(){
|
||||
Service.sync();
|
||||
});
|
||||
|
||||
add_test(function test_upload_crypto_keys_login_prolonged_server_maintenance_error(){
|
||||
// Test crypto/keys prolonged server maintenance errors are reported.
|
||||
let server = sync_httpd_setup();
|
||||
|
||||
// Start off with an empty account, do not upload a key.
|
||||
Service.username = "broken.keys";
|
||||
Service.password = "ilovejane";
|
||||
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
|
||||
Service.clusterURL = "http://localhost:8080/maintenance/";
|
||||
|
||||
let backoffInterval;
|
||||
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
|
||||
Svc.Obs.remove("weave:service:backoff:interval", observe);
|
||||
backoffInterval = subject;
|
||||
});
|
||||
|
||||
Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
|
||||
Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
|
||||
do_check_true(Status.enforceBackoff);
|
||||
do_check_eq(backoffInterval, 42);
|
||||
do_check_eq(Status.service, SYNC_FAILED);
|
||||
do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE);
|
||||
|
||||
clean();
|
||||
server.stop(run_next_test);
|
||||
});
|
||||
|
||||
do_check_false(Status.enforceBackoff);
|
||||
do_check_eq(Status.service, STATUS_OK);
|
||||
|
||||
setLastSync(PROLONGED_ERROR_DURATION);
|
||||
Service.sync();
|
||||
});
|
||||
|
||||
add_test(function test_wipeServer_login_prolonged_server_maintenance_error(){
|
||||
// Test that we report prolonged server maintenance errors that occur whilst
|
||||
// wiping the server.
|
||||
let server = sync_httpd_setup();
|
||||
|
||||
// Start off with an empty account, do not upload a key.
|
||||
Service.username = "broken.wipe";
|
||||
Service.password = "ilovejane";
|
||||
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
|
||||
Service.clusterURL = "http://localhost:8080/maintenance/";
|
||||
|
||||
let backoffInterval;
|
||||
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
|
||||
Svc.Obs.remove("weave:service:backoff:interval", observe);
|
||||
backoffInterval = subject;
|
||||
});
|
||||
|
||||
Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
|
||||
Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
|
||||
do_check_true(Status.enforceBackoff);
|
||||
do_check_eq(backoffInterval, 42);
|
||||
do_check_eq(Status.service, SYNC_FAILED);
|
||||
do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE);
|
||||
|
||||
clean();
|
||||
server.stop(run_next_test);
|
||||
});
|
||||
|
||||
do_check_false(Status.enforceBackoff);
|
||||
do_check_eq(Status.service, STATUS_OK);
|
||||
|
||||
setLastSync(PROLONGED_ERROR_DURATION);
|
||||
Service.sync();
|
||||
});
|
||||
|
||||
add_test(function test_wipeRemote_prolonged_server_maintenance_error(){
|
||||
// Test that we report prolonged server maintenance errors that occur whilst
|
||||
// wiping all remote devices.
|
||||
let server = sync_httpd_setup();
|
||||
|
||||
Service.username = "broken.wipe";
|
||||
Service.password = "ilovejane";
|
||||
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
|
||||
Service.clusterURL = "http://localhost:8080/maintenance/";
|
||||
generateAndUploadKeys();
|
||||
|
||||
let engine = Engines.get("catapult");
|
||||
engine.exception = null;
|
||||
engine.enabled = true;
|
||||
|
||||
let backoffInterval;
|
||||
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
|
||||
Svc.Obs.remove("weave:service:backoff:interval", observe);
|
||||
backoffInterval = subject;
|
||||
});
|
||||
|
||||
Svc.Obs.add("weave:ui:sync:error", function onUIUpdate() {
|
||||
Svc.Obs.remove("weave:ui:sync:error", onUIUpdate);
|
||||
do_check_true(Status.enforceBackoff);
|
||||
do_check_eq(backoffInterval, 42);
|
||||
do_check_eq(Status.service, SYNC_FAILED);
|
||||
do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE);
|
||||
do_check_eq(Svc.Prefs.get("firstSync"), "wipeRemote");
|
||||
|
||||
clean();
|
||||
server.stop(run_next_test);
|
||||
});
|
||||
|
||||
do_check_false(Status.enforceBackoff);
|
||||
do_check_eq(Status.service, STATUS_OK);
|
||||
|
||||
Svc.Prefs.set("firstSync", "wipeRemote");
|
||||
setLastSync(PROLONGED_ERROR_DURATION);
|
||||
Service.sync();
|
||||
});
|
||||
|
||||
add_test(function test_sync_syncAndReportErrors_server_maintenance_error() {
|
||||
// Test server maintenance errors are reported
|
||||
// when calling syncAndReportErrors.
|
||||
@ -1004,9 +1156,9 @@ add_test(function test_info_collections_login_syncAndReportErrors_server_mainten
|
||||
let server = sync_httpd_setup();
|
||||
setUp();
|
||||
|
||||
Service.username = "broken.info";
|
||||
Service.clusterURL = "http://localhost:8080/maintenance/";
|
||||
|
||||
Service.username = "johnsmith";
|
||||
let backoffInterval;
|
||||
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
|
||||
Svc.Obs.remove("weave:service:backoff:interval", observe);
|
||||
@ -1037,9 +1189,9 @@ add_test(function test_meta_global_login_syncAndReportErrors_server_maintenance_
|
||||
let server = sync_httpd_setup();
|
||||
setUp();
|
||||
|
||||
Service.username = "broken.meta";
|
||||
Service.clusterURL = "http://localhost:8080/maintenance/";
|
||||
|
||||
Service.username = "janesmith";
|
||||
let backoffInterval;
|
||||
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
|
||||
Svc.Obs.remove("weave:service:backoff:interval", observe);
|
||||
@ -1064,14 +1216,14 @@ add_test(function test_meta_global_login_syncAndReportErrors_server_maintenance_
|
||||
ErrorHandler.syncAndReportErrors();
|
||||
});
|
||||
|
||||
add_test(function test_crypto_keys_login_syncAndReportErrors_server_maintenance_error() {
|
||||
add_test(function test_download_crypto_keys_login_syncAndReportErrors_server_maintenance_error() {
|
||||
// Test crypto/keys server maintenance errors are reported
|
||||
// when calling syncAndReportErrors.
|
||||
let server = sync_httpd_setup();
|
||||
setUp();
|
||||
|
||||
Service.username = "broken.keys";
|
||||
Service.clusterURL = "http://localhost:8080/maintenance/";
|
||||
Service.username = "foo";
|
||||
// Force re-download of keys
|
||||
CollectionKeys.clear();
|
||||
|
||||
@ -1099,6 +1251,117 @@ add_test(function test_crypto_keys_login_syncAndReportErrors_server_maintenance_
|
||||
ErrorHandler.syncAndReportErrors();
|
||||
});
|
||||
|
||||
add_test(function test_upload_crypto_keys_login_syncAndReportErrors_server_maintenance_error() {
|
||||
// Test crypto/keys server maintenance errors are reported
|
||||
// when calling syncAndReportErrors.
|
||||
let server = sync_httpd_setup();
|
||||
|
||||
// Start off with an empty account, do not upload a key.
|
||||
Service.username = "broken.keys";
|
||||
Service.password = "ilovejane";
|
||||
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
|
||||
Service.clusterURL = "http://localhost:8080/maintenance/";
|
||||
|
||||
let backoffInterval;
|
||||
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
|
||||
Svc.Obs.remove("weave:service:backoff:interval", observe);
|
||||
backoffInterval = subject;
|
||||
});
|
||||
|
||||
Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
|
||||
Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
|
||||
do_check_true(Status.enforceBackoff);
|
||||
do_check_eq(backoffInterval, 42);
|
||||
do_check_eq(Status.service, LOGIN_FAILED);
|
||||
do_check_eq(Status.login, SERVER_MAINTENANCE);
|
||||
|
||||
clean();
|
||||
server.stop(run_next_test);
|
||||
});
|
||||
|
||||
do_check_false(Status.enforceBackoff);
|
||||
do_check_eq(Status.service, STATUS_OK);
|
||||
|
||||
setLastSync(NON_PROLONGED_ERROR_DURATION);
|
||||
ErrorHandler.syncAndReportErrors();
|
||||
});
|
||||
|
||||
add_test(function test_wipeServer_login_syncAndReportErrors_server_maintenance_error() {
|
||||
// Test crypto/keys server maintenance errors are reported
|
||||
// when calling syncAndReportErrors.
|
||||
let server = sync_httpd_setup();
|
||||
|
||||
// Start off with an empty account, do not upload a key.
|
||||
Service.username = "broken.wipe";
|
||||
Service.password = "ilovejane";
|
||||
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
|
||||
Service.clusterURL = "http://localhost:8080/maintenance/";
|
||||
|
||||
let backoffInterval;
|
||||
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
|
||||
Svc.Obs.remove("weave:service:backoff:interval", observe);
|
||||
backoffInterval = subject;
|
||||
});
|
||||
|
||||
Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
|
||||
Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
|
||||
do_check_true(Status.enforceBackoff);
|
||||
do_check_eq(backoffInterval, 42);
|
||||
do_check_eq(Status.service, LOGIN_FAILED);
|
||||
do_check_eq(Status.login, SERVER_MAINTENANCE);
|
||||
|
||||
clean();
|
||||
server.stop(run_next_test);
|
||||
});
|
||||
|
||||
do_check_false(Status.enforceBackoff);
|
||||
do_check_eq(Status.service, STATUS_OK);
|
||||
|
||||
setLastSync(NON_PROLONGED_ERROR_DURATION);
|
||||
ErrorHandler.syncAndReportErrors();
|
||||
});
|
||||
|
||||
add_test(function test_wipeRemote_syncAndReportErrors_server_maintenance_error(){
|
||||
// Test that we report prolonged server maintenance errors that occur whilst
|
||||
// wiping all remote devices.
|
||||
let server = sync_httpd_setup();
|
||||
|
||||
Service.username = "broken.wipe";
|
||||
Service.password = "ilovejane";
|
||||
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
|
||||
Service.clusterURL = "http://localhost:8080/maintenance/";
|
||||
generateAndUploadKeys();
|
||||
|
||||
let engine = Engines.get("catapult");
|
||||
engine.exception = null;
|
||||
engine.enabled = true;
|
||||
|
||||
let backoffInterval;
|
||||
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
|
||||
Svc.Obs.remove("weave:service:backoff:interval", observe);
|
||||
backoffInterval = subject;
|
||||
});
|
||||
|
||||
Svc.Obs.add("weave:ui:sync:error", function onUIUpdate() {
|
||||
Svc.Obs.remove("weave:ui:sync:error", onUIUpdate);
|
||||
do_check_true(Status.enforceBackoff);
|
||||
do_check_eq(backoffInterval, 42);
|
||||
do_check_eq(Status.service, SYNC_FAILED);
|
||||
do_check_eq(Status.sync, SERVER_MAINTENANCE);
|
||||
do_check_eq(Svc.Prefs.get("firstSync"), "wipeRemote");
|
||||
|
||||
clean();
|
||||
server.stop(run_next_test);
|
||||
});
|
||||
|
||||
do_check_false(Status.enforceBackoff);
|
||||
do_check_eq(Status.service, STATUS_OK);
|
||||
|
||||
Svc.Prefs.set("firstSync", "wipeRemote");
|
||||
setLastSync(NON_PROLONGED_ERROR_DURATION);
|
||||
ErrorHandler.syncAndReportErrors();
|
||||
});
|
||||
|
||||
add_test(function test_sync_syncAndReportErrors_prolonged_server_maintenance_error() {
|
||||
// Test prolonged server maintenance errors are
|
||||
// reported when calling syncAndReportErrors.
|
||||
@ -1132,9 +1395,9 @@ add_test(function test_info_collections_login_syncAndReportErrors_prolonged_serv
|
||||
let server = sync_httpd_setup();
|
||||
setUp();
|
||||
|
||||
Service.username = "broken.info";
|
||||
Service.clusterURL = "http://localhost:8080/maintenance/";
|
||||
|
||||
Service.username = "johnsmith";
|
||||
let backoffInterval;
|
||||
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
|
||||
Svc.Obs.remove("weave:service:backoff:interval", observe);
|
||||
@ -1165,9 +1428,9 @@ add_test(function test_meta_global_login_syncAndReportErrors_prolonged_server_ma
|
||||
let server = sync_httpd_setup();
|
||||
setUp();
|
||||
|
||||
Service.username = "broken.meta";
|
||||
Service.clusterURL = "http://localhost:8080/maintenance/";
|
||||
|
||||
Service.username = "janesmith";
|
||||
let backoffInterval;
|
||||
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
|
||||
Svc.Obs.remove("weave:service:backoff:interval", observe);
|
||||
@ -1192,14 +1455,14 @@ add_test(function test_meta_global_login_syncAndReportErrors_prolonged_server_ma
|
||||
ErrorHandler.syncAndReportErrors();
|
||||
});
|
||||
|
||||
add_test(function test_crypto_keys_login_syncAndReportErrors_prolonged_server_maintenance_error() {
|
||||
add_test(function test_download_crypto_keys_login_syncAndReportErrors_prolonged_server_maintenance_error() {
|
||||
// Test crypto/keys server maintenance errors are reported
|
||||
// when calling syncAndReportErrors.
|
||||
let server = sync_httpd_setup();
|
||||
setUp();
|
||||
|
||||
Service.username = "broken.keys";
|
||||
Service.clusterURL = "http://localhost:8080/maintenance/";
|
||||
Service.username = "foo";
|
||||
// Force re-download of keys
|
||||
CollectionKeys.clear();
|
||||
|
||||
@ -1227,6 +1490,76 @@ add_test(function test_crypto_keys_login_syncAndReportErrors_prolonged_server_ma
|
||||
ErrorHandler.syncAndReportErrors();
|
||||
});
|
||||
|
||||
add_test(function test_upload_crypto_keys_login_syncAndReportErrors_prolonged_server_maintenance_error() {
|
||||
// Test crypto/keys server maintenance errors are reported
|
||||
// when calling syncAndReportErrors.
|
||||
let server = sync_httpd_setup();
|
||||
|
||||
// Start off with an empty account, do not upload a key.
|
||||
Service.username = "broken.keys";
|
||||
Service.password = "ilovejane";
|
||||
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
|
||||
Service.clusterURL = "http://localhost:8080/maintenance/";
|
||||
|
||||
let backoffInterval;
|
||||
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
|
||||
Svc.Obs.remove("weave:service:backoff:interval", observe);
|
||||
backoffInterval = subject;
|
||||
});
|
||||
|
||||
Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
|
||||
Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
|
||||
do_check_true(Status.enforceBackoff);
|
||||
do_check_eq(backoffInterval, 42);
|
||||
do_check_eq(Status.service, LOGIN_FAILED);
|
||||
do_check_eq(Status.login, SERVER_MAINTENANCE);
|
||||
|
||||
clean();
|
||||
server.stop(run_next_test);
|
||||
});
|
||||
|
||||
do_check_false(Status.enforceBackoff);
|
||||
do_check_eq(Status.service, STATUS_OK);
|
||||
|
||||
setLastSync(PROLONGED_ERROR_DURATION);
|
||||
ErrorHandler.syncAndReportErrors();
|
||||
});
|
||||
|
||||
add_test(function test_wipeServer_login_syncAndReportErrors_prolonged_server_maintenance_error() {
|
||||
// Test crypto/keys server maintenance errors are reported
|
||||
// when calling syncAndReportErrors.
|
||||
let server = sync_httpd_setup();
|
||||
|
||||
// Start off with an empty account, do not upload a key.
|
||||
Service.username = "broken.wipe";
|
||||
Service.password = "ilovejane";
|
||||
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
|
||||
Service.clusterURL = "http://localhost:8080/maintenance/";
|
||||
|
||||
let backoffInterval;
|
||||
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
|
||||
Svc.Obs.remove("weave:service:backoff:interval", observe);
|
||||
backoffInterval = subject;
|
||||
});
|
||||
|
||||
Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
|
||||
Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
|
||||
do_check_true(Status.enforceBackoff);
|
||||
do_check_eq(backoffInterval, 42);
|
||||
do_check_eq(Status.service, LOGIN_FAILED);
|
||||
do_check_eq(Status.login, SERVER_MAINTENANCE);
|
||||
|
||||
clean();
|
||||
server.stop(run_next_test);
|
||||
});
|
||||
|
||||
do_check_false(Status.enforceBackoff);
|
||||
do_check_eq(Status.service, STATUS_OK);
|
||||
|
||||
setLastSync(PROLONGED_ERROR_DURATION);
|
||||
ErrorHandler.syncAndReportErrors();
|
||||
});
|
||||
|
||||
add_test(function test_sync_engine_generic_fail() {
|
||||
let server = sync_httpd_setup();
|
||||
|
||||
|
487
services/sync/tests/unit/test_node_reassignment.js
Normal file
487
services/sync/tests/unit/test_node_reassignment.js
Normal file
@ -0,0 +1,487 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
_("Test that node reassignment responses are respected on all kinds of " +
|
||||
"requests.");
|
||||
|
||||
// Don't sync any engines by default.
|
||||
Svc.DefaultPrefs.set("registerEngines", "")
|
||||
|
||||
Cu.import("resource://services-sync/constants.js");
|
||||
Cu.import("resource://services-sync/policies.js");
|
||||
Cu.import("resource://services-sync/rest.js");
|
||||
Cu.import("resource://services-sync/service.js");
|
||||
Cu.import("resource://services-sync/status.js");
|
||||
Cu.import("resource://services-sync/log4moz.js");
|
||||
|
||||
function run_test() {
|
||||
Log4Moz.repository.getLogger("Sync.AsyncResource").level = Log4Moz.Level.Trace;
|
||||
Log4Moz.repository.getLogger("Sync.ErrorHandler").level = Log4Moz.Level.Trace;
|
||||
Log4Moz.repository.getLogger("Sync.Resource").level = Log4Moz.Level.Trace;
|
||||
Log4Moz.repository.getLogger("Sync.RESTRequest").level = Log4Moz.Level.Trace;
|
||||
Log4Moz.repository.getLogger("Sync.Service").level = Log4Moz.Level.Trace;
|
||||
Log4Moz.repository.getLogger("Sync.SyncScheduler").level = Log4Moz.Level.Trace;
|
||||
initTestLogging();
|
||||
|
||||
Engines.register(RotaryEngine);
|
||||
|
||||
// None of the failures in this file should result in a UI error.
|
||||
function onUIError() {
|
||||
do_throw("Errors should not be presented in the UI.");
|
||||
}
|
||||
Svc.Obs.add("weave:ui:login:error", onUIError);
|
||||
Svc.Obs.add("weave:ui:sync:error", onUIError);
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
/**
|
||||
* Emulate the following Zeus config:
|
||||
* $draining = data.get($prefix . $host . " draining");
|
||||
* if ($draining == "drain.") {
|
||||
* log.warn($log_host_db_status . " migrating=1 (node-reassignment)" .
|
||||
* $log_suffix);
|
||||
* http.sendResponse("401 Node reassignment", $content_type,
|
||||
* '"server request: node reassignment"', "");
|
||||
* }
|
||||
*/
|
||||
const reassignBody = "\"server request: node reassignment\"";
|
||||
|
||||
// API-compatible with SyncServer handler. Bind `handler` to something to use
|
||||
// as a ServerCollection handler.
|
||||
function handleReassign(handler, req, resp) {
|
||||
resp.setStatusLine(req.httpVersion, 401, "Node reassignment");
|
||||
resp.setHeader("Content-Type", "application/json");
|
||||
resp.bodyOutputStream.write(reassignBody, reassignBody.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* A node assignment handler.
|
||||
*/
|
||||
const newNodeBody = "http://localhost:8080/";
|
||||
function installNodeHandler(server, next) {
|
||||
function handleNodeRequest(req, resp) {
|
||||
_("Client made a request for a node reassignment.");
|
||||
resp.setStatusLine(req.httpVersion, 200, "OK");
|
||||
resp.setHeader("Content-Type", "text/plain");
|
||||
resp.bodyOutputStream.write(newNodeBody, newNodeBody.length);
|
||||
Utils.nextTick(next);
|
||||
}
|
||||
let nodePath = "/user/1.0/johndoe/node/weave";
|
||||
server.server.registerPathHandler(nodePath, handleNodeRequest);
|
||||
_("Registered node handler at " + nodePath);
|
||||
}
|
||||
|
||||
function prepareServer() {
|
||||
Service.username = "johndoe";
|
||||
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
|
||||
Service.password = "ilovejane";
|
||||
Service.serverURL = "http://localhost:8080/";
|
||||
Service.clusterURL = "http://localhost:8080/";
|
||||
|
||||
do_check_eq(Service.userAPI, "http://localhost:8080/user/1.0/");
|
||||
let server = new SyncServer();
|
||||
server.registerUser("johndoe");
|
||||
server.start();
|
||||
return server;
|
||||
}
|
||||
|
||||
function getReassigned() {
|
||||
try {
|
||||
return Services.prefs.getBoolPref("services.sync.lastSyncReassigned");
|
||||
} catch (ex if (ex.result == Cr.NS_ERROR_UNEXPECTED)) {
|
||||
return false;
|
||||
} catch (ex) {
|
||||
do_throw("Got exception retrieving lastSyncReassigned: " +
|
||||
Utils.exceptionStr(ex));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a test request to `url`, then watch the result of two syncs
|
||||
* to ensure that a node request was made.
|
||||
* Runs `between` between the two. This can be used to undo deliberate failure
|
||||
* setup, detach observers, etc.
|
||||
*/
|
||||
function syncAndExpectNodeReassignment(server, firstNotification, between,
|
||||
secondNotification, url) {
|
||||
function onwards() {
|
||||
let nodeFetched = false;
|
||||
function onFirstSync() {
|
||||
_("First sync completed.");
|
||||
Svc.Obs.remove(firstNotification, onFirstSync);
|
||||
Svc.Obs.add(secondNotification, onSecondSync);
|
||||
|
||||
do_check_eq(Service.clusterURL, "");
|
||||
|
||||
// Track whether we fetched node/weave. We want to wait for the second
|
||||
// sync to finish so that we're cleaned up for the next test, so don't
|
||||
// run_next_test in the node handler.
|
||||
nodeFetched = false;
|
||||
|
||||
// Verify that the client requests a node reassignment.
|
||||
// Install a node handler to watch for these requests.
|
||||
installNodeHandler(server, function () {
|
||||
nodeFetched = true;
|
||||
});
|
||||
|
||||
// Allow for tests to clean up error conditions.
|
||||
between();
|
||||
}
|
||||
function onSecondSync() {
|
||||
_("Second sync completed.");
|
||||
Svc.Obs.remove(secondNotification, onSecondSync);
|
||||
SyncScheduler.clearSyncTriggers();
|
||||
|
||||
// Make absolutely sure that any event listeners are done with their work
|
||||
// before we proceed.
|
||||
waitForZeroTimer(function () {
|
||||
_("Second sync nextTick.");
|
||||
do_check_true(nodeFetched);
|
||||
Service.startOver();
|
||||
server.stop(run_next_test);
|
||||
});
|
||||
}
|
||||
|
||||
Svc.Obs.add(firstNotification, onFirstSync);
|
||||
Service.sync();
|
||||
}
|
||||
|
||||
// Make sure that it works!
|
||||
let request = new RESTRequest(url);
|
||||
request.get(function () {
|
||||
do_check_eq(request.response.status, 401);
|
||||
Utils.nextTick(onwards);
|
||||
});
|
||||
}
|
||||
|
||||
add_test(function test_momentary_401_engine() {
|
||||
_("Test a failure for engine URLs that's resolved by reassignment.");
|
||||
let server = prepareServer();
|
||||
let john = server.user("johndoe");
|
||||
|
||||
_("Enabling the Rotary engine.");
|
||||
let engine = Engines.get("rotary");
|
||||
engine.enabled = true;
|
||||
|
||||
// We need the server to be correctly set up prior to experimenting. Do this
|
||||
// through a sync.
|
||||
let global = {syncID: Service.syncID,
|
||||
storageVersion: STORAGE_VERSION,
|
||||
rotary: {version: engine.version,
|
||||
syncID: engine.syncID}}
|
||||
john.createCollection("meta").insert("global", global);
|
||||
|
||||
_("First sync to prepare server contents.");
|
||||
Service.sync();
|
||||
|
||||
_("Setting up Rotary collection to 401.");
|
||||
let rotary = john.createCollection("rotary");
|
||||
let oldHandler = rotary.collectionHandler;
|
||||
rotary.collectionHandler = handleReassign.bind(this, undefined);
|
||||
|
||||
// We want to verify that the clusterURL pref has been cleared after a 401
|
||||
// inside a sync. Flag the Rotary engine to need syncing.
|
||||
john.collection("rotary").timestamp += 1000;
|
||||
|
||||
function between() {
|
||||
_("Undoing test changes.");
|
||||
rotary.collectionHandler = oldHandler;
|
||||
|
||||
function onLoginStart() {
|
||||
// lastSyncReassigned shouldn't be cleared until a sync has succeeded.
|
||||
_("Ensuring that lastSyncReassigned is still set at next sync start.");
|
||||
Svc.Obs.remove("weave:service:login:start", onLoginStart);
|
||||
do_check_true(getReassigned());
|
||||
}
|
||||
|
||||
_("Adding observer that lastSyncReassigned is still set on login.");
|
||||
Svc.Obs.add("weave:service:login:start", onLoginStart);
|
||||
}
|
||||
|
||||
syncAndExpectNodeReassignment(server,
|
||||
"weave:service:sync:finish",
|
||||
between,
|
||||
"weave:service:sync:finish",
|
||||
Service.storageURL + "rotary");
|
||||
});
|
||||
|
||||
// This test ends up being a failing fetch *after we're already logged in*.
|
||||
add_test(function test_momentary_401_info_collections() {
|
||||
_("Test a failure for info/collections that's resolved by reassignment.");
|
||||
let server = prepareServer();
|
||||
|
||||
_("First sync to prepare server contents.");
|
||||
Service.sync();
|
||||
|
||||
// Return a 401 for info requests, particularly info/collections.
|
||||
let oldHandler = server.toplevelHandlers.info;
|
||||
server.toplevelHandlers.info = handleReassign;
|
||||
|
||||
function undo() {
|
||||
_("Undoing test changes.");
|
||||
server.toplevelHandlers.info = oldHandler;
|
||||
}
|
||||
|
||||
syncAndExpectNodeReassignment(server,
|
||||
"weave:service:sync:error",
|
||||
undo,
|
||||
"weave:service:sync:finish",
|
||||
Service.infoURL);
|
||||
});
|
||||
|
||||
add_test(function test_momentary_401_storage() {
|
||||
_("Test a failure for any storage URL, not just engine parts. " +
|
||||
"Resolved by reassignment.");
|
||||
let server = prepareServer();
|
||||
|
||||
// Return a 401 for all storage requests.
|
||||
let oldHandler = server.toplevelHandlers.storage;
|
||||
server.toplevelHandlers.storage = handleReassign;
|
||||
|
||||
function undo() {
|
||||
_("Undoing test changes.");
|
||||
server.toplevelHandlers.storage = oldHandler;
|
||||
}
|
||||
|
||||
syncAndExpectNodeReassignment(server,
|
||||
"weave:service:login:error",
|
||||
undo,
|
||||
"weave:service:sync:finish",
|
||||
Service.storageURL + "meta/global");
|
||||
});
|
||||
|
||||
add_test(function test_loop_avoidance_storage() {
|
||||
_("Test that a repeated failure doesn't result in a sync loop " +
|
||||
"if node reassignment cannot resolve the failure.");
|
||||
|
||||
let server = prepareServer();
|
||||
|
||||
// Return a 401 for all storage requests.
|
||||
let oldHandler = server.toplevelHandlers.storage;
|
||||
server.toplevelHandlers.storage = handleReassign;
|
||||
|
||||
let firstNotification = "weave:service:login:error";
|
||||
let secondNotification = "weave:service:login:error";
|
||||
let thirdNotification = "weave:service:sync:finish";
|
||||
|
||||
let nodeFetched = false;
|
||||
|
||||
// Track the time. We want to make sure the duration between the first and
|
||||
// second sync is small, and then that the duration between second and third
|
||||
// is set to be large.
|
||||
let now;
|
||||
|
||||
function onFirstSync() {
|
||||
_("First sync completed.");
|
||||
Svc.Obs.remove(firstNotification, onFirstSync);
|
||||
Svc.Obs.add(secondNotification, onSecondSync);
|
||||
|
||||
do_check_eq(Service.clusterURL, "");
|
||||
|
||||
// We got a 401 mid-sync, and set the pref accordingly.
|
||||
do_check_true(Services.prefs.getBoolPref("services.sync.lastSyncReassigned"));
|
||||
|
||||
// Track whether we fetched node/weave. We want to wait for the second
|
||||
// sync to finish so that we're cleaned up for the next test, so don't
|
||||
// run_next_test in the node handler.
|
||||
nodeFetched = false;
|
||||
|
||||
// Verify that the client requests a node reassignment.
|
||||
// Install a node handler to watch for these requests.
|
||||
installNodeHandler(server, function () {
|
||||
nodeFetched = true;
|
||||
});
|
||||
|
||||
// Update the timestamp.
|
||||
now = Date.now();
|
||||
}
|
||||
|
||||
function onSecondSync() {
|
||||
_("Second sync completed.");
|
||||
Svc.Obs.remove(secondNotification, onSecondSync);
|
||||
Svc.Obs.add(thirdNotification, onThirdSync);
|
||||
|
||||
// This sync occurred within the backoff interval.
|
||||
let elapsedTime = Date.now() - now;
|
||||
do_check_true(elapsedTime < MINIMUM_BACKOFF_INTERVAL);
|
||||
|
||||
// This pref will be true until a sync completes successfully.
|
||||
do_check_true(getReassigned());
|
||||
|
||||
// The timer will be set for some distant time.
|
||||
// We store nextSync in prefs, which offers us only limited resolution.
|
||||
// Include that logic here.
|
||||
let expectedNextSync = 1000 * Math.floor((now + MINIMUM_BACKOFF_INTERVAL) / 1000);
|
||||
_("Next sync scheduled for " + SyncScheduler.nextSync);
|
||||
_("Expected to be slightly greater than " + expectedNextSync);
|
||||
|
||||
do_check_true(SyncScheduler.nextSync >= expectedNextSync);
|
||||
do_check_true(!!SyncScheduler.syncTimer);
|
||||
|
||||
// Undo our evil scheme.
|
||||
server.toplevelHandlers.storage = oldHandler;
|
||||
|
||||
// Bring the timer forward to kick off a successful sync, so we can watch
|
||||
// the pref get cleared.
|
||||
SyncScheduler.scheduleNextSync(0);
|
||||
}
|
||||
function onThirdSync() {
|
||||
Svc.Obs.remove(thirdNotification, onThirdSync);
|
||||
|
||||
// That'll do for now; no more syncs.
|
||||
SyncScheduler.clearSyncTriggers();
|
||||
|
||||
// Make absolutely sure that any event listeners are done with their work
|
||||
// before we proceed.
|
||||
waitForZeroTimer(function () {
|
||||
_("Third sync nextTick.");
|
||||
do_check_false(getReassigned());
|
||||
do_check_true(nodeFetched);
|
||||
Service.startOver();
|
||||
server.stop(run_next_test);
|
||||
});
|
||||
}
|
||||
|
||||
Svc.Obs.add(firstNotification, onFirstSync);
|
||||
|
||||
now = Date.now();
|
||||
Service.sync();
|
||||
});
|
||||
|
||||
add_test(function test_loop_avoidance_engine() {
|
||||
_("Test that a repeated 401 in an engine doesn't result in a sync loop " +
|
||||
"if node reassignment cannot resolve the failure.");
|
||||
let server = prepareServer();
|
||||
let john = server.user("johndoe");
|
||||
|
||||
_("Enabling the Rotary engine.");
|
||||
let engine = Engines.get("rotary");
|
||||
engine.enabled = true;
|
||||
|
||||
// We need the server to be correctly set up prior to experimenting. Do this
|
||||
// through a sync.
|
||||
let global = {syncID: Service.syncID,
|
||||
storageVersion: STORAGE_VERSION,
|
||||
rotary: {version: engine.version,
|
||||
syncID: engine.syncID}}
|
||||
john.createCollection("meta").insert("global", global);
|
||||
|
||||
_("First sync to prepare server contents.");
|
||||
Service.sync();
|
||||
|
||||
_("Setting up Rotary collection to 401.");
|
||||
let rotary = john.createCollection("rotary");
|
||||
let oldHandler = rotary.collectionHandler;
|
||||
rotary.collectionHandler = handleReassign.bind(this, undefined);
|
||||
|
||||
// Flag the Rotary engine to need syncing.
|
||||
john.collection("rotary").timestamp += 1000;
|
||||
|
||||
function onLoginStart() {
|
||||
// lastSyncReassigned shouldn't be cleared until a sync has succeeded.
|
||||
_("Ensuring that lastSyncReassigned is still set at next sync start.");
|
||||
do_check_true(getReassigned());
|
||||
}
|
||||
|
||||
function beforeSuccessfulSync() {
|
||||
_("Undoing test changes.");
|
||||
rotary.collectionHandler = oldHandler;
|
||||
}
|
||||
|
||||
function afterSuccessfulSync() {
|
||||
Svc.Obs.remove("weave:service:login:start", onLoginStart);
|
||||
Service.startOver();
|
||||
server.stop(run_next_test);
|
||||
}
|
||||
|
||||
let firstNotification = "weave:service:sync:finish";
|
||||
let secondNotification = "weave:service:sync:finish";
|
||||
let thirdNotification = "weave:service:sync:finish";
|
||||
|
||||
let nodeFetched = false;
|
||||
|
||||
// Track the time. We want to make sure the duration between the first and
|
||||
// second sync is small, and then that the duration between second and third
|
||||
// is set to be large.
|
||||
let now;
|
||||
|
||||
function onFirstSync() {
|
||||
_("First sync completed.");
|
||||
Svc.Obs.remove(firstNotification, onFirstSync);
|
||||
Svc.Obs.add(secondNotification, onSecondSync);
|
||||
|
||||
do_check_eq(Service.clusterURL, "");
|
||||
|
||||
_("Adding observer that lastSyncReassigned is still set on login.");
|
||||
Svc.Obs.add("weave:service:login:start", onLoginStart);
|
||||
|
||||
// We got a 401 mid-sync, and set the pref accordingly.
|
||||
do_check_true(Services.prefs.getBoolPref("services.sync.lastSyncReassigned"));
|
||||
|
||||
// Track whether we fetched node/weave. We want to wait for the second
|
||||
// sync to finish so that we're cleaned up for the next test, so don't
|
||||
// run_next_test in the node handler.
|
||||
nodeFetched = false;
|
||||
|
||||
// Verify that the client requests a node reassignment.
|
||||
// Install a node handler to watch for these requests.
|
||||
installNodeHandler(server, function () {
|
||||
nodeFetched = true;
|
||||
});
|
||||
|
||||
// Update the timestamp.
|
||||
now = Date.now();
|
||||
}
|
||||
|
||||
function onSecondSync() {
|
||||
_("Second sync completed.");
|
||||
Svc.Obs.remove(secondNotification, onSecondSync);
|
||||
Svc.Obs.add(thirdNotification, onThirdSync);
|
||||
|
||||
// This sync occurred within the backoff interval.
|
||||
let elapsedTime = Date.now() - now;
|
||||
do_check_true(elapsedTime < MINIMUM_BACKOFF_INTERVAL);
|
||||
|
||||
// This pref will be true until a sync completes successfully.
|
||||
do_check_true(getReassigned());
|
||||
|
||||
// The timer will be set for some distant time.
|
||||
// We store nextSync in prefs, which offers us only limited resolution.
|
||||
// Include that logic here.
|
||||
let expectedNextSync = 1000 * Math.floor((now + MINIMUM_BACKOFF_INTERVAL) / 1000);
|
||||
_("Next sync scheduled for " + SyncScheduler.nextSync);
|
||||
_("Expected to be slightly greater than " + expectedNextSync);
|
||||
|
||||
do_check_true(SyncScheduler.nextSync >= expectedNextSync);
|
||||
do_check_true(!!SyncScheduler.syncTimer);
|
||||
|
||||
// Undo our evil scheme.
|
||||
beforeSuccessfulSync();
|
||||
|
||||
// Bring the timer forward to kick off a successful sync, so we can watch
|
||||
// the pref get cleared.
|
||||
SyncScheduler.scheduleNextSync(0);
|
||||
}
|
||||
|
||||
function onThirdSync() {
|
||||
Svc.Obs.remove(thirdNotification, onThirdSync);
|
||||
|
||||
// That'll do for now; no more syncs.
|
||||
SyncScheduler.clearSyncTriggers();
|
||||
|
||||
// Make absolutely sure that any event listeners are done with their work
|
||||
// before we proceed.
|
||||
waitForZeroTimer(function () {
|
||||
_("Third sync nextTick.");
|
||||
do_check_false(getReassigned());
|
||||
do_check_true(nodeFetched);
|
||||
afterSuccessfulSync();
|
||||
});
|
||||
}
|
||||
|
||||
Svc.Obs.add(firstNotification, onFirstSync);
|
||||
|
||||
now = Date.now();
|
||||
Service.sync();
|
||||
});
|
@ -6,9 +6,11 @@ Cu.import("resource://services-sync/util.js");
|
||||
|
||||
let logger;
|
||||
|
||||
let fetched = false;
|
||||
function server_open(metadata, response) {
|
||||
let body;
|
||||
if (metadata.method == "GET") {
|
||||
fetched = true;
|
||||
body = "This path exists";
|
||||
response.setStatusLine(metadata.httpVersion, 200, "OK");
|
||||
} else {
|
||||
@ -40,6 +42,15 @@ function server_404(metadata, response) {
|
||||
response.bodyOutputStream.write(body, body.length);
|
||||
}
|
||||
|
||||
let pacFetched = false;
|
||||
function server_pac(metadata, response) {
|
||||
pacFetched = true;
|
||||
let body = 'function FindProxyForURL(url, host) { return "DIRECT"; }';
|
||||
response.setStatusLine(metadata.httpVersion, 200, "OK");
|
||||
response.setHeader("Content-Type", "application/x-ns-proxy-autoconfig", false);
|
||||
response.bodyOutputStream.write(body, body.length);
|
||||
}
|
||||
|
||||
|
||||
let sample_data = {
|
||||
some: "sample_data",
|
||||
@ -150,12 +161,26 @@ function run_test() {
|
||||
"/timestamp": server_timestamp,
|
||||
"/headers": server_headers,
|
||||
"/backoff": server_backoff,
|
||||
"/pac1": server_pac,
|
||||
"/quota-notice": server_quota_notice,
|
||||
"/quota-error": server_quota_error
|
||||
});
|
||||
|
||||
Svc.Prefs.set("network.numRetries", 1); // speed up test
|
||||
|
||||
// This apparently has to come first in order for our PAC URL to be hit.
|
||||
// Don't put any other HTTP requests earlier in the file!
|
||||
_("Testing handling of proxy auth redirection.");
|
||||
PACSystemSettings.PACURI = "http://localhost:8080/pac1";
|
||||
installFakePAC();
|
||||
let proxiedRes = new Resource("http://localhost:8080/open");
|
||||
let content = proxiedRes.get();
|
||||
do_check_true(pacFetched);
|
||||
do_check_true(fetched);
|
||||
do_check_eq(content, "This path exists");
|
||||
pacFetched = fetched = false;
|
||||
uninstallFakePAC();
|
||||
|
||||
_("Resource object members");
|
||||
let res = new Resource("http://localhost:8080/open");
|
||||
do_check_true(res.uri instanceof Ci.nsIURI);
|
||||
@ -168,7 +193,7 @@ function run_test() {
|
||||
do_check_eq(res.data, null);
|
||||
|
||||
_("GET a non-password-protected resource");
|
||||
let content = res.get();
|
||||
content = res.get();
|
||||
do_check_eq(content, "This path exists");
|
||||
do_check_eq(content.status, 200);
|
||||
do_check_true(content.success);
|
||||
@ -474,6 +499,5 @@ function run_test() {
|
||||
let uri2 = Utils.makeURL("http://foo/");
|
||||
uri2.query = query;
|
||||
do_check_eq(uri1.query, uri2.query);
|
||||
|
||||
server.stop(do_test_finished);
|
||||
}
|
||||
|
@ -6,9 +6,11 @@ Cu.import("resource://services-sync/util.js");
|
||||
|
||||
let logger;
|
||||
|
||||
let fetched = false;
|
||||
function server_open(metadata, response) {
|
||||
let body;
|
||||
if (metadata.method == "GET") {
|
||||
fetched = true;
|
||||
body = "This path exists";
|
||||
response.setStatusLine(metadata.httpVersion, 200, "OK");
|
||||
} else {
|
||||
@ -40,6 +42,15 @@ function server_404(metadata, response) {
|
||||
response.bodyOutputStream.write(body, body.length);
|
||||
}
|
||||
|
||||
let pacFetched = false;
|
||||
function server_pac(metadata, response) {
|
||||
_("Invoked PAC handler.");
|
||||
pacFetched = true;
|
||||
let body = 'function FindProxyForURL(url, host) { return "DIRECT"; }';
|
||||
response.setStatusLine(metadata.httpVersion, 200, "OK");
|
||||
response.setHeader("Content-Type", "application/x-ns-proxy-autoconfig", false);
|
||||
response.bodyOutputStream.write(body, body.length);
|
||||
}
|
||||
|
||||
let sample_data = {
|
||||
some: "sample_data",
|
||||
@ -154,6 +165,7 @@ function run_test() {
|
||||
"/timestamp": server_timestamp,
|
||||
"/headers": server_headers,
|
||||
"/backoff": server_backoff,
|
||||
"/pac2": server_pac,
|
||||
"/quota-notice": server_quota_notice,
|
||||
"/quota-error": server_quota_error
|
||||
});
|
||||
@ -162,9 +174,27 @@ function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
// This apparently has to come first in order for our PAC URL to be hit.
|
||||
// Don't put any other HTTP requests earlier in the file!
|
||||
add_test(function test_proxy_auth_redirect() {
|
||||
_("Ensure that a proxy auth redirect (which switches out our channel) " +
|
||||
"doesn't break AsyncResource.");
|
||||
PACSystemSettings.PACURI = "http://localhost:8080/pac2";
|
||||
installFakePAC();
|
||||
let res = new AsyncResource("http://localhost:8080/open");
|
||||
res.get(function (error, result) {
|
||||
do_check_true(!error);
|
||||
do_check_true(pacFetched);
|
||||
do_check_true(fetched);
|
||||
do_check_eq("This path exists", result);
|
||||
pacFetched = fetched = false;
|
||||
uninstallFakePAC();
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_members() {
|
||||
_("Resource object memebers");
|
||||
_("Resource object members");
|
||||
let res = new AsyncResource("http://localhost:8080/open");
|
||||
do_check_true(res.uri instanceof Ci.nsIURI);
|
||||
do_check_eq(res.uri.spec, "http://localhost:8080/open");
|
||||
|
@ -41,6 +41,47 @@ add_test(function test_attributes() {
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
/**
|
||||
* Verify that a proxy auth redirect doesn't break us. This has to be the first
|
||||
* request made in the file!
|
||||
*/
|
||||
add_test(function test_proxy_auth_redirect() {
|
||||
let pacFetched = false;
|
||||
function pacHandler(metadata, response) {
|
||||
pacFetched = true;
|
||||
let body = 'function FindProxyForURL(url, host) { return "DIRECT"; }';
|
||||
response.setStatusLine(metadata.httpVersion, 200, "OK");
|
||||
response.setHeader("Content-Type", "application/x-ns-proxy-autoconfig", false);
|
||||
response.bodyOutputStream.write(body, body.length);
|
||||
}
|
||||
|
||||
let fetched = false;
|
||||
function original(metadata, response) {
|
||||
fetched = true;
|
||||
let body = "TADA!";
|
||||
response.setStatusLine(metadata.httpVersion, 200, "OK");
|
||||
response.bodyOutputStream.write(body, body.length);
|
||||
}
|
||||
|
||||
let server = httpd_setup({
|
||||
"/original": original,
|
||||
"/pac3": pacHandler
|
||||
});
|
||||
PACSystemSettings.PACURI = "http://localhost:8080/pac3";
|
||||
installFakePAC();
|
||||
|
||||
let res = new RESTRequest("http://localhost:8080/original");
|
||||
res.get(function (error) {
|
||||
do_check_true(pacFetched);
|
||||
do_check_true(fetched);
|
||||
do_check_true(!error);
|
||||
do_check_true(this.response.success);
|
||||
do_check_eq("TADA!", this.response.body);
|
||||
uninstallFakePAC();
|
||||
server.stop(run_next_test);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Demonstrate API short-hand: create a request and dispatch it immediately.
|
||||
*/
|
||||
|
@ -2,8 +2,21 @@ Cu.import("resource://services-sync/main.js");
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
Cu.import("resource://services-sync/constants.js");
|
||||
|
||||
Cu.import("resource://services-sync/log4moz.js");
|
||||
|
||||
function run_test() {
|
||||
var requestBody;
|
||||
initTestLogging("Trace");
|
||||
Log4Moz.repository.getLogger("Sync.AsyncResource").level = Log4Moz.Level.Trace;
|
||||
Log4Moz.repository.getLogger("Sync.Resource").level = Log4Moz.Level.Trace;
|
||||
Log4Moz.repository.getLogger("Sync.Service").level = Log4Moz.Level.Trace;
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_test(function test_change_password() {
|
||||
let requestBody;
|
||||
let server;
|
||||
|
||||
function send(statusCode, status, body) {
|
||||
return function(request, response) {
|
||||
requestBody = readBytesFromInputStream(request.bodyInputStream);
|
||||
@ -12,10 +25,7 @@ function run_test() {
|
||||
};
|
||||
}
|
||||
|
||||
let server;
|
||||
|
||||
try {
|
||||
|
||||
Weave.Service.serverURL = "http://localhost:8080/";
|
||||
Weave.Service.username = "johndoe";
|
||||
Weave.Service.password = "ilovejane";
|
||||
@ -26,11 +36,10 @@ function run_test() {
|
||||
do_check_eq(Weave.Service.password, "ilovejane");
|
||||
|
||||
_("Let's fire up the server and actually change the password.");
|
||||
server = httpd_setup({
|
||||
server = httpd_setup({
|
||||
"/user/1.0/johndoe/password": send(200, "OK", ""),
|
||||
"/user/1.0/janedoe/password": send(401, "Unauthorized", "Forbidden!")
|
||||
});
|
||||
do_test_pending();
|
||||
|
||||
res = Weave.Service.changePassword("ILoveJane83");
|
||||
do_check_true(res);
|
||||
@ -60,8 +69,6 @@ function run_test() {
|
||||
} finally {
|
||||
Weave.Svc.Prefs.resetBranch("");
|
||||
Services.logins.removeAllLogins();
|
||||
if (server) {
|
||||
server.stop(do_test_finished);
|
||||
}
|
||||
server.stop(run_next_test);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -51,7 +51,7 @@ function run_test() {
|
||||
do_check_true(Weave.Service.isLoggedIn);
|
||||
do_check_eq(Weave.Status.login, Weave.LOGIN_SUCCEEDED);
|
||||
|
||||
_("Simulate having changed the password somehwere else.");
|
||||
_("Simulate having changed the password somewhere else.");
|
||||
Weave.Service.password = "ilovejosephine";
|
||||
|
||||
_("Let's try to sync.");
|
||||
@ -63,12 +63,18 @@ function run_test() {
|
||||
_("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);
|
||||
_("Sync status won't have changed yet, because we haven't tried again.");
|
||||
|
||||
_("globalScore is reset upon starting a sync.");
|
||||
do_check_eq(SyncScheduler.globalScore, 0);
|
||||
|
||||
_("Our next sync will fail appropriately.");
|
||||
try {
|
||||
Weave.Service.sync();
|
||||
} catch (ex) {
|
||||
}
|
||||
do_check_eq(Weave.Status.login, Weave.LOGIN_FAILED_LOGIN_REJECTED);
|
||||
|
||||
} finally {
|
||||
Weave.Svc.Prefs.resetBranch("");
|
||||
server.stop(do_test_finished);
|
||||
|
@ -29,7 +29,28 @@ function run_test() {
|
||||
let cryptoColl = new ServerCollection({keys: keysWBO});
|
||||
let metaColl = new ServerCollection({global: meta_global});
|
||||
do_test_pending();
|
||||
|
||||
/**
|
||||
* Handle the bulk DELETE request sent by wipeServer.
|
||||
*/
|
||||
function storageHandler(request, response) {
|
||||
do_check_eq("DELETE", request.method);
|
||||
do_check_true(request.hasHeader("X-Confirm-Delete"));
|
||||
|
||||
_("Wiping out all collections.");
|
||||
cryptoColl.delete({});
|
||||
clients.delete({});
|
||||
metaColl.delete({});
|
||||
|
||||
let ts = new_timestamp();
|
||||
collectionsHelper.update_collection("crypto", ts);
|
||||
collectionsHelper.update_collection("clients", ts);
|
||||
collectionsHelper.update_collection("meta", ts);
|
||||
return_timestamp(request, response, ts);
|
||||
}
|
||||
|
||||
let server = httpd_setup({
|
||||
"/1.1/johndoe/storage": storageHandler,
|
||||
"/1.1/johndoe/storage/crypto/keys": upd("crypto", keysWBO.handler()),
|
||||
"/1.1/johndoe/storage/crypto": upd("crypto", cryptoColl.handler()),
|
||||
"/1.1/johndoe/storage/clients": upd("clients", clients.handler()),
|
||||
|
@ -3,9 +3,11 @@ Cu.import("resource://services-sync/engines/clients.js");
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
Cu.import("resource://services-sync/constants.js");
|
||||
Cu.import("resource://services-sync/record.js");
|
||||
Cu.import("resource://services-sync/status.js");
|
||||
|
||||
Svc.DefaultPrefs.set("registerEngines", "");
|
||||
Cu.import("resource://services-sync/service.js");
|
||||
Cu.import("resource://services-sync/policies.js");
|
||||
|
||||
initTestLogging();
|
||||
|
||||
@ -66,13 +68,30 @@ function sync_httpd_setup(handlers) {
|
||||
function setUp() {
|
||||
Service.username = "johndoe";
|
||||
Service.password = "ilovejane";
|
||||
Service.passphrase = "sekrit";
|
||||
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
|
||||
Service.clusterURL = "http://localhost:8080/";
|
||||
// So that we can poke at meta/global.
|
||||
new FakeCryptoService();
|
||||
|
||||
// Ensure that the server has valid keys so that logging in will work and not
|
||||
// result in a server wipe, rendering many of these tests useless.
|
||||
generateNewKeys();
|
||||
let serverKeys = CollectionKeys.asWBO("crypto", "keys");
|
||||
serverKeys.encrypt(Service.syncKeyBundle);
|
||||
return serverKeys.upload(Service.cryptoKeysURL).success;
|
||||
}
|
||||
|
||||
const PAYLOAD = 42;
|
||||
|
||||
|
||||
function run_test() {
|
||||
initTestLogging("Trace");
|
||||
Log4Moz.repository.getLogger("Sync.Service").level = Log4Moz.Level.Trace;
|
||||
Log4Moz.repository.getLogger("Sync.ErrorHandler").level = Log4Moz.Level.Trace;
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_test(function test_newAccount() {
|
||||
_("Test: New account does not disable locally enabled engines.");
|
||||
let engine = Engines.get("steam");
|
||||
@ -89,7 +108,6 @@ add_test(function test_newAccount() {
|
||||
Service._ignorePrefObserver = false;
|
||||
|
||||
_("Sync.");
|
||||
Weave.Service.login();
|
||||
Weave.Service.sync();
|
||||
|
||||
_("Engine continues to be enabled.");
|
||||
@ -118,7 +136,6 @@ add_test(function test_enabledLocally() {
|
||||
engine.enabled = true;
|
||||
|
||||
_("Sync.");
|
||||
Weave.Service.login();
|
||||
Weave.Service.sync();
|
||||
|
||||
_("Meta record now contains the new engine.");
|
||||
@ -143,6 +160,7 @@ add_test(function test_disabledLocally() {
|
||||
version: engine.version}}
|
||||
});
|
||||
let steamCollection = new ServerWBO("steam", PAYLOAD);
|
||||
|
||||
let server = sync_httpd_setup({
|
||||
"/1.1/johndoe/storage/meta/global": metaWBO.handler(),
|
||||
"/1.1/johndoe/storage/steam": steamCollection.handler()
|
||||
@ -157,7 +175,6 @@ add_test(function test_disabledLocally() {
|
||||
engine.enabled = false;
|
||||
|
||||
_("Sync.");
|
||||
Weave.Service.login();
|
||||
Weave.Service.sync();
|
||||
|
||||
_("Meta record no longer contains engine.");
|
||||
@ -174,6 +191,50 @@ add_test(function test_disabledLocally() {
|
||||
}
|
||||
});
|
||||
|
||||
add_test(function test_disabledLocally_wipe503() {
|
||||
_("Test: Engine is enabled on remote clients and disabled locally");
|
||||
Service.syncID = "abcdefghij";
|
||||
let engine = Engines.get("steam");
|
||||
let metaWBO = new ServerWBO("global", {
|
||||
syncID: Service.syncID,
|
||||
storageVersion: STORAGE_VERSION,
|
||||
engines: {steam: {syncID: engine.syncID,
|
||||
version: engine.version}}
|
||||
});
|
||||
let steamCollection = new ServerWBO("steam", PAYLOAD);
|
||||
|
||||
function service_unavailable(request, response) {
|
||||
let body = "Service Unavailable";
|
||||
response.setStatusLine(request.httpVersion, 503, "Service Unavailable");
|
||||
response.setHeader("Retry-After", "23");
|
||||
response.bodyOutputStream.write(body, body.length);
|
||||
}
|
||||
|
||||
let server = sync_httpd_setup({
|
||||
"/1.1/johndoe/storage/meta/global": metaWBO.handler(),
|
||||
"/1.1/johndoe/storage/steam": service_unavailable
|
||||
});
|
||||
setUp();
|
||||
|
||||
_("Disable engine locally.");
|
||||
Service._ignorePrefObserver = true;
|
||||
engine.enabled = true;
|
||||
Service._ignorePrefObserver = false;
|
||||
engine.enabled = false;
|
||||
|
||||
Svc.Obs.add("weave:ui:sync:error", function onSyncError() {
|
||||
Svc.Obs.remove("weave:ui:sync:error", onSyncError);
|
||||
|
||||
do_check_eq(Status.sync, SERVER_MAINTENANCE);
|
||||
|
||||
Service.startOver();
|
||||
server.stop(run_next_test);
|
||||
});
|
||||
|
||||
_("Sync.");
|
||||
ErrorHandler.syncAndReportErrors();
|
||||
});
|
||||
|
||||
add_test(function test_enabledRemotely() {
|
||||
_("Test: Engine is disabled locally and enabled on a remote client");
|
||||
Service.syncID = "abcdefghij";
|
||||
@ -205,7 +266,6 @@ add_test(function test_enabledRemotely() {
|
||||
do_check_false(engine.enabled);
|
||||
|
||||
_("Sync.");
|
||||
Weave.Service.login();
|
||||
Weave.Service.sync();
|
||||
|
||||
_("Engine is enabled.");
|
||||
@ -242,7 +302,6 @@ add_test(function test_disabledRemotelyTwoClients() {
|
||||
Service._ignorePrefObserver = false;
|
||||
|
||||
_("Sync.");
|
||||
Weave.Service.login();
|
||||
Weave.Service.sync();
|
||||
|
||||
_("Disable engine by deleting from meta/global.");
|
||||
@ -284,7 +343,6 @@ add_test(function test_disabledRemotely() {
|
||||
Service._ignorePrefObserver = false;
|
||||
|
||||
_("Sync.");
|
||||
Weave.Service.login();
|
||||
Weave.Service.sync();
|
||||
|
||||
_("Engine is not disabled: only one client.");
|
||||
@ -316,7 +374,6 @@ add_test(function test_dependentEnginesEnabledLocally() {
|
||||
steamEngine.enabled = true;
|
||||
|
||||
_("Sync.");
|
||||
Weave.Service.login();
|
||||
Weave.Service.sync();
|
||||
|
||||
_("Meta record now contains the new engines.");
|
||||
@ -348,6 +405,7 @@ add_test(function test_dependentEnginesDisabledLocally() {
|
||||
|
||||
let steamCollection = new ServerWBO("steam", PAYLOAD);
|
||||
let stirlingCollection = new ServerWBO("stirling", PAYLOAD);
|
||||
|
||||
let server = sync_httpd_setup({
|
||||
"/1.1/johndoe/storage/meta/global": metaWBO.handler(),
|
||||
"/1.1/johndoe/storage/steam": steamCollection.handler(),
|
||||
@ -365,7 +423,6 @@ add_test(function test_dependentEnginesDisabledLocally() {
|
||||
do_check_false(stirlingEngine.enabled);
|
||||
|
||||
_("Sync.");
|
||||
Weave.Service.login();
|
||||
Weave.Service.sync();
|
||||
|
||||
_("Meta record no longer contains engines.");
|
||||
@ -384,7 +441,3 @@ add_test(function test_dependentEnginesDisabledLocally() {
|
||||
server.stop(run_next_test);
|
||||
}
|
||||
});
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
@ -13,22 +13,19 @@ FakeCollection.prototype = {
|
||||
let self = this;
|
||||
return function(request, response) {
|
||||
let body = "";
|
||||
self.timestamp = new_timestamp();
|
||||
let timestamp = "" + self.timestamp;
|
||||
if (request.method == "DELETE") {
|
||||
body = JSON.stringify(Date.now() / 1000);
|
||||
body = timestamp;
|
||||
self.deleted = true;
|
||||
}
|
||||
response.setHeader("X-Weave-Timestamp", timestamp);
|
||||
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 setUpTestFixtures() {
|
||||
let cryptoService = new FakeCryptoService();
|
||||
|
||||
@ -37,7 +34,13 @@ function setUpTestFixtures() {
|
||||
Service.passphrase = "aabcdeabcdeabcdeabcdeabcde";
|
||||
}
|
||||
|
||||
function test_withCollectionList_fail() {
|
||||
|
||||
function run_test() {
|
||||
initTestLogging("Trace");
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_test(function test_wipeServer_list_success() {
|
||||
_("Service.wipeServer() deletes collections given as argument.");
|
||||
|
||||
let steam_coll = new FakeCollection();
|
||||
@ -45,10 +48,42 @@ function test_withCollectionList_fail() {
|
||||
|
||||
let server = httpd_setup({
|
||||
"/1.1/johndoe/storage/steam": steam_coll.handler(),
|
||||
"/1.1/johndoe/storage/petrol": serviceUnavailable,
|
||||
"/1.1/johndoe/storage/diesel": diesel_coll.handler(),
|
||||
"/1.1/johndoe/storage/petrol": httpd_handler(404, "Not Found")
|
||||
});
|
||||
|
||||
try {
|
||||
setUpTestFixtures();
|
||||
|
||||
_("Confirm initial environment.");
|
||||
do_check_false(steam_coll.deleted);
|
||||
do_check_false(diesel_coll.deleted);
|
||||
|
||||
_("wipeServer() will happily ignore the non-existent collection and use the timestamp of the last DELETE that was successful.");
|
||||
let timestamp = Service.wipeServer(["steam", "diesel", "petrol"]);
|
||||
do_check_eq(timestamp, diesel_coll.timestamp);
|
||||
|
||||
_("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_true(diesel_coll.deleted);
|
||||
|
||||
} finally {
|
||||
server.stop(run_next_test);
|
||||
Svc.Prefs.resetBranch("");
|
||||
}
|
||||
});
|
||||
|
||||
add_test(function test_wipeServer_list_503() {
|
||||
_("Service.wipeServer() deletes collections given as argument.");
|
||||
|
||||
let steam_coll = new FakeCollection();
|
||||
let diesel_coll = new FakeCollection();
|
||||
|
||||
let server = httpd_setup({
|
||||
"/1.1/johndoe/storage/steam": steam_coll.handler(),
|
||||
"/1.1/johndoe/storage/petrol": httpd_handler(503, "Service Unavailable"),
|
||||
"/1.1/johndoe/storage/diesel": diesel_coll.handler()
|
||||
});
|
||||
do_test_pending();
|
||||
|
||||
try {
|
||||
setUpTestFixtures();
|
||||
@ -61,84 +96,126 @@ function test_withCollectionList_fail() {
|
||||
let error;
|
||||
try {
|
||||
Service.wipeServer(["non-existent", "steam", "petrol", "diesel"]);
|
||||
do_throw("Should have thrown!");
|
||||
} catch(ex) {
|
||||
error = ex;
|
||||
}
|
||||
_("wipeServer() threw this exception: " + error);
|
||||
do_check_true(error != undefined);
|
||||
do_check_eq(error.status, 503);
|
||||
|
||||
_("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);
|
||||
|
||||
} finally {
|
||||
server.stop(do_test_finished);
|
||||
server.stop(run_next_test);
|
||||
Svc.Prefs.resetBranch("");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function test_wipeServer_leaves_collections() {
|
||||
_("Service.wipeServer() deletes everything but keys.");
|
||||
add_test(function test_wipeServer_all_success() {
|
||||
_("Service.wipeServer() deletes all the things.");
|
||||
|
||||
let steam_coll = new FakeCollection();
|
||||
let diesel_coll = new FakeCollection();
|
||||
let keys_coll = new FakeCollection();
|
||||
|
||||
function info_collections(request, response) {
|
||||
let collections = {};
|
||||
let timestamp = Date.now() / 1000;
|
||||
if (!steam_coll.deleted)
|
||||
collections.steam = timestamp
|
||||
if (!diesel_coll.deleted)
|
||||
collections.diesel = timestamp;
|
||||
if (!keys_coll.deleted)
|
||||
collections.keys = timestamp;
|
||||
let body = JSON.stringify(collections);
|
||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||
response.bodyOutputStream.write(body, body.length);
|
||||
/**
|
||||
* Handle the bulk DELETE request sent by wipeServer.
|
||||
*/
|
||||
let deleted = false;
|
||||
let serverTimestamp;
|
||||
function storageHandler(request, response) {
|
||||
do_check_eq("DELETE", request.method);
|
||||
do_check_true(request.hasHeader("X-Confirm-Delete"));
|
||||
deleted = true;
|
||||
serverTimestamp = return_timestamp(request, response);
|
||||
}
|
||||
|
||||
let server = httpd_setup({
|
||||
"/1.1/johndoe/storage/steam": steam_coll.handler(),
|
||||
"/1.1/johndoe/storage/diesel": diesel_coll.handler(),
|
||||
"/1.1/johndoe/storage/keys": keys_coll.handler(),
|
||||
"/1.1/johndoe/info/collections": info_collections
|
||||
"/1.1/johndoe/storage": storageHandler
|
||||
});
|
||||
do_test_pending();
|
||||
setUpTestFixtures();
|
||||
|
||||
try {
|
||||
setUpTestFixtures();
|
||||
_("Info URL: " + Service.infoURL);
|
||||
_("Try deletion.");
|
||||
let returnedTimestamp = Service.wipeServer();
|
||||
do_check_true(deleted);
|
||||
do_check_eq(returnedTimestamp, serverTimestamp);
|
||||
|
||||
_("Confirm initial environment.");
|
||||
do_check_false(steam_coll.deleted);
|
||||
do_check_false(diesel_coll.deleted);
|
||||
do_check_false(keys_coll.deleted);
|
||||
|
||||
_("Collections: " + new Resource(Service.infoURL).get());
|
||||
_("Try deletion.");
|
||||
Service.wipeServer();
|
||||
_("Collections: " + new Resource(Service.infoURL).get());
|
||||
|
||||
_("Make sure keys is still present.");
|
||||
do_check_true(steam_coll.deleted);
|
||||
do_check_true(diesel_coll.deleted);
|
||||
do_check_false(keys_coll.deleted);
|
||||
|
||||
_("Delete everything.");
|
||||
Service.wipeServer(null, true);
|
||||
do_check_true(steam_coll.deleted);
|
||||
do_check_true(diesel_coll.deleted);
|
||||
do_check_true(keys_coll.deleted);
|
||||
|
||||
} finally {
|
||||
server.stop(do_test_finished);
|
||||
Svc.Prefs.resetBranch("");
|
||||
server.stop(run_next_test);
|
||||
Svc.Prefs.resetBranch("");
|
||||
});
|
||||
|
||||
add_test(function test_wipeServer_all_404() {
|
||||
_("Service.wipeServer() accepts a 404.");
|
||||
|
||||
/**
|
||||
* Handle the bulk DELETE request sent by wipeServer. Returns a 404.
|
||||
*/
|
||||
let deleted = false;
|
||||
let serverTimestamp;
|
||||
function storageHandler(request, response) {
|
||||
do_check_eq("DELETE", request.method);
|
||||
do_check_true(request.hasHeader("X-Confirm-Delete"));
|
||||
deleted = true;
|
||||
serverTimestamp = new_timestamp();
|
||||
response.setHeader("X-Weave-Timestamp", "" + serverTimestamp);
|
||||
response.setStatusLine(request.httpVersion, 404, "Not Found");
|
||||
}
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
initTestLogging("Trace");
|
||||
test_withCollectionList_fail();
|
||||
test_wipeServer_leaves_collections();
|
||||
}
|
||||
let server = httpd_setup({
|
||||
"/1.1/johndoe/storage": storageHandler
|
||||
});
|
||||
setUpTestFixtures();
|
||||
|
||||
_("Try deletion.");
|
||||
let returnedTimestamp = Service.wipeServer();
|
||||
do_check_true(deleted);
|
||||
do_check_eq(returnedTimestamp, serverTimestamp);
|
||||
|
||||
server.stop(run_next_test);
|
||||
Svc.Prefs.resetBranch("");
|
||||
});
|
||||
|
||||
add_test(function test_wipeServer_all_503() {
|
||||
_("Service.wipeServer() throws if it encounters a non-200/404 response.");
|
||||
|
||||
/**
|
||||
* Handle the bulk DELETE request sent by wipeServer. Returns a 503.
|
||||
*/
|
||||
function storageHandler(request, response) {
|
||||
do_check_eq("DELETE", request.method);
|
||||
do_check_true(request.hasHeader("X-Confirm-Delete"));
|
||||
response.setStatusLine(request.httpVersion, 503, "Service Unavailable");
|
||||
}
|
||||
|
||||
let server = httpd_setup({
|
||||
"/1.1/johndoe/storage": storageHandler
|
||||
});
|
||||
setUpTestFixtures();
|
||||
|
||||
_("Try deletion.");
|
||||
let error;
|
||||
try {
|
||||
Service.wipeServer();
|
||||
do_throw("Should have thrown!");
|
||||
} catch (ex) {
|
||||
error = ex;
|
||||
}
|
||||
do_check_eq(error.status, 503);
|
||||
|
||||
server.stop(run_next_test);
|
||||
Svc.Prefs.resetBranch("");
|
||||
});
|
||||
|
||||
add_test(function test_wipeServer_all_connectionRefused() {
|
||||
_("Service.wipeServer() throws if it encounters a network problem.");
|
||||
setUpTestFixtures();
|
||||
|
||||
_("Try deletion.");
|
||||
try {
|
||||
Service.wipeServer();
|
||||
do_throw("Should have thrown!");
|
||||
} catch (ex) {
|
||||
do_check_eq(ex.result, Cr.NS_ERROR_CONNECTION_REFUSED);
|
||||
}
|
||||
|
||||
run_next_test();
|
||||
Svc.Prefs.resetBranch("");
|
||||
});
|
||||
|
@ -71,23 +71,6 @@ function cleanUpAndGo(server) {
|
||||
});
|
||||
}
|
||||
|
||||
let timer;
|
||||
function waitForZeroTimer(callback) {
|
||||
// First wait >100ms (nsITimers can take up to that much time to fire, so
|
||||
// we can account for the timer in delayedAutoconnect) and then two event
|
||||
// loop ticks (to account for the Utils.nextTick() in autoConnect).
|
||||
let ticks = 2;
|
||||
function wait() {
|
||||
if (ticks) {
|
||||
ticks -= 1;
|
||||
Utils.nextTick(wait);
|
||||
return;
|
||||
}
|
||||
callback();
|
||||
}
|
||||
timer = Utils.namedTimer(wait, 150, {}, "timer");
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
initTestLogging("Trace");
|
||||
|
||||
@ -126,7 +109,7 @@ add_test(function test_prefAttributes() {
|
||||
do_check_eq(SyncScheduler.syncThreshold, THRESHOLD);
|
||||
|
||||
_("'globalScore' corresponds to preference, defaults to zero.");
|
||||
do_check_eq(Svc.Prefs.get('globalScore'), undefined);
|
||||
do_check_eq(Svc.Prefs.get('globalScore'), 0);
|
||||
do_check_eq(SyncScheduler.globalScore, 0);
|
||||
SyncScheduler.globalScore = SCORE;
|
||||
do_check_eq(SyncScheduler.globalScore, SCORE);
|
||||
@ -863,3 +846,80 @@ add_test(function test_sync_503_Retry_After() {
|
||||
|
||||
cleanUpAndGo(server);
|
||||
});
|
||||
|
||||
add_test(function test_loginError_recoverable_reschedules() {
|
||||
_("Verify that a recoverable login error schedules a new sync.");
|
||||
Service.username = "johndoe";
|
||||
Service.password = "ilovejane";
|
||||
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
|
||||
Service.clusterURL = "http://localhost:8080/";
|
||||
Service.persistLogin();
|
||||
Status.resetSync(); // reset Status.login
|
||||
|
||||
Svc.Obs.add("weave:service:login:error", function onLoginError() {
|
||||
Svc.Obs.remove("weave:service:login:error", onLoginError);
|
||||
Utils.nextTick(function aLittleBitAfterLoginError() {
|
||||
do_check_eq(Status.login, LOGIN_FAILED_NETWORK_ERROR);
|
||||
|
||||
let expectedNextSync = Date.now() + SyncScheduler.syncInterval;
|
||||
do_check_true(SyncScheduler.nextSync > Date.now());
|
||||
do_check_true(SyncScheduler.nextSync <= expectedNextSync);
|
||||
do_check_true(SyncScheduler.syncTimer.delay > 0);
|
||||
do_check_true(SyncScheduler.syncTimer.delay <= SyncScheduler.syncInterval);
|
||||
|
||||
Svc.Obs.remove("weave:service:sync:start", onSyncStart);
|
||||
cleanUpAndGo();
|
||||
});
|
||||
});
|
||||
|
||||
// Let's set it up so that a sync is overdue, both in terms of previously
|
||||
// scheduled syncs and the global score. We still do not expect an immediate
|
||||
// sync because we just tried (duh).
|
||||
SyncScheduler.nextSync = Date.now() - 100000;
|
||||
SyncScheduler.globalScore = SINGLE_USER_THRESHOLD + 1;
|
||||
function onSyncStart() {
|
||||
do_throw("Shouldn't have started a sync!");
|
||||
}
|
||||
Svc.Obs.add("weave:service:sync:start", onSyncStart);
|
||||
|
||||
// Sanity check.
|
||||
do_check_eq(SyncScheduler.syncTimer, null);
|
||||
do_check_eq(Status.checkSetup(), STATUS_OK);
|
||||
do_check_eq(Status.login, LOGIN_SUCCEEDED);
|
||||
|
||||
SyncScheduler.scheduleNextSync(0);
|
||||
});
|
||||
|
||||
add_test(function test_loginError_fatal_clearsTriggers() {
|
||||
_("Verify that a fatal login error clears sync triggers.");
|
||||
Service.username = "johndoe";
|
||||
Service.password = "ilovejane";
|
||||
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
|
||||
Service.clusterURL = "http://localhost:8080/";
|
||||
Service.persistLogin();
|
||||
Status.resetSync(); // reset Status.login
|
||||
|
||||
let server = httpd_setup({
|
||||
"/1.1/johndoe/info/collections": httpd_handler(401, "Unauthorized")
|
||||
});
|
||||
|
||||
Svc.Obs.add("weave:service:login:error", function onLoginError() {
|
||||
Svc.Obs.remove("weave:service:login:error", onLoginError);
|
||||
Utils.nextTick(function aLittleBitAfterLoginError() {
|
||||
do_check_eq(Status.login, LOGIN_FAILED_LOGIN_REJECTED);
|
||||
|
||||
do_check_eq(SyncScheduler.nextSync, 0);
|
||||
do_check_eq(SyncScheduler.syncTimer, null);
|
||||
|
||||
cleanUpAndGo(server);
|
||||
});
|
||||
});
|
||||
|
||||
// Sanity check.
|
||||
do_check_eq(SyncScheduler.nextSync, 0);
|
||||
do_check_eq(SyncScheduler.syncTimer, null);
|
||||
do_check_eq(Status.checkSetup(), STATUS_OK);
|
||||
do_check_eq(Status.login, LOGIN_SUCCEEDED);
|
||||
|
||||
SyncScheduler.scheduleNextSync(0);
|
||||
});
|
||||
|
@ -48,6 +48,7 @@ skip-if = os == "win" || os == "android"
|
||||
[test_keys.js]
|
||||
[test_load_modules.js]
|
||||
[test_log4moz.js]
|
||||
[test_node_reassignment.js]
|
||||
[test_notifications.js]
|
||||
[test_password_store.js]
|
||||
[test_password_tracker.js]
|
||||
|
@ -126,7 +126,7 @@ var Logger =
|
||||
var now = new Date()
|
||||
this.write(now.getFullYear() + "-" + (now.getMonth() < 9 ? '0' : '') +
|
||||
(now.getMonth() + 1) + "-" +
|
||||
(now.getDay() < 9 ? '0' : '') + (now.getDay() + 1) + " " +
|
||||
(now.getDate() < 9 ? '0' : '') + (now.getDate() + 1) + " " +
|
||||
(now.getHours() < 10 ? '0' : '') + now.getHours() + ":" +
|
||||
(now.getMinutes() < 10 ? '0' : '') + now.getMinutes() + ":" +
|
||||
(now.getSeconds() < 10 ? '0' : '') + now.getSeconds() + " " +
|
||||
|
@ -140,7 +140,6 @@ var TPS =
|
||||
Logger.logInfo("sync error; retrying...");
|
||||
this._syncErrors++;
|
||||
this._waitingForSync = false;
|
||||
Weave.Service.logout();
|
||||
Utils.nextTick(this.RunNextTestAction, this);
|
||||
}
|
||||
else if (this._waitingForSync) {
|
||||
@ -156,7 +155,6 @@ var TPS =
|
||||
// Wait a second before continuing, otherwise we can get
|
||||
// 'sync not complete' errors.
|
||||
Utils.namedTimer(function() {
|
||||
Weave.Service.logout();
|
||||
this.FinishAsyncOperation();
|
||||
}, 1000, this, "postsync");
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ from setuptools import setup, find_packages
|
||||
version = '0.2.40'
|
||||
|
||||
deps = ['pulsebuildmonitor >= 0.2', 'MozillaPulse == .4',
|
||||
'mozinfo == 0.3.1', 'mozprofile == 0.1a',
|
||||
'mozinfo == 0.3.1', 'mozprofile == 0.1t',
|
||||
'mozprocess == 0.1a', 'mozrunner == 3.0a', 'mozregression == 0.3',
|
||||
'mozautolog >= 0.2.1']
|
||||
|
||||
|
@ -37,7 +37,7 @@
|
||||
|
||||
import datetime
|
||||
|
||||
def GenerateEmailBody(data, numpassed, numfailed, serverUrl):
|
||||
def GenerateEmailBody(data, numpassed, numfailed, serverUrl, buildUrl):
|
||||
|
||||
now = datetime.datetime.now()
|
||||
builddate = datetime.datetime.strptime(data['productversion']['buildid'],
|
||||
@ -46,7 +46,7 @@ def GenerateEmailBody(data, numpassed, numfailed, serverUrl):
|
||||
|
||||
row = """
|
||||
<tr>
|
||||
<td><a href="http://hg.mozilla.org/services/services-central/file/tip/services/sync/tests/tps/{name}">{name}</a></td>
|
||||
<td><a href="http://hg.mozilla.org/services/services-central/file/default/services/sync/tests/tps/{name}">{name}</a></td>
|
||||
<td>{state}</td>
|
||||
<td>{message}</td>
|
||||
</tr>
|
||||
@ -54,7 +54,7 @@ def GenerateEmailBody(data, numpassed, numfailed, serverUrl):
|
||||
|
||||
rowWithLog = """
|
||||
<tr>
|
||||
<td><a href="http://hg.mozilla.org/services/services-central/services/sync/tests/tps/file/tip/{name}">{name}</a></td>
|
||||
<td><a href="http://hg.mozilla.org/services/services-central/file/default/services/sync/tests/tps/{name}">{name}</a></td>
|
||||
<td>{state}</td>
|
||||
<td>{message} [<a href="{logurl}">view log</a>]</td>
|
||||
</tr>
|
||||
@ -72,6 +72,9 @@ def GenerateEmailBody(data, numpassed, numfailed, serverUrl):
|
||||
state=test['state'],
|
||||
message=test['message'] if test['message'] else 'None')
|
||||
|
||||
firefox_version = data['productversion']['version']
|
||||
if buildUrl is not None:
|
||||
firefox_version = "<a href='%s'>%s</a>" % (buildUrl, firefox_version)
|
||||
body = """
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
@ -169,7 +172,7 @@ def GenerateEmailBody(data, numpassed, numfailed, serverUrl):
|
||||
</html>
|
||||
|
||||
""".format(date=now.ctime(),
|
||||
firefox_version=data['productversion']['version'],
|
||||
firefox_version=firefox_version,
|
||||
firefox_date=builddate.ctime(),
|
||||
sync_version=data['addonversion']['version'],
|
||||
sync_type=data['synctype'],
|
||||
|
@ -433,7 +433,14 @@ class TPSTestRunner(object):
|
||||
from tps.emailtemplate import GenerateEmailBody
|
||||
|
||||
if body is None:
|
||||
body = GenerateEmailBody(self.postdata, self.numpassed, self.numfailed, self.config['account']['serverURL'])
|
||||
buildUrl = None
|
||||
if self.firefoxRunner and self.firefoxRunner.url:
|
||||
buildUrl = self.firefoxRunner.url
|
||||
body = GenerateEmailBody(self.postdata,
|
||||
self.numpassed,
|
||||
self.numfailed,
|
||||
self.config['account']['serverURL'],
|
||||
self.buildUrl)
|
||||
|
||||
subj = "TPS Report: "
|
||||
if self.numfailed == 0 and self.numpassed > 0:
|
||||
|
Loading…
Reference in New Issue
Block a user