Merge m-c to m-i

This commit is contained in:
Philipp von Weitershausen 2011-08-23 18:33:02 -07:00
commit 6c71c32ba7
40 changed files with 496 additions and 292 deletions

View File

@ -120,20 +120,20 @@
label=" "
onpageshow="gSyncAddDevice.onPageShow();">
<description>
&addDevice.dialog.syncKey.label;
&addDevice.dialog.recoveryKey.label;
</description>
<spacer/>
<groupbox>
<label value="&syncKeyEntry.label;"
accesskey="&syncKeyEntry.accesskey;"
<label value="&recoveryKeyEntry.label;"
accesskey="&recoveryKeyEntry.accesskey;"
control="weavePassphrase"/>
<textbox id="weavePassphrase"
readonly="true"/>
</groupbox>
<groupbox align="center">
<description>&syncKeyBackup.description;</description>
<description>&recoveryKeyBackup.description;</description>
<hbox>
<button id="printSyncKeyButton"
label="&button.syncKeyBackup.print.label;"

View File

@ -92,15 +92,15 @@ let Change = {
document.getElementById("textBox1Row").hidden = true;
document.getElementById("textBox2Row").hidden = true;
document.getElementById("passphraseLabel").value
= this._str("new.synckey.label");
= this._str("new.recoverykey.label");
document.getElementById("passphraseSpacer").hidden = false;
if (this._updatingPassphrase) {
document.getElementById("passphraseHelpBox").hidden = false;
document.title = this._str("new.synckey.title");
introText.textContent = this._str("new.synckey2.introText");
document.title = this._str("new.recoverykey.title");
introText.textContent = this._str("new.recoverykey.introText");
this._dialog.getButton("finish").label
= this._str("new.synckey.acceptButton");
= this._str("new.recoverykey.acceptButton");
}
else {
document.getElementById("generatePassphraseButton").hidden = false;
@ -111,11 +111,11 @@ let Change = {
pp = Weave.Utils.hyphenatePassphrase(pp);
this._passphraseBox.value = pp;
this._passphraseBox.focus();
document.title = this._str("change.synckey2.title");
document.title = this._str("change.recoverykey.title");
introText.textContent = this._str("change.synckey.introText2");
warningText.textContent = this._str("change.synckey2.warningText");
warningText.textContent = this._str("change.recoverykey.warningText");
this._dialog.getButton("finish").label
= this._str("change.synckey.acceptButton");
= this._str("change.recovery.acceptButton");
if (this._duringSetup) {
this._dialog.getButton("finish").disabled = false;
}
@ -137,7 +137,7 @@ let Change = {
else {
document.title = this._str("change.password.title");
box2label.value = this._str("new.password.confirm");
introText.textContent = this._str("change.password2.introText");
introText.textContent = this._str("change.password3.introText");
warningText.textContent = this._str("change.password.warningText");
this._dialog.getButton("finish").label
= this._str("change.password.acceptButton");
@ -195,7 +195,7 @@ let Change = {
if (this._updatingPassphrase) {
Weave.Service.passphrase = pp;
if (Weave.Service.login()) {
this._updateStatus("change.synckey2.success", "success");
this._updateStatus("change.recoverykey.success", "success");
Weave.Service.persistLogin();
}
else {
@ -203,12 +203,12 @@ let Change = {
}
}
else {
this._updateStatus("change.synckey.label", "active");
this._updateStatus("change.recoverykey.label", "active");
if (Weave.Service.changePassphrase(pp))
this._updateStatus("change.synckey2.success", "success");
this._updateStatus("change.recoverykey.success", "success");
else
this._updateStatus("change.synckey2.error", "error");
this._updateStatus("change.recoverykey.error", "error");
}
return false;

View File

@ -138,7 +138,7 @@
<vbox id="passphraseHelpBox"
hidden="true">
<description>
&existingSyncKey.description;
&existingRecoveryKey.description;
<label class="text-link"
href="https://services.mozilla.com/sync/help/manual-setup">
&addDevice.showMeHow.label;

View File

@ -196,17 +196,17 @@
</grid>
</wizardpage>
<wizardpage label="&setup.newSyncKeyPage.title.label;"
<wizardpage label="&setup.newRecoveryKeyPage.title.label;"
onextra1="gSyncSetup.onSyncOptions()"
onpageshow="gSyncSetup.onPageShow();">
<description>
&setup.newSyncKeyPage.description.label;
&setup.newRecoveryKeyPage.description.label;
</description>
<spacer/>
<groupbox>
<label value="&syncKeyEntry.label;"
accesskey="&syncKeyEntry.accesskey;"
<label value="&recoveryKeyEntry.label;"
accesskey="&recoveryKeyEntry.accesskey;"
control="weavePassphrase"/>
<textbox id="weavePassphrase"
readonly="true"
@ -214,7 +214,7 @@
</groupbox>
<groupbox align="center">
<description>&syncKeyBackup.description;</description>
<description>&recoveryKeyBackup.description;</description>
<hbox>
<button id="printSyncKeyButton"
label="&button.syncKeyBackup.print.label;"
@ -351,8 +351,8 @@
<groupbox>
<label id="existingPassphraseLabel"
value="&signIn.syncKey.label;"
accesskey="&signIn.syncKey.accesskey;"
value="&signIn.recoveryKey.label;"
accesskey="&signIn.recoveryKey.accesskey;"
control="existingPassphrase"/>
<textbox id="existingPassphrase"
oninput="gSyncSetup.checkFields()"/>
@ -370,7 +370,7 @@
<vbox id="passphraseHelpBox">
<description>
&existingSyncKey.description;
&existingRecoveryKey.description;
<label class="text-link"
href="https://services.mozilla.com/sync/help/manual-setup">
&addDevice.showMeHow.label;

View File

@ -193,8 +193,8 @@ let gSyncUtils = {
* @param elid : ID of the form element containing the passphrase.
*/
passphraseSave: function(elid) {
let dialogTitle = this.bundle.GetStringFromName("save.synckey.title");
let defaultSaveName = this.bundle.GetStringFromName("save.default.label");
let dialogTitle = this.bundle.GetStringFromName("save.recoverykey.title");
let defaultSaveName = this.bundle.GetStringFromName("save.recoverykey.defaultfilename");
this._preparePPiframe(elid, function(iframe) {
let filepicker = Cc["@mozilla.org/filepicker;1"]
.createInstance(Ci.nsIFilePicker);
@ -243,7 +243,7 @@ let gSyncUtils = {
else if (val1 && val1 == Weave.Service.password)
error = "change.password.pwSameAsPassword";
else if (val1 && val1 == Weave.Service.passphrase)
error = "change.password.pwSameAsSyncKey";
error = "change.password.pwSameAsRecoveryKey";
else if (val1 && val2) {
if (val1 == val2 && val1.length >= Weave.MIN_PASS_LENGTH)
valid = true;

View File

@ -1,25 +1,25 @@
# LOCALIZATION NOTE (change.password.title): This (and associated change.password/passphrase) are used when the user elects to change their password.
#LOCALIZATION NOTE (change.password.title): This (and associated change.password/passphrase) are used when the user elects to change their password.
change.password.title = Change your Password
change.password.acceptButton = Change Password
change.password.status.active = Changing your password…
change.password.status.success = Your password has been changed.
change.password.status.error = There was an error changing your password.
change.password2.introText = Your password must be at least 8 characters long. It cannot be the same as either your user name or your Sync Key.
change.password3.introText = Your password must be at least 8 characters long. It cannot be the same as either your user name or your Recovery Key.
change.password.warningText = Note: All of your other devices will be unable to connect to your account once you change this password.
change.synckey2.title = My Sync Key
change.synckey.acceptButton = Change Sync Key
change.synckey.label = Changing Sync Key and uploading local data, please wait…
change.synckey2.error = There was an error while changing your Sync Key!
change.synckey2.success = Your Sync Key was successfully changed!
change.recoverykey.title = My Recovery Key
change.synckey.acceptButton = Change Recovery Key
change.recoverykey.label = Changing Recovery Key and uploading local data, please wait…
change.recoverykey.error = There was an error while changing your Recovery Key!
change.recoverykey.success = Your Recovery Key was successfully changed!
change.synckey.introText = Firefox Cares About Your Privacy
change.synckey.introText2 = To ensure your total privacy, all of your data is encrypted prior to being uploaded. The key to decrypt your data is not uploaded.
# LOCALIZATION NOTE (change.synckey2.warningText) "Sync" should match &syncBrand.shortName.label; from syncBrand.dtd
change.synckey2.warningText = Note: Changing this will erase all data stored on the Sync server and upload new data secured by this Sync Key. Your other devices will not sync until the new Sync Key is entered for that device.
# LOCALIZATION NOTE (change.recoverykey.warningText) "Sync" should match &syncBrand.shortName.label; from syncBrand.dtd
change.recovery.warningText = Note: Changing this will erase all data stored on the Sync server and upload new data secured by this Recovery Key. Your other devices will not sync until the new Recovery Key is entered for that device.
new.synckey.label = Your Sync Key
new.recoverykey.label = Your Recovery Key
# LOCALIZATION NOTE (new.password.title): This (and associated new.password/passphrase) are used on a second computer when it detects that your password or passphrase has been changed on a different device.
new.password.title = Update Password
@ -29,7 +29,6 @@ new.password.confirm = Confirm your new password
new.password.acceptButton = Update Password
new.password.status.incorrect = Password incorrect, please try again.
new.synckey.title = Update Sync Key
new.synckey2.introText = Your Sync Key was changed using another device, please enter your updated Sync Key.
new.synckey.acceptButton = Update Sync Key
new.synckey.status.incorrect = Sync Key incorrect, please try again.
new.recoverykey.title = Update Recovery Key
new.recoverykey.introText = Your Recovery Key was changed using another device, please enter your updated Recovery Key.
new.recoverykey.acceptButton = Update Recovery Key

View File

@ -19,8 +19,8 @@
<!ENTITY signIn.account2.accesskey "A">
<!ENTITY signIn.password.label "Password">
<!ENTITY signIn.password.accesskey "P">
<!ENTITY signIn.syncKey.label "Sync Key">
<!ENTITY signIn.syncKey.accesskey "K">
<!ENTITY signIn.recoveryKey.label "Recovery Key">
<!ENTITY signIn.recoveryKey.accesskey "K">
<!-- New Account Page 1: Basic Account Info -->
<!ENTITY setup.newAccountDetailsPage.title.label "Account Details">
@ -41,12 +41,12 @@
<!ENTITY setup.tosAgree2.accesskey "">
<!-- New Account Page 2: Sync Key -->
<!ENTITY setup.newSyncKeyPage.title.label "&brandShortName; Cares About Your Privacy">
<!ENTITY setup.newSyncKeyPage.description.label "To ensure your total privacy, all of your data is encrypted prior to being uploaded. The Sync Key which is necessary to decrypt your data is not uploaded.">
<!ENTITY syncKeyEntry.label "Your Sync Key">
<!ENTITY syncKeyEntry.accesskey "K">
<!ENTITY setup.newRecoveryKeyPage.title.label "&brandShortName; Cares About Your Privacy">
<!ENTITY setup.newRecoveryKeyPage.description.label "To ensure your total privacy, all of your data is encrypted prior to being uploaded. The Recovery Key which is necessary to decrypt your data is not uploaded.">
<!ENTITY recoveryKeyEntry.label "Your Recovery Key">
<!ENTITY recoveryKeyEntry.accesskey "K">
<!ENTITY syncGenerateNewKey.label "Generate a new key">
<!ENTITY syncKeyBackup.description "Your Sync Key is required to access &syncBrand.fullName.label; on other machines. Please create a backup copy. We cannot help you recover your Sync Key.">
<!ENTITY recoveryKeyBackup.description "Your Recovery Key is required to access &syncBrand.fullName.label; on other machines. Please create a backup copy. We cannot help you recover your Recovery Key.">
<!ENTITY button.syncKeyBackup.print.label "Print…">
<!ENTITY button.syncKeyBackup.print.accesskey "P">
@ -65,12 +65,12 @@
<!ENTITY addDevice.dialog.enterCode.label "Enter the code that the device provides:">
<!ENTITY addDevice.dialog.tryAgain.label "Please try again.">
<!ENTITY addDevice.dialog.successful.label "The device has been successfully added. The initial synchronization can take several minutes and will finish in the background.">
<!ENTITY addDevice.dialog.syncKey.label "To activate your device you will need to enter your Sync Key. Please print or save this key and take it with you.">
<!ENTITY addDevice.dialog.recoveryKey.label "To activate your device you will need to enter your Recovery Key. Please print or save this key and take it with you.">
<!ENTITY addDevice.dialog.connected.label "Device Connected">
<!-- Existing Account Page 2: Manual Login -->
<!ENTITY setup.signInPage.title.label "Sign In">
<!ENTITY existingSyncKey.description "You can get a copy of your Sync Key by going to &syncBrand.shortName.label; Options on your other device, and selecting &#x0022;My Sync Key&#x0022; under &#x0022;Manage Account&#x0022;.">
<!ENTITY existingRecoveryKey.description "You can get a copy of your Recovery Key by going to &syncBrand.shortName.label; Options on your other device, and selecting &#x0022;My Recovery Key&#x0022; under &#x0022;Manage Account&#x0022;.">
<!ENTITY verifying.label "Verifying…">
<!ENTITY resetPassword.label "Reset Password">
<!ENTITY resetSyncKey.label "I have lost my other device.">

View File

@ -29,8 +29,8 @@ historyDaysCount.label = #1 day of history;#1 days of history
# #1 is the number of passwords (was %S for a short while, use #1 instead, even if both work)
passwordsCount.label = #1 password;#1 passwords
save.synckey.title = Save Sync Key
save.default.label = Firefox Sync Key.html
save.recoverykey.title = Save Recovery Key
save.recoverykey.defaultfilename = Firefox Recovery Key.html
newAccount.action.label = Firefox Sync is now set up to automatically sync all of your browser data.
newAccount.change.label = You can choose exactly what to sync by selecting Sync Options below.

View File

@ -572,7 +572,7 @@
<separator/>
<textbox id="syncsetup-account" class="prompt-edit" placeholder="&sync.account;" oninput="WeaveGlue.canConnect();"/>
<textbox id="syncsetup-password" class="prompt-edit" placeholder="&sync.password;" type="password" oninput="WeaveGlue.canConnect();"/>
<textbox id="syncsetup-synckey" class="prompt-edit" placeholder="&sync.syncKey;" oninput="WeaveGlue.canConnect();"/>
<textbox id="syncsetup-synckey" class="prompt-edit" placeholder="&sync.recoveryKey;" oninput="WeaveGlue.canConnect();"/>
<separator class="thin"/>
<button id="syncsetup-usecustomserver" type="checkbox" class="button-checkbox" pack="start" oncommand="WeaveGlue.toggleCustomServer();">
<image class="button-image-icon"/>

View File

@ -14,7 +14,7 @@
<!ENTITY sync.setup.manual "Enter your Sync account information">
<!ENTITY sync.account "Account Name">
<!ENTITY sync.password "Password">
<!ENTITY sync.syncKey "Sync Key">
<!ENTITY sync.recoveryKey "Recovery Key">
<!ENTITY sync.customServer "Use custom server">
<!ENTITY sync.serverURL "Server URL">
<!ENTITY sync.setup.connect "Connect">

View File

@ -1,9 +1,9 @@
error.login.reason.network = Failed to connect to the server
error.login.reason.synckey = Wrong Sync Key
error.login.reason.recoverykey = Wrong Recovery Key
error.login.reason.account = Incorrect account name or password
error.login.reason.no_username = Missing account name
error.login.reason.no_password2 = Missing password
error.login.reason.no_synckey = No saved Sync Key to use
error.login.reason.no_recoverykey= No saved Recovery Key to use
error.login.reason.server = Server incorrectly configured
error.sync.failed_partial = One or more data types could not be synced
@ -14,13 +14,7 @@ weak-password = Use a stronger password
# this is the fallback, if we hit an error we didn't bother to localize
error.reason.unknown = Unknown error
change.synckey.sameAsSyncKey = The new Sync Key cannot be the same as your Sync Key
change.synckey.sameAsPassword = The Sync Key cannot be the same as your password
change.synckey.sameAsUsername = The Sync Key cannot be the same as your user name
change.synckey.sameAsEmail = The Sync Key cannot be the same as your email address
change.synckey.tooShort = The Sync Key entered is too short
change.password.pwSameAsSyncKey = Password can't match your Sync Key
change.password.pwSameAsRecoveryKey = Password can't match your Recovery Key
change.password.pwSameAsPassword = Password can't match current password
change.password.pwSameAsUsername = Password can't match your user name
change.password.pwSameAsEmail = Password can't match your email address

View File

@ -300,7 +300,7 @@ let Async = {
let row;
while ((row = results.getNextRow()) != null) {
let item = {};
for each (name in this.names) {
for each (let name in this.names) {
item[name] = row.getResultByName(name);
}
this.results.push(item);

View File

@ -147,10 +147,10 @@ ENGINE_SUCCEEDED: "success.engine",
// login failure status codes:
LOGIN_FAILED_NO_USERNAME: "error.login.reason.no_username",
LOGIN_FAILED_NO_PASSWORD: "error.login.reason.no_password2",
LOGIN_FAILED_NO_PASSPHRASE: "error.login.reason.no_synckey",
LOGIN_FAILED_NO_PASSPHRASE: "error.login.reason.no_recoverykey",
LOGIN_FAILED_NETWORK_ERROR: "error.login.reason.network",
LOGIN_FAILED_SERVER_ERROR: "error.login.reason.server",
LOGIN_FAILED_INVALID_PASSPHRASE: "error.login.reason.synckey",
LOGIN_FAILED_INVALID_PASSPHRASE: "error.login.reason.recoverykey",
LOGIN_FAILED_LOGIN_REJECTED: "error.login.reason.account",
// sync failure status codes

View File

@ -622,8 +622,9 @@ SyncEngine.prototype = {
// Mark all items to be uploaded, but treat them as changed from long ago
this._log.debug("First sync, uploading all items");
this._modified = {};
for (let id in this._store.getAllIDs())
for (let id in this._store.getAllIDs()) {
this._modified[id] = 0;
}
}
// Clear the tracker now. If the sync fails we'll add the ones we failed
// to upload back.
@ -631,7 +632,7 @@ SyncEngine.prototype = {
// Array of just the IDs from this._modified. This is what we iterate over
// so we can modify this._modified during the iteration.
this._modifiedIDs = [id for (id in this._modified)];
this._modifiedIDs = Object.keys(this._modified);
this._log.info(this._modifiedIDs.length +
" outgoing items pre-reconciliation");
@ -652,7 +653,7 @@ SyncEngine.prototype = {
batchSize = MOBILE_BATCH_SIZE;
}
newitems.newer = this.lastSync;
newitems.full = true;
newitems.full = true;
newitems.limit = batchSize;
// applied => number of items that should be applied.
@ -1002,7 +1003,7 @@ SyncEngine.prototype = {
if (modified > this.lastSync)
this.lastSync = modified;
let failed_ids = [id for (id in resp.obj.failed)];
let failed_ids = Object.keys(resp.obj.failed);
if (failed_ids.length)
this._log.debug("Records that will be uploaded again because "
+ "the server couldn't store them: "
@ -1079,8 +1080,8 @@ SyncEngine.prototype = {
for (let [id, when] in Iterator(this._modified)) {
this._tracker.addChangedID(id, when);
}
delete this._modified;
delete this._modifiedIDs;
this._modified = {};
this._modifiedIDs = [];
},
_sync: function SyncEngine__sync() {

View File

@ -113,8 +113,9 @@ PlacesItem.prototype = {
_logName: "Sync.Record.PlacesItem",
};
Utils.deferGetSet(PlacesItem, "cleartext", ["hasDupe", "parentid", "parentName",
"type"]);
Utils.deferGetSet(PlacesItem,
"cleartext",
["hasDupe", "parentid", "parentName", "type"]);
function Bookmark(collection, id, type) {
PlacesItem.call(this, collection, id, type || "bookmark");
@ -124,8 +125,10 @@ Bookmark.prototype = {
_logName: "Sync.Record.Bookmark",
};
Utils.deferGetSet(Bookmark, "cleartext", ["title", "bmkUri", "description",
"loadInSidebar", "tags", "keyword"]);
Utils.deferGetSet(Bookmark,
"cleartext",
["title", "bmkUri", "description",
"loadInSidebar", "tags", "keyword"]);
function BookmarkQuery(collection, id) {
Bookmark.call(this, collection, id, "query");
@ -135,8 +138,9 @@ BookmarkQuery.prototype = {
_logName: "Sync.Record.BookmarkQuery",
};
Utils.deferGetSet(BookmarkQuery, "cleartext", ["folderName",
"queryId"]);
Utils.deferGetSet(BookmarkQuery,
"cleartext",
["folderName", "queryId"]);
function BookmarkFolder(collection, id, type) {
PlacesItem.call(this, collection, id, type || "folder");
@ -170,14 +174,6 @@ BookmarkSeparator.prototype = {
Utils.deferGetSet(BookmarkSeparator, "cleartext", "pos");
function archiveBookmarks() {
// Some nightly builds of 3.7 don't have this function
try {
PlacesUtils.archiveBookmarksFile(null, true);
}
catch(ex) {}
}
let kSpecialIds = {
// Special IDs. Note that mobile can attempt to create a record on
@ -389,8 +385,9 @@ BookmarksEngine.prototype = {
SyncEngine.prototype._syncStartup.call(this);
// For first-syncs, make a backup for the user to restore
if (this.lastSync == 0)
archiveBookmarks();
if (this.lastSync == 0) {
PlacesUtils.archiveBookmarksFile(null, true);
}
this.__defineGetter__("_guidMap", function() {
// Create a mapping of folder titles and separator positions to GUID.
@ -1174,7 +1171,7 @@ BookmarksStore.prototype = {
if (record.parentid == "toolbar")
index += 150;
// Add in the bookmark's frecency if we have something
// Add in the bookmark's frecency if we have something.
if (record.bmkUri != null) {
this._frecencyStm.params.url = record.bmkUri;
let result = Async.querySpinningly(this._frecencyStm, this._frecencyCols);
@ -1217,10 +1214,10 @@ BookmarksStore.prototype = {
},
_tagURI: function BStore_tagURI(bmkURI, tags) {
// Filter out any null/undefined/empty tags
// Filter out any null/undefined/empty tags.
tags = tags.filter(function(t) t);
// Temporarily tag a dummy uri to preserve tag ids when untagging
// Temporarily tag a dummy URI to preserve tag ids when untagging.
let dummyURI = Utils.makeURI("about:weave#BStore_tagURI");
PlacesUtils.tagging.tagURI(dummyURI, tags);
PlacesUtils.tagging.untagURI(bmkURI, null);
@ -1239,8 +1236,8 @@ BookmarksStore.prototype = {
},
wipe: function BStore_wipe() {
// Save a backup before clearing out all bookmarks
archiveBookmarks();
// Save a backup before clearing out all bookmarks.
PlacesUtils.archiveBookmarksFile(null, true);
for each (let guid in kSpecialIds.guids)
if (guid != "places") {
@ -1310,10 +1307,10 @@ BookmarksTracker.prototype = {
]),
/**
* Add a bookmark guid to be uploaded and bump up the sync score
* Add a bookmark GUID to be uploaded and bump up the sync score.
*
* @param itemGuid
* Guid of the bookmark to upload
* GUID of the bookmark to upload.
*/
_add: function BMT__add(itemId, guid) {
guid = kSpecialIds.specialGUIDForId(itemId) || guid;
@ -1321,14 +1318,14 @@ BookmarksTracker.prototype = {
this._upScore();
},
/* Every add/remove/change will trigger a sync for MULTI_DEVICE */
/* Every add/remove/change will trigger a sync for MULTI_DEVICE. */
_upScore: function BMT__upScore() {
this.score += SCORE_INCREMENT_XLARGE;
},
/**
* Determine if a change should be ignored: we're ignoring everything or the
* folder is for livemarks
* folder is for livemarks.
*
* @param itemId
* Item under consideration to ignore

View File

@ -199,7 +199,8 @@ ClientEngine.prototype = {
resetEngine: { args: 1, desc: "Clear temporary local data for engine" },
wipeAll: { args: 0, desc: "Delete all client data for all engines" },
wipeEngine: { args: 1, desc: "Delete all client data for engine" },
logout: { args: 0, desc: "Log out client" }
logout: { args: 0, desc: "Log out client" },
displayURI: { args: 2, desc: "Instruct a client to display a URI" }
},
/**
@ -284,6 +285,9 @@ ClientEngine.prototype = {
case "logout":
Weave.Service.logout();
return false;
case "displayURI":
this._handleDisplayURI(args[0], args[1]);
break;
default:
this._log.debug("Received an unknown command: " + command);
break;
@ -330,6 +334,52 @@ ClientEngine.prototype = {
this._sendCommandToClient(command, args, id);
}
}
},
/**
* Send a URI to another client for display.
*
* A side effect is the score is increased dramatically to incur an
* immediate sync.
*
* If an unknown client ID is specified, sendCommand() will throw an
* Error object.
*
* @param uri
* URI (as a string) to send and display on the remote client
* @param clientId
* ID of client to send the command to. If not defined, will be sent
* to all remote clients.
*/
sendURIToClientForDisplay: function sendURIToClientForDisplay(uri, clientId) {
this._log.info("Sending URI to client: " + uri + " -> " + clientId);
this.sendCommand("displayURI", [uri, this.syncID], clientId);
Clients._tracker.score += SCORE_INCREMENT_XLARGE;
},
/**
* Handle a single received 'displayURI' command.
*
* Interested parties should observe the "weave:engine:clients:display-uri"
* topic. The callback will receive an object as the subject parameter with
* the following keys:
*
* uri URI (string) that is requested for display
* clientId ID of client that sent the command
*
* The 'data' parameter to the callback will not be defined.
*
* @param uri
* String URI that was received
* @param clientId
* ID of client that sent URI
*/
_handleDisplayURI: function _handleDisplayURI(uri, clientId) {
this._log.info("Received a URI for display: " + uri + " from " + clientId);
let subject = { uri: uri, client: clientId };
Svc.Obs.notify("weave:engine:clients:display-uri", subject);
}
};

View File

@ -64,36 +64,79 @@ Utils.deferGetSet(FormRec, "cleartext", ["name", "value"]);
let FormWrapper = {
_log: Log4Moz.repository.getLogger("Sync.Engine.Forms"),
getAllEntries: function getAllEntries() {
// Sort by (lastUsed - minLast) / (maxLast - minLast) * timesUsed / maxTimes
let query = Svc.Form.DBConnection.createAsyncStatement(
_getEntryCols: ["name", "value"],
_guidCols: ["guid"],
_stmts: {},
_getStmt: function _getStmt(query) {
if (query in this._stmts) {
return this._stmts[query];
}
this._log.trace("Creating SQL statement: " + query);
let db = Svc.Form.DBConnection;
return this._stmts[query] = db.createAsyncStatement(query);
},
get _getAllEntriesStmt() {
const query =
"SELECT fieldname name, value FROM moz_formhistory " +
"ORDER BY 1.0 * (lastUsed - (SELECT lastUsed FROM moz_formhistory ORDER BY lastUsed ASC LIMIT 1)) / " +
"((SELECT lastUsed FROM moz_formhistory ORDER BY lastUsed DESC LIMIT 1) - (SELECT lastUsed FROM moz_formhistory ORDER BY lastUsed ASC LIMIT 1)) * " +
"timesUsed / (SELECT timesUsed FROM moz_formhistory ORDER BY timesUsed DESC LIMIT 1) DESC " +
"LIMIT 500");
return Async.querySpinningly(query, ["name", "value"]);
"LIMIT 500";
return this._getStmt(query);
},
get _getEntryStmt() {
const query = "SELECT fieldname name, value FROM moz_formhistory " +
"WHERE guid = :guid";
return this._getStmt(query);
},
get _getGUIDStmt() {
const query = "SELECT guid FROM moz_formhistory " +
"WHERE fieldname = :name AND value = :value";
return this._getStmt(query);
},
get _setGUIDStmt() {
const query = "UPDATE moz_formhistory SET guid = :guid " +
"WHERE fieldname = :name AND value = :value";
return this._getStmt(query);
},
get _hasGUIDStmt() {
const query = "SELECT guid FROM moz_formhistory WHERE guid = :guid LIMIT 1";
return this._getStmt(query);
},
get _replaceGUIDStmt() {
const query = "UPDATE moz_formhistory SET guid = :newGUID " +
"WHERE guid = :oldGUID";
return this._getStmt(query);
},
getAllEntries: function getAllEntries() {
return Async.querySpinningly(this._getAllEntriesStmt, this._getEntryCols);
},
getEntry: function getEntry(guid) {
let query = Svc.Form.DBConnection.createAsyncStatement(
"SELECT fieldname name, value FROM moz_formhistory WHERE guid = :guid");
query.params.guid = guid;
return Async.querySpinningly(query, ["name", "value"])[0];
let stmt = this._getEntryStmt;
stmt.params.guid = guid;
return Async.querySpinningly(stmt, this._getEntryCols)[0];
},
getGUID: function getGUID(name, value) {
// Query for the provided entry
let getQuery = Svc.Form.DBConnection.createAsyncStatement(
"SELECT guid FROM moz_formhistory " +
"WHERE fieldname = :name AND value = :value");
getQuery.params.name = name;
getQuery.params.value = value;
// Query for the provided entry.
let getStmt = this._getGUIDStmt;
getStmt.params.name = name;
getStmt.params.value = value;
// Give the GUID if we found one.
let item = Async.querySpinningly(getStmt, this._guidCols)[0];
// Give the guid if we found one
let item = Async.querySpinningly(getQuery, ["guid"])[0];
if (!item) {
// Shouldn't happen, but Bug 597400...
// Might as well just return.
@ -102,36 +145,33 @@ let FormWrapper = {
JSON.stringify(value) + ") => " + item);
return null;
}
if (item.guid != null)
return item.guid;
// We need to create a guid for this entry
let setQuery = Svc.Form.DBConnection.createAsyncStatement(
"UPDATE moz_formhistory SET guid = :guid " +
"WHERE fieldname = :name AND value = :value");
if (item.guid != null) {
return item.guid;
}
// We need to create a GUID for this entry.
let setStmt = this._setGUIDStmt;
let guid = Utils.makeGUID();
setQuery.params.guid = guid;
setQuery.params.name = name;
setQuery.params.value = value;
Async.querySpinningly(setQuery);
setStmt.params.guid = guid;
setStmt.params.name = name;
setStmt.params.value = value;
Async.querySpinningly(setStmt);
return guid;
},
hasGUID: function hasGUID(guid) {
let query = Svc.Form.DBConnection.createAsyncStatement(
"SELECT guid FROM moz_formhistory WHERE guid = :guid LIMIT 1");
query.params.guid = guid;
return Async.querySpinningly(query, ["guid"]).length == 1;
let stmt = this._hasGUIDStmt;
stmt.params.guid = guid;
return Async.querySpinningly(stmt, this._guidCols).length == 1;
},
replaceGUID: function replaceGUID(oldGUID, newGUID) {
let query = Svc.Form.DBConnection.createAsyncStatement(
"UPDATE moz_formhistory SET guid = :newGUID WHERE guid = :oldGUID");
query.params.oldGUID = oldGUID;
query.params.newGUID = newGUID;
Async.querySpinningly(query);
let stmt = this._replaceGUIDStmt;
stmt.params.oldGUID = oldGUID;
stmt.params.newGUID = newGUID;
Async.querySpinningly(stmt);
}
};
@ -149,8 +189,9 @@ FormEngine.prototype = {
get prefName() "history",
_findDupe: function _findDupe(item) {
if (Svc.Form.entryExists(item.name, item.value))
if (Svc.Form.entryExists(item.name, item.value)) {
return FormWrapper.getGUID(item.name, item.value);
}
}
};
@ -173,8 +214,9 @@ FormStore.prototype = {
getAllIDs: function FormStore_getAllIDs() {
let guids = {};
for each (let {name, value} in FormWrapper.getAllEntries())
for each (let {name, value} in FormWrapper.getAllEntries()) {
guids[FormWrapper.getGUID(name, value)] = true;
}
return guids;
},
@ -192,9 +234,9 @@ FormStore.prototype = {
if (entry != null) {
record.name = entry.name;
record.value = entry.value;
}
else
} else {
record.deleted = true;
}
return record;
},
@ -208,8 +250,9 @@ FormStore.prototype = {
// Just skip remove requests for things already gone
let entry = FormWrapper.getEntry(record.id);
if (entry == null)
if (entry == null) {
return;
}
Svc.Form.removeEntry(entry.name, entry.value);
},
@ -281,8 +324,9 @@ FormTracker.prototype = {
},
notify: function FormTracker_notify(formElement, aWindow, actionURI) {
if (this.ignoreAll)
if (this.ignoreAll) {
return;
}
this._log.trace("Form submission notification for " + actionURI.spec);
@ -309,8 +353,9 @@ FormTracker.prototype = {
// Grab the name for debugging, but check if empty when satchel would
let name = el.name;
if (name === "")
if (name === "") {
name = el.id;
}
if (!(el instanceof Ci.nsIDOMHTMLInputElement)) {
this._log.trace(name + " is not a DOMHTMLInputElement: " + el);

View File

@ -46,7 +46,6 @@ const Cu = Components.utils;
const Cr = Components.results;
const HISTORY_TTL = 5184000; // 60 days
const TOPIC_UPDATEPLACES_COMPLETE = "places-updatePlaces-complete";
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://services-sync/constants.js");
@ -254,20 +253,15 @@ HistoryStore.prototype = {
return failed;
}
let cb = Async.makeSyncCallback();
let updatePlacesCallback = {
handleResult: function handleResult() {},
handleError: function handleError(resultCode, placeInfo) {
failed.push(placeInfo.guid);
}
},
handleCompletion: Async.makeSyncCallback()
};
let onComplete = function onComplete(subject, topic, data) {
Svc.Obs.remove(TOPIC_UPDATEPLACES_COMPLETE, onComplete);
cb();
};
Svc.Obs.add(TOPIC_UPDATEPLACES_COMPLETE, onComplete);
this._asyncHistory.updatePlaces(records, updatePlacesCallback);
Async.waitForSyncCallback(cb);
Async.waitForSyncCallback(updatePlacesCallback.handleCompletion);
return failed;
},

View File

@ -236,7 +236,7 @@ let SyncScheduler = {
},
calculateScore: function calculateScore() {
var engines = Engines.getEnabled();
let engines = [Clients].concat(Engines.getEnabled());
for (let i = 0;i < engines.length;i++) {
this._log.trace(engines[i].name + ": score: " + engines[i].score);
this.globalScore += engines[i].score;

View File

@ -1577,7 +1577,7 @@ WeaveSvc.prototype = {
}
// Any remaining engines were either enabled locally or disabled remotely.
for each (engineName in enabled) {
for each (let engineName in enabled) {
let engine = Engines.get(engineName);
if (Svc.Prefs.get("engineStatusChanged." + engine.prefName, false)) {
this._log.trace("The " + engineName + " engine was enabled locally.");

View File

@ -556,10 +556,6 @@ let Utils = {
return T.slice(0, len);
},
byteArrayToString: function byteArrayToString(bytes) {
return [String.fromCharCode(byte) for each (byte in bytes)].join("");
},
/**
* PBKDF2 implementation in Javascript.
*

View File

@ -204,6 +204,14 @@ function generateNewKeys(collections) {
CollectionKeys.setContents(wbo.cleartext, modified);
}
function do_check_empty(obj) {
do_check_attribute_count(obj, 0);
}
function do_check_attribute_count(obj, c) {
do_check_eq(c, Object.keys(obj).length);
}
function do_check_throws(aFunc, aResult, aStack)
{
if (!aStack) {

View File

@ -12,7 +12,7 @@ store.wipe();
function test_tracking() {
_("Verify we've got an empty tracker to work with.");
let tracker = engine._tracker;
do_check_eq([id for (id in tracker.changedIDs)].length, 0);
do_check_empty(tracker.changedIDs);
let folder = PlacesUtils.bookmarks.createFolder(
PlacesUtils.bookmarks.bookmarksMenuFolder,
@ -26,7 +26,7 @@ function test_tracking() {
try {
_("Create bookmark. Won't show because we haven't started tracking yet");
createBmk();
do_check_eq([id for (id in tracker.changedIDs)].length, 0);
do_check_empty(tracker.changedIDs);
do_check_eq(tracker.score, 0);
_("Tell the tracker to start tracking changes.");
@ -34,13 +34,13 @@ function test_tracking() {
createBmk();
// We expect two changed items because the containing folder
// changed as well (new child).
do_check_eq([id for (id in tracker.changedIDs)].length, 2);
do_check_attribute_count(tracker.changedIDs, 2);
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE * 2);
_("Notifying twice won't do any harm.");
Svc.Obs.notify("weave:engine:start-tracking");
createBmk();
do_check_eq([id for (id in tracker.changedIDs)].length, 3);
do_check_attribute_count(tracker.changedIDs, 3);
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE * 4);
_("Let's stop tracking again.");
@ -48,13 +48,13 @@ function test_tracking() {
tracker.resetScore();
Svc.Obs.notify("weave:engine:stop-tracking");
createBmk();
do_check_eq([id for (id in tracker.changedIDs)].length, 0);
do_check_empty(tracker.changedIDs);
do_check_eq(tracker.score, 0);
_("Notifying twice won't do any harm.");
Svc.Obs.notify("weave:engine:stop-tracking");
createBmk();
do_check_eq([id for (id in tracker.changedIDs)].length, 0);
do_check_empty(tracker.changedIDs);
do_check_eq(tracker.score, 0);
} finally {
@ -72,7 +72,7 @@ function test_onItemChanged() {
_("Verify we've got an empty tracker to work with.");
let tracker = engine._tracker;
do_check_eq([id for (id in tracker.changedIDs)].length, 0);
do_check_empty(tracker.changedIDs);
do_check_eq(tracker.score, 0);
try {
@ -107,7 +107,7 @@ function test_onItemChanged() {
function test_onItemMoved() {
_("Verify we've got an empty tracker to work with.");
let tracker = engine._tracker;
do_check_eq([id for (id in tracker.changedIDs)].length, 0);
do_check_empty(tracker.changedIDs);
do_check_eq(tracker.score, 0);
try {

View File

@ -449,6 +449,87 @@ add_test(function test_command_sync() {
}
});
add_test(function test_send_uri_to_client_for_display() {
_("Ensure sendURIToClientForDisplay() sends command properly.");
let tracker = Clients._tracker;
let store = Clients._store;
let remoteId = Utils.makeGUID();
let rec = new ClientsRec("clients", remoteId);
rec.name = "remote";
store.create(rec);
let remoteRecord = store.createRecord(remoteId, "clients");
tracker.clearChangedIDs();
let initialScore = tracker.score;
let uri = "http://www.mozilla.org/";
Clients.sendURIToClientForDisplay(uri, remoteId);
let newRecord = store._remoteClients[remoteId];
do_check_neq(newRecord, undefined);
do_check_eq(newRecord.commands.length, 1);
let command = newRecord.commands[0];
do_check_eq(command.command, "displayURI");
do_check_eq(command.args.length, 2);
do_check_eq(command.args[0], uri);
do_check_true(tracker.score > initialScore);
do_check_true(tracker.score - initialScore >= SCORE_INCREMENT_XLARGE);
_("Ensure unknown client IDs result in exception.");
let unknownId = Utils.makeGUID();
let error;
try {
Clients.sendURIToClientForDisplay(uri, unknownId);
} catch (ex) {
error = ex;
}
do_check_eq(error.message.indexOf("Unknown remote client ID: "), 0);
run_next_test();
});
add_test(function test_receive_display_uri() {
_("Ensure processing of received 'displayURI' commands works.");
// We don't set up WBOs and perform syncing because other tests verify
// the command API works as advertised. This saves us a little work.
let uri = "http://www.mozilla.org/";
let remoteId = Utils.makeGUID();
let command = {
command: "displayURI",
args: [uri, remoteId],
};
Clients.localCommands = [command];
// Received 'displayURI' command should result in the topic defined below
// being called.
let ev = "weave:engine:clients:display-uri";
let handler = function(subject, data) {
Svc.Obs.remove(ev, handler);
do_check_eq(subject.uri, uri);
do_check_eq(subject.client, remoteId);
do_check_eq(data, null);
run_next_test();
};
Svc.Obs.add(ev, handler);
do_check_true(Clients.processIncomingCommands());
});
function run_test() {
initTestLogging("Trace");
Log4Moz.repository.getLogger("Sync.Engine.Clients").level = Log4Moz.Level.Trace;

View File

@ -5,35 +5,35 @@ Cu.import("resource://services-sync/log4moz.js");
function run_test() {
_("Verify we've got an empty tracker to work with.");
let tracker = new FormEngine()._tracker;
do_check_eq([id for (id in tracker.changedIDs)].length, 0);
do_check_empty(tracker.changedIDs);
Log4Moz.repository.rootLogger.addAppender(new Log4Moz.DumpAppender());
try {
_("Create an entry. Won't show because we haven't started tracking yet");
Svc.Form.addEntry("name", "John Doe");
do_check_eq([id for (id in tracker.changedIDs)].length, 0);
do_check_empty(tracker.changedIDs);
_("Tell the tracker to start tracking changes.");
Svc.Obs.notify("weave:engine:start-tracking");
Svc.Form.removeEntry("name", "John Doe");
Svc.Form.addEntry("email", "john@doe.com");
do_check_eq([id for (id in tracker.changedIDs)].length, 2);
do_check_attribute_count(tracker.changedIDs, 2);
_("Notifying twice won't do any harm.");
Svc.Obs.notify("weave:engine:start-tracking");
Svc.Form.addEntry("address", "Memory Lane");
do_check_eq([id for (id in tracker.changedIDs)].length, 3);
do_check_attribute_count(tracker.changedIDs, 3);
_("Let's stop tracking again.");
tracker.clearChangedIDs();
Svc.Obs.notify("weave:engine:stop-tracking");
Svc.Form.removeEntry("address", "Memory Lane");
do_check_eq([id for (id in tracker.changedIDs)].length, 0);
do_check_empty(tracker.changedIDs);
_("Notifying twice won't do any harm.");
Svc.Obs.notify("weave:engine:stop-tracking");
Svc.Form.removeEntry("email", "john@doe.com");
do_check_eq([id for (id in tracker.changedIDs)].length, 0);
do_check_empty(tracker.changedIDs);
_("Test error detection.");
// This throws an exception without the fix for Bug 597400.

View File

@ -78,14 +78,14 @@ function run_test() {
add_test(function test_store() {
_("Verify that we've got an empty store to work with.");
do_check_eq([id for (id in store.getAllIDs())].length, 0);
do_check_empty(store.getAllIDs());
_("Let's create an entry in the database.");
fxuri = Utils.makeURI("http://getfirefox.com/");
PlacesUtils.history.addPageWithDetails(fxuri, "Get Firefox!", TIMESTAMP1);
_("Verify that the entry exists.");
let ids = [id for (id in store.getAllIDs())];
let ids = Object.keys(store.getAllIDs());
do_check_eq(ids.length, 1);
fxguid = ids[0];
do_check_true(store.itemExists(fxguid));
@ -127,7 +127,7 @@ add_test(function test_store_create() {
tbguid = Utils.makeGUID();
tburi = Utils.makeURI("http://getthunderbird.com");
onNextTitleChanged(ensureThrows(function() {
do_check_eq([id for (id in store.getAllIDs())].length, 2);
do_check_attribute_count(store.getAllIDs(), 2);
let queryres = queryHistoryVisits(tburi);
do_check_eq(queryres.length, 1);
do_check_eq(queryres[0].time, TIMESTAMP3);
@ -154,7 +154,7 @@ add_test(function test_null_title() {
visits: [{date: TIMESTAMP3,
type: Ci.nsINavHistoryService.TRANSITION_TYPED}]}
]);
do_check_eq([id for (id in store.getAllIDs())].length, 3);
do_check_attribute_count(store.getAllIDs(), 3);
let queryres = queryHistoryVisits(resuri);
do_check_eq(queryres.length, 1);
do_check_eq(queryres[0].time, TIMESTAMP3);
@ -168,7 +168,7 @@ add_test(function test_invalid_records() {
+ "VALUES ('invalid-uri', 'Invalid URI', '.', 1, " + TIMESTAMP3 + ")";
let stmt = PlacesUtils.history.DBConnection.createAsyncStatement(query);
let result = Async.querySpinningly(stmt);
do_check_eq([id for (id in store.getAllIDs())].length, 4);
do_check_attribute_count(store.getAllIDs(), 4);
_("Make sure we report records with invalid URIs.");
let invalid_uri_guid = Utils.makeGUID();
@ -255,7 +255,7 @@ add_test(function test_remove() {
_("Make sure wipe works.");
store.wipe();
do_check_eq([id for (id in store.getAllIDs())].length, 0);
do_check_empty(store.getAllIDs());
queryres = queryHistoryVisits(fxuri);
do_check_eq(queryres.length, 0);
queryres = queryHistoryVisits(tburi);

View File

@ -34,7 +34,7 @@ function run_test() {
add_test(function test_empty() {
_("Verify we've got an empty tracker to work with.");
do_check_eq([id for (id in tracker.changedIDs)].length, 0);
do_check_empty(tracker.changedIDs);
do_check_eq(tracker.score, 0);
run_next_test();
});
@ -43,7 +43,7 @@ add_test(function test_not_tracking(next) {
_("Create history item. Won't show because we haven't started tracking yet");
addVisit();
Utils.nextTick(function() {
do_check_eq([id for (id in tracker.changedIDs)].length, 0);
do_check_empty(tracker.changedIDs);
do_check_eq(tracker.score, 0);
run_next_test();
});
@ -52,7 +52,7 @@ add_test(function test_not_tracking(next) {
add_test(function test_start_tracking() {
_("Tell the tracker to start tracking changes.");
onScoreUpdated(function() {
do_check_eq([id for (id in tracker.changedIDs)].length, 1);
do_check_attribute_count(tracker.changedIDs, 1);
do_check_eq(tracker.score, SCORE_INCREMENT_SMALL);
run_next_test();
});
@ -63,7 +63,7 @@ add_test(function test_start_tracking() {
add_test(function test_start_tracking_twice() {
_("Notifying twice won't do any harm.");
onScoreUpdated(function() {
do_check_eq([id for (id in tracker.changedIDs)].length, 2);
do_check_attribute_count(tracker.changedIDs, 2);
do_check_eq(tracker.score, 2 * SCORE_INCREMENT_SMALL);
run_next_test();
});
@ -79,7 +79,7 @@ add_test(function test_track_delete() {
onScoreUpdated(function() {
do_check_true(guid in tracker.changedIDs);
do_check_eq([id for (id in tracker.changedIDs)].length, 3);
do_check_attribute_count(tracker.changedIDs, 3);
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE + 2 * SCORE_INCREMENT_SMALL);
run_next_test();
});
@ -101,7 +101,7 @@ add_test(function test_dont_track_expiration() {
onScoreUpdated(function() {
do_check_false(guidToExpire in tracker.changedIDs);
do_check_true(guidToRemove in tracker.changedIDs);
do_check_eq([id for (id in tracker.changedIDs)].length, 1);
do_check_attribute_count(tracker.changedIDs, 1);
run_next_test();
});
@ -125,7 +125,7 @@ add_test(function test_stop_tracking() {
Svc.Obs.notify("weave:engine:stop-tracking");
addVisit();
Utils.nextTick(function() {
do_check_eq([id for (id in tracker.changedIDs)].length, 0);
do_check_empty(tracker.changedIDs);
run_next_test();
});
});
@ -135,7 +135,7 @@ add_test(function test_stop_tracking_twice() {
Svc.Obs.notify("weave:engine:stop-tracking");
addVisit();
Utils.nextTick(function() {
do_check_eq([id for (id in tracker.changedIDs)].length, 0);
do_check_empty(tracker.changedIDs);
run_next_test();
});
});

View File

@ -11,7 +11,7 @@ function test_tracking() {
_("Verify we've got an empty tracker to work with.");
let tracker = engine._tracker;
do_check_eq([id for (id in tracker.changedIDs)].length, 0);
do_check_empty(tracker.changedIDs);
function createPassword() {
_("RECORD NUM: " + recordNum);
@ -30,19 +30,19 @@ function test_tracking() {
try {
_("Create a password record. Won't show because we haven't started tracking yet");
createPassword();
do_check_eq([id for (id in tracker.changedIDs)].length, 0);
do_check_empty(tracker.changedIDs);
do_check_eq(tracker.score, 0);
_("Tell the tracker to start tracking changes.");
Svc.Obs.notify("weave:engine:start-tracking");
createPassword();
do_check_eq([id for (id in tracker.changedIDs)].length, 1);
do_check_attribute_count(tracker.changedIDs, 1);
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE);
_("Notifying twice won't do any harm.");
Svc.Obs.notify("weave:engine:start-tracking");
createPassword();
do_check_eq([id for (id in tracker.changedIDs)].length, 2);
do_check_attribute_count(tracker.changedIDs, 2);
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE * 2);
_("Let's stop tracking again.");
@ -50,13 +50,13 @@ function test_tracking() {
tracker.resetScore();
Svc.Obs.notify("weave:engine:stop-tracking");
createPassword();
do_check_eq([id for (id in tracker.changedIDs)].length, 0);
do_check_empty(tracker.changedIDs);
do_check_eq(tracker.score, 0);
_("Notifying twice won't do any harm.");
Svc.Obs.notify("weave:engine:stop-tracking");
createPassword();
do_check_eq([id for (id in tracker.changedIDs)].length, 0);
do_check_empty(tracker.changedIDs);
do_check_eq(tracker.score, 0);
} finally {
@ -71,7 +71,7 @@ function test_tracking() {
function test_onWipe() {
_("Verify we've got an empty tracker to work with.");
let tracker = engine._tracker;
do_check_eq([id for (id in tracker.changedIDs)].length, 0);
do_check_empty(tracker.changedIDs);
do_check_eq(tracker.score, 0);
try {

View File

@ -36,7 +36,7 @@ function run_test() {
_("The GUID corresponds to XUL App ID.");
let allIDs = store.getAllIDs();
let ids = [id for (id in allIDs)];
let ids = Object.keys(allIDs);
do_check_eq(ids.length, 1);
do_check_eq(ids[0], PREFS_GUID);
do_check_true(allIDs[PREFS_GUID], true);

View File

@ -20,7 +20,7 @@ function run_test() {
_("Engine's getChangedID() just returns the one GUID we have.");
let changedIDs = engine.getChangedIDs();
let ids = [id for (id in changedIDs)];
let ids = Object.keys(changedIDs);
do_check_eq(ids.length, 1);
do_check_eq(ids[0], Utils.encodeBase64url(Services.appinfo.ID));
@ -28,7 +28,7 @@ function run_test() {
do_check_false(tracker.modified);
_("No modified state, so no changed IDs.");
do_check_eq([id for (id in engine.getChangedIDs())].length, 0);
do_check_empty(engine.getChangedIDs());
_("Initial score is 0");
do_check_eq(tracker.score, 0);

View File

@ -2,6 +2,7 @@
http://creativecommons.org/publicdomain/zero/1.0/ */
Cu.import("resource://services-sync/engines.js");
Cu.import("resource://services-sync/engines/clients.js");
Cu.import("resource://services-sync/constants.js");
Cu.import("resource://services-sync/policies.js");
@ -95,3 +96,25 @@ add_test(function test_sync_triggered() {
tracker.score += SCORE_INCREMENT_XLARGE;
});
add_test(function test_clients_engine_sync_triggered() {
_("Ensure that client engine score changes trigger a sync.");
// The clients engine is not registered like other engines. Therefore,
// it needs special treatment throughout the code. Here, we verify the
// global score tracker gives it that treatment. See bug 676042 for more.
let server = sync_httpd_setup();
setUp();
Service.login();
const TOPIC = "weave:service:sync:finish";
Svc.Obs.add(TOPIC, function onSyncFinish() {
Svc.Obs.remove(TOPIC, onSyncFinish);
_("Sync due to clients engine change completed.");
server.stop(run_next_test);
});
SyncScheduler.syncThreshold = MULTI_DEVICE_THRESHOLD;
Clients._tracker.score += SCORE_INCREMENT_XLARGE;
});

View File

@ -402,7 +402,7 @@ add_test(function test_processIncoming_mobile_batchSize() {
_("On a mobile client, we get new records from the server in batches of 50.");
engine._syncStartup();
engine._processIncoming();
do_check_eq([id for (id in engine._store.items)].length, 234);
do_check_attribute_count(engine._store.items, 234);
do_check_true('record-no-0' in engine._store.items);
do_check_true('record-no-49' in engine._store.items);
do_check_true('record-no-50' in engine._store.items);
@ -473,7 +473,7 @@ add_test(function test_processIncoming_store_toFetch() {
// Confirm initial environment
do_check_eq(engine.lastSync, 0);
do_check_eq([id for (id in engine._store.items)].length, 0);
do_check_empty(engine._store.items);
let error;
try {
@ -484,7 +484,7 @@ add_test(function test_processIncoming_store_toFetch() {
do_check_true(!!error);
// Only the first two batches have been applied.
do_check_eq([id for (id in engine._store.items)].length,
do_check_eq(Object.keys(engine._store.items).length,
MOBILE_BATCH_SIZE * 2);
// The third batch is stuck in toFetch. lastSync has been moved forward to
@ -602,14 +602,13 @@ add_test(function test_processIncoming_applyIncomingBatchSize_smaller() {
try {
// Confirm initial environment
do_check_eq([id for (id in engine._store.items)].length, 0);
do_check_empty(engine._store.items);
engine._syncStartup();
engine._processIncoming();
// Records have been applied and the expected failures have failed.
do_check_eq([id for (id in engine._store.items)].length,
APPLY_BATCH_SIZE - 1 - 2);
do_check_attribute_count(engine._store.items, APPLY_BATCH_SIZE - 1 - 2);
do_check_eq(engine.toFetch.length, 0);
do_check_eq(engine.previousFailed.length, 2);
do_check_eq(engine.previousFailed[0], "record-no-0");
@ -658,15 +657,14 @@ add_test(function test_processIncoming_applyIncomingBatchSize_multiple() {
try {
// Confirm initial environment
do_check_eq([id for (id in engine._store.items)].length, 0);
do_check_empty(engine._store.items);
engine._syncStartup();
engine._processIncoming();
// Records have been applied in 3 batches.
do_check_eq(batchCalls, 3);
do_check_eq([id for (id in engine._store.items)].length,
APPLY_BATCH_SIZE * 3);
do_check_attribute_count(engine._store.items, APPLY_BATCH_SIZE * 3);
} finally {
cleanAndGo(server);
@ -712,7 +710,7 @@ add_test(function test_processIncoming_notify_count() {
do_check_eq(engine.lastSync, 0);
do_check_eq(engine.toFetch.length, 0);
do_check_eq(engine.previousFailed.length, 0);
do_check_eq([id for (id in engine._store.items)].length, 0);
do_check_empty(engine._store.items);
let called = 0;
let counts;
@ -728,7 +726,7 @@ add_test(function test_processIncoming_notify_count() {
engine._processIncoming();
// Confirm failures.
do_check_eq([id for (id in engine._store.items)].length, 12);
do_check_attribute_count(engine._store.items, 12);
do_check_eq(engine.previousFailed.length, 3);
do_check_eq(engine.previousFailed[0], "record-no-0");
do_check_eq(engine.previousFailed[1], "record-no-5");
@ -744,7 +742,7 @@ add_test(function test_processIncoming_notify_count() {
engine._processIncoming();
// Confirming removed failures.
do_check_eq([id for (id in engine._store.items)].length, 14);
do_check_attribute_count(engine._store.items, 14);
do_check_eq(engine.previousFailed.length, 1);
do_check_eq(engine.previousFailed[0], "record-no-0");
@ -799,7 +797,7 @@ add_test(function test_processIncoming_previousFailed() {
do_check_eq(engine.lastSync, 0);
do_check_eq(engine.toFetch.length, 0);
do_check_eq(engine.previousFailed.length, 0);
do_check_eq([id for (id in engine._store.items)].length, 0);
do_check_empty(engine._store.items);
// Initial failed items in previousFailed to be reset.
let previousFailed = [Utils.makeGUID(), Utils.makeGUID(), Utils.makeGUID()];
@ -811,7 +809,7 @@ add_test(function test_processIncoming_previousFailed() {
engine._processIncoming();
// Expected result: 4 sync batches with 2 failures each => 8 failures
do_check_eq([id for (id in engine._store.items)].length, 6);
do_check_attribute_count(engine._store.items, 6);
do_check_eq(engine.previousFailed.length, 8);
do_check_eq(engine.previousFailed[0], "record-no-0");
do_check_eq(engine.previousFailed[1], "record-no-1");
@ -827,7 +825,7 @@ add_test(function test_processIncoming_previousFailed() {
// A second sync with the same failed items should not add the same items again.
// Items that did not fail a second time should no longer be in previousFailed.
do_check_eq([id for (id in engine._store.items)].length, 10);
do_check_attribute_count(engine._store.items, 10);
do_check_eq(engine.previousFailed.length, 4);
do_check_eq(engine.previousFailed[0], "record-no-0");
do_check_eq(engine.previousFailed[1], "record-no-1");
@ -854,7 +852,7 @@ add_test(function test_processIncoming_failed_records() {
// Let's create three and a bit batches worth of server side records.
let collection = new ServerCollection();
const NUMBER_OF_RECORDS = MOBILE_BATCH_SIZE * 3 + 5;
for (var i = 0; i < NUMBER_OF_RECORDS; i++) {
for (let i = 0; i < NUMBER_OF_RECORDS; i++) {
let id = 'record-no-' + i;
let payload = encryptPayload({id: id, denomination: "Record No. " + id});
let wbo = new ServerWBO(id, payload);
@ -916,7 +914,7 @@ add_test(function test_processIncoming_failed_records() {
do_check_eq(engine.lastSync, 0);
do_check_eq(engine.toFetch.length, 0);
do_check_eq(engine.previousFailed.length, 0);
do_check_eq([id for (id in engine._store.items)].length, 0);
do_check_empty(engine._store.items);
let observerSubject;
let observerData;
@ -930,8 +928,8 @@ add_test(function test_processIncoming_failed_records() {
engine._processIncoming();
// Ensure that all records but the bogus 4 have been applied.
do_check_eq([id for (id in engine._store.items)].length,
NUMBER_OF_RECORDS - BOGUS_RECORDS.length);
do_check_attribute_count(engine._store.items,
NUMBER_OF_RECORDS - BOGUS_RECORDS.length);
// Ensure that the bogus records will be fetched again on the next sync.
do_check_eq(engine.previousFailed.length, BOGUS_RECORDS.length);
@ -1530,7 +1528,7 @@ add_test(function test_syncapplied_observer() {
engine._syncStartup();
engine._processIncoming();
do_check_eq([id for (id in engine._store.items)].length, 10);
do_check_attribute_count(engine._store.items, 10);
do_check_eq(numApplyCalls, 1);
do_check_eq(engine_name, "rotary");

View File

@ -47,7 +47,7 @@ function run_test() {
_("We assume that tabs have changed at startup.");
let tracker = engine._tracker;
do_check_true(tracker.modified);
do_check_true(Utils.deepEquals([id for (id in engine.getChangedIDs())],
do_check_true(Utils.deepEquals(Object.keys(engine.getChangedIDs()),
[Clients.localID]));
let logs;
@ -91,7 +91,7 @@ function run_test() {
// Send a fake tab event
tracker.onTab({type: evttype , originalTarget: evttype});
do_check_true(tracker.modified);
do_check_true(Utils.deepEquals([id for (id in engine.getChangedIDs())],
do_check_true(Utils.deepEquals(Object.keys(engine.getChangedIDs()),
[Clients.localID]));
do_check_eq(logs.length, idx+1);
do_check_eq(logs[idx].target, evttype);
@ -105,7 +105,7 @@ function run_test() {
do_check_false(tracker.modified);
tracker.onTab({type: "pageshow", originalTarget: "pageshow"});
do_check_true(Utils.deepEquals([id for (id in engine.getChangedIDs())],
do_check_true(Utils.deepEquals(Object.keys(engine.getChangedIDs()),
[Clients.localID]));
do_check_eq(logs.length, idx); // test that setTabValue isn't called
}

View File

@ -10,7 +10,7 @@
<Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
<em:minVersion>3.5.0</em:minVersion>
<em:maxVersion>8.0.*</em:maxVersion>
<em:maxVersion>10.0.*</em:maxVersion>
</Description>
</em:targetApplication>

View File

@ -43,7 +43,7 @@ version = '0.2.40'
deps = ['pulsebuildmonitor >= 0.2', 'MozillaPulse == .4',
'mozinfo == 0.3.1', 'mozprofile == 0.1a',
'mozprocess == 0.1a', 'mozrunner == 3.0a', 'mozregression == 0.3',
'mozautolog >= 0.2.0']
'mozautolog >= 0.2.1']
# we only support python 2.6+ right now
assert sys.version_info[0] == 2

View File

@ -377,14 +377,6 @@ GetHiddenState(bool aIsRedirect,
PlacesEvent::PlacesEvent(const char* aTopic)
: mTopic(aTopic)
, mDoubleEnqueue(false)
{
}
PlacesEvent::PlacesEvent(const char* aTopic,
bool aDoubleEnqueue)
: mTopic(aTopic)
, mDoubleEnqueue(aDoubleEnqueue)
{
}
@ -405,16 +397,10 @@ PlacesEvent::Complete()
void
PlacesEvent::Notify()
{
if (mDoubleEnqueue) {
mDoubleEnqueue = false;
(void)NS_DispatchToMainThread(this);
}
else {
NS_ASSERTION(NS_IsMainThread(), "Must only be used on the main thread!");
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
(void)obs->NotifyObservers(nsnull, mTopic, nsnull);
}
NS_ASSERTION(NS_IsMainThread(), "Must only be used on the main thread!");
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
(void)obs->NotifyObservers(nsnull, mTopic, nsnull);
}
}

View File

@ -298,12 +298,10 @@ public:
NS_DECL_MOZISTORAGECOMPLETIONCALLBACK
PlacesEvent(const char* aTopic);
PlacesEvent(const char* aTopic, bool aDoubleEnqueue);
protected:
void Notify();
const char* const mTopic;
bool mDoubleEnqueue;
};
} // namespace places

View File

@ -63,9 +63,6 @@
// Initial size for the cache holding visited status observers.
#define VISIT_OBSERVERS_INITIAL_CACHE_SIZE 128
// Topic used to notify that work in mozIAsyncHistory::updatePlaces is done.
#define TOPIC_UPDATEPLACES_COMPLETE "places-updatePlaces-complete"
using namespace mozilla::dom;
using mozilla::unused;
@ -531,14 +528,14 @@ private:
};
/**
* Notifies a callback object about completion.
* Notifies a callback object when a visit has been handled.
*/
class NotifyCompletion : public nsRunnable
class NotifyVisitInfoCallback : public nsRunnable
{
public:
NotifyCompletion(mozIVisitInfoCallback* aCallback,
const VisitData& aPlace,
nsresult aResult)
NotifyVisitInfoCallback(mozIVisitInfoCallback* aCallback,
const VisitData& aPlace,
nsresult aResult)
: mCallback(aCallback)
, mPlace(aPlace)
, mResult(aResult)
@ -590,6 +587,44 @@ private:
const nsresult mResult;
};
/**
* Notifies a callback object when the operation is complete.
*/
class NotifyCompletion : public nsRunnable
{
public:
NotifyCompletion(mozIVisitInfoCallback* aCallback)
: mCallback(aCallback)
{
NS_PRECONDITION(aCallback, "Must pass a non-null callback!");
}
NS_IMETHOD Run()
{
if (NS_IsMainThread()) {
(void)mCallback->HandleCompletion();
}
else {
(void)NS_DispatchToMainThread(this);
// Also dispatch an event to release the reference to the callback after
// we have run.
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
(void)NS_ProxyRelease(mainThread, mCallback, PR_TRUE);
}
return NS_OK;
}
private:
/**
* Callers MUST hold a strong reference to this because we may be created
* off of the main thread, and therefore cannot call AddRef on this object
* (and therefore cannot hold a strong reference to it). If invoked from a
* background thread, NotifyCompletion will release the reference to this.
*/
mozIVisitInfoCallback* mCallback;
};
/**
* Checks to see if we can add aURI to history, and dispatches an error to
* aCallback (if provided) if we cannot.
@ -618,7 +653,7 @@ CanAddURI(nsIURI* aURI,
// We cannot add the URI. Notify the callback, if we were given one.
if (aCallback) {
// NotifyCompletion does not hold a strong reference to the callback, so we
// NotifyVisitInfoCallback does not hold a strong reference to the callback, so we
// have to manage it by AddRefing now and then releasing it after the event
// has run.
NS_ADDREF(aCallback);
@ -626,11 +661,11 @@ CanAddURI(nsIURI* aURI,
VisitData place(aURI);
place.guid = aGUID;
nsCOMPtr<nsIRunnable> event =
new NotifyCompletion(aCallback, place, NS_ERROR_INVALID_ARG);
new NotifyVisitInfoCallback(aCallback, place, NS_ERROR_INVALID_ARG);
(void)NS_DispatchToMainThread(event);
// Also dispatch an event to release our reference to the callback after
// NotifyCompletion has run.
// NotifyVisitInfoCallback has run.
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
(void)NS_ProxyRelease(mainThread, aCallback, PR_TRUE);
}
@ -697,7 +732,7 @@ public:
nsresult rv = DoDatabaseInserts(known, place, referrer);
if (mCallback) {
nsCOMPtr<nsIRunnable> event =
new NotifyCompletion(mCallback, place, rv);
new NotifyVisitInfoCallback(mCallback, place, rv);
nsresult rv2 = NS_DispatchToMainThread(event);
NS_ENSURE_SUCCESS(rv2, rv2);
}
@ -1230,16 +1265,16 @@ StoreAndNotifyEmbedVisit(VisitData& aPlace,
navHistory->registerEmbedVisit(uri, aPlace.visitTime);
if (aCallback) {
// NotifyCompletion does not hold a strong reference to the callback, so we
// have to manage it by AddRefing now and then releasing it after the event
// has run.
// NotifyVisitInfoCallback does not hold a strong reference to the callback,
// so we have to manage it by AddRefing now and then releasing it after the
// event has run.
NS_ADDREF(aCallback);
nsCOMPtr<nsIRunnable> event =
new NotifyCompletion(aCallback, aPlace, NS_OK);
new NotifyVisitInfoCallback(aCallback, aPlace, NS_OK);
(void)NS_DispatchToMainThread(event);
// Also dispatch an event to release our reference to the callback after
// NotifyCompletion has run.
// NotifyVisitInfoCallback has run.
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
(void)NS_ProxyRelease(mainThread, aCallback, PR_TRUE);
}
@ -1975,14 +2010,22 @@ History::UpdatePlaces(const jsval& aPlaceInfos,
NS_ENSURE_SUCCESS(rv, rv);
}
// Be sure to notify that all of our operations are complete. This is
// double enqueued to make sure that all database notifications and all embed
// or canAddURI notifications have finished.
nsCOMPtr<nsIEventTarget> backgroundThread = do_GetInterface(dbConn);
NS_ENSURE_TRUE(backgroundThread, NS_ERROR_UNEXPECTED);
nsRefPtr<PlacesEvent> completeEvent =
new PlacesEvent(TOPIC_UPDATEPLACES_COMPLETE, true);
(void)backgroundThread->Dispatch(completeEvent, 0);
// Be sure to notify that all of our operations are complete. This
// is dispatched to the background thread first and redirected to the
// main thread from there to make sure that all database notifications
// and all embed or canAddURI notifications have finished.
if (aCallback) {
// NotifyCompletion does not hold a strong reference to the callback,
// so we have to manage it by AddRefing now. NotifyCompletion will
// release it for us once it has dispatched the callback to the main
// thread.
NS_ADDREF(aCallback);
nsCOMPtr<nsIEventTarget> backgroundThread = do_GetInterface(dbConn);
NS_ENSURE_TRUE(backgroundThread, NS_ERROR_UNEXPECTED);
nsCOMPtr<nsIRunnable> event = new NotifyCompletion(aCallback);
(void)backgroundThread->Dispatch(event, NS_DISPATCH_NORMAL);
}
return NS_OK;
}

View File

@ -122,7 +122,7 @@ interface mozIPlaceInfo : nsISupports
/**
* @status EXPERIMENTAL
*/
[scriptable, uuid(eb0b406f-8f57-4f2b-b0da-8883684b138a)]
[scriptable, uuid(1f266877-2859-418b-a11b-ec3ae4f4f93d)]
interface mozIVisitInfoCallback : nsISupports
{
/**
@ -147,6 +147,12 @@ interface mozIVisitInfoCallback : nsISupports
*/
void handleResult(in mozIPlaceInfo aPlaceInfo);
/**
* Called when the mozIAsyncHistory::updatePlaces has finished processing
* all mozIPlaceInfo records.
*/
void handleCompletion();
};
/**

View File

@ -17,7 +17,6 @@ XPCOMUtils.defineLazyServiceGetter(this, "gGlobalHistory",
"nsIGlobalHistory2");
const TEST_DOMAIN = "http://mozilla.org/";
const TOPIC_UPDATEPLACES_COMPLETE = "places-updatePlaces-complete";
const URI_VISIT_SAVED = "uri-visit-saved";
const RECENT_EVENT_THRESHOLD = 15 * 60 * 1000000;
@ -742,14 +741,14 @@ function test_place_id_ignored()
}));
}
function test_observer_topic_dispatched_when_complete()
function test_handleCompletion_called_when_complete()
{
// We test a normal visit, and embeded visit, and a uri that would fail
// the canAddURI test to make sure that the notification happens after *all*
// of them have had a callback.
let places = [
{ uri: NetUtil.newURI(TEST_DOMAIN +
"test_observer_topic_dispatched_when_complete"),
"test_handleCompletion_called_when_complete"),
visits: [
new VisitInfo(),
new VisitInfo(TRANSITION_EMBED),
@ -777,20 +776,13 @@ function test_observer_topic_dispatched_when_complete()
},
handleError: function handleError(aResultCode, aPlaceInfo) {
callbackCountFailure++;
}
});
let observer = {
observe: function(aSubject, aTopic, aData)
{
do_check_eq(aTopic, TOPIC_UPDATEPLACES_COMPLETE);
},
handleCompletion: function handleCompletion() {
do_check_eq(callbackCountSuccess, EXPECTED_COUNT_SUCCESS);
do_check_eq(callbackCountFailure, EXPECTED_COUNT_FAILURE);
Services.obs.removeObserver(observer, TOPIC_UPDATEPLACES_COMPLETE);
waitForAsyncUpdates(run_next_test);
},
};
Services.obs.addObserver(observer, TOPIC_UPDATEPLACES_COMPLETE, false);
});
}
function test_add_visit()
@ -1300,15 +1292,8 @@ function test_callbacks_not_supplied()
}
});
gHistory.updatePlaces(places, {} );
let observer = {
observe: function(aSubject, aTopic, aData)
{
Services.obs.removeObserver(observer, TOPIC_UPDATEPLACES_COMPLETE);
waitForAsyncUpdates(run_next_test);
},
};
Services.obs.addObserver(observer, TOPIC_UPDATEPLACES_COMPLETE, false);
gHistory.updatePlaces(places, {});
waitForAsyncUpdates(run_next_test);
}
////////////////////////////////////////////////////////////////////////////////
@ -1333,7 +1318,7 @@ function test_callbacks_not_supplied()
test_unstored_sessionId_ignored,
test_old_referrer_ignored,
test_place_id_ignored,
test_observer_topic_dispatched_when_complete,
test_handleCompletion_called_when_complete,
test_add_visit,
test_properties_saved,
test_guid_saved,