mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 21:31:04 +00:00
Bug 1303945 - Avoid having TPS write directly to the form history database and remove private-browsing tests. r=tcsc
MozReview-Commit-ID: JsPGSJpQ7b1 --HG-- extra : rebase_source : 42011ef085f1d119d43b14c2ac0619777fcb2c68
This commit is contained in:
parent
de2be84723
commit
9ae1545799
@ -107,7 +107,7 @@ Tracker.prototype = {
|
||||
Utils.jsonLoad("changes/" + this.file, this, function(json) {
|
||||
if (json && (typeof(json) == "object")) {
|
||||
this.changedIDs = json;
|
||||
} else {
|
||||
} else if (json !== null) {
|
||||
this._log.warn("Changed IDs file " + this.file + " contains non-object value.");
|
||||
json = null;
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ this.Utils = {
|
||||
return func.call(thisArg);
|
||||
}
|
||||
catch(ex) {
|
||||
thisArg._log.debug("Exception", ex);
|
||||
thisArg._log.debug("Exception calling " + (func.name || "anonymous function"), ex);
|
||||
if (exceptionCallback) {
|
||||
return exceptionCallback.call(thisArg, ex);
|
||||
}
|
||||
@ -358,7 +358,8 @@ this.Utils = {
|
||||
json = yield CommonUtils.readJSON(path);
|
||||
} catch (e) {
|
||||
if (e instanceof OS.File.Error && e.becauseNoSuchFile) {
|
||||
// Ignore non-existent files.
|
||||
// Ignore non-existent files, but explicitly return null.
|
||||
json = null;
|
||||
} else {
|
||||
if (that._log) {
|
||||
that._log.debug("Failed to load json", e);
|
||||
|
@ -16,7 +16,6 @@
|
||||
"test_bug575423.js",
|
||||
"test_bug546807.js",
|
||||
"test_history_collision.js",
|
||||
"test_privbrw_formdata.js",
|
||||
"test_privbrw_passwords.js",
|
||||
"test_privbrw_tabs.js",
|
||||
"test_bookmarks_in_same_named_folder.js",
|
||||
|
@ -31,6 +31,11 @@ var formdata1 = [
|
||||
}
|
||||
];
|
||||
|
||||
// This is currently pointless - it *looks* like it is trying to check that
|
||||
// one of the entries in formdata1 has been removed, but (a) the delete code
|
||||
// isn't active (see comments below), and (b) the way the verification works
|
||||
// means it would never do the right thing - it only checks all the entries
|
||||
// here exist, but not that they are the only entries in the DB.
|
||||
var formdata2 = [
|
||||
{ fieldname: "testing",
|
||||
value: "success",
|
||||
@ -47,6 +52,11 @@ var formdata_delete = [
|
||||
}
|
||||
];
|
||||
|
||||
var formdata_new = [
|
||||
{ fieldname: "new-field",
|
||||
value: "new-value"
|
||||
}
|
||||
]
|
||||
/*
|
||||
* Test phases
|
||||
*/
|
||||
@ -72,12 +82,15 @@ Phase('phase3', [
|
||||
[Formdata.delete, formdata_delete],
|
||||
//[Formdata.verifyNot, formdata_delete],
|
||||
[Formdata.verify, formdata2],
|
||||
// add new data after the first Sync, ensuring the tracker works.
|
||||
[Formdata.add, formdata_new],
|
||||
[Sync],
|
||||
]);
|
||||
|
||||
Phase('phase4', [
|
||||
[Sync],
|
||||
[Formdata.verify, formdata2],
|
||||
[Formdata.verify, formdata_new],
|
||||
//[Formdata.verifyNot, formdata_delete]
|
||||
]);
|
||||
|
||||
|
@ -1,73 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*
|
||||
* The list of phases mapped to their corresponding profiles. The object
|
||||
* here must be in strict JSON format, as it will get parsed by the Python
|
||||
* testrunner (no single quotes, extra comma's, etc).
|
||||
*/
|
||||
EnableEngines(["forms"]);
|
||||
|
||||
var phases = { "phase1": "profile1",
|
||||
"phase2": "profile2",
|
||||
"phase3": "profile1",
|
||||
"phase4": "profile2" };
|
||||
|
||||
/*
|
||||
* Form data
|
||||
*/
|
||||
|
||||
// the form data to add to the browser
|
||||
var formdata1 = [
|
||||
{ fieldname: "name",
|
||||
value: "xyz",
|
||||
date: -1
|
||||
},
|
||||
{ fieldname: "email",
|
||||
value: "abc@gmail.com",
|
||||
date: -2
|
||||
},
|
||||
{ fieldname: "username",
|
||||
value: "joe"
|
||||
}
|
||||
];
|
||||
|
||||
// the form data to add in private browsing mode
|
||||
var formdata2 = [
|
||||
{ fieldname: "password",
|
||||
value: "secret",
|
||||
date: -1
|
||||
},
|
||||
{ fieldname: "city",
|
||||
value: "mtview"
|
||||
}
|
||||
];
|
||||
|
||||
/*
|
||||
* Test phases
|
||||
*/
|
||||
|
||||
Phase('phase1', [
|
||||
[Formdata.add, formdata1],
|
||||
[Formdata.verify, formdata1],
|
||||
[Sync]
|
||||
]);
|
||||
|
||||
Phase('phase2', [
|
||||
[Sync],
|
||||
[Formdata.verify, formdata1]
|
||||
]);
|
||||
|
||||
Phase('phase3', [
|
||||
[Sync],
|
||||
[Windows.add, { private: true }],
|
||||
[Formdata.add, formdata2],
|
||||
[Formdata.verify, formdata2],
|
||||
[Sync],
|
||||
]);
|
||||
|
||||
Phase('phase4', [
|
||||
[Sync],
|
||||
[Formdata.verify, formdata1],
|
||||
[Formdata.verifyNot, formdata2]
|
||||
]);
|
@ -31,7 +31,7 @@ function run_test() {
|
||||
Service.sync();
|
||||
Service._locked = false;
|
||||
|
||||
do_check_true(debug[debug.length - 2].startsWith("Exception: Could not acquire lock. Label: \"service.js: login\"."));
|
||||
do_check_true(debug[debug.length - 2].startsWith("Exception calling WrappedLock: Could not acquire lock. Label: \"service.js: login\"."));
|
||||
do_check_eq(info[info.length - 1], "Cannot start sync: already syncing?");
|
||||
}
|
||||
|
||||
|
@ -13,74 +13,45 @@ const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
|
||||
Cu.import("resource://tps/logger.jsm");
|
||||
|
||||
var formService = Cc["@mozilla.org/satchel/form-history;1"]
|
||||
.getService(Ci.nsIFormHistory2);
|
||||
Cu.import("resource://gre/modules/FormHistory.jsm");
|
||||
Cu.import("resource://gre/modules/Log.jsm");
|
||||
|
||||
/**
|
||||
* FormDB
|
||||
*
|
||||
* Helper object containing methods to interact with the moz_formhistory
|
||||
* SQLite table.
|
||||
* Helper object containing methods to interact with the FormHistory module.
|
||||
*/
|
||||
var FormDB = {
|
||||
/**
|
||||
* makeGUID
|
||||
*
|
||||
* Generates a brand-new globally unique identifier (GUID). Borrowed
|
||||
* from Weave's utils.js.
|
||||
*
|
||||
* @return the new guid
|
||||
*/
|
||||
makeGUID: function makeGUID() {
|
||||
// 70 characters that are not-escaped URL-friendly
|
||||
const code =
|
||||
"!()*-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~";
|
||||
|
||||
let guid = "";
|
||||
let num = 0;
|
||||
let val;
|
||||
|
||||
// Generate ten 70-value characters for a 70^10 (~61.29-bit) GUID
|
||||
for (let i = 0; i < 10; i++) {
|
||||
// Refresh the number source after using it a few times
|
||||
if (i == 0 || i == 5)
|
||||
num = Math.random();
|
||||
|
||||
// Figure out which code to use for the next GUID character
|
||||
num *= 70;
|
||||
val = Math.floor(num);
|
||||
guid += code[val];
|
||||
num -= val;
|
||||
}
|
||||
|
||||
return guid;
|
||||
_update(data) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let handlers = {
|
||||
handleError(error) {
|
||||
Logger.logError("Error occurred updating form history: " + Log.exceptionStr(error));
|
||||
reject(error);
|
||||
},
|
||||
handleCompletion(reason) {
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
FormHistory.update(data, handlers);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* insertValue
|
||||
*
|
||||
* Inserts the specified value for the specified fieldname into the
|
||||
* moz_formhistory table.
|
||||
* Adds the specified value for the specified fieldname into form history.
|
||||
*
|
||||
* @param fieldname The form fieldname to insert
|
||||
* @param value The form value to insert
|
||||
* @param us The time, in microseconds, to use for the lastUsed
|
||||
* and firstUsed columns
|
||||
* @return nothing
|
||||
* @return Promise<undefined>
|
||||
*/
|
||||
insertValue: function (fieldname, value, us) {
|
||||
let query = this.createStatement(
|
||||
"INSERT INTO moz_formhistory " +
|
||||
"(fieldname, value, timesUsed, firstUsed, lastUsed, guid) VALUES " +
|
||||
"(:fieldname, :value, :timesUsed, :firstUsed, :lastUsed, :guid)");
|
||||
query.params.fieldname = fieldname;
|
||||
query.params.value = value;
|
||||
query.params.timesUsed = 1;
|
||||
query.params.firstUsed = us;
|
||||
query.params.lastUsed = us;
|
||||
query.params.guid = this.makeGUID();
|
||||
query.execute();
|
||||
query.reset();
|
||||
insertValue(fieldname, value, us) {
|
||||
let data = { op: "add", fieldname, value, timesUsed: 1,
|
||||
firstUsed: us, lastUsed: us }
|
||||
return this._update(data);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -90,15 +61,10 @@ var FormDB = {
|
||||
*
|
||||
* @param id The id of the row to update
|
||||
* @param newvalue The new value to set
|
||||
* @return nothing
|
||||
* @return Promise<undefined>
|
||||
*/
|
||||
updateValue: function (id, newvalue) {
|
||||
let query = this.createStatement(
|
||||
"UPDATE moz_formhistory SET value = :value WHERE id = :id");
|
||||
query.params.id = id;
|
||||
query.params.value = newvalue;
|
||||
query.execute();
|
||||
query.reset();
|
||||
updateValue(id, newvalue) {
|
||||
return this._update({ op: "update", guid: id, value: newvalue });
|
||||
},
|
||||
|
||||
/**
|
||||
@ -109,52 +75,44 @@ var FormDB = {
|
||||
*
|
||||
* @param fieldname The fieldname of the row to query
|
||||
* @param value The value of the row to query
|
||||
* @return null if no row is found with the specified fieldname and value,
|
||||
* or an object containing the row's id, lastUsed, and firstUsed
|
||||
* values
|
||||
* @return Promise<null if no row is found with the specified fieldname and value,
|
||||
* or an object containing the row's guid, lastUsed, and firstUsed
|
||||
* values>
|
||||
*/
|
||||
getDataForValue: function (fieldname, value) {
|
||||
let query = this.createStatement(
|
||||
"SELECT id, lastUsed, firstUsed FROM moz_formhistory WHERE " +
|
||||
"fieldname = :fieldname AND value = :value");
|
||||
query.params.fieldname = fieldname;
|
||||
query.params.value = value;
|
||||
if (!query.executeStep())
|
||||
return null;
|
||||
|
||||
return {
|
||||
id: query.row.id,
|
||||
lastUsed: query.row.lastUsed,
|
||||
firstUsed: query.row.firstUsed
|
||||
};
|
||||
getDataForValue(fieldname, value) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let result = null;
|
||||
let handlers = {
|
||||
handleResult(oneResult) {
|
||||
if (result != null) {
|
||||
reject("more than 1 result for this query");
|
||||
return;
|
||||
}
|
||||
result = oneResult;
|
||||
},
|
||||
handleError(error) {
|
||||
Logger.logError("Error occurred updating form history: " + Log.exceptionStr(error));
|
||||
reject(error);
|
||||
},
|
||||
handleCompletion(reason) {
|
||||
resolve(result);
|
||||
}
|
||||
}
|
||||
FormHistory.search(["guid", "lastUsed", "firstUsed"], { fieldname }, handlers);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* createStatement
|
||||
* remove
|
||||
*
|
||||
* Creates a statement from a SQL string. This function is borrowed
|
||||
* from Weave's forms.js.
|
||||
* Removes the specified GUID from the database.
|
||||
*
|
||||
* @param query The SQL query string
|
||||
* @return the mozIStorageStatement created from the specified SQL
|
||||
* @param guid The guid of the item to delete
|
||||
* @return Promise<>
|
||||
*/
|
||||
createStatement: function createStatement(query) {
|
||||
try {
|
||||
// Just return the statement right away if it's okay
|
||||
return formService.DBConnection.createStatement(query);
|
||||
}
|
||||
catch(ex) {
|
||||
// Assume guid column must not exist yet, so add it with an index
|
||||
formService.DBConnection.executeSimpleSQL(
|
||||
"ALTER TABLE moz_formhistory ADD COLUMN guid TEXT");
|
||||
formService.DBConnection.executeSimpleSQL(
|
||||
"CREATE INDEX IF NOT EXISTS moz_formhistory_guid_index " +
|
||||
"ON moz_formhistory (guid)");
|
||||
}
|
||||
|
||||
// Try creating the query now that the column exists
|
||||
return formService.DBConnection.createStatement(query);
|
||||
}
|
||||
remove(guid) {
|
||||
return this._update({ op: "remove", guid });
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
@ -204,18 +162,18 @@ FormData.prototype = {
|
||||
Logger.AssertTrue(this.fieldname != null && this.value != null,
|
||||
"Must specify both fieldname and value");
|
||||
|
||||
let formdata = FormDB.getDataForValue(this.fieldname, this.value);
|
||||
if (!formdata) {
|
||||
// this item doesn't exist yet in the db, so we need to insert it
|
||||
FormDB.insertValue(this.fieldname, this.value,
|
||||
this.hours_to_us(this.date));
|
||||
}
|
||||
else {
|
||||
/* Right now, we ignore this case. If bug 552531 is ever fixed,
|
||||
we might need to add code here to update the firstUsed or
|
||||
lastUsed fields, as appropriate.
|
||||
*/
|
||||
}
|
||||
return FormDB.getDataForValue(this.fieldname, this.value).then(formdata => {
|
||||
if (!formdata) {
|
||||
// this item doesn't exist yet in the db, so we need to insert it
|
||||
return FormDB.insertValue(this.fieldname, this.value,
|
||||
this.hours_to_us(this.date));
|
||||
} else {
|
||||
/* Right now, we ignore this case. If bug 552531 is ever fixed,
|
||||
we might need to add code here to update the firstUsed or
|
||||
lastUsed fields, as appropriate.
|
||||
*/
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@ -227,21 +185,22 @@ FormData.prototype = {
|
||||
* @return true if this entry exists in the database, otherwise false
|
||||
*/
|
||||
Find: function() {
|
||||
let formdata = FormDB.getDataForValue(this.fieldname, this.value);
|
||||
let status = formdata != null;
|
||||
if (status) {
|
||||
/*
|
||||
//form history dates currently not synced! bug 552531
|
||||
let us = this.hours_to_us(this.date);
|
||||
status = Logger.AssertTrue(
|
||||
us >= formdata.firstUsed && us <= formdata.lastUsed,
|
||||
"No match for with that date value");
|
||||
return FormDB.getDataForValue(this.fieldname, this.value).then(formdata => {
|
||||
let status = formdata != null;
|
||||
if (status) {
|
||||
/*
|
||||
//form history dates currently not synced! bug 552531
|
||||
let us = this.hours_to_us(this.date);
|
||||
status = Logger.AssertTrue(
|
||||
us >= formdata.firstUsed && us <= formdata.lastUsed,
|
||||
"No match for with that date value");
|
||||
|
||||
if (status)
|
||||
*/
|
||||
this.id = formdata.id;
|
||||
}
|
||||
return status;
|
||||
if (status)
|
||||
*/
|
||||
this.id = formdata.guid;
|
||||
}
|
||||
return status;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@ -255,7 +214,6 @@ FormData.prototype = {
|
||||
Remove: function() {
|
||||
/* Right now Weave doesn't handle this correctly, see bug 568363.
|
||||
*/
|
||||
formService.removeEntry(this.fieldname, this.value);
|
||||
return true;
|
||||
return FormDB.remove(this.id);
|
||||
},
|
||||
};
|
||||
|
@ -82,7 +82,7 @@ const ACTIONS = [
|
||||
const OBSERVER_TOPICS = ["fxaccounts:onlogin",
|
||||
"fxaccounts:onlogout",
|
||||
"private-browsing",
|
||||
"quit-application-requested",
|
||||
"profile-before-change",
|
||||
"sessionstore-windows-restored",
|
||||
"weave:engine:start-tracking",
|
||||
"weave:engine:stop-tracking",
|
||||
@ -166,7 +166,7 @@ var TPS = {
|
||||
Logger.logInfo("private browsing " + data);
|
||||
break;
|
||||
|
||||
case "quit-application-requested":
|
||||
case "profile-before-change":
|
||||
OBSERVER_TOPICS.forEach(function(topic) {
|
||||
Services.obs.removeObserver(this, topic);
|
||||
}, this);
|
||||
@ -367,17 +367,18 @@ var TPS = {
|
||||
let formdata = new FormData(datum, this._usSinceEpoch);
|
||||
switch(action) {
|
||||
case ACTION_ADD:
|
||||
formdata.Create();
|
||||
Async.promiseSpinningly(formdata.Create());
|
||||
break;
|
||||
case ACTION_DELETE:
|
||||
formdata.Remove();
|
||||
Async.promiseSpinningly(formdata.Remove());
|
||||
break;
|
||||
case ACTION_VERIFY:
|
||||
Logger.AssertTrue(formdata.Find(), "form data not found");
|
||||
Logger.AssertTrue(Async.promiseSpinningly(formdata.Find()),
|
||||
"form data not found");
|
||||
break;
|
||||
case ACTION_VERIFY_NOT:
|
||||
Logger.AssertTrue(!formdata.Find(),
|
||||
"form data found, but it shouldn't be present");
|
||||
Logger.AssertTrue(!Async.promiseSpinningly(formdata.Find()),
|
||||
"form data found, but it shouldn't be present");
|
||||
break;
|
||||
default:
|
||||
Logger.AssertTrue(false, "invalid action: " + action);
|
||||
@ -591,7 +592,14 @@ var TPS = {
|
||||
Logger.logError("Failed to wipe server: " + Log.exceptionStr(ex));
|
||||
}
|
||||
try {
|
||||
Authentication.signOut();
|
||||
if (Authentication.isLoggedIn) {
|
||||
// signout and wait for Sync to completely reset itself.
|
||||
Logger.logInfo("signing out");
|
||||
let waiter = this.createEventWaiter("weave:service:start-over:finish");
|
||||
Authentication.signOut();
|
||||
waiter();
|
||||
Logger.logInfo("signout complete");
|
||||
}
|
||||
} catch (e) {
|
||||
Logger.logError("Failed to sign out: " + Log.exceptionStr(e));
|
||||
}
|
||||
@ -701,8 +709,8 @@ var TPS = {
|
||||
let validator = new FormValidator();
|
||||
let serverRecords = validator.getServerItems(engine);
|
||||
let clientRecords = Async.promiseSpinningly(validator.getClientItems());
|
||||
clientRecordDumpStr = JSON.stringify(clientRecords);
|
||||
serverRecordDumpStr = JSON.stringify(serverRecords);
|
||||
clientRecordDumpStr = JSON.stringify(clientRecords, undefined, 2);
|
||||
serverRecordDumpStr = JSON.stringify(serverRecords, undefined, 2);
|
||||
let { problemData } = validator.compareClientWithServer(clientRecords, serverRecords);
|
||||
for (let { name, count } of problemData.getSummary()) {
|
||||
if (count) {
|
||||
@ -975,25 +983,57 @@ var TPS = {
|
||||
frame.runTestFile(mozmillfile.path, null);
|
||||
},
|
||||
|
||||
/**
|
||||
* Return an object that when called, will block until the named event
|
||||
* is observed. This is similar to waitForEvent, although is typically safer
|
||||
* if you need to do some other work that may make the event fire.
|
||||
*
|
||||
* eg:
|
||||
* doSomething(); // causes the event to be fired.
|
||||
* waitForEvent("something");
|
||||
* is risky as the call to doSomething may trigger the event before the
|
||||
* waitForEvent call is made. Contrast with:
|
||||
*
|
||||
* let waiter = createEventWaiter("something"); // does *not* block.
|
||||
* doSomething(); // causes the event to be fired.
|
||||
* waiter(); // will return as soon as the event fires, even if it fires
|
||||
* // before this function is called.
|
||||
*
|
||||
* @param aEventName
|
||||
* String event to wait for.
|
||||
*/
|
||||
createEventWaiter(aEventName) {
|
||||
Logger.logInfo("Setting up wait for " + aEventName + "...");
|
||||
let cb = Async.makeSpinningCallback();
|
||||
Svc.Obs.add(aEventName, cb);
|
||||
return function() {
|
||||
try {
|
||||
cb.wait();
|
||||
} finally {
|
||||
Svc.Obs.remove(aEventName, cb);
|
||||
Logger.logInfo(aEventName + " observed!");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Synchronously wait for the named event to be observed.
|
||||
*
|
||||
* When the event is observed, the function will wait an extra tick before
|
||||
* returning.
|
||||
*
|
||||
* Note that in general, you should probably use createEventWaiter unless you
|
||||
* are 100% sure that the event being waited on can only be sent after this
|
||||
* call adds the listener.
|
||||
*
|
||||
* @param aEventName
|
||||
* String event to wait for.
|
||||
*/
|
||||
waitForEvent: function waitForEvent(aEventName) {
|
||||
Logger.logInfo("Waiting for " + aEventName + "...");
|
||||
let cb = Async.makeSpinningCallback();
|
||||
Svc.Obs.add(aEventName, cb);
|
||||
cb.wait();
|
||||
Svc.Obs.remove(aEventName, cb);
|
||||
Logger.logInfo(aEventName + " observed!");
|
||||
this.createEventWaiter(aEventName)();
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Waits for Sync to logged in before returning
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user